From d32171df859f801a313234e80af8d32c1e7801d6 Mon Sep 17 00:00:00 2001 From: Dmitry Arkhipov Date: Thu, 13 Aug 2026 18:42:06 +0300 Subject: [PATCH 1/3] more test coverage for key_value_pair --- test/object.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/test/object.cpp b/test/object.cpp index 17b2a752f..e328e4ff4 100644 --- a/test/object.cpp +++ b/test/object.cpp @@ -272,6 +272,17 @@ class object_test { object o; } + { + key_value_pair kp("a", "b"); + } + { + // this test is mostly for getting more test coverage + monotonic_resource mr; + std::pair p("k", "v"); + key_value_pair kp(p, &mr); + BOOST_TEST(kp.key() == "k"); + BOOST_TEST(kp.value() == "v"); + } } void From c39a3a779191e6856405a817acec48441a95a18a Mon Sep 17 00:00:00 2001 From: Dmitry Arkhipov Date: Thu, 13 Aug 2026 19:16:23 +0300 Subject: [PATCH 2/3] exclude unreachable lines from coverage --- include/boost/json/basic_parser_impl.hpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/include/boost/json/basic_parser_impl.hpp b/include/boost/json/basic_parser_impl.hpp index acd58e60c..34860bcb2 100644 --- a/include/boost/json/basic_parser_impl.hpp +++ b/include/boost/json/basic_parser_impl.hpp @@ -434,7 +434,7 @@ parse_comment(const char* p, st_.pop(st); switch(st) { - default: BOOST_JSON_UNREACHABLE(); + default: BOOST_JSON_UNREACHABLE(); // LCOV_EXCL_LINE case state::com1: goto do_com1; case state::com2: goto do_com2; case state::com3: goto do_com3; @@ -723,7 +723,7 @@ resume_value(const char* p, st_.peek(st); switch(st) { - default: BOOST_JSON_UNREACHABLE(); + default: BOOST_JSON_UNREACHABLE(); // LCOV_EXCL_LINE case state::lit1: return parse_literal(p, detail::literals_c() ); @@ -859,7 +859,7 @@ parse_literal(const char* p, Literal) } else { - BOOST_JSON_UNREACHABLE(); + BOOST_JSON_UNREACHABLE(); // LCOV_EXCL_LINE } cs += sz; @@ -947,7 +947,7 @@ parse_literal(const char* p, Literal) ec_))) return fail(cs.begin()); break; - default: BOOST_JSON_UNREACHABLE(); + default: BOOST_JSON_UNREACHABLE(); // LCOV_EXCL_LINE } cs += size; @@ -977,7 +977,7 @@ parse_string(const char* p, st_.pop(total); switch(st) { - default: BOOST_JSON_UNREACHABLE(); + default: BOOST_JSON_UNREACHABLE(); // LCOV_EXCL_LINE case state::str2: goto do_str2; case state::str8: goto do_str8; case state::str1: break; @@ -1166,7 +1166,7 @@ parse_escaped( st_.pop(st); switch(st) { - default: BOOST_JSON_UNREACHABLE(); + default: BOOST_JSON_UNREACHABLE(); // LCOV_EXCL_LINE case state::str3: goto do_str3; case state::str4: goto do_str4; case state::str5: goto do_str5; @@ -1714,7 +1714,7 @@ parse_object(const char* p, st_.pop(size); switch(st) { - default: BOOST_JSON_UNREACHABLE(); + default: BOOST_JSON_UNREACHABLE(); // LCOV_EXCL_LINE case state::obj1: goto do_obj1; case state::obj2: goto do_obj2; case state::obj3: goto do_obj3; @@ -1883,7 +1883,7 @@ parse_array(const char* p, st_.pop(size); switch(st) { - default: BOOST_JSON_UNREACHABLE(); + default: BOOST_JSON_UNREACHABLE(); // LCOV_EXCL_LINE case state::arr1: goto do_arr1; case state::arr2: goto do_arr2; case state::arr3: goto do_arr3; @@ -2133,7 +2133,7 @@ parse_number(const char* p, st_.pop(st); switch(st) { - default: BOOST_JSON_UNREACHABLE(); + default: BOOST_JSON_UNREACHABLE(); // LCOV_EXCL_LINE case state::num1: goto do_num1; case state::num2: goto do_num2; case state::num3: goto do_num3; From e1ce9e3b1bdea934ae0482b92cef1579fc05b3ac Mon Sep 17 00:00:00 2001 From: Dmitry Arkhipov Date: Mon, 17 Aug 2026 15:22:50 +0300 Subject: [PATCH 3/3] more parser coverage --- test/basic_parser.cpp | 49 ++++++++++++- test/limits.cpp | 158 +++++++++++++++++++++++++++++++++++++----- 2 files changed, 187 insertions(+), 20 deletions(-) diff --git a/test/basic_parser.cpp b/test/basic_parser.cpp index 82ac57f67..0f5ba952a 100644 --- a/test/basic_parser.cpp +++ b/test/basic_parser.cpp @@ -594,8 +594,8 @@ class basic_parser_test TEST_GOOD("1000000000000000000000000.000000000001"); TEST_GOOD("1000000000000000000000000.0e1 "); TEST_GOOD("1000000000000000000000000.0 "); - TEST_GOOD("1000000000.1000000000 "); + TEST_GOOD("100000000000000000000e+2147483647"); TEST_BAD(""); TEST_BAD("- "); @@ -1752,6 +1752,52 @@ class basic_parser_test #pragma warning(pop) #endif + struct null_handler + { + constexpr static std::size_t max_object_size = std::size_t(-1); + constexpr static std::size_t max_array_size = std::size_t(-1); + constexpr static std::size_t max_key_size = std::size_t(-1); + constexpr static std::size_t max_string_size = std::size_t(-1); + + bool on_document_begin( system::error_code& ) { return true; } + bool on_document_end( system::error_code& ) { return true; } + bool on_object_begin( system::error_code& ) { return true; } + bool on_object_end( std::size_t, system::error_code& ) { return true; } + bool on_array_begin( system::error_code& ) { return true; } + bool on_array_end( std::size_t, system::error_code& ) { return true; } + bool on_key_part( string_view, std::size_t, system::error_code& ) { return true; } + bool on_key( string_view, std::size_t, system::error_code& ) { return true; } + bool on_string_part( string_view, std::size_t, system::error_code& ) { return true; } + bool on_string( string_view, std::size_t, system::error_code& ) { return true; } + bool on_number_part( string_view, system::error_code&) { return true; } + bool on_int64( std::int64_t, string_view, system::error_code& ) { return true; } + bool on_uint64( std::uint64_t, string_view, system::error_code& ) { return true; } + bool on_double( double, string_view, system::error_code& ) { return true; } + bool on_bool( bool, system::error_code& ) { return true; } + bool on_null( system::error_code& ) { return true; } + bool on_comment_part( string_view, system::error_code& ) { return true; } + bool on_comment( string_view, system::error_code& ) { return true; } + }; + + void + testManualFail() + { + basic_parser p({}); + p.fail( system::error_code() ); + BOOST_TEST( !p.done() ); + BOOST_TEST( p.last_error() == error::incomplete ); + BOOST_TEST( p.last_error().has_location() ); + + p.reset(); + + system::error_code ec; + p.write_some(false, "null", 4, ec); + BOOST_TEST( p.done() ); + p.fail( make_error_code(error::array_too_large) ); + BOOST_TEST( !p.done() ); + BOOST_TEST( p.last_error() == error::array_too_large ); + } + void run() { @@ -1776,6 +1822,7 @@ class basic_parser_test testNumberLiteral(); testStickyErrors(); testStdTypes(); + testManualFail(); } }; diff --git a/test/limits.cpp b/test/limits.cpp index 44e22a9d3..84813e266 100644 --- a/test/limits.cpp +++ b/test/limits.cpp @@ -220,6 +220,52 @@ class limits_test BOOST_TEST(ec.has_location()); } + // string in parser, fails at the beginning of the 2nd escape sequence + { + stream_parser p; + system::error_code ec; + p.write_some("\"", 1, ec); + BOOST_TEST( !ec.failed() ); + for(std::size_t i = 0; i < string::max_size(); ++i) + { + p.write_some("0", 1, ec); + } + p.write_some("\\n\\", 3, ec); + BOOST_TEST(ec == error::string_too_large); + BOOST_TEST(ec.has_location()); + } + + // string in parser, fails at the beginning of the 2nd + // (unicode) escape sequence + { + stream_parser p; + system::error_code ec; + p.write_some("\"", 1, ec); + BOOST_TEST( !ec.failed() ); + for(std::size_t i = 0; i < string::max_size(); ++i) + { + p.write_some("0", 1, ec); + } + p.write_some("\\n\\u", 4, ec); + BOOST_TEST(ec == error::string_too_large); + BOOST_TEST(ec.has_location()); + } + + // string in parser, fails after the last escape + { + stream_parser p; + system::error_code ec; + p.write_some("\"", 1, ec); + BOOST_TEST( !ec.failed() ); + for(std::size_t i = 0; i < string::max_size(); ++i) + { + p.write_some("0", 1, ec); + } + p.write_some("\\n0", 3, ec); + BOOST_TEST(ec == error::string_too_large); + BOOST_TEST(ec.has_location()); + } + // key in parser { stream_parser p; @@ -353,30 +399,104 @@ class limits_test testNumber() { // very long floating point number - std::array buffer; - buffer.fill('0'); - buffer.data()[1] = '.'; + { + std::array buffer; + buffer.fill('0'); + buffer.data()[1] = '.'; - parse_options precise; - precise.numbers = number_precision::precise; + parse_options precise; + precise.numbers = number_precision::precise; - stream_parser p( {}, precise ); - system::error_code ec; - p.write( buffer.data(), 1, ec ); - BOOST_TEST_THROWS_WITH_LOCATION( - p.write( buffer.data() + 1, buffer.size() - 1, ec )); - BOOST_TEST( !ec ); + stream_parser p( {}, precise ); + system::error_code ec; + p.write( buffer.data(), 1, ec ); + BOOST_TEST_THROWS_WITH_LOCATION( + p.write( buffer.data() + 1, buffer.size() - 1, ec )); + BOOST_TEST( !ec ); + + // now we make the number one character shorter + p.reset(); + p.write( buffer.data(), 1, ec ); + BOOST_TEST( !ec ); + + p.write( buffer.data() + 1, buffer.size() - 2, ec ); + BOOST_TEST( !ec ); + + auto jv = p.release(); + BOOST_TEST( jv.as_double() == 0 ); + } + +#ifndef BOOST_JSON_NO_LONG_TESTS + // number with too many digits before decimal point + { + stream_parser p; + system::error_code ec; + std::string number(static_cast(INT_MAX) + 19, '1'); + p.write_some(number, ec); + BOOST_TEST( !ec.failed() ); - // now we make the number one character shorter - p.reset(); - p.write( buffer.data(), 1, ec ); - BOOST_TEST( !ec ); + p.write_some("1", 1, ec); + BOOST_TEST(ec == error::exponent_overflow); + BOOST_TEST(ec.has_location()); + } + + // number with too many digits after decimal point + { + stream_parser p; + system::error_code ec; + p.write_some("0.", 2, ec); + BOOST_TEST( !ec.failed() ); + std::string number(static_cast(INT_MAX), '0'); + p.write_some(number, ec); + BOOST_TEST( !ec.failed() ); + + p.write_some("0", 1, ec); + BOOST_TEST(ec == error::exponent_overflow); + BOOST_TEST(ec.has_location()); + } - p.write( buffer.data() + 1, buffer.size() - 2, ec ); - BOOST_TEST( !ec ); + // number with non-zero mantissa, many digits after decimal point, + // and overflowing negative exponent + { + stream_parser p; + system::error_code ec; + p.write_some("0.", 2, ec); + BOOST_TEST( !ec.failed() ); + std::string number(static_cast(INT_MAX) - 308, '0'); + p.write_some(number, ec); + BOOST_TEST( !ec.failed() ); + p.write_some("1e-", 3, ec); + BOOST_TEST( !ec.failed() ); + p.write_some("2147483647", 10, ec); + BOOST_TEST( !ec.failed() ); + // 0.(INT_MAX - 308 zeroes)1e-2147483647 + + p.write_some(" ", 1, ec); + BOOST_TEST(ec == error::exponent_overflow); + BOOST_TEST(ec.has_location()); + } - auto jv = p.release(); - BOOST_TEST( jv.as_double() == 0 ); + // number with non-zero mantissa, many digits before decimal point, + // and overflowing positive exponent + { + stream_parser p; + system::error_code ec; + p.write_some("1", 1, ec); + BOOST_TEST( !ec.failed() ); + std::string number(static_cast(INT_MAX) - 289, '0'); + p.write_some(number, ec); + BOOST_TEST( !ec.failed() ); + p.write_some("1e+", 3, ec); + BOOST_TEST( !ec.failed() ); + p.write_some("2147483647", 10, ec); + BOOST_TEST( !ec.failed() ); + // 1(INT_MAX - 289 zeroes)1e+2147483647 + + p.write_some(" ", 1, ec); + BOOST_TEST(ec == error::exponent_overflow); + BOOST_TEST(ec.has_location()); + } +#endif // BOOST_JSON_NO_LONG_TESTS } void