forked from bblanchon/ArduinoJson
Improved coverage of JsonDeserializer
This commit is contained in:
@ -2,6 +2,7 @@
|
|||||||
// Copyright Benoit Blanchon 2014-2020
|
// Copyright Benoit Blanchon 2014-2020
|
||||||
// MIT License
|
// MIT License
|
||||||
|
|
||||||
|
#define ARDUINOJSON_ENABLE_COMMENTS 1
|
||||||
#include <ArduinoJson.h>
|
#include <ArduinoJson.h>
|
||||||
#include <catch.hpp>
|
#include <catch.hpp>
|
||||||
|
|
||||||
@ -257,11 +258,11 @@ TEST_CASE("Filtering") {
|
|||||||
JSON_ARRAY_SIZE(0)
|
JSON_ARRAY_SIZE(0)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// ignore errors in skipped value
|
// detect errors in skipped value
|
||||||
"[!,2,\\]",
|
"[!,2,\\]",
|
||||||
"[false]",
|
"[false]",
|
||||||
10,
|
10,
|
||||||
DeserializationError::Ok,
|
DeserializationError::InvalidInput,
|
||||||
"[]",
|
"[]",
|
||||||
JSON_ARRAY_SIZE(0)
|
JSON_ARRAY_SIZE(0)
|
||||||
},
|
},
|
||||||
@ -374,11 +375,11 @@ TEST_CASE("Filtering") {
|
|||||||
0
|
0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// ignore invalid value in skipped object
|
// detect invalid value in skipped object
|
||||||
"{'hello':!}",
|
"{'hello':!}",
|
||||||
"false",
|
"false",
|
||||||
10,
|
10,
|
||||||
DeserializationError::Ok,
|
DeserializationError::InvalidInput,
|
||||||
"null",
|
"null",
|
||||||
0
|
0
|
||||||
},
|
},
|
||||||
@ -387,7 +388,7 @@ TEST_CASE("Filtering") {
|
|||||||
"{'hello':\\}",
|
"{'hello':\\}",
|
||||||
"false",
|
"false",
|
||||||
10,
|
10,
|
||||||
DeserializationError::Ok,
|
DeserializationError::InvalidInput,
|
||||||
"null",
|
"null",
|
||||||
0
|
0
|
||||||
},
|
},
|
||||||
@ -454,6 +455,168 @@ TEST_CASE("Filtering") {
|
|||||||
"null",
|
"null",
|
||||||
0
|
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,
|
||||||
|
"[]",
|
||||||
|
JSON_ARRAY_SIZE(0)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// incomplete comment at the begining of an array
|
||||||
|
"[/*]",
|
||||||
|
"[false]",
|
||||||
|
10,
|
||||||
|
DeserializationError::IncompleteInput,
|
||||||
|
"[]",
|
||||||
|
JSON_ARRAY_SIZE(0)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// invalid comment before key
|
||||||
|
"{/1:2}",
|
||||||
|
"{}",
|
||||||
|
10,
|
||||||
|
DeserializationError::InvalidInput,
|
||||||
|
"{}",
|
||||||
|
JSON_OBJECT_SIZE(0)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// incomplete comment before key
|
||||||
|
"{/*:2}",
|
||||||
|
"{}",
|
||||||
|
10,
|
||||||
|
DeserializationError::IncompleteInput,
|
||||||
|
"{}",
|
||||||
|
JSON_OBJECT_SIZE(0)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// invalid comment after key
|
||||||
|
"{\"example\"/1:2}",
|
||||||
|
"{}",
|
||||||
|
10,
|
||||||
|
DeserializationError::InvalidInput,
|
||||||
|
"{}",
|
||||||
|
JSON_OBJECT_SIZE(0) + 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// incomplete comment after key
|
||||||
|
"{\"example\"/*:2}",
|
||||||
|
"{}",
|
||||||
|
10,
|
||||||
|
DeserializationError::IncompleteInput,
|
||||||
|
"{}",
|
||||||
|
JSON_OBJECT_SIZE(0) + 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// invalid comment after colon
|
||||||
|
"{\"example\":/12}",
|
||||||
|
"{}",
|
||||||
|
10,
|
||||||
|
DeserializationError::InvalidInput,
|
||||||
|
"{}",
|
||||||
|
JSON_OBJECT_SIZE(0)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// incomplete comment after colon
|
||||||
|
"{\"example\":/*2}",
|
||||||
|
"{}",
|
||||||
|
10,
|
||||||
|
DeserializationError::IncompleteInput,
|
||||||
|
"{}",
|
||||||
|
JSON_OBJECT_SIZE(0)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// comment next to an integer
|
||||||
|
"{\"ignore\":1//,\"example\":2\n}",
|
||||||
|
"{\"example\":true}",
|
||||||
|
10,
|
||||||
|
DeserializationError::Ok,
|
||||||
|
"{}",
|
||||||
|
JSON_OBJECT_SIZE(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 after 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 after after value of a skipped object
|
||||||
|
"{\"example\":2/*}",
|
||||||
|
"false",
|
||||||
|
10,
|
||||||
|
DeserializationError::IncompleteInput,
|
||||||
|
"null",
|
||||||
|
0
|
||||||
|
},
|
||||||
}; // clang-format on
|
}; // clang-format on
|
||||||
|
|
||||||
for (size_t i = 0; i < sizeof(testCases) / sizeof(testCases[0]); i++) {
|
for (size_t i = 0; i < sizeof(testCases) / sizeof(testCases[0]); i++) {
|
||||||
|
@ -13,7 +13,9 @@ TEST_CASE("Truncated JSON input") {
|
|||||||
// true
|
// true
|
||||||
"t", "tr", "tru",
|
"t", "tr", "tru",
|
||||||
// null
|
// null
|
||||||
"n", "nu", "nul"};
|
"n", "nu", "nul",
|
||||||
|
// object
|
||||||
|
"{", "{a", "{a:", "{a:1", "{a:1,", "{a:1,"};
|
||||||
const size_t testCount = sizeof(testCases) / sizeof(testCases[0]);
|
const size_t testCount = sizeof(testCases) / sizeof(testCases[0]);
|
||||||
|
|
||||||
DynamicJsonDocument doc(4096);
|
DynamicJsonDocument doc(4096);
|
||||||
|
@ -15,6 +15,14 @@ TEST_CASE("Valid JSON strings value") {
|
|||||||
TestCase testCases[] = {
|
TestCase testCases[] = {
|
||||||
{"\"hello world\"", "hello world"},
|
{"\"hello world\"", "hello world"},
|
||||||
{"\'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"},
|
{"\"1\\\"2\\\\3\\/4\\b5\\f6\\n7\\r8\\t9\"", "1\"2\\3/4\b5\f6\n7\r8\t9"},
|
||||||
{"'\\u0041'", "A"},
|
{"'\\u0041'", "A"},
|
||||||
{"'\\u00e4'", "\xc3\xa4"}, // ä
|
{"'\\u00e4'", "\xc3\xa4"}, // ä
|
||||||
@ -33,8 +41,8 @@ TEST_CASE("Valid JSON strings value") {
|
|||||||
const TestCase& testCase = testCases[i];
|
const TestCase& testCase = testCases[i];
|
||||||
CAPTURE(testCase.input);
|
CAPTURE(testCase.input);
|
||||||
DeserializationError err = deserializeJson(doc, testCase.input);
|
DeserializationError err = deserializeJson(doc, testCase.input);
|
||||||
REQUIRE(err == DeserializationError::Ok);
|
CHECK(err == DeserializationError::Ok);
|
||||||
REQUIRE(doc.as<std::string>() == testCase.expectedOutput);
|
CHECK(doc.as<std::string>() == testCase.expectedOutput);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -54,7 +62,7 @@ TEST_CASE("Truncated JSON string") {
|
|||||||
|
|
||||||
TEST_CASE("Invalid JSON string") {
|
TEST_CASE("Invalid JSON string") {
|
||||||
const char* testCases[] = {"'\\u'", "'\\u000g'", "'\\u000'",
|
const char* testCases[] = {"'\\u'", "'\\u000g'", "'\\u000'",
|
||||||
"'\\u000G'", "'\\u000/'", "\\x1234"};
|
"'\\u000G'", "'\\u000/'", "'\\x1234'"};
|
||||||
const size_t testCount = sizeof(testCases) / sizeof(testCases[0]);
|
const size_t testCount = sizeof(testCases) / sizeof(testCases[0]);
|
||||||
|
|
||||||
DynamicJsonDocument doc(4096);
|
DynamicJsonDocument doc(4096);
|
||||||
@ -67,9 +75,15 @@ TEST_CASE("Invalid JSON string") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Not enough room to duplicate the string") {
|
TEST_CASE("Not enough room to duplicate the string") {
|
||||||
DynamicJsonDocument doc(4);
|
DynamicJsonDocument doc(JSON_OBJECT_SIZE(0));
|
||||||
|
|
||||||
REQUIRE(deserializeJson(doc, "\"hello world!\"") ==
|
SECTION("Quoted string") {
|
||||||
|
REQUIRE(deserializeJson(doc, "{\"example\":1}") ==
|
||||||
DeserializationError::NoMemory);
|
DeserializationError::NoMemory);
|
||||||
REQUIRE(doc.isNull() == true);
|
}
|
||||||
|
|
||||||
|
SECTION("Non-quoted string") {
|
||||||
|
REQUIRE(deserializeJson(doc, "{example:1}") ==
|
||||||
|
DeserializationError::NoMemory);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,38 @@ TEST_CASE("serializeJson(JsonVariant)") {
|
|||||||
|
|
||||||
SECTION("string") {
|
SECTION("string") {
|
||||||
check(std::string("hello"), "\"hello\"");
|
check(std::string("hello"), "\"hello\"");
|
||||||
|
|
||||||
|
SECTION("Escape quotation mark") {
|
||||||
|
check(std::string("hello \"world\""), "\"hello \\\"world\\\"\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Escape reverse solidus") {
|
||||||
|
check(std::string("hello\\world"), "\"hello\\\\world\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Don't escape solidus") {
|
||||||
|
check(std::string("fifty/fifty"), "\"fifty/fifty\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Escape backspace") {
|
||||||
|
check(std::string("hello\bworld"), "\"hello\\bworld\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Escape formfeed") {
|
||||||
|
check(std::string("hello\fworld"), "\"hello\\fworld\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Escape linefeed") {
|
||||||
|
check(std::string("hello\nworld"), "\"hello\\nworld\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Escape carriage return") {
|
||||||
|
check(std::string("hello\rworld"), "\"hello\\rworld\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Escape tab") {
|
||||||
|
check(std::string("hello\tworld"), "\"hello\\tworld\"");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("SerializedValue<const char*>") {
|
SECTION("SerializedValue<const char*>") {
|
||||||
|
@ -12,7 +12,7 @@ class EscapeSequence {
|
|||||||
public:
|
public:
|
||||||
// Optimized for code size on a 8-bit AVR
|
// Optimized for code size on a 8-bit AVR
|
||||||
static char escapeChar(char c) {
|
static char escapeChar(char c) {
|
||||||
const char *p = escapeTable(false);
|
const char *p = escapeTable(true);
|
||||||
while (p[0] && p[1] != c) {
|
while (p[0] && p[1] != c) {
|
||||||
p += 2;
|
p += 2;
|
||||||
}
|
}
|
||||||
@ -21,10 +21,10 @@ class EscapeSequence {
|
|||||||
|
|
||||||
// Optimized for code size on a 8-bit AVR
|
// Optimized for code size on a 8-bit AVR
|
||||||
static char unescapeChar(char c) {
|
static char unescapeChar(char c) {
|
||||||
const char *p = escapeTable(true);
|
const char *p = escapeTable(false);
|
||||||
for (;;) {
|
for (;;) {
|
||||||
if (p[0] == '\0')
|
if (p[0] == '\0')
|
||||||
return c;
|
return 0;
|
||||||
if (p[0] == c)
|
if (p[0] == c)
|
||||||
return p[1];
|
return p[1];
|
||||||
p += 2;
|
p += 2;
|
||||||
@ -32,8 +32,8 @@ class EscapeSequence {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static const char *escapeTable(bool excludeIdenticals) {
|
static const char *escapeTable(bool excludeSolidus) {
|
||||||
return &"\"\"\\\\b\bf\fn\nr\rt\t"[excludeIdenticals ? 4 : 0];
|
return &"//\"\"\\\\b\bf\fn\nr\rt\t"[excludeSolidus ? 2 : 0];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
} // namespace ARDUINOJSON_NAMESPACE
|
} // namespace ARDUINOJSON_NAMESPACE
|
||||||
|
@ -299,7 +299,9 @@ class JsonDeserializer {
|
|||||||
// Skip spaces
|
// Skip spaces
|
||||||
err = skipSpacesAndComments();
|
err = skipSpacesAndComments();
|
||||||
if (err)
|
if (err)
|
||||||
return err; // Colon
|
return err;
|
||||||
|
|
||||||
|
// Colon
|
||||||
if (!eat(':'))
|
if (!eat(':'))
|
||||||
return DeserializationError::InvalidInput;
|
return DeserializationError::InvalidInput;
|
||||||
|
|
||||||
@ -393,8 +395,7 @@ class JsonDeserializer {
|
|||||||
StringBuilder builder = _stringStorage.startString();
|
StringBuilder builder = _stringStorage.startString();
|
||||||
|
|
||||||
char c = current();
|
char c = current();
|
||||||
if (c == '\0')
|
ARDUINOJSON_ASSERT(c);
|
||||||
return DeserializationError::IncompleteInput;
|
|
||||||
|
|
||||||
if (canBeInNonQuotedString(c)) { // no quotes
|
if (canBeInNonQuotedString(c)) { // no quotes
|
||||||
do {
|
do {
|
||||||
@ -482,7 +483,7 @@ class JsonDeserializer {
|
|||||||
|
|
||||||
DeserializationError skipNumericValue() {
|
DeserializationError skipNumericValue() {
|
||||||
char c = current();
|
char c = current();
|
||||||
while (c && c != '}' && c != ',' && c != ']' && c != ':') {
|
while (canBeInNonQuotedString(c)) {
|
||||||
move();
|
move();
|
||||||
c = current();
|
c = current();
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user