Initial commit of Arduino libraries

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

View File

@@ -0,0 +1,26 @@
# ArduinoJson - https://arduinojson.org
# Copyright © 2014-2025, Benoit BLANCHON
# MIT License
add_executable(JsonDeserializerTests
array.cpp
DeserializationError.cpp
destination_types.cpp
errors.cpp
filter.cpp
input_types.cpp
misc.cpp
nestingLimit.cpp
number.cpp
object.cpp
string.cpp
)
set_target_properties(JsonDeserializerTests PROPERTIES UNITY_BUILD OFF)
add_test(JsonDeserializer JsonDeserializerTests)
set_tests_properties(JsonDeserializer
PROPERTIES
LABELS "Catch"
)

View File

@@ -0,0 +1,122 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
#include <sstream>
void testStringification(DeserializationError error, std::string expected) {
REQUIRE(error.c_str() == expected);
}
void testBoolification(DeserializationError error, bool expected) {
// DeserializationError on left-hand side
CHECK(bool(error) == expected);
CHECK(bool(error) != !expected);
CHECK(!bool(error) == !expected);
// DeserializationError on right-hand side
CHECK(expected == bool(error));
CHECK(!expected != bool(error));
CHECK(!expected == !bool(error));
}
#define TEST_STRINGIFICATION(symbol) \
testStringification(DeserializationError::symbol, #symbol)
#define TEST_BOOLIFICATION(symbol, expected) \
testBoolification(DeserializationError::symbol, expected)
TEST_CASE("DeserializationError") {
SECTION("c_str()") {
TEST_STRINGIFICATION(Ok);
TEST_STRINGIFICATION(EmptyInput);
TEST_STRINGIFICATION(IncompleteInput);
TEST_STRINGIFICATION(InvalidInput);
TEST_STRINGIFICATION(NoMemory);
TEST_STRINGIFICATION(TooDeep);
}
SECTION("as boolean") {
TEST_BOOLIFICATION(Ok, false);
TEST_BOOLIFICATION(EmptyInput, true);
TEST_BOOLIFICATION(IncompleteInput, true);
TEST_BOOLIFICATION(InvalidInput, true);
TEST_BOOLIFICATION(NoMemory, true);
TEST_BOOLIFICATION(TooDeep, true);
}
SECTION("ostream DeserializationError") {
std::stringstream s;
s << DeserializationError(DeserializationError::InvalidInput);
REQUIRE(s.str() == "InvalidInput");
}
SECTION("ostream DeserializationError::Code") {
std::stringstream s;
s << DeserializationError::InvalidInput;
REQUIRE(s.str() == "InvalidInput");
}
SECTION("switch") {
DeserializationError err = DeserializationError::InvalidInput;
switch (err.code()) {
case DeserializationError::InvalidInput:
SUCCEED();
break;
default:
FAIL();
break;
}
}
SECTION("Use in a condition") {
DeserializationError invalidInput(DeserializationError::InvalidInput);
DeserializationError ok(DeserializationError::Ok);
SECTION("if (!err)") {
if (!invalidInput)
FAIL();
}
SECTION("if (err)") {
if (ok)
FAIL();
}
}
SECTION("Comparisons") {
DeserializationError invalidInput(DeserializationError::InvalidInput);
DeserializationError ok(DeserializationError::Ok);
SECTION("DeserializationError == Code") {
REQUIRE(invalidInput == DeserializationError::InvalidInput);
REQUIRE(ok == DeserializationError::Ok);
}
SECTION("Code == DeserializationError") {
REQUIRE(DeserializationError::InvalidInput == invalidInput);
REQUIRE(DeserializationError::Ok == ok);
}
SECTION("DeserializationError != Code") {
REQUIRE(invalidInput != DeserializationError::Ok);
REQUIRE(ok != DeserializationError::InvalidInput);
}
SECTION("Code != DeserializationError") {
REQUIRE(DeserializationError::Ok != invalidInput);
REQUIRE(DeserializationError::InvalidInput != ok);
}
SECTION("DeserializationError == DeserializationError") {
REQUIRE_FALSE(invalidInput == ok);
}
SECTION("DeserializationError != DeserializationError") {
REQUIRE(invalidInput != ok);
}
}
}

View File

@@ -0,0 +1,337 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
#include "Allocators.hpp"
using ArduinoJson::detail::sizeofArray;
TEST_CASE("deserialize JSON array") {
SpyingAllocator spy;
JsonDocument doc(&spy);
SECTION("An empty array") {
DeserializationError err = deserializeJson(doc, "[]");
JsonArray arr = doc.as<JsonArray>();
REQUIRE(err == DeserializationError::Ok);
REQUIRE(0 == arr.size());
}
SECTION("Spaces") {
SECTION("Before the opening bracket") {
DeserializationError err = deserializeJson(doc, " []");
JsonArray arr = doc.as<JsonArray>();
REQUIRE(err == DeserializationError::Ok);
REQUIRE(0 == arr.size());
}
SECTION("Before first value") {
DeserializationError err = deserializeJson(doc, "[ \t\r\n42]");
JsonArray arr = doc.as<JsonArray>();
REQUIRE(err == DeserializationError::Ok);
REQUIRE(1 == arr.size());
REQUIRE(arr[0] == 42);
}
SECTION("After first value") {
DeserializationError err = deserializeJson(doc, "[42 \t\r\n]");
JsonArray arr = doc.as<JsonArray>();
REQUIRE(err == DeserializationError::Ok);
REQUIRE(1 == arr.size());
REQUIRE(arr[0] == 42);
}
}
SECTION("Values types") {
SECTION("On integer") {
DeserializationError err = deserializeJson(doc, "[42]");
JsonArray arr = doc.as<JsonArray>();
REQUIRE(err == DeserializationError::Ok);
REQUIRE(1 == arr.size());
REQUIRE(arr[0] == 42);
}
SECTION("Two integers") {
DeserializationError err = deserializeJson(doc, "[42,84]");
JsonArray arr = doc.as<JsonArray>();
REQUIRE(err == DeserializationError::Ok);
REQUIRE(2 == arr.size());
REQUIRE(arr[0] == 42);
REQUIRE(arr[1] == 84);
}
SECTION("Float") {
DeserializationError err = deserializeJson(doc, "[4.2,1e2]");
JsonArray arr = doc.as<JsonArray>();
REQUIRE(err == DeserializationError::Ok);
REQUIRE(2 == arr.size());
REQUIRE(arr[0].as<float>() == Approx(4.2f));
REQUIRE(arr[1] == 1e2f);
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Reallocate(sizeofPool(), sizeofPool(2)),
});
}
SECTION("Double") {
DeserializationError err = deserializeJson(doc, "[4.2123456,-7E89]");
JsonArray arr = doc.as<JsonArray>();
REQUIRE(err == DeserializationError::Ok);
REQUIRE(2 == arr.size());
REQUIRE(arr[0].as<double>() == Approx(4.2123456));
REQUIRE(arr[1] == -7E89);
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Reallocate(sizeofPool(), sizeofPool(4)),
});
}
SECTION("Unsigned long") {
DeserializationError err = deserializeJson(doc, "[4294967295]");
JsonArray arr = doc.as<JsonArray>();
REQUIRE(err == DeserializationError::Ok);
REQUIRE(1 == arr.size());
REQUIRE(arr[0] == 4294967295UL);
}
SECTION("Boolean") {
DeserializationError err = deserializeJson(doc, "[true,false]");
JsonArray arr = doc.as<JsonArray>();
REQUIRE(err == DeserializationError::Ok);
REQUIRE(2 == arr.size());
REQUIRE(arr[0] == true);
REQUIRE(arr[1] == false);
}
SECTION("Null") {
DeserializationError err = deserializeJson(doc, "[null,null]");
JsonArray arr = doc.as<JsonArray>();
REQUIRE(err == DeserializationError::Ok);
REQUIRE(2 == arr.size());
REQUIRE(arr[0].as<const char*>() == 0);
REQUIRE(arr[1].as<const char*>() == 0);
}
}
SECTION("Quotes") {
SECTION("Double quotes") {
DeserializationError err =
deserializeJson(doc, "[ \"hello\" , \"world\" ]");
JsonArray arr = doc.as<JsonArray>();
REQUIRE(err == DeserializationError::Ok);
REQUIRE(2 == arr.size());
REQUIRE(arr[0] == "hello");
REQUIRE(arr[1] == "world");
}
SECTION("Single quotes") {
DeserializationError err = deserializeJson(doc, "[ 'hello' , 'world' ]");
JsonArray arr = doc.as<JsonArray>();
REQUIRE(err == DeserializationError::Ok);
REQUIRE(2 == arr.size());
REQUIRE(arr[0] == "hello");
REQUIRE(arr[1] == "world");
}
SECTION("No quotes") {
DeserializationError err = deserializeJson(doc, "[ hello , world ]");
REQUIRE(err == DeserializationError::InvalidInput);
}
SECTION("Double quotes (empty strings)") {
DeserializationError err = deserializeJson(doc, "[\"\",\"\"]");
JsonArray arr = doc.as<JsonArray>();
REQUIRE(err == DeserializationError::Ok);
REQUIRE(2 == arr.size());
REQUIRE(arr[0] == "");
REQUIRE(arr[1] == "");
}
SECTION("Single quotes (empty strings)") {
DeserializationError err = deserializeJson(doc, "[\'\',\'\']");
JsonArray arr = doc.as<JsonArray>();
REQUIRE(err == DeserializationError::Ok);
REQUIRE(2 == arr.size());
REQUIRE(arr[0] == "");
REQUIRE(arr[1] == "");
}
SECTION("No quotes (empty strings)") {
DeserializationError err = deserializeJson(doc, "[,]");
REQUIRE(err == DeserializationError::InvalidInput);
}
SECTION("Closing single quotes missing") {
DeserializationError err = deserializeJson(doc, "[\"]");
REQUIRE(err == DeserializationError::IncompleteInput);
}
SECTION("Closing double quotes missing") {
DeserializationError err = deserializeJson(doc, "[\']");
REQUIRE(err == DeserializationError::IncompleteInput);
}
}
SECTION("Premature null-terminator") {
SECTION("After opening bracket") {
DeserializationError err = deserializeJson(doc, "[");
REQUIRE(err == DeserializationError::IncompleteInput);
}
SECTION("After value") {
DeserializationError err = deserializeJson(doc, "[1");
REQUIRE(err == DeserializationError::IncompleteInput);
}
SECTION("After comma") {
DeserializationError err = deserializeJson(doc, "[1,");
REQUIRE(err == DeserializationError::IncompleteInput);
}
}
SECTION("Premature end of input") {
const char* input = "[1,2]";
SECTION("After opening bracket") {
DeserializationError err = deserializeJson(doc, input, 1);
REQUIRE(err == DeserializationError::IncompleteInput);
}
SECTION("After value") {
DeserializationError err = deserializeJson(doc, input, 2);
REQUIRE(err == DeserializationError::IncompleteInput);
}
SECTION("After comma") {
DeserializationError err = deserializeJson(doc, input, 3);
REQUIRE(err == DeserializationError::IncompleteInput);
}
}
SECTION("Misc") {
SECTION("Nested objects") {
char jsonString[] =
" [ { \"a\" : 1 , \"b\" : 2 } , { \"c\" : 3 , \"d\" : 4 } ] ";
DeserializationError err = deserializeJson(doc, jsonString);
JsonArray arr = doc.as<JsonArray>();
JsonObject object1 = arr[0];
const JsonObject object2 = arr[1];
JsonObject object3 = arr[2];
REQUIRE(err == DeserializationError::Ok);
REQUIRE(object1.isNull() == false);
REQUIRE(object2.isNull() == false);
REQUIRE(object3.isNull() == true);
REQUIRE(2 == object1.size());
REQUIRE(2 == object2.size());
REQUIRE(0 == object3.size());
REQUIRE(1 == object1["a"].as<int>());
REQUIRE(2 == object1["b"].as<int>());
REQUIRE(3 == object2["c"].as<int>());
REQUIRE(4 == object2["d"].as<int>());
REQUIRE(0 == object3["e"].as<int>());
}
}
SECTION("Should clear the JsonArray") {
deserializeJson(doc, "[1,2,3,4]");
spy.clearLog();
deserializeJson(doc, "[]");
JsonArray arr = doc.as<JsonArray>();
REQUIRE(arr.size() == 0);
REQUIRE(spy.log() == AllocatorLog{
Deallocate(sizeofArray(4)),
});
}
}
TEST_CASE("deserialize JSON array under memory constraints") {
TimebombAllocator timebomb(100);
SpyingAllocator spy(&timebomb);
JsonDocument doc(&spy);
SECTION("empty array requires no allocation") {
timebomb.setCountdown(0);
char input[] = "[]";
DeserializationError err = deserializeJson(doc, input);
REQUIRE(err == DeserializationError::Ok);
}
SECTION("allocation of pool list fails") {
timebomb.setCountdown(0);
char input[] = "[1]";
DeserializationError err = deserializeJson(doc, input);
REQUIRE(err == DeserializationError::NoMemory);
REQUIRE(doc.as<std::string>() == "[]");
}
SECTION("allocation of pool fails") {
timebomb.setCountdown(0);
char input[] = "[1]";
DeserializationError err = deserializeJson(doc, input);
REQUIRE(err == DeserializationError::NoMemory);
REQUIRE(doc.as<std::string>() == "[]");
}
SECTION("allocation of string fails in array") {
timebomb.setCountdown(1);
char input[] = "[0,\"hi!\"]";
DeserializationError err = deserializeJson(doc, input);
REQUIRE(err == DeserializationError::NoMemory);
REQUIRE(doc.as<std::string>() == "[0,null]");
}
SECTION("don't store space characters") {
deserializeJson(doc, " [ \"1234567\" ] ");
REQUIRE(spy.log() ==
AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofStringBuffer()),
Reallocate(sizeofStringBuffer(), sizeofString("1234567")),
Reallocate(sizeofPool(), sizeofArray(1)),
});
}
}

View File

@@ -0,0 +1,109 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
#include <string>
#include "Allocators.hpp"
#include "Literals.hpp"
using ArduinoJson::detail::sizeofArray;
using ArduinoJson::detail::sizeofObject;
TEST_CASE("deserializeJson(JsonDocument&)") {
SpyingAllocator spy;
JsonDocument doc(&spy);
doc.add("hello"_s);
spy.clearLog();
auto err = deserializeJson(doc, "[42]");
REQUIRE(err == DeserializationError::Ok);
REQUIRE(doc.as<std::string>() == "[42]");
REQUIRE(spy.log() == AllocatorLog{
Deallocate(sizeofPool()),
Deallocate(sizeofString("hello")),
Allocate(sizeofPool()),
Reallocate(sizeofPool(), sizeofArray(1)),
});
}
TEST_CASE("deserializeJson(JsonVariant)") {
SECTION("variant is bound") {
SpyingAllocator spy;
JsonDocument doc(&spy);
doc.add("hello"_s);
spy.clearLog();
JsonVariant variant = doc[0];
auto err = deserializeJson(variant, "[42]");
REQUIRE(err == DeserializationError::Ok);
REQUIRE(doc.as<std::string>() == "[[42]]");
REQUIRE(spy.log() == AllocatorLog{
Deallocate(sizeofString("hello")),
});
}
SECTION("variant is unbound") {
JsonVariant variant;
auto err = deserializeJson(variant, "[42]");
REQUIRE(err == DeserializationError::NoMemory);
}
}
TEST_CASE("deserializeJson(ElementProxy)") {
SpyingAllocator spy;
JsonDocument doc(&spy);
doc.add("hello"_s);
spy.clearLog();
SECTION("element already exists") {
auto err = deserializeJson(doc[0], "[42]");
REQUIRE(err == DeserializationError::Ok);
REQUIRE(doc.as<std::string>() == "[[42]]");
REQUIRE(spy.log() == AllocatorLog{
Deallocate(sizeofString("hello")),
});
}
SECTION("element must be created") {
auto err = deserializeJson(doc[1], "[42]");
REQUIRE(err == DeserializationError::Ok);
REQUIRE(doc.as<std::string>() == "[\"hello\",[42]]");
REQUIRE(spy.log() == AllocatorLog{});
}
}
TEST_CASE("deserializeJson(MemberProxy)") {
SpyingAllocator spy;
JsonDocument doc(&spy);
doc["hello"_s] = "world"_s;
spy.clearLog();
SECTION("member already exists") {
auto err = deserializeJson(doc["hello"], "[42]");
REQUIRE(err == DeserializationError::Ok);
REQUIRE(doc.as<std::string>() == "{\"hello\":[42]}");
REQUIRE(spy.log() == AllocatorLog{
Deallocate(sizeofString("world")),
});
}
SECTION("member must be created exists") {
auto err = deserializeJson(doc["value"], "[42]");
REQUIRE(err == DeserializationError::Ok);
REQUIRE(doc.as<std::string>() == "{\"hello\":\"world\",\"value\":[42]}");
REQUIRE(spy.log() == AllocatorLog{});
}
}

View File

@@ -0,0 +1,162 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// MIT License
#define ARDUINOJSON_DECODE_UNICODE 1
#include <ArduinoJson.h>
#include <catch.hpp>
#include "Allocators.hpp"
TEST_CASE("deserializeJson() returns IncompleteInput") {
const char* testCases[] = {
// strings
"\"\\",
"\"hello",
"\'hello",
// unicode
"'\\u",
"'\\u00",
"'\\u000",
// false
"f",
"fa",
"fal",
"fals",
// true
"t",
"tr",
"tru",
// null
"n",
"nu",
"nul",
// object
"{",
"{a",
"{a:",
"{a:1",
"{a:1,",
"{a:1,b",
"{a:1,b:",
};
for (auto input : testCases) {
SECTION(input) {
JsonDocument doc;
REQUIRE(deserializeJson(doc, input) ==
DeserializationError::IncompleteInput);
}
}
}
TEST_CASE("deserializeJson() returns InvalidInput") {
const char* testCases[] = {
// unicode
"'\\u'", "'\\u000g'", "'\\u000'", "'\\u000G'", "'\\u000/'", "\\x1234",
// numbers
"6a9", "1,", "2]", "3}",
// constants
"nulL", "tru3", "fals3",
// garbage
"%*$£¤"};
for (auto input : testCases) {
SECTION(input) {
JsonDocument doc;
REQUIRE(deserializeJson(doc, input) ==
DeserializationError::InvalidInput);
}
}
}
TEST_CASE("deserializeJson() oversees some edge cases") {
const char* testCases[] = {
"'\\ud83d'", // leading surrogate without a trailing surrogate
"'\\udda4'", // trailing surrogate without a leading surrogate
"'\\ud83d\\ud83d'", // two leading surrogates
};
for (auto input : testCases) {
SECTION(input) {
JsonDocument doc;
REQUIRE(deserializeJson(doc, input) == DeserializationError::Ok);
}
}
}
TEST_CASE("deserializeJson() returns EmptyInput") {
JsonDocument doc;
SECTION("null") {
auto err = deserializeJson(doc, static_cast<const char*>(0));
REQUIRE(err == DeserializationError::EmptyInput);
}
SECTION("Empty string") {
auto err = deserializeJson(doc, "");
REQUIRE(err == DeserializationError::EmptyInput);
}
SECTION("Only spaces") {
auto err = deserializeJson(doc, " \t\n\r");
REQUIRE(err == DeserializationError::EmptyInput);
}
}
TEST_CASE("deserializeJson() returns NoMemory if string length overflows") {
JsonDocument doc;
auto maxLength = ArduinoJson::detail::StringNode::maxLength;
SECTION("max length should succeed") {
auto err = deserializeJson(doc, "\"" + std::string(maxLength, 'a') + "\"");
REQUIRE(err == DeserializationError::Ok);
}
SECTION("one above max length should fail") {
auto err =
deserializeJson(doc, "\"" + std::string(maxLength + 1, 'a') + "\"");
REQUIRE(err == DeserializationError::NoMemory);
}
}
TEST_CASE("deserializeJson() returns NoMemory if extension allocation fails") {
JsonDocument doc(FailingAllocator::instance());
SECTION("uint32_t should pass") {
auto err = deserializeJson(doc, "4294967295");
REQUIRE(err == DeserializationError::Ok);
}
SECTION("uint64_t should fail") {
auto err = deserializeJson(doc, "18446744073709551615");
REQUIRE(err == DeserializationError::NoMemory);
}
SECTION("int32_t should pass") {
auto err = deserializeJson(doc, "-2147483648");
REQUIRE(err == DeserializationError::Ok);
}
SECTION("int64_t should fail") {
auto err = deserializeJson(doc, "-9223372036854775808");
REQUIRE(err == DeserializationError::NoMemory);
}
SECTION("float should pass") {
auto err = deserializeJson(doc, "3.402823e38");
REQUIRE(err == DeserializationError::Ok);
}
SECTION("double should fail") {
auto err = deserializeJson(doc, "1.7976931348623157e308");
REQUIRE(err == DeserializationError::NoMemory);
}
}

View File

@@ -0,0 +1,831 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// MIT License
#define ARDUINOJSON_ENABLE_COMMENTS 1
#include <ArduinoJson.h>
#include <catch.hpp>
#include <sstream>
#include <string>
#include "Allocators.hpp"
#include "Literals.hpp"
using ArduinoJson::detail::sizeofArray;
using ArduinoJson::detail::sizeofObject;
TEST_CASE("Filtering") {
struct TestCase {
const char* description;
const char* input;
const char* filter;
uint8_t nestingLimit;
DeserializationError error;
const char* output;
size_t memoryUsage;
};
TestCase testCases[] = {
{
"Input is object, filter is null", // description
"{\"hello\":\"world\"}", // input
"null", // filter
10, // nestingLimit
DeserializationError::Ok, // error
"null", // output
0, // memoryUsage
},
{
"Input is object, filter is false",
"{\"hello\":\"world\"}",
"false",
10,
DeserializationError::Ok,
"null",
0,
},
{
"Input is object, filter is true",
"{\"abcdefg\":\"hijklmn\"}",
"true",
10,
DeserializationError::Ok,
"{\"abcdefg\":\"hijklmn\"}",
sizeofObject(1) + sizeofString("abcdefg") + sizeofString("hijklmn"),
},
{
"Input is object, filter is empty object",
"{\"hello\":\"world\"}",
"{}",
10,
DeserializationError::Ok,
"{}",
sizeofObject(0),
},
{
"Input in an object, but filter wants an array",
"{\"hello\":\"world\"}",
"[]",
10,
DeserializationError::Ok,
"null",
0,
},
{
"Member is a string, but filter wants an array",
"{\"example\":\"example\"}",
"{\"example\":[true]}",
10,
DeserializationError::Ok,
"{\"example\":null}",
sizeofObject(1) + sizeofString("example"),
},
{
"Member is a number, but filter wants an array",
"{\"example\":42}",
"{\"example\":[true]}",
10,
DeserializationError::Ok,
"{\"example\":null}",
sizeofObject(1) + sizeofString("example"),
},
{
"Input is an array, but filter wants an object",
"[\"hello\",\"world\"]",
"{}",
10,
DeserializationError::Ok,
"null",
0,
},
{
"Input is a bool, but filter wants an object",
"true",
"{}",
10,
DeserializationError::Ok,
"null",
0,
},
{
"Input is a string, but filter wants an object",
"\"hello\"",
"{}",
10,
DeserializationError::Ok,
"null",
0,
},
{
"Skip an integer",
"{\"an_integer\":666,example:42}",
"{\"example\":true}",
10,
DeserializationError::Ok,
"{\"example\":42}",
sizeofObject(1) + sizeofString("example"),
},
{
"Skip a float",
"{\"a_float\":12.34e-6,example:42}",
"{\"example\":true}",
10,
DeserializationError::Ok,
"{\"example\":42}",
sizeofObject(1) + sizeofString("example"),
},
{
"Skip false",
"{\"a_bool\":false,example:42}",
"{\"example\":true}",
10,
DeserializationError::Ok,
"{\"example\":42}",
sizeofObject(1) + sizeofString("example"),
},
{
"Skip true",
"{\"a_bool\":true,example:42}",
"{\"example\":true}",
10,
DeserializationError::Ok,
"{\"example\":42}",
sizeofObject(1) + sizeofString("example"),
},
{
"Skip null",
"{\"a_bool\":null,example:42}",
"{\"example\":true}",
10,
DeserializationError::Ok,
"{\"example\":42}",
sizeofObject(1) + sizeofString("example"),
},
{
"Skip a double-quoted string",
"{\"a_double_quoted_string\":\"hello\",example:42}",
"{\"example\":true}",
10,
DeserializationError::Ok,
"{\"example\":42}",
sizeofObject(1) + sizeofString("example"),
},
{
"Skip a single-quoted string",
"{\"a_single_quoted_string\":'hello',example:42}",
"{\"example\":true}",
10,
DeserializationError::Ok,
"{\"example\":42}",
sizeofObject(1) + sizeofString("example"),
},
{
"Skip an empty array",
"{\"an_empty_array\":[],example:42}",
"{\"example\":true}",
10,
DeserializationError::Ok,
"{\"example\":42}",
sizeofObject(1) + sizeofString("example"),
},
{
"Skip an empty array with spaces in it",
"{\"an_empty_array\":[\t],example:42}",
"{\"example\":true}",
10,
DeserializationError::Ok,
"{\"example\":42}",
sizeofObject(1) + sizeofString("example"),
},
{
"Skip an array",
"{\"an_array\":[1,2,3],example:42}",
"{\"example\":true}",
10,
DeserializationError::Ok,
"{\"example\":42}",
sizeofObject(1) + sizeofString("example"),
},
{
"Skip an array with spaces in it",
"{\"an_array\": [ 1 , 2 , 3 ] ,example:42}",
"{\"example\":true}",
10,
DeserializationError::Ok,
"{\"example\":42}",
sizeofObject(1) + sizeofString("example"),
},
{
"Skip an empty nested object",
"{\"an_empty_object\":{},example:42}",
"{\"example\":true}",
10,
DeserializationError::Ok,
"{\"example\":42}",
sizeofObject(1) + sizeofString("example"),
},
{
"Skip an empty nested object with spaces in it",
"{\"an_empty_object\":{ },example:42}",
"{\"example\":true}",
10,
DeserializationError::Ok,
"{\"example\":42}",
sizeofObject(1) + sizeofString("example"),
},
{
"Skip a nested object",
"{\"an_object\":{a:1,'b':2,\"c\":3},example:42}",
"{\"example\":true}",
10,
DeserializationError::Ok,
"{\"example\":42}",
sizeofObject(1) + sizeofString("example"),
},
{
"Skip an object with spaces in it",
"{\"an_object\" : { a : 1 , 'b' : 2 , \"c\" : 3 } ,example:42}",
"{\"example\":true}",
10,
DeserializationError::Ok,
"{\"example\":42}",
sizeofObject(1) + sizeofString("example"),
},
{
"Skip a string in a nested object",
"{\"an_integer\": 0,\"example\":{\"type\":\"int\",\"outcome\":42}}",
"{\"example\":{\"outcome\":true}}",
10,
DeserializationError::Ok,
"{\"example\":{\"outcome\":42}}",
2 * sizeofObject(1) + 2 * sizeofString("example"),
},
{
"wildcard",
"{\"example\":{\"type\":\"int\",\"outcome\":42}}",
"{\"*\":{\"outcome\":true}}",
10,
DeserializationError::Ok,
"{\"example\":{\"outcome\":42}}",
2 * sizeofObject(1) + 2 * sizeofString("example"),
},
{
"exclusion filter (issue #1628)",
"{\"example\":1,\"ignored\":2}",
"{\"*\":true,\"ignored\":false}",
10,
DeserializationError::Ok,
"{\"example\":1}",
sizeofObject(1) + sizeofString("example"),
},
{
"only the first element of array counts",
"[1,2,3]",
"[true, false]",
10,
DeserializationError::Ok,
"[1,2,3]",
sizeofArray(3),
},
{
"only the first element of array counts",
"[1,2,3]",
"[false, true]",
10,
DeserializationError::Ok,
"[]",
sizeofArray(0),
},
{
"filter members of object in array",
"[{\"example\":1,\"ignore\":2},{\"example\":3,\"ignore\":4}]",
"[{\"example\":true}]",
10,
DeserializationError::Ok,
"[{\"example\":1},{\"example\":3}]",
sizeofArray(2) + 2 * sizeofObject(1) + sizeofString("example"),
},
{
"Unclosed single quote in skipped element",
"[',2,3]",
"[false,true]",
10,
DeserializationError::IncompleteInput,
"[]",
sizeofArray(0),
},
{
"Unclosed double quote in skipped element",
"[\",2,3]",
"[false,true]",
10,
DeserializationError::IncompleteInput,
"[]",
sizeofArray(0),
},
{
"Detect errors in skipped value",
"[!,2,\\]",
"[false]",
10,
DeserializationError::InvalidInput,
"[]",
sizeofArray(0),
},
{
"Detect incomplete string event if it's skipped",
"\"ABC",
"false",
10,
DeserializationError::IncompleteInput,
"null",
0,
},
{
"Detect incomplete string event if it's skipped",
"'ABC",
"false",
10,
DeserializationError::IncompleteInput,
"null",
0,
},
{
"Handle escaped quotes",
"'A\\'BC'",
"false",
10,
DeserializationError::Ok,
"null",
0,
},
{
"Handle escaped quotes",
"\"A\\\"BC\"",
"false",
10,
DeserializationError::Ok,
"null",
0,
},
{
"Detect incomplete string in presence of escaped quotes",
"'A\\'BC",
"false",
10,
DeserializationError::IncompleteInput,
"null",
0,
},
{
"Detect incomplete string in presence of escaped quotes",
"\"A\\\"BC",
"false",
10,
DeserializationError::IncompleteInput,
"null",
0,
},
{
"skip empty array",
"[]",
"false",
10,
DeserializationError::Ok,
"null",
0,
},
{
"Skip empty array with spaces",
" [ ] ",
"false",
10,
DeserializationError::Ok,
"null",
0,
},
{
"Bubble up element error even if array is skipped",
"[1,'2,3]",
"false",
10,
DeserializationError::IncompleteInput,
"null",
0,
},
{
"Bubble up member error even if object is skipped",
"{'hello':'worl}",
"false",
10,
DeserializationError::IncompleteInput,
"null",
0,
},
{
"Bubble up colon error even if object is skipped",
"{'hello','world'}",
"false",
10,
DeserializationError::InvalidInput,
"null",
0,
},
{
"Bubble up key error even if object is skipped",
"{'hello:1}",
"false",
10,
DeserializationError::IncompleteInput,
"null",
0,
},
{
"Detect invalid value in skipped object",
"{'hello':!}",
"false",
10,
DeserializationError::InvalidInput,
"null",
0,
},
{
"Ignore invalid value in skipped object",
"{'hello':\\}",
"false",
10,
DeserializationError::InvalidInput,
"null",
0,
},
{
"Check nesting limit even for ignored objects",
"{}",
"false",
0,
DeserializationError::TooDeep,
"null",
0,
},
{
"Check nesting limit even for ignored objects",
"{'hello':{}}",
"false",
1,
DeserializationError::TooDeep,
"null",
0,
},
{
"Check nesting limit even for ignored values in objects",
"{'hello':{}}",
"{}",
1,
DeserializationError::TooDeep,
"{}",
sizeofObject(0),
},
{
"Check nesting limit even for ignored arrays",
"[]",
"false",
0,
DeserializationError::TooDeep,
"null",
0,
},
{
"Check nesting limit even for ignored arrays",
"[[]]",
"false",
1,
DeserializationError::TooDeep,
"null",
0,
},
{
"Check nesting limit even for ignored values in arrays",
"[[]]",
"[]",
1,
DeserializationError::TooDeep,
"[]",
sizeofArray(0),
},
{
"Supports back-slash at the end of skipped string",
"\"hell\\",
"false",
1,
DeserializationError::IncompleteInput,
"null",
0,
},
{
"Invalid comment at after an element in a skipped array",
"[1/]",
"false",
10,
DeserializationError::InvalidInput,
"null",
0,
},
{
"Incomplete comment at after an element in a skipped array",
"[1/*]",
"false",
10,
DeserializationError::IncompleteInput,
"null",
0,
},
{
"Missing comma in a skipped array",
"[1 2]",
"false",
10,
DeserializationError::InvalidInput,
"null",
0,
},
{
"Invalid comment at the beginning of array",
"[/1]",
"[false]",
10,
DeserializationError::InvalidInput,
"[]",
sizeofArray(0),
},
{
"Incomplete comment at the begining of an array",
"[/*]",
"[false]",
10,
DeserializationError::IncompleteInput,
"[]",
sizeofArray(0),
},
{
"Invalid comment before key",
"{/1:2}",
"{}",
10,
DeserializationError::InvalidInput,
"{}",
sizeofObject(0),
},
{
"Incomplete comment before key",
"{/*:2}",
"{}",
10,
DeserializationError::IncompleteInput,
"{}",
sizeofObject(0),
},
{
"Invalid comment after key",
"{\"example\"/1:2}",
"{}",
10,
DeserializationError::InvalidInput,
"{}",
sizeofObject(0),
},
{
"Incomplete comment after key",
"{\"example\"/*:2}",
"{}",
10,
DeserializationError::IncompleteInput,
"{}",
sizeofObject(0),
},
{
"Invalid comment after colon",
"{\"example\":/12}",
"{}",
10,
DeserializationError::InvalidInput,
"{}",
sizeofObject(0),
},
{
"Incomplete comment after colon",
"{\"example\":/*2}",
"{}",
10,
DeserializationError::IncompleteInput,
"{}",
sizeofObject(0),
},
{
"Comment next to an integer",
"{\"ignore\":1//,\"example\":2\n}",
"{\"example\":true}",
10,
DeserializationError::Ok,
"{}",
sizeofObject(0),
},
{
"Invalid comment after opening brace of a skipped object",
"{/1:2}",
"false",
10,
DeserializationError::InvalidInput,
"null",
0,
},
{
"Incomplete after opening brace of a skipped object",
"{/*:2}",
"false",
10,
DeserializationError::IncompleteInput,
"null",
0,
},
{
"Invalid comment after key of a skipped object",
"{\"example\"/:2}",
"false",
10,
DeserializationError::InvalidInput,
"null",
0,
},
{
"Incomplete comment after key of a skipped object",
"{\"example\"/*:2}",
"false",
10,
DeserializationError::IncompleteInput,
"null",
0,
},
{
"Invalid comment after value in a skipped object",
"{\"example\":2/}",
"false",
10,
DeserializationError::InvalidInput,
"null",
0,
},
{
"Incomplete comment after value of a skipped object",
"{\"example\":2/*}",
"false",
10,
DeserializationError::IncompleteInput,
"null",
0,
},
{
"Incomplete comment after comma in skipped object",
"{\"example\":2,/*}",
"false",
10,
DeserializationError::IncompleteInput,
"null",
0,
},
{
"NUL character in key",
"{\"x\":0,\"x\\u0000a\":1,\"x\\u0000b\":2}",
"{\"x\\u0000a\":true}",
10,
DeserializationError::Ok,
"{\"x\\u0000a\":1}",
sizeofObject(1) + sizeofString("x?a"),
},
};
for (auto& tc : testCases) {
SECTION(tc.description) {
SpyingAllocator spy;
JsonDocument filter;
JsonDocument doc(&spy);
REQUIRE(deserializeJson(filter, tc.filter) == DeserializationError::Ok);
CHECK(deserializeJson(
doc, tc.input, DeserializationOption::Filter(filter),
DeserializationOption::NestingLimit(tc.nestingLimit)) ==
tc.error);
CHECK(doc.as<std::string>() == tc.output);
doc.shrinkToFit();
CHECK(spy.allocatedBytes() == tc.memoryUsage);
}
}
}
TEST_CASE("Overloads") {
JsonDocument doc;
JsonDocument filter;
using namespace DeserializationOption;
// deserializeJson(..., Filter)
SECTION("const char*, Filter") {
deserializeJson(doc, "{}", Filter(filter));
}
SECTION("const char*, size_t, Filter") {
deserializeJson(doc, "{}", 2, Filter(filter));
}
SECTION("const std::string&, Filter") {
deserializeJson(doc, "{}"_s, Filter(filter));
}
SECTION("std::istream&, Filter") {
std::stringstream s("{}");
deserializeJson(doc, s, Filter(filter));
}
#ifdef HAS_VARIABLE_LENGTH_ARRAY
SECTION("char[n], Filter") {
size_t i = 4;
char vla[i];
strcpy(vla, "{}");
deserializeJson(doc, vla, Filter(filter));
}
#endif
// deserializeJson(..., Filter, NestingLimit)
SECTION("const char*, Filter, NestingLimit") {
deserializeJson(doc, "{}", Filter(filter), NestingLimit(5));
}
SECTION("const char*, size_t, Filter, NestingLimit") {
deserializeJson(doc, "{}", 2, Filter(filter), NestingLimit(5));
}
SECTION("const std::string&, Filter, NestingLimit") {
deserializeJson(doc, "{}"_s, Filter(filter), NestingLimit(5));
}
SECTION("std::istream&, Filter, NestingLimit") {
std::stringstream s("{}");
deserializeJson(doc, s, Filter(filter), NestingLimit(5));
}
#ifdef HAS_VARIABLE_LENGTH_ARRAY
SECTION("char[n], Filter, NestingLimit") {
size_t i = 4;
char vla[i];
strcpy(vla, "{}");
deserializeJson(doc, vla, Filter(filter), NestingLimit(5));
}
#endif
// deserializeJson(..., NestingLimit, Filter)
SECTION("const char*, NestingLimit, Filter") {
deserializeJson(doc, "{}", NestingLimit(5), Filter(filter));
}
SECTION("const char*, size_t, NestingLimit, Filter") {
deserializeJson(doc, "{}", 2, NestingLimit(5), Filter(filter));
}
SECTION("const std::string&, NestingLimit, Filter") {
deserializeJson(doc, "{}"_s, NestingLimit(5), Filter(filter));
}
SECTION("std::istream&, NestingLimit, Filter") {
std::stringstream s("{}");
deserializeJson(doc, s, NestingLimit(5), Filter(filter));
}
#ifdef HAS_VARIABLE_LENGTH_ARRAY
SECTION("char[n], NestingLimit, Filter") {
size_t i = 4;
char vla[i];
strcpy(vla, "{}");
deserializeJson(doc, vla, NestingLimit(5), Filter(filter));
}
#endif
}
TEST_CASE("shrink filter") {
JsonDocument doc;
SpyingAllocator spy;
JsonDocument filter(&spy);
filter["a"] = true;
spy.clearLog();
deserializeJson(doc, "{}", DeserializationOption::Filter(filter));
REQUIRE(spy.log() == AllocatorLog{
Reallocate(sizeofPool(), sizeofObject(1)),
});
}

View File

@@ -0,0 +1,232 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
#include <sstream>
#include "Allocators.hpp"
#include "CustomReader.hpp"
#include "Literals.hpp"
using ArduinoJson::detail::sizeofObject;
TEST_CASE("deserializeJson(char*)") {
SpyingAllocator spy;
JsonDocument doc(&spy);
char input[] = "{\"hello\":\"world\"}";
DeserializationError err = deserializeJson(doc, input);
REQUIRE(err == DeserializationError::Ok);
REQUIRE(spy.log() ==
AllocatorLog{
Allocate(sizeofStringBuffer()),
Allocate(sizeofPool()),
Reallocate(sizeofStringBuffer(), sizeofString("hello")),
Allocate(sizeofStringBuffer()),
Reallocate(sizeofStringBuffer(), sizeofString("world")),
Reallocate(sizeofPool(), sizeofObject(1)),
});
}
TEST_CASE("deserializeJson(unsigned char*, unsigned int)") { // issue #1897
JsonDocument doc;
unsigned char input[] = "{\"hello\":\"world\"}";
unsigned char* input_ptr = input;
unsigned int size = sizeof(input);
DeserializationError err = deserializeJson(doc, input_ptr, size);
REQUIRE(err == DeserializationError::Ok);
}
TEST_CASE("deserializeJson(uint8_t*, size_t)") { // issue #1898
JsonDocument doc;
uint8_t input[] = "{\"hello\":\"world\"}";
uint8_t* input_ptr = input;
size_t size = sizeof(input);
DeserializationError err = deserializeJson(doc, input_ptr, size);
REQUIRE(err == DeserializationError::Ok);
}
TEST_CASE("deserializeJson(const std::string&)") {
JsonDocument doc;
SECTION("should accept const string") {
const std::string input("[42]");
DeserializationError err = deserializeJson(doc, input);
REQUIRE(err == DeserializationError::Ok);
}
SECTION("should accept temporary string") {
DeserializationError err = deserializeJson(doc, "[42]"_s);
REQUIRE(err == DeserializationError::Ok);
}
SECTION("should duplicate content") {
std::string input("[\"hello\"]");
DeserializationError err = deserializeJson(doc, input);
input[2] = 'X'; // alter the string tomake sure we made a copy
JsonArray array = doc.as<JsonArray>();
REQUIRE(err == DeserializationError::Ok);
REQUIRE("hello"_s == array[0]);
}
}
TEST_CASE("deserializeJson(std::istream&)") {
JsonDocument doc;
SECTION("array") {
std::istringstream json(" [ 42 ] ");
DeserializationError err = deserializeJson(doc, json);
JsonArray arr = doc.as<JsonArray>();
REQUIRE(err == DeserializationError::Ok);
REQUIRE(1 == arr.size());
REQUIRE(42 == arr[0]);
}
SECTION("object") {
std::istringstream json(" { hello : 'world' }");
DeserializationError err = deserializeJson(doc, json);
JsonObject obj = doc.as<JsonObject>();
REQUIRE(err == DeserializationError::Ok);
REQUIRE(1 == obj.size());
REQUIRE("world"_s == obj["hello"]);
}
SECTION("Should not read after the closing brace of an empty object") {
std::istringstream json("{}123");
deserializeJson(doc, json);
REQUIRE('1' == char(json.get()));
}
SECTION("Should not read after the closing brace") {
std::istringstream json("{\"hello\":\"world\"}123");
deserializeJson(doc, json);
REQUIRE('1' == char(json.get()));
}
SECTION("Should not read after the closing bracket of an empty array") {
std::istringstream json("[]123");
deserializeJson(doc, json);
REQUIRE('1' == char(json.get()));
}
SECTION("Should not read after the closing bracket") {
std::istringstream json("[\"hello\",\"world\"]123");
deserializeJson(doc, json);
REQUIRE('1' == char(json.get()));
}
SECTION("Should not read after the closing quote") {
std::istringstream json("\"hello\"123");
deserializeJson(doc, json);
REQUIRE('1' == char(json.get()));
}
}
#ifdef HAS_VARIABLE_LENGTH_ARRAY
TEST_CASE("deserializeJson(VLA)") {
size_t i = 9;
char vla[i];
strcpy(vla, "{\"a\":42}");
JsonDocument doc;
DeserializationError err = deserializeJson(doc, vla);
REQUIRE(err == DeserializationError::Ok);
}
#endif
TEST_CASE("deserializeJson(CustomReader)") {
JsonDocument doc;
CustomReader reader("[4,2]");
DeserializationError err = deserializeJson(doc, reader);
REQUIRE(err == DeserializationError::Ok);
REQUIRE(doc.size() == 2);
REQUIRE(doc[0] == 4);
REQUIRE(doc[1] == 2);
}
TEST_CASE("deserializeJson(JsonDocument&, MemberProxy)") {
JsonDocument doc1;
doc1["payload"] = "[4,2]";
JsonDocument doc2;
DeserializationError err = deserializeJson(doc2, doc1["payload"]);
REQUIRE(err == DeserializationError::Ok);
REQUIRE(doc2.size() == 2);
REQUIRE(doc2[0] == 4);
REQUIRE(doc2[1] == 2);
}
TEST_CASE("deserializeJson(JsonDocument&, JsonVariant)") {
JsonDocument doc1;
doc1["payload"] = "[4,2]";
JsonDocument doc2;
DeserializationError err =
deserializeJson(doc2, doc1["payload"].as<JsonVariant>());
REQUIRE(err == DeserializationError::Ok);
REQUIRE(doc2.size() == 2);
REQUIRE(doc2[0] == 4);
REQUIRE(doc2[1] == 2);
}
TEST_CASE("deserializeJson(JsonDocument&, JsonVariantConst)") {
JsonDocument doc1;
doc1["payload"] = "[4,2]";
JsonDocument doc2;
DeserializationError err =
deserializeJson(doc2, doc1["payload"].as<JsonVariantConst>());
REQUIRE(err == DeserializationError::Ok);
REQUIRE(doc2.size() == 2);
REQUIRE(doc2[0] == 4);
REQUIRE(doc2[1] == 2);
}
TEST_CASE("deserializeJson(JsonDocument&, ElementProxy)") {
JsonDocument doc1;
doc1[0] = "[4,2]";
JsonDocument doc2;
DeserializationError err = deserializeJson(doc2, doc1[0]);
REQUIRE(err == DeserializationError::Ok);
REQUIRE(doc2.size() == 2);
REQUIRE(doc2[0] == 4);
REQUIRE(doc2[1] == 2);
}

View File

@@ -0,0 +1,49 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
#include "Allocators.hpp"
using ArduinoJson::detail::sizeofArray;
TEST_CASE("deserializeJson() misc cases") {
SpyingAllocator spy;
JsonDocument doc(&spy);
SECTION("null") {
DeserializationError err = deserializeJson(doc, "null");
REQUIRE(err == DeserializationError::Ok);
REQUIRE(doc.is<float>() == false);
}
SECTION("true") {
DeserializationError err = deserializeJson(doc, "true");
REQUIRE(err == DeserializationError::Ok);
REQUIRE(doc.is<bool>());
REQUIRE(doc.as<bool>() == true);
}
SECTION("false") {
DeserializationError err = deserializeJson(doc, "false");
REQUIRE(err == DeserializationError::Ok);
REQUIRE(doc.is<bool>());
REQUIRE(doc.as<bool>() == false);
}
SECTION("Should clear the JsonVariant") {
deserializeJson(doc, "[1,2,3]");
spy.clearLog();
deserializeJson(doc, "{}");
REQUIRE(doc.is<JsonObject>());
REQUIRE(spy.log() == AllocatorLog{
Deallocate(sizeofArray(3)),
});
}
}

View File

@@ -0,0 +1,105 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
#include <sstream>
#include "Literals.hpp"
#define SHOULD_WORK(expression) REQUIRE(DeserializationError::Ok == expression);
#define SHOULD_FAIL(expression) \
REQUIRE(DeserializationError::TooDeep == expression);
TEST_CASE("JsonDeserializer nesting") {
JsonDocument doc;
SECTION("Input = const char*") {
SECTION("limit = 0") {
DeserializationOption::NestingLimit nesting(0);
SHOULD_WORK(deserializeJson(doc, "\"toto\"", nesting));
SHOULD_WORK(deserializeJson(doc, "123", nesting));
SHOULD_WORK(deserializeJson(doc, "true", nesting));
SHOULD_FAIL(deserializeJson(doc, "[]", nesting));
SHOULD_FAIL(deserializeJson(doc, "{}", nesting));
SHOULD_FAIL(deserializeJson(doc, "[\"toto\"]", nesting));
SHOULD_FAIL(deserializeJson(doc, "{\"toto\":1}", nesting));
}
SECTION("limit = 1") {
DeserializationOption::NestingLimit nesting(1);
SHOULD_WORK(deserializeJson(doc, "[\"toto\"]", nesting));
SHOULD_WORK(deserializeJson(doc, "{\"toto\":1}", nesting));
SHOULD_FAIL(deserializeJson(doc, "{\"toto\":{}}", nesting));
SHOULD_FAIL(deserializeJson(doc, "{\"toto\":[]}", nesting));
SHOULD_FAIL(deserializeJson(doc, "[[\"toto\"]]", nesting));
SHOULD_FAIL(deserializeJson(doc, "[{\"toto\":1}]", nesting));
}
}
SECTION("char* and size_t") {
SECTION("limit = 0") {
DeserializationOption::NestingLimit nesting(0);
SHOULD_WORK(deserializeJson(doc, "\"toto\"", 6, nesting));
SHOULD_WORK(deserializeJson(doc, "123", 3, nesting));
SHOULD_WORK(deserializeJson(doc, "true", 4, nesting));
SHOULD_FAIL(deserializeJson(doc, "[]", 2, nesting));
SHOULD_FAIL(deserializeJson(doc, "{}", 2, nesting));
SHOULD_FAIL(deserializeJson(doc, "[\"toto\"]", 8, nesting));
SHOULD_FAIL(deserializeJson(doc, "{\"toto\":1}", 10, nesting));
}
SECTION("limit = 1") {
DeserializationOption::NestingLimit nesting(1);
SHOULD_WORK(deserializeJson(doc, "[\"toto\"]", 8, nesting));
SHOULD_WORK(deserializeJson(doc, "{\"toto\":1}", 10, nesting));
SHOULD_FAIL(deserializeJson(doc, "{\"toto\":{}}", 11, nesting));
SHOULD_FAIL(deserializeJson(doc, "{\"toto\":[]}", 11, nesting));
SHOULD_FAIL(deserializeJson(doc, "[[\"toto\"]]", 10, nesting));
SHOULD_FAIL(deserializeJson(doc, "[{\"toto\":1}]", 12, nesting));
}
}
SECTION("Input = std::string") {
SECTION("limit = 0") {
DeserializationOption::NestingLimit nesting(0);
SHOULD_WORK(deserializeJson(doc, "\"toto\""_s, nesting));
SHOULD_WORK(deserializeJson(doc, "123"_s, nesting));
SHOULD_WORK(deserializeJson(doc, "true"_s, nesting));
SHOULD_FAIL(deserializeJson(doc, "[]"_s, nesting));
SHOULD_FAIL(deserializeJson(doc, "{}"_s, nesting));
SHOULD_FAIL(deserializeJson(doc, "[\"toto\"]"_s, nesting));
SHOULD_FAIL(deserializeJson(doc, "{\"toto\":1}"_s, nesting));
}
SECTION("limit = 1") {
DeserializationOption::NestingLimit nesting(1);
SHOULD_WORK(deserializeJson(doc, "[\"toto\"]"_s, nesting));
SHOULD_WORK(deserializeJson(doc, "{\"toto\":1}"_s, nesting));
SHOULD_FAIL(deserializeJson(doc, "{\"toto\":{}}"_s, nesting));
SHOULD_FAIL(deserializeJson(doc, "{\"toto\":[]}"_s, nesting));
SHOULD_FAIL(deserializeJson(doc, "[[\"toto\"]]"_s, nesting));
SHOULD_FAIL(deserializeJson(doc, "[{\"toto\":1}]"_s, nesting));
}
}
SECTION("Input = std::istream") {
SECTION("limit = 0") {
DeserializationOption::NestingLimit nesting(0);
std::istringstream good("true");
std::istringstream bad("[]");
SHOULD_WORK(deserializeJson(doc, good, nesting));
SHOULD_FAIL(deserializeJson(doc, bad, nesting));
}
SECTION("limit = 1") {
DeserializationOption::NestingLimit nesting(1);
std::istringstream good("[\"toto\"]");
std::istringstream bad("{\"toto\":{}}");
SHOULD_WORK(deserializeJson(doc, good, nesting));
SHOULD_FAIL(deserializeJson(doc, bad, nesting));
}
}
}

View File

@@ -0,0 +1,133 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// MIT License
#define ARDUINOJSON_USE_LONG_LONG 0
#define ARDUINOJSON_ENABLE_NAN 1
#define ARDUINOJSON_ENABLE_INFINITY 1
#include <ArduinoJson.h>
#include <limits.h>
#include <catch.hpp>
namespace my {
using ArduinoJson::detail::isinf;
using ArduinoJson::detail::isnan;
} // namespace my
TEST_CASE("deserialize an integer") {
JsonDocument doc;
SECTION("Integer") {
SECTION("0") {
DeserializationError err = deserializeJson(doc, "0");
REQUIRE(err == DeserializationError::Ok);
REQUIRE(doc.is<int>() == true);
REQUIRE(doc.as<int>() == 0);
REQUIRE(doc.as<std::string>() == "0"); // issue #808
}
SECTION("Negative") {
DeserializationError err = deserializeJson(doc, "-42");
REQUIRE(err == DeserializationError::Ok);
REQUIRE(doc.is<int>());
REQUIRE_FALSE(doc.is<bool>());
REQUIRE(doc.as<int>() == -42);
}
#if LONG_MAX == 2147483647
SECTION("LONG_MAX") {
DeserializationError err = deserializeJson(doc, "2147483647");
REQUIRE(err == DeserializationError::Ok);
REQUIRE(doc.is<long>() == true);
REQUIRE(doc.as<long>() == LONG_MAX);
}
SECTION("LONG_MAX + 1") {
DeserializationError err = deserializeJson(doc, "2147483648");
CAPTURE(LONG_MIN);
REQUIRE(err == DeserializationError::Ok);
REQUIRE(doc.is<long>() == false);
REQUIRE(doc.is<float>() == true);
}
#endif
#if LONG_MIN == -2147483648
SECTION("LONG_MIN") {
DeserializationError err = deserializeJson(doc, "-2147483648");
REQUIRE(err == DeserializationError::Ok);
REQUIRE(doc.is<long>() == true);
REQUIRE(doc.as<long>() == LONG_MIN);
}
SECTION("LONG_MIN - 1") {
DeserializationError err = deserializeJson(doc, "-2147483649");
REQUIRE(err == DeserializationError::Ok);
REQUIRE(doc.is<long>() == false);
REQUIRE(doc.is<float>() == true);
}
#endif
#if ULONG_MAX == 4294967295
SECTION("ULONG_MAX") {
DeserializationError err = deserializeJson(doc, "4294967295");
REQUIRE(err == DeserializationError::Ok);
REQUIRE(doc.is<unsigned long>() == true);
REQUIRE(doc.as<unsigned long>() == ULONG_MAX);
REQUIRE(doc.is<long>() == false);
}
SECTION("ULONG_MAX + 1") {
DeserializationError err = deserializeJson(doc, "4294967296");
REQUIRE(err == DeserializationError::Ok);
REQUIRE(doc.is<unsigned long>() == false);
REQUIRE(doc.is<float>() == true);
}
#endif
}
SECTION("Floats") {
SECTION("Double") {
DeserializationError err = deserializeJson(doc, "-1.23e+4");
REQUIRE(err == DeserializationError::Ok);
REQUIRE_FALSE(doc.is<int>());
REQUIRE(doc.is<double>());
REQUIRE(doc.as<double>() == Approx(-1.23e+4));
}
SECTION("NaN") {
DeserializationError err = deserializeJson(doc, "NaN");
REQUIRE(err == DeserializationError::Ok);
REQUIRE(doc.is<float>() == true);
REQUIRE(my::isnan(doc.as<float>()));
}
SECTION("Infinity") {
DeserializationError err = deserializeJson(doc, "Infinity");
REQUIRE(err == DeserializationError::Ok);
REQUIRE(doc.is<float>() == true);
REQUIRE(my::isinf(doc.as<float>()));
}
SECTION("+Infinity") {
DeserializationError err = deserializeJson(doc, "+Infinity");
REQUIRE(err == DeserializationError::Ok);
REQUIRE(doc.is<float>() == true);
REQUIRE(my::isinf(doc.as<float>()));
}
SECTION("-Infinity") {
DeserializationError err = deserializeJson(doc, "-Infinity");
REQUIRE(err == DeserializationError::Ok);
REQUIRE(doc.is<float>() == true);
REQUIRE(my::isinf(doc.as<float>()));
}
}
}

View File

@@ -0,0 +1,400 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
#include "Allocators.hpp"
#include "Literals.hpp"
using ArduinoJson::detail::sizeofObject;
TEST_CASE("deserialize JSON object") {
SpyingAllocator spy;
JsonDocument doc(&spy);
SECTION("An empty object") {
DeserializationError err = deserializeJson(doc, "{}");
JsonObject obj = doc.as<JsonObject>();
REQUIRE(err == DeserializationError::Ok);
REQUIRE(doc.is<JsonObject>());
REQUIRE(obj.size() == 0);
}
SECTION("Quotes") {
SECTION("Double quotes") {
DeserializationError err = deserializeJson(doc, "{\"key\":\"value\"}");
JsonObject obj = doc.as<JsonObject>();
REQUIRE(err == DeserializationError::Ok);
REQUIRE(doc.is<JsonObject>());
REQUIRE(obj.size() == 1);
REQUIRE(obj["key"] == "value");
}
SECTION("Single quotes") {
DeserializationError err = deserializeJson(doc, "{'key':'value'}");
JsonObject obj = doc.as<JsonObject>();
REQUIRE(err == DeserializationError::Ok);
REQUIRE(doc.is<JsonObject>());
REQUIRE(obj.size() == 1);
REQUIRE(obj["key"] == "value");
}
SECTION("No quotes") {
DeserializationError err = deserializeJson(doc, "{key:'value'}");
JsonObject obj = doc.as<JsonObject>();
REQUIRE(err == DeserializationError::Ok);
REQUIRE(doc.is<JsonObject>());
REQUIRE(obj.size() == 1);
REQUIRE(obj["key"] == "value");
}
SECTION("No quotes, allow underscore in key") {
DeserializationError err = deserializeJson(doc, "{_k_e_y_:42}");
JsonObject obj = doc.as<JsonObject>();
REQUIRE(err == DeserializationError::Ok);
REQUIRE(doc.is<JsonObject>());
REQUIRE(obj.size() == 1);
REQUIRE(obj["_k_e_y_"] == 42);
}
}
SECTION("Spaces") {
SECTION("Before the key") {
DeserializationError err = deserializeJson(doc, "{ \"key\":\"value\"}");
JsonObject obj = doc.as<JsonObject>();
REQUIRE(err == DeserializationError::Ok);
REQUIRE(doc.is<JsonObject>());
REQUIRE(obj.size() == 1);
REQUIRE(obj["key"] == "value");
}
SECTION("After the key") {
DeserializationError err = deserializeJson(doc, "{\"key\" :\"value\"}");
JsonObject obj = doc.as<JsonObject>();
REQUIRE(err == DeserializationError::Ok);
REQUIRE(doc.is<JsonObject>());
REQUIRE(obj.size() == 1);
REQUIRE(obj["key"] == "value");
}
SECTION("Before the value") {
DeserializationError err = deserializeJson(doc, "{\"key\": \"value\"}");
JsonObject obj = doc.as<JsonObject>();
REQUIRE(err == DeserializationError::Ok);
REQUIRE(doc.is<JsonObject>());
REQUIRE(obj.size() == 1);
REQUIRE(obj["key"] == "value");
}
SECTION("After the value") {
DeserializationError err = deserializeJson(doc, "{\"key\":\"value\" }");
JsonObject obj = doc.as<JsonObject>();
REQUIRE(err == DeserializationError::Ok);
REQUIRE(doc.is<JsonObject>());
REQUIRE(obj.size() == 1);
REQUIRE(obj["key"] == "value");
}
SECTION("Before the comma") {
DeserializationError err =
deserializeJson(doc, "{\"key1\":\"value1\" ,\"key2\":\"value2\"}");
JsonObject obj = doc.as<JsonObject>();
REQUIRE(err == DeserializationError::Ok);
REQUIRE(doc.is<JsonObject>());
REQUIRE(obj.size() == 2);
REQUIRE(obj["key1"] == "value1");
REQUIRE(obj["key2"] == "value2");
}
SECTION("After the comma") {
DeserializationError err =
deserializeJson(doc, "{\"key1\":\"value1\", \"key2\":\"value2\"}");
JsonObject obj = doc.as<JsonObject>();
REQUIRE(err == DeserializationError::Ok);
REQUIRE(doc.is<JsonObject>());
REQUIRE(obj.size() == 2);
REQUIRE(obj["key1"] == "value1");
REQUIRE(obj["key2"] == "value2");
}
}
SECTION("Values types") {
SECTION("String") {
DeserializationError err =
deserializeJson(doc, "{\"key1\":\"value1\",\"key2\":\"value2\"}");
JsonObject obj = doc.as<JsonObject>();
REQUIRE(err == DeserializationError::Ok);
REQUIRE(doc.is<JsonObject>());
REQUIRE(obj.size() == 2);
REQUIRE(obj["key1"] == "value1");
REQUIRE(obj["key2"] == "value2");
}
SECTION("Integer") {
DeserializationError err =
deserializeJson(doc, "{\"key1\":42,\"key2\":-42}");
JsonObject obj = doc.as<JsonObject>();
REQUIRE(err == DeserializationError::Ok);
REQUIRE(doc.is<JsonObject>());
REQUIRE(obj.size() == 2);
REQUIRE(obj["key1"] == 42);
REQUIRE(obj["key2"] == -42);
}
SECTION("Float") {
DeserializationError err =
deserializeJson(doc, "{\"key1\":12.345,\"key2\":-7E3}");
JsonObject obj = doc.as<JsonObject>();
REQUIRE(err == DeserializationError::Ok);
REQUIRE(doc.is<JsonObject>());
REQUIRE(obj.size() == 2);
REQUIRE(obj["key1"].as<float>() == Approx(12.345f));
REQUIRE(obj["key2"] == -7E3f);
}
SECTION("Double") {
DeserializationError err =
deserializeJson(doc, "{\"key1\":12.3456789,\"key2\":-7E89}");
JsonObject obj = doc.as<JsonObject>();
REQUIRE(err == DeserializationError::Ok);
REQUIRE(doc.is<JsonObject>());
REQUIRE(obj.size() == 2);
REQUIRE(obj["key1"].as<double>() == Approx(12.3456789));
REQUIRE(obj["key2"] == -7E89);
}
SECTION("Booleans") {
DeserializationError err =
deserializeJson(doc, "{\"key1\":true,\"key2\":false}");
JsonObject obj = doc.as<JsonObject>();
REQUIRE(err == DeserializationError::Ok);
REQUIRE(doc.is<JsonObject>());
REQUIRE(obj.size() == 2);
REQUIRE(obj["key1"] == true);
REQUIRE(obj["key2"] == false);
}
SECTION("Null") {
DeserializationError err =
deserializeJson(doc, "{\"key1\":null,\"key2\":null}");
JsonObject obj = doc.as<JsonObject>();
REQUIRE(err == DeserializationError::Ok);
REQUIRE(doc.is<JsonObject>());
REQUIRE(obj.size() == 2);
REQUIRE(obj["key1"].as<const char*>() == 0);
REQUIRE(obj["key2"].as<const char*>() == 0);
}
SECTION("Array") {
char jsonString[] = " { \"ab\" : [ 1 , 2 ] , \"cd\" : [ 3 , 4 ] } ";
DeserializationError err = deserializeJson(doc, jsonString);
JsonObject obj = doc.as<JsonObject>();
JsonArray array1 = obj["ab"];
const JsonArray array2 = obj["cd"];
JsonArray array3 = obj["ef"];
REQUIRE(err == DeserializationError::Ok);
REQUIRE(array1.isNull() == false);
REQUIRE(array2.isNull() == false);
REQUIRE(array3.isNull() == true);
REQUIRE(2 == array1.size());
REQUIRE(2 == array2.size());
REQUIRE(0 == array3.size());
REQUIRE(1 == array1[0].as<int>());
REQUIRE(2 == array1[1].as<int>());
REQUIRE(3 == array2[0].as<int>());
REQUIRE(4 == array2[1].as<int>());
REQUIRE(0 == array3[0].as<int>());
}
}
SECTION("Premature null terminator") {
SECTION("After opening brace") {
DeserializationError err = deserializeJson(doc, "{");
REQUIRE(err == DeserializationError::IncompleteInput);
}
SECTION("After key") {
DeserializationError err = deserializeJson(doc, "{\"hello\"");
REQUIRE(err == DeserializationError::IncompleteInput);
}
SECTION("After colon") {
DeserializationError err = deserializeJson(doc, "{\"hello\":");
REQUIRE(err == DeserializationError::IncompleteInput);
}
SECTION("After value") {
DeserializationError err = deserializeJson(doc, "{\"hello\":\"world\"");
REQUIRE(err == DeserializationError::IncompleteInput);
}
SECTION("After comma") {
DeserializationError err = deserializeJson(doc, "{\"hello\":\"world\",");
REQUIRE(err == DeserializationError::IncompleteInput);
}
}
SECTION("Misc") {
SECTION("A quoted key without value") {
DeserializationError err = deserializeJson(doc, "{\"key\"}");
REQUIRE(err == DeserializationError::InvalidInput);
}
SECTION("A non-quoted key without value") {
DeserializationError err = deserializeJson(doc, "{key}");
REQUIRE(err == DeserializationError::InvalidInput);
}
SECTION("A dangling comma") {
DeserializationError err = deserializeJson(doc, "{\"key1\":\"value1\",}");
REQUIRE(err == DeserializationError::InvalidInput);
}
SECTION("null as a key") {
DeserializationError err = deserializeJson(doc, "{null:\"value\"}");
REQUIRE(err == DeserializationError::Ok);
}
SECTION("Repeated key") {
DeserializationError err =
deserializeJson(doc, "{alfa:{bravo:{charlie:1}},alfa:2}");
REQUIRE(err == DeserializationError::Ok);
REQUIRE(doc.as<std::string>() == "{\"alfa\":2}");
REQUIRE(spy.log() ==
AllocatorLog{
Allocate(sizeofStringBuffer()),
Allocate(sizeofPool()),
Reallocate(sizeofStringBuffer(), sizeofString("alfa")),
Allocate(sizeofStringBuffer()),
Reallocate(sizeofStringBuffer(), sizeofString("bravo")),
Allocate(sizeofStringBuffer()),
Reallocate(sizeofStringBuffer(), sizeofString("charlie")),
Allocate(sizeofStringBuffer()),
Deallocate(sizeofString("bravo")),
Deallocate(sizeofString("charlie")),
Deallocate(sizeofStringBuffer()),
Reallocate(sizeofPool(), sizeofObject(2) + sizeofObject(1)),
});
}
SECTION("Repeated key with zero copy mode") { // issue #1697
char input[] = "{a:{b:{c:1}},a:2}";
DeserializationError err = deserializeJson(doc, input);
REQUIRE(err == DeserializationError::Ok);
REQUIRE(doc["a"] == 2);
}
SECTION("NUL in keys") {
DeserializationError err =
deserializeJson(doc, "{\"x\":0,\"x\\u0000a\":1,\"x\\u0000b\":2}");
REQUIRE(err == DeserializationError::Ok);
REQUIRE(doc.as<std::string>() ==
"{\"x\":0,\"x\\u0000a\":1,\"x\\u0000b\":2}");
}
}
SECTION("Should clear the JsonObject") {
deserializeJson(doc, "{\"hello\":\"world\"}");
spy.clearLog();
deserializeJson(doc, "{}");
REQUIRE(doc.is<JsonObject>());
REQUIRE(doc.size() == 0);
REQUIRE(spy.log() == AllocatorLog{
Deallocate(sizeofObject(1)),
Deallocate(sizeofString("hello")),
Deallocate(sizeofString("world")),
});
}
SECTION("Issue #1335") {
std::string json("{\"a\":{},\"b\":{}}");
deserializeJson(doc, json);
CHECK(doc.as<std::string>() == json);
}
}
TEST_CASE("deserialize JSON object under memory constraints") {
TimebombAllocator timebomb(1024);
JsonDocument doc(&timebomb);
SECTION("empty object requires no allocation") {
timebomb.setCountdown(0);
char input[] = "{}";
DeserializationError err = deserializeJson(doc, input);
REQUIRE(err == DeserializationError::Ok);
REQUIRE(doc.as<std::string>() == "{}");
}
SECTION("key allocation fails") {
timebomb.setCountdown(0);
char input[] = "{\"a\":1}";
DeserializationError err = deserializeJson(doc, input);
REQUIRE(err == DeserializationError::NoMemory);
REQUIRE(doc.as<std::string>() == "{}");
}
SECTION("pool allocation fails") {
timebomb.setCountdown(1);
char input[] = "{\"a\":1}";
DeserializationError err = deserializeJson(doc, input);
REQUIRE(err == DeserializationError::NoMemory);
REQUIRE(doc.as<std::string>() == "{}");
}
SECTION("string allocation fails") {
timebomb.setCountdown(3);
char input[] = "{\"alfa\":\"bravo\"}";
DeserializationError err = deserializeJson(doc, input);
REQUIRE(err == DeserializationError::NoMemory);
REQUIRE(doc.as<std::string>() == "{\"alfa\":null}");
}
}

View File

@@ -0,0 +1,214 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// MIT License
#define ARDUINOJSON_DECODE_UNICODE 1
#include <ArduinoJson.h>
#include <catch.hpp>
#include "Allocators.hpp"
using ArduinoJson::detail::sizeofArray;
using ArduinoJson::detail::sizeofObject;
TEST_CASE("Valid JSON strings value") {
struct TestCase {
const char* input;
const char* expectedOutput;
};
TestCase testCases[] = {
{"\"hello world\"", "hello world"},
{"\'hello world\'", "hello world"},
{"'\"'", "\""},
{"'\\\\'", "\\"},
{"'\\/'", "/"},
{"'\\b'", "\b"},
{"'\\f'", "\f"},
{"'\\n'", "\n"},
{"'\\r'", "\r"},
{"'\\t'", "\t"},
{"\"1\\\"2\\\\3\\/4\\b5\\f6\\n7\\r8\\t9\"", "1\"2\\3/4\b5\f6\n7\r8\t9"},
{"'\\u0041'", "A"},
{"'\\u00e4'", "\xc3\xa4"}, // ä
{"'\\u00E4'", "\xc3\xa4"}, // ä
{"'\\u3042'", "\xe3\x81\x82"}, // あ
{"'\\ud83d\\udda4'", "\xf0\x9f\x96\xa4"}, // 🖤
{"'\\uF053'", "\xef\x81\x93"}, // issue #1173
{"'\\uF015'", "\xef\x80\x95"}, // issue #1173
{"'\\uF054'", "\xef\x81\x94"}, // issue #1173
};
const size_t testCount = sizeof(testCases) / sizeof(testCases[0]);
JsonDocument doc;
for (size_t i = 0; i < testCount; i++) {
const TestCase& testCase = testCases[i];
CAPTURE(testCase.input);
DeserializationError err = deserializeJson(doc, testCase.input);
CHECK(err == DeserializationError::Ok);
CHECK(doc.as<std::string>() == testCase.expectedOutput);
}
}
TEST_CASE("\\u0000") {
JsonDocument doc;
DeserializationError err = deserializeJson(doc, "\"wx\\u0000yz\"");
REQUIRE(err == DeserializationError::Ok);
const char* result = doc.as<const char*>();
CHECK(result[0] == 'w');
CHECK(result[1] == 'x');
CHECK(result[2] == 0);
CHECK(result[3] == 'y');
CHECK(result[4] == 'z');
CHECK(result[5] == 0);
CHECK(doc.as<JsonString>().size() == 5);
CHECK(doc.as<std::string>().size() == 5);
}
TEST_CASE("Truncated JSON string") {
const char* testCases[] = {"\"hello", "\'hello", "'\\u", "'\\u00", "'\\u000"};
const size_t testCount = sizeof(testCases) / sizeof(testCases[0]);
JsonDocument doc;
for (size_t i = 0; i < testCount; i++) {
const char* input = testCases[i];
CAPTURE(input);
REQUIRE(deserializeJson(doc, input) ==
DeserializationError::IncompleteInput);
}
}
TEST_CASE("Escape single quote in single quoted string") {
JsonDocument doc;
DeserializationError err = deserializeJson(doc, "'ab\\\'cd'");
REQUIRE(err == DeserializationError::Ok);
CHECK(doc.as<std::string>() == "ab\'cd");
}
TEST_CASE("Escape double quote in double quoted string") {
JsonDocument doc;
DeserializationError err = deserializeJson(doc, "'ab\\\"cd'");
REQUIRE(err == DeserializationError::Ok);
CHECK(doc.as<std::string>() == "ab\"cd");
}
TEST_CASE("Invalid JSON string") {
const char* testCases[] = {"'\\u'", "'\\u000g'", "'\\u000'",
"'\\u000G'", "'\\u000/'", "'\\x1234'"};
const size_t testCount = sizeof(testCases) / sizeof(testCases[0]);
JsonDocument doc;
for (size_t i = 0; i < testCount; i++) {
const char* input = testCases[i];
CAPTURE(input);
REQUIRE(deserializeJson(doc, input) == DeserializationError::InvalidInput);
}
}
TEST_CASE("Allocation of the key fails") {
TimebombAllocator timebomb(0);
SpyingAllocator spy(&timebomb);
JsonDocument doc(&spy);
SECTION("Quoted string, first member") {
REQUIRE(deserializeJson(doc, "{\"example\":1}") ==
DeserializationError::NoMemory);
REQUIRE(spy.log() == AllocatorLog{
AllocateFail(sizeofStringBuffer()),
});
}
SECTION("Quoted string, second member") {
timebomb.setCountdown(3);
REQUIRE(deserializeJson(doc, "{\"hello\":1,\"world\"}") ==
DeserializationError::NoMemory);
REQUIRE(spy.log() ==
AllocatorLog{
Allocate(sizeofStringBuffer()),
Allocate(sizeofPool()),
Reallocate(sizeofStringBuffer(), sizeofString("hello")),
AllocateFail(sizeofStringBuffer()),
ReallocateFail(sizeofPool(), sizeofObject(1)),
});
}
SECTION("Non-Quoted string, first member") {
REQUIRE(deserializeJson(doc, "{example:1}") ==
DeserializationError::NoMemory);
REQUIRE(spy.log() == AllocatorLog{
AllocateFail(sizeofStringBuffer()),
});
}
SECTION("Non-Quoted string, second member") {
timebomb.setCountdown(3);
REQUIRE(deserializeJson(doc, "{hello:1,world}") ==
DeserializationError::NoMemory);
REQUIRE(spy.log() ==
AllocatorLog{
Allocate(sizeofStringBuffer()),
Allocate(sizeofPool()),
Reallocate(sizeofStringBuffer(), sizeofString("hello")),
AllocateFail(sizeofStringBuffer()),
ReallocateFail(sizeofPool(), sizeofObject(1)),
});
}
}
TEST_CASE("String allocation fails") {
SpyingAllocator spy(FailingAllocator::instance());
JsonDocument doc(&spy);
SECTION("Input is const char*") {
REQUIRE(deserializeJson(doc, "\"hello\"") ==
DeserializationError::NoMemory);
REQUIRE(spy.log() == AllocatorLog{
AllocateFail(sizeofStringBuffer()),
});
}
}
TEST_CASE("Deduplicate values") {
SpyingAllocator spy;
JsonDocument doc(&spy);
deserializeJson(doc, "[\"example\",\"example\"]");
CHECK(doc[0].as<const char*>() == doc[1].as<const char*>());
REQUIRE(spy.log() ==
AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofStringBuffer()),
Reallocate(sizeofStringBuffer(), sizeofString("example")),
Allocate(sizeofStringBuffer()),
Deallocate(sizeofStringBuffer()),
Reallocate(sizeofPool(), sizeofArray(2)),
});
}
TEST_CASE("Deduplicate keys") {
SpyingAllocator spy;
JsonDocument doc(&spy);
deserializeJson(doc, "[{\"example\":1},{\"example\":2}]");
const char* key1 = doc[0].as<JsonObject>().begin()->key().c_str();
const char* key2 = doc[1].as<JsonObject>().begin()->key().c_str();
CHECK(key1 == key2);
REQUIRE(spy.log() ==
AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofStringBuffer()),
Reallocate(sizeofStringBuffer(), sizeofString("example")),
Allocate(sizeofStringBuffer()),
Deallocate(sizeofStringBuffer()),
Reallocate(sizeofPool(), sizeofArray(2) + 2 * sizeofObject(1)),
});
}