summaryrefslogtreecommitdiff
path: root/utils/config/tree_test.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'utils/config/tree_test.cpp')
-rw-r--r--utils/config/tree_test.cpp1086
1 files changed, 1086 insertions, 0 deletions
diff --git a/utils/config/tree_test.cpp b/utils/config/tree_test.cpp
new file mode 100644
index 000000000000..b6efd64a84a6
--- /dev/null
+++ b/utils/config/tree_test.cpp
@@ -0,0 +1,1086 @@
+// Copyright 2012 The Kyua Authors.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of Google Inc. nor the names of its contributors
+// may be used to endorse or promote products derived from this software
+// without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "utils/config/tree.ipp"
+
+#include <atf-c++.hpp>
+
+#include "utils/config/nodes.ipp"
+#include "utils/format/macros.hpp"
+#include "utils/text/operations.ipp"
+
+namespace config = utils::config;
+namespace text = utils::text;
+
+
+namespace {
+
+
+/// Simple wrapper around an integer value without default constructors.
+///
+/// The purpose of this type is to have a simple class without default
+/// constructors to validate that we can use it as a leaf of a tree.
+class int_wrapper {
+ /// The wrapped integer value.
+ int _value;
+
+public:
+ /// Constructs a new wrapped integer.
+ ///
+ /// \param value_ The value to store in the object.
+ explicit int_wrapper(int value_) :
+ _value(value_)
+ {
+ }
+
+ /// \return The integer value stored by the object.
+ int
+ value(void) const
+ {
+ return _value;
+ }
+};
+
+
+/// Custom tree leaf type for an object without defualt constructors.
+class wrapped_int_node : public config::typed_leaf_node< int_wrapper > {
+public:
+ /// Copies the node.
+ ///
+ /// \return A dynamically-allocated node.
+ virtual base_node*
+ deep_copy(void) const
+ {
+ std::auto_ptr< wrapped_int_node > new_node(new wrapped_int_node());
+ new_node->_value = _value;
+ return new_node.release();
+ }
+
+ /// Pushes the node's value onto the Lua stack.
+ ///
+ /// \param state The Lua state onto which to push the value.
+ void
+ push_lua(lutok::state& state) const
+ {
+ state.push_integer(
+ config::typed_leaf_node< int_wrapper >::value().value());
+ }
+
+ /// Sets the value of the node from an entry in the Lua stack.
+ ///
+ /// \param state The Lua state from which to get the value.
+ /// \param value_index The stack index in which the value resides.
+ void
+ set_lua(lutok::state& state, const int value_index)
+ {
+ ATF_REQUIRE(state.is_number(value_index));
+ int_wrapper new_value(state.to_integer(value_index));
+ config::typed_leaf_node< int_wrapper >::set(new_value);
+ }
+
+ /// Sets the value of the node from a raw string representation.
+ ///
+ /// \param raw_value The value to set the node to.
+ void
+ set_string(const std::string& raw_value)
+ {
+ int_wrapper new_value(text::to_type< int >(raw_value));
+ config::typed_leaf_node< int_wrapper >::set(new_value);
+ }
+
+ /// Converts the contents of the node to a string.
+ ///
+ /// \return A string representation of the value held by the node.
+ std::string
+ to_string(void) const
+ {
+ return F("%s") %
+ config::typed_leaf_node< int_wrapper >::value().value();
+ }
+};
+
+
+} // anonymous namespace
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(define_set_lookup__one_level);
+ATF_TEST_CASE_BODY(define_set_lookup__one_level)
+{
+ config::tree tree;
+
+ tree.define< config::int_node >("var1");
+ tree.define< config::string_node >("var2");
+ tree.define< config::bool_node >("var3");
+
+ tree.set< config::int_node >("var1", 42);
+ tree.set< config::string_node >("var2", "hello");
+ tree.set< config::bool_node >("var3", false);
+
+ ATF_REQUIRE_EQ(42, tree.lookup< config::int_node >("var1"));
+ ATF_REQUIRE_EQ("hello", tree.lookup< config::string_node >("var2"));
+ ATF_REQUIRE(!tree.lookup< config::bool_node >("var3"));
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(define_set_lookup__multiple_levels);
+ATF_TEST_CASE_BODY(define_set_lookup__multiple_levels)
+{
+ config::tree tree;
+
+ tree.define< config::int_node >("foo.bar.1");
+ tree.define< config::string_node >("foo.bar.2");
+ tree.define< config::bool_node >("foo.3");
+ tree.define_dynamic("sub.tree");
+
+ tree.set< config::int_node >("foo.bar.1", 42);
+ tree.set< config::string_node >("foo.bar.2", "hello");
+ tree.set< config::bool_node >("foo.3", true);
+ tree.set< config::string_node >("sub.tree.1", "bye");
+ tree.set< config::int_node >("sub.tree.2", 4);
+ tree.set< config::int_node >("sub.tree.3.4", 123);
+
+ ATF_REQUIRE_EQ(42, tree.lookup< config::int_node >("foo.bar.1"));
+ ATF_REQUIRE_EQ("hello", tree.lookup< config::string_node >("foo.bar.2"));
+ ATF_REQUIRE(tree.lookup< config::bool_node >("foo.3"));
+ ATF_REQUIRE_EQ(4, tree.lookup< config::int_node >("sub.tree.2"));
+ ATF_REQUIRE_EQ(123, tree.lookup< config::int_node >("sub.tree.3.4"));
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(deep_copy__empty);
+ATF_TEST_CASE_BODY(deep_copy__empty)
+{
+ config::tree tree1;
+ config::tree tree2 = tree1.deep_copy();
+
+ tree1.define< config::bool_node >("var1");
+ // This would crash if the copy shared the internal data.
+ tree2.define< config::int_node >("var1");
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(deep_copy__some);
+ATF_TEST_CASE_BODY(deep_copy__some)
+{
+ config::tree tree1;
+ tree1.define< config::bool_node >("this.is.a.var");
+ tree1.set< config::bool_node >("this.is.a.var", true);
+ tree1.define< config::int_node >("this.is.another.var");
+ tree1.set< config::int_node >("this.is.another.var", 34);
+ tree1.define< config::int_node >("and.another");
+ tree1.set< config::int_node >("and.another", 123);
+
+ config::tree tree2 = tree1.deep_copy();
+ tree2.set< config::bool_node >("this.is.a.var", false);
+ tree2.set< config::int_node >("this.is.another.var", 43);
+
+ ATF_REQUIRE( tree1.lookup< config::bool_node >("this.is.a.var"));
+ ATF_REQUIRE(!tree2.lookup< config::bool_node >("this.is.a.var"));
+
+ ATF_REQUIRE_EQ(34, tree1.lookup< config::int_node >("this.is.another.var"));
+ ATF_REQUIRE_EQ(43, tree2.lookup< config::int_node >("this.is.another.var"));
+
+ ATF_REQUIRE_EQ(123, tree1.lookup< config::int_node >("and.another"));
+ ATF_REQUIRE_EQ(123, tree2.lookup< config::int_node >("and.another"));
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(combine__empty);
+ATF_TEST_CASE_BODY(combine__empty)
+{
+ const config::tree t1, t2;
+ const config::tree combined = t1.combine(t2);
+
+ const config::tree expected;
+ ATF_REQUIRE(expected == combined);
+}
+
+
+static void
+init_tree_for_combine_test(config::tree& tree)
+{
+ tree.define< config::int_node >("int-node");
+ tree.define< config::string_node >("string-node");
+ tree.define< config::int_node >("unused.node");
+ tree.define< config::int_node >("deeper.int.node");
+ tree.define_dynamic("deeper.dynamic");
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(combine__same_layout__no_overrides);
+ATF_TEST_CASE_BODY(combine__same_layout__no_overrides)
+{
+ config::tree t1, t2;
+ init_tree_for_combine_test(t1);
+ init_tree_for_combine_test(t2);
+ t1.set< config::int_node >("int-node", 3);
+ t1.set< config::string_node >("string-node", "foo");
+ t1.set< config::int_node >("deeper.int.node", 15);
+ t1.set_string("deeper.dynamic.first", "value1");
+ t1.set_string("deeper.dynamic.second", "value2");
+ const config::tree combined = t1.combine(t2);
+
+ ATF_REQUIRE(t1 == combined);
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(combine__same_layout__no_base);
+ATF_TEST_CASE_BODY(combine__same_layout__no_base)
+{
+ config::tree t1, t2;
+ init_tree_for_combine_test(t1);
+ init_tree_for_combine_test(t2);
+ t2.set< config::int_node >("int-node", 3);
+ t2.set< config::string_node >("string-node", "foo");
+ t2.set< config::int_node >("deeper.int.node", 15);
+ t2.set_string("deeper.dynamic.first", "value1");
+ t2.set_string("deeper.dynamic.second", "value2");
+ const config::tree combined = t1.combine(t2);
+
+ ATF_REQUIRE(t2 == combined);
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(combine__same_layout__mix);
+ATF_TEST_CASE_BODY(combine__same_layout__mix)
+{
+ config::tree t1, t2;
+ init_tree_for_combine_test(t1);
+ init_tree_for_combine_test(t2);
+ t1.set< config::int_node >("int-node", 3);
+ t2.set< config::int_node >("int-node", 5);
+ t1.set< config::string_node >("string-node", "foo");
+ t2.set< config::int_node >("deeper.int.node", 15);
+ t1.set_string("deeper.dynamic.first", "value1");
+ t1.set_string("deeper.dynamic.second", "value2.1");
+ t2.set_string("deeper.dynamic.second", "value2.2");
+ t2.set_string("deeper.dynamic.third", "value3");
+ const config::tree combined = t1.combine(t2);
+
+ config::tree expected;
+ init_tree_for_combine_test(expected);
+ expected.set< config::int_node >("int-node", 5);
+ expected.set< config::string_node >("string-node", "foo");
+ expected.set< config::int_node >("deeper.int.node", 15);
+ expected.set_string("deeper.dynamic.first", "value1");
+ expected.set_string("deeper.dynamic.second", "value2.2");
+ expected.set_string("deeper.dynamic.third", "value3");
+ ATF_REQUIRE(expected == combined);
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(combine__different_layout);
+ATF_TEST_CASE_BODY(combine__different_layout)
+{
+ config::tree t1;
+ t1.define< config::int_node >("common.base1");
+ t1.define< config::int_node >("common.base2");
+ t1.define_dynamic("dynamic.base");
+ t1.define< config::int_node >("unset.base");
+
+ config::tree t2;
+ t2.define< config::int_node >("common.base2");
+ t2.define< config::int_node >("common.base3");
+ t2.define_dynamic("dynamic.other");
+ t2.define< config::int_node >("unset.other");
+
+ t1.set< config::int_node >("common.base1", 1);
+ t1.set< config::int_node >("common.base2", 2);
+ t1.set_string("dynamic.base.first", "foo");
+ t1.set_string("dynamic.base.second", "bar");
+
+ t2.set< config::int_node >("common.base2", 4);
+ t2.set< config::int_node >("common.base3", 3);
+ t2.set_string("dynamic.other.first", "FOO");
+ t2.set_string("dynamic.other.second", "BAR");
+
+ config::tree combined = t1.combine(t2);
+
+ config::tree expected;
+ expected.define< config::int_node >("common.base1");
+ expected.define< config::int_node >("common.base2");
+ expected.define< config::int_node >("common.base3");
+ expected.define_dynamic("dynamic.base");
+ expected.define_dynamic("dynamic.other");
+ expected.define< config::int_node >("unset.base");
+ expected.define< config::int_node >("unset.other");
+
+ expected.set< config::int_node >("common.base1", 1);
+ expected.set< config::int_node >("common.base2", 4);
+ expected.set< config::int_node >("common.base3", 3);
+ expected.set_string("dynamic.base.first", "foo");
+ expected.set_string("dynamic.base.second", "bar");
+ expected.set_string("dynamic.other.first", "FOO");
+ expected.set_string("dynamic.other.second", "BAR");
+
+ ATF_REQUIRE(expected == combined);
+
+ // The combined tree should have respected existing but unset nodes. Check
+ // that these calls do not crash.
+ combined.set< config::int_node >("unset.base", 5);
+ combined.set< config::int_node >("unset.other", 5);
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(combine__dynamic_wins);
+ATF_TEST_CASE_BODY(combine__dynamic_wins)
+{
+ config::tree t1;
+ t1.define< config::int_node >("inner.leaf1");
+ t1.set< config::int_node >("inner.leaf1", 3);
+
+ config::tree t2;
+ t2.define_dynamic("inner");
+ t2.set_string("inner.leaf2", "4");
+
+ config::tree combined = t1.combine(t2);
+
+ config::tree expected;
+ expected.define_dynamic("inner");
+ expected.set_string("inner.leaf1", "3");
+ expected.set_string("inner.leaf2", "4");
+
+ ATF_REQUIRE(expected == combined);
+
+ // The combined inner node should have become dynamic so this call should
+ // not fail.
+ combined.set_string("inner.leaf3", "5");
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(combine__inner_leaf_mismatch);
+ATF_TEST_CASE_BODY(combine__inner_leaf_mismatch)
+{
+ config::tree t1;
+ t1.define< config::int_node >("top.foo.bar");
+
+ config::tree t2;
+ t2.define< config::int_node >("top.foo");
+
+ ATF_REQUIRE_THROW_RE(config::bad_combination_error,
+ "'top.foo' is an inner node in the base tree but a "
+ "leaf node in the overrides tree",
+ t1.combine(t2));
+
+ ATF_REQUIRE_THROW_RE(config::bad_combination_error,
+ "'top.foo' is a leaf node in the base tree but an "
+ "inner node in the overrides tree",
+ t2.combine(t1));
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(lookup__invalid_key);
+ATF_TEST_CASE_BODY(lookup__invalid_key)
+{
+ config::tree tree;
+
+ ATF_REQUIRE_THROW(config::invalid_key_error,
+ tree.lookup< config::int_node >("."));
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(lookup__unknown_key);
+ATF_TEST_CASE_BODY(lookup__unknown_key)
+{
+ config::tree tree;
+
+ tree.define< config::int_node >("foo.bar");
+ tree.define< config::int_node >("a.b.c");
+ tree.define_dynamic("a.d");
+ tree.set< config::int_node >("a.b.c", 123);
+ tree.set< config::int_node >("a.d.100", 0);
+
+ ATF_REQUIRE_THROW(config::unknown_key_error,
+ tree.lookup< config::int_node >("abc"));
+
+ ATF_REQUIRE_THROW(config::unknown_key_error,
+ tree.lookup< config::int_node >("foo"));
+ ATF_REQUIRE_THROW(config::unknown_key_error,
+ tree.lookup< config::int_node >("foo.bar"));
+ ATF_REQUIRE_THROW(config::unknown_key_error,
+ tree.lookup< config::int_node >("foo.bar.baz"));
+
+ ATF_REQUIRE_THROW(config::unknown_key_error,
+ tree.lookup< config::int_node >("a"));
+ ATF_REQUIRE_THROW(config::unknown_key_error,
+ tree.lookup< config::int_node >("a.b"));
+ ATF_REQUIRE_THROW(config::unknown_key_error,
+ tree.lookup< config::int_node >("a.c"));
+ (void)tree.lookup< config::int_node >("a.b.c");
+ ATF_REQUIRE_THROW(config::unknown_key_error,
+ tree.lookup< config::int_node >("a.b.c.d"));
+ ATF_REQUIRE_THROW(config::unknown_key_error,
+ tree.lookup< config::int_node >("a.d"));
+ (void)tree.lookup< config::int_node >("a.d.100");
+ ATF_REQUIRE_THROW(config::unknown_key_error,
+ tree.lookup< config::int_node >("a.d.101"));
+ ATF_REQUIRE_THROW(config::unknown_key_error,
+ tree.lookup< config::int_node >("a.d.100.3"));
+ ATF_REQUIRE_THROW(config::unknown_key_error,
+ tree.lookup< config::int_node >("a.d.e"));
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(is_set__one_level);
+ATF_TEST_CASE_BODY(is_set__one_level)
+{
+ config::tree tree;
+
+ tree.define< config::int_node >("var1");
+ tree.define< config::string_node >("var2");
+ tree.define< config::bool_node >("var3");
+
+ tree.set< config::int_node >("var1", 42);
+ tree.set< config::bool_node >("var3", false);
+
+ ATF_REQUIRE( tree.is_set("var1"));
+ ATF_REQUIRE(!tree.is_set("var2"));
+ ATF_REQUIRE( tree.is_set("var3"));
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(is_set__multiple_levels);
+ATF_TEST_CASE_BODY(is_set__multiple_levels)
+{
+ config::tree tree;
+
+ tree.define< config::int_node >("a.b.var1");
+ tree.define< config::string_node >("a.b.var2");
+ tree.define< config::bool_node >("e.var3");
+
+ tree.set< config::int_node >("a.b.var1", 42);
+ tree.set< config::bool_node >("e.var3", false);
+
+ ATF_REQUIRE(!tree.is_set("a"));
+ ATF_REQUIRE(!tree.is_set("a.b"));
+ ATF_REQUIRE( tree.is_set("a.b.var1"));
+ ATF_REQUIRE(!tree.is_set("a.b.var1.trailing"));
+
+ ATF_REQUIRE(!tree.is_set("a"));
+ ATF_REQUIRE(!tree.is_set("a.b"));
+ ATF_REQUIRE(!tree.is_set("a.b.var2"));
+ ATF_REQUIRE(!tree.is_set("a.b.var2.trailing"));
+
+ ATF_REQUIRE(!tree.is_set("e"));
+ ATF_REQUIRE( tree.is_set("e.var3"));
+ ATF_REQUIRE(!tree.is_set("e.var3.trailing"));
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(is_set__invalid_key);
+ATF_TEST_CASE_BODY(is_set__invalid_key)
+{
+ config::tree tree;
+
+ ATF_REQUIRE_THROW(config::invalid_key_error, tree.is_set(".abc"));
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(set__invalid_key);
+ATF_TEST_CASE_BODY(set__invalid_key)
+{
+ config::tree tree;
+
+ ATF_REQUIRE_THROW(config::invalid_key_error,
+ tree.set< config::int_node >("foo.", 54));
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(set__invalid_key_value);
+ATF_TEST_CASE_BODY(set__invalid_key_value)
+{
+ config::tree tree;
+
+ tree.define< config::int_node >("foo.bar");
+ tree.define_dynamic("a.d");
+
+ ATF_REQUIRE_THROW(config::invalid_key_value,
+ tree.set< config::int_node >("foo", 3));
+ ATF_REQUIRE_THROW(config::invalid_key_value,
+ tree.set< config::int_node >("a", -10));
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(set__unknown_key);
+ATF_TEST_CASE_BODY(set__unknown_key)
+{
+ config::tree tree;
+
+ tree.define< config::int_node >("foo.bar");
+ tree.define< config::int_node >("a.b.c");
+ tree.define_dynamic("a.d");
+ tree.set< config::int_node >("a.b.c", 123);
+ tree.set< config::string_node >("a.d.3", "foo");
+
+ ATF_REQUIRE_THROW(config::unknown_key_error,
+ tree.set< config::int_node >("abc", 2));
+
+ tree.set< config::int_node >("foo.bar", 15);
+ ATF_REQUIRE_THROW(config::unknown_key_error,
+ tree.set< config::int_node >("foo.bar.baz", 0));
+
+ ATF_REQUIRE_THROW(config::unknown_key_error,
+ tree.set< config::int_node >("a.c", 100));
+ tree.set< config::int_node >("a.b.c", -3);
+ ATF_REQUIRE_THROW(config::unknown_key_error,
+ tree.set< config::int_node >("a.b.c.d", 82));
+ tree.set< config::string_node >("a.d.3", "bar");
+ tree.set< config::string_node >("a.d.4", "bar");
+ ATF_REQUIRE_THROW(config::unknown_key_error,
+ tree.set< config::int_node >("a.d.4.5", 82));
+ tree.set< config::int_node >("a.d.5.6", 82);
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(set__unknown_key_not_strict);
+ATF_TEST_CASE_BODY(set__unknown_key_not_strict)
+{
+ config::tree tree(false);
+
+ tree.define< config::int_node >("foo.bar");
+ tree.define< config::int_node >("a.b.c");
+ tree.define_dynamic("a.d");
+ tree.set< config::int_node >("a.b.c", 123);
+ tree.set< config::string_node >("a.d.3", "foo");
+
+ tree.set< config::int_node >("abc", 2);
+ ATF_REQUIRE(!tree.is_set("abc"));
+
+ tree.set< config::int_node >("foo.bar", 15);
+ tree.set< config::int_node >("foo.bar.baz", 0);
+ ATF_REQUIRE(!tree.is_set("foo.bar.baz"));
+
+ tree.set< config::int_node >("a.c", 100);
+ ATF_REQUIRE(!tree.is_set("a.c"));
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(push_lua__ok);
+ATF_TEST_CASE_BODY(push_lua__ok)
+{
+ config::tree tree;
+
+ tree.define< config::int_node >("top.integer");
+ tree.define< wrapped_int_node >("top.custom");
+ tree.define_dynamic("dynamic");
+ tree.set< config::int_node >("top.integer", 5);
+ tree.set< wrapped_int_node >("top.custom", int_wrapper(10));
+ tree.set_string("dynamic.first", "foo");
+
+ lutok::state state;
+ tree.push_lua("top.integer", state);
+ tree.push_lua("top.custom", state);
+ tree.push_lua("dynamic.first", state);
+ ATF_REQUIRE(state.is_number(-3));
+ ATF_REQUIRE_EQ(5, state.to_integer(-3));
+ ATF_REQUIRE(state.is_number(-2));
+ ATF_REQUIRE_EQ(10, state.to_integer(-2));
+ ATF_REQUIRE(state.is_string(-1));
+ ATF_REQUIRE_EQ("foo", state.to_string(-1));
+ state.pop(3);
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(set_lua__ok);
+ATF_TEST_CASE_BODY(set_lua__ok)
+{
+ config::tree tree;
+
+ tree.define< config::int_node >("top.integer");
+ tree.define< wrapped_int_node >("top.custom");
+ tree.define_dynamic("dynamic");
+
+ {
+ lutok::state state;
+ state.push_integer(5);
+ state.push_integer(10);
+ state.push_string("foo");
+ tree.set_lua("top.integer", state, -3);
+ tree.set_lua("top.custom", state, -2);
+ tree.set_lua("dynamic.first", state, -1);
+ state.pop(3);
+ }
+
+ ATF_REQUIRE_EQ(5, tree.lookup< config::int_node >("top.integer"));
+ ATF_REQUIRE_EQ(10, tree.lookup< wrapped_int_node >("top.custom").value());
+ ATF_REQUIRE_EQ("foo", tree.lookup< config::string_node >("dynamic.first"));
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(lookup_rw);
+ATF_TEST_CASE_BODY(lookup_rw)
+{
+ config::tree tree;
+
+ tree.define< config::int_node >("var1");
+ tree.define< config::bool_node >("var3");
+
+ tree.set< config::int_node >("var1", 42);
+ tree.set< config::bool_node >("var3", false);
+
+ tree.lookup_rw< config::int_node >("var1") += 10;
+ ATF_REQUIRE_EQ(52, tree.lookup< config::int_node >("var1"));
+ ATF_REQUIRE(!tree.lookup< config::bool_node >("var3"));
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(lookup_string__ok);
+ATF_TEST_CASE_BODY(lookup_string__ok)
+{
+ config::tree tree;
+
+ tree.define< config::int_node >("var1");
+ tree.define< config::string_node >("b.var2");
+ tree.define< config::bool_node >("c.d.var3");
+
+ tree.set< config::int_node >("var1", 42);
+ tree.set< config::string_node >("b.var2", "hello");
+ tree.set< config::bool_node >("c.d.var3", false);
+
+ ATF_REQUIRE_EQ("42", tree.lookup_string("var1"));
+ ATF_REQUIRE_EQ("hello", tree.lookup_string("b.var2"));
+ ATF_REQUIRE_EQ("false", tree.lookup_string("c.d.var3"));
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(lookup_string__invalid_key);
+ATF_TEST_CASE_BODY(lookup_string__invalid_key)
+{
+ config::tree tree;
+
+ ATF_REQUIRE_THROW(config::invalid_key_error, tree.lookup_string(""));
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(lookup_string__unknown_key);
+ATF_TEST_CASE_BODY(lookup_string__unknown_key)
+{
+ config::tree tree;
+
+ tree.define< config::int_node >("a.b.c");
+
+ ATF_REQUIRE_THROW(config::unknown_key_error, tree.lookup_string("a.b"));
+ ATF_REQUIRE_THROW(config::unknown_key_error, tree.lookup_string("a.b.c.d"));
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(set_string__ok);
+ATF_TEST_CASE_BODY(set_string__ok)
+{
+ config::tree tree;
+
+ tree.define< config::int_node >("foo.bar.1");
+ tree.define< config::string_node >("foo.bar.2");
+ tree.define_dynamic("sub.tree");
+
+ tree.set_string("foo.bar.1", "42");
+ tree.set_string("foo.bar.2", "hello");
+ tree.set_string("sub.tree.2", "15");
+ tree.set_string("sub.tree.3.4", "bye");
+
+ ATF_REQUIRE_EQ(42, tree.lookup< config::int_node >("foo.bar.1"));
+ ATF_REQUIRE_EQ("hello", tree.lookup< config::string_node >("foo.bar.2"));
+ ATF_REQUIRE_EQ("15", tree.lookup< config::string_node >("sub.tree.2"));
+ ATF_REQUIRE_EQ("bye", tree.lookup< config::string_node >("sub.tree.3.4"));
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(set_string__invalid_key);
+ATF_TEST_CASE_BODY(set_string__invalid_key)
+{
+ config::tree tree;
+
+ ATF_REQUIRE_THROW(config::invalid_key_error, tree.set_string(".", "foo"));
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(set_string__invalid_key_value);
+ATF_TEST_CASE_BODY(set_string__invalid_key_value)
+{
+ config::tree tree;
+
+ tree.define< config::int_node >("foo.bar");
+
+ ATF_REQUIRE_THROW(config::invalid_key_value,
+ tree.set_string("foo", "abc"));
+ ATF_REQUIRE_THROW(config::invalid_key_value,
+ tree.set_string("foo.bar", " -3"));
+ ATF_REQUIRE_THROW(config::invalid_key_value,
+ tree.set_string("foo.bar", "3 "));
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(set_string__unknown_key);
+ATF_TEST_CASE_BODY(set_string__unknown_key)
+{
+ config::tree tree;
+
+ tree.define< config::int_node >("foo.bar");
+ tree.define< config::int_node >("a.b.c");
+ tree.define_dynamic("a.d");
+ tree.set_string("a.b.c", "123");
+ tree.set_string("a.d.3", "foo");
+
+ ATF_REQUIRE_THROW(config::unknown_key_error, tree.set_string("abc", "2"));
+
+ tree.set_string("foo.bar", "15");
+ ATF_REQUIRE_THROW(config::unknown_key_error,
+ tree.set_string("foo.bar.baz", "0"));
+
+ ATF_REQUIRE_THROW(config::unknown_key_error,
+ tree.set_string("a.c", "100"));
+ tree.set_string("a.b.c", "-3");
+ ATF_REQUIRE_THROW(config::unknown_key_error,
+ tree.set_string("a.b.c.d", "82"));
+ tree.set_string("a.d.3", "bar");
+ tree.set_string("a.d.4", "bar");
+ ATF_REQUIRE_THROW(config::unknown_key_error,
+ tree.set_string("a.d.4.5", "82"));
+ tree.set_string("a.d.5.6", "82");
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(set_string__unknown_key_not_strict);
+ATF_TEST_CASE_BODY(set_string__unknown_key_not_strict)
+{
+ config::tree tree(false);
+
+ tree.define< config::int_node >("foo.bar");
+ tree.define< config::int_node >("a.b.c");
+ tree.define_dynamic("a.d");
+ tree.set_string("a.b.c", "123");
+ tree.set_string("a.d.3", "foo");
+
+ tree.set_string("abc", "2");
+ ATF_REQUIRE(!tree.is_set("abc"));
+
+ tree.set_string("foo.bar", "15");
+ tree.set_string("foo.bar.baz", "0");
+ ATF_REQUIRE(!tree.is_set("foo.bar.baz"));
+
+ tree.set_string("a.c", "100");
+ ATF_REQUIRE(!tree.is_set("a.c"));
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(all_properties__none);
+ATF_TEST_CASE_BODY(all_properties__none)
+{
+ const config::tree tree;
+ ATF_REQUIRE(tree.all_properties().empty());
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(all_properties__all_set);
+ATF_TEST_CASE_BODY(all_properties__all_set)
+{
+ config::tree tree;
+
+ tree.define< config::int_node >("plain");
+ tree.set< config::int_node >("plain", 1234);
+
+ tree.define< config::int_node >("static.first");
+ tree.set< config::int_node >("static.first", -3);
+ tree.define< config::string_node >("static.second");
+ tree.set< config::string_node >("static.second", "some text");
+
+ tree.define_dynamic("dynamic");
+ tree.set< config::string_node >("dynamic.first", "hello");
+ tree.set< config::string_node >("dynamic.second", "bye");
+
+ config::properties_map exp_properties;
+ exp_properties["plain"] = "1234";
+ exp_properties["static.first"] = "-3";
+ exp_properties["static.second"] = "some text";
+ exp_properties["dynamic.first"] = "hello";
+ exp_properties["dynamic.second"] = "bye";
+
+ const config::properties_map properties = tree.all_properties();
+ ATF_REQUIRE(exp_properties == properties);
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(all_properties__some_unset);
+ATF_TEST_CASE_BODY(all_properties__some_unset)
+{
+ config::tree tree;
+
+ tree.define< config::int_node >("static.first");
+ tree.set< config::int_node >("static.first", -3);
+ tree.define< config::string_node >("static.second");
+
+ tree.define_dynamic("dynamic");
+
+ config::properties_map exp_properties;
+ exp_properties["static.first"] = "-3";
+
+ const config::properties_map properties = tree.all_properties();
+ ATF_REQUIRE(exp_properties == properties);
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(all_properties__subtree__inner);
+ATF_TEST_CASE_BODY(all_properties__subtree__inner)
+{
+ config::tree tree;
+
+ tree.define< config::int_node >("root.a.b.c.first");
+ tree.define< config::int_node >("root.a.b.c.second");
+ tree.define< config::int_node >("root.a.d.first");
+
+ tree.set< config::int_node >("root.a.b.c.first", 1);
+ tree.set< config::int_node >("root.a.b.c.second", 2);
+ tree.set< config::int_node >("root.a.d.first", 3);
+
+ {
+ config::properties_map exp_properties;
+ exp_properties["root.a.b.c.first"] = "1";
+ exp_properties["root.a.b.c.second"] = "2";
+ exp_properties["root.a.d.first"] = "3";
+ ATF_REQUIRE(exp_properties == tree.all_properties("root"));
+ ATF_REQUIRE(exp_properties == tree.all_properties("root.a"));
+ }
+
+ {
+ config::properties_map exp_properties;
+ exp_properties["root.a.b.c.first"] = "1";
+ exp_properties["root.a.b.c.second"] = "2";
+ ATF_REQUIRE(exp_properties == tree.all_properties("root.a.b"));
+ ATF_REQUIRE(exp_properties == tree.all_properties("root.a.b.c"));
+ }
+
+ {
+ config::properties_map exp_properties;
+ exp_properties["root.a.d.first"] = "3";
+ ATF_REQUIRE(exp_properties == tree.all_properties("root.a.d"));
+ }
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(all_properties__subtree__leaf);
+ATF_TEST_CASE_BODY(all_properties__subtree__leaf)
+{
+ config::tree tree;
+
+ tree.define< config::int_node >("root.a.b.c.first");
+ tree.set< config::int_node >("root.a.b.c.first", 1);
+ ATF_REQUIRE_THROW_RE(config::value_error, "Cannot export.*leaf",
+ tree.all_properties("root.a.b.c.first"));
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(all_properties__subtree__strip_key);
+ATF_TEST_CASE_BODY(all_properties__subtree__strip_key)
+{
+ config::tree tree;
+
+ tree.define< config::int_node >("root.a.b.c.first");
+ tree.define< config::int_node >("root.a.b.c.second");
+ tree.define< config::int_node >("root.a.d.first");
+
+ tree.set< config::int_node >("root.a.b.c.first", 1);
+ tree.set< config::int_node >("root.a.b.c.second", 2);
+ tree.set< config::int_node >("root.a.d.first", 3);
+
+ config::properties_map exp_properties;
+ exp_properties["b.c.first"] = "1";
+ exp_properties["b.c.second"] = "2";
+ exp_properties["d.first"] = "3";
+ ATF_REQUIRE(exp_properties == tree.all_properties("root.a", true));
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(all_properties__subtree__invalid_key);
+ATF_TEST_CASE_BODY(all_properties__subtree__invalid_key)
+{
+ config::tree tree;
+
+ ATF_REQUIRE_THROW(config::invalid_key_error, tree.all_properties("."));
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(all_properties__subtree__unknown_key);
+ATF_TEST_CASE_BODY(all_properties__subtree__unknown_key)
+{
+ config::tree tree;
+
+ tree.define< config::int_node >("root.a.b.c.first");
+ tree.set< config::int_node >("root.a.b.c.first", 1);
+ tree.define< config::int_node >("root.a.b.c.unset");
+
+ ATF_REQUIRE_THROW(config::unknown_key_error,
+ tree.all_properties("root.a.b.c.first.foo"));
+ ATF_REQUIRE_THROW_RE(config::value_error, "Cannot export.*leaf",
+ tree.all_properties("root.a.b.c.unset"));
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(operators_eq_and_ne__empty);
+ATF_TEST_CASE_BODY(operators_eq_and_ne__empty)
+{
+ config::tree t1;
+ config::tree t2;
+ ATF_REQUIRE( t1 == t2);
+ ATF_REQUIRE(!(t1 != t2));
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(operators_eq_and_ne__shallow_copy);
+ATF_TEST_CASE_BODY(operators_eq_and_ne__shallow_copy)
+{
+ config::tree t1;
+ t1.define< config::int_node >("root.a.b.c.first");
+ t1.set< config::int_node >("root.a.b.c.first", 1);
+ config::tree t2 = t1;
+ ATF_REQUIRE( t1 == t2);
+ ATF_REQUIRE(!(t1 != t2));
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(operators_eq_and_ne__deep_copy);
+ATF_TEST_CASE_BODY(operators_eq_and_ne__deep_copy)
+{
+ config::tree t1;
+ t1.define< config::int_node >("root.a.b.c.first");
+ t1.set< config::int_node >("root.a.b.c.first", 1);
+ config::tree t2 = t1.deep_copy();
+ ATF_REQUIRE( t1 == t2);
+ ATF_REQUIRE(!(t1 != t2));
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(operators_eq_and_ne__some_contents);
+ATF_TEST_CASE_BODY(operators_eq_and_ne__some_contents)
+{
+ config::tree t1, t2;
+
+ t1.define< config::int_node >("root.a.b.c.first");
+ t1.set< config::int_node >("root.a.b.c.first", 1);
+ ATF_REQUIRE(!(t1 == t2));
+ ATF_REQUIRE( t1 != t2);
+
+ t2.define< config::int_node >("root.a.b.c.first");
+ t2.set< config::int_node >("root.a.b.c.first", 1);
+ ATF_REQUIRE( t1 == t2);
+ ATF_REQUIRE(!(t1 != t2));
+
+ t1.set< config::int_node >("root.a.b.c.first", 2);
+ ATF_REQUIRE(!(t1 == t2));
+ ATF_REQUIRE( t1 != t2);
+
+ t2.set< config::int_node >("root.a.b.c.first", 2);
+ ATF_REQUIRE( t1 == t2);
+ ATF_REQUIRE(!(t1 != t2));
+
+ t1.define< config::string_node >("another.key");
+ t1.set< config::string_node >("another.key", "some text");
+ ATF_REQUIRE(!(t1 == t2));
+ ATF_REQUIRE( t1 != t2);
+
+ t2.define< config::string_node >("another.key");
+ t2.set< config::string_node >("another.key", "some text");
+ ATF_REQUIRE( t1 == t2);
+ ATF_REQUIRE(!(t1 != t2));
+}
+
+
+ATF_TEST_CASE_WITHOUT_HEAD(custom_leaf__no_default_ctor);
+ATF_TEST_CASE_BODY(custom_leaf__no_default_ctor)
+{
+ config::tree tree;
+
+ tree.define< wrapped_int_node >("test1");
+ tree.define< wrapped_int_node >("test2");
+ tree.set< wrapped_int_node >("test1", int_wrapper(5));
+ tree.set< wrapped_int_node >("test2", int_wrapper(10));
+ const int_wrapper& test1 = tree.lookup< wrapped_int_node >("test1");
+ ATF_REQUIRE_EQ(5, test1.value());
+ const int_wrapper& test2 = tree.lookup< wrapped_int_node >("test2");
+ ATF_REQUIRE_EQ(10, test2.value());
+}
+
+
+ATF_INIT_TEST_CASES(tcs)
+{
+ ATF_ADD_TEST_CASE(tcs, define_set_lookup__one_level);
+ ATF_ADD_TEST_CASE(tcs, define_set_lookup__multiple_levels);
+
+ ATF_ADD_TEST_CASE(tcs, deep_copy__empty);
+ ATF_ADD_TEST_CASE(tcs, deep_copy__some);
+
+ ATF_ADD_TEST_CASE(tcs, combine__empty);
+ ATF_ADD_TEST_CASE(tcs, combine__same_layout__no_overrides);
+ ATF_ADD_TEST_CASE(tcs, combine__same_layout__no_base);
+ ATF_ADD_TEST_CASE(tcs, combine__same_layout__mix);
+ ATF_ADD_TEST_CASE(tcs, combine__different_layout);
+ ATF_ADD_TEST_CASE(tcs, combine__dynamic_wins);
+ ATF_ADD_TEST_CASE(tcs, combine__inner_leaf_mismatch);
+
+ ATF_ADD_TEST_CASE(tcs, lookup__invalid_key);
+ ATF_ADD_TEST_CASE(tcs, lookup__unknown_key);
+
+ ATF_ADD_TEST_CASE(tcs, is_set__one_level);
+ ATF_ADD_TEST_CASE(tcs, is_set__multiple_levels);
+ ATF_ADD_TEST_CASE(tcs, is_set__invalid_key);
+
+ ATF_ADD_TEST_CASE(tcs, set__invalid_key);
+ ATF_ADD_TEST_CASE(tcs, set__invalid_key_value);
+ ATF_ADD_TEST_CASE(tcs, set__unknown_key);
+ ATF_ADD_TEST_CASE(tcs, set__unknown_key_not_strict);
+
+ ATF_ADD_TEST_CASE(tcs, push_lua__ok);
+ ATF_ADD_TEST_CASE(tcs, set_lua__ok);
+
+ ATF_ADD_TEST_CASE(tcs, lookup_rw);
+
+ ATF_ADD_TEST_CASE(tcs, lookup_string__ok);
+ ATF_ADD_TEST_CASE(tcs, lookup_string__invalid_key);
+ ATF_ADD_TEST_CASE(tcs, lookup_string__unknown_key);
+
+ ATF_ADD_TEST_CASE(tcs, set_string__ok);
+ ATF_ADD_TEST_CASE(tcs, set_string__invalid_key);
+ ATF_ADD_TEST_CASE(tcs, set_string__invalid_key_value);
+ ATF_ADD_TEST_CASE(tcs, set_string__unknown_key);
+ ATF_ADD_TEST_CASE(tcs, set_string__unknown_key_not_strict);
+
+ ATF_ADD_TEST_CASE(tcs, all_properties__none);
+ ATF_ADD_TEST_CASE(tcs, all_properties__all_set);
+ ATF_ADD_TEST_CASE(tcs, all_properties__some_unset);
+ ATF_ADD_TEST_CASE(tcs, all_properties__subtree__inner);
+ ATF_ADD_TEST_CASE(tcs, all_properties__subtree__leaf);
+ ATF_ADD_TEST_CASE(tcs, all_properties__subtree__strip_key);
+ ATF_ADD_TEST_CASE(tcs, all_properties__subtree__invalid_key);
+ ATF_ADD_TEST_CASE(tcs, all_properties__subtree__unknown_key);
+
+ ATF_ADD_TEST_CASE(tcs, operators_eq_and_ne__empty);
+ ATF_ADD_TEST_CASE(tcs, operators_eq_and_ne__shallow_copy);
+ ATF_ADD_TEST_CASE(tcs, operators_eq_and_ne__deep_copy);
+ ATF_ADD_TEST_CASE(tcs, operators_eq_and_ne__some_contents);
+
+ ATF_ADD_TEST_CASE(tcs, custom_leaf__no_default_ctor);
+}