Initial commit of Arduino libraries
This commit is contained in:
26
ArduinoJson/extras/tests/JsonDeserializer/CMakeLists.txt
Normal file
26
ArduinoJson/extras/tests/JsonDeserializer/CMakeLists.txt
Normal file
@@ -0,0 +1,26 @@
|
||||
# ArduinoJson - https://arduinojson.org
|
||||
# Copyright © 2014-2025, Benoit BLANCHON
|
||||
# MIT License
|
||||
|
||||
add_executable(JsonDeserializerTests
|
||||
array.cpp
|
||||
DeserializationError.cpp
|
||||
destination_types.cpp
|
||||
errors.cpp
|
||||
filter.cpp
|
||||
input_types.cpp
|
||||
misc.cpp
|
||||
nestingLimit.cpp
|
||||
number.cpp
|
||||
object.cpp
|
||||
string.cpp
|
||||
)
|
||||
|
||||
set_target_properties(JsonDeserializerTests PROPERTIES UNITY_BUILD OFF)
|
||||
|
||||
add_test(JsonDeserializer JsonDeserializerTests)
|
||||
|
||||
set_tests_properties(JsonDeserializer
|
||||
PROPERTIES
|
||||
LABELS "Catch"
|
||||
)
|
||||
@@ -0,0 +1,122 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2025, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <catch.hpp>
|
||||
|
||||
#include <sstream>
|
||||
|
||||
void testStringification(DeserializationError error, std::string expected) {
|
||||
REQUIRE(error.c_str() == expected);
|
||||
}
|
||||
|
||||
void testBoolification(DeserializationError error, bool expected) {
|
||||
// DeserializationError on left-hand side
|
||||
CHECK(bool(error) == expected);
|
||||
CHECK(bool(error) != !expected);
|
||||
CHECK(!bool(error) == !expected);
|
||||
|
||||
// DeserializationError on right-hand side
|
||||
CHECK(expected == bool(error));
|
||||
CHECK(!expected != bool(error));
|
||||
CHECK(!expected == !bool(error));
|
||||
}
|
||||
|
||||
#define TEST_STRINGIFICATION(symbol) \
|
||||
testStringification(DeserializationError::symbol, #symbol)
|
||||
|
||||
#define TEST_BOOLIFICATION(symbol, expected) \
|
||||
testBoolification(DeserializationError::symbol, expected)
|
||||
|
||||
TEST_CASE("DeserializationError") {
|
||||
SECTION("c_str()") {
|
||||
TEST_STRINGIFICATION(Ok);
|
||||
TEST_STRINGIFICATION(EmptyInput);
|
||||
TEST_STRINGIFICATION(IncompleteInput);
|
||||
TEST_STRINGIFICATION(InvalidInput);
|
||||
TEST_STRINGIFICATION(NoMemory);
|
||||
TEST_STRINGIFICATION(TooDeep);
|
||||
}
|
||||
|
||||
SECTION("as boolean") {
|
||||
TEST_BOOLIFICATION(Ok, false);
|
||||
TEST_BOOLIFICATION(EmptyInput, true);
|
||||
TEST_BOOLIFICATION(IncompleteInput, true);
|
||||
TEST_BOOLIFICATION(InvalidInput, true);
|
||||
TEST_BOOLIFICATION(NoMemory, true);
|
||||
TEST_BOOLIFICATION(TooDeep, true);
|
||||
}
|
||||
|
||||
SECTION("ostream DeserializationError") {
|
||||
std::stringstream s;
|
||||
s << DeserializationError(DeserializationError::InvalidInput);
|
||||
REQUIRE(s.str() == "InvalidInput");
|
||||
}
|
||||
|
||||
SECTION("ostream DeserializationError::Code") {
|
||||
std::stringstream s;
|
||||
s << DeserializationError::InvalidInput;
|
||||
REQUIRE(s.str() == "InvalidInput");
|
||||
}
|
||||
|
||||
SECTION("switch") {
|
||||
DeserializationError err = DeserializationError::InvalidInput;
|
||||
switch (err.code()) {
|
||||
case DeserializationError::InvalidInput:
|
||||
SUCCEED();
|
||||
break;
|
||||
default:
|
||||
FAIL();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Use in a condition") {
|
||||
DeserializationError invalidInput(DeserializationError::InvalidInput);
|
||||
DeserializationError ok(DeserializationError::Ok);
|
||||
|
||||
SECTION("if (!err)") {
|
||||
if (!invalidInput)
|
||||
FAIL();
|
||||
}
|
||||
|
||||
SECTION("if (err)") {
|
||||
if (ok)
|
||||
FAIL();
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Comparisons") {
|
||||
DeserializationError invalidInput(DeserializationError::InvalidInput);
|
||||
DeserializationError ok(DeserializationError::Ok);
|
||||
|
||||
SECTION("DeserializationError == Code") {
|
||||
REQUIRE(invalidInput == DeserializationError::InvalidInput);
|
||||
REQUIRE(ok == DeserializationError::Ok);
|
||||
}
|
||||
|
||||
SECTION("Code == DeserializationError") {
|
||||
REQUIRE(DeserializationError::InvalidInput == invalidInput);
|
||||
REQUIRE(DeserializationError::Ok == ok);
|
||||
}
|
||||
|
||||
SECTION("DeserializationError != Code") {
|
||||
REQUIRE(invalidInput != DeserializationError::Ok);
|
||||
REQUIRE(ok != DeserializationError::InvalidInput);
|
||||
}
|
||||
|
||||
SECTION("Code != DeserializationError") {
|
||||
REQUIRE(DeserializationError::Ok != invalidInput);
|
||||
REQUIRE(DeserializationError::InvalidInput != ok);
|
||||
}
|
||||
|
||||
SECTION("DeserializationError == DeserializationError") {
|
||||
REQUIRE_FALSE(invalidInput == ok);
|
||||
}
|
||||
|
||||
SECTION("DeserializationError != DeserializationError") {
|
||||
REQUIRE(invalidInput != ok);
|
||||
}
|
||||
}
|
||||
}
|
||||
337
ArduinoJson/extras/tests/JsonDeserializer/array.cpp
Normal file
337
ArduinoJson/extras/tests/JsonDeserializer/array.cpp
Normal file
@@ -0,0 +1,337 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2025, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <catch.hpp>
|
||||
|
||||
#include "Allocators.hpp"
|
||||
|
||||
using ArduinoJson::detail::sizeofArray;
|
||||
|
||||
TEST_CASE("deserialize JSON array") {
|
||||
SpyingAllocator spy;
|
||||
JsonDocument doc(&spy);
|
||||
|
||||
SECTION("An empty array") {
|
||||
DeserializationError err = deserializeJson(doc, "[]");
|
||||
JsonArray arr = doc.as<JsonArray>();
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(0 == arr.size());
|
||||
}
|
||||
|
||||
SECTION("Spaces") {
|
||||
SECTION("Before the opening bracket") {
|
||||
DeserializationError err = deserializeJson(doc, " []");
|
||||
JsonArray arr = doc.as<JsonArray>();
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(0 == arr.size());
|
||||
}
|
||||
|
||||
SECTION("Before first value") {
|
||||
DeserializationError err = deserializeJson(doc, "[ \t\r\n42]");
|
||||
JsonArray arr = doc.as<JsonArray>();
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(1 == arr.size());
|
||||
REQUIRE(arr[0] == 42);
|
||||
}
|
||||
|
||||
SECTION("After first value") {
|
||||
DeserializationError err = deserializeJson(doc, "[42 \t\r\n]");
|
||||
JsonArray arr = doc.as<JsonArray>();
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(1 == arr.size());
|
||||
REQUIRE(arr[0] == 42);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Values types") {
|
||||
SECTION("On integer") {
|
||||
DeserializationError err = deserializeJson(doc, "[42]");
|
||||
JsonArray arr = doc.as<JsonArray>();
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(1 == arr.size());
|
||||
REQUIRE(arr[0] == 42);
|
||||
}
|
||||
|
||||
SECTION("Two integers") {
|
||||
DeserializationError err = deserializeJson(doc, "[42,84]");
|
||||
JsonArray arr = doc.as<JsonArray>();
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(2 == arr.size());
|
||||
REQUIRE(arr[0] == 42);
|
||||
REQUIRE(arr[1] == 84);
|
||||
}
|
||||
|
||||
SECTION("Float") {
|
||||
DeserializationError err = deserializeJson(doc, "[4.2,1e2]");
|
||||
JsonArray arr = doc.as<JsonArray>();
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(2 == arr.size());
|
||||
REQUIRE(arr[0].as<float>() == Approx(4.2f));
|
||||
REQUIRE(arr[1] == 1e2f);
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Allocate(sizeofPool()),
|
||||
Reallocate(sizeofPool(), sizeofPool(2)),
|
||||
});
|
||||
}
|
||||
|
||||
SECTION("Double") {
|
||||
DeserializationError err = deserializeJson(doc, "[4.2123456,-7E89]");
|
||||
JsonArray arr = doc.as<JsonArray>();
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(2 == arr.size());
|
||||
REQUIRE(arr[0].as<double>() == Approx(4.2123456));
|
||||
REQUIRE(arr[1] == -7E89);
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Allocate(sizeofPool()),
|
||||
Reallocate(sizeofPool(), sizeofPool(4)),
|
||||
});
|
||||
}
|
||||
|
||||
SECTION("Unsigned long") {
|
||||
DeserializationError err = deserializeJson(doc, "[4294967295]");
|
||||
JsonArray arr = doc.as<JsonArray>();
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(1 == arr.size());
|
||||
REQUIRE(arr[0] == 4294967295UL);
|
||||
}
|
||||
|
||||
SECTION("Boolean") {
|
||||
DeserializationError err = deserializeJson(doc, "[true,false]");
|
||||
JsonArray arr = doc.as<JsonArray>();
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(2 == arr.size());
|
||||
REQUIRE(arr[0] == true);
|
||||
REQUIRE(arr[1] == false);
|
||||
}
|
||||
|
||||
SECTION("Null") {
|
||||
DeserializationError err = deserializeJson(doc, "[null,null]");
|
||||
JsonArray arr = doc.as<JsonArray>();
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(2 == arr.size());
|
||||
REQUIRE(arr[0].as<const char*>() == 0);
|
||||
REQUIRE(arr[1].as<const char*>() == 0);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Quotes") {
|
||||
SECTION("Double quotes") {
|
||||
DeserializationError err =
|
||||
deserializeJson(doc, "[ \"hello\" , \"world\" ]");
|
||||
JsonArray arr = doc.as<JsonArray>();
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(2 == arr.size());
|
||||
REQUIRE(arr[0] == "hello");
|
||||
REQUIRE(arr[1] == "world");
|
||||
}
|
||||
|
||||
SECTION("Single quotes") {
|
||||
DeserializationError err = deserializeJson(doc, "[ 'hello' , 'world' ]");
|
||||
JsonArray arr = doc.as<JsonArray>();
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(2 == arr.size());
|
||||
REQUIRE(arr[0] == "hello");
|
||||
REQUIRE(arr[1] == "world");
|
||||
}
|
||||
|
||||
SECTION("No quotes") {
|
||||
DeserializationError err = deserializeJson(doc, "[ hello , world ]");
|
||||
REQUIRE(err == DeserializationError::InvalidInput);
|
||||
}
|
||||
|
||||
SECTION("Double quotes (empty strings)") {
|
||||
DeserializationError err = deserializeJson(doc, "[\"\",\"\"]");
|
||||
JsonArray arr = doc.as<JsonArray>();
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(2 == arr.size());
|
||||
REQUIRE(arr[0] == "");
|
||||
REQUIRE(arr[1] == "");
|
||||
}
|
||||
|
||||
SECTION("Single quotes (empty strings)") {
|
||||
DeserializationError err = deserializeJson(doc, "[\'\',\'\']");
|
||||
JsonArray arr = doc.as<JsonArray>();
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(2 == arr.size());
|
||||
REQUIRE(arr[0] == "");
|
||||
REQUIRE(arr[1] == "");
|
||||
}
|
||||
|
||||
SECTION("No quotes (empty strings)") {
|
||||
DeserializationError err = deserializeJson(doc, "[,]");
|
||||
|
||||
REQUIRE(err == DeserializationError::InvalidInput);
|
||||
}
|
||||
|
||||
SECTION("Closing single quotes missing") {
|
||||
DeserializationError err = deserializeJson(doc, "[\"]");
|
||||
|
||||
REQUIRE(err == DeserializationError::IncompleteInput);
|
||||
}
|
||||
|
||||
SECTION("Closing double quotes missing") {
|
||||
DeserializationError err = deserializeJson(doc, "[\']");
|
||||
|
||||
REQUIRE(err == DeserializationError::IncompleteInput);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Premature null-terminator") {
|
||||
SECTION("After opening bracket") {
|
||||
DeserializationError err = deserializeJson(doc, "[");
|
||||
|
||||
REQUIRE(err == DeserializationError::IncompleteInput);
|
||||
}
|
||||
|
||||
SECTION("After value") {
|
||||
DeserializationError err = deserializeJson(doc, "[1");
|
||||
|
||||
REQUIRE(err == DeserializationError::IncompleteInput);
|
||||
}
|
||||
|
||||
SECTION("After comma") {
|
||||
DeserializationError err = deserializeJson(doc, "[1,");
|
||||
|
||||
REQUIRE(err == DeserializationError::IncompleteInput);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Premature end of input") {
|
||||
const char* input = "[1,2]";
|
||||
|
||||
SECTION("After opening bracket") {
|
||||
DeserializationError err = deserializeJson(doc, input, 1);
|
||||
|
||||
REQUIRE(err == DeserializationError::IncompleteInput);
|
||||
}
|
||||
|
||||
SECTION("After value") {
|
||||
DeserializationError err = deserializeJson(doc, input, 2);
|
||||
|
||||
REQUIRE(err == DeserializationError::IncompleteInput);
|
||||
}
|
||||
|
||||
SECTION("After comma") {
|
||||
DeserializationError err = deserializeJson(doc, input, 3);
|
||||
|
||||
REQUIRE(err == DeserializationError::IncompleteInput);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Misc") {
|
||||
SECTION("Nested objects") {
|
||||
char jsonString[] =
|
||||
" [ { \"a\" : 1 , \"b\" : 2 } , { \"c\" : 3 , \"d\" : 4 } ] ";
|
||||
|
||||
DeserializationError err = deserializeJson(doc, jsonString);
|
||||
JsonArray arr = doc.as<JsonArray>();
|
||||
|
||||
JsonObject object1 = arr[0];
|
||||
const JsonObject object2 = arr[1];
|
||||
JsonObject object3 = arr[2];
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
|
||||
REQUIRE(object1.isNull() == false);
|
||||
REQUIRE(object2.isNull() == false);
|
||||
REQUIRE(object3.isNull() == true);
|
||||
|
||||
REQUIRE(2 == object1.size());
|
||||
REQUIRE(2 == object2.size());
|
||||
REQUIRE(0 == object3.size());
|
||||
|
||||
REQUIRE(1 == object1["a"].as<int>());
|
||||
REQUIRE(2 == object1["b"].as<int>());
|
||||
REQUIRE(3 == object2["c"].as<int>());
|
||||
REQUIRE(4 == object2["d"].as<int>());
|
||||
REQUIRE(0 == object3["e"].as<int>());
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Should clear the JsonArray") {
|
||||
deserializeJson(doc, "[1,2,3,4]");
|
||||
spy.clearLog();
|
||||
|
||||
deserializeJson(doc, "[]");
|
||||
|
||||
JsonArray arr = doc.as<JsonArray>();
|
||||
REQUIRE(arr.size() == 0);
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Deallocate(sizeofArray(4)),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("deserialize JSON array under memory constraints") {
|
||||
TimebombAllocator timebomb(100);
|
||||
SpyingAllocator spy(&timebomb);
|
||||
JsonDocument doc(&spy);
|
||||
|
||||
SECTION("empty array requires no allocation") {
|
||||
timebomb.setCountdown(0);
|
||||
char input[] = "[]";
|
||||
|
||||
DeserializationError err = deserializeJson(doc, input);
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
}
|
||||
|
||||
SECTION("allocation of pool list fails") {
|
||||
timebomb.setCountdown(0);
|
||||
char input[] = "[1]";
|
||||
|
||||
DeserializationError err = deserializeJson(doc, input);
|
||||
|
||||
REQUIRE(err == DeserializationError::NoMemory);
|
||||
REQUIRE(doc.as<std::string>() == "[]");
|
||||
}
|
||||
|
||||
SECTION("allocation of pool fails") {
|
||||
timebomb.setCountdown(0);
|
||||
char input[] = "[1]";
|
||||
|
||||
DeserializationError err = deserializeJson(doc, input);
|
||||
|
||||
REQUIRE(err == DeserializationError::NoMemory);
|
||||
REQUIRE(doc.as<std::string>() == "[]");
|
||||
}
|
||||
|
||||
SECTION("allocation of string fails in array") {
|
||||
timebomb.setCountdown(1);
|
||||
char input[] = "[0,\"hi!\"]";
|
||||
|
||||
DeserializationError err = deserializeJson(doc, input);
|
||||
|
||||
REQUIRE(err == DeserializationError::NoMemory);
|
||||
REQUIRE(doc.as<std::string>() == "[0,null]");
|
||||
}
|
||||
|
||||
SECTION("don't store space characters") {
|
||||
deserializeJson(doc, " [ \"1234567\" ] ");
|
||||
|
||||
REQUIRE(spy.log() ==
|
||||
AllocatorLog{
|
||||
Allocate(sizeofPool()),
|
||||
Allocate(sizeofStringBuffer()),
|
||||
Reallocate(sizeofStringBuffer(), sizeofString("1234567")),
|
||||
Reallocate(sizeofPool(), sizeofArray(1)),
|
||||
});
|
||||
}
|
||||
}
|
||||
109
ArduinoJson/extras/tests/JsonDeserializer/destination_types.cpp
Normal file
109
ArduinoJson/extras/tests/JsonDeserializer/destination_types.cpp
Normal file
@@ -0,0 +1,109 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2025, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
|
||||
#include <catch.hpp>
|
||||
#include <string>
|
||||
|
||||
#include "Allocators.hpp"
|
||||
#include "Literals.hpp"
|
||||
|
||||
using ArduinoJson::detail::sizeofArray;
|
||||
using ArduinoJson::detail::sizeofObject;
|
||||
|
||||
TEST_CASE("deserializeJson(JsonDocument&)") {
|
||||
SpyingAllocator spy;
|
||||
JsonDocument doc(&spy);
|
||||
doc.add("hello"_s);
|
||||
spy.clearLog();
|
||||
|
||||
auto err = deserializeJson(doc, "[42]");
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.as<std::string>() == "[42]");
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Deallocate(sizeofPool()),
|
||||
Deallocate(sizeofString("hello")),
|
||||
Allocate(sizeofPool()),
|
||||
Reallocate(sizeofPool(), sizeofArray(1)),
|
||||
});
|
||||
}
|
||||
|
||||
TEST_CASE("deserializeJson(JsonVariant)") {
|
||||
SECTION("variant is bound") {
|
||||
SpyingAllocator spy;
|
||||
JsonDocument doc(&spy);
|
||||
doc.add("hello"_s);
|
||||
spy.clearLog();
|
||||
|
||||
JsonVariant variant = doc[0];
|
||||
|
||||
auto err = deserializeJson(variant, "[42]");
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.as<std::string>() == "[[42]]");
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Deallocate(sizeofString("hello")),
|
||||
});
|
||||
}
|
||||
|
||||
SECTION("variant is unbound") {
|
||||
JsonVariant variant;
|
||||
|
||||
auto err = deserializeJson(variant, "[42]");
|
||||
|
||||
REQUIRE(err == DeserializationError::NoMemory);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("deserializeJson(ElementProxy)") {
|
||||
SpyingAllocator spy;
|
||||
JsonDocument doc(&spy);
|
||||
doc.add("hello"_s);
|
||||
spy.clearLog();
|
||||
|
||||
SECTION("element already exists") {
|
||||
auto err = deserializeJson(doc[0], "[42]");
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.as<std::string>() == "[[42]]");
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Deallocate(sizeofString("hello")),
|
||||
});
|
||||
}
|
||||
|
||||
SECTION("element must be created") {
|
||||
auto err = deserializeJson(doc[1], "[42]");
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.as<std::string>() == "[\"hello\",[42]]");
|
||||
REQUIRE(spy.log() == AllocatorLog{});
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("deserializeJson(MemberProxy)") {
|
||||
SpyingAllocator spy;
|
||||
JsonDocument doc(&spy);
|
||||
doc["hello"_s] = "world"_s;
|
||||
spy.clearLog();
|
||||
|
||||
SECTION("member already exists") {
|
||||
auto err = deserializeJson(doc["hello"], "[42]");
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.as<std::string>() == "{\"hello\":[42]}");
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Deallocate(sizeofString("world")),
|
||||
});
|
||||
}
|
||||
|
||||
SECTION("member must be created exists") {
|
||||
auto err = deserializeJson(doc["value"], "[42]");
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.as<std::string>() == "{\"hello\":\"world\",\"value\":[42]}");
|
||||
REQUIRE(spy.log() == AllocatorLog{});
|
||||
}
|
||||
}
|
||||
162
ArduinoJson/extras/tests/JsonDeserializer/errors.cpp
Normal file
162
ArduinoJson/extras/tests/JsonDeserializer/errors.cpp
Normal file
@@ -0,0 +1,162 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2025, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#define ARDUINOJSON_DECODE_UNICODE 1
|
||||
#include <ArduinoJson.h>
|
||||
#include <catch.hpp>
|
||||
|
||||
#include "Allocators.hpp"
|
||||
|
||||
TEST_CASE("deserializeJson() returns IncompleteInput") {
|
||||
const char* testCases[] = {
|
||||
// strings
|
||||
"\"\\",
|
||||
"\"hello",
|
||||
"\'hello",
|
||||
// unicode
|
||||
"'\\u",
|
||||
"'\\u00",
|
||||
"'\\u000",
|
||||
// false
|
||||
"f",
|
||||
"fa",
|
||||
"fal",
|
||||
"fals",
|
||||
// true
|
||||
"t",
|
||||
"tr",
|
||||
"tru",
|
||||
// null
|
||||
"n",
|
||||
"nu",
|
||||
"nul",
|
||||
// object
|
||||
"{",
|
||||
"{a",
|
||||
"{a:",
|
||||
"{a:1",
|
||||
"{a:1,",
|
||||
"{a:1,b",
|
||||
"{a:1,b:",
|
||||
};
|
||||
|
||||
for (auto input : testCases) {
|
||||
SECTION(input) {
|
||||
JsonDocument doc;
|
||||
REQUIRE(deserializeJson(doc, input) ==
|
||||
DeserializationError::IncompleteInput);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("deserializeJson() returns InvalidInput") {
|
||||
const char* testCases[] = {
|
||||
// unicode
|
||||
"'\\u'", "'\\u000g'", "'\\u000'", "'\\u000G'", "'\\u000/'", "\\x1234",
|
||||
// numbers
|
||||
"6a9", "1,", "2]", "3}",
|
||||
// constants
|
||||
"nulL", "tru3", "fals3",
|
||||
// garbage
|
||||
"%*$£¤"};
|
||||
|
||||
for (auto input : testCases) {
|
||||
SECTION(input) {
|
||||
JsonDocument doc;
|
||||
REQUIRE(deserializeJson(doc, input) ==
|
||||
DeserializationError::InvalidInput);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("deserializeJson() oversees some edge cases") {
|
||||
const char* testCases[] = {
|
||||
"'\\ud83d'", // leading surrogate without a trailing surrogate
|
||||
"'\\udda4'", // trailing surrogate without a leading surrogate
|
||||
"'\\ud83d\\ud83d'", // two leading surrogates
|
||||
};
|
||||
|
||||
for (auto input : testCases) {
|
||||
SECTION(input) {
|
||||
JsonDocument doc;
|
||||
REQUIRE(deserializeJson(doc, input) == DeserializationError::Ok);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("deserializeJson() returns EmptyInput") {
|
||||
JsonDocument doc;
|
||||
|
||||
SECTION("null") {
|
||||
auto err = deserializeJson(doc, static_cast<const char*>(0));
|
||||
REQUIRE(err == DeserializationError::EmptyInput);
|
||||
}
|
||||
|
||||
SECTION("Empty string") {
|
||||
auto err = deserializeJson(doc, "");
|
||||
REQUIRE(err == DeserializationError::EmptyInput);
|
||||
}
|
||||
|
||||
SECTION("Only spaces") {
|
||||
auto err = deserializeJson(doc, " \t\n\r");
|
||||
REQUIRE(err == DeserializationError::EmptyInput);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("deserializeJson() returns NoMemory if string length overflows") {
|
||||
JsonDocument doc;
|
||||
auto maxLength = ArduinoJson::detail::StringNode::maxLength;
|
||||
|
||||
SECTION("max length should succeed") {
|
||||
auto err = deserializeJson(doc, "\"" + std::string(maxLength, 'a') + "\"");
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
}
|
||||
|
||||
SECTION("one above max length should fail") {
|
||||
auto err =
|
||||
deserializeJson(doc, "\"" + std::string(maxLength + 1, 'a') + "\"");
|
||||
REQUIRE(err == DeserializationError::NoMemory);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("deserializeJson() returns NoMemory if extension allocation fails") {
|
||||
JsonDocument doc(FailingAllocator::instance());
|
||||
|
||||
SECTION("uint32_t should pass") {
|
||||
auto err = deserializeJson(doc, "4294967295");
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
}
|
||||
|
||||
SECTION("uint64_t should fail") {
|
||||
auto err = deserializeJson(doc, "18446744073709551615");
|
||||
|
||||
REQUIRE(err == DeserializationError::NoMemory);
|
||||
}
|
||||
|
||||
SECTION("int32_t should pass") {
|
||||
auto err = deserializeJson(doc, "-2147483648");
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
}
|
||||
|
||||
SECTION("int64_t should fail") {
|
||||
auto err = deserializeJson(doc, "-9223372036854775808");
|
||||
|
||||
REQUIRE(err == DeserializationError::NoMemory);
|
||||
}
|
||||
|
||||
SECTION("float should pass") {
|
||||
auto err = deserializeJson(doc, "3.402823e38");
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
}
|
||||
|
||||
SECTION("double should fail") {
|
||||
auto err = deserializeJson(doc, "1.7976931348623157e308");
|
||||
|
||||
REQUIRE(err == DeserializationError::NoMemory);
|
||||
}
|
||||
}
|
||||
831
ArduinoJson/extras/tests/JsonDeserializer/filter.cpp
Normal file
831
ArduinoJson/extras/tests/JsonDeserializer/filter.cpp
Normal file
@@ -0,0 +1,831 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2025, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#define ARDUINOJSON_ENABLE_COMMENTS 1
|
||||
#include <ArduinoJson.h>
|
||||
#include <catch.hpp>
|
||||
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include "Allocators.hpp"
|
||||
#include "Literals.hpp"
|
||||
|
||||
using ArduinoJson::detail::sizeofArray;
|
||||
using ArduinoJson::detail::sizeofObject;
|
||||
|
||||
TEST_CASE("Filtering") {
|
||||
struct TestCase {
|
||||
const char* description;
|
||||
const char* input;
|
||||
const char* filter;
|
||||
uint8_t nestingLimit;
|
||||
DeserializationError error;
|
||||
const char* output;
|
||||
size_t memoryUsage;
|
||||
};
|
||||
|
||||
TestCase testCases[] = {
|
||||
{
|
||||
"Input is object, filter is null", // description
|
||||
"{\"hello\":\"world\"}", // input
|
||||
"null", // filter
|
||||
10, // nestingLimit
|
||||
DeserializationError::Ok, // error
|
||||
"null", // output
|
||||
0, // memoryUsage
|
||||
},
|
||||
{
|
||||
"Input is object, filter is false",
|
||||
"{\"hello\":\"world\"}",
|
||||
"false",
|
||||
10,
|
||||
DeserializationError::Ok,
|
||||
"null",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"Input is object, filter is true",
|
||||
"{\"abcdefg\":\"hijklmn\"}",
|
||||
"true",
|
||||
10,
|
||||
DeserializationError::Ok,
|
||||
"{\"abcdefg\":\"hijklmn\"}",
|
||||
sizeofObject(1) + sizeofString("abcdefg") + sizeofString("hijklmn"),
|
||||
},
|
||||
{
|
||||
"Input is object, filter is empty object",
|
||||
"{\"hello\":\"world\"}",
|
||||
"{}",
|
||||
10,
|
||||
DeserializationError::Ok,
|
||||
"{}",
|
||||
sizeofObject(0),
|
||||
},
|
||||
{
|
||||
"Input in an object, but filter wants an array",
|
||||
"{\"hello\":\"world\"}",
|
||||
"[]",
|
||||
10,
|
||||
DeserializationError::Ok,
|
||||
"null",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"Member is a string, but filter wants an array",
|
||||
"{\"example\":\"example\"}",
|
||||
"{\"example\":[true]}",
|
||||
10,
|
||||
DeserializationError::Ok,
|
||||
"{\"example\":null}",
|
||||
sizeofObject(1) + sizeofString("example"),
|
||||
},
|
||||
{
|
||||
"Member is a number, but filter wants an array",
|
||||
"{\"example\":42}",
|
||||
"{\"example\":[true]}",
|
||||
10,
|
||||
DeserializationError::Ok,
|
||||
"{\"example\":null}",
|
||||
sizeofObject(1) + sizeofString("example"),
|
||||
},
|
||||
{
|
||||
"Input is an array, but filter wants an object",
|
||||
"[\"hello\",\"world\"]",
|
||||
"{}",
|
||||
10,
|
||||
DeserializationError::Ok,
|
||||
"null",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"Input is a bool, but filter wants an object",
|
||||
"true",
|
||||
"{}",
|
||||
10,
|
||||
DeserializationError::Ok,
|
||||
"null",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"Input is a string, but filter wants an object",
|
||||
"\"hello\"",
|
||||
"{}",
|
||||
10,
|
||||
DeserializationError::Ok,
|
||||
"null",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"Skip an integer",
|
||||
"{\"an_integer\":666,example:42}",
|
||||
"{\"example\":true}",
|
||||
10,
|
||||
DeserializationError::Ok,
|
||||
"{\"example\":42}",
|
||||
sizeofObject(1) + sizeofString("example"),
|
||||
},
|
||||
{
|
||||
"Skip a float",
|
||||
"{\"a_float\":12.34e-6,example:42}",
|
||||
"{\"example\":true}",
|
||||
10,
|
||||
DeserializationError::Ok,
|
||||
"{\"example\":42}",
|
||||
sizeofObject(1) + sizeofString("example"),
|
||||
},
|
||||
{
|
||||
"Skip false",
|
||||
"{\"a_bool\":false,example:42}",
|
||||
"{\"example\":true}",
|
||||
10,
|
||||
DeserializationError::Ok,
|
||||
"{\"example\":42}",
|
||||
sizeofObject(1) + sizeofString("example"),
|
||||
},
|
||||
{
|
||||
"Skip true",
|
||||
"{\"a_bool\":true,example:42}",
|
||||
"{\"example\":true}",
|
||||
10,
|
||||
DeserializationError::Ok,
|
||||
"{\"example\":42}",
|
||||
sizeofObject(1) + sizeofString("example"),
|
||||
},
|
||||
{
|
||||
"Skip null",
|
||||
"{\"a_bool\":null,example:42}",
|
||||
"{\"example\":true}",
|
||||
10,
|
||||
DeserializationError::Ok,
|
||||
"{\"example\":42}",
|
||||
sizeofObject(1) + sizeofString("example"),
|
||||
},
|
||||
{
|
||||
"Skip a double-quoted string",
|
||||
"{\"a_double_quoted_string\":\"hello\",example:42}",
|
||||
"{\"example\":true}",
|
||||
10,
|
||||
DeserializationError::Ok,
|
||||
"{\"example\":42}",
|
||||
sizeofObject(1) + sizeofString("example"),
|
||||
},
|
||||
{
|
||||
"Skip a single-quoted string",
|
||||
"{\"a_single_quoted_string\":'hello',example:42}",
|
||||
"{\"example\":true}",
|
||||
10,
|
||||
DeserializationError::Ok,
|
||||
"{\"example\":42}",
|
||||
sizeofObject(1) + sizeofString("example"),
|
||||
},
|
||||
{
|
||||
"Skip an empty array",
|
||||
"{\"an_empty_array\":[],example:42}",
|
||||
"{\"example\":true}",
|
||||
10,
|
||||
DeserializationError::Ok,
|
||||
"{\"example\":42}",
|
||||
sizeofObject(1) + sizeofString("example"),
|
||||
},
|
||||
{
|
||||
"Skip an empty array with spaces in it",
|
||||
"{\"an_empty_array\":[\t],example:42}",
|
||||
"{\"example\":true}",
|
||||
10,
|
||||
DeserializationError::Ok,
|
||||
"{\"example\":42}",
|
||||
sizeofObject(1) + sizeofString("example"),
|
||||
},
|
||||
{
|
||||
"Skip an array",
|
||||
"{\"an_array\":[1,2,3],example:42}",
|
||||
"{\"example\":true}",
|
||||
10,
|
||||
DeserializationError::Ok,
|
||||
"{\"example\":42}",
|
||||
sizeofObject(1) + sizeofString("example"),
|
||||
},
|
||||
{
|
||||
"Skip an array with spaces in it",
|
||||
"{\"an_array\": [ 1 , 2 , 3 ] ,example:42}",
|
||||
"{\"example\":true}",
|
||||
10,
|
||||
DeserializationError::Ok,
|
||||
"{\"example\":42}",
|
||||
sizeofObject(1) + sizeofString("example"),
|
||||
},
|
||||
{
|
||||
"Skip an empty nested object",
|
||||
"{\"an_empty_object\":{},example:42}",
|
||||
"{\"example\":true}",
|
||||
10,
|
||||
DeserializationError::Ok,
|
||||
"{\"example\":42}",
|
||||
sizeofObject(1) + sizeofString("example"),
|
||||
},
|
||||
{
|
||||
"Skip an empty nested object with spaces in it",
|
||||
"{\"an_empty_object\":{ },example:42}",
|
||||
"{\"example\":true}",
|
||||
10,
|
||||
DeserializationError::Ok,
|
||||
"{\"example\":42}",
|
||||
sizeofObject(1) + sizeofString("example"),
|
||||
},
|
||||
{
|
||||
"Skip a nested object",
|
||||
"{\"an_object\":{a:1,'b':2,\"c\":3},example:42}",
|
||||
"{\"example\":true}",
|
||||
10,
|
||||
DeserializationError::Ok,
|
||||
"{\"example\":42}",
|
||||
sizeofObject(1) + sizeofString("example"),
|
||||
},
|
||||
{
|
||||
"Skip an object with spaces in it",
|
||||
"{\"an_object\" : { a : 1 , 'b' : 2 , \"c\" : 3 } ,example:42}",
|
||||
"{\"example\":true}",
|
||||
10,
|
||||
DeserializationError::Ok,
|
||||
"{\"example\":42}",
|
||||
sizeofObject(1) + sizeofString("example"),
|
||||
},
|
||||
{
|
||||
"Skip a string in a nested object",
|
||||
"{\"an_integer\": 0,\"example\":{\"type\":\"int\",\"outcome\":42}}",
|
||||
"{\"example\":{\"outcome\":true}}",
|
||||
10,
|
||||
DeserializationError::Ok,
|
||||
"{\"example\":{\"outcome\":42}}",
|
||||
2 * sizeofObject(1) + 2 * sizeofString("example"),
|
||||
},
|
||||
{
|
||||
"wildcard",
|
||||
"{\"example\":{\"type\":\"int\",\"outcome\":42}}",
|
||||
"{\"*\":{\"outcome\":true}}",
|
||||
10,
|
||||
DeserializationError::Ok,
|
||||
"{\"example\":{\"outcome\":42}}",
|
||||
2 * sizeofObject(1) + 2 * sizeofString("example"),
|
||||
},
|
||||
{
|
||||
"exclusion filter (issue #1628)",
|
||||
"{\"example\":1,\"ignored\":2}",
|
||||
"{\"*\":true,\"ignored\":false}",
|
||||
10,
|
||||
DeserializationError::Ok,
|
||||
"{\"example\":1}",
|
||||
sizeofObject(1) + sizeofString("example"),
|
||||
},
|
||||
{
|
||||
"only the first element of array counts",
|
||||
"[1,2,3]",
|
||||
"[true, false]",
|
||||
10,
|
||||
DeserializationError::Ok,
|
||||
"[1,2,3]",
|
||||
sizeofArray(3),
|
||||
},
|
||||
{
|
||||
"only the first element of array counts",
|
||||
"[1,2,3]",
|
||||
"[false, true]",
|
||||
10,
|
||||
DeserializationError::Ok,
|
||||
"[]",
|
||||
sizeofArray(0),
|
||||
},
|
||||
{
|
||||
"filter members of object in array",
|
||||
"[{\"example\":1,\"ignore\":2},{\"example\":3,\"ignore\":4}]",
|
||||
"[{\"example\":true}]",
|
||||
10,
|
||||
DeserializationError::Ok,
|
||||
"[{\"example\":1},{\"example\":3}]",
|
||||
sizeofArray(2) + 2 * sizeofObject(1) + sizeofString("example"),
|
||||
},
|
||||
{
|
||||
"Unclosed single quote in skipped element",
|
||||
"[',2,3]",
|
||||
"[false,true]",
|
||||
10,
|
||||
DeserializationError::IncompleteInput,
|
||||
"[]",
|
||||
sizeofArray(0),
|
||||
},
|
||||
{
|
||||
"Unclosed double quote in skipped element",
|
||||
"[\",2,3]",
|
||||
"[false,true]",
|
||||
10,
|
||||
DeserializationError::IncompleteInput,
|
||||
"[]",
|
||||
sizeofArray(0),
|
||||
},
|
||||
{
|
||||
"Detect errors in skipped value",
|
||||
"[!,2,\\]",
|
||||
"[false]",
|
||||
10,
|
||||
DeserializationError::InvalidInput,
|
||||
"[]",
|
||||
sizeofArray(0),
|
||||
},
|
||||
{
|
||||
"Detect incomplete string event if it's skipped",
|
||||
"\"ABC",
|
||||
"false",
|
||||
10,
|
||||
DeserializationError::IncompleteInput,
|
||||
"null",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"Detect incomplete string event if it's skipped",
|
||||
"'ABC",
|
||||
"false",
|
||||
10,
|
||||
DeserializationError::IncompleteInput,
|
||||
"null",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"Handle escaped quotes",
|
||||
"'A\\'BC'",
|
||||
"false",
|
||||
10,
|
||||
DeserializationError::Ok,
|
||||
"null",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"Handle escaped quotes",
|
||||
"\"A\\\"BC\"",
|
||||
"false",
|
||||
10,
|
||||
DeserializationError::Ok,
|
||||
"null",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"Detect incomplete string in presence of escaped quotes",
|
||||
"'A\\'BC",
|
||||
"false",
|
||||
10,
|
||||
DeserializationError::IncompleteInput,
|
||||
"null",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"Detect incomplete string in presence of escaped quotes",
|
||||
"\"A\\\"BC",
|
||||
"false",
|
||||
10,
|
||||
DeserializationError::IncompleteInput,
|
||||
"null",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"skip empty array",
|
||||
"[]",
|
||||
"false",
|
||||
10,
|
||||
DeserializationError::Ok,
|
||||
"null",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"Skip empty array with spaces",
|
||||
" [ ] ",
|
||||
"false",
|
||||
10,
|
||||
DeserializationError::Ok,
|
||||
"null",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"Bubble up element error even if array is skipped",
|
||||
"[1,'2,3]",
|
||||
"false",
|
||||
10,
|
||||
DeserializationError::IncompleteInput,
|
||||
"null",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"Bubble up member error even if object is skipped",
|
||||
"{'hello':'worl}",
|
||||
"false",
|
||||
10,
|
||||
DeserializationError::IncompleteInput,
|
||||
"null",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"Bubble up colon error even if object is skipped",
|
||||
"{'hello','world'}",
|
||||
"false",
|
||||
10,
|
||||
DeserializationError::InvalidInput,
|
||||
"null",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"Bubble up key error even if object is skipped",
|
||||
"{'hello:1}",
|
||||
"false",
|
||||
10,
|
||||
DeserializationError::IncompleteInput,
|
||||
"null",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"Detect invalid value in skipped object",
|
||||
"{'hello':!}",
|
||||
"false",
|
||||
10,
|
||||
DeserializationError::InvalidInput,
|
||||
"null",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"Ignore invalid value in skipped object",
|
||||
"{'hello':\\}",
|
||||
"false",
|
||||
10,
|
||||
DeserializationError::InvalidInput,
|
||||
"null",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"Check nesting limit even for ignored objects",
|
||||
"{}",
|
||||
"false",
|
||||
0,
|
||||
DeserializationError::TooDeep,
|
||||
"null",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"Check nesting limit even for ignored objects",
|
||||
"{'hello':{}}",
|
||||
"false",
|
||||
1,
|
||||
DeserializationError::TooDeep,
|
||||
"null",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"Check nesting limit even for ignored values in objects",
|
||||
"{'hello':{}}",
|
||||
"{}",
|
||||
1,
|
||||
DeserializationError::TooDeep,
|
||||
"{}",
|
||||
sizeofObject(0),
|
||||
},
|
||||
{
|
||||
"Check nesting limit even for ignored arrays",
|
||||
"[]",
|
||||
"false",
|
||||
0,
|
||||
DeserializationError::TooDeep,
|
||||
"null",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"Check nesting limit even for ignored arrays",
|
||||
"[[]]",
|
||||
"false",
|
||||
1,
|
||||
DeserializationError::TooDeep,
|
||||
"null",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"Check nesting limit even for ignored values in arrays",
|
||||
"[[]]",
|
||||
"[]",
|
||||
1,
|
||||
DeserializationError::TooDeep,
|
||||
"[]",
|
||||
sizeofArray(0),
|
||||
},
|
||||
{
|
||||
"Supports back-slash at the end of skipped string",
|
||||
"\"hell\\",
|
||||
"false",
|
||||
1,
|
||||
DeserializationError::IncompleteInput,
|
||||
"null",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"Invalid comment at after an element in a skipped array",
|
||||
"[1/]",
|
||||
"false",
|
||||
10,
|
||||
DeserializationError::InvalidInput,
|
||||
"null",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"Incomplete comment at after an element in a skipped array",
|
||||
"[1/*]",
|
||||
"false",
|
||||
10,
|
||||
DeserializationError::IncompleteInput,
|
||||
"null",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"Missing comma in a skipped array",
|
||||
"[1 2]",
|
||||
"false",
|
||||
10,
|
||||
DeserializationError::InvalidInput,
|
||||
"null",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"Invalid comment at the beginning of array",
|
||||
"[/1]",
|
||||
"[false]",
|
||||
10,
|
||||
DeserializationError::InvalidInput,
|
||||
"[]",
|
||||
sizeofArray(0),
|
||||
},
|
||||
{
|
||||
"Incomplete comment at the begining of an array",
|
||||
"[/*]",
|
||||
"[false]",
|
||||
10,
|
||||
DeserializationError::IncompleteInput,
|
||||
"[]",
|
||||
sizeofArray(0),
|
||||
},
|
||||
{
|
||||
"Invalid comment before key",
|
||||
"{/1:2}",
|
||||
"{}",
|
||||
10,
|
||||
DeserializationError::InvalidInput,
|
||||
"{}",
|
||||
sizeofObject(0),
|
||||
},
|
||||
{
|
||||
"Incomplete comment before key",
|
||||
"{/*:2}",
|
||||
"{}",
|
||||
10,
|
||||
DeserializationError::IncompleteInput,
|
||||
"{}",
|
||||
sizeofObject(0),
|
||||
},
|
||||
{
|
||||
"Invalid comment after key",
|
||||
"{\"example\"/1:2}",
|
||||
"{}",
|
||||
10,
|
||||
DeserializationError::InvalidInput,
|
||||
"{}",
|
||||
sizeofObject(0),
|
||||
},
|
||||
{
|
||||
"Incomplete comment after key",
|
||||
"{\"example\"/*:2}",
|
||||
"{}",
|
||||
10,
|
||||
DeserializationError::IncompleteInput,
|
||||
"{}",
|
||||
sizeofObject(0),
|
||||
},
|
||||
{
|
||||
"Invalid comment after colon",
|
||||
"{\"example\":/12}",
|
||||
"{}",
|
||||
10,
|
||||
DeserializationError::InvalidInput,
|
||||
"{}",
|
||||
sizeofObject(0),
|
||||
},
|
||||
{
|
||||
"Incomplete comment after colon",
|
||||
"{\"example\":/*2}",
|
||||
"{}",
|
||||
10,
|
||||
DeserializationError::IncompleteInput,
|
||||
"{}",
|
||||
sizeofObject(0),
|
||||
},
|
||||
{
|
||||
"Comment next to an integer",
|
||||
"{\"ignore\":1//,\"example\":2\n}",
|
||||
"{\"example\":true}",
|
||||
10,
|
||||
DeserializationError::Ok,
|
||||
"{}",
|
||||
sizeofObject(0),
|
||||
},
|
||||
{
|
||||
"Invalid comment after opening brace of a skipped object",
|
||||
"{/1:2}",
|
||||
"false",
|
||||
10,
|
||||
DeserializationError::InvalidInput,
|
||||
"null",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"Incomplete after opening brace of a skipped object",
|
||||
"{/*:2}",
|
||||
"false",
|
||||
10,
|
||||
DeserializationError::IncompleteInput,
|
||||
"null",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"Invalid comment after key of a skipped object",
|
||||
"{\"example\"/:2}",
|
||||
"false",
|
||||
10,
|
||||
DeserializationError::InvalidInput,
|
||||
"null",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"Incomplete comment after key of a skipped object",
|
||||
"{\"example\"/*:2}",
|
||||
"false",
|
||||
10,
|
||||
DeserializationError::IncompleteInput,
|
||||
"null",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"Invalid comment after value in a skipped object",
|
||||
"{\"example\":2/}",
|
||||
"false",
|
||||
10,
|
||||
DeserializationError::InvalidInput,
|
||||
"null",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"Incomplete comment after value of a skipped object",
|
||||
"{\"example\":2/*}",
|
||||
"false",
|
||||
10,
|
||||
DeserializationError::IncompleteInput,
|
||||
"null",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"Incomplete comment after comma in skipped object",
|
||||
"{\"example\":2,/*}",
|
||||
"false",
|
||||
10,
|
||||
DeserializationError::IncompleteInput,
|
||||
"null",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"NUL character in key",
|
||||
"{\"x\":0,\"x\\u0000a\":1,\"x\\u0000b\":2}",
|
||||
"{\"x\\u0000a\":true}",
|
||||
10,
|
||||
DeserializationError::Ok,
|
||||
"{\"x\\u0000a\":1}",
|
||||
sizeofObject(1) + sizeofString("x?a"),
|
||||
},
|
||||
};
|
||||
|
||||
for (auto& tc : testCases) {
|
||||
SECTION(tc.description) {
|
||||
SpyingAllocator spy;
|
||||
JsonDocument filter;
|
||||
JsonDocument doc(&spy);
|
||||
|
||||
REQUIRE(deserializeJson(filter, tc.filter) == DeserializationError::Ok);
|
||||
|
||||
CHECK(deserializeJson(
|
||||
doc, tc.input, DeserializationOption::Filter(filter),
|
||||
DeserializationOption::NestingLimit(tc.nestingLimit)) ==
|
||||
tc.error);
|
||||
|
||||
CHECK(doc.as<std::string>() == tc.output);
|
||||
|
||||
doc.shrinkToFit();
|
||||
CHECK(spy.allocatedBytes() == tc.memoryUsage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Overloads") {
|
||||
JsonDocument doc;
|
||||
JsonDocument filter;
|
||||
|
||||
using namespace DeserializationOption;
|
||||
|
||||
// deserializeJson(..., Filter)
|
||||
|
||||
SECTION("const char*, Filter") {
|
||||
deserializeJson(doc, "{}", Filter(filter));
|
||||
}
|
||||
|
||||
SECTION("const char*, size_t, Filter") {
|
||||
deserializeJson(doc, "{}", 2, Filter(filter));
|
||||
}
|
||||
|
||||
SECTION("const std::string&, Filter") {
|
||||
deserializeJson(doc, "{}"_s, Filter(filter));
|
||||
}
|
||||
|
||||
SECTION("std::istream&, Filter") {
|
||||
std::stringstream s("{}");
|
||||
deserializeJson(doc, s, Filter(filter));
|
||||
}
|
||||
|
||||
#ifdef HAS_VARIABLE_LENGTH_ARRAY
|
||||
SECTION("char[n], Filter") {
|
||||
size_t i = 4;
|
||||
char vla[i];
|
||||
strcpy(vla, "{}");
|
||||
deserializeJson(doc, vla, Filter(filter));
|
||||
}
|
||||
#endif
|
||||
|
||||
// deserializeJson(..., Filter, NestingLimit)
|
||||
|
||||
SECTION("const char*, Filter, NestingLimit") {
|
||||
deserializeJson(doc, "{}", Filter(filter), NestingLimit(5));
|
||||
}
|
||||
|
||||
SECTION("const char*, size_t, Filter, NestingLimit") {
|
||||
deserializeJson(doc, "{}", 2, Filter(filter), NestingLimit(5));
|
||||
}
|
||||
|
||||
SECTION("const std::string&, Filter, NestingLimit") {
|
||||
deserializeJson(doc, "{}"_s, Filter(filter), NestingLimit(5));
|
||||
}
|
||||
|
||||
SECTION("std::istream&, Filter, NestingLimit") {
|
||||
std::stringstream s("{}");
|
||||
deserializeJson(doc, s, Filter(filter), NestingLimit(5));
|
||||
}
|
||||
|
||||
#ifdef HAS_VARIABLE_LENGTH_ARRAY
|
||||
SECTION("char[n], Filter, NestingLimit") {
|
||||
size_t i = 4;
|
||||
char vla[i];
|
||||
strcpy(vla, "{}");
|
||||
deserializeJson(doc, vla, Filter(filter), NestingLimit(5));
|
||||
}
|
||||
#endif
|
||||
|
||||
// deserializeJson(..., NestingLimit, Filter)
|
||||
|
||||
SECTION("const char*, NestingLimit, Filter") {
|
||||
deserializeJson(doc, "{}", NestingLimit(5), Filter(filter));
|
||||
}
|
||||
|
||||
SECTION("const char*, size_t, NestingLimit, Filter") {
|
||||
deserializeJson(doc, "{}", 2, NestingLimit(5), Filter(filter));
|
||||
}
|
||||
|
||||
SECTION("const std::string&, NestingLimit, Filter") {
|
||||
deserializeJson(doc, "{}"_s, NestingLimit(5), Filter(filter));
|
||||
}
|
||||
|
||||
SECTION("std::istream&, NestingLimit, Filter") {
|
||||
std::stringstream s("{}");
|
||||
deserializeJson(doc, s, NestingLimit(5), Filter(filter));
|
||||
}
|
||||
|
||||
#ifdef HAS_VARIABLE_LENGTH_ARRAY
|
||||
SECTION("char[n], NestingLimit, Filter") {
|
||||
size_t i = 4;
|
||||
char vla[i];
|
||||
strcpy(vla, "{}");
|
||||
deserializeJson(doc, vla, NestingLimit(5), Filter(filter));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST_CASE("shrink filter") {
|
||||
JsonDocument doc;
|
||||
SpyingAllocator spy;
|
||||
JsonDocument filter(&spy);
|
||||
filter["a"] = true;
|
||||
spy.clearLog();
|
||||
|
||||
deserializeJson(doc, "{}", DeserializationOption::Filter(filter));
|
||||
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Reallocate(sizeofPool(), sizeofObject(1)),
|
||||
});
|
||||
}
|
||||
232
ArduinoJson/extras/tests/JsonDeserializer/input_types.cpp
Normal file
232
ArduinoJson/extras/tests/JsonDeserializer/input_types.cpp
Normal file
@@ -0,0 +1,232 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2025, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
|
||||
#include <catch.hpp>
|
||||
#include <sstream>
|
||||
|
||||
#include "Allocators.hpp"
|
||||
#include "CustomReader.hpp"
|
||||
#include "Literals.hpp"
|
||||
|
||||
using ArduinoJson::detail::sizeofObject;
|
||||
|
||||
TEST_CASE("deserializeJson(char*)") {
|
||||
SpyingAllocator spy;
|
||||
JsonDocument doc(&spy);
|
||||
|
||||
char input[] = "{\"hello\":\"world\"}";
|
||||
|
||||
DeserializationError err = deserializeJson(doc, input);
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
|
||||
REQUIRE(spy.log() ==
|
||||
AllocatorLog{
|
||||
Allocate(sizeofStringBuffer()),
|
||||
Allocate(sizeofPool()),
|
||||
Reallocate(sizeofStringBuffer(), sizeofString("hello")),
|
||||
Allocate(sizeofStringBuffer()),
|
||||
Reallocate(sizeofStringBuffer(), sizeofString("world")),
|
||||
Reallocate(sizeofPool(), sizeofObject(1)),
|
||||
});
|
||||
}
|
||||
|
||||
TEST_CASE("deserializeJson(unsigned char*, unsigned int)") { // issue #1897
|
||||
JsonDocument doc;
|
||||
|
||||
unsigned char input[] = "{\"hello\":\"world\"}";
|
||||
unsigned char* input_ptr = input;
|
||||
unsigned int size = sizeof(input);
|
||||
|
||||
DeserializationError err = deserializeJson(doc, input_ptr, size);
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
}
|
||||
|
||||
TEST_CASE("deserializeJson(uint8_t*, size_t)") { // issue #1898
|
||||
JsonDocument doc;
|
||||
|
||||
uint8_t input[] = "{\"hello\":\"world\"}";
|
||||
uint8_t* input_ptr = input;
|
||||
size_t size = sizeof(input);
|
||||
|
||||
DeserializationError err = deserializeJson(doc, input_ptr, size);
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
}
|
||||
|
||||
TEST_CASE("deserializeJson(const std::string&)") {
|
||||
JsonDocument doc;
|
||||
|
||||
SECTION("should accept const string") {
|
||||
const std::string input("[42]");
|
||||
|
||||
DeserializationError err = deserializeJson(doc, input);
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
}
|
||||
|
||||
SECTION("should accept temporary string") {
|
||||
DeserializationError err = deserializeJson(doc, "[42]"_s);
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
}
|
||||
|
||||
SECTION("should duplicate content") {
|
||||
std::string input("[\"hello\"]");
|
||||
|
||||
DeserializationError err = deserializeJson(doc, input);
|
||||
input[2] = 'X'; // alter the string tomake sure we made a copy
|
||||
|
||||
JsonArray array = doc.as<JsonArray>();
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE("hello"_s == array[0]);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("deserializeJson(std::istream&)") {
|
||||
JsonDocument doc;
|
||||
|
||||
SECTION("array") {
|
||||
std::istringstream json(" [ 42 ] ");
|
||||
|
||||
DeserializationError err = deserializeJson(doc, json);
|
||||
JsonArray arr = doc.as<JsonArray>();
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(1 == arr.size());
|
||||
REQUIRE(42 == arr[0]);
|
||||
}
|
||||
|
||||
SECTION("object") {
|
||||
std::istringstream json(" { hello : 'world' }");
|
||||
|
||||
DeserializationError err = deserializeJson(doc, json);
|
||||
JsonObject obj = doc.as<JsonObject>();
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(1 == obj.size());
|
||||
REQUIRE("world"_s == obj["hello"]);
|
||||
}
|
||||
|
||||
SECTION("Should not read after the closing brace of an empty object") {
|
||||
std::istringstream json("{}123");
|
||||
|
||||
deserializeJson(doc, json);
|
||||
|
||||
REQUIRE('1' == char(json.get()));
|
||||
}
|
||||
|
||||
SECTION("Should not read after the closing brace") {
|
||||
std::istringstream json("{\"hello\":\"world\"}123");
|
||||
|
||||
deserializeJson(doc, json);
|
||||
|
||||
REQUIRE('1' == char(json.get()));
|
||||
}
|
||||
|
||||
SECTION("Should not read after the closing bracket of an empty array") {
|
||||
std::istringstream json("[]123");
|
||||
|
||||
deserializeJson(doc, json);
|
||||
|
||||
REQUIRE('1' == char(json.get()));
|
||||
}
|
||||
|
||||
SECTION("Should not read after the closing bracket") {
|
||||
std::istringstream json("[\"hello\",\"world\"]123");
|
||||
|
||||
deserializeJson(doc, json);
|
||||
|
||||
REQUIRE('1' == char(json.get()));
|
||||
}
|
||||
|
||||
SECTION("Should not read after the closing quote") {
|
||||
std::istringstream json("\"hello\"123");
|
||||
|
||||
deserializeJson(doc, json);
|
||||
|
||||
REQUIRE('1' == char(json.get()));
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAS_VARIABLE_LENGTH_ARRAY
|
||||
TEST_CASE("deserializeJson(VLA)") {
|
||||
size_t i = 9;
|
||||
char vla[i];
|
||||
strcpy(vla, "{\"a\":42}");
|
||||
|
||||
JsonDocument doc;
|
||||
DeserializationError err = deserializeJson(doc, vla);
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST_CASE("deserializeJson(CustomReader)") {
|
||||
JsonDocument doc;
|
||||
CustomReader reader("[4,2]");
|
||||
DeserializationError err = deserializeJson(doc, reader);
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.size() == 2);
|
||||
REQUIRE(doc[0] == 4);
|
||||
REQUIRE(doc[1] == 2);
|
||||
}
|
||||
|
||||
TEST_CASE("deserializeJson(JsonDocument&, MemberProxy)") {
|
||||
JsonDocument doc1;
|
||||
doc1["payload"] = "[4,2]";
|
||||
|
||||
JsonDocument doc2;
|
||||
DeserializationError err = deserializeJson(doc2, doc1["payload"]);
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc2.size() == 2);
|
||||
REQUIRE(doc2[0] == 4);
|
||||
REQUIRE(doc2[1] == 2);
|
||||
}
|
||||
|
||||
TEST_CASE("deserializeJson(JsonDocument&, JsonVariant)") {
|
||||
JsonDocument doc1;
|
||||
doc1["payload"] = "[4,2]";
|
||||
|
||||
JsonDocument doc2;
|
||||
DeserializationError err =
|
||||
deserializeJson(doc2, doc1["payload"].as<JsonVariant>());
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc2.size() == 2);
|
||||
REQUIRE(doc2[0] == 4);
|
||||
REQUIRE(doc2[1] == 2);
|
||||
}
|
||||
|
||||
TEST_CASE("deserializeJson(JsonDocument&, JsonVariantConst)") {
|
||||
JsonDocument doc1;
|
||||
doc1["payload"] = "[4,2]";
|
||||
|
||||
JsonDocument doc2;
|
||||
DeserializationError err =
|
||||
deserializeJson(doc2, doc1["payload"].as<JsonVariantConst>());
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc2.size() == 2);
|
||||
REQUIRE(doc2[0] == 4);
|
||||
REQUIRE(doc2[1] == 2);
|
||||
}
|
||||
|
||||
TEST_CASE("deserializeJson(JsonDocument&, ElementProxy)") {
|
||||
JsonDocument doc1;
|
||||
doc1[0] = "[4,2]";
|
||||
|
||||
JsonDocument doc2;
|
||||
DeserializationError err = deserializeJson(doc2, doc1[0]);
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc2.size() == 2);
|
||||
REQUIRE(doc2[0] == 4);
|
||||
REQUIRE(doc2[1] == 2);
|
||||
}
|
||||
49
ArduinoJson/extras/tests/JsonDeserializer/misc.cpp
Normal file
49
ArduinoJson/extras/tests/JsonDeserializer/misc.cpp
Normal file
@@ -0,0 +1,49 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2025, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <catch.hpp>
|
||||
|
||||
#include "Allocators.hpp"
|
||||
|
||||
using ArduinoJson::detail::sizeofArray;
|
||||
|
||||
TEST_CASE("deserializeJson() misc cases") {
|
||||
SpyingAllocator spy;
|
||||
JsonDocument doc(&spy);
|
||||
|
||||
SECTION("null") {
|
||||
DeserializationError err = deserializeJson(doc, "null");
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.is<float>() == false);
|
||||
}
|
||||
|
||||
SECTION("true") {
|
||||
DeserializationError err = deserializeJson(doc, "true");
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.is<bool>());
|
||||
REQUIRE(doc.as<bool>() == true);
|
||||
}
|
||||
|
||||
SECTION("false") {
|
||||
DeserializationError err = deserializeJson(doc, "false");
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.is<bool>());
|
||||
REQUIRE(doc.as<bool>() == false);
|
||||
}
|
||||
|
||||
SECTION("Should clear the JsonVariant") {
|
||||
deserializeJson(doc, "[1,2,3]");
|
||||
spy.clearLog();
|
||||
|
||||
deserializeJson(doc, "{}");
|
||||
|
||||
REQUIRE(doc.is<JsonObject>());
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Deallocate(sizeofArray(3)),
|
||||
});
|
||||
}
|
||||
}
|
||||
105
ArduinoJson/extras/tests/JsonDeserializer/nestingLimit.cpp
Normal file
105
ArduinoJson/extras/tests/JsonDeserializer/nestingLimit.cpp
Normal file
@@ -0,0 +1,105 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2025, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <catch.hpp>
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include "Literals.hpp"
|
||||
|
||||
#define SHOULD_WORK(expression) REQUIRE(DeserializationError::Ok == expression);
|
||||
#define SHOULD_FAIL(expression) \
|
||||
REQUIRE(DeserializationError::TooDeep == expression);
|
||||
|
||||
TEST_CASE("JsonDeserializer nesting") {
|
||||
JsonDocument doc;
|
||||
|
||||
SECTION("Input = const char*") {
|
||||
SECTION("limit = 0") {
|
||||
DeserializationOption::NestingLimit nesting(0);
|
||||
SHOULD_WORK(deserializeJson(doc, "\"toto\"", nesting));
|
||||
SHOULD_WORK(deserializeJson(doc, "123", nesting));
|
||||
SHOULD_WORK(deserializeJson(doc, "true", nesting));
|
||||
SHOULD_FAIL(deserializeJson(doc, "[]", nesting));
|
||||
SHOULD_FAIL(deserializeJson(doc, "{}", nesting));
|
||||
SHOULD_FAIL(deserializeJson(doc, "[\"toto\"]", nesting));
|
||||
SHOULD_FAIL(deserializeJson(doc, "{\"toto\":1}", nesting));
|
||||
}
|
||||
|
||||
SECTION("limit = 1") {
|
||||
DeserializationOption::NestingLimit nesting(1);
|
||||
SHOULD_WORK(deserializeJson(doc, "[\"toto\"]", nesting));
|
||||
SHOULD_WORK(deserializeJson(doc, "{\"toto\":1}", nesting));
|
||||
SHOULD_FAIL(deserializeJson(doc, "{\"toto\":{}}", nesting));
|
||||
SHOULD_FAIL(deserializeJson(doc, "{\"toto\":[]}", nesting));
|
||||
SHOULD_FAIL(deserializeJson(doc, "[[\"toto\"]]", nesting));
|
||||
SHOULD_FAIL(deserializeJson(doc, "[{\"toto\":1}]", nesting));
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("char* and size_t") {
|
||||
SECTION("limit = 0") {
|
||||
DeserializationOption::NestingLimit nesting(0);
|
||||
SHOULD_WORK(deserializeJson(doc, "\"toto\"", 6, nesting));
|
||||
SHOULD_WORK(deserializeJson(doc, "123", 3, nesting));
|
||||
SHOULD_WORK(deserializeJson(doc, "true", 4, nesting));
|
||||
SHOULD_FAIL(deserializeJson(doc, "[]", 2, nesting));
|
||||
SHOULD_FAIL(deserializeJson(doc, "{}", 2, nesting));
|
||||
SHOULD_FAIL(deserializeJson(doc, "[\"toto\"]", 8, nesting));
|
||||
SHOULD_FAIL(deserializeJson(doc, "{\"toto\":1}", 10, nesting));
|
||||
}
|
||||
|
||||
SECTION("limit = 1") {
|
||||
DeserializationOption::NestingLimit nesting(1);
|
||||
SHOULD_WORK(deserializeJson(doc, "[\"toto\"]", 8, nesting));
|
||||
SHOULD_WORK(deserializeJson(doc, "{\"toto\":1}", 10, nesting));
|
||||
SHOULD_FAIL(deserializeJson(doc, "{\"toto\":{}}", 11, nesting));
|
||||
SHOULD_FAIL(deserializeJson(doc, "{\"toto\":[]}", 11, nesting));
|
||||
SHOULD_FAIL(deserializeJson(doc, "[[\"toto\"]]", 10, nesting));
|
||||
SHOULD_FAIL(deserializeJson(doc, "[{\"toto\":1}]", 12, nesting));
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Input = std::string") {
|
||||
SECTION("limit = 0") {
|
||||
DeserializationOption::NestingLimit nesting(0);
|
||||
SHOULD_WORK(deserializeJson(doc, "\"toto\""_s, nesting));
|
||||
SHOULD_WORK(deserializeJson(doc, "123"_s, nesting));
|
||||
SHOULD_WORK(deserializeJson(doc, "true"_s, nesting));
|
||||
SHOULD_FAIL(deserializeJson(doc, "[]"_s, nesting));
|
||||
SHOULD_FAIL(deserializeJson(doc, "{}"_s, nesting));
|
||||
SHOULD_FAIL(deserializeJson(doc, "[\"toto\"]"_s, nesting));
|
||||
SHOULD_FAIL(deserializeJson(doc, "{\"toto\":1}"_s, nesting));
|
||||
}
|
||||
|
||||
SECTION("limit = 1") {
|
||||
DeserializationOption::NestingLimit nesting(1);
|
||||
SHOULD_WORK(deserializeJson(doc, "[\"toto\"]"_s, nesting));
|
||||
SHOULD_WORK(deserializeJson(doc, "{\"toto\":1}"_s, nesting));
|
||||
SHOULD_FAIL(deserializeJson(doc, "{\"toto\":{}}"_s, nesting));
|
||||
SHOULD_FAIL(deserializeJson(doc, "{\"toto\":[]}"_s, nesting));
|
||||
SHOULD_FAIL(deserializeJson(doc, "[[\"toto\"]]"_s, nesting));
|
||||
SHOULD_FAIL(deserializeJson(doc, "[{\"toto\":1}]"_s, nesting));
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Input = std::istream") {
|
||||
SECTION("limit = 0") {
|
||||
DeserializationOption::NestingLimit nesting(0);
|
||||
std::istringstream good("true");
|
||||
std::istringstream bad("[]");
|
||||
SHOULD_WORK(deserializeJson(doc, good, nesting));
|
||||
SHOULD_FAIL(deserializeJson(doc, bad, nesting));
|
||||
}
|
||||
|
||||
SECTION("limit = 1") {
|
||||
DeserializationOption::NestingLimit nesting(1);
|
||||
std::istringstream good("[\"toto\"]");
|
||||
std::istringstream bad("{\"toto\":{}}");
|
||||
SHOULD_WORK(deserializeJson(doc, good, nesting));
|
||||
SHOULD_FAIL(deserializeJson(doc, bad, nesting));
|
||||
}
|
||||
}
|
||||
}
|
||||
133
ArduinoJson/extras/tests/JsonDeserializer/number.cpp
Normal file
133
ArduinoJson/extras/tests/JsonDeserializer/number.cpp
Normal file
@@ -0,0 +1,133 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2025, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#define ARDUINOJSON_USE_LONG_LONG 0
|
||||
#define ARDUINOJSON_ENABLE_NAN 1
|
||||
#define ARDUINOJSON_ENABLE_INFINITY 1
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <limits.h>
|
||||
#include <catch.hpp>
|
||||
|
||||
namespace my {
|
||||
using ArduinoJson::detail::isinf;
|
||||
using ArduinoJson::detail::isnan;
|
||||
} // namespace my
|
||||
|
||||
TEST_CASE("deserialize an integer") {
|
||||
JsonDocument doc;
|
||||
|
||||
SECTION("Integer") {
|
||||
SECTION("0") {
|
||||
DeserializationError err = deserializeJson(doc, "0");
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.is<int>() == true);
|
||||
REQUIRE(doc.as<int>() == 0);
|
||||
REQUIRE(doc.as<std::string>() == "0"); // issue #808
|
||||
}
|
||||
|
||||
SECTION("Negative") {
|
||||
DeserializationError err = deserializeJson(doc, "-42");
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.is<int>());
|
||||
REQUIRE_FALSE(doc.is<bool>());
|
||||
REQUIRE(doc.as<int>() == -42);
|
||||
}
|
||||
|
||||
#if LONG_MAX == 2147483647
|
||||
SECTION("LONG_MAX") {
|
||||
DeserializationError err = deserializeJson(doc, "2147483647");
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.is<long>() == true);
|
||||
REQUIRE(doc.as<long>() == LONG_MAX);
|
||||
}
|
||||
|
||||
SECTION("LONG_MAX + 1") {
|
||||
DeserializationError err = deserializeJson(doc, "2147483648");
|
||||
|
||||
CAPTURE(LONG_MIN);
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.is<long>() == false);
|
||||
REQUIRE(doc.is<float>() == true);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if LONG_MIN == -2147483648
|
||||
SECTION("LONG_MIN") {
|
||||
DeserializationError err = deserializeJson(doc, "-2147483648");
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.is<long>() == true);
|
||||
REQUIRE(doc.as<long>() == LONG_MIN);
|
||||
}
|
||||
|
||||
SECTION("LONG_MIN - 1") {
|
||||
DeserializationError err = deserializeJson(doc, "-2147483649");
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.is<long>() == false);
|
||||
REQUIRE(doc.is<float>() == true);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ULONG_MAX == 4294967295
|
||||
SECTION("ULONG_MAX") {
|
||||
DeserializationError err = deserializeJson(doc, "4294967295");
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.is<unsigned long>() == true);
|
||||
REQUIRE(doc.as<unsigned long>() == ULONG_MAX);
|
||||
REQUIRE(doc.is<long>() == false);
|
||||
}
|
||||
|
||||
SECTION("ULONG_MAX + 1") {
|
||||
DeserializationError err = deserializeJson(doc, "4294967296");
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.is<unsigned long>() == false);
|
||||
REQUIRE(doc.is<float>() == true);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
SECTION("Floats") {
|
||||
SECTION("Double") {
|
||||
DeserializationError err = deserializeJson(doc, "-1.23e+4");
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE_FALSE(doc.is<int>());
|
||||
REQUIRE(doc.is<double>());
|
||||
REQUIRE(doc.as<double>() == Approx(-1.23e+4));
|
||||
}
|
||||
|
||||
SECTION("NaN") {
|
||||
DeserializationError err = deserializeJson(doc, "NaN");
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.is<float>() == true);
|
||||
REQUIRE(my::isnan(doc.as<float>()));
|
||||
}
|
||||
|
||||
SECTION("Infinity") {
|
||||
DeserializationError err = deserializeJson(doc, "Infinity");
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.is<float>() == true);
|
||||
REQUIRE(my::isinf(doc.as<float>()));
|
||||
}
|
||||
|
||||
SECTION("+Infinity") {
|
||||
DeserializationError err = deserializeJson(doc, "+Infinity");
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.is<float>() == true);
|
||||
REQUIRE(my::isinf(doc.as<float>()));
|
||||
}
|
||||
|
||||
SECTION("-Infinity") {
|
||||
DeserializationError err = deserializeJson(doc, "-Infinity");
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.is<float>() == true);
|
||||
REQUIRE(my::isinf(doc.as<float>()));
|
||||
}
|
||||
}
|
||||
}
|
||||
400
ArduinoJson/extras/tests/JsonDeserializer/object.cpp
Normal file
400
ArduinoJson/extras/tests/JsonDeserializer/object.cpp
Normal file
@@ -0,0 +1,400 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2025, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <catch.hpp>
|
||||
|
||||
#include "Allocators.hpp"
|
||||
#include "Literals.hpp"
|
||||
|
||||
using ArduinoJson::detail::sizeofObject;
|
||||
|
||||
TEST_CASE("deserialize JSON object") {
|
||||
SpyingAllocator spy;
|
||||
JsonDocument doc(&spy);
|
||||
|
||||
SECTION("An empty object") {
|
||||
DeserializationError err = deserializeJson(doc, "{}");
|
||||
JsonObject obj = doc.as<JsonObject>();
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.is<JsonObject>());
|
||||
REQUIRE(obj.size() == 0);
|
||||
}
|
||||
|
||||
SECTION("Quotes") {
|
||||
SECTION("Double quotes") {
|
||||
DeserializationError err = deserializeJson(doc, "{\"key\":\"value\"}");
|
||||
JsonObject obj = doc.as<JsonObject>();
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.is<JsonObject>());
|
||||
REQUIRE(obj.size() == 1);
|
||||
REQUIRE(obj["key"] == "value");
|
||||
}
|
||||
|
||||
SECTION("Single quotes") {
|
||||
DeserializationError err = deserializeJson(doc, "{'key':'value'}");
|
||||
JsonObject obj = doc.as<JsonObject>();
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.is<JsonObject>());
|
||||
REQUIRE(obj.size() == 1);
|
||||
REQUIRE(obj["key"] == "value");
|
||||
}
|
||||
|
||||
SECTION("No quotes") {
|
||||
DeserializationError err = deserializeJson(doc, "{key:'value'}");
|
||||
JsonObject obj = doc.as<JsonObject>();
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.is<JsonObject>());
|
||||
REQUIRE(obj.size() == 1);
|
||||
REQUIRE(obj["key"] == "value");
|
||||
}
|
||||
|
||||
SECTION("No quotes, allow underscore in key") {
|
||||
DeserializationError err = deserializeJson(doc, "{_k_e_y_:42}");
|
||||
JsonObject obj = doc.as<JsonObject>();
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.is<JsonObject>());
|
||||
REQUIRE(obj.size() == 1);
|
||||
REQUIRE(obj["_k_e_y_"] == 42);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Spaces") {
|
||||
SECTION("Before the key") {
|
||||
DeserializationError err = deserializeJson(doc, "{ \"key\":\"value\"}");
|
||||
JsonObject obj = doc.as<JsonObject>();
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.is<JsonObject>());
|
||||
REQUIRE(obj.size() == 1);
|
||||
REQUIRE(obj["key"] == "value");
|
||||
}
|
||||
|
||||
SECTION("After the key") {
|
||||
DeserializationError err = deserializeJson(doc, "{\"key\" :\"value\"}");
|
||||
JsonObject obj = doc.as<JsonObject>();
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.is<JsonObject>());
|
||||
REQUIRE(obj.size() == 1);
|
||||
REQUIRE(obj["key"] == "value");
|
||||
}
|
||||
|
||||
SECTION("Before the value") {
|
||||
DeserializationError err = deserializeJson(doc, "{\"key\": \"value\"}");
|
||||
JsonObject obj = doc.as<JsonObject>();
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.is<JsonObject>());
|
||||
REQUIRE(obj.size() == 1);
|
||||
REQUIRE(obj["key"] == "value");
|
||||
}
|
||||
|
||||
SECTION("After the value") {
|
||||
DeserializationError err = deserializeJson(doc, "{\"key\":\"value\" }");
|
||||
JsonObject obj = doc.as<JsonObject>();
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.is<JsonObject>());
|
||||
REQUIRE(obj.size() == 1);
|
||||
REQUIRE(obj["key"] == "value");
|
||||
}
|
||||
|
||||
SECTION("Before the comma") {
|
||||
DeserializationError err =
|
||||
deserializeJson(doc, "{\"key1\":\"value1\" ,\"key2\":\"value2\"}");
|
||||
JsonObject obj = doc.as<JsonObject>();
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.is<JsonObject>());
|
||||
REQUIRE(obj.size() == 2);
|
||||
REQUIRE(obj["key1"] == "value1");
|
||||
REQUIRE(obj["key2"] == "value2");
|
||||
}
|
||||
|
||||
SECTION("After the comma") {
|
||||
DeserializationError err =
|
||||
deserializeJson(doc, "{\"key1\":\"value1\", \"key2\":\"value2\"}");
|
||||
JsonObject obj = doc.as<JsonObject>();
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.is<JsonObject>());
|
||||
REQUIRE(obj.size() == 2);
|
||||
REQUIRE(obj["key1"] == "value1");
|
||||
REQUIRE(obj["key2"] == "value2");
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Values types") {
|
||||
SECTION("String") {
|
||||
DeserializationError err =
|
||||
deserializeJson(doc, "{\"key1\":\"value1\",\"key2\":\"value2\"}");
|
||||
JsonObject obj = doc.as<JsonObject>();
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.is<JsonObject>());
|
||||
REQUIRE(obj.size() == 2);
|
||||
REQUIRE(obj["key1"] == "value1");
|
||||
REQUIRE(obj["key2"] == "value2");
|
||||
}
|
||||
|
||||
SECTION("Integer") {
|
||||
DeserializationError err =
|
||||
deserializeJson(doc, "{\"key1\":42,\"key2\":-42}");
|
||||
JsonObject obj = doc.as<JsonObject>();
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.is<JsonObject>());
|
||||
REQUIRE(obj.size() == 2);
|
||||
REQUIRE(obj["key1"] == 42);
|
||||
REQUIRE(obj["key2"] == -42);
|
||||
}
|
||||
|
||||
SECTION("Float") {
|
||||
DeserializationError err =
|
||||
deserializeJson(doc, "{\"key1\":12.345,\"key2\":-7E3}");
|
||||
JsonObject obj = doc.as<JsonObject>();
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.is<JsonObject>());
|
||||
REQUIRE(obj.size() == 2);
|
||||
REQUIRE(obj["key1"].as<float>() == Approx(12.345f));
|
||||
REQUIRE(obj["key2"] == -7E3f);
|
||||
}
|
||||
|
||||
SECTION("Double") {
|
||||
DeserializationError err =
|
||||
deserializeJson(doc, "{\"key1\":12.3456789,\"key2\":-7E89}");
|
||||
JsonObject obj = doc.as<JsonObject>();
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.is<JsonObject>());
|
||||
REQUIRE(obj.size() == 2);
|
||||
REQUIRE(obj["key1"].as<double>() == Approx(12.3456789));
|
||||
REQUIRE(obj["key2"] == -7E89);
|
||||
}
|
||||
|
||||
SECTION("Booleans") {
|
||||
DeserializationError err =
|
||||
deserializeJson(doc, "{\"key1\":true,\"key2\":false}");
|
||||
JsonObject obj = doc.as<JsonObject>();
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.is<JsonObject>());
|
||||
REQUIRE(obj.size() == 2);
|
||||
REQUIRE(obj["key1"] == true);
|
||||
REQUIRE(obj["key2"] == false);
|
||||
}
|
||||
|
||||
SECTION("Null") {
|
||||
DeserializationError err =
|
||||
deserializeJson(doc, "{\"key1\":null,\"key2\":null}");
|
||||
JsonObject obj = doc.as<JsonObject>();
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.is<JsonObject>());
|
||||
REQUIRE(obj.size() == 2);
|
||||
REQUIRE(obj["key1"].as<const char*>() == 0);
|
||||
REQUIRE(obj["key2"].as<const char*>() == 0);
|
||||
}
|
||||
|
||||
SECTION("Array") {
|
||||
char jsonString[] = " { \"ab\" : [ 1 , 2 ] , \"cd\" : [ 3 , 4 ] } ";
|
||||
|
||||
DeserializationError err = deserializeJson(doc, jsonString);
|
||||
JsonObject obj = doc.as<JsonObject>();
|
||||
|
||||
JsonArray array1 = obj["ab"];
|
||||
const JsonArray array2 = obj["cd"];
|
||||
JsonArray array3 = obj["ef"];
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
|
||||
REQUIRE(array1.isNull() == false);
|
||||
REQUIRE(array2.isNull() == false);
|
||||
REQUIRE(array3.isNull() == true);
|
||||
|
||||
REQUIRE(2 == array1.size());
|
||||
REQUIRE(2 == array2.size());
|
||||
REQUIRE(0 == array3.size());
|
||||
|
||||
REQUIRE(1 == array1[0].as<int>());
|
||||
REQUIRE(2 == array1[1].as<int>());
|
||||
|
||||
REQUIRE(3 == array2[0].as<int>());
|
||||
REQUIRE(4 == array2[1].as<int>());
|
||||
|
||||
REQUIRE(0 == array3[0].as<int>());
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Premature null terminator") {
|
||||
SECTION("After opening brace") {
|
||||
DeserializationError err = deserializeJson(doc, "{");
|
||||
|
||||
REQUIRE(err == DeserializationError::IncompleteInput);
|
||||
}
|
||||
|
||||
SECTION("After key") {
|
||||
DeserializationError err = deserializeJson(doc, "{\"hello\"");
|
||||
|
||||
REQUIRE(err == DeserializationError::IncompleteInput);
|
||||
}
|
||||
|
||||
SECTION("After colon") {
|
||||
DeserializationError err = deserializeJson(doc, "{\"hello\":");
|
||||
|
||||
REQUIRE(err == DeserializationError::IncompleteInput);
|
||||
}
|
||||
|
||||
SECTION("After value") {
|
||||
DeserializationError err = deserializeJson(doc, "{\"hello\":\"world\"");
|
||||
|
||||
REQUIRE(err == DeserializationError::IncompleteInput);
|
||||
}
|
||||
|
||||
SECTION("After comma") {
|
||||
DeserializationError err = deserializeJson(doc, "{\"hello\":\"world\",");
|
||||
|
||||
REQUIRE(err == DeserializationError::IncompleteInput);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Misc") {
|
||||
SECTION("A quoted key without value") {
|
||||
DeserializationError err = deserializeJson(doc, "{\"key\"}");
|
||||
|
||||
REQUIRE(err == DeserializationError::InvalidInput);
|
||||
}
|
||||
|
||||
SECTION("A non-quoted key without value") {
|
||||
DeserializationError err = deserializeJson(doc, "{key}");
|
||||
|
||||
REQUIRE(err == DeserializationError::InvalidInput);
|
||||
}
|
||||
|
||||
SECTION("A dangling comma") {
|
||||
DeserializationError err = deserializeJson(doc, "{\"key1\":\"value1\",}");
|
||||
|
||||
REQUIRE(err == DeserializationError::InvalidInput);
|
||||
}
|
||||
|
||||
SECTION("null as a key") {
|
||||
DeserializationError err = deserializeJson(doc, "{null:\"value\"}");
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
}
|
||||
|
||||
SECTION("Repeated key") {
|
||||
DeserializationError err =
|
||||
deserializeJson(doc, "{alfa:{bravo:{charlie:1}},alfa:2}");
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.as<std::string>() == "{\"alfa\":2}");
|
||||
REQUIRE(spy.log() ==
|
||||
AllocatorLog{
|
||||
Allocate(sizeofStringBuffer()),
|
||||
Allocate(sizeofPool()),
|
||||
Reallocate(sizeofStringBuffer(), sizeofString("alfa")),
|
||||
Allocate(sizeofStringBuffer()),
|
||||
Reallocate(sizeofStringBuffer(), sizeofString("bravo")),
|
||||
Allocate(sizeofStringBuffer()),
|
||||
Reallocate(sizeofStringBuffer(), sizeofString("charlie")),
|
||||
Allocate(sizeofStringBuffer()),
|
||||
Deallocate(sizeofString("bravo")),
|
||||
Deallocate(sizeofString("charlie")),
|
||||
Deallocate(sizeofStringBuffer()),
|
||||
Reallocate(sizeofPool(), sizeofObject(2) + sizeofObject(1)),
|
||||
});
|
||||
}
|
||||
|
||||
SECTION("Repeated key with zero copy mode") { // issue #1697
|
||||
char input[] = "{a:{b:{c:1}},a:2}";
|
||||
DeserializationError err = deserializeJson(doc, input);
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc["a"] == 2);
|
||||
}
|
||||
|
||||
SECTION("NUL in keys") {
|
||||
DeserializationError err =
|
||||
deserializeJson(doc, "{\"x\":0,\"x\\u0000a\":1,\"x\\u0000b\":2}");
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.as<std::string>() ==
|
||||
"{\"x\":0,\"x\\u0000a\":1,\"x\\u0000b\":2}");
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Should clear the JsonObject") {
|
||||
deserializeJson(doc, "{\"hello\":\"world\"}");
|
||||
spy.clearLog();
|
||||
|
||||
deserializeJson(doc, "{}");
|
||||
|
||||
REQUIRE(doc.is<JsonObject>());
|
||||
REQUIRE(doc.size() == 0);
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
Deallocate(sizeofObject(1)),
|
||||
Deallocate(sizeofString("hello")),
|
||||
Deallocate(sizeofString("world")),
|
||||
});
|
||||
}
|
||||
|
||||
SECTION("Issue #1335") {
|
||||
std::string json("{\"a\":{},\"b\":{}}");
|
||||
deserializeJson(doc, json);
|
||||
CHECK(doc.as<std::string>() == json);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("deserialize JSON object under memory constraints") {
|
||||
TimebombAllocator timebomb(1024);
|
||||
JsonDocument doc(&timebomb);
|
||||
|
||||
SECTION("empty object requires no allocation") {
|
||||
timebomb.setCountdown(0);
|
||||
char input[] = "{}";
|
||||
|
||||
DeserializationError err = deserializeJson(doc, input);
|
||||
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
REQUIRE(doc.as<std::string>() == "{}");
|
||||
}
|
||||
|
||||
SECTION("key allocation fails") {
|
||||
timebomb.setCountdown(0);
|
||||
char input[] = "{\"a\":1}";
|
||||
|
||||
DeserializationError err = deserializeJson(doc, input);
|
||||
|
||||
REQUIRE(err == DeserializationError::NoMemory);
|
||||
REQUIRE(doc.as<std::string>() == "{}");
|
||||
}
|
||||
|
||||
SECTION("pool allocation fails") {
|
||||
timebomb.setCountdown(1);
|
||||
char input[] = "{\"a\":1}";
|
||||
|
||||
DeserializationError err = deserializeJson(doc, input);
|
||||
|
||||
REQUIRE(err == DeserializationError::NoMemory);
|
||||
REQUIRE(doc.as<std::string>() == "{}");
|
||||
}
|
||||
|
||||
SECTION("string allocation fails") {
|
||||
timebomb.setCountdown(3);
|
||||
char input[] = "{\"alfa\":\"bravo\"}";
|
||||
|
||||
DeserializationError err = deserializeJson(doc, input);
|
||||
|
||||
REQUIRE(err == DeserializationError::NoMemory);
|
||||
REQUIRE(doc.as<std::string>() == "{\"alfa\":null}");
|
||||
}
|
||||
}
|
||||
214
ArduinoJson/extras/tests/JsonDeserializer/string.cpp
Normal file
214
ArduinoJson/extras/tests/JsonDeserializer/string.cpp
Normal file
@@ -0,0 +1,214 @@
|
||||
// ArduinoJson - https://arduinojson.org
|
||||
// Copyright © 2014-2025, Benoit BLANCHON
|
||||
// MIT License
|
||||
|
||||
#define ARDUINOJSON_DECODE_UNICODE 1
|
||||
#include <ArduinoJson.h>
|
||||
#include <catch.hpp>
|
||||
|
||||
#include "Allocators.hpp"
|
||||
|
||||
using ArduinoJson::detail::sizeofArray;
|
||||
using ArduinoJson::detail::sizeofObject;
|
||||
|
||||
TEST_CASE("Valid JSON strings value") {
|
||||
struct TestCase {
|
||||
const char* input;
|
||||
const char* expectedOutput;
|
||||
};
|
||||
|
||||
TestCase testCases[] = {
|
||||
{"\"hello world\"", "hello world"},
|
||||
{"\'hello world\'", "hello world"},
|
||||
{"'\"'", "\""},
|
||||
{"'\\\\'", "\\"},
|
||||
{"'\\/'", "/"},
|
||||
{"'\\b'", "\b"},
|
||||
{"'\\f'", "\f"},
|
||||
{"'\\n'", "\n"},
|
||||
{"'\\r'", "\r"},
|
||||
{"'\\t'", "\t"},
|
||||
{"\"1\\\"2\\\\3\\/4\\b5\\f6\\n7\\r8\\t9\"", "1\"2\\3/4\b5\f6\n7\r8\t9"},
|
||||
{"'\\u0041'", "A"},
|
||||
{"'\\u00e4'", "\xc3\xa4"}, // ä
|
||||
{"'\\u00E4'", "\xc3\xa4"}, // ä
|
||||
{"'\\u3042'", "\xe3\x81\x82"}, // あ
|
||||
{"'\\ud83d\\udda4'", "\xf0\x9f\x96\xa4"}, // 🖤
|
||||
{"'\\uF053'", "\xef\x81\x93"}, // issue #1173
|
||||
{"'\\uF015'", "\xef\x80\x95"}, // issue #1173
|
||||
{"'\\uF054'", "\xef\x81\x94"}, // issue #1173
|
||||
};
|
||||
const size_t testCount = sizeof(testCases) / sizeof(testCases[0]);
|
||||
|
||||
JsonDocument doc;
|
||||
|
||||
for (size_t i = 0; i < testCount; i++) {
|
||||
const TestCase& testCase = testCases[i];
|
||||
CAPTURE(testCase.input);
|
||||
DeserializationError err = deserializeJson(doc, testCase.input);
|
||||
CHECK(err == DeserializationError::Ok);
|
||||
CHECK(doc.as<std::string>() == testCase.expectedOutput);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("\\u0000") {
|
||||
JsonDocument doc;
|
||||
|
||||
DeserializationError err = deserializeJson(doc, "\"wx\\u0000yz\"");
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
|
||||
const char* result = doc.as<const char*>();
|
||||
CHECK(result[0] == 'w');
|
||||
CHECK(result[1] == 'x');
|
||||
CHECK(result[2] == 0);
|
||||
CHECK(result[3] == 'y');
|
||||
CHECK(result[4] == 'z');
|
||||
CHECK(result[5] == 0);
|
||||
|
||||
CHECK(doc.as<JsonString>().size() == 5);
|
||||
CHECK(doc.as<std::string>().size() == 5);
|
||||
}
|
||||
|
||||
TEST_CASE("Truncated JSON string") {
|
||||
const char* testCases[] = {"\"hello", "\'hello", "'\\u", "'\\u00", "'\\u000"};
|
||||
const size_t testCount = sizeof(testCases) / sizeof(testCases[0]);
|
||||
|
||||
JsonDocument doc;
|
||||
|
||||
for (size_t i = 0; i < testCount; i++) {
|
||||
const char* input = testCases[i];
|
||||
CAPTURE(input);
|
||||
REQUIRE(deserializeJson(doc, input) ==
|
||||
DeserializationError::IncompleteInput);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Escape single quote in single quoted string") {
|
||||
JsonDocument doc;
|
||||
|
||||
DeserializationError err = deserializeJson(doc, "'ab\\\'cd'");
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
CHECK(doc.as<std::string>() == "ab\'cd");
|
||||
}
|
||||
|
||||
TEST_CASE("Escape double quote in double quoted string") {
|
||||
JsonDocument doc;
|
||||
|
||||
DeserializationError err = deserializeJson(doc, "'ab\\\"cd'");
|
||||
REQUIRE(err == DeserializationError::Ok);
|
||||
CHECK(doc.as<std::string>() == "ab\"cd");
|
||||
}
|
||||
|
||||
TEST_CASE("Invalid JSON string") {
|
||||
const char* testCases[] = {"'\\u'", "'\\u000g'", "'\\u000'",
|
||||
"'\\u000G'", "'\\u000/'", "'\\x1234'"};
|
||||
const size_t testCount = sizeof(testCases) / sizeof(testCases[0]);
|
||||
|
||||
JsonDocument doc;
|
||||
|
||||
for (size_t i = 0; i < testCount; i++) {
|
||||
const char* input = testCases[i];
|
||||
CAPTURE(input);
|
||||
REQUIRE(deserializeJson(doc, input) == DeserializationError::InvalidInput);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Allocation of the key fails") {
|
||||
TimebombAllocator timebomb(0);
|
||||
SpyingAllocator spy(&timebomb);
|
||||
JsonDocument doc(&spy);
|
||||
|
||||
SECTION("Quoted string, first member") {
|
||||
REQUIRE(deserializeJson(doc, "{\"example\":1}") ==
|
||||
DeserializationError::NoMemory);
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
AllocateFail(sizeofStringBuffer()),
|
||||
});
|
||||
}
|
||||
|
||||
SECTION("Quoted string, second member") {
|
||||
timebomb.setCountdown(3);
|
||||
REQUIRE(deserializeJson(doc, "{\"hello\":1,\"world\"}") ==
|
||||
DeserializationError::NoMemory);
|
||||
REQUIRE(spy.log() ==
|
||||
AllocatorLog{
|
||||
Allocate(sizeofStringBuffer()),
|
||||
Allocate(sizeofPool()),
|
||||
Reallocate(sizeofStringBuffer(), sizeofString("hello")),
|
||||
AllocateFail(sizeofStringBuffer()),
|
||||
ReallocateFail(sizeofPool(), sizeofObject(1)),
|
||||
});
|
||||
}
|
||||
|
||||
SECTION("Non-Quoted string, first member") {
|
||||
REQUIRE(deserializeJson(doc, "{example:1}") ==
|
||||
DeserializationError::NoMemory);
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
AllocateFail(sizeofStringBuffer()),
|
||||
});
|
||||
}
|
||||
|
||||
SECTION("Non-Quoted string, second member") {
|
||||
timebomb.setCountdown(3);
|
||||
REQUIRE(deserializeJson(doc, "{hello:1,world}") ==
|
||||
DeserializationError::NoMemory);
|
||||
REQUIRE(spy.log() ==
|
||||
AllocatorLog{
|
||||
Allocate(sizeofStringBuffer()),
|
||||
Allocate(sizeofPool()),
|
||||
Reallocate(sizeofStringBuffer(), sizeofString("hello")),
|
||||
AllocateFail(sizeofStringBuffer()),
|
||||
ReallocateFail(sizeofPool(), sizeofObject(1)),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("String allocation fails") {
|
||||
SpyingAllocator spy(FailingAllocator::instance());
|
||||
JsonDocument doc(&spy);
|
||||
|
||||
SECTION("Input is const char*") {
|
||||
REQUIRE(deserializeJson(doc, "\"hello\"") ==
|
||||
DeserializationError::NoMemory);
|
||||
REQUIRE(spy.log() == AllocatorLog{
|
||||
AllocateFail(sizeofStringBuffer()),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Deduplicate values") {
|
||||
SpyingAllocator spy;
|
||||
JsonDocument doc(&spy);
|
||||
deserializeJson(doc, "[\"example\",\"example\"]");
|
||||
|
||||
CHECK(doc[0].as<const char*>() == doc[1].as<const char*>());
|
||||
REQUIRE(spy.log() ==
|
||||
AllocatorLog{
|
||||
Allocate(sizeofPool()),
|
||||
Allocate(sizeofStringBuffer()),
|
||||
Reallocate(sizeofStringBuffer(), sizeofString("example")),
|
||||
Allocate(sizeofStringBuffer()),
|
||||
Deallocate(sizeofStringBuffer()),
|
||||
Reallocate(sizeofPool(), sizeofArray(2)),
|
||||
});
|
||||
}
|
||||
|
||||
TEST_CASE("Deduplicate keys") {
|
||||
SpyingAllocator spy;
|
||||
JsonDocument doc(&spy);
|
||||
deserializeJson(doc, "[{\"example\":1},{\"example\":2}]");
|
||||
|
||||
const char* key1 = doc[0].as<JsonObject>().begin()->key().c_str();
|
||||
const char* key2 = doc[1].as<JsonObject>().begin()->key().c_str();
|
||||
CHECK(key1 == key2);
|
||||
|
||||
REQUIRE(spy.log() ==
|
||||
AllocatorLog{
|
||||
Allocate(sizeofPool()),
|
||||
Allocate(sizeofStringBuffer()),
|
||||
Reallocate(sizeofStringBuffer(), sizeofString("example")),
|
||||
Allocate(sizeofStringBuffer()),
|
||||
Deallocate(sizeofStringBuffer()),
|
||||
Reallocate(sizeofPool(), sizeofArray(2) + 2 * sizeofObject(1)),
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user