Cover the edit storage in tests

Test edits of an empty document and assignments through a view of a
value that is no longer part of the document; copy the entries of a
block through deref() (one path for links and values); mark the
4 GiB limit and the returns of find_parent() that no document reaches.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
Niels Lohmann
2026-09-29 14:03:15 +02:00
parent 2e5419eebc
commit 266c2249fe
3 changed files with 26 additions and 24 deletions
+5 -12
View File
@@ -73,7 +73,7 @@ inline std::uint32_t append_text(document_data& d, const char* s, std::size_t n)
const std::size_t cap = (std::max)(e.text_cap * 2, e.text_used + n + 256);
if (cap > 0xFFFFFFFFu)
{
throw_out_of_range(416, "edits of 4 GiB or more are not supported by json_document");
throw_out_of_range(416, "edits of 4 GiB or more are not supported by json_document"); // LCOV_EXCL_LINE (4 GiB)
}
std::unique_ptr<char[]> fresh(new char[cap]); // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
if (e.text_used != 0)
@@ -161,14 +161,7 @@ inline node* block_of(document_data& d, node* n, std::size_t extra)
{
*o++ = *c++; // the key
}
if (c->kind == kind_link)
{
*o = *c;
}
else
{
make_link(*o, c);
}
make_link(*o, document_data::deref(c));
++o;
c = document_data::after(c);
}
@@ -197,14 +190,14 @@ inline node* find_parent(const document_data& d, const node* target)
auto it = d.edits->regions.upper_bound(target);
if (it == d.edits->regions.begin())
{
return nullptr;
return nullptr; // LCOV_EXCL_LINE (an array/object with elements is in the index or a new value)
}
--it;
lo = it->first;
hi = lo + lo->next;
if (!lt(target, hi))
{
return nullptr; // a single-node value, reached through a link
return nullptr; // LCOV_EXCL_LINE (a single-node value, reached through a link)
}
// the root of a new value is the element sequence of its owner, or a linked value
c = it->second != nullptr ? it->second : lo;
@@ -217,7 +210,7 @@ inline node* find_parent(const document_data& d, const node* target)
{
if (!is_container(*c))
{
return nullptr;
return nullptr; // LCOV_EXCL_LINE (the value is inside c)
}
const bool object = c->kind == static_cast<std::uint8_t>(value_t::object);
const node* down = nullptr;
+5 -12
View File
@@ -2546,7 +2546,7 @@ inline std::uint32_t append_text(document_data& d, const char* s, std::size_t n)
const std::size_t cap = (std::max)(e.text_cap * 2, e.text_used + n + 256);
if (cap > 0xFFFFFFFFu)
{
throw_out_of_range(416, "edits of 4 GiB or more are not supported by json_document");
throw_out_of_range(416, "edits of 4 GiB or more are not supported by json_document"); // LCOV_EXCL_LINE (4 GiB)
}
std::unique_ptr<char[]> fresh(new char[cap]); // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
if (e.text_used != 0)
@@ -2634,14 +2634,7 @@ inline node* block_of(document_data& d, node* n, std::size_t extra)
{
*o++ = *c++; // the key
}
if (c->kind == kind_link)
{
*o = *c;
}
else
{
make_link(*o, c);
}
make_link(*o, document_data::deref(c));
++o;
c = document_data::after(c);
}
@@ -2670,14 +2663,14 @@ inline node* find_parent(const document_data& d, const node* target)
auto it = d.edits->regions.upper_bound(target);
if (it == d.edits->regions.begin())
{
return nullptr;
return nullptr; // LCOV_EXCL_LINE (an array/object with elements is in the index or a new value)
}
--it;
lo = it->first;
hi = lo + lo->next;
if (!lt(target, hi))
{
return nullptr; // a single-node value, reached through a link
return nullptr; // LCOV_EXCL_LINE (a single-node value, reached through a link)
}
// the root of a new value is the element sequence of its owner, or a linked value
c = it->second != nullptr ? it->second : lo;
@@ -2690,7 +2683,7 @@ inline node* find_parent(const document_data& d, const node* target)
{
if (!is_container(*c))
{
return nullptr;
return nullptr; // LCOV_EXCL_LINE (the value is inside c)
}
const bool object = c->kind == static_cast<std::uint8_t>(value_t::object);
const node* down = nullptr;
+16
View File
@@ -327,6 +327,12 @@ std::string exception_of_call(const std::function<void()>& f)
TEST_CASE("json_view edits: errors")
{
SECTION("an empty document")
{
json_editable_document d;
CHECK_THROWS_WITH_AS(d.set(d.root(), 1), "[json.exception.invalid_iterator.202] view does not belong to this document", json::invalid_iterator&);
}
json_editable_document d = json_editable_document::parse(R"({"o": {"a": 1}, "a": [1, 2], "n": 1, "z": null})");
const json_editable_view root = d.root();
const json_document other = json_document::parse("[1]");
@@ -367,6 +373,16 @@ TEST_CASE("json_view edits: errors")
TEST_CASE("json_view edits: views and values")
{
SECTION("a value that is no longer part of the document")
{
json_editable_document d = json_editable_document::parse("[[[1,2]]]");
const json_editable_view inner = d.root()[0][0];
d.set(d.root()[0], json::array({7}));
d.set(inner, 5);
CHECK(d.root().dump() == "[[7]]");
CHECK(inner.get<int>() == 5);
}
SECTION("strings stay valid while more edits come")
{
json_editable_document d = json_editable_document::parse("[]");