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 "engine/metadata.hpp" 30 31 #include <memory> 32 33 #include "engine/exceptions.hpp" 34 #include "utils/config/exceptions.hpp" 35 #include "utils/config/nodes.ipp" 36 #include "utils/config/tree.ipp" 37 #include "utils/datetime.hpp" 38 #include "utils/defs.hpp" 39 #include "utils/format/macros.hpp" 40 #include "utils/fs/exceptions.hpp" 41 #include "utils/fs/operations.hpp" 42 #include "utils/fs/path.hpp" 43 #include "utils/memory.hpp" 44 #include "utils/passwd.hpp" 45 #include "utils/sanity.hpp" 46 #include "utils/text/exceptions.hpp" 47 #include "utils/text/operations.hpp" 48 #include "utils/units.hpp" 49 50 namespace config = utils::config; 51 namespace datetime = utils::datetime; 52 namespace fs = utils::fs; 53 namespace passwd = utils::passwd; 54 namespace text = utils::text; 55 namespace units = utils::units; 56 57 58 /// The default timeout value for test cases that do not provide one. 59 /// TODO(jmmv): We should not be doing this; see issue 5 for details. 60 datetime::delta engine::default_timeout(300, 0); 61 62 63 namespace { 64 65 66 /// A leaf node that holds a bytes quantity. 67 class bytes_node : public config::native_leaf_node< units::bytes > { 68 public: 69 /// Copies the node. 70 /// 71 /// \return A dynamically-allocated node. 72 virtual base_node* 73 deep_copy(void) const 74 { 75 std::unique_ptr< bytes_node > new_node(new bytes_node()); 76 new_node->_value = _value; 77 return new_node.release(); 78 } 79 80 /// Pushes the node's value onto the Lua stack. 81 /// 82 /// \param unused_state The Lua state onto which to push the value. 83 void 84 push_lua(lutok::state& UTILS_UNUSED_PARAM(state)) const 85 { 86 UNREACHABLE; 87 } 88 89 /// Sets the value of the node from an entry in the Lua stack. 90 /// 91 /// \param unused_state The Lua state from which to get the value. 92 /// \param unused_index The stack index in which the value resides. 93 void 94 set_lua(lutok::state& UTILS_UNUSED_PARAM(state), 95 const int UTILS_UNUSED_PARAM(index)) 96 { 97 UNREACHABLE; 98 } 99 }; 100 101 102 /// A leaf node that holds a time delta. 103 class delta_node : public config::typed_leaf_node< datetime::delta > { 104 public: 105 /// Copies the node. 106 /// 107 /// \return A dynamically-allocated node. 108 virtual base_node* 109 deep_copy(void) const 110 { 111 std::unique_ptr< delta_node > new_node(new delta_node()); 112 new_node->_value = _value; 113 return new_node.release(); 114 } 115 116 /// Sets the value of the node from a raw string representation. 117 /// 118 /// \param raw_value The value to set the node to. 119 /// 120 /// \throw value_error If the value is invalid. 121 void 122 set_string(const std::string& raw_value) 123 { 124 unsigned int seconds; 125 try { 126 seconds = text::to_type< unsigned int >(raw_value); 127 } catch (const text::error& e) { 128 throw config::value_error(F("Invalid time delta %s") % raw_value); 129 } 130 set(datetime::delta(seconds, 0)); 131 } 132 133 /// Converts the contents of the node to a string. 134 /// 135 /// \pre The node must have a value. 136 /// 137 /// \return A string representation of the value held by the node. 138 std::string 139 to_string(void) const 140 { 141 return F("%s") % value().seconds; 142 } 143 144 /// Pushes the node's value onto the Lua stack. 145 /// 146 /// \param unused_state The Lua state onto which to push the value. 147 void 148 push_lua(lutok::state& UTILS_UNUSED_PARAM(state)) const 149 { 150 UNREACHABLE; 151 } 152 153 /// Sets the value of the node from an entry in the Lua stack. 154 /// 155 /// \param unused_state The Lua state from which to get the value. 156 /// \param unused_index The stack index in which the value resides. 157 void 158 set_lua(lutok::state& UTILS_UNUSED_PARAM(state), 159 const int UTILS_UNUSED_PARAM(index)) 160 { 161 UNREACHABLE; 162 } 163 }; 164 165 166 /// A leaf node that holds a "required user" property. 167 /// 168 /// This node is just a string, but it provides validation of the only allowed 169 /// values. 170 class user_node : public config::string_node { 171 /// Copies the node. 172 /// 173 /// \return A dynamically-allocated node. 174 virtual base_node* 175 deep_copy(void) const 176 { 177 std::unique_ptr< user_node > new_node(new user_node()); 178 new_node->_value = _value; 179 return new_node.release(); 180 } 181 182 /// Checks a given user textual representation for validity. 183 /// 184 /// \param user The value to validate. 185 /// 186 /// \throw config::value_error If the value is not valid. 187 void 188 validate(const value_type& user) const 189 { 190 if (!user.empty() && user != "root" && user != "unprivileged") 191 throw config::value_error("Invalid required user value"); 192 } 193 }; 194 195 196 /// A leaf node that holds a set of paths. 197 /// 198 /// This node type is used to represent the value of the required files and 199 /// required programs, for example, and these do not allow relative paths. We 200 /// check this here. 201 class paths_set_node : public config::base_set_node< fs::path > { 202 /// Copies the node. 203 /// 204 /// \return A dynamically-allocated node. 205 virtual base_node* 206 deep_copy(void) const 207 { 208 std::unique_ptr< paths_set_node > new_node(new paths_set_node()); 209 new_node->_value = _value; 210 return new_node.release(); 211 } 212 213 /// Converts a single path to the native type. 214 /// 215 /// \param raw_value The value to parse. 216 /// 217 /// \return The parsed value. 218 /// 219 /// \throw config::value_error If the value is invalid. 220 fs::path 221 parse_one(const std::string& raw_value) const 222 { 223 try { 224 return fs::path(raw_value); 225 } catch (const fs::error& e) { 226 throw config::value_error(e.what()); 227 } 228 } 229 230 /// Checks a collection of paths for validity. 231 /// 232 /// \param paths The value to validate. 233 /// 234 /// \throw config::value_error If the value is not valid. 235 void 236 validate(const value_type& paths) const 237 { 238 for (value_type::const_iterator iter = paths.begin(); 239 iter != paths.end(); ++iter) { 240 const fs::path& path = *iter; 241 if (!path.is_absolute() && path.ncomponents() > 1) 242 throw config::value_error(F("Relative path '%s' not allowed") % 243 *iter); 244 } 245 } 246 }; 247 248 249 /// Initializes a tree to hold test case requirements. 250 /// 251 /// \param [in,out] tree The tree to initialize. 252 static void 253 init_tree(config::tree& tree) 254 { 255 tree.define< config::strings_set_node >("allowed_architectures"); 256 tree.set< config::strings_set_node >("allowed_architectures", 257 engine::strings_set()); 258 259 tree.define< config::strings_set_node >("allowed_platforms"); 260 tree.set< config::strings_set_node >("allowed_platforms", 261 engine::strings_set()); 262 263 tree.define_dynamic("custom"); 264 265 tree.define< config::string_node >("description"); 266 tree.set< config::string_node >("description", ""); 267 268 tree.define< config::bool_node >("has_cleanup"); 269 tree.set< config::bool_node >("has_cleanup", false); 270 271 tree.define< config::strings_set_node >("required_configs"); 272 tree.set< config::strings_set_node >("required_configs", 273 engine::strings_set()); 274 275 tree.define< paths_set_node >("required_files"); 276 tree.set< paths_set_node >("required_files", engine::paths_set()); 277 278 tree.define< bytes_node >("required_memory"); 279 tree.set< bytes_node >("required_memory", units::bytes(0)); 280 281 tree.define< paths_set_node >("required_programs"); 282 tree.set< paths_set_node >("required_programs", engine::paths_set()); 283 284 tree.define< user_node >("required_user"); 285 tree.set< user_node >("required_user", ""); 286 287 tree.define< delta_node >("timeout"); 288 tree.set< delta_node >("timeout", engine::default_timeout); 289 } 290 291 292 /// Looks up a value in a tree with error rewriting. 293 /// 294 /// \tparam NodeType The type of the node. 295 /// \param tree The tree in which to insert the value. 296 /// \param key The key to set. 297 /// 298 /// \return A read-write reference to the value in the node. 299 /// 300 /// \throw engine::error If the key is not known or if the value is not valid. 301 template< class NodeType > 302 typename NodeType::value_type& 303 lookup_rw(config::tree& tree, const std::string& key) 304 { 305 try { 306 return tree.lookup_rw< NodeType >(key); 307 } catch (const config::unknown_key_error& e) { 308 throw engine::error(F("Unknown metadata property %s") % key); 309 } catch (const config::value_error& e) { 310 throw engine::error(F("Invalid value for metadata property %s: %s") % 311 key % e.what()); 312 } 313 } 314 315 316 /// Sets a value in a tree with error rewriting. 317 /// 318 /// \tparam NodeType The type of the node. 319 /// \param tree The tree in which to insert the value. 320 /// \param key The key to set. 321 /// \param value The value to set the node to. 322 /// 323 /// \throw engine::error If the key is not known or if the value is not valid. 324 template< class NodeType > 325 void 326 set(config::tree& tree, const std::string& key, 327 const typename NodeType::value_type& value) 328 { 329 try { 330 tree.set< NodeType >(key, value); 331 } catch (const config::unknown_key_error& e) { 332 throw engine::error(F("Unknown metadata property %s") % key); 333 } catch (const config::value_error& e) { 334 throw engine::error(F("Invalid value for metadata property %s: %s") % 335 key % e.what()); 336 } 337 } 338 339 340 /// Checks if all required configuration variables are present. 341 /// 342 /// \param required_configs Set of required variable names. 343 /// \param user_config Runtime user configuration. 344 /// \param test_suite_name Name of the test suite the test belongs to. 345 /// 346 /// \return Empty if all variables are present or an error message otherwise. 347 static std::string 348 check_required_configs(const engine::strings_set& required_configs, 349 const config::tree& user_config, 350 const std::string& test_suite_name) 351 { 352 for (engine::strings_set::const_iterator iter = required_configs.begin(); 353 iter != required_configs.end(); iter++) { 354 std::string property; 355 // TODO(jmmv): All this rewrite logic belongs in the ATF interface. 356 if ((*iter) == "unprivileged-user" || (*iter) == "unprivileged_user") 357 property = "unprivileged_user"; 358 else 359 property = F("test_suites.%s.%s") % test_suite_name % (*iter); 360 361 if (!user_config.is_set(property)) 362 return F("Required configuration property '%s' not defined") % 363 (*iter); 364 } 365 return ""; 366 } 367 368 369 /// Checks if the allowed architectures match the current architecture. 370 /// 371 /// \param allowed_architectures Set of allowed architectures. 372 /// \param user_config Runtime user configuration. 373 /// 374 /// \return Empty if the current architecture is in the list or an error 375 /// message otherwise. 376 static std::string 377 check_allowed_architectures(const engine::strings_set& allowed_architectures, 378 const config::tree& user_config) 379 { 380 if (!allowed_architectures.empty()) { 381 const std::string architecture = 382 user_config.lookup< config::string_node >("architecture"); 383 if (allowed_architectures.find(architecture) == 384 allowed_architectures.end()) 385 return F("Current architecture '%s' not supported") % architecture; 386 } 387 return ""; 388 } 389 390 391 /// Checks if the allowed platforms match the current architecture. 392 /// 393 /// \param allowed_platforms Set of allowed platforms. 394 /// \param user_config Runtime user configuration. 395 /// 396 /// \return Empty if the current platform is in the list or an error message 397 /// otherwise. 398 static std::string 399 check_allowed_platforms(const engine::strings_set& allowed_platforms, 400 const config::tree& user_config) 401 { 402 if (!allowed_platforms.empty()) { 403 const std::string platform = 404 user_config.lookup< config::string_node >("platform"); 405 if (allowed_platforms.find(platform) == allowed_platforms.end()) 406 return F("Current platform '%s' not supported") % platform; 407 } 408 return ""; 409 } 410 411 412 /// Checks if the current user matches the required user. 413 /// 414 /// \param required_user Name of the required user category. 415 /// \param user_config Runtime user configuration. 416 /// 417 /// \return Empty if the current user fits the required user characteristics or 418 /// an error message otherwise. 419 static std::string 420 check_required_user(const std::string& required_user, 421 const config::tree& user_config) 422 { 423 if (!required_user.empty()) { 424 const passwd::user user = passwd::current_user(); 425 if (required_user == "root") { 426 if (!user.is_root()) 427 return "Requires root privileges"; 428 } else if (required_user == "unprivileged") { 429 if (user.is_root()) 430 if (!user_config.is_set("unprivileged_user")) 431 return "Requires an unprivileged user but the " 432 "unprivileged-user configuration variable is not " 433 "defined"; 434 } else 435 UNREACHABLE_MSG("Value of require.user not properly validated"); 436 } 437 return ""; 438 } 439 440 441 /// Checks if all required files exist. 442 /// 443 /// \param required_files Set of paths. 444 /// 445 /// \return Empty if the required files all exist or an error message otherwise. 446 static std::string 447 check_required_files(const engine::paths_set& required_files) 448 { 449 for (engine::paths_set::const_iterator iter = required_files.begin(); 450 iter != required_files.end(); iter++) { 451 INV((*iter).is_absolute()); 452 if (!fs::exists(*iter)) 453 return F("Required file '%s' not found") % *iter; 454 } 455 return ""; 456 } 457 458 459 /// Checks if all required programs exist. 460 /// 461 /// \param required_programs Set of paths. 462 /// 463 /// \return Empty if the required programs all exist or an error message 464 /// otherwise. 465 static std::string 466 check_required_programs(const engine::paths_set& required_programs) 467 { 468 for (engine::paths_set::const_iterator iter = required_programs.begin(); 469 iter != required_programs.end(); iter++) { 470 if ((*iter).is_absolute()) { 471 if (!fs::exists(*iter)) 472 return F("Required program '%s' not found") % *iter; 473 } else { 474 if (!fs::find_in_path((*iter).c_str())) 475 return F("Required program '%s' not found in PATH") % *iter; 476 } 477 } 478 return ""; 479 } 480 481 482 /// Checks if the current system has the specified amount of memory. 483 /// 484 /// \param required_memory Amount of required physical memory, or zero if not 485 /// applicable. 486 /// 487 /// \return Empty if the current system has the required amount of memory or an 488 /// error message otherwise. 489 static std::string 490 check_required_memory(const units::bytes& required_memory) 491 { 492 if (required_memory > 0) { 493 const units::bytes physical_memory = utils::physical_memory(); 494 if (physical_memory > 0 && physical_memory < required_memory) 495 return F("Requires %s bytes of physical memory but only %s " 496 "available") % 497 required_memory.format() % physical_memory.format(); 498 } 499 return ""; 500 } 501 502 503 } // anonymous namespace 504 505 506 /// Internal implementation of the metadata class. 507 struct engine::metadata::impl { 508 /// Metadata properties. 509 config::tree props; 510 511 /// Constructor. 512 /// 513 /// \param props_ Metadata properties of the test. 514 impl(const utils::config::tree& props_) : 515 props(props_) 516 { 517 } 518 519 /// Equality comparator. 520 /// 521 /// \param other The other object to compare this one to. 522 /// 523 /// \return True if this object and other are equal; false otherwise. 524 bool 525 operator==(const impl& other) const 526 { 527 return props == other.props; 528 } 529 }; 530 531 532 /// Constructor. 533 /// 534 /// \param props Metadata properties of the test. 535 engine::metadata::metadata(const utils::config::tree& props) : 536 _pimpl(new impl(props)) 537 { 538 } 539 540 541 /// Destructor. 542 engine::metadata::~metadata(void) 543 { 544 } 545 546 547 /// Returns the architectures allowed by the test. 548 /// 549 /// \return Set of architectures, or empty if this does not apply. 550 const engine::strings_set& 551 engine::metadata::allowed_architectures(void) const 552 { 553 return _pimpl->props.lookup< config::strings_set_node >( 554 "allowed_architectures"); 555 } 556 557 558 /// Returns the platforms allowed by the test. 559 /// 560 /// \return Set of platforms, or empty if this does not apply. 561 const engine::strings_set& 562 engine::metadata::allowed_platforms(void) const 563 { 564 return _pimpl->props.lookup< config::strings_set_node >("allowed_platforms"); 565 } 566 567 568 /// Returns all the user-defined metadata properties. 569 /// 570 /// \return A key/value map of properties. 571 engine::properties_map 572 engine::metadata::custom(void) const 573 { 574 return _pimpl->props.all_properties("custom", true); 575 } 576 577 578 /// Returns the description of the test. 579 /// 580 /// \return Textual description; may be empty. 581 const std::string& 582 engine::metadata::description(void) const 583 { 584 return _pimpl->props.lookup< config::string_node >("description"); 585 } 586 587 588 /// Returns whether the test has a cleanup part or not. 589 /// 590 /// \return True if there is a cleanup part; false otherwise. 591 bool 592 engine::metadata::has_cleanup(void) const 593 { 594 return _pimpl->props.lookup< config::bool_node >("has_cleanup"); 595 } 596 597 598 /// Returns the list of configuration variables needed by the test. 599 /// 600 /// \return Set of configuration variables. 601 const engine::strings_set& 602 engine::metadata::required_configs(void) const 603 { 604 return _pimpl->props.lookup< config::strings_set_node >("required_configs"); 605 } 606 607 608 /// Returns the list of files needed by the test. 609 /// 610 /// \return Set of paths. 611 const engine::paths_set& 612 engine::metadata::required_files(void) const 613 { 614 return _pimpl->props.lookup< paths_set_node >("required_files"); 615 } 616 617 618 /// Returns the amount of memory required by the test. 619 /// 620 /// \return Number of bytes, or 0 if this does not apply. 621 const units::bytes& 622 engine::metadata::required_memory(void) const 623 { 624 return _pimpl->props.lookup< bytes_node >("required_memory"); 625 } 626 627 628 /// Returns the list of programs needed by the test. 629 /// 630 /// \return Set of paths. 631 const engine::paths_set& 632 engine::metadata::required_programs(void) const 633 { 634 return _pimpl->props.lookup< paths_set_node >("required_programs"); 635 } 636 637 638 /// Returns the user required by the test. 639 /// 640 /// \return One of unprivileged, root or empty. 641 const std::string& 642 engine::metadata::required_user(void) const 643 { 644 return _pimpl->props.lookup< user_node >("required_user"); 645 } 646 647 648 /// Returns the timeout of the test. 649 /// 650 /// \return A time delta; should be compared to default_timeout to see if it has 651 /// been overriden. 652 const datetime::delta& 653 engine::metadata::timeout(void) const 654 { 655 return _pimpl->props.lookup< delta_node >("timeout"); 656 } 657 658 659 /// Externalizes the metadata to a set of key/value textual pairs. 660 /// 661 /// \return A key/value representation of the metadata. 662 engine::properties_map 663 engine::metadata::to_properties(void) const 664 { 665 return _pimpl->props.all_properties(); 666 } 667 668 669 /// Equality comparator. 670 /// 671 /// \param other The other object to compare this one to. 672 /// 673 /// \return True if this object and other are equal; false otherwise. 674 bool 675 engine::metadata::operator==(const metadata& other) const 676 { 677 return _pimpl == other._pimpl || *_pimpl == *other._pimpl; 678 } 679 680 681 /// Inequality comparator. 682 /// 683 /// \param other The other object to compare this one to. 684 /// 685 /// \return True if this object and other are different; false otherwise. 686 bool 687 engine::metadata::operator!=(const metadata& other) const 688 { 689 return !(*this == other); 690 } 691 692 693 /// Injects the object into a stream. 694 /// 695 /// \param output The stream into which to inject the object. 696 /// \param object The object to format. 697 /// 698 /// \return The output stream. 699 std::ostream& 700 engine::operator<<(std::ostream& output, const metadata& object) 701 { 702 output << "metadata{"; 703 704 bool first = true; 705 const engine::properties_map props = object.to_properties(); 706 for (engine::properties_map::const_iterator iter = props.begin(); 707 iter != props.end(); ++iter) { 708 if (!first) 709 output << ", "; 710 output << F("%s=%s") % (*iter).first % 711 text::quote((*iter).second, '\''); 712 first = false; 713 } 714 715 output << "}"; 716 return output; 717 } 718 719 720 /// Internal implementation of the metadata_builder class. 721 struct engine::metadata_builder::impl { 722 /// Collection of requirements. 723 config::tree props; 724 725 /// Whether we have created a metadata object or not. 726 bool built; 727 728 /// Constructor. 729 impl(void) : 730 built(false) 731 { 732 init_tree(props); 733 } 734 735 /// Constructor. 736 impl(const engine::metadata& base) : 737 props(base._pimpl->props.deep_copy()), 738 built(false) 739 { 740 } 741 }; 742 743 744 /// Constructor. 745 engine::metadata_builder::metadata_builder(void) : 746 _pimpl(new impl()) 747 { 748 } 749 750 751 /// Constructor. 752 engine::metadata_builder::metadata_builder(const engine::metadata& base) : 753 _pimpl(new impl(base)) 754 { 755 } 756 757 758 /// Destructor. 759 engine::metadata_builder::~metadata_builder(void) 760 { 761 } 762 763 764 /// Accumulates an additional allowed architecture. 765 /// 766 /// \param arch The architecture. 767 /// 768 /// \return A reference to this builder. 769 /// 770 /// \throw engine::error If the value is invalid. 771 engine::metadata_builder& 772 engine::metadata_builder::add_allowed_architecture(const std::string& arch) 773 { 774 lookup_rw< config::strings_set_node >( 775 _pimpl->props, "allowed_architectures").insert(arch); 776 return *this; 777 } 778 779 780 /// Accumulates an additional allowed platform. 781 /// 782 /// \param platform The platform. 783 /// 784 /// \return A reference to this builder. 785 /// 786 /// \throw engine::error If the value is invalid. 787 engine::metadata_builder& 788 engine::metadata_builder::add_allowed_platform(const std::string& platform) 789 { 790 lookup_rw< config::strings_set_node >( 791 _pimpl->props, "allowed_platforms").insert(platform); 792 return *this; 793 } 794 795 796 /// Accumulates a single user-defined property. 797 /// 798 /// \param key Name of the property to define. 799 /// \param value Value of the property. 800 /// 801 /// \return A reference to this builder. 802 /// 803 /// \throw engine::error If the value is invalid. 804 engine::metadata_builder& 805 engine::metadata_builder::add_custom(const std::string& key, 806 const std::string& value) 807 { 808 _pimpl->props.set_string(F("custom.%s") % key, value); 809 return *this; 810 } 811 812 813 /// Accumulates an additional required configuration variable. 814 /// 815 /// \param var The name of the configuration variable. 816 /// 817 /// \return A reference to this builder. 818 /// 819 /// \throw engine::error If the value is invalid. 820 engine::metadata_builder& 821 engine::metadata_builder::add_required_config(const std::string& var) 822 { 823 lookup_rw< config::strings_set_node >( 824 _pimpl->props, "required_configs").insert(var); 825 return *this; 826 } 827 828 829 /// Accumulates an additional required file. 830 /// 831 /// \param path The path to the file. 832 /// 833 /// \return A reference to this builder. 834 /// 835 /// \throw engine::error If the value is invalid. 836 engine::metadata_builder& 837 engine::metadata_builder::add_required_file(const fs::path& path) 838 { 839 lookup_rw< paths_set_node >(_pimpl->props, "required_files").insert(path); 840 return *this; 841 } 842 843 844 /// Accumulates an additional required program. 845 /// 846 /// \param path The path to the program. 847 /// 848 /// \return A reference to this builder. 849 /// 850 /// \throw engine::error If the value is invalid. 851 engine::metadata_builder& 852 engine::metadata_builder::add_required_program(const fs::path& path) 853 { 854 lookup_rw< paths_set_node >(_pimpl->props, 855 "required_programs").insert(path); 856 return *this; 857 } 858 859 860 /// Sets the architectures allowed by the test. 861 /// 862 /// \param as Set of architectures. 863 /// 864 /// \return A reference to this builder. 865 /// 866 /// \throw engine::error If the value is invalid. 867 engine::metadata_builder& 868 engine::metadata_builder::set_allowed_architectures(const strings_set& as) 869 { 870 set< config::strings_set_node >(_pimpl->props, "allowed_architectures", as); 871 return *this; 872 } 873 874 875 /// Sets the platforms allowed by the test. 876 /// 877 /// \return ps Set of platforms. 878 /// 879 /// \return A reference to this builder. 880 /// 881 /// \throw engine::error If the value is invalid. 882 engine::metadata_builder& 883 engine::metadata_builder::set_allowed_platforms(const strings_set& ps) 884 { 885 set< config::strings_set_node >(_pimpl->props, "allowed_platforms", ps); 886 return *this; 887 } 888 889 890 /// Sets the user-defined properties. 891 /// 892 /// \param props The custom properties to set. 893 /// 894 /// \return A reference to this builder. 895 /// 896 /// \throw engine::error If the value is invalid. 897 engine::metadata_builder& 898 engine::metadata_builder::set_custom(const properties_map& props) 899 { 900 for (properties_map::const_iterator iter = props.begin(); 901 iter != props.end(); ++iter) 902 _pimpl->props.set_string(F("custom.%s") % (*iter).first, 903 (*iter).second); 904 return *this; 905 } 906 907 908 /// Sets the description of the test. 909 /// 910 /// \param description Textual description of the test. 911 /// 912 /// \return A reference to this builder. 913 /// 914 /// \throw engine::error If the value is invalid. 915 engine::metadata_builder& 916 engine::metadata_builder::set_description(const std::string& description) 917 { 918 set< config::string_node >(_pimpl->props, "description", description); 919 return *this; 920 } 921 922 923 /// Sets whether the test has a cleanup part or not. 924 /// 925 /// \param cleanup True if the test has a cleanup part; false otherwise. 926 /// 927 /// \return A reference to this builder. 928 /// 929 /// \throw engine::error If the value is invalid. 930 engine::metadata_builder& 931 engine::metadata_builder::set_has_cleanup(const bool cleanup) 932 { 933 set< config::bool_node >(_pimpl->props, "has_cleanup", cleanup); 934 return *this; 935 } 936 937 938 /// Sets the list of configuration variables needed by the test. 939 /// 940 /// \param vars Set of configuration variables. 941 /// 942 /// \return A reference to this builder. 943 /// 944 /// \throw engine::error If the value is invalid. 945 engine::metadata_builder& 946 engine::metadata_builder::set_required_configs(const strings_set& vars) 947 { 948 set< config::strings_set_node >(_pimpl->props, "required_configs", vars); 949 return *this; 950 } 951 952 953 /// Sets the list of files needed by the test. 954 /// 955 /// \param files Set of paths. 956 /// 957 /// \return A reference to this builder. 958 /// 959 /// \throw engine::error If the value is invalid. 960 engine::metadata_builder& 961 engine::metadata_builder::set_required_files(const paths_set& files) 962 { 963 set< paths_set_node >(_pimpl->props, "required_files", files); 964 return *this; 965 } 966 967 968 /// Sets the amount of memory required by the test. 969 /// 970 /// \param bytes Number of bytes. 971 /// 972 /// \return A reference to this builder. 973 /// 974 /// \throw engine::error If the value is invalid. 975 engine::metadata_builder& 976 engine::metadata_builder::set_required_memory(const units::bytes& bytes) 977 { 978 set< bytes_node >(_pimpl->props, "required_memory", bytes); 979 return *this; 980 } 981 982 983 /// Sets the list of programs needed by the test. 984 /// 985 /// \param progs Set of paths. 986 /// 987 /// \return A reference to this builder. 988 /// 989 /// \throw engine::error If the value is invalid. 990 engine::metadata_builder& 991 engine::metadata_builder::set_required_programs(const paths_set& progs) 992 { 993 set< paths_set_node >(_pimpl->props, "required_programs", progs); 994 return *this; 995 } 996 997 998 /// Sets the user required by the test. 999 /// 1000 /// \param user One of unprivileged, root or empty. 1001 /// 1002 /// \return A reference to this builder. 1003 /// 1004 /// \throw engine::error If the value is invalid. 1005 engine::metadata_builder& 1006 engine::metadata_builder::set_required_user(const std::string& user) 1007 { 1008 set< user_node >(_pimpl->props, "required_user", user); 1009 return *this; 1010 } 1011 1012 1013 /// Sets a metadata property by name from its textual representation. 1014 /// 1015 /// \param key The property to set. 1016 /// \param value The value to set the property to. 1017 /// 1018 /// \return A reference to this builder. 1019 /// 1020 /// \throw engine::error If the value is invalid or the key does not exist. 1021 engine::metadata_builder& 1022 engine::metadata_builder::set_string(const std::string& key, 1023 const std::string& value) 1024 { 1025 try { 1026 _pimpl->props.set_string(key, value); 1027 } catch (const config::unknown_key_error& e) { 1028 throw engine::format_error(F("Unknown metadata property %s") % key); 1029 } catch (const config::value_error& e) { 1030 throw engine::format_error( 1031 F("Invalid value for metadata property %s: %s") % key % e.what()); 1032 } 1033 return *this; 1034 } 1035 1036 1037 /// Sets the timeout of the test. 1038 /// 1039 /// \param timeout The timeout to set. 1040 /// 1041 /// \return A reference to this builder. 1042 /// 1043 /// \throw engine::error If the value is invalid. 1044 engine::metadata_builder& 1045 engine::metadata_builder::set_timeout(const datetime::delta& timeout) 1046 { 1047 set< delta_node >(_pimpl->props, "timeout", timeout); 1048 return *this; 1049 } 1050 1051 1052 /// Creates a new metadata object. 1053 /// 1054 /// \pre This has not yet been called. We only support calling this function 1055 /// once due to the way the internal tree works: we pass around references, not 1056 /// deep copies, so if we allowed a second build, we'd encourage reusing the 1057 /// same builder to construct different metadata objects, and this could have 1058 /// unintended consequences. 1059 /// 1060 /// \return The constructed metadata object. 1061 engine::metadata 1062 engine::metadata_builder::build(void) const 1063 { 1064 PRE(!_pimpl->built); 1065 _pimpl->built = true; 1066 1067 return metadata(_pimpl->props); 1068 } 1069 1070 1071 /// Checks if all the requirements specified by the test case are met. 1072 /// 1073 /// \param md The test metadata. 1074 /// \param cfg The engine configuration. 1075 /// \param test_suite Name of the test suite the test belongs to. 1076 /// 1077 /// \return A string describing the reason for skipping the test, or empty if 1078 /// the test should be executed. 1079 std::string 1080 engine::check_reqs(const engine::metadata& md, const config::tree& cfg, 1081 const std::string& test_suite) 1082 { 1083 std::string reason; 1084 1085 reason = check_required_configs(md.required_configs(), cfg, test_suite); 1086 if (!reason.empty()) 1087 return reason; 1088 1089 reason = check_allowed_architectures(md.allowed_architectures(), cfg); 1090 if (!reason.empty()) 1091 return reason; 1092 1093 reason = check_allowed_platforms(md.allowed_platforms(), cfg); 1094 if (!reason.empty()) 1095 return reason; 1096 1097 reason = check_required_user(md.required_user(), cfg); 1098 if (!reason.empty()) 1099 return reason; 1100 1101 reason = check_required_files(md.required_files()); 1102 if (!reason.empty()) 1103 return reason; 1104 1105 reason = check_required_programs(md.required_programs()); 1106 if (!reason.empty()) 1107 return reason; 1108 1109 reason = check_required_memory(md.required_memory()); 1110 if (!reason.empty()) 1111 return reason; 1112 1113 INV(reason.empty()); 1114 return reason; 1115 } 1116