Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/kyua/utils/config/lua_module_test.cpp
48081 views
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/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.
54
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*
68
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
79
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
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
void
100
set_string(const std::string& /* raw_value */)
101
{
102
ATF_FAIL("Should not be used");
103
}
104
105
/// Converts the contents of the node to a string.
106
///
107
/// \post The test case is marked as failed, as this function is not
108
/// supposed to be invoked by the lua_module code.
109
///
110
/// \return Nothing.
111
std::string
112
to_string(void) const
113
{
114
ATF_FAIL("Should not be used");
115
}
116
};
117
118
119
} // anonymous namespace
120
121
122
ATF_TEST_CASE_WITHOUT_HEAD(top__valid_types);
123
ATF_TEST_CASE_BODY(top__valid_types)
124
{
125
config::tree tree;
126
tree.define< config::bool_node >("top_boolean");
127
tree.define< config::int_node >("top_integer");
128
tree.define< config::string_node >("top_string");
129
130
{
131
lutok::state state;
132
config::redirect(state, tree);
133
lutok::do_string(state,
134
"top_boolean = true\n"
135
"top_integer = 12345\n"
136
"top_string = 'a foo'\n",
137
0, 0, 0);
138
}
139
140
ATF_REQUIRE_EQ(true, tree.lookup< config::bool_node >("top_boolean"));
141
ATF_REQUIRE_EQ(12345, tree.lookup< config::int_node >("top_integer"));
142
ATF_REQUIRE_EQ("a foo", tree.lookup< config::string_node >("top_string"));
143
}
144
145
146
ATF_TEST_CASE_WITHOUT_HEAD(top__invalid_types);
147
ATF_TEST_CASE_BODY(top__invalid_types)
148
{
149
config::tree tree;
150
tree.define< config::bool_node >("top_boolean");
151
tree.define< config::int_node >("top_integer");
152
153
{
154
lutok::state state;
155
config::redirect(state, tree);
156
ATF_REQUIRE_THROW_RE(
157
lutok::error,
158
"Invalid value for property 'top_boolean': Not a boolean",
159
lutok::do_string(state,
160
"top_boolean = true\n"
161
"top_integer = 8\n"
162
"top_boolean = 'foo'\n",
163
0, 0, 0));
164
}
165
166
ATF_REQUIRE_EQ(true, tree.lookup< config::bool_node >("top_boolean"));
167
ATF_REQUIRE_EQ(8, tree.lookup< config::int_node >("top_integer"));
168
}
169
170
171
ATF_TEST_CASE_WITHOUT_HEAD(top__reuse);
172
ATF_TEST_CASE_BODY(top__reuse)
173
{
174
config::tree tree;
175
tree.define< config::int_node >("first");
176
tree.define< config::int_node >("second");
177
178
{
179
lutok::state state;
180
config::redirect(state, tree);
181
lutok::do_string(state, "first = 100; second = first * 2", 0, 0, 0);
182
}
183
184
ATF_REQUIRE_EQ(100, tree.lookup< config::int_node >("first"));
185
ATF_REQUIRE_EQ(200, tree.lookup< config::int_node >("second"));
186
}
187
188
189
ATF_TEST_CASE_WITHOUT_HEAD(top__reset);
190
ATF_TEST_CASE_BODY(top__reset)
191
{
192
config::tree tree;
193
tree.define< config::int_node >("first");
194
195
{
196
lutok::state state;
197
config::redirect(state, tree);
198
lutok::do_string(state, "first = 100; first = 200", 0, 0, 0);
199
}
200
201
ATF_REQUIRE_EQ(200, tree.lookup< config::int_node >("first"));
202
}
203
204
205
ATF_TEST_CASE_WITHOUT_HEAD(top__already_set_on_entry);
206
ATF_TEST_CASE_BODY(top__already_set_on_entry)
207
{
208
config::tree tree;
209
tree.define< config::int_node >("first");
210
tree.set< config::int_node >("first", 100);
211
212
{
213
lutok::state state;
214
config::redirect(state, tree);
215
lutok::do_string(state, "first = first * 15", 0, 0, 0);
216
}
217
218
ATF_REQUIRE_EQ(1500, tree.lookup< config::int_node >("first"));
219
}
220
221
222
ATF_TEST_CASE_WITHOUT_HEAD(subtree__valid_types);
223
ATF_TEST_CASE_BODY(subtree__valid_types)
224
{
225
config::tree tree;
226
tree.define< config::bool_node >("root.boolean");
227
tree.define< config::int_node >("root.a.integer");
228
tree.define< config::string_node >("root.string");
229
230
{
231
lutok::state state;
232
config::redirect(state, tree);
233
lutok::do_string(state,
234
"root.boolean = true\n"
235
"root.a.integer = 12345\n"
236
"root.string = 'a foo'\n",
237
0, 0, 0);
238
}
239
240
ATF_REQUIRE_EQ(true, tree.lookup< config::bool_node >("root.boolean"));
241
ATF_REQUIRE_EQ(12345, tree.lookup< config::int_node >("root.a.integer"));
242
ATF_REQUIRE_EQ("a foo", tree.lookup< config::string_node >("root.string"));
243
}
244
245
246
ATF_TEST_CASE_WITHOUT_HEAD(subtree__reuse);
247
ATF_TEST_CASE_BODY(subtree__reuse)
248
{
249
config::tree tree;
250
tree.define< config::int_node >("a.first");
251
tree.define< config::int_node >("a.second");
252
253
{
254
lutok::state state;
255
config::redirect(state, tree);
256
lutok::do_string(state, "a.first = 100; a.second = a.first * 2",
257
0, 0, 0);
258
}
259
260
ATF_REQUIRE_EQ(100, tree.lookup< config::int_node >("a.first"));
261
ATF_REQUIRE_EQ(200, tree.lookup< config::int_node >("a.second"));
262
}
263
264
265
ATF_TEST_CASE_WITHOUT_HEAD(subtree__reset);
266
ATF_TEST_CASE_BODY(subtree__reset)
267
{
268
config::tree tree;
269
tree.define< config::int_node >("a.first");
270
271
{
272
lutok::state state;
273
config::redirect(state, tree);
274
lutok::do_string(state, "a.first = 100; a.first = 200", 0, 0, 0);
275
}
276
277
ATF_REQUIRE_EQ(200, tree.lookup< config::int_node >("a.first"));
278
}
279
280
281
ATF_TEST_CASE_WITHOUT_HEAD(subtree__already_set_on_entry);
282
ATF_TEST_CASE_BODY(subtree__already_set_on_entry)
283
{
284
config::tree tree;
285
tree.define< config::int_node >("a.first");
286
tree.set< config::int_node >("a.first", 100);
287
288
{
289
lutok::state state;
290
config::redirect(state, tree);
291
lutok::do_string(state, "a.first = a.first * 15", 0, 0, 0);
292
}
293
294
ATF_REQUIRE_EQ(1500, tree.lookup< config::int_node >("a.first"));
295
}
296
297
298
ATF_TEST_CASE_WITHOUT_HEAD(subtree__override_inner);
299
ATF_TEST_CASE_BODY(subtree__override_inner)
300
{
301
config::tree tree;
302
tree.define_dynamic("root");
303
304
{
305
lutok::state state;
306
config::redirect(state, tree);
307
lutok::do_string(state, "root.test = 'a'", 0, 0, 0);
308
ATF_REQUIRE_THROW_RE(lutok::error, "Invalid value for property 'root'",
309
lutok::do_string(state, "root = 'b'", 0, 0, 0));
310
// Ensure that the previous assignment to 'root' did not cause any
311
// inconsistencies in the environment that would prevent a new
312
// assignment from working.
313
lutok::do_string(state, "root.test2 = 'c'", 0, 0, 0);
314
}
315
316
ATF_REQUIRE_EQ("a", tree.lookup< config::string_node >("root.test"));
317
ATF_REQUIRE_EQ("c", tree.lookup< config::string_node >("root.test2"));
318
}
319
320
321
ATF_TEST_CASE_WITHOUT_HEAD(dynamic_subtree__strings);
322
ATF_TEST_CASE_BODY(dynamic_subtree__strings)
323
{
324
config::tree tree;
325
tree.define_dynamic("root");
326
327
lutok::state state;
328
config::redirect(state, tree);
329
lutok::do_string(state,
330
"root.key1 = 1234\n"
331
"root.a.b.key2 = 'foo bar'\n",
332
0, 0, 0);
333
334
ATF_REQUIRE_EQ("1234", tree.lookup< config::string_node >("root.key1"));
335
ATF_REQUIRE_EQ("foo bar",
336
tree.lookup< config::string_node >("root.a.b.key2"));
337
}
338
339
340
ATF_TEST_CASE_WITHOUT_HEAD(dynamic_subtree__invalid_types);
341
ATF_TEST_CASE_BODY(dynamic_subtree__invalid_types)
342
{
343
config::tree tree;
344
tree.define_dynamic("root");
345
346
lutok::state state;
347
config::redirect(state, tree);
348
ATF_REQUIRE_THROW_RE(lutok::error,
349
"Invalid value for property 'root.boolean': "
350
"Not a string",
351
lutok::do_string(state, "root.boolean = true",
352
0, 0, 0));
353
ATF_REQUIRE_THROW_RE(lutok::error,
354
"Invalid value for property 'root.table': "
355
"Not a string",
356
lutok::do_string(state, "root.table = {}",
357
0, 0, 0));
358
ATF_REQUIRE(!tree.is_set("root.boolean"));
359
ATF_REQUIRE(!tree.is_set("root.table"));
360
}
361
362
363
ATF_TEST_CASE_WITHOUT_HEAD(locals);
364
ATF_TEST_CASE_BODY(locals)
365
{
366
config::tree tree;
367
tree.define< config::int_node >("the_key");
368
369
{
370
lutok::state state;
371
config::redirect(state, tree);
372
lutok::do_string(state,
373
"local function generate()\n"
374
" return 15\n"
375
"end\n"
376
"local test_var = 20\n"
377
"the_key = generate() + test_var\n",
378
0, 0, 0);
379
}
380
381
ATF_REQUIRE_EQ(35, tree.lookup< config::int_node >("the_key"));
382
}
383
384
385
ATF_TEST_CASE_WITHOUT_HEAD(custom_node);
386
ATF_TEST_CASE_BODY(custom_node)
387
{
388
config::tree tree;
389
tree.define< custom_node >("key1");
390
tree.define< custom_node >("key2");
391
tree.set< custom_node >("key2", custom_type(10));
392
393
{
394
lutok::state state;
395
config::redirect(state, tree);
396
lutok::do_string(state, "key1 = 512\n", 0, 0, 0);
397
lutok::do_string(state, "key2 = key2 * 2\n", 0, 0, 0);
398
}
399
400
ATF_REQUIRE_EQ(1024, tree.lookup< custom_node >("key1").value);
401
ATF_REQUIRE_EQ(200, tree.lookup< custom_node >("key2").value);
402
}
403
404
405
ATF_TEST_CASE_WITHOUT_HEAD(invalid_key);
406
ATF_TEST_CASE_BODY(invalid_key)
407
{
408
config::tree tree;
409
410
lutok::state state;
411
config::redirect(state, tree);
412
ATF_REQUIRE_THROW_RE(lutok::error, "Empty component in key 'root.'",
413
lutok::do_string(state, "root['']['a'] = 12345\n",
414
0, 0, 0));
415
}
416
417
418
ATF_TEST_CASE_WITHOUT_HEAD(unknown_key);
419
ATF_TEST_CASE_BODY(unknown_key)
420
{
421
config::tree tree;
422
tree.define< config::bool_node >("static.bool");
423
424
lutok::state state;
425
config::redirect(state, tree);
426
ATF_REQUIRE_THROW_RE(lutok::error,
427
"Unknown configuration property 'static.int'",
428
lutok::do_string(state,
429
"static.int = 12345\n",
430
0, 0, 0));
431
}
432
433
434
ATF_TEST_CASE_WITHOUT_HEAD(value_error);
435
ATF_TEST_CASE_BODY(value_error)
436
{
437
config::tree tree;
438
tree.define< config::bool_node >("a.b");
439
440
lutok::state state;
441
config::redirect(state, tree);
442
ATF_REQUIRE_THROW_RE(lutok::error,
443
"Invalid value for property 'a.b': Not a boolean",
444
lutok::do_string(state, "a.b = 12345\n", 0, 0, 0));
445
ATF_REQUIRE_THROW_RE(lutok::error,
446
"Invalid value for property 'a': ",
447
lutok::do_string(state, "a = 1\n", 0, 0, 0));
448
}
449
450
451
ATF_INIT_TEST_CASES(tcs)
452
{
453
ATF_ADD_TEST_CASE(tcs, top__valid_types);
454
ATF_ADD_TEST_CASE(tcs, top__invalid_types);
455
ATF_ADD_TEST_CASE(tcs, top__reuse);
456
ATF_ADD_TEST_CASE(tcs, top__reset);
457
ATF_ADD_TEST_CASE(tcs, top__already_set_on_entry);
458
459
ATF_ADD_TEST_CASE(tcs, subtree__valid_types);
460
ATF_ADD_TEST_CASE(tcs, subtree__reuse);
461
ATF_ADD_TEST_CASE(tcs, subtree__reset);
462
ATF_ADD_TEST_CASE(tcs, subtree__already_set_on_entry);
463
ATF_ADD_TEST_CASE(tcs, subtree__override_inner);
464
465
ATF_ADD_TEST_CASE(tcs, dynamic_subtree__strings);
466
ATF_ADD_TEST_CASE(tcs, dynamic_subtree__invalid_types);
467
468
ATF_ADD_TEST_CASE(tcs, locals);
469
ATF_ADD_TEST_CASE(tcs, custom_node);
470
471
ATF_ADD_TEST_CASE(tcs, invalid_key);
472
ATF_ADD_TEST_CASE(tcs, unknown_key);
473
ATF_ADD_TEST_CASE(tcs, value_error);
474
}
475
476