1*eb8f6990SEnji Cooper // Copyright 2024 The Kyua Authors. 2*eb8f6990SEnji Cooper // All rights reserved. 3*eb8f6990SEnji Cooper // 4*eb8f6990SEnji Cooper // Redistribution and use in source and binary forms, with or without 5*eb8f6990SEnji Cooper // modification, are permitted provided that the following conditions are 6*eb8f6990SEnji Cooper // met: 7*eb8f6990SEnji Cooper // 8*eb8f6990SEnji Cooper // * Redistributions of source code must retain the above copyright 9*eb8f6990SEnji Cooper // notice, this list of conditions and the following disclaimer. 10*eb8f6990SEnji Cooper // * Redistributions in binary form must reproduce the above copyright 11*eb8f6990SEnji Cooper // notice, this list of conditions and the following disclaimer in the 12*eb8f6990SEnji Cooper // documentation and/or other materials provided with the distribution. 13*eb8f6990SEnji Cooper // * Neither the name of Google Inc. nor the names of its contributors 14*eb8f6990SEnji Cooper // may be used to endorse or promote products derived from this software 15*eb8f6990SEnji Cooper // without specific prior written permission. 16*eb8f6990SEnji Cooper // 17*eb8f6990SEnji Cooper // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18*eb8f6990SEnji Cooper // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19*eb8f6990SEnji Cooper // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20*eb8f6990SEnji Cooper // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21*eb8f6990SEnji Cooper // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22*eb8f6990SEnji Cooper // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23*eb8f6990SEnji Cooper // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24*eb8f6990SEnji Cooper // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25*eb8f6990SEnji Cooper // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26*eb8f6990SEnji Cooper // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27*eb8f6990SEnji Cooper // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28*eb8f6990SEnji Cooper 29*eb8f6990SEnji Cooper #include "engine/googletest_result.hpp" 30*eb8f6990SEnji Cooper 31*eb8f6990SEnji Cooper #include <cstdlib> 32*eb8f6990SEnji Cooper #include <fstream> 33*eb8f6990SEnji Cooper #include <regex> 34*eb8f6990SEnji Cooper #include <utility> 35*eb8f6990SEnji Cooper 36*eb8f6990SEnji Cooper #include "engine/exceptions.hpp" 37*eb8f6990SEnji Cooper #include "model/test_result.hpp" 38*eb8f6990SEnji Cooper #include "utils/fs/path.hpp" 39*eb8f6990SEnji Cooper #include "utils/format/macros.hpp" 40*eb8f6990SEnji Cooper #include "utils/optional.ipp" 41*eb8f6990SEnji Cooper #include "utils/process/status.hpp" 42*eb8f6990SEnji Cooper #include "utils/sanity.hpp" 43*eb8f6990SEnji Cooper #include "utils/text/exceptions.hpp" 44*eb8f6990SEnji Cooper #include "utils/text/operations.ipp" 45*eb8f6990SEnji Cooper 46*eb8f6990SEnji Cooper namespace fs = utils::fs; 47*eb8f6990SEnji Cooper namespace process = utils::process; 48*eb8f6990SEnji Cooper namespace text = utils::text; 49*eb8f6990SEnji Cooper 50*eb8f6990SEnji Cooper using utils::none; 51*eb8f6990SEnji Cooper using utils::optional; 52*eb8f6990SEnji Cooper 53*eb8f6990SEnji Cooper 54*eb8f6990SEnji Cooper namespace { 55*eb8f6990SEnji Cooper 56*eb8f6990SEnji Cooper /// Internal string for specifying invalid output. 57*eb8f6990SEnji Cooper const std::string invalid_output_message = "invalid output"; 58*eb8f6990SEnji Cooper 59*eb8f6990SEnji Cooper /// Regular expression for disabled tests line. 60*eb8f6990SEnji Cooper const std::regex disabled_re( 61*eb8f6990SEnji Cooper R"RE((YOU HAVE [[:digit:]]+ DISABLED TESTS?))RE" 62*eb8f6990SEnji Cooper ); 63*eb8f6990SEnji Cooper 64*eb8f6990SEnji Cooper /// Regular expression for starting sentinel of results block. 65*eb8f6990SEnji Cooper const std::regex starting_sentinel_re( 66*eb8f6990SEnji Cooper R"(\[[[:space:]]+RUN[[:space:]]+\][[:space:]]+[A-Za-z0-9]+\.[A-Za-z0-9]+)" 67*eb8f6990SEnji Cooper ); 68*eb8f6990SEnji Cooper 69*eb8f6990SEnji Cooper /// Regular expression for ending sentinel of results block. 70*eb8f6990SEnji Cooper const std::regex ending_sentinel_re( 71*eb8f6990SEnji Cooper R"RE(\[[[:space:]]+(FAILED|OK|SKIPPED)[[:space:]]+\])RE" 72*eb8f6990SEnji Cooper ); 73*eb8f6990SEnji Cooper 74*eb8f6990SEnji Cooper /// Parses a test result that does not accept a reason. 75*eb8f6990SEnji Cooper /// 76*eb8f6990SEnji Cooper /// \param status The result status name. 77*eb8f6990SEnji Cooper /// \param rest The rest of the line after the status name. 78*eb8f6990SEnji Cooper /// 79*eb8f6990SEnji Cooper /// \return An object representing the test result. 80*eb8f6990SEnji Cooper /// 81*eb8f6990SEnji Cooper /// \throw format_error If the result is invalid (i.e. rest is invalid). 82*eb8f6990SEnji Cooper /// 83*eb8f6990SEnji Cooper /// \pre status must be "successful". 84*eb8f6990SEnji Cooper static engine::googletest_result 85*eb8f6990SEnji Cooper parse_without_reason(const std::string& status, const std::string& rest) 86*eb8f6990SEnji Cooper { 87*eb8f6990SEnji Cooper if (!rest.empty()) 88*eb8f6990SEnji Cooper throw engine::format_error(F("%s cannot have a reason") % status); 89*eb8f6990SEnji Cooper 90*eb8f6990SEnji Cooper if (status == "skipped") 91*eb8f6990SEnji Cooper return engine::googletest_result(engine::googletest_result::skipped); 92*eb8f6990SEnji Cooper else { 93*eb8f6990SEnji Cooper INV(status == "successful"); 94*eb8f6990SEnji Cooper return engine::googletest_result(engine::googletest_result::successful); 95*eb8f6990SEnji Cooper } 96*eb8f6990SEnji Cooper } 97*eb8f6990SEnji Cooper 98*eb8f6990SEnji Cooper 99*eb8f6990SEnji Cooper /// Parses a test result that needs a reason. 100*eb8f6990SEnji Cooper /// 101*eb8f6990SEnji Cooper /// \param status The result status name. 102*eb8f6990SEnji Cooper /// \param rest The rest of the line after the status name. 103*eb8f6990SEnji Cooper /// 104*eb8f6990SEnji Cooper /// \return An object representing the test result. 105*eb8f6990SEnji Cooper /// 106*eb8f6990SEnji Cooper /// \throw format_error If the result is invalid (i.e. rest is invalid). 107*eb8f6990SEnji Cooper /// 108*eb8f6990SEnji Cooper /// \pre status must be one of "broken", "disabled", "failed", or "skipped". 109*eb8f6990SEnji Cooper static engine::googletest_result 110*eb8f6990SEnji Cooper parse_with_reason(const std::string& status, const std::string& rest) 111*eb8f6990SEnji Cooper { 112*eb8f6990SEnji Cooper using engine::googletest_result; 113*eb8f6990SEnji Cooper 114*eb8f6990SEnji Cooper INV(!rest.empty()); 115*eb8f6990SEnji Cooper 116*eb8f6990SEnji Cooper if (status == "broken") 117*eb8f6990SEnji Cooper return googletest_result(googletest_result::broken, rest); 118*eb8f6990SEnji Cooper else if (status == "disabled") 119*eb8f6990SEnji Cooper return googletest_result(googletest_result::disabled, rest); 120*eb8f6990SEnji Cooper else if (status == "failed") 121*eb8f6990SEnji Cooper return googletest_result(googletest_result::failed, rest); 122*eb8f6990SEnji Cooper else if (status == "skipped") 123*eb8f6990SEnji Cooper return googletest_result(googletest_result::skipped, rest); 124*eb8f6990SEnji Cooper else 125*eb8f6990SEnji Cooper PRE_MSG(false, "Unexpected status"); 126*eb8f6990SEnji Cooper } 127*eb8f6990SEnji Cooper 128*eb8f6990SEnji Cooper 129*eb8f6990SEnji Cooper /// Formats the termination status of a process to be used with validate_result. 130*eb8f6990SEnji Cooper /// 131*eb8f6990SEnji Cooper /// \param status The status to format. 132*eb8f6990SEnji Cooper /// 133*eb8f6990SEnji Cooper /// \return A string describing the status. 134*eb8f6990SEnji Cooper static std::string 135*eb8f6990SEnji Cooper format_status(const process::status& status) 136*eb8f6990SEnji Cooper { 137*eb8f6990SEnji Cooper if (status.exited()) 138*eb8f6990SEnji Cooper return F("exited with code %s") % status.exitstatus(); 139*eb8f6990SEnji Cooper else if (status.signaled()) 140*eb8f6990SEnji Cooper return F("received signal %s%s") % status.termsig() % 141*eb8f6990SEnji Cooper (status.coredump() ? " (core dumped)" : ""); 142*eb8f6990SEnji Cooper else 143*eb8f6990SEnji Cooper return F("terminated in an unknown manner"); 144*eb8f6990SEnji Cooper } 145*eb8f6990SEnji Cooper 146*eb8f6990SEnji Cooper 147*eb8f6990SEnji Cooper } // anonymous namespace 148*eb8f6990SEnji Cooper 149*eb8f6990SEnji Cooper 150*eb8f6990SEnji Cooper /// Constructs a raw result with a type. 151*eb8f6990SEnji Cooper /// 152*eb8f6990SEnji Cooper /// The reason is left uninitialized. 153*eb8f6990SEnji Cooper /// 154*eb8f6990SEnji Cooper /// \param type_ The type of the result. 155*eb8f6990SEnji Cooper engine::googletest_result::googletest_result(const types type_) : 156*eb8f6990SEnji Cooper _type(type_) 157*eb8f6990SEnji Cooper { 158*eb8f6990SEnji Cooper } 159*eb8f6990SEnji Cooper 160*eb8f6990SEnji Cooper 161*eb8f6990SEnji Cooper /// Constructs a raw result with a type and a reason. 162*eb8f6990SEnji Cooper /// 163*eb8f6990SEnji Cooper /// \param type_ The type of the result. 164*eb8f6990SEnji Cooper /// \param reason_ The reason for the result. 165*eb8f6990SEnji Cooper engine::googletest_result::googletest_result( 166*eb8f6990SEnji Cooper const types type_, 167*eb8f6990SEnji Cooper const std::string& reason_) : 168*eb8f6990SEnji Cooper _type(type_), _reason(reason_) 169*eb8f6990SEnji Cooper { 170*eb8f6990SEnji Cooper } 171*eb8f6990SEnji Cooper 172*eb8f6990SEnji Cooper 173*eb8f6990SEnji Cooper /// Parses an input stream to extract a test result. 174*eb8f6990SEnji Cooper /// 175*eb8f6990SEnji Cooper /// If the parsing fails for any reason, the test result is 'broken' and it 176*eb8f6990SEnji Cooper /// contains the reason for the parsing failure. Test cases that report results 177*eb8f6990SEnji Cooper /// in an inconsistent state cannot be trusted (e.g. the test program code may 178*eb8f6990SEnji Cooper /// have a bug), and thus why they are reported as broken instead of just failed 179*eb8f6990SEnji Cooper /// (which is a legitimate result for a test case). 180*eb8f6990SEnji Cooper /// 181*eb8f6990SEnji Cooper /// \param input The stream to read from. 182*eb8f6990SEnji Cooper /// 183*eb8f6990SEnji Cooper /// \return A generic representation of the result of the test case. 184*eb8f6990SEnji Cooper /// 185*eb8f6990SEnji Cooper /// \throw format_error If the input is invalid. 186*eb8f6990SEnji Cooper engine::googletest_result 187*eb8f6990SEnji Cooper engine::googletest_result::parse(std::istream& input) 188*eb8f6990SEnji Cooper { 189*eb8f6990SEnji Cooper std::vector<std::string> lines; 190*eb8f6990SEnji Cooper 191*eb8f6990SEnji Cooper do { 192*eb8f6990SEnji Cooper std::string line; 193*eb8f6990SEnji Cooper std::getline(input, line, '\n'); 194*eb8f6990SEnji Cooper if (!input.eof()) 195*eb8f6990SEnji Cooper line.push_back('\n'); 196*eb8f6990SEnji Cooper lines.push_back(line); 197*eb8f6990SEnji Cooper } while (input.good()); 198*eb8f6990SEnji Cooper 199*eb8f6990SEnji Cooper bool capture_context = false; 200*eb8f6990SEnji Cooper bool valid_output = false; 201*eb8f6990SEnji Cooper std::string context, status; 202*eb8f6990SEnji Cooper 203*eb8f6990SEnji Cooper for (auto& line: lines) { 204*eb8f6990SEnji Cooper std::smatch matches; 205*eb8f6990SEnji Cooper 206*eb8f6990SEnji Cooper if (regex_search(line, matches, disabled_re)) { 207*eb8f6990SEnji Cooper context = matches[1]; 208*eb8f6990SEnji Cooper status = "disabled"; 209*eb8f6990SEnji Cooper valid_output = true; 210*eb8f6990SEnji Cooper break; 211*eb8f6990SEnji Cooper } 212*eb8f6990SEnji Cooper if (regex_search(line, matches, starting_sentinel_re)) { 213*eb8f6990SEnji Cooper capture_context = true; 214*eb8f6990SEnji Cooper context = ""; 215*eb8f6990SEnji Cooper continue; 216*eb8f6990SEnji Cooper } 217*eb8f6990SEnji Cooper if (regex_search(line, matches, ending_sentinel_re)) { 218*eb8f6990SEnji Cooper std::string googletest_res = matches[1]; 219*eb8f6990SEnji Cooper if (googletest_res == "OK") { 220*eb8f6990SEnji Cooper context = ""; 221*eb8f6990SEnji Cooper status = "successful"; 222*eb8f6990SEnji Cooper } else if (googletest_res == "FAILED") { 223*eb8f6990SEnji Cooper status = "failed"; 224*eb8f6990SEnji Cooper } else { 225*eb8f6990SEnji Cooper INV(googletest_res == "SKIPPED"); 226*eb8f6990SEnji Cooper status = "skipped"; 227*eb8f6990SEnji Cooper } 228*eb8f6990SEnji Cooper capture_context = false; 229*eb8f6990SEnji Cooper valid_output = true; 230*eb8f6990SEnji Cooper } 231*eb8f6990SEnji Cooper if (capture_context) { 232*eb8f6990SEnji Cooper context += line; 233*eb8f6990SEnji Cooper } 234*eb8f6990SEnji Cooper } 235*eb8f6990SEnji Cooper if (!valid_output) { 236*eb8f6990SEnji Cooper context = invalid_output_message; 237*eb8f6990SEnji Cooper status = "broken"; 238*eb8f6990SEnji Cooper } 239*eb8f6990SEnji Cooper if (status == "skipped" && context.empty()) 240*eb8f6990SEnji Cooper context = bogus_googletest_skipped_nul_message; 241*eb8f6990SEnji Cooper if (context.empty()) 242*eb8f6990SEnji Cooper return parse_without_reason(status, context); 243*eb8f6990SEnji Cooper else 244*eb8f6990SEnji Cooper return parse_with_reason(status, context); 245*eb8f6990SEnji Cooper } 246*eb8f6990SEnji Cooper 247*eb8f6990SEnji Cooper 248*eb8f6990SEnji Cooper /// Loads a test case result from a file. 249*eb8f6990SEnji Cooper /// 250*eb8f6990SEnji Cooper /// \param file The file to parse. 251*eb8f6990SEnji Cooper /// 252*eb8f6990SEnji Cooper /// \return The parsed test case result if all goes well. 253*eb8f6990SEnji Cooper /// 254*eb8f6990SEnji Cooper /// \throw std::runtime_error If the file does not exist. 255*eb8f6990SEnji Cooper /// \throw engine::format_error If the contents of the file are bogus. 256*eb8f6990SEnji Cooper engine::googletest_result 257*eb8f6990SEnji Cooper engine::googletest_result::load(const fs::path& file) 258*eb8f6990SEnji Cooper { 259*eb8f6990SEnji Cooper std::ifstream input(file.c_str()); 260*eb8f6990SEnji Cooper if (!input) 261*eb8f6990SEnji Cooper throw std::runtime_error("Cannot open results file"); 262*eb8f6990SEnji Cooper else 263*eb8f6990SEnji Cooper return parse(input); 264*eb8f6990SEnji Cooper } 265*eb8f6990SEnji Cooper 266*eb8f6990SEnji Cooper 267*eb8f6990SEnji Cooper /// Gets the type of the result. 268*eb8f6990SEnji Cooper /// 269*eb8f6990SEnji Cooper /// \return A result type. 270*eb8f6990SEnji Cooper engine::googletest_result::types 271*eb8f6990SEnji Cooper engine::googletest_result::type(void) const 272*eb8f6990SEnji Cooper { 273*eb8f6990SEnji Cooper return _type; 274*eb8f6990SEnji Cooper } 275*eb8f6990SEnji Cooper 276*eb8f6990SEnji Cooper 277*eb8f6990SEnji Cooper /// Gets the optional reason of the result. 278*eb8f6990SEnji Cooper /// 279*eb8f6990SEnji Cooper /// \return The reason of the result if present; none otherwise. 280*eb8f6990SEnji Cooper const optional< std::string >& 281*eb8f6990SEnji Cooper engine::googletest_result::reason(void) const 282*eb8f6990SEnji Cooper { 283*eb8f6990SEnji Cooper return _reason; 284*eb8f6990SEnji Cooper } 285*eb8f6990SEnji Cooper 286*eb8f6990SEnji Cooper 287*eb8f6990SEnji Cooper /// Checks whether the result should be reported as good or not. 288*eb8f6990SEnji Cooper /// 289*eb8f6990SEnji Cooper /// \return True if the result can be considered "good", false otherwise. 290*eb8f6990SEnji Cooper bool 291*eb8f6990SEnji Cooper engine::googletest_result::good(void) const 292*eb8f6990SEnji Cooper { 293*eb8f6990SEnji Cooper switch (_type) { 294*eb8f6990SEnji Cooper case googletest_result::disabled: 295*eb8f6990SEnji Cooper case googletest_result::skipped: 296*eb8f6990SEnji Cooper case googletest_result::successful: 297*eb8f6990SEnji Cooper return true; 298*eb8f6990SEnji Cooper 299*eb8f6990SEnji Cooper case googletest_result::broken: 300*eb8f6990SEnji Cooper case googletest_result::failed: 301*eb8f6990SEnji Cooper return false; 302*eb8f6990SEnji Cooper 303*eb8f6990SEnji Cooper default: 304*eb8f6990SEnji Cooper UNREACHABLE; 305*eb8f6990SEnji Cooper } 306*eb8f6990SEnji Cooper } 307*eb8f6990SEnji Cooper 308*eb8f6990SEnji Cooper 309*eb8f6990SEnji Cooper /// Reinterprets a raw result based on the termination status of the test case. 310*eb8f6990SEnji Cooper /// 311*eb8f6990SEnji Cooper /// This reinterpretation ensures that the termination conditions of the program 312*eb8f6990SEnji Cooper /// match what is expected of the paticular result reported by the test program. 313*eb8f6990SEnji Cooper /// If such conditions do not match, the test program is considered bogus and is 314*eb8f6990SEnji Cooper /// thus reported as broken. 315*eb8f6990SEnji Cooper /// 316*eb8f6990SEnji Cooper /// This is just a helper function for calculate_result(); the real result of 317*eb8f6990SEnji Cooper /// the test case cannot be inferred from apply() only. 318*eb8f6990SEnji Cooper /// 319*eb8f6990SEnji Cooper /// \param status The exit status of the test program, or none if the test 320*eb8f6990SEnji Cooper /// program timed out. 321*eb8f6990SEnji Cooper /// 322*eb8f6990SEnji Cooper /// \result The adjusted result. The original result is transformed into broken 323*eb8f6990SEnji Cooper /// if the exit status of the program does not match our expectations. 324*eb8f6990SEnji Cooper engine::googletest_result 325*eb8f6990SEnji Cooper engine::googletest_result::apply(const optional< process::status >& status) 326*eb8f6990SEnji Cooper const 327*eb8f6990SEnji Cooper { 328*eb8f6990SEnji Cooper if (!status) { 329*eb8f6990SEnji Cooper return *this; 330*eb8f6990SEnji Cooper } 331*eb8f6990SEnji Cooper 332*eb8f6990SEnji Cooper auto check_status = [&status](bool expect_pass) -> bool { 333*eb8f6990SEnji Cooper if (!status.get().exited()) { 334*eb8f6990SEnji Cooper return false; 335*eb8f6990SEnji Cooper } 336*eb8f6990SEnji Cooper auto exit_status = status.get().exitstatus(); 337*eb8f6990SEnji Cooper if (expect_pass) { 338*eb8f6990SEnji Cooper return exit_status == EXIT_SUCCESS; 339*eb8f6990SEnji Cooper } else { 340*eb8f6990SEnji Cooper return exit_status == EXIT_FAILURE; 341*eb8f6990SEnji Cooper } 342*eb8f6990SEnji Cooper }; 343*eb8f6990SEnji Cooper 344*eb8f6990SEnji Cooper switch (_type) { 345*eb8f6990SEnji Cooper case googletest_result::broken: 346*eb8f6990SEnji Cooper return *this; 347*eb8f6990SEnji Cooper 348*eb8f6990SEnji Cooper case googletest_result::disabled: 349*eb8f6990SEnji Cooper if (check_status(true)) { 350*eb8f6990SEnji Cooper return *this; 351*eb8f6990SEnji Cooper } 352*eb8f6990SEnji Cooper return googletest_result(googletest_result::broken, 353*eb8f6990SEnji Cooper "Disabled test case should have reported success but " + 354*eb8f6990SEnji Cooper format_status(status.get())); 355*eb8f6990SEnji Cooper 356*eb8f6990SEnji Cooper case googletest_result::failed: 357*eb8f6990SEnji Cooper if (check_status(false)) { 358*eb8f6990SEnji Cooper return *this; 359*eb8f6990SEnji Cooper } 360*eb8f6990SEnji Cooper return googletest_result(googletest_result::broken, 361*eb8f6990SEnji Cooper "Failed test case should have reported failure but " + 362*eb8f6990SEnji Cooper format_status(status.get())); 363*eb8f6990SEnji Cooper 364*eb8f6990SEnji Cooper case googletest_result::skipped: 365*eb8f6990SEnji Cooper if (check_status(true)) { 366*eb8f6990SEnji Cooper return *this; 367*eb8f6990SEnji Cooper } 368*eb8f6990SEnji Cooper return googletest_result(googletest_result::broken, 369*eb8f6990SEnji Cooper "Skipped test case should have reported success but " + 370*eb8f6990SEnji Cooper format_status(status.get())); 371*eb8f6990SEnji Cooper 372*eb8f6990SEnji Cooper case googletest_result::successful: 373*eb8f6990SEnji Cooper if (check_status(true)) { 374*eb8f6990SEnji Cooper return *this; 375*eb8f6990SEnji Cooper } 376*eb8f6990SEnji Cooper return googletest_result(googletest_result::broken, 377*eb8f6990SEnji Cooper "Passed test case should have reported success but " + 378*eb8f6990SEnji Cooper format_status(status.get())); 379*eb8f6990SEnji Cooper } 380*eb8f6990SEnji Cooper 381*eb8f6990SEnji Cooper UNREACHABLE; 382*eb8f6990SEnji Cooper } 383*eb8f6990SEnji Cooper 384*eb8f6990SEnji Cooper 385*eb8f6990SEnji Cooper /// Converts an internal result to the interface-agnostic representation. 386*eb8f6990SEnji Cooper /// 387*eb8f6990SEnji Cooper /// \return A generic result instance representing this result. 388*eb8f6990SEnji Cooper model::test_result 389*eb8f6990SEnji Cooper engine::googletest_result::externalize(void) const 390*eb8f6990SEnji Cooper { 391*eb8f6990SEnji Cooper switch (_type) { 392*eb8f6990SEnji Cooper case googletest_result::broken: 393*eb8f6990SEnji Cooper return model::test_result(model::test_result_broken, _reason.get()); 394*eb8f6990SEnji Cooper 395*eb8f6990SEnji Cooper case googletest_result::disabled: 396*eb8f6990SEnji Cooper return model::test_result(model::test_result_skipped, _reason.get()); 397*eb8f6990SEnji Cooper 398*eb8f6990SEnji Cooper case googletest_result::failed: 399*eb8f6990SEnji Cooper return model::test_result(model::test_result_failed, _reason.get()); 400*eb8f6990SEnji Cooper 401*eb8f6990SEnji Cooper case googletest_result::skipped: 402*eb8f6990SEnji Cooper return model::test_result(model::test_result_skipped, _reason.get()); 403*eb8f6990SEnji Cooper 404*eb8f6990SEnji Cooper case googletest_result::successful: 405*eb8f6990SEnji Cooper return model::test_result(model::test_result_passed); 406*eb8f6990SEnji Cooper 407*eb8f6990SEnji Cooper default: 408*eb8f6990SEnji Cooper UNREACHABLE; 409*eb8f6990SEnji Cooper } 410*eb8f6990SEnji Cooper } 411*eb8f6990SEnji Cooper 412*eb8f6990SEnji Cooper 413*eb8f6990SEnji Cooper /// Compares two raw results for equality. 414*eb8f6990SEnji Cooper /// 415*eb8f6990SEnji Cooper /// \param other The result to compare to. 416*eb8f6990SEnji Cooper /// 417*eb8f6990SEnji Cooper /// \return True if the two raw results are equal; false otherwise. 418*eb8f6990SEnji Cooper bool 419*eb8f6990SEnji Cooper engine::googletest_result::operator==(const googletest_result& other) const 420*eb8f6990SEnji Cooper { 421*eb8f6990SEnji Cooper return _type == other._type && 422*eb8f6990SEnji Cooper _reason == other._reason; 423*eb8f6990SEnji Cooper } 424*eb8f6990SEnji Cooper 425*eb8f6990SEnji Cooper 426*eb8f6990SEnji Cooper /// Compares two raw results for inequality. 427*eb8f6990SEnji Cooper /// 428*eb8f6990SEnji Cooper /// \param other The result to compare to. 429*eb8f6990SEnji Cooper /// 430*eb8f6990SEnji Cooper /// \return True if the two raw results are different; false otherwise. 431*eb8f6990SEnji Cooper bool 432*eb8f6990SEnji Cooper engine::googletest_result::operator!=(const googletest_result& other) const 433*eb8f6990SEnji Cooper { 434*eb8f6990SEnji Cooper return !(*this == other); 435*eb8f6990SEnji Cooper } 436*eb8f6990SEnji Cooper 437*eb8f6990SEnji Cooper 438*eb8f6990SEnji Cooper /// Injects the object into a stream. 439*eb8f6990SEnji Cooper /// 440*eb8f6990SEnji Cooper /// \param output The stream into which to inject the object. 441*eb8f6990SEnji Cooper /// \param object The object to format. 442*eb8f6990SEnji Cooper /// 443*eb8f6990SEnji Cooper /// \return The output stream. 444*eb8f6990SEnji Cooper std::ostream& 445*eb8f6990SEnji Cooper engine::operator<<(std::ostream& output, const googletest_result& object) 446*eb8f6990SEnji Cooper { 447*eb8f6990SEnji Cooper std::string result_name; 448*eb8f6990SEnji Cooper switch (object.type()) { 449*eb8f6990SEnji Cooper case googletest_result::broken: result_name = "broken"; break; 450*eb8f6990SEnji Cooper case googletest_result::disabled: result_name = "disabled"; break; 451*eb8f6990SEnji Cooper case googletest_result::failed: result_name = "failed"; break; 452*eb8f6990SEnji Cooper case googletest_result::skipped: result_name = "skipped"; break; 453*eb8f6990SEnji Cooper case googletest_result::successful: result_name = "successful"; break; 454*eb8f6990SEnji Cooper } 455*eb8f6990SEnji Cooper 456*eb8f6990SEnji Cooper const optional< std::string >& reason = object.reason(); 457*eb8f6990SEnji Cooper 458*eb8f6990SEnji Cooper output << F("model::test_result{type=%s, reason=%s}") 459*eb8f6990SEnji Cooper % text::quote(result_name, '\'') 460*eb8f6990SEnji Cooper % (reason ? text::quote(reason.get(), '\'') : "none"); 461*eb8f6990SEnji Cooper 462*eb8f6990SEnji Cooper return output; 463*eb8f6990SEnji Cooper } 464*eb8f6990SEnji Cooper 465*eb8f6990SEnji Cooper 466*eb8f6990SEnji Cooper /// Calculates the user-visible result of a test case. 467*eb8f6990SEnji Cooper /// 468*eb8f6990SEnji Cooper /// This function needs to perform magic to ensure that what the test case 469*eb8f6990SEnji Cooper /// reports as its result is what the user should really see: i.e. it adjusts 470*eb8f6990SEnji Cooper /// the reported status of the test to the exit conditions of its body and 471*eb8f6990SEnji Cooper /// cleanup parts. 472*eb8f6990SEnji Cooper /// 473*eb8f6990SEnji Cooper /// \param body_status The termination status of the process that executed 474*eb8f6990SEnji Cooper /// the body of the test. None if the body timed out. 475*eb8f6990SEnji Cooper /// \param results_file The path to the results file that the test case body is 476*eb8f6990SEnji Cooper /// supposed to have created. 477*eb8f6990SEnji Cooper /// 478*eb8f6990SEnji Cooper /// \return The calculated test case result. 479*eb8f6990SEnji Cooper model::test_result 480*eb8f6990SEnji Cooper engine::calculate_googletest_result( 481*eb8f6990SEnji Cooper const optional< process::status >& body_status, 482*eb8f6990SEnji Cooper const fs::path& results_file) 483*eb8f6990SEnji Cooper { 484*eb8f6990SEnji Cooper using engine::googletest_result; 485*eb8f6990SEnji Cooper 486*eb8f6990SEnji Cooper googletest_result result(googletest_result::broken, "Unknown result"); 487*eb8f6990SEnji Cooper try { 488*eb8f6990SEnji Cooper result = googletest_result::load(results_file); 489*eb8f6990SEnji Cooper /// atf_result.cpp handles this by side-effect by setting a broken 490*eb8f6990SEnji Cooper /// result with "", which results in a std::runtime_error throw from 491*eb8f6990SEnji Cooper /// atf_result::parse(..). googletest_result::parse doesn't do that and 492*eb8f6990SEnji Cooper /// instead returns a broken result with a reason of 493*eb8f6990SEnji Cooper /// `invalid_output_message`, so that case needs to be tested for after 494*eb8f6990SEnji Cooper /// the fact to make sure the reason why `invalid_output_message` was 495*eb8f6990SEnji Cooper /// because of a program that crashed or failed. 496*eb8f6990SEnji Cooper if (result.type() == googletest_result::broken && body_status) { 497*eb8f6990SEnji Cooper const optional< std::string >& reason = result.reason(); 498*eb8f6990SEnji Cooper if (reason && reason.get() == invalid_output_message) { 499*eb8f6990SEnji Cooper result = googletest_result( 500*eb8f6990SEnji Cooper googletest_result::broken, 501*eb8f6990SEnji Cooper F("Error: Premature exit. Test case %s") % 502*eb8f6990SEnji Cooper format_status(body_status.get())); 503*eb8f6990SEnji Cooper } 504*eb8f6990SEnji Cooper } 505*eb8f6990SEnji Cooper } catch (const engine::format_error& error) { 506*eb8f6990SEnji Cooper if (body_status) 507*eb8f6990SEnji Cooper result = googletest_result(googletest_result::broken, 508*eb8f6990SEnji Cooper F("Error: %s. Test case %s") % 509*eb8f6990SEnji Cooper error.what() % 510*eb8f6990SEnji Cooper format_status(body_status.get())); 511*eb8f6990SEnji Cooper else { 512*eb8f6990SEnji Cooper // The test case timed out. apply() handles this case later. 513*eb8f6990SEnji Cooper } 514*eb8f6990SEnji Cooper } catch (const std::runtime_error& error) { 515*eb8f6990SEnji Cooper if (body_status) 516*eb8f6990SEnji Cooper result = googletest_result( 517*eb8f6990SEnji Cooper googletest_result::broken, 518*eb8f6990SEnji Cooper F("Error: Premature exit. Test case %s") % 519*eb8f6990SEnji Cooper format_status(body_status.get())); 520*eb8f6990SEnji Cooper else { 521*eb8f6990SEnji Cooper // The test case timed out. apply() handles this case later. 522*eb8f6990SEnji Cooper } 523*eb8f6990SEnji Cooper } 524*eb8f6990SEnji Cooper 525*eb8f6990SEnji Cooper result = result.apply(body_status); 526*eb8f6990SEnji Cooper 527*eb8f6990SEnji Cooper return result.externalize(); 528*eb8f6990SEnji Cooper } 529