From cdf24bccde91a4d0f812b1da8ee3f6cef5af9ec8 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Fri, 21 Aug 2026 02:53:10 +0200 Subject: [PATCH] Split the regression tests far enough to leave room The first split left unit-regression2.cpp 0.7% below the size develop links at, which the comparison change in the follow-up immediately used up: the MinGW linker fails on test-regression2_cpp20 again, naming copy_shallow and to_partial_ordering among the relocations it cannot fit. Move the sections from "issue #2067" on, and the helper types they use, so that the file stops being the one that decides whether the tests can be linked at all. At -O0 and C++20, unit-regression2.cpp is now 2,964,944 bytes against develop's 4,708,248, and 3,070,568 bytes with the follow-up applied - roughly a third smaller either way, rather than a fraction of a percent larger. The 135 assertions are the same ones as before, now spread over three test cases in two files. Also silence the clang-tidy findings the deep-nesting tests draw: the copies they make are what is being tested, and the reserve() computation gets its parentheses. Signed-off-by: Niels Lohmann --- tests/src/unit-large_json.cpp | 10 +- tests/src/unit-regression2.cpp | 780 ------------------------------- tests/src/unit-regression3.cpp | 806 +++++++++++++++++++++++++++++++++ 3 files changed, 811 insertions(+), 785 deletions(-) diff --git a/tests/src/unit-large_json.cpp b/tests/src/unit-large_json.cpp index 4e3bdfa8d..f10c8be41 100644 --- a/tests/src/unit-large_json.cpp +++ b/tests/src/unit-large_json.cpp @@ -67,7 +67,7 @@ TEST_CASE("tests on deeply nested JSONs") { const json j = json::parse(std::string(depth, '[') + '0' + std::string(depth, ']')); - const json copy(j); + const json copy(j); // NOLINT(performance-unnecessary-copy-initialization): the copy is what is tested std::size_t copy_depth = 0; CHECK(*innermost_value(copy, copy_depth) == 0); @@ -77,7 +77,7 @@ TEST_CASE("tests on deeply nested JSONs") SECTION("object") { std::string s; - s.reserve(6 * depth + 1); + s.reserve((6 * depth) + 1); for (std::size_t i = 0; i < depth; ++i) { s += "{\"a\":"; @@ -87,7 +87,7 @@ TEST_CASE("tests on deeply nested JSONs") const json j = json::parse(s); - const json copy(j); + const json copy(j); // NOLINT(performance-unnecessary-copy-initialization): the copy is what is tested std::size_t copy_depth = 0; CHECK(*innermost_value(copy, copy_depth) == 1); @@ -119,7 +119,7 @@ TEST_CASE("tests on deeply nested JSONs") CAPTURE(d); const json array = json::parse(std::string(d, '[') + '0' + std::string(d, ']')); - const json array_copy(array); + const json array_copy(array); // NOLINT(performance-unnecessary-copy-initialization): the copy is what is tested std::size_t array_depth = 0; CHECK(*innermost_value(array_copy, array_depth) == 0); CHECK(array_depth == d); @@ -133,7 +133,7 @@ TEST_CASE("tests on deeply nested JSONs") object_text.append(d, '}'); const json object = json::parse(object_text); - const json object_copy(object); + const json object_copy(object); // NOLINT(performance-unnecessary-copy-initialization): the copy is what is tested std::size_t object_depth = 0; CHECK(*innermost_value(object_copy, object_depth) == 1); CHECK(object_depth == d); diff --git a/tests/src/unit-regression2.cpp b/tests/src/unit-regression2.cpp index 6b0dda4df..b5fb9d837 100644 --- a/tests/src/unit-regression2.cpp +++ b/tests/src/unit-regression2.cpp @@ -173,274 +173,6 @@ struct NotSerializableData float myfloat; }; -///////////////////////////////////////////////////////////////////// -// for #2574 -///////////////////////////////////////////////////////////////////// - -struct NonDefaultConstructible -{ - explicit NonDefaultConstructible(int a) - : x(a) - {} - int x; -}; - -namespace nlohmann -{ -template<> -struct adl_serializer -{ - static NonDefaultConstructible from_json(json const& j) - { - return NonDefaultConstructible(j.get()); - } -}; -} // namespace nlohmann - -///////////////////////////////////////////////////////////////////// -// for #2824 -///////////////////////////////////////////////////////////////////// - -class sax_no_exception : public nlohmann::detail::json_sax_dom_parser -{ - public: - explicit sax_no_exception(json& j) - : nlohmann::detail::json_sax_dom_parser(j, false) - {} - - static bool parse_error(std::size_t /*position*/, const std::string& /*last_token*/, const json::exception& ex) - { - error_string = new std::string(ex.what()); // NOLINT(cppcoreguidelines-owning-memory) - return false; - } - - static std::string* error_string; -}; - -std::string* sax_no_exception::error_string = nullptr; - -///////////////////////////////////////////////////////////////////// -// for #2982 -///////////////////////////////////////////////////////////////////// - -template -class my_allocator : public std::allocator -{ - public: - using std::allocator::allocator; - - my_allocator() = default; - template my_allocator(const my_allocator& /*unused*/) { } - - template - struct rebind - { - using other = my_allocator; - }; -}; - -///////////////////////////////////////////////////////////////////// -// for #3077 -///////////////////////////////////////////////////////////////////// - -class FooAlloc -{}; - -class Foo -{ - public: - explicit Foo(const FooAlloc& /* unused */ = FooAlloc()) {} - - bool value = false; -}; - -class FooBar -{ - public: - Foo foo{}; // NOLINT(readability-redundant-member-init) -}; - -inline void from_json(const nlohmann::json& j, FooBar& fb) // NOLINT(misc-use-internal-linkage) -{ - j.at("value").get_to(fb.foo.value); -} - -///////////////////////////////////////////////////////////////////// -// for #3171 -///////////////////////////////////////////////////////////////////// - -struct for_3171_base // NOLINT(cppcoreguidelines-special-member-functions) -{ - for_3171_base(const std::string& /*unused*/ = {}) {} - virtual ~for_3171_base(); - - for_3171_base(const for_3171_base& other) // NOLINT(hicpp-use-equals-default,modernize-use-equals-default) - : str(other.str) - {} - - for_3171_base& operator=(const for_3171_base& other) - { - if (this != &other) - { - str = other.str; - } - return *this; - } - - for_3171_base(for_3171_base&& other) noexcept - : str(std::move(other.str)) - {} - - for_3171_base& operator=(for_3171_base&& other) noexcept - { - if (this != &other) - { - str = std::move(other.str); - } - return *this; - } - - virtual void _from_json(const json& j) - { - j.at("str").get_to(str); - } - - std::string str{}; // NOLINT(readability-redundant-member-init) -}; - -for_3171_base::~for_3171_base() = default; - -struct for_3171_derived : public for_3171_base -{ - for_3171_derived() = default; - ~for_3171_derived() override; - explicit for_3171_derived(const std::string& /*unused*/) { } - - for_3171_derived(const for_3171_derived& other) // NOLINT(hicpp-use-equals-default,modernize-use-equals-default) - : for_3171_base(other) - {} - - for_3171_derived& operator=(const for_3171_derived& other) - { - if (this != &other) - { - for_3171_base::operator=(other); // Call base class assignment operator - } - return *this; - } - - for_3171_derived(for_3171_derived&& other) noexcept - : for_3171_base(std::move(other)) - {} - - for_3171_derived& operator=(for_3171_derived&& other) noexcept - { - if (this != &other) - { - for_3171_base::operator=(std::move(other)); // Call base class move assignment operator - } - return *this; - } -}; - -for_3171_derived::~for_3171_derived() = default; - -inline void from_json(const json& j, for_3171_base& tb) // NOLINT(misc-use-internal-linkage) -{ - tb._from_json(j); -} - -///////////////////////////////////////////////////////////////////// -// for #3312 -///////////////////////////////////////////////////////////////////// - -#ifdef JSON_HAS_CPP_20 -struct for_3312 -{ - std::string name; -}; - -inline void from_json(const json& j, for_3312& obj) // NOLINT(misc-use-internal-linkage) -{ - j.at("name").get_to(obj.name); -} -#endif - -///////////////////////////////////////////////////////////////////// -// for #3204 -///////////////////////////////////////////////////////////////////// - -struct for_3204_foo -{ - for_3204_foo() = default; - explicit for_3204_foo(std::string /*unused*/) {} // NOLINT(performance-unnecessary-value-param) -}; - -struct for_3204_bar -{ - enum constructed_from_t // NOLINT(cppcoreguidelines-use-enum-class) - { - constructed_from_none = 0, - constructed_from_foo = 1, - constructed_from_json = 2 - }; - - explicit for_3204_bar(std::function /*unused*/) noexcept // NOLINT(performance-unnecessary-value-param) - : constructed_from(constructed_from_foo) {} - explicit for_3204_bar(std::function /*unused*/) noexcept // NOLINT(performance-unnecessary-value-param) - : constructed_from(constructed_from_json) {} - - constructed_from_t constructed_from = constructed_from_none; -}; - -///////////////////////////////////////////////////////////////////// -// for #3333 -///////////////////////////////////////////////////////////////////// - -struct for_3333 final -{ - for_3333(int x_ = 0, int y_ = 0) : x(x_), y(y_) {} - - template - for_3333(const T& /*unused*/) - { - CHECK(false); - } - - int x = 0; - int y = 0; -}; - -template <> -inline for_3333::for_3333(const json& j) - : for_3333(j.value("x", 0), j.value("y", 0)) -{} - -///////////////////////////////////////////////////////////////////// -// for #3810 -///////////////////////////////////////////////////////////////////// - -struct Example_3810 -{ - int bla{}; - - Example_3810() = default; -}; - -NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Example_3810, bla) // NOLINT(misc-use-internal-linkage) - -///////////////////////////////////////////////////////////////////// -// for #4740 -///////////////////////////////////////////////////////////////////// - -#ifdef JSON_HAS_CPP_17 -struct Example_4740 -{ - std::optional host = std::nullopt; - std::optional port = std::nullopt; - NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Example_4740, host, port) -}; -#endif TEST_CASE("regression tests 2") { @@ -727,518 +459,6 @@ TEST_CASE("regression tests 2") CHECK(result.dump() == R"([{"op":"add","path":"/foo/-","value":"3"}])"); } - SECTION("issue #2067 - cannot serialize binary data to text JSON") - { - const std::array data = {{0x81, 0xA4, 0x64, 0x61, 0x74, 0x61, 0xC4, 0x0F, 0x33, 0x30, 0x30, 0x32, 0x33, 0x34, 0x30, 0x31, 0x30, 0x37, 0x30, 0x35, 0x30, 0x31, 0x30}}; - const json j = json::from_msgpack(data.data(), data.size()); - CHECK_NOTHROW( - j.dump(4, // Indent - ' ', // Indent char - false, // Ensure ascii - json::error_handler_t::strict // Error - )); - } - - SECTION("PR #2181 - regression bug with lvalue") - { - // see https://github.com/nlohmann/json/pull/2181#issuecomment-653326060 - const json j{{"x", "test"}}; - const std::string defval = "default value"; - auto val = j.value("x", defval); // NOLINT(bugprone-unused-local-non-trivial-variable) - auto val2 = j.value("y", defval); // NOLINT(bugprone-unused-local-non-trivial-variable) - } - - SECTION("issue #2293 - eof doesn't cause parsing to stop") - { - const std::vector data = - { - 0x7B, - 0x6F, - 0x62, - 0x6A, - 0x65, - 0x63, - 0x74, - 0x20, - 0x4F, - 0x42 - }; - const json result = json::from_cbor(data, true, false); - CHECK(result.is_discarded()); - } - - SECTION("issue #2315 - json.update and vectordoes not work with ordered_json") - { - nlohmann::ordered_json jsonAnimals = {{"animal", "dog"}}; - const nlohmann::ordered_json jsonCat = {{"animal", "cat"}}; - jsonAnimals.update(jsonCat); - CHECK(jsonAnimals["animal"] == "cat"); - - auto jsonAnimals_parsed = nlohmann::ordered_json::parse(jsonAnimals.dump()); - CHECK(jsonAnimals == jsonAnimals_parsed); - - const std::vector> intData = {std::make_pair("aaaa", 11), - std::make_pair("bbb", 222) - }; - nlohmann::ordered_json jsonObj; - for (const auto& data : intData) - { - jsonObj[data.first] = data.second; - } - CHECK(jsonObj["aaaa"] == 11); - CHECK(jsonObj["bbb"] == 222); - } - - SECTION("issue #2330 - ignore_comment=true fails on multiple consecutive lines starting with comments") - { - const std::string ss = "//\n//\n{\n}\n"; - const json j = json::parse(ss, nullptr, true, true); - CHECK(j.dump() == "{}"); - } - -#ifdef JSON_HAS_CPP_20 -#ifndef _LIBCPP_VERSION // see https://github.com/nlohmann/json/issues/4490 - // classic Intel ICC reports as includable but cannot actually compile - // std::span/std::as_bytes usage below -#if __has_include() && !defined(__ICC) && !defined(__INTEL_COMPILER) - SECTION("issue #2546 - parsing containers of std::byte") - { - const char DATA[] = R"("Hello, world!")"; // NOLINT(misc-const-correctness,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) - const auto s = std::as_bytes(std::span(DATA)); - const json j = json::parse(s); - CHECK(j.dump() == "\"Hello, world!\""); - } -#endif -#endif -#endif - - SECTION("issue #2574 - Deserialization to std::array, std::pair, and std::tuple with non-default constructable types fails") - { - SECTION("std::array") - { - { - const json j = {7, 4}; - auto arr = j.get>(); - CHECK(arr[0].x == 7); - CHECK(arr[1].x == 4); - } - - { - const json j = 7; - CHECK_THROWS_AS((j.get>()), json::type_error); - } - } - - SECTION("std::pair") - { - { - const json j = {3, 8}; - auto p = j.get>(); - CHECK(p.first.x == 3); - CHECK(p.second.x == 8); - } - - { - const json j = {4, 1}; - auto p = j.get>(); - CHECK(p.first == 4); - CHECK(p.second.x == 1); - } - - { - const json j = {6, 7}; - auto p = j.get>(); - CHECK(p.first.x == 6); - CHECK(p.second == 7); - } - - { - const json j = 7; - CHECK_THROWS_AS((j.get>()), json::type_error); - } - } - - SECTION("std::tuple") - { - { - const json j = {9}; - auto t = j.get>(); - CHECK(std::get<0>(t).x == 9); - } - - { - const json j = {9, 8, 7}; - auto t = j.get>(); - CHECK(std::get<0>(t).x == 9); - CHECK(std::get<1>(t) == 8); - CHECK(std::get<2>(t).x == 7); - } - - { - const json j = 7; - CHECK_THROWS_AS((j.get>()), json::type_error); - } - } - } - - SECTION("issue #4530 - Serialization of empty tuple") - { - const auto source_tuple = std::tuple<>(); - const nlohmann::json j = source_tuple; - - CHECK(j.get() == source_tuple); - CHECK("[]" == j.dump()); - } - - SECTION("issue #2865 - ASAN detects memory leaks") - { - // the code below is expected to not leak memory - { - nlohmann::json o; - const std::string s = "bar"; - - nlohmann::to_json(o["foo"], s); - - nlohmann::json p = o; - - // call to_json with a non-null JSON value - nlohmann::to_json(p["foo"], s); - } - - { - nlohmann::json o; - const std::string s = "bar"; - - nlohmann::to_json(o["foo"], s); - - // call to_json with a non-null JSON value - nlohmann::to_json(o["foo"], s); - } - } - - SECTION("issue #2824 - encoding of json::exception::what()") - { - json j; - sax_no_exception sax(j); - - CHECK(!json::sax_parse("xyz", &sax)); - CHECK(*sax_no_exception::error_string == "[json.exception.parse_error.101] parse error at line 1, column 1: syntax error while parsing value - invalid literal; last read: 'x'"); - delete sax_no_exception::error_string; // NOLINT(cppcoreguidelines-owning-memory) - } - - SECTION("issue #2825 - Properly constrain the basic_json conversion operator") - { - static_assert(std::is_copy_assignable::value, "ordered_json must be copy assignable"); - } - - SECTION("issue #2958 - Inserting in unordered json using a pointer retains the leading slash") - { - const std::string p = "/root"; - - json test1; - test1[json::json_pointer(p)] = json::object(); - CHECK(test1.dump() == "{\"root\":{}}"); - - ordered_json test2; - test2[ordered_json::json_pointer(p)] = json::object(); - CHECK(test2.dump() == "{\"root\":{}}"); - - // json::json_pointer and ordered_json::json_pointer are the same type; behave as above - ordered_json test3; - test3[json::json_pointer(p)] = json::object(); - CHECK(std::is_same::value); - CHECK(test3.dump() == "{\"root\":{}}"); - } - - SECTION("issue #2982 - to_{binary format} does not provide a mechanism for specifying a custom allocator for the returned type") - { - std::vector> my_vector; - const json j = {1, 2, 3, 4}; - json::to_cbor(j, my_vector); - json k = json::from_cbor(my_vector); - CHECK(j == k); - } - -#if JSON_HAS_FILESYSTEM || JSON_HAS_EXPERIMENTAL_FILESYSTEM - // JSON_HAS_CPP_17 (do not remove; see note at top of file) - SECTION("issue #3070 - Version 3.10.3 breaks backward-compatibility with 3.10.2 ") - { - nlohmann::detail::std_fs::path text_path("/tmp/text.txt"); - const json j(text_path); - - const auto j_path = j.get(); - CHECK(j_path == text_path); - -#if DOCTEST_CLANG || DOCTEST_GCC >= DOCTEST_COMPILER(8, 4, 0) - // only known to work on Clang and GCC >=8.4 - CHECK_THROWS_WITH_AS(nlohmann::detail::std_fs::path(json(1)), "[json.exception.type_error.302] type must be string, but is number", json::type_error); -#endif - } -#endif - - SECTION("issue #3077 - explicit constructor with default does not compile") - { - json j; - j[0]["value"] = true; - std::vector foo; - j.get_to(foo); - } - - SECTION("issue #3108 - ordered_json doesn't support range based erase") - { - ordered_json j = {1, 2, 2, 4}; - - auto last = std::unique(j.begin(), j.end()); - j.erase(last, j.end()); - - CHECK(j.dump() == "[1,2,4]"); - - j.erase(std::remove_if(j.begin(), j.end(), [](const ordered_json & val) - { - return val == 2; - }), j.end()); - - CHECK(j.dump() == "[1,4]"); - } - - SECTION("issue #3343 - json and ordered_json are not interchangeable") - { - json::object_t jobj({ { "product", "one" } }); - ordered_json::object_t ojobj({{"product", "one"}}); - - auto jit = jobj.begin(); - auto ojit = ojobj.begin(); - - CHECK(jit->first == ojit->first); - CHECK(jit->second.get() == ojit->second.get()); - } - - SECTION("issue #3171 - if class is_constructible from std::string wrong from_json overload is being selected, compilation failed") - { - const json j{{ "str", "value"}}; - - // failed with: error: no match for ‘operator=’ (operand types are ‘for_3171_derived’ and ‘const nlohmann::basic_json<>::string_t’ - // {aka ‘const std::__cxx11::basic_string’}) - // s = *j.template get_ptr(); - auto td = j.get(); - - CHECK(td.str == "value"); - } - -#ifdef JSON_HAS_CPP_20 - SECTION("issue #3312 - Parse to custom class from unordered_json breaks on G++11.2.0 with C++20") - { - // see test for #3171 - const ordered_json j = {{"name", "class"}}; - for_3312 obj{}; - - j.get_to(obj); - - CHECK(obj.name == "class"); - } -#endif - -#if defined(JSON_HAS_CPP_17) && JSON_USE_IMPLICIT_CONVERSIONS - SECTION("issue #3428 - Error occurred when converting nlohmann::json to std::any") - { - const json j; - const std::any a1 = j; - std::any&& a2 = j; - - CHECK(a1.type() == typeid(j)); - CHECK(a2.type() == typeid(j)); - } -#endif - - SECTION("issue #3204 - ambiguous regression") - { - const for_3204_bar bar_from_foo([](for_3204_foo) noexcept {}); // NOLINT(performance-unnecessary-value-param) - const for_3204_bar bar_from_json([](json) noexcept {}); // NOLINT(performance-unnecessary-value-param) - - CHECK(bar_from_foo.constructed_from == for_3204_bar::constructed_from_foo); - CHECK(bar_from_json.constructed_from == for_3204_bar::constructed_from_json); - } - - SECTION("issue #3333 - Ambiguous conversion from nlohmann::basic_json<> to custom class") - { - const json j - { - {"x", 1}, - {"y", 2} - }; - const for_3333 p = j; - - CHECK(p.x == 1); - CHECK(p.y == 2); - } - - SECTION("issue #3810 - ordered_json doesn't support construction from C array of custom type") - { - Example_3810 states[45]; // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) - - // fix "not used" warning - states[0].bla = 1; - - const auto* const expected = R"([{"bla":1},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0}])"; - - // This works: - nlohmann::json j; - j["test"] = states; - CHECK(j["test"].dump() == expected); - - // This doesn't compile: - nlohmann::ordered_json oj; - oj["test"] = states; - CHECK(oj["test"].dump() == expected); - } - -#ifdef JSON_HAS_CPP_17 - SECTION("issue #4740 - build issue with std::optional") - { - const auto t1 = Example_4740(); - const auto j1 = nlohmann::json(t1); - CHECK(j1.dump() == "{\"host\":null,\"port\":null}"); - const auto t2 = j1.get(); - CHECK(!t2.host.has_value()); - CHECK(!t2.port.has_value()); - - // improve coverage - auto t3 = Example_4740(); - t3.port = 80; - t3.host = "example.com"; - const auto j2 = nlohmann::json(t3); - CHECK(j2.dump() == "{\"host\":\"example.com\",\"port\":80}"); - const auto t4 = j2.get(); - CHECK(t4.host.has_value()); - CHECK(t4.port.has_value()); - } -#endif - -#if !defined(_MSVC_LANG) - // MSVC returns garbage on invalid enum values, so this test is excluded - // there. - SECTION("issue #4762 - json exception 302 with unhelpful explanation : type must be number, but is number") - { - // In #4762, the main issue was that a json object with an invalid type - // returned "number" as type_name(), because this was the default case. - // This test makes sure we now return "invalid" instead. - json j; - j.m_data.m_type = static_cast(100); // NOLINT(clang-analyzer-optin.core.EnumCastOutOfRange) - CHECK(j.type_name() == "invalid"); - } -#endif - -#ifdef JSON_HAS_CPP_17 - SECTION("issue #4804: from_cbor incompatible with std::vector as binary_t") - { - const std::vector data = {0x80}; - const auto decoded = json_4804::from_cbor(data); - CHECK((decoded == json_4804::array())); - } - - SECTION("discussion #4209 - custom BinaryType direct assignment and round-tripping") - { - // Test that assigning a custom BinaryType directly creates a binary value, not an array - const std::vector original{std::byte{1}, std::byte{2}, std::byte{3}}; - const json_4804 j = original; - CHECK(j.is_binary()); - CHECK(!j.is_array()); - - // Test round-tripping: extracting the binary value back as the custom container type - const auto extracted = j.get>(); - CHECK(extracted == original); - - // Test that the default json alias behavior is unchanged: std::vector -> array - const json default_json = std::vector {1, 2, 3}; - CHECK(default_json.is_array()); - CHECK(!default_json.is_binary()); - } - - SECTION("discussion #4209 - custom BinaryType extraction from parsed array") - { - // Test that extracting a custom BinaryType from a parsed JSON array still works - // (not just from a binary-typed node) - const auto j = json_4804::parse("[1,2,3]"); - CHECK(j.is_array()); - CHECK(!j.is_binary()); - - // Extracting as custom BinaryType should work from arrays - const auto extracted = j.get>(); - CHECK(extracted.size() == 3); - CHECK(extracted[0] == std::byte{1}); - CHECK(extracted[1] == std::byte{2}); - CHECK(extracted[2] == std::byte{3}); - } - - SECTION("issue #5046 - implicit conversion of return json to std::optional no longer implicit") - { - const json jval{}; - auto GetValue = [](const json & valRoot) -> std::optional - { - if (valRoot.contains("default")) - { - return valRoot.at("default"); - } - return std::nullopt; - }; - auto result = GetValue(jval); - CHECK(!result.has_value()); - } -#endif - -#if JSON_HAS_RANGES == 1 - SECTION("issue #4440 - assert when using std::views::filter and GCC 10") - { - auto noOpFilter = std::views::filter([](auto&&) noexcept - { - return true; - }); - json j = {1, 2, 3}; - auto filtered = j | noOpFilter; - CHECK(*filtered.begin() == 1); - } -#endif - -#if JSON_HAS_RANGES && !defined(__MINGW32__) - SECTION("issue #4916 - constructing array from C++20 ranges view does not work") - { - std::vector nums{1, 2, 37, 42, 21}; - auto filteredNums = nums | std::views::filter([](int i) - { - return i > 10; - }); - json const j(filteredNums); - CHECK(j.type() == json::value_t::array); - CHECK(j == json({37, 42, 21})); - } -#endif - - // owning_view is not available in libstdc++ < 12 -#if JSON_HAS_RANGES && !defined(__MINGW32__) && !(defined(__GLIBCXX__) && _GLIBCXX_RELEASE < 12) - SECTION("issue #4916 - constructing array from prvalue C++20 ranges view (owning_view)") - { - json const j(std::vector {1, 2, 37, 42, 21} | std::views::filter([](int i) - { - return i > 10; - })); - CHECK(j.type() == json::value_t::array); - CHECK(j == json({37, 42, 21})); - } -#endif - -#if JSON_HAS_RANGES && !defined(__MINGW32__) - SECTION("issue #4916 - constructing array from C++20 transform view (prvalue elements)") - { - std::vector nums{1, 2, 3}; - auto t = nums | std::views::transform([](int i) noexcept - { - return i * 2; - }); - json const j(t); - CHECK(j.type() == json::value_t::array); - CHECK(j == json({2, 4, 6})); - } -#endif } DOCTEST_CLANG_SUPPRESS_WARNING_POP diff --git a/tests/src/unit-regression3.cpp b/tests/src/unit-regression3.cpp index 192baa32e..db190f111 100644 --- a/tests/src/unit-regression3.cpp +++ b/tests/src/unit-regression3.cpp @@ -30,8 +30,24 @@ using ordered_json = nlohmann::ordered_json; #include #include #include +#include +#include #include +#ifdef JSON_HAS_CPP_17 + #include + #include + #if __has_include() + #include + #elif __has_include() + #include + #endif +#endif + +#if JSON_HAS_RANGES == 1 + #include +#endif + // NLOHMANN_JSON_SERIALIZE_ENUM uses a static std::pair DOCTEST_CLANG_SUPPRESS_WARNING_PUSH DOCTEST_CLANG_SUPPRESS_WARNING("-Wexit-time-destructors") @@ -350,4 +366,794 @@ TEST_CASE("issue #5338 - truncated CBOR tagged binary subtype is rejected") } } +///////////////////////////////////////////////////////////////////// +// for #3171 +///////////////////////////////////////////////////////////////////// + +struct for_3171_base // NOLINT(cppcoreguidelines-special-member-functions) +{ + for_3171_base(const std::string& /*unused*/ = {}) {} + virtual ~for_3171_base(); + + for_3171_base(const for_3171_base& other) // NOLINT(hicpp-use-equals-default,modernize-use-equals-default) + : str(other.str) + {} + + for_3171_base& operator=(const for_3171_base& other) + { + if (this != &other) + { + str = other.str; + } + return *this; + } + + for_3171_base(for_3171_base&& other) noexcept + : str(std::move(other.str)) + {} + + for_3171_base& operator=(for_3171_base&& other) noexcept + { + if (this != &other) + { + str = std::move(other.str); + } + return *this; + } + + virtual void _from_json(const json& j) + { + j.at("str").get_to(str); + } + + std::string str{}; // NOLINT(readability-redundant-member-init) +}; + +for_3171_base::~for_3171_base() = default; + +struct for_3171_derived : public for_3171_base +{ + for_3171_derived() = default; + ~for_3171_derived() override; + explicit for_3171_derived(const std::string& /*unused*/) { } + + for_3171_derived(const for_3171_derived& other) // NOLINT(hicpp-use-equals-default,modernize-use-equals-default) + : for_3171_base(other) + {} + + for_3171_derived& operator=(const for_3171_derived& other) + { + if (this != &other) + { + for_3171_base::operator=(other); // Call base class assignment operator + } + return *this; + } + + for_3171_derived(for_3171_derived&& other) noexcept + : for_3171_base(std::move(other)) + {} + + for_3171_derived& operator=(for_3171_derived&& other) noexcept + { + if (this != &other) + { + for_3171_base::operator=(std::move(other)); // Call base class move assignment operator + } + return *this; + } +}; + +for_3171_derived::~for_3171_derived() = default; + +inline void from_json(const json& j, for_3171_base& tb) // NOLINT(misc-use-internal-linkage) +{ + tb._from_json(j); +} + +///////////////////////////////////////////////////////////////////// +// for #3312 +///////////////////////////////////////////////////////////////////// + +#ifdef JSON_HAS_CPP_20 +struct for_3312 +{ + std::string name; +}; + +inline void from_json(const json& j, for_3312& obj) // NOLINT(misc-use-internal-linkage) +{ + j.at("name").get_to(obj.name); +} +#endif + +///////////////////////////////////////////////////////////////////// +// for #3204 +///////////////////////////////////////////////////////////////////// + +struct for_3204_foo +{ + for_3204_foo() = default; + explicit for_3204_foo(std::string /*unused*/) {} // NOLINT(performance-unnecessary-value-param) +}; + +struct for_3204_bar +{ + enum constructed_from_t // NOLINT(cppcoreguidelines-use-enum-class) + { + constructed_from_none = 0, + constructed_from_foo = 1, + constructed_from_json = 2 + }; + + explicit for_3204_bar(std::function /*unused*/) noexcept // NOLINT(performance-unnecessary-value-param) + : constructed_from(constructed_from_foo) {} + explicit for_3204_bar(std::function /*unused*/) noexcept // NOLINT(performance-unnecessary-value-param) + : constructed_from(constructed_from_json) {} + + constructed_from_t constructed_from = constructed_from_none; +}; + +///////////////////////////////////////////////////////////////////// +// for #3333 +///////////////////////////////////////////////////////////////////// + +struct for_3333 final +{ + for_3333(int x_ = 0, int y_ = 0) : x(x_), y(y_) {} + + template + for_3333(const T& /*unused*/) + { + CHECK(false); + } + + int x = 0; + int y = 0; +}; + +template <> +inline for_3333::for_3333(const json& j) + : for_3333(j.value("x", 0), j.value("y", 0)) +{} + +///////////////////////////////////////////////////////////////////// +// for #3810 +///////////////////////////////////////////////////////////////////// + +struct Example_3810 +{ + int bla{}; + + Example_3810() = default; +}; + +NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Example_3810, bla) // NOLINT(misc-use-internal-linkage) + +///////////////////////////////////////////////////////////////////// +// for #4740 +///////////////////////////////////////////////////////////////////// + +#ifdef JSON_HAS_CPP_17 +struct Example_4740 +{ + std::optional host = std::nullopt; + std::optional port = std::nullopt; + NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Example_4740, host, port) +}; +#endif + +TEST_CASE("regression tests 2 (continued)") +{ + SECTION("issue #3171 - if class is_constructible from std::string wrong from_json overload is being selected, compilation failed") + { + const json j{{ "str", "value"}}; + + // failed with: error: no match for ‘operator=’ (operand types are ‘for_3171_derived’ and ‘const nlohmann::basic_json<>::string_t’ + // {aka ‘const std::__cxx11::basic_string’}) + // s = *j.template get_ptr(); + auto td = j.get(); + + CHECK(td.str == "value"); + } + +#ifdef JSON_HAS_CPP_20 + SECTION("issue #3312 - Parse to custom class from unordered_json breaks on G++11.2.0 with C++20") + { + // see test for #3171 + const ordered_json j = {{"name", "class"}}; + for_3312 obj{}; + + j.get_to(obj); + + CHECK(obj.name == "class"); + } +#endif + +#if defined(JSON_HAS_CPP_17) && JSON_USE_IMPLICIT_CONVERSIONS + SECTION("issue #3428 - Error occurred when converting nlohmann::json to std::any") + { + const json j; + const std::any a1 = j; + std::any&& a2 = j; + + CHECK(a1.type() == typeid(j)); + CHECK(a2.type() == typeid(j)); + } +#endif + + SECTION("issue #3204 - ambiguous regression") + { + const for_3204_bar bar_from_foo([](for_3204_foo) noexcept {}); // NOLINT(performance-unnecessary-value-param) + const for_3204_bar bar_from_json([](json) noexcept {}); // NOLINT(performance-unnecessary-value-param) + + CHECK(bar_from_foo.constructed_from == for_3204_bar::constructed_from_foo); + CHECK(bar_from_json.constructed_from == for_3204_bar::constructed_from_json); + } + + SECTION("issue #3333 - Ambiguous conversion from nlohmann::basic_json<> to custom class") + { + const json j + { + {"x", 1}, + {"y", 2} + }; + const for_3333 p = j; + + CHECK(p.x == 1); + CHECK(p.y == 2); + } + + SECTION("issue #3810 - ordered_json doesn't support construction from C array of custom type") + { + Example_3810 states[45]; // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) + + // fix "not used" warning + states[0].bla = 1; + + const auto* const expected = R"([{"bla":1},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0}])"; + + // This works: + nlohmann::json j; + j["test"] = states; + CHECK(j["test"].dump() == expected); + + // This doesn't compile: + nlohmann::ordered_json oj; + oj["test"] = states; + CHECK(oj["test"].dump() == expected); + } + +#ifdef JSON_HAS_CPP_17 + SECTION("issue #4740 - build issue with std::optional") + { + const auto t1 = Example_4740(); + const auto j1 = nlohmann::json(t1); + CHECK(j1.dump() == "{\"host\":null,\"port\":null}"); + const auto t2 = j1.get(); + CHECK(!t2.host.has_value()); + CHECK(!t2.port.has_value()); + + // improve coverage + auto t3 = Example_4740(); + t3.port = 80; + t3.host = "example.com"; + const auto j2 = nlohmann::json(t3); + CHECK(j2.dump() == "{\"host\":\"example.com\",\"port\":80}"); + const auto t4 = j2.get(); + CHECK(t4.host.has_value()); + CHECK(t4.port.has_value()); + } +#endif + +#if !defined(_MSVC_LANG) + // MSVC returns garbage on invalid enum values, so this test is excluded + // there. + SECTION("issue #4762 - json exception 302 with unhelpful explanation : type must be number, but is number") + { + // In #4762, the main issue was that a json object with an invalid type + // returned "number" as type_name(), because this was the default case. + // This test makes sure we now return "invalid" instead. + json j; + j.m_data.m_type = static_cast(100); // NOLINT(clang-analyzer-optin.core.EnumCastOutOfRange) + CHECK(j.type_name() == "invalid"); + } +#endif + +#ifdef JSON_HAS_CPP_17 + SECTION("issue #4804: from_cbor incompatible with std::vector as binary_t") + { + const std::vector data = {0x80}; + const auto decoded = json_4804::from_cbor(data); + CHECK((decoded == json_4804::array())); + } + + SECTION("discussion #4209 - custom BinaryType direct assignment and round-tripping") + { + // Test that assigning a custom BinaryType directly creates a binary value, not an array + const std::vector original{std::byte{1}, std::byte{2}, std::byte{3}}; + const json_4804 j = original; + CHECK(j.is_binary()); + CHECK(!j.is_array()); + + // Test round-tripping: extracting the binary value back as the custom container type + const auto extracted = j.get>(); + CHECK(extracted == original); + + // Test that the default json alias behavior is unchanged: std::vector -> array + const json default_json = std::vector {1, 2, 3}; + CHECK(default_json.is_array()); + CHECK(!default_json.is_binary()); + } + + SECTION("discussion #4209 - custom BinaryType extraction from parsed array") + { + // Test that extracting a custom BinaryType from a parsed JSON array still works + // (not just from a binary-typed node) + const auto j = json_4804::parse("[1,2,3]"); + CHECK(j.is_array()); + CHECK(!j.is_binary()); + + // Extracting as custom BinaryType should work from arrays + const auto extracted = j.get>(); + CHECK(extracted.size() == 3); + CHECK(extracted[0] == std::byte{1}); + CHECK(extracted[1] == std::byte{2}); + CHECK(extracted[2] == std::byte{3}); + } + + SECTION("issue #5046 - implicit conversion of return json to std::optional no longer implicit") + { + const json jval{}; + auto GetValue = [](const json & valRoot) -> std::optional + { + if (valRoot.contains("default")) + { + return valRoot.at("default"); + } + return std::nullopt; + }; + auto result = GetValue(jval); + CHECK(!result.has_value()); + } +#endif + +#if JSON_HAS_RANGES == 1 + SECTION("issue #4440 - assert when using std::views::filter and GCC 10") + { + auto noOpFilter = std::views::filter([](auto&&) noexcept + { + return true; + }); + json j = {1, 2, 3}; + auto filtered = j | noOpFilter; + CHECK(*filtered.begin() == 1); + } +#endif + +#if JSON_HAS_RANGES && !defined(__MINGW32__) + SECTION("issue #4916 - constructing array from C++20 ranges view does not work") + { + std::vector nums{1, 2, 37, 42, 21}; + auto filteredNums = nums | std::views::filter([](int i) + { + return i > 10; + }); + json const j(filteredNums); + CHECK(j.type() == json::value_t::array); + CHECK(j == json({37, 42, 21})); + } +#endif + + // owning_view is not available in libstdc++ < 12 +#if JSON_HAS_RANGES && !defined(__MINGW32__) && !(defined(__GLIBCXX__) && _GLIBCXX_RELEASE < 12) + SECTION("issue #4916 - constructing array from prvalue C++20 ranges view (owning_view)") + { + json const j(std::vector {1, 2, 37, 42, 21} | std::views::filter([](int i) + { + return i > 10; + })); + CHECK(j.type() == json::value_t::array); + CHECK(j == json({37, 42, 21})); + } +#endif + +#if JSON_HAS_RANGES && !defined(__MINGW32__) + SECTION("issue #4916 - constructing array from C++20 transform view (prvalue elements)") + { + std::vector nums{1, 2, 3}; + auto t = nums | std::views::transform([](int i) noexcept + { + return i * 2; + }); + json const j(t); + CHECK(j.type() == json::value_t::array); + CHECK(j == json({2, 4, 6})); + } +#endif +} + +///////////////////////////////////////////////////////////////////// +// for #2574 +///////////////////////////////////////////////////////////////////// + +struct NonDefaultConstructible +{ + explicit NonDefaultConstructible(int a) + : x(a) + {} + int x; +}; + +namespace nlohmann +{ +template<> +struct adl_serializer +{ + static NonDefaultConstructible from_json(json const& j) + { + return NonDefaultConstructible(j.get()); + } +}; +} // namespace nlohmann + +///////////////////////////////////////////////////////////////////// +// for #2824 +///////////////////////////////////////////////////////////////////// + +class sax_no_exception : public nlohmann::detail::json_sax_dom_parser +{ + public: + explicit sax_no_exception(json& j) + : nlohmann::detail::json_sax_dom_parser(j, false) + {} + + static bool parse_error(std::size_t /*position*/, const std::string& /*last_token*/, const json::exception& ex) + { + error_string = new std::string(ex.what()); // NOLINT(cppcoreguidelines-owning-memory) + return false; + } + + static std::string* error_string; +}; + +std::string* sax_no_exception::error_string = nullptr; + +///////////////////////////////////////////////////////////////////// +// for #2982 +///////////////////////////////////////////////////////////////////// + +template +class my_allocator : public std::allocator +{ + public: + using std::allocator::allocator; + + my_allocator() = default; + template my_allocator(const my_allocator& /*unused*/) { } + + template + struct rebind + { + using other = my_allocator; + }; +}; + +///////////////////////////////////////////////////////////////////// +// for #3077 +///////////////////////////////////////////////////////////////////// + +class FooAlloc +{}; + +class Foo +{ + public: + explicit Foo(const FooAlloc& /* unused */ = FooAlloc()) {} + + bool value = false; +}; + +class FooBar +{ + public: + Foo foo{}; // NOLINT(readability-redundant-member-init) +}; + +inline void from_json(const nlohmann::json& j, FooBar& fb) // NOLINT(misc-use-internal-linkage) +{ + j.at("value").get_to(fb.foo.value); +} + + +TEST_CASE("regression tests 2 (continued 2)") +{ + SECTION("issue #2067 - cannot serialize binary data to text JSON") + { + const std::array data = {{0x81, 0xA4, 0x64, 0x61, 0x74, 0x61, 0xC4, 0x0F, 0x33, 0x30, 0x30, 0x32, 0x33, 0x34, 0x30, 0x31, 0x30, 0x37, 0x30, 0x35, 0x30, 0x31, 0x30}}; + const json j = json::from_msgpack(data.data(), data.size()); + CHECK_NOTHROW( + j.dump(4, // Indent + ' ', // Indent char + false, // Ensure ascii + json::error_handler_t::strict // Error + )); + } + + SECTION("PR #2181 - regression bug with lvalue") + { + // see https://github.com/nlohmann/json/pull/2181#issuecomment-653326060 + const json j{{"x", "test"}}; + const std::string defval = "default value"; + auto val = j.value("x", defval); // NOLINT(bugprone-unused-local-non-trivial-variable) + auto val2 = j.value("y", defval); // NOLINT(bugprone-unused-local-non-trivial-variable) + } + + SECTION("issue #2293 - eof doesn't cause parsing to stop") + { + const std::vector data = + { + 0x7B, + 0x6F, + 0x62, + 0x6A, + 0x65, + 0x63, + 0x74, + 0x20, + 0x4F, + 0x42 + }; + const json result = json::from_cbor(data, true, false); + CHECK(result.is_discarded()); + } + + SECTION("issue #2315 - json.update and vectordoes not work with ordered_json") + { + nlohmann::ordered_json jsonAnimals = {{"animal", "dog"}}; + const nlohmann::ordered_json jsonCat = {{"animal", "cat"}}; + jsonAnimals.update(jsonCat); + CHECK(jsonAnimals["animal"] == "cat"); + + auto jsonAnimals_parsed = nlohmann::ordered_json::parse(jsonAnimals.dump()); + CHECK(jsonAnimals == jsonAnimals_parsed); + + const std::vector> intData = {std::make_pair("aaaa", 11), + std::make_pair("bbb", 222) + }; + nlohmann::ordered_json jsonObj; + for (const auto& data : intData) + { + jsonObj[data.first] = data.second; + } + CHECK(jsonObj["aaaa"] == 11); + CHECK(jsonObj["bbb"] == 222); + } + + SECTION("issue #2330 - ignore_comment=true fails on multiple consecutive lines starting with comments") + { + const std::string ss = "//\n//\n{\n}\n"; + const json j = json::parse(ss, nullptr, true, true); + CHECK(j.dump() == "{}"); + } + +#ifdef JSON_HAS_CPP_20 +#ifndef _LIBCPP_VERSION // see https://github.com/nlohmann/json/issues/4490 + // classic Intel ICC reports as includable but cannot actually compile + // std::span/std::as_bytes usage below +#if __has_include() && !defined(__ICC) && !defined(__INTEL_COMPILER) + SECTION("issue #2546 - parsing containers of std::byte") + { + const char DATA[] = R"("Hello, world!")"; // NOLINT(misc-const-correctness,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) + const auto s = std::as_bytes(std::span(DATA)); + const json j = json::parse(s); + CHECK(j.dump() == "\"Hello, world!\""); + } +#endif +#endif +#endif + + SECTION("issue #2574 - Deserialization to std::array, std::pair, and std::tuple with non-default constructable types fails") + { + SECTION("std::array") + { + { + const json j = {7, 4}; + auto arr = j.get>(); + CHECK(arr[0].x == 7); + CHECK(arr[1].x == 4); + } + + { + const json j = 7; + CHECK_THROWS_AS((j.get>()), json::type_error); + } + } + + SECTION("std::pair") + { + { + const json j = {3, 8}; + auto p = j.get>(); + CHECK(p.first.x == 3); + CHECK(p.second.x == 8); + } + + { + const json j = {4, 1}; + auto p = j.get>(); + CHECK(p.first == 4); + CHECK(p.second.x == 1); + } + + { + const json j = {6, 7}; + auto p = j.get>(); + CHECK(p.first.x == 6); + CHECK(p.second == 7); + } + + { + const json j = 7; + CHECK_THROWS_AS((j.get>()), json::type_error); + } + } + + SECTION("std::tuple") + { + { + const json j = {9}; + auto t = j.get>(); + CHECK(std::get<0>(t).x == 9); + } + + { + const json j = {9, 8, 7}; + auto t = j.get>(); + CHECK(std::get<0>(t).x == 9); + CHECK(std::get<1>(t) == 8); + CHECK(std::get<2>(t).x == 7); + } + + { + const json j = 7; + CHECK_THROWS_AS((j.get>()), json::type_error); + } + } + } + + SECTION("issue #4530 - Serialization of empty tuple") + { + const auto source_tuple = std::tuple<>(); + const nlohmann::json j = source_tuple; + + CHECK(j.get() == source_tuple); + CHECK("[]" == j.dump()); + } + + SECTION("issue #2865 - ASAN detects memory leaks") + { + // the code below is expected to not leak memory + { + nlohmann::json o; + const std::string s = "bar"; + + nlohmann::to_json(o["foo"], s); + + nlohmann::json p = o; + + // call to_json with a non-null JSON value + nlohmann::to_json(p["foo"], s); + } + + { + nlohmann::json o; + const std::string s = "bar"; + + nlohmann::to_json(o["foo"], s); + + // call to_json with a non-null JSON value + nlohmann::to_json(o["foo"], s); + } + } + + SECTION("issue #2824 - encoding of json::exception::what()") + { + json j; + sax_no_exception sax(j); + + CHECK(!json::sax_parse("xyz", &sax)); + CHECK(*sax_no_exception::error_string == "[json.exception.parse_error.101] parse error at line 1, column 1: syntax error while parsing value - invalid literal; last read: 'x'"); + delete sax_no_exception::error_string; // NOLINT(cppcoreguidelines-owning-memory) + } + + SECTION("issue #2825 - Properly constrain the basic_json conversion operator") + { + static_assert(std::is_copy_assignable::value, "ordered_json must be copy assignable"); + } + + SECTION("issue #2958 - Inserting in unordered json using a pointer retains the leading slash") + { + const std::string p = "/root"; + + json test1; + test1[json::json_pointer(p)] = json::object(); + CHECK(test1.dump() == "{\"root\":{}}"); + + ordered_json test2; + test2[ordered_json::json_pointer(p)] = json::object(); + CHECK(test2.dump() == "{\"root\":{}}"); + + // json::json_pointer and ordered_json::json_pointer are the same type; behave as above + ordered_json test3; + test3[json::json_pointer(p)] = json::object(); + CHECK(std::is_same::value); + CHECK(test3.dump() == "{\"root\":{}}"); + } + + SECTION("issue #2982 - to_{binary format} does not provide a mechanism for specifying a custom allocator for the returned type") + { + std::vector> my_vector; + const json j = {1, 2, 3, 4}; + json::to_cbor(j, my_vector); + json k = json::from_cbor(my_vector); + CHECK(j == k); + } + +#if JSON_HAS_FILESYSTEM || JSON_HAS_EXPERIMENTAL_FILESYSTEM + // JSON_HAS_CPP_17 (do not remove; see note at top of file) + SECTION("issue #3070 - Version 3.10.3 breaks backward-compatibility with 3.10.2 ") + { + nlohmann::detail::std_fs::path text_path("/tmp/text.txt"); + const json j(text_path); + + const auto j_path = j.get(); + CHECK(j_path == text_path); + +#if DOCTEST_CLANG || DOCTEST_GCC >= DOCTEST_COMPILER(8, 4, 0) + // only known to work on Clang and GCC >=8.4 + CHECK_THROWS_WITH_AS(nlohmann::detail::std_fs::path(json(1)), "[json.exception.type_error.302] type must be string, but is number", json::type_error); +#endif + } +#endif + + SECTION("issue #3077 - explicit constructor with default does not compile") + { + json j; + j[0]["value"] = true; + std::vector foo; + j.get_to(foo); + } + + SECTION("issue #3108 - ordered_json doesn't support range based erase") + { + ordered_json j = {1, 2, 2, 4}; + + auto last = std::unique(j.begin(), j.end()); + j.erase(last, j.end()); + + CHECK(j.dump() == "[1,2,4]"); + + j.erase(std::remove_if(j.begin(), j.end(), [](const ordered_json & val) + { + return val == 2; + }), j.end()); + + CHECK(j.dump() == "[1,4]"); + } + + SECTION("issue #3343 - json and ordered_json are not interchangeable") + { + json::object_t jobj({ { "product", "one" } }); + ordered_json::object_t ojobj({{"product", "one"}}); + + auto jit = jobj.begin(); + auto ojit = ojobj.begin(); + + CHECK(jit->first == ojit->first); + CHECK(jit->second.get() == ojit->second.get()); + } + +} + DOCTEST_CLANG_SUPPRESS_WARNING_POP