* CI: measure line coverage without lcov's unexecuted-block adjustment Drop geninfo_unexecuted_blocks=1 from the lcov capture of ci_test_coverage: it reset every line of an inlined or templated function to 0 as soon as one copy had an unexecuted block, which understated the line coverage by about three points. Add -fvar-tracking-assignments to both coverage builds. At -O0, GCC inlines always_inline functions before it places the coverage counters, so the call sites of such helpers (put_literal() and others) got no line record and showed as 0 in an -O3 translation unit that never reaches them. With the flag, the call sites carry counts in every translation unit. The generated code is identical. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Remove unreachable code found by the coverage analysis Remove code that no input can reach, and mark the lines that cannot be covered with LCOV_EXCL_LINE: - BJData writer: the dry_run parameter of the ND-array element writers was always true; replace them by range checks. - BON8 writer: call throw_integer_out_of_range() directly instead of write_bon8_value() for values that always throw. - binary_writer: integer_bits() is only called for integers of up to 64 bits. - serializer, view serializer, lexer: replace guards that the callers make unreachable by assertions or by the simpler code (surrogate pairs under ensure_ascii, next_unget, '-' at the end of the buffer). - json_view: drop unreachable branches in scalar(), set(), check_utf8_scan(), append_input_impl() and unescape(). - Mark exception-cleanup braces, 2 GiB limits, big-endian and 32-bit code, and fall-through case labels with LCOV_EXCL_LINE. No behavior changes for any input that reaches these functions. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Tests: cover the remaining lines of the library headers Add tests for the lines of include/nlohmann that were not covered: the binary readers and writers, the lexer and locale paths, the json_view modules, the custom number, key and array types, json_pointer, json_patch, diff and the iterators. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 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> * Return std::size_t from the hash helper in the ordered_json test MSVC (Win32) rejected the implicit conversion of the unsigned long long product to the 32-bit std::size_t (C4244 as an error). Also note when JSON_NO_THREAD_LOCAL is set in the regression test. 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> --------- Signed-off-by: Niels Lohmann <mail@nlohmann.me> Signed-off-by: Claude <noreply@anthropic.com> Co-authored-by: Claude <noreply@anthropic.com>
84 lines
3.5 KiB
C++
84 lines
3.5 KiB
C++
// __ _____ _____ _____
|
|
// __| | __| | | | 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());
|
|
}
|
|
}
|