Update to ArduinoJson 7.0.4

This commit is contained in:
iranl
2024-04-19 14:44:01 +02:00
parent 1378732081
commit 81be0a689a
444 changed files with 10842 additions and 9422 deletions

View File

@@ -1,5 +1,5 @@
# ArduinoJson - https://arduinojson.org
# Copyright © 2014-2023, Benoit BLANCHON
# Copyright © 2014-2024, Benoit BLANCHON
# MIT License
set(CMAKE_CXX_STANDARD 11)
@@ -12,15 +12,19 @@ link_libraries(ArduinoJson catch)
include_directories(Helpers)
add_subdirectory(Cpp17)
add_subdirectory(Cpp20)
add_subdirectory(Deprecated)
add_subdirectory(FailingBuilds)
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(MemoryPool)
add_subdirectory(JsonVariantConst)
add_subdirectory(ResourceManager)
add_subdirectory(Misc)
add_subdirectory(MixedConfiguration)
add_subdirectory(MsgPackDeserializer)

View File

@@ -1,5 +1,5 @@
# ArduinoJson - https://arduinojson.org
# Copyright © 2014-2023, Benoit BLANCHON
# Copyright © 2014-2024, Benoit BLANCHON
# MIT License
if(MSVC_VERSION LESS 1910)

View File

@@ -1,14 +1,27 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2024, 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 <string_view>
#include "Allocators.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") {
StaticJsonDocument<256> doc;
SpyingAllocator spy;
JsonDocument doc(&spy);
JsonVariant variant = doc.to<JsonVariant>();
SECTION("deserializeJson()") {
@@ -19,7 +32,7 @@ TEST_CASE("string_view") {
SECTION("JsonDocument::set()") {
doc.set(std::string_view("123", 2));
REQUIRE(doc.as<std::string>() == "12");
REQUIRE(doc.as<std::string_view>() == "12");
}
SECTION("JsonDocument::operator[]() const") {
@@ -53,16 +66,15 @@ TEST_CASE("string_view") {
SECTION("String deduplication") {
doc.add(std::string_view("example one", 7));
REQUIRE(doc.memoryUsage() == JSON_ARRAY_SIZE(1) + 8);
doc.add(std::string_view("example two", 7));
REQUIRE(doc.memoryUsage() == JSON_ARRAY_SIZE(2) + 8);
doc.add(std::string_view("example\0tree", 12));
REQUIRE(doc.memoryUsage() == JSON_ARRAY_SIZE(3) + 21);
doc.add(std::string_view("example\0tree and a half", 12));
REQUIRE(doc.memoryUsage() == JSON_ARRAY_SIZE(4) + 21);
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("example")),
Allocate(sizeofString("example tree")),
});
}
SECTION("as<std::string_view>()") {

View File

@@ -1,5 +1,5 @@
# ArduinoJson - https://arduinojson.org
# Copyright © 2014-2023, Benoit BLANCHON
# Copyright © 2014-2024, Benoit BLANCHON
# MIT License
if(MSVC_VERSION LESS 1910)

View File

@@ -4,7 +4,7 @@
#include <string>
TEST_CASE("C++20 smoke test") {
StaticJsonDocument<128> doc;
JsonDocument doc;
deserializeJson(doc, "{\"hello\":\"world\"}");
REQUIRE(doc["hello"] == "world");

View File

@@ -0,0 +1,69 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2024, 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 == "ARAARDDD");
}
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();
}
}

View File

@@ -0,0 +1,34 @@
# ArduinoJson - https://arduinojson.org
# Copyright © 2014-2024, 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
createNestedArray.cpp
createNestedObject.cpp
BasicJsonDocument.cpp
DynamicJsonDocument.cpp
macros.cpp
memoryUsage.cpp
shallowCopy.cpp
StaticJsonDocument.cpp
)
add_test(Deprecated DeprecatedTests)
set_tests_properties(Deprecated
PROPERTIES
LABELS "Catch"
)

View File

@@ -0,0 +1,37 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2024, 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();
}
}

View File

@@ -0,0 +1,32 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2024, 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);
}
}

View File

@@ -0,0 +1,38 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2024, 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]]");
}

View File

@@ -0,0 +1,111 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
#include <string>
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(std::string("key"));
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(std::string("key"));
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(std::string("key"));
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
}

View File

@@ -0,0 +1,111 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
#include <string>
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(std::string("key"));
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(std::string("key"));
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(std::string("key"));
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
}

View File

@@ -0,0 +1,18 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2024, 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
}

View File

@@ -0,0 +1,51 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2024, 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);
}

View File

@@ -0,0 +1,14 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2024, 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\"}}");
}

View File

@@ -1,5 +1,5 @@
# ArduinoJson - https://arduinojson.org
# Copyright © 2014-2023, Benoit BLANCHON
# Copyright © 2014-2024, Benoit BLANCHON
# MIT License
macro(build_should_fail target)
@@ -23,18 +23,12 @@ endmacro()
add_executable(Issue978 Issue978.cpp)
build_should_fail(Issue978)
add_executable(Issue1189 Issue1189.cpp)
build_should_fail(Issue1189)
add_executable(read_long_long read_long_long.cpp)
build_should_fail(read_long_long)
add_executable(write_long_long write_long_long.cpp)
build_should_fail(write_long_long)
add_executable(delete_jsondocument delete_jsondocument.cpp)
build_should_fail(delete_jsondocument)
add_executable(variant_as_char variant_as_char.cpp)
build_should_fail(variant_as_char)

View File

@@ -1,13 +0,0 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2023, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
// a function should not be able to get a JsonDocument by value
void f(JsonDocument) {}
int main() {
DynamicJsonDocument doc(1024);
f(doc);
}

View File

@@ -1,5 +1,5 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2023, Benoit BLANCHON
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
@@ -8,6 +8,6 @@ struct Stream {};
int main() {
Stream* stream = 0;
DynamicJsonDocument doc(1024);
JsonDocument doc;
deserializeJson(doc, stream);
}

View File

@@ -1,5 +1,5 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2023, Benoit BLANCHON
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
@@ -7,6 +7,6 @@
// See issue #1498
int main() {
DynamicJsonDocument doc(1024);
JsonDocument doc;
doc["dummy"] = 'A';
}

View File

@@ -1,12 +0,0 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2023, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
struct Stream {};
int main() {
JsonDocument* doc = new DynamicJsonDocument(42);
delete doc;
}

View File

@@ -1,5 +1,5 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2023, Benoit BLANCHON
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#define ARDUINOJSON_USE_LONG_LONG 0
@@ -11,6 +11,6 @@
ARDUINOJSON_ASSERT_INTEGER_TYPE_IS_SUPPORTED(long long)
int main() {
DynamicJsonDocument doc(1024);
JsonDocument doc;
doc["dummy"].as<long long>();
}

View File

@@ -1,5 +1,5 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2023, Benoit BLANCHON
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
@@ -7,6 +7,6 @@
// See issue #1498
int main() {
DynamicJsonDocument doc(1024);
JsonDocument doc;
doc["dummy"].as<char>();
}

View File

@@ -1,5 +1,5 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2023, Benoit BLANCHON
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#define ARDUINOJSON_USE_LONG_LONG 0
@@ -10,6 +10,6 @@
#endif
int main() {
DynamicJsonDocument doc(1024);
JsonDocument doc;
doc["dummy"] = static_cast<long long>(42);
}

View File

@@ -0,0 +1,283 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#pragma once
#include <ArduinoJson/Memory/Allocator.hpp>
#include <ArduinoJson/Memory/StringBuilder.hpp>
#include <ArduinoJson/Memory/VariantPool.hpp>
#include <sstream>
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];
sprintf(buffer, "allocate(%zu)", s);
return AllocatorLogEntry(buffer);
}
inline AllocatorLogEntry AllocateFail(size_t s) {
char buffer[32];
sprintf(buffer, "allocate(%zu) -> nullptr", s);
return AllocatorLogEntry(buffer);
}
inline AllocatorLogEntry Reallocate(size_t s1, size_t s2) {
char buffer[32];
sprintf(buffer, "reallocate(%zu, %zu)", s1, s2);
return AllocatorLogEntry(buffer);
}
inline AllocatorLogEntry ReallocateFail(size_t s1, size_t s2) {
char buffer[32];
sprintf(buffer, "reallocate(%zu, %zu) -> nullptr", s1, s2);
return AllocatorLogEntry(buffer);
}
inline AllocatorLogEntry Deallocate(size_t s) {
char buffer[32];
sprintf(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_;
};
inline size_t sizeofPoolList(size_t n = ARDUINOJSON_INITIAL_POOL_COUNT) {
return sizeof(ArduinoJson::detail::VariantPool) * n;
}
inline size_t sizeofPool(
ArduinoJson::detail::SlotCount n = ARDUINOJSON_POOL_CAPACITY) {
return ArduinoJson::detail::VariantPool::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));
}

View File

@@ -1,5 +1,5 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2023, Benoit BLANCHON
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#pragma once
@@ -7,5 +7,7 @@
#include "api/Print.h"
#include "api/Stream.h"
#include "api/String.h"
#include "avr/pgmspace.h"
#define ARDUINO
#define ARDUINO_H_INCLUDED 1

View File

@@ -1,5 +1,5 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2023, Benoit BLANCHON
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#pragma once
@@ -7,18 +7,18 @@
#include <sstream>
class CustomReader {
std::stringstream _stream;
std::stringstream stream_;
public:
CustomReader(const char* input) : _stream(input) {}
CustomReader(const char* input) : stream_(input) {}
CustomReader(const CustomReader&) = delete;
int read() {
return _stream.get();
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());
stream_.read(buffer, static_cast<std::streamsize>(length));
return static_cast<size_t>(stream_.gcount());
}
};

View File

@@ -1,5 +1,5 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2023, Benoit BLANCHON
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#pragma once

View File

@@ -1,5 +1,5 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2023, Benoit BLANCHON
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#pragma once

View File

@@ -1,5 +1,5 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2023, Benoit BLANCHON
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#pragma once
@@ -9,11 +9,14 @@
// Reproduces Arduino's String class
class String {
public:
String() : _maxCapacity(1024) {}
explicit String(const char* s) : _str(s), _maxCapacity(1024) {}
String() = default;
String(const char* s) {
if (s)
str_.assign(s);
}
void limitCapacityTo(size_t maxCapacity) {
_maxCapacity = maxCapacity;
maxCapacity_ = maxCapacity;
}
unsigned char concat(const char* s) {
@@ -21,45 +24,48 @@ class String {
}
size_t length() const {
return _str.size();
return str_.size();
}
const char* c_str() const {
return _str.c_str();
return str_.c_str();
}
bool operator==(const char* s) const {
return _str == s;
return str_ == s;
}
String& operator=(const char* s) {
_str.assign(s);
if (s)
str_.assign(s);
else
str_.clear();
return *this;
}
char operator[](unsigned int index) const {
if (index >= _str.size())
if (index >= str_.size())
return 0;
return _str[index];
return str_[index];
}
friend std::ostream& operator<<(std::ostream& lhs, const ::String& rhs) {
lhs << rhs._str;
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)
if (str_.size() + n > maxCapacity_)
return 0;
_str.append(s, n);
str_.append(s, n);
return 1;
}
private:
std::string _str;
size_t _maxCapacity;
std::string str_;
size_t maxCapacity_ = 1024;
};
class StringSumHelper : public ::String {};

View File

@@ -1,7 +1,9 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2023, Benoit BLANCHON
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#pragma once
#include <stdint.h> // uint8_t
#define PROGMEM

View File

@@ -1,5 +1,5 @@
# ArduinoJson - https://arduinojson.org
# Copyright © 2014-2023, Benoit BLANCHON
# Copyright © 2014-2024, Benoit BLANCHON
# MIT License
add_executable(IntegrationTests
@@ -9,7 +9,7 @@ add_executable(IntegrationTests
openweathermap.cpp
)
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU")
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

View File

@@ -1,12 +1,12 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2023, Benoit BLANCHON
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
TEST_CASE("Gbathree") {
DynamicJsonDocument doc(4096);
JsonDocument doc;
DeserializationError error = deserializeJson(
doc,

View File

@@ -1,5 +1,5 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2023, Benoit BLANCHON
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
@@ -8,8 +8,8 @@
// https://github.com/bblanchon/ArduinoJson/issues/772
TEST_CASE("Issue772") {
DynamicJsonDocument doc1(4096);
DynamicJsonDocument doc2(4096);
JsonDocument doc1;
JsonDocument doc2;
DeserializationError err;
std::string data =
"{\"state\":{\"reported\":{\"timestamp\":\"2018-07-02T09:40:12Z\","

View File

@@ -1,5 +1,5 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2023, Benoit BLANCHON
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
@@ -53,12 +53,12 @@ TEST_CASE("OpenWeatherMap") {
"]}";
// clang-format on
StaticJsonDocument<512> filter;
JsonDocument filter;
filter["list"][0]["dt"] = true;
filter["list"][0]["main"]["temp"] = true;
filter["list"][0]["weather"][0]["description"] = true;
DynamicJsonDocument doc(16384);
JsonDocument doc;
REQUIRE(
deserializeJson(doc, input_json, DeserializationOption::Filter(filter)) ==

View File

@@ -1,12 +1,12 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2023, Benoit BLANCHON
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
void check(std::string originalJson) {
DynamicJsonDocument doc(16384);
JsonDocument doc;
std::string prettyJson;
deserializeJson(doc, originalJson);

View File

@@ -1,5 +1,5 @@
# ArduinoJson - https://arduinojson.org
# Copyright © 2014-2023, Benoit BLANCHON
# Copyright © 2014-2024, Benoit BLANCHON
# MIT License
add_executable(JsonArrayTests
@@ -7,11 +7,9 @@ add_executable(JsonArrayTests
clear.cpp
compare.cpp
copyArray.cpp
createNested.cpp
equals.cpp
isNull.cpp
iterator.cpp
memoryUsage.cpp
nesting.cpp
remove.cpp
size.cpp

View File

@@ -1,12 +1,17 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2023, Benoit BLANCHON
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
TEST_CASE("JsonArray::add()") {
DynamicJsonDocument doc(4096);
#include "Allocators.hpp"
using ArduinoJson::detail::sizeofArray;
TEST_CASE("JsonArray::add(T)") {
SpyingAllocator spy;
JsonDocument doc(&spy);
JsonArray array = doc.to<JsonArray>();
SECTION("int") {
@@ -51,7 +56,7 @@ TEST_CASE("JsonArray::add()") {
#endif
SECTION("nested array") {
DynamicJsonDocument doc2(4096);
JsonDocument doc2;
JsonArray arr = doc2.to<JsonArray>();
array.add(arr);
@@ -62,7 +67,7 @@ TEST_CASE("JsonArray::add()") {
}
SECTION("nested object") {
DynamicJsonDocument doc2(4096);
JsonDocument doc2;
JsonObject obj = doc2.to<JsonObject>();
array.add(obj);
@@ -74,7 +79,7 @@ TEST_CASE("JsonArray::add()") {
SECTION("array subscript") {
const char* str = "hello";
DynamicJsonDocument doc2(4096);
JsonDocument doc2;
JsonArray arr = doc2.to<JsonArray>();
arr.add(str);
@@ -85,7 +90,7 @@ TEST_CASE("JsonArray::add()") {
SECTION("object subscript") {
const char* str = "hello";
DynamicJsonDocument doc2(4096);
JsonDocument doc2;
JsonObject obj = doc2.to<JsonObject>();
obj["x"] = str;
@@ -96,43 +101,81 @@ TEST_CASE("JsonArray::add()") {
SECTION("should not duplicate const char*") {
array.add("world");
const size_t expectedSize = JSON_ARRAY_SIZE(1);
REQUIRE(expectedSize == doc.memoryUsage());
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
});
}
SECTION("should duplicate char*") {
array.add(const_cast<char*>("world"));
const size_t expectedSize = JSON_ARRAY_SIZE(1) + JSON_STRING_SIZE(5);
REQUIRE(expectedSize == doc.memoryUsage());
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("world")),
});
}
SECTION("should duplicate std::string") {
array.add(std::string("world"));
const size_t expectedSize = JSON_ARRAY_SIZE(1) + JSON_STRING_SIZE(5);
REQUIRE(expectedSize == doc.memoryUsage());
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("world")),
});
}
SECTION("should not duplicate serialized(const char*)") {
SECTION("should duplicate serialized(const char*)") {
array.add(serialized("{}"));
const size_t expectedSize = JSON_ARRAY_SIZE(1);
REQUIRE(expectedSize == doc.memoryUsage());
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("{}")),
});
}
SECTION("should duplicate serialized(char*)") {
array.add(serialized(const_cast<char*>("{}")));
const size_t expectedSize = JSON_ARRAY_SIZE(1) + JSON_STRING_SIZE(2);
REQUIRE(expectedSize == doc.memoryUsage());
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("{}")),
});
}
SECTION("should duplicate serialized(std::string)") {
array.add(serialized(std::string("{}")));
const size_t expectedSize = JSON_ARRAY_SIZE(1) + JSON_STRING_SIZE(2);
REQUIRE(expectedSize == doc.memoryUsage());
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("{}")),
});
}
SECTION("should duplicate serialized(std::string)") {
array.add(serialized(std::string("\0XX", 3)));
const size_t expectedSize = JSON_ARRAY_SIZE(1) + JSON_STRING_SIZE(3);
REQUIRE(expectedSize == doc.memoryUsage());
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString(" XX")),
});
}
}
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]");
}
}

View File

@@ -1,10 +1,12 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2023, Benoit BLANCHON
// Copyright © 2014-2024, 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;
@@ -14,7 +16,7 @@ TEST_CASE("JsonArray::clear()") {
}
SECTION("Removes all elements") {
StaticJsonDocument<64> doc;
JsonDocument doc;
JsonArray array = doc.to<JsonArray>();
array.add(1);
array.add(2);
@@ -22,4 +24,23 @@ TEST_CASE("JsonArray::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()),
});
}
}

View File

@@ -1,12 +1,12 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2023, Benoit BLANCHON
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
TEST_CASE("Compare JsonArray with JsonArray") {
StaticJsonDocument<256> doc;
JsonDocument doc;
SECTION("Compare with unbound") {
JsonArray array = doc.to<JsonArray>();
@@ -43,15 +43,15 @@ TEST_CASE("Compare JsonArray with JsonArray") {
}
SECTION("Compare with identical array") {
JsonArray array1 = doc.createNestedArray();
JsonArray array1 = doc.add<JsonArray>();
array1.add(1);
array1.add("hello");
array1.createNestedObject();
array1.add<JsonObject>();
JsonArray array2 = doc.createNestedArray();
JsonArray array2 = doc.add<JsonArray>();
array2.add(1);
array2.add("hello");
array2.createNestedObject();
array2.add<JsonObject>();
CHECK(array1 == array2);
CHECK(array1 <= array2);
@@ -62,15 +62,15 @@ TEST_CASE("Compare JsonArray with JsonArray") {
}
SECTION("Compare with different array") {
JsonArray array1 = doc.createNestedArray();
JsonArray array1 = doc.add<JsonArray>();
array1.add(1);
array1.add("hello1");
array1.createNestedObject();
array1.add<JsonObject>();
JsonArray array2 = doc.createNestedArray();
JsonArray array2 = doc.add<JsonArray>();
array2.add(1);
array2.add("hello2");
array2.createNestedObject();
array2.add<JsonObject>();
CHECK(array1 != array2);
CHECK_FALSE(array1 == array2);
@@ -82,7 +82,7 @@ TEST_CASE("Compare JsonArray with JsonArray") {
}
TEST_CASE("Compare JsonArray with JsonVariant") {
StaticJsonDocument<256> doc;
JsonDocument doc;
SECTION("Compare with self") {
JsonArray array = doc.to<JsonArray>();
@@ -107,15 +107,15 @@ TEST_CASE("Compare JsonArray with JsonVariant") {
}
SECTION("Compare with identical array") {
JsonArray array = doc.createNestedArray();
JsonArray array = doc.add<JsonArray>();
array.add(1);
array.add("hello");
array.createNestedObject();
array.add<JsonObject>();
JsonVariant variant = doc.createNestedArray();
JsonVariant variant = doc.add<JsonArray>();
variant.add(1);
variant.add("hello");
variant.createNestedObject();
variant.add<JsonObject>();
CHECK(array == variant);
CHECK(array <= variant);
@@ -133,15 +133,15 @@ TEST_CASE("Compare JsonArray with JsonVariant") {
}
SECTION("Compare with different array") {
JsonArray array = doc.createNestedArray();
JsonArray array = doc.add<JsonArray>();
array.add(1);
array.add("hello1");
array.createNestedObject();
array.add<JsonObject>();
JsonVariant variant = doc.createNestedArray();
JsonVariant variant = doc.add<JsonArray>();
variant.add(1);
variant.add("hello2");
variant.createNestedObject();
variant.add<JsonObject>();
CHECK(array != variant);
CHECK_FALSE(array == variant);
@@ -153,7 +153,7 @@ TEST_CASE("Compare JsonArray with JsonVariant") {
}
TEST_CASE("Compare JsonArray with JsonVariantConst") {
StaticJsonDocument<256> doc;
JsonDocument doc;
SECTION("Compare with unbound") {
JsonArray array = doc.to<JsonArray>();
@@ -199,15 +199,15 @@ TEST_CASE("Compare JsonArray with JsonVariantConst") {
}
SECTION("Compare with identical array") {
JsonArray array = doc.createNestedArray();
JsonArray array = doc.add<JsonArray>();
array.add(1);
array.add("hello");
array.createNestedObject();
array.add<JsonObject>();
JsonArray array2 = doc.createNestedArray();
JsonArray array2 = doc.add<JsonArray>();
array2.add(1);
array2.add("hello");
array2.createNestedObject();
array2.add<JsonObject>();
JsonVariantConst variant = array2;
CHECK(array == variant);
@@ -226,15 +226,15 @@ TEST_CASE("Compare JsonArray with JsonVariantConst") {
}
SECTION("Compare with different array") {
JsonArray array = doc.createNestedArray();
JsonArray array = doc.add<JsonArray>();
array.add(1);
array.add("hello1");
array.createNestedObject();
array.add<JsonObject>();
JsonArray array2 = doc.createNestedArray();
JsonArray array2 = doc.add<JsonArray>();
array2.add(1);
array2.add("hello2");
array2.createNestedObject();
array2.add<JsonObject>();
JsonVariantConst variant = array2;
CHECK(array != variant);
@@ -247,7 +247,7 @@ TEST_CASE("Compare JsonArray with JsonVariantConst") {
}
TEST_CASE("Compare JsonArray with JsonArrayConst") {
StaticJsonDocument<256> doc;
JsonDocument doc;
SECTION("Compare with unbound") {
JsonArray array = doc.to<JsonArray>();
@@ -292,15 +292,15 @@ TEST_CASE("Compare JsonArray with JsonArrayConst") {
}
SECTION("Compare with identical array") {
JsonArray array1 = doc.createNestedArray();
JsonArray array1 = doc.add<JsonArray>();
array1.add(1);
array1.add("hello");
array1.createNestedObject();
array1.add<JsonObject>();
JsonArray array2 = doc.createNestedArray();
JsonArray array2 = doc.add<JsonArray>();
array2.add(1);
array2.add("hello");
array2.createNestedObject();
array2.add<JsonObject>();
JsonArrayConst carray2 = array2;
CHECK(array1 == carray2);
@@ -319,15 +319,15 @@ TEST_CASE("Compare JsonArray with JsonArrayConst") {
}
SECTION("Compare with different array") {
JsonArray array1 = doc.createNestedArray();
JsonArray array1 = doc.add<JsonArray>();
array1.add(1);
array1.add("hello1");
array1.createNestedObject();
array1.add<JsonObject>();
JsonArray array2 = doc.createNestedArray();
JsonArray array2 = doc.add<JsonArray>();
array2.add(1);
array2.add("hello2");
array2.createNestedObject();
array2.add<JsonObject>();
JsonArrayConst carray2 = array2;
CHECK(array1 != carray2);
@@ -347,7 +347,7 @@ TEST_CASE("Compare JsonArray with JsonArrayConst") {
}
TEST_CASE("Compare JsonArrayConst with JsonArrayConst") {
StaticJsonDocument<256> doc;
JsonDocument doc;
SECTION("Compare with unbound") {
JsonArray array = doc.to<JsonArray>();
@@ -387,16 +387,16 @@ TEST_CASE("Compare JsonArrayConst with JsonArrayConst") {
}
SECTION("Compare with identical array") {
JsonArray array1 = doc.createNestedArray();
JsonArray array1 = doc.add<JsonArray>();
array1.add(1);
array1.add("hello");
array1.createNestedObject();
array1.add<JsonObject>();
JsonArrayConst carray1 = array1;
JsonArray array2 = doc.createNestedArray();
JsonArray array2 = doc.add<JsonArray>();
array2.add(1);
array2.add("hello");
array2.createNestedObject();
array2.add<JsonObject>();
JsonArrayConst carray2 = array2;
CHECK(carray1 == carray2);
@@ -408,16 +408,16 @@ TEST_CASE("Compare JsonArrayConst with JsonArrayConst") {
}
SECTION("Compare with different array") {
JsonArray array1 = doc.createNestedArray();
JsonArray array1 = doc.add<JsonArray>();
array1.add(1);
array1.add("hello1");
array1.createNestedObject();
array1.add<JsonObject>();
JsonArrayConst carray1 = array1;
JsonArray array2 = doc.createNestedArray();
JsonArray array2 = doc.add<JsonArray>();
array2.add(1);
array2.add("hello2");
array2.createNestedObject();
array2.add<JsonObject>();
JsonArrayConst carray2 = array2;
CHECK(carray1 != carray2);
@@ -430,7 +430,7 @@ TEST_CASE("Compare JsonArrayConst with JsonArrayConst") {
}
TEST_CASE("Compare JsonArrayConst with JsonVariant") {
StaticJsonDocument<256> doc;
JsonDocument doc;
SECTION("Compare with self") {
JsonArray array = doc.to<JsonArray>();
@@ -455,16 +455,16 @@ TEST_CASE("Compare JsonArrayConst with JsonVariant") {
}
SECTION("Compare with identical array") {
JsonArray array1 = doc.createNestedArray();
JsonArray array1 = doc.add<JsonArray>();
array1.add(1);
array1.add("hello");
array1.createNestedObject();
array1.add<JsonObject>();
JsonArrayConst carray1 = array1;
JsonArray array2 = doc.createNestedArray();
JsonArray array2 = doc.add<JsonArray>();
array2.add(1);
array2.add("hello");
array2.createNestedObject();
array2.add<JsonObject>();
JsonVariant variant2 = array2;
CHECK(carray1 == variant2);
@@ -483,16 +483,16 @@ TEST_CASE("Compare JsonArrayConst with JsonVariant") {
}
SECTION("Compare with different array") {
JsonArray array1 = doc.createNestedArray();
JsonArray array1 = doc.add<JsonArray>();
array1.add(1);
array1.add("hello1");
array1.createNestedObject();
array1.add<JsonObject>();
JsonArrayConst carray1 = array1;
JsonArray array2 = doc.createNestedArray();
JsonArray array2 = doc.add<JsonArray>();
array2.add(1);
array2.add("hello2");
array2.createNestedObject();
array2.add<JsonObject>();
JsonVariant variant2 = array2;
CHECK(carray1 != variant2);

View File

@@ -1,13 +1,15 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2023, Benoit BLANCHON
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
#include "Allocators.hpp"
TEST_CASE("copyArray()") {
SECTION("int[] -> JsonArray") {
DynamicJsonDocument doc(4096);
JsonDocument doc;
JsonArray array = doc.to<JsonArray>();
char json[32];
int source[] = {1, 2, 3};
@@ -20,7 +22,7 @@ TEST_CASE("copyArray()") {
}
SECTION("std::string[] -> JsonArray") {
DynamicJsonDocument doc(4096);
JsonDocument doc;
JsonArray array = doc.to<JsonArray>();
char json[32];
std::string source[] = {"a", "b", "c"};
@@ -33,7 +35,7 @@ TEST_CASE("copyArray()") {
}
SECTION("const char*[] -> JsonArray") {
DynamicJsonDocument doc(4096);
JsonDocument doc;
JsonArray array = doc.to<JsonArray>();
char json[32];
const char* source[] = {"a", "b", "c"};
@@ -46,7 +48,7 @@ TEST_CASE("copyArray()") {
}
SECTION("const char[][] -> JsonArray") {
DynamicJsonDocument doc(4096);
JsonDocument doc;
JsonArray array = doc.to<JsonArray>();
char json[32];
char source[][2] = {"a", "b", "c"};
@@ -59,7 +61,7 @@ TEST_CASE("copyArray()") {
}
SECTION("const char[][] -> JsonDocument") {
DynamicJsonDocument doc(4096);
JsonDocument doc;
char json[32];
char source[][2] = {"a", "b", "c"};
@@ -71,7 +73,7 @@ TEST_CASE("copyArray()") {
}
SECTION("const char[][] -> MemberProxy") {
DynamicJsonDocument doc(4096);
JsonDocument doc;
char json[32];
char source[][2] = {"a", "b", "c"};
@@ -83,7 +85,7 @@ TEST_CASE("copyArray()") {
}
SECTION("int[] -> JsonDocument") {
DynamicJsonDocument doc(4096);
JsonDocument doc;
char json[32];
int source[] = {1, 2, 3};
@@ -95,7 +97,7 @@ TEST_CASE("copyArray()") {
}
SECTION("int[] -> MemberProxy") {
DynamicJsonDocument doc(4096);
JsonDocument doc;
char json[32];
int source[] = {1, 2, 3};
@@ -107,21 +109,16 @@ TEST_CASE("copyArray()") {
}
SECTION("int[] -> JsonArray, but not enough memory") {
const size_t SIZE = JSON_ARRAY_SIZE(2);
StaticJsonDocument<SIZE> doc;
JsonDocument doc(FailingAllocator::instance());
JsonArray array = doc.to<JsonArray>();
char json[32];
int source[] = {1, 2, 3};
bool ok = copyArray(source, array);
REQUIRE_FALSE(ok);
serializeJson(array, json);
CHECK(std::string("[1,2]") == json);
}
SECTION("int[][] -> JsonArray") {
DynamicJsonDocument doc(4096);
JsonDocument doc;
JsonArray array = doc.to<JsonArray>();
char json[32];
int source[][3] = {{1, 2, 3}, {4, 5, 6}};
@@ -134,7 +131,7 @@ TEST_CASE("copyArray()") {
}
SECTION("int[][] -> MemberProxy") {
DynamicJsonDocument doc(4096);
JsonDocument doc;
char json[32];
int source[][3] = {{1, 2, 3}, {4, 5, 6}};
@@ -146,7 +143,7 @@ TEST_CASE("copyArray()") {
}
SECTION("int[][] -> JsonDocument") {
DynamicJsonDocument doc(4096);
JsonDocument doc;
char json[32];
int source[][3] = {{1, 2, 3}, {4, 5, 6}};
@@ -158,25 +155,16 @@ TEST_CASE("copyArray()") {
}
SECTION("int[][] -> JsonArray, but not enough memory") {
const size_t SIZE =
JSON_ARRAY_SIZE(2) + JSON_ARRAY_SIZE(3) + JSON_ARRAY_SIZE(2);
StaticJsonDocument<SIZE> doc;
JsonDocument doc(FailingAllocator::instance());
JsonArray array = doc.to<JsonArray>();
char json[32] = "";
int source[][3] = {{1, 2, 3}, {4, 5, 6}};
CAPTURE(SIZE);
bool ok = copyArray(source, array);
CAPTURE(doc.memoryUsage());
CHECK_FALSE(ok);
serializeJson(array, json);
CHECK(std::string("[[1,2,3],[4,5]]") == json);
REQUIRE(ok == false);
}
SECTION("JsonArray -> int[], with more space than needed") {
DynamicJsonDocument doc(4096);
JsonDocument doc;
char json[] = "[1,2,3]";
DeserializationError err = deserializeJson(doc, json);
CHECK(err == DeserializationError::Ok);
@@ -193,7 +181,7 @@ TEST_CASE("copyArray()") {
}
SECTION("JsonArray -> int[], without enough space") {
DynamicJsonDocument doc(4096);
JsonDocument doc;
char json[] = "[1,2,3]";
DeserializationError err = deserializeJson(doc, json);
CHECK(err == DeserializationError::Ok);
@@ -208,7 +196,7 @@ TEST_CASE("copyArray()") {
}
SECTION("JsonArray -> std::string[]") {
DynamicJsonDocument doc(4096);
JsonDocument doc;
char json[] = "[\"a\",\"b\",\"c\"]";
DeserializationError err = deserializeJson(doc, json);
CHECK(err == DeserializationError::Ok);
@@ -225,7 +213,7 @@ TEST_CASE("copyArray()") {
}
SECTION("JsonArray -> char[N][]") {
DynamicJsonDocument doc(4096);
JsonDocument doc;
char json[] = "[\"a12345\",\"b123456\",\"c1234567\"]";
DeserializationError err = deserializeJson(doc, json);
CHECK(err == DeserializationError::Ok);
@@ -242,7 +230,7 @@ TEST_CASE("copyArray()") {
}
SECTION("JsonDocument -> int[]") {
DynamicJsonDocument doc(4096);
JsonDocument doc;
char json[] = "[1,2,3]";
DeserializationError err = deserializeJson(doc, json);
CHECK(err == DeserializationError::Ok);
@@ -258,7 +246,7 @@ TEST_CASE("copyArray()") {
}
SECTION("MemberProxy -> int[]") {
DynamicJsonDocument doc(4096);
JsonDocument doc;
char json[] = "{\"data\":[1,2,3]}";
DeserializationError err = deserializeJson(doc, json);
CHECK(err == DeserializationError::Ok);
@@ -274,7 +262,7 @@ TEST_CASE("copyArray()") {
}
SECTION("ElementProxy -> int[]") {
DynamicJsonDocument doc(4096);
JsonDocument doc;
char json[] = "[[1,2,3]]";
DeserializationError err = deserializeJson(doc, json);
CHECK(err == DeserializationError::Ok);
@@ -290,7 +278,7 @@ TEST_CASE("copyArray()") {
}
SECTION("JsonArray -> int[][]") {
DynamicJsonDocument doc(4096);
JsonDocument doc;
char json[] = "[[1,2],[3],[4]]";
DeserializationError err = deserializeJson(doc, json);
@@ -309,7 +297,7 @@ TEST_CASE("copyArray()") {
}
SECTION("JsonDocument -> int[][]") {
DynamicJsonDocument doc(4096);
JsonDocument doc;
char json[] = "[[1,2],[3],[4]]";
DeserializationError err = deserializeJson(doc, json);
@@ -327,7 +315,7 @@ TEST_CASE("copyArray()") {
}
SECTION("MemberProxy -> int[][]") {
DynamicJsonDocument doc(4096);
JsonDocument doc;
char json[] = "{\"data\":[[1,2],[3],[4]]}";
DeserializationError err = deserializeJson(doc, json);

View File

@@ -1,21 +0,0 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2023, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
TEST_CASE("JsonArray basics") {
DynamicJsonDocument doc(4096);
JsonArray array = doc.to<JsonArray>();
SECTION("CreateNestedArray") {
JsonArray arr = array.createNestedArray();
REQUIRE(arr == array[0].as<JsonArray>());
}
SECTION("CreateNestedObject") {
JsonObject obj = array.createNestedObject();
REQUIRE(obj == array[0].as<JsonObject>());
}
}

View File

@@ -1,25 +1,22 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2023, Benoit BLANCHON
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
TEST_CASE("JsonArray::operator==()") {
DynamicJsonDocument doc1(4096);
JsonDocument doc1;
JsonArray array1 = doc1.to<JsonArray>();
JsonArrayConst array1c = array1;
DynamicJsonDocument doc2(4096);
JsonDocument doc2;
JsonArray array2 = doc2.to<JsonArray>();
JsonArrayConst array2c = array2;
SECTION("should return false when arrays differ") {
array1.add("coucou");
array2.add(1);
REQUIRE_FALSE(array1 == array2);
REQUIRE_FALSE(array1c == array2c);
}
SECTION("should return false when LHS has more elements") {
@@ -28,7 +25,6 @@ TEST_CASE("JsonArray::operator==()") {
array2.add(1);
REQUIRE_FALSE(array1 == array2);
REQUIRE_FALSE(array1c == array2c);
}
SECTION("should return false when RHS has more elements") {
@@ -37,7 +33,6 @@ TEST_CASE("JsonArray::operator==()") {
array2.add(2);
REQUIRE_FALSE(array1 == array2);
REQUIRE_FALSE(array1c == array2c);
}
SECTION("should return true when arrays equal") {
@@ -45,7 +40,6 @@ TEST_CASE("JsonArray::operator==()") {
array2.add("coucou");
REQUIRE(array1 == array2);
REQUIRE(array1c == array2c);
}
SECTION("should return false when RHS is null") {

View File

@@ -1,5 +1,5 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2023, Benoit BLANCHON
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
@@ -12,25 +12,12 @@ TEST_CASE("JsonArray::isNull()") {
}
SECTION("returns false") {
DynamicJsonDocument doc(4096);
JsonDocument doc;
JsonArray arr = doc.to<JsonArray>();
REQUIRE(arr.isNull() == false);
}
}
TEST_CASE("JsonArrayConst::isNull()") {
SECTION("returns true") {
JsonArrayConst arr;
REQUIRE(arr.isNull() == true);
}
SECTION("returns false") {
DynamicJsonDocument doc(4096);
JsonArrayConst arr = doc.to<JsonArray>();
REQUIRE(arr.isNull() == false);
}
}
TEST_CASE("JsonArray::operator bool()") {
SECTION("returns false") {
JsonArray arr;
@@ -38,21 +25,8 @@ TEST_CASE("JsonArray::operator bool()") {
}
SECTION("returns true") {
DynamicJsonDocument doc(4096);
JsonDocument doc;
JsonArray arr = doc.to<JsonArray>();
REQUIRE(static_cast<bool>(arr) == true);
}
}
TEST_CASE("JsonArrayConst::operator bool()") {
SECTION("returns false") {
JsonArrayConst arr;
REQUIRE(static_cast<bool>(arr) == false);
}
SECTION("returns true") {
DynamicJsonDocument doc(4096);
JsonArrayConst arr = doc.to<JsonArray>();
REQUIRE(static_cast<bool>(arr) == true);
}
}

View File

@@ -1,35 +1,29 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2023, Benoit BLANCHON
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
template <typename TArray>
static void run_iterator_test() {
StaticJsonDocument<JSON_ARRAY_SIZE(2)> doc;
JsonArray tmp = doc.to<JsonArray>();
tmp.add(12);
tmp.add(34);
TArray array = tmp;
typename TArray::iterator it = array.begin();
typename TArray::iterator end = array.end();
REQUIRE(end != it);
REQUIRE(12 == it->template as<int>());
REQUIRE(12 == static_cast<int>(*it));
++it;
REQUIRE(end != it);
REQUIRE(34 == it->template as<int>());
REQUIRE(34 == static_cast<int>(*it));
++it;
REQUIRE(end == it);
}
TEST_CASE("JsonArray::begin()/end()") {
SECTION("Non null JsonArray") {
run_iterator_test<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") {
@@ -38,15 +32,3 @@ TEST_CASE("JsonArray::begin()/end()") {
REQUIRE(array.begin() == array.end());
}
}
TEST_CASE("JsonArrayConst::begin()/end()") {
SECTION("Non null JsonArrayConst") {
run_iterator_test<JsonArrayConst>();
}
SECTION("Null JsonArrayConst") {
JsonArrayConst array;
REQUIRE(array.begin() == array.end());
}
}

View File

@@ -1,42 +0,0 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2023, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
TEST_CASE("JsonArray::memoryUsage()") {
DynamicJsonDocument doc(4096);
JsonArray arr = doc.to<JsonArray>();
SECTION("return 0 if uninitialized") {
JsonArray unitialized;
REQUIRE(unitialized.memoryUsage() == 0);
}
SECTION("JSON_ARRAY_SIZE(0) if empty") {
REQUIRE(arr.memoryUsage() == JSON_ARRAY_SIZE(0));
}
SECTION("JSON_ARRAY_SIZE(1) after add") {
arr.add("hello");
REQUIRE(arr.memoryUsage() == JSON_ARRAY_SIZE(1));
}
SECTION("includes the size of the string") {
arr.add(std::string("hello"));
REQUIRE(arr.memoryUsage() == JSON_ARRAY_SIZE(1) + 6);
}
SECTION("includes the size of the nested array") {
JsonArray nested = arr.createNestedArray();
nested.add(42);
REQUIRE(arr.memoryUsage() == 2 * JSON_ARRAY_SIZE(1));
}
SECTION("includes the size of the nested arrect") {
JsonObject nested = arr.createNestedObject();
nested["hello"] = "world";
REQUIRE(arr.memoryUsage() == JSON_OBJECT_SIZE(1) + JSON_ARRAY_SIZE(1));
}
}

View File

@@ -1,12 +1,12 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2023, Benoit BLANCHON
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
TEST_CASE("JsonArray::nesting()") {
DynamicJsonDocument doc(4096);
JsonDocument doc;
JsonArray arr = doc.to<JsonArray>();
SECTION("return 0 if uninitialized") {
@@ -24,12 +24,12 @@ TEST_CASE("JsonArray::nesting()") {
}
SECTION("returns 2 with nested array") {
arr.createNestedArray();
arr.add<JsonArray>();
REQUIRE(arr.nesting() == 2);
}
SECTION("returns 2 with nested object") {
arr.createNestedObject();
arr.add<JsonObject>();
REQUIRE(arr.nesting() == 2);
}
}

View File

@@ -1,12 +1,14 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2023, Benoit BLANCHON
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
#include "Allocators.hpp"
TEST_CASE("JsonArray::remove()") {
DynamicJsonDocument doc(4096);
JsonDocument doc;
JsonArray array = doc.to<JsonArray>();
array.add(1);
array.add(2);
@@ -87,3 +89,23 @@ TEST_CASE("JsonArray::remove()") {
unboundArray.remove(unboundArray.begin());
}
}
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
});
}

View File

@@ -1,12 +1,12 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2023, Benoit BLANCHON
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
TEST_CASE("JsonArray::size()") {
DynamicJsonDocument doc(4096);
JsonDocument doc;
JsonArray array = doc.to<JsonArray>();
SECTION("returns 0 is empty") {

View File

@@ -1,5 +1,5 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2023, Benoit BLANCHON
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
@@ -12,7 +12,7 @@ static void eraseString(std::string& str) {
}
TEST_CASE("std::string") {
DynamicJsonDocument doc(4096);
JsonDocument doc;
JsonArray array = doc.to<JsonArray>();
SECTION("add()") {

View File

@@ -1,13 +1,16 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2023, Benoit BLANCHON
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <stdint.h>
#include <catch.hpp>
#include "Allocators.hpp"
TEST_CASE("JsonArray::operator[]") {
DynamicJsonDocument doc(4096);
SpyingAllocator spy;
JsonDocument doc(&spy);
JsonArray array = doc.to<JsonArray>();
SECTION("Pad with null") {
@@ -65,7 +68,7 @@ TEST_CASE("JsonArray::operator[]") {
}
SECTION("nested array") {
DynamicJsonDocument doc2(4096);
JsonDocument doc2;
JsonArray arr2 = doc2.to<JsonArray>();
array[0] = arr2;
@@ -76,7 +79,7 @@ TEST_CASE("JsonArray::operator[]") {
}
SECTION("nested object") {
DynamicJsonDocument doc2(4096);
JsonDocument doc2;
JsonObject obj = doc2.to<JsonObject>();
array[0] = obj;
@@ -87,7 +90,7 @@ TEST_CASE("JsonArray::operator[]") {
}
SECTION("array subscript") {
DynamicJsonDocument doc2(4096);
JsonDocument doc2;
JsonArray arr2 = doc2.to<JsonArray>();
const char* str = "hello";
@@ -100,7 +103,7 @@ TEST_CASE("JsonArray::operator[]") {
SECTION("object subscript") {
const char* str = "hello";
DynamicJsonDocument doc2(4096);
JsonDocument doc2;
JsonObject obj = doc2.to<JsonObject>();
obj["x"] = str;
@@ -112,20 +115,25 @@ TEST_CASE("JsonArray::operator[]") {
SECTION("should not duplicate const char*") {
array[0] = "world";
const size_t expectedSize = JSON_ARRAY_SIZE(1);
REQUIRE(expectedSize == doc.memoryUsage());
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
});
}
SECTION("should duplicate char*") {
array[0] = const_cast<char*>("world");
const size_t expectedSize = JSON_ARRAY_SIZE(1) + JSON_STRING_SIZE(5);
REQUIRE(expectedSize == doc.memoryUsage());
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("world")),
});
}
SECTION("should duplicate std::string") {
array[0] = std::string("world");
const size_t expectedSize = JSON_ARRAY_SIZE(1) + JSON_STRING_SIZE(5);
REQUIRE(expectedSize == doc.memoryUsage());
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("world")),
});
}
SECTION("array[0].to<JsonObject>()") {
@@ -157,18 +165,3 @@ TEST_CASE("JsonArray::operator[]") {
}
#endif
}
TEST_CASE("JsonArrayConst::operator[]") {
DynamicJsonDocument doc(4096);
JsonArray array = doc.to<JsonArray>();
array.add(0);
SECTION("int") {
array[0] = 123;
JsonArrayConst carr = array;
REQUIRE(123 == carr[0].as<int>());
REQUIRE(true == carr[0].is<int>());
REQUIRE(false == carr[0].is<bool>());
}
}

View File

@@ -1,5 +1,5 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2023, Benoit BLANCHON
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
@@ -19,14 +19,6 @@ TEST_CASE("Unbound JsonArray") {
REQUIRE(0 == array.size());
}
SECTION("CreateNestedArrayFails") {
REQUIRE(array.createNestedArray().isNull());
}
SECTION("CreateNestedObjectFails") {
REQUIRE(array.createNestedObject().isNull());
}
SECTION("PrintToWritesBrackets") {
char buffer[32];
serializeJson(array, buffer, sizeof(buffer));

View File

@@ -0,0 +1,19 @@
# ArduinoJson - https://arduinojson.org
# Copyright © 2014-2024, 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"
)

View File

@@ -0,0 +1,63 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2024, 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);
}
}

View File

@@ -0,0 +1,32 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2024, 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);
}
}

View File

@@ -0,0 +1,34 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2024, 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());
}
}

View File

@@ -0,0 +1,35 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2024, 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);
}
}

View File

@@ -0,0 +1,27 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2024, 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());
}
}

View File

@@ -0,0 +1,20 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2024, 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);
REQUIRE(1 == arr[0].as<int>());
REQUIRE(2 == arr[1].as<int>());
REQUIRE(3 == arr[2].as<int>());
REQUIRE(0 == arr[3].as<int>());
}

View File

@@ -1,20 +1,18 @@
# ArduinoJson - https://arduinojson.org
# Copyright © 2014-2023, Benoit BLANCHON
# Copyright © 2014-2024, Benoit BLANCHON
# MIT License
add_executable(JsonDeserializerTests
array.cpp
array_static.cpp
DeserializationError.cpp
destination_types.cpp
errors.cpp
filter.cpp
incomplete_input.cpp
input_types.cpp
invalid_input.cpp
misc.cpp
nestingLimit.cpp
number.cpp
object.cpp
object_static.cpp
string.cpp
)

View File

@@ -1,5 +1,5 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2023, Benoit BLANCHON
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>

View File

@@ -1,12 +1,17 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2023, Benoit BLANCHON
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
#include "Allocators.hpp"
using ArduinoJson::detail::sizeofArray;
TEST_CASE("deserialize JSON array") {
DynamicJsonDocument doc(4096);
SpyingAllocator spy;
JsonDocument doc(&spy);
SECTION("An empty array") {
DeserializationError err = deserializeJson(doc, "[]");
@@ -244,10 +249,71 @@ TEST_CASE("deserialize JSON array") {
SECTION("Should clear the JsonArray") {
deserializeJson(doc, "[1,2,3,4]");
deserializeJson(doc, "[]");
JsonArray arr = doc.as<JsonArray>();
spy.clearLog();
deserializeJson(doc, "[]");
JsonArray arr = doc.as<JsonArray>();
REQUIRE(arr.size() == 0);
REQUIRE(doc.memoryUsage() == JSON_ARRAY_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)),
});
}
}

View File

@@ -1,89 +0,0 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2023, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
TEST_CASE("deserialize JSON array with a StaticJsonDocument") {
SECTION("BufferOfTheRightSizeForEmptyArray") {
StaticJsonDocument<JSON_ARRAY_SIZE(0)> doc;
char input[] = "[]";
DeserializationError err = deserializeJson(doc, input);
REQUIRE(err == DeserializationError::Ok);
}
SECTION("TooSmallBufferForArrayWithOneValue") {
StaticJsonDocument<JSON_ARRAY_SIZE(0)> doc;
char input[] = "[1]";
DeserializationError err = deserializeJson(doc, input);
REQUIRE(err == DeserializationError::NoMemory);
}
SECTION("BufferOfTheRightSizeForArrayWithOneValue") {
StaticJsonDocument<JSON_ARRAY_SIZE(1)> doc;
char input[] = "[1]";
DeserializationError err = deserializeJson(doc, input);
REQUIRE(err == DeserializationError::Ok);
}
SECTION("TooSmallBufferForArrayWithNestedObject") {
StaticJsonDocument<JSON_ARRAY_SIZE(0) + JSON_OBJECT_SIZE(0)> doc;
char input[] = "[{}]";
DeserializationError err = deserializeJson(doc, input);
REQUIRE(err == DeserializationError::NoMemory);
}
SECTION("BufferOfTheRightSizeForArrayWithNestedObject") {
StaticJsonDocument<JSON_ARRAY_SIZE(1) + JSON_OBJECT_SIZE(0)> doc;
char input[] = "[{}]";
DeserializationError err = deserializeJson(doc, input);
REQUIRE(err == DeserializationError::Ok);
}
SECTION("CopyStringNotSpaces") {
StaticJsonDocument<100> doc;
deserializeJson(doc, " [ \"1234567\" ] ");
REQUIRE(JSON_ARRAY_SIZE(1) + JSON_STRING_SIZE(7) == doc.memoryUsage());
// note: we use a string of 8 bytes to be sure that the StaticMemoryPool
// will not insert bytes to enforce alignement
}
SECTION("Should clear the JsonArray") {
StaticJsonDocument<JSON_ARRAY_SIZE(4)> doc;
char input[] = "[1,2,3,4]";
deserializeJson(doc, input);
deserializeJson(doc, "[]");
JsonArray arr = doc.as<JsonArray>();
REQUIRE(arr.size() == 0);
REQUIRE(doc.memoryUsage() == JSON_ARRAY_SIZE(0));
}
SECTION("Array") {
StaticJsonDocument<JSON_ARRAY_SIZE(2)> doc;
char input[] = "[1,2]";
DeserializationError err = deserializeJson(doc, input);
JsonArray arr = doc.as<JsonArray>();
REQUIRE(err == DeserializationError::Ok);
REQUIRE(doc.is<JsonArray>());
REQUIRE(doc.memoryUsage() == JSON_ARRAY_SIZE(2));
REQUIRE(arr[0] == 1);
REQUIRE(arr[1] == 2);
}
}

View File

@@ -0,0 +1,108 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
#include <string>
#include "Allocators.hpp"
using ArduinoJson::detail::sizeofArray;
using ArduinoJson::detail::sizeofObject;
TEST_CASE("deserializeJson(JsonDocument&)") {
SpyingAllocator spy;
JsonDocument doc(&spy);
doc.add(std::string("hello"));
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(std::string("hello"));
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(std::string("hello"));
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[std::string("hello")] = std::string("world");
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{});
}
}

View File

@@ -0,0 +1,120 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#define ARDUINOJSON_DECODE_UNICODE 1
#include <ArduinoJson.h>
#include <catch.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);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,29 +0,0 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2023, Benoit BLANCHON
// MIT License
#define ARDUINOJSON_DECODE_UNICODE 1
#include <ArduinoJson.h>
#include <catch.hpp>
TEST_CASE("Truncated JSON input") {
const char* testCases[] = {"\"hello", "\'hello", "'\\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,"};
const size_t testCount = sizeof(testCases) / sizeof(testCases[0]);
DynamicJsonDocument doc(4096);
for (size_t i = 0; i < testCount; i++) {
const char* input = testCases[i];
CAPTURE(input);
REQUIRE(deserializeJson(doc, input) ==
DeserializationError::IncompleteInput);
}
}

View File

@@ -1,5 +1,5 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2023, Benoit BLANCHON
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
@@ -7,25 +7,58 @@
#include <catch.hpp>
#include <sstream>
#include "Allocators.hpp"
#include "CustomReader.hpp"
using ArduinoJson::detail::sizeofObject;
TEST_CASE("deserializeJson(char*)") {
StaticJsonDocument<1024> doc;
SpyingAllocator spy;
JsonDocument doc(&spy);
SECTION("should not duplicate strings") {
char input[] = "{\"hello\":\"world\"}";
char input[] = "{\"hello\":\"world\"}";
DeserializationError err = deserializeJson(doc, input);
DeserializationError err = deserializeJson(doc, input);
REQUIRE(err == DeserializationError::Ok);
CHECK(doc.memoryUsage() == JSON_OBJECT_SIZE(1));
CHECK(doc.as<JsonVariant>().memoryUsage() ==
JSON_OBJECT_SIZE(1)); // issue #1318
}
REQUIRE(err == DeserializationError::Ok);
REQUIRE(spy.log() ==
AllocatorLog{
Allocate(sizeofStringBuffer()),
Reallocate(sizeofStringBuffer(), sizeofString("hello")),
Allocate(sizeofPool()),
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&)") {
DynamicJsonDocument doc(4096);
JsonDocument doc;
SECTION("should accept const string") {
const std::string input("[42]");
@@ -54,7 +87,7 @@ TEST_CASE("deserializeJson(const std::string&)") {
}
TEST_CASE("deserializeJson(std::istream&)") {
DynamicJsonDocument doc(4096);
JsonDocument doc;
SECTION("array") {
std::istringstream json(" [ 42 ] ");
@@ -125,7 +158,7 @@ TEST_CASE("deserializeJson(VLA)") {
char vla[i];
strcpy(vla, "{\"a\":42}");
StaticJsonDocument<JSON_OBJECT_SIZE(1)> doc;
JsonDocument doc;
DeserializationError err = deserializeJson(doc, vla);
REQUIRE(err == DeserializationError::Ok);
@@ -133,7 +166,7 @@ TEST_CASE("deserializeJson(VLA)") {
#endif
TEST_CASE("deserializeJson(CustomReader)") {
DynamicJsonDocument doc(4096);
JsonDocument doc;
CustomReader reader("[4,2]");
DeserializationError err = deserializeJson(doc, reader);
@@ -144,10 +177,10 @@ TEST_CASE("deserializeJson(CustomReader)") {
}
TEST_CASE("deserializeJson(JsonDocument&, MemberProxy)") {
DynamicJsonDocument doc1(4096);
JsonDocument doc1;
doc1["payload"] = "[4,2]";
DynamicJsonDocument doc2(4096);
JsonDocument doc2;
DeserializationError err = deserializeJson(doc2, doc1["payload"]);
REQUIRE(err == DeserializationError::Ok);
@@ -157,10 +190,10 @@ TEST_CASE("deserializeJson(JsonDocument&, MemberProxy)") {
}
TEST_CASE("deserializeJson(JsonDocument&, JsonVariant)") {
DynamicJsonDocument doc1(4096);
JsonDocument doc1;
doc1["payload"] = "[4,2]";
DynamicJsonDocument doc2(4096);
JsonDocument doc2;
DeserializationError err =
deserializeJson(doc2, doc1["payload"].as<JsonVariant>());
@@ -171,10 +204,10 @@ TEST_CASE("deserializeJson(JsonDocument&, JsonVariant)") {
}
TEST_CASE("deserializeJson(JsonDocument&, JsonVariantConst)") {
DynamicJsonDocument doc1(4096);
JsonDocument doc1;
doc1["payload"] = "[4,2]";
DynamicJsonDocument doc2(4096);
JsonDocument doc2;
DeserializationError err =
deserializeJson(doc2, doc1["payload"].as<JsonVariantConst>());
@@ -185,10 +218,10 @@ TEST_CASE("deserializeJson(JsonDocument&, JsonVariantConst)") {
}
TEST_CASE("deserializeJson(JsonDocument&, ElementProxy)") {
DynamicJsonDocument doc1(4096);
JsonDocument doc1;
doc1[0] = "[4,2]";
DynamicJsonDocument doc2(4096);
JsonDocument doc2;
DeserializationError err = deserializeJson(doc2, doc1[0]);
REQUIRE(err == DeserializationError::Ok);

View File

@@ -1,40 +0,0 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2023, Benoit BLANCHON
// MIT License
#define ARDUINOJSON_DECODE_UNICODE 1
#include <ArduinoJson.h>
#include <catch.hpp>
TEST_CASE("Invalid JSON input") {
const char* testCases[] = {"'\\u'", "'\\u000g'", "'\\u000'", "'\\u000G'",
"'\\u000/'", "\\x1234", "6a9", "1,",
"nulL", "tru3", "fals3", "2]",
"3}"};
const size_t testCount = sizeof(testCases) / sizeof(testCases[0]);
DynamicJsonDocument doc(4096);
for (size_t i = 0; i < testCount; i++) {
const char* input = testCases[i];
CAPTURE(input);
REQUIRE(deserializeJson(doc, input) == DeserializationError::InvalidInput);
}
}
TEST_CASE("Invalid JSON input that should pass") {
const char* testCases[] = {
"'\\ud83d'", // leading surrogate without a trailing surrogate
"'\\udda4'", // trailing surrogate without a leading surrogate
"'\\ud83d\\ud83d'", // two leading surrogates
};
const size_t testCount = sizeof(testCases) / sizeof(testCases[0]);
DynamicJsonDocument doc(4096);
for (size_t i = 0; i < testCount; i++) {
const char* input = testCases[i];
CAPTURE(input);
REQUIRE(deserializeJson(doc, input) == DeserializationError::Ok);
}
}

View File

@@ -1,117 +1,49 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2023, Benoit BLANCHON
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
using namespace Catch::Matchers;
#include "Allocators.hpp"
TEST_CASE("deserializeJson(DynamicJsonDocument&)") {
DynamicJsonDocument doc(4096);
using ArduinoJson::detail::sizeofArray;
SECTION("Edge cases") {
SECTION("null char*") {
DeserializationError err = deserializeJson(doc, static_cast<char*>(0));
TEST_CASE("deserializeJson() misc cases") {
SpyingAllocator spy;
JsonDocument doc(&spy);
REQUIRE(err != DeserializationError::Ok);
}
SECTION("null const char*") {
DeserializationError err =
deserializeJson(doc, static_cast<const char*>(0));
REQUIRE(err != DeserializationError::Ok);
}
SECTION("Empty input") {
DeserializationError err = deserializeJson(doc, "");
REQUIRE(err == DeserializationError::EmptyInput);
}
SECTION("Only spaces") {
DeserializationError err = deserializeJson(doc, " \t\n\r");
REQUIRE(err == DeserializationError::EmptyInput);
}
SECTION("issue #628") {
DeserializationError err = deserializeJson(doc, "null");
REQUIRE(err == DeserializationError::Ok);
REQUIRE(doc.is<float>() == false);
}
SECTION("Garbage") {
DeserializationError err = deserializeJson(doc, "%*$£¤");
REQUIRE(err == DeserializationError::InvalidInput);
}
SECTION("null") {
DeserializationError err = deserializeJson(doc, "null");
REQUIRE(err == DeserializationError::Ok);
REQUIRE(doc.is<float>() == false);
}
SECTION("Booleans") {
SECTION("True") {
DeserializationError err = deserializeJson(doc, "true");
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);
}
REQUIRE(err == DeserializationError::Ok);
REQUIRE(doc.is<bool>());
REQUIRE(doc.as<bool>() == true);
}
SECTION("Premature null-terminator") {
SECTION("In escape sequence") {
DeserializationError err = deserializeJson(doc, "\"\\");
SECTION("false") {
DeserializationError err = deserializeJson(doc, "false");
REQUIRE(err == DeserializationError::IncompleteInput);
}
SECTION("In double quoted string") {
DeserializationError err = deserializeJson(doc, "\"hello");
REQUIRE(err == DeserializationError::IncompleteInput);
}
SECTION("In single quoted string") {
DeserializationError err = deserializeJson(doc, "'hello");
REQUIRE(err == DeserializationError::IncompleteInput);
}
}
SECTION("Premature end of input") {
SECTION("In escape sequence") {
DeserializationError err = deserializeJson(doc, "\"\\n\"", 2);
REQUIRE(err == DeserializationError::IncompleteInput);
}
SECTION("In double quoted string") {
DeserializationError err = deserializeJson(doc, "\"hello\"", 6);
REQUIRE(err == DeserializationError::IncompleteInput);
}
SECTION("In single quoted string") {
DeserializationError err = deserializeJson(doc, "'hello'", 6);
REQUIRE(err == DeserializationError::IncompleteInput);
}
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(doc.memoryUsage() == JSON_OBJECT_SIZE(0));
REQUIRE(spy.log() == AllocatorLog{
Deallocate(sizeofArray(3)),
});
}
}

View File

@@ -1,5 +1,5 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2023, Benoit BLANCHON
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
@@ -12,7 +12,7 @@
REQUIRE(DeserializationError::TooDeep == expression);
TEST_CASE("JsonDeserializer nesting") {
DynamicJsonDocument doc(4096);
JsonDocument doc;
SECTION("Input = const char*") {
SECTION("limit = 0") {

View File

@@ -1,5 +1,5 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2023, Benoit BLANCHON
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#define ARDUINOJSON_USE_LONG_LONG 0
@@ -16,7 +16,7 @@ using ArduinoJson::detail::isnan;
} // namespace my
TEST_CASE("deserialize an integer") {
DynamicJsonDocument doc(4096);
JsonDocument doc;
SECTION("Integer") {
SECTION("0") {

View File

@@ -1,12 +1,17 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2023, Benoit BLANCHON
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
#include "Allocators.hpp"
using ArduinoJson::detail::sizeofObject;
TEST_CASE("deserialize JSON object") {
DynamicJsonDocument doc(4096);
SpyingAllocator spy;
JsonDocument doc(&spy);
SECTION("An empty object") {
DeserializationError err = deserializeJson(doc, "{}");
@@ -277,7 +282,22 @@ TEST_CASE("deserialize JSON object") {
DeserializationError err = deserializeJson(doc, "{a:{b:{c:1}},a:2}");
REQUIRE(err == DeserializationError::Ok);
REQUIRE(doc["a"] == 2);
REQUIRE(doc.as<std::string>() == "{\"a\":2}");
REQUIRE(spy.log() ==
AllocatorLog{
Allocate(sizeofStringBuffer()),
Reallocate(sizeofStringBuffer(), sizeofString("a")),
Allocate(sizeofPool()),
Allocate(sizeofStringBuffer()),
Reallocate(sizeofStringBuffer(), sizeofString("b")),
Allocate(sizeofStringBuffer()),
Reallocate(sizeofStringBuffer(), sizeofString("c")),
Allocate(sizeofStringBuffer()),
Deallocate(sizeofString("b")),
Deallocate(sizeofString("c")),
Deallocate(sizeofStringBuffer()),
Reallocate(sizeofPool(), sizeofObject(2) + sizeofObject(1)),
});
}
SECTION("Repeated key with zero copy mode") { // issue #1697
@@ -299,12 +319,17 @@ TEST_CASE("deserialize JSON object") {
SECTION("Should clear the JsonObject") {
deserializeJson(doc, "{\"hello\":\"world\"}");
spy.clearLog();
deserializeJson(doc, "{}");
JsonObject obj = doc.as<JsonObject>();
REQUIRE(doc.is<JsonObject>());
REQUIRE(obj.size() == 0);
REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(0));
REQUIRE(doc.size() == 0);
REQUIRE(spy.log() == AllocatorLog{
Deallocate(sizeofObject(1)),
Deallocate(sizeofString("hello")),
Deallocate(sizeofString("world")),
});
}
SECTION("Issue #1335") {
@@ -313,3 +338,48 @@ TEST_CASE("deserialize JSON object") {
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(2);
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[] = "{\"a\":\"b\"}";
DeserializationError err = deserializeJson(doc, input);
REQUIRE(err == DeserializationError::NoMemory);
REQUIRE(doc.as<std::string>() == "{\"a\":null}");
}
}

View File

@@ -1,64 +0,0 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2023, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
TEST_CASE("deserialize JSON object with StaticJsonDocument") {
SECTION("BufferOfTheRightSizeForEmptyObject") {
StaticJsonDocument<JSON_OBJECT_SIZE(0)> doc;
char input[] = "{}";
DeserializationError err = deserializeJson(doc, input);
REQUIRE(err == DeserializationError::Ok);
}
SECTION("TooSmallBufferForObjectWithOneValue") {
StaticJsonDocument<JSON_OBJECT_SIZE(0)> doc;
char input[] = "{\"a\":1}";
DeserializationError err = deserializeJson(doc, input);
REQUIRE(err == DeserializationError::NoMemory);
}
SECTION("BufferOfTheRightSizeForObjectWithOneValue") {
StaticJsonDocument<JSON_OBJECT_SIZE(1)> doc;
char input[] = "{\"a\":1}";
DeserializationError err = deserializeJson(doc, input);
REQUIRE(err == DeserializationError::Ok);
}
SECTION("TooSmallBufferForObjectWithNestedObject") {
StaticJsonDocument<JSON_OBJECT_SIZE(0) + JSON_ARRAY_SIZE(0)> doc;
char input[] = "{\"a\":[]}";
DeserializationError err = deserializeJson(doc, input);
REQUIRE(err == DeserializationError::NoMemory);
}
SECTION("BufferOfTheRightSizeForObjectWithNestedObject") {
StaticJsonDocument<JSON_OBJECT_SIZE(1) + JSON_ARRAY_SIZE(0)> doc;
char input[] = "{\"a\":[]}";
DeserializationError err = deserializeJson(doc, input);
REQUIRE(err == DeserializationError::Ok);
}
SECTION("Should clear the JsonObject") {
StaticJsonDocument<JSON_OBJECT_SIZE(1)> doc;
char input[] = "{\"hello\":\"world\"}";
deserializeJson(doc, input);
deserializeJson(doc, "{}");
REQUIRE(doc.as<JsonObject>().size() == 0);
REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(0));
}
}

View File

@@ -1,11 +1,16 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2023, Benoit BLANCHON
// Copyright © 2014-2024, 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;
@@ -35,7 +40,7 @@ TEST_CASE("Valid JSON strings value") {
};
const size_t testCount = sizeof(testCases) / sizeof(testCases[0]);
DynamicJsonDocument doc(4096);
JsonDocument doc;
for (size_t i = 0; i < testCount; i++) {
const TestCase& testCase = testCases[i];
@@ -47,7 +52,7 @@ TEST_CASE("Valid JSON strings value") {
}
TEST_CASE("\\u0000") {
StaticJsonDocument<200> doc;
JsonDocument doc;
DeserializationError err = deserializeJson(doc, "\"wx\\u0000yz\"");
REQUIRE(err == DeserializationError::Ok);
@@ -68,7 +73,7 @@ TEST_CASE("Truncated JSON string") {
const char* testCases[] = {"\"hello", "\'hello", "'\\u", "'\\u00", "'\\u000"};
const size_t testCount = sizeof(testCases) / sizeof(testCases[0]);
DynamicJsonDocument doc(4096);
JsonDocument doc;
for (size_t i = 0; i < testCount; i++) {
const char* input = testCases[i];
@@ -83,7 +88,7 @@ TEST_CASE("Invalid JSON string") {
"'\\u000G'", "'\\u000/'", "'\\x1234'"};
const size_t testCount = sizeof(testCases) / sizeof(testCases[0]);
DynamicJsonDocument doc(4096);
JsonDocument doc;
for (size_t i = 0; i < testCount; i++) {
const char* input = testCases[i];
@@ -92,41 +97,102 @@ TEST_CASE("Invalid JSON string") {
}
}
TEST_CASE("Not enough room to save the key") {
DynamicJsonDocument doc(JSON_OBJECT_SIZE(1) + 8);
TEST_CASE("Allocation of the key fails") {
TimebombAllocator timebomb(0);
SpyingAllocator spy(&timebomb);
JsonDocument doc(&spy);
SECTION("Quoted string") {
SECTION("Quoted string, first member") {
REQUIRE(deserializeJson(doc, "{\"example\":1}") ==
DeserializationError::Ok);
REQUIRE(deserializeJson(doc, "{\"accuracy\":1}") ==
DeserializationError::NoMemory);
REQUIRE(deserializeJson(doc, "{\"hello\":1,\"world\"}") ==
DeserializationError::NoMemory); // fails in the second string
REQUIRE(spy.log() == AllocatorLog{
AllocateFail(sizeofStringBuffer()),
});
}
SECTION("Non-quoted string") {
REQUIRE(deserializeJson(doc, "{example:1}") == DeserializationError::Ok);
REQUIRE(deserializeJson(doc, "{accuracy:1}") ==
SECTION("Quoted string, second member") {
timebomb.setCountdown(3);
REQUIRE(deserializeJson(doc, "{\"hello\":1,\"world\"}") ==
DeserializationError::NoMemory);
REQUIRE(spy.log() ==
AllocatorLog{
Allocate(sizeofStringBuffer()),
Reallocate(sizeofStringBuffer(), sizeofString("hello")),
Allocate(sizeofPool()),
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); // fails in the second string
DeserializationError::NoMemory);
REQUIRE(spy.log() ==
AllocatorLog{
Allocate(sizeofStringBuffer()),
Reallocate(sizeofStringBuffer(), sizeofString("hello")),
Allocate(sizeofPool()),
AllocateFail(sizeofStringBuffer()),
ReallocateFail(sizeofPool(), sizeofObject(1)),
});
}
}
TEST_CASE("Empty memory pool") {
// NOLINTNEXTLINE(clang-analyzer-optin.portability.UnixAPI)
DynamicJsonDocument doc(0);
TEST_CASE("String allocation fails") {
SpyingAllocator spy(FailingAllocator::instance());
JsonDocument doc(&spy);
SECTION("Input is const char*") {
REQUIRE(deserializeJson(doc, "\"hello\"") ==
DeserializationError::NoMemory);
REQUIRE(deserializeJson(doc, "\"\"") == DeserializationError::NoMemory);
}
SECTION("Input is const char*") {
char hello[] = "\"hello\"";
REQUIRE(deserializeJson(doc, hello) == DeserializationError::Ok);
char empty[] = "\"hello\"";
REQUIRE(deserializeJson(doc, empty) == DeserializationError::Ok);
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)),
});
}

View File

@@ -1,180 +0,0 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2023, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <stdlib.h> // malloc, free
#include <catch.hpp>
#include <sstream>
#include <utility>
class SpyingAllocator {
public:
SpyingAllocator(const SpyingAllocator& src) : _log(src._log) {}
SpyingAllocator(std::ostream& log) : _log(log) {}
SpyingAllocator& operator=(const SpyingAllocator& src) = delete;
void* allocate(size_t n) {
_log << "A" << n;
return malloc(n);
}
void deallocate(void* p) {
_log << "F";
free(p);
}
private:
std::ostream& _log;
};
class ControllableAllocator {
public:
ControllableAllocator() : _enabled(true) {}
void* allocate(size_t n) {
return _enabled ? malloc(n) : 0;
}
void deallocate(void* p) {
free(p);
}
void disable() {
_enabled = false;
}
private:
bool _enabled;
};
TEST_CASE("BasicJsonDocument") {
std::stringstream log;
SECTION("Construct/Destruct") {
{ BasicJsonDocument<SpyingAllocator> doc(4096, log); }
REQUIRE(log.str() == "A4096F");
}
SECTION("Copy construct") {
{
BasicJsonDocument<SpyingAllocator> doc1(4096, log);
doc1.set(std::string("The size of this string is 32!!"));
BasicJsonDocument<SpyingAllocator> 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(doc2.capacity() == 4096);
}
REQUIRE(log.str() == "A4096A4096FF");
}
SECTION("Move construct") {
{
BasicJsonDocument<SpyingAllocator> doc1(4096, log);
doc1.set(std::string("The size of this string is 32!!"));
BasicJsonDocument<SpyingAllocator> doc2(std::move(doc1));
REQUIRE(doc2.as<std::string>() == "The size of this string is 32!!");
REQUIRE(doc1.as<std::string>() == "null");
REQUIRE(doc1.capacity() == 0);
REQUIRE(doc2.capacity() == 4096);
}
REQUIRE(log.str() == "A4096F");
}
SECTION("Copy assign larger") {
{
BasicJsonDocument<SpyingAllocator> doc1(4096, log);
doc1.set(std::string("The size of this string is 32!!"));
BasicJsonDocument<SpyingAllocator> doc2(8, log);
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(doc2.capacity() == 4096);
}
REQUIRE(log.str() == "A4096A8FA4096FF");
}
SECTION("Copy assign smaller") {
{
BasicJsonDocument<SpyingAllocator> doc1(1024, log);
doc1.set(std::string("The size of this string is 32!!"));
BasicJsonDocument<SpyingAllocator> doc2(4096, log);
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(doc2.capacity() == 1024);
}
REQUIRE(log.str() == "A1024A4096FA1024FF");
}
SECTION("Copy assign same size") {
{
BasicJsonDocument<SpyingAllocator> doc1(1024, log);
doc1.set(std::string("The size of this string is 32!!"));
BasicJsonDocument<SpyingAllocator> doc2(1024, log);
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(doc2.capacity() == 1024);
}
REQUIRE(log.str() == "A1024A1024FF");
}
SECTION("Move assign") {
{
BasicJsonDocument<SpyingAllocator> doc1(4096, log);
doc1.set(std::string("The size of this string is 32!!"));
BasicJsonDocument<SpyingAllocator> doc2(8, log);
doc2 = std::move(doc1);
REQUIRE(doc2.as<std::string>() == "The size of this string is 32!!");
REQUIRE(doc1.as<std::string>() == "null");
REQUIRE(doc1.capacity() == 0);
REQUIRE(doc2.capacity() == 4096);
}
REQUIRE(log.str() == "A4096A8FF");
}
SECTION("garbageCollect()") {
BasicJsonDocument<ControllableAllocator> doc(4096);
SECTION("when allocation succeeds") {
deserializeJson(doc, "{\"blanket\":1,\"dancing\":2}");
REQUIRE(doc.capacity() == 4096);
REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(2) + 16);
doc.remove("blanket");
bool result = doc.garbageCollect();
REQUIRE(result == true);
REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(1) + 8);
REQUIRE(doc.capacity() == 4096);
REQUIRE(doc.as<std::string>() == "{\"dancing\":2}");
}
SECTION("when allocation fails") {
deserializeJson(doc, "{\"blanket\":1,\"dancing\":2}");
REQUIRE(doc.capacity() == 4096);
REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(2) + 16);
doc.remove("blanket");
doc.allocator().disable();
bool result = doc.garbageCollect();
REQUIRE(result == false);
REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(2) + 16);
REQUIRE(doc.capacity() == 4096);
REQUIRE(doc.as<std::string>() == "{\"dancing\":2}");
}
}
}

View File

@@ -1,15 +1,15 @@
# ArduinoJson - https://arduinojson.org
# Copyright © 2014-2023, Benoit BLANCHON
# Copyright © 2014-2024, Benoit BLANCHON
# MIT License
add_executable(JsonDocumentTests
add.cpp
BasicJsonDocument.cpp
assignment.cpp
cast.cpp
clear.cpp
compare.cpp
constructor.cpp
containsKey.cpp
createNested.cpp
DynamicJsonDocument.cpp
ElementProxy.cpp
isNull.cpp
issue1120.cpp
@@ -19,7 +19,6 @@ add_executable(JsonDocumentTests
remove.cpp
shrinkToFit.cpp
size.cpp
StaticJsonDocument.cpp
subscript.cpp
swap.cpp
)

View File

@@ -1,230 +0,0 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2023, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
using ArduinoJson::detail::addPadding;
static void REQUIRE_JSON(JsonDocument& doc, const std::string& expected) {
std::string json;
serializeJson(doc, json);
REQUIRE(json == expected);
}
TEST_CASE("DynamicJsonDocument") {
DynamicJsonDocument doc(4096);
SECTION("serializeJson()") {
JsonObject obj = doc.to<JsonObject>();
obj["hello"] = "world";
std::string json;
serializeJson(doc, json);
REQUIRE(json == "{\"hello\":\"world\"}");
}
SECTION("memoryUsage()") {
SECTION("starts at zero") {
REQUIRE(doc.memoryUsage() == 0);
}
SECTION("JSON_ARRAY_SIZE(0)") {
doc.to<JsonArray>();
REQUIRE(doc.memoryUsage() == JSON_ARRAY_SIZE(0));
}
SECTION("JSON_ARRAY_SIZE(1)") {
doc.to<JsonArray>().add(42);
REQUIRE(doc.memoryUsage() == JSON_ARRAY_SIZE(1));
}
SECTION("JSON_ARRAY_SIZE(1) + JSON_ARRAY_SIZE(0)") {
doc.to<JsonArray>().createNestedArray();
REQUIRE(doc.memoryUsage() == JSON_ARRAY_SIZE(1) + JSON_ARRAY_SIZE(0));
}
}
SECTION("capacity()") {
SECTION("matches constructor argument") {
DynamicJsonDocument doc2(256);
REQUIRE(doc2.capacity() == 256);
}
SECTION("rounds up constructor argument") {
DynamicJsonDocument doc2(253);
REQUIRE(doc2.capacity() == 256);
}
}
SECTION("memoryUsage()") {
SECTION("Increases after adding value to array") {
JsonArray arr = doc.to<JsonArray>();
REQUIRE(doc.memoryUsage() == JSON_ARRAY_SIZE(0));
arr.add(42);
REQUIRE(doc.memoryUsage() == JSON_ARRAY_SIZE(1));
arr.add(43);
REQUIRE(doc.memoryUsage() == JSON_ARRAY_SIZE(2));
}
SECTION("Increases after adding value to object") {
JsonObject obj = doc.to<JsonObject>();
REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(0));
obj["a"] = 1;
REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(1));
obj["b"] = 2;
REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(2));
}
}
}
TEST_CASE("DynamicJsonDocument constructor") {
SECTION("Copy constructor") {
DynamicJsonDocument doc1(1234);
deserializeJson(doc1, "{\"hello\":\"world\"}");
DynamicJsonDocument doc2 = doc1;
REQUIRE_JSON(doc2, "{\"hello\":\"world\"}");
REQUIRE(doc2.capacity() == doc1.capacity());
}
SECTION("Construct from StaticJsonDocument") {
StaticJsonDocument<200> doc1;
deserializeJson(doc1, "{\"hello\":\"world\"}");
DynamicJsonDocument doc2 = doc1;
REQUIRE_JSON(doc2, "{\"hello\":\"world\"}");
REQUIRE(doc2.capacity() == doc1.capacity());
}
SECTION("Construct from JsonObject") {
StaticJsonDocument<200> doc1;
JsonObject obj = doc1.to<JsonObject>();
obj["hello"] = "world";
DynamicJsonDocument doc2 = obj;
REQUIRE_JSON(doc2, "{\"hello\":\"world\"}");
REQUIRE(doc2.capacity() == addPadding(doc1.memoryUsage()));
}
SECTION("Construct from JsonArray") {
StaticJsonDocument<200> doc1;
JsonArray arr = doc1.to<JsonArray>();
arr.add("hello");
DynamicJsonDocument doc2 = arr;
REQUIRE_JSON(doc2, "[\"hello\"]");
REQUIRE(doc2.capacity() == addPadding(doc1.memoryUsage()));
}
SECTION("Construct from JsonVariant") {
StaticJsonDocument<200> doc1;
deserializeJson(doc1, "42");
DynamicJsonDocument doc2 = doc1.as<JsonVariant>();
REQUIRE_JSON(doc2, "42");
REQUIRE(doc2.capacity() == addPadding(doc1.memoryUsage()));
}
}
TEST_CASE("DynamicJsonDocument assignment") {
SECTION("Copy assignment reallocates when capacity is smaller") {
DynamicJsonDocument doc1(1234);
deserializeJson(doc1, "{\"hello\":\"world\"}");
DynamicJsonDocument doc2(8);
doc2 = doc1;
REQUIRE_JSON(doc2, "{\"hello\":\"world\"}");
REQUIRE(doc2.capacity() == doc1.capacity());
}
SECTION("Copy assignment reallocates when capacity is larger") {
DynamicJsonDocument doc1(100);
deserializeJson(doc1, "{\"hello\":\"world\"}");
DynamicJsonDocument doc2(1234);
doc2 = doc1;
REQUIRE_JSON(doc2, "{\"hello\":\"world\"}");
REQUIRE(doc2.capacity() == doc1.capacity());
}
SECTION("Assign from StaticJsonDocument") {
StaticJsonDocument<200> doc1;
deserializeJson(doc1, "{\"hello\":\"world\"}");
DynamicJsonDocument doc2(4096);
doc2.to<JsonVariant>().set(666);
doc2 = doc1;
REQUIRE_JSON(doc2, "{\"hello\":\"world\"}");
}
SECTION("Assign from JsonObject") {
StaticJsonDocument<200> doc1;
JsonObject obj = doc1.to<JsonObject>();
obj["hello"] = "world";
DynamicJsonDocument doc2(4096);
doc2 = obj;
REQUIRE_JSON(doc2, "{\"hello\":\"world\"}");
REQUIRE(doc2.capacity() == 4096);
}
SECTION("Assign from JsonArray") {
StaticJsonDocument<200> doc1;
JsonArray arr = doc1.to<JsonArray>();
arr.add("hello");
DynamicJsonDocument doc2(4096);
doc2 = arr;
REQUIRE_JSON(doc2, "[\"hello\"]");
REQUIRE(doc2.capacity() == 4096);
}
SECTION("Assign from JsonVariant") {
StaticJsonDocument<200> doc1;
deserializeJson(doc1, "42");
DynamicJsonDocument doc2(4096);
doc2 = doc1.as<JsonVariant>();
REQUIRE_JSON(doc2, "42");
REQUIRE(doc2.capacity() == 4096);
}
SECTION("Assign from MemberProxy") {
StaticJsonDocument<200> doc1;
doc1["value"] = 42;
DynamicJsonDocument doc2(4096);
doc2 = doc1["value"];
REQUIRE_JSON(doc2, "42");
REQUIRE(doc2.capacity() == 4096);
}
SECTION("Assign from ElementProxy") {
StaticJsonDocument<200> doc1;
doc1[0] = 42;
DynamicJsonDocument doc2(4096);
doc2 = doc1[0];
REQUIRE_JSON(doc2, "42");
REQUIRE(doc2.capacity() == 4096);
}
}

View File

@@ -1,5 +1,5 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2023, Benoit BLANCHON
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
@@ -8,8 +8,8 @@
typedef ArduinoJson::detail::ElementProxy<JsonDocument&> ElementProxy;
TEST_CASE("ElementProxy::add()") {
DynamicJsonDocument doc(4096);
doc.add();
JsonDocument doc;
doc.add<JsonVariant>();
ElementProxy ep = doc[0];
SECTION("add(int)") {
@@ -34,8 +34,8 @@ TEST_CASE("ElementProxy::add()") {
}
TEST_CASE("ElementProxy::clear()") {
DynamicJsonDocument doc(4096);
doc.add();
JsonDocument doc;
doc.add<JsonVariant>();
ElementProxy ep = doc[0];
SECTION("size goes back to zero") {
@@ -54,7 +54,7 @@ TEST_CASE("ElementProxy::clear()") {
}
TEST_CASE("ElementProxy::operator==()") {
DynamicJsonDocument doc(4096);
JsonDocument doc;
SECTION("1 vs 1") {
doc.add(1);
@@ -94,8 +94,8 @@ TEST_CASE("ElementProxy::operator==()") {
}
TEST_CASE("ElementProxy::remove()") {
DynamicJsonDocument doc(4096);
doc.add();
JsonDocument doc;
doc.add<JsonVariant>();
ElementProxy ep = doc[0];
SECTION("remove(int)") {
@@ -142,7 +142,7 @@ TEST_CASE("ElementProxy::remove()") {
}
TEST_CASE("ElementProxy::set()") {
DynamicJsonDocument doc(4096);
JsonDocument doc;
ElementProxy ep = doc[0];
SECTION("set(int)") {
@@ -167,8 +167,8 @@ TEST_CASE("ElementProxy::set()") {
}
TEST_CASE("ElementProxy::size()") {
DynamicJsonDocument doc(4096);
doc.add();
JsonDocument doc;
doc.add<JsonVariant>();
ElementProxy ep = doc[0];
SECTION("returns 0") {
@@ -188,23 +188,8 @@ TEST_CASE("ElementProxy::size()") {
}
}
TEST_CASE("ElementProxy::memoryUsage()") {
DynamicJsonDocument doc(4096);
doc.add();
ElementProxy ep = doc[0];
SECTION("returns 0 for null") {
REQUIRE(ep.memoryUsage() == 0);
}
SECTION("returns size for string") {
ep.set(std::string("hello"));
REQUIRE(ep.memoryUsage() == 6);
}
}
TEST_CASE("ElementProxy::operator[]") {
DynamicJsonDocument doc(4096);
JsonDocument doc;
ElementProxy ep = doc[1];
SECTION("set member") {
@@ -221,7 +206,7 @@ TEST_CASE("ElementProxy::operator[]") {
}
TEST_CASE("ElementProxy cast to JsonVariantConst") {
DynamicJsonDocument doc(4096);
JsonDocument doc;
doc[0] = "world";
const ElementProxy ep = doc[0];
@@ -232,7 +217,7 @@ TEST_CASE("ElementProxy cast to JsonVariantConst") {
}
TEST_CASE("ElementProxy cast to JsonVariant") {
DynamicJsonDocument doc(4096);
JsonDocument doc;
doc[0] = "world";
ElementProxy ep = doc[0];
@@ -245,11 +230,3 @@ TEST_CASE("ElementProxy cast to JsonVariant") {
CHECK(doc.as<std::string>() == "[\"toto\"]");
}
TEST_CASE("ElementProxy::shallowCopy()") {
StaticJsonDocument<1024> doc1, doc2;
doc2["hello"] = "world";
doc1[0].shallowCopy(doc2);
CHECK(doc1.as<std::string>() == "[{\"hello\":\"world\"}]");
}

View File

@@ -1,15 +1,23 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2023, Benoit BLANCHON
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#define ARDUINOJSON_ENABLE_ARDUINO_STRING 1
#define ARDUINOJSON_ENABLE_PROGMEM 1
#include <ArduinoJson.h>
#include <catch.hpp>
#include "Allocators.hpp"
using ArduinoJson::detail::sizeofArray;
using ArduinoJson::detail::sizeofObject;
typedef ArduinoJson::detail::MemberProxy<JsonDocument&, const char*>
MemberProxy;
TEST_CASE("MemberProxy::add()") {
DynamicJsonDocument doc(4096);
JsonDocument doc;
MemberProxy mp = doc["hello"];
SECTION("add(int)") {
@@ -26,7 +34,7 @@ TEST_CASE("MemberProxy::add()") {
}
TEST_CASE("MemberProxy::clear()") {
DynamicJsonDocument doc(4096);
JsonDocument doc;
MemberProxy mp = doc["hello"];
SECTION("size goes back to zero") {
@@ -45,7 +53,7 @@ TEST_CASE("MemberProxy::clear()") {
}
TEST_CASE("MemberProxy::operator==()") {
DynamicJsonDocument doc(4096);
JsonDocument doc;
SECTION("1 vs 1") {
doc["a"] = 1;
@@ -85,7 +93,7 @@ TEST_CASE("MemberProxy::operator==()") {
}
TEST_CASE("MemberProxy::containsKey()") {
DynamicJsonDocument doc(4096);
JsonDocument doc;
MemberProxy mp = doc["hello"];
SECTION("containsKey(const char*)") {
@@ -104,7 +112,7 @@ TEST_CASE("MemberProxy::containsKey()") {
}
TEST_CASE("MemberProxy::operator|()") {
DynamicJsonDocument doc(4096);
JsonDocument doc;
SECTION("const char*") {
doc["a"] = "hello";
@@ -127,7 +135,7 @@ TEST_CASE("MemberProxy::operator|()") {
JsonObject object = doc.to<JsonObject>();
object["hello"] = "world";
StaticJsonDocument<0> emptyDoc;
JsonDocument emptyDoc;
JsonObject anotherObject = object["hello"] | emptyDoc.to<JsonObject>();
REQUIRE(anotherObject.isNull() == false);
@@ -136,7 +144,7 @@ TEST_CASE("MemberProxy::operator|()") {
}
TEST_CASE("MemberProxy::remove()") {
DynamicJsonDocument doc(4096);
JsonDocument doc;
MemberProxy mp = doc["hello"];
SECTION("remove(int)") {
@@ -183,7 +191,7 @@ TEST_CASE("MemberProxy::remove()") {
}
TEST_CASE("MemberProxy::set()") {
DynamicJsonDocument doc(4096);
JsonDocument doc;
MemberProxy mp = doc["hello"];
SECTION("set(int)") {
@@ -208,7 +216,7 @@ TEST_CASE("MemberProxy::set()") {
}
TEST_CASE("MemberProxy::size()") {
DynamicJsonDocument doc(4096);
JsonDocument doc;
MemberProxy mp = doc["hello"];
SECTION("returns 0") {
@@ -230,22 +238,8 @@ TEST_CASE("MemberProxy::size()") {
}
}
TEST_CASE("MemberProxy::memoryUsage()") {
DynamicJsonDocument doc(4096);
MemberProxy mp = doc["hello"];
SECTION("returns 0 when null") {
REQUIRE(mp.memoryUsage() == 0);
}
SECTION("return the size for a string") {
mp.set(std::string("hello"));
REQUIRE(mp.memoryUsage() == 6);
}
}
TEST_CASE("MemberProxy::operator[]") {
DynamicJsonDocument doc(4096);
JsonDocument doc;
MemberProxy mp = doc["hello"];
SECTION("set member") {
@@ -262,7 +256,7 @@ TEST_CASE("MemberProxy::operator[]") {
}
TEST_CASE("MemberProxy cast to JsonVariantConst") {
DynamicJsonDocument doc(4096);
JsonDocument doc;
doc["hello"] = "world";
const MemberProxy mp = doc["hello"];
@@ -273,7 +267,7 @@ TEST_CASE("MemberProxy cast to JsonVariantConst") {
}
TEST_CASE("MemberProxy cast to JsonVariant") {
DynamicJsonDocument doc(4096);
JsonDocument doc;
doc["hello"] = "world";
MemberProxy mp = doc["hello"];
@@ -287,42 +281,83 @@ TEST_CASE("MemberProxy cast to JsonVariant") {
CHECK(doc.as<std::string>() == "{\"hello\":\"toto\"}");
}
TEST_CASE("MemberProxy::createNestedArray()") {
StaticJsonDocument<1024> doc;
JsonArray arr = doc["items"].createNestedArray();
arr.add(42);
TEST_CASE("Deduplicate keys") {
SpyingAllocator spy;
JsonDocument doc(&spy);
CHECK(doc["items"][0][0] == 42);
SECTION("std::string") {
doc[0][std::string("example")] = 1;
doc[1][std::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("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::createNestedArray(key)") {
StaticJsonDocument<1024> doc;
JsonArray arr = doc["weather"].createNestedArray("temp");
arr.add(42);
TEST_CASE("MemberProxy under memory constraints") {
KillswitchAllocator killswitch;
SpyingAllocator spy(&killswitch);
JsonDocument doc(&spy);
CHECK(doc["weather"]["temp"][0] == 42);
}
TEST_CASE("MemberProxy::createNestedObject()") {
StaticJsonDocument<1024> doc;
JsonObject obj = doc["items"].createNestedObject();
obj["value"] = 42;
CHECK(doc["items"][0]["value"] == 42);
}
TEST_CASE("MemberProxy::createNestedObject(key)") {
StaticJsonDocument<1024> doc;
JsonObject obj = doc["status"].createNestedObject("weather");
obj["temp"] = 42;
CHECK(doc["status"]["weather"]["temp"] == 42);
}
TEST_CASE("MemberProxy::shallowCopy()") {
StaticJsonDocument<1024> doc1, doc2;
doc2["hello"] = "world";
doc1["obj"].shallowCopy(doc2);
CHECK(doc1.as<std::string>() == "{\"obj\":{\"hello\":\"world\"}}");
SECTION("key allocation fails") {
killswitch.on();
doc[std::string("hello")] = "world";
REQUIRE(doc.is<JsonObject>());
REQUIRE(doc.size() == 0);
REQUIRE(doc.overflowed() == true);
REQUIRE(spy.log() == AllocatorLog{
AllocateFail(sizeofString("hello")),
});
}
}

View File

@@ -1,224 +0,0 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2023, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
static void REQUIRE_JSON(JsonDocument& doc, const std::string& expected) {
std::string json;
serializeJson(doc, json);
REQUIRE(json == expected);
}
TEST_CASE("StaticJsonDocument") {
SECTION("capacity()") {
SECTION("matches template argument") {
StaticJsonDocument<256> doc;
REQUIRE(doc.capacity() == 256);
}
SECTION("rounds up template argument") {
StaticJsonDocument<253> doc;
REQUIRE(doc.capacity() == 256);
}
}
SECTION("serializeJson()") {
StaticJsonDocument<200> doc;
JsonObject obj = doc.to<JsonObject>();
obj["hello"] = "world";
std::string json;
serializeJson(doc, json);
REQUIRE(json == "{\"hello\":\"world\"}");
}
SECTION("Copy assignment") {
StaticJsonDocument<200> doc1, doc2;
doc1.to<JsonVariant>().set(666);
deserializeJson(doc2, "{\"hello\":\"world\"}");
doc1 = doc2;
REQUIRE_JSON(doc2, "{\"hello\":\"world\"}");
}
SECTION("Contructor") {
SECTION("Copy constructor") {
StaticJsonDocument<200> doc1;
deserializeJson(doc1, "{\"hello\":\"world\"}");
StaticJsonDocument<200> doc2 = doc1;
deserializeJson(doc1, "{\"HELLO\":\"WORLD\"}");
REQUIRE_JSON(doc2, "{\"hello\":\"world\"}");
}
SECTION("Construct from StaticJsonDocument of different size") {
StaticJsonDocument<300> doc1;
deserializeJson(doc1, "{\"hello\":\"world\"}");
StaticJsonDocument<200> doc2 = doc1;
REQUIRE_JSON(doc2, "{\"hello\":\"world\"}");
}
SECTION("Construct from DynamicJsonDocument") {
DynamicJsonDocument doc1(4096);
deserializeJson(doc1, "{\"hello\":\"world\"}");
StaticJsonDocument<200> doc2 = doc1;
REQUIRE_JSON(doc2, "{\"hello\":\"world\"}");
}
SECTION("Construct from JsonObject") {
DynamicJsonDocument doc1(4096);
deserializeJson(doc1, "{\"hello\":\"world\"}");
StaticJsonDocument<200> doc2 = doc1.as<JsonObject>();
deserializeJson(doc1, "{\"HELLO\":\"WORLD\"}");
REQUIRE_JSON(doc2, "{\"hello\":\"world\"}");
}
SECTION("Construct from JsonArray") {
DynamicJsonDocument doc1(4096);
deserializeJson(doc1, "[\"hello\",\"world\"]");
StaticJsonDocument<200> doc2 = doc1.as<JsonArray>();
deserializeJson(doc1, "[\"HELLO\",\"WORLD\"]");
REQUIRE_JSON(doc2, "[\"hello\",\"world\"]");
}
SECTION("Construct from JsonVariant") {
DynamicJsonDocument doc1(4096);
deserializeJson(doc1, "42");
StaticJsonDocument<200> doc2 = doc1.as<JsonVariant>();
REQUIRE_JSON(doc2, "42");
}
}
SECTION("Assignment") {
SECTION("Copy assignment") {
StaticJsonDocument<200> doc1, doc2;
doc1.to<JsonVariant>().set(666);
deserializeJson(doc1, "{\"hello\":\"world\"}");
doc2 = doc1;
deserializeJson(doc1, "{\"HELLO\":\"WORLD\"}");
REQUIRE_JSON(doc2, "{\"hello\":\"world\"}");
}
SECTION("Assign from StaticJsonDocument of different capacity") {
StaticJsonDocument<200> doc1;
StaticJsonDocument<300> doc2;
doc1.to<JsonVariant>().set(666);
deserializeJson(doc1, "{\"hello\":\"world\"}");
doc2 = doc1;
REQUIRE_JSON(doc2, "{\"hello\":\"world\"}");
}
SECTION("Assign from DynamicJsonDocument") {
StaticJsonDocument<200> doc1;
DynamicJsonDocument doc2(4096);
doc1.to<JsonVariant>().set(666);
deserializeJson(doc1, "{\"hello\":\"world\"}");
doc2 = doc1;
deserializeJson(doc1, "{\"HELLO\":\"WORLD\"}");
REQUIRE_JSON(doc2, "{\"hello\":\"world\"}");
}
SECTION("Assign from JsonArray") {
StaticJsonDocument<200> doc1;
DynamicJsonDocument doc2(4096);
doc1.to<JsonVariant>().set(666);
deserializeJson(doc1, "[\"hello\",\"world\"]");
doc2 = doc1.as<JsonArray>();
deserializeJson(doc1, "[\"HELLO\",\"WORLD\"]");
REQUIRE_JSON(doc2, "[\"hello\",\"world\"]");
}
SECTION("Assign from JsonArrayConst") {
StaticJsonDocument<200> doc1;
DynamicJsonDocument doc2(4096);
doc1.to<JsonVariant>().set(666);
deserializeJson(doc1, "[\"hello\",\"world\"]");
doc2 = doc1.as<JsonArrayConst>();
deserializeJson(doc1, "[\"HELLO\",\"WORLD\"]");
REQUIRE_JSON(doc2, "[\"hello\",\"world\"]");
}
SECTION("Assign from JsonObject") {
StaticJsonDocument<200> doc1;
DynamicJsonDocument doc2(4096);
doc1.to<JsonVariant>().set(666);
deserializeJson(doc1, "{\"hello\":\"world\"}");
doc2 = doc1.as<JsonObject>();
deserializeJson(doc1, "{\"HELLO\":\"WORLD\"}");
REQUIRE_JSON(doc2, "{\"hello\":\"world\"}");
}
SECTION("Assign from JsonObjectConst") {
StaticJsonDocument<200> doc1;
DynamicJsonDocument doc2(4096);
doc1.to<JsonVariant>().set(666);
deserializeJson(doc1, "{\"hello\":\"world\"}");
doc2 = doc1.as<JsonObjectConst>();
deserializeJson(doc1, "{\"HELLO\":\"WORLD\"}");
REQUIRE_JSON(doc2, "{\"hello\":\"world\"}");
}
SECTION("Assign from JsonVariant") {
DynamicJsonDocument doc1(4096);
doc1.to<JsonVariant>().set(666);
deserializeJson(doc1, "42");
StaticJsonDocument<200> doc2;
doc2 = doc1.as<JsonVariant>();
REQUIRE_JSON(doc2, "42");
}
SECTION("Assign from JsonVariantConst") {
DynamicJsonDocument doc1(4096);
doc1.to<JsonVariant>().set(666);
deserializeJson(doc1, "42");
StaticJsonDocument<200> doc2;
doc2 = doc1.as<JsonVariantConst>();
REQUIRE_JSON(doc2, "42");
}
}
SECTION("garbageCollect()") {
StaticJsonDocument<256> doc;
doc[std::string("example")] = std::string("jukebox");
doc.remove("example");
REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(1) + 16);
doc.garbageCollect();
REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(0));
REQUIRE_JSON(doc, "{}");
}
}

View File

@@ -1,22 +1,104 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2023, Benoit BLANCHON
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#define ARDUINOJSON_ENABLE_ARDUINO_STRING 1
#define ARDUINOJSON_ENABLE_PROGMEM 1
#include <ArduinoJson.h>
#include <catch.hpp>
TEST_CASE("JsonDocument::add()") {
DynamicJsonDocument doc(4096);
#include "Allocators.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("const char*") {
doc.add("hello");
REQUIRE(doc.as<std::string>() == "[\"hello\"]");
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
});
}
SECTION("std::string") {
doc.add(std::string("example"));
doc.add(std::string("example"));
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")),
});
}
}
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("JsonObject") {
JsonObject object = doc.add<JsonObject>();
object["hello"] = "world";
REQUIRE(doc.as<std::string>() == "[{\"hello\":\"world\"}]");
}
SECTION("JsonVariant") {
JsonVariant variant = doc.add<JsonVariant>();
variant.set(42);
REQUIRE(doc.as<std::string>() == "[42]");
}
}

View File

@@ -0,0 +1,134 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
#include "Allocators.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(sizeofString("hello")),
Allocate(sizeofPool()),
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(sizeofString("hello")),
Allocate(sizeofPool()),
Allocate(sizeofString("world")),
});
}
SECTION("Move assign") {
{
JsonDocument doc1(&spyingAllocator);
doc1[std::string("hello")] = std::string("world");
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(sizeofString("hello")),
Allocate(sizeofPool()),
Allocate(sizeofString("world")),
Deallocate(sizeofString("hello")),
Deallocate(sizeofString("world")),
Deallocate(sizeofPool()),
});
}
SECTION("Assign from JsonObject") {
JsonDocument doc1;
JsonObject obj = doc1.to<JsonObject>();
obj["hello"] = "world";
JsonDocument doc2;
doc2 = obj;
REQUIRE(doc2.as<std::string>() == "{\"hello\":\"world\"}");
}
SECTION("Assign from JsonArray") {
JsonDocument doc1;
JsonArray arr = doc1.to<JsonArray>();
arr.add("hello");
JsonDocument doc2;
doc2 = arr;
REQUIRE(doc2.as<std::string>() == "[\"hello\"]");
}
SECTION("Assign from JsonVariant") {
JsonDocument doc1;
deserializeJson(doc1, "42");
JsonDocument doc2;
doc2 = doc1.as<JsonVariant>();
REQUIRE(doc2.as<std::string>() == "42");
}
SECTION("Assign from MemberProxy") {
JsonDocument doc1;
doc1["value"] = 42;
JsonDocument doc2;
doc2 = doc1["value"];
REQUIRE(doc2.as<std::string>() == "42");
}
SECTION("Assign from ElementProxy") {
JsonDocument doc1;
doc1[0] = 42;
JsonDocument doc2;
doc2 = doc1[0];
REQUIRE(doc2.as<std::string>() == "42");
}
}

View File

@@ -1,5 +1,5 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2023, Benoit BLANCHON
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
@@ -8,7 +8,7 @@
#include <string>
TEST_CASE("Implicit cast to JsonVariant") {
StaticJsonDocument<128> doc;
JsonDocument doc;
doc["hello"] = "world";

View File

@@ -0,0 +1,47 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
#include <stdlib.h> // malloc, free
#include <string>
#include "Allocators.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[std::string("hello")] = std::string("world");
spy.clearLog();
doc.clear();
REQUIRE(doc.isNull());
REQUIRE(spy.log() == AllocatorLog{
Deallocate(sizeofPool()),
Deallocate(sizeofString("hello")),
Deallocate(sizeofString("world")),
});
}
SECTION("clear free list") { // issue #2034
JsonObject obj = doc.to<JsonObject>();
obj["a"] = 1;
obj.clear(); // puts the slot in the free list
doc.clear();
doc["b"] = 2; // will it pick from the free list?
}
}

View File

@@ -1,103 +1,29 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2023, Benoit BLANCHON
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
TEST_CASE("DynamicJsonDocument::operator==(const DynamicJsonDocument&)") {
DynamicJsonDocument doc1(4096);
DynamicJsonDocument doc2(4096);
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);
}
}
TEST_CASE("DynamicJsonDocument::operator==(const StaticJsonDocument&)") {
DynamicJsonDocument doc1(4096);
StaticJsonDocument<256> 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);
}
}
TEST_CASE("StaticJsonDocument::operator==(const DynamicJsonDocument&)") {
StaticJsonDocument<256> doc1;
DynamicJsonDocument doc2(4096);
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);
}
}
TEST_CASE("JsonDocument::operator==(const JsonDocument&)") {
StaticJsonDocument<256> doc1;
StaticJsonDocument<256> doc2;
const JsonDocument& ref1 = doc1;
const JsonDocument& ref2 = doc2;
JsonDocument doc1;
JsonDocument doc2;
SECTION("Empty") {
REQUIRE(ref1 == ref2);
REQUIRE_FALSE(ref1 != ref2);
REQUIRE(doc1 == doc2);
REQUIRE_FALSE(doc1 != doc2);
}
SECTION("With same object") {
doc1["hello"] = "world";
doc2["hello"] = "world";
REQUIRE(ref1 == ref2);
REQUIRE_FALSE(ref1 != ref2);
REQUIRE(doc1 == doc2);
REQUIRE_FALSE(doc1 != doc2);
}
SECTION("With different object") {
doc1["hello"] = "world";
doc2["world"] = "hello";
REQUIRE_FALSE(ref1 == ref2);
REQUIRE(ref1 != ref2);
REQUIRE_FALSE(doc1 == doc2);
REQUIRE(doc1 != doc2);
}
}

View File

@@ -0,0 +1,120 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
#include "Allocators.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(std::string("The size of this string is 32!!"));
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(std::string("The size of this string is 32!!"));
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");
}
}

View File

@@ -1,12 +1,12 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2023, Benoit BLANCHON
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
TEST_CASE("JsonDocument::containsKey()") {
DynamicJsonDocument doc(4096);
JsonDocument doc;
SECTION("returns true on object") {
doc["hello"] = "world";

View File

@@ -1,66 +0,0 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2023, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
TEST_CASE("JsonDocument::createNestedArray()") {
DynamicJsonDocument doc(4096);
SECTION("promotes to array") {
doc.createNestedArray();
REQUIRE(doc.is<JsonArray>());
}
}
TEST_CASE("JsonDocument::createNestedArray(key)") {
DynamicJsonDocument doc(4096);
SECTION("key is const char*") {
SECTION("promotes to object") {
doc.createNestedArray("hello");
REQUIRE(doc.is<JsonObject>());
}
}
SECTION("key is std::string") {
SECTION("promotes to object") {
doc.createNestedArray(std::string("hello"));
REQUIRE(doc.is<JsonObject>());
}
}
}
TEST_CASE("JsonDocument::createNestedObject()") {
DynamicJsonDocument doc(4096);
SECTION("promotes to array") {
doc.createNestedObject();
REQUIRE(doc.is<JsonArray>());
}
}
TEST_CASE("JsonDocument::createNestedObject(key)") {
DynamicJsonDocument doc(4096);
SECTION("key is const char*") {
SECTION("promotes to object") {
doc.createNestedObject("hello");
REQUIRE(doc.is<JsonObject>());
}
}
SECTION("key is std::string") {
SECTION("promotes to object") {
doc.createNestedObject(std::string("hello"));
REQUIRE(doc.is<JsonObject>());
}
}
}

View File

@@ -1,12 +1,12 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2023, Benoit BLANCHON
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
TEST_CASE("JsonDocument::isNull()") {
DynamicJsonDocument doc(4096);
JsonDocument doc;
SECTION("returns true if uninitialized") {
REQUIRE(doc.isNull() == true);

View File

@@ -3,7 +3,7 @@
#include <catch.hpp>
TEST_CASE("Issue #1120") {
StaticJsonDocument<500> doc;
JsonDocument doc;
constexpr char str[] =
"{\"contents\":[{\"module\":\"Packet\"},{\"module\":\"Analog\"}]}";
deserializeJson(doc, str);

View File

@@ -1,12 +1,12 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2023, Benoit BLANCHON
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
TEST_CASE("JsonDocument::nesting()") {
DynamicJsonDocument doc(4096);
JsonDocument doc;
SECTION("return 0 if uninitialized") {
REQUIRE(doc.nesting() == 0);

View File

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

View File

@@ -1,12 +1,12 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2023, Benoit BLANCHON
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
TEST_CASE("JsonDocument::remove()") {
DynamicJsonDocument doc(4096);
JsonDocument doc;
SECTION("remove(int)") {
doc.add(1);

View File

@@ -1,5 +1,5 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2023, Benoit BLANCHON
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
@@ -8,145 +8,176 @@
#include <stdlib.h> // malloc, free
#include <string>
class ArmoredAllocator {
#include "Allocators.hpp"
using ArduinoJson::detail::sizeofArray;
using ArduinoJson::detail::sizeofObject;
class ArmoredAllocator : public Allocator {
public:
ArmoredAllocator() : _ptr(0), _size(0) {}
virtual ~ArmoredAllocator() {}
void* allocate(size_t size) {
_ptr = malloc(size);
_size = size;
return _ptr;
void* allocate(size_t size) override {
return malloc(size);
}
void deallocate(void* ptr) {
REQUIRE(ptr == _ptr);
void deallocate(void* ptr) override {
free(ptr);
_ptr = 0;
_size = 0;
}
void* reallocate(void* ptr, size_t new_size) {
REQUIRE(ptr == _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);
memcpy(new_ptr, _ptr, std::min(new_size, _size));
memset(_ptr, '#', _size); // erase
free(_ptr);
_ptr = new_ptr;
memset(new_ptr, '#', new_size); // erase
if (ptr) {
memcpy(new_ptr, ptr, std::min(new_size, new_size));
free(ptr);
}
return new_ptr;
}
private:
void* _ptr;
size_t _size;
};
typedef BasicJsonDocument<ArmoredAllocator> ShrinkToFitTestDocument;
void testShrinkToFit(ShrinkToFitTestDocument& doc, std::string expected_json,
size_t expected_size) {
// test twice: shrinkToFit() should be idempotent
for (int i = 0; i < 2; i++) {
doc.shrinkToFit();
REQUIRE(doc.capacity() == expected_size);
REQUIRE(doc.memoryUsage() == expected_size);
std::string json;
serializeJson(doc, json);
REQUIRE(json == expected_json);
}
}
TEST_CASE("BasicJsonDocument::shrinkToFit()") {
ShrinkToFitTestDocument doc(4096);
TEST_CASE("JsonDocument::shrinkToFit()") {
ArmoredAllocator armoredAllocator;
SpyingAllocator spyingAllocator(&armoredAllocator);
JsonDocument doc(&spyingAllocator);
SECTION("null") {
testShrinkToFit(doc, "null", 0);
doc.shrinkToFit();
REQUIRE(doc.as<std::string>() == "null");
REQUIRE(spyingAllocator.log() == AllocatorLog{});
}
SECTION("empty object") {
deserializeJson(doc, "{}");
testShrinkToFit(doc, "{}", JSON_OBJECT_SIZE(0));
doc.shrinkToFit();
REQUIRE(doc.as<std::string>() == "{}");
REQUIRE(spyingAllocator.log() == AllocatorLog{});
}
SECTION("empty array") {
deserializeJson(doc, "[]");
testShrinkToFit(doc, "[]", JSON_ARRAY_SIZE(0));
doc.shrinkToFit();
REQUIRE(doc.as<std::string>() == "[]");
REQUIRE(spyingAllocator.log() == AllocatorLog{});
}
SECTION("linked string") {
doc.set("hello");
testShrinkToFit(doc, "\"hello\"", 0);
doc.shrinkToFit();
REQUIRE(doc.as<std::string>() == "hello");
REQUIRE(spyingAllocator.log() == AllocatorLog{});
}
SECTION("owned string") {
doc.set(std::string("abcdefg"));
testShrinkToFit(doc, "\"abcdefg\"", 8);
REQUIRE(doc.as<std::string>() == "abcdefg");
doc.shrinkToFit();
REQUIRE(doc.as<std::string>() == "abcdefg");
REQUIRE(spyingAllocator.log() == AllocatorLog{
Allocate(sizeofString("abcdefg")),
});
}
SECTION("linked raw") {
doc.set(serialized("[{},123]"));
testShrinkToFit(doc, "[{},123]", 0);
}
SECTION("raw string") {
doc.set(serialized("[{},12]"));
SECTION("owned raw") {
doc.set(serialized(std::string("[{},12]")));
testShrinkToFit(doc, "[{},12]", 8);
doc.shrinkToFit();
REQUIRE(doc.as<std::string>() == "[{},12]");
REQUIRE(spyingAllocator.log() == AllocatorLog{
Allocate(sizeofString("[{},12]")),
});
}
SECTION("linked key") {
doc["key"] = 42;
testShrinkToFit(doc, "{\"key\":42}", JSON_OBJECT_SIZE(1));
doc.shrinkToFit();
REQUIRE(doc.as<std::string>() == "{\"key\":42}");
REQUIRE(spyingAllocator.log() ==
AllocatorLog{
Allocate(sizeofPool()),
Reallocate(sizeofPool(), sizeofObject(1)),
});
}
SECTION("owned key") {
doc[std::string("abcdefg")] = 42;
testShrinkToFit(doc, "{\"abcdefg\":42}", JSON_OBJECT_SIZE(1) + 8);
doc.shrinkToFit();
REQUIRE(doc.as<std::string>() == "{\"abcdefg\":42}");
REQUIRE(spyingAllocator.log() ==
AllocatorLog{
Allocate(sizeofString("abcdefg")),
Allocate(sizeofPool()),
Reallocate(sizeofPool(), sizeofObject(1)),
});
}
SECTION("linked string in array") {
doc.add("hello");
testShrinkToFit(doc, "[\"hello\"]", JSON_ARRAY_SIZE(1));
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(std::string("abcdefg"));
testShrinkToFit(doc, "[\"abcdefg\"]", JSON_ARRAY_SIZE(1) + 8);
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";
testShrinkToFit(doc, "{\"key\":\"hello\"}", JSON_OBJECT_SIZE(1));
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"] = std::string("abcdefg");
testShrinkToFit(doc, "{\"key\":\"abcdefg\"}", JSON_ARRAY_SIZE(1) + 8);
}
SECTION("unaligned") {
doc.add(std::string("?")); // two bytes in the string pool
REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(1) + 2);
doc.shrinkToFit();
// the new capacity should be padded to align the pointers
REQUIRE(doc.capacity() == JSON_OBJECT_SIZE(1) + sizeof(void*));
REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(1) + 2);
REQUIRE(doc[0] == "?");
REQUIRE(doc.as<std::string>() == "{\"key\":\"abcdefg\"}");
REQUIRE(spyingAllocator.log() ==
AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("abcdefg")),
Reallocate(sizeofPool(), sizeofPool(1)),
});
}
}
TEST_CASE("DynamicJsonDocument::shrinkToFit()") {
DynamicJsonDocument doc(4096);
deserializeJson(doc, "{\"hello\":[\"world\"]");
doc.shrinkToFit();
std::string json;
serializeJson(doc, json);
REQUIRE(json == "{\"hello\":[\"world\"]}");
}

View File

@@ -1,12 +1,12 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2023, Benoit BLANCHON
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
TEST_CASE("JsonDocument::size()") {
DynamicJsonDocument doc(4096);
JsonDocument doc;
SECTION("returns 0") {
REQUIRE(doc.size() == 0);

View File

@@ -1,12 +1,12 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2023, Benoit BLANCHON
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
TEST_CASE("JsonDocument::operator[]") {
DynamicJsonDocument doc(4096);
JsonDocument doc;
const JsonDocument& cdoc = doc;
SECTION("object") {
@@ -37,7 +37,7 @@ TEST_CASE("JsonDocument::operator[]") {
}
TEST_CASE("JsonDocument automatically promotes to object") {
DynamicJsonDocument doc(4096);
JsonDocument doc;
doc["one"]["two"]["three"] = 4;
@@ -45,7 +45,7 @@ TEST_CASE("JsonDocument automatically promotes to object") {
}
TEST_CASE("JsonDocument automatically promotes to array") {
DynamicJsonDocument doc(4096);
JsonDocument doc;
doc[2] = 2;

View File

@@ -7,21 +7,19 @@
using namespace std;
TEST_CASE("std::swap") {
SECTION("DynamicJsonDocument*") {
DynamicJsonDocument *p1, *p2;
SECTION("JsonDocument*") {
JsonDocument *p1, *p2;
swap(p1, p2); // issue #1678
}
SECTION("DynamicJsonDocument") {
DynamicJsonDocument doc1(0x10), doc2(0x20);
SECTION("JsonDocument") {
JsonDocument doc1, doc2;
doc1.set("hello");
doc2.set("world");
swap(doc1, doc2);
CHECK(doc1.capacity() == 0x20);
CHECK(doc1.as<string>() == "world");
CHECK(doc2.capacity() == 0x10);
CHECK(doc2.as<string>() == "hello");
}
}

View File

@@ -1,5 +1,5 @@
# ArduinoJson - https://arduinojson.org
# Copyright © 2014-2023, Benoit BLANCHON
# Copyright © 2014-2024, Benoit BLANCHON
# MIT License
add_executable(JsonObjectTests
@@ -7,18 +7,15 @@ add_executable(JsonObjectTests
compare.cpp
containsKey.cpp
copy.cpp
createNestedArray.cpp
createNestedObject.cpp
equals.cpp
invalid.cpp
isNull.cpp
iterator.cpp
memoryUsage.cpp
nesting.cpp
remove.cpp
size.cpp
std_string.cpp
subscript.cpp
unbound.cpp
)
add_test(JsonObject JsonObjectTests)

View File

@@ -1,5 +1,5 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2023, Benoit BLANCHON
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
@@ -14,7 +14,7 @@ TEST_CASE("JsonObject::clear()") {
}
SECTION("Removes all elements") {
StaticJsonDocument<64> doc;
JsonDocument doc;
JsonObject obj = doc.to<JsonObject>();
obj["hello"] = 1;
obj["world"] = 2;

View File

@@ -1,12 +1,12 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2023, Benoit BLANCHON
// Copyright © 2014-2024, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
TEST_CASE("Compare JsonObject with JsonObject") {
StaticJsonDocument<512> doc;
JsonDocument doc;
SECTION("Compare with unbound") {
JsonObject object = doc.to<JsonObject>();
@@ -43,12 +43,12 @@ TEST_CASE("Compare JsonObject with JsonObject") {
}
SECTION("Compare with identical object") {
JsonObject object1 = doc.createNestedObject();
JsonObject object1 = doc.add<JsonObject>();
object1["a"] = 1;
object1["b"] = "hello";
object1["c"][0] = false;
JsonObject object2 = doc.createNestedObject();
JsonObject object2 = doc.add<JsonObject>();
object2["a"] = 1;
object2["b"] = "hello";
object2["c"][0] = false;
@@ -62,12 +62,12 @@ TEST_CASE("Compare JsonObject with JsonObject") {
}
SECTION("Compare with different object") {
JsonObject object1 = doc.createNestedObject();
JsonObject object1 = doc.add<JsonObject>();
object1["a"] = 1;
object1["b"] = "hello1";
object1["c"][0] = false;
JsonObject object2 = doc.createNestedObject();
JsonObject object2 = doc.add<JsonObject>();
object2["a"] = 1;
object2["b"] = "hello2";
object2["c"][0] = false;
@@ -82,7 +82,7 @@ TEST_CASE("Compare JsonObject with JsonObject") {
}
TEST_CASE("Compare JsonObject with JsonVariant") {
StaticJsonDocument<512> doc;
JsonDocument doc;
SECTION("Compare with self") {
JsonObject object = doc.to<JsonObject>();
@@ -107,12 +107,12 @@ TEST_CASE("Compare JsonObject with JsonVariant") {
}
SECTION("Compare with identical object") {
JsonObject object = doc.createNestedObject();
JsonObject object = doc.add<JsonObject>();
object["a"] = 1;
object["b"] = "hello";
object["c"][0] = false;
JsonVariant variant = doc.createNestedObject();
JsonVariant variant = doc.add<JsonObject>();
variant["a"] = 1;
variant["b"] = "hello";
variant["c"][0] = false;
@@ -133,12 +133,12 @@ TEST_CASE("Compare JsonObject with JsonVariant") {
}
SECTION("Compare with different object") {
JsonObject object = doc.createNestedObject();
JsonObject object = doc.add<JsonObject>();
object["a"] = 1;
object["b"] = "hello1";
object["c"][0] = false;
JsonVariant variant = doc.createNestedObject();
JsonVariant variant = doc.add<JsonObject>();
variant["a"] = 1;
variant["b"] = "hello2";
variant["c"][0] = false;
@@ -153,7 +153,7 @@ TEST_CASE("Compare JsonObject with JsonVariant") {
}
TEST_CASE("Compare JsonObject with JsonVariantConst") {
StaticJsonDocument<512> doc;
JsonDocument doc;
SECTION("Compare with unbound") {
JsonObject object = doc.to<JsonObject>();
@@ -199,12 +199,12 @@ TEST_CASE("Compare JsonObject with JsonVariantConst") {
}
SECTION("Compare with identical object") {
JsonObject object = doc.createNestedObject();
JsonObject object = doc.add<JsonObject>();
object["a"] = 1;
object["b"] = "hello";
object["c"][0] = false;
JsonObject object2 = doc.createNestedObject();
JsonObject object2 = doc.add<JsonObject>();
object2["a"] = 1;
object2["b"] = "hello";
object2["c"][0] = false;
@@ -226,12 +226,12 @@ TEST_CASE("Compare JsonObject with JsonVariantConst") {
}
SECTION("Compare with different object") {
JsonObject object = doc.createNestedObject();
JsonObject object = doc.add<JsonObject>();
object["a"] = 1;
object["b"] = "hello1";
object["c"][0] = false;
JsonObject object2 = doc.createNestedObject();
JsonObject object2 = doc.add<JsonObject>();
object2["a"] = 1;
object2["b"] = "hello2";
object2["c"][0] = false;
@@ -247,7 +247,7 @@ TEST_CASE("Compare JsonObject with JsonVariantConst") {
}
TEST_CASE("Compare JsonObject with JsonObjectConst") {
StaticJsonDocument<512> doc;
JsonDocument doc;
SECTION("Compare with unbound") {
JsonObject object = doc.to<JsonObject>();
@@ -292,12 +292,12 @@ TEST_CASE("Compare JsonObject with JsonObjectConst") {
}
SECTION("Compare with identical object") {
JsonObject object1 = doc.createNestedObject();
JsonObject object1 = doc.add<JsonObject>();
object1["a"] = 1;
object1["b"] = "hello";
object1["c"][0] = false;
JsonObject object2 = doc.createNestedObject();
JsonObject object2 = doc.add<JsonObject>();
object2["a"] = 1;
object2["b"] = "hello";
object2["c"][0] = false;
@@ -319,12 +319,12 @@ TEST_CASE("Compare JsonObject with JsonObjectConst") {
}
SECTION("Compare with different object") {
JsonObject object1 = doc.createNestedObject();
JsonObject object1 = doc.add<JsonObject>();
object1["a"] = 1;
object1["b"] = "hello1";
object1["c"][0] = false;
JsonObject object2 = doc.createNestedObject();
JsonObject object2 = doc.add<JsonObject>();
object2["a"] = 1;
object2["b"] = "hello2";
object2["c"][0] = false;
@@ -347,7 +347,7 @@ TEST_CASE("Compare JsonObject with JsonObjectConst") {
}
TEST_CASE("Compare JsonObjectConst with JsonObjectConst") {
StaticJsonDocument<512> doc;
JsonDocument doc;
SECTION("Compare with unbound") {
JsonObject object = doc.to<JsonObject>();
@@ -387,13 +387,13 @@ TEST_CASE("Compare JsonObjectConst with JsonObjectConst") {
}
SECTION("Compare with identical object") {
JsonObject object1 = doc.createNestedObject();
JsonObject object1 = doc.add<JsonObject>();
object1["a"] = 1;
object1["b"] = "hello";
object1["c"][0] = false;
JsonObjectConst carray1 = object1;
JsonObject object2 = doc.createNestedObject();
JsonObject object2 = doc.add<JsonObject>();
object2["a"] = 1;
object2["b"] = "hello";
object2["c"][0] = false;
@@ -408,13 +408,13 @@ TEST_CASE("Compare JsonObjectConst with JsonObjectConst") {
}
SECTION("Compare with different object") {
JsonObject object1 = doc.createNestedObject();
JsonObject object1 = doc.add<JsonObject>();
object1["a"] = 1;
object1["b"] = "hello1";
object1["c"][0] = false;
JsonObjectConst carray1 = object1;
JsonObject object2 = doc.createNestedObject();
JsonObject object2 = doc.add<JsonObject>();
object2["a"] = 1;
object2["b"] = "hello2";
object2["c"][0] = false;
@@ -430,7 +430,7 @@ TEST_CASE("Compare JsonObjectConst with JsonObjectConst") {
}
TEST_CASE("Compare JsonObjectConst with JsonVariant") {
StaticJsonDocument<512> doc;
JsonDocument doc;
SECTION("Compare with self") {
JsonObject object = doc.to<JsonObject>();
@@ -455,13 +455,13 @@ TEST_CASE("Compare JsonObjectConst with JsonVariant") {
}
SECTION("Compare with identical object") {
JsonObject object1 = doc.createNestedObject();
JsonObject object1 = doc.add<JsonObject>();
object1["a"] = 1;
object1["b"] = "hello";
object1["c"][0] = false;
JsonObjectConst carray1 = object1;
JsonObject object2 = doc.createNestedObject();
JsonObject object2 = doc.add<JsonObject>();
object2["a"] = 1;
object2["b"] = "hello";
object2["c"][0] = false;
@@ -483,13 +483,13 @@ TEST_CASE("Compare JsonObjectConst with JsonVariant") {
}
SECTION("Compare with different object") {
JsonObject object1 = doc.createNestedObject();
JsonObject object1 = doc.add<JsonObject>();
object1["a"] = 1;
object1["b"] = "hello1";
object1["c"][0] = false;
JsonObjectConst carray1 = object1;
JsonObject object2 = doc.createNestedObject();
JsonObject object2 = doc.add<JsonObject>();
object2["a"] = 1;
object2["b"] = "hello2";
object2["c"][0] = false;

Some files were not shown because too many files have changed in this diff Show More