boost/libs/json/test/serializer.cpp
2021-10-05 21:37:46 +02:00

566 lines
14 KiB
C++

//
// 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 <boost/json/serializer.hpp>
#include <boost/json/parse.hpp>
#include <boost/json/serialize.hpp>
#include <iostream>
#include "parse-vectors.hpp"
#include "test.hpp"
#include "test_suite.hpp"
BOOST_JSON_NS_BEGIN
BOOST_STATIC_ASSERT( std::is_nothrow_destructible<serializer>::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