1159571Sgallatin// Copyright 2012 The Kyua Authors.
2155852Sgallatin// All rights reserved.
3247160Sgallatin//
4155852Sgallatin// Redistribution and use in source and binary forms, with or without
5155852Sgallatin// modification, are permitted provided that the following conditions are
6155852Sgallatin// met:
7155852Sgallatin//
8155852Sgallatin// * Redistributions of source code must retain the above copyright
9155852Sgallatin//   notice, this list of conditions and the following disclaimer.
10155852Sgallatin// * Redistributions in binary form must reproduce the above copyright
11155852Sgallatin//   notice, this list of conditions and the following disclaimer in the
12171405Sgallatin//   documentation and/or other materials provided with the distribution.
13155852Sgallatin// * Neither the name of Google Inc. nor the names of its contributors
14155852Sgallatin//   may be used to endorse or promote products derived from this software
15155852Sgallatin//   without specific prior written permission.
16155852Sgallatin//
17155852Sgallatin// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18155852Sgallatin// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19155852Sgallatin// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20155852Sgallatin// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21155852Sgallatin// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22155852Sgallatin// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23155852Sgallatin// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24155852Sgallatin// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25155852Sgallatin// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26155852Sgallatin// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27155852Sgallatin// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28155852Sgallatin
29155852Sgallatin#include "model/metadata.hpp"
30155852Sgallatin
31155852Sgallatin#include <memory>
32155852Sgallatin
33155852Sgallatin#include "model/exceptions.hpp"
34155852Sgallatin#include "model/types.hpp"
35155852Sgallatin#include "utils/config/exceptions.hpp"
36155852Sgallatin#include "utils/config/nodes.ipp"
37155852Sgallatin#include "utils/config/tree.ipp"
38155852Sgallatin#include "utils/datetime.hpp"
39155852Sgallatin#include "utils/defs.hpp"
40155852Sgallatin#include "utils/format/macros.hpp"
41155852Sgallatin#include "utils/fs/exceptions.hpp"
42155852Sgallatin#include "utils/fs/path.hpp"
43168191Sjhb#include "utils/noncopyable.hpp"
44155852Sgallatin#include "utils/optional.ipp"
45155852Sgallatin#include "utils/sanity.hpp"
46155852Sgallatin#include "utils/text/exceptions.hpp"
47155852Sgallatin#include "utils/text/operations.hpp"
48198250Sgallatin#include "utils/units.hpp"
49155852Sgallatin
50155852Sgallatinnamespace config = utils::config;
51155852Sgallatinnamespace datetime = utils::datetime;
52155852Sgallatinnamespace fs = utils::fs;
53155852Sgallatinnamespace text = utils::text;
54155852Sgallatinnamespace units = utils::units;
55155852Sgallatin
56155852Sgallatinusing utils::optional;
57155852Sgallatin
58155852Sgallatin
59155852Sgallatinnamespace {
60155852Sgallatin
61155852Sgallatin
62155852Sgallatin/// Global instance of defaults.
63155852Sgallatin///
64155852Sgallatin/// This exists so that the getters in metadata can return references instead
65247011Sgallatin/// of object copies.  Use get_defaults() to query.
66162322Sgallatinstatic optional< config::tree > defaults;
67247133Sgallatin
68247011Sgallatin
69155852Sgallatin/// A leaf node that holds a bytes quantity.
70155852Sgallatinclass bytes_node : public config::native_leaf_node< units::bytes > {
71169840Sgallatinpublic:
72155852Sgallatin    /// Copies the node.
73155852Sgallatin    ///
74155852Sgallatin    /// \return A dynamically-allocated node.
75175365Sgallatin    virtual base_node*
76155852Sgallatin    deep_copy(void) const
77155852Sgallatin    {
78155852Sgallatin        std::auto_ptr< bytes_node > new_node(new bytes_node());
79180567Sgallatin        new_node->_value = _value;
80155852Sgallatin        return new_node.release();
81155852Sgallatin    }
82155852Sgallatin
83155852Sgallatin    /// Pushes the node's value onto the Lua stack.
84170330Sgallatin    void
85170330Sgallatin    push_lua(lutok::state& /* state */) const
86170330Sgallatin    {
87170330Sgallatin        UNREACHABLE;
88159571Sgallatin    }
89159571Sgallatin
90175365Sgallatin    /// Sets the value of the node from an entry in the Lua stack.
91159571Sgallatin    void
92193311Sgallatin    set_lua(lutok::state& /* state */, const int /* index */)
93193311Sgallatin    {
94193311Sgallatin        UNREACHABLE;
95155852Sgallatin    }
96194743Sgallatin};
97247011Sgallatin
98194743Sgallatin
99155852Sgallatin/// A leaf node that holds a time delta.
100159571Sgallatinclass delta_node : public config::typed_leaf_node< datetime::delta > {
101164513Sgallatinpublic:
102159571Sgallatin    /// Copies the node.
103159612Sgallatin    ///
104159571Sgallatin    /// \return A dynamically-allocated node.
105159612Sgallatin    virtual base_node*
106166373Sgallatin    deep_copy(void) const
107175365Sgallatin    {
108202121Sgallatin        std::auto_ptr< delta_node > new_node(new delta_node());
109175365Sgallatin        new_node->_value = _value;
110194836Sgallatin        return new_node.release();
111197391Sgallatin    }
112159571Sgallatin
113159571Sgallatin    /// Sets the value of the node from a raw string representation.
114175365Sgallatin    ///
115175365Sgallatin    /// \param raw_value The value to set the node to.
116155852Sgallatin    ///
117159571Sgallatin    /// \throw value_error If the value is invalid.
118159571Sgallatin    void
119159571Sgallatin    set_string(const std::string& raw_value)
120159571Sgallatin    {
121159571Sgallatin        unsigned int seconds;
122155852Sgallatin        try {
123159571Sgallatin            seconds = text::to_type< unsigned int >(raw_value);
124155852Sgallatin        } catch (const text::error& e) {
125155852Sgallatin            throw config::value_error(F("Invalid time delta %s") % raw_value);
126159571Sgallatin        }
127159571Sgallatin        set(datetime::delta(seconds, 0));
128159571Sgallatin    }
129159571Sgallatin
130246128Ssbz    /// Converts the contents of the node to a string.
131246128Ssbz    ///
132155852Sgallatin    /// \pre The node must have a value.
133155852Sgallatin    ///
134159571Sgallatin    /// \return A string representation of the value held by the node.
135155852Sgallatin    std::string
136159571Sgallatin    to_string(void) const
137159571Sgallatin    {
138159571Sgallatin        return F("%s") % value().seconds;
139155852Sgallatin    }
140155852Sgallatin
141159571Sgallatin    /// Pushes the node's value onto the Lua stack.
142155852Sgallatin    void
143155852Sgallatin    push_lua(lutok::state& /* state */) const
144159571Sgallatin    {
145159571Sgallatin        UNREACHABLE;
146171500Sgallatin    }
147155852Sgallatin
148175365Sgallatin    /// Sets the value of the node from an entry in the Lua stack.
149169376Sgallatin    void
150197395Sgallatin    set_lua(lutok::state& /* state */, const int /* index */)
151170559Sgallatin    {
152170559Sgallatin        UNREACHABLE;
153169376Sgallatin    }
154155852Sgallatin};
155159571Sgallatin
156155852Sgallatin
157188736Sgallatin/// A leaf node that holds a "required user" property.
158188736Sgallatin///
159188736Sgallatin/// This node is just a string, but it provides validation of the only allowed
160188736Sgallatin/// values.
161188736Sgallatinclass user_node : public config::string_node {
162188736Sgallatin    /// Copies the node.
163188736Sgallatin    ///
164188736Sgallatin    /// \return A dynamically-allocated node.
165188736Sgallatin    virtual base_node*
166188736Sgallatin    deep_copy(void) const
167188736Sgallatin    {
168188736Sgallatin        std::auto_ptr< user_node > new_node(new user_node());
169188736Sgallatin        new_node->_value = _value;
170188736Sgallatin        return new_node.release();
171188736Sgallatin    }
172188736Sgallatin
173188736Sgallatin    /// Checks a given user textual representation for validity.
174188736Sgallatin    ///
175188736Sgallatin    /// \param user The value to validate.
176188736Sgallatin    ///
177188736Sgallatin    /// \throw config::value_error If the value is not valid.
178188736Sgallatin    void
179188736Sgallatin    validate(const value_type& user) const
180155852Sgallatin    {
181155852Sgallatin        if (!user.empty() && user != "root" && user != "unprivileged")
182155852Sgallatin            throw config::value_error("Invalid required user value");
183159571Sgallatin    }
184155852Sgallatin};
185171500Sgallatin
186155852Sgallatin
187177104Sgallatin/// A leaf node that holds a set of paths.
188155852Sgallatin///
189170853Sgallatin/// This node type is used to represent the value of the required files and
190170330Sgallatin/// required programs, for example, and these do not allow relative paths.  We
191170330Sgallatin/// check this here.
192170330Sgallatinclass paths_set_node : public config::base_set_node< fs::path > {
193177104Sgallatin    /// Copies the node.
194170330Sgallatin    ///
195170330Sgallatin    /// \return A dynamically-allocated node.
196170853Sgallatin    virtual base_node*
197155852Sgallatin    deep_copy(void) const
198171500Sgallatin    {
199155852Sgallatin        std::auto_ptr< paths_set_node > new_node(new paths_set_node());
200155852Sgallatin        new_node->_value = _value;
201155852Sgallatin        return new_node.release();
202155852Sgallatin    }
203155852Sgallatin
204159571Sgallatin    /// Converts a single path to the native type.
205155852Sgallatin    ///
206155852Sgallatin    /// \param raw_value The value to parse.
207155852Sgallatin    ///
208155852Sgallatin    /// \return The parsed value.
209155852Sgallatin    ///
210155852Sgallatin    /// \throw config::value_error If the value is invalid.
211155852Sgallatin    fs::path
212155852Sgallatin    parse_one(const std::string& raw_value) const
213159571Sgallatin    {
214155852Sgallatin        try {
215155852Sgallatin            return fs::path(raw_value);
216155852Sgallatin        } catch (const fs::error& e) {
217155852Sgallatin            throw config::value_error(e.what());
218175365Sgallatin        }
219155852Sgallatin    }
220175365Sgallatin
221175365Sgallatin    /// Checks a collection of paths for validity.
222175365Sgallatin    ///
223175365Sgallatin    /// \param paths The value to validate.
224175365Sgallatin    ///
225175365Sgallatin    /// \throw config::value_error If the value is not valid.
226175365Sgallatin    void
227175365Sgallatin    validate(const value_type& paths) const
228155852Sgallatin    {
229155852Sgallatin        for (value_type::const_iterator iter = paths.begin();
230155852Sgallatin             iter != paths.end(); ++iter) {
231175365Sgallatin            const fs::path& path = *iter;
232155852Sgallatin            if (!path.is_absolute() && path.ncomponents() > 1)
233155852Sgallatin                throw config::value_error(F("Relative path '%s' not allowed") %
234155852Sgallatin                                          *iter);
235155852Sgallatin        }
236155852Sgallatin    }
237175365Sgallatin};
238155852Sgallatin
239155852Sgallatin
240155852Sgallatin/// Initializes a tree to hold test case requirements.
241155852Sgallatin///
242155852Sgallatin/// \param [in,out] tree The tree to initialize.
243155852Sgallatinstatic void
244155852Sgallatininit_tree(config::tree& tree)
245155852Sgallatin{
246155852Sgallatin    tree.define< config::strings_set_node >("allowed_architectures");
247155852Sgallatin    tree.define< config::strings_set_node >("allowed_platforms");
248155852Sgallatin    tree.define_dynamic("custom");
249155852Sgallatin    tree.define< config::string_node >("description");
250155852Sgallatin    tree.define< config::bool_node >("has_cleanup");
251155852Sgallatin    tree.define< config::bool_node >("is_exclusive");
252155852Sgallatin    tree.define< config::strings_set_node >("required_configs");
253155852Sgallatin    tree.define< bytes_node >("required_disk_space");
254155852Sgallatin    tree.define< paths_set_node >("required_files");
255155852Sgallatin    tree.define< bytes_node >("required_memory");
256155852Sgallatin    tree.define< paths_set_node >("required_programs");
257159571Sgallatin    tree.define< user_node >("required_user");
258155852Sgallatin    tree.define< delta_node >("timeout");
259155852Sgallatin}
260155852Sgallatin
261155852Sgallatin
262155852Sgallatin/// Sets default values on a tree object.
263155852Sgallatin///
264155852Sgallatin/// \param [in,out] tree The tree to configure.
265155852Sgallatinstatic void
266155852Sgallatinset_defaults(config::tree& tree)
267155852Sgallatin{
268155852Sgallatin    tree.set< config::strings_set_node >("allowed_architectures",
269155852Sgallatin                                         model::strings_set());
270155852Sgallatin    tree.set< config::strings_set_node >("allowed_platforms",
271155852Sgallatin                                         model::strings_set());
272155852Sgallatin    tree.set< config::string_node >("description", "");
273155852Sgallatin    tree.set< config::bool_node >("has_cleanup", false);
274159571Sgallatin    tree.set< config::bool_node >("is_exclusive", false);
275155852Sgallatin    tree.set< config::strings_set_node >("required_configs",
276155852Sgallatin                                         model::strings_set());
277155852Sgallatin    tree.set< bytes_node >("required_disk_space", units::bytes(0));
278155852Sgallatin    tree.set< paths_set_node >("required_files", model::paths_set());
279155852Sgallatin    tree.set< bytes_node >("required_memory", units::bytes(0));
280155852Sgallatin    tree.set< paths_set_node >("required_programs", model::paths_set());
281155852Sgallatin    tree.set< user_node >("required_user", "");
282155852Sgallatin    // TODO(jmmv): We shouldn't be setting a default timeout like this.  See
283155852Sgallatin    // Issue 5 for details.
284155852Sgallatin    tree.set< delta_node >("timeout", datetime::delta(300, 0));
285155852Sgallatin}
286155852Sgallatin
287155852Sgallatin
288155852Sgallatin/// Queries the global defaults tree object with lazy initialization.
289159571Sgallatin///
290155852Sgallatin/// \return A metadata tree.  This object is statically allocated so it is
291247268Sgallatin/// acceptable to obtain references to it and its members.
292247159Sgallatinconst config::tree&
293247268Sgallatinget_defaults(void)
294155852Sgallatin{
295155852Sgallatin    if (!defaults) {
296155852Sgallatin        config::tree props;
297247159Sgallatin        init_tree(props);
298247268Sgallatin        set_defaults(props);
299247268Sgallatin        defaults = props;
300247268Sgallatin    }
301247268Sgallatin    return defaults.get();
302247268Sgallatin}
303247268Sgallatin
304155852Sgallatin
305247268Sgallatin/// Looks up a value in a tree with error rewriting.
306247268Sgallatin///
307247268Sgallatin/// \tparam NodeType The type of the node.
308247268Sgallatin/// \param tree The tree in which to insert the value.
309247268Sgallatin/// \param key The key to set.
310155852Sgallatin///
311247268Sgallatin/// \return A read-write reference to the value in the node.
312247268Sgallatin///
313159612Sgallatin/// \throw model::error If the key is not known or if the value is not valid.
314247268Sgallatintemplate< class NodeType >
315247268Sgallatintypename NodeType::value_type&
316247268Sgallatinlookup_rw(config::tree& tree, const std::string& key)
317159612Sgallatin{
318247268Sgallatin    try {
319247268Sgallatin        return tree.lookup_rw< NodeType >(key);
320247268Sgallatin    } catch (const config::unknown_key_error& e) {
321247159Sgallatin        throw model::error(F("Unknown metadata property %s") % key);
322247159Sgallatin    } catch (const config::value_error& e) {
323247159Sgallatin        throw model::error(F("Invalid value for metadata property %s: %s") %
324247268Sgallatin                            key % e.what());
325247268Sgallatin    }
326155852Sgallatin}
327247268Sgallatin
328155852Sgallatin
329155852Sgallatin/// Sets a value in a tree with error rewriting.
330155852Sgallatin///
331155852Sgallatin/// \tparam NodeType The type of the node.
332155852Sgallatin/// \param tree The tree in which to insert the value.
333155852Sgallatin/// \param key The key to set.
334155852Sgallatin/// \param value The value to set the node to.
335155852Sgallatin///
336155852Sgallatin/// \throw model::error If the key is not known or if the value is not valid.
337155852Sgallatintemplate< class NodeType >
338155852Sgallatinvoid
339188531Srdivackyset(config::tree& tree, const std::string& key,
340169376Sgallatin    const typename NodeType::value_type& value)
341169376Sgallatin{
342155852Sgallatin    try {
343155852Sgallatin        tree.set< NodeType >(key, value);
344169376Sgallatin    } catch (const config::unknown_key_error& e) {
345155852Sgallatin        throw model::error(F("Unknown metadata property %s") % key);
346169376Sgallatin    } catch (const config::value_error& e) {
347169376Sgallatin        throw model::error(F("Invalid value for metadata property %s: %s") %
348155852Sgallatin                            key % e.what());
349155852Sgallatin    }
350155852Sgallatin}
351169376Sgallatin
352169376Sgallatin
353169376Sgallatin}  // anonymous namespace
354169376Sgallatin
355169376Sgallatin
356169376Sgallatin/// Internal implementation of the metadata class.
357169376Sgallatinstruct model::metadata::impl : utils::noncopyable {
358169376Sgallatin    /// Metadata properties.
359169376Sgallatin    config::tree props;
360169376Sgallatin
361169376Sgallatin    /// Constructor.
362169376Sgallatin    ///
363169376Sgallatin    /// \param props_ Metadata properties of the test.
364169376Sgallatin    impl(const utils::config::tree& props_) :
365169376Sgallatin        props(props_)
366169376Sgallatin    {
367169376Sgallatin    }
368169376Sgallatin
369169376Sgallatin    /// Equality comparator.
370169376Sgallatin    ///
371169376Sgallatin    /// \param other The other object to compare this one to.
372169376Sgallatin    ///
373169376Sgallatin    /// \return True if this object and other are equal; false otherwise.
374169376Sgallatin    bool
375169376Sgallatin    operator==(const impl& other) const
376169376Sgallatin    {
377169376Sgallatin        return (get_defaults().combine(props) ==
378169376Sgallatin                get_defaults().combine(other.props));
379169376Sgallatin    }
380169376Sgallatin};
381169376Sgallatin
382169376Sgallatin
383169376Sgallatin/// Constructor.
384155852Sgallatin///
385155852Sgallatin/// \param props Metadata properties of the test.
386155852Sgallatinmodel::metadata::metadata(const utils::config::tree& props) :
387155852Sgallatin    _pimpl(new impl(props))
388155852Sgallatin{
389155852Sgallatin}
390155852Sgallatin
391155852Sgallatin
392155852Sgallatin/// Destructor.
393155852Sgallatinmodel::metadata::~metadata(void)
394155852Sgallatin{
395155852Sgallatin}
396155852Sgallatin
397155852Sgallatin
398169376Sgallatin/// Applies a set of overrides to this metadata object.
399155852Sgallatin///
400155852Sgallatin/// \param overrides The overrides to apply.  Any values explicitly set in this
401155852Sgallatin///     other object will override any possible values set in this object.
402155852Sgallatin///
403155852Sgallatin/// \return A new metadata object with the combination.
404155852Sgallatinmodel::metadata
405155852Sgallatinmodel::metadata::apply_overrides(const metadata& overrides) const
406155852Sgallatin{
407155852Sgallatin    return metadata(_pimpl->props.combine(overrides._pimpl->props));
408155852Sgallatin}
409155852Sgallatin
410155852Sgallatin
411155852Sgallatin/// Returns the architectures allowed by the test.
412155852Sgallatin///
413155852Sgallatin/// \return Set of architectures, or empty if this does not apply.
414155852Sgallatinconst model::strings_set&
415155852Sgallatinmodel::metadata::allowed_architectures(void) const
416155852Sgallatin{
417155852Sgallatin    if (_pimpl->props.is_set("allowed_architectures")) {
418155852Sgallatin        return _pimpl->props.lookup< config::strings_set_node >(
419155852Sgallatin            "allowed_architectures");
420169376Sgallatin    } else {
421155852Sgallatin        return get_defaults().lookup< config::strings_set_node >(
422155852Sgallatin            "allowed_architectures");
423155852Sgallatin    }
424155852Sgallatin}
425155852Sgallatin
426155852Sgallatin
427155852Sgallatin/// Returns the platforms allowed by the test.
428155852Sgallatin///
429155852Sgallatin/// \return Set of platforms, or empty if this does not apply.
430155852Sgallatinconst model::strings_set&
431169376Sgallatinmodel::metadata::allowed_platforms(void) const
432155852Sgallatin{
433155852Sgallatin    if (_pimpl->props.is_set("allowed_platforms")) {
434155852Sgallatin        return _pimpl->props.lookup< config::strings_set_node >(
435155852Sgallatin            "allowed_platforms");
436155852Sgallatin    } else {
437155852Sgallatin        return get_defaults().lookup< config::strings_set_node >(
438155852Sgallatin            "allowed_platforms");
439155852Sgallatin    }
440155852Sgallatin}
441155852Sgallatin
442155852Sgallatin
443169376Sgallatin/// Returns all the user-defined metadata properties.
444155852Sgallatin///
445155852Sgallatin/// \return A key/value map of properties.
446155852Sgallatinmodel::properties_map
447155852Sgallatinmodel::metadata::custom(void) const
448155852Sgallatin{
449155852Sgallatin    return _pimpl->props.all_properties("custom", true);
450155852Sgallatin}
451155852Sgallatin
452169376Sgallatin
453155852Sgallatin/// Returns the description of the test.
454155852Sgallatin///
455155852Sgallatin/// \return Textual description; may be empty.
456159612Sgallatinconst std::string&
457159612Sgallatinmodel::metadata::description(void) const
458159612Sgallatin{
459159612Sgallatin    if (_pimpl->props.is_set("description")) {
460159612Sgallatin        return _pimpl->props.lookup< config::string_node >("description");
461169376Sgallatin    } else {
462155852Sgallatin        return get_defaults().lookup< config::string_node >("description");
463155852Sgallatin    }
464169376Sgallatin}
465171500Sgallatin
466155852Sgallatin
467155852Sgallatin/// Returns whether the test has a cleanup part or not.
468155852Sgallatin///
469169376Sgallatin/// \return True if there is a cleanup part; false otherwise.
470155852Sgallatinbool
471155852Sgallatinmodel::metadata::has_cleanup(void) const
472169376Sgallatin{
473169376Sgallatin    if (_pimpl->props.is_set("has_cleanup")) {
474169376Sgallatin        return _pimpl->props.lookup< config::bool_node >("has_cleanup");
475169376Sgallatin    } else {
476169376Sgallatin        return get_defaults().lookup< config::bool_node >("has_cleanup");
477169376Sgallatin    }
478169376Sgallatin}
479169376Sgallatin
480169376Sgallatin
481169376Sgallatin/// Returns whether the test is exclusive or not.
482169376Sgallatin///
483169376Sgallatin/// \return True if the test has to be run on its own, not concurrently with any
484169376Sgallatin/// other tests; false otherwise.
485169376Sgallatinbool
486169376Sgallatinmodel::metadata::is_exclusive(void) const
487169376Sgallatin{
488169376Sgallatin    if (_pimpl->props.is_set("is_exclusive")) {
489169376Sgallatin        return _pimpl->props.lookup< config::bool_node >("is_exclusive");
490169376Sgallatin    } else {
491169376Sgallatin        return get_defaults().lookup< config::bool_node >("is_exclusive");
492169376Sgallatin    }
493175365Sgallatin}
494169376Sgallatin
495169376Sgallatin
496169376Sgallatin/// Returns the list of configuration variables needed by the test.
497169376Sgallatin///
498169376Sgallatin/// \return Set of configuration variables.
499169376Sgallatinconst model::strings_set&
500169376Sgallatinmodel::metadata::required_configs(void) const
501169376Sgallatin{
502169376Sgallatin    if (_pimpl->props.is_set("required_configs")) {
503169376Sgallatin        return _pimpl->props.lookup< config::strings_set_node >(
504169376Sgallatin            "required_configs");
505169376Sgallatin    } else {
506169376Sgallatin        return get_defaults().lookup< config::strings_set_node >(
507169376Sgallatin            "required_configs");
508169376Sgallatin    }
509169376Sgallatin}
510169376Sgallatin
511169376Sgallatin
512169376Sgallatin/// Returns the amount of free disk space required by the test.
513169376Sgallatin///
514169376Sgallatin/// \return Number of bytes, or 0 if this does not apply.
515169376Sgallatinconst units::bytes&
516169376Sgallatinmodel::metadata::required_disk_space(void) const
517169376Sgallatin{
518169376Sgallatin    if (_pimpl->props.is_set("required_disk_space")) {
519169376Sgallatin        return _pimpl->props.lookup< bytes_node >("required_disk_space");
520169376Sgallatin    } else {
521169376Sgallatin        return get_defaults().lookup< bytes_node >("required_disk_space");
522169376Sgallatin    }
523169376Sgallatin}
524169376Sgallatin
525169376Sgallatin
526169376Sgallatin/// Returns the list of files needed by the test.
527169376Sgallatin///
528169376Sgallatin/// \return Set of paths.
529169376Sgallatinconst model::paths_set&
530169376Sgallatinmodel::metadata::required_files(void) const
531169376Sgallatin{
532169376Sgallatin    if (_pimpl->props.is_set("required_files")) {
533169376Sgallatin        return _pimpl->props.lookup< paths_set_node >("required_files");
534169376Sgallatin    } else {
535155852Sgallatin        return get_defaults().lookup< paths_set_node >("required_files");
536155852Sgallatin    }
537155852Sgallatin}
538155852Sgallatin
539155852Sgallatin
540155852Sgallatin/// Returns the amount of memory required by the test.
541155852Sgallatin///
542155852Sgallatin/// \return Number of bytes, or 0 if this does not apply.
543155852Sgallatinconst units::bytes&
544155852Sgallatinmodel::metadata::required_memory(void) const
545155852Sgallatin{
546155852Sgallatin    if (_pimpl->props.is_set("required_memory")) {
547155852Sgallatin        return _pimpl->props.lookup< bytes_node >("required_memory");
548155852Sgallatin    } else {
549175365Sgallatin        return get_defaults().lookup< bytes_node >("required_memory");
550155852Sgallatin    }
551175365Sgallatin}
552155852Sgallatin
553155852Sgallatin
554169376Sgallatin/// Returns the list of programs needed by the test.
555169376Sgallatin///
556169376Sgallatin/// \return Set of paths.
557169376Sgallatinconst model::paths_set&
558169376Sgallatinmodel::metadata::required_programs(void) const
559169376Sgallatin{
560169376Sgallatin    if (_pimpl->props.is_set("required_programs")) {
561175365Sgallatin        return _pimpl->props.lookup< paths_set_node >("required_programs");
562169376Sgallatin    } else {
563169376Sgallatin        return get_defaults().lookup< paths_set_node >("required_programs");
564169376Sgallatin    }
565169376Sgallatin}
566219902Sjhb
567169376Sgallatin
568169376Sgallatin/// Returns the user required by the test.
569169376Sgallatin///
570169376Sgallatin/// \return One of unprivileged, root or empty.
571175365Sgallatinconst std::string&
572169376Sgallatinmodel::metadata::required_user(void) const
573169376Sgallatin{
574169376Sgallatin    if (_pimpl->props.is_set("required_user")) {
575169376Sgallatin        return _pimpl->props.lookup< user_node >("required_user");
576169376Sgallatin    } else {
577169376Sgallatin        return get_defaults().lookup< user_node >("required_user");
578169376Sgallatin    }
579169376Sgallatin}
580175365Sgallatin
581169376Sgallatin
582169376Sgallatin/// Returns the timeout of the test.
583169376Sgallatin///
584169376Sgallatin/// \return A time delta; should be compared to default_timeout to see if it has
585169376Sgallatin/// been overriden.
586169376Sgallatinconst datetime::delta&
587169376Sgallatinmodel::metadata::timeout(void) const
588169376Sgallatin{
589169376Sgallatin    if (_pimpl->props.is_set("timeout")) {
590169376Sgallatin        return _pimpl->props.lookup< delta_node >("timeout");
591169376Sgallatin    } else {
592247159Sgallatin        return get_defaults().lookup< delta_node >("timeout");
593169376Sgallatin    }
594247159Sgallatin}
595247159Sgallatin
596169376Sgallatin
597169376Sgallatin/// Externalizes the metadata to a set of key/value textual pairs.
598169376Sgallatin///
599169376Sgallatin/// \return A key/value representation of the metadata.
600169376Sgallatinmodel::properties_map
601169376Sgallatinmodel::metadata::to_properties(void) const
602169376Sgallatin{
603169376Sgallatin    const config::tree fully_specified = get_defaults().combine(_pimpl->props);
604169376Sgallatin    return fully_specified.all_properties();
605169376Sgallatin}
606169376Sgallatin
607169376Sgallatin
608169376Sgallatin/// Equality comparator.
609159571Sgallatin///
610155852Sgallatin/// \param other The other object to compare this one to.
611169376Sgallatin///
612197391Sgallatin/// \return True if this object and other are equal; false otherwise.
613155852Sgallatinbool
614197391Sgallatinmodel::metadata::operator==(const metadata& other) const
615197391Sgallatin{
616164513Sgallatin    return _pimpl == other._pimpl || *_pimpl == *other._pimpl;
617197391Sgallatin}
618197391Sgallatin
619164513Sgallatin
620164513Sgallatin/// Inequality comparator.
621164513Sgallatin///
622164513Sgallatin/// \param other The other object to compare this one to.
623164513Sgallatin///
624164513Sgallatin/// \return True if this object and other are different; false otherwise.
625164513Sgallatinbool
626164513Sgallatinmodel::metadata::operator!=(const metadata& other) const
627164513Sgallatin{
628164513Sgallatin    return !(*this == other);
629164513Sgallatin}
630164513Sgallatin
631164513Sgallatin
632164513Sgallatin/// Injects the object into a stream.
633164513Sgallatin///
634164513Sgallatin/// \param output The stream into which to inject the object.
635164513Sgallatin/// \param object The object to format.
636164513Sgallatin///
637164513Sgallatin/// \return The output stream.
638164513Sgallatinstd::ostream&
639169376Sgallatinmodel::operator<<(std::ostream& output, const metadata& object)
640169376Sgallatin{
641155852Sgallatin    output << "metadata{";
642155852Sgallatin
643155852Sgallatin    bool first = true;
644159571Sgallatin    const model::properties_map props = object.to_properties();
645175365Sgallatin    for (model::properties_map::const_iterator iter = props.begin();
646155852Sgallatin         iter != props.end(); ++iter) {
647159571Sgallatin        if (!first)
648175365Sgallatin            output << ", ";
649155852Sgallatin        output << F("%s=%s") % (*iter).first %
650175365Sgallatin            text::quote((*iter).second, '\'');
651155852Sgallatin        first = false;
652155852Sgallatin    }
653160456Sgallatin
654160456Sgallatin    output << "}";
655160456Sgallatin    return output;
656155852Sgallatin}
657166875Sgallatin
658160456Sgallatin
659160456Sgallatin/// Internal implementation of the metadata_builder class.
660160456Sgallatinstruct model::metadata_builder::impl : utils::noncopyable {
661160456Sgallatin    /// Collection of requirements.
662160456Sgallatin    config::tree props;
663160456Sgallatin
664160456Sgallatin    /// Whether we have created a metadata object or not.
665247268Sgallatin    bool built;
666160456Sgallatin
667160456Sgallatin    /// Constructor.
668160456Sgallatin    impl(void) :
669166875Sgallatin        built(false)
670166875Sgallatin    {
671160456Sgallatin        init_tree(props);
672166875Sgallatin    }
673166875Sgallatin
674160456Sgallatin    /// Constructor.
675160456Sgallatin    ///
676160456Sgallatin    /// \param base The base model to construct a copy from.
677160456Sgallatin    impl(const model::metadata& base) :
678160456Sgallatin        props(base._pimpl->props.deep_copy()),
679160456Sgallatin        built(false)
680160456Sgallatin    {
681160456Sgallatin    }
682160456Sgallatin};
683160456Sgallatin
684171500Sgallatin
685171500Sgallatin/// Constructor.
686171500Sgallatinmodel::metadata_builder::metadata_builder(void) :
687171500Sgallatin    _pimpl(new impl())
688171500Sgallatin{
689171500Sgallatin}
690171500Sgallatin
691171500Sgallatin
692171500Sgallatin/// Constructor.
693171500Sgallatinmodel::metadata_builder::metadata_builder(const model::metadata& base) :
694171500Sgallatin    _pimpl(new impl(base))
695171500Sgallatin{
696171500Sgallatin}
697171500Sgallatin
698171500Sgallatin
699171500Sgallatin/// Destructor.
700155852Sgallatinmodel::metadata_builder::~metadata_builder(void)
701159571Sgallatin{
702155852Sgallatin}
703171500Sgallatin
704171500Sgallatin
705166756Sluigi/// Accumulates an additional allowed architecture.
706155852Sgallatin///
707155852Sgallatin/// \param arch The architecture.
708155852Sgallatin///
709160456Sgallatin/// \return A reference to this builder.
710160456Sgallatin///
711171500Sgallatin/// \throw model::error If the value is invalid.
712155852Sgallatinmodel::metadata_builder&
713155852Sgallatinmodel::metadata_builder::add_allowed_architecture(const std::string& arch)
714155852Sgallatin{
715155852Sgallatin    if (!_pimpl->props.is_set("allowed_architectures")) {
716155852Sgallatin        _pimpl->props.set< config::strings_set_node >(
717155852Sgallatin            "allowed_architectures",
718155852Sgallatin            get_defaults().lookup< config::strings_set_node >(
719171500Sgallatin                "allowed_architectures"));
720171500Sgallatin    }
721171500Sgallatin    lookup_rw< config::strings_set_node >(
722171500Sgallatin        _pimpl->props, "allowed_architectures").insert(arch);
723171500Sgallatin    return *this;
724171500Sgallatin}
725171500Sgallatin
726171500Sgallatin
727171500Sgallatin/// Accumulates an additional allowed platform.
728171500Sgallatin///
729155852Sgallatin/// \param platform The platform.
730155852Sgallatin///
731155852Sgallatin/// \return A reference to this builder.
732171500Sgallatin///
733171500Sgallatin/// \throw model::error If the value is invalid.
734171500Sgallatinmodel::metadata_builder&
735171500Sgallatinmodel::metadata_builder::add_allowed_platform(const std::string& platform)
736171500Sgallatin{
737171500Sgallatin    if (!_pimpl->props.is_set("allowed_platforms")) {
738171500Sgallatin        _pimpl->props.set< config::strings_set_node >(
739171500Sgallatin            "allowed_platforms",
740171500Sgallatin            get_defaults().lookup< config::strings_set_node >(
741171500Sgallatin                "allowed_platforms"));
742171500Sgallatin    }
743171500Sgallatin    lookup_rw< config::strings_set_node >(
744171500Sgallatin        _pimpl->props, "allowed_platforms").insert(platform);
745171500Sgallatin    return *this;
746171500Sgallatin}
747171500Sgallatin
748171500Sgallatin
749155852Sgallatin/// Accumulates a single user-defined property.
750155852Sgallatin///
751171500Sgallatin/// \param key Name of the property to define.
752171500Sgallatin/// \param value Value of the property.
753155852Sgallatin///
754155852Sgallatin/// \return A reference to this builder.
755171500Sgallatin///
756155852Sgallatin/// \throw model::error If the value is invalid.
757171500Sgallatinmodel::metadata_builder&
758160456Sgallatinmodel::metadata_builder::add_custom(const std::string& key,
759160456Sgallatin                                     const std::string& value)
760160456Sgallatin{
761171500Sgallatin    _pimpl->props.set_string(F("custom.%s") % key, value);
762155852Sgallatin    return *this;
763155852Sgallatin}
764171500Sgallatin
765160456Sgallatin
766171500Sgallatin/// Accumulates an additional required configuration variable.
767171500Sgallatin///
768185255Sgallatin/// \param var The name of the configuration variable.
769160456Sgallatin///
770185255Sgallatin/// \return A reference to this builder.
771160456Sgallatin///
772155852Sgallatin/// \throw model::error If the value is invalid.
773171500Sgallatinmodel::metadata_builder&
774155852Sgallatinmodel::metadata_builder::add_required_config(const std::string& var)
775171500Sgallatin{
776171500Sgallatin    if (!_pimpl->props.is_set("required_configs")) {
777171500Sgallatin        _pimpl->props.set< config::strings_set_node >(
778171500Sgallatin            "required_configs",
779155852Sgallatin            get_defaults().lookup< config::strings_set_node >(
780155852Sgallatin                "required_configs"));
781155852Sgallatin    }
782155852Sgallatin    lookup_rw< config::strings_set_node >(
783155852Sgallatin        _pimpl->props, "required_configs").insert(var);
784155852Sgallatin    return *this;
785155852Sgallatin}
786155852Sgallatin
787155852Sgallatin
788155852Sgallatin/// Accumulates an additional required file.
789155852Sgallatin///
790159571Sgallatin/// \param path The path to the file.
791155852Sgallatin///
792155852Sgallatin/// \return A reference to this builder.
793155852Sgallatin///
794155852Sgallatin/// \throw model::error If the value is invalid.
795155852Sgallatinmodel::metadata_builder&
796155852Sgallatinmodel::metadata_builder::add_required_file(const fs::path& path)
797155852Sgallatin{
798155852Sgallatin    if (!_pimpl->props.is_set("required_files")) {
799155852Sgallatin        _pimpl->props.set< paths_set_node >(
800155852Sgallatin            "required_files",
801155852Sgallatin            get_defaults().lookup< paths_set_node >("required_files"));
802155852Sgallatin    }
803185255Sgallatin    lookup_rw< paths_set_node >(_pimpl->props, "required_files").insert(path);
804155852Sgallatin    return *this;
805155852Sgallatin}
806155852Sgallatin
807155852Sgallatin
808155852Sgallatin/// Accumulates an additional required program.
809155852Sgallatin///
810159571Sgallatin/// \param path The path to the program.
811159571Sgallatin///
812155852Sgallatin/// \return A reference to this builder.
813155852Sgallatin///
814155852Sgallatin/// \throw model::error If the value is invalid.
815159571Sgallatinmodel::metadata_builder&
816159571Sgallatinmodel::metadata_builder::add_required_program(const fs::path& path)
817155852Sgallatin{
818155852Sgallatin    if (!_pimpl->props.is_set("required_programs")) {
819155852Sgallatin        _pimpl->props.set< paths_set_node >(
820155852Sgallatin            "required_programs",
821155852Sgallatin            get_defaults().lookup< paths_set_node >("required_programs"));
822162328Sgallatin    }
823155852Sgallatin    lookup_rw< paths_set_node >(_pimpl->props,
824159571Sgallatin                                "required_programs").insert(path);
825185255Sgallatin    return *this;
826155852Sgallatin}
827185255Sgallatin
828155852Sgallatin
829155852Sgallatin/// Sets the architectures allowed by the test.
830155852Sgallatin///
831155852Sgallatin/// \param as Set of architectures.
832155852Sgallatin///
833155852Sgallatin/// \return A reference to this builder.
834155852Sgallatin///
835155852Sgallatin/// \throw model::error If the value is invalid.
836155852Sgallatinmodel::metadata_builder&
837155852Sgallatinmodel::metadata_builder::set_allowed_architectures(
838155852Sgallatin    const model::strings_set& as)
839155852Sgallatin{
840155852Sgallatin    set< config::strings_set_node >(_pimpl->props, "allowed_architectures", as);
841155852Sgallatin    return *this;
842159571Sgallatin}
843155852Sgallatin
844155852Sgallatin
845155852Sgallatin/// Sets the platforms allowed by the test.
846155852Sgallatin///
847162328Sgallatin/// \return ps Set of platforms.
848155852Sgallatin///
849169384Sgallatin/// \return A reference to this builder.
850155852Sgallatin///
851155852Sgallatin/// \throw model::error If the value is invalid.
852155852Sgallatinmodel::metadata_builder&
853155852Sgallatinmodel::metadata_builder::set_allowed_platforms(const model::strings_set& ps)
854155852Sgallatin{
855155852Sgallatin    set< config::strings_set_node >(_pimpl->props, "allowed_platforms", ps);
856155852Sgallatin    return *this;
857155852Sgallatin}
858159571Sgallatin
859159571Sgallatin
860155852Sgallatin/// Sets the user-defined properties.
861155852Sgallatin///
862155852Sgallatin/// \param props The custom properties to set.
863166370Sgallatin///
864155852Sgallatin/// \return A reference to this builder.
865185255Sgallatin///
866159571Sgallatin/// \throw model::error If the value is invalid.
867155852Sgallatinmodel::metadata_builder&
868159612Sgallatinmodel::metadata_builder::set_custom(const model::properties_map& props)
869169384Sgallatin{
870159612Sgallatin    for (model::properties_map::const_iterator iter = props.begin();
871155852Sgallatin         iter != props.end(); ++iter)
872155852Sgallatin        _pimpl->props.set_string(F("custom.%s") % (*iter).first,
873185255Sgallatin                                 (*iter).second);
874169384Sgallatin    return *this;
875169384Sgallatin}
876169384Sgallatin
877169384Sgallatin
878169384Sgallatin/// Sets the description of the test.
879169384Sgallatin///
880169384Sgallatin/// \param description Textual description of the test.
881169384Sgallatin///
882169384Sgallatin/// \return A reference to this builder.
883169384Sgallatin///
884169384Sgallatin/// \throw model::error If the value is invalid.
885169384Sgallatinmodel::metadata_builder&
886169384Sgallatinmodel::metadata_builder::set_description(const std::string& description)
887169384Sgallatin{
888171917Sgallatin    set< config::string_node >(_pimpl->props, "description", description);
889171917Sgallatin    return *this;
890171917Sgallatin}
891206662Sgallatin
892206662Sgallatin
893206662Sgallatin/// Sets whether the test has a cleanup part or not.
894169384Sgallatin///
895169384Sgallatin/// \param cleanup True if the test has a cleanup part; false otherwise.
896169384Sgallatin///
897169384Sgallatin/// \return A reference to this builder.
898169384Sgallatin///
899169384Sgallatin/// \throw model::error If the value is invalid.
900169384Sgallatinmodel::metadata_builder&
901155852Sgallatinmodel::metadata_builder::set_has_cleanup(const bool cleanup)
902169384Sgallatin{
903169384Sgallatin    set< config::bool_node >(_pimpl->props, "has_cleanup", cleanup);
904155852Sgallatin    return *this;
905169384Sgallatin}
906169384Sgallatin
907169384Sgallatin
908169384Sgallatin/// Sets whether the test is exclusive or not.
909166370Sgallatin///
910169384Sgallatin/// \param exclusive True if the test is exclusive; false otherwise.
911155852Sgallatin///
912155852Sgallatin/// \return A reference to this builder.
913160456Sgallatin///
914160456Sgallatin/// \throw model::error If the value is invalid.
915160456Sgallatinmodel::metadata_builder&
916160456Sgallatinmodel::metadata_builder::set_is_exclusive(const bool exclusive)
917160456Sgallatin{
918160456Sgallatin    set< config::bool_node >(_pimpl->props, "is_exclusive", exclusive);
919160456Sgallatin    return *this;
920155852Sgallatin}
921160456Sgallatin
922160456Sgallatin
923160456Sgallatin/// Sets the list of configuration variables needed by the test.
924160456Sgallatin///
925160456Sgallatin/// \param vars Set of configuration variables.
926160456Sgallatin///
927160456Sgallatin/// \return A reference to this builder.
928160456Sgallatin///
929160456Sgallatin/// \throw model::error If the value is invalid.
930160456Sgallatinmodel::metadata_builder&
931160456Sgallatinmodel::metadata_builder::set_required_configs(const model::strings_set& vars)
932160456Sgallatin{
933160456Sgallatin    set< config::strings_set_node >(_pimpl->props, "required_configs", vars);
934160456Sgallatin    return *this;
935160456Sgallatin}
936160456Sgallatin
937160456Sgallatin
938160456Sgallatin/// Sets the amount of free disk space required by the test.
939160456Sgallatin///
940160456Sgallatin/// \param bytes Number of bytes.
941160456Sgallatin///
942160456Sgallatin/// \return A reference to this builder.
943160456Sgallatin///
944166875Sgallatin/// \throw model::error If the value is invalid.
945166875Sgallatinmodel::metadata_builder&
946166875Sgallatinmodel::metadata_builder::set_required_disk_space(const units::bytes& bytes)
947166875Sgallatin{
948166875Sgallatin    set< bytes_node >(_pimpl->props, "required_disk_space", bytes);
949166875Sgallatin    return *this;
950166875Sgallatin}
951166875Sgallatin
952166875Sgallatin
953166875Sgallatin/// Sets the list of files needed by the test.
954166875Sgallatin///
955166875Sgallatin/// \param files Set of paths.
956166875Sgallatin///
957166875Sgallatin/// \return A reference to this builder.
958166875Sgallatin///
959160456Sgallatin/// \throw model::error If the value is invalid.
960160456Sgallatinmodel::metadata_builder&
961160456Sgallatinmodel::metadata_builder::set_required_files(const model::paths_set& files)
962160456Sgallatin{
963155852Sgallatin    set< paths_set_node >(_pimpl->props, "required_files", files);
964175365Sgallatin    return *this;
965155852Sgallatin}
966155852Sgallatin
967155852Sgallatin
968155852Sgallatin/// Sets the amount of memory required by the test.
969155852Sgallatin///
970155852Sgallatin/// \param bytes Number of bytes.
971155852Sgallatin///
972155852Sgallatin/// \return A reference to this builder.
973155852Sgallatin///
974155852Sgallatin/// \throw model::error If the value is invalid.
975159571Sgallatinmodel::metadata_builder&
976155852Sgallatinmodel::metadata_builder::set_required_memory(const units::bytes& bytes)
977175365Sgallatin{
978175365Sgallatin    set< bytes_node >(_pimpl->props, "required_memory", bytes);
979160456Sgallatin    return *this;
980160456Sgallatin}
981160456Sgallatin
982160456Sgallatin
983160456Sgallatin/// Sets the list of programs needed by the test.
984160456Sgallatin///
985160456Sgallatin/// \param progs Set of paths.
986160456Sgallatin///
987160456Sgallatin/// \return A reference to this builder.
988160456Sgallatin///
989175365Sgallatin/// \throw model::error If the value is invalid.
990160456Sgallatinmodel::metadata_builder&
991160456Sgallatinmodel::metadata_builder::set_required_programs(const model::paths_set& progs)
992160456Sgallatin{
993160456Sgallatin    set< paths_set_node >(_pimpl->props, "required_programs", progs);
994160456Sgallatin    return *this;
995160456Sgallatin}
996160456Sgallatin
997164513Sgallatin
998175365Sgallatin/// Sets the user required by the test.
999164513Sgallatin///
1000155852Sgallatin/// \param user One of unprivileged, root or empty.
1001155852Sgallatin///
1002155852Sgallatin/// \return A reference to this builder.
1003155852Sgallatin///
1004185255Sgallatin/// \throw model::error If the value is invalid.
1005155852Sgallatinmodel::metadata_builder&
1006155852Sgallatinmodel::metadata_builder::set_required_user(const std::string& user)
1007155852Sgallatin{
1008155852Sgallatin    set< user_node >(_pimpl->props, "required_user", user);
1009155852Sgallatin    return *this;
1010159571Sgallatin}
1011159571Sgallatin
1012155852Sgallatin
1013155852Sgallatin/// Sets a metadata property by name from its textual representation.
1014155852Sgallatin///
1015155852Sgallatin/// \param key The property to set.
1016155852Sgallatin/// \param value The value to set the property to.
1017155852Sgallatin///
1018155852Sgallatin/// \return A reference to this builder.
1019155852Sgallatin///
1020155852Sgallatin/// \throw model::error If the value is invalid or the key does not exist.
1021155852Sgallatinmodel::metadata_builder&
1022159571Sgallatinmodel::metadata_builder::set_string(const std::string& key,
1023155852Sgallatin                                    const std::string& value)
1024155852Sgallatin{
1025155852Sgallatin    try {
1026155852Sgallatin        _pimpl->props.set_string(key, value);
1027162328Sgallatin    } catch (const config::unknown_key_error& e) {
1028159571Sgallatin        throw model::format_error(F("Unknown metadata property %s") % key);
1029185255Sgallatin    } catch (const config::value_error& e) {
1030155852Sgallatin        throw model::format_error(
1031185255Sgallatin            F("Invalid value for metadata property %s: %s") % key % e.what());
1032155852Sgallatin    }
1033155852Sgallatin    return *this;
1034155852Sgallatin}
1035155852Sgallatin
1036155852Sgallatin
1037155852Sgallatin/// Sets the timeout of the test.
1038155852Sgallatin///
1039155852Sgallatin/// \param timeout The timeout to set.
1040155852Sgallatin///
1041155852Sgallatin/// \return A reference to this builder.
1042155852Sgallatin///
1043155852Sgallatin/// \throw model::error If the value is invalid.
1044155852Sgallatinmodel::metadata_builder&
1045155852Sgallatinmodel::metadata_builder::set_timeout(const datetime::delta& timeout)
1046155852Sgallatin{
1047155852Sgallatin    set< delta_node >(_pimpl->props, "timeout", timeout);
1048155852Sgallatin    return *this;
1049159571Sgallatin}
1050155852Sgallatin
1051159571Sgallatin
1052155852Sgallatin/// Creates a new metadata object.
1053155852Sgallatin///
1054155852Sgallatin/// \pre This has not yet been called.  We only support calling this function
1055155852Sgallatin/// once due to the way the internal tree works: we pass around references, not
1056155852Sgallatin/// deep copies, so if we allowed a second build, we'd encourage reusing the
1057155852Sgallatin/// same builder to construct different metadata objects, and this could have
1058155852Sgallatin/// unintended consequences.
1059155852Sgallatin///
1060155852Sgallatin/// \return The constructed metadata object.
1061159612Sgallatinmodel::metadata
1062155852Sgallatinmodel::metadata_builder::build(void) const
1063155852Sgallatin{
1064155852Sgallatin    PRE(!_pimpl->built);
1065155852Sgallatin    _pimpl->built = true;
1066159571Sgallatin
1067155852Sgallatin    return metadata(_pimpl->props);
1068159571Sgallatin}
1069155852Sgallatin