mirror of
https://github.com/nlohmann/json.git
synced 2026-10-01 06:25:17 +00:00
Add tests for uncovered code paths (#5581)
* Add tests for uncovered code paths Cover code the test suite did not reach, found from the Coveralls report of develop and a local coverage run of HEAD: - dump() of every kind of value below the bound of the recursive descent (pretty-printed objects, binary values, discarded values, scalars), and flushes of the escape and write buffers mid-string and mid-binary - the iterative comparison: objects with different keys, containers that are a prefix of each other, and elements that cannot be ordered, each both at the top level and below the nesting bound - SAX handlers that stop at any event, including the end of a nested container, in the BSON, CBOR, MessagePack, UBJSON and BJData readers - from_bson/cbor/msgpack/ubjson/bjdata returning a discarded value through the iterator and pointer overloads - JSON Patch, diff, merge_patch and update(..., true) on ordered_json - smaller gaps: get_allocator(), to_ubjson/to_bjdata into a string, value() with an unresolvable JSON pointer, integer/float comparison below the integer range and with negative fractions, conversion to a custom binary type, std::formatter::parse on a spec without '}', unescape() of a lone '~', and the callback parser's start_array() Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Cover more paths that were thought unreachable - parse_float_fast() declining malformed or inexact input, called directly since the lexer only passes well-formed numbers to it - a UTF-16 high surrogate followed by a unit above the low surrogates - self-assignment of a const_iterator - a truncated CBOR string read through non-contiguous iterators - serializing a long double under the de_DE locale, which undoes the locale's decimal point and thousands separator - values read from a binary format carrying no diagnostic positions, with and without a parser callback Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Fix the CI failures of the new coverage tests - declare the self-assignment reference const (misc-const-correctness) - expect the (/path) prefix that JSON_DIAGNOSTICS adds to the messages of the failing ordered_json patch operations Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Expect the byte range JSON_DIAGNOSTIC_POSITIONS adds to the patch errors Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Build the expected dump of the nested-object test with += clang-tidy (performance-inefficient-string-concatenation) reported the chain of operator+ calls that assembled the expected indented output. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Compare the BJData and UBJSON test outputs byte by byte Building a std::string from the byte vector converts each byte implicitly, which -fsanitize=integer reports for bytes of 0x80 and above (ci_test_clang_sanitizer). Signed-off-by: Niels Lohmann <mail@nlohmann.me> --------- Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
@@ -639,3 +639,163 @@ TEST_CASE("serialization of deeply nested values")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
// wraps @a inner into @a depth single-element arrays
|
||||
json wrap_in_arrays(const json& inner, const std::size_t depth)
|
||||
{
|
||||
json j = inner;
|
||||
for (std::size_t i = 0; i < depth; ++i)
|
||||
{
|
||||
j = json::array({std::move(j)});
|
||||
}
|
||||
return j;
|
||||
}
|
||||
|
||||
// what wrap_in_arrays(inner, depth).dump(2) is expected to be: the arrays
|
||||
// around inner.dump(2), with inner's own lines indented by the depth
|
||||
std::string expected_pretty_in_arrays(const json& inner, const std::size_t depth)
|
||||
{
|
||||
std::string expected;
|
||||
for (std::size_t i = 0; i < depth; ++i)
|
||||
{
|
||||
expected += std::string(2 * i, ' ') + "[\n";
|
||||
}
|
||||
|
||||
const std::string indent(2 * depth, ' ');
|
||||
expected += indent;
|
||||
for (const char c : inner.dump(2))
|
||||
{
|
||||
expected += c;
|
||||
if (c == '\n')
|
||||
{
|
||||
expected += indent;
|
||||
}
|
||||
}
|
||||
|
||||
for (std::size_t i = depth; i > 0; --i)
|
||||
{
|
||||
expected += '\n' + std::string(2 * (i - 1), ' ') + ']';
|
||||
}
|
||||
return expected;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TEST_CASE("serialization of every kind of value below the bound of the descent")
|
||||
{
|
||||
// Values nested deeper than the bound are written without the call stack,
|
||||
// by code of their own; each kind of value must come out the same there as
|
||||
// it does at the top level, compact and pretty-printed.
|
||||
std::vector<json> values =
|
||||
{
|
||||
json::parse(R"({"a": 1, "b": [1, 2, {"c": "x"}], "d": {}, "e": []})"),
|
||||
json::parse(R"([1, [2, 3], {"k": null}, "s"])"),
|
||||
json::object(),
|
||||
json::array(),
|
||||
json::binary({1, 2, 3}, 42),
|
||||
json::binary({1, 2, 3}),
|
||||
json::binary({}, 7),
|
||||
json::binary({}),
|
||||
"a string with \"escapes\"\n",
|
||||
true,
|
||||
false,
|
||||
-42,
|
||||
42u,
|
||||
1.5,
|
||||
nullptr,
|
||||
json(json::value_t::discarded),
|
||||
};
|
||||
// a pretty-printed object whose members are themselves deep
|
||||
values.push_back({{"x", wrap_in_arrays(1, 5)}, {"y", {{"z", 2}}}});
|
||||
|
||||
for (const std::size_t depth : std::vector<std::size_t> {1, 200})
|
||||
{
|
||||
CAPTURE(depth);
|
||||
for (const auto& inner : values)
|
||||
{
|
||||
CAPTURE(inner.dump());
|
||||
const json j = wrap_in_arrays(inner, depth);
|
||||
CHECK(j.dump() == std::string(depth, '[') + inner.dump() + std::string(depth, ']'));
|
||||
CHECK(j.dump(2) == expected_pretty_in_arrays(inner, depth));
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("pretty-printed objects across the bound")
|
||||
{
|
||||
for (std::size_t d = 120; d <= 140; ++d)
|
||||
{
|
||||
CAPTURE(d);
|
||||
|
||||
// built from the inside out: {"k": <level below>, "n": <level>}
|
||||
json j = 7;
|
||||
std::string expected = "7";
|
||||
for (std::size_t i = d; i > 0; --i)
|
||||
{
|
||||
j = json({{"k", std::move(j)}, {"n", i}});
|
||||
|
||||
const std::string indent(2 * i, ' ');
|
||||
const std::string outer_indent(2 * (i - 1), ' ');
|
||||
std::string next = "{\n";
|
||||
next += indent;
|
||||
next += "\"k\": ";
|
||||
next += expected;
|
||||
next += ",\n";
|
||||
next += indent;
|
||||
next += "\"n\": ";
|
||||
next += std::to_string(i);
|
||||
next += '\n';
|
||||
next += outer_indent;
|
||||
next += '}';
|
||||
expected = std::move(next);
|
||||
}
|
||||
|
||||
CHECK(j.dump(2) == expected);
|
||||
CHECK(json::parse(j.dump(2)) == j);
|
||||
CHECK(json::parse(j.dump()) == j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("serializer buffers are flushed mid-string and mid-binary")
|
||||
{
|
||||
SECTION("a long run of escaped characters")
|
||||
{
|
||||
// each character is escaped on its own, so the escape buffer fills up
|
||||
const json newlines = std::string(600, '\n');
|
||||
std::string expected = "\"";
|
||||
for (int i = 0; i < 600; ++i)
|
||||
{
|
||||
expected += "\\n";
|
||||
}
|
||||
expected += '"';
|
||||
CHECK(newlines.dump() == expected);
|
||||
|
||||
// every character is \u-escaped under ensure_ascii
|
||||
std::string umlauts;
|
||||
std::string escaped_umlauts = "\"";
|
||||
for (int i = 0; i < 300; ++i)
|
||||
{
|
||||
umlauts += "\xC3\xA4";
|
||||
escaped_umlauts += "\\u00e4";
|
||||
}
|
||||
escaped_umlauts += '"';
|
||||
CHECK(json(umlauts).dump(-1, ' ', true) == escaped_umlauts);
|
||||
}
|
||||
|
||||
SECTION("a large binary value")
|
||||
{
|
||||
std::vector<std::uint8_t> bytes(3000);
|
||||
std::string expected_bytes;
|
||||
std::string expected_pretty_bytes;
|
||||
for (std::size_t i = 0; i < bytes.size(); ++i)
|
||||
{
|
||||
bytes[i] = static_cast<std::uint8_t>(i % 256);
|
||||
expected_bytes += (i == 0 ? "" : ",") + std::to_string(i % 256);
|
||||
expected_pretty_bytes += (i == 0 ? "" : ", ") + std::to_string(i % 256);
|
||||
}
|
||||
const json j = json::binary(bytes);
|
||||
CHECK(j.dump() == "{\"bytes\":[" + expected_bytes + "],\"subtype\":null}");
|
||||
CHECK(j.dump(2) == "{\n \"bytes\": [" + expected_pretty_bytes + "],\n \"subtype\": null\n}");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user