1 // Copyright 2010 The Kyua Authors. 2 // All rights reserved. 3 // 4 // Redistribution and use in source and binary forms, with or without 5 // modification, are permitted provided that the following conditions are 6 // met: 7 // 8 // * Redistributions of source code must retain the above copyright 9 // notice, this list of conditions and the following disclaimer. 10 // * Redistributions in binary form must reproduce the above copyright 11 // notice, this list of conditions and the following disclaimer in the 12 // documentation and/or other materials provided with the distribution. 13 // * Neither the name of Google Inc. nor the names of its contributors 14 // may be used to endorse or promote products derived from this software 15 // without specific prior written permission. 16 // 17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 29 #include "model/test_case.hpp" 30 31 #include "model/metadata.hpp" 32 #include "model/test_result.hpp" 33 #include "utils/format/macros.hpp" 34 #include "utils/noncopyable.hpp" 35 #include "utils/optional.ipp" 36 #include "utils/text/operations.ipp" 37 38 namespace text = utils::text; 39 40 using utils::none; 41 using utils::optional; 42 43 44 /// Internal implementation for a test_case. 45 struct model::test_case::impl : utils::noncopyable { 46 /// Name of the test case; must be unique within the test program. 47 std::string name; 48 49 /// Metadata of the container test program. 50 /// 51 /// Yes, this is a pointer. Yes, we do not own the object pointed to. 52 /// However, because this is only intended to point to the metadata object 53 /// of test programs _containing_ this test case, we can assume that the 54 /// referenced object will be alive for the lifetime of this test case. 55 const model::metadata* md_defaults; 56 57 /// Test case metadata. 58 model::metadata md; 59 60 /// Fake result to return instead of running the test case. 61 optional< model::test_result > fake_result; 62 63 /// Constructor. 64 /// 65 /// \param name_ The name of the test case within the test program. 66 /// \param md_defaults_ Metadata of the container test program. 67 /// \param md_ Metadata of the test case. 68 /// \param fake_result_ Fake result to return instead of running the test 69 /// case. 70 impl(const std::string& name_, 71 const model::metadata* md_defaults_, 72 const model::metadata& md_, 73 const optional< model::test_result >& fake_result_) : 74 name(name_), 75 md_defaults(md_defaults_), 76 md(md_), 77 fake_result(fake_result_) 78 { 79 } 80 81 /// Gets the test case metadata. 82 /// 83 /// This combines the test case's metadata with any possible test program 84 /// metadata, using the latter as defaults. 85 /// 86 /// \return The test case metadata. 87 model::metadata 88 get_metadata(void) const 89 { 90 if (md_defaults != NULL) { 91 return md_defaults->apply_overrides(md); 92 } else { 93 return md; 94 } 95 } 96 97 /// Equality comparator. 98 /// 99 /// \param other The other object to compare this one to. 100 /// 101 /// \return True if this object and other are equal; false otherwise. 102 bool 103 operator==(const impl& other) const 104 { 105 return (name == other.name && 106 get_metadata() == other.get_metadata() && 107 fake_result == other.fake_result); 108 } 109 }; 110 111 112 /// Constructs a new test case from an already-built impl oject. 113 /// 114 /// \param pimpl_ The internal representation of the test case. 115 model::test_case::test_case(std::shared_ptr< impl > pimpl_) : 116 _pimpl(pimpl_) 117 { 118 } 119 120 121 /// Constructs a new test case. 122 /// 123 /// \param name_ The name of the test case within the test program. 124 /// \param md_ Metadata of the test case. 125 model::test_case::test_case(const std::string& name_, 126 const model::metadata& md_) : 127 _pimpl(new impl(name_, NULL, md_, none)) 128 { 129 } 130 131 132 133 /// Constructs a new fake test case. 134 /// 135 /// A fake test case is a test case that is not really defined by the test 136 /// program. Such test cases have a name surrounded by '__' and, when executed, 137 /// they return a fixed, pre-recorded result. 138 /// 139 /// This is necessary for the cases where listing the test cases of a test 140 /// program fails. In this scenario, we generate a single test case within 141 /// the test program that unconditionally returns a failure. 142 /// 143 /// TODO(jmmv): Need to get rid of this. We should be able to report the 144 /// status of test programs independently of test cases, as some interfaces 145 /// don't know about the latter at all. 146 /// 147 /// \param name_ The name to give to this fake test case. This name has to be 148 /// prefixed and suffixed by '__' to clearly denote that this is internal. 149 /// \param description_ The description of the test case, if any. 150 /// \param test_result_ The fake result to return when this test case is run. 151 model::test_case::test_case( 152 const std::string& name_, 153 const std::string& description_, 154 const model::test_result& test_result_) : 155 _pimpl(new impl( 156 name_, 157 NULL, 158 model::metadata_builder().set_description(description_).build(), 159 utils::make_optional(test_result_))) 160 { 161 PRE_MSG(name_.length() > 4 && name_.substr(0, 2) == "__" && 162 name_.substr(name_.length() - 2) == "__", 163 "Invalid fake name provided to fake test case"); 164 } 165 166 167 /// Destroys a test case. 168 model::test_case::~test_case(void) 169 { 170 } 171 172 173 /// Constructs a new test case applying metadata defaults. 174 /// 175 /// This method is intended to be used by the container test program when 176 /// ownership of the test is given to it. At that point, the test case receives 177 /// the default metadata properties of the test program, not the global 178 /// defaults. 179 /// 180 /// \param defaults The metadata properties to use as defaults. The provided 181 /// object's lifetime MUST extend the lifetime of the test case. Because 182 /// this is only intended to point at the metadata of the test program 183 /// containing this test case, this assumption should hold. 184 /// 185 /// \return A new test case. 186 model::test_case 187 model::test_case::apply_metadata_defaults(const metadata* defaults) const 188 { 189 return test_case(std::shared_ptr< impl >(new impl( 190 _pimpl->name, 191 defaults, 192 _pimpl->md, 193 _pimpl->fake_result))); 194 } 195 196 197 /// Gets the test case name. 198 /// 199 /// \return The test case name, relative to the test program. 200 const std::string& 201 model::test_case::name(void) const 202 { 203 return _pimpl->name; 204 } 205 206 207 /// Gets the test case metadata. 208 /// 209 /// This combines the test case's metadata with any possible test program 210 /// metadata, using the latter as defaults. You should use this method in 211 /// generaland not get_raw_metadata(). 212 /// 213 /// \return The test case metadata. 214 model::metadata 215 model::test_case::get_metadata(void) const 216 { 217 return _pimpl->get_metadata(); 218 } 219 220 221 /// Gets the original test case metadata without test program overrides. 222 /// 223 /// This method should be used for storage purposes as serialized test cases 224 /// should record exactly whatever the test case reported and not what the test 225 /// program may have provided. The final values will be reconstructed at load 226 /// time. 227 /// 228 /// \return The test case metadata. 229 const model::metadata& 230 model::test_case::get_raw_metadata(void) const 231 { 232 return _pimpl->md; 233 } 234 235 236 /// Gets the fake result pre-stored for this test case. 237 /// 238 /// \return A fake result, or none if not defined. 239 optional< model::test_result > 240 model::test_case::fake_result(void) const 241 { 242 return _pimpl->fake_result; 243 } 244 245 246 /// Equality comparator. 247 /// 248 /// \warning Because test cases reference their container test programs, and 249 /// test programs include test cases, we cannot perform a full comparison here: 250 /// otherwise, we'd enter an inifinte loop. Therefore, out of necessity, this 251 /// does NOT compare whether the container test programs of the affected test 252 /// cases are the same. 253 /// 254 /// \param other The other object to compare this one to. 255 /// 256 /// \return True if this object and other are equal; false otherwise. 257 bool 258 model::test_case::operator==(const test_case& other) const 259 { 260 return _pimpl == other._pimpl || *_pimpl == *other._pimpl; 261 } 262 263 264 /// Inequality comparator. 265 /// 266 /// \param other The other object to compare this one to. 267 /// 268 /// \return True if this object and other are different; false otherwise. 269 bool 270 model::test_case::operator!=(const test_case& other) const 271 { 272 return !(*this == other); 273 } 274 275 276 /// Injects the object into a stream. 277 /// 278 /// \param output The stream into which to inject the object. 279 /// \param object The object to format. 280 /// 281 /// \return The output stream. 282 std::ostream& 283 model::operator<<(std::ostream& output, const test_case& object) 284 { 285 output << F("test_case{name=%s, metadata=%s}") 286 % text::quote(object.name(), '\'') 287 % object.get_metadata(); 288 return output; 289 } 290 291 292 /// Adds an already-constructed test case. 293 /// 294 /// \param test_case The test case to add. 295 /// 296 /// \return A reference to this builder. 297 model::test_cases_map_builder& 298 model::test_cases_map_builder::add(const test_case& test_case) 299 { 300 _test_cases.insert( 301 test_cases_map::value_type(test_case.name(), test_case)); 302 return *this; 303 } 304 305 306 /// Constructs and adds a new test case with default metadata. 307 /// 308 /// \param test_case_name The name of the test case to add. 309 /// 310 /// \return A reference to this builder. 311 model::test_cases_map_builder& 312 model::test_cases_map_builder::add(const std::string& test_case_name) 313 { 314 return add(test_case(test_case_name, metadata_builder().build())); 315 } 316 317 318 /// Constructs and adds a new test case with explicit metadata. 319 /// 320 /// \param test_case_name The name of the test case to add. 321 /// \param metadata The metadata of the test case. 322 /// 323 /// \return A reference to this builder. 324 model::test_cases_map_builder& 325 model::test_cases_map_builder::add(const std::string& test_case_name, 326 const metadata& metadata) 327 { 328 return add(test_case(test_case_name, metadata)); 329 } 330 331 332 /// Creates a new test_cases_map. 333 /// 334 /// \return The constructed test_cases_map. 335 model::test_cases_map 336 model::test_cases_map_builder::build(void) const 337 { 338 return _test_cases; 339 } 340