// // Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) // Copyright (c) 2020 Krystian Stasiowski (sdkrystian@gmail.com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // // Official repository: https://github.com/boostorg/json // // Test that header file is self-contained. #include #include #include #include #include #include #include "parse-vectors.hpp" #include "test.hpp" #include "test_suite.hpp" BOOST_JSON_NS_BEGIN BOOST_STATIC_ASSERT( std::is_nothrow_destructible::value ); class stream_parser_test { public: ::test_suite::log_type log; //------------------------------------------------------ void testCtors() { // stream_parser(stream_parser const&) BOOST_STATIC_ASSERT( ! std::is_copy_constructible::value); // operator=(stream_parser const&) BOOST_STATIC_ASSERT( ! std::is_copy_assignable::value); // ~stream_parser() { { stream_parser p; } { stream_parser p; p.reset(make_shared_resource< monotonic_resource>()); } } // stream_parser(storage_ptr, parse_options, unsigned char*, size_t) { unsigned char buf[256]; stream_parser p( storage_ptr(), parse_options(), &buf[0], sizeof(buf)); } // stream_parser() { stream_parser p; } // stream_parser(storage_ptr, parse_options) { stream_parser p(storage_ptr{}, parse_options()); } // stream_parser(storage_ptr) { stream_parser p(storage_ptr{}); } // stream_parser(storage_ptr, parse_options, unsigned char[]) { unsigned char buf[256]; stream_parser p( storage_ptr(), parse_options(), buf); } #if defined(__cpp_lib_byte) // stream_parser(storage_ptr, parse_options, std::byte*, size_t) { std::byte buf[256]; stream_parser p( storage_ptr(), parse_options(), &buf[0], sizeof(buf)); } // stream_parser(storage_ptr, parse_options, std::byte[]) { std::byte buf[256]; stream_parser p( storage_ptr(), parse_options(), buf); } #endif // stream_parser(storage_ptr, parse_options, unsigned char[], size_t) { unsigned char buf[256]; stream_parser p( storage_ptr(), parse_options(), buf, sizeof(buf)); } #if defined(__cpp_lib_byte) // stream_parser(storage_ptr, parse_options, std::byte[], size_t) { std::byte buf[256]; stream_parser p( storage_ptr(), parse_options(), buf, sizeof(buf)); } #endif } void testMembers() { // write_some(char const*, size_t, error_code&) // write_some(char const*, size_t, std::error_code&) // write_some(string_view, error_code&) // write_some(string_view, std::error_code&) { { stream_parser p; error_code ec; BOOST_TEST(p.write_some( "[]*", ec) == 2); BOOST_TEST(! ec); } { stream_parser p; std::error_code ec; BOOST_TEST(p.write_some( "[]*", ec) == 2); BOOST_TEST(! ec); } { stream_parser p; error_code ec; BOOST_TEST(p.write_some( "[*", ec) == 1); BOOST_TEST(ec); } { stream_parser p; std::error_code ec; BOOST_TEST(p.write_some( "[*", ec) == 1); BOOST_TEST(ec); } } // write_some(char const*, size_t) // write_some(string_view) { { stream_parser p; BOOST_TEST( p.write_some("[]*") == 2); } { stream_parser p; BOOST_TEST_THROWS( p.write_some("[*"), system_error); } } // write(char const*, size_t, error_code&) // write(char const*, size_t, std::error_code&) // write(string_view, error_code&) // write(string_view, std::error_code&) { { stream_parser p; error_code ec; BOOST_TEST(p.write( "null", ec) == 4); BOOST_TEST(! ec); } { stream_parser p; std::error_code ec; BOOST_TEST(p.write( "null", ec) == 4); BOOST_TEST(! ec); } { stream_parser p; error_code ec; p.write("[]*", ec), BOOST_TEST( ec == error::extra_data); BOOST_TEST(ec.has_location()); } { stream_parser p; std::error_code ec; p.write("[]*", ec), BOOST_TEST(ec == error::extra_data); } } // write(char const*, size_t) // write(string_view) { { stream_parser p; BOOST_TEST(p.write( "null") == 4); } { stream_parser p; BOOST_TEST_THROWS( p.write("[]*"), system_error); } } // finish() // finish(error_code&) // finish(std::error_code&) { { stream_parser p; p.write("1"); BOOST_TEST(! p.done()); p.finish(); BOOST_TEST(p.done()); } { stream_parser p; BOOST_TEST(! p.done()); p.write("1."); BOOST_TEST_THROWS( p.finish(), system_error); } { stream_parser p; p.write("[1,2"); error_code ec; p.finish(ec); BOOST_TEST( ec == error::incomplete); BOOST_TEST(ec.has_location()); } { stream_parser p; p.write("[1,2"); error_code ec; p.finish(ec); BOOST_TEST_THROWS( p.finish(), system_error); } { stream_parser p; p.write("[1,2"); std::error_code ec; p.finish(ec); BOOST_TEST(ec == error::incomplete); } { stream_parser p; p.write("[1,2"); std::error_code ec; p.finish(ec); BOOST_TEST_THROWS( p.finish(), system_error); } } // release() { { stream_parser p; BOOST_TEST( p.write_some("[") == 1); BOOST_TEST(! p.done()); BOOST_TEST_THROWS( p.release(), system_error); } { stream_parser p; BOOST_TEST( p.write_some("[]*") == 2); BOOST_TEST(p.done()); p.release(); } { stream_parser p; p.write("["); BOOST_TEST(! p.done()); BOOST_TEST_THROWS( p.release(), system_error); } { stream_parser p; error_code ec; p.write("[]*", ec); BOOST_TEST( ec == error::extra_data); BOOST_TEST(ec.has_location()); BOOST_TEST(! p.done()); BOOST_TEST_THROWS( p.release(), system_error); } } } //------------------------------------------------------ static value from_string_test( string_view s, storage_ptr sp = {}, const parse_options& po = parse_options()) { stream_parser p(storage_ptr(), po); error_code ec; p.reset(std::move(sp)); p.write(s.data(), s.size(), ec); if(BOOST_TEST(! ec)) p.finish(ec); BOOST_TEST(! ec); return p.release(); } void static check_round_trip(value const& jv1, const parse_options& po = parse_options()) { auto const s2 = //to_string_test(jv1); // use this if serializer is broken serialize(jv1); auto jv2 = from_string_test(s2, {}, po); BOOST_TEST(equal(jv1, jv2)); } template void static grind_one( string_view s, storage_ptr sp, F const& f, const parse_options& po = parse_options()) { auto const jv = from_string_test(s, sp, po); f(jv, po); } static void grind_one(string_view s) { auto const jv = from_string_test(s); check_round_trip(jv); } template static void grind(string_view s, F const& f, const parse_options& po = parse_options()) { try { grind_one(s, {}, f, po); fail_loop([&](storage_ptr const& sp) { grind_one(s, sp, f, po); }); if(s.size() > 1) { // Destroy the stream_parser at every // split point to check leaks. for(std::size_t i = 1; i < s.size(); ++i) { fail_resource mr; mr.fail_max = 0; stream_parser p(storage_ptr(), po); error_code ec; p.reset(&mr); p.write(s.data(), i, ec); if(BOOST_TEST(! ec)) p.write( s.data() + i, s.size() - i, ec); if(BOOST_TEST(! ec)) p.finish(ec); if(BOOST_TEST(! ec)) f(p.release(), po); } } } catch(std::exception const&) { BOOST_TEST_FAIL(); } } static void grind(string_view s, const parse_options& po = parse_options()) { grind(s, [](value const& jv, const parse_options& po) { check_round_trip(jv, po); }, po); } static void grind_int64(string_view s, int64_t v) { grind(s, [v](value const& jv, const parse_options&) { if(! BOOST_TEST(jv.is_int64())) return; BOOST_TEST(jv.get_int64() == v); }); } static void grind_uint64(string_view s, uint64_t v) { grind(s, [v](value const& jv, const parse_options&) { if(! BOOST_TEST(jv.is_uint64())) return; BOOST_TEST(jv.get_uint64() == v); }); } static void grind_double(string_view s, double v) { grind(s, [v](value const& jv, const parse_options&) { if(! BOOST_TEST(jv.is_double())) return; BOOST_TEST(jv.get_double() == v); }); } //------------------------------------------------------ void testNull() { grind("null"); grind(" null"); grind(" null"); grind("null\n"); grind("null\n\n"); grind("\r null\t "); } void testBool() { grind("true"); grind(" true"); grind(" true"); grind("true\n"); grind("true\n\n"); grind("\r true\t "); grind("false"); grind(" false"); grind(" false"); grind("false\n"); grind("false\n\n"); grind("\r false\t "); } //------------------------------------------------------ void testString() { grind("\"\""); grind("\"x\""); grind(" \"x\""); grind(" \"x\""); grind("\"x\"\n"); grind("\"x\"\n\n"); grind("\r \"x\"\t "); grind("\"abcdefghij\""); grind("\"" "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz" "\""); grind("\"" "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz" "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz" "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz" "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz" "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz" "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz" "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz" "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz" "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz" "\""); // escapes grind("\"\\\"\""); grind("\"\\\\\""); grind("\"\\/\""); grind("\"\\b\""); grind("\"\\f\""); grind("\"\\n\""); grind("\"\\r\""); grind("\"\\t\""); // unicode grind("\"\\u0000\""); grind("\"\\ud7fF\""); grind("\"\\ue000\""); grind("\"\\ufFfF\""); grind("\"\\ud800\\udc00\""); grind("\"\\udbff\\udffF\""); // big string { std::string const big(4000, '*'); { std::string js; js = "\"" + big + "\""; auto const N = js.size() / 2; error_code ec; stream_parser p; p.write(js.data(), N, ec); if(BOOST_TEST(! ec)) { p.write(js.data() + N, js.size() - N, ec); if(BOOST_TEST(! ec)) p.finish(ec); if(BOOST_TEST(! ec)) check_round_trip( p.release()); } } } } //------------------------------------------------------ struct f_boost { static string_view name() noexcept { return "boost"; } double operator()(string_view s) const { BOOST_TEST_CHECKPOINT(); error_code ec; stream_parser p; p.write(s.data(), s.size(), ec); if(BOOST_TEST(! ec)) p.finish(ec); if(! BOOST_TEST(! ec)) return 0; auto const jv = p.release(); double const d = jv.as_double(); grind_double(s, d); return d; } }; bool within_1ulp(double x, double y) { std::uint64_t bx, by; std::memcpy(&bx, &x, sizeof(x)); std::memcpy(&by, &y, sizeof(y)); auto diff = bx - by; switch (diff) { case 0: case 1: case 0xffffffffffffffff: return true; default: break; } return false; } // Verify that f converts to the // same double produced by `strtod`. // Requires `s` is not represented by an integral type. template void fc(std::string const& s, F const& f) { char* str_end; double const need = std::strtod(s.c_str(), &str_end); // BOOST_TEST(str_end == &s.back() + 1); double const got = f(s); auto same = got == need; auto close = same ? true : within_1ulp(got, need); if( !BOOST_TEST(close) ) { std::cerr << "Failure on '" << s << "': " << got << " != " << need << "\n"; } } void fc(std::string const& s) { fc(s, f_boost{}); fc(s + std::string( 64, ' ' ), f_boost{}); } void testNumber() { grind("0"); grind(" 0"); grind(" 0"); grind("0\n"); grind("0\n\n"); grind("\r 0\t "); grind_int64( "-9223372036854775808", INT64_MIN); grind_int64( "-9223372036854775807", -9223372036854775807); grind_int64( "-999999999999999999", -999999999999999999); grind_int64( "-99999999999999999", -99999999999999999); grind_int64( "-9999999999999999", -9999999999999999); grind_int64( "-999999999999999", -999999999999999); grind_int64( "-99999999999999", -99999999999999); grind_int64( "-9999999999999", -9999999999999); grind_int64( "-999999999999", -999999999999); grind_int64( "-99999999999", -99999999999); grind_int64( "-9999999999", -9999999999); grind_int64( "-999999999", -999999999); grind_int64( "-99999999", -99999999); grind_int64( "-9999999", -9999999); grind_int64( "-999999", -999999); grind_int64( "-99999", -99999); grind_int64( "-9999", -9999); grind_int64( "-999", -999); grind_int64( "-99", -99); grind_int64( "-9", -9); grind_int64( "-0", 0); grind_int64( "0", 0); grind_int64( "1", 1); grind_int64( "9", 9); grind_int64( "99", 99); grind_int64( "999", 999); grind_int64( "9999", 9999); grind_int64( "99999", 99999); grind_int64( "999999", 999999); grind_int64( "9999999", 9999999); grind_int64( "99999999", 99999999); grind_int64( "999999999", 999999999); grind_int64( "9999999999", 9999999999); grind_int64( "99999999999", 99999999999); grind_int64( "999999999999", 999999999999); grind_int64( "9999999999999", 9999999999999); grind_int64( "99999999999999", 99999999999999); grind_int64( "999999999999999", 999999999999999); grind_int64( "9999999999999999", 9999999999999999); grind_int64( "99999999999999999", 99999999999999999); grind_int64( "999999999999999999", 999999999999999999); grind_int64( "9223372036854775807", INT64_MAX); grind_uint64( "9223372036854775808", 9223372036854775808ULL); grind_uint64( "9999999999999999999", 9999999999999999999ULL); grind_uint64( "18446744073709551615", UINT64_MAX); grind_double("-1.010", -1.01); grind_double("-0.010", -0.01); grind_double("-0.0", -0.0); grind_double("-0e0", -0.0); grind_double( "18446744073709551616", 1.8446744073709552e+19); grind_double("-18446744073709551616", -1.8446744073709552e+19); grind_double( "18446744073709551616.0", 1.8446744073709552e+19); grind_double( "18446744073709551616.00009", 1.8446744073709552e+19); grind_double( "1844674407370955161600000", 1.8446744073709552e+24); grind_double("-1844674407370955161600000", -1.8446744073709552e+24); grind_double( "1844674407370955161600000.0", 1.8446744073709552e+24); grind_double( "1844674407370955161600000.00009", 1.8446744073709552e+24); grind_double( "1.0", 1.0); grind_double( "1.1", 1.1); grind_double( "1.11", 1.11); grind_double( "1.11111", 1.11111); grind_double( "11.1111", 11.1111); grind_double( "111.111", 111.111); fc("-999999999999999999999"); fc("-100000000000000000009"); fc("-10000000000000000000"); fc("-9223372036854775809"); fc("18446744073709551616"); fc("99999999999999999999"); fc("999999999999999999999"); fc("1000000000000000000000"); fc("9999999999999999999999"); fc("99999999999999999999999"); fc("-0.9999999999999999999999"); fc("-0.9999999999999999"); fc("-0.9007199254740991"); fc("-0.999999999999999"); fc("-0.99999999999999"); fc("-0.9999999999999"); fc("-0.999999999999"); fc("-0.99999999999"); fc("-0.9999999999"); fc("-0.999999999"); fc("-0.99999999"); fc("-0.9999999"); fc("-0.999999"); fc("-0.99999"); fc("-0.9999"); fc("-0.8125"); fc("-0.999"); fc("-0.99"); fc("-1.0"); fc("-0.9"); fc("-0.0"); fc("0.0"); fc("0.9"); fc("0.99"); fc("0.999"); fc("0.8125"); fc("0.9999"); fc("0.99999"); fc("0.999999"); fc("0.9999999"); fc("0.99999999"); fc("0.999999999"); fc("0.9999999999"); fc("0.99999999999"); fc("0.999999999999"); fc("0.9999999999999"); fc("0.99999999999999"); fc("0.999999999999999"); fc("0.9007199254740991"); fc("0.9999999999999999"); fc("0.9999999999999999999999"); fc("0.999999999999999999999999999"); fc("-1e308"); fc("-1e-308"); fc("-9999e300"); fc("-999e100"); fc("-99e10"); fc("-9e1"); fc("9e1"); fc("99e10"); fc("999e100"); fc("9999e300"); fc("999999999999999999.0"); fc("999999999999999999999.0"); fc("999999999999999999999e5"); fc("999999999999999999999.0e5"); fc("0.00000000000000001"); fc("-1e-1"); fc("-1e0"); fc("-1e1"); fc("0e0"); fc("1e0"); fc("1e10"); fc("0." "00000000000000000000000000000000000000000000000000" // 50 zeroes "1e50"); fc("-0." "00000000000000000000000000000000000000000000000000" // 50 zeroes "1e50"); fc("0." "00000000000000000000000000000000000000000000000000" "00000000000000000000000000000000000000000000000000" "00000000000000000000000000000000000000000000000000" "00000000000000000000000000000000000000000000000000" "00000000000000000000000000000000000000000000000000" "00000000000000000000000000000000000000000000000000" "00000000000000000000000000000000000000000000000000" "00000000000000000000000000000000000000000000000000" "00000000000000000000000000000000000000000000000000" "00000000000000000000000000000000000000000000000000" // 500 zeroes "1e600"); fc("-0." "00000000000000000000000000000000000000000000000000" "00000000000000000000000000000000000000000000000000" "00000000000000000000000000000000000000000000000000" "00000000000000000000000000000000000000000000000000" "00000000000000000000000000000000000000000000000000" "00000000000000000000000000000000000000000000000000" "00000000000000000000000000000000000000000000000000" "00000000000000000000000000000000000000000000000000" "00000000000000000000000000000000000000000000000000" "00000000000000000000000000000000000000000000000000" // 500 zeroes "1e600"); fc("0e" "00000000000000000000000000000000000000000000000000" "00000000000000000000000000000000000000000000000000" "00000000000000000000000000000000000000000000000000" "00000000000000000000000000000000000000000000000000" "00000000000000000000000000000000000000000000000000" "00000000000000000000000000000000000000000000000000" "00000000000000000000000000000000000000000000000000" "00000000000000000000000000000000000000000000000000" "00000000000000000000000000000000000000000000000000" "00000000000000000000000000000000000000000000000000" // 500 zeroes ); } //------------------------------------------------------ void testArray() { grind("[]"); grind(" []"); grind("[] "); grind(" [] "); grind(" [ ] "); grind("[1]"); grind("[ 1]"); grind("[1 ]"); grind("[ 1 ]"); grind("[1,2]"); grind("[ 1,2]"); grind("[1 ,2]"); grind("[1, 2]"); grind("[1,2 ]"); grind("[ 1 ,2]"); grind("[1 , 2]"); grind("[1, 2 ]"); grind("[[]]"); grind("[[],[]]"); grind("[[],[],[]]"); grind("[[[]],[[],[]],[[],[],[]]]"); grind("[{},[],\"x\",1,-1,1.0,true,null]"); // depth { error_code ec; parse_options opt; opt.max_depth = 0; stream_parser p(storage_ptr(), opt); p.write("[]", 2, ec); BOOST_TEST( ec == error::too_deep); BOOST_TEST(ec.has_location()); } } //------------------------------------------------------ void testObject() { grind("{}"); grind(" {}"); grind("{} "); grind(" {} "); grind(" { } "); grind("{\"1\":1}"); grind("{ \"1\":1}"); grind("{\"1\" :1}"); grind("{\"1\": 1}"); grind("{\"1\":1 }"); grind("{ \"1\" :1 }"); grind("{\"1\" : 1 }"); grind("{\"1\":1,\"2\":2}"); grind("{\"1\":1, \"2\":2}"); grind("{\"1\":1, \"2\" : 2 }"); grind("{\"\":[]}"); grind("{\"1\":[],\"2\":[]}"); grind( "{\"1\":{\"2\":{}},\"3\":{\"4\":{},\"5\":{}}," "\"6\":{\"7\":{},\"8\":{},\"9\":{}}}"); grind( "{\"1\":{},\"2\":[],\"3\":\"x\",\"4\":1," "\"5\":-1,\"6\":1.0,\"7\":false,\"8\":null}"); // big keys { std::string const big(4000, '*'); { std::string js; js = "{\"" + big + "\":null}"; grind(js); } { std::string js; js = "{\"x\":\"" + big + "\"}"; grind(js); } { std::string js; js = "{\"" + big + "\":\"" + big + "\"}"; grind(js); } } // depth { error_code ec; parse_options opt; opt.max_depth = 0; stream_parser p(storage_ptr(), opt); p.write("{}", 2, ec); BOOST_TEST( ec == error::too_deep); BOOST_TEST(ec.has_location()); } } //------------------------------------------------------ void testFreeFunctions() { string_view const js = "{\"1\":{},\"2\":[],\"3\":\"x\",\"4\":1," "\"5\":-1,\"6\":1.0,\"7\":false,\"8\":null}"; // parse(string_view, error_code) { { error_code ec; auto jv = parse(js, ec); BOOST_TEST(! ec); check_round_trip(jv); } { error_code ec; auto jv = parse("xxx", ec); BOOST_TEST(ec); BOOST_TEST(ec.has_location()); BOOST_TEST(jv.is_null()); } } // parse(string_view, storage_ptr, error_code) { { error_code ec; monotonic_resource mr; auto jv = parse(js, ec, &mr); BOOST_TEST(! ec); //check_round_trip(jv); } { error_code ec; monotonic_resource mr; auto jv = parse("xxx", ec, &mr); BOOST_TEST(ec); BOOST_TEST(ec.has_location()); BOOST_TEST(jv.is_null()); } } // parse(string_view) { { check_round_trip( parse(js)); } { value jv; BOOST_TEST_THROWS( jv = parse("{,"), system_error); } } // parse(string_view, storage_ptr) { { monotonic_resource mr; check_round_trip(parse(js, &mr)); } { monotonic_resource mr; value jv; BOOST_TEST_THROWS( jv = parse("xxx", &mr), system_error); } } } void testSampleJson() { string_view in = R"xx({ "glossary": { "title": "example glossary", "GlossDiv": { "title": "S", "GlossList": { "GlossEntry": { "ID": "SGML", "SortAs": "SGML", "GlossTerm": "Standard Generalized Markup Language", "Acronym": "SGML", "Abbrev": "ISO 8879:1986", "GlossDef": { "para": "A meta-markup language, used to create markup languages such as DocBook.", "GlossSeeAlso": ["GML", "XML"] }, "GlossSee": "markup" } } } } })xx"; string_view out = "{\"glossary\":{\"title\":\"example glossary\",\"GlossDiv\":" "{\"title\":\"S\",\"GlossList\":{\"GlossEntry\":{\"ID\":\"SGML\"," "\"SortAs\":\"SGML\",\"GlossTerm\":\"Standard Generalized Markup " "Language\",\"Acronym\":\"SGML\",\"Abbrev\":\"ISO 8879:1986\"," "\"GlossDef\":{\"para\":\"A meta-markup language, used to create " "markup languages such as DocBook.\",\"GlossSeeAlso\":[\"GML\",\"XML\"]}," "\"GlossSee\":\"markup\"}}}}}"; storage_ptr sp = make_shared_resource(); stream_parser p(sp); error_code ec; p.write(in.data(), in.size(), ec); if(BOOST_TEST(! ec)) p.finish(ec); if(BOOST_TEST(! ec)) BOOST_TEST(serialize(p.release()) == out); } void testTrailingCommas() { parse_options enabled; enabled.allow_trailing_commas = true; grind("[1,]", enabled); grind("[1, 2,]", enabled); grind("{\"a\": 1,}", enabled); grind("{\"a\": 1, \"b\": 2,}", enabled); } void testComments() { parse_options enabled; enabled.allow_comments = true; grind("[/* c */1]", enabled); grind("[1/* c */]", enabled); grind("[1] /* c */", enabled); grind("/* c */ [1]", enabled); grind("[1/* c */, 2]", enabled); grind("[1, /* c */ 2]", enabled); grind("// c++ \n[1]", enabled); grind("[1] // c++ \n", enabled); grind("[1] // c++", enabled); grind("[// c++ \n1]", enabled); grind("[1// c++ \n]", enabled); grind("[1// c++ \n, 2]", enabled); grind("[1, // c++ \n 2]", enabled); grind("{/* c */\"a\":1}", enabled); grind("{\"a\":1/* c */}", enabled); grind("{\"a\":1} /* c */", enabled); grind("/* c */ {\"a\":1}", enabled); grind("{\"a\":1/* c */, \"b\":1}", enabled); grind("{\"a\":1, /* c */ \"b\":1}", enabled); grind("{\"a\"/* c */:1}", enabled); grind("{\"a\":/* c */1}", enabled); grind("// c++ \n{\"a\":1}", enabled); grind("{\"a\":1} // c++ \n", enabled); grind("{\"a\":1} // c++", enabled); grind("{// c++ \n\"a\":1}", enabled); grind("{\"a\":1// c++ \n}", enabled); grind("{\"a\":1// c++ \n, \"b\":2}", enabled); grind("{\"a\":1, // c++ \n \"b\":2}", enabled); grind("{\"a\"// c++ \n:1}", enabled); grind("{\"a\":// c++ \n1}", enabled); } void testUnicodeStrings() { // Embedded NULL correctly converted { auto expected = string_view("Hello\x00World", 11); { auto s = string_view(R"json("Hello\u0000World")json"); grind(s); BOOST_TEST(json::parse(s).as_string() == expected); } { auto s = string_view(R"json(["Hello\u0000World"])json"); grind(s); BOOST_TEST(json::parse(s).as_array().at(0).as_string() == expected); } } // surrogate pairs correctly converted to UTF-8 { auto expected = string_view("\xF0\x9D\x84\x9E", 4); { auto s = string_view(R"json("\uD834\uDD1E")json"); grind(s); BOOST_TEST(json::parse(s).as_string() == expected); } { auto s = string_view(R"json(["\uD834\uDD1E"])json"); grind(s); BOOST_TEST(json::parse(s).as_array().at(0).as_string() == expected); } } } void testDupeKeys() { { value jv = parse(R"({"a":1,"b":2,"a":3})"); object& jo = jv.as_object(); BOOST_TEST(jo.size() == 2); BOOST_TEST(jo.at("a").as_int64() == 3); BOOST_TEST(jo.at("b").as_int64() == 2); } { value jv = parse(R"({"a":1,"a":3,"b":2})"); object& jo = jv.as_object(); BOOST_TEST(jo.size() == 2); BOOST_TEST(jo.at("a").as_int64() == 3); BOOST_TEST(jo.at("b").as_int64() == 2); } { value jv = parse(R"({"a":1,"a":3,"b":2,"a":4})"); object& jo = jv.as_object(); BOOST_TEST(jo.size() == 2); BOOST_TEST(jo.at("a").as_int64() == 4); BOOST_TEST(jo.at("b").as_int64() == 2); } { value jv = parse(R"({"a":1,"a":3,"b":2,"a":4,"b":5})"); object& jo = jv.as_object(); BOOST_TEST(jo.size() == 2); BOOST_TEST(jo.at("a").as_int64() == 4); BOOST_TEST(jo.at("b").as_int64() == 5); } } //------------------------------------------------------ // https://github.com/boostorg/json/issues/15 void testIssue15() { BOOST_TEST( json::parse("{\"port\": 12345}") .as_object() .at("port") .as_int64() == 12345); } // https://github.com/boostorg/json/issues/45 void testIssue45() { struct T { value jv; T(value jv_) : jv(jv_) { } }; auto const jv = parse("[]"); auto const t = T{jv}; BOOST_TEST(serialize(t.jv) == "[]"); } //------------------------------------------------------ void run() { testCtors(); testMembers(); testNull(); testBool(); testString(); testNumber(); testArray(); testObject(); testFreeFunctions(); testSampleJson(); testUnicodeStrings(); testTrailingCommas(); testComments(); testDupeKeys(); testIssue15(); testIssue45(); } }; TEST_SUITE(stream_parser_test, "boost.json.stream_parser"); BOOST_JSON_NS_END