Initial commit of Arduino libraries

This commit is contained in:
Sam
2025-05-23 10:47:41 +10:00
commit 5bfce5fc3e
2476 changed files with 1108481 additions and 0 deletions

View File

@@ -0,0 +1,31 @@
# ArduinoJson - https://arduinojson.org
# Copyright © 2014-2025, Benoit BLANCHON
# MIT License
add_executable(JsonDocumentTests
add.cpp
assignment.cpp
cast.cpp
clear.cpp
compare.cpp
constructor.cpp
ElementProxy.cpp
isNull.cpp
issue1120.cpp
MemberProxy.cpp
nesting.cpp
overflowed.cpp
remove.cpp
set.cpp
shrinkToFit.cpp
size.cpp
subscript.cpp
swap.cpp
)
add_test(JsonDocument JsonDocumentTests)
set_tests_properties(JsonDocument
PROPERTIES
LABELS "Catch"
)

View File

@@ -0,0 +1,297 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
#include "Allocators.hpp"
#include "Literals.hpp"
using ElementProxy = ArduinoJson::detail::ElementProxy<JsonDocument&>;
TEST_CASE("ElementProxy::add()") {
SpyingAllocator spy;
JsonDocument doc(&spy);
doc.add<JsonVariant>();
const ElementProxy& ep = doc[0];
SECTION("integer") {
ep.add(42);
REQUIRE(doc.as<std::string>() == "[[42]]");
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
});
}
SECTION("string literal") {
ep.add("world");
REQUIRE(doc.as<std::string>() == "[[\"world\"]]");
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
});
}
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("VLA") {
size_t i = 8;
char vla[i];
strcpy(vla, "world");
ep.add(vla);
REQUIRE(doc.as<std::string>() == "[[\"world\"]]");
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("world")),
});
}
#endif
}
TEST_CASE("ElementProxy::clear()") {
JsonDocument doc;
doc.add<JsonVariant>();
const ElementProxy& ep = doc[0];
SECTION("size goes back to zero") {
ep.add(42);
ep.clear();
REQUIRE(ep.size() == 0);
}
SECTION("isNull() return true") {
ep.add("hello");
ep.clear();
REQUIRE(ep.isNull() == true);
}
}
TEST_CASE("ElementProxy::operator==()") {
JsonDocument doc;
SECTION("1 vs 1") {
doc.add(1);
doc.add(1);
REQUIRE(doc[0] <= doc[1]);
REQUIRE(doc[0] == doc[1]);
REQUIRE(doc[0] >= doc[1]);
REQUIRE_FALSE(doc[0] != doc[1]);
REQUIRE_FALSE(doc[0] < doc[1]);
REQUIRE_FALSE(doc[0] > doc[1]);
}
SECTION("1 vs 2") {
doc.add(1);
doc.add(2);
REQUIRE(doc[0] != doc[1]);
REQUIRE(doc[0] < doc[1]);
REQUIRE(doc[0] <= doc[1]);
REQUIRE_FALSE(doc[0] == doc[1]);
REQUIRE_FALSE(doc[0] > doc[1]);
REQUIRE_FALSE(doc[0] >= doc[1]);
}
SECTION("'abc' vs 'bcd'") {
doc.add("abc");
doc.add("bcd");
REQUIRE(doc[0] != doc[1]);
REQUIRE(doc[0] < doc[1]);
REQUIRE(doc[0] <= doc[1]);
REQUIRE_FALSE(doc[0] == doc[1]);
REQUIRE_FALSE(doc[0] > doc[1]);
REQUIRE_FALSE(doc[0] >= doc[1]);
}
}
TEST_CASE("ElementProxy::remove()") {
JsonDocument doc;
doc.add<JsonVariant>();
const ElementProxy& ep = doc[0];
SECTION("remove(int)") {
ep.add(1);
ep.add(2);
ep.add(3);
ep.remove(1);
REQUIRE(ep.as<std::string>() == "[1,3]");
}
SECTION("remove(const char *)") {
ep["a"] = 1;
ep["b"] = 2;
ep.remove("a");
REQUIRE(ep.as<std::string>() == "{\"b\":2}");
}
SECTION("remove(std::string)") {
ep["a"] = 1;
ep["b"] = 2;
ep.remove("b"_s);
REQUIRE(ep.as<std::string>() == "{\"a\":1}");
}
#ifdef HAS_VARIABLE_LENGTH_ARRAY
SECTION("remove(vla)") {
ep["a"] = 1;
ep["b"] = 2;
size_t i = 4;
char vla[i];
strcpy(vla, "b");
ep.remove(vla);
REQUIRE(ep.as<std::string>() == "{\"a\":1}");
}
#endif
}
TEST_CASE("ElementProxy::set()") {
JsonDocument doc;
const ElementProxy& ep = doc[0];
SECTION("set(int)") {
ep.set(42);
REQUIRE(doc.as<std::string>() == "[42]");
}
SECTION("set(const char*)") {
ep.set("world");
REQUIRE(doc.as<std::string>() == "[\"world\"]");
}
SECTION("set(char[])") {
char s[] = "world";
ep.set(s);
strcpy(s, "!!!!!");
REQUIRE(doc.as<std::string>() == "[\"world\"]");
}
#ifdef HAS_VARIABLE_LENGTH_ARRAY
SECTION("set(VLA)") {
size_t i = 8;
char vla[i];
strcpy(vla, "world");
ep.set(vla);
REQUIRE(doc.as<std::string>() == "[\"world\"]");
}
#endif
}
TEST_CASE("ElementProxy::size()") {
JsonDocument doc;
doc.add<JsonVariant>();
const ElementProxy& ep = doc[0];
SECTION("returns 0") {
REQUIRE(ep.size() == 0);
}
SECTION("as an array, returns 2") {
ep.add(1);
ep.add(2);
REQUIRE(ep.size() == 2);
}
SECTION("as an object, returns 2") {
ep["a"] = 1;
ep["b"] = 2;
REQUIRE(ep.size() == 2);
}
}
TEST_CASE("ElementProxy::operator[]") {
JsonDocument doc;
const ElementProxy& ep = doc[1];
SECTION("set member") {
ep["world"] = 42;
REQUIRE(doc.as<std::string>() == "[null,{\"world\":42}]");
}
SECTION("set element") {
ep[2] = 42;
REQUIRE(doc.as<std::string>() == "[null,[null,null,42]]");
}
#ifdef HAS_VARIABLE_LENGTH_ARRAY
SECTION("set VLA") {
size_t i = 8;
char vla[i];
strcpy(vla, "world");
ep[0] = vla;
REQUIRE(doc.as<std::string>() == "[null,[\"world\"]]");
}
#endif
}
TEST_CASE("ElementProxy cast to JsonVariantConst") {
JsonDocument doc;
doc[0] = "world";
const ElementProxy& ep = doc[0];
JsonVariantConst var = ep;
CHECK(var.as<std::string>() == "world");
}
TEST_CASE("ElementProxy cast to JsonVariant") {
JsonDocument doc;
doc[0] = "world";
const ElementProxy& ep = doc[0];
JsonVariant var = ep;
CHECK(var.as<std::string>() == "world");
var.set("toto");
CHECK(doc.as<std::string>() == "[\"toto\"]");
}

View File

@@ -0,0 +1,432 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// MIT License
#define ARDUINOJSON_ENABLE_ARDUINO_STRING 1
#define ARDUINOJSON_ENABLE_PROGMEM 1
#include <ArduinoJson.h>
#include <catch.hpp>
#include "Allocators.hpp"
#include "Literals.hpp"
using ArduinoJson::detail::sizeofArray;
using ArduinoJson::detail::sizeofObject;
TEST_CASE("MemberProxy::add()") {
SpyingAllocator spy;
JsonDocument doc(&spy);
const auto& mp = doc["hello"];
SECTION("integer") {
mp.add(42);
REQUIRE(doc.as<std::string>() == "{\"hello\":[42]}");
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
});
}
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("VLA") {
size_t i = 16;
char vla[i];
strcpy(vla, "world");
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;
const auto& mp = doc["hello"];
SECTION("size goes back to zero") {
mp.add(42);
mp.clear();
REQUIRE(mp.size() == 0);
}
SECTION("isNull() return true") {
mp.add("hello");
mp.clear();
REQUIRE(mp.isNull() == true);
}
}
TEST_CASE("MemberProxy::operator==()") {
JsonDocument doc;
SECTION("1 vs 1") {
doc["a"] = 1;
doc["b"] = 1;
REQUIRE(doc["a"] <= doc["b"]);
REQUIRE(doc["a"] == doc["b"]);
REQUIRE(doc["a"] >= doc["b"]);
REQUIRE_FALSE(doc["a"] != doc["b"]);
REQUIRE_FALSE(doc["a"] < doc["b"]);
REQUIRE_FALSE(doc["a"] > doc["b"]);
}
SECTION("1 vs 2") {
doc["a"] = 1;
doc["b"] = 2;
REQUIRE(doc["a"] != doc["b"]);
REQUIRE(doc["a"] < doc["b"]);
REQUIRE(doc["a"] <= doc["b"]);
REQUIRE_FALSE(doc["a"] == doc["b"]);
REQUIRE_FALSE(doc["a"] > doc["b"]);
REQUIRE_FALSE(doc["a"] >= doc["b"]);
}
SECTION("'abc' vs 'bcd'") {
doc["a"] = "abc";
doc["b"] = "bcd";
REQUIRE(doc["a"] != doc["b"]);
REQUIRE(doc["a"] < doc["b"]);
REQUIRE(doc["a"] <= doc["b"]);
REQUIRE_FALSE(doc["a"] == doc["b"]);
REQUIRE_FALSE(doc["a"] > doc["b"]);
REQUIRE_FALSE(doc["a"] >= doc["b"]);
}
}
TEST_CASE("MemberProxy::operator|()") {
JsonDocument doc;
SECTION("const char*") {
doc["a"] = "hello";
REQUIRE((doc["a"] | "world") == "hello"_s);
REQUIRE((doc["b"] | "world") == "world"_s);
}
SECTION("Issue #1411") {
doc["sensor"] = "gps";
const char* test = "test"; // <- the literal must be captured in a variable
// to trigger the bug
const char* sensor = doc["sensor"] | test; // "gps"
REQUIRE(sensor == "gps"_s);
}
SECTION("Issue #1415") {
JsonObject object = doc.to<JsonObject>();
object["hello"] = "world";
JsonDocument emptyDoc;
JsonObject anotherObject = object["hello"] | emptyDoc.to<JsonObject>();
REQUIRE(anotherObject.isNull() == false);
REQUIRE(anotherObject.size() == 0);
}
}
TEST_CASE("MemberProxy::remove()") {
JsonDocument doc;
const auto& mp = doc["hello"];
SECTION("remove(int)") {
mp.add(1);
mp.add(2);
mp.add(3);
mp.remove(1);
REQUIRE(mp.as<std::string>() == "[1,3]");
}
SECTION("remove(const char *)") {
mp["a"] = 1;
mp["b"] = 2;
mp.remove("a");
REQUIRE(mp.as<std::string>() == "{\"b\":2}");
}
SECTION("remove(std::string)") {
mp["a"] = 1;
mp["b"] = 2;
mp.remove("b"_s);
REQUIRE(mp.as<std::string>() == "{\"a\":1}");
}
#ifdef HAS_VARIABLE_LENGTH_ARRAY
SECTION("remove(vla)") {
mp["a"] = 1;
mp["b"] = 2;
size_t i = 4;
char vla[i];
strcpy(vla, "b");
mp.remove(vla);
REQUIRE(mp.as<std::string>() == "{\"a\":1}");
}
#endif
}
TEST_CASE("MemberProxy::set()") {
JsonDocument doc;
const auto& mp = doc["hello"];
SECTION("set(int)") {
mp.set(42);
REQUIRE(doc.as<std::string>() == "{\"hello\":42}");
}
SECTION("set(const char*)") {
mp.set("world");
REQUIRE(doc.as<std::string>() == "{\"hello\":\"world\"}");
}
SECTION("set(char[])") { // issue #1191
char s[] = "world";
mp.set(s);
strcpy(s, "!!!!!");
REQUIRE(doc.as<std::string>() == "{\"hello\":\"world\"}");
}
#ifdef HAS_VARIABLE_LENGTH_ARRAY
SECTION("set(vla)") {
size_t i = 8;
char vla[i];
strcpy(vla, "world");
mp.set(vla);
REQUIRE(doc.as<std::string>() == "{\"hello\":\"world\"}");
}
#endif
}
TEST_CASE("MemberProxy::size()") {
JsonDocument doc;
const auto& mp = doc["hello"];
SECTION("returns 0") {
REQUIRE(mp.size() == 0);
}
SECTION("as an array, return 2") {
mp.add(1);
mp.add(2);
REQUIRE(mp.size() == 2);
}
SECTION("as an object, return 2") {
mp["a"] = 1;
mp["b"] = 2;
REQUIRE(mp.size() == 2);
}
}
TEST_CASE("MemberProxy::operator[]") {
JsonDocument doc;
const auto& mp = doc["hello"];
SECTION("set member") {
mp["world"] = 42;
REQUIRE(doc.as<std::string>() == "{\"hello\":{\"world\":42}}");
}
SECTION("set element") {
mp[2] = 42;
REQUIRE(doc.as<std::string>() == "{\"hello\":[null,null,42]}");
}
}
TEST_CASE("MemberProxy cast to JsonVariantConst") {
JsonDocument doc;
doc["hello"] = "world";
const auto& mp = doc["hello"];
JsonVariantConst var = mp;
CHECK(var.as<std::string>() == "world");
}
TEST_CASE("MemberProxy cast to JsonVariant") {
JsonDocument doc;
doc["hello"] = "world";
const auto& mp = doc["hello"];
JsonVariant var = mp;
CHECK(var.as<std::string>() == "world");
var.set("toto");
CHECK(doc.as<std::string>() == "{\"hello\":\"toto\"}");
}
TEST_CASE("Deduplicate keys") {
SpyingAllocator spy;
JsonDocument doc(&spy);
SECTION("std::string") {
doc[0]["example"_s] = 1;
doc[1]["example"_s] = 2;
const char* key1 = doc[0].as<JsonObject>().begin()->key().c_str();
const char* key2 = doc[1].as<JsonObject>().begin()->key().c_str();
CHECK(key1 == key2);
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("example")),
});
}
SECTION("char*") {
char key[] = "example";
doc[0][key] = 1;
doc[1][key] = 2;
const char* key1 = doc[0].as<JsonObject>().begin()->key().c_str();
const char* key2 = doc[1].as<JsonObject>().begin()->key().c_str();
CHECK(key1 == key2);
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("example")),
});
}
SECTION("Arduino String") {
doc[0][String("example")] = 1;
doc[1][String("example")] = 2;
const char* key1 = doc[0].as<JsonObject>().begin()->key().c_str();
const char* key2 = doc[1].as<JsonObject>().begin()->key().c_str();
CHECK(key1 == key2);
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("example")),
});
}
SECTION("Flash string") {
doc[0][F("example")] = 1;
doc[1][F("example")] = 2;
const char* key1 = doc[0].as<JsonObject>().begin()->key().c_str();
const char* key2 = doc[1].as<JsonObject>().begin()->key().c_str();
CHECK(key1 == key2);
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("example")),
});
}
}
TEST_CASE("MemberProxy under memory constraints") {
TimebombAllocator timebomb(1);
SpyingAllocator spy(&timebomb);
JsonDocument doc(&spy);
SECTION("key slot allocation fails") {
timebomb.setCountdown(0);
doc["hello"_s] = "world";
REQUIRE(doc.is<JsonObject>());
REQUIRE(doc.size() == 0);
REQUIRE(doc.overflowed() == true);
REQUIRE(spy.log() == AllocatorLog{
AllocateFail(sizeofPool()),
});
}
SECTION("value slot allocation fails") {
timebomb.setCountdown(1);
// fill the pool entirely, but leave one slot for the key
doc["foo"][ARDUINOJSON_POOL_CAPACITY - 4] = 1;
REQUIRE(doc.overflowed() == false);
doc["hello"_s] = "world";
REQUIRE(doc.is<JsonObject>());
REQUIRE(doc.size() == 1);
REQUIRE(doc.overflowed() == true);
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
AllocateFail(sizeofPool()),
});
}
SECTION("key string allocation fails") {
timebomb.setCountdown(1);
doc["hello"_s] = "world";
REQUIRE(doc.is<JsonObject>());
REQUIRE(doc.size() == 0);
REQUIRE(doc.overflowed() == true);
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
AllocateFail(sizeofString("hello")),
});
}
}

View File

@@ -0,0 +1,176 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// MIT License
#define ARDUINOJSON_ENABLE_ARDUINO_STRING 1
#define ARDUINOJSON_ENABLE_PROGMEM 1
#include <ArduinoJson.h>
#include <catch.hpp>
#include "Allocators.hpp"
#include "Literals.hpp"
using ArduinoJson::detail::sizeofArray;
TEST_CASE("JsonDocument::add(T)") {
SpyingAllocator spy;
JsonDocument doc(&spy);
SECTION("integer") {
doc.add(42);
REQUIRE(doc.as<std::string>() == "[42]");
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
});
}
SECTION("string literal") {
doc.add("hello");
REQUIRE(doc.as<std::string>() == "[\"hello\"]");
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
});
}
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);
CHECK(doc[0].as<const char*>() == doc[1].as<const char*>());
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("example")),
});
}
SECTION("char*") {
char value[] = "example";
doc.add(value);
doc.add(value);
CHECK(doc[0].as<const char*>() == doc[1].as<const char*>());
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("example")),
});
}
SECTION("Arduino String") {
doc.add(String("example"));
doc.add(String("example"));
CHECK(doc[0].as<const char*>() == doc[1].as<const char*>());
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("example")),
});
}
SECTION("Flash string") {
doc.add(F("example"));
doc.add(F("example"));
CHECK(doc[0].as<const char*>() == doc[1].as<const char*>());
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("example")),
});
}
#ifdef HAS_VARIABLE_LENGTH_ARRAY
SECTION("VLA") {
size_t i = 16;
char vla[i];
strcpy(vla, "example");
doc.add(vla);
doc.add(vla);
CHECK(doc[0].as<const char*>() == doc[1].as<const char*>());
REQUIRE("example"_s == doc[0]);
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("example")),
});
}
#endif
}
TEST_CASE("JsonDocument::add<T>()") {
JsonDocument doc;
SECTION("JsonArray") {
JsonArray array = doc.add<JsonArray>();
array.add(1);
array.add(2);
REQUIRE(doc.as<std::string>() == "[[1,2]]");
}
SECTION("JsonVariant") {
JsonVariant variant = doc.add<JsonVariant>();
variant.set(42);
REQUIRE(doc.as<std::string>() == "[42]");
}
}
TEST_CASE("JsonObject::add(JsonObject) ") {
JsonDocument doc1;
doc1["hello"_s] = "world"_s;
TimebombAllocator allocator(10);
SpyingAllocator spy(&allocator);
JsonDocument doc2(&spy);
SECTION("success") {
bool result = doc2.add(doc1.as<JsonObject>());
REQUIRE(result == true);
REQUIRE(doc2.as<std::string>() == "[{\"hello\":\"world\"}]");
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("hello")),
Allocate(sizeofString("world")),
});
}
SECTION("partial failure") { // issue #2081
allocator.setCountdown(2);
bool result = doc2.add(doc1.as<JsonObject>());
REQUIRE(result == false);
REQUIRE(doc2.as<std::string>() == "[]");
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("hello")),
AllocateFail(sizeofString("world")),
Deallocate(sizeofString("hello")),
});
}
SECTION("complete failure") {
allocator.setCountdown(0);
bool result = doc2.add(doc1.as<JsonObject>());
REQUIRE(result == false);
REQUIRE(doc2.as<std::string>() == "[]");
REQUIRE(spy.log() == AllocatorLog{
AllocateFail(sizeofPool()),
});
}
}

View File

@@ -0,0 +1,135 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
#include "Allocators.hpp"
#include "Literals.hpp"
TEST_CASE("JsonDocument assignment") {
SpyingAllocator spyingAllocator;
SECTION("Copy assignment same capacity") {
JsonDocument doc1(&spyingAllocator);
deserializeJson(doc1, "{\"hello\":\"world\"}");
JsonDocument doc2(&spyingAllocator);
spyingAllocator.clearLog();
doc2 = doc1;
REQUIRE(doc2.as<std::string>() == "{\"hello\":\"world\"}");
REQUIRE(spyingAllocator.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("hello")),
Allocate(sizeofString("world")),
});
}
SECTION("Copy assignment reallocates when capacity is smaller") {
JsonDocument doc1(&spyingAllocator);
deserializeJson(doc1, "[{\"hello\":\"world\"}]");
JsonDocument doc2(&spyingAllocator);
spyingAllocator.clearLog();
doc2 = doc1;
REQUIRE(doc2.as<std::string>() == "[{\"hello\":\"world\"}]");
REQUIRE(spyingAllocator.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("hello")),
Allocate(sizeofString("world")),
});
}
SECTION("Copy assignment reallocates when capacity is larger") {
JsonDocument doc1(&spyingAllocator);
deserializeJson(doc1, "{\"hello\":\"world\"}");
JsonDocument doc2(&spyingAllocator);
spyingAllocator.clearLog();
doc2 = doc1;
REQUIRE(doc2.as<std::string>() == "{\"hello\":\"world\"}");
REQUIRE(spyingAllocator.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("hello")),
Allocate(sizeofString("world")),
});
}
SECTION("Move assign") {
{
JsonDocument doc1(&spyingAllocator);
doc1["hello"_s] = "world"_s;
JsonDocument doc2(&spyingAllocator);
doc2 = std::move(doc1);
REQUIRE(doc2.as<std::string>() == "{\"hello\":\"world\"}");
REQUIRE(doc1.as<std::string>() == "null");
}
REQUIRE(spyingAllocator.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("hello")),
Allocate(sizeofString("world")),
Deallocate(sizeofString("hello")),
Deallocate(sizeofString("world")),
Deallocate(sizeofPool()),
});
}
SECTION("Assign from JsonObject") {
JsonDocument doc1;
JsonObject obj = doc1.to<JsonObject>();
obj["hello"] = "world";
JsonDocument doc2;
doc2 = obj;
REQUIRE(doc2.as<std::string>() == "{\"hello\":\"world\"}");
}
SECTION("Assign from JsonArray") {
JsonDocument doc1;
JsonArray arr = doc1.to<JsonArray>();
arr.add("hello");
JsonDocument doc2;
doc2 = arr;
REQUIRE(doc2.as<std::string>() == "[\"hello\"]");
}
SECTION("Assign from JsonVariant") {
JsonDocument doc1;
deserializeJson(doc1, "42");
JsonDocument doc2;
doc2 = doc1.as<JsonVariant>();
REQUIRE(doc2.as<std::string>() == "42");
}
SECTION("Assign from MemberProxy") {
JsonDocument doc1;
doc1["value"] = 42;
JsonDocument doc2;
doc2 = doc1["value"];
REQUIRE(doc2.as<std::string>() == "42");
}
SECTION("Assign from ElementProxy") {
JsonDocument doc1;
doc1[0] = 42;
JsonDocument doc2;
doc2 = doc1[0];
REQUIRE(doc2.as<std::string>() == "42");
}
}

View File

@@ -0,0 +1,18 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
#include <string>
TEST_CASE("Implicit cast to JsonVariant") {
JsonDocument doc;
doc["hello"] = "world";
JsonVariant var = doc;
CHECK(var.as<std::string>() == "{\"hello\":\"world\"}");
}

View File

@@ -0,0 +1,48 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
#include <stdlib.h> // malloc, free
#include <string>
#include "Allocators.hpp"
#include "Literals.hpp"
TEST_CASE("JsonDocument::clear()") {
SpyingAllocator spy;
JsonDocument doc(&spy);
SECTION("null") {
doc.clear();
REQUIRE(doc.isNull());
REQUIRE(spy.log() == AllocatorLog{});
}
SECTION("releases resources") {
doc["hello"_s] = "world"_s;
spy.clearLog();
doc.clear();
REQUIRE(doc.isNull());
REQUIRE(spy.log() == AllocatorLog{
Deallocate(sizeofPool()),
Deallocate(sizeofString("hello")),
Deallocate(sizeofString("world")),
});
}
SECTION("clear free list") { // issue #2034
JsonObject obj = doc.to<JsonObject>();
obj["a"] = 1;
obj.clear(); // puts the slot in the free list
doc.clear();
doc["b"] = 2; // will it pick from the free list?
}
}

View File

@@ -0,0 +1,29 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
TEST_CASE("JsonDocument::operator==(const JsonDocument&)") {
JsonDocument doc1;
JsonDocument doc2;
SECTION("Empty") {
REQUIRE(doc1 == doc2);
REQUIRE_FALSE(doc1 != doc2);
}
SECTION("With same object") {
doc1["hello"] = "world";
doc2["hello"] = "world";
REQUIRE(doc1 == doc2);
REQUIRE_FALSE(doc1 != doc2);
}
SECTION("With different object") {
doc1["hello"] = "world";
doc2["world"] = "hello";
REQUIRE_FALSE(doc1 == doc2);
REQUIRE(doc1 != doc2);
}
}

View File

@@ -0,0 +1,148 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
#include "Allocators.hpp"
#include "Literals.hpp"
using ArduinoJson::detail::addPadding;
TEST_CASE("JsonDocument constructor") {
SpyingAllocator spyingAllocator;
SECTION("JsonDocument(size_t)") {
{ JsonDocument doc(&spyingAllocator); }
REQUIRE(spyingAllocator.log() == AllocatorLog{});
}
SECTION("JsonDocument(const JsonDocument&)") {
{
JsonDocument doc1(&spyingAllocator);
doc1.set("The size of this string is 32!!"_s);
JsonDocument doc2(doc1);
REQUIRE(doc1.as<std::string>() == "The size of this string is 32!!");
REQUIRE(doc2.as<std::string>() == "The size of this string is 32!!");
}
REQUIRE(spyingAllocator.log() == AllocatorLog{
Allocate(sizeofStringBuffer()),
Allocate(sizeofStringBuffer()),
Deallocate(sizeofStringBuffer()),
Deallocate(sizeofStringBuffer()),
});
}
SECTION("JsonDocument(JsonDocument&&)") {
{
JsonDocument doc1(&spyingAllocator);
doc1.set("The size of this string is 32!!"_s);
JsonDocument doc2(std::move(doc1));
REQUIRE(doc2.as<std::string>() == "The size of this string is 32!!");
REQUIRE(doc1.as<std::string>() == "null");
}
REQUIRE(spyingAllocator.log() == AllocatorLog{
Allocate(sizeofStringBuffer()),
Deallocate(sizeofStringBuffer()),
});
}
SECTION("JsonDocument(JsonObject, Allocator*)") {
JsonDocument doc1;
JsonObject obj = doc1.to<JsonObject>();
obj["hello"] = "world";
JsonDocument doc2(obj, &spyingAllocator);
REQUIRE(doc2.as<std::string>() == "{\"hello\":\"world\"}");
REQUIRE(spyingAllocator.log() == AllocatorLog{
Allocate(sizeofPool()),
});
}
SECTION("JsonDocument(JsonObject)") {
JsonDocument doc1;
JsonObject obj = doc1.to<JsonObject>();
obj["hello"] = "world";
JsonDocument doc2(obj);
REQUIRE(doc2.as<std::string>() == "{\"hello\":\"world\"}");
}
SECTION("JsonDocument(JsonArray, Allocator*)") {
JsonDocument doc1;
JsonArray arr = doc1.to<JsonArray>();
arr.add("hello");
JsonDocument doc2(arr, &spyingAllocator);
REQUIRE(doc2.as<std::string>() == "[\"hello\"]");
REQUIRE(spyingAllocator.log() == AllocatorLog{
Allocate(sizeofPool()),
});
}
SECTION("JsonDocument(JsonArray)") {
JsonDocument doc1;
JsonArray arr = doc1.to<JsonArray>();
arr.add("hello");
JsonDocument doc2(arr);
REQUIRE(doc2.as<std::string>() == "[\"hello\"]");
}
SECTION("JsonDocument(JsonVariant, Allocator*)") {
JsonDocument doc1;
deserializeJson(doc1, "\"hello\"");
JsonDocument doc2(doc1.as<JsonVariant>(), &spyingAllocator);
REQUIRE(doc2.as<std::string>() == "hello");
REQUIRE(spyingAllocator.log() == AllocatorLog{
Allocate(sizeofString("hello")),
});
}
SECTION("JsonDocument(JsonVariant)") {
JsonDocument doc1;
deserializeJson(doc1, "\"hello\"");
JsonDocument doc2(doc1.as<JsonVariant>());
REQUIRE(doc2.as<std::string>() == "hello");
}
SECTION("JsonDocument(JsonVariantConst)") {
JsonDocument doc1;
deserializeJson(doc1, "\"hello\"");
JsonDocument doc2(doc1.as<JsonVariantConst>());
REQUIRE(doc2.as<std::string>() == "hello");
}
SECTION("JsonDocument(ElementProxy)") {
JsonDocument doc1;
deserializeJson(doc1, "[\"hello\",\"world\"]");
JsonDocument doc2(doc1[1]);
REQUIRE(doc2.as<std::string>() == "world");
}
SECTION("JsonDocument(MemberProxy)") {
JsonDocument doc1;
deserializeJson(doc1, "{\"hello\":\"world\"}");
JsonDocument doc2(doc1["hello"]);
REQUIRE(doc2.as<std::string>() == "world");
}
}

View File

@@ -0,0 +1,39 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
TEST_CASE("JsonDocument::isNull()") {
JsonDocument doc;
SECTION("returns true if uninitialized") {
REQUIRE(doc.isNull() == true);
}
SECTION("returns false after to<JsonObject>()") {
doc.to<JsonObject>();
REQUIRE(doc.isNull() == false);
}
SECTION("returns false after to<JsonArray>()") {
doc.to<JsonArray>();
REQUIRE(doc.isNull() == false);
}
SECTION("returns true after to<JsonVariant>()") {
REQUIRE(doc.isNull() == true);
}
SECTION("returns false after set()") {
doc.to<JsonVariant>().set(42);
REQUIRE(doc.isNull() == false);
}
SECTION("returns true after clear()") {
doc.to<JsonObject>();
doc.clear();
REQUIRE(doc.isNull() == true);
}
}

View File

@@ -0,0 +1,52 @@
#include <ArduinoJson.h>
#include <catch.hpp>
#include "Literals.hpp"
TEST_CASE("Issue #1120") {
JsonDocument doc;
constexpr char str[] =
"{\"contents\":[{\"module\":\"Packet\"},{\"module\":\"Analog\"}]}";
deserializeJson(doc, str);
SECTION("MemberProxy<std::string>::isNull()") {
SECTION("returns false") {
CHECK(doc["contents"_s].isNull() == false);
}
SECTION("returns true") {
CHECK(doc["zontents"_s].isNull() == true);
}
}
SECTION("ElementProxy<MemberProxy<const char*> >::isNull()") {
SECTION("returns false") { // Issue #1120
CHECK(doc["contents"][1].isNull() == false);
}
SECTION("returns true") {
CHECK(doc["contents"][2].isNull() == true);
}
}
SECTION("MemberProxy<ElementProxy<MemberProxy>, const char*>::isNull()") {
SECTION("returns false") {
CHECK(doc["contents"][1]["module"].isNull() == false);
}
SECTION("returns true") {
CHECK(doc["contents"][1]["zodule"].isNull() == true);
}
}
SECTION("MemberProxy<ElementProxy<MemberProxy>, std::string>::isNull()") {
SECTION("returns false") {
CHECK(doc["contents"][1]["module"_s].isNull() == false);
}
SECTION("returns true") {
CHECK(doc["contents"][1]["zodule"_s].isNull() == true);
}
}
}

View File

@@ -0,0 +1,30 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
TEST_CASE("JsonDocument::nesting()") {
JsonDocument doc;
SECTION("return 0 if uninitialized") {
REQUIRE(doc.nesting() == 0);
}
SECTION("returns 0 for string") {
JsonVariant var = doc.to<JsonVariant>();
var.set("hello");
REQUIRE(doc.nesting() == 0);
}
SECTION("returns 1 for empty object") {
doc.to<JsonObject>();
REQUIRE(doc.nesting() == 1);
}
SECTION("returns 1 for empty array") {
doc.to<JsonArray>();
REQUIRE(doc.nesting() == 1);
}
}

View File

@@ -0,0 +1,96 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
#include "Allocators.hpp"
#include "Literals.hpp"
TEST_CASE("JsonDocument::overflowed()") {
TimebombAllocator timebomb(10);
JsonDocument doc(&timebomb);
SECTION("returns false on a fresh object") {
timebomb.setCountdown(0);
CHECK(doc.overflowed() == false);
}
SECTION("returns true after a failed insertion") {
timebomb.setCountdown(0);
doc.add(0);
CHECK(doc.overflowed() == true);
}
SECTION("returns false after successful insertion") {
timebomb.setCountdown(2);
doc.add(0);
CHECK(doc.overflowed() == false);
}
SECTION("returns true after a failed string copy") {
timebomb.setCountdown(0);
doc.add("example"_s);
CHECK(doc.overflowed() == true);
}
SECTION("returns false after a successful string copy") {
timebomb.setCountdown(3);
doc.add("example"_s);
CHECK(doc.overflowed() == false);
}
SECTION("returns true after a failed member add") {
timebomb.setCountdown(0);
doc["example"] = true;
CHECK(doc.overflowed() == true);
}
SECTION("returns true after a failed deserialization") {
timebomb.setCountdown(0);
deserializeJson(doc, "[1, 2]");
CHECK(doc.overflowed() == true);
}
SECTION("returns false after a successful deserialization") {
timebomb.setCountdown(3);
deserializeJson(doc, "[\"example\"]");
CHECK(doc.overflowed() == false);
}
SECTION("returns false after clear()") {
timebomb.setCountdown(0);
doc.add(0);
doc.clear();
CHECK(doc.overflowed() == false);
}
SECTION("remains false after shrinkToFit()") {
timebomb.setCountdown(2);
doc.add(0);
timebomb.setCountdown(2);
doc.shrinkToFit();
CHECK(doc.overflowed() == false);
}
SECTION("remains true after shrinkToFit()") {
timebomb.setCountdown(0);
doc.add(0);
timebomb.setCountdown(2);
doc.shrinkToFit();
CHECK(doc.overflowed() == true);
}
SECTION("returns false when string length doesn't overflow") {
auto maxLength = ArduinoJson::detail::StringNode::maxLength;
CHECK(doc.set(std::string(maxLength, 'a')) == true);
CHECK(doc.overflowed() == false);
}
SECTION("returns true when string length overflows") {
auto maxLength = ArduinoJson::detail::StringNode::maxLength;
CHECK(doc.set(std::string(maxLength + 1, 'a')) == false);
CHECK(doc.overflowed() == true);
}
}

View File

@@ -0,0 +1,85 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
#include "Literals.hpp"
TEST_CASE("JsonDocument::remove()") {
JsonDocument doc;
SECTION("remove(int)") {
doc.add(1);
doc.add(2);
doc.add(3);
doc.remove(1);
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(static_cast<const char*>("a"));
REQUIRE(doc.as<std::string>() == "{\"b\":2}");
}
SECTION("remove(std::string)") {
doc["a"] = 1;
doc["b"] = 2;
doc.remove("b"_s);
REQUIRE(doc.as<std::string>() == "{\"a\":1}");
}
#ifdef HAS_VARIABLE_LENGTH_ARRAY
SECTION("remove(vla)") {
doc["a"] = 1;
doc["b"] = 2;
size_t i = 4;
char vla[i];
strcpy(vla, "b");
doc.remove(vla);
REQUIRE(doc.as<std::string>() == "{\"a\":1}");
}
#endif
SECTION("remove(JsonVariant) from object") {
doc["a"] = 1;
doc["b"] = 2;
doc["c"] = "b";
doc.remove(doc["c"]);
REQUIRE(doc.as<std::string>() == "{\"a\":1,\"c\":\"b\"}");
}
SECTION("remove(JsonVariant) from array") {
doc[0] = 3;
doc[1] = 2;
doc[2] = 1;
doc.remove(doc[2]);
doc.remove(doc[3]); // noop
REQUIRE(doc.as<std::string>() == "[3,1]");
}
}

View File

@@ -0,0 +1,111 @@
#define ARDUINOJSON_ENABLE_ARDUINO_STRING 1
#define ARDUINOJSON_ENABLE_PROGMEM 1
#include <ArduinoJson.h>
#include <catch.hpp>
#include "Allocators.hpp"
#include "Literals.hpp"
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);
REQUIRE(doc.as<std::string>() == "42");
REQUIRE(spy.log() == AllocatorLog{});
}
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);
REQUIRE(doc.as<const char*>() == "example"_s);
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofString("example")),
});
}
SECTION("char*") {
char value[] = "example";
doc.set(value);
REQUIRE(doc.as<const char*>() == "example"_s);
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofString("example")),
});
}
SECTION("Arduino String") {
doc.set(String("example"));
REQUIRE(doc.as<const char*>() == "example"_s);
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofString("example")),
});
}
SECTION("Flash string") {
doc.set(F("example"));
REQUIRE(doc.as<const char*>() == "example"_s);
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofString("example")),
});
}
SECTION("Flash tiny string") { // issue #2170
doc.set(F("abc"));
REQUIRE(doc.as<const char*>() == "abc"_s);
REQUIRE(spy.log() == AllocatorLog{});
}
#ifdef HAS_VARIABLE_LENGTH_ARRAY
SECTION("VLA") {
size_t i = 16;
char vla[i];
strcpy(vla, "example");
doc.set(vla);
REQUIRE(doc.as<const char*>() == "example"_s);
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofString("example")),
});
}
#endif
}

View File

@@ -0,0 +1,184 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
#include <stdlib.h> // malloc, free
#include <string>
#include "Allocators.hpp"
#include "Literals.hpp"
using ArduinoJson::detail::sizeofArray;
using ArduinoJson::detail::sizeofObject;
class ArmoredAllocator : public Allocator {
public:
virtual ~ArmoredAllocator() {}
void* allocate(size_t size) override {
return malloc(size);
}
void deallocate(void* ptr) override {
free(ptr);
}
void* reallocate(void* ptr, size_t new_size) override {
// don't call realloc, instead alloc a new buffer and erase the old one
// this way we make sure we support relocation
void* new_ptr = malloc(new_size);
memset(new_ptr, '#', new_size); // erase
if (ptr) {
memcpy(new_ptr, ptr, std::min(new_size, new_size));
free(ptr);
}
return new_ptr;
}
};
TEST_CASE("JsonDocument::shrinkToFit()") {
ArmoredAllocator armoredAllocator;
SpyingAllocator spyingAllocator(&armoredAllocator);
JsonDocument doc(&spyingAllocator);
SECTION("null") {
doc.shrinkToFit();
REQUIRE(doc.as<std::string>() == "null");
REQUIRE(spyingAllocator.log() == AllocatorLog{});
}
SECTION("empty object") {
deserializeJson(doc, "{}");
doc.shrinkToFit();
REQUIRE(doc.as<std::string>() == "{}");
REQUIRE(spyingAllocator.log() == AllocatorLog{});
}
SECTION("empty array") {
deserializeJson(doc, "[]");
doc.shrinkToFit();
REQUIRE(doc.as<std::string>() == "[]");
REQUIRE(spyingAllocator.log() == AllocatorLog{});
}
SECTION("linked string") {
doc.set("hello");
doc.shrinkToFit();
REQUIRE(doc.as<std::string>() == "hello");
REQUIRE(spyingAllocator.log() == AllocatorLog{});
}
SECTION("owned string") {
doc.set("abcdefg"_s);
REQUIRE(doc.as<std::string>() == "abcdefg");
doc.shrinkToFit();
REQUIRE(doc.as<std::string>() == "abcdefg");
REQUIRE(spyingAllocator.log() == AllocatorLog{
Allocate(sizeofString("abcdefg")),
});
}
SECTION("raw string") {
doc.set(serialized("[{},12]"));
doc.shrinkToFit();
REQUIRE(doc.as<std::string>() == "[{},12]");
REQUIRE(spyingAllocator.log() == AllocatorLog{
Allocate(sizeofString("[{},12]")),
});
}
SECTION("linked key") {
doc["key"] = 42;
doc.shrinkToFit();
REQUIRE(doc.as<std::string>() == "{\"key\":42}");
REQUIRE(spyingAllocator.log() ==
AllocatorLog{
Allocate(sizeofPool()),
Reallocate(sizeofPool(), sizeofObject(1)),
});
}
SECTION("owned key") {
doc["abcdefg"_s] = 42;
doc.shrinkToFit();
REQUIRE(doc.as<std::string>() == "{\"abcdefg\":42}");
REQUIRE(spyingAllocator.log() ==
AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("abcdefg")),
Reallocate(sizeofPool(), sizeofObject(1)),
});
}
SECTION("linked string in array") {
doc.add("hello");
doc.shrinkToFit();
REQUIRE(doc.as<std::string>() == "[\"hello\"]");
REQUIRE(spyingAllocator.log() ==
AllocatorLog{
Allocate(sizeofPool()),
Reallocate(sizeofPool(), sizeofArray(1)),
});
}
SECTION("owned string in array") {
doc.add("abcdefg"_s);
doc.shrinkToFit();
REQUIRE(doc.as<std::string>() == "[\"abcdefg\"]");
REQUIRE(spyingAllocator.log() ==
AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("abcdefg")),
Reallocate(sizeofPool(), sizeofArray(1)),
});
}
SECTION("linked string in object") {
doc["key"] = "hello";
doc.shrinkToFit();
REQUIRE(doc.as<std::string>() == "{\"key\":\"hello\"}");
REQUIRE(spyingAllocator.log() ==
AllocatorLog{
Allocate(sizeofPool()),
Reallocate(sizeofPool(), sizeofObject(1)),
});
}
SECTION("owned string in object") {
doc["key"] = "abcdefg"_s;
doc.shrinkToFit();
REQUIRE(doc.as<std::string>() == "{\"key\":\"abcdefg\"}");
REQUIRE(spyingAllocator.log() ==
AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("abcdefg")),
Reallocate(sizeofPool(), sizeofPool(2)),
});
}
}

View File

@@ -0,0 +1,28 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
TEST_CASE("JsonDocument::size()") {
JsonDocument doc;
SECTION("returns 0") {
REQUIRE(doc.size() == 0);
}
SECTION("as an array, return 2") {
doc.add(1);
doc.add(2);
REQUIRE(doc.size() == 2);
}
SECTION("as an object, return 2") {
doc["a"] = 1;
doc["b"] = 2;
REQUIRE(doc.size() == 2);
}
}

View File

@@ -0,0 +1,167 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
#include "Allocators.hpp"
#include "Literals.hpp"
TEST_CASE("JsonDocument::operator[]") {
JsonDocument doc;
const JsonDocument& cdoc = doc;
SECTION("object") {
doc["abc"_s] = "ABC";
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") {
REQUIRE(doc["abc"_s] == "ABC");
REQUIRE(cdoc["abc"_s] == "ABC");
REQUIRE(doc["abc\0d"_s] == "ABCD");
REQUIRE(cdoc["abc\0d"_s] == "ABCD");
}
SECTION("JsonVariant") {
doc["key1"] = "abc";
doc["key2"] = "abc\0d"_s;
doc["key3"] = "foo";
CHECK(doc[doc["key1"]] == "ABC");
CHECK(doc[doc["key2"]] == "ABCD");
CHECK(doc[doc["key3"]] == nullptr);
CHECK(doc[doc["key4"]] == nullptr);
CHECK(cdoc[cdoc["key1"]] == "ABC");
CHECK(cdoc[cdoc["key2"]] == "ABCD");
CHECK(cdoc[cdoc["key3"]] == nullptr);
CHECK(cdoc[cdoc["key4"]] == nullptr);
}
SECTION("supports operator|") {
REQUIRE((doc["abc"] | "nope") == "ABC"_s);
REQUIRE((doc["def"] | "nope") == "nope"_s);
}
#if defined(HAS_VARIABLE_LENGTH_ARRAY) && \
!defined(SUBSCRIPT_CONFLICTS_WITH_BUILTIN_OPERATOR)
SECTION("supports VLAs") {
size_t i = 16;
char vla[i];
strcpy(vla, "hello");
doc[vla] = "world";
REQUIRE(doc[vla] == "world");
REQUIRE(cdoc[vla] == "world");
}
#endif
}
SECTION("array") {
deserializeJson(doc, "[\"hello\",\"world\"]");
SECTION("int") {
REQUIRE(doc[1] == "world");
REQUIRE(cdoc[1] == "world");
}
SECTION("JsonVariant") {
doc[2] = 1;
REQUIRE(doc[doc[2]] == "world");
REQUIRE(cdoc[doc[2]] == "world");
}
}
}
TEST_CASE("JsonDocument automatically promotes to object") {
JsonDocument doc;
doc["one"]["two"]["three"] = 4;
REQUIRE(doc["one"]["two"]["three"] == 4);
}
TEST_CASE("JsonDocument automatically promotes to array") {
JsonDocument doc;
doc[2] = 2;
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

@@ -0,0 +1,25 @@
#include <ArduinoJson.h>
#include <catch.hpp>
#include <string>
#include <utility>
using namespace std;
TEST_CASE("std::swap") {
SECTION("JsonDocument*") {
JsonDocument *p1, *p2;
swap(p1, p2); // issue #1678
}
SECTION("JsonDocument") {
JsonDocument doc1, doc2;
doc1.set("hello");
doc2.set("world");
swap(doc1, doc2);
CHECK(doc1.as<string>() == "world");
CHECK(doc2.as<string>() == "hello");
}
}