/* This file is part of solidity. solidity is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. solidity is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with solidity. If not, see . */ // SPDX-License-Identifier: GPL-3.0 /** * @date 2018 * Unit tests for JSON.h. */ #include #include #include 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, std::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; Json::Value jsonChild; jsonChild["3.1"] = "3.1"; jsonChild["3.2"] = 2; json["1"] = 1; json["2"] = "2"; json["3"] = jsonChild; json["4"] = "ऑ ऒ ओ औ क ख"; json["5"] = "\xff"; BOOST_CHECK( "{\n" " \"1\": 1,\n" " \"2\": \"2\",\n" " \"3\":\n" " {\n" " \"3.1\": \"3.1\",\n" " \"3.2\": 2\n" " },\n" " \"4\": \"\\u0911 \\u0912 \\u0913 \\u0914 \\u0915 \\u0916\",\n" " \"5\": \"\\ufffd\"\n" "}" == jsonPrettyPrint(json)); } BOOST_AUTO_TEST_CASE(json_compact_print) { Json::Value json; Json::Value jsonChild; jsonChild["3.1"] = "3.1"; jsonChild["3.2"] = 2; 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},\"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 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 ECMA-262 BOOST_CHECK(!jsonParseStrict("{\"1\":2,\"2\":\"2\",\"3\":{\"3.1\":\"3.1\",\"3.2\":3}}}}}}}}}}", json, &errors)); // 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 )); BOOST_CHECK(json["1"] == 3); BOOST_CHECK(json["2"] == "2"); BOOST_CHECK(json["3"]["3.1"] == "3.1"); BOOST_CHECK(json["3"]["3.2"] == 5); // 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()); // 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_CASE(json_isOfType) { Json::Value json; json["float"] = 3.1f; json["double"] = 3.1; json["int"] = 2; json["int64"] = Json::Int64{0x4000000000000000}; json["string"] = "Hello World!"; BOOST_CHECK(isOfType(json["float"])); BOOST_CHECK(isOfType(json["double"])); BOOST_CHECK(isOfType(json["int"])); BOOST_CHECK(isOfType(json["int"])); BOOST_CHECK(isOfType(json["int"])); BOOST_CHECK(isOfType(json["int"])); BOOST_CHECK(isOfType(json["int64"])); BOOST_CHECK(isOfType(json["int64"])); BOOST_CHECK(isOfType(json["string"])); BOOST_CHECK(!isOfType(json["int64"])); BOOST_CHECK(!isOfType(json["double"])); BOOST_CHECK(!isOfType(json["string"])); BOOST_CHECK(!isOfType(json["string"])); BOOST_CHECK(!isOfType(json["string"])); BOOST_CHECK(!isOfType(json["string"])); BOOST_CHECK(!isOfType(json["string"])); BOOST_CHECK(!isOfType(json["string"])); } BOOST_AUTO_TEST_CASE(json_isisOfTypeIfExists) { Json::Value json; json["float"] = 3.1f; json["double"] = 3.1; json["int"] = 2; json["int64"] = Json::Int64{0x4000000000000000}; json["string"] = "Hello World!"; BOOST_CHECK(isOfTypeIfExists(json, "float")); BOOST_CHECK(isOfTypeIfExists(json, "double")); BOOST_CHECK(isOfTypeIfExists(json, "int")); BOOST_CHECK(isOfTypeIfExists(json, "int")); BOOST_CHECK(isOfTypeIfExists(json, "int")); BOOST_CHECK(isOfTypeIfExists(json, "int")); BOOST_CHECK(isOfTypeIfExists(json, "int64")); BOOST_CHECK(isOfTypeIfExists(json, "int64")); BOOST_CHECK(isOfTypeIfExists(json, "string")); BOOST_CHECK(!isOfTypeIfExists(json, "int64")); BOOST_CHECK(!isOfTypeIfExists(json, "double")); BOOST_CHECK(!isOfTypeIfExists(json, "string")); BOOST_CHECK(!isOfTypeIfExists(json, "string")); BOOST_CHECK(!isOfTypeIfExists(json, "string")); BOOST_CHECK(!isOfTypeIfExists(json, "string")); BOOST_CHECK(!isOfTypeIfExists(json, "string")); BOOST_CHECK(!isOfTypeIfExists(json, "string")); BOOST_CHECK(isOfTypeIfExists(json, "NOT_EXISTING")); } BOOST_AUTO_TEST_CASE(json_getOrDefault) { Json::Value json; json["float"] = 3.1f; json["double"] = 3.1; json["int"] = 2; json["int64"] = Json::Int64{0x4000000000000000}; json["uint64"] = Json::UInt64{0x5000000000000000}; json["string"] = "Hello World!"; BOOST_CHECK(getOrDefault(json["float"]) == 3.1f); BOOST_CHECK(getOrDefault(json["float"], -1.1f) == 3.1f); BOOST_CHECK(getOrDefault(json["no_float"], -1.1f) == -1.1f); BOOST_CHECK(getOrDefault(json["double"]) == 3.1); BOOST_CHECK(getOrDefault(json["double"], -1) == 3.1); BOOST_CHECK(getOrDefault(json["no_double"], -1.1) == -1.1); BOOST_CHECK(getOrDefault(json["int"]) == 2); BOOST_CHECK(getOrDefault(json["int"], -1) == 2); BOOST_CHECK(getOrDefault(json["no_int"], -1) == -1); BOOST_CHECK(getOrDefault(json["int"]) == 2); BOOST_CHECK(getOrDefault(json["int"], -1) == 2); BOOST_CHECK(getOrDefault(json["no_int"], -1) == -1); BOOST_CHECK(getOrDefault(json["int"]) == 2); BOOST_CHECK(getOrDefault(json["int"], 1) == 2); BOOST_CHECK(getOrDefault(json["no_int"], 1) == 1); BOOST_CHECK(getOrDefault(json["int"]) == 2); BOOST_CHECK(getOrDefault(json["int"], -1) == 2); BOOST_CHECK(getOrDefault(json["no_int"], -1) == -1); BOOST_CHECK(getOrDefault(json["int64"]) == 0x4000000000000000); BOOST_CHECK(getOrDefault(json["int64"], -1) == 0x4000000000000000); BOOST_CHECK(getOrDefault(json["no_int64"], -1) == -1); BOOST_CHECK(getOrDefault(json["int64"]) == 0x4000000000000000); BOOST_CHECK(getOrDefault(json["int64"], 1) == 0x4000000000000000); BOOST_CHECK(getOrDefault(json["no_int64"], 1) == 1); BOOST_CHECK(getOrDefault(json["uint64"]) == 0x5000000000000000); BOOST_CHECK(getOrDefault(json["uint64"], 1) == 0x5000000000000000); BOOST_CHECK(getOrDefault(json["no_uint64"], 1) == 1); BOOST_CHECK(getOrDefault(json["string"], "ERROR") == "Hello World!"); BOOST_CHECK(getOrDefault(json["no_string"]).empty()); BOOST_CHECK(getOrDefault(json["no_string"], "ERROR") == "ERROR"); } BOOST_AUTO_TEST_CASE(json_get) { Json::Value json; json["float"] = 3.1f; json["double"] = 3.1; json["int"] = 2; json["int64"] = Json::Int64{0x4000000000000000}; json["uint64"] = Json::UInt64{0x5000000000000000}; json["string"] = "Hello World!"; BOOST_CHECK(get(json["float"]) == 3.1f); BOOST_CHECK(get(json["double"]) == 3.1); BOOST_CHECK(get(json["int"]) == 2); BOOST_CHECK(get(json["int"]) == 2); BOOST_CHECK(get(json["int"]) == 2); BOOST_CHECK(get(json["int"]) == 2); BOOST_CHECK(get(json["int64"]) == 0x4000000000000000); BOOST_CHECK(get(json["int64"]) == 0x4000000000000000); BOOST_CHECK(get(json["uint64"]) == 0x5000000000000000); BOOST_CHECK(get(json["string"]) == "Hello World!"); } BOOST_AUTO_TEST_SUITE_END() }