mirror of
https://github.com/nlohmann/json.git
synced 2026-09-30 06:14:59 +01: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:
@@ -3763,6 +3763,49 @@ TEST_CASE("BJData")
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("BJData input that cannot be read is discarded by every overload")
|
||||
{
|
||||
std::vector<std::uint8_t> input = json::to_bjdata(json({{"a", {1, 2}}}));
|
||||
input.pop_back();
|
||||
|
||||
json _;
|
||||
CHECK_THROWS_AS(_ = json::from_bjdata(input.begin(), input.end()), json::parse_error&);
|
||||
CHECK(json::from_bjdata(input, true, false).is_discarded());
|
||||
CHECK(json::from_bjdata(input.begin(), input.end(), true, false).is_discarded());
|
||||
}
|
||||
|
||||
TEST_CASE("BJData SAX parsing stops at every event")
|
||||
{
|
||||
// Containers are opened and closed by the loop that reads them; a SAX
|
||||
// handler that rejects any event - including the end of a nested
|
||||
// container - must stop the parse right there.
|
||||
const auto count_events = [](const std::vector<std::uint8_t>& input)
|
||||
{
|
||||
int events = 0;
|
||||
while (true)
|
||||
{
|
||||
SaxCountdown scp(events);
|
||||
if (json::sax_parse(input, &scp, json::input_format_t::bjdata))
|
||||
{
|
||||
return events;
|
||||
}
|
||||
++events;
|
||||
REQUIRE(events < 1000);
|
||||
}
|
||||
};
|
||||
|
||||
// 20 events: every container kind closes inside another one
|
||||
const json j = json::parse(R"({"a": [1, {"b": []}], "c": {"d": [[2]]}})");
|
||||
CHECK(count_events(json::to_bjdata(j)) == 20);
|
||||
CHECK(count_events(json::to_bjdata(j, true)) == 20);
|
||||
CHECK(count_events(json::to_bjdata(j, true, true)) == 20);
|
||||
|
||||
// an ND-array is announced as an annotated object: start_object, then
|
||||
// _ArrayType_, _ArraySize_ and _ArrayData_ with its elements
|
||||
const json ndarray = json::parse(R"({"_ArrayType_": "uint8", "_ArraySize_": [2, 2], "_ArrayData_": [1, 2, 3, 4]})");
|
||||
CHECK(count_events(json::to_bjdata(ndarray, true, true)) == 16);
|
||||
}
|
||||
|
||||
TEST_CASE("issue #5405 - array reserve for definite-length BJData arrays")
|
||||
{
|
||||
#if !defined(JSON_NOEXCEPTION)
|
||||
@@ -4247,6 +4290,65 @@ TEST_CASE("all BJData first bytes")
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST_CASE("BJData and UBJSON can be written to a string")
|
||||
{
|
||||
const std::vector<json> values =
|
||||
{
|
||||
{{"a", {1, 2.5, "x", nullptr}}, {"b", json::binary({1, 2})}},
|
||||
// an annotated ND-array, and objects that only look like one
|
||||
json::parse(R"({"_ArrayType_": "uint8", "_ArraySize_": [2, 2], "_ArrayData_": [1, 2, 3, 4]})"),
|
||||
json::parse(R"({"_ArrayType_": 1, "_ArraySize_": [2, 2], "_ArrayData_": [1, 2, 3, 4]})"),
|
||||
json::parse(R"({"_ArrayType_": "uint8", "_ArraySize_": 4, "_ArrayData_": [1, 2, 3, 4]})"),
|
||||
json::parse(R"({"_ArrayType_": "uint8", "_ArraySize_": [2, -2], "_ArrayData_": [1, 2, 3, 4]})"),
|
||||
json::parse(R"({"_ArrayType_": "uint8", "_ArraySize_": [2, 2], "_ArrayData_": [1, 2, 3]})"),
|
||||
json::parse(R"({"_ArrayType_": "uint8", "_ArraySize_": [2, 2], "_ArrayData_": 1})"),
|
||||
};
|
||||
|
||||
// compared byte by byte: building a std::string from the bytes would
|
||||
// convert them implicitly, which -fsanitize=integer reports for bytes of
|
||||
// 0x80 and above
|
||||
const auto same_bytes = [](const std::vector<std::uint8_t>& bytes, const std::string & text)
|
||||
{
|
||||
return bytes.size() == text.size() && std::equal(bytes.begin(), bytes.end(), text.begin(), [](std::uint8_t byte, char c)
|
||||
{
|
||||
return byte == static_cast<std::uint8_t>(c);
|
||||
});
|
||||
};
|
||||
|
||||
for (const auto& j : values)
|
||||
{
|
||||
CAPTURE(j.dump());
|
||||
for (const bool use_size :
|
||||
{
|
||||
false, true
|
||||
})
|
||||
{
|
||||
for (const bool use_type :
|
||||
{
|
||||
false, true
|
||||
})
|
||||
{
|
||||
if (use_type && !use_size)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
CAPTURE(use_size);
|
||||
CAPTURE(use_type);
|
||||
|
||||
const auto bjdata = json::to_bjdata(j, use_size, use_type);
|
||||
std::string bjdata_string;
|
||||
json::to_bjdata(j, bjdata_string, use_size, use_type);
|
||||
CHECK(same_bytes(bjdata, bjdata_string));
|
||||
|
||||
const auto ubjson = json::to_ubjson(j, use_size, use_type);
|
||||
std::string ubjson_string;
|
||||
json::to_ubjson(j, ubjson_string, use_size, use_type);
|
||||
CHECK(same_bytes(ubjson, ubjson_string));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("BJData use_type requires use_size")
|
||||
{
|
||||
SECTION("non-empty object throws other_error.502")
|
||||
@@ -4265,6 +4367,17 @@ TEST_CASE("BJData use_type requires use_size")
|
||||
json::other_error&);
|
||||
}
|
||||
|
||||
SECTION("non-empty binary value throws other_error.502")
|
||||
{
|
||||
const json j = json::binary({1, 2, 3});
|
||||
CHECK_THROWS_WITH_AS(json::to_bjdata(j, false, true),
|
||||
"[json.exception.other_error.502] use_type requires use_size = true",
|
||||
json::other_error&);
|
||||
CHECK_THROWS_WITH_AS(json::to_ubjson(j, false, true),
|
||||
"[json.exception.other_error.502] use_type requires use_size = true",
|
||||
json::other_error&);
|
||||
}
|
||||
|
||||
SECTION("scalars do not throw with use_type=true, use_count=false")
|
||||
{
|
||||
CHECK_NOTHROW(json::to_bjdata(42, false, true));
|
||||
|
||||
Reference in New Issue
Block a user