You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
385 lines
11 KiB
385 lines
11 KiB
// ArduinoJson - https://arduinojson.org
|
|
// Copyright © 2014-2024, Benoit BLANCHON
|
|
// MIT License
|
|
|
|
#include <ArduinoJson.h>
|
|
#include <catch.hpp>
|
|
|
|
#include "Allocators.hpp"
|
|
|
|
using ArduinoJson::detail::sizeofObject;
|
|
|
|
TEST_CASE("deserialize JSON object") {
|
|
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("Double") {
|
|
DeserializationError err =
|
|
deserializeJson(doc, "{\"key1\":12.345,\"key2\":-7E89}");
|
|
JsonObject obj = doc.as<JsonObject>();
|
|
|
|
REQUIRE(err == DeserializationError::Ok);
|
|
REQUIRE(doc.is<JsonObject>());
|
|
REQUIRE(obj.size() == 2);
|
|
REQUIRE(obj["key1"] == 12.345);
|
|
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, "{a:{b:{c:1}},a:2}");
|
|
|
|
REQUIRE(err == DeserializationError::Ok);
|
|
REQUIRE(doc.as<std::string>() == "{\"a\":2}");
|
|
REQUIRE(spy.log() ==
|
|
AllocatorLog{
|
|
Allocate(sizeofStringBuffer()),
|
|
Reallocate(sizeofStringBuffer(), sizeofString("a")),
|
|
Allocate(sizeofPool()),
|
|
Allocate(sizeofStringBuffer()),
|
|
Reallocate(sizeofStringBuffer(), sizeofString("b")),
|
|
Allocate(sizeofStringBuffer()),
|
|
Reallocate(sizeofStringBuffer(), sizeofString("c")),
|
|
Allocate(sizeofStringBuffer()),
|
|
Deallocate(sizeofString("b")),
|
|
Deallocate(sizeofString("c")),
|
|
Deallocate(sizeofStringBuffer()),
|
|
Reallocate(sizeofPool(), sizeofObject(2) + sizeofObject(1)),
|
|
});
|
|
}
|
|
|
|
SECTION("Repeated key with zero copy mode") { // issue #1697
|
|
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") { // we don't support NULs in keys
|
|
DeserializationError err =
|
|
deserializeJson(doc, "{\"x\\u0000a\":1,\"x\\u0000b\":2}");
|
|
|
|
REQUIRE(err == DeserializationError::Ok);
|
|
REQUIRE(doc.as<std::string>() == "{\"x\":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(2);
|
|
char input[] = "{\"a\":1}";
|
|
|
|
DeserializationError err = deserializeJson(doc, input);
|
|
|
|
REQUIRE(err == DeserializationError::NoMemory);
|
|
REQUIRE(doc.as<std::string>() == "{}");
|
|
}
|
|
|
|
SECTION("string allocation fails") {
|
|
timebomb.setCountdown(3);
|
|
char input[] = "{\"a\":\"b\"}";
|
|
|
|
DeserializationError err = deserializeJson(doc, input);
|
|
|
|
REQUIRE(err == DeserializationError::NoMemory);
|
|
REQUIRE(doc.as<std::string>() == "{\"a\":null}");
|
|
}
|
|
}
|
|
|