diff options
Diffstat (limited to 'model/test_case.cpp')
-rw-r--r-- | model/test_case.cpp | 339 |
1 files changed, 339 insertions, 0 deletions
diff --git a/model/test_case.cpp b/model/test_case.cpp new file mode 100644 index 000000000000..f5f6a979eed3 --- /dev/null +++ b/model/test_case.cpp @@ -0,0 +1,339 @@ +// Copyright 2010 The Kyua Authors. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of Google Inc. nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "model/test_case.hpp" + +#include "model/metadata.hpp" +#include "model/test_result.hpp" +#include "utils/format/macros.hpp" +#include "utils/noncopyable.hpp" +#include "utils/optional.ipp" +#include "utils/text/operations.ipp" + +namespace text = utils::text; + +using utils::none; +using utils::optional; + + +/// Internal implementation for a test_case. +struct model::test_case::impl : utils::noncopyable { + /// Name of the test case; must be unique within the test program. + std::string name; + + /// Metadata of the container test program. + /// + /// Yes, this is a pointer. Yes, we do not own the object pointed to. + /// However, because this is only intended to point to the metadata object + /// of test programs _containing_ this test case, we can assume that the + /// referenced object will be alive for the lifetime of this test case. + const model::metadata* md_defaults; + + /// Test case metadata. + model::metadata md; + + /// Fake result to return instead of running the test case. + optional< model::test_result > fake_result; + + /// Constructor. + /// + /// \param name_ The name of the test case within the test program. + /// \param md_defaults_ Metadata of the container test program. + /// \param md_ Metadata of the test case. + /// \param fake_result_ Fake result to return instead of running the test + /// case. + impl(const std::string& name_, + const model::metadata* md_defaults_, + const model::metadata& md_, + const optional< model::test_result >& fake_result_) : + name(name_), + md_defaults(md_defaults_), + md(md_), + fake_result(fake_result_) + { + } + + /// Gets the test case metadata. + /// + /// This combines the test case's metadata with any possible test program + /// metadata, using the latter as defaults. + /// + /// \return The test case metadata. + model::metadata + get_metadata(void) const + { + if (md_defaults != NULL) { + return md_defaults->apply_overrides(md); + } else { + return md; + } + } + + /// Equality comparator. + /// + /// \param other The other object to compare this one to. + /// + /// \return True if this object and other are equal; false otherwise. + bool + operator==(const impl& other) const + { + return (name == other.name && + get_metadata() == other.get_metadata() && + fake_result == other.fake_result); + } +}; + + +/// Constructs a new test case from an already-built impl oject. +/// +/// \param pimpl_ The internal representation of the test case. +model::test_case::test_case(std::shared_ptr< impl > pimpl_) : + _pimpl(pimpl_) +{ +} + + +/// Constructs a new test case. +/// +/// \param name_ The name of the test case within the test program. +/// \param md_ Metadata of the test case. +model::test_case::test_case(const std::string& name_, + const model::metadata& md_) : + _pimpl(new impl(name_, NULL, md_, none)) +{ +} + + + +/// Constructs a new fake test case. +/// +/// A fake test case is a test case that is not really defined by the test +/// program. Such test cases have a name surrounded by '__' and, when executed, +/// they return a fixed, pre-recorded result. +/// +/// This is necessary for the cases where listing the test cases of a test +/// program fails. In this scenario, we generate a single test case within +/// the test program that unconditionally returns a failure. +/// +/// TODO(jmmv): Need to get rid of this. We should be able to report the +/// status of test programs independently of test cases, as some interfaces +/// don't know about the latter at all. +/// +/// \param name_ The name to give to this fake test case. This name has to be +/// prefixed and suffixed by '__' to clearly denote that this is internal. +/// \param description_ The description of the test case, if any. +/// \param test_result_ The fake result to return when this test case is run. +model::test_case::test_case( + const std::string& name_, + const std::string& description_, + const model::test_result& test_result_) : + _pimpl(new impl( + name_, + NULL, + model::metadata_builder().set_description(description_).build(), + utils::make_optional(test_result_))) +{ + PRE_MSG(name_.length() > 4 && name_.substr(0, 2) == "__" && + name_.substr(name_.length() - 2) == "__", + "Invalid fake name provided to fake test case"); +} + + +/// Destroys a test case. +model::test_case::~test_case(void) +{ +} + + +/// Constructs a new test case applying metadata defaults. +/// +/// This method is intended to be used by the container test program when +/// ownership of the test is given to it. At that point, the test case receives +/// the default metadata properties of the test program, not the global +/// defaults. +/// +/// \param defaults The metadata properties to use as defaults. The provided +/// object's lifetime MUST extend the lifetime of the test case. Because +/// this is only intended to point at the metadata of the test program +/// containing this test case, this assumption should hold. +/// +/// \return A new test case. +model::test_case +model::test_case::apply_metadata_defaults(const metadata* defaults) const +{ + return test_case(std::shared_ptr< impl >(new impl( + _pimpl->name, + defaults, + _pimpl->md, + _pimpl->fake_result))); +} + + +/// Gets the test case name. +/// +/// \return The test case name, relative to the test program. +const std::string& +model::test_case::name(void) const +{ + return _pimpl->name; +} + + +/// Gets the test case metadata. +/// +/// This combines the test case's metadata with any possible test program +/// metadata, using the latter as defaults. You should use this method in +/// generaland not get_raw_metadata(). +/// +/// \return The test case metadata. +model::metadata +model::test_case::get_metadata(void) const +{ + return _pimpl->get_metadata(); +} + + +/// Gets the original test case metadata without test program overrides. +/// +/// This method should be used for storage purposes as serialized test cases +/// should record exactly whatever the test case reported and not what the test +/// program may have provided. The final values will be reconstructed at load +/// time. +/// +/// \return The test case metadata. +const model::metadata& +model::test_case::get_raw_metadata(void) const +{ + return _pimpl->md; +} + + +/// Gets the fake result pre-stored for this test case. +/// +/// \return A fake result, or none if not defined. +optional< model::test_result > +model::test_case::fake_result(void) const +{ + return _pimpl->fake_result; +} + + +/// Equality comparator. +/// +/// \warning Because test cases reference their container test programs, and +/// test programs include test cases, we cannot perform a full comparison here: +/// otherwise, we'd enter an inifinte loop. Therefore, out of necessity, this +/// does NOT compare whether the container test programs of the affected test +/// cases are the same. +/// +/// \param other The other object to compare this one to. +/// +/// \return True if this object and other are equal; false otherwise. +bool +model::test_case::operator==(const test_case& other) const +{ + return _pimpl == other._pimpl || *_pimpl == *other._pimpl; +} + + +/// Inequality comparator. +/// +/// \param other The other object to compare this one to. +/// +/// \return True if this object and other are different; false otherwise. +bool +model::test_case::operator!=(const test_case& other) const +{ + return !(*this == other); +} + + +/// Injects the object into a stream. +/// +/// \param output The stream into which to inject the object. +/// \param object The object to format. +/// +/// \return The output stream. +std::ostream& +model::operator<<(std::ostream& output, const test_case& object) +{ + output << F("test_case{name=%s, metadata=%s}") + % text::quote(object.name(), '\'') + % object.get_metadata(); + return output; +} + + +/// Adds an already-constructed test case. +/// +/// \param test_case The test case to add. +/// +/// \return A reference to this builder. +model::test_cases_map_builder& +model::test_cases_map_builder::add(const test_case& test_case) +{ + _test_cases.insert( + test_cases_map::value_type(test_case.name(), test_case)); + return *this; +} + + +/// Constructs and adds a new test case with default metadata. +/// +/// \param test_case_name The name of the test case to add. +/// +/// \return A reference to this builder. +model::test_cases_map_builder& +model::test_cases_map_builder::add(const std::string& test_case_name) +{ + return add(test_case(test_case_name, metadata_builder().build())); +} + + +/// Constructs and adds a new test case with explicit metadata. +/// +/// \param test_case_name The name of the test case to add. +/// \param metadata The metadata of the test case. +/// +/// \return A reference to this builder. +model::test_cases_map_builder& +model::test_cases_map_builder::add(const std::string& test_case_name, + const metadata& metadata) +{ + return add(test_case(test_case_name, metadata)); +} + + +/// Creates a new test_cases_map. +/// +/// \return The constructed test_cases_map. +model::test_cases_map +model::test_cases_map_builder::build(void) const +{ + return _test_cases; +} |