Initial commit of Arduino libraries
This commit is contained in:
36
ArduinoJson/extras/tests/CMakeLists.txt
Normal file
36
ArduinoJson/extras/tests/CMakeLists.txt
Normal file
@@ -0,0 +1,36 @@
|
||||
# ArduinoJson - https://arduinojson.org
|
||||
# Copyright © 2014-2025, Benoit BLANCHON
|
||||
# MIT License
|
||||
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
link_libraries(ArduinoJson)
|
||||
|
||||
# Failing builds should only link with ArduinoJson, not catch
|
||||
add_subdirectory(FailingBuilds)
|
||||
|
||||
add_subdirectory(catch)
|
||||
link_libraries(catch)
|
||||
|
||||
include_directories(Helpers)
|
||||
add_subdirectory(Cpp17)
|
||||
add_subdirectory(Cpp20)
|
||||
add_subdirectory(Deprecated)
|
||||
add_subdirectory(IntegrationTests)
|
||||
add_subdirectory(JsonArray)
|
||||
add_subdirectory(JsonArrayConst)
|
||||
add_subdirectory(JsonDeserializer)
|
||||
add_subdirectory(JsonDocument)
|
||||
add_subdirectory(JsonObject)
|
||||
add_subdirectory(JsonObjectConst)
|
||||
add_subdirectory(JsonSerializer)
|
||||
add_subdirectory(JsonVariant)
|
||||
add_subdirectory(JsonVariantConst)
|
||||
add_subdirectory(ResourceManager)
|
||||
add_subdirectory(Misc)
|
||||
add_subdirectory(MixedConfiguration)
|
||||
add_subdirectory(MsgPackDeserializer)
|
||||
add_subdirectory(MsgPackSerializer)
|
||||
add_subdirectory(Numbers)
|
||||
add_subdirectory(TextFormatter)
|
||||
29
ArduinoJson/extras/tests/Cpp17/CMakeLists.txt
Normal file
29
ArduinoJson/extras/tests/Cpp17/CMakeLists.txt
Normal file
@@ -0,0 +1,29 @@
|
||||
# ArduinoJson - https://arduinojson.org
|
||||
# Copyright © 2014-2025, Benoit BLANCHON
|
||||
# MIT License
|
||||
|
||||
if(MSVC_VERSION LESS 1910)
|
||||
return()
|
||||
endif()
|
||||
|
||||
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5)
|
||||
return()
|
||||
endif()
|
||||
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 7)
|
||||
return()
|
||||
endif()
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
add_executable(Cpp17Tests
|
||||
string_view.cpp
|
||||
)
|
||||
|
||||
add_test(Cpp17 Cpp17Tests)
|
||||
|
||||
set_tests_properties(Cpp17
|
||||
PROPERTIES
|
||||
LABELS "Catch"
|
||||
)
|
||||
113
ArduinoJson/extras/tests/Cpp17/string_view.cpp
Normal file
113
ArduinoJson/extras/tests/Cpp17/string_view.cpp
Normal file
@@ -0,0 +1,113 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2025, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
// we expect ArduinoJson.h to include <string_view>
|
||||
// but we don't want it to included accidentally
|
||||
#undef ARDUINO
|
||||
#define ARDUINOJSON_ENABLE_STD_STREAM 0
|
||||
#define ARDUINOJSON_ENABLE_STD_STRING 0
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <catch.hpp>
|
||||
|
||||
#include "Allocators.hpp"
|
||||
#include "Literals.hpp"
|
||||
|
||||
#if !ARDUINOJSON_ENABLE_STRING_VIEW
|
||||
# error ARDUINOJSON_ENABLE_STRING_VIEW must be set to 1
|
||||
#endif
|
||||
|
||||
using ArduinoJson::detail::sizeofArray;
|
||||
|
||||
TEST_CASE("string_view") {
|
||||
SpyingAllocator spy;
|
||||
JsonDocument doc(&spy);
|
||||
JsonVariant variant = doc.to<JsonVariant>();
|
||||
|
||||
SECTION("deserializeJson()") {
|
||||
auto err = deserializeJson(doc, std::string_view("123", 2));
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.as<int>() == 12);
|
||||
}
|
||||
|
||||
SECTION("JsonDocument::set()") {
|
||||
doc.set(std::string_view("123", 2));
|
||||
REQUIRE(doc.as<std::string_view>() == "12");
|
||||
}
|
||||
|
||||
SECTION("JsonDocument::operator[]() const") {
|
||||
doc["ab"] = "Yes";
|
||||
doc["abc"] = "No";
|
||||
REQUIRE(doc[std::string_view("abc", 2)] == "Yes");
|
||||
}
|
||||
|
||||
SECTION("JsonDocument::operator[]()") {
|
||||
doc[std::string_view("abc", 2)] = "Yes";
|
||||
REQUIRE(doc["ab"] == "Yes");
|
||||
}
|
||||
|
||||
SECTION("JsonVariant::operator==()") {
|
||||
variant.set("A");
|
||||
REQUIRE(variant == std::string_view("AX", 1));
|
||||
REQUIRE_FALSE(variant == std::string_view("BX", 1));
|
||||
}
|
||||
|
||||
SECTION("JsonVariant::operator>()") {
|
||||
variant.set("B");
|
||||
REQUIRE(variant > std::string_view("AX", 1));
|
||||
REQUIRE_FALSE(variant > std::string_view("CX", 1));
|
||||
}
|
||||
|
||||
SECTION("JsonVariant::operator<()") {
|
||||
variant.set("B");
|
||||
REQUIRE(variant < std::string_view("CX", 1));
|
||||
REQUIRE_FALSE(variant < std::string_view("AX", 1));
|
||||
}
|
||||
|
||||
SECTION("String deduplication") {
|
||||
doc.add(std::string_view("example one", 7));
|
||||
doc.add(std::string_view("example two", 7));
|
||||
doc.add(std::string_view("example\0tree", 12));
|
||||
doc.add(std::string_view("example\0tree and a half", 12));
|
||||
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Allocate(sizeofPool()),
|
||||
Allocate(sizeofString("example")),
|
||||
Allocate(sizeofString("example tree")),
|
||||
});
|
||||
}
|
||||
|
||||
SECTION("as<std::string_view>()") {
|
||||
doc["s"] = "Hello World";
|
||||
doc["i"] = 42;
|
||||
REQUIRE(doc["s"].as<std::string_view>() == std::string_view("Hello World"));
|
||||
REQUIRE(doc["i"].as<std::string_view>() == std::string_view());
|
||||
}
|
||||
|
||||
SECTION("is<std::string_view>()") {
|
||||
doc["s"] = "Hello World";
|
||||
doc["i"] = 42;
|
||||
REQUIRE(doc["s"].is<std::string_view>() == true);
|
||||
REQUIRE(doc["i"].is<std::string_view>() == false);
|
||||
}
|
||||
|
||||
SECTION("String containing NUL") {
|
||||
doc.set("hello\0world"_s);
|
||||
REQUIRE(doc.as<std::string_view>().size() == 11);
|
||||
REQUIRE(doc.as<std::string_view>() == std::string_view("hello\0world", 11));
|
||||
}
|
||||
}
|
||||
|
||||
using ArduinoJson::detail::adaptString;
|
||||
|
||||
TEST_CASE("StringViewAdapter") {
|
||||
std::string_view str("bravoXXX", 5);
|
||||
auto adapter = adaptString(str);
|
||||
|
||||
CHECK(stringCompare(adapter, adaptString("alpha", 5)) > 0);
|
||||
CHECK(stringCompare(adapter, adaptString("bravo", 5)) == 0);
|
||||
CHECK(stringCompare(adapter, adaptString("charlie", 7)) < 0);
|
||||
|
||||
CHECK(adapter.size() == 5);
|
||||
}
|
||||
29
ArduinoJson/extras/tests/Cpp20/CMakeLists.txt
Normal file
29
ArduinoJson/extras/tests/Cpp20/CMakeLists.txt
Normal file
@@ -0,0 +1,29 @@
|
||||
# ArduinoJson - https://arduinojson.org
|
||||
# Copyright © 2014-2025, Benoit BLANCHON
|
||||
# MIT License
|
||||
|
||||
if(MSVC_VERSION LESS 1910)
|
||||
return()
|
||||
endif()
|
||||
|
||||
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 10)
|
||||
return()
|
||||
endif()
|
||||
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 10)
|
||||
return()
|
||||
endif()
|
||||
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
add_executable(Cpp20Tests
|
||||
smoke_test.cpp
|
||||
)
|
||||
|
||||
add_test(Cpp20 Cpp20Tests)
|
||||
|
||||
set_tests_properties(Cpp20
|
||||
PROPERTIES
|
||||
LABELS "Catch"
|
||||
)
|
||||
15
ArduinoJson/extras/tests/Cpp20/smoke_test.cpp
Normal file
15
ArduinoJson/extras/tests/Cpp20/smoke_test.cpp
Normal file
@@ -0,0 +1,15 @@
|
||||
#include <ArduinoJson.h>
|
||||
|
||||
#include <catch.hpp>
|
||||
#include <string>
|
||||
|
||||
TEST_CASE("C++20 smoke test") {
|
||||
JsonDocument doc;
|
||||
|
||||
deserializeJson(doc, "{\"hello\":\"world\"}");
|
||||
REQUIRE(doc["hello"] == "world");
|
||||
|
||||
std::string json;
|
||||
serializeJson(doc, json);
|
||||
REQUIRE(json == "{\"hello\":\"world\"}");
|
||||
}
|
||||
69
ArduinoJson/extras/tests/Deprecated/BasicJsonDocument.cpp
Normal file
69
ArduinoJson/extras/tests/Deprecated/BasicJsonDocument.cpp
Normal file
@@ -0,0 +1,69 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2025, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <catch.hpp>
|
||||
|
||||
#include <string>
|
||||
|
||||
using ArduinoJson::detail::is_base_of;
|
||||
|
||||
static std::string allocatorLog;
|
||||
|
||||
struct CustomAllocator {
|
||||
CustomAllocator() {
|
||||
allocatorLog = "";
|
||||
}
|
||||
|
||||
void* allocate(size_t n) {
|
||||
allocatorLog += "A";
|
||||
return malloc(n);
|
||||
}
|
||||
|
||||
void deallocate(void* p) {
|
||||
free(p);
|
||||
allocatorLog += "D";
|
||||
}
|
||||
|
||||
void* reallocate(void* p, size_t n) {
|
||||
allocatorLog += "R";
|
||||
return realloc(p, n);
|
||||
}
|
||||
};
|
||||
|
||||
TEST_CASE("BasicJsonDocument") {
|
||||
allocatorLog.clear();
|
||||
|
||||
SECTION("is a JsonDocument") {
|
||||
REQUIRE(
|
||||
is_base_of<JsonDocument, BasicJsonDocument<CustomAllocator>>::value ==
|
||||
true);
|
||||
}
|
||||
|
||||
SECTION("deserialize / serialize") {
|
||||
BasicJsonDocument<CustomAllocator> doc(256);
|
||||
deserializeJson(doc, "{\"hello\":\"world\"}");
|
||||
REQUIRE(doc.as<std::string>() == "{\"hello\":\"world\"}");
|
||||
doc.clear();
|
||||
REQUIRE(allocatorLog == "AARARDDD");
|
||||
}
|
||||
|
||||
SECTION("copy") {
|
||||
BasicJsonDocument<CustomAllocator> doc(256);
|
||||
doc["hello"] = "world";
|
||||
auto copy = doc;
|
||||
REQUIRE(copy.as<std::string>() == "{\"hello\":\"world\"}");
|
||||
REQUIRE(allocatorLog == "AA");
|
||||
}
|
||||
|
||||
SECTION("capacity") {
|
||||
BasicJsonDocument<CustomAllocator> doc(256);
|
||||
REQUIRE(doc.capacity() == 256);
|
||||
}
|
||||
|
||||
SECTION("garbageCollect()") {
|
||||
BasicJsonDocument<CustomAllocator> doc(256);
|
||||
doc.garbageCollect();
|
||||
}
|
||||
}
|
||||
35
ArduinoJson/extras/tests/Deprecated/CMakeLists.txt
Normal file
35
ArduinoJson/extras/tests/Deprecated/CMakeLists.txt
Normal file
@@ -0,0 +1,35 @@
|
||||
# ArduinoJson - https://arduinojson.org
|
||||
# Copyright © 2014-2025, Benoit BLANCHON
|
||||
# MIT License
|
||||
|
||||
if(CMAKE_CXX_COMPILER_ID MATCHES "(GNU|Clang)")
|
||||
add_compile_options(
|
||||
-w
|
||||
)
|
||||
endif()
|
||||
|
||||
if(MSVC)
|
||||
add_compile_options(
|
||||
/wd4996
|
||||
)
|
||||
endif()
|
||||
|
||||
add_executable(DeprecatedTests
|
||||
add.cpp
|
||||
BasicJsonDocument.cpp
|
||||
containsKey.cpp
|
||||
createNestedArray.cpp
|
||||
createNestedObject.cpp
|
||||
DynamicJsonDocument.cpp
|
||||
macros.cpp
|
||||
memoryUsage.cpp
|
||||
shallowCopy.cpp
|
||||
StaticJsonDocument.cpp
|
||||
)
|
||||
|
||||
add_test(Deprecated DeprecatedTests)
|
||||
|
||||
set_tests_properties(Deprecated
|
||||
PROPERTIES
|
||||
LABELS "Catch"
|
||||
)
|
||||
37
ArduinoJson/extras/tests/Deprecated/DynamicJsonDocument.cpp
Normal file
37
ArduinoJson/extras/tests/Deprecated/DynamicJsonDocument.cpp
Normal file
@@ -0,0 +1,37 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2025, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <catch.hpp>
|
||||
|
||||
using ArduinoJson::detail::is_base_of;
|
||||
|
||||
TEST_CASE("DynamicJsonDocument") {
|
||||
SECTION("is a JsonDocument") {
|
||||
REQUIRE(is_base_of<JsonDocument, DynamicJsonDocument>::value == true);
|
||||
}
|
||||
|
||||
SECTION("deserialize / serialize") {
|
||||
DynamicJsonDocument doc(256);
|
||||
deserializeJson(doc, "{\"hello\":\"world\"}");
|
||||
REQUIRE(doc.as<std::string>() == "{\"hello\":\"world\"}");
|
||||
}
|
||||
|
||||
SECTION("copy") {
|
||||
DynamicJsonDocument doc(256);
|
||||
doc["hello"] = "world";
|
||||
auto copy = doc;
|
||||
REQUIRE(copy.as<std::string>() == "{\"hello\":\"world\"}");
|
||||
}
|
||||
|
||||
SECTION("capacity") {
|
||||
DynamicJsonDocument doc(256);
|
||||
REQUIRE(doc.capacity() == 256);
|
||||
}
|
||||
|
||||
SECTION("garbageCollect()") {
|
||||
DynamicJsonDocument doc(256);
|
||||
doc.garbageCollect();
|
||||
}
|
||||
}
|
||||
32
ArduinoJson/extras/tests/Deprecated/StaticJsonDocument.cpp
Normal file
32
ArduinoJson/extras/tests/Deprecated/StaticJsonDocument.cpp
Normal file
@@ -0,0 +1,32 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2025, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <catch.hpp>
|
||||
|
||||
using ArduinoJson::detail::is_base_of;
|
||||
|
||||
TEST_CASE("StaticJsonDocument") {
|
||||
SECTION("is a JsonDocument") {
|
||||
REQUIRE(is_base_of<JsonDocument, StaticJsonDocument<256>>::value == true);
|
||||
}
|
||||
|
||||
SECTION("deserialize / serialize") {
|
||||
StaticJsonDocument<256> doc;
|
||||
deserializeJson(doc, "{\"hello\":\"world\"}");
|
||||
REQUIRE(doc.as<std::string>() == "{\"hello\":\"world\"}");
|
||||
}
|
||||
|
||||
SECTION("copy") {
|
||||
StaticJsonDocument<256> doc;
|
||||
doc["hello"] = "world";
|
||||
auto copy = doc;
|
||||
REQUIRE(copy.as<std::string>() == "{\"hello\":\"world\"}");
|
||||
}
|
||||
|
||||
SECTION("capacity") {
|
||||
StaticJsonDocument<256> doc;
|
||||
REQUIRE(doc.capacity() == 256);
|
||||
}
|
||||
}
|
||||
38
ArduinoJson/extras/tests/Deprecated/add.cpp
Normal file
38
ArduinoJson/extras/tests/Deprecated/add.cpp
Normal file
@@ -0,0 +1,38 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2025, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <catch.hpp>
|
||||
|
||||
TEST_CASE("JsonArray::add()") {
|
||||
JsonDocument doc;
|
||||
JsonArray array = doc.to<JsonArray>();
|
||||
array.add().set(42);
|
||||
REQUIRE(doc.as<std::string>() == "[42]");
|
||||
}
|
||||
|
||||
TEST_CASE("JsonDocument::add()") {
|
||||
JsonDocument doc;
|
||||
doc.add().set(42);
|
||||
REQUIRE(doc.as<std::string>() == "[42]");
|
||||
}
|
||||
|
||||
TEST_CASE("ElementProxy::add()") {
|
||||
JsonDocument doc;
|
||||
doc[0].add().set(42);
|
||||
REQUIRE(doc.as<std::string>() == "[[42]]");
|
||||
}
|
||||
|
||||
TEST_CASE("MemberProxy::add()") {
|
||||
JsonDocument doc;
|
||||
doc["x"].add().set(42);
|
||||
REQUIRE(doc.as<std::string>() == "{\"x\":[42]}");
|
||||
}
|
||||
|
||||
TEST_CASE("JsonVariant::add()") {
|
||||
JsonDocument doc;
|
||||
JsonVariant v = doc.add();
|
||||
v.add().set(42);
|
||||
REQUIRE(doc.as<std::string>() == "[[42]]");
|
||||
}
|
||||
246
ArduinoJson/extras/tests/Deprecated/containsKey.cpp
Normal file
246
ArduinoJson/extras/tests/Deprecated/containsKey.cpp
Normal file
@@ -0,0 +1,246 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2025, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <catch.hpp>
|
||||
|
||||
#include "Literals.hpp"
|
||||
|
||||
TEST_CASE("JsonDocument::containsKey()") {
|
||||
JsonDocument doc;
|
||||
|
||||
SECTION("returns true on object") {
|
||||
doc["hello"] = "world";
|
||||
|
||||
REQUIRE(doc.containsKey("hello") == true);
|
||||
}
|
||||
|
||||
SECTION("returns true when value is null") {
|
||||
doc["hello"] = static_cast<const char*>(0);
|
||||
|
||||
REQUIRE(doc.containsKey("hello") == true);
|
||||
}
|
||||
|
||||
SECTION("returns true when key is a std::string") {
|
||||
doc["hello"] = "world";
|
||||
|
||||
REQUIRE(doc.containsKey("hello"_s) == true);
|
||||
}
|
||||
|
||||
SECTION("returns false on object") {
|
||||
doc["world"] = "hello";
|
||||
|
||||
REQUIRE(doc.containsKey("hello") == false);
|
||||
}
|
||||
|
||||
SECTION("returns false on array") {
|
||||
doc.add("hello");
|
||||
|
||||
REQUIRE(doc.containsKey("hello") == false);
|
||||
}
|
||||
|
||||
SECTION("returns false on null") {
|
||||
REQUIRE(doc.containsKey("hello") == false);
|
||||
}
|
||||
|
||||
SECTION("supports JsonVariant") {
|
||||
doc["hello"] = "world";
|
||||
doc["key"] = "hello";
|
||||
|
||||
REQUIRE(doc.containsKey(doc["key"]) == true);
|
||||
REQUIRE(doc.containsKey(doc["foo"]) == false);
|
||||
}
|
||||
|
||||
#ifdef HAS_VARIABLE_LENGTH_ARRAY
|
||||
SECTION("supports VLAs") {
|
||||
size_t i = 16;
|
||||
char vla[i];
|
||||
strcpy(vla, "hello");
|
||||
|
||||
doc["hello"] = "world";
|
||||
|
||||
REQUIRE(doc.containsKey(vla) == true);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST_CASE("MemberProxy::containsKey()") {
|
||||
JsonDocument doc;
|
||||
const auto& mp = doc["hello"];
|
||||
|
||||
SECTION("containsKey(const char*)") {
|
||||
mp["key"] = "value";
|
||||
|
||||
REQUIRE(mp.containsKey("key") == true);
|
||||
REQUIRE(mp.containsKey("key") == true);
|
||||
}
|
||||
|
||||
SECTION("containsKey(std::string)") {
|
||||
mp["key"] = "value";
|
||||
|
||||
REQUIRE(mp.containsKey("key"_s) == true);
|
||||
REQUIRE(mp.containsKey("key"_s) == true);
|
||||
}
|
||||
|
||||
#ifdef HAS_VARIABLE_LENGTH_ARRAY
|
||||
SECTION("supports VLAs") {
|
||||
size_t i = 16;
|
||||
char vla[i];
|
||||
strcpy(vla, "hello");
|
||||
|
||||
mp["hello"] = "world";
|
||||
|
||||
REQUIRE(mp.containsKey(vla) == true);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST_CASE("JsonObject::containsKey()") {
|
||||
JsonDocument doc;
|
||||
JsonObject obj = doc.to<JsonObject>();
|
||||
obj["hello"] = 42;
|
||||
|
||||
SECTION("returns true only if key is present") {
|
||||
REQUIRE(false == obj.containsKey("world"));
|
||||
REQUIRE(true == obj.containsKey("hello"));
|
||||
}
|
||||
|
||||
SECTION("returns false after remove()") {
|
||||
obj.remove("hello");
|
||||
|
||||
REQUIRE(false == obj.containsKey("hello"));
|
||||
}
|
||||
|
||||
#ifdef HAS_VARIABLE_LENGTH_ARRAY
|
||||
SECTION("key is a VLA") {
|
||||
size_t i = 16;
|
||||
char vla[i];
|
||||
strcpy(vla, "hello");
|
||||
|
||||
REQUIRE(true == obj.containsKey(vla));
|
||||
}
|
||||
#endif
|
||||
|
||||
SECTION("key is a JsonVariant") {
|
||||
doc["key"] = "hello";
|
||||
REQUIRE(true == obj.containsKey(obj["key"]));
|
||||
REQUIRE(false == obj.containsKey(obj["hello"]));
|
||||
}
|
||||
|
||||
SECTION("std::string") {
|
||||
REQUIRE(true == obj.containsKey("hello"_s));
|
||||
}
|
||||
|
||||
SECTION("unsigned char[]") {
|
||||
unsigned char key[] = "hello";
|
||||
REQUIRE(true == obj.containsKey(key));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("JsonObjectConst::containsKey()") {
|
||||
JsonDocument doc;
|
||||
doc["hello"] = 42;
|
||||
auto obj = doc.as<JsonObjectConst>();
|
||||
|
||||
SECTION("supports const char*") {
|
||||
REQUIRE(false == obj.containsKey("world"));
|
||||
REQUIRE(true == obj.containsKey("hello"));
|
||||
}
|
||||
|
||||
SECTION("supports std::string") {
|
||||
REQUIRE(false == obj.containsKey("world"_s));
|
||||
REQUIRE(true == obj.containsKey("hello"_s));
|
||||
}
|
||||
|
||||
#ifdef HAS_VARIABLE_LENGTH_ARRAY
|
||||
SECTION("supports VLA") {
|
||||
size_t i = 16;
|
||||
char vla[i];
|
||||
strcpy(vla, "hello");
|
||||
|
||||
REQUIRE(true == obj.containsKey(vla));
|
||||
}
|
||||
#endif
|
||||
|
||||
SECTION("supports JsonVariant") {
|
||||
doc["key"] = "hello";
|
||||
REQUIRE(true == obj.containsKey(obj["key"]));
|
||||
REQUIRE(false == obj.containsKey(obj["hello"]));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("JsonVariant::containsKey()") {
|
||||
JsonDocument doc;
|
||||
JsonVariant var = doc.to<JsonVariant>();
|
||||
|
||||
SECTION("returns false is unbound") {
|
||||
CHECK_FALSE(JsonVariant().containsKey("hello"));
|
||||
}
|
||||
|
||||
SECTION("containsKey(const char*)") {
|
||||
var["hello"] = "world";
|
||||
|
||||
REQUIRE(var.containsKey("hello") == true);
|
||||
REQUIRE(var.containsKey("world") == false);
|
||||
}
|
||||
|
||||
SECTION("containsKey(std::string)") {
|
||||
var["hello"] = "world";
|
||||
|
||||
REQUIRE(var.containsKey("hello"_s) == true);
|
||||
REQUIRE(var.containsKey("world"_s) == false);
|
||||
}
|
||||
|
||||
SECTION("containsKey(JsonVariant)") {
|
||||
var["hello"] = "world";
|
||||
var["key"] = "hello";
|
||||
|
||||
REQUIRE(var.containsKey(doc["key"]) == true);
|
||||
REQUIRE(var.containsKey(doc["foo"]) == false);
|
||||
}
|
||||
|
||||
#ifdef HAS_VARIABLE_LENGTH_ARRAY
|
||||
SECTION("supports VLAs") {
|
||||
size_t i = 16;
|
||||
char vla[i];
|
||||
strcpy(vla, "hello");
|
||||
|
||||
var["hello"] = "world";
|
||||
|
||||
REQUIRE(var.containsKey(vla) == true);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST_CASE("JsonVariantConst::containsKey()") {
|
||||
JsonDocument doc;
|
||||
doc["hello"] = "world";
|
||||
JsonVariantConst var = doc.as<JsonVariant>();
|
||||
|
||||
SECTION("support const char*") {
|
||||
REQUIRE(var.containsKey("hello") == true);
|
||||
REQUIRE(var.containsKey("world") == false);
|
||||
}
|
||||
|
||||
SECTION("support std::string") {
|
||||
REQUIRE(var.containsKey("hello"_s) == true);
|
||||
REQUIRE(var.containsKey("world"_s) == false);
|
||||
}
|
||||
|
||||
#ifdef HAS_VARIABLE_LENGTH_ARRAY
|
||||
SECTION("supports VLA") {
|
||||
size_t i = 16;
|
||||
char vla[i];
|
||||
strcpy(vla, "hello");
|
||||
|
||||
REQUIRE(true == var.containsKey(vla));
|
||||
}
|
||||
#endif
|
||||
|
||||
SECTION("support JsonVariant") {
|
||||
doc["key"] = "hello";
|
||||
REQUIRE(var.containsKey(var["key"]) == true);
|
||||
REQUIRE(var.containsKey(var["foo"]) == false);
|
||||
}
|
||||
}
|
||||
113
ArduinoJson/extras/tests/Deprecated/createNestedArray.cpp
Normal file
113
ArduinoJson/extras/tests/Deprecated/createNestedArray.cpp
Normal file
@@ -0,0 +1,113 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2025, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <catch.hpp>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "Literals.hpp"
|
||||
|
||||
TEST_CASE("JsonDocument::createNestedArray()") {
|
||||
JsonDocument doc;
|
||||
|
||||
SECTION("createNestedArray()") {
|
||||
JsonArray array = doc.createNestedArray();
|
||||
array.add(42);
|
||||
REQUIRE(doc.as<std::string>() == "[[42]]");
|
||||
}
|
||||
|
||||
SECTION("createNestedArray(const char*)") {
|
||||
JsonArray array = doc.createNestedArray("key");
|
||||
array.add(42);
|
||||
REQUIRE(doc.as<std::string>() == "{\"key\":[42]}");
|
||||
}
|
||||
|
||||
SECTION("createNestedArray(std::string)") {
|
||||
JsonArray array = doc.createNestedArray("key"_s);
|
||||
array.add(42);
|
||||
REQUIRE(doc.as<std::string>() == "{\"key\":[42]}");
|
||||
}
|
||||
|
||||
#ifdef HAS_VARIABLE_LENGTH_ARRAY
|
||||
SECTION("createNestedArray(VLA)") {
|
||||
size_t i = 16;
|
||||
char vla[i];
|
||||
strcpy(vla, "key");
|
||||
JsonArray array = doc.createNestedArray(vla);
|
||||
array.add(42);
|
||||
REQUIRE(doc.as<std::string>() == "{\"key\":[42]}");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST_CASE("JsonArray::createNestedArray()") {
|
||||
JsonDocument doc;
|
||||
JsonArray array = doc.to<JsonArray>();
|
||||
JsonArray nestedArray = array.createNestedArray();
|
||||
nestedArray.add(42);
|
||||
REQUIRE(doc.as<std::string>() == "[[42]]");
|
||||
}
|
||||
|
||||
TEST_CASE("JsonObject::createNestedArray()") {
|
||||
JsonDocument doc;
|
||||
JsonObject object = doc.to<JsonObject>();
|
||||
|
||||
SECTION("createNestedArray(const char*)") {
|
||||
JsonArray array = object.createNestedArray("key");
|
||||
array.add(42);
|
||||
REQUIRE(doc.as<std::string>() == "{\"key\":[42]}");
|
||||
}
|
||||
|
||||
SECTION("createNestedArray(std::string)") {
|
||||
JsonArray array = object.createNestedArray("key"_s);
|
||||
array.add(42);
|
||||
REQUIRE(doc.as<std::string>() == "{\"key\":[42]}");
|
||||
}
|
||||
|
||||
#ifdef HAS_VARIABLE_LENGTH_ARRAY
|
||||
SECTION("createNestedArray(VLA)") {
|
||||
size_t i = 16;
|
||||
char vla[i];
|
||||
strcpy(vla, "key");
|
||||
JsonArray array = object.createNestedArray(vla);
|
||||
array.add(42);
|
||||
REQUIRE(doc.as<std::string>() == "{\"key\":[42]}");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST_CASE("JsonVariant::createNestedArray()") {
|
||||
JsonDocument doc;
|
||||
JsonVariant variant = doc.to<JsonVariant>();
|
||||
|
||||
SECTION("createNestedArray()") {
|
||||
JsonArray array = variant.createNestedArray();
|
||||
array.add(42);
|
||||
REQUIRE(doc.as<std::string>() == "[[42]]");
|
||||
}
|
||||
|
||||
SECTION("createNestedArray(const char*)") {
|
||||
JsonArray array = variant.createNestedArray("key");
|
||||
array.add(42);
|
||||
REQUIRE(doc.as<std::string>() == "{\"key\":[42]}");
|
||||
}
|
||||
|
||||
SECTION("createNestedArray(std::string)") {
|
||||
JsonArray array = variant.createNestedArray("key"_s);
|
||||
array.add(42);
|
||||
REQUIRE(doc.as<std::string>() == "{\"key\":[42]}");
|
||||
}
|
||||
|
||||
#ifdef HAS_VARIABLE_LENGTH_ARRAY
|
||||
SECTION("createNestedArray(VLA)") {
|
||||
size_t i = 16;
|
||||
char vla[i];
|
||||
strcpy(vla, "key");
|
||||
JsonArray array = variant.createNestedArray(vla);
|
||||
array.add(42);
|
||||
REQUIRE(doc.as<std::string>() == "{\"key\":[42]}");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
113
ArduinoJson/extras/tests/Deprecated/createNestedObject.cpp
Normal file
113
ArduinoJson/extras/tests/Deprecated/createNestedObject.cpp
Normal file
@@ -0,0 +1,113 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2025, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <catch.hpp>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "Literals.hpp"
|
||||
|
||||
TEST_CASE("JsonDocument::createNestedObject()") {
|
||||
JsonDocument doc;
|
||||
|
||||
SECTION("createNestedObject()") {
|
||||
JsonObject object = doc.createNestedObject();
|
||||
object["hello"] = "world";
|
||||
REQUIRE(doc.as<std::string>() == "[{\"hello\":\"world\"}]");
|
||||
}
|
||||
|
||||
SECTION("createNestedObject(const char*)") {
|
||||
JsonObject object = doc.createNestedObject("key");
|
||||
object["hello"] = "world";
|
||||
REQUIRE(doc.as<std::string>() == "{\"key\":{\"hello\":\"world\"}}");
|
||||
}
|
||||
|
||||
SECTION("createNestedObject(std::string)") {
|
||||
JsonObject object = doc.createNestedObject("key"_s);
|
||||
object["hello"] = "world";
|
||||
REQUIRE(doc.as<std::string>() == "{\"key\":{\"hello\":\"world\"}}");
|
||||
}
|
||||
|
||||
#ifdef HAS_VARIABLE_LENGTH_ARRAY
|
||||
SECTION("createNestedObject(VLA)") {
|
||||
size_t i = 16;
|
||||
char vla[i];
|
||||
strcpy(vla, "key");
|
||||
JsonObject object = doc.createNestedObject(vla);
|
||||
object["hello"] = "world";
|
||||
REQUIRE(doc.as<std::string>() == "{\"key\":{\"hello\":\"world\"}}");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST_CASE("JsonArray::createNestedObject()") {
|
||||
JsonDocument doc;
|
||||
JsonArray array = doc.to<JsonArray>();
|
||||
JsonObject object = array.createNestedObject();
|
||||
object["hello"] = "world";
|
||||
REQUIRE(doc.as<std::string>() == "[{\"hello\":\"world\"}]");
|
||||
}
|
||||
|
||||
TEST_CASE("JsonObject::createNestedObject()") {
|
||||
JsonDocument doc;
|
||||
JsonObject object = doc.to<JsonObject>();
|
||||
|
||||
SECTION("createNestedObject(const char*)") {
|
||||
JsonObject nestedObject = object.createNestedObject("key");
|
||||
nestedObject["hello"] = "world";
|
||||
REQUIRE(doc.as<std::string>() == "{\"key\":{\"hello\":\"world\"}}");
|
||||
}
|
||||
|
||||
SECTION("createNestedObject(std::string)") {
|
||||
JsonObject nestedObject = object.createNestedObject("key"_s);
|
||||
nestedObject["hello"] = "world";
|
||||
REQUIRE(doc.as<std::string>() == "{\"key\":{\"hello\":\"world\"}}");
|
||||
}
|
||||
|
||||
#ifdef HAS_VARIABLE_LENGTH_ARRAY
|
||||
SECTION("createNestedObject(VLA)") {
|
||||
size_t i = 16;
|
||||
char vla[i];
|
||||
strcpy(vla, "key");
|
||||
JsonObject nestedObject = object.createNestedObject(vla);
|
||||
nestedObject["hello"] = "world";
|
||||
REQUIRE(doc.as<std::string>() == "{\"key\":{\"hello\":\"world\"}}");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST_CASE("JsonVariant::createNestedObject()") {
|
||||
JsonDocument doc;
|
||||
JsonVariant variant = doc.to<JsonVariant>();
|
||||
|
||||
SECTION("createNestedObject()") {
|
||||
JsonObject object = variant.createNestedObject();
|
||||
object["hello"] = "world";
|
||||
REQUIRE(doc.as<std::string>() == "[{\"hello\":\"world\"}]");
|
||||
}
|
||||
|
||||
SECTION("createNestedObject(const char*)") {
|
||||
JsonObject object = variant.createNestedObject("key");
|
||||
object["hello"] = "world";
|
||||
REQUIRE(doc.as<std::string>() == "{\"key\":{\"hello\":\"world\"}}");
|
||||
}
|
||||
|
||||
SECTION("createNestedObject(std::string)") {
|
||||
JsonObject object = variant.createNestedObject("key"_s);
|
||||
object["hello"] = "world";
|
||||
REQUIRE(doc.as<std::string>() == "{\"key\":{\"hello\":\"world\"}}");
|
||||
}
|
||||
|
||||
#ifdef HAS_VARIABLE_LENGTH_ARRAY
|
||||
SECTION("createNestedObject(VLA)") {
|
||||
size_t i = 16;
|
||||
char vla[i];
|
||||
strcpy(vla, "key");
|
||||
JsonObject object = variant.createNestedObject(vla);
|
||||
object["hello"] = "world";
|
||||
REQUIRE(doc.as<std::string>() == "{\"key\":{\"hello\":\"world\"}}");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
18
ArduinoJson/extras/tests/Deprecated/macros.cpp
Normal file
18
ArduinoJson/extras/tests/Deprecated/macros.cpp
Normal file
@@ -0,0 +1,18 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2025, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <catch.hpp>
|
||||
|
||||
TEST_CASE("JSON_ARRAY_SIZE") {
|
||||
REQUIRE(JSON_ARRAY_SIZE(10) == ArduinoJson::detail::sizeofArray(10));
|
||||
}
|
||||
|
||||
TEST_CASE("JSON_OBJECT_SIZE") {
|
||||
REQUIRE(JSON_OBJECT_SIZE(10) == ArduinoJson::detail::sizeofObject(10));
|
||||
}
|
||||
|
||||
TEST_CASE("JSON_STRING_SIZE") {
|
||||
REQUIRE(JSON_STRING_SIZE(10) == 11); // issue #2054
|
||||
}
|
||||
51
ArduinoJson/extras/tests/Deprecated/memoryUsage.cpp
Normal file
51
ArduinoJson/extras/tests/Deprecated/memoryUsage.cpp
Normal file
@@ -0,0 +1,51 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2025, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <catch.hpp>
|
||||
|
||||
TEST_CASE("JsonArray::memoryUsage()") {
|
||||
JsonArray array;
|
||||
REQUIRE(array.memoryUsage() == 0);
|
||||
}
|
||||
|
||||
TEST_CASE("JsonArrayConst::memoryUsage()") {
|
||||
JsonArrayConst array;
|
||||
REQUIRE(array.memoryUsage() == 0);
|
||||
}
|
||||
|
||||
TEST_CASE("JsonDocument::memoryUsage()") {
|
||||
JsonDocument doc;
|
||||
REQUIRE(doc.memoryUsage() == 0);
|
||||
}
|
||||
|
||||
TEST_CASE("JsonObject::memoryUsage()") {
|
||||
JsonObject array;
|
||||
REQUIRE(array.memoryUsage() == 0);
|
||||
}
|
||||
|
||||
TEST_CASE("JsonObjectConst::memoryUsage()") {
|
||||
JsonObjectConst array;
|
||||
REQUIRE(array.memoryUsage() == 0);
|
||||
}
|
||||
|
||||
TEST_CASE("JsonVariant::memoryUsage()") {
|
||||
JsonVariant doc;
|
||||
REQUIRE(doc.memoryUsage() == 0);
|
||||
}
|
||||
|
||||
TEST_CASE("JsonVariantConst::memoryUsage()") {
|
||||
JsonVariantConst doc;
|
||||
REQUIRE(doc.memoryUsage() == 0);
|
||||
}
|
||||
|
||||
TEST_CASE("ElementProxy::memoryUsage()") {
|
||||
JsonDocument doc;
|
||||
REQUIRE(doc[0].memoryUsage() == 0);
|
||||
}
|
||||
|
||||
TEST_CASE("MemberProxy::memoryUsage()") {
|
||||
JsonDocument doc;
|
||||
REQUIRE(doc["hello"].memoryUsage() == 0);
|
||||
}
|
||||
14
ArduinoJson/extras/tests/Deprecated/shallowCopy.cpp
Normal file
14
ArduinoJson/extras/tests/Deprecated/shallowCopy.cpp
Normal file
@@ -0,0 +1,14 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2025, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <catch.hpp>
|
||||
|
||||
TEST_CASE("shallowCopy()") {
|
||||
JsonDocument doc1, doc2;
|
||||
doc1["b"] = "c";
|
||||
doc2["a"].shallowCopy(doc1);
|
||||
|
||||
REQUIRE(doc2.as<std::string>() == "{\"a\":{\"b\":\"c\"}}");
|
||||
}
|
||||
32
ArduinoJson/extras/tests/FailingBuilds/CMakeLists.txt
Normal file
32
ArduinoJson/extras/tests/FailingBuilds/CMakeLists.txt
Normal file
@@ -0,0 +1,32 @@
|
||||
# ArduinoJson - https://arduinojson.org
|
||||
# Copyright © 2014-2025, Benoit BLANCHON
|
||||
# MIT License
|
||||
|
||||
macro(add_failing_build source_file)
|
||||
get_filename_component(target ${source_file} NAME_WE)
|
||||
|
||||
add_executable(${target} ${source_file})
|
||||
|
||||
set_target_properties(${target}
|
||||
PROPERTIES
|
||||
EXCLUDE_FROM_ALL TRUE
|
||||
EXCLUDE_FROM_DEFAULT_BUILD TRUE
|
||||
)
|
||||
add_test(
|
||||
NAME ${target}
|
||||
COMMAND ${CMAKE_COMMAND} --build . --target ${target} --config $<CONFIGURATION>
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||
)
|
||||
set_tests_properties(${target}
|
||||
PROPERTIES
|
||||
WILL_FAIL TRUE
|
||||
LABELS "WillFail"
|
||||
)
|
||||
endmacro()
|
||||
|
||||
add_failing_build(Issue978.cpp)
|
||||
add_failing_build(read_long_long.cpp)
|
||||
add_failing_build(write_long_long.cpp)
|
||||
add_failing_build(variant_as_char.cpp)
|
||||
add_failing_build(assign_char.cpp)
|
||||
add_failing_build(deserialize_object.cpp)
|
||||
13
ArduinoJson/extras/tests/FailingBuilds/Issue978.cpp
Normal file
13
ArduinoJson/extras/tests/FailingBuilds/Issue978.cpp
Normal file
@@ -0,0 +1,13 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2025, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
|
||||
struct Stream {};
|
||||
|
||||
int main() {
|
||||
Stream* stream = 0;
|
||||
JsonDocument doc;
|
||||
deserializeJson(doc, stream);
|
||||
}
|
||||
12
ArduinoJson/extras/tests/FailingBuilds/assign_char.cpp
Normal file
12
ArduinoJson/extras/tests/FailingBuilds/assign_char.cpp
Normal file
@@ -0,0 +1,12 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2025, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
|
||||
// See issue #1498
|
||||
|
||||
int main() {
|
||||
JsonDocument doc;
|
||||
doc["dummy"] = 'A';
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2025, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
|
||||
// See issue #2135
|
||||
|
||||
int main() {
|
||||
JsonObject obj;
|
||||
deserializeJson(obj, "");
|
||||
}
|
||||
16
ArduinoJson/extras/tests/FailingBuilds/read_long_long.cpp
Normal file
16
ArduinoJson/extras/tests/FailingBuilds/read_long_long.cpp
Normal file
@@ -0,0 +1,16 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2025, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#define ARDUINOJSON_USE_LONG_LONG 0
|
||||
#include <ArduinoJson.h>
|
||||
|
||||
#if defined(__SIZEOF_LONG__) && __SIZEOF_LONG__ >= 8
|
||||
# error This test requires sizeof(long) < 8
|
||||
#endif
|
||||
|
||||
ARDUINOJSON_ASSERT_INTEGER_TYPE_IS_SUPPORTED(long long)
|
||||
int main() {
|
||||
JsonDocument doc;
|
||||
doc["dummy"].as<long long>();
|
||||
}
|
||||
12
ArduinoJson/extras/tests/FailingBuilds/variant_as_char.cpp
Normal file
12
ArduinoJson/extras/tests/FailingBuilds/variant_as_char.cpp
Normal file
@@ -0,0 +1,12 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2025, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
|
||||
// See issue #1498
|
||||
|
||||
int main() {
|
||||
JsonDocument doc;
|
||||
doc["dummy"].as<char>();
|
||||
}
|
||||
15
ArduinoJson/extras/tests/FailingBuilds/write_long_long.cpp
Normal file
15
ArduinoJson/extras/tests/FailingBuilds/write_long_long.cpp
Normal file
@@ -0,0 +1,15 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2025, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#define ARDUINOJSON_USE_LONG_LONG 0
|
||||
#include <ArduinoJson.h>
|
||||
|
||||
#if defined(__SIZEOF_LONG__) && __SIZEOF_LONG__ >= 8
|
||||
# error This test requires sizeof(long) < 8
|
||||
#endif
|
||||
|
||||
int main() {
|
||||
JsonDocument doc;
|
||||
doc["dummy"] = static_cast<long long>(42);
|
||||
}
|
||||
288
ArduinoJson/extras/tests/Helpers/Allocators.hpp
Normal file
288
ArduinoJson/extras/tests/Helpers/Allocators.hpp
Normal file
@@ -0,0 +1,288 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2025, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ArduinoJson/Memory/Allocator.hpp>
|
||||
#include <ArduinoJson/Memory/MemoryPool.hpp>
|
||||
#include <ArduinoJson/Memory/StringBuilder.hpp>
|
||||
|
||||
#include <sstream>
|
||||
|
||||
namespace {
|
||||
|
||||
struct FailingAllocator : ArduinoJson::Allocator {
|
||||
static FailingAllocator* instance() {
|
||||
static FailingAllocator allocator;
|
||||
return &allocator;
|
||||
}
|
||||
|
||||
private:
|
||||
FailingAllocator() = default;
|
||||
~FailingAllocator() = default;
|
||||
|
||||
void* allocate(size_t) override {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void deallocate(void*) override {}
|
||||
|
||||
void* reallocate(void*, size_t) override {
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
class AllocatorLogEntry {
|
||||
public:
|
||||
AllocatorLogEntry(std::string s, size_t n = 1) : str_(s), count_(n) {}
|
||||
|
||||
const std::string& str() const {
|
||||
return str_;
|
||||
}
|
||||
|
||||
size_t count() const {
|
||||
return count_;
|
||||
}
|
||||
|
||||
AllocatorLogEntry operator*(size_t n) const {
|
||||
return AllocatorLogEntry(str_, n);
|
||||
}
|
||||
|
||||
private:
|
||||
std::string str_;
|
||||
size_t count_;
|
||||
};
|
||||
|
||||
inline AllocatorLogEntry Allocate(size_t s) {
|
||||
char buffer[32];
|
||||
snprintf(buffer, sizeof(buffer), "allocate(%zu)", s);
|
||||
return AllocatorLogEntry(buffer);
|
||||
}
|
||||
|
||||
inline AllocatorLogEntry AllocateFail(size_t s) {
|
||||
char buffer[32];
|
||||
snprintf(buffer, sizeof(buffer), "allocate(%zu) -> nullptr", s);
|
||||
return AllocatorLogEntry(buffer);
|
||||
}
|
||||
|
||||
inline AllocatorLogEntry Reallocate(size_t s1, size_t s2) {
|
||||
char buffer[32];
|
||||
snprintf(buffer, sizeof(buffer), "reallocate(%zu, %zu)", s1, s2);
|
||||
return AllocatorLogEntry(buffer);
|
||||
}
|
||||
|
||||
inline AllocatorLogEntry ReallocateFail(size_t s1, size_t s2) {
|
||||
char buffer[32];
|
||||
snprintf(buffer, sizeof(buffer), "reallocate(%zu, %zu) -> nullptr", s1, s2);
|
||||
return AllocatorLogEntry(buffer);
|
||||
}
|
||||
|
||||
inline AllocatorLogEntry Deallocate(size_t s) {
|
||||
char buffer[32];
|
||||
snprintf(buffer, sizeof(buffer), "deallocate(%zu)", s);
|
||||
return AllocatorLogEntry(buffer);
|
||||
}
|
||||
|
||||
class AllocatorLog {
|
||||
public:
|
||||
AllocatorLog() = default;
|
||||
AllocatorLog(std::initializer_list<AllocatorLogEntry> list) {
|
||||
for (auto& entry : list)
|
||||
append(entry);
|
||||
}
|
||||
|
||||
void clear() {
|
||||
log_.str("");
|
||||
}
|
||||
|
||||
void append(const AllocatorLogEntry& entry) {
|
||||
for (size_t i = 0; i < entry.count(); i++)
|
||||
log_ << entry.str() << "\n";
|
||||
}
|
||||
|
||||
std::string str() const {
|
||||
auto s = log_.str();
|
||||
if (s.empty())
|
||||
return "(empty)";
|
||||
s.pop_back(); // remove the trailing '\n'
|
||||
return s;
|
||||
}
|
||||
|
||||
bool operator==(const AllocatorLog& other) const {
|
||||
return str() == other.str();
|
||||
}
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& os, const AllocatorLog& log) {
|
||||
os << log.str();
|
||||
return os;
|
||||
}
|
||||
|
||||
private:
|
||||
std::ostringstream log_;
|
||||
};
|
||||
|
||||
class SpyingAllocator : public ArduinoJson::Allocator {
|
||||
public:
|
||||
SpyingAllocator(
|
||||
Allocator* upstream = ArduinoJson::detail::DefaultAllocator::instance())
|
||||
: upstream_(upstream) {}
|
||||
virtual ~SpyingAllocator() {}
|
||||
|
||||
size_t allocatedBytes() const {
|
||||
return allocatedBytes_;
|
||||
}
|
||||
|
||||
void* allocate(size_t n) override {
|
||||
auto block = reinterpret_cast<AllocatedBlock*>(
|
||||
upstream_->allocate(sizeof(AllocatedBlock) + n - 1));
|
||||
if (block) {
|
||||
log_.append(Allocate(n));
|
||||
allocatedBytes_ += n;
|
||||
block->size = n;
|
||||
return block->payload;
|
||||
} else {
|
||||
log_.append(AllocateFail(n));
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void deallocate(void* p) override {
|
||||
auto block = AllocatedBlock::fromPayload(p);
|
||||
allocatedBytes_ -= block->size;
|
||||
log_.append(Deallocate(block ? block->size : 0));
|
||||
upstream_->deallocate(block);
|
||||
}
|
||||
|
||||
void* reallocate(void* p, size_t n) override {
|
||||
auto block = AllocatedBlock::fromPayload(p);
|
||||
auto oldSize = block ? block->size : 0;
|
||||
block = reinterpret_cast<AllocatedBlock*>(
|
||||
upstream_->reallocate(block, sizeof(AllocatedBlock) + n - 1));
|
||||
if (block) {
|
||||
log_.append(Reallocate(oldSize, n));
|
||||
block->size = n;
|
||||
allocatedBytes_ += n - oldSize;
|
||||
return block->payload;
|
||||
} else {
|
||||
log_.append(ReallocateFail(oldSize, n));
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void clearLog() {
|
||||
log_.clear();
|
||||
}
|
||||
|
||||
const AllocatorLog& log() const {
|
||||
return log_;
|
||||
}
|
||||
|
||||
private:
|
||||
struct AllocatedBlock {
|
||||
size_t size;
|
||||
char payload[1];
|
||||
|
||||
static AllocatedBlock* fromPayload(void* p) {
|
||||
if (!p)
|
||||
return nullptr;
|
||||
return reinterpret_cast<AllocatedBlock*>(
|
||||
// Cast to void* to silence "cast increases required alignment of
|
||||
// target type [-Werror=cast-align]"
|
||||
reinterpret_cast<void*>(reinterpret_cast<char*>(p) -
|
||||
offsetof(AllocatedBlock, payload)));
|
||||
}
|
||||
};
|
||||
|
||||
AllocatorLog log_;
|
||||
Allocator* upstream_;
|
||||
size_t allocatedBytes_ = 0;
|
||||
};
|
||||
|
||||
class KillswitchAllocator : public ArduinoJson::Allocator {
|
||||
public:
|
||||
KillswitchAllocator(
|
||||
Allocator* upstream = ArduinoJson::detail::DefaultAllocator::instance())
|
||||
: working_(true), upstream_(upstream) {}
|
||||
virtual ~KillswitchAllocator() {}
|
||||
|
||||
void* allocate(size_t n) override {
|
||||
return working_ ? upstream_->allocate(n) : 0;
|
||||
}
|
||||
|
||||
void deallocate(void* p) override {
|
||||
upstream_->deallocate(p);
|
||||
}
|
||||
|
||||
void* reallocate(void* ptr, size_t n) override {
|
||||
return working_ ? upstream_->reallocate(ptr, n) : 0;
|
||||
}
|
||||
|
||||
// Turn the killswitch on, so all allocation fail
|
||||
void on() {
|
||||
working_ = false;
|
||||
}
|
||||
|
||||
private:
|
||||
bool working_;
|
||||
Allocator* upstream_;
|
||||
};
|
||||
|
||||
class TimebombAllocator : public ArduinoJson::Allocator {
|
||||
public:
|
||||
TimebombAllocator(
|
||||
size_t initialCountdown,
|
||||
Allocator* upstream = ArduinoJson::detail::DefaultAllocator::instance())
|
||||
: countdown_(initialCountdown), upstream_(upstream) {}
|
||||
virtual ~TimebombAllocator() {}
|
||||
|
||||
void* allocate(size_t n) override {
|
||||
if (!countdown_)
|
||||
return nullptr;
|
||||
countdown_--;
|
||||
return upstream_->allocate(n);
|
||||
}
|
||||
|
||||
void deallocate(void* p) override {
|
||||
upstream_->deallocate(p);
|
||||
}
|
||||
|
||||
void* reallocate(void* ptr, size_t n) override {
|
||||
if (!countdown_)
|
||||
return nullptr;
|
||||
countdown_--;
|
||||
return upstream_->reallocate(ptr, n);
|
||||
}
|
||||
|
||||
void setCountdown(size_t value) {
|
||||
countdown_ = value;
|
||||
}
|
||||
|
||||
private:
|
||||
size_t countdown_ = 0;
|
||||
Allocator* upstream_;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
inline size_t sizeofPoolList(size_t n = ARDUINOJSON_INITIAL_POOL_COUNT) {
|
||||
using namespace ArduinoJson::detail;
|
||||
return sizeof(MemoryPool<VariantData>) * n;
|
||||
}
|
||||
|
||||
inline size_t sizeofPool(
|
||||
ArduinoJson::detail::SlotCount n = ARDUINOJSON_POOL_CAPACITY) {
|
||||
using namespace ArduinoJson::detail;
|
||||
return MemoryPool<VariantData>::slotsToBytes(n);
|
||||
}
|
||||
|
||||
inline size_t sizeofStringBuffer(size_t iteration = 1) {
|
||||
// returns 31, 63, 127, 255, etc.
|
||||
auto capacity = ArduinoJson::detail::StringBuilder::initialCapacity;
|
||||
for (size_t i = 1; i < iteration; i++)
|
||||
capacity = capacity * 2 + 1;
|
||||
return ArduinoJson::detail::sizeofString(capacity);
|
||||
}
|
||||
|
||||
inline size_t sizeofString(const char* s) {
|
||||
return ArduinoJson::detail::sizeofString(strlen(s));
|
||||
}
|
||||
13
ArduinoJson/extras/tests/Helpers/Arduino.h
Normal file
13
ArduinoJson/extras/tests/Helpers/Arduino.h
Normal file
@@ -0,0 +1,13 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2025, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "api/Print.h"
|
||||
#include "api/Stream.h"
|
||||
#include "api/String.h"
|
||||
#include "avr/pgmspace.h"
|
||||
|
||||
#define ARDUINO
|
||||
#define ARDUINO_H_INCLUDED 1
|
||||
24
ArduinoJson/extras/tests/Helpers/CustomReader.hpp
Normal file
24
ArduinoJson/extras/tests/Helpers/CustomReader.hpp
Normal file
@@ -0,0 +1,24 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2025, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <sstream>
|
||||
|
||||
class CustomReader {
|
||||
std::stringstream stream_;
|
||||
|
||||
public:
|
||||
CustomReader(const char* input) : stream_(input) {}
|
||||
CustomReader(const CustomReader&) = delete;
|
||||
|
||||
int read() {
|
||||
return stream_.get();
|
||||
}
|
||||
|
||||
size_t readBytes(char* buffer, size_t length) {
|
||||
stream_.read(buffer, static_cast<std::streamsize>(length));
|
||||
return static_cast<size_t>(stream_.gcount());
|
||||
}
|
||||
};
|
||||
12
ArduinoJson/extras/tests/Helpers/Literals.hpp
Normal file
12
ArduinoJson/extras/tests/Helpers/Literals.hpp
Normal file
@@ -0,0 +1,12 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2025, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
// the space before _s is required by GCC 4.8
|
||||
inline std::string operator"" _s(const char* str, size_t len) {
|
||||
return std::string(str, len);
|
||||
}
|
||||
33
ArduinoJson/extras/tests/Helpers/api/Print.h
Normal file
33
ArduinoJson/extras/tests/Helpers/api/Print.h
Normal file
@@ -0,0 +1,33 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2025, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
class Print {
|
||||
public:
|
||||
virtual ~Print() {}
|
||||
|
||||
virtual size_t write(uint8_t) = 0;
|
||||
virtual size_t write(const uint8_t* buffer, size_t size) = 0;
|
||||
|
||||
size_t write(const char* str) {
|
||||
if (!str)
|
||||
return 0;
|
||||
return write(reinterpret_cast<const uint8_t*>(str), strlen(str));
|
||||
}
|
||||
|
||||
size_t write(const char* buffer, size_t size) {
|
||||
return write(reinterpret_cast<const uint8_t*>(buffer), size);
|
||||
}
|
||||
};
|
||||
|
||||
class Printable {
|
||||
public:
|
||||
virtual ~Printable() {}
|
||||
virtual size_t printTo(Print& p) const = 0;
|
||||
};
|
||||
14
ArduinoJson/extras/tests/Helpers/api/Stream.h
Normal file
14
ArduinoJson/extras/tests/Helpers/api/Stream.h
Normal file
@@ -0,0 +1,14 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2025, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#pragma once
|
||||
|
||||
// Reproduces Arduino's Stream class
|
||||
class Stream // : public Print
|
||||
{
|
||||
public:
|
||||
virtual ~Stream() {}
|
||||
virtual int read() = 0;
|
||||
virtual size_t readBytes(char* buffer, size_t length) = 0;
|
||||
};
|
||||
75
ArduinoJson/extras/tests/Helpers/api/String.h
Normal file
75
ArduinoJson/extras/tests/Helpers/api/String.h
Normal file
@@ -0,0 +1,75 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2025, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
// Reproduces Arduino's String class
|
||||
class String {
|
||||
public:
|
||||
String() = default;
|
||||
String(const char* s) {
|
||||
if (s)
|
||||
str_.assign(s);
|
||||
}
|
||||
|
||||
void limitCapacityTo(size_t maxCapacity) {
|
||||
maxCapacity_ = maxCapacity;
|
||||
}
|
||||
|
||||
unsigned char concat(const char* s) {
|
||||
return concat(s, strlen(s));
|
||||
}
|
||||
|
||||
size_t length() const {
|
||||
return str_.size();
|
||||
}
|
||||
|
||||
const char* c_str() const {
|
||||
return str_.c_str();
|
||||
}
|
||||
|
||||
bool operator==(const char* s) const {
|
||||
return str_ == s;
|
||||
}
|
||||
|
||||
String& operator=(const char* s) {
|
||||
if (s)
|
||||
str_.assign(s);
|
||||
else
|
||||
str_.clear();
|
||||
return *this;
|
||||
}
|
||||
|
||||
char operator[](unsigned int index) const {
|
||||
if (index >= str_.size())
|
||||
return 0;
|
||||
return str_[index];
|
||||
}
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& lhs, const ::String& rhs) {
|
||||
lhs << rhs.str_;
|
||||
return lhs;
|
||||
}
|
||||
|
||||
protected:
|
||||
// This function is protected in most Arduino cores
|
||||
unsigned char concat(const char* s, size_t n) {
|
||||
if (str_.size() + n > maxCapacity_)
|
||||
return 0;
|
||||
str_.append(s, n);
|
||||
return 1;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string str_;
|
||||
size_t maxCapacity_ = 1024;
|
||||
};
|
||||
|
||||
class StringSumHelper : public ::String {};
|
||||
|
||||
inline bool operator==(const std::string& lhs, const ::String& rhs) {
|
||||
return lhs == rhs.c_str();
|
||||
}
|
||||
31
ArduinoJson/extras/tests/Helpers/avr/pgmspace.h
Normal file
31
ArduinoJson/extras/tests/Helpers/avr/pgmspace.h
Normal file
@@ -0,0 +1,31 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2025, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h> // uint8_t
|
||||
|
||||
#define PROGMEM
|
||||
|
||||
class __FlashStringHelper;
|
||||
|
||||
inline const void* convertPtrToFlash(const void* s) {
|
||||
return reinterpret_cast<const char*>(s) + 42;
|
||||
}
|
||||
|
||||
inline const void* convertFlashToPtr(const void* s) {
|
||||
return reinterpret_cast<const char*>(s) - 42;
|
||||
}
|
||||
|
||||
#define PSTR(X) reinterpret_cast<const char*>(convertPtrToFlash(X))
|
||||
#define F(X) reinterpret_cast<const __FlashStringHelper*>(PSTR(X))
|
||||
|
||||
inline uint8_t pgm_read_byte(const void* p) {
|
||||
return *reinterpret_cast<const uint8_t*>(convertFlashToPtr(p));
|
||||
}
|
||||
|
||||
#define ARDUINOJSON_DEFINE_PROGMEM_ARRAY(type, name, ...) \
|
||||
static type const ARDUINOJSON_CONCAT2(name, _progmem)[] = __VA_ARGS__; \
|
||||
static type const* name = reinterpret_cast<type const*>( \
|
||||
convertPtrToFlash(ARDUINOJSON_CONCAT2(name, _progmem)));
|
||||
24
ArduinoJson/extras/tests/IntegrationTests/CMakeLists.txt
Normal file
24
ArduinoJson/extras/tests/IntegrationTests/CMakeLists.txt
Normal file
@@ -0,0 +1,24 @@
|
||||
# ArduinoJson - https://arduinojson.org
|
||||
# Copyright © 2014-2025, Benoit BLANCHON
|
||||
# MIT License
|
||||
|
||||
add_executable(IntegrationTests
|
||||
gbathree.cpp
|
||||
issue772.cpp
|
||||
round_trip.cpp
|
||||
openweathermap.cpp
|
||||
)
|
||||
|
||||
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 6)
|
||||
target_compile_options(IntegrationTests
|
||||
PUBLIC
|
||||
-fsingle-precision-constant # issue 544
|
||||
)
|
||||
endif()
|
||||
|
||||
add_test(IntegrationTests IntegrationTests)
|
||||
|
||||
set_tests_properties(IntegrationTests
|
||||
PROPERTIES
|
||||
LABELS "Catch"
|
||||
)
|
||||
210
ArduinoJson/extras/tests/IntegrationTests/gbathree.cpp
Normal file
210
ArduinoJson/extras/tests/IntegrationTests/gbathree.cpp
Normal file
@@ -0,0 +1,210 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2025, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <catch.hpp>
|
||||
|
||||
TEST_CASE("Gbathree") {
|
||||
JsonDocument doc;
|
||||
|
||||
DeserializationError error = deserializeJson(
|
||||
doc,
|
||||
"{\"protocol_name\":\"fluorescence\",\"repeats\":1,\"wait\":0,"
|
||||
"\"averages\":1,\"measurements\":3,\"meas2_light\":15,\"meas1_"
|
||||
"baseline\":0,\"act_light\":20,\"pulsesize\":25,\"pulsedistance\":"
|
||||
"10000,\"actintensity1\":50,\"actintensity2\":255,\"measintensity\":"
|
||||
"255,\"calintensity\":255,\"pulses\":[50,50,50],\"act\":[2,1,2,2],"
|
||||
"\"red\":[2,2,2,2],\"detectors\":[[34,34,34,34],[34,34,34,34],[34,"
|
||||
"34,34,34],[34,34,34,34]],\"alta\":[2,2,2,2],\"altb\":[2,2,2,2],"
|
||||
"\"measlights\":[[15,15,15,15],[15,15,15,15],[15,15,15,15],[15,15,"
|
||||
"15,15]],\"measlights2\":[[15,15,15,15],[15,15,15,15],[15,15,15,15],"
|
||||
"[15,15,15,15]],\"altc\":[2,2,2,2],\"altd\":[2,2,2,2]}");
|
||||
JsonObject root = doc.as<JsonObject>();
|
||||
|
||||
SECTION("Success") {
|
||||
REQUIRE(error == DeserializationError::Ok);
|
||||
}
|
||||
|
||||
SECTION("ProtocolName") {
|
||||
REQUIRE("fluorescence" == root["protocol_name"]);
|
||||
}
|
||||
|
||||
SECTION("Repeats") {
|
||||
REQUIRE(1 == root["repeats"]);
|
||||
}
|
||||
|
||||
SECTION("Wait") {
|
||||
REQUIRE(0 == root["wait"]);
|
||||
}
|
||||
|
||||
SECTION("Measurements") {
|
||||
REQUIRE(3 == root["measurements"]);
|
||||
}
|
||||
|
||||
SECTION("Meas2_Light") {
|
||||
REQUIRE(15 == root["meas2_light"]);
|
||||
}
|
||||
|
||||
SECTION("Meas1_Baseline") {
|
||||
REQUIRE(0 == root["meas1_baseline"]);
|
||||
}
|
||||
|
||||
SECTION("Act_Light") {
|
||||
REQUIRE(20 == root["act_light"]);
|
||||
}
|
||||
|
||||
SECTION("Pulsesize") {
|
||||
REQUIRE(25 == root["pulsesize"]);
|
||||
}
|
||||
|
||||
SECTION("Pulsedistance") {
|
||||
REQUIRE(10000 == root["pulsedistance"]);
|
||||
}
|
||||
|
||||
SECTION("Actintensity1") {
|
||||
REQUIRE(50 == root["actintensity1"]);
|
||||
}
|
||||
|
||||
SECTION("Actintensity2") {
|
||||
REQUIRE(255 == root["actintensity2"]);
|
||||
}
|
||||
|
||||
SECTION("Measintensity") {
|
||||
REQUIRE(255 == root["measintensity"]);
|
||||
}
|
||||
|
||||
SECTION("Calintensity") {
|
||||
REQUIRE(255 == root["calintensity"]);
|
||||
}
|
||||
|
||||
SECTION("Pulses") {
|
||||
// "pulses":[50,50,50]
|
||||
|
||||
JsonArray array = root["pulses"];
|
||||
REQUIRE(array.isNull() == false);
|
||||
|
||||
REQUIRE(3 == array.size());
|
||||
|
||||
for (size_t i = 0; i < 3; i++) {
|
||||
REQUIRE(50 == array[i]);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Act") {
|
||||
// "act":[2,1,2,2]
|
||||
|
||||
JsonArray array = root["act"];
|
||||
REQUIRE(array.isNull() == false);
|
||||
|
||||
REQUIRE(4 == array.size());
|
||||
REQUIRE(2 == array[0]);
|
||||
REQUIRE(1 == array[1]);
|
||||
REQUIRE(2 == array[2]);
|
||||
REQUIRE(2 == array[3]);
|
||||
}
|
||||
|
||||
SECTION("Detectors") {
|
||||
// "detectors":[[34,34,34,34],[34,34,34,34],[34,34,34,34],[34,34,34,34]]
|
||||
|
||||
JsonArray array = root["detectors"];
|
||||
REQUIRE(array.isNull() == false);
|
||||
REQUIRE(4 == array.size());
|
||||
|
||||
for (size_t i = 0; i < 4; i++) {
|
||||
JsonArray nestedArray = array[i];
|
||||
REQUIRE(4 == nestedArray.size());
|
||||
|
||||
for (size_t j = 0; j < 4; j++) {
|
||||
REQUIRE(34 == nestedArray[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Alta") {
|
||||
// alta:[2,2,2,2]
|
||||
|
||||
JsonArray array = root["alta"];
|
||||
REQUIRE(array.isNull() == false);
|
||||
|
||||
REQUIRE(4 == array.size());
|
||||
|
||||
for (size_t i = 0; i < 4; i++) {
|
||||
REQUIRE(2 == array[i]);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Altb") {
|
||||
// altb:[2,2,2,2]
|
||||
|
||||
JsonArray array = root["altb"];
|
||||
REQUIRE(array.isNull() == false);
|
||||
|
||||
REQUIRE(4 == array.size());
|
||||
|
||||
for (size_t i = 0; i < 4; i++) {
|
||||
REQUIRE(2 == array[i]);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Measlights") {
|
||||
// "measlights":[[15,15,15,15],[15,15,15,15],[15,15,15,15],[15,15,15,15]]
|
||||
|
||||
JsonArray array = root["measlights"];
|
||||
REQUIRE(array.isNull() == false);
|
||||
REQUIRE(4 == array.size());
|
||||
|
||||
for (size_t i = 0; i < 4; i++) {
|
||||
JsonArray nestedArray = array[i];
|
||||
|
||||
REQUIRE(4 == nestedArray.size());
|
||||
|
||||
for (size_t j = 0; j < 4; j++) {
|
||||
REQUIRE(15 == nestedArray[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Measlights2") {
|
||||
// "measlights2":[[15,15,15,15],[15,15,15,15],[15,15,15,15],[15,15,15,15]]
|
||||
|
||||
JsonArray array = root["measlights2"];
|
||||
REQUIRE(array.isNull() == false);
|
||||
REQUIRE(4 == array.size());
|
||||
|
||||
for (size_t i = 0; i < 4; i++) {
|
||||
JsonArray nestedArray = array[i];
|
||||
REQUIRE(4 == nestedArray.size());
|
||||
|
||||
for (size_t j = 0; j < 4; j++) {
|
||||
REQUIRE(15 == nestedArray[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Altc") {
|
||||
// altc:[2,2,2,2]
|
||||
|
||||
JsonArray array = root["altc"];
|
||||
REQUIRE(array.isNull() == false);
|
||||
|
||||
REQUIRE(4 == array.size());
|
||||
|
||||
for (size_t i = 0; i < 4; i++) {
|
||||
REQUIRE(2 == array[i]);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Altd") {
|
||||
// altd:[2,2,2,2]
|
||||
|
||||
JsonArray array = root["altd"];
|
||||
REQUIRE(array.isNull() == false);
|
||||
|
||||
REQUIRE(4 == array.size());
|
||||
|
||||
for (size_t i = 0; i < 4; i++) {
|
||||
REQUIRE(2 == array[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
28
ArduinoJson/extras/tests/IntegrationTests/issue772.cpp
Normal file
28
ArduinoJson/extras/tests/IntegrationTests/issue772.cpp
Normal file
@@ -0,0 +1,28 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2025, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <catch.hpp>
|
||||
|
||||
// https://github.com/bblanchon/ArduinoJson/issues/772
|
||||
|
||||
TEST_CASE("Issue772") {
|
||||
JsonDocument doc1;
|
||||
JsonDocument doc2;
|
||||
DeserializationError err;
|
||||
std::string data =
|
||||
"{\"state\":{\"reported\":{\"timestamp\":\"2018-07-02T09:40:12Z\","
|
||||
"\"mac\":\"2C3AE84FC076\",\"firmwareVersion\":\"v0.2.7-5-gf4d4d78\","
|
||||
"\"visibleLight\":261,\"infraRed\":255,\"ultraViolet\":0.02,"
|
||||
"\"Temperature\":26.63,\"Pressure\":101145.7,\"Humidity\":54.79883,"
|
||||
"\"Vbat\":4.171261,\"soilMoisture\":0,\"ActB\":0}}}";
|
||||
err = deserializeJson(doc1, data);
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
|
||||
data = "";
|
||||
serializeMsgPack(doc1, data);
|
||||
err = deserializeMsgPack(doc2, data);
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
}
|
||||
68
ArduinoJson/extras/tests/IntegrationTests/openweathermap.cpp
Normal file
68
ArduinoJson/extras/tests/IntegrationTests/openweathermap.cpp
Normal file
File diff suppressed because one or more lines are too long
82
ArduinoJson/extras/tests/IntegrationTests/round_trip.cpp
Normal file
82
ArduinoJson/extras/tests/IntegrationTests/round_trip.cpp
Normal file
@@ -0,0 +1,82 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2025, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <catch.hpp>
|
||||
|
||||
void check(std::string originalJson) {
|
||||
JsonDocument doc;
|
||||
|
||||
std::string prettyJson;
|
||||
deserializeJson(doc, originalJson);
|
||||
serializeJsonPretty(doc, prettyJson);
|
||||
|
||||
std::string finalJson;
|
||||
deserializeJson(doc, originalJson);
|
||||
serializeJson(doc, finalJson);
|
||||
|
||||
REQUIRE(originalJson == finalJson);
|
||||
}
|
||||
|
||||
TEST_CASE("Round Trip: parse -> prettyPrint -> parse -> print") {
|
||||
SECTION("OpenWeatherMap") {
|
||||
check(
|
||||
"{\"coord\":{\"lon\":145.77,\"lat\":-16.92},\"sys\":{\"type\":1,\"id\":"
|
||||
"8166,\"message\":0.1222,\"country\":\"AU\",\"sunrise\":1414784325,"
|
||||
"\"sunset\":1414830137},\"weather\":[{\"id\":801,\"main\":\"Clouds\","
|
||||
"\"description\":\"few clouds\",\"icon\":\"02n\"}],\"base\":\"cmc "
|
||||
"stations\",\"main\":{\"temp\":296.15,\"pressure\":1014,\"humidity\":"
|
||||
"83,\"temp_min\":296.15,\"temp_max\":296.15},\"wind\":{\"speed\":2.22,"
|
||||
"\"deg\":114.501},\"clouds\":{\"all\":20},\"dt\":1414846800,\"id\":"
|
||||
"2172797,\"name\":\"Cairns\",\"cod\":200}");
|
||||
}
|
||||
|
||||
SECTION("YahooQueryLanguage") {
|
||||
check(
|
||||
"{\"query\":{\"count\":40,\"created\":\"2014-11-01T14:16:49Z\","
|
||||
"\"lang\":\"fr-FR\",\"results\":{\"item\":[{\"title\":\"Burkina army "
|
||||
"backs Zida as interim leader\"},{\"title\":\"British jets intercept "
|
||||
"Russian bombers\"},{\"title\":\"Doubts chip away at nation's most "
|
||||
"trusted agencies\"},{\"title\":\"Cruise ship stuck off Norway, no "
|
||||
"damage\"},{\"title\":\"U.S. military launches 10 air strikes in "
|
||||
"Syria, Iraq\"},{\"title\":\"Blackout hits Bangladesh as line from "
|
||||
"India fails\"},{\"title\":\"Burkina Faso president in Ivory Coast "
|
||||
"after ouster\"},{\"title\":\"Kurds in Turkey rally to back city "
|
||||
"besieged by IS\"},{\"title\":\"A majority of Scots would vote for "
|
||||
"independence now:poll\"},{\"title\":\"Tunisia elections possible "
|
||||
"model for region\"},{\"title\":\"Islamic State kills 85 more members "
|
||||
"of Iraqi tribe\"},{\"title\":\"Iraqi officials:IS extremists line "
|
||||
"up, kill 50\"},{\"title\":\"Burkina Faso army backs presidential "
|
||||
"guard official to lead transition\"},{\"title\":\"Kurdish peshmerga "
|
||||
"arrive with weapons in Syria's Kobani\"},{\"title\":\"Driver sought "
|
||||
"in crash that killed 3 on Halloween\"},{\"title\":\"Ex-Marine arrives "
|
||||
"in US after release from Mexico jail\"},{\"title\":\"UN panel "
|
||||
"scrambling to finish climate report\"},{\"title\":\"Investigators, "
|
||||
"Branson go to spacecraft crash site\"},{\"title\":\"Soldiers vie for "
|
||||
"power after Burkina Faso president quits\"},{\"title\":\"For a man "
|
||||
"without a party, turnout is big test\"},{\"title\":\"'We just had a "
|
||||
"hunch':US marshals nab Eric Frein\"},{\"title\":\"Boko Haram leader "
|
||||
"threatens to kill German hostage\"},{\"title\":\"Nurse free to move "
|
||||
"about as restrictions eased\"},{\"title\":\"Former Burkina president "
|
||||
"Compaore arrives in Ivory Coast:sources\"},{\"title\":\"Libyan port "
|
||||
"rebel leader refuses to hand over oil ports to rival "
|
||||
"group\"},{\"title\":\"Iraqi peshmerga fighters prepare for Syria "
|
||||
"battle\"},{\"title\":\"1 Dem Senate candidate welcoming Obama's "
|
||||
"help\"},{\"title\":\"Bikers cancel party after police recover "
|
||||
"bar\"},{\"title\":\"New question in Texas:Can Davis survive "
|
||||
"defeat?\"},{\"title\":\"Ukraine rebels to hold election, despite "
|
||||
"criticism\"},{\"title\":\"Iraqi officials say Islamic State group "
|
||||
"lines up, kills 50 tribesmen, women in Anbar "
|
||||
"province\"},{\"title\":\"James rebounds, leads Cavaliers past "
|
||||
"Bulls\"},{\"title\":\"UK warns travelers they could be terror "
|
||||
"targets\"},{\"title\":\"Hello Kitty celebrates 40th "
|
||||
"birthday\"},{\"title\":\"A look at people killed during space "
|
||||
"missions\"},{\"title\":\"Nigeria's purported Boko Haram leader says "
|
||||
"has 'married off' girls:AFP\"},{\"title\":\"Mexico orders immediate "
|
||||
"release of Marine veteran\"},{\"title\":\"As election closes in, "
|
||||
"Obama on center stage\"},{\"title\":\"Body of Zambian president "
|
||||
"arrives home\"},{\"title\":\"South Africa arrests 2 Vietnamese for "
|
||||
"poaching\"}]}}}");
|
||||
}
|
||||
}
|
||||
25
ArduinoJson/extras/tests/JsonArray/CMakeLists.txt
Normal file
25
ArduinoJson/extras/tests/JsonArray/CMakeLists.txt
Normal file
@@ -0,0 +1,25 @@
|
||||
# ArduinoJson - https://arduinojson.org
|
||||
# Copyright © 2014-2025, Benoit BLANCHON
|
||||
# MIT License
|
||||
|
||||
add_executable(JsonArrayTests
|
||||
add.cpp
|
||||
clear.cpp
|
||||
compare.cpp
|
||||
copyArray.cpp
|
||||
equals.cpp
|
||||
isNull.cpp
|
||||
iterator.cpp
|
||||
nesting.cpp
|
||||
remove.cpp
|
||||
size.cpp
|
||||
subscript.cpp
|
||||
unbound.cpp
|
||||
)
|
||||
|
||||
add_test(JsonArray JsonArrayTests)
|
||||
|
||||
set_tests_properties(JsonArray
|
||||
PROPERTIES
|
||||
LABELS "Catch"
|
||||
)
|
||||
262
ArduinoJson/extras/tests/JsonArray/add.cpp
Normal file
262
ArduinoJson/extras/tests/JsonArray/add.cpp
Normal file
@@ -0,0 +1,262 @@
|
||||
// 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::sizeofArray;
|
||||
|
||||
TEST_CASE("JsonArray::add(T)") {
|
||||
SpyingAllocator spy;
|
||||
JsonDocument doc(&spy);
|
||||
JsonArray array = doc.to<JsonArray>();
|
||||
|
||||
SECTION("int") {
|
||||
array.add(123);
|
||||
|
||||
REQUIRE(123 == array[0].as<int>());
|
||||
REQUIRE(array[0].is<int>());
|
||||
REQUIRE(array[0].is<double>());
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Allocate(sizeofPool()),
|
||||
});
|
||||
}
|
||||
|
||||
SECTION("double") {
|
||||
array.add(123.45);
|
||||
|
||||
REQUIRE(123.45 == array[0].as<double>());
|
||||
REQUIRE(array[0].is<double>());
|
||||
REQUIRE_FALSE(array[0].is<bool>());
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Allocate(sizeofPool()),
|
||||
});
|
||||
}
|
||||
|
||||
SECTION("bool") {
|
||||
array.add(true);
|
||||
|
||||
REQUIRE(array[0].as<bool>() == true);
|
||||
REQUIRE(array[0].is<bool>());
|
||||
REQUIRE_FALSE(array[0].is<int>());
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Allocate(sizeofPool()),
|
||||
});
|
||||
}
|
||||
|
||||
SECTION("string literal") {
|
||||
array.add("hello");
|
||||
|
||||
REQUIRE(array[0].as<std::string>() == "hello");
|
||||
REQUIRE(array[0].is<const char*>());
|
||||
REQUIRE(array[0].is<int>() == false);
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Allocate(sizeofPool()),
|
||||
});
|
||||
}
|
||||
|
||||
SECTION("std::string") {
|
||||
array.add("hello"_s);
|
||||
|
||||
REQUIRE(array[0].as<std::string>() == "hello");
|
||||
REQUIRE(array[0].is<const char*>() == true);
|
||||
REQUIRE(array[0].is<int>() == false);
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Allocate(sizeofPool()),
|
||||
Allocate(sizeofString("hello")),
|
||||
});
|
||||
}
|
||||
|
||||
SECTION("const char*") {
|
||||
const char* str = "hello";
|
||||
array.add(str);
|
||||
|
||||
REQUIRE(array[0].as<std::string>() == "hello");
|
||||
REQUIRE(array[0].as<const char*>() != str);
|
||||
REQUIRE(array[0].is<const char*>() == true);
|
||||
REQUIRE(array[0].is<int>() == false);
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Allocate(sizeofPool()),
|
||||
Allocate(sizeofString("hello")),
|
||||
});
|
||||
}
|
||||
|
||||
SECTION("serialized(const char*)") {
|
||||
array.add(serialized("{}"));
|
||||
|
||||
REQUIRE(doc.as<std::string>() == "[{}]");
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Allocate(sizeofPool()),
|
||||
Allocate(sizeofString("{}")),
|
||||
});
|
||||
}
|
||||
|
||||
SECTION("serialized(char*)") {
|
||||
array.add(serialized(const_cast<char*>("{}")));
|
||||
|
||||
REQUIRE(doc.as<std::string>() == "[{}]");
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Allocate(sizeofPool()),
|
||||
Allocate(sizeofString("{}")),
|
||||
});
|
||||
}
|
||||
|
||||
SECTION("serialized(std::string)") {
|
||||
array.add(serialized("{}"_s));
|
||||
|
||||
REQUIRE(doc.as<std::string>() == "[{}]");
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Allocate(sizeofPool()),
|
||||
Allocate(sizeofString("{}")),
|
||||
});
|
||||
}
|
||||
|
||||
SECTION("serialized(std::string)") {
|
||||
array.add(serialized("\0XX"_s));
|
||||
|
||||
REQUIRE(doc.as<std::string>() == "[\0XX]"_s);
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Allocate(sizeofPool()),
|
||||
Allocate(sizeofString(" XX")),
|
||||
});
|
||||
}
|
||||
|
||||
#ifdef HAS_VARIABLE_LENGTH_ARRAY
|
||||
SECTION("vla") {
|
||||
size_t i = 16;
|
||||
char vla[i];
|
||||
strcpy(vla, "world");
|
||||
|
||||
array.add(vla);
|
||||
|
||||
strcpy(vla, "hello");
|
||||
REQUIRE(array[0] == "world"_s);
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Allocate(sizeofPool()),
|
||||
Allocate(sizeofString("hello")),
|
||||
});
|
||||
}
|
||||
#endif
|
||||
|
||||
SECTION("nested array") {
|
||||
JsonDocument doc2;
|
||||
JsonArray arr = doc2.to<JsonArray>();
|
||||
|
||||
array.add(arr);
|
||||
|
||||
REQUIRE(arr == array[0].as<JsonArray>());
|
||||
REQUIRE(array[0].is<JsonArray>());
|
||||
REQUIRE_FALSE(array[0].is<int>());
|
||||
}
|
||||
|
||||
SECTION("nested object") {
|
||||
JsonDocument doc2;
|
||||
JsonObject obj = doc2.to<JsonObject>();
|
||||
|
||||
array.add(obj);
|
||||
|
||||
REQUIRE(obj == array[0].as<JsonObject>());
|
||||
REQUIRE(array[0].is<JsonObject>());
|
||||
REQUIRE_FALSE(array[0].is<int>());
|
||||
}
|
||||
|
||||
SECTION("array subscript") {
|
||||
const char* str = "hello";
|
||||
JsonDocument doc2;
|
||||
JsonArray arr = doc2.to<JsonArray>();
|
||||
arr.add(str);
|
||||
|
||||
array.add(arr[0]);
|
||||
|
||||
REQUIRE(str == array[0]);
|
||||
}
|
||||
|
||||
SECTION("object subscript") {
|
||||
const char* str = "hello";
|
||||
JsonDocument doc2;
|
||||
JsonObject obj = doc2.to<JsonObject>();
|
||||
obj["x"] = str;
|
||||
|
||||
array.add(obj["x"]);
|
||||
|
||||
REQUIRE(str == array[0]);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("JsonArray::add<T>()") {
|
||||
JsonDocument doc;
|
||||
JsonArray array = doc.to<JsonArray>();
|
||||
|
||||
SECTION("add<JsonArray>()") {
|
||||
JsonArray nestedArray = array.add<JsonArray>();
|
||||
nestedArray.add(1);
|
||||
nestedArray.add(2);
|
||||
REQUIRE(doc.as<std::string>() == "[[1,2]]");
|
||||
}
|
||||
|
||||
SECTION("add<JsonObject>()") {
|
||||
JsonObject nestedObject = array.add<JsonObject>();
|
||||
nestedObject["a"] = 1;
|
||||
nestedObject["b"] = 2;
|
||||
REQUIRE(doc.as<std::string>() == "[{\"a\":1,\"b\":2}]");
|
||||
}
|
||||
|
||||
SECTION("add<JsonVariant>()") {
|
||||
JsonVariant nestedVariant = array.add<JsonVariant>();
|
||||
nestedVariant.set(42);
|
||||
REQUIRE(doc.as<std::string>() == "[42]");
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("JsonObject::add(JsonObject) ") {
|
||||
JsonDocument doc1;
|
||||
doc1["key1"_s] = "value1"_s;
|
||||
|
||||
TimebombAllocator allocator(10);
|
||||
SpyingAllocator spy(&allocator);
|
||||
JsonDocument doc2(&spy);
|
||||
JsonArray array = doc2.to<JsonArray>();
|
||||
|
||||
SECTION("success") {
|
||||
bool result = array.add(doc1.as<JsonObject>());
|
||||
|
||||
REQUIRE(result == true);
|
||||
REQUIRE(doc2.as<std::string>() == "[{\"key1\":\"value1\"}]");
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Allocate(sizeofPool()),
|
||||
Allocate(sizeofString("key1")),
|
||||
Allocate(sizeofString("value1")),
|
||||
});
|
||||
}
|
||||
|
||||
SECTION("partial failure") { // issue #2081
|
||||
allocator.setCountdown(2);
|
||||
|
||||
bool result = array.add(doc1.as<JsonObject>());
|
||||
|
||||
REQUIRE(result == false);
|
||||
REQUIRE(doc2.as<std::string>() == "[]");
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Allocate(sizeofPool()),
|
||||
Allocate(sizeofString("key1")),
|
||||
AllocateFail(sizeofString("value1")),
|
||||
Deallocate(sizeofString("key1")),
|
||||
});
|
||||
}
|
||||
|
||||
SECTION("complete failure") {
|
||||
allocator.setCountdown(0);
|
||||
|
||||
bool result = array.add(doc1.as<JsonObject>());
|
||||
|
||||
REQUIRE(result == false);
|
||||
REQUIRE(doc2.as<std::string>() == "[]");
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
AllocateFail(sizeofPool()),
|
||||
});
|
||||
}
|
||||
}
|
||||
46
ArduinoJson/extras/tests/JsonArray/clear.cpp
Normal file
46
ArduinoJson/extras/tests/JsonArray/clear.cpp
Normal file
@@ -0,0 +1,46 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2025, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <catch.hpp>
|
||||
|
||||
#include "Allocators.hpp"
|
||||
|
||||
TEST_CASE("JsonArray::clear()") {
|
||||
SECTION("No-op on null JsonArray") {
|
||||
JsonArray array;
|
||||
array.clear();
|
||||
REQUIRE(array.isNull() == true);
|
||||
REQUIRE(array.size() == 0);
|
||||
}
|
||||
|
||||
SECTION("Removes all elements") {
|
||||
JsonDocument doc;
|
||||
JsonArray array = doc.to<JsonArray>();
|
||||
array.add(1);
|
||||
array.add(2);
|
||||
array.clear();
|
||||
REQUIRE(array.size() == 0);
|
||||
REQUIRE(array.isNull() == false);
|
||||
}
|
||||
|
||||
SECTION("Removed elements are recycled") {
|
||||
SpyingAllocator spy;
|
||||
JsonDocument doc(&spy);
|
||||
JsonArray array = doc.to<JsonArray>();
|
||||
|
||||
// fill the pool entirely
|
||||
for (int i = 0; i < ARDUINOJSON_POOL_CAPACITY; i++)
|
||||
array.add(i);
|
||||
|
||||
// clear and fill again
|
||||
array.clear();
|
||||
for (int i = 0; i < ARDUINOJSON_POOL_CAPACITY; i++)
|
||||
array.add(i);
|
||||
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Allocate(sizeofPool()),
|
||||
});
|
||||
}
|
||||
}
|
||||
512
ArduinoJson/extras/tests/JsonArray/compare.cpp
Normal file
512
ArduinoJson/extras/tests/JsonArray/compare.cpp
Normal file
@@ -0,0 +1,512 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2025, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <catch.hpp>
|
||||
|
||||
TEST_CASE("Compare JsonArray with JsonArray") {
|
||||
JsonDocument doc;
|
||||
|
||||
SECTION("Compare with unbound") {
|
||||
JsonArray array = doc.to<JsonArray>();
|
||||
array.add(1);
|
||||
array.add("hello");
|
||||
JsonArray unbound;
|
||||
|
||||
CHECK(array != unbound);
|
||||
CHECK_FALSE(array == unbound);
|
||||
CHECK_FALSE(array <= unbound);
|
||||
CHECK_FALSE(array >= unbound);
|
||||
CHECK_FALSE(array > unbound);
|
||||
CHECK_FALSE(array < unbound);
|
||||
|
||||
CHECK(unbound != array);
|
||||
CHECK_FALSE(unbound == array);
|
||||
CHECK_FALSE(unbound <= array);
|
||||
CHECK_FALSE(unbound >= array);
|
||||
CHECK_FALSE(unbound > array);
|
||||
CHECK_FALSE(unbound < array);
|
||||
}
|
||||
|
||||
SECTION("Compare with self") {
|
||||
JsonArray array = doc.to<JsonArray>();
|
||||
array.add(1);
|
||||
array.add("hello");
|
||||
|
||||
CHECK(array == array);
|
||||
CHECK(array <= array);
|
||||
CHECK(array >= array);
|
||||
CHECK_FALSE(array != array);
|
||||
CHECK_FALSE(array > array);
|
||||
CHECK_FALSE(array < array);
|
||||
}
|
||||
|
||||
SECTION("Compare with identical array") {
|
||||
JsonArray array1 = doc.add<JsonArray>();
|
||||
array1.add(1);
|
||||
array1.add("hello");
|
||||
array1.add<JsonObject>();
|
||||
|
||||
JsonArray array2 = doc.add<JsonArray>();
|
||||
array2.add(1);
|
||||
array2.add("hello");
|
||||
array2.add<JsonObject>();
|
||||
|
||||
CHECK(array1 == array2);
|
||||
CHECK(array1 <= array2);
|
||||
CHECK(array1 >= array2);
|
||||
CHECK_FALSE(array1 != array2);
|
||||
CHECK_FALSE(array1 > array2);
|
||||
CHECK_FALSE(array1 < array2);
|
||||
}
|
||||
|
||||
SECTION("Compare with different array") {
|
||||
JsonArray array1 = doc.add<JsonArray>();
|
||||
array1.add(1);
|
||||
array1.add("hello1");
|
||||
array1.add<JsonObject>();
|
||||
|
||||
JsonArray array2 = doc.add<JsonArray>();
|
||||
array2.add(1);
|
||||
array2.add("hello2");
|
||||
array2.add<JsonObject>();
|
||||
|
||||
CHECK(array1 != array2);
|
||||
CHECK_FALSE(array1 == array2);
|
||||
CHECK_FALSE(array1 > array2);
|
||||
CHECK_FALSE(array1 < array2);
|
||||
CHECK_FALSE(array1 <= array2);
|
||||
CHECK_FALSE(array1 >= array2);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Compare JsonArray with JsonVariant") {
|
||||
JsonDocument doc;
|
||||
|
||||
SECTION("Compare with self") {
|
||||
JsonArray array = doc.to<JsonArray>();
|
||||
array.add(1);
|
||||
array.add("hello");
|
||||
|
||||
JsonVariant variant = array;
|
||||
|
||||
CHECK(array == variant);
|
||||
CHECK(array <= variant);
|
||||
CHECK(array >= variant);
|
||||
CHECK_FALSE(array != variant);
|
||||
CHECK_FALSE(array > variant);
|
||||
CHECK_FALSE(array < variant);
|
||||
|
||||
CHECK(variant == array);
|
||||
CHECK(variant <= array);
|
||||
CHECK(variant >= array);
|
||||
CHECK_FALSE(variant != array);
|
||||
CHECK_FALSE(variant > array);
|
||||
CHECK_FALSE(variant < array);
|
||||
}
|
||||
|
||||
SECTION("Compare with identical array") {
|
||||
JsonArray array = doc.add<JsonArray>();
|
||||
array.add(1);
|
||||
array.add("hello");
|
||||
array.add<JsonObject>();
|
||||
|
||||
JsonVariant variant = doc.add<JsonArray>();
|
||||
variant.add(1);
|
||||
variant.add("hello");
|
||||
variant.add<JsonObject>();
|
||||
|
||||
CHECK(array == variant);
|
||||
CHECK(array <= variant);
|
||||
CHECK(array >= variant);
|
||||
CHECK_FALSE(array != variant);
|
||||
CHECK_FALSE(array > variant);
|
||||
CHECK_FALSE(array < variant);
|
||||
|
||||
CHECK(variant == array);
|
||||
CHECK(variant <= array);
|
||||
CHECK(variant >= array);
|
||||
CHECK_FALSE(variant != array);
|
||||
CHECK_FALSE(variant > array);
|
||||
CHECK_FALSE(variant < array);
|
||||
}
|
||||
|
||||
SECTION("Compare with different array") {
|
||||
JsonArray array = doc.add<JsonArray>();
|
||||
array.add(1);
|
||||
array.add("hello1");
|
||||
array.add<JsonObject>();
|
||||
|
||||
JsonVariant variant = doc.add<JsonArray>();
|
||||
variant.add(1);
|
||||
variant.add("hello2");
|
||||
variant.add<JsonObject>();
|
||||
|
||||
CHECK(array != variant);
|
||||
CHECK_FALSE(array == variant);
|
||||
CHECK_FALSE(array > variant);
|
||||
CHECK_FALSE(array < variant);
|
||||
CHECK_FALSE(array <= variant);
|
||||
CHECK_FALSE(array >= variant);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Compare JsonArray with JsonVariantConst") {
|
||||
JsonDocument doc;
|
||||
|
||||
SECTION("Compare with unbound") {
|
||||
JsonArray array = doc.to<JsonArray>();
|
||||
array.add(1);
|
||||
array.add("hello");
|
||||
JsonVariantConst unbound;
|
||||
|
||||
CHECK(array != unbound);
|
||||
CHECK_FALSE(array == unbound);
|
||||
CHECK_FALSE(array <= unbound);
|
||||
CHECK_FALSE(array >= unbound);
|
||||
CHECK_FALSE(array > unbound);
|
||||
CHECK_FALSE(array < unbound);
|
||||
|
||||
CHECK(unbound != array);
|
||||
CHECK_FALSE(unbound == array);
|
||||
CHECK_FALSE(unbound <= array);
|
||||
CHECK_FALSE(unbound >= array);
|
||||
CHECK_FALSE(unbound > array);
|
||||
CHECK_FALSE(unbound < array);
|
||||
}
|
||||
|
||||
SECTION("Compare with self") {
|
||||
JsonArray array = doc.to<JsonArray>();
|
||||
array.add(1);
|
||||
array.add("hello");
|
||||
|
||||
JsonVariantConst variant = array;
|
||||
|
||||
CHECK(array == variant);
|
||||
CHECK(array <= variant);
|
||||
CHECK(array >= variant);
|
||||
CHECK_FALSE(array != variant);
|
||||
CHECK_FALSE(array > variant);
|
||||
CHECK_FALSE(array < variant);
|
||||
|
||||
CHECK(variant == array);
|
||||
CHECK(variant <= array);
|
||||
CHECK(variant >= array);
|
||||
CHECK_FALSE(variant != array);
|
||||
CHECK_FALSE(variant > array);
|
||||
CHECK_FALSE(variant < array);
|
||||
}
|
||||
|
||||
SECTION("Compare with identical array") {
|
||||
JsonArray array = doc.add<JsonArray>();
|
||||
array.add(1);
|
||||
array.add("hello");
|
||||
array.add<JsonObject>();
|
||||
|
||||
JsonArray array2 = doc.add<JsonArray>();
|
||||
array2.add(1);
|
||||
array2.add("hello");
|
||||
array2.add<JsonObject>();
|
||||
JsonVariantConst variant = array2;
|
||||
|
||||
CHECK(array == variant);
|
||||
CHECK(array <= variant);
|
||||
CHECK(array >= variant);
|
||||
CHECK_FALSE(array != variant);
|
||||
CHECK_FALSE(array > variant);
|
||||
CHECK_FALSE(array < variant);
|
||||
|
||||
CHECK(variant == array);
|
||||
CHECK(variant <= array);
|
||||
CHECK(variant >= array);
|
||||
CHECK_FALSE(variant != array);
|
||||
CHECK_FALSE(variant > array);
|
||||
CHECK_FALSE(variant < array);
|
||||
}
|
||||
|
||||
SECTION("Compare with different array") {
|
||||
JsonArray array = doc.add<JsonArray>();
|
||||
array.add(1);
|
||||
array.add("hello1");
|
||||
array.add<JsonObject>();
|
||||
|
||||
JsonArray array2 = doc.add<JsonArray>();
|
||||
array2.add(1);
|
||||
array2.add("hello2");
|
||||
array2.add<JsonObject>();
|
||||
JsonVariantConst variant = array2;
|
||||
|
||||
CHECK(array != variant);
|
||||
CHECK_FALSE(array == variant);
|
||||
CHECK_FALSE(array > variant);
|
||||
CHECK_FALSE(array < variant);
|
||||
CHECK_FALSE(array <= variant);
|
||||
CHECK_FALSE(array >= variant);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Compare JsonArray with JsonArrayConst") {
|
||||
JsonDocument doc;
|
||||
|
||||
SECTION("Compare with unbound") {
|
||||
JsonArray array = doc.to<JsonArray>();
|
||||
array.add(1);
|
||||
array.add("hello");
|
||||
JsonArrayConst unbound;
|
||||
|
||||
CHECK(array != unbound);
|
||||
CHECK_FALSE(array == unbound);
|
||||
CHECK_FALSE(array <= unbound);
|
||||
CHECK_FALSE(array >= unbound);
|
||||
CHECK_FALSE(array > unbound);
|
||||
CHECK_FALSE(array < unbound);
|
||||
|
||||
CHECK(unbound != array);
|
||||
CHECK_FALSE(unbound == array);
|
||||
CHECK_FALSE(unbound <= array);
|
||||
CHECK_FALSE(unbound >= array);
|
||||
CHECK_FALSE(unbound > array);
|
||||
CHECK_FALSE(unbound < array);
|
||||
}
|
||||
|
||||
SECTION("Compare with self") {
|
||||
JsonArray array = doc.to<JsonArray>();
|
||||
array.add(1);
|
||||
array.add("hello");
|
||||
JsonArrayConst carray = array;
|
||||
|
||||
CHECK(array == carray);
|
||||
CHECK(array <= carray);
|
||||
CHECK(array >= carray);
|
||||
CHECK_FALSE(array != carray);
|
||||
CHECK_FALSE(array > carray);
|
||||
CHECK_FALSE(array < carray);
|
||||
|
||||
CHECK(carray == array);
|
||||
CHECK(carray <= array);
|
||||
CHECK(carray >= array);
|
||||
CHECK_FALSE(carray != array);
|
||||
CHECK_FALSE(carray > array);
|
||||
CHECK_FALSE(carray < array);
|
||||
}
|
||||
|
||||
SECTION("Compare with identical array") {
|
||||
JsonArray array1 = doc.add<JsonArray>();
|
||||
array1.add(1);
|
||||
array1.add("hello");
|
||||
array1.add<JsonObject>();
|
||||
|
||||
JsonArray array2 = doc.add<JsonArray>();
|
||||
array2.add(1);
|
||||
array2.add("hello");
|
||||
array2.add<JsonObject>();
|
||||
JsonArrayConst carray2 = array2;
|
||||
|
||||
CHECK(array1 == carray2);
|
||||
CHECK(array1 <= carray2);
|
||||
CHECK(array1 >= carray2);
|
||||
CHECK_FALSE(array1 != carray2);
|
||||
CHECK_FALSE(array1 > carray2);
|
||||
CHECK_FALSE(array1 < carray2);
|
||||
|
||||
CHECK(carray2 == array1);
|
||||
CHECK(carray2 <= array1);
|
||||
CHECK(carray2 >= array1);
|
||||
CHECK_FALSE(carray2 != array1);
|
||||
CHECK_FALSE(carray2 > array1);
|
||||
CHECK_FALSE(carray2 < array1);
|
||||
}
|
||||
|
||||
SECTION("Compare with different array") {
|
||||
JsonArray array1 = doc.add<JsonArray>();
|
||||
array1.add(1);
|
||||
array1.add("hello1");
|
||||
array1.add<JsonObject>();
|
||||
|
||||
JsonArray array2 = doc.add<JsonArray>();
|
||||
array2.add(1);
|
||||
array2.add("hello2");
|
||||
array2.add<JsonObject>();
|
||||
JsonArrayConst carray2 = array2;
|
||||
|
||||
CHECK(array1 != carray2);
|
||||
CHECK_FALSE(array1 == carray2);
|
||||
CHECK_FALSE(array1 > carray2);
|
||||
CHECK_FALSE(array1 < carray2);
|
||||
CHECK_FALSE(array1 <= carray2);
|
||||
CHECK_FALSE(array1 >= carray2);
|
||||
|
||||
CHECK(carray2 != array1);
|
||||
CHECK_FALSE(carray2 == array1);
|
||||
CHECK_FALSE(carray2 > array1);
|
||||
CHECK_FALSE(carray2 < array1);
|
||||
CHECK_FALSE(carray2 <= array1);
|
||||
CHECK_FALSE(carray2 >= array1);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Compare JsonArrayConst with JsonArrayConst") {
|
||||
JsonDocument doc;
|
||||
|
||||
SECTION("Compare with unbound") {
|
||||
JsonArray array = doc.to<JsonArray>();
|
||||
array.add(1);
|
||||
array.add("hello");
|
||||
|
||||
JsonArrayConst carray = array;
|
||||
JsonArrayConst unbound;
|
||||
|
||||
CHECK(carray != unbound);
|
||||
CHECK_FALSE(carray == unbound);
|
||||
CHECK_FALSE(carray <= unbound);
|
||||
CHECK_FALSE(carray >= unbound);
|
||||
CHECK_FALSE(carray > unbound);
|
||||
CHECK_FALSE(carray < unbound);
|
||||
|
||||
CHECK(unbound != carray);
|
||||
CHECK_FALSE(unbound == carray);
|
||||
CHECK_FALSE(unbound <= carray);
|
||||
CHECK_FALSE(unbound >= carray);
|
||||
CHECK_FALSE(unbound > carray);
|
||||
CHECK_FALSE(unbound < carray);
|
||||
}
|
||||
|
||||
SECTION("Compare with self") {
|
||||
JsonArray array = doc.to<JsonArray>();
|
||||
array.add(1);
|
||||
array.add("hello");
|
||||
JsonArrayConst carray = array;
|
||||
|
||||
CHECK(carray == carray);
|
||||
CHECK(carray <= carray);
|
||||
CHECK(carray >= carray);
|
||||
CHECK_FALSE(carray != carray);
|
||||
CHECK_FALSE(carray > carray);
|
||||
CHECK_FALSE(carray < carray);
|
||||
}
|
||||
|
||||
SECTION("Compare with identical array") {
|
||||
JsonArray array1 = doc.add<JsonArray>();
|
||||
array1.add(1);
|
||||
array1.add("hello");
|
||||
array1.add<JsonObject>();
|
||||
JsonArrayConst carray1 = array1;
|
||||
|
||||
JsonArray array2 = doc.add<JsonArray>();
|
||||
array2.add(1);
|
||||
array2.add("hello");
|
||||
array2.add<JsonObject>();
|
||||
JsonArrayConst carray2 = array2;
|
||||
|
||||
CHECK(carray1 == carray2);
|
||||
CHECK(carray1 <= carray2);
|
||||
CHECK(carray1 >= carray2);
|
||||
CHECK_FALSE(carray1 != carray2);
|
||||
CHECK_FALSE(carray1 > carray2);
|
||||
CHECK_FALSE(carray1 < carray2);
|
||||
}
|
||||
|
||||
SECTION("Compare with different array") {
|
||||
JsonArray array1 = doc.add<JsonArray>();
|
||||
array1.add(1);
|
||||
array1.add("hello1");
|
||||
array1.add<JsonObject>();
|
||||
JsonArrayConst carray1 = array1;
|
||||
|
||||
JsonArray array2 = doc.add<JsonArray>();
|
||||
array2.add(1);
|
||||
array2.add("hello2");
|
||||
array2.add<JsonObject>();
|
||||
JsonArrayConst carray2 = array2;
|
||||
|
||||
CHECK(carray1 != carray2);
|
||||
CHECK_FALSE(carray1 == carray2);
|
||||
CHECK_FALSE(carray1 > carray2);
|
||||
CHECK_FALSE(carray1 < carray2);
|
||||
CHECK_FALSE(carray1 <= carray2);
|
||||
CHECK_FALSE(carray1 >= carray2);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Compare JsonArrayConst with JsonVariant") {
|
||||
JsonDocument doc;
|
||||
|
||||
SECTION("Compare with self") {
|
||||
JsonArray array = doc.to<JsonArray>();
|
||||
array.add(1);
|
||||
array.add("hello");
|
||||
JsonArrayConst carray = array;
|
||||
JsonVariant variant = array;
|
||||
|
||||
CHECK(carray == variant);
|
||||
CHECK(carray <= variant);
|
||||
CHECK(carray >= variant);
|
||||
CHECK_FALSE(carray != variant);
|
||||
CHECK_FALSE(carray > variant);
|
||||
CHECK_FALSE(carray < variant);
|
||||
|
||||
CHECK(variant == carray);
|
||||
CHECK(variant <= carray);
|
||||
CHECK(variant >= carray);
|
||||
CHECK_FALSE(variant != carray);
|
||||
CHECK_FALSE(variant > carray);
|
||||
CHECK_FALSE(variant < carray);
|
||||
}
|
||||
|
||||
SECTION("Compare with identical array") {
|
||||
JsonArray array1 = doc.add<JsonArray>();
|
||||
array1.add(1);
|
||||
array1.add("hello");
|
||||
array1.add<JsonObject>();
|
||||
JsonArrayConst carray1 = array1;
|
||||
|
||||
JsonArray array2 = doc.add<JsonArray>();
|
||||
array2.add(1);
|
||||
array2.add("hello");
|
||||
array2.add<JsonObject>();
|
||||
JsonVariant variant2 = array2;
|
||||
|
||||
CHECK(carray1 == variant2);
|
||||
CHECK(carray1 <= variant2);
|
||||
CHECK(carray1 >= variant2);
|
||||
CHECK_FALSE(carray1 != variant2);
|
||||
CHECK_FALSE(carray1 > variant2);
|
||||
CHECK_FALSE(carray1 < variant2);
|
||||
|
||||
CHECK(variant2 == carray1);
|
||||
CHECK(variant2 <= carray1);
|
||||
CHECK(variant2 >= carray1);
|
||||
CHECK_FALSE(variant2 != carray1);
|
||||
CHECK_FALSE(variant2 > carray1);
|
||||
CHECK_FALSE(variant2 < carray1);
|
||||
}
|
||||
|
||||
SECTION("Compare with different array") {
|
||||
JsonArray array1 = doc.add<JsonArray>();
|
||||
array1.add(1);
|
||||
array1.add("hello1");
|
||||
array1.add<JsonObject>();
|
||||
JsonArrayConst carray1 = array1;
|
||||
|
||||
JsonArray array2 = doc.add<JsonArray>();
|
||||
array2.add(1);
|
||||
array2.add("hello2");
|
||||
array2.add<JsonObject>();
|
||||
JsonVariant variant2 = array2;
|
||||
|
||||
CHECK(carray1 != variant2);
|
||||
CHECK_FALSE(carray1 == variant2);
|
||||
CHECK_FALSE(carray1 > variant2);
|
||||
CHECK_FALSE(carray1 < variant2);
|
||||
CHECK_FALSE(carray1 <= variant2);
|
||||
CHECK_FALSE(carray1 >= variant2);
|
||||
|
||||
CHECK(variant2 != carray1);
|
||||
CHECK_FALSE(variant2 == carray1);
|
||||
CHECK_FALSE(variant2 > carray1);
|
||||
CHECK_FALSE(variant2 < carray1);
|
||||
CHECK_FALSE(variant2 <= carray1);
|
||||
CHECK_FALSE(variant2 >= carray1);
|
||||
}
|
||||
}
|
||||
335
ArduinoJson/extras/tests/JsonArray/copyArray.cpp
Normal file
335
ArduinoJson/extras/tests/JsonArray/copyArray.cpp
Normal file
@@ -0,0 +1,335 @@
|
||||
// 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("copyArray()") {
|
||||
SECTION("int[] -> JsonArray") {
|
||||
JsonDocument doc;
|
||||
JsonArray array = doc.to<JsonArray>();
|
||||
char json[32];
|
||||
int source[] = {1, 2, 3};
|
||||
|
||||
bool ok = copyArray(source, array);
|
||||
CHECK(ok);
|
||||
|
||||
serializeJson(array, json);
|
||||
CHECK("[1,2,3]"_s == json);
|
||||
}
|
||||
|
||||
SECTION("std::string[] -> JsonArray") {
|
||||
JsonDocument doc;
|
||||
JsonArray array = doc.to<JsonArray>();
|
||||
char json[32];
|
||||
std::string source[] = {"a", "b", "c"};
|
||||
|
||||
bool ok = copyArray(source, array);
|
||||
CHECK(ok);
|
||||
|
||||
serializeJson(array, json);
|
||||
CHECK("[\"a\",\"b\",\"c\"]"_s == json);
|
||||
}
|
||||
|
||||
SECTION("const char*[] -> JsonArray") {
|
||||
JsonDocument doc;
|
||||
JsonArray array = doc.to<JsonArray>();
|
||||
char json[32];
|
||||
const char* source[] = {"a", "b", "c"};
|
||||
|
||||
bool ok = copyArray(source, array);
|
||||
CHECK(ok);
|
||||
|
||||
serializeJson(array, json);
|
||||
CHECK("[\"a\",\"b\",\"c\"]"_s == json);
|
||||
}
|
||||
|
||||
SECTION("const char[][] -> JsonArray") {
|
||||
JsonDocument doc;
|
||||
JsonArray array = doc.to<JsonArray>();
|
||||
char json[32];
|
||||
char source[][2] = {"a", "b", "c"};
|
||||
|
||||
bool ok = copyArray(source, array);
|
||||
CHECK(ok);
|
||||
|
||||
serializeJson(array, json);
|
||||
CHECK("[\"a\",\"b\",\"c\"]"_s == json);
|
||||
}
|
||||
|
||||
SECTION("const char[][] -> JsonDocument") {
|
||||
JsonDocument doc;
|
||||
char json[32];
|
||||
char source[][2] = {"a", "b", "c"};
|
||||
|
||||
bool ok = copyArray(source, doc);
|
||||
CHECK(ok);
|
||||
|
||||
serializeJson(doc, json);
|
||||
CHECK("[\"a\",\"b\",\"c\"]"_s == json);
|
||||
}
|
||||
|
||||
SECTION("const char[][] -> MemberProxy") {
|
||||
JsonDocument doc;
|
||||
char json[32];
|
||||
char source[][2] = {"a", "b", "c"};
|
||||
|
||||
bool ok = copyArray(source, doc["data"]);
|
||||
CHECK(ok);
|
||||
|
||||
serializeJson(doc, json);
|
||||
CHECK("{\"data\":[\"a\",\"b\",\"c\"]}"_s == json);
|
||||
}
|
||||
|
||||
SECTION("int[] -> JsonDocument") {
|
||||
JsonDocument doc;
|
||||
char json[32];
|
||||
int source[] = {1, 2, 3};
|
||||
|
||||
bool ok = copyArray(source, doc);
|
||||
CHECK(ok);
|
||||
|
||||
serializeJson(doc, json);
|
||||
CHECK("[1,2,3]"_s == json);
|
||||
}
|
||||
|
||||
SECTION("int[] -> MemberProxy") {
|
||||
JsonDocument doc;
|
||||
char json[32];
|
||||
int source[] = {1, 2, 3};
|
||||
|
||||
bool ok = copyArray(source, doc["data"]);
|
||||
CHECK(ok);
|
||||
|
||||
serializeJson(doc, json);
|
||||
CHECK("{\"data\":[1,2,3]}"_s == json);
|
||||
}
|
||||
|
||||
SECTION("int[] -> JsonArray, but not enough memory") {
|
||||
JsonDocument doc(FailingAllocator::instance());
|
||||
JsonArray array = doc.to<JsonArray>();
|
||||
int source[] = {1, 2, 3};
|
||||
|
||||
bool ok = copyArray(source, array);
|
||||
REQUIRE_FALSE(ok);
|
||||
}
|
||||
|
||||
SECTION("int[][] -> JsonArray") {
|
||||
JsonDocument doc;
|
||||
JsonArray array = doc.to<JsonArray>();
|
||||
char json[32];
|
||||
int source[][3] = {{1, 2, 3}, {4, 5, 6}};
|
||||
|
||||
bool ok = copyArray(source, array);
|
||||
CHECK(ok);
|
||||
|
||||
serializeJson(array, json);
|
||||
CHECK("[[1,2,3],[4,5,6]]"_s == json);
|
||||
}
|
||||
|
||||
SECTION("int[][] -> MemberProxy") {
|
||||
JsonDocument doc;
|
||||
char json[32];
|
||||
int source[][3] = {{1, 2, 3}, {4, 5, 6}};
|
||||
|
||||
bool ok = copyArray(source, doc["data"]);
|
||||
CHECK(ok);
|
||||
|
||||
serializeJson(doc, json);
|
||||
CHECK("{\"data\":[[1,2,3],[4,5,6]]}"_s == json);
|
||||
}
|
||||
|
||||
SECTION("int[][] -> JsonDocument") {
|
||||
JsonDocument doc;
|
||||
char json[32];
|
||||
int source[][3] = {{1, 2, 3}, {4, 5, 6}};
|
||||
|
||||
bool ok = copyArray(source, doc);
|
||||
CHECK(ok);
|
||||
|
||||
serializeJson(doc, json);
|
||||
CHECK("[[1,2,3],[4,5,6]]"_s == json);
|
||||
}
|
||||
|
||||
SECTION("int[][] -> JsonArray, but not enough memory") {
|
||||
JsonDocument doc(FailingAllocator::instance());
|
||||
JsonArray array = doc.to<JsonArray>();
|
||||
int source[][3] = {{1, 2, 3}, {4, 5, 6}};
|
||||
|
||||
bool ok = copyArray(source, array);
|
||||
REQUIRE(ok == false);
|
||||
}
|
||||
|
||||
SECTION("JsonArray -> int[], with more space than needed") {
|
||||
JsonDocument doc;
|
||||
char json[] = "[1,2,3]";
|
||||
DeserializationError err = deserializeJson(doc, json);
|
||||
CHECK(err == DeserializationError::Ok);
|
||||
JsonArray array = doc.as<JsonArray>();
|
||||
|
||||
int destination[4] = {0};
|
||||
size_t result = copyArray(array, destination);
|
||||
|
||||
CHECK(3 == result);
|
||||
CHECK(1 == destination[0]);
|
||||
CHECK(2 == destination[1]);
|
||||
CHECK(3 == destination[2]);
|
||||
CHECK(0 == destination[3]);
|
||||
}
|
||||
|
||||
SECTION("JsonArray -> int[], without enough space") {
|
||||
JsonDocument doc;
|
||||
char json[] = "[1,2,3]";
|
||||
DeserializationError err = deserializeJson(doc, json);
|
||||
CHECK(err == DeserializationError::Ok);
|
||||
JsonArray array = doc.as<JsonArray>();
|
||||
|
||||
int destination[2] = {0};
|
||||
size_t result = copyArray(array, destination);
|
||||
|
||||
CHECK(2 == result);
|
||||
CHECK(1 == destination[0]);
|
||||
CHECK(2 == destination[1]);
|
||||
}
|
||||
|
||||
SECTION("JsonArray -> std::string[]") {
|
||||
JsonDocument doc;
|
||||
char json[] = "[\"a\",\"b\",\"c\"]";
|
||||
DeserializationError err = deserializeJson(doc, json);
|
||||
CHECK(err == DeserializationError::Ok);
|
||||
JsonArray array = doc.as<JsonArray>();
|
||||
|
||||
std::string destination[4];
|
||||
size_t result = copyArray(array, destination);
|
||||
|
||||
CHECK(3 == result);
|
||||
CHECK("a" == destination[0]);
|
||||
CHECK("b" == destination[1]);
|
||||
CHECK("c" == destination[2]);
|
||||
CHECK("" == destination[3]);
|
||||
}
|
||||
|
||||
SECTION("JsonArray -> char[N][]") {
|
||||
JsonDocument doc;
|
||||
char json[] = "[\"a12345\",\"b123456\",\"c1234567\"]";
|
||||
DeserializationError err = deserializeJson(doc, json);
|
||||
CHECK(err == DeserializationError::Ok);
|
||||
JsonArray array = doc.as<JsonArray>();
|
||||
|
||||
char destination[4][8] = {{0}};
|
||||
size_t result = copyArray(array, destination);
|
||||
|
||||
CHECK(3 == result);
|
||||
CHECK("a12345"_s == destination[0]);
|
||||
CHECK("b123456"_s == destination[1]);
|
||||
CHECK("c123456"_s == destination[2]); // truncated
|
||||
CHECK(std::string("") == destination[3]);
|
||||
}
|
||||
|
||||
SECTION("JsonDocument -> int[]") {
|
||||
JsonDocument doc;
|
||||
char json[] = "[1,2,3]";
|
||||
DeserializationError err = deserializeJson(doc, json);
|
||||
CHECK(err == DeserializationError::Ok);
|
||||
|
||||
int destination[4] = {0};
|
||||
size_t result = copyArray(doc, destination);
|
||||
|
||||
CHECK(3 == result);
|
||||
CHECK(1 == destination[0]);
|
||||
CHECK(2 == destination[1]);
|
||||
CHECK(3 == destination[2]);
|
||||
CHECK(0 == destination[3]);
|
||||
}
|
||||
|
||||
SECTION("MemberProxy -> int[]") {
|
||||
JsonDocument doc;
|
||||
char json[] = "{\"data\":[1,2,3]}";
|
||||
DeserializationError err = deserializeJson(doc, json);
|
||||
CHECK(err == DeserializationError::Ok);
|
||||
|
||||
int destination[4] = {0};
|
||||
size_t result = copyArray(doc["data"], destination);
|
||||
|
||||
CHECK(3 == result);
|
||||
CHECK(1 == destination[0]);
|
||||
CHECK(2 == destination[1]);
|
||||
CHECK(3 == destination[2]);
|
||||
CHECK(0 == destination[3]);
|
||||
}
|
||||
|
||||
SECTION("ElementProxy -> int[]") {
|
||||
JsonDocument doc;
|
||||
char json[] = "[[1,2,3]]";
|
||||
DeserializationError err = deserializeJson(doc, json);
|
||||
CHECK(err == DeserializationError::Ok);
|
||||
|
||||
int destination[4] = {0};
|
||||
size_t result = copyArray(doc[0], destination);
|
||||
|
||||
CHECK(3 == result);
|
||||
CHECK(1 == destination[0]);
|
||||
CHECK(2 == destination[1]);
|
||||
CHECK(3 == destination[2]);
|
||||
CHECK(0 == destination[3]);
|
||||
}
|
||||
|
||||
SECTION("JsonArray -> int[][]") {
|
||||
JsonDocument doc;
|
||||
char json[] = "[[1,2],[3],[4]]";
|
||||
|
||||
DeserializationError err = deserializeJson(doc, json);
|
||||
CHECK(err == DeserializationError::Ok);
|
||||
JsonArray array = doc.as<JsonArray>();
|
||||
|
||||
int destination[3][2] = {{0}};
|
||||
copyArray(array, destination);
|
||||
|
||||
CHECK(1 == destination[0][0]);
|
||||
CHECK(2 == destination[0][1]);
|
||||
CHECK(3 == destination[1][0]);
|
||||
CHECK(0 == destination[1][1]);
|
||||
CHECK(4 == destination[2][0]);
|
||||
CHECK(0 == destination[2][1]);
|
||||
}
|
||||
|
||||
SECTION("JsonDocument -> int[][]") {
|
||||
JsonDocument doc;
|
||||
char json[] = "[[1,2],[3],[4]]";
|
||||
|
||||
DeserializationError err = deserializeJson(doc, json);
|
||||
CHECK(err == DeserializationError::Ok);
|
||||
|
||||
int destination[3][2] = {{0}};
|
||||
copyArray(doc, destination);
|
||||
|
||||
CHECK(1 == destination[0][0]);
|
||||
CHECK(2 == destination[0][1]);
|
||||
CHECK(3 == destination[1][0]);
|
||||
CHECK(0 == destination[1][1]);
|
||||
CHECK(4 == destination[2][0]);
|
||||
CHECK(0 == destination[2][1]);
|
||||
}
|
||||
|
||||
SECTION("MemberProxy -> int[][]") {
|
||||
JsonDocument doc;
|
||||
char json[] = "{\"data\":[[1,2],[3],[4]]}";
|
||||
|
||||
DeserializationError err = deserializeJson(doc, json);
|
||||
CHECK(err == DeserializationError::Ok);
|
||||
|
||||
int destination[3][2] = {{0}};
|
||||
copyArray(doc["data"], destination);
|
||||
|
||||
CHECK(1 == destination[0][0]);
|
||||
CHECK(2 == destination[0][1]);
|
||||
CHECK(3 == destination[1][0]);
|
||||
CHECK(0 == destination[1][1]);
|
||||
CHECK(4 == destination[2][0]);
|
||||
CHECK(0 == destination[2][1]);
|
||||
}
|
||||
}
|
||||
63
ArduinoJson/extras/tests/JsonArray/equals.cpp
Normal file
63
ArduinoJson/extras/tests/JsonArray/equals.cpp
Normal file
@@ -0,0 +1,63 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2025, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <catch.hpp>
|
||||
|
||||
TEST_CASE("JsonArray::operator==()") {
|
||||
JsonDocument doc1;
|
||||
JsonArray array1 = doc1.to<JsonArray>();
|
||||
|
||||
JsonDocument doc2;
|
||||
JsonArray array2 = doc2.to<JsonArray>();
|
||||
|
||||
SECTION("should return false when arrays differ") {
|
||||
array1.add("coucou");
|
||||
array2.add(1);
|
||||
|
||||
REQUIRE_FALSE(array1 == array2);
|
||||
}
|
||||
|
||||
SECTION("should return false when LHS has more elements") {
|
||||
array1.add(1);
|
||||
array1.add(2);
|
||||
array2.add(1);
|
||||
|
||||
REQUIRE_FALSE(array1 == array2);
|
||||
}
|
||||
|
||||
SECTION("should return false when RHS has more elements") {
|
||||
array1.add(1);
|
||||
array2.add(1);
|
||||
array2.add(2);
|
||||
|
||||
REQUIRE_FALSE(array1 == array2);
|
||||
}
|
||||
|
||||
SECTION("should return true when arrays equal") {
|
||||
array1.add("coucou");
|
||||
array2.add("coucou");
|
||||
|
||||
REQUIRE(array1 == array2);
|
||||
}
|
||||
|
||||
SECTION("should return false when RHS is null") {
|
||||
JsonArray null;
|
||||
|
||||
REQUIRE_FALSE(array1 == null);
|
||||
}
|
||||
|
||||
SECTION("should return false when LHS is null") {
|
||||
JsonArray null;
|
||||
|
||||
REQUIRE_FALSE(null == array1);
|
||||
}
|
||||
|
||||
SECTION("should return true when both are null") {
|
||||
JsonArray null1;
|
||||
JsonArray null2;
|
||||
|
||||
REQUIRE(null1 == null2);
|
||||
}
|
||||
}
|
||||
32
ArduinoJson/extras/tests/JsonArray/isNull.cpp
Normal file
32
ArduinoJson/extras/tests/JsonArray/isNull.cpp
Normal file
@@ -0,0 +1,32 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2025, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <catch.hpp>
|
||||
|
||||
TEST_CASE("JsonArray::isNull()") {
|
||||
SECTION("returns true") {
|
||||
JsonArray arr;
|
||||
REQUIRE(arr.isNull() == true);
|
||||
}
|
||||
|
||||
SECTION("returns false") {
|
||||
JsonDocument doc;
|
||||
JsonArray arr = doc.to<JsonArray>();
|
||||
REQUIRE(arr.isNull() == false);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("JsonArray::operator bool()") {
|
||||
SECTION("returns false") {
|
||||
JsonArray arr;
|
||||
REQUIRE(static_cast<bool>(arr) == false);
|
||||
}
|
||||
|
||||
SECTION("returns true") {
|
||||
JsonDocument doc;
|
||||
JsonArray arr = doc.to<JsonArray>();
|
||||
REQUIRE(static_cast<bool>(arr) == true);
|
||||
}
|
||||
}
|
||||
34
ArduinoJson/extras/tests/JsonArray/iterator.cpp
Normal file
34
ArduinoJson/extras/tests/JsonArray/iterator.cpp
Normal file
@@ -0,0 +1,34 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2025, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <catch.hpp>
|
||||
|
||||
TEST_CASE("JsonArray::begin()/end()") {
|
||||
SECTION("Non null JsonArray") {
|
||||
JsonDocument doc;
|
||||
JsonArray array = doc.to<JsonArray>();
|
||||
array.add(12);
|
||||
array.add(34);
|
||||
|
||||
auto it = array.begin();
|
||||
auto end = array.end();
|
||||
|
||||
REQUIRE(end != it);
|
||||
REQUIRE(12 == it->as<int>());
|
||||
REQUIRE(12 == static_cast<int>(*it));
|
||||
++it;
|
||||
REQUIRE(end != it);
|
||||
REQUIRE(34 == it->as<int>());
|
||||
REQUIRE(34 == static_cast<int>(*it));
|
||||
++it;
|
||||
REQUIRE(end == it);
|
||||
}
|
||||
|
||||
SECTION("Null JsonArray") {
|
||||
JsonArray array;
|
||||
|
||||
REQUIRE(array.begin() == array.end());
|
||||
}
|
||||
}
|
||||
35
ArduinoJson/extras/tests/JsonArray/nesting.cpp
Normal file
35
ArduinoJson/extras/tests/JsonArray/nesting.cpp
Normal file
@@ -0,0 +1,35 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2025, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <catch.hpp>
|
||||
|
||||
TEST_CASE("JsonArray::nesting()") {
|
||||
JsonDocument doc;
|
||||
JsonArray arr = doc.to<JsonArray>();
|
||||
|
||||
SECTION("return 0 if uninitialized") {
|
||||
JsonArray unitialized;
|
||||
REQUIRE(unitialized.nesting() == 0);
|
||||
}
|
||||
|
||||
SECTION("returns 1 for empty array") {
|
||||
REQUIRE(arr.nesting() == 1);
|
||||
}
|
||||
|
||||
SECTION("returns 1 for flat array") {
|
||||
arr.add("hello");
|
||||
REQUIRE(arr.nesting() == 1);
|
||||
}
|
||||
|
||||
SECTION("returns 2 with nested array") {
|
||||
arr.add<JsonArray>();
|
||||
REQUIRE(arr.nesting() == 2);
|
||||
}
|
||||
|
||||
SECTION("returns 2 with nested object") {
|
||||
arr.add<JsonObject>();
|
||||
REQUIRE(arr.nesting() == 2);
|
||||
}
|
||||
}
|
||||
126
ArduinoJson/extras/tests/JsonArray/remove.cpp
Normal file
126
ArduinoJson/extras/tests/JsonArray/remove.cpp
Normal file
@@ -0,0 +1,126 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2025, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <catch.hpp>
|
||||
|
||||
#include "Allocators.hpp"
|
||||
|
||||
TEST_CASE("JsonArray::remove()") {
|
||||
JsonDocument doc;
|
||||
JsonArray array = doc.to<JsonArray>();
|
||||
array.add(1);
|
||||
array.add(2);
|
||||
array.add(3);
|
||||
|
||||
SECTION("remove first by index") {
|
||||
array.remove(0);
|
||||
|
||||
REQUIRE(2 == array.size());
|
||||
REQUIRE(array[0] == 2);
|
||||
REQUIRE(array[1] == 3);
|
||||
}
|
||||
|
||||
SECTION("remove middle by index") {
|
||||
array.remove(1);
|
||||
|
||||
REQUIRE(2 == array.size());
|
||||
REQUIRE(array[0] == 1);
|
||||
REQUIRE(array[1] == 3);
|
||||
}
|
||||
|
||||
SECTION("remove last by index") {
|
||||
array.remove(2);
|
||||
|
||||
REQUIRE(2 == array.size());
|
||||
REQUIRE(array[0] == 1);
|
||||
REQUIRE(array[1] == 2);
|
||||
}
|
||||
|
||||
SECTION("remove first by iterator") {
|
||||
JsonArray::iterator it = array.begin();
|
||||
array.remove(it);
|
||||
|
||||
REQUIRE(2 == array.size());
|
||||
REQUIRE(array[0] == 2);
|
||||
REQUIRE(array[1] == 3);
|
||||
}
|
||||
|
||||
SECTION("remove middle by iterator") {
|
||||
JsonArray::iterator it = array.begin();
|
||||
++it;
|
||||
array.remove(it);
|
||||
|
||||
REQUIRE(2 == array.size());
|
||||
REQUIRE(array[0] == 1);
|
||||
REQUIRE(array[1] == 3);
|
||||
}
|
||||
|
||||
SECTION("remove last bty iterator") {
|
||||
JsonArray::iterator it = array.begin();
|
||||
++it;
|
||||
++it;
|
||||
array.remove(it);
|
||||
|
||||
REQUIRE(2 == array.size());
|
||||
REQUIRE(array[0] == 1);
|
||||
REQUIRE(array[1] == 2);
|
||||
}
|
||||
|
||||
SECTION("remove end()") {
|
||||
array.remove(array.end());
|
||||
|
||||
REQUIRE(3 == array.size());
|
||||
}
|
||||
|
||||
SECTION("In a loop") {
|
||||
for (JsonArray::iterator it = array.begin(); it != array.end(); ++it) {
|
||||
if (*it == 2)
|
||||
array.remove(it);
|
||||
}
|
||||
|
||||
REQUIRE(2 == array.size());
|
||||
REQUIRE(array[0] == 1);
|
||||
REQUIRE(array[1] == 3);
|
||||
}
|
||||
|
||||
SECTION("remove by index on unbound reference") {
|
||||
JsonArray unboundArray;
|
||||
unboundArray.remove(20);
|
||||
}
|
||||
|
||||
SECTION("remove by iterator on unbound reference") {
|
||||
JsonArray unboundArray;
|
||||
unboundArray.remove(unboundArray.begin());
|
||||
}
|
||||
|
||||
SECTION("use JsonVariant as index") {
|
||||
array.remove(array[3]); // no effect with null variant
|
||||
array.remove(array[0]); // remove element at index 1
|
||||
|
||||
REQUIRE(2 == array.size());
|
||||
REQUIRE(array[0] == 1);
|
||||
REQUIRE(array[1] == 3);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Removed elements are recycled") {
|
||||
SpyingAllocator spy;
|
||||
JsonDocument doc(&spy);
|
||||
JsonArray array = doc.to<JsonArray>();
|
||||
|
||||
// fill the pool entirely
|
||||
for (int i = 0; i < ARDUINOJSON_POOL_CAPACITY; i++)
|
||||
array.add(i);
|
||||
|
||||
// free one slot in the pool
|
||||
array.remove(0);
|
||||
|
||||
// add one element; it should use the free slot
|
||||
array.add(42);
|
||||
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Allocate(sizeofPool()), // only one pool
|
||||
});
|
||||
}
|
||||
31
ArduinoJson/extras/tests/JsonArray/size.cpp
Normal file
31
ArduinoJson/extras/tests/JsonArray/size.cpp
Normal file
@@ -0,0 +1,31 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2025, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <catch.hpp>
|
||||
|
||||
TEST_CASE("JsonArray::size()") {
|
||||
JsonDocument doc;
|
||||
JsonArray array = doc.to<JsonArray>();
|
||||
|
||||
SECTION("returns 0 is empty") {
|
||||
REQUIRE(0U == array.size());
|
||||
}
|
||||
|
||||
SECTION("increases after add()") {
|
||||
array.add("hello");
|
||||
REQUIRE(1U == array.size());
|
||||
|
||||
array.add("world");
|
||||
REQUIRE(2U == array.size());
|
||||
}
|
||||
|
||||
SECTION("remains the same after replacing an element") {
|
||||
array.add("hello");
|
||||
REQUIRE(1U == array.size());
|
||||
|
||||
array[0] = "hello";
|
||||
REQUIRE(1U == array.size());
|
||||
}
|
||||
}
|
||||
171
ArduinoJson/extras/tests/JsonArray/subscript.cpp
Normal file
171
ArduinoJson/extras/tests/JsonArray/subscript.cpp
Normal file
@@ -0,0 +1,171 @@
|
||||
// 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("JsonArray::operator[]") {
|
||||
SpyingAllocator spy;
|
||||
JsonDocument doc(&spy);
|
||||
JsonArray array = doc.to<JsonArray>();
|
||||
|
||||
SECTION("Pad with null") {
|
||||
array[2] = 2;
|
||||
array[5] = 5;
|
||||
REQUIRE(array.size() == 6);
|
||||
REQUIRE(array[0].isNull() == true);
|
||||
REQUIRE(array[1].isNull() == true);
|
||||
REQUIRE(array[2].isNull() == false);
|
||||
REQUIRE(array[3].isNull() == true);
|
||||
REQUIRE(array[4].isNull() == true);
|
||||
REQUIRE(array[5].isNull() == false);
|
||||
REQUIRE(array[2] == 2);
|
||||
REQUIRE(array[5] == 5);
|
||||
}
|
||||
|
||||
SECTION("int") {
|
||||
array[0] = 123;
|
||||
REQUIRE(123 == array[0].as<int>());
|
||||
REQUIRE(true == array[0].is<int>());
|
||||
REQUIRE(false == array[0].is<bool>());
|
||||
}
|
||||
|
||||
#if ARDUINOJSON_USE_LONG_LONG
|
||||
SECTION("long long") {
|
||||
array[0] = 9223372036854775807;
|
||||
REQUIRE(9223372036854775807 == array[0].as<int64_t>());
|
||||
REQUIRE(true == array[0].is<int64_t>());
|
||||
REQUIRE(false == array[0].is<int32_t>());
|
||||
REQUIRE(false == array[0].is<bool>());
|
||||
}
|
||||
#endif
|
||||
|
||||
SECTION("double") {
|
||||
array[0] = 123.45;
|
||||
REQUIRE(123.45 == array[0].as<double>());
|
||||
REQUIRE(true == array[0].is<double>());
|
||||
REQUIRE(false == array[0].is<int>());
|
||||
}
|
||||
|
||||
SECTION("bool") {
|
||||
array[0] = true;
|
||||
REQUIRE(true == array[0].as<bool>());
|
||||
REQUIRE(true == array[0].is<bool>());
|
||||
REQUIRE(false == array[0].is<int>());
|
||||
}
|
||||
|
||||
SECTION("string literal") {
|
||||
array[0] = "hello";
|
||||
|
||||
REQUIRE(array[0].as<std::string>() == "hello");
|
||||
REQUIRE(true == array[0].is<const char*>());
|
||||
REQUIRE(false == array[0].is<int>());
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Allocate(sizeofPool()),
|
||||
Allocate(sizeofString("world")),
|
||||
});
|
||||
}
|
||||
|
||||
SECTION("const char*") {
|
||||
const char* str = "hello";
|
||||
array[0] = str;
|
||||
|
||||
REQUIRE(array[0].as<std::string>() == "hello");
|
||||
REQUIRE(true == array[0].is<const char*>());
|
||||
REQUIRE(false == array[0].is<int>());
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Allocate(sizeofPool()),
|
||||
Allocate(sizeofString("world")),
|
||||
});
|
||||
}
|
||||
|
||||
SECTION("std::string") {
|
||||
array[0] = "hello"_s;
|
||||
|
||||
REQUIRE(array[0].as<std::string>() == "hello");
|
||||
REQUIRE(true == array[0].is<const char*>());
|
||||
REQUIRE(false == array[0].is<int>());
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Allocate(sizeofPool()),
|
||||
Allocate(sizeofString("world")),
|
||||
});
|
||||
}
|
||||
|
||||
#ifdef HAS_VARIABLE_LENGTH_ARRAY
|
||||
SECTION("VLA") {
|
||||
size_t i = 16;
|
||||
char vla[i];
|
||||
strcpy(vla, "world");
|
||||
|
||||
array.add("hello");
|
||||
array[0] = vla;
|
||||
|
||||
REQUIRE(array[0] == "world"_s);
|
||||
}
|
||||
#endif
|
||||
|
||||
SECTION("nested array") {
|
||||
JsonDocument doc2;
|
||||
JsonArray arr2 = doc2.to<JsonArray>();
|
||||
|
||||
array[0] = arr2;
|
||||
|
||||
REQUIRE(arr2 == array[0].as<JsonArray>());
|
||||
REQUIRE(true == array[0].is<JsonArray>());
|
||||
REQUIRE(false == array[0].is<int>());
|
||||
}
|
||||
|
||||
SECTION("nested object") {
|
||||
JsonDocument doc2;
|
||||
JsonObject obj = doc2.to<JsonObject>();
|
||||
|
||||
array[0] = obj;
|
||||
|
||||
REQUIRE(obj == array[0].as<JsonObject>());
|
||||
REQUIRE(true == array[0].is<JsonObject>());
|
||||
REQUIRE(false == array[0].is<int>());
|
||||
}
|
||||
|
||||
SECTION("array subscript") {
|
||||
JsonDocument doc2;
|
||||
JsonArray arr2 = doc2.to<JsonArray>();
|
||||
const char* str = "hello";
|
||||
|
||||
arr2.add(str);
|
||||
|
||||
array[0] = arr2[0];
|
||||
|
||||
REQUIRE(str == array[0]);
|
||||
}
|
||||
|
||||
SECTION("object subscript") {
|
||||
const char* str = "hello";
|
||||
JsonDocument doc2;
|
||||
JsonObject obj = doc2.to<JsonObject>();
|
||||
|
||||
obj["x"] = str;
|
||||
|
||||
array[0] = obj["x"];
|
||||
|
||||
REQUIRE(str == array[0]);
|
||||
}
|
||||
|
||||
SECTION("array[0].to<JsonObject>()") {
|
||||
JsonObject obj = array[0].to<JsonObject>();
|
||||
REQUIRE(obj.isNull() == false);
|
||||
}
|
||||
|
||||
SECTION("Use a JsonVariant as index") {
|
||||
array[0] = 1;
|
||||
array[1] = 2;
|
||||
array[2] = 3;
|
||||
|
||||
REQUIRE(array[array[1]] == 3);
|
||||
REQUIRE(array[array[3]] == nullptr);
|
||||
}
|
||||
}
|
||||
27
ArduinoJson/extras/tests/JsonArray/unbound.cpp
Normal file
27
ArduinoJson/extras/tests/JsonArray/unbound.cpp
Normal file
@@ -0,0 +1,27 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2025, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <catch.hpp>
|
||||
|
||||
using namespace Catch::Matchers;
|
||||
|
||||
TEST_CASE("Unbound JsonArray") {
|
||||
JsonArray array;
|
||||
|
||||
SECTION("SubscriptFails") {
|
||||
REQUIRE(array[0].isNull());
|
||||
}
|
||||
|
||||
SECTION("AddFails") {
|
||||
array.add(1);
|
||||
REQUIRE(0 == array.size());
|
||||
}
|
||||
|
||||
SECTION("PrintToWritesBrackets") {
|
||||
char buffer[32];
|
||||
serializeJson(array, buffer, sizeof(buffer));
|
||||
REQUIRE_THAT(buffer, Equals("null"));
|
||||
}
|
||||
}
|
||||
19
ArduinoJson/extras/tests/JsonArrayConst/CMakeLists.txt
Normal file
19
ArduinoJson/extras/tests/JsonArrayConst/CMakeLists.txt
Normal file
@@ -0,0 +1,19 @@
|
||||
# ArduinoJson - https://arduinojson.org
|
||||
# Copyright © 2014-2025, Benoit BLANCHON
|
||||
# MIT License
|
||||
|
||||
add_executable(JsonArrayConstTests
|
||||
equals.cpp
|
||||
isNull.cpp
|
||||
iterator.cpp
|
||||
nesting.cpp
|
||||
size.cpp
|
||||
subscript.cpp
|
||||
)
|
||||
|
||||
add_test(JsonArrayConst JsonArrayConstTests)
|
||||
|
||||
set_tests_properties(JsonArrayConst
|
||||
PROPERTIES
|
||||
LABELS "Catch"
|
||||
)
|
||||
63
ArduinoJson/extras/tests/JsonArrayConst/equals.cpp
Normal file
63
ArduinoJson/extras/tests/JsonArrayConst/equals.cpp
Normal file
@@ -0,0 +1,63 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2025, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <catch.hpp>
|
||||
|
||||
TEST_CASE("JsonArrayConst::operator==()") {
|
||||
JsonDocument doc1;
|
||||
JsonArrayConst array1 = doc1.to<JsonArray>();
|
||||
|
||||
JsonDocument doc2;
|
||||
JsonArrayConst array2 = doc2.to<JsonArray>();
|
||||
|
||||
SECTION("should return false when arrays differ") {
|
||||
doc1.add("coucou");
|
||||
doc2.add(1);
|
||||
|
||||
REQUIRE_FALSE(array1 == array2);
|
||||
}
|
||||
|
||||
SECTION("should return false when LHS has more elements") {
|
||||
doc1.add(1);
|
||||
doc1.add(2);
|
||||
doc2.add(1);
|
||||
|
||||
REQUIRE_FALSE(array1 == array2);
|
||||
}
|
||||
|
||||
SECTION("should return false when RHS has more elements") {
|
||||
doc1.add(1);
|
||||
doc2.add(1);
|
||||
doc2.add(2);
|
||||
|
||||
REQUIRE_FALSE(array1 == array2);
|
||||
}
|
||||
|
||||
SECTION("should return true when arrays equal") {
|
||||
doc1.add("coucou");
|
||||
doc2.add("coucou");
|
||||
|
||||
REQUIRE(array1 == array2);
|
||||
}
|
||||
|
||||
SECTION("should return false when RHS is null") {
|
||||
JsonArrayConst null;
|
||||
|
||||
REQUIRE_FALSE(array1 == null);
|
||||
}
|
||||
|
||||
SECTION("should return false when LHS is null") {
|
||||
JsonArrayConst null;
|
||||
|
||||
REQUIRE_FALSE(null == array1);
|
||||
}
|
||||
|
||||
SECTION("should return true when both are null") {
|
||||
JsonArrayConst null1;
|
||||
JsonArrayConst null2;
|
||||
|
||||
REQUIRE(null1 == null2);
|
||||
}
|
||||
}
|
||||
32
ArduinoJson/extras/tests/JsonArrayConst/isNull.cpp
Normal file
32
ArduinoJson/extras/tests/JsonArrayConst/isNull.cpp
Normal file
@@ -0,0 +1,32 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2025, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <catch.hpp>
|
||||
|
||||
TEST_CASE("JsonArrayConst::isNull()") {
|
||||
SECTION("returns true") {
|
||||
JsonArrayConst arr;
|
||||
REQUIRE(arr.isNull() == true);
|
||||
}
|
||||
|
||||
SECTION("returns false") {
|
||||
JsonDocument doc;
|
||||
JsonArrayConst arr = doc.to<JsonArray>();
|
||||
REQUIRE(arr.isNull() == false);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("JsonArrayConst::operator bool()") {
|
||||
SECTION("returns false") {
|
||||
JsonArrayConst arr;
|
||||
REQUIRE(static_cast<bool>(arr) == false);
|
||||
}
|
||||
|
||||
SECTION("returns true") {
|
||||
JsonDocument doc;
|
||||
JsonArrayConst arr = doc.to<JsonArray>();
|
||||
REQUIRE(static_cast<bool>(arr) == true);
|
||||
}
|
||||
}
|
||||
34
ArduinoJson/extras/tests/JsonArrayConst/iterator.cpp
Normal file
34
ArduinoJson/extras/tests/JsonArrayConst/iterator.cpp
Normal file
@@ -0,0 +1,34 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2025, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <catch.hpp>
|
||||
|
||||
TEST_CASE("JsonArrayConst::begin()/end()") {
|
||||
SECTION("Non null JsonArrayConst") {
|
||||
JsonDocument doc;
|
||||
JsonArrayConst array = doc.to<JsonArray>();
|
||||
doc.add(12);
|
||||
doc.add(34);
|
||||
|
||||
auto it = array.begin();
|
||||
auto end = array.end();
|
||||
|
||||
REQUIRE(end != it);
|
||||
REQUIRE(12 == it->as<int>());
|
||||
REQUIRE(12 == static_cast<int>(*it));
|
||||
++it;
|
||||
REQUIRE(end != it);
|
||||
REQUIRE(34 == it->as<int>());
|
||||
REQUIRE(34 == static_cast<int>(*it));
|
||||
++it;
|
||||
REQUIRE(end == it);
|
||||
}
|
||||
|
||||
SECTION("Null JsonArrayConst") {
|
||||
JsonArrayConst array;
|
||||
|
||||
REQUIRE(array.begin() == array.end());
|
||||
}
|
||||
}
|
||||
35
ArduinoJson/extras/tests/JsonArrayConst/nesting.cpp
Normal file
35
ArduinoJson/extras/tests/JsonArrayConst/nesting.cpp
Normal file
@@ -0,0 +1,35 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2025, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <catch.hpp>
|
||||
|
||||
TEST_CASE("JsonArrayConst::nesting()") {
|
||||
JsonDocument doc;
|
||||
JsonArrayConst arr = doc.to<JsonArray>();
|
||||
|
||||
SECTION("return 0 if unbound") {
|
||||
JsonArrayConst unbound;
|
||||
REQUIRE(unbound.nesting() == 0);
|
||||
}
|
||||
|
||||
SECTION("returns 1 for empty array") {
|
||||
REQUIRE(arr.nesting() == 1);
|
||||
}
|
||||
|
||||
SECTION("returns 1 for flat array") {
|
||||
doc.add("hello");
|
||||
REQUIRE(arr.nesting() == 1);
|
||||
}
|
||||
|
||||
SECTION("returns 2 with nested array") {
|
||||
doc.add<JsonArray>();
|
||||
REQUIRE(arr.nesting() == 2);
|
||||
}
|
||||
|
||||
SECTION("returns 2 with nested object") {
|
||||
doc.add<JsonObject>();
|
||||
REQUIRE(arr.nesting() == 2);
|
||||
}
|
||||
}
|
||||
27
ArduinoJson/extras/tests/JsonArrayConst/size.cpp
Normal file
27
ArduinoJson/extras/tests/JsonArrayConst/size.cpp
Normal file
@@ -0,0 +1,27 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2025, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <catch.hpp>
|
||||
|
||||
TEST_CASE("JsonArrayConst::size()") {
|
||||
JsonDocument doc;
|
||||
JsonArrayConst array = doc.to<JsonArray>();
|
||||
|
||||
SECTION("returns 0 if unbound") {
|
||||
JsonArrayConst unbound;
|
||||
REQUIRE(0U == unbound.size());
|
||||
}
|
||||
|
||||
SECTION("returns 0 is empty") {
|
||||
REQUIRE(0U == array.size());
|
||||
}
|
||||
|
||||
SECTION("return number of elements") {
|
||||
doc.add("hello");
|
||||
doc.add("world");
|
||||
|
||||
REQUIRE(2U == array.size());
|
||||
}
|
||||
}
|
||||
27
ArduinoJson/extras/tests/JsonArrayConst/subscript.cpp
Normal file
27
ArduinoJson/extras/tests/JsonArrayConst/subscript.cpp
Normal file
@@ -0,0 +1,27 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2025, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <stdint.h>
|
||||
#include <catch.hpp>
|
||||
|
||||
TEST_CASE("JsonArrayConst::operator[]") {
|
||||
JsonDocument doc;
|
||||
JsonArrayConst arr = doc.to<JsonArray>();
|
||||
doc.add(1);
|
||||
doc.add(2);
|
||||
doc.add(3);
|
||||
|
||||
SECTION("int") {
|
||||
REQUIRE(1 == arr[0].as<int>());
|
||||
REQUIRE(2 == arr[1].as<int>());
|
||||
REQUIRE(3 == arr[2].as<int>());
|
||||
REQUIRE(0 == arr[3].as<int>());
|
||||
}
|
||||
|
||||
SECTION("JsonVariant") {
|
||||
REQUIRE(2 == arr[arr[0]].as<int>());
|
||||
REQUIRE(0 == arr[arr[3]].as<int>());
|
||||
}
|
||||
}
|
||||
26
ArduinoJson/extras/tests/JsonDeserializer/CMakeLists.txt
Normal file
26
ArduinoJson/extras/tests/JsonDeserializer/CMakeLists.txt
Normal file
@@ -0,0 +1,26 @@
|
||||
# ArduinoJson - https://arduinojson.org
|
||||
# Copyright © 2014-2025, Benoit BLANCHON
|
||||
# MIT License
|
||||
|
||||
add_executable(JsonDeserializerTests
|
||||
array.cpp
|
||||
DeserializationError.cpp
|
||||
destination_types.cpp
|
||||
errors.cpp
|
||||
filter.cpp
|
||||
input_types.cpp
|
||||
misc.cpp
|
||||
nestingLimit.cpp
|
||||
number.cpp
|
||||
object.cpp
|
||||
string.cpp
|
||||
)
|
||||
|
||||
set_target_properties(JsonDeserializerTests PROPERTIES UNITY_BUILD OFF)
|
||||
|
||||
add_test(JsonDeserializer JsonDeserializerTests)
|
||||
|
||||
set_tests_properties(JsonDeserializer
|
||||
PROPERTIES
|
||||
LABELS "Catch"
|
||||
)
|
||||
@@ -0,0 +1,122 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2025, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <catch.hpp>
|
||||
|
||||
#include <sstream>
|
||||
|
||||
void testStringification(DeserializationError error, std::string expected) {
|
||||
REQUIRE(error.c_str() == expected);
|
||||
}
|
||||
|
||||
void testBoolification(DeserializationError error, bool expected) {
|
||||
// DeserializationError on left-hand side
|
||||
CHECK(bool(error) == expected);
|
||||
CHECK(bool(error) != !expected);
|
||||
CHECK(!bool(error) == !expected);
|
||||
|
||||
// DeserializationError on right-hand side
|
||||
CHECK(expected == bool(error));
|
||||
CHECK(!expected != bool(error));
|
||||
CHECK(!expected == !bool(error));
|
||||
}
|
||||
|
||||
#define TEST_STRINGIFICATION(symbol) \
|
||||
testStringification(DeserializationError::symbol, #symbol)
|
||||
|
||||
#define TEST_BOOLIFICATION(symbol, expected) \
|
||||
testBoolification(DeserializationError::symbol, expected)
|
||||
|
||||
TEST_CASE("DeserializationError") {
|
||||
SECTION("c_str()") {
|
||||
TEST_STRINGIFICATION(Ok);
|
||||
TEST_STRINGIFICATION(EmptyInput);
|
||||
TEST_STRINGIFICATION(IncompleteInput);
|
||||
TEST_STRINGIFICATION(InvalidInput);
|
||||
TEST_STRINGIFICATION(NoMemory);
|
||||
TEST_STRINGIFICATION(TooDeep);
|
||||
}
|
||||
|
||||
SECTION("as boolean") {
|
||||
TEST_BOOLIFICATION(Ok, false);
|
||||
TEST_BOOLIFICATION(EmptyInput, true);
|
||||
TEST_BOOLIFICATION(IncompleteInput, true);
|
||||
TEST_BOOLIFICATION(InvalidInput, true);
|
||||
TEST_BOOLIFICATION(NoMemory, true);
|
||||
TEST_BOOLIFICATION(TooDeep, true);
|
||||
}
|
||||
|
||||
SECTION("ostream DeserializationError") {
|
||||
std::stringstream s;
|
||||
s << DeserializationError(DeserializationError::InvalidInput);
|
||||
REQUIRE(s.str() == "InvalidInput");
|
||||
}
|
||||
|
||||
SECTION("ostream DeserializationError::Code") {
|
||||
std::stringstream s;
|
||||
s << DeserializationError::InvalidInput;
|
||||
REQUIRE(s.str() == "InvalidInput");
|
||||
}
|
||||
|
||||
SECTION("switch") {
|
||||
DeserializationError err = DeserializationError::InvalidInput;
|
||||
switch (err.code()) {
|
||||
case DeserializationError::InvalidInput:
|
||||
SUCCEED();
|
||||
break;
|
||||
default:
|
||||
FAIL();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Use in a condition") {
|
||||
DeserializationError invalidInput(DeserializationError::InvalidInput);
|
||||
DeserializationError ok(DeserializationError::Ok);
|
||||
|
||||
SECTION("if (!err)") {
|
||||
if (!invalidInput)
|
||||
FAIL();
|
||||
}
|
||||
|
||||
SECTION("if (err)") {
|
||||
if (ok)
|
||||
FAIL();
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Comparisons") {
|
||||
DeserializationError invalidInput(DeserializationError::InvalidInput);
|
||||
DeserializationError ok(DeserializationError::Ok);
|
||||
|
||||
SECTION("DeserializationError == Code") {
|
||||
REQUIRE(invalidInput == DeserializationError::InvalidInput);
|
||||
REQUIRE(ok == DeserializationError::Ok);
|
||||
}
|
||||
|
||||
SECTION("Code == DeserializationError") {
|
||||
REQUIRE(DeserializationError::InvalidInput == invalidInput);
|
||||
REQUIRE(DeserializationError::Ok == ok);
|
||||
}
|
||||
|
||||
SECTION("DeserializationError != Code") {
|
||||
REQUIRE(invalidInput != DeserializationError::Ok);
|
||||
REQUIRE(ok != DeserializationError::InvalidInput);
|
||||
}
|
||||
|
||||
SECTION("Code != DeserializationError") {
|
||||
REQUIRE(DeserializationError::Ok != invalidInput);
|
||||
REQUIRE(DeserializationError::InvalidInput != ok);
|
||||
}
|
||||
|
||||
SECTION("DeserializationError == DeserializationError") {
|
||||
REQUIRE_FALSE(invalidInput == ok);
|
||||
}
|
||||
|
||||
SECTION("DeserializationError != DeserializationError") {
|
||||
REQUIRE(invalidInput != ok);
|
||||
}
|
||||
}
|
||||
}
|
||||
337
ArduinoJson/extras/tests/JsonDeserializer/array.cpp
Normal file
337
ArduinoJson/extras/tests/JsonDeserializer/array.cpp
Normal file
@@ -0,0 +1,337 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2025, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <catch.hpp>
|
||||
|
||||
#include "Allocators.hpp"
|
||||
|
||||
using ArduinoJson::detail::sizeofArray;
|
||||
|
||||
TEST_CASE("deserialize JSON array") {
|
||||
SpyingAllocator spy;
|
||||
JsonDocument doc(&spy);
|
||||
|
||||
SECTION("An empty array") {
|
||||
DeserializationError err = deserializeJson(doc, "[]");
|
||||
JsonArray arr = doc.as<JsonArray>();
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(0 == arr.size());
|
||||
}
|
||||
|
||||
SECTION("Spaces") {
|
||||
SECTION("Before the opening bracket") {
|
||||
DeserializationError err = deserializeJson(doc, " []");
|
||||
JsonArray arr = doc.as<JsonArray>();
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(0 == arr.size());
|
||||
}
|
||||
|
||||
SECTION("Before first value") {
|
||||
DeserializationError err = deserializeJson(doc, "[ \t\r\n42]");
|
||||
JsonArray arr = doc.as<JsonArray>();
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(1 == arr.size());
|
||||
REQUIRE(arr[0] == 42);
|
||||
}
|
||||
|
||||
SECTION("After first value") {
|
||||
DeserializationError err = deserializeJson(doc, "[42 \t\r\n]");
|
||||
JsonArray arr = doc.as<JsonArray>();
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(1 == arr.size());
|
||||
REQUIRE(arr[0] == 42);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Values types") {
|
||||
SECTION("On integer") {
|
||||
DeserializationError err = deserializeJson(doc, "[42]");
|
||||
JsonArray arr = doc.as<JsonArray>();
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(1 == arr.size());
|
||||
REQUIRE(arr[0] == 42);
|
||||
}
|
||||
|
||||
SECTION("Two integers") {
|
||||
DeserializationError err = deserializeJson(doc, "[42,84]");
|
||||
JsonArray arr = doc.as<JsonArray>();
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(2 == arr.size());
|
||||
REQUIRE(arr[0] == 42);
|
||||
REQUIRE(arr[1] == 84);
|
||||
}
|
||||
|
||||
SECTION("Float") {
|
||||
DeserializationError err = deserializeJson(doc, "[4.2,1e2]");
|
||||
JsonArray arr = doc.as<JsonArray>();
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(2 == arr.size());
|
||||
REQUIRE(arr[0].as<float>() == Approx(4.2f));
|
||||
REQUIRE(arr[1] == 1e2f);
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Allocate(sizeofPool()),
|
||||
Reallocate(sizeofPool(), sizeofPool(2)),
|
||||
});
|
||||
}
|
||||
|
||||
SECTION("Double") {
|
||||
DeserializationError err = deserializeJson(doc, "[4.2123456,-7E89]");
|
||||
JsonArray arr = doc.as<JsonArray>();
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(2 == arr.size());
|
||||
REQUIRE(arr[0].as<double>() == Approx(4.2123456));
|
||||
REQUIRE(arr[1] == -7E89);
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Allocate(sizeofPool()),
|
||||
Reallocate(sizeofPool(), sizeofPool(4)),
|
||||
});
|
||||
}
|
||||
|
||||
SECTION("Unsigned long") {
|
||||
DeserializationError err = deserializeJson(doc, "[4294967295]");
|
||||
JsonArray arr = doc.as<JsonArray>();
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(1 == arr.size());
|
||||
REQUIRE(arr[0] == 4294967295UL);
|
||||
}
|
||||
|
||||
SECTION("Boolean") {
|
||||
DeserializationError err = deserializeJson(doc, "[true,false]");
|
||||
JsonArray arr = doc.as<JsonArray>();
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(2 == arr.size());
|
||||
REQUIRE(arr[0] == true);
|
||||
REQUIRE(arr[1] == false);
|
||||
}
|
||||
|
||||
SECTION("Null") {
|
||||
DeserializationError err = deserializeJson(doc, "[null,null]");
|
||||
JsonArray arr = doc.as<JsonArray>();
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(2 == arr.size());
|
||||
REQUIRE(arr[0].as<const char*>() == 0);
|
||||
REQUIRE(arr[1].as<const char*>() == 0);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Quotes") {
|
||||
SECTION("Double quotes") {
|
||||
DeserializationError err =
|
||||
deserializeJson(doc, "[ \"hello\" , \"world\" ]");
|
||||
JsonArray arr = doc.as<JsonArray>();
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(2 == arr.size());
|
||||
REQUIRE(arr[0] == "hello");
|
||||
REQUIRE(arr[1] == "world");
|
||||
}
|
||||
|
||||
SECTION("Single quotes") {
|
||||
DeserializationError err = deserializeJson(doc, "[ 'hello' , 'world' ]");
|
||||
JsonArray arr = doc.as<JsonArray>();
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(2 == arr.size());
|
||||
REQUIRE(arr[0] == "hello");
|
||||
REQUIRE(arr[1] == "world");
|
||||
}
|
||||
|
||||
SECTION("No quotes") {
|
||||
DeserializationError err = deserializeJson(doc, "[ hello , world ]");
|
||||
REQUIRE(err == DeserializationError::InvalidInput);
|
||||
}
|
||||
|
||||
SECTION("Double quotes (empty strings)") {
|
||||
DeserializationError err = deserializeJson(doc, "[\"\",\"\"]");
|
||||
JsonArray arr = doc.as<JsonArray>();
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(2 == arr.size());
|
||||
REQUIRE(arr[0] == "");
|
||||
REQUIRE(arr[1] == "");
|
||||
}
|
||||
|
||||
SECTION("Single quotes (empty strings)") {
|
||||
DeserializationError err = deserializeJson(doc, "[\'\',\'\']");
|
||||
JsonArray arr = doc.as<JsonArray>();
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(2 == arr.size());
|
||||
REQUIRE(arr[0] == "");
|
||||
REQUIRE(arr[1] == "");
|
||||
}
|
||||
|
||||
SECTION("No quotes (empty strings)") {
|
||||
DeserializationError err = deserializeJson(doc, "[,]");
|
||||
|
||||
REQUIRE(err == DeserializationError::InvalidInput);
|
||||
}
|
||||
|
||||
SECTION("Closing single quotes missing") {
|
||||
DeserializationError err = deserializeJson(doc, "[\"]");
|
||||
|
||||
REQUIRE(err == DeserializationError::IncompleteInput);
|
||||
}
|
||||
|
||||
SECTION("Closing double quotes missing") {
|
||||
DeserializationError err = deserializeJson(doc, "[\']");
|
||||
|
||||
REQUIRE(err == DeserializationError::IncompleteInput);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Premature null-terminator") {
|
||||
SECTION("After opening bracket") {
|
||||
DeserializationError err = deserializeJson(doc, "[");
|
||||
|
||||
REQUIRE(err == DeserializationError::IncompleteInput);
|
||||
}
|
||||
|
||||
SECTION("After value") {
|
||||
DeserializationError err = deserializeJson(doc, "[1");
|
||||
|
||||
REQUIRE(err == DeserializationError::IncompleteInput);
|
||||
}
|
||||
|
||||
SECTION("After comma") {
|
||||
DeserializationError err = deserializeJson(doc, "[1,");
|
||||
|
||||
REQUIRE(err == DeserializationError::IncompleteInput);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Premature end of input") {
|
||||
const char* input = "[1,2]";
|
||||
|
||||
SECTION("After opening bracket") {
|
||||
DeserializationError err = deserializeJson(doc, input, 1);
|
||||
|
||||
REQUIRE(err == DeserializationError::IncompleteInput);
|
||||
}
|
||||
|
||||
SECTION("After value") {
|
||||
DeserializationError err = deserializeJson(doc, input, 2);
|
||||
|
||||
REQUIRE(err == DeserializationError::IncompleteInput);
|
||||
}
|
||||
|
||||
SECTION("After comma") {
|
||||
DeserializationError err = deserializeJson(doc, input, 3);
|
||||
|
||||
REQUIRE(err == DeserializationError::IncompleteInput);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Misc") {
|
||||
SECTION("Nested objects") {
|
||||
char jsonString[] =
|
||||
" [ { \"a\" : 1 , \"b\" : 2 } , { \"c\" : 3 , \"d\" : 4 } ] ";
|
||||
|
||||
DeserializationError err = deserializeJson(doc, jsonString);
|
||||
JsonArray arr = doc.as<JsonArray>();
|
||||
|
||||
JsonObject object1 = arr[0];
|
||||
const JsonObject object2 = arr[1];
|
||||
JsonObject object3 = arr[2];
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
|
||||
REQUIRE(object1.isNull() == false);
|
||||
REQUIRE(object2.isNull() == false);
|
||||
REQUIRE(object3.isNull() == true);
|
||||
|
||||
REQUIRE(2 == object1.size());
|
||||
REQUIRE(2 == object2.size());
|
||||
REQUIRE(0 == object3.size());
|
||||
|
||||
REQUIRE(1 == object1["a"].as<int>());
|
||||
REQUIRE(2 == object1["b"].as<int>());
|
||||
REQUIRE(3 == object2["c"].as<int>());
|
||||
REQUIRE(4 == object2["d"].as<int>());
|
||||
REQUIRE(0 == object3["e"].as<int>());
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Should clear the JsonArray") {
|
||||
deserializeJson(doc, "[1,2,3,4]");
|
||||
spy.clearLog();
|
||||
|
||||
deserializeJson(doc, "[]");
|
||||
|
||||
JsonArray arr = doc.as<JsonArray>();
|
||||
REQUIRE(arr.size() == 0);
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Deallocate(sizeofArray(4)),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("deserialize JSON array under memory constraints") {
|
||||
TimebombAllocator timebomb(100);
|
||||
SpyingAllocator spy(&timebomb);
|
||||
JsonDocument doc(&spy);
|
||||
|
||||
SECTION("empty array requires no allocation") {
|
||||
timebomb.setCountdown(0);
|
||||
char input[] = "[]";
|
||||
|
||||
DeserializationError err = deserializeJson(doc, input);
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
}
|
||||
|
||||
SECTION("allocation of pool list fails") {
|
||||
timebomb.setCountdown(0);
|
||||
char input[] = "[1]";
|
||||
|
||||
DeserializationError err = deserializeJson(doc, input);
|
||||
|
||||
REQUIRE(err == DeserializationError::NoMemory);
|
||||
REQUIRE(doc.as<std::string>() == "[]");
|
||||
}
|
||||
|
||||
SECTION("allocation of pool fails") {
|
||||
timebomb.setCountdown(0);
|
||||
char input[] = "[1]";
|
||||
|
||||
DeserializationError err = deserializeJson(doc, input);
|
||||
|
||||
REQUIRE(err == DeserializationError::NoMemory);
|
||||
REQUIRE(doc.as<std::string>() == "[]");
|
||||
}
|
||||
|
||||
SECTION("allocation of string fails in array") {
|
||||
timebomb.setCountdown(1);
|
||||
char input[] = "[0,\"hi!\"]";
|
||||
|
||||
DeserializationError err = deserializeJson(doc, input);
|
||||
|
||||
REQUIRE(err == DeserializationError::NoMemory);
|
||||
REQUIRE(doc.as<std::string>() == "[0,null]");
|
||||
}
|
||||
|
||||
SECTION("don't store space characters") {
|
||||
deserializeJson(doc, " [ \"1234567\" ] ");
|
||||
|
||||
REQUIRE(spy.log() ==
|
||||
AllocatorLog{
|
||||
Allocate(sizeofPool()),
|
||||
Allocate(sizeofStringBuffer()),
|
||||
Reallocate(sizeofStringBuffer(), sizeofString("1234567")),
|
||||
Reallocate(sizeofPool(), sizeofArray(1)),
|
||||
});
|
||||
}
|
||||
}
|
||||
109
ArduinoJson/extras/tests/JsonDeserializer/destination_types.cpp
Normal file
109
ArduinoJson/extras/tests/JsonDeserializer/destination_types.cpp
Normal file
@@ -0,0 +1,109 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2025, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
|
||||
#include <catch.hpp>
|
||||
#include <string>
|
||||
|
||||
#include "Allocators.hpp"
|
||||
#include "Literals.hpp"
|
||||
|
||||
using ArduinoJson::detail::sizeofArray;
|
||||
using ArduinoJson::detail::sizeofObject;
|
||||
|
||||
TEST_CASE("deserializeJson(JsonDocument&)") {
|
||||
SpyingAllocator spy;
|
||||
JsonDocument doc(&spy);
|
||||
doc.add("hello"_s);
|
||||
spy.clearLog();
|
||||
|
||||
auto err = deserializeJson(doc, "[42]");
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.as<std::string>() == "[42]");
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Deallocate(sizeofPool()),
|
||||
Deallocate(sizeofString("hello")),
|
||||
Allocate(sizeofPool()),
|
||||
Reallocate(sizeofPool(), sizeofArray(1)),
|
||||
});
|
||||
}
|
||||
|
||||
TEST_CASE("deserializeJson(JsonVariant)") {
|
||||
SECTION("variant is bound") {
|
||||
SpyingAllocator spy;
|
||||
JsonDocument doc(&spy);
|
||||
doc.add("hello"_s);
|
||||
spy.clearLog();
|
||||
|
||||
JsonVariant variant = doc[0];
|
||||
|
||||
auto err = deserializeJson(variant, "[42]");
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.as<std::string>() == "[[42]]");
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Deallocate(sizeofString("hello")),
|
||||
});
|
||||
}
|
||||
|
||||
SECTION("variant is unbound") {
|
||||
JsonVariant variant;
|
||||
|
||||
auto err = deserializeJson(variant, "[42]");
|
||||
|
||||
REQUIRE(err == DeserializationError::NoMemory);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("deserializeJson(ElementProxy)") {
|
||||
SpyingAllocator spy;
|
||||
JsonDocument doc(&spy);
|
||||
doc.add("hello"_s);
|
||||
spy.clearLog();
|
||||
|
||||
SECTION("element already exists") {
|
||||
auto err = deserializeJson(doc[0], "[42]");
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.as<std::string>() == "[[42]]");
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Deallocate(sizeofString("hello")),
|
||||
});
|
||||
}
|
||||
|
||||
SECTION("element must be created") {
|
||||
auto err = deserializeJson(doc[1], "[42]");
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.as<std::string>() == "[\"hello\",[42]]");
|
||||
REQUIRE(spy.log() == AllocatorLog{});
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("deserializeJson(MemberProxy)") {
|
||||
SpyingAllocator spy;
|
||||
JsonDocument doc(&spy);
|
||||
doc["hello"_s] = "world"_s;
|
||||
spy.clearLog();
|
||||
|
||||
SECTION("member already exists") {
|
||||
auto err = deserializeJson(doc["hello"], "[42]");
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.as<std::string>() == "{\"hello\":[42]}");
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Deallocate(sizeofString("world")),
|
||||
});
|
||||
}
|
||||
|
||||
SECTION("member must be created exists") {
|
||||
auto err = deserializeJson(doc["value"], "[42]");
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.as<std::string>() == "{\"hello\":\"world\",\"value\":[42]}");
|
||||
REQUIRE(spy.log() == AllocatorLog{});
|
||||
}
|
||||
}
|
||||
162
ArduinoJson/extras/tests/JsonDeserializer/errors.cpp
Normal file
162
ArduinoJson/extras/tests/JsonDeserializer/errors.cpp
Normal file
@@ -0,0 +1,162 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2025, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#define ARDUINOJSON_DECODE_UNICODE 1
|
||||
#include <ArduinoJson.h>
|
||||
#include <catch.hpp>
|
||||
|
||||
#include "Allocators.hpp"
|
||||
|
||||
TEST_CASE("deserializeJson() returns IncompleteInput") {
|
||||
const char* testCases[] = {
|
||||
// strings
|
||||
"\"\\",
|
||||
"\"hello",
|
||||
"\'hello",
|
||||
// unicode
|
||||
"'\\u",
|
||||
"'\\u00",
|
||||
"'\\u000",
|
||||
// false
|
||||
"f",
|
||||
"fa",
|
||||
"fal",
|
||||
"fals",
|
||||
// true
|
||||
"t",
|
||||
"tr",
|
||||
"tru",
|
||||
// null
|
||||
"n",
|
||||
"nu",
|
||||
"nul",
|
||||
// object
|
||||
"{",
|
||||
"{a",
|
||||
"{a:",
|
||||
"{a:1",
|
||||
"{a:1,",
|
||||
"{a:1,b",
|
||||
"{a:1,b:",
|
||||
};
|
||||
|
||||
for (auto input : testCases) {
|
||||
SECTION(input) {
|
||||
JsonDocument doc;
|
||||
REQUIRE(deserializeJson(doc, input) ==
|
||||
DeserializationError::IncompleteInput);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("deserializeJson() returns InvalidInput") {
|
||||
const char* testCases[] = {
|
||||
// unicode
|
||||
"'\\u'", "'\\u000g'", "'\\u000'", "'\\u000G'", "'\\u000/'", "\\x1234",
|
||||
// numbers
|
||||
"6a9", "1,", "2]", "3}",
|
||||
// constants
|
||||
"nulL", "tru3", "fals3",
|
||||
// garbage
|
||||
"%*$£¤"};
|
||||
|
||||
for (auto input : testCases) {
|
||||
SECTION(input) {
|
||||
JsonDocument doc;
|
||||
REQUIRE(deserializeJson(doc, input) ==
|
||||
DeserializationError::InvalidInput);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("deserializeJson() oversees some edge cases") {
|
||||
const char* testCases[] = {
|
||||
"'\\ud83d'", // leading surrogate without a trailing surrogate
|
||||
"'\\udda4'", // trailing surrogate without a leading surrogate
|
||||
"'\\ud83d\\ud83d'", // two leading surrogates
|
||||
};
|
||||
|
||||
for (auto input : testCases) {
|
||||
SECTION(input) {
|
||||
JsonDocument doc;
|
||||
REQUIRE(deserializeJson(doc, input) == DeserializationError::Ok);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("deserializeJson() returns EmptyInput") {
|
||||
JsonDocument doc;
|
||||
|
||||
SECTION("null") {
|
||||
auto err = deserializeJson(doc, static_cast<const char*>(0));
|
||||
REQUIRE(err == DeserializationError::EmptyInput);
|
||||
}
|
||||
|
||||
SECTION("Empty string") {
|
||||
auto err = deserializeJson(doc, "");
|
||||
REQUIRE(err == DeserializationError::EmptyInput);
|
||||
}
|
||||
|
||||
SECTION("Only spaces") {
|
||||
auto err = deserializeJson(doc, " \t\n\r");
|
||||
REQUIRE(err == DeserializationError::EmptyInput);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("deserializeJson() returns NoMemory if string length overflows") {
|
||||
JsonDocument doc;
|
||||
auto maxLength = ArduinoJson::detail::StringNode::maxLength;
|
||||
|
||||
SECTION("max length should succeed") {
|
||||
auto err = deserializeJson(doc, "\"" + std::string(maxLength, 'a') + "\"");
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
}
|
||||
|
||||
SECTION("one above max length should fail") {
|
||||
auto err =
|
||||
deserializeJson(doc, "\"" + std::string(maxLength + 1, 'a') + "\"");
|
||||
REQUIRE(err == DeserializationError::NoMemory);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("deserializeJson() returns NoMemory if extension allocation fails") {
|
||||
JsonDocument doc(FailingAllocator::instance());
|
||||
|
||||
SECTION("uint32_t should pass") {
|
||||
auto err = deserializeJson(doc, "4294967295");
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
}
|
||||
|
||||
SECTION("uint64_t should fail") {
|
||||
auto err = deserializeJson(doc, "18446744073709551615");
|
||||
|
||||
REQUIRE(err == DeserializationError::NoMemory);
|
||||
}
|
||||
|
||||
SECTION("int32_t should pass") {
|
||||
auto err = deserializeJson(doc, "-2147483648");
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
}
|
||||
|
||||
SECTION("int64_t should fail") {
|
||||
auto err = deserializeJson(doc, "-9223372036854775808");
|
||||
|
||||
REQUIRE(err == DeserializationError::NoMemory);
|
||||
}
|
||||
|
||||
SECTION("float should pass") {
|
||||
auto err = deserializeJson(doc, "3.402823e38");
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
}
|
||||
|
||||
SECTION("double should fail") {
|
||||
auto err = deserializeJson(doc, "1.7976931348623157e308");
|
||||
|
||||
REQUIRE(err == DeserializationError::NoMemory);
|
||||
}
|
||||
}
|
||||
831
ArduinoJson/extras/tests/JsonDeserializer/filter.cpp
Normal file
831
ArduinoJson/extras/tests/JsonDeserializer/filter.cpp
Normal file
@@ -0,0 +1,831 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2025, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#define ARDUINOJSON_ENABLE_COMMENTS 1
|
||||
#include <ArduinoJson.h>
|
||||
#include <catch.hpp>
|
||||
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include "Allocators.hpp"
|
||||
#include "Literals.hpp"
|
||||
|
||||
using ArduinoJson::detail::sizeofArray;
|
||||
using ArduinoJson::detail::sizeofObject;
|
||||
|
||||
TEST_CASE("Filtering") {
|
||||
struct TestCase {
|
||||
const char* description;
|
||||
const char* input;
|
||||
const char* filter;
|
||||
uint8_t nestingLimit;
|
||||
DeserializationError error;
|
||||
const char* output;
|
||||
size_t memoryUsage;
|
||||
};
|
||||
|
||||
TestCase testCases[] = {
|
||||
{
|
||||
"Input is object, filter is null", // description
|
||||
"{\"hello\":\"world\"}", // input
|
||||
"null", // filter
|
||||
10, // nestingLimit
|
||||
DeserializationError::Ok, // error
|
||||
"null", // output
|
||||
0, // memoryUsage
|
||||
},
|
||||
{
|
||||
"Input is object, filter is false",
|
||||
"{\"hello\":\"world\"}",
|
||||
"false",
|
||||
10,
|
||||
DeserializationError::Ok,
|
||||
"null",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"Input is object, filter is true",
|
||||
"{\"abcdefg\":\"hijklmn\"}",
|
||||
"true",
|
||||
10,
|
||||
DeserializationError::Ok,
|
||||
"{\"abcdefg\":\"hijklmn\"}",
|
||||
sizeofObject(1) + sizeofString("abcdefg") + sizeofString("hijklmn"),
|
||||
},
|
||||
{
|
||||
"Input is object, filter is empty object",
|
||||
"{\"hello\":\"world\"}",
|
||||
"{}",
|
||||
10,
|
||||
DeserializationError::Ok,
|
||||
"{}",
|
||||
sizeofObject(0),
|
||||
},
|
||||
{
|
||||
"Input in an object, but filter wants an array",
|
||||
"{\"hello\":\"world\"}",
|
||||
"[]",
|
||||
10,
|
||||
DeserializationError::Ok,
|
||||
"null",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"Member is a string, but filter wants an array",
|
||||
"{\"example\":\"example\"}",
|
||||
"{\"example\":[true]}",
|
||||
10,
|
||||
DeserializationError::Ok,
|
||||
"{\"example\":null}",
|
||||
sizeofObject(1) + sizeofString("example"),
|
||||
},
|
||||
{
|
||||
"Member is a number, but filter wants an array",
|
||||
"{\"example\":42}",
|
||||
"{\"example\":[true]}",
|
||||
10,
|
||||
DeserializationError::Ok,
|
||||
"{\"example\":null}",
|
||||
sizeofObject(1) + sizeofString("example"),
|
||||
},
|
||||
{
|
||||
"Input is an array, but filter wants an object",
|
||||
"[\"hello\",\"world\"]",
|
||||
"{}",
|
||||
10,
|
||||
DeserializationError::Ok,
|
||||
"null",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"Input is a bool, but filter wants an object",
|
||||
"true",
|
||||
"{}",
|
||||
10,
|
||||
DeserializationError::Ok,
|
||||
"null",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"Input is a string, but filter wants an object",
|
||||
"\"hello\"",
|
||||
"{}",
|
||||
10,
|
||||
DeserializationError::Ok,
|
||||
"null",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"Skip an integer",
|
||||
"{\"an_integer\":666,example:42}",
|
||||
"{\"example\":true}",
|
||||
10,
|
||||
DeserializationError::Ok,
|
||||
"{\"example\":42}",
|
||||
sizeofObject(1) + sizeofString("example"),
|
||||
},
|
||||
{
|
||||
"Skip a float",
|
||||
"{\"a_float\":12.34e-6,example:42}",
|
||||
"{\"example\":true}",
|
||||
10,
|
||||
DeserializationError::Ok,
|
||||
"{\"example\":42}",
|
||||
sizeofObject(1) + sizeofString("example"),
|
||||
},
|
||||
{
|
||||
"Skip false",
|
||||
"{\"a_bool\":false,example:42}",
|
||||
"{\"example\":true}",
|
||||
10,
|
||||
DeserializationError::Ok,
|
||||
"{\"example\":42}",
|
||||
sizeofObject(1) + sizeofString("example"),
|
||||
},
|
||||
{
|
||||
"Skip true",
|
||||
"{\"a_bool\":true,example:42}",
|
||||
"{\"example\":true}",
|
||||
10,
|
||||
DeserializationError::Ok,
|
||||
"{\"example\":42}",
|
||||
sizeofObject(1) + sizeofString("example"),
|
||||
},
|
||||
{
|
||||
"Skip null",
|
||||
"{\"a_bool\":null,example:42}",
|
||||
"{\"example\":true}",
|
||||
10,
|
||||
DeserializationError::Ok,
|
||||
"{\"example\":42}",
|
||||
sizeofObject(1) + sizeofString("example"),
|
||||
},
|
||||
{
|
||||
"Skip a double-quoted string",
|
||||
"{\"a_double_quoted_string\":\"hello\",example:42}",
|
||||
"{\"example\":true}",
|
||||
10,
|
||||
DeserializationError::Ok,
|
||||
"{\"example\":42}",
|
||||
sizeofObject(1) + sizeofString("example"),
|
||||
},
|
||||
{
|
||||
"Skip a single-quoted string",
|
||||
"{\"a_single_quoted_string\":'hello',example:42}",
|
||||
"{\"example\":true}",
|
||||
10,
|
||||
DeserializationError::Ok,
|
||||
"{\"example\":42}",
|
||||
sizeofObject(1) + sizeofString("example"),
|
||||
},
|
||||
{
|
||||
"Skip an empty array",
|
||||
"{\"an_empty_array\":[],example:42}",
|
||||
"{\"example\":true}",
|
||||
10,
|
||||
DeserializationError::Ok,
|
||||
"{\"example\":42}",
|
||||
sizeofObject(1) + sizeofString("example"),
|
||||
},
|
||||
{
|
||||
"Skip an empty array with spaces in it",
|
||||
"{\"an_empty_array\":[\t],example:42}",
|
||||
"{\"example\":true}",
|
||||
10,
|
||||
DeserializationError::Ok,
|
||||
"{\"example\":42}",
|
||||
sizeofObject(1) + sizeofString("example"),
|
||||
},
|
||||
{
|
||||
"Skip an array",
|
||||
"{\"an_array\":[1,2,3],example:42}",
|
||||
"{\"example\":true}",
|
||||
10,
|
||||
DeserializationError::Ok,
|
||||
"{\"example\":42}",
|
||||
sizeofObject(1) + sizeofString("example"),
|
||||
},
|
||||
{
|
||||
"Skip an array with spaces in it",
|
||||
"{\"an_array\": [ 1 , 2 , 3 ] ,example:42}",
|
||||
"{\"example\":true}",
|
||||
10,
|
||||
DeserializationError::Ok,
|
||||
"{\"example\":42}",
|
||||
sizeofObject(1) + sizeofString("example"),
|
||||
},
|
||||
{
|
||||
"Skip an empty nested object",
|
||||
"{\"an_empty_object\":{},example:42}",
|
||||
"{\"example\":true}",
|
||||
10,
|
||||
DeserializationError::Ok,
|
||||
"{\"example\":42}",
|
||||
sizeofObject(1) + sizeofString("example"),
|
||||
},
|
||||
{
|
||||
"Skip an empty nested object with spaces in it",
|
||||
"{\"an_empty_object\":{ },example:42}",
|
||||
"{\"example\":true}",
|
||||
10,
|
||||
DeserializationError::Ok,
|
||||
"{\"example\":42}",
|
||||
sizeofObject(1) + sizeofString("example"),
|
||||
},
|
||||
{
|
||||
"Skip a nested object",
|
||||
"{\"an_object\":{a:1,'b':2,\"c\":3},example:42}",
|
||||
"{\"example\":true}",
|
||||
10,
|
||||
DeserializationError::Ok,
|
||||
"{\"example\":42}",
|
||||
sizeofObject(1) + sizeofString("example"),
|
||||
},
|
||||
{
|
||||
"Skip an object with spaces in it",
|
||||
"{\"an_object\" : { a : 1 , 'b' : 2 , \"c\" : 3 } ,example:42}",
|
||||
"{\"example\":true}",
|
||||
10,
|
||||
DeserializationError::Ok,
|
||||
"{\"example\":42}",
|
||||
sizeofObject(1) + sizeofString("example"),
|
||||
},
|
||||
{
|
||||
"Skip a string in a nested object",
|
||||
"{\"an_integer\": 0,\"example\":{\"type\":\"int\",\"outcome\":42}}",
|
||||
"{\"example\":{\"outcome\":true}}",
|
||||
10,
|
||||
DeserializationError::Ok,
|
||||
"{\"example\":{\"outcome\":42}}",
|
||||
2 * sizeofObject(1) + 2 * sizeofString("example"),
|
||||
},
|
||||
{
|
||||
"wildcard",
|
||||
"{\"example\":{\"type\":\"int\",\"outcome\":42}}",
|
||||
"{\"*\":{\"outcome\":true}}",
|
||||
10,
|
||||
DeserializationError::Ok,
|
||||
"{\"example\":{\"outcome\":42}}",
|
||||
2 * sizeofObject(1) + 2 * sizeofString("example"),
|
||||
},
|
||||
{
|
||||
"exclusion filter (issue #1628)",
|
||||
"{\"example\":1,\"ignored\":2}",
|
||||
"{\"*\":true,\"ignored\":false}",
|
||||
10,
|
||||
DeserializationError::Ok,
|
||||
"{\"example\":1}",
|
||||
sizeofObject(1) + sizeofString("example"),
|
||||
},
|
||||
{
|
||||
"only the first element of array counts",
|
||||
"[1,2,3]",
|
||||
"[true, false]",
|
||||
10,
|
||||
DeserializationError::Ok,
|
||||
"[1,2,3]",
|
||||
sizeofArray(3),
|
||||
},
|
||||
{
|
||||
"only the first element of array counts",
|
||||
"[1,2,3]",
|
||||
"[false, true]",
|
||||
10,
|
||||
DeserializationError::Ok,
|
||||
"[]",
|
||||
sizeofArray(0),
|
||||
},
|
||||
{
|
||||
"filter members of object in array",
|
||||
"[{\"example\":1,\"ignore\":2},{\"example\":3,\"ignore\":4}]",
|
||||
"[{\"example\":true}]",
|
||||
10,
|
||||
DeserializationError::Ok,
|
||||
"[{\"example\":1},{\"example\":3}]",
|
||||
sizeofArray(2) + 2 * sizeofObject(1) + sizeofString("example"),
|
||||
},
|
||||
{
|
||||
"Unclosed single quote in skipped element",
|
||||
"[',2,3]",
|
||||
"[false,true]",
|
||||
10,
|
||||
DeserializationError::IncompleteInput,
|
||||
"[]",
|
||||
sizeofArray(0),
|
||||
},
|
||||
{
|
||||
"Unclosed double quote in skipped element",
|
||||
"[\",2,3]",
|
||||
"[false,true]",
|
||||
10,
|
||||
DeserializationError::IncompleteInput,
|
||||
"[]",
|
||||
sizeofArray(0),
|
||||
},
|
||||
{
|
||||
"Detect errors in skipped value",
|
||||
"[!,2,\\]",
|
||||
"[false]",
|
||||
10,
|
||||
DeserializationError::InvalidInput,
|
||||
"[]",
|
||||
sizeofArray(0),
|
||||
},
|
||||
{
|
||||
"Detect incomplete string event if it's skipped",
|
||||
"\"ABC",
|
||||
"false",
|
||||
10,
|
||||
DeserializationError::IncompleteInput,
|
||||
"null",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"Detect incomplete string event if it's skipped",
|
||||
"'ABC",
|
||||
"false",
|
||||
10,
|
||||
DeserializationError::IncompleteInput,
|
||||
"null",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"Handle escaped quotes",
|
||||
"'A\\'BC'",
|
||||
"false",
|
||||
10,
|
||||
DeserializationError::Ok,
|
||||
"null",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"Handle escaped quotes",
|
||||
"\"A\\\"BC\"",
|
||||
"false",
|
||||
10,
|
||||
DeserializationError::Ok,
|
||||
"null",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"Detect incomplete string in presence of escaped quotes",
|
||||
"'A\\'BC",
|
||||
"false",
|
||||
10,
|
||||
DeserializationError::IncompleteInput,
|
||||
"null",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"Detect incomplete string in presence of escaped quotes",
|
||||
"\"A\\\"BC",
|
||||
"false",
|
||||
10,
|
||||
DeserializationError::IncompleteInput,
|
||||
"null",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"skip empty array",
|
||||
"[]",
|
||||
"false",
|
||||
10,
|
||||
DeserializationError::Ok,
|
||||
"null",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"Skip empty array with spaces",
|
||||
" [ ] ",
|
||||
"false",
|
||||
10,
|
||||
DeserializationError::Ok,
|
||||
"null",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"Bubble up element error even if array is skipped",
|
||||
"[1,'2,3]",
|
||||
"false",
|
||||
10,
|
||||
DeserializationError::IncompleteInput,
|
||||
"null",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"Bubble up member error even if object is skipped",
|
||||
"{'hello':'worl}",
|
||||
"false",
|
||||
10,
|
||||
DeserializationError::IncompleteInput,
|
||||
"null",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"Bubble up colon error even if object is skipped",
|
||||
"{'hello','world'}",
|
||||
"false",
|
||||
10,
|
||||
DeserializationError::InvalidInput,
|
||||
"null",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"Bubble up key error even if object is skipped",
|
||||
"{'hello:1}",
|
||||
"false",
|
||||
10,
|
||||
DeserializationError::IncompleteInput,
|
||||
"null",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"Detect invalid value in skipped object",
|
||||
"{'hello':!}",
|
||||
"false",
|
||||
10,
|
||||
DeserializationError::InvalidInput,
|
||||
"null",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"Ignore invalid value in skipped object",
|
||||
"{'hello':\\}",
|
||||
"false",
|
||||
10,
|
||||
DeserializationError::InvalidInput,
|
||||
"null",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"Check nesting limit even for ignored objects",
|
||||
"{}",
|
||||
"false",
|
||||
0,
|
||||
DeserializationError::TooDeep,
|
||||
"null",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"Check nesting limit even for ignored objects",
|
||||
"{'hello':{}}",
|
||||
"false",
|
||||
1,
|
||||
DeserializationError::TooDeep,
|
||||
"null",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"Check nesting limit even for ignored values in objects",
|
||||
"{'hello':{}}",
|
||||
"{}",
|
||||
1,
|
||||
DeserializationError::TooDeep,
|
||||
"{}",
|
||||
sizeofObject(0),
|
||||
},
|
||||
{
|
||||
"Check nesting limit even for ignored arrays",
|
||||
"[]",
|
||||
"false",
|
||||
0,
|
||||
DeserializationError::TooDeep,
|
||||
"null",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"Check nesting limit even for ignored arrays",
|
||||
"[[]]",
|
||||
"false",
|
||||
1,
|
||||
DeserializationError::TooDeep,
|
||||
"null",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"Check nesting limit even for ignored values in arrays",
|
||||
"[[]]",
|
||||
"[]",
|
||||
1,
|
||||
DeserializationError::TooDeep,
|
||||
"[]",
|
||||
sizeofArray(0),
|
||||
},
|
||||
{
|
||||
"Supports back-slash at the end of skipped string",
|
||||
"\"hell\\",
|
||||
"false",
|
||||
1,
|
||||
DeserializationError::IncompleteInput,
|
||||
"null",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"Invalid comment at after an element in a skipped array",
|
||||
"[1/]",
|
||||
"false",
|
||||
10,
|
||||
DeserializationError::InvalidInput,
|
||||
"null",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"Incomplete comment at after an element in a skipped array",
|
||||
"[1/*]",
|
||||
"false",
|
||||
10,
|
||||
DeserializationError::IncompleteInput,
|
||||
"null",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"Missing comma in a skipped array",
|
||||
"[1 2]",
|
||||
"false",
|
||||
10,
|
||||
DeserializationError::InvalidInput,
|
||||
"null",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"Invalid comment at the beginning of array",
|
||||
"[/1]",
|
||||
"[false]",
|
||||
10,
|
||||
DeserializationError::InvalidInput,
|
||||
"[]",
|
||||
sizeofArray(0),
|
||||
},
|
||||
{
|
||||
"Incomplete comment at the begining of an array",
|
||||
"[/*]",
|
||||
"[false]",
|
||||
10,
|
||||
DeserializationError::IncompleteInput,
|
||||
"[]",
|
||||
sizeofArray(0),
|
||||
},
|
||||
{
|
||||
"Invalid comment before key",
|
||||
"{/1:2}",
|
||||
"{}",
|
||||
10,
|
||||
DeserializationError::InvalidInput,
|
||||
"{}",
|
||||
sizeofObject(0),
|
||||
},
|
||||
{
|
||||
"Incomplete comment before key",
|
||||
"{/*:2}",
|
||||
"{}",
|
||||
10,
|
||||
DeserializationError::IncompleteInput,
|
||||
"{}",
|
||||
sizeofObject(0),
|
||||
},
|
||||
{
|
||||
"Invalid comment after key",
|
||||
"{\"example\"/1:2}",
|
||||
"{}",
|
||||
10,
|
||||
DeserializationError::InvalidInput,
|
||||
"{}",
|
||||
sizeofObject(0),
|
||||
},
|
||||
{
|
||||
"Incomplete comment after key",
|
||||
"{\"example\"/*:2}",
|
||||
"{}",
|
||||
10,
|
||||
DeserializationError::IncompleteInput,
|
||||
"{}",
|
||||
sizeofObject(0),
|
||||
},
|
||||
{
|
||||
"Invalid comment after colon",
|
||||
"{\"example\":/12}",
|
||||
"{}",
|
||||
10,
|
||||
DeserializationError::InvalidInput,
|
||||
"{}",
|
||||
sizeofObject(0),
|
||||
},
|
||||
{
|
||||
"Incomplete comment after colon",
|
||||
"{\"example\":/*2}",
|
||||
"{}",
|
||||
10,
|
||||
DeserializationError::IncompleteInput,
|
||||
"{}",
|
||||
sizeofObject(0),
|
||||
},
|
||||
{
|
||||
"Comment next to an integer",
|
||||
"{\"ignore\":1//,\"example\":2\n}",
|
||||
"{\"example\":true}",
|
||||
10,
|
||||
DeserializationError::Ok,
|
||||
"{}",
|
||||
sizeofObject(0),
|
||||
},
|
||||
{
|
||||
"Invalid comment after opening brace of a skipped object",
|
||||
"{/1:2}",
|
||||
"false",
|
||||
10,
|
||||
DeserializationError::InvalidInput,
|
||||
"null",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"Incomplete after opening brace of a skipped object",
|
||||
"{/*:2}",
|
||||
"false",
|
||||
10,
|
||||
DeserializationError::IncompleteInput,
|
||||
"null",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"Invalid comment after key of a skipped object",
|
||||
"{\"example\"/:2}",
|
||||
"false",
|
||||
10,
|
||||
DeserializationError::InvalidInput,
|
||||
"null",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"Incomplete comment after key of a skipped object",
|
||||
"{\"example\"/*:2}",
|
||||
"false",
|
||||
10,
|
||||
DeserializationError::IncompleteInput,
|
||||
"null",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"Invalid comment after value in a skipped object",
|
||||
"{\"example\":2/}",
|
||||
"false",
|
||||
10,
|
||||
DeserializationError::InvalidInput,
|
||||
"null",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"Incomplete comment after value of a skipped object",
|
||||
"{\"example\":2/*}",
|
||||
"false",
|
||||
10,
|
||||
DeserializationError::IncompleteInput,
|
||||
"null",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"Incomplete comment after comma in skipped object",
|
||||
"{\"example\":2,/*}",
|
||||
"false",
|
||||
10,
|
||||
DeserializationError::IncompleteInput,
|
||||
"null",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"NUL character in key",
|
||||
"{\"x\":0,\"x\\u0000a\":1,\"x\\u0000b\":2}",
|
||||
"{\"x\\u0000a\":true}",
|
||||
10,
|
||||
DeserializationError::Ok,
|
||||
"{\"x\\u0000a\":1}",
|
||||
sizeofObject(1) + sizeofString("x?a"),
|
||||
},
|
||||
};
|
||||
|
||||
for (auto& tc : testCases) {
|
||||
SECTION(tc.description) {
|
||||
SpyingAllocator spy;
|
||||
JsonDocument filter;
|
||||
JsonDocument doc(&spy);
|
||||
|
||||
REQUIRE(deserializeJson(filter, tc.filter) == DeserializationError::Ok);
|
||||
|
||||
CHECK(deserializeJson(
|
||||
doc, tc.input, DeserializationOption::Filter(filter),
|
||||
DeserializationOption::NestingLimit(tc.nestingLimit)) ==
|
||||
tc.error);
|
||||
|
||||
CHECK(doc.as<std::string>() == tc.output);
|
||||
|
||||
doc.shrinkToFit();
|
||||
CHECK(spy.allocatedBytes() == tc.memoryUsage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Overloads") {
|
||||
JsonDocument doc;
|
||||
JsonDocument filter;
|
||||
|
||||
using namespace DeserializationOption;
|
||||
|
||||
// deserializeJson(..., Filter)
|
||||
|
||||
SECTION("const char*, Filter") {
|
||||
deserializeJson(doc, "{}", Filter(filter));
|
||||
}
|
||||
|
||||
SECTION("const char*, size_t, Filter") {
|
||||
deserializeJson(doc, "{}", 2, Filter(filter));
|
||||
}
|
||||
|
||||
SECTION("const std::string&, Filter") {
|
||||
deserializeJson(doc, "{}"_s, Filter(filter));
|
||||
}
|
||||
|
||||
SECTION("std::istream&, Filter") {
|
||||
std::stringstream s("{}");
|
||||
deserializeJson(doc, s, Filter(filter));
|
||||
}
|
||||
|
||||
#ifdef HAS_VARIABLE_LENGTH_ARRAY
|
||||
SECTION("char[n], Filter") {
|
||||
size_t i = 4;
|
||||
char vla[i];
|
||||
strcpy(vla, "{}");
|
||||
deserializeJson(doc, vla, Filter(filter));
|
||||
}
|
||||
#endif
|
||||
|
||||
// deserializeJson(..., Filter, NestingLimit)
|
||||
|
||||
SECTION("const char*, Filter, NestingLimit") {
|
||||
deserializeJson(doc, "{}", Filter(filter), NestingLimit(5));
|
||||
}
|
||||
|
||||
SECTION("const char*, size_t, Filter, NestingLimit") {
|
||||
deserializeJson(doc, "{}", 2, Filter(filter), NestingLimit(5));
|
||||
}
|
||||
|
||||
SECTION("const std::string&, Filter, NestingLimit") {
|
||||
deserializeJson(doc, "{}"_s, Filter(filter), NestingLimit(5));
|
||||
}
|
||||
|
||||
SECTION("std::istream&, Filter, NestingLimit") {
|
||||
std::stringstream s("{}");
|
||||
deserializeJson(doc, s, Filter(filter), NestingLimit(5));
|
||||
}
|
||||
|
||||
#ifdef HAS_VARIABLE_LENGTH_ARRAY
|
||||
SECTION("char[n], Filter, NestingLimit") {
|
||||
size_t i = 4;
|
||||
char vla[i];
|
||||
strcpy(vla, "{}");
|
||||
deserializeJson(doc, vla, Filter(filter), NestingLimit(5));
|
||||
}
|
||||
#endif
|
||||
|
||||
// deserializeJson(..., NestingLimit, Filter)
|
||||
|
||||
SECTION("const char*, NestingLimit, Filter") {
|
||||
deserializeJson(doc, "{}", NestingLimit(5), Filter(filter));
|
||||
}
|
||||
|
||||
SECTION("const char*, size_t, NestingLimit, Filter") {
|
||||
deserializeJson(doc, "{}", 2, NestingLimit(5), Filter(filter));
|
||||
}
|
||||
|
||||
SECTION("const std::string&, NestingLimit, Filter") {
|
||||
deserializeJson(doc, "{}"_s, NestingLimit(5), Filter(filter));
|
||||
}
|
||||
|
||||
SECTION("std::istream&, NestingLimit, Filter") {
|
||||
std::stringstream s("{}");
|
||||
deserializeJson(doc, s, NestingLimit(5), Filter(filter));
|
||||
}
|
||||
|
||||
#ifdef HAS_VARIABLE_LENGTH_ARRAY
|
||||
SECTION("char[n], NestingLimit, Filter") {
|
||||
size_t i = 4;
|
||||
char vla[i];
|
||||
strcpy(vla, "{}");
|
||||
deserializeJson(doc, vla, NestingLimit(5), Filter(filter));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST_CASE("shrink filter") {
|
||||
JsonDocument doc;
|
||||
SpyingAllocator spy;
|
||||
JsonDocument filter(&spy);
|
||||
filter["a"] = true;
|
||||
spy.clearLog();
|
||||
|
||||
deserializeJson(doc, "{}", DeserializationOption::Filter(filter));
|
||||
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Reallocate(sizeofPool(), sizeofObject(1)),
|
||||
});
|
||||
}
|
||||
232
ArduinoJson/extras/tests/JsonDeserializer/input_types.cpp
Normal file
232
ArduinoJson/extras/tests/JsonDeserializer/input_types.cpp
Normal file
@@ -0,0 +1,232 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2025, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
|
||||
#include <catch.hpp>
|
||||
#include <sstream>
|
||||
|
||||
#include "Allocators.hpp"
|
||||
#include "CustomReader.hpp"
|
||||
#include "Literals.hpp"
|
||||
|
||||
using ArduinoJson::detail::sizeofObject;
|
||||
|
||||
TEST_CASE("deserializeJson(char*)") {
|
||||
SpyingAllocator spy;
|
||||
JsonDocument doc(&spy);
|
||||
|
||||
char input[] = "{\"hello\":\"world\"}";
|
||||
|
||||
DeserializationError err = deserializeJson(doc, input);
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
|
||||
REQUIRE(spy.log() ==
|
||||
AllocatorLog{
|
||||
Allocate(sizeofStringBuffer()),
|
||||
Allocate(sizeofPool()),
|
||||
Reallocate(sizeofStringBuffer(), sizeofString("hello")),
|
||||
Allocate(sizeofStringBuffer()),
|
||||
Reallocate(sizeofStringBuffer(), sizeofString("world")),
|
||||
Reallocate(sizeofPool(), sizeofObject(1)),
|
||||
});
|
||||
}
|
||||
|
||||
TEST_CASE("deserializeJson(unsigned char*, unsigned int)") { // issue #1897
|
||||
JsonDocument doc;
|
||||
|
||||
unsigned char input[] = "{\"hello\":\"world\"}";
|
||||
unsigned char* input_ptr = input;
|
||||
unsigned int size = sizeof(input);
|
||||
|
||||
DeserializationError err = deserializeJson(doc, input_ptr, size);
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
}
|
||||
|
||||
TEST_CASE("deserializeJson(uint8_t*, size_t)") { // issue #1898
|
||||
JsonDocument doc;
|
||||
|
||||
uint8_t input[] = "{\"hello\":\"world\"}";
|
||||
uint8_t* input_ptr = input;
|
||||
size_t size = sizeof(input);
|
||||
|
||||
DeserializationError err = deserializeJson(doc, input_ptr, size);
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
}
|
||||
|
||||
TEST_CASE("deserializeJson(const std::string&)") {
|
||||
JsonDocument doc;
|
||||
|
||||
SECTION("should accept const string") {
|
||||
const std::string input("[42]");
|
||||
|
||||
DeserializationError err = deserializeJson(doc, input);
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
}
|
||||
|
||||
SECTION("should accept temporary string") {
|
||||
DeserializationError err = deserializeJson(doc, "[42]"_s);
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
}
|
||||
|
||||
SECTION("should duplicate content") {
|
||||
std::string input("[\"hello\"]");
|
||||
|
||||
DeserializationError err = deserializeJson(doc, input);
|
||||
input[2] = 'X'; // alter the string tomake sure we made a copy
|
||||
|
||||
JsonArray array = doc.as<JsonArray>();
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE("hello"_s == array[0]);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("deserializeJson(std::istream&)") {
|
||||
JsonDocument doc;
|
||||
|
||||
SECTION("array") {
|
||||
std::istringstream json(" [ 42 ] ");
|
||||
|
||||
DeserializationError err = deserializeJson(doc, json);
|
||||
JsonArray arr = doc.as<JsonArray>();
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(1 == arr.size());
|
||||
REQUIRE(42 == arr[0]);
|
||||
}
|
||||
|
||||
SECTION("object") {
|
||||
std::istringstream json(" { hello : 'world' }");
|
||||
|
||||
DeserializationError err = deserializeJson(doc, json);
|
||||
JsonObject obj = doc.as<JsonObject>();
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(1 == obj.size());
|
||||
REQUIRE("world"_s == obj["hello"]);
|
||||
}
|
||||
|
||||
SECTION("Should not read after the closing brace of an empty object") {
|
||||
std::istringstream json("{}123");
|
||||
|
||||
deserializeJson(doc, json);
|
||||
|
||||
REQUIRE('1' == char(json.get()));
|
||||
}
|
||||
|
||||
SECTION("Should not read after the closing brace") {
|
||||
std::istringstream json("{\"hello\":\"world\"}123");
|
||||
|
||||
deserializeJson(doc, json);
|
||||
|
||||
REQUIRE('1' == char(json.get()));
|
||||
}
|
||||
|
||||
SECTION("Should not read after the closing bracket of an empty array") {
|
||||
std::istringstream json("[]123");
|
||||
|
||||
deserializeJson(doc, json);
|
||||
|
||||
REQUIRE('1' == char(json.get()));
|
||||
}
|
||||
|
||||
SECTION("Should not read after the closing bracket") {
|
||||
std::istringstream json("[\"hello\",\"world\"]123");
|
||||
|
||||
deserializeJson(doc, json);
|
||||
|
||||
REQUIRE('1' == char(json.get()));
|
||||
}
|
||||
|
||||
SECTION("Should not read after the closing quote") {
|
||||
std::istringstream json("\"hello\"123");
|
||||
|
||||
deserializeJson(doc, json);
|
||||
|
||||
REQUIRE('1' == char(json.get()));
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAS_VARIABLE_LENGTH_ARRAY
|
||||
TEST_CASE("deserializeJson(VLA)") {
|
||||
size_t i = 9;
|
||||
char vla[i];
|
||||
strcpy(vla, "{\"a\":42}");
|
||||
|
||||
JsonDocument doc;
|
||||
DeserializationError err = deserializeJson(doc, vla);
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST_CASE("deserializeJson(CustomReader)") {
|
||||
JsonDocument doc;
|
||||
CustomReader reader("[4,2]");
|
||||
DeserializationError err = deserializeJson(doc, reader);
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.size() == 2);
|
||||
REQUIRE(doc[0] == 4);
|
||||
REQUIRE(doc[1] == 2);
|
||||
}
|
||||
|
||||
TEST_CASE("deserializeJson(JsonDocument&, MemberProxy)") {
|
||||
JsonDocument doc1;
|
||||
doc1["payload"] = "[4,2]";
|
||||
|
||||
JsonDocument doc2;
|
||||
DeserializationError err = deserializeJson(doc2, doc1["payload"]);
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc2.size() == 2);
|
||||
REQUIRE(doc2[0] == 4);
|
||||
REQUIRE(doc2[1] == 2);
|
||||
}
|
||||
|
||||
TEST_CASE("deserializeJson(JsonDocument&, JsonVariant)") {
|
||||
JsonDocument doc1;
|
||||
doc1["payload"] = "[4,2]";
|
||||
|
||||
JsonDocument doc2;
|
||||
DeserializationError err =
|
||||
deserializeJson(doc2, doc1["payload"].as<JsonVariant>());
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc2.size() == 2);
|
||||
REQUIRE(doc2[0] == 4);
|
||||
REQUIRE(doc2[1] == 2);
|
||||
}
|
||||
|
||||
TEST_CASE("deserializeJson(JsonDocument&, JsonVariantConst)") {
|
||||
JsonDocument doc1;
|
||||
doc1["payload"] = "[4,2]";
|
||||
|
||||
JsonDocument doc2;
|
||||
DeserializationError err =
|
||||
deserializeJson(doc2, doc1["payload"].as<JsonVariantConst>());
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc2.size() == 2);
|
||||
REQUIRE(doc2[0] == 4);
|
||||
REQUIRE(doc2[1] == 2);
|
||||
}
|
||||
|
||||
TEST_CASE("deserializeJson(JsonDocument&, ElementProxy)") {
|
||||
JsonDocument doc1;
|
||||
doc1[0] = "[4,2]";
|
||||
|
||||
JsonDocument doc2;
|
||||
DeserializationError err = deserializeJson(doc2, doc1[0]);
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc2.size() == 2);
|
||||
REQUIRE(doc2[0] == 4);
|
||||
REQUIRE(doc2[1] == 2);
|
||||
}
|
||||
49
ArduinoJson/extras/tests/JsonDeserializer/misc.cpp
Normal file
49
ArduinoJson/extras/tests/JsonDeserializer/misc.cpp
Normal file
@@ -0,0 +1,49 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2025, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <catch.hpp>
|
||||
|
||||
#include "Allocators.hpp"
|
||||
|
||||
using ArduinoJson::detail::sizeofArray;
|
||||
|
||||
TEST_CASE("deserializeJson() misc cases") {
|
||||
SpyingAllocator spy;
|
||||
JsonDocument doc(&spy);
|
||||
|
||||
SECTION("null") {
|
||||
DeserializationError err = deserializeJson(doc, "null");
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.is<float>() == false);
|
||||
}
|
||||
|
||||
SECTION("true") {
|
||||
DeserializationError err = deserializeJson(doc, "true");
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.is<bool>());
|
||||
REQUIRE(doc.as<bool>() == true);
|
||||
}
|
||||
|
||||
SECTION("false") {
|
||||
DeserializationError err = deserializeJson(doc, "false");
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.is<bool>());
|
||||
REQUIRE(doc.as<bool>() == false);
|
||||
}
|
||||
|
||||
SECTION("Should clear the JsonVariant") {
|
||||
deserializeJson(doc, "[1,2,3]");
|
||||
spy.clearLog();
|
||||
|
||||
deserializeJson(doc, "{}");
|
||||
|
||||
REQUIRE(doc.is<JsonObject>());
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Deallocate(sizeofArray(3)),
|
||||
});
|
||||
}
|
||||
}
|
||||
105
ArduinoJson/extras/tests/JsonDeserializer/nestingLimit.cpp
Normal file
105
ArduinoJson/extras/tests/JsonDeserializer/nestingLimit.cpp
Normal file
@@ -0,0 +1,105 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2025, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <catch.hpp>
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include "Literals.hpp"
|
||||
|
||||
#define SHOULD_WORK(expression) REQUIRE(DeserializationError::Ok == expression);
|
||||
#define SHOULD_FAIL(expression) \
|
||||
REQUIRE(DeserializationError::TooDeep == expression);
|
||||
|
||||
TEST_CASE("JsonDeserializer nesting") {
|
||||
JsonDocument doc;
|
||||
|
||||
SECTION("Input = const char*") {
|
||||
SECTION("limit = 0") {
|
||||
DeserializationOption::NestingLimit nesting(0);
|
||||
SHOULD_WORK(deserializeJson(doc, "\"toto\"", nesting));
|
||||
SHOULD_WORK(deserializeJson(doc, "123", nesting));
|
||||
SHOULD_WORK(deserializeJson(doc, "true", nesting));
|
||||
SHOULD_FAIL(deserializeJson(doc, "[]", nesting));
|
||||
SHOULD_FAIL(deserializeJson(doc, "{}", nesting));
|
||||
SHOULD_FAIL(deserializeJson(doc, "[\"toto\"]", nesting));
|
||||
SHOULD_FAIL(deserializeJson(doc, "{\"toto\":1}", nesting));
|
||||
}
|
||||
|
||||
SECTION("limit = 1") {
|
||||
DeserializationOption::NestingLimit nesting(1);
|
||||
SHOULD_WORK(deserializeJson(doc, "[\"toto\"]", nesting));
|
||||
SHOULD_WORK(deserializeJson(doc, "{\"toto\":1}", nesting));
|
||||
SHOULD_FAIL(deserializeJson(doc, "{\"toto\":{}}", nesting));
|
||||
SHOULD_FAIL(deserializeJson(doc, "{\"toto\":[]}", nesting));
|
||||
SHOULD_FAIL(deserializeJson(doc, "[[\"toto\"]]", nesting));
|
||||
SHOULD_FAIL(deserializeJson(doc, "[{\"toto\":1}]", nesting));
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("char* and size_t") {
|
||||
SECTION("limit = 0") {
|
||||
DeserializationOption::NestingLimit nesting(0);
|
||||
SHOULD_WORK(deserializeJson(doc, "\"toto\"", 6, nesting));
|
||||
SHOULD_WORK(deserializeJson(doc, "123", 3, nesting));
|
||||
SHOULD_WORK(deserializeJson(doc, "true", 4, nesting));
|
||||
SHOULD_FAIL(deserializeJson(doc, "[]", 2, nesting));
|
||||
SHOULD_FAIL(deserializeJson(doc, "{}", 2, nesting));
|
||||
SHOULD_FAIL(deserializeJson(doc, "[\"toto\"]", 8, nesting));
|
||||
SHOULD_FAIL(deserializeJson(doc, "{\"toto\":1}", 10, nesting));
|
||||
}
|
||||
|
||||
SECTION("limit = 1") {
|
||||
DeserializationOption::NestingLimit nesting(1);
|
||||
SHOULD_WORK(deserializeJson(doc, "[\"toto\"]", 8, nesting));
|
||||
SHOULD_WORK(deserializeJson(doc, "{\"toto\":1}", 10, nesting));
|
||||
SHOULD_FAIL(deserializeJson(doc, "{\"toto\":{}}", 11, nesting));
|
||||
SHOULD_FAIL(deserializeJson(doc, "{\"toto\":[]}", 11, nesting));
|
||||
SHOULD_FAIL(deserializeJson(doc, "[[\"toto\"]]", 10, nesting));
|
||||
SHOULD_FAIL(deserializeJson(doc, "[{\"toto\":1}]", 12, nesting));
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Input = std::string") {
|
||||
SECTION("limit = 0") {
|
||||
DeserializationOption::NestingLimit nesting(0);
|
||||
SHOULD_WORK(deserializeJson(doc, "\"toto\""_s, nesting));
|
||||
SHOULD_WORK(deserializeJson(doc, "123"_s, nesting));
|
||||
SHOULD_WORK(deserializeJson(doc, "true"_s, nesting));
|
||||
SHOULD_FAIL(deserializeJson(doc, "[]"_s, nesting));
|
||||
SHOULD_FAIL(deserializeJson(doc, "{}"_s, nesting));
|
||||
SHOULD_FAIL(deserializeJson(doc, "[\"toto\"]"_s, nesting));
|
||||
SHOULD_FAIL(deserializeJson(doc, "{\"toto\":1}"_s, nesting));
|
||||
}
|
||||
|
||||
SECTION("limit = 1") {
|
||||
DeserializationOption::NestingLimit nesting(1);
|
||||
SHOULD_WORK(deserializeJson(doc, "[\"toto\"]"_s, nesting));
|
||||
SHOULD_WORK(deserializeJson(doc, "{\"toto\":1}"_s, nesting));
|
||||
SHOULD_FAIL(deserializeJson(doc, "{\"toto\":{}}"_s, nesting));
|
||||
SHOULD_FAIL(deserializeJson(doc, "{\"toto\":[]}"_s, nesting));
|
||||
SHOULD_FAIL(deserializeJson(doc, "[[\"toto\"]]"_s, nesting));
|
||||
SHOULD_FAIL(deserializeJson(doc, "[{\"toto\":1}]"_s, nesting));
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Input = std::istream") {
|
||||
SECTION("limit = 0") {
|
||||
DeserializationOption::NestingLimit nesting(0);
|
||||
std::istringstream good("true");
|
||||
std::istringstream bad("[]");
|
||||
SHOULD_WORK(deserializeJson(doc, good, nesting));
|
||||
SHOULD_FAIL(deserializeJson(doc, bad, nesting));
|
||||
}
|
||||
|
||||
SECTION("limit = 1") {
|
||||
DeserializationOption::NestingLimit nesting(1);
|
||||
std::istringstream good("[\"toto\"]");
|
||||
std::istringstream bad("{\"toto\":{}}");
|
||||
SHOULD_WORK(deserializeJson(doc, good, nesting));
|
||||
SHOULD_FAIL(deserializeJson(doc, bad, nesting));
|
||||
}
|
||||
}
|
||||
}
|
||||
133
ArduinoJson/extras/tests/JsonDeserializer/number.cpp
Normal file
133
ArduinoJson/extras/tests/JsonDeserializer/number.cpp
Normal file
@@ -0,0 +1,133 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2025, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#define ARDUINOJSON_USE_LONG_LONG 0
|
||||
#define ARDUINOJSON_ENABLE_NAN 1
|
||||
#define ARDUINOJSON_ENABLE_INFINITY 1
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <limits.h>
|
||||
#include <catch.hpp>
|
||||
|
||||
namespace my {
|
||||
using ArduinoJson::detail::isinf;
|
||||
using ArduinoJson::detail::isnan;
|
||||
} // namespace my
|
||||
|
||||
TEST_CASE("deserialize an integer") {
|
||||
JsonDocument doc;
|
||||
|
||||
SECTION("Integer") {
|
||||
SECTION("0") {
|
||||
DeserializationError err = deserializeJson(doc, "0");
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.is<int>() == true);
|
||||
REQUIRE(doc.as<int>() == 0);
|
||||
REQUIRE(doc.as<std::string>() == "0"); // issue #808
|
||||
}
|
||||
|
||||
SECTION("Negative") {
|
||||
DeserializationError err = deserializeJson(doc, "-42");
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.is<int>());
|
||||
REQUIRE_FALSE(doc.is<bool>());
|
||||
REQUIRE(doc.as<int>() == -42);
|
||||
}
|
||||
|
||||
#if LONG_MAX == 2147483647
|
||||
SECTION("LONG_MAX") {
|
||||
DeserializationError err = deserializeJson(doc, "2147483647");
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.is<long>() == true);
|
||||
REQUIRE(doc.as<long>() == LONG_MAX);
|
||||
}
|
||||
|
||||
SECTION("LONG_MAX + 1") {
|
||||
DeserializationError err = deserializeJson(doc, "2147483648");
|
||||
|
||||
CAPTURE(LONG_MIN);
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.is<long>() == false);
|
||||
REQUIRE(doc.is<float>() == true);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if LONG_MIN == -2147483648
|
||||
SECTION("LONG_MIN") {
|
||||
DeserializationError err = deserializeJson(doc, "-2147483648");
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.is<long>() == true);
|
||||
REQUIRE(doc.as<long>() == LONG_MIN);
|
||||
}
|
||||
|
||||
SECTION("LONG_MIN - 1") {
|
||||
DeserializationError err = deserializeJson(doc, "-2147483649");
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.is<long>() == false);
|
||||
REQUIRE(doc.is<float>() == true);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ULONG_MAX == 4294967295
|
||||
SECTION("ULONG_MAX") {
|
||||
DeserializationError err = deserializeJson(doc, "4294967295");
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.is<unsigned long>() == true);
|
||||
REQUIRE(doc.as<unsigned long>() == ULONG_MAX);
|
||||
REQUIRE(doc.is<long>() == false);
|
||||
}
|
||||
|
||||
SECTION("ULONG_MAX + 1") {
|
||||
DeserializationError err = deserializeJson(doc, "4294967296");
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.is<unsigned long>() == false);
|
||||
REQUIRE(doc.is<float>() == true);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
SECTION("Floats") {
|
||||
SECTION("Double") {
|
||||
DeserializationError err = deserializeJson(doc, "-1.23e+4");
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE_FALSE(doc.is<int>());
|
||||
REQUIRE(doc.is<double>());
|
||||
REQUIRE(doc.as<double>() == Approx(-1.23e+4));
|
||||
}
|
||||
|
||||
SECTION("NaN") {
|
||||
DeserializationError err = deserializeJson(doc, "NaN");
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.is<float>() == true);
|
||||
REQUIRE(my::isnan(doc.as<float>()));
|
||||
}
|
||||
|
||||
SECTION("Infinity") {
|
||||
DeserializationError err = deserializeJson(doc, "Infinity");
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.is<float>() == true);
|
||||
REQUIRE(my::isinf(doc.as<float>()));
|
||||
}
|
||||
|
||||
SECTION("+Infinity") {
|
||||
DeserializationError err = deserializeJson(doc, "+Infinity");
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.is<float>() == true);
|
||||
REQUIRE(my::isinf(doc.as<float>()));
|
||||
}
|
||||
|
||||
SECTION("-Infinity") {
|
||||
DeserializationError err = deserializeJson(doc, "-Infinity");
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.is<float>() == true);
|
||||
REQUIRE(my::isinf(doc.as<float>()));
|
||||
}
|
||||
}
|
||||
}
|
||||
400
ArduinoJson/extras/tests/JsonDeserializer/object.cpp
Normal file
400
ArduinoJson/extras/tests/JsonDeserializer/object.cpp
Normal file
@@ -0,0 +1,400 @@
|
||||
// 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;
|
||||
|
||||
TEST_CASE("deserialize JSON object") {
|
||||
SpyingAllocator spy;
|
||||
JsonDocument doc(&spy);
|
||||
|
||||
SECTION("An empty object") {
|
||||
DeserializationError err = deserializeJson(doc, "{}");
|
||||
JsonObject obj = doc.as<JsonObject>();
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.is<JsonObject>());
|
||||
REQUIRE(obj.size() == 0);
|
||||
}
|
||||
|
||||
SECTION("Quotes") {
|
||||
SECTION("Double quotes") {
|
||||
DeserializationError err = deserializeJson(doc, "{\"key\":\"value\"}");
|
||||
JsonObject obj = doc.as<JsonObject>();
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.is<JsonObject>());
|
||||
REQUIRE(obj.size() == 1);
|
||||
REQUIRE(obj["key"] == "value");
|
||||
}
|
||||
|
||||
SECTION("Single quotes") {
|
||||
DeserializationError err = deserializeJson(doc, "{'key':'value'}");
|
||||
JsonObject obj = doc.as<JsonObject>();
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.is<JsonObject>());
|
||||
REQUIRE(obj.size() == 1);
|
||||
REQUIRE(obj["key"] == "value");
|
||||
}
|
||||
|
||||
SECTION("No quotes") {
|
||||
DeserializationError err = deserializeJson(doc, "{key:'value'}");
|
||||
JsonObject obj = doc.as<JsonObject>();
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.is<JsonObject>());
|
||||
REQUIRE(obj.size() == 1);
|
||||
REQUIRE(obj["key"] == "value");
|
||||
}
|
||||
|
||||
SECTION("No quotes, allow underscore in key") {
|
||||
DeserializationError err = deserializeJson(doc, "{_k_e_y_:42}");
|
||||
JsonObject obj = doc.as<JsonObject>();
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.is<JsonObject>());
|
||||
REQUIRE(obj.size() == 1);
|
||||
REQUIRE(obj["_k_e_y_"] == 42);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Spaces") {
|
||||
SECTION("Before the key") {
|
||||
DeserializationError err = deserializeJson(doc, "{ \"key\":\"value\"}");
|
||||
JsonObject obj = doc.as<JsonObject>();
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.is<JsonObject>());
|
||||
REQUIRE(obj.size() == 1);
|
||||
REQUIRE(obj["key"] == "value");
|
||||
}
|
||||
|
||||
SECTION("After the key") {
|
||||
DeserializationError err = deserializeJson(doc, "{\"key\" :\"value\"}");
|
||||
JsonObject obj = doc.as<JsonObject>();
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.is<JsonObject>());
|
||||
REQUIRE(obj.size() == 1);
|
||||
REQUIRE(obj["key"] == "value");
|
||||
}
|
||||
|
||||
SECTION("Before the value") {
|
||||
DeserializationError err = deserializeJson(doc, "{\"key\": \"value\"}");
|
||||
JsonObject obj = doc.as<JsonObject>();
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.is<JsonObject>());
|
||||
REQUIRE(obj.size() == 1);
|
||||
REQUIRE(obj["key"] == "value");
|
||||
}
|
||||
|
||||
SECTION("After the value") {
|
||||
DeserializationError err = deserializeJson(doc, "{\"key\":\"value\" }");
|
||||
JsonObject obj = doc.as<JsonObject>();
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.is<JsonObject>());
|
||||
REQUIRE(obj.size() == 1);
|
||||
REQUIRE(obj["key"] == "value");
|
||||
}
|
||||
|
||||
SECTION("Before the comma") {
|
||||
DeserializationError err =
|
||||
deserializeJson(doc, "{\"key1\":\"value1\" ,\"key2\":\"value2\"}");
|
||||
JsonObject obj = doc.as<JsonObject>();
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.is<JsonObject>());
|
||||
REQUIRE(obj.size() == 2);
|
||||
REQUIRE(obj["key1"] == "value1");
|
||||
REQUIRE(obj["key2"] == "value2");
|
||||
}
|
||||
|
||||
SECTION("After the comma") {
|
||||
DeserializationError err =
|
||||
deserializeJson(doc, "{\"key1\":\"value1\", \"key2\":\"value2\"}");
|
||||
JsonObject obj = doc.as<JsonObject>();
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.is<JsonObject>());
|
||||
REQUIRE(obj.size() == 2);
|
||||
REQUIRE(obj["key1"] == "value1");
|
||||
REQUIRE(obj["key2"] == "value2");
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Values types") {
|
||||
SECTION("String") {
|
||||
DeserializationError err =
|
||||
deserializeJson(doc, "{\"key1\":\"value1\",\"key2\":\"value2\"}");
|
||||
JsonObject obj = doc.as<JsonObject>();
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.is<JsonObject>());
|
||||
REQUIRE(obj.size() == 2);
|
||||
REQUIRE(obj["key1"] == "value1");
|
||||
REQUIRE(obj["key2"] == "value2");
|
||||
}
|
||||
|
||||
SECTION("Integer") {
|
||||
DeserializationError err =
|
||||
deserializeJson(doc, "{\"key1\":42,\"key2\":-42}");
|
||||
JsonObject obj = doc.as<JsonObject>();
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.is<JsonObject>());
|
||||
REQUIRE(obj.size() == 2);
|
||||
REQUIRE(obj["key1"] == 42);
|
||||
REQUIRE(obj["key2"] == -42);
|
||||
}
|
||||
|
||||
SECTION("Float") {
|
||||
DeserializationError err =
|
||||
deserializeJson(doc, "{\"key1\":12.345,\"key2\":-7E3}");
|
||||
JsonObject obj = doc.as<JsonObject>();
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.is<JsonObject>());
|
||||
REQUIRE(obj.size() == 2);
|
||||
REQUIRE(obj["key1"].as<float>() == Approx(12.345f));
|
||||
REQUIRE(obj["key2"] == -7E3f);
|
||||
}
|
||||
|
||||
SECTION("Double") {
|
||||
DeserializationError err =
|
||||
deserializeJson(doc, "{\"key1\":12.3456789,\"key2\":-7E89}");
|
||||
JsonObject obj = doc.as<JsonObject>();
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.is<JsonObject>());
|
||||
REQUIRE(obj.size() == 2);
|
||||
REQUIRE(obj["key1"].as<double>() == Approx(12.3456789));
|
||||
REQUIRE(obj["key2"] == -7E89);
|
||||
}
|
||||
|
||||
SECTION("Booleans") {
|
||||
DeserializationError err =
|
||||
deserializeJson(doc, "{\"key1\":true,\"key2\":false}");
|
||||
JsonObject obj = doc.as<JsonObject>();
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.is<JsonObject>());
|
||||
REQUIRE(obj.size() == 2);
|
||||
REQUIRE(obj["key1"] == true);
|
||||
REQUIRE(obj["key2"] == false);
|
||||
}
|
||||
|
||||
SECTION("Null") {
|
||||
DeserializationError err =
|
||||
deserializeJson(doc, "{\"key1\":null,\"key2\":null}");
|
||||
JsonObject obj = doc.as<JsonObject>();
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.is<JsonObject>());
|
||||
REQUIRE(obj.size() == 2);
|
||||
REQUIRE(obj["key1"].as<const char*>() == 0);
|
||||
REQUIRE(obj["key2"].as<const char*>() == 0);
|
||||
}
|
||||
|
||||
SECTION("Array") {
|
||||
char jsonString[] = " { \"ab\" : [ 1 , 2 ] , \"cd\" : [ 3 , 4 ] } ";
|
||||
|
||||
DeserializationError err = deserializeJson(doc, jsonString);
|
||||
JsonObject obj = doc.as<JsonObject>();
|
||||
|
||||
JsonArray array1 = obj["ab"];
|
||||
const JsonArray array2 = obj["cd"];
|
||||
JsonArray array3 = obj["ef"];
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
|
||||
REQUIRE(array1.isNull() == false);
|
||||
REQUIRE(array2.isNull() == false);
|
||||
REQUIRE(array3.isNull() == true);
|
||||
|
||||
REQUIRE(2 == array1.size());
|
||||
REQUIRE(2 == array2.size());
|
||||
REQUIRE(0 == array3.size());
|
||||
|
||||
REQUIRE(1 == array1[0].as<int>());
|
||||
REQUIRE(2 == array1[1].as<int>());
|
||||
|
||||
REQUIRE(3 == array2[0].as<int>());
|
||||
REQUIRE(4 == array2[1].as<int>());
|
||||
|
||||
REQUIRE(0 == array3[0].as<int>());
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Premature null terminator") {
|
||||
SECTION("After opening brace") {
|
||||
DeserializationError err = deserializeJson(doc, "{");
|
||||
|
||||
REQUIRE(err == DeserializationError::IncompleteInput);
|
||||
}
|
||||
|
||||
SECTION("After key") {
|
||||
DeserializationError err = deserializeJson(doc, "{\"hello\"");
|
||||
|
||||
REQUIRE(err == DeserializationError::IncompleteInput);
|
||||
}
|
||||
|
||||
SECTION("After colon") {
|
||||
DeserializationError err = deserializeJson(doc, "{\"hello\":");
|
||||
|
||||
REQUIRE(err == DeserializationError::IncompleteInput);
|
||||
}
|
||||
|
||||
SECTION("After value") {
|
||||
DeserializationError err = deserializeJson(doc, "{\"hello\":\"world\"");
|
||||
|
||||
REQUIRE(err == DeserializationError::IncompleteInput);
|
||||
}
|
||||
|
||||
SECTION("After comma") {
|
||||
DeserializationError err = deserializeJson(doc, "{\"hello\":\"world\",");
|
||||
|
||||
REQUIRE(err == DeserializationError::IncompleteInput);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Misc") {
|
||||
SECTION("A quoted key without value") {
|
||||
DeserializationError err = deserializeJson(doc, "{\"key\"}");
|
||||
|
||||
REQUIRE(err == DeserializationError::InvalidInput);
|
||||
}
|
||||
|
||||
SECTION("A non-quoted key without value") {
|
||||
DeserializationError err = deserializeJson(doc, "{key}");
|
||||
|
||||
REQUIRE(err == DeserializationError::InvalidInput);
|
||||
}
|
||||
|
||||
SECTION("A dangling comma") {
|
||||
DeserializationError err = deserializeJson(doc, "{\"key1\":\"value1\",}");
|
||||
|
||||
REQUIRE(err == DeserializationError::InvalidInput);
|
||||
}
|
||||
|
||||
SECTION("null as a key") {
|
||||
DeserializationError err = deserializeJson(doc, "{null:\"value\"}");
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
}
|
||||
|
||||
SECTION("Repeated key") {
|
||||
DeserializationError err =
|
||||
deserializeJson(doc, "{alfa:{bravo:{charlie:1}},alfa:2}");
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.as<std::string>() == "{\"alfa\":2}");
|
||||
REQUIRE(spy.log() ==
|
||||
AllocatorLog{
|
||||
Allocate(sizeofStringBuffer()),
|
||||
Allocate(sizeofPool()),
|
||||
Reallocate(sizeofStringBuffer(), sizeofString("alfa")),
|
||||
Allocate(sizeofStringBuffer()),
|
||||
Reallocate(sizeofStringBuffer(), sizeofString("bravo")),
|
||||
Allocate(sizeofStringBuffer()),
|
||||
Reallocate(sizeofStringBuffer(), sizeofString("charlie")),
|
||||
Allocate(sizeofStringBuffer()),
|
||||
Deallocate(sizeofString("bravo")),
|
||||
Deallocate(sizeofString("charlie")),
|
||||
Deallocate(sizeofStringBuffer()),
|
||||
Reallocate(sizeofPool(), sizeofObject(2) + sizeofObject(1)),
|
||||
});
|
||||
}
|
||||
|
||||
SECTION("Repeated key with zero copy mode") { // issue #1697
|
||||
char input[] = "{a:{b:{c:1}},a:2}";
|
||||
DeserializationError err = deserializeJson(doc, input);
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc["a"] == 2);
|
||||
}
|
||||
|
||||
SECTION("NUL in keys") {
|
||||
DeserializationError err =
|
||||
deserializeJson(doc, "{\"x\":0,\"x\\u0000a\":1,\"x\\u0000b\":2}");
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.as<std::string>() ==
|
||||
"{\"x\":0,\"x\\u0000a\":1,\"x\\u0000b\":2}");
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Should clear the JsonObject") {
|
||||
deserializeJson(doc, "{\"hello\":\"world\"}");
|
||||
spy.clearLog();
|
||||
|
||||
deserializeJson(doc, "{}");
|
||||
|
||||
REQUIRE(doc.is<JsonObject>());
|
||||
REQUIRE(doc.size() == 0);
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Deallocate(sizeofObject(1)),
|
||||
Deallocate(sizeofString("hello")),
|
||||
Deallocate(sizeofString("world")),
|
||||
});
|
||||
}
|
||||
|
||||
SECTION("Issue #1335") {
|
||||
std::string json("{\"a\":{},\"b\":{}}");
|
||||
deserializeJson(doc, json);
|
||||
CHECK(doc.as<std::string>() == json);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("deserialize JSON object under memory constraints") {
|
||||
TimebombAllocator timebomb(1024);
|
||||
JsonDocument doc(&timebomb);
|
||||
|
||||
SECTION("empty object requires no allocation") {
|
||||
timebomb.setCountdown(0);
|
||||
char input[] = "{}";
|
||||
|
||||
DeserializationError err = deserializeJson(doc, input);
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.as<std::string>() == "{}");
|
||||
}
|
||||
|
||||
SECTION("key allocation fails") {
|
||||
timebomb.setCountdown(0);
|
||||
char input[] = "{\"a\":1}";
|
||||
|
||||
DeserializationError err = deserializeJson(doc, input);
|
||||
|
||||
REQUIRE(err == DeserializationError::NoMemory);
|
||||
REQUIRE(doc.as<std::string>() == "{}");
|
||||
}
|
||||
|
||||
SECTION("pool allocation fails") {
|
||||
timebomb.setCountdown(1);
|
||||
char input[] = "{\"a\":1}";
|
||||
|
||||
DeserializationError err = deserializeJson(doc, input);
|
||||
|
||||
REQUIRE(err == DeserializationError::NoMemory);
|
||||
REQUIRE(doc.as<std::string>() == "{}");
|
||||
}
|
||||
|
||||
SECTION("string allocation fails") {
|
||||
timebomb.setCountdown(3);
|
||||
char input[] = "{\"alfa\":\"bravo\"}";
|
||||
|
||||
DeserializationError err = deserializeJson(doc, input);
|
||||
|
||||
REQUIRE(err == DeserializationError::NoMemory);
|
||||
REQUIRE(doc.as<std::string>() == "{\"alfa\":null}");
|
||||
}
|
||||
}
|
||||
214
ArduinoJson/extras/tests/JsonDeserializer/string.cpp
Normal file
214
ArduinoJson/extras/tests/JsonDeserializer/string.cpp
Normal file
@@ -0,0 +1,214 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2025, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#define ARDUINOJSON_DECODE_UNICODE 1
|
||||
#include <ArduinoJson.h>
|
||||
#include <catch.hpp>
|
||||
|
||||
#include "Allocators.hpp"
|
||||
|
||||
using ArduinoJson::detail::sizeofArray;
|
||||
using ArduinoJson::detail::sizeofObject;
|
||||
|
||||
TEST_CASE("Valid JSON strings value") {
|
||||
struct TestCase {
|
||||
const char* input;
|
||||
const char* expectedOutput;
|
||||
};
|
||||
|
||||
TestCase testCases[] = {
|
||||
{"\"hello world\"", "hello world"},
|
||||
{"\'hello world\'", "hello world"},
|
||||
{"'\"'", "\""},
|
||||
{"'\\\\'", "\\"},
|
||||
{"'\\/'", "/"},
|
||||
{"'\\b'", "\b"},
|
||||
{"'\\f'", "\f"},
|
||||
{"'\\n'", "\n"},
|
||||
{"'\\r'", "\r"},
|
||||
{"'\\t'", "\t"},
|
||||
{"\"1\\\"2\\\\3\\/4\\b5\\f6\\n7\\r8\\t9\"", "1\"2\\3/4\b5\f6\n7\r8\t9"},
|
||||
{"'\\u0041'", "A"},
|
||||
{"'\\u00e4'", "\xc3\xa4"}, // ä
|
||||
{"'\\u00E4'", "\xc3\xa4"}, // ä
|
||||
{"'\\u3042'", "\xe3\x81\x82"}, // あ
|
||||
{"'\\ud83d\\udda4'", "\xf0\x9f\x96\xa4"}, // 🖤
|
||||
{"'\\uF053'", "\xef\x81\x93"}, // issue #1173
|
||||
{"'\\uF015'", "\xef\x80\x95"}, // issue #1173
|
||||
{"'\\uF054'", "\xef\x81\x94"}, // issue #1173
|
||||
};
|
||||
const size_t testCount = sizeof(testCases) / sizeof(testCases[0]);
|
||||
|
||||
JsonDocument doc;
|
||||
|
||||
for (size_t i = 0; i < testCount; i++) {
|
||||
const TestCase& testCase = testCases[i];
|
||||
CAPTURE(testCase.input);
|
||||
DeserializationError err = deserializeJson(doc, testCase.input);
|
||||
CHECK(err == DeserializationError::Ok);
|
||||
CHECK(doc.as<std::string>() == testCase.expectedOutput);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("\\u0000") {
|
||||
JsonDocument doc;
|
||||
|
||||
DeserializationError err = deserializeJson(doc, "\"wx\\u0000yz\"");
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
|
||||
const char* result = doc.as<const char*>();
|
||||
CHECK(result[0] == 'w');
|
||||
CHECK(result[1] == 'x');
|
||||
CHECK(result[2] == 0);
|
||||
CHECK(result[3] == 'y');
|
||||
CHECK(result[4] == 'z');
|
||||
CHECK(result[5] == 0);
|
||||
|
||||
CHECK(doc.as<JsonString>().size() == 5);
|
||||
CHECK(doc.as<std::string>().size() == 5);
|
||||
}
|
||||
|
||||
TEST_CASE("Truncated JSON string") {
|
||||
const char* testCases[] = {"\"hello", "\'hello", "'\\u", "'\\u00", "'\\u000"};
|
||||
const size_t testCount = sizeof(testCases) / sizeof(testCases[0]);
|
||||
|
||||
JsonDocument doc;
|
||||
|
||||
for (size_t i = 0; i < testCount; i++) {
|
||||
const char* input = testCases[i];
|
||||
CAPTURE(input);
|
||||
REQUIRE(deserializeJson(doc, input) ==
|
||||
DeserializationError::IncompleteInput);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Escape single quote in single quoted string") {
|
||||
JsonDocument doc;
|
||||
|
||||
DeserializationError err = deserializeJson(doc, "'ab\\\'cd'");
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
CHECK(doc.as<std::string>() == "ab\'cd");
|
||||
}
|
||||
|
||||
TEST_CASE("Escape double quote in double quoted string") {
|
||||
JsonDocument doc;
|
||||
|
||||
DeserializationError err = deserializeJson(doc, "'ab\\\"cd'");
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
CHECK(doc.as<std::string>() == "ab\"cd");
|
||||
}
|
||||
|
||||
TEST_CASE("Invalid JSON string") {
|
||||
const char* testCases[] = {"'\\u'", "'\\u000g'", "'\\u000'",
|
||||
"'\\u000G'", "'\\u000/'", "'\\x1234'"};
|
||||
const size_t testCount = sizeof(testCases) / sizeof(testCases[0]);
|
||||
|
||||
JsonDocument doc;
|
||||
|
||||
for (size_t i = 0; i < testCount; i++) {
|
||||
const char* input = testCases[i];
|
||||
CAPTURE(input);
|
||||
REQUIRE(deserializeJson(doc, input) == DeserializationError::InvalidInput);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Allocation of the key fails") {
|
||||
TimebombAllocator timebomb(0);
|
||||
SpyingAllocator spy(&timebomb);
|
||||
JsonDocument doc(&spy);
|
||||
|
||||
SECTION("Quoted string, first member") {
|
||||
REQUIRE(deserializeJson(doc, "{\"example\":1}") ==
|
||||
DeserializationError::NoMemory);
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
AllocateFail(sizeofStringBuffer()),
|
||||
});
|
||||
}
|
||||
|
||||
SECTION("Quoted string, second member") {
|
||||
timebomb.setCountdown(3);
|
||||
REQUIRE(deserializeJson(doc, "{\"hello\":1,\"world\"}") ==
|
||||
DeserializationError::NoMemory);
|
||||
REQUIRE(spy.log() ==
|
||||
AllocatorLog{
|
||||
Allocate(sizeofStringBuffer()),
|
||||
Allocate(sizeofPool()),
|
||||
Reallocate(sizeofStringBuffer(), sizeofString("hello")),
|
||||
AllocateFail(sizeofStringBuffer()),
|
||||
ReallocateFail(sizeofPool(), sizeofObject(1)),
|
||||
});
|
||||
}
|
||||
|
||||
SECTION("Non-Quoted string, first member") {
|
||||
REQUIRE(deserializeJson(doc, "{example:1}") ==
|
||||
DeserializationError::NoMemory);
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
AllocateFail(sizeofStringBuffer()),
|
||||
});
|
||||
}
|
||||
|
||||
SECTION("Non-Quoted string, second member") {
|
||||
timebomb.setCountdown(3);
|
||||
REQUIRE(deserializeJson(doc, "{hello:1,world}") ==
|
||||
DeserializationError::NoMemory);
|
||||
REQUIRE(spy.log() ==
|
||||
AllocatorLog{
|
||||
Allocate(sizeofStringBuffer()),
|
||||
Allocate(sizeofPool()),
|
||||
Reallocate(sizeofStringBuffer(), sizeofString("hello")),
|
||||
AllocateFail(sizeofStringBuffer()),
|
||||
ReallocateFail(sizeofPool(), sizeofObject(1)),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("String allocation fails") {
|
||||
SpyingAllocator spy(FailingAllocator::instance());
|
||||
JsonDocument doc(&spy);
|
||||
|
||||
SECTION("Input is const char*") {
|
||||
REQUIRE(deserializeJson(doc, "\"hello\"") ==
|
||||
DeserializationError::NoMemory);
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
AllocateFail(sizeofStringBuffer()),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Deduplicate values") {
|
||||
SpyingAllocator spy;
|
||||
JsonDocument doc(&spy);
|
||||
deserializeJson(doc, "[\"example\",\"example\"]");
|
||||
|
||||
CHECK(doc[0].as<const char*>() == doc[1].as<const char*>());
|
||||
REQUIRE(spy.log() ==
|
||||
AllocatorLog{
|
||||
Allocate(sizeofPool()),
|
||||
Allocate(sizeofStringBuffer()),
|
||||
Reallocate(sizeofStringBuffer(), sizeofString("example")),
|
||||
Allocate(sizeofStringBuffer()),
|
||||
Deallocate(sizeofStringBuffer()),
|
||||
Reallocate(sizeofPool(), sizeofArray(2)),
|
||||
});
|
||||
}
|
||||
|
||||
TEST_CASE("Deduplicate keys") {
|
||||
SpyingAllocator spy;
|
||||
JsonDocument doc(&spy);
|
||||
deserializeJson(doc, "[{\"example\":1},{\"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(sizeofStringBuffer()),
|
||||
Reallocate(sizeofStringBuffer(), sizeofString("example")),
|
||||
Allocate(sizeofStringBuffer()),
|
||||
Deallocate(sizeofStringBuffer()),
|
||||
Reallocate(sizeofPool(), sizeofArray(2) + 2 * sizeofObject(1)),
|
||||
});
|
||||
}
|
||||
31
ArduinoJson/extras/tests/JsonDocument/CMakeLists.txt
Normal file
31
ArduinoJson/extras/tests/JsonDocument/CMakeLists.txt
Normal 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"
|
||||
)
|
||||
297
ArduinoJson/extras/tests/JsonDocument/ElementProxy.cpp
Normal file
297
ArduinoJson/extras/tests/JsonDocument/ElementProxy.cpp
Normal 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\"]");
|
||||
}
|
||||
432
ArduinoJson/extras/tests/JsonDocument/MemberProxy.cpp
Normal file
432
ArduinoJson/extras/tests/JsonDocument/MemberProxy.cpp
Normal 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")),
|
||||
});
|
||||
}
|
||||
}
|
||||
176
ArduinoJson/extras/tests/JsonDocument/add.cpp
Normal file
176
ArduinoJson/extras/tests/JsonDocument/add.cpp
Normal 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()),
|
||||
});
|
||||
}
|
||||
}
|
||||
135
ArduinoJson/extras/tests/JsonDocument/assignment.cpp
Normal file
135
ArduinoJson/extras/tests/JsonDocument/assignment.cpp
Normal 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");
|
||||
}
|
||||
}
|
||||
18
ArduinoJson/extras/tests/JsonDocument/cast.cpp
Normal file
18
ArduinoJson/extras/tests/JsonDocument/cast.cpp
Normal 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\"}");
|
||||
}
|
||||
48
ArduinoJson/extras/tests/JsonDocument/clear.cpp
Normal file
48
ArduinoJson/extras/tests/JsonDocument/clear.cpp
Normal 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?
|
||||
}
|
||||
}
|
||||
29
ArduinoJson/extras/tests/JsonDocument/compare.cpp
Normal file
29
ArduinoJson/extras/tests/JsonDocument/compare.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
148
ArduinoJson/extras/tests/JsonDocument/constructor.cpp
Normal file
148
ArduinoJson/extras/tests/JsonDocument/constructor.cpp
Normal 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");
|
||||
}
|
||||
}
|
||||
39
ArduinoJson/extras/tests/JsonDocument/isNull.cpp
Normal file
39
ArduinoJson/extras/tests/JsonDocument/isNull.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
52
ArduinoJson/extras/tests/JsonDocument/issue1120.cpp
Normal file
52
ArduinoJson/extras/tests/JsonDocument/issue1120.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
30
ArduinoJson/extras/tests/JsonDocument/nesting.cpp
Normal file
30
ArduinoJson/extras/tests/JsonDocument/nesting.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
96
ArduinoJson/extras/tests/JsonDocument/overflowed.cpp
Normal file
96
ArduinoJson/extras/tests/JsonDocument/overflowed.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
85
ArduinoJson/extras/tests/JsonDocument/remove.cpp
Normal file
85
ArduinoJson/extras/tests/JsonDocument/remove.cpp
Normal 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]");
|
||||
}
|
||||
}
|
||||
111
ArduinoJson/extras/tests/JsonDocument/set.cpp
Normal file
111
ArduinoJson/extras/tests/JsonDocument/set.cpp
Normal 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
|
||||
}
|
||||
184
ArduinoJson/extras/tests/JsonDocument/shrinkToFit.cpp
Normal file
184
ArduinoJson/extras/tests/JsonDocument/shrinkToFit.cpp
Normal 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)),
|
||||
});
|
||||
}
|
||||
}
|
||||
28
ArduinoJson/extras/tests/JsonDocument/size.cpp
Normal file
28
ArduinoJson/extras/tests/JsonDocument/size.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
167
ArduinoJson/extras/tests/JsonDocument/subscript.cpp
Normal file
167
ArduinoJson/extras/tests/JsonDocument/subscript.cpp
Normal 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
|
||||
}
|
||||
25
ArduinoJson/extras/tests/JsonDocument/swap.cpp
Normal file
25
ArduinoJson/extras/tests/JsonDocument/swap.cpp
Normal 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");
|
||||
}
|
||||
}
|
||||
25
ArduinoJson/extras/tests/JsonObject/CMakeLists.txt
Normal file
25
ArduinoJson/extras/tests/JsonObject/CMakeLists.txt
Normal file
@@ -0,0 +1,25 @@
|
||||
# ArduinoJson - https://arduinojson.org
|
||||
# Copyright © 2014-2025, Benoit BLANCHON
|
||||
# MIT License
|
||||
|
||||
add_executable(JsonObjectTests
|
||||
clear.cpp
|
||||
compare.cpp
|
||||
equals.cpp
|
||||
isNull.cpp
|
||||
iterator.cpp
|
||||
nesting.cpp
|
||||
remove.cpp
|
||||
set.cpp
|
||||
size.cpp
|
||||
std_string.cpp
|
||||
subscript.cpp
|
||||
unbound.cpp
|
||||
)
|
||||
|
||||
add_test(JsonObject JsonObjectTests)
|
||||
|
||||
set_tests_properties(JsonObject
|
||||
PROPERTIES
|
||||
LABELS "Catch"
|
||||
)
|
||||
25
ArduinoJson/extras/tests/JsonObject/clear.cpp
Normal file
25
ArduinoJson/extras/tests/JsonObject/clear.cpp
Normal file
@@ -0,0 +1,25 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2025, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <catch.hpp>
|
||||
|
||||
TEST_CASE("JsonObject::clear()") {
|
||||
SECTION("No-op on null JsonObject") {
|
||||
JsonObject obj;
|
||||
obj.clear();
|
||||
REQUIRE(obj.isNull() == true);
|
||||
REQUIRE(obj.size() == 0);
|
||||
}
|
||||
|
||||
SECTION("Removes all elements") {
|
||||
JsonDocument doc;
|
||||
JsonObject obj = doc.to<JsonObject>();
|
||||
obj["hello"] = 1;
|
||||
obj["world"] = 2;
|
||||
obj.clear();
|
||||
REQUIRE(obj.size() == 0);
|
||||
REQUIRE(obj.isNull() == false);
|
||||
}
|
||||
}
|
||||
512
ArduinoJson/extras/tests/JsonObject/compare.cpp
Normal file
512
ArduinoJson/extras/tests/JsonObject/compare.cpp
Normal file
@@ -0,0 +1,512 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2025, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <catch.hpp>
|
||||
|
||||
TEST_CASE("Compare JsonObject with JsonObject") {
|
||||
JsonDocument doc;
|
||||
|
||||
SECTION("Compare with unbound") {
|
||||
JsonObject object = doc.to<JsonObject>();
|
||||
object["a"] = 1;
|
||||
object["b"] = "hello";
|
||||
JsonObject unbound;
|
||||
|
||||
CHECK(object != unbound);
|
||||
CHECK_FALSE(object == unbound);
|
||||
CHECK_FALSE(object <= unbound);
|
||||
CHECK_FALSE(object >= unbound);
|
||||
CHECK_FALSE(object > unbound);
|
||||
CHECK_FALSE(object < unbound);
|
||||
|
||||
CHECK(unbound != object);
|
||||
CHECK_FALSE(unbound == object);
|
||||
CHECK_FALSE(unbound <= object);
|
||||
CHECK_FALSE(unbound >= object);
|
||||
CHECK_FALSE(unbound > object);
|
||||
CHECK_FALSE(unbound < object);
|
||||
}
|
||||
|
||||
SECTION("Compare with self") {
|
||||
JsonObject object = doc.to<JsonObject>();
|
||||
object["a"] = 1;
|
||||
object["b"] = "hello";
|
||||
|
||||
CHECK(object == object);
|
||||
CHECK(object <= object);
|
||||
CHECK(object >= object);
|
||||
CHECK_FALSE(object != object);
|
||||
CHECK_FALSE(object > object);
|
||||
CHECK_FALSE(object < object);
|
||||
}
|
||||
|
||||
SECTION("Compare with identical object") {
|
||||
JsonObject object1 = doc.add<JsonObject>();
|
||||
object1["a"] = 1;
|
||||
object1["b"] = "hello";
|
||||
object1["c"][0] = false;
|
||||
|
||||
JsonObject object2 = doc.add<JsonObject>();
|
||||
object2["a"] = 1;
|
||||
object2["b"] = "hello";
|
||||
object2["c"][0] = false;
|
||||
|
||||
CHECK(object1 == object2);
|
||||
CHECK(object1 <= object2);
|
||||
CHECK(object1 >= object2);
|
||||
CHECK_FALSE(object1 != object2);
|
||||
CHECK_FALSE(object1 > object2);
|
||||
CHECK_FALSE(object1 < object2);
|
||||
}
|
||||
|
||||
SECTION("Compare with different object") {
|
||||
JsonObject object1 = doc.add<JsonObject>();
|
||||
object1["a"] = 1;
|
||||
object1["b"] = "hello1";
|
||||
object1["c"][0] = false;
|
||||
|
||||
JsonObject object2 = doc.add<JsonObject>();
|
||||
object2["a"] = 1;
|
||||
object2["b"] = "hello2";
|
||||
object2["c"][0] = false;
|
||||
|
||||
CHECK(object1 != object2);
|
||||
CHECK_FALSE(object1 == object2);
|
||||
CHECK_FALSE(object1 > object2);
|
||||
CHECK_FALSE(object1 < object2);
|
||||
CHECK_FALSE(object1 <= object2);
|
||||
CHECK_FALSE(object1 >= object2);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Compare JsonObject with JsonVariant") {
|
||||
JsonDocument doc;
|
||||
|
||||
SECTION("Compare with self") {
|
||||
JsonObject object = doc.to<JsonObject>();
|
||||
object["a"] = 1;
|
||||
object["b"] = "hello";
|
||||
|
||||
JsonVariant variant = object;
|
||||
|
||||
CHECK(object == variant);
|
||||
CHECK(object <= variant);
|
||||
CHECK(object >= variant);
|
||||
CHECK_FALSE(object != variant);
|
||||
CHECK_FALSE(object > variant);
|
||||
CHECK_FALSE(object < variant);
|
||||
|
||||
CHECK(variant == object);
|
||||
CHECK(variant <= object);
|
||||
CHECK(variant >= object);
|
||||
CHECK_FALSE(variant != object);
|
||||
CHECK_FALSE(variant > object);
|
||||
CHECK_FALSE(variant < object);
|
||||
}
|
||||
|
||||
SECTION("Compare with identical object") {
|
||||
JsonObject object = doc.add<JsonObject>();
|
||||
object["a"] = 1;
|
||||
object["b"] = "hello";
|
||||
object["c"][0] = false;
|
||||
|
||||
JsonVariant variant = doc.add<JsonObject>();
|
||||
variant["a"] = 1;
|
||||
variant["b"] = "hello";
|
||||
variant["c"][0] = false;
|
||||
|
||||
CHECK(object == variant);
|
||||
CHECK(object <= variant);
|
||||
CHECK(object >= variant);
|
||||
CHECK_FALSE(object != variant);
|
||||
CHECK_FALSE(object > variant);
|
||||
CHECK_FALSE(object < variant);
|
||||
|
||||
CHECK(variant == object);
|
||||
CHECK(variant <= object);
|
||||
CHECK(variant >= object);
|
||||
CHECK_FALSE(variant != object);
|
||||
CHECK_FALSE(variant > object);
|
||||
CHECK_FALSE(variant < object);
|
||||
}
|
||||
|
||||
SECTION("Compare with different object") {
|
||||
JsonObject object = doc.add<JsonObject>();
|
||||
object["a"] = 1;
|
||||
object["b"] = "hello1";
|
||||
object["c"][0] = false;
|
||||
|
||||
JsonVariant variant = doc.add<JsonObject>();
|
||||
variant["a"] = 1;
|
||||
variant["b"] = "hello2";
|
||||
variant["c"][0] = false;
|
||||
|
||||
CHECK(object != variant);
|
||||
CHECK_FALSE(object == variant);
|
||||
CHECK_FALSE(object > variant);
|
||||
CHECK_FALSE(object < variant);
|
||||
CHECK_FALSE(object <= variant);
|
||||
CHECK_FALSE(object >= variant);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Compare JsonObject with JsonVariantConst") {
|
||||
JsonDocument doc;
|
||||
|
||||
SECTION("Compare with unbound") {
|
||||
JsonObject object = doc.to<JsonObject>();
|
||||
object["a"] = 1;
|
||||
object["b"] = "hello";
|
||||
JsonVariantConst unbound;
|
||||
|
||||
CHECK(object != unbound);
|
||||
CHECK_FALSE(object == unbound);
|
||||
CHECK_FALSE(object <= unbound);
|
||||
CHECK_FALSE(object >= unbound);
|
||||
CHECK_FALSE(object > unbound);
|
||||
CHECK_FALSE(object < unbound);
|
||||
|
||||
CHECK(unbound != object);
|
||||
CHECK_FALSE(unbound == object);
|
||||
CHECK_FALSE(unbound <= object);
|
||||
CHECK_FALSE(unbound >= object);
|
||||
CHECK_FALSE(unbound > object);
|
||||
CHECK_FALSE(unbound < object);
|
||||
}
|
||||
|
||||
SECTION("Compare with self") {
|
||||
JsonObject object = doc.to<JsonObject>();
|
||||
object["a"] = 1;
|
||||
object["b"] = "hello";
|
||||
|
||||
JsonVariantConst variant = object;
|
||||
|
||||
CHECK(object == variant);
|
||||
CHECK(object <= variant);
|
||||
CHECK(object >= variant);
|
||||
CHECK_FALSE(object != variant);
|
||||
CHECK_FALSE(object > variant);
|
||||
CHECK_FALSE(object < variant);
|
||||
|
||||
CHECK(variant == object);
|
||||
CHECK(variant <= object);
|
||||
CHECK(variant >= object);
|
||||
CHECK_FALSE(variant != object);
|
||||
CHECK_FALSE(variant > object);
|
||||
CHECK_FALSE(variant < object);
|
||||
}
|
||||
|
||||
SECTION("Compare with identical object") {
|
||||
JsonObject object = doc.add<JsonObject>();
|
||||
object["a"] = 1;
|
||||
object["b"] = "hello";
|
||||
object["c"][0] = false;
|
||||
|
||||
JsonObject object2 = doc.add<JsonObject>();
|
||||
object2["a"] = 1;
|
||||
object2["b"] = "hello";
|
||||
object2["c"][0] = false;
|
||||
JsonVariantConst variant = object2;
|
||||
|
||||
CHECK(object == variant);
|
||||
CHECK(object <= variant);
|
||||
CHECK(object >= variant);
|
||||
CHECK_FALSE(object != variant);
|
||||
CHECK_FALSE(object > variant);
|
||||
CHECK_FALSE(object < variant);
|
||||
|
||||
CHECK(variant == object);
|
||||
CHECK(variant <= object);
|
||||
CHECK(variant >= object);
|
||||
CHECK_FALSE(variant != object);
|
||||
CHECK_FALSE(variant > object);
|
||||
CHECK_FALSE(variant < object);
|
||||
}
|
||||
|
||||
SECTION("Compare with different object") {
|
||||
JsonObject object = doc.add<JsonObject>();
|
||||
object["a"] = 1;
|
||||
object["b"] = "hello1";
|
||||
object["c"][0] = false;
|
||||
|
||||
JsonObject object2 = doc.add<JsonObject>();
|
||||
object2["a"] = 1;
|
||||
object2["b"] = "hello2";
|
||||
object2["c"][0] = false;
|
||||
JsonVariantConst variant = object2;
|
||||
|
||||
CHECK(object != variant);
|
||||
CHECK_FALSE(object == variant);
|
||||
CHECK_FALSE(object > variant);
|
||||
CHECK_FALSE(object < variant);
|
||||
CHECK_FALSE(object <= variant);
|
||||
CHECK_FALSE(object >= variant);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Compare JsonObject with JsonObjectConst") {
|
||||
JsonDocument doc;
|
||||
|
||||
SECTION("Compare with unbound") {
|
||||
JsonObject object = doc.to<JsonObject>();
|
||||
object["a"] = 1;
|
||||
object["b"] = "hello";
|
||||
JsonObjectConst unbound;
|
||||
|
||||
CHECK(object != unbound);
|
||||
CHECK_FALSE(object == unbound);
|
||||
CHECK_FALSE(object <= unbound);
|
||||
CHECK_FALSE(object >= unbound);
|
||||
CHECK_FALSE(object > unbound);
|
||||
CHECK_FALSE(object < unbound);
|
||||
|
||||
CHECK(unbound != object);
|
||||
CHECK_FALSE(unbound == object);
|
||||
CHECK_FALSE(unbound <= object);
|
||||
CHECK_FALSE(unbound >= object);
|
||||
CHECK_FALSE(unbound > object);
|
||||
CHECK_FALSE(unbound < object);
|
||||
}
|
||||
|
||||
SECTION("Compare with self") {
|
||||
JsonObject object = doc.to<JsonObject>();
|
||||
object["a"] = 1;
|
||||
object["b"] = "hello";
|
||||
JsonObjectConst cobject = object;
|
||||
|
||||
CHECK(object == cobject);
|
||||
CHECK(object <= cobject);
|
||||
CHECK(object >= cobject);
|
||||
CHECK_FALSE(object != cobject);
|
||||
CHECK_FALSE(object > cobject);
|
||||
CHECK_FALSE(object < cobject);
|
||||
|
||||
CHECK(cobject == object);
|
||||
CHECK(cobject <= object);
|
||||
CHECK(cobject >= object);
|
||||
CHECK_FALSE(cobject != object);
|
||||
CHECK_FALSE(cobject > object);
|
||||
CHECK_FALSE(cobject < object);
|
||||
}
|
||||
|
||||
SECTION("Compare with identical object") {
|
||||
JsonObject object1 = doc.add<JsonObject>();
|
||||
object1["a"] = 1;
|
||||
object1["b"] = "hello";
|
||||
object1["c"][0] = false;
|
||||
|
||||
JsonObject object2 = doc.add<JsonObject>();
|
||||
object2["a"] = 1;
|
||||
object2["b"] = "hello";
|
||||
object2["c"][0] = false;
|
||||
JsonObjectConst carray2 = object2;
|
||||
|
||||
CHECK(object1 == carray2);
|
||||
CHECK(object1 <= carray2);
|
||||
CHECK(object1 >= carray2);
|
||||
CHECK_FALSE(object1 != carray2);
|
||||
CHECK_FALSE(object1 > carray2);
|
||||
CHECK_FALSE(object1 < carray2);
|
||||
|
||||
CHECK(carray2 == object1);
|
||||
CHECK(carray2 <= object1);
|
||||
CHECK(carray2 >= object1);
|
||||
CHECK_FALSE(carray2 != object1);
|
||||
CHECK_FALSE(carray2 > object1);
|
||||
CHECK_FALSE(carray2 < object1);
|
||||
}
|
||||
|
||||
SECTION("Compare with different object") {
|
||||
JsonObject object1 = doc.add<JsonObject>();
|
||||
object1["a"] = 1;
|
||||
object1["b"] = "hello1";
|
||||
object1["c"][0] = false;
|
||||
|
||||
JsonObject object2 = doc.add<JsonObject>();
|
||||
object2["a"] = 1;
|
||||
object2["b"] = "hello2";
|
||||
object2["c"][0] = false;
|
||||
JsonObjectConst carray2 = object2;
|
||||
|
||||
CHECK(object1 != carray2);
|
||||
CHECK_FALSE(object1 == carray2);
|
||||
CHECK_FALSE(object1 > carray2);
|
||||
CHECK_FALSE(object1 < carray2);
|
||||
CHECK_FALSE(object1 <= carray2);
|
||||
CHECK_FALSE(object1 >= carray2);
|
||||
|
||||
CHECK(carray2 != object1);
|
||||
CHECK_FALSE(carray2 == object1);
|
||||
CHECK_FALSE(carray2 > object1);
|
||||
CHECK_FALSE(carray2 < object1);
|
||||
CHECK_FALSE(carray2 <= object1);
|
||||
CHECK_FALSE(carray2 >= object1);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Compare JsonObjectConst with JsonObjectConst") {
|
||||
JsonDocument doc;
|
||||
|
||||
SECTION("Compare with unbound") {
|
||||
JsonObject object = doc.to<JsonObject>();
|
||||
object["a"] = 1;
|
||||
object["b"] = "hello";
|
||||
|
||||
JsonObjectConst cobject = object;
|
||||
JsonObjectConst unbound;
|
||||
|
||||
CHECK(cobject != unbound);
|
||||
CHECK_FALSE(cobject == unbound);
|
||||
CHECK_FALSE(cobject <= unbound);
|
||||
CHECK_FALSE(cobject >= unbound);
|
||||
CHECK_FALSE(cobject > unbound);
|
||||
CHECK_FALSE(cobject < unbound);
|
||||
|
||||
CHECK(unbound != cobject);
|
||||
CHECK_FALSE(unbound == cobject);
|
||||
CHECK_FALSE(unbound <= cobject);
|
||||
CHECK_FALSE(unbound >= cobject);
|
||||
CHECK_FALSE(unbound > cobject);
|
||||
CHECK_FALSE(unbound < cobject);
|
||||
}
|
||||
|
||||
SECTION("Compare with self") {
|
||||
JsonObject object = doc.to<JsonObject>();
|
||||
object["a"] = 1;
|
||||
object["b"] = "hello";
|
||||
JsonObjectConst cobject = object;
|
||||
|
||||
CHECK(cobject == cobject);
|
||||
CHECK(cobject <= cobject);
|
||||
CHECK(cobject >= cobject);
|
||||
CHECK_FALSE(cobject != cobject);
|
||||
CHECK_FALSE(cobject > cobject);
|
||||
CHECK_FALSE(cobject < cobject);
|
||||
}
|
||||
|
||||
SECTION("Compare with identical object") {
|
||||
JsonObject object1 = doc.add<JsonObject>();
|
||||
object1["a"] = 1;
|
||||
object1["b"] = "hello";
|
||||
object1["c"][0] = false;
|
||||
JsonObjectConst carray1 = object1;
|
||||
|
||||
JsonObject object2 = doc.add<JsonObject>();
|
||||
object2["a"] = 1;
|
||||
object2["b"] = "hello";
|
||||
object2["c"][0] = false;
|
||||
JsonObjectConst carray2 = object2;
|
||||
|
||||
CHECK(carray1 == carray2);
|
||||
CHECK(carray1 <= carray2);
|
||||
CHECK(carray1 >= carray2);
|
||||
CHECK_FALSE(carray1 != carray2);
|
||||
CHECK_FALSE(carray1 > carray2);
|
||||
CHECK_FALSE(carray1 < carray2);
|
||||
}
|
||||
|
||||
SECTION("Compare with different object") {
|
||||
JsonObject object1 = doc.add<JsonObject>();
|
||||
object1["a"] = 1;
|
||||
object1["b"] = "hello1";
|
||||
object1["c"][0] = false;
|
||||
JsonObjectConst carray1 = object1;
|
||||
|
||||
JsonObject object2 = doc.add<JsonObject>();
|
||||
object2["a"] = 1;
|
||||
object2["b"] = "hello2";
|
||||
object2["c"][0] = false;
|
||||
JsonObjectConst carray2 = object2;
|
||||
|
||||
CHECK(carray1 != carray2);
|
||||
CHECK_FALSE(carray1 == carray2);
|
||||
CHECK_FALSE(carray1 > carray2);
|
||||
CHECK_FALSE(carray1 < carray2);
|
||||
CHECK_FALSE(carray1 <= carray2);
|
||||
CHECK_FALSE(carray1 >= carray2);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Compare JsonObjectConst with JsonVariant") {
|
||||
JsonDocument doc;
|
||||
|
||||
SECTION("Compare with self") {
|
||||
JsonObject object = doc.to<JsonObject>();
|
||||
object["a"] = 1;
|
||||
object["b"] = "hello";
|
||||
JsonObjectConst cobject = object;
|
||||
JsonVariant variant = object;
|
||||
|
||||
CHECK(cobject == variant);
|
||||
CHECK(cobject <= variant);
|
||||
CHECK(cobject >= variant);
|
||||
CHECK_FALSE(cobject != variant);
|
||||
CHECK_FALSE(cobject > variant);
|
||||
CHECK_FALSE(cobject < variant);
|
||||
|
||||
CHECK(variant == cobject);
|
||||
CHECK(variant <= cobject);
|
||||
CHECK(variant >= cobject);
|
||||
CHECK_FALSE(variant != cobject);
|
||||
CHECK_FALSE(variant > cobject);
|
||||
CHECK_FALSE(variant < cobject);
|
||||
}
|
||||
|
||||
SECTION("Compare with identical object") {
|
||||
JsonObject object1 = doc.add<JsonObject>();
|
||||
object1["a"] = 1;
|
||||
object1["b"] = "hello";
|
||||
object1["c"][0] = false;
|
||||
JsonObjectConst carray1 = object1;
|
||||
|
||||
JsonObject object2 = doc.add<JsonObject>();
|
||||
object2["a"] = 1;
|
||||
object2["b"] = "hello";
|
||||
object2["c"][0] = false;
|
||||
JsonVariant variant2 = object2;
|
||||
|
||||
CHECK(carray1 == variant2);
|
||||
CHECK(carray1 <= variant2);
|
||||
CHECK(carray1 >= variant2);
|
||||
CHECK_FALSE(carray1 != variant2);
|
||||
CHECK_FALSE(carray1 > variant2);
|
||||
CHECK_FALSE(carray1 < variant2);
|
||||
|
||||
CHECK(variant2 == carray1);
|
||||
CHECK(variant2 <= carray1);
|
||||
CHECK(variant2 >= carray1);
|
||||
CHECK_FALSE(variant2 != carray1);
|
||||
CHECK_FALSE(variant2 > carray1);
|
||||
CHECK_FALSE(variant2 < carray1);
|
||||
}
|
||||
|
||||
SECTION("Compare with different object") {
|
||||
JsonObject object1 = doc.add<JsonObject>();
|
||||
object1["a"] = 1;
|
||||
object1["b"] = "hello1";
|
||||
object1["c"][0] = false;
|
||||
JsonObjectConst carray1 = object1;
|
||||
|
||||
JsonObject object2 = doc.add<JsonObject>();
|
||||
object2["a"] = 1;
|
||||
object2["b"] = "hello2";
|
||||
object2["c"][0] = false;
|
||||
JsonVariant variant2 = object2;
|
||||
|
||||
CHECK(carray1 != variant2);
|
||||
CHECK_FALSE(carray1 == variant2);
|
||||
CHECK_FALSE(carray1 > variant2);
|
||||
CHECK_FALSE(carray1 < variant2);
|
||||
CHECK_FALSE(carray1 <= variant2);
|
||||
CHECK_FALSE(carray1 >= variant2);
|
||||
|
||||
CHECK(variant2 != carray1);
|
||||
CHECK_FALSE(variant2 == carray1);
|
||||
CHECK_FALSE(variant2 > carray1);
|
||||
CHECK_FALSE(variant2 < carray1);
|
||||
CHECK_FALSE(variant2 <= carray1);
|
||||
CHECK_FALSE(variant2 >= carray1);
|
||||
}
|
||||
}
|
||||
59
ArduinoJson/extras/tests/JsonObject/equals.cpp
Normal file
59
ArduinoJson/extras/tests/JsonObject/equals.cpp
Normal file
@@ -0,0 +1,59 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2025, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <catch.hpp>
|
||||
|
||||
TEST_CASE("JsonObject::operator==()") {
|
||||
JsonDocument doc1;
|
||||
JsonObject obj1 = doc1.to<JsonObject>();
|
||||
|
||||
JsonDocument doc2;
|
||||
JsonObject obj2 = doc2.to<JsonObject>();
|
||||
|
||||
SECTION("should return false when objs differ") {
|
||||
obj1["hello"] = "coucou";
|
||||
obj2["world"] = 1;
|
||||
|
||||
REQUIRE_FALSE(obj1 == obj2);
|
||||
}
|
||||
|
||||
SECTION("should return false when LHS has more elements") {
|
||||
obj1["hello"] = "coucou";
|
||||
obj1["world"] = 666;
|
||||
obj2["hello"] = "coucou";
|
||||
|
||||
REQUIRE_FALSE(obj1 == obj2);
|
||||
}
|
||||
|
||||
SECTION("should return false when RKS has more elements") {
|
||||
obj1["hello"] = "coucou";
|
||||
obj2["hello"] = "coucou";
|
||||
obj2["world"] = 666;
|
||||
|
||||
REQUIRE_FALSE(obj1 == obj2);
|
||||
}
|
||||
|
||||
SECTION("should return true when objs equal") {
|
||||
obj1["hello"] = "world";
|
||||
obj1["anwser"] = 42;
|
||||
// insert in different order
|
||||
obj2["anwser"] = 42;
|
||||
obj2["hello"] = "world";
|
||||
|
||||
REQUIRE(obj1 == obj2);
|
||||
}
|
||||
|
||||
SECTION("should return false when RHS is null") {
|
||||
JsonObject null;
|
||||
|
||||
REQUIRE_FALSE(obj1 == null);
|
||||
}
|
||||
|
||||
SECTION("should return false when LHS is null") {
|
||||
JsonObject null;
|
||||
|
||||
REQUIRE_FALSE(null == obj2);
|
||||
}
|
||||
}
|
||||
32
ArduinoJson/extras/tests/JsonObject/isNull.cpp
Normal file
32
ArduinoJson/extras/tests/JsonObject/isNull.cpp
Normal file
@@ -0,0 +1,32 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2025, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <catch.hpp>
|
||||
|
||||
TEST_CASE("JsonObject::isNull()") {
|
||||
SECTION("returns true") {
|
||||
JsonObject obj;
|
||||
REQUIRE(obj.isNull() == true);
|
||||
}
|
||||
|
||||
SECTION("returns false") {
|
||||
JsonDocument doc;
|
||||
JsonObject obj = doc.to<JsonObject>();
|
||||
REQUIRE(obj.isNull() == false);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("JsonObject::operator bool()") {
|
||||
SECTION("returns false") {
|
||||
JsonObject obj;
|
||||
REQUIRE(static_cast<bool>(obj) == false);
|
||||
}
|
||||
|
||||
SECTION("returns true") {
|
||||
JsonDocument doc;
|
||||
JsonObject obj = doc.to<JsonObject>();
|
||||
REQUIRE(static_cast<bool>(obj) == true);
|
||||
}
|
||||
}
|
||||
36
ArduinoJson/extras/tests/JsonObject/iterator.cpp
Normal file
36
ArduinoJson/extras/tests/JsonObject/iterator.cpp
Normal file
@@ -0,0 +1,36 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2025, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <catch.hpp>
|
||||
|
||||
TEST_CASE("JsonObject::begin()/end()") {
|
||||
JsonDocument doc;
|
||||
JsonObject obj = doc.to<JsonObject>();
|
||||
obj["ab"] = 12;
|
||||
obj["cd"] = 34;
|
||||
|
||||
SECTION("NonConstIterator") {
|
||||
JsonObject::iterator it = obj.begin();
|
||||
REQUIRE(obj.end() != it);
|
||||
REQUIRE(it->key() == "ab");
|
||||
REQUIRE(12 == it->value());
|
||||
++it;
|
||||
REQUIRE(obj.end() != it);
|
||||
REQUIRE(it->key() == "cd");
|
||||
REQUIRE(34 == it->value());
|
||||
++it;
|
||||
REQUIRE(obj.end() == it);
|
||||
}
|
||||
|
||||
SECTION("Dereferencing end() is safe") {
|
||||
REQUIRE(obj.end()->key().isNull());
|
||||
REQUIRE(obj.end()->value().isNull());
|
||||
}
|
||||
|
||||
SECTION("null JsonObject") {
|
||||
JsonObject null;
|
||||
REQUIRE(null.begin() == null.end());
|
||||
}
|
||||
}
|
||||
35
ArduinoJson/extras/tests/JsonObject/nesting.cpp
Normal file
35
ArduinoJson/extras/tests/JsonObject/nesting.cpp
Normal file
@@ -0,0 +1,35 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2025, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <catch.hpp>
|
||||
|
||||
TEST_CASE("JsonObject::nesting()") {
|
||||
JsonDocument doc;
|
||||
JsonObject obj = doc.to<JsonObject>();
|
||||
|
||||
SECTION("return 0 if uninitialized") {
|
||||
JsonObject unitialized;
|
||||
REQUIRE(unitialized.nesting() == 0);
|
||||
}
|
||||
|
||||
SECTION("returns 1 for empty object") {
|
||||
REQUIRE(obj.nesting() == 1);
|
||||
}
|
||||
|
||||
SECTION("returns 1 for flat object") {
|
||||
obj["hello"] = "world";
|
||||
REQUIRE(obj.nesting() == 1);
|
||||
}
|
||||
|
||||
SECTION("returns 2 with nested array") {
|
||||
obj["nested"].to<JsonArray>();
|
||||
REQUIRE(obj.nesting() == 2);
|
||||
}
|
||||
|
||||
SECTION("returns 2 with nested object") {
|
||||
obj["nested"].to<JsonObject>();
|
||||
REQUIRE(obj.nesting() == 2);
|
||||
}
|
||||
}
|
||||
89
ArduinoJson/extras/tests/JsonObject/remove.cpp
Normal file
89
ArduinoJson/extras/tests/JsonObject/remove.cpp
Normal file
@@ -0,0 +1,89 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2025, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <catch.hpp>
|
||||
#include <string>
|
||||
|
||||
TEST_CASE("JsonObject::remove()") {
|
||||
JsonDocument doc;
|
||||
JsonObject obj = doc.to<JsonObject>();
|
||||
obj["a"] = 0;
|
||||
obj["b"] = 1;
|
||||
obj["c"] = 2;
|
||||
std::string result;
|
||||
|
||||
SECTION("remove(key)") {
|
||||
SECTION("Remove first") {
|
||||
obj.remove("a");
|
||||
serializeJson(obj, result);
|
||||
REQUIRE("{\"b\":1,\"c\":2}" == result);
|
||||
}
|
||||
|
||||
SECTION("Remove middle") {
|
||||
obj.remove("b");
|
||||
serializeJson(obj, result);
|
||||
REQUIRE("{\"a\":0,\"c\":2}" == result);
|
||||
}
|
||||
|
||||
SECTION("Remove last") {
|
||||
obj.remove("c");
|
||||
serializeJson(obj, result);
|
||||
REQUIRE("{\"a\":0,\"b\":1}" == result);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("remove(iterator)") {
|
||||
JsonObject::iterator it = obj.begin();
|
||||
|
||||
SECTION("Remove first") {
|
||||
obj.remove(it);
|
||||
serializeJson(obj, result);
|
||||
REQUIRE("{\"b\":1,\"c\":2}" == result);
|
||||
}
|
||||
|
||||
SECTION("Remove middle") {
|
||||
++it;
|
||||
obj.remove(it);
|
||||
serializeJson(obj, result);
|
||||
REQUIRE("{\"a\":0,\"c\":2}" == result);
|
||||
}
|
||||
|
||||
SECTION("Remove last") {
|
||||
++it;
|
||||
++it;
|
||||
obj.remove(it);
|
||||
serializeJson(obj, result);
|
||||
REQUIRE("{\"a\":0,\"b\":1}" == result);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAS_VARIABLE_LENGTH_ARRAY
|
||||
SECTION("key is a vla") {
|
||||
size_t i = 16;
|
||||
char vla[i];
|
||||
strcpy(vla, "b");
|
||||
obj.remove(vla);
|
||||
|
||||
serializeJson(obj, result);
|
||||
REQUIRE("{\"a\":0,\"c\":2}" == result);
|
||||
}
|
||||
#endif
|
||||
|
||||
SECTION("remove by key on unbound reference") {
|
||||
JsonObject unboundObject;
|
||||
unboundObject.remove("key");
|
||||
}
|
||||
|
||||
SECTION("remove by iterator on unbound reference") {
|
||||
JsonObject unboundObject;
|
||||
unboundObject.remove(unboundObject.begin());
|
||||
}
|
||||
|
||||
SECTION("remove(JsonVariant)") {
|
||||
obj["key"] = "b";
|
||||
obj.remove(obj["key"]);
|
||||
REQUIRE("{\"a\":0,\"c\":2,\"key\":\"b\"}" == doc.as<std::string>());
|
||||
}
|
||||
}
|
||||
142
ArduinoJson/extras/tests/JsonObject/set.cpp
Normal file
142
ArduinoJson/extras/tests/JsonObject/set.cpp
Normal 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("JsonObject::set()") {
|
||||
SpyingAllocator spy;
|
||||
JsonDocument doc1(&spy);
|
||||
JsonDocument doc2(&spy);
|
||||
|
||||
JsonObject obj1 = doc1.to<JsonObject>();
|
||||
JsonObject obj2 = doc2.to<JsonObject>();
|
||||
|
||||
SECTION("doesn't copy static string in key or value") {
|
||||
obj1["hello"] = "world";
|
||||
spy.clearLog();
|
||||
|
||||
bool success = obj2.set(obj1);
|
||||
|
||||
REQUIRE(success == true);
|
||||
REQUIRE(obj2["hello"] == "world"_s);
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Allocate(sizeofPool()),
|
||||
});
|
||||
}
|
||||
|
||||
SECTION("copy local string value") {
|
||||
obj1["hello"] = "world"_s;
|
||||
spy.clearLog();
|
||||
|
||||
bool success = obj2.set(obj1);
|
||||
|
||||
REQUIRE(success == true);
|
||||
REQUIRE(obj2["hello"] == "world"_s);
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Allocate(sizeofPool()),
|
||||
Allocate(sizeofString("world")),
|
||||
});
|
||||
}
|
||||
|
||||
SECTION("copy local key") {
|
||||
obj1["hello"_s] = "world";
|
||||
spy.clearLog();
|
||||
|
||||
bool success = obj2.set(obj1);
|
||||
|
||||
REQUIRE(success == true);
|
||||
REQUIRE(obj2["hello"] == "world"_s);
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Allocate(sizeofPool()),
|
||||
Allocate(sizeofString("hello")),
|
||||
});
|
||||
}
|
||||
|
||||
SECTION("copy string from deserializeJson()") {
|
||||
deserializeJson(doc1, "{'hello':'world'}");
|
||||
spy.clearLog();
|
||||
|
||||
bool success = obj2.set(obj1);
|
||||
|
||||
REQUIRE(success == true);
|
||||
REQUIRE(obj2["hello"] == "world"_s);
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Allocate(sizeofPool()),
|
||||
Allocate(sizeofString("hello")),
|
||||
Allocate(sizeofString("world")),
|
||||
});
|
||||
}
|
||||
|
||||
SECTION("copy string from deserializeMsgPack()") {
|
||||
deserializeMsgPack(doc1, "\x81\xA5hello\xA5world");
|
||||
spy.clearLog();
|
||||
|
||||
bool success = obj2.set(obj1);
|
||||
|
||||
REQUIRE(success == true);
|
||||
REQUIRE(obj2["hello"] == "world"_s);
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Allocate(sizeofPool()),
|
||||
Allocate(sizeofString("hello")),
|
||||
Allocate(sizeofString("world")),
|
||||
});
|
||||
}
|
||||
|
||||
SECTION("should work with JsonObjectConst") {
|
||||
obj1["hello"] = "world";
|
||||
|
||||
obj2.set(static_cast<JsonObjectConst>(obj1));
|
||||
|
||||
REQUIRE(obj2["hello"] == "world"_s);
|
||||
}
|
||||
|
||||
SECTION("copy fails in the middle of an object") {
|
||||
TimebombAllocator timebomb(2);
|
||||
JsonDocument doc3(&timebomb);
|
||||
JsonObject obj3 = doc3.to<JsonObject>();
|
||||
|
||||
obj1["alpha"_s] = 1;
|
||||
obj1["beta"_s] = 2;
|
||||
|
||||
bool success = obj3.set(obj1);
|
||||
|
||||
REQUIRE(success == false);
|
||||
REQUIRE(doc3.as<std::string>() == "{\"alpha\":1}");
|
||||
}
|
||||
|
||||
SECTION("copy fails in the middle of an array") {
|
||||
TimebombAllocator timebomb(1);
|
||||
JsonDocument doc3(&timebomb);
|
||||
JsonObject obj3 = doc3.to<JsonObject>();
|
||||
|
||||
obj1["hello"][0] = "world"_s;
|
||||
|
||||
bool success = obj3.set(obj1);
|
||||
|
||||
REQUIRE(success == false);
|
||||
REQUIRE(doc3.as<std::string>() == "{\"hello\":[]}");
|
||||
}
|
||||
|
||||
SECTION("destination is null") {
|
||||
JsonObject null;
|
||||
obj1["hello"] = "world";
|
||||
|
||||
bool success = null.set(obj1);
|
||||
|
||||
REQUIRE(success == false);
|
||||
}
|
||||
|
||||
SECTION("source is null") {
|
||||
JsonObject null;
|
||||
obj1["hello"] = "world";
|
||||
|
||||
bool success = obj1.set(null);
|
||||
|
||||
REQUIRE(success == false);
|
||||
}
|
||||
}
|
||||
39
ArduinoJson/extras/tests/JsonObject/size.cpp
Normal file
39
ArduinoJson/extras/tests/JsonObject/size.cpp
Normal file
@@ -0,0 +1,39 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2025, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <catch.hpp>
|
||||
#include <string>
|
||||
|
||||
TEST_CASE("JsonObject::size()") {
|
||||
JsonDocument doc;
|
||||
JsonObject obj = doc.to<JsonObject>();
|
||||
|
||||
SECTION("initial size is zero") {
|
||||
REQUIRE(0 == obj.size());
|
||||
}
|
||||
|
||||
SECTION("increases when values are added") {
|
||||
obj["hello"] = 42;
|
||||
REQUIRE(1 == obj.size());
|
||||
}
|
||||
|
||||
SECTION("decreases when values are removed") {
|
||||
obj["hello"] = 42;
|
||||
obj.remove("hello");
|
||||
REQUIRE(0 == obj.size());
|
||||
}
|
||||
|
||||
SECTION("doesn't increase when the same key is added twice") {
|
||||
obj["hello"] = 1;
|
||||
obj["hello"] = 2;
|
||||
REQUIRE(1 == obj.size());
|
||||
}
|
||||
|
||||
SECTION("doesn't decrease when another key is removed") {
|
||||
obj["hello"] = 1;
|
||||
obj.remove("world");
|
||||
REQUIRE(1 == obj.size());
|
||||
}
|
||||
}
|
||||
61
ArduinoJson/extras/tests/JsonObject/std_string.cpp
Normal file
61
ArduinoJson/extras/tests/JsonObject/std_string.cpp
Normal file
@@ -0,0 +1,61 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2025, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <catch.hpp>
|
||||
|
||||
#include "Literals.hpp"
|
||||
|
||||
static void eraseString(std::string& str) {
|
||||
char* p = const_cast<char*>(str.c_str());
|
||||
while (*p)
|
||||
*p++ = '*';
|
||||
}
|
||||
|
||||
TEST_CASE("std::string") {
|
||||
JsonDocument doc;
|
||||
|
||||
SECTION("operator[]") {
|
||||
char json[] = "{\"key\":\"value\"}";
|
||||
|
||||
deserializeJson(doc, json);
|
||||
JsonObject obj = doc.as<JsonObject>();
|
||||
|
||||
REQUIRE("value"_s == obj["key"_s]);
|
||||
}
|
||||
|
||||
SECTION("operator[] const") {
|
||||
char json[] = "{\"key\":\"value\"}";
|
||||
|
||||
deserializeJson(doc, json);
|
||||
JsonObject obj = doc.as<JsonObject>();
|
||||
|
||||
REQUIRE("value"_s == obj["key"_s]);
|
||||
}
|
||||
|
||||
SECTION("remove()") {
|
||||
JsonObject obj = doc.to<JsonObject>();
|
||||
obj["key"] = "value";
|
||||
|
||||
obj.remove("key"_s);
|
||||
|
||||
REQUIRE(0 == obj.size());
|
||||
}
|
||||
|
||||
SECTION("operator[], set key") {
|
||||
std::string key("hello");
|
||||
JsonObject obj = doc.to<JsonObject>();
|
||||
obj[key] = "world";
|
||||
eraseString(key);
|
||||
REQUIRE("world"_s == obj["hello"]);
|
||||
}
|
||||
|
||||
SECTION("operator[], set value") {
|
||||
std::string value("world");
|
||||
JsonObject obj = doc.to<JsonObject>();
|
||||
obj["hello"] = value;
|
||||
eraseString(value);
|
||||
REQUIRE("world"_s == obj["hello"]);
|
||||
}
|
||||
}
|
||||
267
ArduinoJson/extras/tests/JsonObject/subscript.cpp
Normal file
267
ArduinoJson/extras/tests/JsonObject/subscript.cpp
Normal file
@@ -0,0 +1,267 @@
|
||||
// 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("JsonObject::operator[]") {
|
||||
SpyingAllocator spy;
|
||||
JsonDocument doc(&spy);
|
||||
JsonObject obj = doc.to<JsonObject>();
|
||||
|
||||
SECTION("int") {
|
||||
obj["hello"] = 123;
|
||||
|
||||
REQUIRE(123 == obj["hello"].as<int>());
|
||||
REQUIRE(true == obj["hello"].is<int>());
|
||||
REQUIRE(false == obj["hello"].is<bool>());
|
||||
}
|
||||
|
||||
SECTION("volatile int") { // issue #415
|
||||
volatile int i = 123;
|
||||
obj["hello"] = i;
|
||||
|
||||
REQUIRE(123 == obj["hello"].as<int>());
|
||||
REQUIRE(true == obj["hello"].is<int>());
|
||||
REQUIRE(false == obj["hello"].is<bool>());
|
||||
}
|
||||
|
||||
SECTION("double") {
|
||||
obj["hello"] = 123.45;
|
||||
|
||||
REQUIRE(true == obj["hello"].is<double>());
|
||||
REQUIRE(false == obj["hello"].is<long>());
|
||||
REQUIRE(123.45 == obj["hello"].as<double>());
|
||||
}
|
||||
|
||||
SECTION("bool") {
|
||||
obj["hello"] = true;
|
||||
|
||||
REQUIRE(true == obj["hello"].is<bool>());
|
||||
REQUIRE(false == obj["hello"].is<long>());
|
||||
REQUIRE(true == obj["hello"].as<bool>());
|
||||
}
|
||||
|
||||
SECTION("const char*") {
|
||||
obj["hello"] = "h3110";
|
||||
|
||||
REQUIRE(true == obj["hello"].is<const char*>());
|
||||
REQUIRE(false == obj["hello"].is<long>());
|
||||
REQUIRE("h3110"_s == obj["hello"].as<const char*>());
|
||||
}
|
||||
|
||||
SECTION("array") {
|
||||
JsonDocument doc2;
|
||||
JsonArray arr = doc2.to<JsonArray>();
|
||||
|
||||
obj["hello"] = arr;
|
||||
|
||||
REQUIRE(arr == obj["hello"].as<JsonArray>());
|
||||
REQUIRE(true == obj["hello"].is<JsonArray>());
|
||||
REQUIRE(false == obj["hello"].is<JsonObject>());
|
||||
}
|
||||
|
||||
SECTION("object") {
|
||||
JsonDocument doc2;
|
||||
JsonObject obj2 = doc2.to<JsonObject>();
|
||||
|
||||
obj["hello"] = obj2;
|
||||
|
||||
REQUIRE(obj2 == obj["hello"].as<JsonObject>());
|
||||
REQUIRE(true == obj["hello"].is<JsonObject>());
|
||||
REQUIRE(false == obj["hello"].is<JsonArray>());
|
||||
}
|
||||
|
||||
SECTION("array subscript") {
|
||||
JsonDocument doc2;
|
||||
JsonArray arr = doc2.to<JsonArray>();
|
||||
arr.add(42);
|
||||
|
||||
obj["a"] = arr[0];
|
||||
|
||||
REQUIRE(42 == obj["a"]);
|
||||
}
|
||||
|
||||
SECTION("object subscript") {
|
||||
JsonDocument doc2;
|
||||
JsonObject obj2 = doc2.to<JsonObject>();
|
||||
obj2["x"] = 42;
|
||||
|
||||
obj["a"] = obj2["x"];
|
||||
|
||||
REQUIRE(42 == obj["a"]);
|
||||
}
|
||||
|
||||
SECTION("char key[]") { // issue #423
|
||||
char key[] = "hello";
|
||||
obj[key] = 42;
|
||||
REQUIRE(42 == obj[key]);
|
||||
}
|
||||
|
||||
SECTION("should not duplicate const char*") {
|
||||
obj["hello"] = "world";
|
||||
REQUIRE(spy.log() == AllocatorLog{Allocate(sizeofPool())});
|
||||
}
|
||||
|
||||
SECTION("should duplicate char* value") {
|
||||
obj["hello"] = const_cast<char*>("world");
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Allocate(sizeofPool()),
|
||||
Allocate(sizeofString("world")),
|
||||
});
|
||||
}
|
||||
|
||||
SECTION("should duplicate char* key") {
|
||||
obj[const_cast<char*>("hello")] = "world";
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Allocate(sizeofPool()),
|
||||
Allocate(sizeofString("hello")),
|
||||
});
|
||||
}
|
||||
|
||||
SECTION("should duplicate char* key&value") {
|
||||
obj[const_cast<char*>("hello")] = const_cast<char*>("world");
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Allocate(sizeofPool()),
|
||||
Allocate(sizeofString("hello")),
|
||||
Allocate(sizeofString("world")),
|
||||
});
|
||||
}
|
||||
|
||||
SECTION("should duplicate std::string value") {
|
||||
obj["hello"] = "world"_s;
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Allocate(sizeofPool()),
|
||||
Allocate(sizeofString("world")),
|
||||
});
|
||||
}
|
||||
|
||||
SECTION("should duplicate std::string key") {
|
||||
obj["hello"_s] = "world";
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Allocate(sizeofPool()),
|
||||
Allocate(sizeofString("hello")),
|
||||
});
|
||||
}
|
||||
|
||||
SECTION("should duplicate std::string key&value") {
|
||||
obj["hello"_s] = "world"_s;
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Allocate(sizeofPool()),
|
||||
Allocate(sizeofString("hello")),
|
||||
Allocate(sizeofString("world")),
|
||||
});
|
||||
}
|
||||
|
||||
SECTION("should duplicate a non-static JsonString key") {
|
||||
obj[JsonString("hello", false)] = "world";
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Allocate(sizeofPool()),
|
||||
Allocate(sizeofString("hello")),
|
||||
});
|
||||
}
|
||||
|
||||
SECTION("should not duplicate a static JsonString key") {
|
||||
obj[JsonString("hello", true)] = "world";
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Allocate(sizeofPool()),
|
||||
});
|
||||
}
|
||||
|
||||
SECTION("should ignore null key") {
|
||||
// object must have a value to make a call to strcmp()
|
||||
obj["dummy"] = 42;
|
||||
|
||||
const char* null = 0;
|
||||
obj[null] = 666;
|
||||
|
||||
REQUIRE(obj.size() == 1);
|
||||
REQUIRE(obj[null] == null);
|
||||
}
|
||||
|
||||
SECTION("obj[key].to<JsonArray>()") {
|
||||
JsonArray arr = obj["hello"].to<JsonArray>();
|
||||
|
||||
REQUIRE(arr.isNull() == false);
|
||||
}
|
||||
|
||||
#if defined(HAS_VARIABLE_LENGTH_ARRAY) && \
|
||||
!defined(SUBSCRIPT_CONFLICTS_WITH_BUILTIN_OPERATOR)
|
||||
SECTION("obj[VLA] = str") {
|
||||
size_t i = 16;
|
||||
char vla[i];
|
||||
strcpy(vla, "hello");
|
||||
|
||||
obj[vla] = "world";
|
||||
|
||||
REQUIRE("world"_s == obj["hello"]);
|
||||
}
|
||||
|
||||
SECTION("obj[str] = VLA") { // issue #416
|
||||
size_t i = 32;
|
||||
char vla[i];
|
||||
strcpy(vla, "world");
|
||||
|
||||
obj["hello"] = vla;
|
||||
|
||||
REQUIRE("world"_s == obj["hello"].as<const char*>());
|
||||
}
|
||||
|
||||
SECTION("obj.set(VLA, str)") {
|
||||
size_t i = 16;
|
||||
char vla[i];
|
||||
strcpy(vla, "hello");
|
||||
|
||||
obj[vla] = "world";
|
||||
|
||||
REQUIRE("world"_s == obj["hello"]);
|
||||
}
|
||||
|
||||
SECTION("obj.set(str, VLA)") {
|
||||
size_t i = 32;
|
||||
char vla[i];
|
||||
strcpy(vla, "world");
|
||||
|
||||
obj["hello"].set(vla);
|
||||
|
||||
REQUIRE("world"_s == obj["hello"].as<const char*>());
|
||||
}
|
||||
|
||||
SECTION("obj[VLA]") {
|
||||
size_t i = 16;
|
||||
char vla[i];
|
||||
strcpy(vla, "hello");
|
||||
|
||||
deserializeJson(doc, "{\"hello\":\"world\"}");
|
||||
|
||||
obj = doc.as<JsonObject>();
|
||||
REQUIRE("world"_s == obj[vla]);
|
||||
}
|
||||
#endif
|
||||
|
||||
SECTION("chain") {
|
||||
obj["hello"]["world"] = 123;
|
||||
|
||||
REQUIRE(123 == obj["hello"]["world"].as<int>());
|
||||
REQUIRE(true == obj["hello"]["world"].is<int>());
|
||||
REQUIRE(false == obj["hello"]["world"].is<bool>());
|
||||
}
|
||||
|
||||
SECTION("JsonVariant") {
|
||||
obj["hello"] = "world";
|
||||
obj["a\0b"_s] = "ABC";
|
||||
|
||||
doc["key1"] = "hello";
|
||||
doc["key2"] = "a\0b"_s;
|
||||
doc["key3"] = "foo";
|
||||
|
||||
REQUIRE(obj[obj["key1"]] == "world");
|
||||
REQUIRE(obj[obj["key2"]] == "ABC");
|
||||
REQUIRE(obj[obj["key3"]] == nullptr);
|
||||
REQUIRE(obj[obj["key4"]] == nullptr);
|
||||
}
|
||||
}
|
||||
27
ArduinoJson/extras/tests/JsonObject/unbound.cpp
Normal file
27
ArduinoJson/extras/tests/JsonObject/unbound.cpp
Normal file
@@ -0,0 +1,27 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2025, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <catch.hpp>
|
||||
|
||||
using namespace Catch::Matchers;
|
||||
|
||||
TEST_CASE("Unbound JsonObject") {
|
||||
JsonObject obj;
|
||||
|
||||
SECTION("retrieve member") {
|
||||
REQUIRE(obj["key"].isNull());
|
||||
}
|
||||
|
||||
SECTION("add member") {
|
||||
obj["hello"] = "world";
|
||||
REQUIRE(0 == obj.size());
|
||||
}
|
||||
|
||||
SECTION("serialize") {
|
||||
char buffer[32];
|
||||
serializeJson(obj, buffer, sizeof(buffer));
|
||||
REQUIRE_THAT(buffer, Equals("null"));
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user