mirror of
https://github.com/nlohmann/json.git
synced 2026-11-02 19:05:35 +00:00
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 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N5NasAgbDGG28MqXjQvLqp Signed-off-by: Claude <noreply@anthropic.com>
This commit is contained in:
6 files changed
+117
-91
No files matched your search
@@ -139,20 +139,20 @@ namespace
|
|||||||
class strict_key
|
class strict_key
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
strict_key() = default;
|
|
||||||
|
|
||||||
strict_key(const char* value)
|
strict_key(const char* value)
|
||||||
: m_value(value)
|
: m_value(value)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
strict_key(const std::string& /*value*/) = delete;
|
strict_key(const std::string& /*value*/) = delete;
|
||||||
|
|
||||||
|
#if JSON_DIAGNOSTICS
|
||||||
// Required by JSON_DIAGNOSTICS, which reads object keys through data()
|
// Required by JSON_DIAGNOSTICS, which reads object keys through data()
|
||||||
// when building the path of an exception.
|
// when building the path of an exception.
|
||||||
const char* data() const noexcept
|
const char* data() const noexcept
|
||||||
{
|
{
|
||||||
return m_value.data();
|
return m_value.data();
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
friend bool operator<(const strict_key& lhs, const strict_key& rhs)
|
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());
|
return strict_key(key.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
strict_key make_strict_key(const strict_key& key)
|
|
||||||
{
|
|
||||||
return key;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class Key, class Value, class Compare, class Allocator>
|
template<class Key, class Value, class Compare, class Allocator>
|
||||||
class strict_object
|
class strict_object
|
||||||
: public std::map <
|
: public std::map <
|
||||||
@@ -184,16 +179,6 @@ class strict_object
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
strict_object() = default;
|
strict_object() = default;
|
||||||
|
|
||||||
// the range of another object type, whose keys are made into strict_keys
|
|
||||||
template<class Iterator>
|
|
||||||
strict_object(Iterator first, Iterator last)
|
|
||||||
{
|
|
||||||
for (; first != last; ++first)
|
|
||||||
{
|
|
||||||
this->emplace(make_strict_key(first->first), Value(std::move(first->second)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename T, typename = void>
|
template<typename T, typename = void>
|
||||||
|
|||||||
@@ -21,22 +21,13 @@ using json_view_test::generator;
|
|||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <functional>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_map>
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
// These tests were split off unit-json_view_dump.cpp, whose object file got too
|
// 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
|
// 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.
|
// 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<typename Key, typename Value, typename /*Compare*/, typename Allocator>
|
|
||||||
using unordered_object = std::unordered_map<Key, Value, std::hash<Key>, std::equal_to<Key>, Allocator>;
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
TEST_CASE("json_view comparison")
|
TEST_CASE("json_view comparison")
|
||||||
{
|
{
|
||||||
SECTION("equality of the values parse() produces")
|
SECTION("equality of the values parse() produces")
|
||||||
@@ -214,59 +205,6 @@ TEST_CASE("json_view comparison")
|
|||||||
CHECK(!(null_document.root() == discarded));
|
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<unordered_object>;
|
|
||||||
using unordered_document = nlohmann::basic_json_document<unordered_json>;
|
|
||||||
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")
|
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
|
// nested arrays and objects around depths where the walk over equal text gives up
|
||||||
|
|||||||
@@ -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 <https://nlohmann.me>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#include "doctest_compatibility.h"
|
||||||
|
|
||||||
|
#include <nlohmann/json_view.hpp>
|
||||||
|
using nlohmann::json;
|
||||||
|
using nlohmann::json_document;
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <map>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
// 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<typename Key, typename Value, typename /*Compare*/, typename Allocator>
|
||||||
|
using descending_object = std::map<Key, Value, std::greater<Key>, 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<descending_object>;
|
||||||
|
using descending_document = nlohmann::basic_json_document<descending_json>;
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -247,15 +247,15 @@ TEST_CASE("json_view dump")
|
|||||||
})
|
})
|
||||||
{
|
{
|
||||||
CAPTURE(token)
|
CAPTURE(token)
|
||||||
const std::string text = std::string("[") + token + padding + "]";
|
const std::string padded = std::string("[") + token + padding + "]";
|
||||||
const json_document d = json_document::parse(text);
|
const json_document padded_document = json_document::parse(padded);
|
||||||
const json j = json::parse(text);
|
const json padded_json = json::parse(padded);
|
||||||
CHECK(d.root()[0].get<double>() == j[0].get<double>());
|
CHECK(padded_document.root()[0].get<double>() == padded_json[0].get<double>());
|
||||||
CHECK(d.root().dump() == j.dump());
|
CHECK(padded_document.root().dump() == padded_json.dump());
|
||||||
// a float document converts the tokens as well
|
// a float document converts the tokens as well
|
||||||
using json_float = nlohmann::basic_json<std::map, std::vector, std::string, bool, std::int64_t, std::uint64_t, float>;
|
using json_float = nlohmann::basic_json<std::map, std::vector, std::string, bool, std::int64_t, std::uint64_t, float>;
|
||||||
const nlohmann::basic_json_document<json_float> float_doc = nlohmann::basic_json_document<json_float>::parse(text);
|
const nlohmann::basic_json_document<json_float> float_doc = nlohmann::basic_json_document<json_float>::parse(padded);
|
||||||
CHECK(float_doc.root()[0].get<float>() == json_float::parse(text)[0].get<float>());
|
CHECK(float_doc.root()[0].get<float>() == json_float::parse(padded)[0].get<float>());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1553,11 +1553,19 @@ TEST_CASE("regression tests 1")
|
|||||||
|
|
||||||
SECTION("a missing member of an object")
|
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()
|
// the timestamp of "c" is that of "b" again, so that emplace()
|
||||||
// finds the member "b", a number, which has no member "d"
|
// 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_THROWS_AS(j[my_json::json_pointer("/k/a/c/d")], my_json::out_of_range&);
|
||||||
CHECK(j["x"]["a"].dump() == R"({"b":1})");
|
CHECK(j["x"]["a"].dump() == R"({"b":1})");
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("a null value is converted to an object")
|
SECTION("a null value is converted to an object")
|
||||||
|
|||||||
@@ -109,6 +109,14 @@ class oversized_get_area_streambuf : public std::streambuf
|
|||||||
setg(m_data.data(), m_data.data(), m_data.data() + claimed_size);
|
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:
|
private:
|
||||||
std::array<char, 8> m_data{{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'}};
|
std::array<char, 8> 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")
|
SECTION("skipping more characters than gbump() takes at once")
|
||||||
{
|
{
|
||||||
// gbump() takes an int, but a get area can be larger
|
// 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<std::size_t>(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
|
else
|
||||||
{
|
{
|
||||||
const std::size_t claimed = std::size_t{3} << 30u;
|
|
||||||
oversized_get_area_streambuf buf(claimed);
|
|
||||||
std::istream is(&buf);
|
std::istream is(&buf);
|
||||||
nlohmann::detail::input_stream_adapter ia(is);
|
nlohmann::detail::input_stream_adapter ia(is);
|
||||||
CHECK(ia.get_character() == 'a');
|
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());
|
ia.bulk_skip(ia.bulk_remaining());
|
||||||
CHECK(ia.bulk_remaining() == 0);
|
CHECK(ia.bulk_remaining() == 0);
|
||||||
|
|
||||||
|
buf.restore_get_area();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
Reference in new issue
Block a user