Update ArduinoJSON

This commit is contained in:
iranl
2025-01-06 22:27:44 +01:00
parent 565a887c95
commit a379b30c11
82 changed files with 1098 additions and 1500 deletions

View File

@@ -0,0 +1,29 @@
#!/usr/bin/awk -f
# Start echoing after the first list item
/\* / {
STARTED=1
EMPTY_LINE=0
}
# Remember if we have seen an empty line
/^[[:space:]]*$/ {
EMPTY_LINE=1
}
# Exit when seeing a new version number
/^v[[:digit:]]/ {
if (STARTED) exit
}
# Print if the line is not empty
# and restore the empty line we have skipped
!/^[[:space:]]*$/ {
if (STARTED) {
if (EMPTY_LINE) {
print ""
EMPTY_LINE=0
}
print
}
}

View File

@@ -1,14 +0,0 @@
#!/bin/bash
set -eu
TAG="$1"
CHANGELOG="$2"
cat << END
## Changes
$(awk '/\* /{ FOUND=1 } /^[[:space:]]*$/ { if(FOUND) exit } { if(FOUND) print }' "$CHANGELOG")
[View version history](https://github.com/bblanchon/ArduinoJson/blob/$TAG/CHANGELOG.md)
END

View File

@@ -14,5 +14,5 @@ date: '$(date +'%Y-%m-%d')'
$(extras/scripts/wandbox/publish.sh "$ARDUINOJSON_H")
---
$(awk '/\* /{ FOUND=1; print; next } { if (FOUND) exit}' "$CHANGELOG")
$(extras/scripts/extract_changes.awk "$CHANGELOG")
END

View File

@@ -15,7 +15,7 @@ compile() {
"code":$(read_string "$FILE_PATH"),
"codes": [{"file":"ArduinoJson.h","code":$(read_string "$ARDUINOJSON_H")}],
"options": "warning,c++11",
"compiler": "gcc-5.5.0",
"compiler": "gcc-head",
"save": true
}
END

View File

@@ -67,7 +67,7 @@ TEST_CASE("JsonDocument::containsKey()") {
TEST_CASE("MemberProxy::containsKey()") {
JsonDocument doc;
auto mp = doc["hello"];
const auto& mp = doc["hello"];
SECTION("containsKey(const char*)") {
mp["key"] = "value";

View File

@@ -13,7 +13,6 @@ add_executable(JsonArrayTests
nesting.cpp
remove.cpp
size.cpp
std_string.cpp
subscript.cpp
unbound.cpp
)

View File

@@ -17,31 +17,112 @@ TEST_CASE("JsonArray::add(T)") {
SECTION("int") {
array.add(123);
REQUIRE(123 == array[0].as<int>());
REQUIRE(array[0].is<int>());
REQUIRE(array[0].is<double>());
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
});
}
SECTION("double") {
array.add(123.45);
REQUIRE(123.45 == array[0].as<double>());
REQUIRE(array[0].is<double>());
REQUIRE_FALSE(array[0].is<bool>());
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
});
}
SECTION("bool") {
array.add(true);
REQUIRE(true == array[0].as<bool>());
REQUIRE(array[0].as<bool>() == true);
REQUIRE(array[0].is<bool>());
REQUIRE_FALSE(array[0].is<int>());
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
});
}
SECTION("string literal") {
array.add("hello");
REQUIRE(array[0].as<std::string>() == "hello");
REQUIRE(array[0].is<const char*>());
REQUIRE(array[0].is<int>() == false);
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
});
}
SECTION("std::string") {
array.add("hello"_s);
REQUIRE(array[0].as<std::string>() == "hello");
REQUIRE(array[0].is<const char*>() == true);
REQUIRE(array[0].is<int>() == false);
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("hello")),
});
}
SECTION("const char*") {
const char* str = "hello";
array.add(str);
REQUIRE(str == array[0].as<std::string>());
REQUIRE(array[0].is<const char*>());
REQUIRE_FALSE(array[0].is<int>());
REQUIRE(array[0].as<std::string>() == "hello");
REQUIRE(array[0].as<const char*>() != str);
REQUIRE(array[0].is<const char*>() == true);
REQUIRE(array[0].is<int>() == false);
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("hello")),
});
}
SECTION("serialized(const char*)") {
array.add(serialized("{}"));
REQUIRE(doc.as<std::string>() == "[{}]");
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("{}")),
});
}
SECTION("serialized(char*)") {
array.add(serialized(const_cast<char*>("{}")));
REQUIRE(doc.as<std::string>() == "[{}]");
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("{}")),
});
}
SECTION("serialized(std::string)") {
array.add(serialized("{}"_s));
REQUIRE(doc.as<std::string>() == "[{}]");
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("{}")),
});
}
SECTION("serialized(std::string)") {
array.add(serialized("\0XX"_s));
REQUIRE(doc.as<std::string>() == "[\0XX]"_s);
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString(" XX")),
});
}
#ifdef HAS_VARIABLE_LENGTH_ARRAY
@@ -52,7 +133,12 @@ TEST_CASE("JsonArray::add(T)") {
array.add(vla);
REQUIRE("world"_s == array[0]);
strcpy(vla, "hello");
REQUIRE(array[0] == "world"_s);
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("hello")),
});
}
#endif
@@ -99,61 +185,6 @@ TEST_CASE("JsonArray::add(T)") {
REQUIRE(str == array[0]);
}
SECTION("should not duplicate const char*") {
array.add("world");
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
});
}
SECTION("should duplicate char*") {
array.add(const_cast<char*>("world"));
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("world")),
});
}
SECTION("should duplicate std::string") {
array.add("world"_s);
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("world")),
});
}
SECTION("should duplicate serialized(const char*)") {
array.add(serialized("{}"));
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("{}")),
});
}
SECTION("should duplicate serialized(char*)") {
array.add(serialized(const_cast<char*>("{}")));
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("{}")),
});
}
SECTION("should duplicate serialized(std::string)") {
array.add(serialized("{}"_s));
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("{}")),
});
}
SECTION("should duplicate serialized(std::string)") {
array.add(serialized("\0XX"_s));
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString(" XX")),
});
}
}
TEST_CASE("JsonArray::add<T>()") {

View File

@@ -1,34 +0,0 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
#include "Literals.hpp"
static void eraseString(std::string& str) {
char* p = const_cast<char*>(str.c_str());
while (*p)
*p++ = '*';
}
TEST_CASE("std::string") {
JsonDocument doc;
JsonArray array = doc.to<JsonArray>();
SECTION("add()") {
std::string value("hello");
array.add(value);
eraseString(value);
REQUIRE("hello"_s == array[0]);
}
SECTION("operator[]") {
std::string value("world");
array.add("hello");
array[0] = value;
eraseString(value);
REQUIRE("world"_s == array[0]);
}
}

View File

@@ -59,15 +59,56 @@ TEST_CASE("JsonArray::operator[]") {
REQUIRE(false == array[0].is<int>());
}
SECTION("const char*") {
const char* str = "hello";
SECTION("string literal") {
array[0] = "hello";
array[0] = str;
REQUIRE(str == array[0].as<const char*>());
REQUIRE(array[0].as<std::string>() == "hello");
REQUIRE(true == array[0].is<const char*>());
REQUIRE(false == array[0].is<int>());
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("world")),
});
}
SECTION("const char*") {
const char* str = "hello";
array[0] = str;
REQUIRE(array[0].as<std::string>() == "hello");
REQUIRE(true == array[0].is<const char*>());
REQUIRE(false == array[0].is<int>());
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("world")),
});
}
SECTION("std::string") {
array[0] = "hello"_s;
REQUIRE(array[0].as<std::string>() == "hello");
REQUIRE(true == array[0].is<const char*>());
REQUIRE(false == array[0].is<int>());
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("world")),
});
}
#ifdef HAS_VARIABLE_LENGTH_ARRAY
SECTION("VLA") {
size_t i = 16;
char vla[i];
strcpy(vla, "world");
array.add("hello");
array[0] = vla;
REQUIRE(array[0] == "world"_s);
}
#endif
SECTION("nested array") {
JsonDocument doc2;
JsonArray arr2 = doc2.to<JsonArray>();
@@ -114,58 +155,11 @@ TEST_CASE("JsonArray::operator[]") {
REQUIRE(str == array[0]);
}
SECTION("should not duplicate const char*") {
array[0] = "world";
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
});
}
SECTION("should duplicate char*") {
array[0] = const_cast<char*>("world");
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("world")),
});
}
SECTION("should duplicate std::string") {
array[0] = "world"_s;
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("world")),
});
}
SECTION("array[0].to<JsonObject>()") {
JsonObject obj = array[0].to<JsonObject>();
REQUIRE(obj.isNull() == false);
}
#ifdef HAS_VARIABLE_LENGTH_ARRAY
SECTION("set(VLA)") {
size_t i = 16;
char vla[i];
strcpy(vla, "world");
array.add("hello");
array[0].set(vla);
REQUIRE("world"_s == array[0]);
}
SECTION("operator=(VLA)") {
size_t i = 16;
char vla[i];
strcpy(vla, "world");
array.add("hello");
array[0] = vla;
REQUIRE("world"_s == array[0]);
}
#endif
SECTION("Use a JsonVariant as index") {
array[0] = 1;
array[1] = 2;

View File

@@ -693,6 +693,15 @@ TEST_CASE("Filtering") {
"null",
0,
},
{
"NUL character in key",
"{\"x\":0,\"x\\u0000a\":1,\"x\\u0000b\":2}",
"{\"x\\u0000a\":true}",
10,
DeserializationError::Ok,
"{\"x\\u0000a\":1}",
sizeofObject(1) + sizeofString("x?a"),
},
};
for (auto& tc : testCases) {

View File

@@ -6,6 +6,7 @@
#include <catch.hpp>
#include "Allocators.hpp"
#include "Literals.hpp"
using ArduinoJson::detail::sizeofObject;
@@ -322,10 +323,11 @@ TEST_CASE("deserialize JSON object") {
SECTION("NUL in keys") {
DeserializationError err =
deserializeJson(doc, "{\"x\\u0000a\":1,\"x\\u0000b\":2}");
deserializeJson(doc, "{\"x\":0,\"x\\u0000a\":1,\"x\\u0000b\":2}");
REQUIRE(err == DeserializationError::Ok);
REQUIRE(doc.as<std::string>() == "{\"x\\u0000a\":1,\"x\\u0000b\":2}");
REQUIRE(doc.as<std::string>() ==
"{\"x\":0,\"x\\u0000a\":1,\"x\\u0000b\":2}");
}
}

View File

@@ -5,37 +5,60 @@
#include <ArduinoJson.h>
#include <catch.hpp>
#include "Allocators.hpp"
#include "Literals.hpp"
using ElementProxy = ArduinoJson::detail::ElementProxy<JsonDocument&>;
TEST_CASE("ElementProxy::add()") {
JsonDocument doc;
SpyingAllocator spy;
JsonDocument doc(&spy);
doc.add<JsonVariant>();
ElementProxy ep = doc[0];
const ElementProxy& ep = doc[0];
SECTION("add(int)") {
SECTION("integer") {
ep.add(42);
REQUIRE(doc.as<std::string>() == "[[42]]");
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
});
}
SECTION("add(const char*)") {
SECTION("string literal") {
ep.add("world");
REQUIRE(doc.as<std::string>() == "[[\"world\"]]");
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
});
}
SECTION("add(char[])") {
SECTION("const char*") {
const char* s = "world";
ep.add(s);
REQUIRE(doc.as<std::string>() == "[[\"world\"]]");
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("world")),
});
}
SECTION("char[]") {
char s[] = "world";
ep.add(s);
strcpy(s, "!!!!!");
REQUIRE(doc.as<std::string>() == "[[\"world\"]]");
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("world")),
});
}
#ifdef HAS_VARIABLE_LENGTH_ARRAY
SECTION("set(vla)") {
SECTION("VLA") {
size_t i = 8;
char vla[i];
strcpy(vla, "world");
@@ -43,6 +66,10 @@ TEST_CASE("ElementProxy::add()") {
ep.add(vla);
REQUIRE(doc.as<std::string>() == "[[\"world\"]]");
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("world")),
});
}
#endif
}
@@ -50,7 +77,7 @@ TEST_CASE("ElementProxy::add()") {
TEST_CASE("ElementProxy::clear()") {
JsonDocument doc;
doc.add<JsonVariant>();
ElementProxy ep = doc[0];
const ElementProxy& ep = doc[0];
SECTION("size goes back to zero") {
ep.add(42);
@@ -110,7 +137,7 @@ TEST_CASE("ElementProxy::operator==()") {
TEST_CASE("ElementProxy::remove()") {
JsonDocument doc;
doc.add<JsonVariant>();
ElementProxy ep = doc[0];
const ElementProxy& ep = doc[0];
SECTION("remove(int)") {
ep.add(1);
@@ -157,7 +184,7 @@ TEST_CASE("ElementProxy::remove()") {
TEST_CASE("ElementProxy::set()") {
JsonDocument doc;
ElementProxy ep = doc[0];
const ElementProxy& ep = doc[0];
SECTION("set(int)") {
ep.set(42);
@@ -195,7 +222,7 @@ TEST_CASE("ElementProxy::set()") {
TEST_CASE("ElementProxy::size()") {
JsonDocument doc;
doc.add<JsonVariant>();
ElementProxy ep = doc[0];
const ElementProxy& ep = doc[0];
SECTION("returns 0") {
REQUIRE(ep.size() == 0);
@@ -216,7 +243,7 @@ TEST_CASE("ElementProxy::size()") {
TEST_CASE("ElementProxy::operator[]") {
JsonDocument doc;
ElementProxy ep = doc[1];
const ElementProxy& ep = doc[1];
SECTION("set member") {
ep["world"] = 42;
@@ -247,7 +274,7 @@ TEST_CASE("ElementProxy cast to JsonVariantConst") {
JsonDocument doc;
doc[0] = "world";
const ElementProxy ep = doc[0];
const ElementProxy& ep = doc[0];
JsonVariantConst var = ep;
@@ -258,7 +285,7 @@ TEST_CASE("ElementProxy cast to JsonVariant") {
JsonDocument doc;
doc[0] = "world";
ElementProxy ep = doc[0];
const ElementProxy& ep = doc[0];
JsonVariant var = ep;

View File

@@ -14,27 +14,54 @@
using ArduinoJson::detail::sizeofArray;
using ArduinoJson::detail::sizeofObject;
using MemberProxy =
ArduinoJson::detail::MemberProxy<JsonDocument&, const char*>;
TEST_CASE("MemberProxy::add()") {
JsonDocument doc;
MemberProxy mp = doc["hello"];
SpyingAllocator spy;
JsonDocument doc(&spy);
const auto& mp = doc["hello"];
SECTION("add(int)") {
SECTION("integer") {
mp.add(42);
REQUIRE(doc.as<std::string>() == "{\"hello\":[42]}");
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
});
}
SECTION("add(const char*)") {
SECTION("string literal") {
mp.add("world");
REQUIRE(doc.as<std::string>() == "{\"hello\":[\"world\"]}");
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
});
}
SECTION("const char*") {
const char* temp = "world";
mp.add(temp);
REQUIRE(doc.as<std::string>() == "{\"hello\":[\"world\"]}");
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("world")),
});
}
SECTION("char[]") {
char temp[] = "world";
mp.add(temp);
REQUIRE(doc.as<std::string>() == "{\"hello\":[\"world\"]}");
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("world")),
});
}
#ifdef HAS_VARIABLE_LENGTH_ARRAY
SECTION("add(vla)") {
SECTION("VLA") {
size_t i = 16;
char vla[i];
strcpy(vla, "world");
@@ -42,13 +69,17 @@ TEST_CASE("MemberProxy::add()") {
mp.add(vla);
REQUIRE(doc.as<std::string>() == "{\"hello\":[\"world\"]}");
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("world")),
});
}
#endif
}
TEST_CASE("MemberProxy::clear()") {
JsonDocument doc;
MemberProxy mp = doc["hello"];
const auto& mp = doc["hello"];
SECTION("size goes back to zero") {
mp.add(42);
@@ -139,7 +170,7 @@ TEST_CASE("MemberProxy::operator|()") {
TEST_CASE("MemberProxy::remove()") {
JsonDocument doc;
MemberProxy mp = doc["hello"];
const auto& mp = doc["hello"];
SECTION("remove(int)") {
mp.add(1);
@@ -186,7 +217,7 @@ TEST_CASE("MemberProxy::remove()") {
TEST_CASE("MemberProxy::set()") {
JsonDocument doc;
MemberProxy mp = doc["hello"];
const auto& mp = doc["hello"];
SECTION("set(int)") {
mp.set(42);
@@ -223,7 +254,7 @@ TEST_CASE("MemberProxy::set()") {
TEST_CASE("MemberProxy::size()") {
JsonDocument doc;
MemberProxy mp = doc["hello"];
const auto& mp = doc["hello"];
SECTION("returns 0") {
REQUIRE(mp.size() == 0);
@@ -246,7 +277,7 @@ TEST_CASE("MemberProxy::size()") {
TEST_CASE("MemberProxy::operator[]") {
JsonDocument doc;
MemberProxy mp = doc["hello"];
const auto& mp = doc["hello"];
SECTION("set member") {
mp["world"] = 42;
@@ -265,7 +296,7 @@ TEST_CASE("MemberProxy cast to JsonVariantConst") {
JsonDocument doc;
doc["hello"] = "world";
const MemberProxy mp = doc["hello"];
const auto& mp = doc["hello"];
JsonVariantConst var = mp;
@@ -276,7 +307,7 @@ TEST_CASE("MemberProxy cast to JsonVariant") {
JsonDocument doc;
doc["hello"] = "world";
MemberProxy mp = doc["hello"];
const auto& mp = doc["hello"];
JsonVariant var = mp;

View File

@@ -26,7 +26,7 @@ TEST_CASE("JsonDocument::add(T)") {
});
}
SECTION("const char*") {
SECTION("string literal") {
doc.add("hello");
REQUIRE(doc.as<std::string>() == "[\"hello\"]");
@@ -35,6 +35,17 @@ TEST_CASE("JsonDocument::add(T)") {
});
}
SECTION("const char*") {
const char* value = "hello";
doc.add(value);
REQUIRE(doc.as<std::string>() == "[\"hello\"]");
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("hello")),
});
}
SECTION("std::string") {
doc.add("example"_s);
doc.add("example"_s);

View File

@@ -1,54 +0,0 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
#include "Literals.hpp"
TEST_CASE("JsonDocument::containsKey()") {
JsonDocument doc;
SECTION("returns true on object") {
doc["hello"] = "world";
REQUIRE(doc.containsKey("hello") == true);
}
SECTION("returns true when value is null") {
doc["hello"] = static_cast<const char*>(0);
REQUIRE(doc.containsKey("hello") == true);
}
SECTION("returns true when key is a std::string") {
doc["hello"] = "world";
REQUIRE(doc.containsKey("hello"_s) == true);
}
SECTION("returns false on object") {
doc["world"] = "hello";
REQUIRE(doc.containsKey("hello") == false);
}
SECTION("returns false on array") {
doc.add("hello");
REQUIRE(doc.containsKey("hello") == false);
}
SECTION("returns false on null") {
REQUIRE(doc.containsKey("hello") == false);
}
SECTION("support JsonVariant") {
doc["hello"] = "world";
doc["key"] = "hello";
REQUIRE(doc.containsKey(doc["key"]) == true);
REQUIRE(doc.containsKey(doc["foo"]) == false);
}
}

View File

@@ -12,49 +12,41 @@ TEST_CASE("Issue #1120") {
SECTION("MemberProxy<std::string>::isNull()") {
SECTION("returns false") {
auto value = doc["contents"_s];
CHECK(value.isNull() == false);
CHECK(doc["contents"_s].isNull() == false);
}
SECTION("returns true") {
auto value = doc["zontents"_s];
CHECK(value.isNull() == true);
CHECK(doc["zontents"_s].isNull() == true);
}
}
SECTION("ElementProxy<MemberProxy<const char*> >::isNull()") {
SECTION("returns false") { // Issue #1120
auto value = doc["contents"][1];
CHECK(value.isNull() == false);
CHECK(doc["contents"][1].isNull() == false);
}
SECTION("returns true") {
auto value = doc["contents"][2];
CHECK(value.isNull() == true);
CHECK(doc["contents"][2].isNull() == true);
}
}
SECTION("MemberProxy<ElementProxy<MemberProxy>, const char*>::isNull()") {
SECTION("returns false") {
auto value = doc["contents"][1]["module"];
CHECK(value.isNull() == false);
CHECK(doc["contents"][1]["module"].isNull() == false);
}
SECTION("returns true") {
auto value = doc["contents"][1]["zodule"];
CHECK(value.isNull() == true);
CHECK(doc["contents"][1]["zodule"].isNull() == true);
}
}
SECTION("MemberProxy<ElementProxy<MemberProxy>, std::string>::isNull()") {
SECTION("returns false") {
auto value = doc["contents"][1]["module"_s];
CHECK(value.isNull() == false);
CHECK(doc["contents"][1]["module"_s].isNull() == false);
}
SECTION("returns true") {
auto value = doc["contents"][1]["zodule"_s];
CHECK(value.isNull() == true);
CHECK(doc["contents"][1]["zodule"_s].isNull() == true);
}
}
}

View File

@@ -20,11 +20,21 @@ TEST_CASE("JsonDocument::remove()") {
REQUIRE(doc.as<std::string>() == "[1,3]");
}
SECTION("string literal") {
doc["a"] = 1;
doc["a\0b"_s] = 2;
doc["b"] = 3;
doc.remove("a\0b");
REQUIRE(doc.as<std::string>() == "{\"a\":1,\"b\":3}");
}
SECTION("remove(const char *)") {
doc["a"] = 1;
doc["b"] = 2;
doc.remove("a");
doc.remove(static_cast<const char*>("a"));
REQUIRE(doc.as<std::string>() == "{\"b\":2}");
}

View File

@@ -11,6 +11,21 @@ TEST_CASE("JsonDocument::set()") {
SpyingAllocator spy;
JsonDocument doc(&spy);
SECTION("nullptr") {
doc.set(nullptr);
REQUIRE(doc.isNull());
REQUIRE(spy.log() == AllocatorLog{});
}
SECTION("integer&") {
int toto = 42;
doc.set(toto);
REQUIRE(doc.as<std::string>() == "42");
REQUIRE(spy.log() == AllocatorLog{});
}
SECTION("integer") {
doc.set(42);
@@ -18,13 +33,23 @@ TEST_CASE("JsonDocument::set()") {
REQUIRE(spy.log() == AllocatorLog{});
}
SECTION("const char*") {
SECTION("string literal") {
doc.set("example");
REQUIRE(doc.as<const char*>() == "example"_s);
REQUIRE(spy.log() == AllocatorLog{});
}
SECTION("const char*") {
const char* value = "example";
doc.set(value);
REQUIRE(doc.as<const char*>() == "example"_s);
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofString("example")),
});
}
SECTION("std::string") {
doc.set("example"_s);

View File

@@ -5,6 +5,7 @@
#include <ArduinoJson.h>
#include <catch.hpp>
#include "Allocators.hpp"
#include "Literals.hpp"
TEST_CASE("JsonDocument::operator[]") {
@@ -16,8 +17,16 @@ TEST_CASE("JsonDocument::operator[]") {
doc["abc\0d"_s] = "ABCD";
SECTION("const char*") {
const char* key = "abc";
REQUIRE(doc[key] == "ABC");
REQUIRE(cdoc[key] == "ABC");
}
SECTION("string literal") {
REQUIRE(doc["abc"] == "ABC");
REQUIRE(cdoc["abc"] == "ABC");
REQUIRE(doc["abc\0d"] == "ABCD");
REQUIRE(cdoc["abc\0d"] == "ABCD");
}
SECTION("std::string") {
@@ -94,3 +103,65 @@ TEST_CASE("JsonDocument automatically promotes to array") {
REQUIRE(doc.as<std::string>() == "[null,null,2]");
}
TEST_CASE("JsonDocument::operator[] key storage") {
SpyingAllocator spy;
JsonDocument doc(&spy);
SECTION("string literal") {
doc["hello"] = 0;
REQUIRE(doc.as<std::string>() == "{\"hello\":0}");
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
});
}
SECTION("const char*") {
const char* key = "hello";
doc[key] = 0;
REQUIRE(doc.as<std::string>() == "{\"hello\":0}");
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("hello")),
});
}
SECTION("char[]") {
char key[] = "hello";
doc[key] = 0;
REQUIRE(doc.as<std::string>() == "{\"hello\":0}");
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("hello")),
});
}
SECTION("std::string") {
doc["hello"_s] = 0;
REQUIRE(doc.as<std::string>() == "{\"hello\":0}");
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("hello")),
});
}
#if defined(HAS_VARIABLE_LENGTH_ARRAY) && \
!defined(SUBSCRIPT_CONFLICTS_WITH_BUILTIN_OPERATOR)
SECTION("VLA") {
size_t i = 16;
char vla[i];
strcpy(vla, "hello");
doc[vla] = 0;
REQUIRE(doc.as<std::string>() == "{\"hello\":0}");
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("hello")),
});
}
#endif
}

View File

@@ -1,39 +0,0 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
TEST_CASE("JsonObject::containsKey()") {
JsonDocument doc;
JsonObject obj = doc.to<JsonObject>();
obj["hello"] = 42;
SECTION("returns true only if key is present") {
REQUIRE(false == obj.containsKey("world"));
REQUIRE(true == obj.containsKey("hello"));
}
SECTION("returns false after remove()") {
obj.remove("hello");
REQUIRE(false == obj.containsKey("hello"));
}
#ifdef HAS_VARIABLE_LENGTH_ARRAY
SECTION("key is a VLA") {
size_t i = 16;
char vla[i];
strcpy(vla, "hello");
REQUIRE(true == obj.containsKey(vla));
}
#endif
SECTION("key is a JsonVariant") {
doc["key"] = "hello";
REQUIRE(true == obj.containsKey(obj["key"]));
REQUIRE(false == obj.containsKey(obj["hello"]));
}
}

View File

@@ -158,7 +158,7 @@ TEST_CASE("JsonObject::operator[]") {
}
SECTION("should duplicate a non-static JsonString key") {
obj[JsonString("hello", JsonString::Copied)] = "world";
obj[JsonString("hello", false)] = "world";
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("hello")),
@@ -166,7 +166,7 @@ TEST_CASE("JsonObject::operator[]") {
}
SECTION("should not duplicate a static JsonString key") {
obj[JsonString("hello", JsonString::Linked)] = "world";
obj[JsonString("hello", true)] = "world";
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
});

View File

@@ -1,40 +0,0 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
#include "Literals.hpp"
TEST_CASE("JsonObjectConst::containsKey()") {
JsonDocument doc;
doc["hello"] = 42;
auto obj = doc.as<JsonObjectConst>();
SECTION("supports const char*") {
REQUIRE(false == obj.containsKey("world"));
REQUIRE(true == obj.containsKey("hello"));
}
SECTION("supports std::string") {
REQUIRE(false == obj.containsKey("world"_s));
REQUIRE(true == obj.containsKey("hello"_s));
}
#ifdef HAS_VARIABLE_LENGTH_ARRAY
SECTION("supports VLA") {
size_t i = 16;
char vla[i];
strcpy(vla, "hello");
REQUIRE(true == obj.containsKey(vla));
}
#endif
SECTION("supports JsonVariant") {
doc["key"] = "hello";
REQUIRE(true == obj.containsKey(obj["key"]));
REQUIRE(false == obj.containsKey(obj["hello"]));
}
}

View File

@@ -184,7 +184,7 @@ TEST_CASE("JsonVariant::as()") {
REQUIRE(variant.as<long>() == 42L);
REQUIRE(variant.as<JsonString>() == "42");
REQUIRE(variant.as<JsonString>().isLinked() == true);
REQUIRE(variant.as<JsonString>().isStatic() == true);
}
SECTION("set(\"hello\")") {
@@ -207,7 +207,7 @@ TEST_CASE("JsonVariant::as()") {
REQUIRE(variant.as<const char*>() == "4.2"_s);
REQUIRE(variant.as<std::string>() == "4.2"_s);
REQUIRE(variant.as<JsonString>() == "4.2");
REQUIRE(variant.as<JsonString>().isLinked() == false);
REQUIRE(variant.as<JsonString>().isStatic() == false);
}
SECTION("set(\"true\")") {

View File

@@ -1,36 +0,0 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <stdint.h>
#include <catch.hpp>
#include "Literals.hpp"
TEST_CASE("JsonVariant::containsKey()") {
JsonDocument doc;
JsonVariant var = doc.to<JsonVariant>();
SECTION("containsKey(const char*)") {
var["hello"] = "world";
REQUIRE(var.containsKey("hello") == true);
REQUIRE(var.containsKey("world") == false);
}
SECTION("containsKey(std::string)") {
var["hello"] = "world";
REQUIRE(var.containsKey("hello"_s) == true);
REQUIRE(var.containsKey("world"_s) == false);
}
SECTION("containsKey(JsonVariant)") {
var["hello"] = "world";
var["key"] = "hello";
REQUIRE(var.containsKey(doc["key"]) == true);
REQUIRE(var.containsKey(doc["foo"]) == false);
}
}

View File

@@ -17,6 +17,15 @@ TEST_CASE("JsonVariant::set() when there is enough memory") {
JsonDocument doc(&spy);
JsonVariant variant = doc.to<JsonVariant>();
SECTION("string literal") {
bool result = variant.set("hello\0world");
REQUIRE(result == true);
CHECK(variant ==
"hello"_s); // linked string cannot contain '\0' at the moment
CHECK(spy.log() == AllocatorLog{});
}
SECTION("const char*") {
char str[16];
@@ -25,8 +34,10 @@ TEST_CASE("JsonVariant::set() when there is enough memory") {
strcpy(str, "world");
REQUIRE(result == true);
REQUIRE(variant == "world"); // stores by pointer
REQUIRE(spy.log() == AllocatorLog{});
REQUIRE(variant == "hello"); // stores by copy
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofString("hello")),
});
}
SECTION("(const char*)0") {
@@ -34,6 +45,7 @@ TEST_CASE("JsonVariant::set() when there is enough memory") {
REQUIRE(result == true);
REQUIRE(variant.isNull());
REQUIRE(variant.as<const char*>() == nullptr);
REQUIRE(spy.log() == AllocatorLog{});
}
@@ -105,16 +117,14 @@ TEST_CASE("JsonVariant::set() when there is enough memory") {
#endif
SECTION("std::string") {
std::string str;
str = "hello";
std::string str = "hello\0world"_s;
bool result = variant.set(str);
str.replace(0, 5, "world");
REQUIRE(result == true);
REQUIRE(variant == "hello"); // stores by copy
REQUIRE(variant == "hello\0world"_s); // stores by copy
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofString("hello")),
Allocate(sizeofString("hello?world")),
});
}
@@ -122,7 +132,7 @@ TEST_CASE("JsonVariant::set() when there is enough memory") {
char str[16];
strcpy(str, "hello");
bool result = variant.set(JsonString(str, JsonString::Linked));
bool result = variant.set(JsonString(str, true));
strcpy(str, "world");
REQUIRE(result == true);
@@ -134,7 +144,7 @@ TEST_CASE("JsonVariant::set() when there is enough memory") {
char str[16];
strcpy(str, "hello");
bool result = variant.set(JsonString(str, JsonString::Copied));
bool result = variant.set(JsonString(str));
strcpy(str, "world");
REQUIRE(result == true);

View File

@@ -7,14 +7,8 @@
#include <catch.hpp>
#include <limits>
template <typename T>
void checkValue(T expected) {
JsonDocument doc;
JsonVariant variant = doc.to<JsonVariant>();
variant.set(expected);
REQUIRE(expected == variant.as<T>());
}
#include "Allocators.hpp"
#include "Literals.hpp"
template <typename T>
void checkReference(T& expected) {
@@ -39,27 +33,29 @@ void checkNumericType() {
}
TEST_CASE("JsonVariant set()/get()") {
SpyingAllocator spy;
JsonDocument doc(&spy);
JsonVariant variant = doc.to<JsonVariant>();
#if ARDUINOJSON_USE_LONG_LONG
SECTION("SizeOfJsonInteger") {
REQUIRE(8 == sizeof(JsonInteger));
}
#endif
SECTION("Null") {
checkValue<const char*>(NULL);
}
SECTION("const char*") {
checkValue<const char*>("hello");
}
SECTION("std::string") {
checkValue<std::string>("hello");
}
// /!\ Most test were moved to `JsonVariant/set.cpp`
// TODO: move the remaining tests too
SECTION("False") {
checkValue<bool>(false);
variant.set(false);
REQUIRE(variant.as<bool>() == false);
REQUIRE(spy.log() == AllocatorLog{});
}
SECTION("True") {
checkValue<bool>(true);
variant.set(true);
REQUIRE(variant.as<bool>() == true);
REQUIRE(spy.log() == AllocatorLog{});
}
SECTION("Double") {
@@ -129,10 +125,12 @@ TEST_CASE("JsonVariant set()/get()") {
#endif
SECTION("CanStoreObject") {
JsonDocument doc;
JsonObject object = doc.to<JsonObject>();
JsonDocument doc2;
JsonObject object = doc2.to<JsonObject>();
checkValue<JsonObject>(object);
variant.set(object);
REQUIRE(variant.is<JsonObject>());
REQUIRE(variant.as<JsonObject>() == object);
}
}

View File

@@ -1,41 +0,0 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <stdint.h>
#include <catch.hpp>
#include "Literals.hpp"
TEST_CASE("JsonVariantConst::containsKey()") {
JsonDocument doc;
doc["hello"] = "world";
JsonVariantConst var = doc.as<JsonVariant>();
SECTION("support const char*") {
REQUIRE(var.containsKey("hello") == true);
REQUIRE(var.containsKey("world") == false);
}
SECTION("support std::string") {
REQUIRE(var.containsKey("hello"_s) == true);
REQUIRE(var.containsKey("world"_s) == false);
}
#ifdef HAS_VARIABLE_LENGTH_ARRAY
SECTION("supports VLA") {
size_t i = 16;
char vla[i];
strcpy(vla, "hello");
REQUIRE(true == var.containsKey(vla));
}
#endif
SECTION("support JsonVariant") {
doc["key"] = "hello";
REQUIRE(var.containsKey(var["key"]) == true);
REQUIRE(var.containsKey(var["foo"]) == false);
}
}

View File

@@ -54,13 +54,22 @@ TEST_CASE("JsonVariantConst::operator[]") {
object["abc"_s] = "ABC";
object["abc\0d"_s] = "ABCD";
SECTION("supports const char*") {
SECTION("string literal") {
REQUIRE(var["ab"] == "AB"_s);
REQUIRE(var["abc"] == "ABC"_s);
REQUIRE(var["abc\0d"] == "ABCD"_s);
REQUIRE(var["def"].isNull());
REQUIRE(var[0].isNull());
}
SECTION("const char*") {
REQUIRE(var[static_cast<const char*>("ab")] == "AB"_s);
REQUIRE(var[static_cast<const char*>("abc")] == "ABC"_s);
REQUIRE(var[static_cast<const char*>("abc\0d")] == "ABC"_s);
REQUIRE(var[static_cast<const char*>("def")].isNull());
REQUIRE(var[static_cast<const char*>(0)].isNull());
}
SECTION("supports std::string") {
REQUIRE(var["ab"_s] == "AB"_s);
REQUIRE(var["abc"_s] == "ABC"_s);

View File

@@ -1,44 +0,0 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#include <ArduinoJson/Numbers/FloatParts.hpp>
#include <catch.hpp>
using namespace ArduinoJson::detail;
TEST_CASE("FloatParts<double>") {
SECTION("1.7976931348623157E+308") {
FloatParts<double> parts(1.7976931348623157E+308);
REQUIRE(parts.integral == 1);
REQUIRE(parts.decimal == 797693135);
REQUIRE(parts.decimalPlaces == 9);
REQUIRE(parts.exponent == 308);
}
SECTION("4.94065645841247e-324") {
FloatParts<double> parts(4.94065645841247e-324);
REQUIRE(parts.integral == 4);
REQUIRE(parts.decimal == 940656458);
REQUIRE(parts.decimalPlaces == 9);
REQUIRE(parts.exponent == -324);
}
}
TEST_CASE("FloatParts<float>") {
SECTION("3.4E+38") {
FloatParts<float> parts(3.4E+38f);
REQUIRE(parts.integral == 3);
REQUIRE(parts.decimal == 4);
REQUIRE(parts.decimalPlaces == 1);
REQUIRE(parts.exponent == 38);
}
SECTION("1.17549435e38") {
FloatParts<float> parts(1.17549435e-38f);
REQUIRE(parts.integral == 1);
REQUIRE(parts.decimal == 175494);
REQUIRE(parts.decimalPlaces == 6);
REQUIRE(parts.exponent == -38);
}
}

View File

@@ -13,7 +13,7 @@ TEST_CASE("JsonString") {
CHECK(s.isNull() == true);
CHECK(s.c_str() == 0);
CHECK(s.isLinked() == true);
CHECK(s.isStatic() == true);
CHECK(s == JsonString());
CHECK(s != "");
}
@@ -96,7 +96,7 @@ TEST_CASE("JsonString") {
JsonString s("hello world", 5);
CHECK(s.size() == 5);
CHECK(s.isLinked() == true);
CHECK(s.isStatic() == false);
CHECK(s == "hello");
CHECK(s != "hello world");
}

View File

@@ -5,6 +5,7 @@
#include <Arduino.h>
#include <ArduinoJson/Strings/IsString.hpp>
#include <ArduinoJson/Strings/JsonString.hpp>
#include <ArduinoJson/Strings/StringAdapters.hpp>
#include <catch.hpp>
@@ -16,27 +17,36 @@ using ArduinoJson::JsonString;
using namespace ArduinoJson::detail;
TEST_CASE("adaptString()") {
SECTION("string literal") {
auto s = adaptString("bravo\0alpha");
CHECK(s.isNull() == false);
CHECK(s.size() == 11);
CHECK(s.isStatic() == true);
}
SECTION("null const char*") {
auto s = adaptString(static_cast<const char*>(0));
CHECK(s.isNull() == true);
CHECK(s.size() == 0);
CHECK(s.isLinked() == true);
}
SECTION("non-null const char*") {
auto s = adaptString("bravo");
const char* p = "bravo";
auto s = adaptString(p);
CHECK(s.isNull() == false);
CHECK(s.size() == 5);
CHECK(s.isLinked() == true);
CHECK(s.isStatic() == false);
CHECK(s.data() == p);
}
SECTION("null const char* + size") {
auto s = adaptString(static_cast<const char*>(0), 10);
CHECK(s.isNull() == true);
CHECK(s.isLinked() == false);
CHECK(s.isStatic() == false);
}
SECTION("non-null const char* + size") {
@@ -44,7 +54,7 @@ TEST_CASE("adaptString()") {
CHECK(s.isNull() == false);
CHECK(s.size() == 5);
CHECK(s.isLinked() == false);
CHECK(s.isStatic() == false);
}
SECTION("null Flash string") {
@@ -52,7 +62,7 @@ TEST_CASE("adaptString()") {
CHECK(s.isNull() == true);
CHECK(s.size() == 0);
CHECK(s.isLinked() == false);
CHECK(s.isStatic() == false);
}
SECTION("non-null Flash string") {
@@ -60,7 +70,7 @@ TEST_CASE("adaptString()") {
CHECK(s.isNull() == false);
CHECK(s.size() == 5);
CHECK(s.isLinked() == false);
CHECK(s.isStatic() == false);
}
SECTION("std::string") {
@@ -69,7 +79,7 @@ TEST_CASE("adaptString()") {
CHECK(s.isNull() == false);
CHECK(s.size() == 5);
CHECK(s.isLinked() == false);
CHECK(s.isStatic() == false);
}
SECTION("Arduino String") {
@@ -78,7 +88,7 @@ TEST_CASE("adaptString()") {
CHECK(s.isNull() == false);
CHECK(s.size() == 5);
CHECK(s.isLinked() == false);
CHECK(s.isStatic() == false);
}
SECTION("custom_string") {
@@ -87,25 +97,25 @@ TEST_CASE("adaptString()") {
CHECK(s.isNull() == false);
CHECK(s.size() == 5);
CHECK(s.isLinked() == false);
CHECK(s.isStatic() == false);
}
SECTION("JsonString linked") {
JsonString orig("hello", JsonString::Ownership::Linked);
JsonString orig("hello", true);
auto s = adaptString(orig);
CHECK(s.isNull() == false);
CHECK(s.size() == 5);
CHECK(s.isLinked() == true);
CHECK(s.isStatic() == true);
}
SECTION("JsonString copied") {
JsonString orig("hello", JsonString::Ownership::Copied);
JsonString orig("hello", false);
auto s = adaptString(orig);
CHECK(s.isNull() == false);
CHECK(s.size() == 5);
CHECK(s.isLinked() == false);
CHECK(s.isStatic() == false);
}
}

View File

@@ -211,6 +211,23 @@ TEST_CASE("Polyfills/type_traits") {
CHECK(is_enum<bool>::value == false);
CHECK(is_enum<double>::value == false);
}
SECTION("remove_cv") {
CHECK(is_same<remove_cv_t<const int>, int>::value);
CHECK(is_same<remove_cv_t<volatile int>, int>::value);
CHECK(is_same<remove_cv_t<const volatile int>, int>::value);
CHECK(is_same<remove_cv_t<int>, int>::value);
CHECK(is_same<remove_cv_t<decltype("toto")>, decltype("toto")>::value);
}
SECTION("decay") {
CHECK(is_same<decay_t<int>, int>::value);
CHECK(is_same<decay_t<int&>, int>::value);
CHECK(is_same<decay_t<int&&>, int>::value);
CHECK(is_same<decay_t<int[]>, int*>::value);
CHECK(is_same<decay_t<int[10]>, int*>::value);
CHECK(is_same<decay_t<decltype("toto")>, const char*>::value);
}
}
TEST_CASE("is_std_string") {

View File

@@ -139,7 +139,8 @@ TEST_CASE("serialize MsgPack value") {
SECTION("str 32") {
std::string shortest(65536, '?');
checkVariant(shortest.c_str(), "\xDB\x00\x01\x00\x00"_s + shortest);
checkVariant(JsonString(shortest.c_str(), true), // force store by pointer
"\xDB\x00\x01\x00\x00"_s + shortest);
}
SECTION("serialized(const char*)") {