xref: /netbsd-src/external/bsd/kyua-cli/dist/utils/config/lua_module_test.cpp (revision 46b85cbbd3d745f264b248ec8702f62b162c7789)
1 // Copyright 2012 Google Inc.
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/lua_module.hpp"
30 
31 #include <atf-c++.hpp>
32 
33 #include <lutok/exceptions.hpp>
34 #include <lutok/operations.hpp>
35 #include <lutok/state.ipp>
36 
37 #include "utils/config/tree.ipp"
38 #include "utils/defs.hpp"
39 
40 namespace config = utils::config;
41 
42 
43 namespace {
44 
45 
46 /// Non-native type to use as a leaf node.
47 struct custom_type {
48     /// The value recorded in the object.
49     int value;
50 
51     /// Constructs a new object.
52     ///
53     /// \param value_ The value to store in the object.
custom_type__anon7348df3f0111::custom_type54     explicit custom_type(const int value_) :
55         value(value_)
56     {
57     }
58 };
59 
60 
61 /// Custom implementation of a node type for testing purposes.
62 class custom_node : public config::typed_leaf_node< custom_type > {
63 public:
64     /// Copies the node.
65     ///
66     /// \return A dynamically-allocated node.
67     virtual base_node*
deep_copy(void) const68     deep_copy(void) const
69     {
70         std::unique_ptr< custom_node > new_node(new custom_node());
71         new_node->_value = _value;
72         return new_node.release();
73     }
74 
75     /// Pushes the node's value onto the Lua stack.
76     ///
77     /// \param state The Lua state onto which to push the value.
78     void
push_lua(lutok::state & state) const79     push_lua(lutok::state& state) const
80     {
81         state.push_integer(value().value * 5);
82     }
83 
84     /// Sets the value of the node from an entry in the Lua stack.
85     ///
86     /// \param state The Lua state from which to get the value.
87     /// \param value_index The stack index in which the value resides.
88     void
set_lua(lutok::state & state,const int value_index)89     set_lua(lutok::state& state, const int value_index)
90     {
91         ATF_REQUIRE(state.is_number(value_index));
92         set(custom_type(state.to_integer(value_index) * 2));
93     }
94 
95     /// Sets the value of the node from a raw string representation.
96     ///
97     /// \post The test case is marked as failed, as this function is not
98     /// supposed to be invoked by the lua_module code.
99     ///
100     /// \param unused_raw_value The value to set the node to.
101     void
set_string(const std::string & UTILS_UNUSED_PARAM (raw_value))102     set_string(const std::string& UTILS_UNUSED_PARAM(raw_value))
103     {
104         ATF_FAIL("Should not be used");
105     }
106 
107     /// Converts the contents of the node to a string.
108     ///
109     /// \post The test case is marked as failed, as this function is not
110     /// supposed to be invoked by the lua_module code.
111     ///
112     /// \return Nothing.
113     std::string
to_string(void) const114     to_string(void) const
115     {
116         ATF_FAIL("Should not be used");
117     }
118 };
119 
120 
121 }  // anonymous namespace
122 
123 
124 ATF_TEST_CASE_WITHOUT_HEAD(top__valid_types);
ATF_TEST_CASE_BODY(top__valid_types)125 ATF_TEST_CASE_BODY(top__valid_types)
126 {
127     config::tree tree;
128     tree.define< config::bool_node >("top_boolean");
129     tree.define< config::int_node >("top_integer");
130     tree.define< config::string_node >("top_string");
131 
132     {
133         lutok::state state;
134         config::redirect(state, tree);
135         lutok::do_string(state,
136                          "top_boolean = true\n"
137                          "top_integer = 12345\n"
138                          "top_string = 'a foo'\n");
139     }
140 
141     ATF_REQUIRE_EQ(true, tree.lookup< config::bool_node >("top_boolean"));
142     ATF_REQUIRE_EQ(12345, tree.lookup< config::int_node >("top_integer"));
143     ATF_REQUIRE_EQ("a foo", tree.lookup< config::string_node >("top_string"));
144 }
145 
146 
147 ATF_TEST_CASE_WITHOUT_HEAD(top__reuse);
ATF_TEST_CASE_BODY(top__reuse)148 ATF_TEST_CASE_BODY(top__reuse)
149 {
150     config::tree tree;
151     tree.define< config::int_node >("first");
152     tree.define< config::int_node >("second");
153 
154     {
155         lutok::state state;
156         config::redirect(state, tree);
157         lutok::do_string(state, "first = 100; second = first * 2");
158     }
159 
160     ATF_REQUIRE_EQ(100, tree.lookup< config::int_node >("first"));
161     ATF_REQUIRE_EQ(200, tree.lookup< config::int_node >("second"));
162 }
163 
164 
165 ATF_TEST_CASE_WITHOUT_HEAD(top__reset);
ATF_TEST_CASE_BODY(top__reset)166 ATF_TEST_CASE_BODY(top__reset)
167 {
168     config::tree tree;
169     tree.define< config::int_node >("first");
170 
171     {
172         lutok::state state;
173         config::redirect(state, tree);
174         lutok::do_string(state, "first = 100; first = 200");
175     }
176 
177     ATF_REQUIRE_EQ(200, tree.lookup< config::int_node >("first"));
178 }
179 
180 
181 ATF_TEST_CASE_WITHOUT_HEAD(top__already_set_on_entry);
ATF_TEST_CASE_BODY(top__already_set_on_entry)182 ATF_TEST_CASE_BODY(top__already_set_on_entry)
183 {
184     config::tree tree;
185     tree.define< config::int_node >("first");
186     tree.set< config::int_node >("first", 100);
187 
188     {
189         lutok::state state;
190         config::redirect(state, tree);
191         lutok::do_string(state, "first = first * 15");
192     }
193 
194     ATF_REQUIRE_EQ(1500, tree.lookup< config::int_node >("first"));
195 }
196 
197 
198 ATF_TEST_CASE_WITHOUT_HEAD(subtree__valid_types);
ATF_TEST_CASE_BODY(subtree__valid_types)199 ATF_TEST_CASE_BODY(subtree__valid_types)
200 {
201     config::tree tree;
202     tree.define< config::bool_node >("root.boolean");
203     tree.define< config::int_node >("root.a.integer");
204     tree.define< config::string_node >("root.string");
205 
206     {
207         lutok::state state;
208         config::redirect(state, tree);
209         lutok::do_string(state,
210                          "root.boolean = true\n"
211                          "root.a.integer = 12345\n"
212                          "root.string = 'a foo'\n");
213     }
214 
215     ATF_REQUIRE_EQ(true, tree.lookup< config::bool_node >("root.boolean"));
216     ATF_REQUIRE_EQ(12345, tree.lookup< config::int_node >("root.a.integer"));
217     ATF_REQUIRE_EQ("a foo", tree.lookup< config::string_node >("root.string"));
218 }
219 
220 
221 ATF_TEST_CASE_WITHOUT_HEAD(subtree__reuse);
ATF_TEST_CASE_BODY(subtree__reuse)222 ATF_TEST_CASE_BODY(subtree__reuse)
223 {
224     config::tree tree;
225     tree.define< config::int_node >("a.first");
226     tree.define< config::int_node >("a.second");
227 
228     {
229         lutok::state state;
230         config::redirect(state, tree);
231         lutok::do_string(state, "a.first = 100; a.second = a.first * 2");
232     }
233 
234     ATF_REQUIRE_EQ(100, tree.lookup< config::int_node >("a.first"));
235     ATF_REQUIRE_EQ(200, tree.lookup< config::int_node >("a.second"));
236 }
237 
238 
239 ATF_TEST_CASE_WITHOUT_HEAD(subtree__reset);
ATF_TEST_CASE_BODY(subtree__reset)240 ATF_TEST_CASE_BODY(subtree__reset)
241 {
242     config::tree tree;
243     tree.define< config::int_node >("a.first");
244 
245     {
246         lutok::state state;
247         config::redirect(state, tree);
248         lutok::do_string(state, "a.first = 100; a.first = 200");
249     }
250 
251     ATF_REQUIRE_EQ(200, tree.lookup< config::int_node >("a.first"));
252 }
253 
254 
255 ATF_TEST_CASE_WITHOUT_HEAD(subtree__already_set_on_entry);
ATF_TEST_CASE_BODY(subtree__already_set_on_entry)256 ATF_TEST_CASE_BODY(subtree__already_set_on_entry)
257 {
258     config::tree tree;
259     tree.define< config::int_node >("a.first");
260     tree.set< config::int_node >("a.first", 100);
261 
262     {
263         lutok::state state;
264         config::redirect(state, tree);
265         lutok::do_string(state, "a.first = a.first * 15");
266     }
267 
268     ATF_REQUIRE_EQ(1500, tree.lookup< config::int_node >("a.first"));
269 }
270 
271 
272 ATF_TEST_CASE_WITHOUT_HEAD(subtree__override_inner);
ATF_TEST_CASE_BODY(subtree__override_inner)273 ATF_TEST_CASE_BODY(subtree__override_inner)
274 {
275     config::tree tree;
276     tree.define_dynamic("root");
277 
278     {
279         lutok::state state;
280         config::redirect(state, tree);
281         lutok::do_string(state, "root.test = 'a'");
282         ATF_REQUIRE_THROW_RE(lutok::error, "Invalid value for key 'root'",
283                              lutok::do_string(state, "root = 'b'"));
284         // Ensure that the previous assignment to 'root' did not cause any
285         // inconsistencies in the environment that would prevent a new
286         // assignment from working.
287         lutok::do_string(state, "root.test2 = 'c'");
288     }
289 
290     ATF_REQUIRE_EQ("a", tree.lookup< config::string_node >("root.test"));
291     ATF_REQUIRE_EQ("c", tree.lookup< config::string_node >("root.test2"));
292 }
293 
294 
295 ATF_TEST_CASE_WITHOUT_HEAD(dynamic_subtree__strings);
ATF_TEST_CASE_BODY(dynamic_subtree__strings)296 ATF_TEST_CASE_BODY(dynamic_subtree__strings)
297 {
298     config::tree tree;
299     tree.define_dynamic("root");
300 
301     lutok::state state;
302     config::redirect(state, tree);
303     lutok::do_string(state,
304                      "root.key1 = 1234\n"
305                      "root.a.b.key2 = 'foo bar'\n");
306 
307     ATF_REQUIRE_EQ("1234", tree.lookup< config::string_node >("root.key1"));
308     ATF_REQUIRE_EQ("foo bar",
309                    tree.lookup< config::string_node >("root.a.b.key2"));
310 }
311 
312 
313 ATF_TEST_CASE_WITHOUT_HEAD(dynamic_subtree__invalid_types);
ATF_TEST_CASE_BODY(dynamic_subtree__invalid_types)314 ATF_TEST_CASE_BODY(dynamic_subtree__invalid_types)
315 {
316     config::tree tree;
317     tree.define_dynamic("root");
318 
319     lutok::state state;
320     config::redirect(state, tree);
321     ATF_REQUIRE_THROW_RE(lutok::error,
322                          "Invalid value for key 'root.boolean' "
323                          "\\(Not a string\\)",
324                          lutok::do_string(state, "root.boolean = true"));
325     ATF_REQUIRE_THROW_RE(lutok::error,
326                          "Invalid value for key 'root.table' "
327                          "\\(Not a string\\)",
328                          lutok::do_string(state, "root.table = {}"));
329     ATF_REQUIRE(!tree.is_set("root.boolean"));
330     ATF_REQUIRE(!tree.is_set("root.table"));
331 }
332 
333 
334 ATF_TEST_CASE_WITHOUT_HEAD(locals);
ATF_TEST_CASE_BODY(locals)335 ATF_TEST_CASE_BODY(locals)
336 {
337     config::tree tree;
338     tree.define< config::int_node >("the_key");
339 
340     {
341         lutok::state state;
342         config::redirect(state, tree);
343         lutok::do_string(state,
344                          "local function generate()\n"
345                          "    return 15\n"
346                          "end\n"
347                          "local test_var = 20\n"
348                          "the_key = generate() + test_var\n");
349     }
350 
351     ATF_REQUIRE_EQ(35, tree.lookup< config::int_node >("the_key"));
352 }
353 
354 
355 ATF_TEST_CASE_WITHOUT_HEAD(custom_node);
ATF_TEST_CASE_BODY(custom_node)356 ATF_TEST_CASE_BODY(custom_node)
357 {
358     config::tree tree;
359     tree.define< custom_node >("key1");
360     tree.define< custom_node >("key2");
361     tree.set< custom_node >("key2", custom_type(10));
362 
363     {
364         lutok::state state;
365         config::redirect(state, tree);
366         lutok::do_string(state, "key1 = 512\n");
367         lutok::do_string(state, "key2 = key2 * 2\n");
368     }
369 
370     ATF_REQUIRE_EQ(1024, tree.lookup< custom_node >("key1").value);
371     ATF_REQUIRE_EQ(200, tree.lookup< custom_node >("key2").value);
372 }
373 
374 
375 ATF_TEST_CASE_WITHOUT_HEAD(invalid_key);
ATF_TEST_CASE_BODY(invalid_key)376 ATF_TEST_CASE_BODY(invalid_key)
377 {
378     config::tree tree;
379 
380     lutok::state state;
381     config::redirect(state, tree);
382     ATF_REQUIRE_THROW_RE(lutok::error, "Empty component in key 'root.'",
383                          lutok::do_string(state, "root['']['a'] = 12345\n"));
384 }
385 
386 
387 ATF_TEST_CASE_WITHOUT_HEAD(unknown_key);
ATF_TEST_CASE_BODY(unknown_key)388 ATF_TEST_CASE_BODY(unknown_key)
389 {
390     config::tree tree;
391     tree.define< config::bool_node >("static.bool");
392 
393     lutok::state state;
394     config::redirect(state, tree);
395     ATF_REQUIRE_THROW_RE(lutok::error,
396                          "Unknown configuration property 'static.int'",
397                          lutok::do_string(state,
398                                           "static.int = 12345\n"));
399 }
400 
401 
402 ATF_TEST_CASE_WITHOUT_HEAD(value_error);
ATF_TEST_CASE_BODY(value_error)403 ATF_TEST_CASE_BODY(value_error)
404 {
405     config::tree tree;
406     tree.define< config::bool_node >("a.b");
407 
408     lutok::state state;
409     config::redirect(state, tree);
410     ATF_REQUIRE_THROW_RE(lutok::error,
411                          "Invalid value for key 'a.b' \\(Not a boolean\\)",
412                          lutok::do_string(state, "a.b = 12345\n"));
413     ATF_REQUIRE_THROW_RE(lutok::error,
414                          "Invalid value for key 'a'",
415                          lutok::do_string(state, "a = 1\n"));
416 }
417 
418 
ATF_INIT_TEST_CASES(tcs)419 ATF_INIT_TEST_CASES(tcs)
420 {
421     ATF_ADD_TEST_CASE(tcs, top__valid_types);
422     ATF_ADD_TEST_CASE(tcs, top__reuse);
423     ATF_ADD_TEST_CASE(tcs, top__reset);
424     ATF_ADD_TEST_CASE(tcs, top__already_set_on_entry);
425 
426     ATF_ADD_TEST_CASE(tcs, subtree__valid_types);
427     ATF_ADD_TEST_CASE(tcs, subtree__reuse);
428     ATF_ADD_TEST_CASE(tcs, subtree__reset);
429     ATF_ADD_TEST_CASE(tcs, subtree__already_set_on_entry);
430     ATF_ADD_TEST_CASE(tcs, subtree__override_inner);
431 
432     ATF_ADD_TEST_CASE(tcs, dynamic_subtree__strings);
433     ATF_ADD_TEST_CASE(tcs, dynamic_subtree__invalid_types);
434 
435     ATF_ADD_TEST_CASE(tcs, locals);
436     ATF_ADD_TEST_CASE(tcs, custom_node);
437 
438     ATF_ADD_TEST_CASE(tcs, invalid_key);
439     ATF_ADD_TEST_CASE(tcs, unknown_key);
440     ATF_ADD_TEST_CASE(tcs, value_error);
441 }
442