From 2301f01284a7067b597445d7db06d87eaa2e59d0 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 2 Nov 2026 05:59:57 +0000 Subject: [PATCH] Fix test failures in the coverage tests - unit-custom-object-key-type: remove the unused make_strict_key(strict_key) overload, the unused range constructor and default constructor, and guard data() with JSON_DIAGNOSTICS (unused-function/-member-function errors with GCC and clang -Weverything). - unit-json_view_compare: move the two new sections to the new unit-json_view_compare_values.cpp, as the object file got too large for the MinGW assembler again, and use a std::map with std::greater instead of std::unordered_map, which does not accept the incomplete basic_json (clang 3.9, icpc). - unit-json_view_dump: rename the variables that hid outer ones (MSVC C4456). - unit-stream-bulk-scan: restore the real size of the oversized get area before the adapter is destroyed (JSON_PRECISE_STREAM_POSITION reads the character at the read position), and skip the test where the streambuf cannot hold a get area larger than an int (MSVC). - unit-regression1: with JSON_NO_THREAD_LOCAL, copies are built member by member and keep correct timestamps, so check the corresponding outcome. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01N5NasAgbDGG28MqXjQvLqp Signed-off-by: Claude --- tests/src/unit-custom-object-key-type.cpp | 19 +---- tests/src/unit-json_view_compare.cpp | 62 --------------- tests/src/unit-json_view_compare_values.cpp | 83 +++++++++++++++++++++ tests/src/unit-json_view_dump.cpp | 14 ++-- tests/src/unit-regression1.cpp | 10 ++- tests/src/unit-stream-bulk-scan.cpp | 20 ++++- 6 files changed, 117 insertions(+), 91 deletions(-) create mode 100644 tests/src/unit-json_view_compare_values.cpp diff --git a/tests/src/unit-custom-object-key-type.cpp b/tests/src/unit-custom-object-key-type.cpp index 350a30050..acb78d628 100644 --- a/tests/src/unit-custom-object-key-type.cpp +++ b/tests/src/unit-custom-object-key-type.cpp @@ -139,20 +139,20 @@ namespace class strict_key { public: - strict_key() = default; - strict_key(const char* value) : m_value(value) {} strict_key(const std::string& /*value*/) = delete; +#if JSON_DIAGNOSTICS // Required by JSON_DIAGNOSTICS, which reads object keys through data() // when building the path of an exception. const char* data() const noexcept { return m_value.data(); } +#endif friend bool operator<(const strict_key& lhs, const strict_key& rhs) { @@ -168,11 +168,6 @@ strict_key make_strict_key(const std::string& key) return strict_key(key.c_str()); } -strict_key make_strict_key(const strict_key& key) -{ - return key; -} - template class strict_object : public std::map < @@ -184,16 +179,6 @@ class strict_object { public: strict_object() = default; - - // the range of another object type, whose keys are made into strict_keys - template - strict_object(Iterator first, Iterator last) - { - for (; first != last; ++first) - { - this->emplace(make_strict_key(first->first), Value(std::move(first->second))); - } - } }; template diff --git a/tests/src/unit-json_view_compare.cpp b/tests/src/unit-json_view_compare.cpp index 8316ec74a..48527adb4 100644 --- a/tests/src/unit-json_view_compare.cpp +++ b/tests/src/unit-json_view_compare.cpp @@ -21,22 +21,13 @@ using json_view_test::generator; #include #include -#include #include -#include #include // These tests were split off unit-json_view_dump.cpp, whose object file got too // large for the MinGW assembler ("file too big"). Like that file, this one is // also built with C++17, as it mentions JSON_HAS_CPP_17. -namespace -{ -// an object type without a comparison of keys, and with an arbitrary order of members -template -using unordered_object = std::unordered_map, std::equal_to, Allocator>; -} // namespace - TEST_CASE("json_view comparison") { SECTION("equality of the values parse() produces") @@ -214,59 +205,6 @@ TEST_CASE("json_view comparison") CHECK(!(null_document.root() == discarded)); } - SECTION("values that differ in their first bytes, or are written differently") - { - const auto same = [](const char* x, const char* y) - { - const json_document dx = json_document::parse(x); - const json_document dy = json_document::parse(y); - const bool equal = dx.root() == dy.root(); - CHECK(equal == (json::parse(x) == json::parse(y))); - CHECK(equal == (dx.root() == json::parse(y))); - const nlohmann::json_editable_document ex = nlohmann::json_editable_document::parse(x); - CHECK(equal == (ex.root() == dy.root())); - return equal; - }; - // booleans - CHECK(!same("true", "false")); - CHECK(!same("[true]", "[false]")); - CHECK(!same(R"({"a":false,"b":[1]})", R"({"a":true,"b":[1]})")); - CHECK(same(R"({"a":true,"b":[1]})", R"({"a":true,"b":[1]})")); - // strings and keys of equal length that differ in the first eight bytes (they are compared in words) - CHECK(!same(R"("abcdefghijklmnopqrstuvwxyz")", R"("Xbcdefghijklmnopqrstuvwxyz")")); - CHECK(!same(R"(["abcdefghijklmnopq",1])", R"(["abcdefghXjklmnopq",1])")); - CHECK(!same(R"({"abcdefghijklmnopq":1})", R"({"Xbcdefghijklmnopq":1})")); - CHECK(!same(R"({"abcdefghijklmnopq":[1]})", R"({"abcdefghijklmnopX":[1]})")); - // floats of one layout and different tokens are the same value - CHECK(same("[1.5e0, 2]", "[1.5E0, 2]")); - CHECK(same("[1.5e0, 2]", "[1.5e+0, 2]")); - CHECK(same("[1.50, 2]", "[1.5, 2]")); - CHECK(!same("[1.5e0, 2]", "[1.6e0, 2]")); - CHECK(!same("[1.5e0, 2]", "[1.5E1, 2]")); - CHECK(same(R"({"a":1.25e0,"b":[3]})", R"({"a":1.25E0,"b":[3]})")); - } - - SECTION("a basic_json whose members are not sorted") - { - // (an object type that is not an ordered map, and does not keep its members sorted either) - using unordered_json = nlohmann::basic_json; - using unordered_document = nlohmann::basic_json_document; - std::string text = "{"; - for (char key = 'a'; key <= 'p'; ++key) - { - text += (key != 'a' ? ",\"" : "\"") + std::string(1, key) + "\":[" + std::to_string(key) + "]"; - } - text += "}"; - const unordered_json j = unordered_json::parse(text); - const unordered_document d = unordered_document::parse(text); - CHECK(d.root() == j); - CHECK(j == d.root()); - unordered_json other = j; - other["k"] = 0; - CHECK(d.root() != other); - CHECK(other != d.root()); - } - SECTION("values that are the same text, or the same value written differently, at any depth") { // nested arrays and objects around depths where the walk over equal text gives up diff --git a/tests/src/unit-json_view_compare_values.cpp b/tests/src/unit-json_view_compare_values.cpp new file mode 100644 index 000000000..132978d8d --- /dev/null +++ b/tests/src/unit-json_view_compare_values.cpp @@ -0,0 +1,83 @@ +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ (supporting code) +// | | |__ | | | | | | version 3.12.0 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann +// SPDX-License-Identifier: MIT + +#include "doctest_compatibility.h" + +#include +using nlohmann::json; +using nlohmann::json_document; + +#include +#include +#include + +// These tests were split off unit-json_view_compare.cpp, whose object file got +// too large for the MinGW assembler ("file too big"). + +namespace +{ +// an object type that does not enumerate its members in ascending order of keys +template +using descending_object = std::map, Allocator>; +} // namespace + +TEST_CASE("json_view comparison of differing values") +{ + SECTION("values that differ in their first bytes, or are written differently") + { + const auto same = [](const char* x, const char* y) + { + const json_document dx = json_document::parse(x); + const json_document dy = json_document::parse(y); + const bool equal = dx.root() == dy.root(); + CHECK(equal == (json::parse(x) == json::parse(y))); + CHECK(equal == (dx.root() == json::parse(y))); + const nlohmann::json_editable_document ex = nlohmann::json_editable_document::parse(x); + CHECK(equal == (ex.root() == dy.root())); + return equal; + }; + // booleans + CHECK(!same("true", "false")); + CHECK(!same("[true]", "[false]")); + CHECK(!same(R"({"a":false,"b":[1]})", R"({"a":true,"b":[1]})")); + CHECK(same(R"({"a":true,"b":[1]})", R"({"a":true,"b":[1]})")); + // strings and keys of equal length that differ in the first eight bytes (they are compared in words) + CHECK(!same(R"("abcdefghijklmnopqrstuvwxyz")", R"("Xbcdefghijklmnopqrstuvwxyz")")); + CHECK(!same(R"(["abcdefghijklmnopq",1])", R"(["abcdefghXjklmnopq",1])")); + CHECK(!same(R"({"abcdefghijklmnopq":1})", R"({"Xbcdefghijklmnopq":1})")); + CHECK(!same(R"({"abcdefghijklmnopq":[1]})", R"({"abcdefghijklmnopX":[1]})")); + // floats of one layout and different tokens are the same value + CHECK(same("[1.5e0, 2]", "[1.5E0, 2]")); + CHECK(same("[1.5e0, 2]", "[1.5e+0, 2]")); + CHECK(same("[1.50, 2]", "[1.5, 2]")); + CHECK(!same("[1.5e0, 2]", "[1.6e0, 2]")); + CHECK(!same("[1.5e0, 2]", "[1.5E1, 2]")); + CHECK(same(R"({"a":1.25e0,"b":[3]})", R"({"a":1.25E0,"b":[3]})")); + } + + SECTION("a basic_json whose members are not sorted") + { + // (an object type that is not an ordered map, and does not keep its members sorted either) + using descending_json = nlohmann::basic_json; + using descending_document = nlohmann::basic_json_document; + std::string text = "{"; + for (char key = 'a'; key <= 'p'; ++key) + { + text += (key != 'a' ? ",\"" : "\"") + std::string(1, key) + "\":[" + std::to_string(key) + "]"; + } + text += "}"; + const descending_json j = descending_json::parse(text); + const descending_document d = descending_document::parse(text); + CHECK(d.root() == j); + CHECK(j == d.root()); + descending_json other = j; + other["k"] = 0; + CHECK(d.root() != other); + CHECK(other != d.root()); + } +} diff --git a/tests/src/unit-json_view_dump.cpp b/tests/src/unit-json_view_dump.cpp index 301f07a53..fc6501f62 100644 --- a/tests/src/unit-json_view_dump.cpp +++ b/tests/src/unit-json_view_dump.cpp @@ -247,15 +247,15 @@ TEST_CASE("json_view dump") }) { CAPTURE(token) - const std::string text = std::string("[") + token + padding + "]"; - const json_document d = json_document::parse(text); - const json j = json::parse(text); - CHECK(d.root()[0].get() == j[0].get()); - CHECK(d.root().dump() == j.dump()); + const std::string padded = std::string("[") + token + padding + "]"; + const json_document padded_document = json_document::parse(padded); + const json padded_json = json::parse(padded); + CHECK(padded_document.root()[0].get() == padded_json[0].get()); + CHECK(padded_document.root().dump() == padded_json.dump()); // a float document converts the tokens as well using json_float = nlohmann::basic_json; - const nlohmann::basic_json_document float_doc = nlohmann::basic_json_document::parse(text); - CHECK(float_doc.root()[0].get() == json_float::parse(text)[0].get()); + const nlohmann::basic_json_document float_doc = nlohmann::basic_json_document::parse(padded); + CHECK(float_doc.root()[0].get() == json_float::parse(padded)[0].get()); } } diff --git a/tests/src/unit-regression1.cpp b/tests/src/unit-regression1.cpp index e62b5c58b..9338365c8 100644 --- a/tests/src/unit-regression1.cpp +++ b/tests/src/unit-regression1.cpp @@ -1553,11 +1553,19 @@ TEST_CASE("regression tests 1") SECTION("a missing member of an object") { + my_json j = make(); +#ifdef JSON_NO_THREAD_LOCAL + // the objects below the outermost one are not copied by their + // object_t here, but member by member, which keeps their + // timestamps correct: "c" is not mistaken for "b" and is inserted + CHECK(j[my_json::json_pointer("/k/a/c/d")].is_null()); + CHECK(j["x"]["a"].dump() == R"({"b":1,"c":{"d":null}})"); +#else // the timestamp of "c" is that of "b" again, so that emplace() // finds the member "b", a number, which has no member "d" - my_json j = make(); CHECK_THROWS_AS(j[my_json::json_pointer("/k/a/c/d")], my_json::out_of_range&); CHECK(j["x"]["a"].dump() == R"({"b":1})"); +#endif } SECTION("a null value is converted to an object") diff --git a/tests/src/unit-stream-bulk-scan.cpp b/tests/src/unit-stream-bulk-scan.cpp index c1f162466..0e0fbd72e 100644 --- a/tests/src/unit-stream-bulk-scan.cpp +++ b/tests/src/unit-stream-bulk-scan.cpp @@ -109,6 +109,14 @@ class oversized_get_area_streambuf : public std::streambuf setg(m_data.data(), m_data.data(), m_data.data() + claimed_size); } + // Give the get area back its real size, so that the adapter may read the + // character at the read position when it is destroyed (with + // JSON_PRECISE_STREAM_POSITION, it consumes the last character returned). + void restore_get_area() + { + setg(m_data.data(), m_data.data(), m_data.data() + m_data.size()); + } + private: std::array m_data{{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'}}; }; @@ -458,14 +466,16 @@ TEST_CASE("parsing from a std::istream scans the get area in bulk") SECTION("skipping more characters than gbump() takes at once") { // gbump() takes an int, but a get area can be larger - if (sizeof(std::size_t) < 8) + const std::size_t claimed = sizeof(std::size_t) < 8 ? 0 : std::size_t{3} << 30u; + oversized_get_area_streambuf buf(claimed); + if (claimed == 0 || static_cast(buf.in_avail()) != claimed) { - MESSAGE("a get area larger than an int needs a 64-bit size_t"); + // a 32-bit size_t, or a standard library whose streambuf stores + // the size of the get area in an int (MSVC) + MESSAGE("a get area larger than an int is not supported"); } else { - const std::size_t claimed = std::size_t{3} << 30u; - oversized_get_area_streambuf buf(claimed); std::istream is(&buf); nlohmann::detail::input_stream_adapter ia(is); CHECK(ia.get_character() == 'a'); @@ -477,6 +487,8 @@ TEST_CASE("parsing from a std::istream scans the get area in bulk") ia.bulk_skip(ia.bulk_remaining()); CHECK(ia.bulk_remaining() == 0); + + buf.restore_get_area(); } } #endif