Home | History | Annotate | Line # | Download | only in engine
      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