// // Copyright (c) 2019 Vinnie Falco (vinnie.falco@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 "parse-vectors.hpp" #include "test.hpp" #include "test_suite.hpp" BOOST_JSON_NS_BEGIN BOOST_STATIC_ASSERT( std::is_nothrow_destructible::value ); class serializer_test { public: //------------------------------------------------------ // From the javadoc void print( std::ostream& os, value const& jv) { serializer sr; sr.reset( &jv ); while( ! sr.done() ) { char buf[ 4000 ]; os << sr.read( buf ); } } //------------------------------------------------------ ::test_suite::log_type log; void grind_one( string_view s, value const& jv, string_view name = {}) { { error_code ec; auto const s1 = serialize(jv); auto const jv2 = parse(s1, ec); if(! BOOST_TEST(equal(jv, jv2))) { if(name.empty()) log << " " << s << "\n" " " << s1 << std::endl; else log << name << ":\n" " " << s << "\n" " " << s1 << std::endl; } } // large buffer { error_code ec; serializer sr; sr.reset(&jv); string js; js.reserve(4096); js.grow(sr.read( js.data(), js.capacity()).size()); auto const s1 = serialize(jv); auto const jv2 = parse(s1, ec); BOOST_TEST(equal(jv, jv2)); } } void grind( string_view s0, value const& jv, string_view name = {}) { grind_one(s0, jv, name); auto const s1 = serialize(jv); for(std::size_t i = 1; i < s1.size(); ++i) { serializer sr; sr.reset(&jv); string s2; s2.reserve(s1.size()); s2.grow(sr.read( s2.data(), i).size()); auto const dump = [&] { if(name.empty()) log << " " << s0 << "\n" " " << s1 << "\n" " " << s2 << std::endl; else log << name << ":\n" " " << s0 << "\n" " " << s1 << "\n" " " << s2 << std::endl; }; if(! BOOST_TEST( s2.size() == i)) { dump(); break; } s2.grow(sr.read( s2.data() + i, s1.size() - i).size()); if(! BOOST_TEST( s2.size() == s1.size())) { dump(); break; } if(! BOOST_TEST(s2 == s1)) { dump(); break; } } } //------------------------------------------------------ void testNull() { check("null"); } void testBoolean() { check("true"); check("false"); } void testString() { check("\"\""); check("\"x\""); check("\"xyz\""); check("\"x z\""); // escapes check("\"\\\"\""); // double quote check("\"\\\\\""); // backslash check("\"\\b\""); // backspace check("\"\\f\""); // formfeed check("\"\\n\""); // newline check("\"\\r\""); // carriage return check("\"\\t\""); // horizontal tab // control characters check("\"\\u0000\""); check("\"\\u0001\""); check("\"\\u0002\""); check("\"\\u0003\""); check("\"\\u0004\""); check("\"\\u0005\""); check("\"\\u0006\""); check("\"\\u0007\""); check("\"\\u0008\""); check("\"\\u0009\""); check("\"\\u000a\""); check("\"\\u000b\""); check("\"\\u000c\""); check("\"\\u000d\""); check("\"\\u000e\""); check("\"\\u000f\""); check("\"\\u0010\""); check("\"\\u0011\""); check("\"\\u0012\""); check("\"\\u0013\""); check("\"\\u0014\""); check("\"\\u0015\""); check("\"\\u0016\""); check("\"\\u0017\""); check("\"\\u0018\""); check("\"\\u0019\""); check("\"\\u0020\""); check("\"\\u0021\""); } void testNumber() { // VFALCO These don't perfectly round-trip, // because the representations are not exact. // The test needs to do a better job of comparison. check("-999999999999999999999"); check("-100000000000000000009"); check("-10000000000000000000"); //check("-9223372036854775809"); check("-9223372036854775808"); check("-9223372036854775807"); check("-999999999999999999"); check("-99999999999999999"); check("-9999999999999999"); check("-999999999999999"); check("-99999999999999"); check("-9999999999999"); check("-999999999999"); check("-99999999999"); check("-9999999999"); check("-999999999"); check("-99999999"); check("-9999999"); check("-999999"); check("-99999"); check("-9999"); check("-999"); check("-99"); check("-9"); check("-0"); check("-0.0"); check( "0"); check( "9"); check( "99"); check( "999"); check( "9999"); check( "99999"); check( "999999"); check( "9999999"); check( "99999999"); check( "999999999"); check( "9999999999"); check( "99999999999"); check( "999999999999"); check( "9999999999999"); check( "99999999999999"); check( "999999999999999"); check( "9999999999999999"); check( "99999999999999999"); check( "999999999999999999"); check( "9223372036854775807"); check( "9223372036854775808"); check( "9999999999999999999"); check( "18446744073709551615"); //check( "18446744073709551616"); check( "99999999999999999999"); check( "999999999999999999999"); check( "1000000000000000000000"); check( "9999999999999999999999"); check( "99999999999999999999999"); //check("-0.9999999999999999999999"); check("-0.9999999999999999"); //check("-0.9007199254740991"); //check("-0.999999999999999"); //check("-0.99999999999999"); //check("-0.9999999999999"); //check("-0.999999999999"); //check("-0.99999999999"); //check("-0.9999999999"); //check("-0.999999999"); //check("-0.99999999"); //check("-0.9999999"); //check("-0.999999"); //check("-0.99999"); //check("-0.9999"); //check("-0.8125"); //check("-0.999"); //check("-0.99"); check("-1.0"); check("-0.9"); check("-0.0"); check( "0.0"); check( "0.9"); //check( "0.99"); //check( "0.999"); //check( "0.8125"); //check( "0.9999"); //check( "0.99999"); //check( "0.999999"); //check( "0.9999999"); //check( "0.99999999"); //check( "0.999999999"); //check( "0.9999999999"); //check( "0.99999999999"); //check( "0.999999999999"); //check( "0.9999999999999"); //check( "0.99999999999999"); //check( "0.999999999999999"); //check( "0.9007199254740991"); check( "0.9999999999999999"); //check( "0.9999999999999999999999"); //check( "0.999999999999999999999999999"); check("-1e308"); check("-1e-308"); //check("-9999e300"); //check("-999e100"); //check("-99e10"); check("-9e1"); check( "9e1"); //check( "99e10"); //check( "999e100"); //check( "9999e300"); check( "999999999999999999.0"); check( "999999999999999999999.0"); check( "999999999999999999999e5"); check( "999999999999999999999.0e5"); check("-1e-1"); check("-1e0"); check("-1e1"); check( "0e0"); check( "1e0"); check( "1e10"); } void testArray() { check("[]"); check("[[]]"); check("[[],[],[]]"); check("[[[[[[[[[[]]]]]]]]]]"); check("[{}]"); check("[{},{}]"); check("[1,2,3,4,5]"); check("[true,false,null]"); } void testObject() { check("{}"); check("{\"x\":1}"); check("{\"x\":[]}"); check("{\"x\":1,\"y\":null}"); } //------------------------------------------------------ void testMembers() { // serializer() { serializer sr; char buf[32]; BOOST_TEST(sr.read(buf) == "null"); } // done() { value jv = 1; serializer sr; sr.reset(&jv); BOOST_TEST(! sr.done()); char buf[32]; BOOST_TEST(sr.read(buf) == "1"); BOOST_TEST(sr.done()); } // read() { value jv = 1; serializer sr; sr.reset(&jv); char buf[1024]; auto const s = sr.read(buf); BOOST_TEST(sr.done()); BOOST_TEST(s == "1"); } // checked read() { serializer sr; char buf[100]; BOOST_TEST(sr.read(buf, 50) == "null"); } // reset(value) { char buf[100]; serializer sr; value jv = { 1, 2, 3 }; sr.reset(&jv); BOOST_TEST(sr.read(buf) == "[1,2,3]"); } // reset(array) { char buf[100]; serializer sr; array arr = { 1, 2, 3 }; sr.reset(&arr); BOOST_TEST(sr.read(buf) == "[1,2,3]"); } // reset(object) { char buf[100]; serializer sr; object obj = { {"k1",1}, {"k2",2} }; sr.reset(&obj); BOOST_TEST(sr.read(buf) == "{\"k1\":1,\"k2\":2}"); } // reset(string) { char buf[100]; serializer sr; string str = "123"; sr.reset(&str); BOOST_TEST(sr.read(buf) == "\"123\""); } } void check( string_view s, string_view name = {}) { try { auto const jv = parse(s); grind(s, jv, name); } catch(std::exception const&) { BOOST_TEST_FAIL(); } } void testVectors() { #if 0 check( 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"); #endif parse_vectors const pv; for(auto const e : pv) { if(e.result != 'y') continue; // skip these failures for now if( e.name == "number" || e.name == "number_real_exponent" || e.name == "number_real_fraction_exponent" || e.name == "number_simple_real" || e.name == "object_extreme_numbers" || e.name == "pass01" ) continue; check(e.text, e.name); } } std::string to_ostream(value const& jv) { std::stringstream ss; ss << jv; return ss.str(); } void testOstream() { for(string_view js : { "{\"1\":{},\"2\":[],\"3\":\"x\",\"4\":1," "\"5\":-1,\"6\":144.0,\"7\":false,\"8\":null}", "[1,2,3,4,5]" }) { error_code ec; auto const jv1 = parse(js, ec); if(! BOOST_TEST(! ec)) return; auto const jv2 = parse(to_ostream(jv1), ec); if(! BOOST_TEST(! ec)) return; if(! BOOST_TEST(equal(jv1, jv2))) log << " " << js << "\n" " " << jv1 << "\n" " " << jv2 << std::endl; } } void testNumberRoundTrips() { // no decimal or exponent parsed as integer BOOST_TEST(parse("-0").as_int64() == 0); BOOST_TEST(serialize(parse("-0")) == "0"); BOOST_TEST(parse("-0.0").as_double() == -0); BOOST_TEST(serialize(parse("0.0")) == "0E0"); BOOST_TEST(parse("0.0").as_double() == 0); BOOST_TEST(serialize(parse("-0.0")) == "-0E0"); } void run() { testNull(); testBoolean(); testString(); testNumber(); testArray(); testObject(); testMembers(); testVectors(); testOstream(); testNumberRoundTrips(); } }; TEST_SUITE(serializer_test, "boost.json.serializer"); BOOST_JSON_NS_END