xref: /freebsd/contrib/kyua/engine/googletest_result.cpp (revision eb8f69902f7de8012a31a85db2f88a54584f81af)
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