From 69e9531181566de1306d500ff62b0fd6ae9373c7 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 15 Sep 2021 19:06:35 +0100 Subject: [PATCH] Add JSON tests for unicode, all types, and conformance to ECMA-262/ECMA-404 Also avoid using toStyledString --- test/libsolidity/SolidityNatspecJSON.cpp | 8 +- test/libsolutil/JSON.cpp | 93 ++++++++++++++++++++++-- 2 files changed, 90 insertions(+), 11 deletions(-) diff --git a/test/libsolidity/SolidityNatspecJSON.cpp b/test/libsolidity/SolidityNatspecJSON.cpp index b295b8091..6ee55d586 100644 --- a/test/libsolidity/SolidityNatspecJSON.cpp +++ b/test/libsolidity/SolidityNatspecJSON.cpp @@ -63,8 +63,8 @@ public: BOOST_CHECK_MESSAGE( expectedDocumentation == generatedDocumentation, - "Expected:\n" << expectedDocumentation.toStyledString() << - "\n but got:\n" << generatedDocumentation.toStyledString() + "Expected:\n" << util::jsonPrettyPrint(expectedDocumentation) << + "\n but got:\n" << util::jsonPrettyPrint(generatedDocumentation) ); } @@ -2089,8 +2089,8 @@ BOOST_AUTO_TEST_CASE(dev_explicit_inehrit_complex) BOOST_CHECK_MESSAGE( expectedDocumentation == generatedDocumentation, - "Expected:\n" << expectedDocumentation.toStyledString() << - "\n but got:\n" << generatedDocumentation.toStyledString() + "Expected:\n" << util::jsonPrettyPrint(expectedDocumentation) << + "\n but got:\n" << util::jsonPrettyPrint(generatedDocumentation) ); } diff --git a/test/libsolutil/JSON.cpp b/test/libsolutil/JSON.cpp index 666397877..1b0ed9388 100644 --- a/test/libsolutil/JSON.cpp +++ b/test/libsolutil/JSON.cpp @@ -33,6 +33,48 @@ namespace solidity::util::test BOOST_AUTO_TEST_SUITE(JsonTest, *boost::unit_test::label("nooptions")) +BOOST_AUTO_TEST_CASE(json_types) +{ + auto check = [](Json::Value value, string const& expectation) { + BOOST_CHECK(jsonCompactPrint(value) == expectation); + }; + + Json::Value value; + BOOST_CHECK(value.empty()); + value = {}; + BOOST_CHECK(value.empty()); + value = Json::Value(); + BOOST_CHECK(value.empty()); + value = Json::nullValue; + BOOST_CHECK(value.empty()); + + check(value, "null"); + check({}, "null"); + check(Json::Value(), "null"); + check(Json::nullValue, "null"); + check(Json::objectValue, "{}"); + check(Json::arrayValue, "[]"); + check(Json::UInt(1), "1"); + check(Json::UInt(-1), "4294967295"); + check(Json::UInt64(1), "1"); + check(Json::UInt64(-1), "18446744073709551615"); + check(Json::LargestUInt(1), "1"); + check(Json::LargestUInt(-1), "18446744073709551615"); + check(Json::LargestUInt(0xffffffff), "4294967295"); + check(Json::Value("test"), "\"test\""); + check("test", "\"test\""); + check(true, "true"); + + value = Json::objectValue; + value["key"] = "value"; + check(value, "{\"key\":\"value\"}"); + + value = Json::arrayValue; + value.append(1); + value.append(2); + check(value, "[1,2]"); +} + BOOST_AUTO_TEST_CASE(json_pretty_print) { Json::Value json; @@ -43,6 +85,8 @@ BOOST_AUTO_TEST_CASE(json_pretty_print) json["1"] = 1; json["2"] = "2"; json["3"] = jsonChild; + json["4"] = "ऑ ऒ ओ औ क ख"; + json["5"] = "\xff"; BOOST_CHECK( "{\n" @@ -52,7 +96,9 @@ BOOST_AUTO_TEST_CASE(json_pretty_print) " {\n" " \"3.1\": \"3.1\",\n" " \"3.2\": 2\n" - " }\n" + " },\n" + " \"4\": \"\\u0911 \\u0912 \\u0913 \\u0914 \\u0915 \\u0916\",\n" + " \"5\": \"\\ufffd\"\n" "}" == jsonPrettyPrint(json)); } @@ -66,26 +112,32 @@ BOOST_AUTO_TEST_CASE(json_compact_print) json["1"] = 1; json["2"] = "2"; json["3"] = jsonChild; + json["4"] = "ऑ ऒ ओ औ क ख"; + json["5"] = "\xff"; - BOOST_CHECK("{\"1\":1,\"2\":\"2\",\"3\":{\"3.1\":\"3.1\",\"3.2\":2}}" == jsonCompactPrint(json)); + BOOST_CHECK("{\"1\":1,\"2\":\"2\",\"3\":{\"3.1\":\"3.1\",\"3.2\":2},\"4\":\"\\u0911 \\u0912 \\u0913 \\u0914 \\u0915 \\u0916\",\"5\":\"\\ufffd\"}" == jsonCompactPrint(json)); } BOOST_AUTO_TEST_CASE(parse_json_strict) { + // In this test we check conformance against JSON.parse (https://tc39.es/ecma262/multipage/structured-data.html#sec-json.parse) + // and ECMA-404 (https://www.ecma-international.org/publications-and-standards/standards/ecma-404/) + Json::Value json; std::string errors; - // just parse a valid json input + // Just parse a valid json input BOOST_CHECK(jsonParseStrict("{\"1\":1,\"2\":\"2\",\"3\":{\"3.1\":\"3.1\",\"3.2\":2}}", json, &errors)); BOOST_CHECK(json["1"] == 1); BOOST_CHECK(json["2"] == "2"); BOOST_CHECK(json["3"]["3.1"] == "3.1"); BOOST_CHECK(json["3"]["3.2"] == 2); - // trailing garbage is not allowed in strict-mode + // Trailing garbage is not allowed in ECMA-262 BOOST_CHECK(!jsonParseStrict("{\"1\":2,\"2\":\"2\",\"3\":{\"3.1\":\"3.1\",\"3.2\":3}}}}}}}}}}", json, &errors)); - // comments are allowed in strict-mode? - that's strange... + // Comments are not allowed in ECMA-262 + // ... but JSONCPP allows them BOOST_CHECK(jsonParseStrict( "{\"1\":3, // awesome comment\n\"2\":\"2\",\"3\":{\"3.1\":\"3.1\",\"3.2\":5}}", json, &errors )); @@ -94,15 +146,42 @@ BOOST_AUTO_TEST_CASE(parse_json_strict) BOOST_CHECK(json["3"]["3.1"] == "3.1"); BOOST_CHECK(json["3"]["3.2"] == 5); - // root element can only be object or array + // According to ECMA-404 object, array, number, string, true, false, null are allowed + // ... but JSONCPP disallows value types BOOST_CHECK(jsonParseStrict("[]", json, &errors)); + BOOST_CHECK(json.isArray()); BOOST_CHECK(jsonParseStrict("{}", json, &errors)); + BOOST_CHECK(json.isObject()); BOOST_CHECK(!jsonParseStrict("1", json, &errors)); + // BOOST_CHECK(json.isNumeric()); BOOST_CHECK(!jsonParseStrict("\"hello\"", json, &errors)); + // BOOST_CHECK(json.isString()); + BOOST_CHECK(!jsonParseStrict("true", json, &errors)); + // BOOST_CHECK(json.isBool()); + BOOST_CHECK(!jsonParseStrict("null", json, &errors)); + // BOOST_CHECK(json.isNull()); - // non-UTF-8 escapes allowed?? + // Single quotes are also disallowed by ECMA-404 + BOOST_CHECK(!jsonParseStrict("'hello'", json, &errors)); + // BOOST_CHECK(json.isString()); + + // Only string keys in objects are allowed in ECMA-404 + BOOST_CHECK(!jsonParseStrict("{ 42: \"hello\" }", json, &errors)); + + // According to ECMA-404 hex escape sequences are not allowed, only unicode (\uNNNN) and + // a few control characters (\b, \f, \n, \r, \t) + // + // More lenient parsers allow hex escapes as long as they translate to a valid UTF-8 encoding. + // + // ... but JSONCPP allows any hex escapes BOOST_CHECK(jsonParseStrict("[ \"\x80\xec\x80\" ]", json, &errors)); + BOOST_CHECK(json.isArray()); BOOST_CHECK(json[0] == "\x80\xec\x80"); + + // This would be valid more lenient parsers. + BOOST_CHECK(jsonParseStrict("[ \"\xF0\x9F\x98\x8A\" ]", json, &errors)); + BOOST_CHECK(json.isArray()); + BOOST_CHECK(json[0] == "😊"); } BOOST_AUTO_TEST_SUITE_END()