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. 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 /// 100 /// \param unused_raw_value The value to set the node to. 101 void 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 114 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); 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); 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); 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); 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); 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); 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); 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); 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); 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); 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); 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); 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); 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); 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); 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); 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 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