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 50namespace config = utils::config; 51namespace datetime = utils::datetime; 52namespace fs = utils::fs; 53namespace text = utils::text; 54namespace units = utils::units; 55 56using utils::optional; 57 58 59namespace { 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. 66static optional< config::tree > defaults; 67 68 69/// A leaf node that holds a bytes quantity. 70class bytes_node : public config::native_leaf_node< units::bytes > { 71public: 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. 100class delta_node : public config::typed_leaf_node< datetime::delta > { 101public: 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. 161class 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. 192class 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. 243static void 244init_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. 265static void 266set_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. 292const config::tree& 293get_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. 314template< class NodeType > 315typename NodeType::value_type& 316lookup_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. 337template< class NodeType > 338void 339set(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. 357struct 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. 386model::metadata::metadata(const utils::config::tree& props) : 387 _pimpl(new impl(props)) 388{ 389} 390 391 392/// Destructor. 393model::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. 404model::metadata 405model::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. 414const model::strings_set& 415model::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. 430const model::strings_set& 431model::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. 446model::properties_map 447model::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. 456const std::string& 457model::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. 470bool 471model::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. 485bool 486model::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. 499const model::strings_set& 500model::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. 515const units::bytes& 516model::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. 529const model::paths_set& 530model::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. 543const units::bytes& 544model::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. 557const model::paths_set& 558model::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. 571const std::string& 572model::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. 586const datetime::delta& 587model::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. 600model::properties_map 601model::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. 613bool 614model::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. 625bool 626model::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. 638std::ostream& 639model::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. 660struct 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. 686model::metadata_builder::metadata_builder(void) : 687 _pimpl(new impl()) 688{ 689} 690 691 692/// Constructor. 693model::metadata_builder::metadata_builder(const model::metadata& base) : 694 _pimpl(new impl(base)) 695{ 696} 697 698 699/// Destructor. 700model::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. 712model::metadata_builder& 713model::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. 734model::metadata_builder& 735model::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. 757model::metadata_builder& 758model::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. 773model::metadata_builder& 774model::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. 795model::metadata_builder& 796model::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. 815model::metadata_builder& 816model::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. 836model::metadata_builder& 837model::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. 852model::metadata_builder& 853model::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. 867model::metadata_builder& 868model::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. 885model::metadata_builder& 886model::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. 900model::metadata_builder& 901model::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. 915model::metadata_builder& 916model::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. 930model::metadata_builder& 931model::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. 945model::metadata_builder& 946model::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. 960model::metadata_builder& 961model::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. 975model::metadata_builder& 976model::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. 990model::metadata_builder& 991model::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. 1005model::metadata_builder& 1006model::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. 1021model::metadata_builder& 1022model::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. 1044model::metadata_builder& 1045model::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. 1061model::metadata 1062model::metadata_builder::build(void) const 1063{ 1064 PRE(!_pimpl->built); 1065 _pimpl->built = true; 1066 1067 return metadata(_pimpl->props); 1068} 1069