// // Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) // Copyright (c) 2021 Peter Dimov // // 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 "test.hpp" #include "test_suite.hpp" struct X { int a; float b; std::string c; }; BOOST_DESCRIBE_STRUCT(X, (), (a, b, c)) bool operator==( X const& x1, X const& x2 ) { return x1.a == x2.a && x1.b == x2.b && x1.c == x2.c; } struct Y { std::vector v; std::map m; }; BOOST_DESCRIBE_STRUCT(Y, (), (v, m)) bool operator==( Y const& y1, Y const& y2 ) { return y1.v == y2.v && y1.m == y2.m; } BOOST_DEFINE_ENUM_CLASS(E, x, y, z) namespace boost { namespace json { class parse_into_test { public: template void testParseInto( T const& t ) { #if defined(__GNUC__) && __GNUC__ < 5 # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wmissing-field-initializers" #endif T t1( t ); std::string json = serialize( value_from( t1 ) ); T t2{}; error_code jec; parse_into(t2, json, jec); BOOST_TEST( !jec.failed() ) && BOOST_TEST( t1 == t2 ); T t3{}; std::error_code ec; parse_into(t3, json, ec); BOOST_TEST( !ec ) && BOOST_TEST( t1 == t3 ); T t4{}; parse_into(t4, json); BOOST_TEST( t1 == t4 ); std::istringstream is(json); T t5{}; jec = {}; parse_into(t5, is, jec); BOOST_TEST( !jec.failed() ) && BOOST_TEST( t1 == t5 ); is.clear(); is.seekg(0); T t6{}; ec = {}; parse_into(t6, is, ec); BOOST_TEST( !ec ) && BOOST_TEST( t1 == t6 ); is.str(json); is.clear(); T t7{}; parse_into(t7, is); BOOST_TEST( t1 == t7 ); parse_options opt; opt.allow_comments = true; json = "// this is a comment\n" + json; T t8{}; parser_for p( opt, &t8 ); for( auto& c: json ) { std::size_t const n = p.write_some( true, &c, 1, jec ); BOOST_TEST( !jec.failed() ); BOOST_TEST( n == 1 ); } p.write_some(false, nullptr, 0, jec); BOOST_TEST( !jec.failed() ); BOOST_TEST( t1 == t8 ); #if defined(__GNUC__) && __GNUC__ < 5 # pragma GCC diagnostic pop #endif } template void testParseIntoErrors( error e, value const& sample ) { #if defined(__GNUC__) && __GNUC__ < 5 # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wmissing-field-initializers" #endif error_code ec; T t{}; std::string json = serialize(sample); parser_for p( parse_options{}, &t ); for( auto& c: json ) { std::size_t const n = p.write_some( true, &c, 1, ec ); if( ec.failed() ) break; BOOST_TEST( n == 1 ); } if( !ec.failed() ) p.write_some(false, nullptr, 0, ec); BOOST_TEST( ec.failed() ); BOOST_TEST( ec.has_location() ); BOOST_TEST( ec == e ); #if defined(__GNUC__) && __GNUC__ < 5 # pragma GCC diagnostic pop #endif } void testNull() { testParseInto( nullptr ); testParseIntoErrors< std::nullptr_t >( error::not_null, 100 ); } void testBoolean() { testParseInto( false ); testParseInto( true ); testParseIntoErrors< bool >( error::not_bool, nullptr ); } void testIntegral() { testParseInto( 'A' ); // ? testParseInto( -127 ); testParseInto( 255 ); testParseInto( -32767 ); testParseInto( 65535 ); testParseInto( -32767 ); testParseInto( 65535 ); testParseInto( LONG_MIN ); testParseInto( ULONG_MAX ); testParseInto( LLONG_MIN ); testParseInto( ULLONG_MAX ); testParseIntoErrors< int >( error::not_integer, "123" ); testParseIntoErrors< int >( error::not_integer, true ); testParseIntoErrors< int >( error::not_exact, LLONG_MIN ); testParseIntoErrors< int >( error::not_exact, ULONG_MAX ); } void testFloatingPoint() { testParseInto( 0.25f ); testParseInto( 1.125 ); // value_from doesn't support long double // testParseInto( 2.25L ); { double d1 = 12; std::string json = serialize( value_from( 12 ) ); error_code ec; double d2; parse_into(d2, json, ec); BOOST_TEST( !ec.failed() ); BOOST_TEST( d1 == d2 ); d1 = double(UINT64_MAX); json = serialize( value_from( UINT64_MAX ) ); parse_into(d2, json, ec); BOOST_TEST( !ec.failed() ); BOOST_TEST( d1 == d2 ); } testParseIntoErrors< double >( error::not_double, { {"value", 12.1 } } ); } void testString() { testParseInto( "" ); testParseInto( "12345" ); testParseIntoErrors< string >( error::not_string, UINT64_MAX ); } void testSequence() { testParseInto>( { nullptr, nullptr } ); testParseInto< std::vector >( {} ); testParseInto< std::vector >( { true, false } ); testParseInto< std::vector >( {} ); testParseInto< std::vector >( { 1, 2, 3 } ); testParseInto< std::vector >( { 1, 2, UINT64_MAX } ); testParseInto< std::vector >( {} ); testParseInto< std::vector >( { 1.02f, 2.11f, 3.14f } ); testParseInto< std::vector >( {} ); testParseInto< std::vector >( { "one", "two", "three" } ); testParseInto< std::vector> >( {} ); testParseInto< std::vector> >( { {}, { 1 }, { 2, 3 }, { 4, 5, 6 } } ); testParseInto< std::vector> >( { {}, { {"1", 2}, {"3", 4} }, { { "5", 6 } } } ); // clang <= 5 doesn't like when std::array is created from init-list std::array arr; arr.fill(17); testParseInto< std::array >( arr ); testParseIntoErrors< std::vector >( error::not_array, 1 ); testParseIntoErrors< std::vector >( error::not_array, "abcd" ); testParseIntoErrors< std::vector> >( error::not_array, {1, 2, 3} ); testParseIntoErrors< std::array >( error::size_mismatch, {1, 2, 3} ); testParseInto< std::vector> >( {arr,arr,arr} ); std::vector v; parse_into(v, "[1,2,3,4]"); BOOST_TEST( v.size() == 4 ); parse_into(v, "[5,6,7]"); BOOST_TEST( v.size() == 3 ); } void testMap() { testParseInto< std::map >( {} ); testParseInto< std::map >( { { "one", 1 }, { "two", 2 } } ); testParseInto< std::map >( {} ); testParseInto< std::map >( { { "one", 1 }, { "two", 2 } } ); testParseInto< std::map> >( {} ); testParseInto< std::map> >( { { "one", { 1 } }, { "two", { 2, 3 } } } ); testParseInto< std::map> >( {} ); testParseInto< std::map> >( { { "one", {} }, { "two", { { "1", 1 }, { "2", 2 } } } } ); testParseIntoErrors< std::map >( error::not_object, { "1", 1, "2", 2} ); testParseIntoErrors< std::map> >( error::not_object, { {"1", {}}, {"2", {"3", 4}} } ); std::map m; parse_into(m, R"( {"1": 1, "2": 2, "3": 3} )"); BOOST_TEST( m.size() == 3 ); parse_into(m, R"( {"4": 4, "5": 5} )"); BOOST_TEST( m.size() == 2 ); } void testTuple() { testParseInto>( {} ); testParseInto>( { 1, 3.14f } ); testParseInto>( {nullptr, UINT64_MAX} ); testParseInto>( {} ); testParseInto>( std::make_tuple(1, 3.14f, "hello") ); testParseInto>>( {} ); testParseInto>>( { { 1, 2 }, { 3, 4 } } ); testParseInto>>>( {} ); testParseInto>>>( { { { 1, 2 }, { 3, 4 } } } ); testParseInto>>>( { { { 1, 2 }, { 3, 4 } }, { { 5, 6 }, { 7, 8 } } } ); testParseInto>>>( {} ); testParseInto>>>( { { "one", {} } } ); testParseInto>>>( { { "one", { { 1, 2 }, { 3, 4 } } } } ); testParseInto, std::map>>>( {} ); testParseInto, std::map>>>( { { 1, 2, 3 }, { { "one", { 7, true } } } } ); testParseIntoErrors< std::pair >( error::not_array, 1 ); testParseIntoErrors< std::pair >( error::size_mismatch, {1, 2, 3} ); testParseIntoErrors< std::tuple >( error::size_mismatch, {1, 2} ); } void testStruct() { #if defined(BOOST_DESCRIBE_CXX14) testParseInto( {} ); testParseInto( { 1, 3.14f, "hello" } ); testParseInto( {} ); testParseInto( { { { 1, 1.0f, "one" }, { 2, 2.0f, "two" } }, { { "one", { 1, 1.1f, "1" } }, { "two", { 2, 2.2f, "2" } } } } ); testParseIntoErrors( error::not_object, 1 ); testParseIntoErrors( error::unknown_name, { {"a", 1}, {"b", 3.14f}, {"c", "hello"}, {"d", 0} } ); testParseIntoErrors( error::size_mismatch, { {"a", 1} } ); #endif } void testEnum() { #ifdef BOOST_DESCRIBE_CXX14 testParseInto( E::x ); testParseInto( E::y ); testParseInto( E::z ); testParseIntoErrors< E >( error::not_string, (int)(E::y) ); #endif // BOOST_DESCRIBE_CXX14 } template< template class Variant, class Monostate > void testVariant() { testParseInto< Variant >( 1 ); testParseInto< Variant >( 1 ); testParseInto< Variant >( "qwerty" ); testParseInto< Variant >( {} ); testParseInto< Variant >( 1 ); testParseInto< Variant >( "qwerty" ); testParseInto< Variant >( true ); testParseInto< Variant >( UINT64_MAX ); testParseInto< Variant< std::vector > >( std::vector{1, 2, 3, 4, 5} ); testParseInto< Variant< Monostate, std::vector > >( std::vector{1, 2, 3, 4, 5} ); testParseInto< std::vector< Variant > >( {1, 2, 3, "four", 5, "six", "seven", 8}); using V = Variant< std::vector< int >, std::tuple< int, std::string, std::map >, std::tuple< int, std::string, std::map > >; testParseInto< V >( std::make_tuple( 5, "five", std::map{ {"one", 1}, {"pi", 3.14} })); testParseIntoErrors< Variant >( error::exhausted_variants, "a" ); testParseIntoErrors< Variant >( error::exhausted_variants, "a" ); testParseIntoErrors< Variant >( error::exhausted_variants, "a" ); } void testOptional() { #ifndef BOOST_NO_CXX17_HDR_OPTIONAL testParseInto< std::optional >( std::nullopt ); testParseInto< std::optional >( 1 ); testParseInto< std::optional> >( std::nullopt ); testParseInto< std::optional> >( std::vector{} ); testParseInto< std::optional> >( std::vector{nullptr, nullptr} ); testParseInto< std::optional> >( std::vector{"1", "2", "3"} ); testParseInto< std::vector< std::optional > >( {1, 2, 3, std::nullopt, 5, std::nullopt, std::nullopt, 8}); #endif } void run() { testNull(); testBoolean(); testIntegral(); testFloatingPoint(); testString(); testSequence(); testMap(); testTuple(); testStruct(); testEnum(); testOptional(); testVariant(); #ifndef BOOST_NO_CXX17_HDR_VARIANT testVariant(); #endif { int n; BOOST_TEST_THROWS_WITH_LOCATION( parse_into( n, "null" ) ); std::stringstream is("null"); BOOST_TEST_THROWS_WITH_LOCATION( parse_into( n, is ) ); } { int n; error_code ec; parse_into( n, "12 1", ec); BOOST_TEST( ec == error::extra_data ); BOOST_TEST( ec.has_location() ); } { std::stringstream is("12 1"); int n; error_code ec; parse_into( n, is, ec); BOOST_TEST( ec == error::extra_data ); BOOST_TEST( ec.has_location() ); } { int n; std::stringstream is("1"); is.setstate( std::ios::failbit ); error_code ec; parse_into(n, is, ec); BOOST_TEST( ec == error::input_error ); BOOST_TEST( ec.has_location() ); } } }; TEST_SUITE(parse_into_test, "boost.json.parse_into"); } // namespace boost } // namespace json