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