// __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) // | | |__ | | | | | | version 3.11.2 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // // SPDX-FileCopyrightText: 2013-2022 Niels Lohmann // SPDX-License-Identifier: MIT #include "doctest_compatibility.h" // for some reason including this after the json header leads to linker errors with VS 2017... #include #define JSON_TESTS_PRIVATE #include using nlohmann::json; #ifdef JSON_TEST_NO_GLOBAL_UDLS using namespace nlohmann::literals; // NOLINT(google-build-using-namespace) #endif #include #include #include #include #include "make_test_data_available.hpp" #ifdef JSON_HAS_CPP_17 #include #endif #include "fifo_map.hpp" ///////////////////////////////////////////////////////////////////// // for #972 ///////////////////////////////////////////////////////////////////// template using my_workaround_fifo_map = nlohmann::fifo_map, A>; using my_json = nlohmann::basic_json; ///////////////////////////////////////////////////////////////////// // for #977 ///////////////////////////////////////////////////////////////////// namespace ns { struct foo { int x; }; template struct foo_serializer; template struct foo_serializer::value>::type> { template static void to_json(BasicJsonType& j, const T& value) { j = BasicJsonType{{"x", value.x}}; } template static void from_json(const BasicJsonType& j, T& value) // !!! { nlohmann::from_json(j.at("x"), value.x); } }; template struct foo_serializer < T, typename std::enable_if < !std::is_same::value >::type > { template static void to_json(BasicJsonType& j, const T& value) noexcept // NOLINT(bugprone-exception-escape) { ::nlohmann::to_json(j, value); } template static void from_json(const BasicJsonType& j, T& value) //!!! { ::nlohmann::from_json(j, value); } }; } // namespace ns using foo_json = nlohmann::basic_json>; ///////////////////////////////////////////////////////////////////// // for #805 ///////////////////////////////////////////////////////////////////// namespace { struct nocopy // NOLINT(cppcoreguidelines-special-member-functions,hicpp-special-member-functions) { nocopy() = default; nocopy(const nocopy&) = delete; nocopy(nocopy&&) = delete; nocopy& operator=(const nocopy&) = delete; nocopy& operator=(nocopy&&) = delete; int val = 0; friend void to_json(json& j, const nocopy& n) { j = {{"val", n.val}}; } }; } // namespace TEST_CASE("regression tests 1") { SECTION("issue #60 - Double quotation mark is not parsed correctly") { SECTION("escape_doublequote") { const auto* s = R"(["\"foo\""])"; json j = json::parse(s); auto expected = R"(["\"foo\""])"_json; CHECK(j == expected); } } SECTION("issue #70 - Handle infinity and NaN cases") { // previously, NAN/INFINITY created a null value; now, the values are // properly stored, but are dumped as "null" SECTION("NAN value") { CHECK(json(NAN).dump() == "null"); CHECK(json(json::number_float_t(NAN)).dump() == "null"); } SECTION("infinity") { CHECK(json(INFINITY).dump() == "null"); CHECK(json(json::number_float_t(INFINITY)).dump() == "null"); } // With 3.0.0, the semantics of this changed: NAN and infinity are // stored properly inside the JSON value (no exception or conversion // to null), but are serialized as null. SECTION("NAN value") { json j1 = NAN; CHECK(j1.is_number_float()); json::number_float_t f1{j1}; CHECK(std::isnan(f1)); json j2 = static_cast(NAN); CHECK(j2.is_number_float()); json::number_float_t f2{j2}; CHECK(std::isnan(f2)); } SECTION("infinity") { json j1 = INFINITY; CHECK(j1.is_number_float()); json::number_float_t f1{j1}; CHECK(!std::isfinite(f1)); json j2 = static_cast(INFINITY); CHECK(j2.is_number_float()); json::number_float_t f2{j2}; CHECK(!std::isfinite(f2)); } } SECTION("pull request #71 - handle enum type") { enum { t = 0, u = 102}; json j = json::array(); j.push_back(t); // maybe this is not the place to test this? json j2 = u; auto anon_enum_value = j2.get(); CHECK(u == anon_enum_value); // check if the actual value was stored CHECK(j2 == 102); static_assert(std::is_same::value, "types must be the same"); j.push_back(json::object( { {"game_type", t} })); } SECTION("issue #76 - dump() / parse() not idempotent") { // create JSON object json fields; fields["one"] = std::string("one"); fields["two"] = std::string("two three"); fields["three"] = std::string("three \"four\""); // create another JSON object by deserializing the serialization std::string payload = fields.dump(); json parsed_fields = json::parse(payload); // check individual fields to match both objects CHECK(parsed_fields["one"] == fields["one"]); CHECK(parsed_fields["two"] == fields["two"]); CHECK(parsed_fields["three"] == fields["three"]); // check individual fields to match original input CHECK(parsed_fields["one"] == std::string("one")); CHECK(parsed_fields["two"] == std::string("two three")); CHECK(parsed_fields["three"] == std::string("three \"four\"")); // check equality of the objects CHECK(parsed_fields == fields); // check equality of the serialized objects CHECK(fields.dump() == parsed_fields.dump()); // check everything in one line CHECK(fields == json::parse(fields.dump())); } SECTION("issue #82 - lexer::get_number return NAN") { const auto* const content = R"( { "Test":"Test1", "Number":100, "Foo":42.42 })"; std::stringstream ss; ss << content; json j; ss >> j; auto test = j["Test"].get(); CHECK(test == "Test1"); int number{j["Number"]}; CHECK(number == 100); float foo{j["Foo"]}; CHECK(static_cast(foo) == Approx(42.42)); } SECTION("issue #89 - nonstandard integer type") { // create JSON class with nonstandard integer number type using custom_json = nlohmann::basic_json; custom_json j; j["int_1"] = 1; CHECK(j["int_1"] == 1); // tests for correct handling of non-standard integers that overflow the type selected by the user // unsigned integer object creation - expected to wrap and still be stored as an integer j = 4294967296U; // 2^32 CHECK(static_cast(j.type()) == static_cast(custom_json::value_t::number_unsigned)); CHECK(j.get() == 0); // Wrap // unsigned integer parsing - expected to overflow and be stored as a float j = custom_json::parse("4294967296"); // 2^32 CHECK(static_cast(j.type()) == static_cast(custom_json::value_t::number_float)); CHECK(j.get() == 4294967296.0f); // integer object creation - expected to wrap and still be stored as an integer j = -2147483649LL; // -2^31-1 CHECK(static_cast(j.type()) == static_cast(custom_json::value_t::number_integer)); CHECK(j.get() == 2147483647); // Wrap // integer parsing - expected to overflow and be stored as a float with rounding j = custom_json::parse("-2147483649"); // -2^31 CHECK(static_cast(j.type()) == static_cast(custom_json::value_t::number_float)); CHECK(j.get() == -2147483650.0f); } SECTION("issue #93 reverse_iterator operator inheritance problem") { { json a = {1, 2, 3}; json::reverse_iterator rit = a.rbegin(); ++rit; CHECK(*rit == json(2)); CHECK(rit.value() == json(2)); } { json a = {1, 2, 3}; json::reverse_iterator rit = ++a.rbegin(); CHECK(*rit == json(2)); CHECK(rit.value() == json(2)); } { json a = {1, 2, 3}; json::reverse_iterator rit = a.rbegin(); ++rit; json b = {0, 0, 0}; std::transform(rit, a.rend(), b.rbegin(), [](json el) { return el; }); CHECK(b == json({0, 1, 2})); } { json a = {1, 2, 3}; json b = {0, 0, 0}; std::transform(++a.rbegin(), a.rend(), b.rbegin(), [](json el) { return el; }); CHECK(b == json({0, 1, 2})); } } SECTION("issue #100 - failed to iterator json object with reverse_iterator") { json config = { { "111", 111 }, { "112", 112 }, { "113", 113 } }; std::stringstream ss; for (auto it = config.begin(); it != config.end(); ++it) { ss << it.key() << ": " << it.value() << '\n'; } for (auto it = config.rbegin(); it != config.rend(); ++it) { ss << it.key() << ": " << it.value() << '\n'; } CHECK(ss.str() == "111: 111\n112: 112\n113: 113\n113: 113\n112: 112\n111: 111\n"); } SECTION("issue #101 - binary string causes numbers to be dumped as hex") { int64_t number = 10; std::string bytes{"\x00" "asdf\n", 6}; json j; j["int64"] = number; j["binary string"] = bytes; // make sure the number is really printed as decimal "10" and not as // hexadecimal "a" CHECK(j.dump() == "{\"binary string\":\"\\u0000asdf\\n\",\"int64\":10}"); } SECTION("issue #111 - subsequent unicode chars") { std::string bytes{0x7, 0x7}; json j; j["string"] = bytes; CHECK(j["string"] == "\u0007\u0007"); } #if JSON_USE_IMPLICIT_CONVERSIONS SECTION("issue #144 - implicit assignment to std::string fails") { json o = {{"name", "value"}}; std::string s1 = o["name"]; CHECK(s1 == "value"); std::string s2; s2 = o["name"]; CHECK(s2 == "value"); // improve coverage o["int"] = 1; #if JSON_DIAGNOSTICS CHECK_THROWS_WITH_AS(s2 = o["int"], "[json.exception.type_error.302] (/int) type must be string, but is number", json::type_error); #else CHECK_THROWS_WITH_AS(s2 = o["int"], "[json.exception.type_error.302] type must be string, but is number", json::type_error); #endif } #endif SECTION("issue #146 - character following a surrogate pair is skipped") { CHECK(json::parse("\"\\ud80c\\udc60abc\"").get() == "\xf0\x93\x81\xa0\x61\x62\x63"); } SECTION("issue #171 - Cannot index by key of type static constexpr const char*") { json j; // Non-const access with key as "char []" char array_key[] = "Key1"; // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) CHECK_NOTHROW(j[array_key] = 1); CHECK(j[array_key] == json(1)); // Non-const access with key as "const char[]" const char const_array_key[] = "Key2"; // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) CHECK_NOTHROW(j[const_array_key] = 2); CHECK(j[const_array_key] == json(2)); // Non-const access with key as "char *" char _ptr_key[] = "Key3"; // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) char* ptr_key = &_ptr_key[0]; // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg) CHECK_NOTHROW(j[ptr_key] = 3); CHECK(j[ptr_key] == json(3)); // Non-const access with key as "const char *" const char* const_ptr_key = "Key4"; CHECK_NOTHROW(j[const_ptr_key] = 4); CHECK(j[const_ptr_key] == json(4)); // Non-const access with key as "static constexpr const char *" static constexpr const char* constexpr_ptr_key = "Key5"; CHECK_NOTHROW(j[constexpr_ptr_key] = 5); CHECK(j[constexpr_ptr_key] == json(5)); const json j_const = j; // Const access with key as "char []" CHECK(j_const[array_key] == json(1)); // Const access with key as "const char[]" CHECK(j_const[const_array_key] == json(2)); // Const access with key as "char *" CHECK(j_const[ptr_key] == json(3)); // Const access with key as "const char *" CHECK(j_const[const_ptr_key] == json(4)); // Const access with key as "static constexpr const char *" CHECK(j_const[constexpr_ptr_key] == json(5)); } SECTION("issue #186 miloyip/nativejson-benchmark: floating-point parsing") { json j; j = json::parse("-0.0"); CHECK(j.get() == -0.0); j = json::parse("2.22507385850720113605740979670913197593481954635164564e-308"); CHECK(j.get() == 2.2250738585072009e-308); j = json::parse("0.999999999999999944488848768742172978818416595458984374"); CHECK(j.get() == 0.99999999999999989); j = json::parse("1.00000000000000011102230246251565404236316680908203126"); CHECK(j.get() == 1.00000000000000022); j = json::parse("7205759403792793199999e-5"); CHECK(j.get() == 72057594037927928.0); j = json::parse("922337203685477529599999e-5"); CHECK(j.get() == 9223372036854774784.0); j = json::parse("1014120480182583464902367222169599999e-5"); CHECK(j.get() == 10141204801825834086073718800384.0); j = json::parse("5708990770823839207320493820740630171355185151999e-3"); CHECK(j.get() == 5708990770823838890407843763683279797179383808.0); // create JSON class with nonstandard float number type // float nlohmann::basic_json j_float = 1.23e25f; CHECK(j_float.get() == 1.23e25f); // double nlohmann::basic_json j_double = 1.23e35; CHECK(j_double.get() == 1.23e35); // long double nlohmann::basic_json j_long_double = 1.23e45L; CHECK(j_long_double.get() == 1.23e45L); } SECTION("issue #228 - double values are serialized with commas as decimal points") { json j1a = 2312.42; json j1b = json::parse("2312.42"); json j2a = 2342e-2; //issue #230 //json j2b = json::parse("2342e-2"); json j3a = 10E3; json j3b = json::parse("10E3"); json j3c = json::parse("10e3"); // class to create a locale that would use a comma for decimals class CommaDecimalSeparator : public std::numpunct { protected: char do_decimal_point() const override { return ','; } char do_thousands_sep() const override { return '.'; } std::string do_grouping() const override { return "\03"; } }; // change locale to mess with decimal points auto orig_locale = std::locale::global(std::locale(std::locale(), new CommaDecimalSeparator)); CHECK(j1a.dump() == "2312.42"); CHECK(j1b.dump() == "2312.42"); // check if locale is properly reset std::stringstream ss; ss.imbue(std::locale(std::locale(), new CommaDecimalSeparator)); ss << 4712.11; CHECK(ss.str() == "4.712,11"); ss << j1a; CHECK(ss.str() == "4.712,112312.42"); ss << 47.11; CHECK(ss.str() == "4.712,112312.4247,11"); CHECK(j2a.dump() == "23.42"); //issue #230 //CHECK(j2b.dump() == "23.42"); CHECK(j3a.dump() == "10000.0"); CHECK(j3b.dump() == "10000.0"); CHECK(j3c.dump() == "10000.0"); //CHECK(j3b.dump() == "1E04"); // roundtrip error //CHECK(j3c.dump() == "1e04"); // roundtrip error std::locale::global(orig_locale); } SECTION("issue #378 - locale-independent num-to-str") { static_cast(setlocale(LC_NUMERIC, "de_DE.UTF-8")); // verify that dumped correctly with '.' and no grouping const json j1 = 12345.67; CHECK(json(12345.67).dump() == "12345.67"); static_cast(setlocale(LC_NUMERIC, "C")); } SECTION("issue #379 - locale-independent str-to-num") { static_cast(setlocale(LC_NUMERIC, "de_DE.UTF-8")); // verify that parsed correctly despite using strtod internally CHECK(json::parse("3.14").get() == 3.14); // check a different code path CHECK(json::parse("1.000000000000000000000000000000000000000000000000000000000000000000000000").get() == 1.0); } SECTION("issue #233 - Can't use basic_json::iterator as a base iterator for std::move_iterator") { json source = {"a", "b", "c"}; json expected = {"a", "b"}; json dest; std::copy_n(std::make_move_iterator(source.begin()), 2, std::back_inserter(dest)); CHECK(dest == expected); } SECTION("issue #235 - ambiguous overload for 'push_back' and 'operator+='") { json data = {{"key", "value"}}; data.push_back({"key2", "value2"}); data += {"key3", "value3"}; CHECK(data == json({{"key", "value"}, {"key2", "value2"}, {"key3", "value3"}})); } SECTION("issue #269 - diff generates incorrect patch when removing multiple array elements") { json doc = R"( { "arr1": [1, 2, 3, 4] } )"_json; json expected = R"( { "arr1": [1, 2] } )"_json; // check roundtrip CHECK(doc.patch(json::diff(doc, expected)) == expected); } SECTION("issue #283 - value() does not work with _json_pointer types") { json j = { {"object", {{"key1", 1}, {"key2", 2}}}, }; int at_integer{j.at("/object/key2"_json_pointer)}; int val_integer = j.value("/object/key2"_json_pointer, 0); CHECK(at_integer == val_integer); } SECTION("issue #304 - Unused variable warning") { // code triggered a "warning: unused variable" warning and is left // here to avoid the warning in the future json object; json patch = json::array(); object = object.patch(patch); } SECTION("issue #306 - Parsing fails without space at end of file") { for (const auto* filename : { TEST_DATA_DIRECTORY "/regression/broken_file.json", TEST_DATA_DIRECTORY "/regression/working_file.json" }) { CAPTURE(filename) json j; std::ifstream f(filename); CHECK_NOTHROW(f >> j); } } SECTION("issue #310 - make json_benchmarks no longer working in 2.0.4") { for (const auto* filename : { TEST_DATA_DIRECTORY "/regression/floats.json", TEST_DATA_DIRECTORY "/regression/signed_ints.json", TEST_DATA_DIRECTORY "/regression/unsigned_ints.json", TEST_DATA_DIRECTORY "/regression/small_signed_ints.json" }) { CAPTURE(filename) json j; std::ifstream f(filename); CHECK_NOTHROW(f >> j); } } SECTION("issue #323 - add nested object capabilities to pointers") { json j; j["/this/that/2"_json_pointer] = 27; CHECK(j == json({{"this", {{"that", {nullptr, nullptr, 27}}}}})); } SECTION("issue #329 - serialized value not always can be parsed") { json _; CHECK_THROWS_WITH_AS(_ = json::parse("22e2222"), "[json.exception.out_of_range.406] number overflow parsing '22e2222'", json::out_of_range&); } SECTION("issue #360 - Loss of precision when serializing ") { auto check_roundtrip = [](double number) { CAPTURE(number) json j = number; CHECK(j.is_number_float()); std::stringstream ss; ss << j; CHECK_NOTHROW(ss >> j); CHECK(j.is_number_float()); CHECK(j.get() == number); }; check_roundtrip(100000000000.1236); check_roundtrip((std::numeric_limits::max)()); // Some more numbers which fail to roundtrip when serialized with digits10 significand digits (instead of max_digits10) check_roundtrip(1.541888611948064e-17); check_roundtrip(5.418771028591015e-16); check_roundtrip(9.398685592608595e-15); check_roundtrip(8.826843952762347e-14); check_roundtrip(8.143291313475335e-13); check_roundtrip(4.851328172762508e-12); check_roundtrip(6.677850998084358e-11); check_roundtrip(3.995398518174525e-10); check_roundtrip(1.960452605645124e-9); check_roundtrip(3.551812586302883e-8); check_roundtrip(2.947988411689261e-7); check_roundtrip(8.210166748056192e-6); check_roundtrip(6.104889704266753e-5); check_roundtrip(0.0008629954631330876); check_roundtrip(0.004936993881051611); check_roundtrip(0.08309725102608073); check_roundtrip(0.5210494268499783); check_roundtrip(6.382927930939767); check_roundtrip(59.94947245358671); check_roundtrip(361.0838651266122); check_roundtrip(4678.354596181877); check_roundtrip(61412.17658956043); check_roundtrip(725696.0799057782); check_roundtrip(2811732.583399828); check_roundtrip(30178351.07533605); check_roundtrip(689684880.3235844); check_roundtrip(5714887673.555147); check_roundtrip(84652038821.18808); check_roundtrip(156510583431.7721); check_roundtrip(5938450569021.732); check_roundtrip(83623297654460.33); check_roundtrip(701466573254773.6); check_roundtrip(1369013370304513); check_roundtrip(96963648023094720); // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions) check_roundtrip(3.478237409280108e+17); } SECTION("issue #366 - json::parse on failed stream gets stuck") { std::ifstream f("file_not_found.json"); json _; CHECK_THROWS_WITH_AS(_ = json::parse(f), "[json.exception.parse_error.101] parse error at line 1, column 1: syntax error while parsing value - unexpected end of input; expected '[', '{', or a literal", json::parse_error&); } SECTION("issue #367 - calling stream at EOF") { std::stringstream ss; json j; ss << "123"; CHECK_NOTHROW(ss >> j); // see https://github.com/nlohmann/json/issues/367#issuecomment-262841893: // ss is not at EOF; this yielded an error before the fix // (threw basic_string::append). No, it should just throw // a parse error because of the EOF. CHECK_THROWS_WITH_AS(ss >> j, "[json.exception.parse_error.101] parse error at line 1, column 1: syntax error while parsing value - unexpected end of input; expected '[', '{', or a literal", json::parse_error&); } SECTION("issue #367 - behavior of operator>> should more closely resemble that of built-in overloads") { SECTION("(empty)") { std::stringstream ss; json j; CHECK_THROWS_WITH_AS(ss >> j, "[json.exception.parse_error.101] parse error at line 1, column 1: syntax error while parsing value - unexpected end of input; expected '[', '{', or a literal", json::parse_error&); } SECTION("(whitespace)") { std::stringstream ss; ss << " "; json j; CHECK_THROWS_WITH_AS(ss >> j, "[json.exception.parse_error.101] parse error at line 1, column 4: syntax error while parsing value - unexpected end of input; expected '[', '{', or a literal", json::parse_error&); } SECTION("one value") { std::stringstream ss; ss << "111"; json j; CHECK_NOTHROW(ss >> j); CHECK(j == 111); CHECK_THROWS_WITH_AS(ss >> j, "[json.exception.parse_error.101] parse error at line 1, column 1: syntax error while parsing value - unexpected end of input; expected '[', '{', or a literal", json::parse_error&); } SECTION("one value + whitespace") { std::stringstream ss; ss << "222 \t\n"; json j; CHECK_NOTHROW(ss >> j); CHECK(j == 222); CHECK_THROWS_WITH_AS(ss >> j, "[json.exception.parse_error.101] parse error at line 2, column 1: syntax error while parsing value - unexpected end of input; expected '[', '{', or a literal", json::parse_error&); } SECTION("whitespace + one value") { std::stringstream ss; ss << "\n\t 333"; json j; CHECK_NOTHROW(ss >> j); CHECK(j == 333); CHECK_THROWS_WITH_AS(ss >> j, "[json.exception.parse_error.101] parse error at line 1, column 1: syntax error while parsing value - unexpected end of input; expected '[', '{', or a literal", json::parse_error&); } SECTION("three values") { std::stringstream ss; ss << " 111 \n222\n \n 333"; json j; CHECK_NOTHROW(ss >> j); CHECK(j == 111); CHECK_NOTHROW(ss >> j); CHECK(j == 222); CHECK_NOTHROW(ss >> j); CHECK(j == 333); CHECK_THROWS_WITH_AS(ss >> j, "[json.exception.parse_error.101] parse error at line 1, column 1: syntax error while parsing value - unexpected end of input; expected '[', '{', or a literal", json::parse_error&); } SECTION("literals without whitespace") { std::stringstream ss; ss << "truefalsenull\"\""; json j; CHECK_NOTHROW(ss >> j); CHECK(j == true); CHECK_NOTHROW(ss >> j); CHECK(j == false); CHECK_NOTHROW(ss >> j); CHECK(j == nullptr); CHECK_NOTHROW(ss >> j); CHECK(j == ""); CHECK_THROWS_WITH_AS(ss >> j, "[json.exception.parse_error.101] parse error at line 1, column 1: syntax error while parsing value - unexpected end of input; expected '[', '{', or a literal", json::parse_error&); } SECTION("example from #529") { std::stringstream ss; ss << "{\n \"one\" : 1,\n \"two\" : 2\n}\n{\n \"three\" : 3\n}"; json j; CHECK_NOTHROW(ss >> j); CHECK(j == json({{"one", 1}, {"two", 2}})); CHECK_NOTHROW(ss >> j); CHECK(j == json({{"three", 3}})); CHECK_THROWS_WITH_AS(ss >> j, "[json.exception.parse_error.101] parse error at line 1, column 1: syntax error while parsing value - unexpected end of input; expected '[', '{', or a literal", json::parse_error&); } SECTION("second example from #529") { std::string str = "{\n\"one\" : 1,\n\"two\" : 2\n}\n{\n\"three\" : 3\n}"; { std::ofstream file("test.json"); file << str; } std::ifstream stream("test.json", std::ifstream::in); json val; size_t i = 0; while (stream.peek() != EOF) { CAPTURE(i) CHECK_NOTHROW(stream >> val); CHECK(i < 2); if (i == 0) { CHECK(val == json({{"one", 1}, {"two", 2}})); } if (i == 1) { CHECK(val == json({{"three", 3}})); } ++i; } static_cast(std::remove("test.json")); } } SECTION("issue #389 - Integer-overflow (OSS-Fuzz issue 267)") { // original test case json j1 = json::parse("-9223372036854775808"); CHECK(j1.is_number_integer()); CHECK(j1.get() == INT64_MIN); // edge case (+1; still an integer) json j2 = json::parse("-9223372036854775807"); CHECK(j2.is_number_integer()); CHECK(j2.get() == INT64_MIN + 1); // edge case (-1; overflow -> floats) json j3 = json::parse("-9223372036854775809"); CHECK(j3.is_number_float()); } SECTION("issue #380 - bug in overflow detection when parsing integers") { json j = json::parse("166020696663385964490"); CHECK(j.is_number_float()); CHECK(j.get() == 166020696663385964490.0); } SECTION("issue #405 - Heap-buffer-overflow (OSS-Fuzz issue 342)") { // original test case std::vector vec {0x65, 0xf5, 0x0a, 0x48, 0x21}; json _; CHECK_THROWS_WITH_AS(_ = json::from_cbor(vec), "[json.exception.parse_error.110] parse error at byte 6: syntax error while parsing CBOR string: unexpected end of input", json::parse_error&); } SECTION("issue #407 - Heap-buffer-overflow (OSS-Fuzz issue 343)") { json _; // original test case: incomplete float64 std::vector vec1 {0xcb, 0x8f, 0x0a}; CHECK_THROWS_WITH_AS(_ = json::from_msgpack(vec1), "[json.exception.parse_error.110] parse error at byte 4: syntax error while parsing MessagePack number: unexpected end of input", json::parse_error&); // related test case: incomplete float32 std::vector vec2 {0xca, 0x8f, 0x0a}; CHECK_THROWS_WITH_AS(_ = json::from_msgpack(vec2), "[json.exception.parse_error.110] parse error at byte 4: syntax error while parsing MessagePack number: unexpected end of input", json::parse_error&); // related test case: incomplete Half-Precision Float (CBOR) std::vector vec3 {0xf9, 0x8f}; CHECK_THROWS_WITH_AS(_ = json::from_cbor(vec3), "[json.exception.parse_error.110] parse error at byte 3: syntax error while parsing CBOR number: unexpected end of input", json::parse_error&); // related test case: incomplete Single-Precision Float (CBOR) std::vector vec4 {0xfa, 0x8f, 0x0a}; CHECK_THROWS_WITH_AS(_ = json::from_cbor(vec4), "[json.exception.parse_error.110] parse error at byte 4: syntax error while parsing CBOR number: unexpected end of input", json::parse_error&); // related test case: incomplete Double-Precision Float (CBOR) std::vector vec5 {0xfb, 0x8f, 0x0a}; CHECK_THROWS_WITH_AS(_ = json::from_cbor(vec5), "[json.exception.parse_error.110] parse error at byte 4: syntax error while parsing CBOR number: unexpected end of input", json::parse_error&); } SECTION("issue #408 - Heap-buffer-overflow (OSS-Fuzz issue 344)") { json _; // original test case std::vector vec1 {0x87}; CHECK_THROWS_WITH_AS(_ = json::from_msgpack(vec1), "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing MessagePack string: unexpected end of input", json::parse_error&); // more test cases for MessagePack for (auto b : { 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, // fixmap 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, // fixarray 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, // fixstr 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf }) { std::vector vec(1, static_cast(b)); CHECK_THROWS_AS(_ = json::from_msgpack(vec), json::parse_error&); } // more test cases for CBOR for (auto b : { 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, // UTF-8 string 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, // array 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7 // map }) { std::vector vec(1, static_cast(b)); CHECK_THROWS_AS(_ = json::from_cbor(vec), json::parse_error&); } // special case: empty input std::vector vec2; CHECK_THROWS_WITH_AS(_ = json::from_cbor(vec2), "[json.exception.parse_error.110] parse error at byte 1: syntax error while parsing CBOR value: unexpected end of input", json::parse_error&); CHECK_THROWS_WITH_AS(_ = json::from_msgpack(vec2), "[json.exception.parse_error.110] parse error at byte 1: syntax error while parsing MessagePack value: unexpected end of input", json::parse_error&); } SECTION("issue #411 - Heap-buffer-overflow (OSS-Fuzz issue 366)") { json _; // original test case: empty UTF-8 string (indefinite length) std::vector vec1 {0x7f}; CHECK_THROWS_WITH_AS(_ = json::from_cbor(vec1), "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing CBOR string: unexpected end of input", json::parse_error&); // related test case: empty array (indefinite length) std::vector vec2 {0x9f}; CHECK_THROWS_WITH_AS(_ = json::from_cbor(vec2), "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing CBOR value: unexpected end of input", json::parse_error&); // related test case: empty map (indefinite length) std::vector vec3 {0xbf}; CHECK_THROWS_WITH_AS(_ = json::from_cbor(vec3), "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing CBOR string: unexpected end of input", json::parse_error&); } SECTION("issue #412 - Heap-buffer-overflow (OSS-Fuzz issue 367)") { // original test case std::vector vec { 0xab, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x00, 0x00, 0x00, 0x60, 0xab, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x00, 0x00, 0x00, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0xa0, 0x9f, 0x9f, 0x97, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60 }; json _; CHECK_THROWS_WITH_AS(_ = json::from_cbor(vec), "[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing CBOR string: expected length specification (0x60-0x7B) or indefinite string type (0x7F); last byte: 0x98", json::parse_error&); // related test case: nonempty UTF-8 string (indefinite length) std::vector vec1 {0x7f, 0x61, 0x61}; CHECK_THROWS_WITH_AS(_ = json::from_cbor(vec1), "[json.exception.parse_error.110] parse error at byte 4: syntax error while parsing CBOR string: unexpected end of input", json::parse_error&); // related test case: nonempty array (indefinite length) std::vector vec2 {0x9f, 0x01}; CHECK_THROWS_WITH_AS(_ = json::from_cbor(vec2), "[json.exception.parse_error.110] parse error at byte 3: syntax error while parsing CBOR value: unexpected end of input", json::parse_error&); // related test case: nonempty map (indefinite length) std::vector vec3 {0xbf, 0x61, 0x61, 0x01}; CHECK_THROWS_WITH_AS(_ = json::from_cbor(vec3), "[json.exception.parse_error.110] parse error at byte 5: syntax error while parsing CBOR string: unexpected end of input", json::parse_error&); } SECTION("issue #414 - compare with literal 0)") { #define CHECK_TYPE(v) \ CHECK((json(v) == (v)));\ CHECK(((v) == json(v)));\ CHECK_FALSE((json(v) != (v)));\ CHECK_FALSE(((v) != json(v))); CHECK_TYPE(nullptr) CHECK_TYPE(0) CHECK_TYPE(0u) CHECK_TYPE(0L) CHECK_TYPE(0.0) CHECK_TYPE("") // NOLINT(readability-container-size-empty) #undef CHECK_TYPE } SECTION("issue #416 - Use-of-uninitialized-value (OSS-Fuzz issue 377)") { // original test case std::vector vec1 { 0x94, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0x3a, 0x96, 0x96, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0x71, 0xb4, 0xb4, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0x3a, 0x96, 0x96, 0xb4, 0xb4, 0xfa, 0x94, 0x94, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0xfa }; json _; CHECK_THROWS_WITH_AS(_ = json::from_cbor(vec1), "[json.exception.parse_error.113] parse error at byte 13: syntax error while parsing CBOR string: expected length specification (0x60-0x7B) or indefinite string type (0x7F); last byte: 0xB4", json::parse_error&); // related test case: double-precision std::vector vec2 { 0x94, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0x3a, 0x96, 0x96, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0x71, 0xb4, 0xb4, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0x3a, 0x96, 0x96, 0xb4, 0xb4, 0xfa, 0x94, 0x94, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0xfb }; CHECK_THROWS_WITH_AS(_ = json::from_cbor(vec2), "[json.exception.parse_error.113] parse error at byte 13: syntax error while parsing CBOR string: expected length specification (0x60-0x7B) or indefinite string type (0x7F); last byte: 0xB4", json::parse_error&); } SECTION("issue #452 - Heap-buffer-overflow (OSS-Fuzz issue 585)") { std::vector vec = {'-', '0', '1', '2', '2', '7', '4'}; json _; CHECK_THROWS_AS(_ = json::parse(vec), json::parse_error&); } SECTION("issue #454 - doubles are printed as integers") { json j = R"({"bool_value":true,"double_value":2.0,"int_value":10,"level1":{"list_value":[3,"hi",false],"tmp":5.0},"string_value":"hello"})"_json; CHECK(j["double_value"].is_number_float()); } #if JSON_USE_IMPLICIT_CONVERSIONS SECTION("issue #464 - VS2017 implicit to std::string conversion fix") { json v = "test"; std::string test; test = v; CHECK(v == "test"); } #endif SECTION("issue #465 - roundtrip error while parsing 1000000000000000010E5") { json j1 = json::parse("1000000000000000010E5"); std::string s1 = j1.dump(); json j2 = json::parse(s1); std::string s2 = j2.dump(); CHECK(s1 == s2); } #if JSON_USE_IMPLICIT_CONVERSIONS SECTION("issue #473 - inconsistent behavior in conversion to array type") { json j_array = {1, 2, 3, 4}; json j_number = 42; json j_null = nullptr; SECTION("std::vector") { auto create = [](const json & j) { std::vector v = j; }; CHECK_NOTHROW(create(j_array)); CHECK_THROWS_WITH_AS(create(j_number), "[json.exception.type_error.302] type must be array, but is number", json::type_error&); CHECK_THROWS_WITH_AS(create(j_null), "[json.exception.type_error.302] type must be array, but is null", json::type_error&); } SECTION("std::list") { auto create = [](const json & j) { std::list v = j; }; CHECK_NOTHROW(create(j_array)); CHECK_THROWS_WITH_AS(create(j_number), "[json.exception.type_error.302] type must be array, but is number", json::type_error&); CHECK_THROWS_WITH_AS(create(j_null), "[json.exception.type_error.302] type must be array, but is null", json::type_error&); } SECTION("std::forward_list") { auto create = [](const json & j) { std::forward_list v = j; }; CHECK_NOTHROW(create(j_array)); CHECK_THROWS_WITH_AS(create(j_number), "[json.exception.type_error.302] type must be array, but is number", json::type_error&); CHECK_THROWS_WITH_AS(create(j_null), "[json.exception.type_error.302] type must be array, but is null", json::type_error&); } } #endif SECTION("issue #486 - json::value_t can't be a map's key type in VC++ 2015") { // the code below must compile with MSVC std::map jsonTypes ; jsonTypes[json::value_t::array] = "array"; } SECTION("issue #494 - conversion from vector to json fails to build") { std::vector boolVector = {false, true, false, false}; json j; j["bool_vector"] = boolVector; CHECK(j["bool_vector"].dump() == "[false,true,false,false]"); } SECTION("issue #504 - assertion error (OSS-Fuzz 856)") { std::vector vec1 = {0xf9, 0xff, 0xff, 0x4a, 0x3a, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x37, 0x02, 0x38}; json j1 = json::from_cbor(vec1, false); // step 2: round trip std::vector vec2 = json::to_cbor(j1); // parse serialization json j2 = json::from_cbor(vec2); // NaN is dumped to "null" CHECK(j2.is_number_float()); CHECK(std::isnan(j2.get())); CHECK(j2.dump() == "null"); // check if serializations match CHECK(json::to_cbor(j2) == vec2); } SECTION("issue #512 - use of overloaded operator '<=' is ambiguous") { json j; j["a"] = 5; // json op scalar CHECK(j["a"] == 5); CHECK(j["a"] != 4); CHECK(j["a"] <= 7); CHECK(j["a"] < 7); CHECK(j["a"] >= 3); CHECK(j["a"] > 3); CHECK(!(j["a"] <= 4)); CHECK(!(j["a"] < 4)); CHECK(!(j["a"] >= 6)); CHECK(!(j["a"] > 6)); // scalar op json CHECK(5 == j["a"]); CHECK(4 != j["a"]); CHECK(7 >= j["a"]); CHECK(7 > j["a"]); CHECK(3 <= j["a"]); CHECK(3 < j["a"]); CHECK(!(4 >= j["a"])); CHECK(!(4 > j["a"])); CHECK(!(6 <= j["a"])); CHECK(!(6 < j["a"])); } SECTION("issue #575 - heap-buffer-overflow (OSS-Fuzz 1400)") { json _; std::vector vec = {'"', '\\', '"', 'X', '"', '"'}; CHECK_THROWS_AS(_ = json::parse(vec), json::parse_error&); } #if JSON_USE_IMPLICIT_CONVERSIONS SECTION("issue #600 - how does one convert a map in Json back to std::map?") { SECTION("example 1") { // create a map std::map m1 {{"key", 1}}; // create and print a JSON from the map json j = m1; // get the map out of JSON std::map m2 = j; // make sure the roundtrip succeeds CHECK(m1 == m2); } SECTION("example 2") { // create a map std::map m1 {{"key", "val"}}; // create and print a JSON from the map json j = m1; // get the map out of JSON std::map m2 = j; // make sure the roundtrip succeeds CHECK(m1 == m2); } } #endif SECTION("issue #602 - BOM not skipped when using json:parse(iterator)") { std::string i = "\xef\xbb\xbf{\n \"foo\": true\n}"; json _; CHECK_NOTHROW(_ = json::parse(i.begin(), i.end())); } #if JSON_USE_IMPLICIT_CONVERSIONS SECTION("issue #702 - conversion from valarray to json fails to build") { SECTION("original example") { std::valarray v; nlohmann::json j; j["test"] = v; } SECTION("full example") { std::valarray v = {1.2, 2.3, 3.4, 4.5}; json j = v; std::valarray vj = j; CHECK(j == json(vj)); CHECK(v.size() == vj.size()); for (size_t i = 0; i < v.size(); ++i) { CHECK(v[i] == vj[i]); CHECK(v[i] == j[i]); } CHECK_THROWS_WITH_AS(json().get>(), "[json.exception.type_error.302] type must be array, but is null", json::type_error&); } } #endif SECTION("issue #367 - Behavior of operator>> should more closely resemble that of built-in overloads.") { SECTION("example 1") { std::istringstream i1_2_3( R"({"first": "one" }{"second": "two"}3)" ); json j1; json j2; json j3; i1_2_3 >> j1; i1_2_3 >> j2; i1_2_3 >> j3; auto m1 = j1.get>(); auto m2 = j2.get>(); int i3{j3}; CHECK( m1 == ( std::map {{ "first", "one" }} )); CHECK( m2 == ( std::map {{ "second", "two" }} )); CHECK( i3 == 3 ); } } SECTION("issue #714 - throw std::ios_base::failure exception when failbit set to true") { { std::ifstream is; is.exceptions( is.exceptions() | std::ios_base::failbit | std::ios_base::badbit ); // handle different exceptions as 'file not found', 'permission denied' is.open(TEST_DATA_DIRECTORY "/regression/working_file.json"); json _; CHECK_NOTHROW(_ = nlohmann::json::parse(is)); } { std::ifstream is; is.exceptions( is.exceptions() | std::ios_base::failbit | std::ios_base::badbit ); // handle different exceptions as 'file not found', 'permission denied' is.open(TEST_DATA_DIRECTORY "/json_nlohmann_tests/all_unicode.json.cbor", std::ios_base::in | std::ios_base::binary); json _; CHECK_NOTHROW(_ = nlohmann::json::from_cbor(is)); } } SECTION("issue #805 - copy constructor is used with std::initializer_list constructor.") { nocopy n; json j; j = {{"nocopy", n}}; CHECK(j["nocopy"]["val"] == 0); } SECTION("issue #838 - incorrect parse error with binary data in keys") { std::array key1 = {{ 103, 92, 117, 48, 48, 48, 55, 92, 114, 215, 126, 214, 95, 92, 34, 174, 40, 71, 38, 174, 40, 71, 38, 223, 134, 247, 127, 0 }}; std::string key1_str(reinterpret_cast(key1.data())); json j = key1_str; CHECK_THROWS_WITH_AS(j.dump(), "[json.exception.type_error.316] invalid UTF-8 byte at index 10: 0x7E", json::type_error&); } #if JSON_USE_IMPLICIT_CONVERSIONS SECTION("issue #843 - converting to array not working") { json j; std::array ar = {{1, 1, 1, 1}}; j = ar; ar = j; } #endif SECTION("issue #894 - invalid RFC6902 copy operation succeeds") { auto model = R"({ "one": { "two": { "three": "hello", "four": 42 } } })"_json; auto p1 = R"([{"op": "move", "from": "/one/two/three", "path": "/a/b/c"}])"_json; CHECK_THROWS_WITH_AS(model.patch(p1), "[json.exception.out_of_range.403] key 'a' not found", json::out_of_range&); auto p2 = R"([{"op": "copy", "from": "/one/two/three", "path": "/a/b/c"}])"_json; CHECK_THROWS_WITH_AS(model.patch(p2), "[json.exception.out_of_range.403] key 'a' not found", json::out_of_range&); } SECTION("issue #961 - incorrect parsing of indefinite length CBOR strings") { std::vector v_cbor = { 0x7F, 0x64, 'a', 'b', 'c', 'd', 0x63, '1', '2', '3', 0xFF }; json j = json::from_cbor(v_cbor); CHECK(j == "abcd123"); } SECTION("issue #962 - Timeout (OSS-Fuzz 6034)") { json _; std::vector v_ubjson = {'[', '$', 'Z', '#', 'L', 0x78, 0x28, 0x00, 0x68, 0x28, 0x69, 0x69, 0x17}; CHECK_THROWS_AS(_ = json::from_ubjson(v_ubjson), json::out_of_range&); //CHECK_THROWS_WITH(json::from_ubjson(v_ubjson), // "[json.exception.out_of_range.408] excessive array size: 8658170730974374167"); v_ubjson[0] = '{'; CHECK_THROWS_AS(_ = json::from_ubjson(v_ubjson), json::out_of_range&); //CHECK_THROWS_WITH(json::from_ubjson(v_ubjson), // "[json.exception.out_of_range.408] excessive object size: 8658170730974374167"); } SECTION("issue #971 - Add a SAX parser - late bug") { // a JSON text const auto* text = R"( { "Image": { "Width": 800, "Height": 600, "Title": "View from 15th Floor", "Thumbnail": { "Url": "http://www.example.com/image/481989943", "Height": 125, "Width": 100 }, "Animated" : false, "IDs": [116, 943, 234, 38793] } } )"; // define parser callback json::parser_callback_t cb = [](int /*depth*/, json::parse_event_t event, json & parsed) { // skip object elements with key "Thumbnail" return !(event == json::parse_event_t::key && parsed == json("Thumbnail")); }; // parse (with callback) and serialize JSON json j_filtered = json::parse(text, cb); CHECK(j_filtered == R"({"Image":{"Animated":false,"Height":600,"IDs":[116,943,234,38793], "Title":"View from 15th Floor","Width":800}})"_json); } SECTION("issue #972 - Segmentation fault on G++ when trying to assign json string literal to custom json type") { my_json foo = R"([1, 2, 3])"_json; } SECTION("issue #977 - Assigning between different json types") { foo_json lj = ns::foo{3}; ns::foo ff(lj); CHECK(lj.is_object()); CHECK(lj.size() == 1); CHECK(lj["x"] == 3); CHECK(ff.x == 3); nlohmann::json nj = lj; // This line works as expected } } #if !defined(JSON_NOEXCEPTION) TEST_CASE("regression tests, exceptions dependent") { SECTION("issue #1340 - eof not set on exhausted input stream") { std::stringstream s("{}{}"); json j; s >> j; s >> j; CHECK_THROWS_AS(s >> j, json::parse_error const&); CHECK(s.eof()); } } #endif ///////////////////////////////////////////////////////////////////// // for #1642 ///////////////////////////////////////////////////////////////////// // the code below fails with Clang on Windows, so we need to exclude it there #if DOCTEST_CLANG && (defined(WIN32) || defined(_WIN32) || defined(__WIN32) && !defined(__CYGWIN__)) #else template class array {}; template class object {}; template class string {}; template class number_integer {}; template class number_unsigned {}; template class number_float {}; #endif