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.
319 lines
8.7 KiB
319 lines
8.7 KiB
// ArduinoJson - https://arduinojson.org |
|
// Copyright © 2014-2024, Benoit BLANCHON |
|
// MIT License |
|
|
|
#include <ArduinoJson.h> |
|
#include <catch.hpp> |
|
|
|
#include "Allocators.hpp" |
|
|
|
using ArduinoJson::detail::sizeofArray; |
|
|
|
TEST_CASE("deserialize JSON array") { |
|
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("Double") { |
|
DeserializationError err = deserializeJson(doc, "[4.2,1e2]"); |
|
JsonArray arr = doc.as<JsonArray>(); |
|
|
|
REQUIRE(err == DeserializationError::Ok); |
|
REQUIRE(2 == arr.size()); |
|
REQUIRE(arr[0] == 4.2); |
|
REQUIRE(arr[1] == 1e2); |
|
} |
|
|
|
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)), |
|
}); |
|
} |
|
}
|
|
|