xref: /freebsd/contrib/kyua/utils/config/tree_test.cpp (revision fe75646a0234a261c0013bf1840fdac4acaf0cec)
1 // Copyright 2012 The Kyua Authors.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
8 // * Redistributions of source code must retain the above copyright
9 //   notice, this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above copyright
11 //   notice, this list of conditions and the following disclaimer in the
12 //   documentation and/or other materials provided with the distribution.
13 // * Neither the name of Google Inc. nor the names of its contributors
14 //   may be used to endorse or promote products derived from this software
15 //   without specific prior written permission.
16 //
17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 
29 #include "utils/config/tree.ipp"
30 
31 #include <atf-c++.hpp>
32 
33 #include "utils/config/nodes.ipp"
34 #include "utils/format/macros.hpp"
35 #include "utils/text/operations.ipp"
36 
37 namespace config = utils::config;
38 namespace text = utils::text;
39 
40 
41 namespace {
42 
43 
44 /// Simple wrapper around an integer value without default constructors.
45 ///
46 /// The purpose of this type is to have a simple class without default
47 /// constructors to validate that we can use it as a leaf of a tree.
48 class int_wrapper {
49     /// The wrapped integer value.
50     int _value;
51 
52 public:
53     /// Constructs a new wrapped integer.
54     ///
55     /// \param value_ The value to store in the object.
56     explicit int_wrapper(int value_) :
57         _value(value_)
58     {
59     }
60 
61     /// \return The integer value stored by the object.
62     int
63     value(void) const
64     {
65         return _value;
66     }
67 };
68 
69 
70 /// Custom tree leaf type for an object without defualt constructors.
71 class wrapped_int_node : public config::typed_leaf_node< int_wrapper > {
72 public:
73     /// Copies the node.
74     ///
75     /// \return A dynamically-allocated node.
76     virtual base_node*
77     deep_copy(void) const
78     {
79         std::auto_ptr< wrapped_int_node > new_node(new wrapped_int_node());
80         new_node->_value = _value;
81         return new_node.release();
82     }
83 
84     /// Pushes the node's value onto the Lua stack.
85     ///
86     /// \param state The Lua state onto which to push the value.
87     void
88     push_lua(lutok::state& state) const
89     {
90         state.push_integer(
91             config::typed_leaf_node< int_wrapper >::value().value());
92     }
93 
94     /// Sets the value of the node from an entry in the Lua stack.
95     ///
96     /// \param state The Lua state from which to get the value.
97     /// \param value_index The stack index in which the value resides.
98     void
99     set_lua(lutok::state& state, const int value_index)
100     {
101         ATF_REQUIRE(state.is_number(value_index));
102         int_wrapper new_value(state.to_integer(value_index));
103         config::typed_leaf_node< int_wrapper >::set(new_value);
104     }
105 
106     /// Sets the value of the node from a raw string representation.
107     ///
108     /// \param raw_value The value to set the node to.
109     void
110     set_string(const std::string& raw_value)
111     {
112         int_wrapper new_value(text::to_type< int >(raw_value));
113         config::typed_leaf_node< int_wrapper >::set(new_value);
114     }
115 
116     /// Converts the contents of the node to a string.
117     ///
118     /// \return A string representation of the value held by the node.
119     std::string
120     to_string(void) const
121     {
122         return F("%s") %
123             config::typed_leaf_node< int_wrapper >::value().value();
124     }
125 };
126 
127 
128 }  // anonymous namespace
129 
130 
131 ATF_TEST_CASE_WITHOUT_HEAD(define_set_lookup__one_level);
132 ATF_TEST_CASE_BODY(define_set_lookup__one_level)
133 {
134     config::tree tree;
135 
136     tree.define< config::int_node >("var1");
137     tree.define< config::string_node >("var2");
138     tree.define< config::bool_node >("var3");
139 
140     tree.set< config::int_node >("var1", 42);
141     tree.set< config::string_node >("var2", "hello");
142     tree.set< config::bool_node >("var3", false);
143 
144     ATF_REQUIRE_EQ(42, tree.lookup< config::int_node >("var1"));
145     ATF_REQUIRE_EQ("hello", tree.lookup< config::string_node >("var2"));
146     ATF_REQUIRE(!tree.lookup< config::bool_node >("var3"));
147 }
148 
149 
150 ATF_TEST_CASE_WITHOUT_HEAD(define_set_lookup__multiple_levels);
151 ATF_TEST_CASE_BODY(define_set_lookup__multiple_levels)
152 {
153     config::tree tree;
154 
155     tree.define< config::int_node >("foo.bar.1");
156     tree.define< config::string_node >("foo.bar.2");
157     tree.define< config::bool_node >("foo.3");
158     tree.define_dynamic("sub.tree");
159 
160     tree.set< config::int_node >("foo.bar.1", 42);
161     tree.set< config::string_node >("foo.bar.2", "hello");
162     tree.set< config::bool_node >("foo.3", true);
163     tree.set< config::string_node >("sub.tree.1", "bye");
164     tree.set< config::int_node >("sub.tree.2", 4);
165     tree.set< config::int_node >("sub.tree.3.4", 123);
166 
167     ATF_REQUIRE_EQ(42, tree.lookup< config::int_node >("foo.bar.1"));
168     ATF_REQUIRE_EQ("hello", tree.lookup< config::string_node >("foo.bar.2"));
169     ATF_REQUIRE(tree.lookup< config::bool_node >("foo.3"));
170     ATF_REQUIRE_EQ(4, tree.lookup< config::int_node >("sub.tree.2"));
171     ATF_REQUIRE_EQ(123, tree.lookup< config::int_node >("sub.tree.3.4"));
172 }
173 
174 
175 ATF_TEST_CASE_WITHOUT_HEAD(deep_copy__empty);
176 ATF_TEST_CASE_BODY(deep_copy__empty)
177 {
178     config::tree tree1;
179     config::tree tree2 = tree1.deep_copy();
180 
181     tree1.define< config::bool_node >("var1");
182     // This would crash if the copy shared the internal data.
183     tree2.define< config::int_node >("var1");
184 }
185 
186 
187 ATF_TEST_CASE_WITHOUT_HEAD(deep_copy__some);
188 ATF_TEST_CASE_BODY(deep_copy__some)
189 {
190     config::tree tree1;
191     tree1.define< config::bool_node >("this.is.a.var");
192     tree1.set< config::bool_node >("this.is.a.var", true);
193     tree1.define< config::int_node >("this.is.another.var");
194     tree1.set< config::int_node >("this.is.another.var", 34);
195     tree1.define< config::int_node >("and.another");
196     tree1.set< config::int_node >("and.another", 123);
197 
198     config::tree tree2 = tree1.deep_copy();
199     tree2.set< config::bool_node >("this.is.a.var", false);
200     tree2.set< config::int_node >("this.is.another.var", 43);
201 
202     ATF_REQUIRE( tree1.lookup< config::bool_node >("this.is.a.var"));
203     ATF_REQUIRE(!tree2.lookup< config::bool_node >("this.is.a.var"));
204 
205     ATF_REQUIRE_EQ(34, tree1.lookup< config::int_node >("this.is.another.var"));
206     ATF_REQUIRE_EQ(43, tree2.lookup< config::int_node >("this.is.another.var"));
207 
208     ATF_REQUIRE_EQ(123, tree1.lookup< config::int_node >("and.another"));
209     ATF_REQUIRE_EQ(123, tree2.lookup< config::int_node >("and.another"));
210 }
211 
212 
213 ATF_TEST_CASE_WITHOUT_HEAD(combine__empty);
214 ATF_TEST_CASE_BODY(combine__empty)
215 {
216     const config::tree t1, t2;
217     const config::tree combined = t1.combine(t2);
218 
219     const config::tree expected;
220     ATF_REQUIRE(expected == combined);
221 }
222 
223 
224 static void
225 init_tree_for_combine_test(config::tree& tree)
226 {
227     tree.define< config::int_node >("int-node");
228     tree.define< config::string_node >("string-node");
229     tree.define< config::int_node >("unused.node");
230     tree.define< config::int_node >("deeper.int.node");
231     tree.define_dynamic("deeper.dynamic");
232 }
233 
234 
235 ATF_TEST_CASE_WITHOUT_HEAD(combine__same_layout__no_overrides);
236 ATF_TEST_CASE_BODY(combine__same_layout__no_overrides)
237 {
238     config::tree t1, t2;
239     init_tree_for_combine_test(t1);
240     init_tree_for_combine_test(t2);
241     t1.set< config::int_node >("int-node", 3);
242     t1.set< config::string_node >("string-node", "foo");
243     t1.set< config::int_node >("deeper.int.node", 15);
244     t1.set_string("deeper.dynamic.first", "value1");
245     t1.set_string("deeper.dynamic.second", "value2");
246     const config::tree combined = t1.combine(t2);
247 
248     ATF_REQUIRE(t1 == combined);
249 }
250 
251 
252 ATF_TEST_CASE_WITHOUT_HEAD(combine__same_layout__no_base);
253 ATF_TEST_CASE_BODY(combine__same_layout__no_base)
254 {
255     config::tree t1, t2;
256     init_tree_for_combine_test(t1);
257     init_tree_for_combine_test(t2);
258     t2.set< config::int_node >("int-node", 3);
259     t2.set< config::string_node >("string-node", "foo");
260     t2.set< config::int_node >("deeper.int.node", 15);
261     t2.set_string("deeper.dynamic.first", "value1");
262     t2.set_string("deeper.dynamic.second", "value2");
263     const config::tree combined = t1.combine(t2);
264 
265     ATF_REQUIRE(t2 == combined);
266 }
267 
268 
269 ATF_TEST_CASE_WITHOUT_HEAD(combine__same_layout__mix);
270 ATF_TEST_CASE_BODY(combine__same_layout__mix)
271 {
272     config::tree t1, t2;
273     init_tree_for_combine_test(t1);
274     init_tree_for_combine_test(t2);
275     t1.set< config::int_node >("int-node", 3);
276     t2.set< config::int_node >("int-node", 5);
277     t1.set< config::string_node >("string-node", "foo");
278     t2.set< config::int_node >("deeper.int.node", 15);
279     t1.set_string("deeper.dynamic.first", "value1");
280     t1.set_string("deeper.dynamic.second", "value2.1");
281     t2.set_string("deeper.dynamic.second", "value2.2");
282     t2.set_string("deeper.dynamic.third", "value3");
283     const config::tree combined = t1.combine(t2);
284 
285     config::tree expected;
286     init_tree_for_combine_test(expected);
287     expected.set< config::int_node >("int-node", 5);
288     expected.set< config::string_node >("string-node", "foo");
289     expected.set< config::int_node >("deeper.int.node", 15);
290     expected.set_string("deeper.dynamic.first", "value1");
291     expected.set_string("deeper.dynamic.second", "value2.2");
292     expected.set_string("deeper.dynamic.third", "value3");
293     ATF_REQUIRE(expected == combined);
294 }
295 
296 
297 ATF_TEST_CASE_WITHOUT_HEAD(combine__different_layout);
298 ATF_TEST_CASE_BODY(combine__different_layout)
299 {
300     config::tree t1;
301     t1.define< config::int_node >("common.base1");
302     t1.define< config::int_node >("common.base2");
303     t1.define_dynamic("dynamic.base");
304     t1.define< config::int_node >("unset.base");
305 
306     config::tree t2;
307     t2.define< config::int_node >("common.base2");
308     t2.define< config::int_node >("common.base3");
309     t2.define_dynamic("dynamic.other");
310     t2.define< config::int_node >("unset.other");
311 
312     t1.set< config::int_node >("common.base1", 1);
313     t1.set< config::int_node >("common.base2", 2);
314     t1.set_string("dynamic.base.first", "foo");
315     t1.set_string("dynamic.base.second", "bar");
316 
317     t2.set< config::int_node >("common.base2", 4);
318     t2.set< config::int_node >("common.base3", 3);
319     t2.set_string("dynamic.other.first", "FOO");
320     t2.set_string("dynamic.other.second", "BAR");
321 
322     config::tree combined = t1.combine(t2);
323 
324     config::tree expected;
325     expected.define< config::int_node >("common.base1");
326     expected.define< config::int_node >("common.base2");
327     expected.define< config::int_node >("common.base3");
328     expected.define_dynamic("dynamic.base");
329     expected.define_dynamic("dynamic.other");
330     expected.define< config::int_node >("unset.base");
331     expected.define< config::int_node >("unset.other");
332 
333     expected.set< config::int_node >("common.base1", 1);
334     expected.set< config::int_node >("common.base2", 4);
335     expected.set< config::int_node >("common.base3", 3);
336     expected.set_string("dynamic.base.first", "foo");
337     expected.set_string("dynamic.base.second", "bar");
338     expected.set_string("dynamic.other.first", "FOO");
339     expected.set_string("dynamic.other.second", "BAR");
340 
341     ATF_REQUIRE(expected == combined);
342 
343     // The combined tree should have respected existing but unset nodes.  Check
344     // that these calls do not crash.
345     combined.set< config::int_node >("unset.base", 5);
346     combined.set< config::int_node >("unset.other", 5);
347 }
348 
349 
350 ATF_TEST_CASE_WITHOUT_HEAD(combine__dynamic_wins);
351 ATF_TEST_CASE_BODY(combine__dynamic_wins)
352 {
353     config::tree t1;
354     t1.define< config::int_node >("inner.leaf1");
355     t1.set< config::int_node >("inner.leaf1", 3);
356 
357     config::tree t2;
358     t2.define_dynamic("inner");
359     t2.set_string("inner.leaf2", "4");
360 
361     config::tree combined = t1.combine(t2);
362 
363     config::tree expected;
364     expected.define_dynamic("inner");
365     expected.set_string("inner.leaf1", "3");
366     expected.set_string("inner.leaf2", "4");
367 
368     ATF_REQUIRE(expected == combined);
369 
370     // The combined inner node should have become dynamic so this call should
371     // not fail.
372     combined.set_string("inner.leaf3", "5");
373 }
374 
375 
376 ATF_TEST_CASE_WITHOUT_HEAD(combine__inner_leaf_mismatch);
377 ATF_TEST_CASE_BODY(combine__inner_leaf_mismatch)
378 {
379     config::tree t1;
380     t1.define< config::int_node >("top.foo.bar");
381 
382     config::tree t2;
383     t2.define< config::int_node >("top.foo");
384 
385     ATF_REQUIRE_THROW_RE(config::bad_combination_error,
386                          "'top.foo' is an inner node in the base tree but a "
387                          "leaf node in the overrides tree",
388                          t1.combine(t2));
389 
390     ATF_REQUIRE_THROW_RE(config::bad_combination_error,
391                          "'top.foo' is a leaf node in the base tree but an "
392                          "inner node in the overrides tree",
393                          t2.combine(t1));
394 }
395 
396 
397 ATF_TEST_CASE_WITHOUT_HEAD(lookup__invalid_key);
398 ATF_TEST_CASE_BODY(lookup__invalid_key)
399 {
400     config::tree tree;
401 
402     ATF_REQUIRE_THROW(config::invalid_key_error,
403                       tree.lookup< config::int_node >("."));
404 }
405 
406 
407 ATF_TEST_CASE_WITHOUT_HEAD(lookup__unknown_key);
408 ATF_TEST_CASE_BODY(lookup__unknown_key)
409 {
410     config::tree tree;
411 
412     tree.define< config::int_node >("foo.bar");
413     tree.define< config::int_node >("a.b.c");
414     tree.define_dynamic("a.d");
415     tree.set< config::int_node >("a.b.c", 123);
416     tree.set< config::int_node >("a.d.100", 0);
417 
418     ATF_REQUIRE_THROW(config::unknown_key_error,
419                       tree.lookup< config::int_node >("abc"));
420 
421     ATF_REQUIRE_THROW(config::unknown_key_error,
422                       tree.lookup< config::int_node >("foo"));
423     ATF_REQUIRE_THROW(config::unknown_key_error,
424                       tree.lookup< config::int_node >("foo.bar"));
425     ATF_REQUIRE_THROW(config::unknown_key_error,
426                       tree.lookup< config::int_node >("foo.bar.baz"));
427 
428     ATF_REQUIRE_THROW(config::unknown_key_error,
429                       tree.lookup< config::int_node >("a"));
430     ATF_REQUIRE_THROW(config::unknown_key_error,
431                       tree.lookup< config::int_node >("a.b"));
432     ATF_REQUIRE_THROW(config::unknown_key_error,
433                       tree.lookup< config::int_node >("a.c"));
434     (void)tree.lookup< config::int_node >("a.b.c");
435     ATF_REQUIRE_THROW(config::unknown_key_error,
436                       tree.lookup< config::int_node >("a.b.c.d"));
437     ATF_REQUIRE_THROW(config::unknown_key_error,
438                       tree.lookup< config::int_node >("a.d"));
439     (void)tree.lookup< config::int_node >("a.d.100");
440     ATF_REQUIRE_THROW(config::unknown_key_error,
441                       tree.lookup< config::int_node >("a.d.101"));
442     ATF_REQUIRE_THROW(config::unknown_key_error,
443                       tree.lookup< config::int_node >("a.d.100.3"));
444     ATF_REQUIRE_THROW(config::unknown_key_error,
445                       tree.lookup< config::int_node >("a.d.e"));
446 }
447 
448 
449 ATF_TEST_CASE_WITHOUT_HEAD(is_set__one_level);
450 ATF_TEST_CASE_BODY(is_set__one_level)
451 {
452     config::tree tree;
453 
454     tree.define< config::int_node >("var1");
455     tree.define< config::string_node >("var2");
456     tree.define< config::bool_node >("var3");
457 
458     tree.set< config::int_node >("var1", 42);
459     tree.set< config::bool_node >("var3", false);
460 
461     ATF_REQUIRE( tree.is_set("var1"));
462     ATF_REQUIRE(!tree.is_set("var2"));
463     ATF_REQUIRE( tree.is_set("var3"));
464 }
465 
466 
467 ATF_TEST_CASE_WITHOUT_HEAD(is_set__multiple_levels);
468 ATF_TEST_CASE_BODY(is_set__multiple_levels)
469 {
470     config::tree tree;
471 
472     tree.define< config::int_node >("a.b.var1");
473     tree.define< config::string_node >("a.b.var2");
474     tree.define< config::bool_node >("e.var3");
475 
476     tree.set< config::int_node >("a.b.var1", 42);
477     tree.set< config::bool_node >("e.var3", false);
478 
479     ATF_REQUIRE(!tree.is_set("a"));
480     ATF_REQUIRE(!tree.is_set("a.b"));
481     ATF_REQUIRE( tree.is_set("a.b.var1"));
482     ATF_REQUIRE(!tree.is_set("a.b.var1.trailing"));
483 
484     ATF_REQUIRE(!tree.is_set("a"));
485     ATF_REQUIRE(!tree.is_set("a.b"));
486     ATF_REQUIRE(!tree.is_set("a.b.var2"));
487     ATF_REQUIRE(!tree.is_set("a.b.var2.trailing"));
488 
489     ATF_REQUIRE(!tree.is_set("e"));
490     ATF_REQUIRE( tree.is_set("e.var3"));
491     ATF_REQUIRE(!tree.is_set("e.var3.trailing"));
492 }
493 
494 
495 ATF_TEST_CASE_WITHOUT_HEAD(is_set__invalid_key);
496 ATF_TEST_CASE_BODY(is_set__invalid_key)
497 {
498     config::tree tree;
499 
500     ATF_REQUIRE_THROW(config::invalid_key_error, tree.is_set(".abc"));
501 }
502 
503 
504 ATF_TEST_CASE_WITHOUT_HEAD(set__invalid_key);
505 ATF_TEST_CASE_BODY(set__invalid_key)
506 {
507     config::tree tree;
508 
509     ATF_REQUIRE_THROW(config::invalid_key_error,
510                       tree.set< config::int_node >("foo.", 54));
511 }
512 
513 
514 ATF_TEST_CASE_WITHOUT_HEAD(set__invalid_key_value);
515 ATF_TEST_CASE_BODY(set__invalid_key_value)
516 {
517     config::tree tree;
518 
519     tree.define< config::int_node >("foo.bar");
520     tree.define_dynamic("a.d");
521 
522     ATF_REQUIRE_THROW(config::invalid_key_value,
523                       tree.set< config::int_node >("foo", 3));
524     ATF_REQUIRE_THROW(config::invalid_key_value,
525                       tree.set< config::int_node >("a", -10));
526 }
527 
528 
529 ATF_TEST_CASE_WITHOUT_HEAD(set__unknown_key);
530 ATF_TEST_CASE_BODY(set__unknown_key)
531 {
532     config::tree tree;
533 
534     tree.define< config::int_node >("foo.bar");
535     tree.define< config::int_node >("a.b.c");
536     tree.define_dynamic("a.d");
537     tree.set< config::int_node >("a.b.c", 123);
538     tree.set< config::string_node >("a.d.3", "foo");
539 
540     ATF_REQUIRE_THROW(config::unknown_key_error,
541                       tree.set< config::int_node >("abc", 2));
542 
543     tree.set< config::int_node >("foo.bar", 15);
544     ATF_REQUIRE_THROW(config::unknown_key_error,
545                       tree.set< config::int_node >("foo.bar.baz", 0));
546 
547     ATF_REQUIRE_THROW(config::unknown_key_error,
548                       tree.set< config::int_node >("a.c", 100));
549     tree.set< config::int_node >("a.b.c", -3);
550     ATF_REQUIRE_THROW(config::unknown_key_error,
551                       tree.set< config::int_node >("a.b.c.d", 82));
552     tree.set< config::string_node >("a.d.3", "bar");
553     tree.set< config::string_node >("a.d.4", "bar");
554     ATF_REQUIRE_THROW(config::unknown_key_error,
555                       tree.set< config::int_node >("a.d.4.5", 82));
556     tree.set< config::int_node >("a.d.5.6", 82);
557 }
558 
559 
560 ATF_TEST_CASE_WITHOUT_HEAD(set__unknown_key_not_strict);
561 ATF_TEST_CASE_BODY(set__unknown_key_not_strict)
562 {
563     config::tree tree(false);
564 
565     tree.define< config::int_node >("foo.bar");
566     tree.define< config::int_node >("a.b.c");
567     tree.define_dynamic("a.d");
568     tree.set< config::int_node >("a.b.c", 123);
569     tree.set< config::string_node >("a.d.3", "foo");
570 
571     tree.set< config::int_node >("abc", 2);
572     ATF_REQUIRE(!tree.is_set("abc"));
573 
574     tree.set< config::int_node >("foo.bar", 15);
575     tree.set< config::int_node >("foo.bar.baz", 0);
576     ATF_REQUIRE(!tree.is_set("foo.bar.baz"));
577 
578     tree.set< config::int_node >("a.c", 100);
579     ATF_REQUIRE(!tree.is_set("a.c"));
580 }
581 
582 
583 ATF_TEST_CASE_WITHOUT_HEAD(push_lua__ok);
584 ATF_TEST_CASE_BODY(push_lua__ok)
585 {
586     config::tree tree;
587 
588     tree.define< config::int_node >("top.integer");
589     tree.define< wrapped_int_node >("top.custom");
590     tree.define_dynamic("dynamic");
591     tree.set< config::int_node >("top.integer", 5);
592     tree.set< wrapped_int_node >("top.custom", int_wrapper(10));
593     tree.set_string("dynamic.first", "foo");
594 
595     lutok::state state;
596     tree.push_lua("top.integer", state);
597     tree.push_lua("top.custom", state);
598     tree.push_lua("dynamic.first", state);
599     ATF_REQUIRE(state.is_number(-3));
600     ATF_REQUIRE_EQ(5, state.to_integer(-3));
601     ATF_REQUIRE(state.is_number(-2));
602     ATF_REQUIRE_EQ(10, state.to_integer(-2));
603     ATF_REQUIRE(state.is_string(-1));
604     ATF_REQUIRE_EQ("foo", state.to_string(-1));
605     state.pop(3);
606 }
607 
608 
609 ATF_TEST_CASE_WITHOUT_HEAD(set_lua__ok);
610 ATF_TEST_CASE_BODY(set_lua__ok)
611 {
612     config::tree tree;
613 
614     tree.define< config::int_node >("top.integer");
615     tree.define< wrapped_int_node >("top.custom");
616     tree.define_dynamic("dynamic");
617 
618     {
619         lutok::state state;
620         state.push_integer(5);
621         state.push_integer(10);
622         state.push_string("foo");
623         tree.set_lua("top.integer", state, -3);
624         tree.set_lua("top.custom", state, -2);
625         tree.set_lua("dynamic.first", state, -1);
626         state.pop(3);
627     }
628 
629     ATF_REQUIRE_EQ(5, tree.lookup< config::int_node >("top.integer"));
630     ATF_REQUIRE_EQ(10, tree.lookup< wrapped_int_node >("top.custom").value());
631     ATF_REQUIRE_EQ("foo", tree.lookup< config::string_node >("dynamic.first"));
632 }
633 
634 
635 ATF_TEST_CASE_WITHOUT_HEAD(lookup_rw);
636 ATF_TEST_CASE_BODY(lookup_rw)
637 {
638     config::tree tree;
639 
640     tree.define< config::int_node >("var1");
641     tree.define< config::bool_node >("var3");
642 
643     tree.set< config::int_node >("var1", 42);
644     tree.set< config::bool_node >("var3", false);
645 
646     tree.lookup_rw< config::int_node >("var1") += 10;
647     ATF_REQUIRE_EQ(52, tree.lookup< config::int_node >("var1"));
648     ATF_REQUIRE(!tree.lookup< config::bool_node >("var3"));
649 }
650 
651 
652 ATF_TEST_CASE_WITHOUT_HEAD(lookup_string__ok);
653 ATF_TEST_CASE_BODY(lookup_string__ok)
654 {
655     config::tree tree;
656 
657     tree.define< config::int_node >("var1");
658     tree.define< config::string_node >("b.var2");
659     tree.define< config::bool_node >("c.d.var3");
660 
661     tree.set< config::int_node >("var1", 42);
662     tree.set< config::string_node >("b.var2", "hello");
663     tree.set< config::bool_node >("c.d.var3", false);
664 
665     ATF_REQUIRE_EQ("42", tree.lookup_string("var1"));
666     ATF_REQUIRE_EQ("hello", tree.lookup_string("b.var2"));
667     ATF_REQUIRE_EQ("false", tree.lookup_string("c.d.var3"));
668 }
669 
670 
671 ATF_TEST_CASE_WITHOUT_HEAD(lookup_string__invalid_key);
672 ATF_TEST_CASE_BODY(lookup_string__invalid_key)
673 {
674     config::tree tree;
675 
676     ATF_REQUIRE_THROW(config::invalid_key_error, tree.lookup_string(""));
677 }
678 
679 
680 ATF_TEST_CASE_WITHOUT_HEAD(lookup_string__unknown_key);
681 ATF_TEST_CASE_BODY(lookup_string__unknown_key)
682 {
683     config::tree tree;
684 
685     tree.define< config::int_node >("a.b.c");
686 
687     ATF_REQUIRE_THROW(config::unknown_key_error, tree.lookup_string("a.b"));
688     ATF_REQUIRE_THROW(config::unknown_key_error, tree.lookup_string("a.b.c.d"));
689 }
690 
691 
692 ATF_TEST_CASE_WITHOUT_HEAD(set_string__ok);
693 ATF_TEST_CASE_BODY(set_string__ok)
694 {
695     config::tree tree;
696 
697     tree.define< config::int_node >("foo.bar.1");
698     tree.define< config::string_node >("foo.bar.2");
699     tree.define_dynamic("sub.tree");
700 
701     tree.set_string("foo.bar.1", "42");
702     tree.set_string("foo.bar.2", "hello");
703     tree.set_string("sub.tree.2", "15");
704     tree.set_string("sub.tree.3.4", "bye");
705 
706     ATF_REQUIRE_EQ(42, tree.lookup< config::int_node >("foo.bar.1"));
707     ATF_REQUIRE_EQ("hello", tree.lookup< config::string_node >("foo.bar.2"));
708     ATF_REQUIRE_EQ("15", tree.lookup< config::string_node >("sub.tree.2"));
709     ATF_REQUIRE_EQ("bye", tree.lookup< config::string_node >("sub.tree.3.4"));
710 }
711 
712 
713 ATF_TEST_CASE_WITHOUT_HEAD(set_string__invalid_key);
714 ATF_TEST_CASE_BODY(set_string__invalid_key)
715 {
716     config::tree tree;
717 
718     ATF_REQUIRE_THROW(config::invalid_key_error, tree.set_string(".", "foo"));
719 }
720 
721 
722 ATF_TEST_CASE_WITHOUT_HEAD(set_string__invalid_key_value);
723 ATF_TEST_CASE_BODY(set_string__invalid_key_value)
724 {
725     config::tree tree;
726 
727     tree.define< config::int_node >("foo.bar");
728 
729     ATF_REQUIRE_THROW(config::invalid_key_value,
730                       tree.set_string("foo", "abc"));
731     ATF_REQUIRE_THROW(config::invalid_key_value,
732                       tree.set_string("foo.bar", " -3"));
733     ATF_REQUIRE_THROW(config::invalid_key_value,
734                       tree.set_string("foo.bar", "3 "));
735 }
736 
737 
738 ATF_TEST_CASE_WITHOUT_HEAD(set_string__unknown_key);
739 ATF_TEST_CASE_BODY(set_string__unknown_key)
740 {
741     config::tree tree;
742 
743     tree.define< config::int_node >("foo.bar");
744     tree.define< config::int_node >("a.b.c");
745     tree.define_dynamic("a.d");
746     tree.set_string("a.b.c", "123");
747     tree.set_string("a.d.3", "foo");
748 
749     ATF_REQUIRE_THROW(config::unknown_key_error, tree.set_string("abc", "2"));
750 
751     tree.set_string("foo.bar", "15");
752     ATF_REQUIRE_THROW(config::unknown_key_error,
753                       tree.set_string("foo.bar.baz", "0"));
754 
755     ATF_REQUIRE_THROW(config::unknown_key_error,
756                       tree.set_string("a.c", "100"));
757     tree.set_string("a.b.c", "-3");
758     ATF_REQUIRE_THROW(config::unknown_key_error,
759                       tree.set_string("a.b.c.d", "82"));
760     tree.set_string("a.d.3", "bar");
761     tree.set_string("a.d.4", "bar");
762     ATF_REQUIRE_THROW(config::unknown_key_error,
763                       tree.set_string("a.d.4.5", "82"));
764     tree.set_string("a.d.5.6", "82");
765 }
766 
767 
768 ATF_TEST_CASE_WITHOUT_HEAD(set_string__unknown_key_not_strict);
769 ATF_TEST_CASE_BODY(set_string__unknown_key_not_strict)
770 {
771     config::tree tree(false);
772 
773     tree.define< config::int_node >("foo.bar");
774     tree.define< config::int_node >("a.b.c");
775     tree.define_dynamic("a.d");
776     tree.set_string("a.b.c", "123");
777     tree.set_string("a.d.3", "foo");
778 
779     tree.set_string("abc", "2");
780     ATF_REQUIRE(!tree.is_set("abc"));
781 
782     tree.set_string("foo.bar", "15");
783     tree.set_string("foo.bar.baz", "0");
784     ATF_REQUIRE(!tree.is_set("foo.bar.baz"));
785 
786     tree.set_string("a.c", "100");
787     ATF_REQUIRE(!tree.is_set("a.c"));
788 }
789 
790 
791 ATF_TEST_CASE_WITHOUT_HEAD(all_properties__none);
792 ATF_TEST_CASE_BODY(all_properties__none)
793 {
794     const config::tree tree;
795     ATF_REQUIRE(tree.all_properties().empty());
796 }
797 
798 
799 ATF_TEST_CASE_WITHOUT_HEAD(all_properties__all_set);
800 ATF_TEST_CASE_BODY(all_properties__all_set)
801 {
802     config::tree tree;
803 
804     tree.define< config::int_node >("plain");
805     tree.set< config::int_node >("plain", 1234);
806 
807     tree.define< config::int_node >("static.first");
808     tree.set< config::int_node >("static.first", -3);
809     tree.define< config::string_node >("static.second");
810     tree.set< config::string_node >("static.second", "some text");
811 
812     tree.define_dynamic("dynamic");
813     tree.set< config::string_node >("dynamic.first", "hello");
814     tree.set< config::string_node >("dynamic.second", "bye");
815 
816     config::properties_map exp_properties;
817     exp_properties["plain"] = "1234";
818     exp_properties["static.first"] = "-3";
819     exp_properties["static.second"] = "some text";
820     exp_properties["dynamic.first"] = "hello";
821     exp_properties["dynamic.second"] = "bye";
822 
823     const config::properties_map properties = tree.all_properties();
824     ATF_REQUIRE(exp_properties == properties);
825 }
826 
827 
828 ATF_TEST_CASE_WITHOUT_HEAD(all_properties__some_unset);
829 ATF_TEST_CASE_BODY(all_properties__some_unset)
830 {
831     config::tree tree;
832 
833     tree.define< config::int_node >("static.first");
834     tree.set< config::int_node >("static.first", -3);
835     tree.define< config::string_node >("static.second");
836 
837     tree.define_dynamic("dynamic");
838 
839     config::properties_map exp_properties;
840     exp_properties["static.first"] = "-3";
841 
842     const config::properties_map properties = tree.all_properties();
843     ATF_REQUIRE(exp_properties == properties);
844 }
845 
846 
847 ATF_TEST_CASE_WITHOUT_HEAD(all_properties__subtree__inner);
848 ATF_TEST_CASE_BODY(all_properties__subtree__inner)
849 {
850     config::tree tree;
851 
852     tree.define< config::int_node >("root.a.b.c.first");
853     tree.define< config::int_node >("root.a.b.c.second");
854     tree.define< config::int_node >("root.a.d.first");
855 
856     tree.set< config::int_node >("root.a.b.c.first", 1);
857     tree.set< config::int_node >("root.a.b.c.second", 2);
858     tree.set< config::int_node >("root.a.d.first", 3);
859 
860     {
861         config::properties_map exp_properties;
862         exp_properties["root.a.b.c.first"] = "1";
863         exp_properties["root.a.b.c.second"] = "2";
864         exp_properties["root.a.d.first"] = "3";
865         ATF_REQUIRE(exp_properties == tree.all_properties("root"));
866         ATF_REQUIRE(exp_properties == tree.all_properties("root.a"));
867     }
868 
869     {
870         config::properties_map exp_properties;
871         exp_properties["root.a.b.c.first"] = "1";
872         exp_properties["root.a.b.c.second"] = "2";
873         ATF_REQUIRE(exp_properties == tree.all_properties("root.a.b"));
874         ATF_REQUIRE(exp_properties == tree.all_properties("root.a.b.c"));
875     }
876 
877     {
878         config::properties_map exp_properties;
879         exp_properties["root.a.d.first"] = "3";
880         ATF_REQUIRE(exp_properties == tree.all_properties("root.a.d"));
881     }
882 }
883 
884 
885 ATF_TEST_CASE_WITHOUT_HEAD(all_properties__subtree__leaf);
886 ATF_TEST_CASE_BODY(all_properties__subtree__leaf)
887 {
888     config::tree tree;
889 
890     tree.define< config::int_node >("root.a.b.c.first");
891     tree.set< config::int_node >("root.a.b.c.first", 1);
892     ATF_REQUIRE_THROW_RE(config::value_error, "Cannot export.*leaf",
893                          tree.all_properties("root.a.b.c.first"));
894 }
895 
896 
897 ATF_TEST_CASE_WITHOUT_HEAD(all_properties__subtree__strip_key);
898 ATF_TEST_CASE_BODY(all_properties__subtree__strip_key)
899 {
900     config::tree tree;
901 
902     tree.define< config::int_node >("root.a.b.c.first");
903     tree.define< config::int_node >("root.a.b.c.second");
904     tree.define< config::int_node >("root.a.d.first");
905 
906     tree.set< config::int_node >("root.a.b.c.first", 1);
907     tree.set< config::int_node >("root.a.b.c.second", 2);
908     tree.set< config::int_node >("root.a.d.first", 3);
909 
910     config::properties_map exp_properties;
911     exp_properties["b.c.first"] = "1";
912     exp_properties["b.c.second"] = "2";
913     exp_properties["d.first"] = "3";
914     ATF_REQUIRE(exp_properties == tree.all_properties("root.a", true));
915 }
916 
917 
918 ATF_TEST_CASE_WITHOUT_HEAD(all_properties__subtree__invalid_key);
919 ATF_TEST_CASE_BODY(all_properties__subtree__invalid_key)
920 {
921     config::tree tree;
922 
923     ATF_REQUIRE_THROW(config::invalid_key_error, tree.all_properties("."));
924 }
925 
926 
927 ATF_TEST_CASE_WITHOUT_HEAD(all_properties__subtree__unknown_key);
928 ATF_TEST_CASE_BODY(all_properties__subtree__unknown_key)
929 {
930     config::tree tree;
931 
932     tree.define< config::int_node >("root.a.b.c.first");
933     tree.set< config::int_node >("root.a.b.c.first", 1);
934     tree.define< config::int_node >("root.a.b.c.unset");
935 
936     ATF_REQUIRE_THROW(config::unknown_key_error,
937                       tree.all_properties("root.a.b.c.first.foo"));
938     ATF_REQUIRE_THROW_RE(config::value_error, "Cannot export.*leaf",
939                          tree.all_properties("root.a.b.c.unset"));
940 }
941 
942 
943 ATF_TEST_CASE_WITHOUT_HEAD(operators_eq_and_ne__empty);
944 ATF_TEST_CASE_BODY(operators_eq_and_ne__empty)
945 {
946     config::tree t1;
947     config::tree t2;
948     ATF_REQUIRE(  t1 == t2);
949     ATF_REQUIRE(!(t1 != t2));
950 }
951 
952 
953 ATF_TEST_CASE_WITHOUT_HEAD(operators_eq_and_ne__shallow_copy);
954 ATF_TEST_CASE_BODY(operators_eq_and_ne__shallow_copy)
955 {
956     config::tree t1;
957     t1.define< config::int_node >("root.a.b.c.first");
958     t1.set< config::int_node >("root.a.b.c.first", 1);
959     config::tree t2 = t1;
960     ATF_REQUIRE(  t1 == t2);
961     ATF_REQUIRE(!(t1 != t2));
962 }
963 
964 
965 ATF_TEST_CASE_WITHOUT_HEAD(operators_eq_and_ne__deep_copy);
966 ATF_TEST_CASE_BODY(operators_eq_and_ne__deep_copy)
967 {
968     config::tree t1;
969     t1.define< config::int_node >("root.a.b.c.first");
970     t1.set< config::int_node >("root.a.b.c.first", 1);
971     config::tree t2 = t1.deep_copy();
972     ATF_REQUIRE(  t1 == t2);
973     ATF_REQUIRE(!(t1 != t2));
974 }
975 
976 
977 ATF_TEST_CASE_WITHOUT_HEAD(operators_eq_and_ne__some_contents);
978 ATF_TEST_CASE_BODY(operators_eq_and_ne__some_contents)
979 {
980     config::tree t1, t2;
981 
982     t1.define< config::int_node >("root.a.b.c.first");
983     t1.set< config::int_node >("root.a.b.c.first", 1);
984     ATF_REQUIRE(!(t1 == t2));
985     ATF_REQUIRE(  t1 != t2);
986 
987     t2.define< config::int_node >("root.a.b.c.first");
988     t2.set< config::int_node >("root.a.b.c.first", 1);
989     ATF_REQUIRE(  t1 == t2);
990     ATF_REQUIRE(!(t1 != t2));
991 
992     t1.set< config::int_node >("root.a.b.c.first", 2);
993     ATF_REQUIRE(!(t1 == t2));
994     ATF_REQUIRE(  t1 != t2);
995 
996     t2.set< config::int_node >("root.a.b.c.first", 2);
997     ATF_REQUIRE(  t1 == t2);
998     ATF_REQUIRE(!(t1 != t2));
999 
1000     t1.define< config::string_node >("another.key");
1001     t1.set< config::string_node >("another.key", "some text");
1002     ATF_REQUIRE(!(t1 == t2));
1003     ATF_REQUIRE(  t1 != t2);
1004 
1005     t2.define< config::string_node >("another.key");
1006     t2.set< config::string_node >("another.key", "some text");
1007     ATF_REQUIRE(  t1 == t2);
1008     ATF_REQUIRE(!(t1 != t2));
1009 }
1010 
1011 
1012 ATF_TEST_CASE_WITHOUT_HEAD(custom_leaf__no_default_ctor);
1013 ATF_TEST_CASE_BODY(custom_leaf__no_default_ctor)
1014 {
1015     config::tree tree;
1016 
1017     tree.define< wrapped_int_node >("test1");
1018     tree.define< wrapped_int_node >("test2");
1019     tree.set< wrapped_int_node >("test1", int_wrapper(5));
1020     tree.set< wrapped_int_node >("test2", int_wrapper(10));
1021     const int_wrapper& test1 = tree.lookup< wrapped_int_node >("test1");
1022     ATF_REQUIRE_EQ(5, test1.value());
1023     const int_wrapper& test2 = tree.lookup< wrapped_int_node >("test2");
1024     ATF_REQUIRE_EQ(10, test2.value());
1025 }
1026 
1027 
1028 ATF_INIT_TEST_CASES(tcs)
1029 {
1030     ATF_ADD_TEST_CASE(tcs, define_set_lookup__one_level);
1031     ATF_ADD_TEST_CASE(tcs, define_set_lookup__multiple_levels);
1032 
1033     ATF_ADD_TEST_CASE(tcs, deep_copy__empty);
1034     ATF_ADD_TEST_CASE(tcs, deep_copy__some);
1035 
1036     ATF_ADD_TEST_CASE(tcs, combine__empty);
1037     ATF_ADD_TEST_CASE(tcs, combine__same_layout__no_overrides);
1038     ATF_ADD_TEST_CASE(tcs, combine__same_layout__no_base);
1039     ATF_ADD_TEST_CASE(tcs, combine__same_layout__mix);
1040     ATF_ADD_TEST_CASE(tcs, combine__different_layout);
1041     ATF_ADD_TEST_CASE(tcs, combine__dynamic_wins);
1042     ATF_ADD_TEST_CASE(tcs, combine__inner_leaf_mismatch);
1043 
1044     ATF_ADD_TEST_CASE(tcs, lookup__invalid_key);
1045     ATF_ADD_TEST_CASE(tcs, lookup__unknown_key);
1046 
1047     ATF_ADD_TEST_CASE(tcs, is_set__one_level);
1048     ATF_ADD_TEST_CASE(tcs, is_set__multiple_levels);
1049     ATF_ADD_TEST_CASE(tcs, is_set__invalid_key);
1050 
1051     ATF_ADD_TEST_CASE(tcs, set__invalid_key);
1052     ATF_ADD_TEST_CASE(tcs, set__invalid_key_value);
1053     ATF_ADD_TEST_CASE(tcs, set__unknown_key);
1054     ATF_ADD_TEST_CASE(tcs, set__unknown_key_not_strict);
1055 
1056     ATF_ADD_TEST_CASE(tcs, push_lua__ok);
1057     ATF_ADD_TEST_CASE(tcs, set_lua__ok);
1058 
1059     ATF_ADD_TEST_CASE(tcs, lookup_rw);
1060 
1061     ATF_ADD_TEST_CASE(tcs, lookup_string__ok);
1062     ATF_ADD_TEST_CASE(tcs, lookup_string__invalid_key);
1063     ATF_ADD_TEST_CASE(tcs, lookup_string__unknown_key);
1064 
1065     ATF_ADD_TEST_CASE(tcs, set_string__ok);
1066     ATF_ADD_TEST_CASE(tcs, set_string__invalid_key);
1067     ATF_ADD_TEST_CASE(tcs, set_string__invalid_key_value);
1068     ATF_ADD_TEST_CASE(tcs, set_string__unknown_key);
1069     ATF_ADD_TEST_CASE(tcs, set_string__unknown_key_not_strict);
1070 
1071     ATF_ADD_TEST_CASE(tcs, all_properties__none);
1072     ATF_ADD_TEST_CASE(tcs, all_properties__all_set);
1073     ATF_ADD_TEST_CASE(tcs, all_properties__some_unset);
1074     ATF_ADD_TEST_CASE(tcs, all_properties__subtree__inner);
1075     ATF_ADD_TEST_CASE(tcs, all_properties__subtree__leaf);
1076     ATF_ADD_TEST_CASE(tcs, all_properties__subtree__strip_key);
1077     ATF_ADD_TEST_CASE(tcs, all_properties__subtree__invalid_key);
1078     ATF_ADD_TEST_CASE(tcs, all_properties__subtree__unknown_key);
1079 
1080     ATF_ADD_TEST_CASE(tcs, operators_eq_and_ne__empty);
1081     ATF_ADD_TEST_CASE(tcs, operators_eq_and_ne__shallow_copy);
1082     ATF_ADD_TEST_CASE(tcs, operators_eq_and_ne__deep_copy);
1083     ATF_ADD_TEST_CASE(tcs, operators_eq_and_ne__some_contents);
1084 
1085     ATF_ADD_TEST_CASE(tcs, custom_leaf__no_default_ctor);
1086 }
1087