aboutsummaryrefslogtreecommitdiff
path: root/thirdparty/ryml/test/test_case.cpp
diff options
context:
space:
mode:
authorStefan Boberg <[email protected]>2025-11-07 14:49:13 +0100
committerGitHub Enterprise <[email protected]>2025-11-07 14:49:13 +0100
commit24e43a913f29ac3b314354e8ce5175f135bcc64f (patch)
treeca442937ceeb63461012b33a4576e9835099f106 /thirdparty/ryml/test/test_case.cpp
parentget oplog attachments (#622) (diff)
downloadzen-24e43a913f29ac3b314354e8ce5175f135bcc64f.tar.xz
zen-24e43a913f29ac3b314354e8ce5175f135bcc64f.zip
switch to xmake for package management (#611)
This change removes our dependency on vcpkg for package management, in favour of bringing some code in-tree in the `thirdparty` folder as well as using the xmake build-in package management feature. For the latter, all the package definitions are maintained in the zen repo itself, in the `repo` folder. It should now also be easier to build the project as it will no longer depend on having the right version of vcpkg installed, which has been a common problem for new people coming in to the codebase. Now you should only need xmake to build. * Bumps xmake requirement on github runners to 2.9.9 to resolve an issue where xmake on Windows invokes cmake with `v144` toolchain which does not exist * BLAKE3 is now in-tree at `thirdparty/blake3` * cpr is now in-tree at `thirdparty/cpr` * cxxopts is now in-tree at `thirdparty/cxxopts` * fmt is now in-tree at `thirdparty/fmt` * robin-map is now in-tree at `thirdparty/robin-map` * ryml is now in-tree at `thirdparty/ryml` * sol2 is now in-tree at `thirdparty/sol2` * spdlog is now in-tree at `thirdparty/spdlog` * utfcpp is now in-tree at `thirdparty/utfcpp` * xmake package repo definitions is in `repo` * implemented support for sanitizers. ASAN is supported on windows, TSAN, UBSAN, MSAN etc are supported on Linux/MacOS though I have not yet tested it extensively on MacOS * the zencore encryption implementation also now supports using mbedTLS which is used on MacOS, though for now we still use openssl on Linux * crashpad * bumps libcurl to 8.11.0 (from 8.8.0) which should address a rare build upload bug
Diffstat (limited to 'thirdparty/ryml/test/test_case.cpp')
-rw-r--r--thirdparty/ryml/test/test_case.cpp898
1 files changed, 898 insertions, 0 deletions
diff --git a/thirdparty/ryml/test/test_case.cpp b/thirdparty/ryml/test/test_case.cpp
new file mode 100644
index 000000000..a850d242b
--- /dev/null
+++ b/thirdparty/ryml/test/test_case.cpp
@@ -0,0 +1,898 @@
+#include "./test_case.hpp"
+#ifndef RYML_SINGLE_HEADER
+#include "c4/yml/common.hpp"
+#include "c4/format.hpp"
+#include "c4/span.hpp"
+#include "c4/yml/std/std.hpp"
+#include "c4/yml/detail/print.hpp"
+#include "c4/yml/detail/checks.hpp"
+#endif
+
+#include <gtest/gtest.h>
+
+#if defined(_MSC_VER)
+# pragma warning(push)
+#elif defined(__clang__)
+# pragma clang diagnostic push
+#elif defined(__GNUC__)
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wuseless-cast"
+# if __GNUC__ >= 6
+# pragma GCC diagnostic ignored "-Wnull-dereference"
+# endif
+#endif
+
+namespace c4 {
+namespace yml {
+
+
+size_t _num_leaves(Tree const& t, size_t node)
+{
+ size_t count = 0;
+ for(size_t ch = t.first_child(node); ch != NONE; ch = t.next_sibling(ch))
+ count += _num_leaves(t, ch);
+ return count;
+}
+
+
+void test_compare(Tree const& actual, Tree const& expected)
+{
+ ASSERT_EQ(actual.empty(), expected.empty());
+ if(actual.empty() || expected.empty())
+ return;
+ EXPECT_EQ(actual.size(), expected.size());
+ EXPECT_EQ(_num_leaves(actual, actual.root_id()), _num_leaves(expected, expected.root_id()));
+ test_compare(actual, actual.root_id(), expected, expected.root_id(), 0);
+}
+
+
+void test_compare(Tree const& actual, size_t node_actual,
+ Tree const& expected, size_t node_expected,
+ size_t level)
+{
+ #define _MORE_INFO "actual=" << node_actual << " vs expected=" << node_expected
+
+ ASSERT_NE(node_actual, (size_t)NONE);
+ ASSERT_NE(node_expected, (size_t)NONE);
+ ASSERT_LT(node_actual, actual.capacity());
+ ASSERT_LT(node_expected, expected.capacity());
+
+ EXPECT_EQ((type_bits)(actual.type(node_actual)&_TYMASK), (type_bits)(expected.type(node_expected)&_TYMASK)) << _MORE_INFO;
+
+ EXPECT_EQ(actual.has_key(node_actual), expected.has_key(node_expected)) << _MORE_INFO;
+ if(actual.has_key(node_actual) && expected.has_key(node_expected))
+ {
+ EXPECT_EQ(actual.key(node_actual), expected.key(node_expected)) << _MORE_INFO;
+ }
+
+ EXPECT_EQ(actual.has_val(node_actual), expected.has_val(node_expected)) << _MORE_INFO;
+ if(actual.has_val(node_actual) && expected.has_val(node_expected))
+ {
+ EXPECT_EQ(actual.val(node_actual), expected.val(node_expected)) << _MORE_INFO;
+ }
+
+ EXPECT_EQ(actual.has_key_tag(node_actual), expected.has_key_tag(node_expected)) << _MORE_INFO;
+ if(actual.has_key_tag(node_actual) && expected.has_key_tag(node_expected))
+ {
+ EXPECT_EQ(actual.key_tag(node_actual), expected.key_tag(node_expected)) << _MORE_INFO;
+ }
+
+ EXPECT_EQ(actual.has_val_tag(node_actual), expected.has_val_tag(node_expected)) << _MORE_INFO;
+ if(actual.has_val_tag(node_actual) && expected.has_val_tag(node_expected))
+ {
+ auto filtered = [](csubstr tag) {
+ if(tag.begins_with("!<!") && tag.ends_with('>'))
+ return tag.offs(3, 1);
+ return tag;
+ };
+ csubstr actual_tag = filtered(actual.val_tag(node_actual));
+ csubstr expected_tag = filtered(actual.val_tag(node_expected));
+ EXPECT_EQ(actual_tag, expected_tag) << _MORE_INFO;
+ }
+
+ EXPECT_EQ(actual.has_key_anchor(node_actual), expected.has_key_anchor(node_expected)) << _MORE_INFO;
+ if(actual.has_key_anchor(node_actual) && expected.has_key_anchor(node_expected))
+ {
+ EXPECT_EQ(actual.key_anchor(node_actual), expected.key_anchor(node_expected)) << _MORE_INFO;
+ }
+
+ EXPECT_EQ(actual.has_val_anchor(node_actual), expected.has_val_anchor(node_expected)) << _MORE_INFO;
+ if(actual.has_val_anchor(node_actual) && expected.has_val_anchor(node_expected))
+ {
+ EXPECT_EQ(actual.val_anchor(node_actual), expected.val_anchor(node_expected)) << _MORE_INFO;
+ }
+
+ EXPECT_EQ(actual.num_children(node_actual), expected.num_children(node_expected)) << _MORE_INFO;
+ for(size_t ia = actual.first_child(node_actual), ib = expected.first_child(node_expected);
+ ia != NONE && ib != NONE;
+ ia = actual.next_sibling(ia), ib = expected.next_sibling(ib))
+ {
+ test_compare(actual, ia, expected, ib, level+1);
+ }
+
+ #undef _MORE_INFO
+}
+
+void test_arena_not_shared(Tree const& a, Tree const& b)
+{
+ for(NodeData const* n = a.m_buf, *e = a.m_buf + a.m_cap; n != e; ++n)
+ {
+ EXPECT_FALSE(b.in_arena(n->m_key.scalar)) << n - a.m_buf;
+ EXPECT_FALSE(b.in_arena(n->m_key.tag )) << n - a.m_buf;
+ EXPECT_FALSE(b.in_arena(n->m_key.anchor)) << n - a.m_buf;
+ EXPECT_FALSE(b.in_arena(n->m_val.scalar)) << n - a.m_buf;
+ EXPECT_FALSE(b.in_arena(n->m_val.tag )) << n - a.m_buf;
+ EXPECT_FALSE(b.in_arena(n->m_val.anchor)) << n - a.m_buf;
+ }
+ for(NodeData const* n = b.m_buf, *e = b.m_buf + b.m_cap; n != e; ++n)
+ {
+ EXPECT_FALSE(a.in_arena(n->m_key.scalar)) << n - b.m_buf;
+ EXPECT_FALSE(a.in_arena(n->m_key.tag )) << n - b.m_buf;
+ EXPECT_FALSE(a.in_arena(n->m_key.anchor)) << n - b.m_buf;
+ EXPECT_FALSE(a.in_arena(n->m_val.scalar)) << n - b.m_buf;
+ EXPECT_FALSE(a.in_arena(n->m_val.tag )) << n - b.m_buf;
+ EXPECT_FALSE(a.in_arena(n->m_val.anchor)) << n - b.m_buf;
+ }
+ for(TagDirective const& td : a.m_tag_directives)
+ {
+ EXPECT_FALSE(b.in_arena(td.handle));
+ EXPECT_FALSE(b.in_arena(td.prefix));
+ }
+ for(TagDirective const& td : b.m_tag_directives)
+ {
+ EXPECT_FALSE(a.in_arena(td.handle));
+ EXPECT_FALSE(a.in_arena(td.prefix));
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+
+// ensure coverage of the default callback report
+#ifndef RYML_NO_DEFAULT_CALLBACKS
+extern void report_error_impl(const char* msg, size_t len, Location loc, FILE *file);
+#endif
+
+std::string format_error(const char* msg, size_t len, Location loc)
+{
+ // ensure coverage of the default callback report
+ #ifndef RYML_NO_DEFAULT_CALLBACKS
+ report_error_impl(msg, len, loc, nullptr);
+ #endif
+ if(!loc) return msg;
+ std::string out;
+ if(!loc.name.empty()) c4::formatrs(append, &out, "{}:", loc.name);
+ c4::formatrs(append, &out, "{}:{}:", loc.line, loc.col);
+ if(loc.offset) c4::formatrs(append, &out, " (@{}B):", loc.offset);
+ c4::formatrs(append, &out, "{}:", csubstr(msg, len));
+ return out;
+}
+
+struct ExpectedError : public std::runtime_error
+{
+ Location error_location;
+ ExpectedError(const char* msg, size_t len, Location loc)
+ : std::runtime_error(format_error(msg, len, loc))
+ , error_location(loc)
+ {
+ }
+};
+
+
+//-----------------------------------------------------------------------------
+
+ExpectError::ExpectError(Tree *tree, Location loc)
+ : m_got_an_error(false)
+ , m_tree(tree)
+ , m_glob_prev(get_callbacks())
+ , m_tree_prev(tree ? tree->callbacks() : Callbacks{})
+ , expected_location(loc)
+{
+ auto err = [](const char* msg, size_t len, Location errloc, void *this_) {
+ ((ExpectError*)this_)->m_got_an_error = true;
+ throw ExpectedError(msg, len, errloc);
+ };
+ #ifdef RYML_NO_DEFAULT_CALLBACKS
+ c4::yml::Callbacks tcb((void*)this, nullptr, nullptr, err);
+ c4::yml::Callbacks gcb((void*)this, nullptr, nullptr, err);
+ #else
+ c4::yml::Callbacks tcb((void*)this, tree ? m_tree_prev.m_allocate : nullptr, tree ? m_tree_prev.m_free : nullptr, err);
+ c4::yml::Callbacks gcb((void*)this, m_glob_prev.m_allocate, m_glob_prev.m_free, err);
+ #endif
+ if(tree)
+ tree->callbacks(tcb);
+ set_callbacks(gcb);
+}
+
+ExpectError::~ExpectError()
+{
+ if(m_tree)
+ m_tree->callbacks(m_tree_prev);
+ set_callbacks(m_tree_prev);
+}
+
+void ExpectError::do_check(Tree *tree, std::function<void()> fn, Location expected_location)
+{
+ auto context = ExpectError(tree, expected_location);
+ try
+ {
+ fn();
+ }
+ catch(ExpectedError const& e)
+ {
+ #if defined(RYML_DBG)
+ std::cout << "---------------\n";
+ std::cout << "got an expected error:\n" << e.what() << "\n";
+ std::cout << "---------------\n";
+ #endif
+ if(context.expected_location)
+ {
+ EXPECT_EQ(static_cast<bool>(context.expected_location),
+ static_cast<bool>(e.error_location));
+ EXPECT_EQ(e.error_location.line, context.expected_location.line);
+ EXPECT_EQ(e.error_location.col, context.expected_location.col);
+ if(context.expected_location.offset)
+ {
+ EXPECT_EQ(e.error_location.offset, context.expected_location.offset);
+ }
+ }
+ };
+ EXPECT_TRUE(context.m_got_an_error);
+}
+
+void ExpectError::check_assertion(Tree *tree, std::function<void()> fn, Location expected_location)
+{
+ #if RYML_USE_ASSERT
+ ExpectError::do_check(tree, fn, expected_location);
+ #else
+ C4_UNUSED(tree);
+ C4_UNUSED(fn);
+ C4_UNUSED(expected_location);
+ #endif
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+
+using N = CaseNode;
+using L = CaseNode::iseqmap;
+
+TEST(CaseNode, setting_up)
+{
+ L tl1 = {DOC, DOC};
+ L tl2 = {(DOC), (DOC)};
+
+ ASSERT_EQ(tl1.size(), tl2.size());
+ N const& d1 = *tl1.begin();
+ N const& d2 = *(tl1.begin() + 1);
+ ASSERT_EQ(d1.reccount(), d2.reccount());
+ ASSERT_EQ((type_bits)d1.type, (type_bits)DOC);
+ ASSERT_EQ((type_bits)d2.type, (type_bits)DOC);
+
+ N n1(tl1);
+ N n2(tl2);
+ ASSERT_EQ(n1.reccount(), n2.reccount());
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+NodeType_e CaseNode::_guess() const
+{
+ NodeType t;
+ C4_ASSERT(!val.empty() != !children.empty() || (val.empty() && children.empty()));
+ if(children.empty())
+ {
+ C4_ASSERT(parent);
+ if(key.empty())
+ {
+ t = VAL;
+ }
+ else
+ {
+ t = KEYVAL;
+ }
+ }
+ else
+ {
+ NodeType_e has_key = key.empty() ? NOTYPE : KEY;
+ auto const& ch = children.front();
+ if(ch.key.empty())
+ {
+ t = (has_key|SEQ);
+ }
+ else
+ {
+ t = (has_key|MAP);
+ }
+ }
+ if( ! key_tag.empty())
+ {
+ C4_ASSERT( ! key.empty());
+ t.add(KEYTAG);
+ }
+ if( ! val_tag.empty())
+ {
+ C4_ASSERT( ! val.empty() || ! children.empty());
+ t.add(VALTAG);
+ }
+ if( ! key_anchor.str.empty())
+ {
+ t.add(key_anchor.type);
+ }
+ if( ! val_anchor.str.empty())
+ {
+ t.add(val_anchor.type);
+ }
+ return t;
+}
+
+
+//-----------------------------------------------------------------------------
+void CaseNode::compare_child(yml::ConstNodeRef const& n, size_t pos) const
+{
+ EXPECT_TRUE(pos < n.num_children());
+ EXPECT_TRUE(pos < children.size());
+
+ if(pos >= n.num_children() || pos >= children.size()) return;
+
+ ASSERT_GT(n.num_children(), pos);
+ auto const& expectedch = children[pos];
+
+ if(type & MAP)
+ {
+ auto actualch = n.find_child(expectedch.key);
+ if(actualch != nullptr)
+ {
+ // there may be duplicate keys.
+ if(actualch.id() != n[pos].id())
+ actualch = n[pos];
+ //EXPECT_EQ(fch, n[ch.key]);
+ EXPECT_EQ(actualch.get(), n[pos].get());
+ //EXPECT_EQ(n[pos], n[ch.key]);
+ EXPECT_EQ(n[expectedch.key].key(), expectedch.key);
+ }
+ else
+ {
+ printf("error: node should have child %.*s: ", (int)expectedch.key.len, expectedch.key.str);
+ print_path(n);
+ printf("\n");
+ print_node(n);
+ GTEST_FAIL();
+ }
+ }
+
+ if(type & SEQ)
+ {
+ EXPECT_FALSE(n[pos].has_key());
+ EXPECT_EQ(n[pos].get()->m_key.scalar, children[pos].key);
+ auto actualch = n.child(pos);
+ EXPECT_EQ(actualch.get(), n[pos].get());
+ }
+
+ if(expectedch.type & KEY)
+ {
+ auto actualfch = n[pos];
+ EXPECT_TRUE(actualfch.has_key()) << "id=" << actualfch.id();
+ if(actualfch.has_key())
+ {
+ EXPECT_EQ(actualfch.key(), expectedch.key) << "id=" << actualfch.id();
+ }
+
+ if( ! expectedch.key_tag.empty())
+ {
+ EXPECT_TRUE(actualfch.has_key_tag()) << "id=" << actualfch.id();
+ if(actualfch.has_key_tag())
+ {
+ EXPECT_EQ(actualfch.key_tag(), expectedch.key_tag) << "id=" << actualfch.id();
+ }
+ }
+ }
+
+ if(expectedch.type & VAL)
+ {
+ auto actualch = n[pos];
+ EXPECT_TRUE(actualch.has_val()) << "id=" << actualch.id();
+ if(actualch.has_val())
+ {
+ EXPECT_EQ(actualch.val(), expectedch.val) << "id=" << actualch.id();
+ }
+
+ if( ! expectedch.val_tag.empty())
+ {
+ EXPECT_TRUE(actualch.has_val_tag()) << "id=" << actualch.id();
+ if(actualch.has_val_tag())
+ {
+ EXPECT_EQ(actualch.val_tag(), expectedch.val_tag) << "id=" << actualch.id();
+ }
+ }
+ }
+}
+
+void CaseNode::compare(yml::ConstNodeRef const& actual, bool ignore_quote) const
+{
+ if(ignore_quote)
+ {
+ const auto actual_type = actual.get()->m_type & ~(VALQUO | KEYQUO);
+ const auto expected_type = type & ~(VALQUO | KEYQUO);
+ EXPECT_EQ(expected_type, actual_type) << "id=" << actual.id();
+ }
+ else
+ {
+ EXPECT_EQ((int)actual.get()->m_type, (int)type) << "id=" << actual.id(); // the type() method masks the type, and thus tag flags are omitted on its return value
+ }
+
+ EXPECT_EQ(actual.num_children(), children.size()) << "id=" << actual.id();
+
+ if(actual.has_key())
+ {
+ EXPECT_EQ(actual.key(), key) << "id=" << actual.id();
+ }
+
+ if(actual.has_val())
+ {
+ EXPECT_EQ(actual.val(), val) << "id=" << actual.id();
+ }
+
+ // check that the children are in the same order
+ {
+ EXPECT_EQ(children.size(), actual.num_children()) << "id=" << actual.id();
+
+ size_t ic = 0;
+ for(auto const &expectedch : children)
+ {
+ SCOPED_TRACE("comparing: iteration based on the ref children");
+ (void)expectedch; // unused
+ compare_child(actual, ic++);
+ }
+
+ ic = 0;
+ for(auto const actualch : actual.children())
+ {
+ SCOPED_TRACE("comparing: iteration based on the yml::Node children");
+ (void)actualch; // unused
+ compare_child(actual, ic++);
+ }
+
+ if(actual.first_child() != nullptr)
+ {
+ ic = 0;
+ for(auto const ch : actual.first_child().siblings())
+ {
+ SCOPED_TRACE("comparing: iteration based on the yml::Node siblings");
+ (void)ch; // unused
+ compare_child(actual, ic++);
+ }
+ }
+ }
+
+ for(size_t i = 0, ei = actual.num_children(), j = 0, ej = children.size(); i < ei && j < ej; ++i, ++j)
+ {
+ children[j].compare(actual[i], ignore_quote);
+ }
+}
+
+void CaseNode::recreate(yml::NodeRef *n) const
+{
+ C4_ASSERT( ! n->has_children());
+ auto *nd = n->get();
+ nd->m_type = type|key_anchor.type|val_anchor.type;
+ nd->m_key.scalar = key;
+ nd->m_key.tag = (key_tag);
+ nd->m_key.anchor = key_anchor.str;
+ nd->m_val.scalar = val;
+ nd->m_val.tag = (val_tag);
+ nd->m_val.anchor = val_anchor.str;
+ auto &tree = *n->tree();
+ size_t nid = n->id(); // don't use node from now on
+ for(auto const& ch : children)
+ {
+ size_t id = tree.append_child(nid);
+ NodeRef chn(n->tree(), id);
+ ch.recreate(&chn);
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+
+void print_path(ConstNodeRef const& n)
+{
+ size_t len = 0;
+ char buf[1024];
+ ConstNodeRef p = n;
+ while(p != nullptr)
+ {
+ if(p.has_key())
+ {
+ len += 1 + p.key().len;
+ }
+ else
+ {
+ int ret = snprintf(buf, sizeof(buf), "/%zd", p.has_parent() ? p.parent().child_pos(p) : 0);
+ RYML_ASSERT(ret >= 0);
+ len += static_cast<size_t>(ret);
+ }
+ p = p.parent();
+ };
+ C4_ASSERT(len < sizeof(buf));
+ size_t pos = len;
+ p = n;
+ while(p.valid() && p != nullptr)
+ {
+ if(p.has_key())
+ {
+ size_t tl = p.key().len;
+ int ret = snprintf(buf + pos - tl, tl, "%.*s", (int)tl, p.key().str);
+ RYML_ASSERT(ret >= 0);
+ pos -= static_cast<size_t>(ret);
+ }
+ else if(p.has_parent())
+ {
+ pos = p.parent().child_pos(p);
+ int ret = snprintf(buf, 0, "/%zd", pos);
+ RYML_ASSERT(ret >= 0);
+ size_t tl = static_cast<size_t>(ret);
+ RYML_ASSERT(pos >= tl);
+ ret = snprintf(buf + static_cast<size_t>(pos - tl), tl, "/%zd", pos);
+ RYML_ASSERT(ret >= 0);
+ pos -= static_cast<size_t>(ret);
+ }
+ p = p.parent();
+ };
+ printf("%.*s", (int)len, buf);
+}
+
+
+
+void print_node(CaseNode const& p, int level)
+{
+ printf("%*s%p", (2*level), "", (void*)&p);
+ if( ! p.parent)
+ {
+ printf(" [ROOT]");
+ }
+ printf(" %s:", NodeType::type_str(p.type));
+ if(p.has_key())
+ {
+ if(p.has_key_anchor())
+ {
+ csubstr ka = p.key_anchor.str;
+ printf(" &%.*s", (int)ka.len, ka.str);
+ }
+ if(p.key_tag.empty())
+ {
+ csubstr v = p.key;
+ printf(" '%.*s'", (int)v.len, v.str);
+ }
+ else
+ {
+ csubstr vt = p.key_tag;
+ csubstr v = p.key;
+ printf(" '%.*s %.*s'", (int)vt.len, vt.str, (int)v.len, v.str);
+ }
+ }
+ if(p.has_val())
+ {
+ if(p.val_tag.empty())
+ {
+ csubstr v = p.val;
+ printf(" '%.*s'", (int)v.len, v.str);
+ }
+ else
+ {
+ csubstr vt = p.val_tag;
+ csubstr v = p.val;
+ printf(" '%.*s %.*s'", (int)vt.len, vt.str, (int)v.len, v.str);
+ }
+ }
+ else
+ {
+ if( ! p.val_tag.empty())
+ {
+ csubstr vt = p.val_tag;
+ printf(" %.*s", (int)vt.len, vt.str);
+ }
+ }
+ if(p.has_val_anchor())
+ {
+ auto &a = p.val_anchor.str;
+ printf(" valanchor='&%.*s'", (int)a.len, a.str);
+ }
+ printf(" (%zd sibs)", p.parent ? p.parent->children.size() : 0);
+ if(p.is_container())
+ {
+ printf(" %zd children:", p.children.size());
+ }
+ printf("\n");
+}
+
+
+void print_tree(ConstNodeRef const& p, int level)
+{
+ print_node(p, level);
+ for(ConstNodeRef ch : p.children())
+ {
+ print_tree(ch, level+1);
+ }
+}
+
+void print_tree(CaseNode const& p, int level)
+{
+ print_node(p, level);
+ for(auto const& ch : p.children)
+ print_tree(ch, level+1);
+}
+
+void print_tree(CaseNode const& t)
+{
+ printf("--------------------------------------\n");
+ print_tree(t, 0);
+ printf("#nodes: %zd\n", t.reccount());
+ printf("--------------------------------------\n");
+}
+
+void test_invariants(ConstNodeRef const& n)
+{
+ #define _MORE_INFO << "id=" << n.id()
+
+ if(n.is_root())
+ {
+ EXPECT_FALSE(n.has_other_siblings()) _MORE_INFO;
+ }
+ // keys or vals cannot be root
+ if(n.has_key() || n.is_val() || n.is_keyval())
+ {
+ EXPECT_TRUE(!n.is_root() || (n.is_doc() && !n.has_key())) _MORE_INFO;
+ }
+ // vals cannot be containers
+ if( ! n.empty() && ! n.is_doc())
+ {
+ EXPECT_NE(n.has_val(), n.is_container()) _MORE_INFO;
+ }
+ if(n.has_children())
+ {
+ EXPECT_TRUE(n.is_container()) _MORE_INFO;
+ EXPECT_FALSE(n.is_val()) _MORE_INFO;
+ }
+ // check parent & sibling reciprocity
+ for(ConstNodeRef s : n.siblings())
+ {
+ EXPECT_TRUE(n.has_sibling(s)) _MORE_INFO;
+ EXPECT_TRUE(s.has_sibling(n)) _MORE_INFO;
+ EXPECT_EQ(s.parent().get(), n.parent().get()) _MORE_INFO;
+ }
+ if(n.parent() != nullptr)
+ {
+ EXPECT_EQ(n.parent().num_children() > 1, n.has_other_siblings()) _MORE_INFO;
+ EXPECT_TRUE(n.parent().has_child(n)) _MORE_INFO;
+ EXPECT_EQ(n.parent().num_children(), n.num_siblings()) _MORE_INFO;
+ // doc parent must be a seq and a stream
+ if(n.is_doc())
+ {
+ EXPECT_TRUE(n.parent().is_seq()) _MORE_INFO;
+ EXPECT_TRUE(n.parent().is_stream()) _MORE_INFO;
+ }
+ }
+ else
+ {
+ EXPECT_TRUE(n.is_root()) _MORE_INFO;
+ }
+ if(n.is_seq())
+ {
+ EXPECT_TRUE(n.is_container()) _MORE_INFO;
+ EXPECT_FALSE(n.is_map()) _MORE_INFO;
+ for(ConstNodeRef ch : n.children())
+ {
+ EXPECT_FALSE(ch.is_keyval()) _MORE_INFO;
+ EXPECT_FALSE(ch.has_key()) _MORE_INFO;
+ }
+ }
+ if(n.is_map())
+ {
+ EXPECT_TRUE(n.is_container()) _MORE_INFO;
+ EXPECT_FALSE(n.is_seq()) _MORE_INFO;
+ for(ConstNodeRef ch : n.children())
+ {
+ EXPECT_TRUE(ch.has_key()) _MORE_INFO;
+ }
+ }
+ if(n.has_key_anchor())
+ {
+ EXPECT_FALSE(n.key_anchor().empty()) _MORE_INFO;
+ EXPECT_FALSE(n.is_key_ref()) _MORE_INFO;
+ }
+ if(n.has_val_anchor())
+ {
+ EXPECT_FALSE(n.val_anchor().empty()) _MORE_INFO;
+ EXPECT_FALSE(n.is_val_ref()) _MORE_INFO;
+ }
+ if(n.is_key_ref())
+ {
+ EXPECT_FALSE(n.key_ref().empty()) _MORE_INFO;
+ EXPECT_FALSE(n.has_key_anchor()) _MORE_INFO;
+ }
+ if(n.is_val_ref())
+ {
+ EXPECT_FALSE(n.val_ref().empty()) _MORE_INFO;
+ EXPECT_FALSE(n.has_val_anchor()) _MORE_INFO;
+ }
+ // ... add more tests here
+
+ // now recurse into the children
+ for(ConstNodeRef ch : n.children())
+ {
+ test_invariants(ch);
+ }
+
+ #undef _MORE_INFO
+}
+
+size_t test_tree_invariants(ConstNodeRef const& n)
+{
+ auto parent = n.parent();
+
+ if(n.get()->m_prev_sibling == NONE)
+ {
+ if(parent != nullptr)
+ {
+ EXPECT_EQ(parent.first_child().get(), n.get());
+ EXPECT_EQ(parent.first_child().id(), n.id());
+ }
+ }
+
+ if(n.get()->m_next_sibling == NONE)
+ {
+ if(parent != nullptr)
+ {
+ EXPECT_EQ(parent.last_child().get(), n.get());
+ EXPECT_EQ(parent.last_child().id(), n.id());
+ }
+ }
+
+ if(parent == nullptr)
+ {
+ EXPECT_TRUE(n.is_root());
+ EXPECT_EQ(n.prev_sibling().get(), nullptr);
+ EXPECT_EQ(n.next_sibling().get(), nullptr);
+ }
+
+ size_t count = 1, num = 0;
+ for(ConstNodeRef ch : n.children())
+ {
+ EXPECT_NE(ch.id(), n.id());
+ count += test_tree_invariants(ch);
+ ++num;
+ }
+
+ EXPECT_EQ(num, n.num_children());
+
+ return count;
+}
+
+void test_invariants(Tree const& t)
+{
+
+ ASSERT_LE(t.size(), t.capacity());
+ EXPECT_EQ(t.size() + t.slack(), t.capacity());
+
+ ASSERT_LE(t.arena_size(), t.arena_capacity());
+ ASSERT_LE(t.arena_slack(), t.arena_capacity());
+ EXPECT_EQ(t.arena_size() + t.arena_slack(), t.arena_capacity());
+
+ if(t.empty())
+ return;
+
+ size_t count = test_tree_invariants(t.rootref());
+ EXPECT_EQ(count, t.size());
+
+ check_invariants(t);
+ test_invariants(t.rootref());
+
+ if(!testing::UnitTest::GetInstance()->current_test_info()->result()->Passed())
+ {
+ print_tree(t);
+ }
+
+ return;
+#if 0 == 1
+ for(size_t i = 0; i < t.m_size; ++i)
+ {
+ auto n = t.get(i);
+ if(n->m_prev_sibling == NONE)
+ {
+ EXPECT_TRUE(i == t.m_head || i == t.m_free_head);
+ }
+ if(n->m_next_sibling == NONE)
+ {
+ EXPECT_TRUE(i == t.m_tail || i == t.m_free_tail);
+ }
+ }
+
+ std::vector<bool> touched(t.capacity());
+
+ for(size_t i = t.m_head; i != NONE; i = t.get(i)->m_next_sibling)
+ touched[i] = true;
+
+ size_t size = 0;
+ for(bool v : touched)
+ size += v;
+
+ EXPECT_EQ(size, t.size());
+
+ touched.clear();
+ touched.resize(t.capacity());
+
+ for(size_t i = t.m_free_head; i != NONE; i = t.get(i)->m_next_sibling)
+ {
+ touched[i] = true;
+ }
+
+ size_t slack = 0;
+ for(auto v : touched)
+ {
+ slack += v;
+ }
+
+ EXPECT_EQ(slack, t.slack());
+ EXPECT_EQ(size+slack, t.capacity());
+
+ // there are more checks to be done
+#endif
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+
+CaseData* get_data(csubstr name)
+{
+ static std::map<csubstr, CaseData> m;
+
+ auto it = m.find(name);
+ CaseData *cd;
+ if(it == m.end())
+ {
+ cd = &m[name];
+ Case const* c = get_case(name);
+ RYML_CHECK(c->src.find("\n\r") == csubstr::npos);
+ {
+ std::string tmp;
+ replace_all("\r", "", c->src, &tmp);
+ cd->unix_style.src_buf.assign(tmp.begin(), tmp.end());
+ cd->unix_style.src = to_substr(cd->unix_style.src_buf);
+ cd->unix_style_json.src_buf.assign(tmp.begin(), tmp.end());
+ cd->unix_style_json.src = to_substr(cd->unix_style.src_buf);
+ }
+ {
+ std::string tmp;
+ replace_all("\n", "\r\n", cd->unix_style.src, &tmp);
+ cd->windows_style.src_buf.assign(tmp.begin(), tmp.end());
+ cd->windows_style.src = to_substr(cd->windows_style.src_buf);
+ cd->windows_style_json.src_buf.assign(tmp.begin(), tmp.end());
+ cd->windows_style_json.src = to_substr(cd->windows_style.src_buf);
+ }
+ }
+ else
+ {
+ cd = &it->second;
+ }
+ return cd;
+}
+
+} // namespace yml
+} // namespace c4
+
+#if defined(_MSC_VER)
+# pragma warning(pop)
+#elif defined(__clang__)
+# pragma clang diagnostic pop
+#elif defined(__GNUC__)
+# pragma GCC diagnostic pop
+#endif