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,33 @@
# ArduinoJson - https://arduinojson.org
# Copyright © 2014-2025, Benoit BLANCHON
# MIT License
add_executable(JsonVariantTests
add.cpp
as.cpp
clear.cpp
compare.cpp
converters.cpp
copy.cpp
is.cpp
isnull.cpp
misc.cpp
nesting.cpp
nullptr.cpp
or.cpp
overflow.cpp
remove.cpp
set.cpp
size.cpp
stl_containers.cpp
subscript.cpp
types.cpp
unbound.cpp
)
add_test(JsonVariant JsonVariantTests)
set_tests_properties(JsonVariant
PROPERTIES
LABELS "Catch"
)

View File

@@ -0,0 +1,128 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <stdint.h>
#include <catch.hpp>
#include "Allocators.hpp"
#include "Literals.hpp"
TEST_CASE("JsonVariant::add(T)") {
JsonDocument doc;
JsonVariant var = doc.to<JsonVariant>();
SECTION("add integer to new variant") {
var.add(42);
REQUIRE(var.as<std::string>() == "[42]");
}
SECTION("add const char* to new variant") {
var.add("hello");
REQUIRE(var.as<std::string>() == "[\"hello\"]");
}
SECTION("add std::string to new variant") {
var.add("hello"_s);
REQUIRE(var.as<std::string>() == "[\"hello\"]");
}
SECTION("add integer to integer") {
var.set(123);
var.add(456); // no-op
REQUIRE(var.as<std::string>() == "123");
}
SECTION("add integer to object") {
var["val"] = 123;
var.add(456); // no-op
REQUIRE(var.as<std::string>() == "{\"val\":123}");
}
#ifdef HAS_VARIABLE_LENGTH_ARRAY
SECTION("supports VLAs") {
size_t i = 16;
char vla[i];
strcpy(vla, "hello");
var.add(vla);
REQUIRE(var.as<std::string>() == "[\"hello\"]");
}
#endif
}
TEST_CASE("JsonVariant::add<T>()") {
JsonDocument doc;
JsonVariant var = doc.to<JsonVariant>();
SECTION("JsonArray") {
JsonArray array = var.add<JsonArray>();
array.add(1);
array.add(2);
REQUIRE(doc.as<std::string>() == "[[1,2]]");
}
SECTION("JsonVariant") {
JsonVariant variant = var.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);
JsonVariant variant = doc2.to<JsonVariant>();
SECTION("success") {
bool result = variant.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 = variant.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 = variant.add(doc1.as<JsonObject>());
REQUIRE(result == false);
REQUIRE(doc2.as<std::string>() == "[]");
REQUIRE(spy.log() == AllocatorLog{
AllocateFail(sizeofPool()),
});
}
}

View File

@@ -0,0 +1,327 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <stdint.h>
#include <catch.hpp>
#include "Literals.hpp"
namespace my {
using ArduinoJson::detail::isinf;
} // namespace my
enum MY_ENUM { ONE = 1, TWO = 2 };
TEST_CASE("JsonVariant::as()") {
static const char* null = 0;
JsonDocument doc;
JsonVariant variant = doc.to<JsonVariant>();
SECTION("not set") {
REQUIRE(false == variant.as<bool>());
REQUIRE(0 == variant.as<int>());
REQUIRE(0.0f == variant.as<float>());
REQUIRE(0 == variant.as<const char*>());
REQUIRE("null" == variant.as<std::string>());
REQUIRE(variant.as<JsonString>().isNull());
REQUIRE(variant.as<MsgPackBinary>().data() == nullptr);
REQUIRE(variant.as<MsgPackExtension>().data() == nullptr);
}
SECTION("set(float)") {
variant.set(4.2f);
REQUIRE(variant.as<bool>());
REQUIRE(0 == variant.as<const char*>());
REQUIRE(variant.as<std::string>() == "4.2");
REQUIRE(variant.as<long>() == 4L);
REQUIRE(variant.as<float>() == 4.2f);
REQUIRE(variant.as<unsigned>() == 4U);
REQUIRE(variant.as<JsonString>().isNull());
REQUIRE(variant.as<MsgPackBinary>().data() == nullptr);
REQUIRE(variant.as<MsgPackExtension>().data() == nullptr);
}
SECTION("set(double)") {
variant.set(4.2);
REQUIRE(variant.as<bool>());
REQUIRE(0 == variant.as<const char*>());
REQUIRE(variant.as<std::string>() == "4.2");
REQUIRE(variant.as<long>() == 4L);
REQUIRE(variant.as<double>() == 4.2);
REQUIRE(variant.as<unsigned>() == 4U);
REQUIRE(variant.as<JsonString>().isNull());
REQUIRE(variant.as<MsgPackBinary>().data() == nullptr);
REQUIRE(variant.as<MsgPackExtension>().data() == nullptr);
}
SECTION("set(0.0)") {
variant.set(0.0);
REQUIRE(variant.as<bool>() == false);
REQUIRE(variant.as<long>() == 0L);
REQUIRE(variant.as<JsonString>().isNull());
REQUIRE(variant.as<MsgPackBinary>().data() == nullptr);
REQUIRE(variant.as<MsgPackExtension>().data() == nullptr);
}
SECTION("set(false)") {
variant.set(false);
REQUIRE(false == variant.as<bool>());
REQUIRE(variant.as<double>() == 0.0);
REQUIRE(variant.as<long>() == 0L);
REQUIRE(variant.as<std::string>() == "false");
REQUIRE(variant.as<JsonString>().isNull());
REQUIRE(variant.as<MsgPackBinary>().data() == nullptr);
REQUIRE(variant.as<MsgPackExtension>().data() == nullptr);
}
SECTION("set(true)") {
variant.set(true);
REQUIRE(variant.as<bool>());
REQUIRE(variant.as<double>() == 1.0);
REQUIRE(variant.as<long>() == 1L);
REQUIRE(variant.as<std::string>() == "true");
REQUIRE(variant.as<JsonString>().isNull());
REQUIRE(variant.as<MsgPackBinary>().data() == nullptr);
REQUIRE(variant.as<MsgPackExtension>().data() == nullptr);
}
SECTION("set(uint32_t)") {
variant.set(4294967295U);
REQUIRE(variant.as<bool>() == true);
REQUIRE(variant.as<double>() == 4294967295.0);
REQUIRE(variant.as<int32_t>() == 0);
REQUIRE(variant.as<uint32_t>() == 4294967295U);
REQUIRE(variant.as<uint64_t>() == 4294967295U);
REQUIRE(variant.as<std::string>() == "4294967295");
REQUIRE(variant.as<JsonString>().isNull());
REQUIRE(variant.as<MsgPackBinary>().data() == nullptr);
REQUIRE(variant.as<MsgPackExtension>().data() == nullptr);
}
SECTION("set(int32_t)") {
variant.set(-2147483648LL);
REQUIRE(variant.as<bool>() == true);
REQUIRE(variant.as<double>() == -2147483648LL);
REQUIRE(variant.as<int32_t>() == -2147483648LL);
REQUIRE(variant.as<int64_t>() == -2147483648LL);
REQUIRE(variant.as<uint32_t>() == 0);
REQUIRE(variant.as<uint64_t>() == 0);
REQUIRE(variant.as<std::string>() == "-2147483648");
REQUIRE(variant.as<JsonString>().isNull());
REQUIRE(variant.as<MsgPackBinary>().data() == nullptr);
REQUIRE(variant.as<MsgPackExtension>().data() == nullptr);
}
SECTION("set(uint64_t)") {
variant.set(4294967296U);
REQUIRE(variant.as<bool>() == true);
REQUIRE(variant.as<double>() == 4294967296.0);
REQUIRE(variant.as<int32_t>() == 0);
REQUIRE(variant.as<uint32_t>() == 0);
REQUIRE(variant.as<uint64_t>() == 4294967296U);
REQUIRE(variant.as<std::string>() == "4294967296");
REQUIRE(variant.as<JsonString>().isNull());
REQUIRE(variant.as<MsgPackBinary>().data() == nullptr);
REQUIRE(variant.as<MsgPackExtension>().data() == nullptr);
}
SECTION("set(int64_t)") {
variant.set(-2147483649LL);
REQUIRE(variant.as<bool>() == true);
REQUIRE(variant.as<double>() == -2147483649LL);
REQUIRE(variant.as<int32_t>() == 0);
REQUIRE(variant.as<int64_t>() == -2147483649LL);
REQUIRE(variant.as<uint32_t>() == 0);
REQUIRE(variant.as<uint64_t>() == 0);
REQUIRE(variant.as<std::string>() == "-2147483649");
REQUIRE(variant.as<JsonString>().isNull());
REQUIRE(variant.as<MsgPackBinary>().data() == nullptr);
REQUIRE(variant.as<MsgPackExtension>().data() == nullptr);
}
SECTION("set(0L)") {
variant.set(0L);
REQUIRE(variant.as<bool>() == false);
REQUIRE(variant.as<double>() == 0.0);
REQUIRE(variant.as<std::string>() == "0");
REQUIRE(variant.as<JsonString>().isNull());
}
SECTION("set(0UL)") {
variant.set(0UL);
REQUIRE(variant.as<bool>() == false);
REQUIRE(variant.as<double>() == 0.0);
REQUIRE(variant.as<std::string>() == "0");
REQUIRE(variant.as<JsonString>().isNull());
}
SECTION("set(null)") {
variant.set(null);
REQUIRE(variant.as<bool>() == false);
REQUIRE(variant.as<double>() == 0.0);
REQUIRE(variant.as<long>() == 0L);
REQUIRE(variant.as<std::string>() == "null");
REQUIRE(variant.as<JsonString>().isNull());
}
SECTION("set(\"42\")") {
variant.set("42");
REQUIRE(variant.as<long>() == 42L);
REQUIRE(variant.as<double>() == 42);
REQUIRE(variant.as<JsonString>() == "42");
REQUIRE(variant.as<JsonString>().isStatic() == true);
}
SECTION("set(\"hello\")") {
variant.set("hello");
REQUIRE(variant.as<bool>() == true);
REQUIRE(variant.as<long>() == 0L);
REQUIRE(variant.as<const char*>() == "hello"_s);
REQUIRE(variant.as<const char*>() == "hello"_s);
REQUIRE(variant.as<std::string>() == "hello"_s);
REQUIRE(variant.as<JsonString>() == "hello");
}
SECTION("set(std::string(\"4.2\")) (tiny string optimization)") {
variant.set("4.2"_s);
REQUIRE(variant.as<bool>() == true);
REQUIRE(variant.as<long>() == 4L);
REQUIRE(variant.as<double>() == Approx(4.2));
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>().isStatic() == false);
}
SECTION("set(std::string(\"123.45\"))") {
variant.set("123.45"_s);
REQUIRE(variant.as<bool>() == true);
REQUIRE(variant.as<long>() == 123L);
REQUIRE(variant.as<double>() == Approx(123.45));
REQUIRE(variant.as<const char*>() == "123.45"_s);
REQUIRE(variant.as<std::string>() == "123.45"_s);
REQUIRE(variant.as<JsonString>() == "123.45");
REQUIRE(variant.as<JsonString>().isStatic() == false);
}
SECTION("set(\"true\")") {
variant.set("true");
REQUIRE(variant.as<bool>() == true);
REQUIRE(variant.as<int>() == 0);
REQUIRE(variant.as<JsonString>() == "true");
}
SECTION("set(-1e300)") {
variant.set(-1e300);
REQUIRE(variant.as<bool>() == true);
REQUIRE(variant.as<double>() == -1e300);
REQUIRE(variant.as<float>() < 0);
REQUIRE(my::isinf(variant.as<float>()));
REQUIRE(variant.as<JsonString>().isNull());
}
SECTION("set(1e300)") {
variant.set(1e300);
REQUIRE(variant.as<bool>() == true);
REQUIRE(variant.as<double>() == 1e300);
REQUIRE(variant.as<float>() > 0);
REQUIRE(my::isinf(variant.as<float>()));
REQUIRE(variant.as<JsonString>().isNull());
}
SECTION("set(1e-300)") {
variant.set(1e-300);
REQUIRE(variant.as<bool>() == true);
REQUIRE(variant.as<double>() == 1e-300);
REQUIRE(variant.as<float>() == 0);
REQUIRE(variant.as<JsonString>().isNull());
}
SECTION("set(serialized(\"hello\"))") {
variant.set(serialized("hello"));
REQUIRE(variant.as<MsgPackBinary>().data() == nullptr);
REQUIRE(variant.as<MsgPackExtension>().data() == nullptr);
}
SECTION("to<JsonObject>()") {
JsonObject obj = variant.to<JsonObject>();
obj["key"] = "value";
SECTION("as<bool>()") {
REQUIRE(variant.as<bool>() == true);
}
SECTION("as<std::string>()") {
REQUIRE(variant.as<std::string>() == "{\"key\":\"value\"}"_s);
}
SECTION("ObjectAsJsonObject") {
JsonObject o = variant.as<JsonObject>();
REQUIRE(o.size() == 1);
REQUIRE(o["key"] == "value"_s);
}
}
SECTION("to<JsonArray>()") {
JsonArray arr = variant.to<JsonArray>();
arr.add(4);
arr.add(2);
SECTION("as<bool>()") {
REQUIRE(variant.as<bool>() == true);
}
SECTION("as<std::string>()") {
REQUIRE(variant.as<std::string>() == "[4,2]"_s);
}
SECTION("as<JsonArray>()") {
JsonArray a = variant.as<JsonArray>();
REQUIRE(a.size() == 2);
REQUIRE(a[0] == 4);
REQUIRE(a[1] == 2);
}
}
#if ARDUINOJSON_USE_LONG_LONG
SECTION("Smallest int64 negative") {
variant.set("-9223372036854775808");
REQUIRE(variant.as<long long>() == -9223372036854775807 - 1);
}
SECTION("Biggest int64 positive") {
variant.set("9223372036854775807");
REQUIRE(variant.as<long long>() == 9223372036854775807);
}
#endif
SECTION("as<enum>()") {
variant.set(1);
REQUIRE(variant.as<MY_ENUM>() == ONE);
}
}

View File

@@ -0,0 +1,40 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <stdint.h>
#include <catch.hpp>
#include "Allocators.hpp"
#include "Literals.hpp"
TEST_CASE("JsonVariant::clear()") {
SpyingAllocator spy;
JsonDocument doc(&spy);
JsonVariant var = doc.to<JsonVariant>();
SECTION("size goes back to zero") {
var.add(42);
var.clear();
REQUIRE(var.size() == 0);
}
SECTION("isNull() return true") {
var.add("hello");
var.clear();
REQUIRE(var.isNull() == true);
}
SECTION("releases owned string") {
var.set("hello"_s);
var.clear();
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofString("hello")),
Deallocate(sizeofString("hello")),
});
}
}

View File

@@ -0,0 +1,352 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
// Most code is already covered by arithmeticCompare.cpp.
// Here, we're just filling the holes
TEST_CASE("Compare JsonVariant with value") {
JsonDocument doc;
JsonVariant a = doc.add<JsonVariant>();
SECTION("null vs (char*)0") {
char* b = 0;
CHECK(a == b);
CHECK(a <= b);
CHECK(a >= b);
CHECK_FALSE(a != b);
CHECK_FALSE(a < b);
CHECK_FALSE(a > b);
}
SECTION("42 vs 42") {
a.set(42);
int b = 42;
CHECK(a == b);
CHECK(a <= b);
CHECK(a >= b);
CHECK_FALSE(a != b);
CHECK_FALSE(a < b);
CHECK_FALSE(a > b);
}
}
TEST_CASE("Compare JsonVariant with JsonVariant") {
JsonDocument doc;
JsonVariant a = doc.add<JsonVariant>();
JsonVariant b = doc.add<JsonVariant>();
SECTION("'abc' vs 'abc'") {
a.set("abc");
b.set("abc");
CHECK(a == b);
CHECK(a <= b);
CHECK(a >= b);
CHECK_FALSE(a != b);
CHECK_FALSE(a < b);
CHECK_FALSE(a > b);
}
SECTION("'abc' vs 'bcd'") {
a.set("abc");
b.set("bcd");
CHECK(a != b);
CHECK(a < b);
CHECK(a <= b);
CHECK_FALSE(a == b);
CHECK_FALSE(a > b);
CHECK_FALSE(a >= b);
}
SECTION("'bcd' vs 'abc'") {
a.set("bcd");
b.set("abc");
CHECK(a != b);
CHECK(a > b);
CHECK(a >= b);
CHECK_FALSE(a < b);
CHECK_FALSE(a <= b);
CHECK_FALSE(a == b);
}
SECTION("serialized('abc') vs serialized('abc')") {
a.set(serialized("abc"));
b.set(serialized("abc"));
CHECK(a == b);
CHECK(a <= b);
CHECK(a >= b);
CHECK_FALSE(a != b);
CHECK_FALSE(a < b);
CHECK_FALSE(a > b);
}
SECTION("serialized('abc') vs serialized('bcd')") {
a.set(serialized("abc"));
b.set(serialized("bcd"));
CHECK(a != b);
CHECK(a < b);
CHECK(a <= b);
CHECK_FALSE(a == b);
CHECK_FALSE(a > b);
CHECK_FALSE(a >= b);
}
SECTION("serialized('bcd') vs serialized('abc')") {
a.set(serialized("bcd"));
b.set(serialized("abc"));
CHECK(a != b);
CHECK(a > b);
CHECK(a >= b);
CHECK_FALSE(a < b);
CHECK_FALSE(a <= b);
CHECK_FALSE(a == b);
}
SECTION("MsgPackBinary('abc') vs MsgPackBinary('abc')") {
a.set(MsgPackBinary("abc", 4));
b.set(MsgPackBinary("abc", 4));
CHECK(a == b);
CHECK(a <= b);
CHECK(a >= b);
CHECK_FALSE(a != b);
CHECK_FALSE(a < b);
CHECK_FALSE(a > b);
}
SECTION("MsgPackBinary('abc') vs MsgPackBinary('bcd')") {
a.set(MsgPackBinary("abc", 4));
b.set(MsgPackBinary("bcd", 4));
CHECK(a != b);
CHECK(a < b);
CHECK(a <= b);
CHECK_FALSE(a == b);
CHECK_FALSE(a > b);
CHECK_FALSE(a >= b);
}
SECTION("MsgPackBinary('bcd') vs MsgPackBinary('abc')") {
a.set(MsgPackBinary("bcd", 4));
b.set(MsgPackBinary("abc", 4));
CHECK(a != b);
CHECK(a > b);
CHECK(a >= b);
CHECK_FALSE(a < b);
CHECK_FALSE(a <= b);
CHECK_FALSE(a == b);
}
SECTION("false vs true") {
a.set(false);
b.set(true);
CHECK(a != b);
CHECK(a < b);
CHECK(a <= b);
CHECK_FALSE(a == b);
CHECK_FALSE(a > b);
CHECK_FALSE(a >= b);
}
SECTION("false vs -1") {
a.set(false);
b.set(-1);
CHECK(a != b);
CHECK(a > b);
CHECK(a >= b);
CHECK_FALSE(a < b);
CHECK_FALSE(a <= b);
CHECK_FALSE(a == b);
}
SECTION("null vs null") {
CHECK(a == b);
CHECK(a <= b);
CHECK(a >= b);
CHECK_FALSE(a != b);
CHECK_FALSE(a < b);
CHECK_FALSE(a > b);
}
SECTION("42 vs 42") {
a.set(42);
b.set(42);
CHECK(a == b);
CHECK(a <= b);
CHECK(a >= b);
CHECK_FALSE(a != b);
CHECK_FALSE(a < b);
CHECK_FALSE(a > b);
}
SECTION("42 vs 42U") {
a.set(42);
b.set(42U);
CHECK(a == b);
CHECK(a <= b);
CHECK(a >= b);
CHECK_FALSE(a != b);
CHECK_FALSE(a < b);
CHECK_FALSE(a > b);
}
SECTION("42 vs 42.0") {
a.set(42);
b.set(42.0);
CHECK(a == b);
CHECK(a <= b);
CHECK(a >= b);
CHECK_FALSE(a != b);
CHECK_FALSE(a < b);
CHECK_FALSE(a > b);
}
SECTION("42.0 vs 42") {
a.set(42.0);
b.set(42);
CHECK(a == b);
CHECK(a <= b);
CHECK(a >= b);
CHECK_FALSE(a != b);
CHECK_FALSE(a < b);
CHECK_FALSE(a > b);
}
SECTION("-42 vs -42") {
a.set(-42);
b.set(-42);
CHECK(a == b);
CHECK(a <= b);
CHECK(a >= b);
CHECK_FALSE(a != b);
CHECK_FALSE(a < b);
CHECK_FALSE(a > b);
}
SECTION("-42 vs 42") {
a.set(-42);
b.set(42);
CHECK(a != b);
CHECK(a < b);
CHECK(a <= b);
CHECK_FALSE(a == b);
CHECK_FALSE(a > b);
CHECK_FALSE(a >= b);
}
SECTION("42 vs -42") {
a.set(42);
b.set(-42);
CHECK(a != b);
CHECK(a > b);
CHECK(a >= b);
CHECK_FALSE(a < b);
CHECK_FALSE(a <= b);
CHECK_FALSE(a == b);
}
SECTION("42.0 vs -42") {
a.set(42.0);
b.set(-42);
CHECK(a != b);
CHECK(a > b);
CHECK(a >= b);
CHECK_FALSE(a < b);
CHECK_FALSE(a <= b);
CHECK_FALSE(a == b);
}
SECTION("42U vs 42U") {
a.set(42U);
b.set(42U);
CHECK(a == b);
CHECK(a <= b);
CHECK(a >= b);
CHECK_FALSE(a != b);
CHECK_FALSE(a < b);
CHECK_FALSE(a > b);
}
SECTION("42U vs 42") {
a.set(42U);
b.set(42);
CHECK(a == b);
CHECK(a <= b);
CHECK(a >= b);
CHECK_FALSE(a != b);
CHECK_FALSE(a < b);
CHECK_FALSE(a > b);
}
SECTION("[1] vs [1]") {
a.add(1);
b.add(1);
CHECK(a <= b);
CHECK(a == b);
CHECK(a >= b);
CHECK_FALSE(a != b);
CHECK_FALSE(a < b);
CHECK_FALSE(a > b);
}
SECTION("[1] vs [2]") {
a.add(1);
b.add(2);
CHECK(a != b);
CHECK_FALSE(a < b);
CHECK_FALSE(a <= b);
CHECK_FALSE(a == b);
CHECK_FALSE(a > b);
CHECK_FALSE(a >= b);
}
SECTION("{x:1} vs {x:1}") {
a["x"] = 1;
b["x"] = 1;
CHECK(a <= b);
CHECK(a == b);
CHECK(a >= b);
CHECK_FALSE(a != b);
CHECK_FALSE(a < b);
CHECK_FALSE(a > b);
}
SECTION("{x:1} vs {x:2}") {
a["x"] = 1;
b["x"] = 2;
CHECK(a != b);
CHECK_FALSE(a < b);
CHECK_FALSE(a <= b);
CHECK_FALSE(a == b);
CHECK_FALSE(a > b);
CHECK_FALSE(a >= b);
}
}

View File

@@ -0,0 +1,142 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <stdint.h>
#include <catch.hpp>
namespace {
struct Date {
int day;
int month;
int year;
};
void convertToJson(const Date& src, JsonVariant dst) {
dst["day"] = src.day;
dst["month"] = src.month;
dst["year"] = src.year;
}
void convertFromJson(JsonVariantConst src, Date& dst) {
dst.day = src["day"];
dst.month = src["month"];
dst.year = src["year"];
}
bool canConvertFromJson(JsonVariantConst src, const Date&) {
return src["day"].is<int>() && src["month"].is<int>() &&
src["year"].is<int>();
}
} // namespace
TEST_CASE("Custom converter with overloading") {
JsonDocument doc;
SECTION("convert JSON to Date") {
doc["date"]["day"] = 2;
doc["date"]["month"] = 3;
doc["date"]["year"] = 2021;
Date date = doc["date"];
REQUIRE(date.day == 2);
REQUIRE(date.month == 3);
REQUIRE(date.year == 2021);
}
SECTION("is<Date>() returns true") {
doc["date"]["day"] = 2;
doc["date"]["month"] = 3;
doc["date"]["year"] = 2021;
REQUIRE(doc["date"].is<Date>());
}
SECTION("is<Date>() returns false") {
doc["date"]["day"] = 2;
doc["date"]["month"] = 3;
doc["date"]["year"] = "2021";
REQUIRE(doc["date"].is<Date>() == false);
}
SECTION("convert Date to JSON") {
Date date = {19, 3, 2021};
doc["date"] = date;
REQUIRE(doc["date"]["day"] == 19);
REQUIRE(doc["date"]["month"] == 3);
REQUIRE(doc["date"]["year"] == 2021);
}
}
class Complex {
public:
explicit Complex(double r, double i) : real_(r), imag_(i) {}
double real() const {
return real_;
}
double imag() const {
return imag_;
}
private:
double real_, imag_;
};
namespace ArduinoJson {
template <>
struct Converter<Complex> {
static void toJson(const Complex& src, JsonVariant dst) {
dst["real"] = src.real();
dst["imag"] = src.imag();
}
static Complex fromJson(JsonVariantConst src) {
return Complex(src["real"], src["imag"]);
}
static bool checkJson(JsonVariantConst src) {
return src["real"].is<double>() && src["imag"].is<double>();
}
};
} // namespace ArduinoJson
TEST_CASE("Custom converter with specialization") {
JsonDocument doc;
SECTION("convert JSON to Complex") {
doc["value"]["real"] = 2;
doc["value"]["imag"] = 3;
Complex value = doc["value"];
REQUIRE(value.real() == 2);
REQUIRE(value.imag() == 3);
}
SECTION("is<Complex>() returns true") {
doc["value"]["real"] = 2;
doc["value"]["imag"] = 3;
REQUIRE(doc["value"].is<Complex>());
}
SECTION("is<Complex>() returns false") {
doc["value"]["real"] = 2;
doc["value"]["imag"] = "3";
REQUIRE(doc["value"].is<Complex>() == false);
}
SECTION("convert value to JSON") {
doc["value"] = Complex(19, 3);
REQUIRE(doc["value"]["real"] == 19);
REQUIRE(doc["value"]["imag"] == 3);
}
}

View File

@@ -0,0 +1,142 @@
// 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("JsonVariant::set(JsonVariant)") {
KillswitchAllocator killswitch;
SpyingAllocator spyingAllocator(&killswitch);
JsonDocument doc1(&spyingAllocator);
JsonDocument doc2(&spyingAllocator);
JsonVariant var1 = doc1.to<JsonVariant>();
JsonVariant var2 = doc2.to<JsonVariant>();
SECTION("stores JsonArray by copy") {
JsonArray arr = doc2.to<JsonArray>();
JsonObject obj = arr.add<JsonObject>();
obj["hello"] = "world";
var1.set(arr);
arr[0] = 666;
REQUIRE(var1.as<std::string>() == "[{\"hello\":\"world\"}]");
}
SECTION("stores JsonObject by copy") {
JsonObject obj = doc2.to<JsonObject>();
JsonArray arr = obj["value"].to<JsonArray>();
arr.add(42);
var1.set(obj);
obj["value"] = 666;
REQUIRE(var1.as<std::string>() == "{\"value\":[42]}");
}
SECTION("stores const char* by reference") {
var1.set("hello!!");
spyingAllocator.clearLog();
var2.set(var1);
REQUIRE(spyingAllocator.log() == AllocatorLog{});
}
SECTION("stores char* by copy") {
char str[] = "hello!!";
var1.set(str);
spyingAllocator.clearLog();
var2.set(var1);
REQUIRE(spyingAllocator.log() == AllocatorLog{
Allocate(sizeofString("hello!!")),
});
}
SECTION("fails gracefully if string allocation fails") {
char str[] = "hello!!";
var1.set(str);
killswitch.on();
spyingAllocator.clearLog();
var2.set(var1);
REQUIRE(doc2.overflowed() == true);
REQUIRE(spyingAllocator.log() == AllocatorLog{
AllocateFail(sizeofString("hello!!")),
});
}
SECTION("stores std::string by copy") {
var1.set("hello!!"_s);
spyingAllocator.clearLog();
var2.set(var1);
REQUIRE(spyingAllocator.log() == AllocatorLog{
Allocate(sizeofString("hello!!")),
});
}
SECTION("stores Serialized<const char*> by copy") {
var1.set(serialized("hello!!", 7));
spyingAllocator.clearLog();
var2.set(var1);
REQUIRE(spyingAllocator.log() == AllocatorLog{
Allocate(sizeofString("hello!!")),
});
}
SECTION("stores Serialized<char*> by copy") {
char str[] = "hello!!";
var1.set(serialized(str, 7));
spyingAllocator.clearLog();
var2.set(var1);
REQUIRE(spyingAllocator.log() == AllocatorLog{
Allocate(sizeofString("hello!!")),
});
}
SECTION("stores Serialized<std::string> by copy") {
var1.set(serialized("hello!!"_s));
spyingAllocator.clearLog();
var2.set(var1);
REQUIRE(spyingAllocator.log() == AllocatorLog{
Allocate(sizeofString("hello!!")),
});
}
SECTION("fails gracefully if raw string allocation fails") {
var1.set(serialized("hello!!"_s));
killswitch.on();
spyingAllocator.clearLog();
var2.set(var1);
REQUIRE(doc2.overflowed() == true);
REQUIRE(spyingAllocator.log() == AllocatorLog{
AllocateFail(sizeofString("hello!!")),
});
}
SECTION("destination is unbound") {
JsonVariant unboundVariant;
unboundVariant.set(var1);
REQUIRE(unboundVariant.isUnbound());
REQUIRE(unboundVariant.isNull());
}
}

View File

@@ -0,0 +1,164 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
enum MYENUM2 { ONE = 1, TWO = 2 };
TEST_CASE("JsonVariant::is<T>()") {
JsonDocument doc;
JsonVariant variant = doc.to<JsonVariant>();
SECTION("unbound") {
variant = JsonVariant();
CHECK(variant.is<JsonObject>() == false);
CHECK(variant.is<JsonArray>() == false);
CHECK(variant.is<JsonVariant>() == false);
CHECK(variant.is<JsonVariantConst>() == false);
CHECK(variant.is<bool>() == false);
CHECK(variant.is<const char*>() == false);
CHECK(variant.is<int>() == false);
CHECK(variant.is<std::string>() == false);
CHECK(variant.is<JsonString>() == false);
CHECK(variant.is<float>() == false);
CHECK(variant.is<MYENUM2>() == false);
CHECK(variant.is<JsonString>() == false);
}
SECTION("null") {
CHECK(variant.is<JsonVariant>() == true);
CHECK(variant.is<JsonVariantConst>() == true);
CHECK(variant.is<JsonObject>() == false);
CHECK(variant.is<JsonArray>() == false);
CHECK(variant.is<bool>() == false);
CHECK(variant.is<const char*>() == false);
CHECK(variant.is<int>() == false);
CHECK(variant.is<std::string>() == false);
CHECK(variant.is<JsonString>() == false);
CHECK(variant.is<float>() == false);
CHECK(variant.is<MYENUM2>() == false);
}
SECTION("true") {
variant.set(true);
CHECK(variant.is<bool>() == true);
CHECK(variant.is<JsonVariant>() == true);
CHECK(variant.is<JsonVariantConst>() == true);
CHECK(variant.is<JsonObject>() == false);
CHECK(variant.is<JsonArray>() == false);
CHECK(variant.is<const char*>() == false);
CHECK(variant.is<int>() == false);
CHECK(variant.is<std::string>() == false);
CHECK(variant.is<JsonString>() == false);
CHECK(variant.is<float>() == false);
CHECK(variant.is<MYENUM2>() == false);
}
SECTION("false") {
variant.set(false);
CHECK(variant.is<bool>() == true);
CHECK(variant.is<JsonVariant>() == true);
CHECK(variant.is<JsonVariantConst>() == true);
CHECK(variant.is<JsonObject>() == false);
CHECK(variant.is<JsonArray>() == false);
CHECK(variant.is<const char*>() == false);
CHECK(variant.is<int>() == false);
CHECK(variant.is<std::string>() == false);
CHECK(variant.is<JsonString>() == false);
CHECK(variant.is<float>() == false);
CHECK(variant.is<MYENUM2>() == false);
}
SECTION("int") {
variant.set(42);
CHECK(variant.is<int>() == true);
CHECK(variant.is<short>() == true);
CHECK(variant.is<long>() == true);
CHECK(variant.is<double>() == true);
CHECK(variant.is<float>() == true);
CHECK(variant.is<MYENUM2>() == true);
CHECK(variant.is<JsonVariant>() == true);
CHECK(variant.is<JsonVariantConst>() == true);
CHECK(variant.is<bool>() == false);
CHECK(variant.is<JsonObject>() == false);
CHECK(variant.is<JsonArray>() == false);
CHECK(variant.is<const char*>() == false);
CHECK(variant.is<std::string>() == false);
CHECK(variant.is<JsonString>() == false);
}
SECTION("double") {
variant.set(4.2);
CHECK(variant.is<double>() == true);
CHECK(variant.is<float>() == true);
CHECK(variant.is<JsonVariant>() == true);
CHECK(variant.is<JsonVariantConst>() == true);
CHECK(variant.is<bool>() == false);
CHECK(variant.is<JsonObject>() == false);
CHECK(variant.is<JsonArray>() == false);
CHECK(variant.is<const char*>() == false);
CHECK(variant.is<int>() == false);
CHECK(variant.is<std::string>() == false);
CHECK(variant.is<JsonString>() == false);
CHECK(variant.is<MYENUM2>() == false);
}
SECTION("const char*") {
variant.set("4.2");
CHECK(variant.is<const char*>() == true);
CHECK(variant.is<const char*>() == true);
CHECK(variant.is<std::string>() == true);
CHECK(variant.is<JsonString>() == true);
CHECK(variant.is<JsonVariant>() == true);
CHECK(variant.is<JsonVariantConst>() == true);
CHECK(variant.is<double>() == false);
CHECK(variant.is<float>() == false);
CHECK(variant.is<bool>() == false);
CHECK(variant.is<JsonObject>() == false);
CHECK(variant.is<JsonArray>() == false);
CHECK(variant.is<int>() == false);
CHECK(variant.is<MYENUM2>() == false);
}
SECTION("JsonArray") {
variant.to<JsonArray>();
CHECK(variant.is<JsonArray>() == true);
CHECK(variant.is<JsonArrayConst>() == true);
CHECK(variant.is<JsonVariant>() == true);
CHECK(variant.is<JsonVariantConst>() == true);
CHECK(variant.is<JsonObject>() == false);
CHECK(variant.is<JsonObjectConst>() == false);
CHECK(variant.is<int>() == false);
CHECK(variant.is<float>() == false);
CHECK(variant.is<bool>() == false);
CHECK(variant.is<const char*>() == false);
CHECK(variant.is<MYENUM2>() == false);
}
SECTION("JsonObject") {
variant.to<JsonObject>();
CHECK(variant.is<JsonObject>() == true);
CHECK(variant.is<JsonObjectConst>() == true);
CHECK(variant.is<JsonVariant>() == true);
CHECK(variant.is<JsonVariantConst>() == true);
CHECK(variant.is<JsonArray>() == false);
CHECK(variant.is<JsonArrayConst>() == false);
CHECK(variant.is<int>() == false);
CHECK(variant.is<float>() == false);
CHECK(variant.is<bool>() == false);
CHECK(variant.is<const char*>() == false);
CHECK(variant.is<MYENUM2>() == false);
CHECK(variant.is<JsonVariant>() == true);
CHECK(variant.is<JsonVariantConst>() == true);
}
}

View File

@@ -0,0 +1,72 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
TEST_CASE("JsonVariant::isNull()") {
JsonDocument doc;
JsonVariant variant = doc.to<JsonVariant>();
SECTION("returns true when Undefined") {
REQUIRE(variant.isNull() == true);
}
SECTION("returns false when Integer") {
variant.set(42);
REQUIRE(variant.isNull() == false);
}
SECTION("returns false when EmptyArray") {
JsonDocument doc2;
JsonArray array = doc2.to<JsonArray>();
variant.set(array);
REQUIRE(variant.isNull() == false);
}
SECTION("returns false when EmptyObject") {
JsonDocument doc2;
JsonObject obj = doc2.to<JsonObject>();
variant.set(obj);
REQUIRE(variant.isNull() == false);
}
SECTION("returns true after set(JsonArray())") {
variant.set(JsonArray());
REQUIRE(variant.isNull() == true);
}
SECTION("returns true after set(JsonObject())") {
variant.set(JsonObject());
REQUIRE(variant.isNull() == true);
}
SECTION("returns false after set('hello')") {
variant.set("hello");
REQUIRE(variant.isNull() == false);
}
SECTION("returns true after set((char*)0)") {
variant.set(static_cast<char*>(0));
REQUIRE(variant.isNull() == true);
}
SECTION("returns true after set((const char*)0)") {
variant.set(static_cast<const char*>(0));
REQUIRE(variant.isNull() == true);
}
SECTION("returns true after set(serialized((char*)0))") {
variant.set(serialized(static_cast<char*>(0)));
REQUIRE(variant.isNull() == true);
}
SECTION("returns true after set(serialized((const char*)0))") {
variant.set(serialized(static_cast<const char*>(0)));
REQUIRE(variant.isNull() == true);
}
}

View File

@@ -0,0 +1,60 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
TEST_CASE("VariantData") {
REQUIRE(std::is_standard_layout<ArduinoJson::detail::VariantData>::value ==
true);
}
TEST_CASE("StringNode") {
REQUIRE(std::is_standard_layout<ArduinoJson::detail::StringNode>::value ==
true);
}
TEST_CASE("JsonVariant from JsonArray") {
SECTION("JsonArray is null") {
JsonArray arr;
JsonVariant v = arr;
REQUIRE(v.isNull() == true);
}
SECTION("JsonArray is not null") {
JsonDocument doc;
JsonArray arr = doc.to<JsonArray>();
arr.add(12);
arr.add(34);
JsonVariant v = arr;
REQUIRE(v.is<JsonArray>() == true);
REQUIRE(v.size() == 2);
REQUIRE(v[0] == 12);
REQUIRE(v[1] == 34);
}
}
TEST_CASE("JsonVariant from JsonObject") {
SECTION("JsonObject is null") {
JsonObject obj;
JsonVariant v = obj;
REQUIRE(v.isNull() == true);
}
SECTION("JsonObject is not null") {
JsonDocument doc;
JsonObject obj = doc.to<JsonObject>();
obj["a"] = 12;
obj["b"] = 34;
JsonVariant v = obj;
REQUIRE(v.is<JsonObject>() == true);
REQUIRE(v.size() == 2);
REQUIRE(v["a"] == 12);
REQUIRE(v["b"] == 34);
}
}

View File

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

View File

@@ -0,0 +1,43 @@
#include <ArduinoJson.h>
#include <catch.hpp>
TEST_CASE("nullptr") {
JsonDocument doc;
JsonVariant variant = doc.to<JsonVariant>();
SECTION("JsonVariant == nullptr") {
REQUIRE(variant == nullptr);
REQUIRE_FALSE(variant != nullptr);
}
SECTION("JsonVariant != nullptr") {
variant.set(42);
REQUIRE_FALSE(variant == nullptr);
REQUIRE(variant != nullptr);
}
SECTION("JsonVariant.set(nullptr)") {
variant.set(42);
variant.set(nullptr);
REQUIRE(variant.isNull());
}
SECTION("JsonVariant.set(nullptr) with unbound reference") {
JsonVariant unboundReference;
unboundReference.set(nullptr);
REQUIRE(variant.isNull());
}
SECTION("JsonVariant.is<nullptr_t>()") {
variant.set(42);
REQUIRE(variant.is<std::nullptr_t>() == false);
variant.clear();
REQUIRE(variant.is<std::nullptr_t>() == true);
}
}

View File

@@ -0,0 +1,159 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
TEST_CASE("JsonVariant::operator|()") {
JsonDocument doc;
JsonVariant variant = doc["value"].to<JsonVariant>();
SECTION("null") {
SECTION("null | const char*") {
std::string result = variant | "default";
REQUIRE(result == "default");
}
SECTION("null | int") {
int result = variant | 42;
REQUIRE(result == 42);
}
SECTION("null | bool") {
bool result = variant | true;
REQUIRE(result == true);
}
SECTION("null | ElementProxy") {
doc["array"][0] = 42;
JsonVariantConst result = variant | doc["array"][0];
REQUIRE(result == 42);
}
SECTION("null | MemberProxy") {
doc["other"] = 42;
JsonVariantConst result = variant | doc["other"];
REQUIRE(result == 42);
}
SECTION("ElementProxy | ElementProxy") {
doc["array"][0] = 42;
JsonVariantConst result = doc["array"][1] | doc["array"][0];
REQUIRE(result == 42);
}
}
SECTION("null") {
variant.set(static_cast<const char*>(0));
SECTION("null | const char*") {
std::string result = variant | "default";
REQUIRE(result == "default");
}
SECTION("null | int") {
int result = variant | 42;
REQUIRE(result == 42);
}
SECTION("null | bool") {
bool result = variant | true;
REQUIRE(result == true);
}
SECTION("null | ElementProxy") {
doc["array"][0] = 42;
JsonVariantConst result = variant | doc["array"][0];
REQUIRE(result == 42);
}
SECTION("null | MemberProxy") {
doc["other"] = 42;
JsonVariantConst result = variant | doc["other"];
REQUIRE(result == 42);
}
}
SECTION("int | const char*") {
variant.set(42);
std::string result = variant | "default";
REQUIRE(result == "default");
}
SECTION("int | uint8_t (out of range)") {
variant.set(666);
uint8_t result = variant | static_cast<uint8_t>(42);
REQUIRE(result == 42);
}
SECTION("int | ElementProxy") {
variant.set(42);
doc["array"][0] = 666;
JsonVariantConst result = variant | doc["array"][0];
REQUIRE(result == 42);
}
SECTION("int | MemberProxy") {
variant.set(42);
doc["other"] = 666;
JsonVariantConst result = variant | doc["other"];
REQUIRE(result == 42);
}
SECTION("int | int") {
variant.set(0);
int result = variant | 666;
REQUIRE(result == 0);
}
SECTION("double | int") {
// NOTE: changed the behavior to fix #981
variant.set(666.0);
int result = variant | 42;
REQUIRE(result == 42);
}
SECTION("bool | bool") {
variant.set(false);
bool result = variant | true;
REQUIRE(result == false);
}
SECTION("int | bool") {
variant.set(0);
bool result = variant | true;
REQUIRE(result == true);
}
SECTION("const char* | const char*") {
variant.set("not default");
std::string result = variant | "default";
REQUIRE(result == "not default");
}
SECTION("const char* | char*") {
char dflt[] = "default";
variant.set("not default");
std::string result = variant | dflt;
REQUIRE(result == "not default");
}
SECTION("int | char*") {
char dflt[] = "default";
variant.set(42);
std::string result = variant | dflt;
REQUIRE(result == "default");
}
SECTION("const char* | int") {
variant.set("not default");
int result = variant | 42;
REQUIRE(result == 42);
}
}

View File

@@ -0,0 +1,72 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
template <typename TOut, typename TIn>
void shouldBeOk(TIn value) {
JsonDocument doc;
JsonVariant var = doc.to<JsonVariant>();
var.set(value);
REQUIRE(var.as<TOut>() == TOut(value));
}
template <typename TOut, typename TIn>
void shouldOverflow(TIn value) {
JsonDocument doc;
JsonVariant var = doc.to<JsonVariant>();
var.set(value);
REQUIRE(var.as<TOut>() == 0);
REQUIRE(var.is<TOut>() == false);
}
TEST_CASE("Handle integer overflow in stored integer") {
SECTION("int8_t") {
// ok
shouldBeOk<int8_t>(-128);
shouldBeOk<int8_t>(42.0);
shouldBeOk<int8_t>(127);
// too low
shouldOverflow<int8_t>(-128.1);
shouldOverflow<int8_t>(-129);
// too high
shouldOverflow<int8_t>(128);
shouldOverflow<int8_t>(127.1);
}
SECTION("int16_t") {
// ok
shouldBeOk<int16_t>(-32768);
shouldBeOk<int16_t>(-32767.9);
shouldBeOk<int16_t>(32766.9);
shouldBeOk<int16_t>(32767);
// too low
shouldOverflow<int16_t>(-32768.1);
shouldOverflow<int16_t>(-32769);
// too high
shouldOverflow<int16_t>(32767.1);
shouldOverflow<int16_t>(32768);
}
SECTION("uint8_t") {
// ok
shouldBeOk<uint8_t>(1);
shouldBeOk<uint8_t>(42.0);
shouldBeOk<uint8_t>(255);
// too low
shouldOverflow<uint8_t>(-1);
shouldOverflow<uint8_t>(-0.1);
// to high
shouldOverflow<uint8_t>(255.1);
shouldOverflow<uint8_t>(256);
shouldOverflow<uint8_t>(257);
}
}

View File

@@ -0,0 +1,128 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <stdint.h>
#include <catch.hpp>
#include "Allocators.hpp"
#include "Literals.hpp"
using ArduinoJson::detail::sizeofArray;
TEST_CASE("JsonVariant::remove(int)") {
SpyingAllocator spy;
JsonDocument doc(&spy);
SECTION("release top level strings") {
doc.add("hello"_s);
doc.add("hello"_s);
doc.add("world"_s);
JsonVariant var = doc.as<JsonVariant>();
REQUIRE(var.as<std::string>() == "[\"hello\",\"hello\",\"world\"]");
spy.clearLog();
var.remove(1);
REQUIRE(var.as<std::string>() == "[\"hello\",\"world\"]");
REQUIRE(spy.log() == AllocatorLog{});
spy.clearLog();
var.remove(1);
REQUIRE(var.as<std::string>() == "[\"hello\"]");
REQUIRE(spy.log() == AllocatorLog{
Deallocate(sizeofString("world")),
});
spy.clearLog();
var.remove(0);
REQUIRE(var.as<std::string>() == "[]");
REQUIRE(spy.log() == AllocatorLog{
Deallocate(sizeofString("hello")),
});
}
SECTION("release strings in nested array") {
doc[0][0] = "hello"_s;
JsonVariant var = doc.as<JsonVariant>();
REQUIRE(var.as<std::string>() == "[[\"hello\"]]");
spy.clearLog();
var.remove(0);
REQUIRE(var.as<std::string>() == "[]");
REQUIRE(spy.log() == AllocatorLog{
Deallocate(sizeofString("hello")),
});
}
}
TEST_CASE("JsonVariant::remove(const char *)") {
JsonDocument doc;
JsonVariant var = doc.to<JsonVariant>();
var["a"] = 1;
var["b"] = 2;
var.remove("a");
REQUIRE(var.as<std::string>() == "{\"b\":2}");
}
TEST_CASE("JsonVariant::remove(std::string)") {
JsonDocument doc;
JsonVariant var = doc.to<JsonVariant>();
var["a"] = 1;
var["b"] = 2;
var.remove("b"_s);
REQUIRE(var.as<std::string>() == "{\"a\":1}");
}
#ifdef HAS_VARIABLE_LENGTH_ARRAY
TEST_CASE("JsonVariant::remove(VLA)") {
JsonDocument doc;
JsonVariant var = doc.to<JsonVariant>();
var["a"] = 1;
var["b"] = 2;
size_t i = 16;
char vla[i];
strcpy(vla, "b");
var.remove("b"_s);
REQUIRE(var.as<std::string>() == "{\"a\":1}");
}
#endif
TEST_CASE("JsonVariant::remove(JsonVariant) from object") {
JsonDocument doc;
JsonVariant var = doc.to<JsonVariant>();
var["a"] = "a";
var["b"] = 2;
var["c"] = "b";
var.remove(var["c"]);
REQUIRE(var.as<std::string>() == "{\"a\":\"a\",\"c\":\"b\"}");
}
TEST_CASE("JsonVariant::remove(JsonVariant) from array") {
JsonDocument doc;
JsonVariant var = doc.to<JsonVariant>();
var[0] = 3;
var[1] = 2;
var[2] = 1;
var.remove(var[2]);
var.remove(var[3]); // noop
REQUIRE(var.as<std::string>() == "[3,1]");
}

View File

@@ -0,0 +1,407 @@
// 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::sizeofObject;
enum ErrorCode { ERROR_01 = 1, ERROR_10 = 10 };
TEST_CASE("JsonVariant::set() when there is enough memory") {
SpyingAllocator spy;
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];
strcpy(str, "hello");
bool result = variant.set(static_cast<const char*>(str));
strcpy(str, "world");
REQUIRE(result == true);
REQUIRE(variant == "hello"); // stores by copy
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofString("hello")),
});
}
SECTION("(const char*)0") {
bool result = variant.set(static_cast<const char*>(0));
REQUIRE(result == true);
REQUIRE(variant.isNull());
REQUIRE(variant.as<const char*>() == nullptr);
REQUIRE(spy.log() == AllocatorLog{});
}
SECTION("char*") {
char str[16];
strcpy(str, "hello");
bool result = variant.set(str);
strcpy(str, "world");
REQUIRE(result == true);
REQUIRE(variant == "hello"); // stores by copy
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofString("hello")),
});
}
SECTION("char* (tiny string optimization)") {
char str[16];
strcpy(str, "abc");
bool result = variant.set(str);
strcpy(str, "def");
REQUIRE(result == true);
REQUIRE(variant == "abc"); // stores by copy
REQUIRE(spy.log() == AllocatorLog{});
}
SECTION("(char*)0") {
bool result = variant.set(static_cast<char*>(0));
REQUIRE(result == true);
REQUIRE(variant.isNull());
REQUIRE(spy.log() == AllocatorLog{});
}
SECTION("unsigned char*") {
char str[16];
strcpy(str, "hello");
bool result = variant.set(reinterpret_cast<unsigned char*>(str));
strcpy(str, "world");
REQUIRE(result == true);
REQUIRE(variant == "hello"); // stores by copy
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofString("hello")),
});
}
SECTION("signed char*") {
char str[16];
strcpy(str, "hello");
bool result = variant.set(reinterpret_cast<signed char*>(str));
strcpy(str, "world");
REQUIRE(result == true);
REQUIRE(variant == "hello"); // stores by copy
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofString("hello")),
});
}
#ifdef HAS_VARIABLE_LENGTH_ARRAY
SECTION("VLA") {
size_t n = 16;
char str[n];
strcpy(str, "hello");
bool result = variant.set(str);
strcpy(str, "world");
REQUIRE(result == true);
REQUIRE(variant == "hello"); // stores by copy
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofString("hello")),
});
}
#endif
SECTION("std::string") {
std::string str = "hello\0world"_s;
bool result = variant.set(str);
str.replace(0, 5, "world");
REQUIRE(result == true);
REQUIRE(variant == "hello\0world"_s); // stores by copy
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofString("hello?world")),
});
}
SECTION("static JsonString") {
char str[16];
strcpy(str, "hello");
bool result = variant.set(JsonString(str, true));
strcpy(str, "world");
REQUIRE(result == true);
REQUIRE(variant == "world"); // stores by pointer
REQUIRE(spy.log() == AllocatorLog{});
}
SECTION("non-static JsonString") {
char str[16];
strcpy(str, "hello");
bool result = variant.set(JsonString(str));
strcpy(str, "world");
REQUIRE(result == true);
REQUIRE(variant == "hello"); // stores by copy
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofString("hello")),
});
}
SECTION("enum") {
ErrorCode code = ERROR_10;
bool result = variant.set(code);
REQUIRE(result == true);
REQUIRE(variant.is<int>() == true);
REQUIRE(variant.as<int>() == 10);
REQUIRE(spy.log() == AllocatorLog{});
}
SECTION("float") {
bool result = variant.set(1.2f);
REQUIRE(result == true);
REQUIRE(variant.is<float>() == true);
REQUIRE(variant.as<float>() == 1.2f);
REQUIRE(spy.log() == AllocatorLog{});
}
SECTION("double") {
bool result = variant.set(1.2);
doc.shrinkToFit();
REQUIRE(result == true);
REQUIRE(variant.is<double>() == true);
REQUIRE(variant.as<double>() == 1.2);
REQUIRE(spy.log() ==
AllocatorLog{
Allocate(sizeofPool()),
Reallocate(sizeofPool(), sizeofPool(1)), // one extension slot
});
}
SECTION("int32_t") {
bool result = variant.set(int32_t(42));
REQUIRE(result == true);
REQUIRE(variant.is<int32_t>() == true);
REQUIRE(variant.as<int32_t>() == 42);
REQUIRE(spy.log() == AllocatorLog{});
}
SECTION("int64_t") {
bool result = variant.set(int64_t(-2147483649LL));
doc.shrinkToFit();
REQUIRE(result == true);
REQUIRE(variant.is<int64_t>() == true);
REQUIRE(variant.as<int64_t>() == -2147483649LL);
REQUIRE(spy.log() ==
AllocatorLog{
Allocate(sizeofPool()),
Reallocate(sizeofPool(), sizeofPool(1)), // one extension slot
});
}
SECTION("uint32_t") {
bool result = variant.set(uint32_t(42));
REQUIRE(result == true);
REQUIRE(variant.is<uint32_t>() == true);
REQUIRE(variant.as<uint32_t>() == 42);
REQUIRE(spy.log() == AllocatorLog{});
}
SECTION("uint64_t") {
bool result = variant.set(uint64_t(4294967296));
doc.shrinkToFit();
REQUIRE(result == true);
REQUIRE(variant.is<uint64_t>() == true);
REQUIRE(variant.as<uint64_t>() == 4294967296);
REQUIRE(spy.log() ==
AllocatorLog{
Allocate(sizeofPool()),
Reallocate(sizeofPool(), sizeofPool(1)), // one extension slot
});
}
SECTION("JsonDocument") {
JsonDocument doc1;
doc1["hello"] = "world";
// Should copy the doc
variant.set(doc1);
doc1.clear();
std::string json;
serializeJson(doc, json);
REQUIRE(json == "{\"hello\":\"world\"}");
}
}
TEST_CASE("JsonVariant::set() with not enough memory") {
JsonDocument doc(FailingAllocator::instance());
JsonVariant v = doc.to<JsonVariant>();
SECTION("std::string") {
bool result = v.set("hello world!!"_s);
REQUIRE(result == false);
REQUIRE(v.isNull());
}
SECTION("Serialized<std::string>") {
bool result = v.set(serialized("hello world!!"_s));
REQUIRE(result == false);
REQUIRE(v.isNull());
}
SECTION("char*") {
char s[] = "hello world!!";
bool result = v.set(s);
REQUIRE(result == false);
REQUIRE(v.isNull());
}
SECTION("float") {
bool result = v.set(1.2f);
REQUIRE(result == true);
REQUIRE(v.is<float>());
}
SECTION("double") {
bool result = v.set(1.2);
REQUIRE(result == false);
REQUIRE(v.isNull());
}
SECTION("int32_t") {
bool result = v.set(-42);
REQUIRE(result == true);
REQUIRE(v.is<int32_t>());
}
SECTION("int64_t") {
bool result = v.set(-2147483649LL);
REQUIRE(result == false);
REQUIRE(v.isNull());
}
SECTION("uint32_t") {
bool result = v.set(42);
REQUIRE(result == true);
REQUIRE(v.is<uint32_t>());
}
SECTION("uint64_t") {
bool result = v.set(4294967296U);
REQUIRE(result == false);
REQUIRE(v.isNull());
}
}
TEST_CASE("JsonVariant::set() releases the previous value") {
SpyingAllocator spy;
JsonDocument doc(&spy);
doc["hello"] = "world"_s;
spy.clearLog();
JsonVariant v = doc["hello"];
SECTION("int") {
v.set(42);
REQUIRE(spy.log() == AllocatorLog{
Deallocate(sizeofString("world")),
});
}
SECTION("bool") {
v.set(false);
REQUIRE(spy.log() == AllocatorLog{
Deallocate(sizeofString("world")),
});
}
SECTION("const char*") {
v.set("hello");
REQUIRE(spy.log() == AllocatorLog{
Deallocate(sizeofString("world")),
});
}
SECTION("float") {
v.set(1.2);
REQUIRE(spy.log() == AllocatorLog{
Deallocate(sizeofString("world")),
});
}
SECTION("Serialized<const char*>") {
v.set(serialized("[]"));
REQUIRE(spy.log() == AllocatorLog{
Deallocate(sizeofString("world")),
Allocate(sizeofString("[]")),
});
}
}
TEST_CASE("JsonVariant::set() reuses extension slot") {
SpyingAllocator spy;
JsonDocument doc(&spy);
JsonVariant variant = doc.to<JsonVariant>();
variant.set(1.2);
doc.shrinkToFit();
spy.clearLog();
SECTION("double") {
bool result = variant.set(3.4);
REQUIRE(result == true);
REQUIRE(spy.log() == AllocatorLog{});
}
SECTION("int64_t") {
bool result = variant.set(-2147483649LL);
REQUIRE(result == true);
REQUIRE(spy.log() == AllocatorLog{});
}
SECTION("uint64_t") {
bool result = variant.set(4294967296U);
REQUIRE(result == true);
REQUIRE(spy.log() == AllocatorLog{});
}
}

View File

@@ -0,0 +1,36 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
TEST_CASE("JsonVariant::size()") {
JsonDocument doc;
JsonVariant variant = doc.to<JsonVariant>();
SECTION("unbound reference") {
JsonVariant unbound;
CHECK(unbound.size() == 0);
}
SECTION("int") {
variant.set(42);
CHECK(variant.size() == 0);
}
SECTION("string") {
variant.set("hello");
CHECK(variant.size() == 0);
}
SECTION("object") {
variant["a"] = 1;
variant["b"] = 2;
CHECK(variant.size() == 2);
}
}

View File

@@ -0,0 +1,138 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <stdint.h>
#include <catch.hpp>
#include <array>
#include <string>
#include <vector>
namespace ArduinoJson {
template <typename T>
struct Converter<std::vector<T>> {
static void toJson(const std::vector<T>& src, JsonVariant dst) {
JsonArray array = dst.to<JsonArray>();
for (T item : src)
array.add(item);
}
static std::vector<T> fromJson(JsonVariantConst src) {
std::vector<T> dst;
for (T item : src.as<JsonArrayConst>())
dst.push_back(item);
return dst;
}
static bool checkJson(JsonVariantConst src) {
JsonArrayConst array = src;
bool result = array;
for (JsonVariantConst item : array)
result &= item.is<T>();
return result;
}
};
template <typename T, size_t N>
struct Converter<std::array<T, N>> {
static void toJson(const std::array<T, N>& src, JsonVariant dst) {
JsonArray array = dst.to<JsonArray>();
for (T item : src)
array.add(item);
}
static std::array<T, N> fromJson(JsonVariantConst src) {
std::array<T, N> dst;
dst.fill(0);
size_t idx = 0;
for (T item : src.as<JsonArrayConst>())
dst[idx++] = item;
return dst;
}
static bool checkJson(JsonVariantConst src) {
JsonArrayConst array = src;
bool result = array;
size_t size = 0;
for (JsonVariantConst item : array) {
result &= item.is<T>();
size++;
}
return result && size == N;
}
};
} // namespace ArduinoJson
TEST_CASE("vector<int>") {
SECTION("toJson") {
std::vector<int> v = {1, 2};
JsonDocument doc;
doc.set(v);
REQUIRE(doc.as<std::string>() == "[1,2]");
}
SECTION("fromJson") {
JsonDocument doc;
doc.add(1);
doc.add(2);
auto v = doc.as<std::vector<int>>();
REQUIRE(v.size() == 2);
CHECK(v[0] == 1);
CHECK(v[1] == 2);
}
SECTION("checkJson") {
JsonDocument doc;
CHECK(doc.is<std::vector<int>>() == false);
doc.add(1);
doc.add(2);
CHECK(doc.is<std::vector<int>>() == true);
doc.add("foo");
CHECK(doc.is<std::vector<int>>() == false);
}
}
TEST_CASE("array<int, 2>") {
using array_type = std::array<int, 2>;
SECTION("toJson") {
array_type v;
v[0] = 1;
v[1] = 2;
JsonDocument doc;
doc.set(v);
REQUIRE(doc.as<std::string>() == "[1,2]");
}
SECTION("fromJson") {
JsonDocument doc;
doc.add(1);
doc.add(2);
auto v = doc.as<array_type>();
REQUIRE(v.size() == 2);
CHECK(v[0] == 1);
CHECK(v[1] == 2);
}
SECTION("checkJson") {
JsonDocument doc;
CHECK(doc.is<array_type>() == false);
doc.add(1);
CHECK(doc.is<array_type>() == false);
doc.add(2);
CHECK(doc.is<array_type>() == true);
doc[0] = "foo";
CHECK(doc.is<array_type>() == false);
}
}

View File

@@ -0,0 +1,159 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
#include "Literals.hpp"
TEST_CASE("JsonVariant::operator[]") {
JsonDocument doc;
JsonVariant var = doc.to<JsonVariant>();
SECTION("The JsonVariant is null") {
REQUIRE(0 == var.size());
REQUIRE(var["0"].isNull());
REQUIRE(var[0].isNull());
}
SECTION("The JsonVariant is a string") {
var.set("hello world");
REQUIRE(0 == var.size());
REQUIRE(var["0"].isNull());
REQUIRE(var[0].isNull());
}
SECTION("The JsonVariant is a JsonArray") {
JsonArray array = var.to<JsonArray>();
SECTION("get value") {
array.add("element at index 0");
array.add("element at index 1");
REQUIRE(2 == var.size());
var[0].as<std::string>();
// REQUIRE("element at index 0"_s == );
REQUIRE("element at index 1"_s == var[1]);
REQUIRE("element at index 0"_s ==
var[static_cast<unsigned char>(0)]); // issue #381
REQUIRE(var[666].isNull());
REQUIRE(var[3].isNull());
REQUIRE(var["0"].isNull());
}
SECTION("set value") {
array.add("hello");
var[1] = "world";
REQUIRE(var.size() == 2);
REQUIRE("world"_s == var[1]);
}
SECTION("set value in a nested object") {
array.add<JsonObject>();
var[0]["hello"] = "world";
REQUIRE(1 == var.size());
REQUIRE(1 == var[0].size());
REQUIRE("world"_s == var[0]["hello"]);
}
SECTION("variant[0] when variant contains an integer") {
var.set(123);
var[0] = 345; // no-op
REQUIRE(var.is<int>());
REQUIRE(var.as<int>() == 123);
}
SECTION("use JsonVariant as index") {
array.add("A");
array.add("B");
array.add(1);
REQUIRE(var[var[2]] == "B");
REQUIRE(var[var[3]].isNull());
}
}
SECTION("The JsonVariant is a JsonObject") {
JsonObject object = var.to<JsonObject>();
SECTION("get value") {
object["a"] = "element at key \"a\"";
object["b"] = "element at key \"b\"";
REQUIRE(2 == var.size());
REQUIRE("element at key \"a\""_s == var["a"]);
REQUIRE("element at key \"b\""_s == var["b"]);
REQUIRE(var["c"].isNull());
REQUIRE(var[0].isNull());
}
SECTION("set value, key is a const char*") {
var["hello"] = "world";
REQUIRE(1 == var.size());
REQUIRE("world"_s == var["hello"]);
}
SECTION("set value, key is a char[]") {
char key[] = "hello";
var[key] = "world";
key[0] = '!'; // make sure the key is duplicated
REQUIRE(1 == var.size());
REQUIRE("world"_s == var["hello"]);
}
SECTION("var[key].to<JsonArray>()") {
JsonArray arr = var["hello"].to<JsonArray>();
REQUIRE(arr.isNull() == false);
}
SECTION("use JsonVariant as key") {
object["a"] = "A";
object["ab"] = "AB";
object["ab\0c"_s] = "ABC";
object["key1"] = "a";
object["key2"] = "ab";
object["key3"] = "ab\0c"_s;
object["key4"] = "foo";
REQUIRE(var[var["key1"]] == "A");
REQUIRE(var[var["key2"]] == "AB");
REQUIRE(var[var["key3"]] == "ABC");
REQUIRE(var[var["key4"]].isNull());
REQUIRE(var[var["key5"]].isNull());
}
}
#if defined(HAS_VARIABLE_LENGTH_ARRAY) && \
!defined(SUBSCRIPT_CONFLICTS_WITH_BUILTIN_OPERATOR)
SECTION("key is a VLA") {
size_t i = 16;
char vla[i];
strcpy(vla, "hello");
deserializeJson(doc, "{\"hello\":\"world\"}");
JsonVariant variant = doc.as<JsonVariant>();
REQUIRE("world"_s == variant[vla]);
}
SECTION("key is a VLA, const JsonVariant") {
size_t i = 16;
char vla[i];
strcpy(vla, "hello");
deserializeJson(doc, "{\"hello\":\"world\"}");
const JsonVariant variant = doc.as<JsonVariant>();
REQUIRE("world"_s == variant[vla]);
}
#endif
}

View File

@@ -0,0 +1,168 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <stdint.h>
#include <catch.hpp>
#include <limits>
#include "Allocators.hpp"
#include "Literals.hpp"
template <typename T>
void checkReference(T& expected) {
JsonVariant variant = expected;
REQUIRE(expected == variant.as<T&>());
}
template <typename T>
void checkNumericType() {
JsonDocument docMin, docMax;
JsonVariant variantMin = docMin.to<JsonVariant>();
JsonVariant variantMax = docMax.to<JsonVariant>();
T min = std::numeric_limits<T>::min();
T max = std::numeric_limits<T>::max();
variantMin.set(min);
variantMax.set(max);
REQUIRE(min == variantMin.as<T>());
REQUIRE(max == variantMax.as<T>());
}
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
// /!\ Most test were moved to `JsonVariant/set.cpp`
// TODO: move the remaining tests too
SECTION("False") {
variant.set(false);
REQUIRE(variant.as<bool>() == false);
REQUIRE(spy.log() == AllocatorLog{});
}
SECTION("True") {
variant.set(true);
REQUIRE(variant.as<bool>() == true);
REQUIRE(spy.log() == AllocatorLog{});
}
SECTION("Double") {
checkNumericType<double>();
}
SECTION("Float") {
checkNumericType<float>();
}
SECTION("SChar") {
checkNumericType<signed char>();
}
SECTION("SInt") {
checkNumericType<signed int>();
}
SECTION("SLong") {
checkNumericType<signed long>();
}
SECTION("SShort") {
checkNumericType<signed short>();
}
SECTION("UChar") {
checkNumericType<unsigned char>();
}
SECTION("UInt") {
checkNumericType<unsigned int>();
}
SECTION("ULong") {
checkNumericType<unsigned long>();
}
SECTION("UShort") {
checkNumericType<unsigned short>();
}
#if ARDUINOJSON_USE_LONG_LONG
SECTION("LongLong") {
checkNumericType<unsigned long long>();
}
SECTION("ULongLong") {
checkNumericType<unsigned long long>();
}
#endif
SECTION("Int8") {
checkNumericType<int8_t>();
}
SECTION("Uint8") {
checkNumericType<uint8_t>();
}
SECTION("Int16") {
checkNumericType<int16_t>();
}
SECTION("Uint16") {
checkNumericType<uint16_t>();
}
SECTION("Int32") {
checkNumericType<int32_t>();
}
SECTION("Uint32") {
checkNumericType<uint32_t>();
}
#if ARDUINOJSON_USE_LONG_LONG
SECTION("Int64") {
checkNumericType<int64_t>();
}
SECTION("Uint64") {
checkNumericType<uint64_t>();
}
#endif
SECTION("CanStoreObject") {
JsonDocument doc2;
JsonObject object = doc2.to<JsonObject>();
variant.set(object);
REQUIRE(variant.is<JsonObject>());
REQUIRE(variant.as<JsonObject>() == object);
}
}
TEST_CASE("volatile") {
JsonDocument doc;
JsonVariant variant = doc.to<JsonVariant>();
SECTION("volatile bool") { // issue #2029
volatile bool f = true;
variant.set(f);
CHECK(variant.is<bool>() == true);
CHECK(variant.as<bool>() == true);
}
SECTION("volatile int") {
volatile int f = 42;
variant.set(f);
CHECK(variant.is<int>() == true);
CHECK(variant.as<int>() == 42);
}
SECTION("volatile float") { // issue #1557
volatile float f = 3.14f;
variant.set(f);
CHECK(variant.is<float>() == true);
CHECK(variant.as<float>() == 3.14f);
}
SECTION("volatile double") {
volatile double f = 3.14;
variant.set(f);
CHECK(variant.is<double>() == true);
CHECK(variant.as<double>() == 3.14);
}
}

View File

@@ -0,0 +1,80 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
#include "Literals.hpp"
TEST_CASE("Unbound JsonVariant") {
JsonVariant variant;
SECTION("as<T>()") {
CHECK(variant.as<bool>() == false);
CHECK(variant.as<int>() == 0);
CHECK(variant.as<float>() == 0.0f);
CHECK(variant.as<const char*>() == 0);
CHECK(variant.as<std::string>() == "null");
CHECK(variant.as<JsonVariant>().isNull());
CHECK(variant.as<JsonVariantConst>().isNull());
CHECK(variant.as<JsonArray>().isNull());
CHECK(variant.as<JsonArrayConst>().isNull());
CHECK(variant.as<JsonObject>().isNull());
CHECK(variant.as<JsonObjectConst>().isNull());
CHECK(variant.as<JsonString>().isNull());
CHECK(variant.as<MsgPackBinary>().data() == nullptr);
CHECK(variant.as<MsgPackBinary>().size() == 0);
CHECK(variant.as<MsgPackExtension>().data() == nullptr);
CHECK(variant.as<MsgPackExtension>().size() == 0);
}
SECTION("is<T>()") {
CHECK_FALSE(variant.is<bool>());
CHECK_FALSE(variant.is<int>());
CHECK_FALSE(variant.is<float>());
CHECK_FALSE(variant.is<const char*>());
CHECK_FALSE(variant.is<std::string>());
CHECK_FALSE(variant.is<JsonVariant>());
CHECK_FALSE(variant.is<JsonVariantConst>());
CHECK_FALSE(variant.is<JsonArray>());
CHECK_FALSE(variant.is<JsonArrayConst>());
CHECK_FALSE(variant.is<JsonObject>());
CHECK_FALSE(variant.is<JsonObjectConst>());
CHECK_FALSE(variant.is<JsonString>());
}
SECTION("set()") {
CHECK_FALSE(variant.set("42"));
CHECK_FALSE(variant.set(42.0));
CHECK_FALSE(variant.set(42L));
CHECK_FALSE(variant.set(42U));
CHECK_FALSE(variant.set(serialized("42")));
CHECK_FALSE(variant.set(serialized("42"_s)));
CHECK_FALSE(variant.set(true));
CHECK_FALSE(variant.set(MsgPackBinary("hello", 5)));
CHECK_FALSE(variant.set(MsgPackExtension(1, "hello", 5)));
}
SECTION("add()") {
CHECK_FALSE(variant.add("42"));
CHECK_FALSE(variant.add(42.0));
CHECK_FALSE(variant.add(42L));
CHECK_FALSE(variant.add(42U));
CHECK_FALSE(variant.add(serialized("42")));
CHECK_FALSE(variant.add(true));
}
SECTION("operator[]") {
CHECK(variant[0].isNull());
CHECK(variant["key"].isNull());
CHECK_FALSE(variant[0].set(1));
CHECK_FALSE(variant["key"].set(1));
CHECK_FALSE(variant["key"_s].set(1));
}
SECTION("remove()") {
variant.remove(0);
variant.remove("hello");
}
}