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 /// Optional pointer to a debugger attached. 64 engine::debugger_ptr debugger; 65 66 /// Constructor. 67 /// 68 /// \param name_ The name of the test case within the test program. 69 /// \param md_defaults_ Metadata of the container test program. 70 /// \param md_ Metadata of the test case. 71 /// \param fake_result_ Fake result to return instead of running the test 72 /// case. 73 impl(const std::string& name_, 74 const model::metadata* md_defaults_, 75 const model::metadata& md_, 76 const optional< model::test_result >& fake_result_) : 77 name(name_), 78 md_defaults(md_defaults_), 79 md(md_), 80 fake_result(fake_result_) 81 { 82 } 83 84 /// Gets the test case metadata. 85 /// 86 /// This combines the test case's metadata with any possible test program 87 /// metadata, using the latter as defaults. 88 /// 89 /// \return The test case metadata. 90 model::metadata 91 get_metadata(void) const 92 { 93 if (md_defaults != NULL) { 94 return md_defaults->apply_overrides(md); 95 } else { 96 return md; 97 } 98 } 99 100 /// Equality comparator. 101 /// 102 /// \param other The other object to compare this one to. 103 /// 104 /// \return True if this object and other are equal; false otherwise. 105 bool 106 operator==(const impl& other) const 107 { 108 return (name == other.name && 109 get_metadata() == other.get_metadata() && 110 fake_result == other.fake_result); 111 } 112 }; 113 114 115 /// Constructs a new test case from an already-built impl oject. 116 /// 117 /// \param pimpl_ The internal representation of the test case. 118 model::test_case::test_case(std::shared_ptr< impl > pimpl_) : 119 _pimpl(pimpl_) 120 { 121 } 122 123 124 /// Constructs a new test case. 125 /// 126 /// \param name_ The name of the test case within the test program. 127 /// \param md_ Metadata of the test case. 128 model::test_case::test_case(const std::string& name_, 129 const model::metadata& md_) : 130 _pimpl(new impl(name_, NULL, md_, none)) 131 { 132 } 133 134 135 136 /// Constructs a new fake test case. 137 /// 138 /// A fake test case is a test case that is not really defined by the test 139 /// program. Such test cases have a name surrounded by '__' and, when executed, 140 /// they return a fixed, pre-recorded result. 141 /// 142 /// This is necessary for the cases where listing the test cases of a test 143 /// program fails. In this scenario, we generate a single test case within 144 /// the test program that unconditionally returns a failure. 145 /// 146 /// TODO(jmmv): Need to get rid of this. We should be able to report the 147 /// status of test programs independently of test cases, as some interfaces 148 /// don't know about the latter at all. 149 /// 150 /// \param name_ The name to give to this fake test case. This name has to be 151 /// prefixed and suffixed by '__' to clearly denote that this is internal. 152 /// \param description_ The description of the test case, if any. 153 /// \param test_result_ The fake result to return when this test case is run. 154 model::test_case::test_case( 155 const std::string& name_, 156 const std::string& description_, 157 const model::test_result& test_result_) : 158 _pimpl(new impl( 159 name_, 160 NULL, 161 model::metadata_builder().set_description(description_).build(), 162 utils::make_optional(test_result_))) 163 { 164 PRE_MSG(name_.length() > 4 && name_.substr(0, 2) == "__" && 165 name_.substr(name_.length() - 2) == "__", 166 "Invalid fake name provided to fake test case"); 167 } 168 169 170 /// Destroys a test case. 171 model::test_case::~test_case(void) 172 { 173 } 174 175 176 /// Constructs a new test case applying metadata defaults. 177 /// 178 /// This method is intended to be used by the container test program when 179 /// ownership of the test is given to it. At that point, the test case receives 180 /// the default metadata properties of the test program, not the global 181 /// defaults. 182 /// 183 /// \param defaults The metadata properties to use as defaults. The provided 184 /// object's lifetime MUST extend the lifetime of the test case. Because 185 /// this is only intended to point at the metadata of the test program 186 /// containing this test case, this assumption should hold. 187 /// 188 /// \return A new test case. 189 model::test_case 190 model::test_case::apply_metadata_defaults(const metadata* defaults) const 191 { 192 return test_case(std::shared_ptr< impl >(new impl( 193 _pimpl->name, 194 defaults, 195 _pimpl->md, 196 _pimpl->fake_result))); 197 } 198 199 200 /// Gets the test case name. 201 /// 202 /// \return The test case name, relative to the test program. 203 const std::string& 204 model::test_case::name(void) const 205 { 206 return _pimpl->name; 207 } 208 209 210 /// Gets the test case metadata. 211 /// 212 /// This combines the test case's metadata with any possible test program 213 /// metadata, using the latter as defaults. You should use this method in 214 /// generaland not get_raw_metadata(). 215 /// 216 /// \return The test case metadata. 217 model::metadata 218 model::test_case::get_metadata(void) const 219 { 220 return _pimpl->get_metadata(); 221 } 222 223 224 /// Gets the original test case metadata without test program overrides. 225 /// 226 /// This method should be used for storage purposes as serialized test cases 227 /// should record exactly whatever the test case reported and not what the test 228 /// program may have provided. The final values will be reconstructed at load 229 /// time. 230 /// 231 /// \return The test case metadata. 232 const model::metadata& 233 model::test_case::get_raw_metadata(void) const 234 { 235 return _pimpl->md; 236 } 237 238 239 /// Attach a debugger to the test case. 240 void 241 model::test_case::attach_debugger(engine::debugger_ptr debugger) const 242 { 243 _pimpl->debugger = debugger; 244 } 245 246 247 /// Gets the optional pointer to a debugger. 248 /// 249 /// \return An optional pointer to a debugger. 250 engine::debugger_ptr 251 model::test_case::get_debugger() const 252 { 253 return _pimpl->debugger; 254 } 255 256 257 /// Gets the fake result pre-stored for this test case. 258 /// 259 /// \return A fake result, or none if not defined. 260 optional< model::test_result > 261 model::test_case::fake_result(void) const 262 { 263 return _pimpl->fake_result; 264 } 265 266 267 /// Equality comparator. 268 /// 269 /// \warning Because test cases reference their container test programs, and 270 /// test programs include test cases, we cannot perform a full comparison here: 271 /// otherwise, we'd enter an inifinte loop. Therefore, out of necessity, this 272 /// does NOT compare whether the container test programs of the affected test 273 /// cases are the same. 274 /// 275 /// \param other The other object to compare this one to. 276 /// 277 /// \return True if this object and other are equal; false otherwise. 278 bool 279 model::test_case::operator==(const test_case& other) const 280 { 281 return _pimpl == other._pimpl || *_pimpl == *other._pimpl; 282 } 283 284 285 /// Inequality comparator. 286 /// 287 /// \param other The other object to compare this one to. 288 /// 289 /// \return True if this object and other are different; false otherwise. 290 bool 291 model::test_case::operator!=(const test_case& other) const 292 { 293 return !(*this == other); 294 } 295 296 297 /// Injects the object into a stream. 298 /// 299 /// \param output The stream into which to inject the object. 300 /// \param object The object to format. 301 /// 302 /// \return The output stream. 303 std::ostream& 304 model::operator<<(std::ostream& output, const test_case& object) 305 { 306 output << F("test_case{name=%s, metadata=%s}") 307 % text::quote(object.name(), '\'') 308 % object.get_metadata(); 309 return output; 310 } 311 312 313 /// Adds an already-constructed test case. 314 /// 315 /// \param test_case The test case to add. 316 /// 317 /// \return A reference to this builder. 318 model::test_cases_map_builder& 319 model::test_cases_map_builder::add(const test_case& test_case) 320 { 321 _test_cases.insert( 322 test_cases_map::value_type(test_case.name(), test_case)); 323 return *this; 324 } 325 326 327 /// Constructs and adds a new test case with default metadata. 328 /// 329 /// \param test_case_name The name of the test case to add. 330 /// 331 /// \return A reference to this builder. 332 model::test_cases_map_builder& 333 model::test_cases_map_builder::add(const std::string& test_case_name) 334 { 335 return add(test_case(test_case_name, metadata_builder().build())); 336 } 337 338 339 /// Constructs and adds a new test case with explicit metadata. 340 /// 341 /// \param test_case_name The name of the test case to add. 342 /// \param metadata The metadata of the test case. 343 /// 344 /// \return A reference to this builder. 345 model::test_cases_map_builder& 346 model::test_cases_map_builder::add(const std::string& test_case_name, 347 const metadata& metadata) 348 { 349 return add(test_case(test_case_name, metadata)); 350 } 351 352 353 /// Creates a new test_cases_map. 354 /// 355 /// \return The constructed test_cases_map. 356 model::test_cases_map 357 model::test_cases_map_builder::build(void) const 358 { 359 return _test_cases; 360 } 361