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::auto_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