mirror of
https://github.com/biojppm/rapidyaml.git
synced 2026-01-18 21:41:18 +01:00
(Const)NodeRef: add .at(), improve coverage, verify error handling
This commit is contained in:
@@ -1355,6 +1355,12 @@ ryml:
|
||||
because this may cost up to 10% in processing time.
|
||||
* `RYML_DEFAULT_CALLBACKS=ON/OFF`. Enable/disable ryml's default
|
||||
implementation of error and allocation callbacks. Defaults to `ON`.
|
||||
* `RYML_DEFAULT_CALLBACK_USES_EXCEPTIONS=ON/OFF` - Enable/disable
|
||||
the same-named macro, which will make the default error handler
|
||||
provided by ryml throw a `std::runtime_error` exception.
|
||||
* `RYML_USE_ASSERT` - enable assertions in the code regardless of
|
||||
build type. This is disabled by default. Failed assertions will
|
||||
trigger a call to the error callback.
|
||||
* `RYML_STANDALONE=ON/OFF`. ryml uses
|
||||
[c4core](https://github.com/biojppm/c4core), a C++ library with low-level
|
||||
multi-platform utilities for C++. When `RYML_STANDALONE=ON`, c4core is
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
### Error handling
|
||||
|
||||
### Fixes
|
||||
Fix major error handling problem reported in [#389](https://github.com/biojppm/rapidyaml/issues/389) ([PR#411](https://github.com/biojppm/rapidyaml/pull/411)):
|
||||
|
||||
- Fix major error handling problem reported in [#389](https://github.com/biojppm/rapidyaml/issues/389) ([PR#411](https://github.com/biojppm/rapidyaml/pull/411)):
|
||||
- The `NodeRef` and `ConstNodeRef` classes had many methods marked `noexcept` that were doing assertions which could throw exceptions, causing an abort instead of a throw whenever the assertion called an exception-throwing error callback.
|
||||
- Also, this problem was compounded by exceptions being enabled in every build type -- despite the intention to have them only in debug builds. There was a problem in the preprocessor code to enable assertions which led to assertions being enabled in release builds even when `RYML_USE_ASSERT` was defined to 0. Thanks to @jdrouhard for reporting this.
|
||||
- Although the code is and was extensively tested, the testing was addressing mostly the happy path. In the fix, I added tests to ensure that the error behavior is as intended.
|
||||
- Together with this changeset, a major revision was carried out of the asserting/checking status of each function in the node classes. In most cases, assertions were added to cases that were missing them. So **beware** - user code that was invalid will now assert or error out. Also, assertions and checks are now directed as much as possible to the callbacks of the closest scope, ie if a tree has custom callbacks, errors should go through those callbacks.
|
||||
- Although the code is and was extensively tested, the testing was addressing mostly the happy path. Tests were added to ensure that the error behavior is as intended.
|
||||
- Together with this changeset, a major revision was carried out of the asserting/checking status of each function in the node classes. In most cases, assertions were added to functions cases that were missing them. So **beware** - user code that was invalid will now assert or error out. Also, assertions and checks are now directed as much as possible to the callbacks of the closest scope, ie if a tree has custom callbacks, errors should go through those callbacks.
|
||||
- Also, the intended assertion behavior is now in place: *no assertions in release builds*. **Beware** as well - user code which was relying on this will now silently succeed and return garbage in release builds. See the next points, which may help:
|
||||
- Added new methods to the node class:
|
||||
- Added new methods to the `NodeRef`/`ConstNodeRef` classes:
|
||||
```c++
|
||||
/** Distinguish between a valid seed vs a valid non-seed ref. */
|
||||
bool readable() const { return valid() && !is_seed(); }
|
||||
@@ -21,8 +21,9 @@
|
||||
* readable, or when it is not a map. This behaviour is similar to
|
||||
* std::vector::at(), but the error consists in calling the error
|
||||
* callback instead of directly raising an exception. */
|
||||
ConstNodeRef ConstNodeRef::at(csubstr key) const;
|
||||
ConstNodeRef NodeRef::at(csubstr key) const;
|
||||
ConstNodeRef at(csubstr key) const;
|
||||
/** Likewise, but return a seed node when the key is not found */
|
||||
ConstNodeRef at(csubstr key);
|
||||
|
||||
/** Get a child by position, with error checking; complexity is
|
||||
* O(pos).
|
||||
@@ -30,16 +31,21 @@
|
||||
* Behaves as operator[](size_t) const, but always raises an error
|
||||
* (even when RYML_USE_ASSERT is set to false) when the returned
|
||||
* node does not exist, or when this node is not readable, or when
|
||||
* it is not a map. This behaviour is similar to
|
||||
* it is not a container. This behaviour is similar to
|
||||
* std::vector::at(), but the error consists in calling the error
|
||||
* callback instead of directly raising an exception. */
|
||||
ConstNodeRef ConstNodeRef::at(size_t pos) const;
|
||||
ConstNodeRef NodeRef::at(size_t pos) const;
|
||||
```
|
||||
ConstNodeRef at(size_t pos) const;
|
||||
/** Likewise, but return a seed node when pos is not found */
|
||||
ConstNodeRef at(csubstr key);
|
||||
```
|
||||
- Added macros and respective cmake options to control error handling:
|
||||
- `RYML_USE_ASSERT` - enable assertions regardless of build type. This is disabled by default.
|
||||
- `RYML_DEFAULT_CALLBACK_USES_EXCEPTIONS` - defines the same macro, which will make the default error handler provided by ryml throw exceptions instead of calling `std::abort()`. This is disabled by default.
|
||||
- Also, `RYML_DEBUG_BREAK()` is now enabled only if `RYML_DBG` is defined, as reported in [#362](https://github.com/biojppm/rapidyaml/issues/362).
|
||||
|
||||
|
||||
### More fixes
|
||||
|
||||
- Fix [#390](https://github.com/biojppm/rapidyaml/pull/390) - `csubstr::first_real_span()` failed on scientific numbers with one digit in the exponent.
|
||||
- Fix [#361](https://github.com/biojppm/rapidyaml/pull/361) - parse error on map scalars containing `:` and starting on the next line:
|
||||
```yaml
|
||||
|
||||
@@ -4190,13 +4190,9 @@ void sample_error_handler()
|
||||
// crash.
|
||||
ryml::set_callbacks(errh.callbacks());
|
||||
errh.check_effect(/*committed*/true);
|
||||
bool had_parse_error = true;
|
||||
errh.check_error_occurs([&had_parse_error]{
|
||||
had_parse_error = true;
|
||||
errh.check_error_occurs([&]{
|
||||
ryml::Tree tree = ryml::parse_in_arena("errorhandler.yml", "[a: b\n}");
|
||||
had_parse_error = false; // this line is not executed
|
||||
});
|
||||
CHECK(had_parse_error);
|
||||
ryml::set_callbacks(errh.defaults); // restore defaults.
|
||||
errh.check_effect(/*committed*/false);
|
||||
}
|
||||
|
||||
@@ -323,6 +323,14 @@ public:
|
||||
C4_ALWAYS_INLINE size_t num_other_siblings() const RYML_NOEXCEPT { _C4RV(); return tree_->num_other_siblings(id_); }
|
||||
C4_ALWAYS_INLINE size_t sibling_pos(ConstImpl const& n) const RYML_NOEXCEPT { _C4RV(); _RYML_CB_ASSERT(tree_->callbacks(), n.readable()); return tree_->child_pos(tree_->parent(id_), n.m_id); }
|
||||
|
||||
/** @} */
|
||||
|
||||
public:
|
||||
|
||||
/** @name square_brackets
|
||||
* operator[] */
|
||||
/** @{ */
|
||||
|
||||
/** Find child by key; complexity is O(num_children).
|
||||
*
|
||||
* Returns the requested node, or an object in seed state if no
|
||||
@@ -332,10 +340,11 @@ public:
|
||||
* to the tree provided that its create() method is called prior
|
||||
* to writing, which happens in most modifying methods in
|
||||
* NodeRef. It is the caller's responsibility to verify that the
|
||||
* returned node is readable before subsequently using it.
|
||||
* returned node is readable before subsequently using it to read
|
||||
* from the tree.
|
||||
*
|
||||
* @warning the calling object must be readable. This precondition
|
||||
* is asserted. This assertion is performed only if @ref
|
||||
* is asserted. The assertion is performed only if @ref
|
||||
* RYML_USE_ASSERT is set to true. As with the non-const overload,
|
||||
* it is UB to call this method if the node is not readable.
|
||||
*
|
||||
@@ -357,10 +366,11 @@ public:
|
||||
* to the tree provided that its create() method is called prior
|
||||
* to writing, which happens in most modifying methods in
|
||||
* NodeRef. It is the caller's responsibility to verify that the
|
||||
* returned node is readable before subsequently using it.
|
||||
* returned node is readable before subsequently using it to read
|
||||
* from the tree.
|
||||
*
|
||||
* @warning the calling object must be readable. This precondition
|
||||
* is asserted. This assertion is performed only if @ref
|
||||
* is asserted. The assertion is performed only if @ref
|
||||
* RYML_USE_ASSERT is set to true. As with the non-const overload,
|
||||
* it is UB to call this method if the node is not readable.
|
||||
*
|
||||
@@ -377,7 +387,7 @@ public:
|
||||
*
|
||||
* Behaves similar to the non-const overload, but further asserts
|
||||
* that the returned node is readable (because it can never be in
|
||||
* a seed state). This assertion is performed only if @ref
|
||||
* a seed state). The assertion is performed only if @ref
|
||||
* RYML_USE_ASSERT is set to true. As with the non-const overload,
|
||||
* it is UB to use the return value if it is not valid.
|
||||
*
|
||||
@@ -407,6 +417,90 @@ public:
|
||||
return {tree_, ch};
|
||||
}
|
||||
|
||||
/** @} */
|
||||
|
||||
public:
|
||||
|
||||
/** @name at
|
||||
*
|
||||
* These functions are the analogue to operator[], with the
|
||||
* difference that they */
|
||||
/** @{ */
|
||||
|
||||
/** Find child by key; complexity is O(num_children).
|
||||
*
|
||||
* Returns the requested node, or an object in seed state if no
|
||||
* such child is found (see @ref NodeRef for an explanation of
|
||||
* what is seed state). When the object is in seed state, using it
|
||||
* to read from the tree is UB. The seed node can be subsequently
|
||||
* used to write to the tree provided that its create() method is
|
||||
* called prior to writing, which happens inside most mutating
|
||||
* methods in NodeRef. It is the caller's responsibility to verify
|
||||
* that the returned node is readable before subsequently using it
|
||||
* to read from the tree.
|
||||
*
|
||||
* @warning This method will call the error callback (regardless
|
||||
* of build type) whenever any of the following preconditions is
|
||||
* violated: a) the object is valid (points at a tree and a node),
|
||||
* b) the calling object must be readable (must not be in seed
|
||||
* state), c) the calling object must be pointing at a MAP
|
||||
* node. The preconditions are similar to the non-const
|
||||
* operator[](csubstr), but instead of using assertions, this
|
||||
* function directly checks those conditions and calls the error
|
||||
* callback if any of the checks fail.
|
||||
*
|
||||
* @note since it is valid behavior for the returned node to be in
|
||||
* seed state, the error callback is not invoked when this
|
||||
* happens. */
|
||||
template<class U=Impl>
|
||||
C4_ALWAYS_INLINE auto at(csubstr key) -> _C4_IF_MUTABLE(Impl)
|
||||
{
|
||||
RYML_CHECK(tree_ != nullptr);
|
||||
_RYML_CB_CHECK(tree_->m_callbacks, (id_ >= 0 && id_ < tree_->capacity()));
|
||||
_RYML_CB_CHECK(tree_->m_callbacks, ((Impl const*)this)->readable());
|
||||
_RYML_CB_CHECK(tree_->m_callbacks, tree_->is_map(id_));
|
||||
size_t ch = tree__->find_child(id__, key);
|
||||
return ch != NONE ? Impl(tree__, ch) : Impl(tree__, id__, key);
|
||||
}
|
||||
|
||||
/** Find child by position; complexity is O(pos).
|
||||
*
|
||||
* Returns the requested node, or an object in seed state if no
|
||||
* such child is found (see @ref NodeRef for an explanation of
|
||||
* what is seed state). When the object is in seed state, using it
|
||||
* to read from the tree is UB. The seed node can be used to write
|
||||
* to the tree provided that its create() method is called prior
|
||||
* to writing, which happens in most modifying methods in
|
||||
* NodeRef. It is the caller's responsibility to verify that the
|
||||
* returned node is readable before subsequently using it to read
|
||||
* from the tree.
|
||||
*
|
||||
* @warning This method will call the error callback (regardless
|
||||
* of build type) whenever any of the following preconditions is
|
||||
* violated: a) the object is valid (points at a tree and a node),
|
||||
* b) the calling object must be readable (must not be in seed
|
||||
* state), c) the calling object must be pointing at a MAP
|
||||
* node. The preconditions are similar to the non-const
|
||||
* operator[](size_t), but instead of using assertions, this
|
||||
* function directly checks those conditions and calls the error
|
||||
* callback if any of the checks fail.
|
||||
*
|
||||
* @note since it is valid behavior for the returned node to be in
|
||||
* seed state, the error callback is not invoked when this
|
||||
* happens. */
|
||||
template<class U=Impl>
|
||||
C4_ALWAYS_INLINE auto at(size_t pos) -> _C4_IF_MUTABLE(Impl)
|
||||
{
|
||||
RYML_CHECK(tree_ != nullptr);
|
||||
const size_t cap = tree_->capacity();
|
||||
_RYML_CB_CHECK(tree_->m_callbacks, (id_ >= 0 && id_ < cap));
|
||||
_RYML_CB_CHECK(tree_->m_callbacks, (pos >= 0 && pos < cap));
|
||||
_RYML_CB_CHECK(tree_->m_callbacks, ((Impl const*)this)->readable());
|
||||
_RYML_CB_CHECK(tree_->m_callbacks, tree_->is_container(id_));
|
||||
size_t ch = tree__->child(id__, pos);
|
||||
return ch != NONE ? Impl(tree__, ch) : Impl(tree__, id__, pos);
|
||||
}
|
||||
|
||||
/** Get a child by name, with error checking; complexity is
|
||||
* O(num_children).
|
||||
*
|
||||
@@ -419,8 +513,9 @@ public:
|
||||
ConstImpl at(csubstr key) const
|
||||
{
|
||||
RYML_CHECK(tree_ != nullptr);
|
||||
_RYML_CB_CHECK(tree_->m_callbacks, (id_ >= 0 && id_ < tree_->capacity()));
|
||||
_RYML_CB_CHECK(tree_->m_callbacks, ((Impl const*)this)->readable());
|
||||
_RYML_CB_CHECK(tree_->m_callbacks, ((Impl const*)this)->is_map());
|
||||
_RYML_CB_CHECK(tree_->m_callbacks, tree_->is_map(id_));
|
||||
size_t ch = tree_->find_child(id_, key);
|
||||
_RYML_CB_CHECK(tree_->m_callbacks, ch != NONE);
|
||||
return {tree_, ch};
|
||||
@@ -432,16 +527,19 @@ public:
|
||||
* Behaves as operator[](size_t) const, but always raises an error
|
||||
* (even when RYML_USE_ASSERT is set to false) when the returned
|
||||
* node does not exist, or when this node is not readable, or when
|
||||
* it is not a map. This behaviour is similar to
|
||||
* it is not a container. This behaviour is similar to
|
||||
* std::vector::at(), but the error consists in calling the error
|
||||
* callback instead of directly raising an exception. */
|
||||
ConstImpl at(size_t pos) const
|
||||
{
|
||||
RYML_CHECK(tree_ != nullptr);
|
||||
const size_t cap = tree_->capacity();
|
||||
_RYML_CB_CHECK(tree_->m_callbacks, (id_ >= 0 && id_ < cap));
|
||||
_RYML_CB_CHECK(tree_->m_callbacks, (pos >= 0 && pos < cap));
|
||||
_RYML_CB_CHECK(tree_->m_callbacks, ((Impl const*)this)->readable());
|
||||
_RYML_CB_CHECK(tree_->m_callbacks, ((Impl const*)this)->is_container());
|
||||
_RYML_CB_CHECK(tree_->m_callbacks, tree_->is_container(id_));
|
||||
size_t ch = tree_->child(id_, pos);
|
||||
_RYML_CB_ASSERT(tree_->m_callbacks, ch != NONE);
|
||||
_RYML_CB_CHECK(tree_->m_callbacks, ch != NONE);
|
||||
return {tree_, ch};
|
||||
}
|
||||
|
||||
@@ -650,6 +748,13 @@ public:
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
/** This object holds a pointer to an existing tree, and a node id. It
|
||||
* can be used only to read from the tree.
|
||||
*
|
||||
*
|
||||
*
|
||||
* @warning The lifetime of the tree must be larger than that of this
|
||||
* object. It is up to the user to ensure that this happens. */
|
||||
class RYML_EXPORT ConstNodeRef : public detail::RoNodeMethods<ConstNodeRef, ConstNodeRef>
|
||||
{
|
||||
public:
|
||||
@@ -746,9 +851,21 @@ public:
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/** a reference to a node in an existing yaml tree, offering a more
|
||||
/** A reference to a node in an existing yaml tree, offering a more
|
||||
* convenient API than the index-based API used in the tree. This
|
||||
* reference can be used to modify the tree. Individual objects may be */
|
||||
* reference can be used to modify the tree.
|
||||
*
|
||||
* When the object is in seed state, using it to read from the tree is
|
||||
* UB. The seed node can be used to write to the tree provided that
|
||||
* its create() method is called prior to writing, which happens in
|
||||
* most modifying methods in NodeRef. It is the owners's
|
||||
* responsibility to verify that the an existing node is readable
|
||||
* before subsequently using it to read from the tree.
|
||||
*
|
||||
* See the quickstart for a more detailed explanation on the
|
||||
*
|
||||
* Individual objects may be
|
||||
* */
|
||||
class RYML_EXPORT NodeRef : public detail::RoNodeMethods<NodeRef, ConstNodeRef>
|
||||
{
|
||||
public:
|
||||
@@ -1318,6 +1435,7 @@ public:
|
||||
/** @} */
|
||||
|
||||
#undef _C4RV
|
||||
#undef _C4RID
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -171,10 +171,10 @@ struct ExpectError
|
||||
|
||||
static void do_check( std::function<void()> fn, Location expected={}) { do_check(nullptr, fn, expected); }
|
||||
static void do_check(Tree *tree, std::function<void()> fn, Location expected={});
|
||||
static void check_success( std::function<void()> fn) { check_success(nullptr, fn); }
|
||||
static void check_success(Tree *tree, std::function<void()> fn);
|
||||
static void check_assertion( std::function<void()> fn, Location expected={}) { check_assertion(nullptr, fn, expected); }
|
||||
static void check_assertion(Tree *tree, std::function<void()> fn, Location expected={});
|
||||
static void check_success(std::function<void()> fn) { check_success(nullptr, fn); }
|
||||
static void check_success(Tree *tree, std::function<void()> fn);
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -149,6 +149,47 @@ TEST(NodeRef, general)
|
||||
EXPECT_EQ(root["b"]["aaa"].val(), "0");
|
||||
}
|
||||
|
||||
TEST(NodeRef, operator_equal_equal)
|
||||
{
|
||||
Tree tree1 = parse_in_arena("{a: a1, b: b1}");
|
||||
NodeRef a1 = tree1["a"];
|
||||
NodeRef b1 = tree1["b"];
|
||||
NodeRef a1_ = a1;
|
||||
NodeRef b1_ = b1;
|
||||
NodeRef seedc1 = tree1["c"];
|
||||
NodeRef seedc1_ = seedc1;
|
||||
NodeRef seedd1 = tree1["d"];
|
||||
NodeRef seedd1_ = seedd1;
|
||||
EXPECT_EQ(a1, a1_);
|
||||
EXPECT_EQ(b1, b1_);
|
||||
EXPECT_NE(a1, b1);
|
||||
EXPECT_NE(b1, a1);
|
||||
EXPECT_EQ(seedc1, seedc1_);
|
||||
EXPECT_EQ(seedd1, seedd1_);
|
||||
EXPECT_NE(seedc1, seedd1);
|
||||
Tree tree2 = parse_in_arena("{a: a2, b: b2}");
|
||||
NodeRef a2 = tree2["a"];
|
||||
NodeRef b2 = tree2["b"];
|
||||
NodeRef a2_ = a2;
|
||||
NodeRef b2_ = b2;
|
||||
NodeRef seedc2 = tree2["c"];
|
||||
NodeRef seedc2_ = seedc2;
|
||||
NodeRef seedd2 = tree2["d"];
|
||||
NodeRef seedd2_ = seedd2;
|
||||
EXPECT_EQ(a2, a2_);
|
||||
EXPECT_EQ(b2, b2_);
|
||||
EXPECT_NE(a2, b2);
|
||||
EXPECT_NE(b2, a2);
|
||||
EXPECT_EQ(seedc2, seedc2_);
|
||||
EXPECT_EQ(seedd2, seedd2_);
|
||||
EXPECT_NE(seedc2, seedd2);
|
||||
//
|
||||
EXPECT_NE(a1, a2);
|
||||
EXPECT_NE(b1, b2);
|
||||
EXPECT_NE(seedc1, seedc2);
|
||||
EXPECT_NE(seedd1, seedd2);
|
||||
}
|
||||
|
||||
TEST(NodeRef, valid_vs_seed_vs_readable)
|
||||
{
|
||||
static_assert(!ConstNodeRef::is_seed(), "ConstNodeRef must never be a seed");
|
||||
@@ -181,133 +222,132 @@ TEST(NodeRef, valid_vs_seed_vs_readable)
|
||||
EXPECT_FALSE(none.readable());
|
||||
}
|
||||
|
||||
#define _TEST_FAIL_READ(method_expr) \
|
||||
#define _TEST_FAIL(method_expr) \
|
||||
{ \
|
||||
SCOPED_TRACE(#method_expr); \
|
||||
std::cout << __FILE__ << ":" << __LINE__ << ": " << #method_expr << "\n"; \
|
||||
if(tree) \
|
||||
ExpectError::check_assertion(tree, [&]{ return method_expr; }); \
|
||||
else \
|
||||
ExpectError::check_assertion([&]{ return method_expr; }); \
|
||||
}
|
||||
#define _TEST_SUCCEED_READ(method_expr) \
|
||||
#define _TEST_SUCCEED(method_expr) \
|
||||
{ \
|
||||
SCOPED_TRACE(#method_expr); \
|
||||
std::cout << __FILE__ << ":" << __LINE__ << ": " << #method_expr << "\n"; \
|
||||
if(tree) \
|
||||
ExpectError::check_success(tree, [&]{ return method_expr; }); \
|
||||
else \
|
||||
ExpectError::check_success([&]{ return method_expr; }); \
|
||||
}
|
||||
|
||||
template<class NodeT>
|
||||
void test_fail_read(Tree *tree, NodeT node)
|
||||
{
|
||||
_TEST_SUCCEED_READ(node.get())
|
||||
_TEST_FAIL_READ(node.type())
|
||||
_TEST_FAIL_READ(node.type_str())
|
||||
_TEST_FAIL_READ(node.key())
|
||||
_TEST_FAIL_READ(node.key_tag())
|
||||
_TEST_FAIL_READ(node.key_anchor())
|
||||
_TEST_FAIL_READ(node.key_ref())
|
||||
_TEST_FAIL_READ(node.key_is_null())
|
||||
_TEST_FAIL_READ(node.keysc())
|
||||
_TEST_FAIL_READ(node.val())
|
||||
_TEST_FAIL_READ(node.val_tag())
|
||||
_TEST_FAIL_READ(node.val_anchor())
|
||||
_TEST_FAIL_READ(node.val_ref())
|
||||
_TEST_FAIL_READ(node.val_is_null())
|
||||
_TEST_FAIL_READ(node.valsc())
|
||||
_TEST_FAIL_READ(node.is_map())
|
||||
_TEST_FAIL_READ(node.empty())
|
||||
_TEST_FAIL_READ(node.is_stream())
|
||||
_TEST_FAIL_READ(node.is_doc())
|
||||
_TEST_FAIL_READ(node.is_container())
|
||||
_TEST_FAIL_READ(node.is_map())
|
||||
_TEST_FAIL_READ(node.is_seq())
|
||||
_TEST_FAIL_READ(node.has_val())
|
||||
_TEST_FAIL_READ(node.has_key())
|
||||
_TEST_FAIL_READ(node.is_keyval())
|
||||
_TEST_FAIL_READ(node.has_key_tag())
|
||||
_TEST_FAIL_READ(node.has_val_tag())
|
||||
_TEST_FAIL_READ(node.has_key_anchor())
|
||||
_TEST_FAIL_READ(node.has_val_anchor())
|
||||
_TEST_FAIL_READ(node.is_val_anchor())
|
||||
_TEST_FAIL_READ(node.has_anchor())
|
||||
_TEST_FAIL_READ(node.is_anchor())
|
||||
_TEST_FAIL_READ(node.is_key_ref())
|
||||
_TEST_FAIL_READ(node.is_val_ref())
|
||||
_TEST_FAIL_READ(node.is_ref())
|
||||
_TEST_FAIL_READ(node.is_anchor_or_ref())
|
||||
_TEST_FAIL_READ(node.is_key_quoted())
|
||||
_TEST_FAIL_READ(node.is_val_quoted())
|
||||
_TEST_FAIL_READ(node.parent_is_seq())
|
||||
_TEST_FAIL_READ(node.parent_is_map())
|
||||
_TEST_FAIL_READ(node.is_root())
|
||||
_TEST_FAIL_READ(node.has_parent())
|
||||
_TEST_FAIL_READ(node.has_child(0))
|
||||
_TEST_FAIL_READ(node.has_child("key"))
|
||||
_TEST_FAIL_READ(node.has_children())
|
||||
_TEST_FAIL_READ(node.has_sibling("key"))
|
||||
_TEST_FAIL_READ(node.has_other_siblings())
|
||||
_TEST_FAIL_READ(node.doc(0))
|
||||
_TEST_FAIL_READ(node.parent())
|
||||
_TEST_FAIL_READ(node.num_children())
|
||||
_TEST_FAIL_READ(node.first_child())
|
||||
_TEST_FAIL_READ(node.last_child())
|
||||
_TEST_FAIL_READ(node.child(0))
|
||||
_TEST_FAIL_READ(node.find_child("key"))
|
||||
_TEST_FAIL_READ(node.prev_sibling())
|
||||
_TEST_FAIL_READ(node.next_sibling())
|
||||
_TEST_FAIL_READ(node.first_sibling())
|
||||
_TEST_FAIL_READ(node.last_sibling())
|
||||
_TEST_FAIL_READ(node.sibling(0))
|
||||
_TEST_FAIL_READ(node.find_sibling("key"))
|
||||
_TEST_FAIL_READ(node.num_children())
|
||||
_TEST_FAIL_READ(node.num_siblings())
|
||||
_TEST_FAIL_READ(node.num_other_siblings())
|
||||
_TEST_FAIL_READ(node["key"])
|
||||
_TEST_FAIL_READ(node[0])
|
||||
_TEST_FAIL_READ(node.at("key"))
|
||||
_TEST_FAIL_READ(node.at(0))
|
||||
_TEST_SUCCEED(node.get())
|
||||
_TEST_FAIL(node.type())
|
||||
_TEST_FAIL(node.type_str())
|
||||
_TEST_FAIL(node.key())
|
||||
_TEST_FAIL(node.key_tag())
|
||||
_TEST_FAIL(node.key_anchor())
|
||||
_TEST_FAIL(node.key_ref())
|
||||
_TEST_FAIL(node.key_is_null())
|
||||
_TEST_FAIL(node.keysc())
|
||||
_TEST_FAIL(node.val())
|
||||
_TEST_FAIL(node.val_tag())
|
||||
_TEST_FAIL(node.val_anchor())
|
||||
_TEST_FAIL(node.val_ref())
|
||||
_TEST_FAIL(node.val_is_null())
|
||||
_TEST_FAIL(node.valsc())
|
||||
_TEST_FAIL(node.is_map())
|
||||
_TEST_FAIL(node.empty())
|
||||
_TEST_FAIL(node.is_stream())
|
||||
_TEST_FAIL(node.is_doc())
|
||||
_TEST_FAIL(node.is_container())
|
||||
_TEST_FAIL(node.is_map())
|
||||
_TEST_FAIL(node.is_seq())
|
||||
_TEST_FAIL(node.has_val())
|
||||
_TEST_FAIL(node.has_key())
|
||||
_TEST_FAIL(node.is_keyval())
|
||||
_TEST_FAIL(node.has_key_tag())
|
||||
_TEST_FAIL(node.has_val_tag())
|
||||
_TEST_FAIL(node.has_key_anchor())
|
||||
_TEST_FAIL(node.has_val_anchor())
|
||||
_TEST_FAIL(node.is_val_anchor())
|
||||
_TEST_FAIL(node.has_anchor())
|
||||
_TEST_FAIL(node.is_anchor())
|
||||
_TEST_FAIL(node.is_key_ref())
|
||||
_TEST_FAIL(node.is_val_ref())
|
||||
_TEST_FAIL(node.is_ref())
|
||||
_TEST_FAIL(node.is_anchor_or_ref())
|
||||
_TEST_FAIL(node.is_key_quoted())
|
||||
_TEST_FAIL(node.is_val_quoted())
|
||||
_TEST_FAIL(node.parent_is_seq())
|
||||
_TEST_FAIL(node.parent_is_map())
|
||||
_TEST_FAIL(node.is_root())
|
||||
_TEST_FAIL(node.has_parent())
|
||||
_TEST_FAIL(node.has_child(0))
|
||||
_TEST_FAIL(node.has_child("key"))
|
||||
_TEST_FAIL(node.has_children())
|
||||
_TEST_FAIL(node.has_sibling("key"))
|
||||
_TEST_FAIL(node.has_other_siblings())
|
||||
_TEST_FAIL(node.doc(0))
|
||||
_TEST_FAIL(node.parent())
|
||||
_TEST_FAIL(node.num_children())
|
||||
_TEST_FAIL(node.first_child())
|
||||
_TEST_FAIL(node.last_child())
|
||||
_TEST_FAIL(node.child(0))
|
||||
_TEST_FAIL(node.find_child("key"))
|
||||
_TEST_FAIL(node.prev_sibling())
|
||||
_TEST_FAIL(node.next_sibling())
|
||||
_TEST_FAIL(node.first_sibling())
|
||||
_TEST_FAIL(node.last_sibling())
|
||||
_TEST_FAIL(node.sibling(0))
|
||||
_TEST_FAIL(node.find_sibling("key"))
|
||||
_TEST_FAIL(node.num_children())
|
||||
_TEST_FAIL(node.num_siblings())
|
||||
_TEST_FAIL(node.num_other_siblings())
|
||||
_TEST_FAIL(node["key"])
|
||||
_TEST_FAIL(node[0])
|
||||
_TEST_FAIL(node.at("key"))
|
||||
_TEST_FAIL(node.at(0))
|
||||
int val;
|
||||
_TEST_FAIL_READ(node >> val)
|
||||
_TEST_FAIL_READ(node >> key(val))
|
||||
_TEST_FAIL_READ(node >> fmt::base64(val))
|
||||
_TEST_FAIL_READ(node >> key(fmt::base64(val)))
|
||||
_TEST_FAIL_READ(node.deserialize_key(fmt::base64(val)))
|
||||
_TEST_FAIL_READ(node.deserialize_val(fmt::base64(val)))
|
||||
_TEST_FAIL_READ(node.get_if("key", &val));
|
||||
_TEST_FAIL_READ(node.get_if("key", &val, 0));
|
||||
_TEST_FAIL(node >> val)
|
||||
_TEST_FAIL(node >> key(val))
|
||||
_TEST_FAIL(node >> fmt::base64(val))
|
||||
_TEST_FAIL(node >> key(fmt::base64(val)))
|
||||
_TEST_FAIL(node.deserialize_key(fmt::base64(val)))
|
||||
_TEST_FAIL(node.deserialize_val(fmt::base64(val)))
|
||||
_TEST_FAIL(node.get_if("key", &val));
|
||||
_TEST_FAIL(node.get_if("key", &val, 0));
|
||||
const NodeT const_node = node;
|
||||
_TEST_FAIL_READ(node.begin());
|
||||
_TEST_FAIL_READ(node.cbegin());
|
||||
_TEST_FAIL_READ(const_node.begin());
|
||||
_TEST_FAIL_READ(const_node.cbegin());
|
||||
_TEST_FAIL_READ(node.end());
|
||||
_TEST_FAIL_READ(node.end());
|
||||
_TEST_FAIL_READ(const_node.end());
|
||||
_TEST_FAIL_READ(const_node.end());
|
||||
_TEST_FAIL_READ(node.children());
|
||||
_TEST_FAIL_READ(node.children());
|
||||
_TEST_FAIL_READ(const_node.children());
|
||||
_TEST_FAIL_READ(const_node.children());
|
||||
_TEST_FAIL_READ(node.siblings());
|
||||
_TEST_FAIL_READ(node.siblings());
|
||||
_TEST_FAIL_READ(const_node.siblings());
|
||||
_TEST_FAIL_READ(const_node.siblings());
|
||||
//_TEST_FAIL_READ(node.visit([](NodeT &n, size_t level){ (void)n; (void)level; return false; }));
|
||||
//_TEST_FAIL_READ(const_node.visit([](const NodeT &n, size_t level){ (void)n; (void)level; return false; }));
|
||||
_TEST_SUCCEED_READ(const_node == node);
|
||||
_TEST_SUCCEED_READ(const_node != node);
|
||||
_TEST_SUCCEED_READ(const_node == nullptr);
|
||||
_TEST_SUCCEED_READ(const_node != nullptr);
|
||||
_TEST_FAIL_READ(const_node == "val");
|
||||
_TEST_FAIL_READ(const_node != "val");
|
||||
_TEST_FAIL(node.begin());
|
||||
_TEST_FAIL(node.cbegin());
|
||||
_TEST_FAIL(const_node.begin());
|
||||
_TEST_FAIL(const_node.cbegin());
|
||||
_TEST_FAIL(node.end());
|
||||
_TEST_FAIL(node.end());
|
||||
_TEST_FAIL(const_node.end());
|
||||
_TEST_FAIL(const_node.end());
|
||||
_TEST_FAIL(node.children());
|
||||
_TEST_FAIL(node.children());
|
||||
_TEST_FAIL(const_node.children());
|
||||
_TEST_FAIL(const_node.children());
|
||||
_TEST_FAIL(node.siblings());
|
||||
_TEST_FAIL(node.siblings());
|
||||
_TEST_FAIL(const_node.siblings());
|
||||
_TEST_FAIL(const_node.siblings());
|
||||
//_TEST_FAIL(node.visit([](NodeT &n, size_t level){ (void)n; (void)level; return false; }));
|
||||
//_TEST_FAIL(const_node.visit([](const NodeT &n, size_t level){ (void)n; (void)level; return false; }));
|
||||
_TEST_SUCCEED(const_node == node);
|
||||
_TEST_SUCCEED(const_node != node);
|
||||
_TEST_SUCCEED(const_node == nullptr);
|
||||
_TEST_SUCCEED(const_node != nullptr);
|
||||
_TEST_FAIL(const_node == "val");
|
||||
_TEST_FAIL(const_node != "val");
|
||||
if(std::is_same<NodeT, NodeRef>::value)
|
||||
{
|
||||
ConstNodeRef other;
|
||||
_TEST_SUCCEED_READ(node == other);
|
||||
_TEST_SUCCEED_READ(node != node);
|
||||
_TEST_SUCCEED(node == other);
|
||||
_TEST_SUCCEED(node != node);
|
||||
}
|
||||
}
|
||||
template<class NodeT>
|
||||
@@ -315,16 +355,16 @@ void test_fail_read_subject(Tree *tree, NodeT node, NodeT subject)
|
||||
{
|
||||
if(node.readable())
|
||||
{
|
||||
_TEST_SUCCEED_READ(node.has_child(subject))
|
||||
_TEST_SUCCEED_READ(node.has_sibling(subject))
|
||||
_TEST_SUCCEED(node.has_child(subject))
|
||||
_TEST_SUCCEED(node.has_sibling(subject))
|
||||
}
|
||||
else
|
||||
{
|
||||
_TEST_FAIL_READ(node.has_child(subject))
|
||||
_TEST_FAIL_READ(node.has_sibling(subject))
|
||||
_TEST_FAIL(node.has_child(subject))
|
||||
_TEST_FAIL(node.has_sibling(subject))
|
||||
}
|
||||
_TEST_FAIL_READ(node.child_pos(subject))
|
||||
_TEST_FAIL_READ(node.sibling_pos(subject))
|
||||
_TEST_FAIL(node.child_pos(subject))
|
||||
_TEST_FAIL(node.sibling_pos(subject))
|
||||
}
|
||||
#undef _TEST_FAIL_READ
|
||||
#undef _TEST_SUCCEED_READ
|
||||
@@ -446,12 +486,10 @@ void noderef_check_tree(ConstNodeRef const& root)
|
||||
EXPECT_EQ( root[5].val(), "5");
|
||||
}
|
||||
|
||||
TEST(NodeRef, append_child)
|
||||
TEST(NodeRef, append_child_1)
|
||||
{
|
||||
Tree t;
|
||||
|
||||
NodeRef root(&t);
|
||||
|
||||
root |= SEQ;
|
||||
root.append_child({"0"});
|
||||
root.append_child({"1"});
|
||||
@@ -459,16 +497,55 @@ TEST(NodeRef, append_child)
|
||||
root.append_child({"3"});
|
||||
root.append_child({"4"});
|
||||
root.append_child({"5"});
|
||||
|
||||
noderef_check_tree(root);
|
||||
}
|
||||
|
||||
TEST(NodeRef, prepend_child)
|
||||
TEST(NodeRef, append_child_2)
|
||||
{
|
||||
Tree t;
|
||||
|
||||
NodeRef root(&t);
|
||||
root |= SEQ;
|
||||
root.append_child() = "0";
|
||||
root.append_child() = "1";
|
||||
root.append_child() = "2";
|
||||
root.append_child() = "3";
|
||||
root.append_child() = "4";
|
||||
root.append_child() = "5";
|
||||
noderef_check_tree(root);
|
||||
}
|
||||
|
||||
TEST(NodeRef, append_sibling_1)
|
||||
{
|
||||
Tree t;
|
||||
NodeRef root(&t);
|
||||
root |= SEQ;
|
||||
NodeRef first = root.append_child({"0"});
|
||||
first.append_sibling({"1"});
|
||||
first.append_sibling({"2"});
|
||||
first.append_sibling({"3"});
|
||||
first.append_sibling({"4"});
|
||||
first.append_sibling({"5"});
|
||||
noderef_check_tree(root);
|
||||
}
|
||||
|
||||
TEST(NodeRef, append_sibling_2)
|
||||
{
|
||||
Tree t;
|
||||
NodeRef root(&t);
|
||||
root |= SEQ;
|
||||
NodeRef first = root.append_child() << "0";
|
||||
first.append_sibling() << "1";
|
||||
first.append_sibling() << "2";
|
||||
first.append_sibling() << "3";
|
||||
first.append_sibling() << "4";
|
||||
first.append_sibling() << "5";
|
||||
noderef_check_tree(root);
|
||||
}
|
||||
|
||||
TEST(NodeRef, prepend_child_1)
|
||||
{
|
||||
Tree t;
|
||||
NodeRef root(&t);
|
||||
root |= SEQ;
|
||||
root.prepend_child({"5"});
|
||||
root.prepend_child({"4"});
|
||||
@@ -476,17 +553,57 @@ TEST(NodeRef, prepend_child)
|
||||
root.prepend_child({"2"});
|
||||
root.prepend_child({"1"});
|
||||
root.prepend_child({"0"});
|
||||
|
||||
noderef_check_tree(root);
|
||||
}
|
||||
|
||||
TEST(NodeRef, insert_child)
|
||||
TEST(NodeRef, prepend_child_2)
|
||||
{
|
||||
Tree t;
|
||||
NodeRef root(&t);
|
||||
root |= SEQ;
|
||||
root.prepend_child() << "5";
|
||||
root.prepend_child() << "4";
|
||||
root.prepend_child() << "3";
|
||||
root.prepend_child() << "2";
|
||||
root.prepend_child() << "1";
|
||||
root.prepend_child() << "0";
|
||||
noderef_check_tree(root);
|
||||
}
|
||||
|
||||
TEST(NodeRef, prepend_sibling_1)
|
||||
{
|
||||
Tree t;
|
||||
NodeRef root(&t);
|
||||
root |= SEQ;
|
||||
NodeRef last = root.prepend_child({"5"});
|
||||
last.prepend_sibling({"4"});
|
||||
last.prepend_sibling({"3"});
|
||||
last.prepend_sibling({"2"});
|
||||
last.prepend_sibling({"1"});
|
||||
last.prepend_sibling({"0"});
|
||||
noderef_check_tree(root);
|
||||
}
|
||||
|
||||
TEST(NodeRef, prepend_sibling_2)
|
||||
{
|
||||
Tree t;
|
||||
NodeRef root(&t);
|
||||
root |= SEQ;
|
||||
NodeRef last = root.prepend_child();
|
||||
last = "5";
|
||||
last.prepend_sibling() = "4";
|
||||
last.prepend_sibling() = "3";
|
||||
last.prepend_sibling() = "2";
|
||||
last.prepend_sibling() = "1";
|
||||
last.prepend_sibling() = "0";
|
||||
noderef_check_tree(root);
|
||||
}
|
||||
|
||||
TEST(NodeRef, insert_child_1)
|
||||
{
|
||||
Tree t;
|
||||
NodeRef root(&t);
|
||||
NodeRef none(&t, NONE);
|
||||
|
||||
root |= SEQ;
|
||||
root.insert_child({"3"}, none);
|
||||
root.insert_child({"4"}, root[0]);
|
||||
@@ -494,7 +611,51 @@ TEST(NodeRef, insert_child)
|
||||
root.insert_child({"5"}, root[2]);
|
||||
root.insert_child({"1"}, root[0]);
|
||||
root.insert_child({"2"}, root[1]);
|
||||
noderef_check_tree(root);
|
||||
}
|
||||
|
||||
TEST(NodeRef, insert_child_2)
|
||||
{
|
||||
Tree t;
|
||||
NodeRef root(&t);
|
||||
NodeRef none(&t, NONE);
|
||||
root |= SEQ;
|
||||
root.insert_child(none) << "3";
|
||||
root.insert_child(root[0]) << "4";
|
||||
root.insert_child(none) << "0";
|
||||
root.insert_child(root[2]) << "5";
|
||||
root.insert_child(root[0]) << "1";
|
||||
root.insert_child(root[1]) << "2";
|
||||
noderef_check_tree(root);
|
||||
}
|
||||
|
||||
TEST(NodeRef, insert_sibling_1)
|
||||
{
|
||||
Tree t;
|
||||
NodeRef root(&t);
|
||||
NodeRef none(&t, NONE);
|
||||
root |= SEQ;
|
||||
NodeRef first = root.insert_child({"3"}, none);
|
||||
first.insert_sibling({"4"}, root[0]);
|
||||
first.insert_sibling({"0"}, none);
|
||||
first.insert_sibling({"5"}, root[2]);
|
||||
first.insert_sibling({"1"}, root[0]);
|
||||
first.insert_sibling({"2"}, root[1]);
|
||||
noderef_check_tree(root);
|
||||
}
|
||||
|
||||
TEST(NodeRef, insert_sibling_2)
|
||||
{
|
||||
Tree t;
|
||||
NodeRef root(&t);
|
||||
NodeRef none(&t, NONE);
|
||||
root |= SEQ;
|
||||
NodeRef first = root.insert_child(none) << "3";
|
||||
first.insert_sibling(root[0]) << "4";
|
||||
first.insert_sibling(none) << "0";
|
||||
first.insert_sibling(root[2]) << "5";
|
||||
first.insert_sibling(root[0]) << "1";
|
||||
first.insert_sibling(root[1]) << "2";
|
||||
noderef_check_tree(root);
|
||||
}
|
||||
|
||||
@@ -1099,6 +1260,26 @@ TEST(NodeRef, overload_sets)
|
||||
}
|
||||
}
|
||||
|
||||
TEST(NodeRef, get_if)
|
||||
{
|
||||
const Tree tree = parse_in_arena("{a: 1, b: 2}");
|
||||
ConstNodeRef root = tree.rootref();
|
||||
int val = 0;
|
||||
EXPECT_TRUE(root.get_if("a", &val));
|
||||
EXPECT_EQ(val, 1);
|
||||
EXPECT_TRUE(root.get_if("b", &val));
|
||||
EXPECT_EQ(val, 2);
|
||||
EXPECT_FALSE(root.get_if("c", &val));
|
||||
EXPECT_EQ(val, 2);
|
||||
int fallback = 3;
|
||||
EXPECT_TRUE(root.get_if("a", &val, fallback));
|
||||
EXPECT_EQ(val, 1);
|
||||
EXPECT_TRUE(root.get_if("b", &val, fallback));
|
||||
EXPECT_EQ(val, 2);
|
||||
EXPECT_FALSE(root.get_if("c", &val, fallback));
|
||||
EXPECT_EQ(val, 3);
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------
|
||||
// this is needed to use the test case library
|
||||
|
||||
@@ -833,14 +833,30 @@ TEST(Tree, clear)
|
||||
//-------------------------------------------
|
||||
|
||||
template<class Function>
|
||||
void verify_assertion(Tree &tree, Function &&fn)
|
||||
void verify_success_(Tree &tree, Function &&fn)
|
||||
{
|
||||
ExpectError::check_success(&tree, [&]{
|
||||
(void)fn(tree);
|
||||
});
|
||||
}
|
||||
template<class Function>
|
||||
void verify_success_(csubstr src, Function &&fn)
|
||||
{
|
||||
Tree tree = parse_in_arena(src);
|
||||
ExpectError::check_success(&tree, [&]{
|
||||
(void)fn(tree);
|
||||
});
|
||||
}
|
||||
|
||||
template<class Function>
|
||||
void verify_assertion_(Tree &tree, Function &&fn)
|
||||
{
|
||||
ExpectError::check_assertion(&tree, [&]{
|
||||
(void)fn(tree);
|
||||
});
|
||||
}
|
||||
template<class Function>
|
||||
void verify_assertion(csubstr src, Function &&fn)
|
||||
void verify_assertion_(csubstr src, Function &&fn)
|
||||
{
|
||||
Tree tree = parse_in_arena(src);
|
||||
ExpectError::check_assertion(&tree, [&]{
|
||||
@@ -849,14 +865,14 @@ void verify_assertion(csubstr src, Function &&fn)
|
||||
}
|
||||
|
||||
template<class Function>
|
||||
void verify_error(Tree &tree, Function &&fn)
|
||||
void verify_error_(Tree &tree, Function &&fn)
|
||||
{
|
||||
ExpectError::do_check(&tree, [&]{
|
||||
(void)fn(tree);
|
||||
});
|
||||
}
|
||||
template<class Function>
|
||||
void verify_error(csubstr src, Function &&fn)
|
||||
void verify_error_(csubstr src, Function &&fn)
|
||||
{
|
||||
Tree tree = parse_in_arena(src);
|
||||
ExpectError::do_check(&tree, [&]{
|
||||
@@ -864,6 +880,24 @@ void verify_error(csubstr src, Function &&fn)
|
||||
});
|
||||
}
|
||||
|
||||
#define verify_success(...) \
|
||||
{ \
|
||||
SCOPED_TRACE("caller"); \
|
||||
verify_success_(__VA_ARGS__); \
|
||||
}
|
||||
|
||||
#define verify_assertion(...) \
|
||||
{ \
|
||||
SCOPED_TRACE("caller"); \
|
||||
verify_assertion_(__VA_ARGS__); \
|
||||
}
|
||||
|
||||
#define verify_error(...) \
|
||||
{ \
|
||||
SCOPED_TRACE("caller"); \
|
||||
verify_error_(__VA_ARGS__); \
|
||||
}
|
||||
|
||||
|
||||
TEST(Tree, ref)
|
||||
{
|
||||
@@ -917,177 +951,221 @@ TEST(Tree, ref_const)
|
||||
}
|
||||
|
||||
|
||||
TEST(Tree, operator_square_brackets)
|
||||
TEST(Tree, operator_square_brackets_seq)
|
||||
{
|
||||
{
|
||||
Tree t = parse_in_arena("[0, 1, 2, 3, 4]");
|
||||
Tree &m = t;
|
||||
Tree const& cm = t;
|
||||
EXPECT_EQ(m[0].val(), "0");
|
||||
EXPECT_EQ(m[1].val(), "1");
|
||||
EXPECT_EQ(m[2].val(), "2");
|
||||
EXPECT_EQ(m[3].val(), "3");
|
||||
EXPECT_EQ(m[4].val(), "4");
|
||||
EXPECT_EQ(cm[0].val(), "0");
|
||||
EXPECT_EQ(cm[1].val(), "1");
|
||||
EXPECT_EQ(cm[2].val(), "2");
|
||||
EXPECT_EQ(cm[3].val(), "3");
|
||||
EXPECT_EQ(cm[4].val(), "4");
|
||||
//
|
||||
EXPECT_TRUE(m[0] == "0");
|
||||
EXPECT_TRUE(m[1] == "1");
|
||||
EXPECT_TRUE(m[2] == "2");
|
||||
EXPECT_TRUE(m[3] == "3");
|
||||
EXPECT_TRUE(m[4] == "4");
|
||||
EXPECT_TRUE(cm[0] == "0");
|
||||
EXPECT_TRUE(cm[1] == "1");
|
||||
EXPECT_TRUE(cm[2] == "2");
|
||||
EXPECT_TRUE(cm[3] == "3");
|
||||
EXPECT_TRUE(cm[4] == "4");
|
||||
//
|
||||
EXPECT_FALSE(m[0] != "0");
|
||||
EXPECT_FALSE(m[1] != "1");
|
||||
EXPECT_FALSE(m[2] != "2");
|
||||
EXPECT_FALSE(m[3] != "3");
|
||||
EXPECT_FALSE(m[4] != "4");
|
||||
EXPECT_FALSE(cm[0] != "0");
|
||||
EXPECT_FALSE(cm[1] != "1");
|
||||
EXPECT_FALSE(cm[2] != "2");
|
||||
EXPECT_FALSE(cm[3] != "3");
|
||||
EXPECT_FALSE(cm[4] != "4");
|
||||
//
|
||||
verify_assertion(t, [&](Tree const&){ return cm[m.capacity()]; });
|
||||
verify_assertion(t, [&](Tree const&){ return cm[NONE]; });
|
||||
verify_assertion(t, [&](Tree const&){ return cm[0][0]; });
|
||||
verify_assertion(t, [&](Tree const&){ return cm["a"]; });
|
||||
}
|
||||
{
|
||||
Tree t = parse_in_arena("{a: 0, b: 1, c: 2, d: 3, e: 4}");
|
||||
Tree &m = t;
|
||||
Tree const& cm = t;
|
||||
EXPECT_EQ(m["a"].val(), "0");
|
||||
EXPECT_EQ(m["b"].val(), "1");
|
||||
EXPECT_EQ(m["c"].val(), "2");
|
||||
EXPECT_EQ(m["d"].val(), "3");
|
||||
EXPECT_EQ(m["e"].val(), "4");
|
||||
EXPECT_EQ(cm["a"].val(), "0");
|
||||
EXPECT_EQ(cm["b"].val(), "1");
|
||||
EXPECT_EQ(cm["c"].val(), "2");
|
||||
EXPECT_EQ(cm["d"].val(), "3");
|
||||
EXPECT_EQ(cm["e"].val(), "4");
|
||||
//
|
||||
EXPECT_TRUE(m["a"] == "0");
|
||||
EXPECT_TRUE(m["b"] == "1");
|
||||
EXPECT_TRUE(m["c"] == "2");
|
||||
EXPECT_TRUE(m["d"] == "3");
|
||||
EXPECT_TRUE(m["e"] == "4");
|
||||
EXPECT_TRUE(cm["a"] == "0");
|
||||
EXPECT_TRUE(cm["b"] == "1");
|
||||
EXPECT_TRUE(cm["c"] == "2");
|
||||
EXPECT_TRUE(cm["d"] == "3");
|
||||
EXPECT_TRUE(cm["e"] == "4");
|
||||
//
|
||||
EXPECT_FALSE(m["a"] != "0");
|
||||
EXPECT_FALSE(m["b"] != "1");
|
||||
EXPECT_FALSE(m["c"] != "2");
|
||||
EXPECT_FALSE(m["d"] != "3");
|
||||
EXPECT_FALSE(m["e"] != "4");
|
||||
EXPECT_FALSE(cm["a"] != "0");
|
||||
EXPECT_FALSE(cm["b"] != "1");
|
||||
EXPECT_FALSE(cm["c"] != "2");
|
||||
EXPECT_FALSE(cm["d"] != "3");
|
||||
EXPECT_FALSE(cm["e"] != "4");
|
||||
//
|
||||
verify_assertion(t, [&](Tree const&){ return cm["f"]; });
|
||||
verify_assertion(t, [&](Tree const&){ return cm["g"]["h"]; });
|
||||
}
|
||||
Tree t = parse_in_arena("[0, 1, 2, 3, 4]");
|
||||
Tree &m = t;
|
||||
Tree const& cm = t;
|
||||
EXPECT_EQ(m[0].val(), "0");
|
||||
EXPECT_EQ(m[1].val(), "1");
|
||||
EXPECT_EQ(m[2].val(), "2");
|
||||
EXPECT_EQ(m[3].val(), "3");
|
||||
EXPECT_EQ(m[4].val(), "4");
|
||||
EXPECT_EQ(cm[0].val(), "0");
|
||||
EXPECT_EQ(cm[1].val(), "1");
|
||||
EXPECT_EQ(cm[2].val(), "2");
|
||||
EXPECT_EQ(cm[3].val(), "3");
|
||||
EXPECT_EQ(cm[4].val(), "4");
|
||||
//
|
||||
EXPECT_TRUE(m[0] == "0");
|
||||
EXPECT_TRUE(m[1] == "1");
|
||||
EXPECT_TRUE(m[2] == "2");
|
||||
EXPECT_TRUE(m[3] == "3");
|
||||
EXPECT_TRUE(m[4] == "4");
|
||||
EXPECT_TRUE(cm[0] == "0");
|
||||
EXPECT_TRUE(cm[1] == "1");
|
||||
EXPECT_TRUE(cm[2] == "2");
|
||||
EXPECT_TRUE(cm[3] == "3");
|
||||
EXPECT_TRUE(cm[4] == "4");
|
||||
//
|
||||
EXPECT_FALSE(m[0] != "0");
|
||||
EXPECT_FALSE(m[1] != "1");
|
||||
EXPECT_FALSE(m[2] != "2");
|
||||
EXPECT_FALSE(m[3] != "3");
|
||||
EXPECT_FALSE(m[4] != "4");
|
||||
EXPECT_FALSE(cm[0] != "0");
|
||||
EXPECT_FALSE(cm[1] != "1");
|
||||
EXPECT_FALSE(cm[2] != "2");
|
||||
EXPECT_FALSE(cm[3] != "3");
|
||||
EXPECT_FALSE(cm[4] != "4");
|
||||
//
|
||||
verify_assertion(t, [&](Tree const&){ return cm[m.capacity()]; });
|
||||
verify_assertion(t, [&](Tree const&){ return cm[NONE]; });
|
||||
verify_assertion(t, [&](Tree const&){ return cm[0][0]; });
|
||||
verify_assertion(t, [&](Tree const&){ return cm["a"]; });
|
||||
}
|
||||
|
||||
TEST(Tree, noderef_at)
|
||||
TEST(Tree, operator_square_brackets_map)
|
||||
{
|
||||
{
|
||||
Tree t = parse_in_arena("[0, 1, 2, 3, 4]");
|
||||
NodeRef m = t.rootref();
|
||||
ConstNodeRef const cm = t.rootref();
|
||||
EXPECT_EQ(m.at(0).val(), "0");
|
||||
EXPECT_EQ(m.at(1).val(), "1");
|
||||
EXPECT_EQ(m.at(2).val(), "2");
|
||||
EXPECT_EQ(m.at(3).val(), "3");
|
||||
EXPECT_EQ(m.at(4).val(), "4");
|
||||
EXPECT_EQ(cm.at(0).val(), "0");
|
||||
EXPECT_EQ(cm.at(1).val(), "1");
|
||||
EXPECT_EQ(cm.at(2).val(), "2");
|
||||
EXPECT_EQ(cm.at(3).val(), "3");
|
||||
EXPECT_EQ(cm.at(4).val(), "4");
|
||||
//
|
||||
EXPECT_TRUE(m.at(0) == "0");
|
||||
EXPECT_TRUE(m.at(1) == "1");
|
||||
EXPECT_TRUE(m.at(2) == "2");
|
||||
EXPECT_TRUE(m.at(3) == "3");
|
||||
EXPECT_TRUE(m.at(4) == "4");
|
||||
EXPECT_TRUE(cm.at(0) == "0");
|
||||
EXPECT_TRUE(cm.at(1) == "1");
|
||||
EXPECT_TRUE(cm.at(2) == "2");
|
||||
EXPECT_TRUE(cm.at(3) == "3");
|
||||
EXPECT_TRUE(cm.at(4) == "4");
|
||||
//
|
||||
EXPECT_FALSE(m.at(0) != "0");
|
||||
EXPECT_FALSE(m.at(1) != "1");
|
||||
EXPECT_FALSE(m.at(2) != "2");
|
||||
EXPECT_FALSE(m.at(3) != "3");
|
||||
EXPECT_FALSE(m.at(4) != "4");
|
||||
EXPECT_FALSE(cm.at(0) != "0");
|
||||
EXPECT_FALSE(cm.at(1) != "1");
|
||||
EXPECT_FALSE(cm.at(2) != "2");
|
||||
EXPECT_FALSE(cm.at(3) != "3");
|
||||
EXPECT_FALSE(cm.at(4) != "4");
|
||||
//
|
||||
//TODO: Not sure what to replace with capacity
|
||||
//verify_error(t, [&](Tree const&){ return cm[m.capacity()]; });
|
||||
verify_error(t, [&](Tree const&){ return cm.at(NONE); });
|
||||
verify_error(t, [&](Tree const&){ return cm.at(0).at(0); });
|
||||
verify_error(t, [&](Tree const&){ return cm.at("a"); });
|
||||
}
|
||||
{
|
||||
Tree t = parse_in_arena("{a: 0, b: 1, c: 2, d: 3, e: 4}");
|
||||
NodeRef m = t.rootref();
|
||||
ConstNodeRef const cm = t.rootref();
|
||||
EXPECT_EQ(m.at("a").val(), "0");
|
||||
EXPECT_EQ(m.at("b").val(), "1");
|
||||
EXPECT_EQ(m.at("c").val(), "2");
|
||||
EXPECT_EQ(m.at("d").val(), "3");
|
||||
EXPECT_EQ(m.at("e").val(), "4");
|
||||
EXPECT_EQ(cm.at("a").val(), "0");
|
||||
EXPECT_EQ(cm.at("b").val(), "1");
|
||||
EXPECT_EQ(cm.at("c").val(), "2");
|
||||
EXPECT_EQ(cm.at("d").val(), "3");
|
||||
EXPECT_EQ(cm.at("e").val(), "4");
|
||||
//
|
||||
EXPECT_TRUE(m.at("a") == "0");
|
||||
EXPECT_TRUE(m.at("b") == "1");
|
||||
EXPECT_TRUE(m.at("c") == "2");
|
||||
EXPECT_TRUE(m.at("d") == "3");
|
||||
EXPECT_TRUE(m.at("e") == "4");
|
||||
EXPECT_TRUE(cm.at("a") == "0");
|
||||
EXPECT_TRUE(cm.at("b") == "1");
|
||||
EXPECT_TRUE(cm.at("c") == "2");
|
||||
EXPECT_TRUE(cm.at("d") == "3");
|
||||
EXPECT_TRUE(cm.at("e") == "4");
|
||||
//
|
||||
EXPECT_FALSE(m.at("a") != "0");
|
||||
EXPECT_FALSE(m.at("b") != "1");
|
||||
EXPECT_FALSE(m.at("c") != "2");
|
||||
EXPECT_FALSE(m.at("d") != "3");
|
||||
EXPECT_FALSE(m.at("e") != "4");
|
||||
EXPECT_FALSE(cm.at("a") != "0");
|
||||
EXPECT_FALSE(cm.at("b") != "1");
|
||||
EXPECT_FALSE(cm.at("c") != "2");
|
||||
EXPECT_FALSE(cm.at("d") != "3");
|
||||
EXPECT_FALSE(cm.at("e") != "4");
|
||||
//
|
||||
verify_error(t, [&](Tree const&){ return cm.at("f"); });
|
||||
verify_error(t, [&](Tree const&){ return cm.at("g").at("h"); });
|
||||
}
|
||||
Tree t = parse_in_arena("{a: 0, b: 1, c: 2, d: 3, e: 4}");
|
||||
Tree &m = t;
|
||||
Tree const& cm = t;
|
||||
EXPECT_EQ(m["a"].val(), "0");
|
||||
EXPECT_EQ(m["b"].val(), "1");
|
||||
EXPECT_EQ(m["c"].val(), "2");
|
||||
EXPECT_EQ(m["d"].val(), "3");
|
||||
EXPECT_EQ(m["e"].val(), "4");
|
||||
EXPECT_EQ(cm["a"].val(), "0");
|
||||
EXPECT_EQ(cm["b"].val(), "1");
|
||||
EXPECT_EQ(cm["c"].val(), "2");
|
||||
EXPECT_EQ(cm["d"].val(), "3");
|
||||
EXPECT_EQ(cm["e"].val(), "4");
|
||||
//
|
||||
EXPECT_TRUE(m["a"] == "0");
|
||||
EXPECT_TRUE(m["b"] == "1");
|
||||
EXPECT_TRUE(m["c"] == "2");
|
||||
EXPECT_TRUE(m["d"] == "3");
|
||||
EXPECT_TRUE(m["e"] == "4");
|
||||
EXPECT_TRUE(cm["a"] == "0");
|
||||
EXPECT_TRUE(cm["b"] == "1");
|
||||
EXPECT_TRUE(cm["c"] == "2");
|
||||
EXPECT_TRUE(cm["d"] == "3");
|
||||
EXPECT_TRUE(cm["e"] == "4");
|
||||
//
|
||||
EXPECT_FALSE(m["a"] != "0");
|
||||
EXPECT_FALSE(m["b"] != "1");
|
||||
EXPECT_FALSE(m["c"] != "2");
|
||||
EXPECT_FALSE(m["d"] != "3");
|
||||
EXPECT_FALSE(m["e"] != "4");
|
||||
EXPECT_FALSE(cm["a"] != "0");
|
||||
EXPECT_FALSE(cm["b"] != "1");
|
||||
EXPECT_FALSE(cm["c"] != "2");
|
||||
EXPECT_FALSE(cm["d"] != "3");
|
||||
EXPECT_FALSE(cm["e"] != "4");
|
||||
//
|
||||
verify_assertion(t, [&](Tree const&){ return cm["f"]; });
|
||||
verify_assertion(t, [&](Tree const&){ return cm["g"]["h"]; });
|
||||
}
|
||||
|
||||
TEST(Tree, noderef_at_seq)
|
||||
{
|
||||
Tree t = parse_in_arena("[0, 1, 2, 3, 4]");
|
||||
NodeRef m = t.rootref();
|
||||
ConstNodeRef const cm = t.rootref();
|
||||
EXPECT_EQ(m.at(0).val(), "0");
|
||||
EXPECT_EQ(m.at(1).val(), "1");
|
||||
EXPECT_EQ(m.at(2).val(), "2");
|
||||
EXPECT_EQ(m.at(3).val(), "3");
|
||||
EXPECT_EQ(m.at(4).val(), "4");
|
||||
EXPECT_EQ(cm.at(0).val(), "0");
|
||||
EXPECT_EQ(cm.at(1).val(), "1");
|
||||
EXPECT_EQ(cm.at(2).val(), "2");
|
||||
EXPECT_EQ(cm.at(3).val(), "3");
|
||||
EXPECT_EQ(cm.at(4).val(), "4");
|
||||
//
|
||||
EXPECT_TRUE(m.at(0) == "0");
|
||||
EXPECT_TRUE(m.at(1) == "1");
|
||||
EXPECT_TRUE(m.at(2) == "2");
|
||||
EXPECT_TRUE(m.at(3) == "3");
|
||||
EXPECT_TRUE(m.at(4) == "4");
|
||||
EXPECT_TRUE(cm.at(0) == "0");
|
||||
EXPECT_TRUE(cm.at(1) == "1");
|
||||
EXPECT_TRUE(cm.at(2) == "2");
|
||||
EXPECT_TRUE(cm.at(3) == "3");
|
||||
EXPECT_TRUE(cm.at(4) == "4");
|
||||
//
|
||||
EXPECT_FALSE(m.at(0) != "0");
|
||||
EXPECT_FALSE(m.at(1) != "1");
|
||||
EXPECT_FALSE(m.at(2) != "2");
|
||||
EXPECT_FALSE(m.at(3) != "3");
|
||||
EXPECT_FALSE(m.at(4) != "4");
|
||||
EXPECT_FALSE(cm.at(0) != "0");
|
||||
EXPECT_FALSE(cm.at(1) != "1");
|
||||
EXPECT_FALSE(cm.at(2) != "2");
|
||||
EXPECT_FALSE(cm.at(3) != "3");
|
||||
EXPECT_FALSE(cm.at(4) != "4");
|
||||
//
|
||||
EXPECT_EQ(cm.num_children(), 5);
|
||||
EXPECT_EQ(cm.num_children(), m.num_children());
|
||||
//
|
||||
verify_error(t, [&](Tree const&){ return cm.at(NONE); });
|
||||
verify_error(t, [&](Tree const&){ return cm.at(t.capacity()); });
|
||||
verify_error(t, [&](Tree const&){ return cm.at(5); });
|
||||
verify_error(t, [&](Tree const&){ return cm.at(6); });
|
||||
verify_error(t, [&](Tree const&){ return cm.at(7); });
|
||||
verify_error(t, [&](Tree const&){ return cm.at(10); });
|
||||
verify_error(t, [&](Tree const&){ return cm.at(0).at(0); });
|
||||
verify_error(t, [&](Tree const&){ return cm.at("a"); });
|
||||
//
|
||||
verify_error(t, [&](Tree const&){ return m.at(NONE); });
|
||||
verify_error(t, [&](Tree const&){ return m.at(t.capacity()); });
|
||||
verify_success(t, [&](Tree const&){ return m.at(5); });
|
||||
verify_success(t, [&](Tree const&){ return m.at(6); });
|
||||
verify_error(t, [&](Tree const&){ return m.at(0).at(0); });
|
||||
verify_error(t, [&](Tree const&){ return m.at("a"); });
|
||||
EXPECT_TRUE(m.at(5).is_seed());
|
||||
EXPECT_TRUE(m.at(6).is_seed());
|
||||
//
|
||||
NodeRef to_be_removed_orig = m.at(4);
|
||||
NodeRef to_be_removed = to_be_removed_orig;
|
||||
EXPECT_EQ(to_be_removed.id(), 5);
|
||||
EXPECT_EQ(to_be_removed_orig.id(), 5);
|
||||
m.remove_child(to_be_removed);
|
||||
EXPECT_EQ(to_be_removed.id(), 5); // it is stale now
|
||||
EXPECT_EQ(to_be_removed_orig.id(), 5); // it is stale now
|
||||
EXPECT_EQ(m.num_children(), 4);
|
||||
EXPECT_TRUE(m.at(4).is_seed());
|
||||
verify_success(t, [&](Tree const&){ return m.at(4); });
|
||||
verify_error(t, [&](Tree const&){ return cm.at(4); });
|
||||
}
|
||||
|
||||
TEST(Tree, noderef_at_map)
|
||||
{
|
||||
Tree t = parse_in_arena("{a: 0, b: 1, c: 2, d: 3, e: 4}");
|
||||
NodeRef m = t.rootref();
|
||||
ConstNodeRef const cm = t.rootref();
|
||||
EXPECT_EQ(m.at("a").val(), "0");
|
||||
EXPECT_EQ(m.at("b").val(), "1");
|
||||
EXPECT_EQ(m.at("c").val(), "2");
|
||||
EXPECT_EQ(m.at("d").val(), "3");
|
||||
EXPECT_EQ(m.at("e").val(), "4");
|
||||
EXPECT_EQ(cm.at("a").val(), "0");
|
||||
EXPECT_EQ(cm.at("b").val(), "1");
|
||||
EXPECT_EQ(cm.at("c").val(), "2");
|
||||
EXPECT_EQ(cm.at("d").val(), "3");
|
||||
EXPECT_EQ(cm.at("e").val(), "4");
|
||||
//
|
||||
EXPECT_TRUE(m.at("a") == "0");
|
||||
EXPECT_TRUE(m.at("b") == "1");
|
||||
EXPECT_TRUE(m.at("c") == "2");
|
||||
EXPECT_TRUE(m.at("d") == "3");
|
||||
EXPECT_TRUE(m.at("e") == "4");
|
||||
EXPECT_TRUE(cm.at("a") == "0");
|
||||
EXPECT_TRUE(cm.at("b") == "1");
|
||||
EXPECT_TRUE(cm.at("c") == "2");
|
||||
EXPECT_TRUE(cm.at("d") == "3");
|
||||
EXPECT_TRUE(cm.at("e") == "4");
|
||||
//
|
||||
EXPECT_FALSE(m.at("a") != "0");
|
||||
EXPECT_FALSE(m.at("b") != "1");
|
||||
EXPECT_FALSE(m.at("c") != "2");
|
||||
EXPECT_FALSE(m.at("d") != "3");
|
||||
EXPECT_FALSE(m.at("e") != "4");
|
||||
EXPECT_FALSE(cm.at("a") != "0");
|
||||
EXPECT_FALSE(cm.at("b") != "1");
|
||||
EXPECT_FALSE(cm.at("c") != "2");
|
||||
EXPECT_FALSE(cm.at("d") != "3");
|
||||
EXPECT_FALSE(cm.at("e") != "4");
|
||||
//
|
||||
verify_error(t, [&](Tree const&){ return cm.at(t.capacity()); });
|
||||
verify_error(t, [&](Tree const&){ return cm.at(NONE); });
|
||||
verify_error(t, [&](Tree const&){ return cm.at(cm.num_children()); });
|
||||
verify_error(t, [&](Tree const&){ return cm.at(5); });
|
||||
verify_error(t, [&](Tree const&){ return cm.at(6); });
|
||||
verify_error(t, [&](Tree const&){ return cm.at("f"); });
|
||||
verify_error(t, [&](Tree const&){ return cm.at("g").at("h"); });
|
||||
//
|
||||
verify_error(t, [&](Tree const&){ return m.at(t.capacity()); });
|
||||
verify_error(t, [&](Tree const&){ return m.at(NONE); });
|
||||
verify_success(t, [&](Tree const&){ return m.at(cm.num_children()); });
|
||||
verify_success(t, [&](Tree const&){ return m.at(5); });
|
||||
verify_success(t, [&](Tree const&){ return m.at(6); });
|
||||
verify_success(t, [&](Tree const&){ return m.at("f"); });
|
||||
verify_error(t, [&](Tree const&){ return m.at("g").at("h"); });
|
||||
EXPECT_TRUE(m.at(cm.num_children()).is_seed());
|
||||
EXPECT_TRUE(m.at(5).is_seed());
|
||||
EXPECT_TRUE(m.at(6).is_seed());
|
||||
EXPECT_TRUE(m.at("f").is_seed());
|
||||
}
|
||||
|
||||
TEST(Tree, relocate)
|
||||
|
||||
Reference in New Issue
Block a user