1*b0d29bc4SBrooks Davis // Copyright 2010 The Kyua Authors.
2*b0d29bc4SBrooks Davis // All rights reserved.
3*b0d29bc4SBrooks Davis //
4*b0d29bc4SBrooks Davis // Redistribution and use in source and binary forms, with or without
5*b0d29bc4SBrooks Davis // modification, are permitted provided that the following conditions are
6*b0d29bc4SBrooks Davis // met:
7*b0d29bc4SBrooks Davis //
8*b0d29bc4SBrooks Davis // * Redistributions of source code must retain the above copyright
9*b0d29bc4SBrooks Davis // notice, this list of conditions and the following disclaimer.
10*b0d29bc4SBrooks Davis // * Redistributions in binary form must reproduce the above copyright
11*b0d29bc4SBrooks Davis // notice, this list of conditions and the following disclaimer in the
12*b0d29bc4SBrooks Davis // documentation and/or other materials provided with the distribution.
13*b0d29bc4SBrooks Davis // * Neither the name of Google Inc. nor the names of its contributors
14*b0d29bc4SBrooks Davis // may be used to endorse or promote products derived from this software
15*b0d29bc4SBrooks Davis // without specific prior written permission.
16*b0d29bc4SBrooks Davis //
17*b0d29bc4SBrooks Davis // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18*b0d29bc4SBrooks Davis // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19*b0d29bc4SBrooks Davis // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20*b0d29bc4SBrooks Davis // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21*b0d29bc4SBrooks Davis // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22*b0d29bc4SBrooks Davis // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23*b0d29bc4SBrooks Davis // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24*b0d29bc4SBrooks Davis // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25*b0d29bc4SBrooks Davis // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26*b0d29bc4SBrooks Davis // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27*b0d29bc4SBrooks Davis // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28*b0d29bc4SBrooks Davis
29*b0d29bc4SBrooks Davis #include "engine/atf_result.hpp"
30*b0d29bc4SBrooks Davis
31*b0d29bc4SBrooks Davis #include <cstdlib>
32*b0d29bc4SBrooks Davis #include <fstream>
33*b0d29bc4SBrooks Davis #include <utility>
34*b0d29bc4SBrooks Davis
35*b0d29bc4SBrooks Davis #include "engine/exceptions.hpp"
36*b0d29bc4SBrooks Davis #include "model/test_result.hpp"
37*b0d29bc4SBrooks Davis #include "utils/fs/path.hpp"
38*b0d29bc4SBrooks Davis #include "utils/format/macros.hpp"
39*b0d29bc4SBrooks Davis #include "utils/optional.ipp"
40*b0d29bc4SBrooks Davis #include "utils/process/status.hpp"
41*b0d29bc4SBrooks Davis #include "utils/sanity.hpp"
42*b0d29bc4SBrooks Davis #include "utils/text/exceptions.hpp"
43*b0d29bc4SBrooks Davis #include "utils/text/operations.ipp"
44*b0d29bc4SBrooks Davis
45*b0d29bc4SBrooks Davis namespace fs = utils::fs;
46*b0d29bc4SBrooks Davis namespace process = utils::process;
47*b0d29bc4SBrooks Davis namespace text = utils::text;
48*b0d29bc4SBrooks Davis
49*b0d29bc4SBrooks Davis using utils::none;
50*b0d29bc4SBrooks Davis using utils::optional;
51*b0d29bc4SBrooks Davis
52*b0d29bc4SBrooks Davis
53*b0d29bc4SBrooks Davis namespace {
54*b0d29bc4SBrooks Davis
55*b0d29bc4SBrooks Davis
56*b0d29bc4SBrooks Davis /// Reads a file and flattens its lines.
57*b0d29bc4SBrooks Davis ///
58*b0d29bc4SBrooks Davis /// The main purpose of this function is to simplify the parsing of a file
59*b0d29bc4SBrooks Davis /// containing the result of a test. Therefore, the return value carries
60*b0d29bc4SBrooks Davis /// several assumptions.
61*b0d29bc4SBrooks Davis ///
62*b0d29bc4SBrooks Davis /// \param input The stream to read from.
63*b0d29bc4SBrooks Davis ///
64*b0d29bc4SBrooks Davis /// \return A pair (line count, contents) detailing how many lines where read
65*b0d29bc4SBrooks Davis /// and their contents. If the file contains a single line with no newline
66*b0d29bc4SBrooks Davis /// character, the line count is 0. If the file includes more than one line,
67*b0d29bc4SBrooks Davis /// the lines are merged together and separated by the magic string
68*b0d29bc4SBrooks Davis /// '<<NEWLINE>>'.
69*b0d29bc4SBrooks Davis static std::pair< size_t, std::string >
read_lines(std::istream & input)70*b0d29bc4SBrooks Davis read_lines(std::istream& input)
71*b0d29bc4SBrooks Davis {
72*b0d29bc4SBrooks Davis std::pair< size_t, std::string > ret = std::make_pair(0, "");
73*b0d29bc4SBrooks Davis
74*b0d29bc4SBrooks Davis do {
75*b0d29bc4SBrooks Davis std::string line;
76*b0d29bc4SBrooks Davis std::getline(input, line);
77*b0d29bc4SBrooks Davis if (input.eof() && !line.empty()) {
78*b0d29bc4SBrooks Davis if (ret.first == 0)
79*b0d29bc4SBrooks Davis ret.second = line;
80*b0d29bc4SBrooks Davis else {
81*b0d29bc4SBrooks Davis ret.second += "<<NEWLINE>>" + line;
82*b0d29bc4SBrooks Davis ret.first++;
83*b0d29bc4SBrooks Davis }
84*b0d29bc4SBrooks Davis } else if (input.good()) {
85*b0d29bc4SBrooks Davis if (ret.first == 0)
86*b0d29bc4SBrooks Davis ret.second = line;
87*b0d29bc4SBrooks Davis else
88*b0d29bc4SBrooks Davis ret.second += "<<NEWLINE>>" + line;
89*b0d29bc4SBrooks Davis ret.first++;
90*b0d29bc4SBrooks Davis }
91*b0d29bc4SBrooks Davis } while (input.good());
92*b0d29bc4SBrooks Davis
93*b0d29bc4SBrooks Davis return ret;
94*b0d29bc4SBrooks Davis }
95*b0d29bc4SBrooks Davis
96*b0d29bc4SBrooks Davis
97*b0d29bc4SBrooks Davis /// Parses a test result that does not accept a reason.
98*b0d29bc4SBrooks Davis ///
99*b0d29bc4SBrooks Davis /// \param status The result status name.
100*b0d29bc4SBrooks Davis /// \param rest The rest of the line after the status name.
101*b0d29bc4SBrooks Davis ///
102*b0d29bc4SBrooks Davis /// \return An object representing the test result.
103*b0d29bc4SBrooks Davis ///
104*b0d29bc4SBrooks Davis /// \throw format_error If the result is invalid (i.e. rest is invalid).
105*b0d29bc4SBrooks Davis ///
106*b0d29bc4SBrooks Davis /// \pre status must be "passed".
107*b0d29bc4SBrooks Davis static engine::atf_result
parse_without_reason(const std::string & status,const std::string & rest)108*b0d29bc4SBrooks Davis parse_without_reason(const std::string& status, const std::string& rest)
109*b0d29bc4SBrooks Davis {
110*b0d29bc4SBrooks Davis if (!rest.empty())
111*b0d29bc4SBrooks Davis throw engine::format_error(F("%s cannot have a reason") % status);
112*b0d29bc4SBrooks Davis PRE(status == "passed");
113*b0d29bc4SBrooks Davis return engine::atf_result(engine::atf_result::passed);
114*b0d29bc4SBrooks Davis }
115*b0d29bc4SBrooks Davis
116*b0d29bc4SBrooks Davis
117*b0d29bc4SBrooks Davis /// Parses a test result that needs a reason.
118*b0d29bc4SBrooks Davis ///
119*b0d29bc4SBrooks Davis /// \param status The result status name.
120*b0d29bc4SBrooks Davis /// \param rest The rest of the line after the status name.
121*b0d29bc4SBrooks Davis ///
122*b0d29bc4SBrooks Davis /// \return An object representing the test result.
123*b0d29bc4SBrooks Davis ///
124*b0d29bc4SBrooks Davis /// \throw format_error If the result is invalid (i.e. rest is invalid).
125*b0d29bc4SBrooks Davis ///
126*b0d29bc4SBrooks Davis /// \pre status must be one of "broken", "expected_death", "expected_failure",
127*b0d29bc4SBrooks Davis /// "expected_timeout", "failed" or "skipped".
128*b0d29bc4SBrooks Davis static engine::atf_result
parse_with_reason(const std::string & status,const std::string & rest)129*b0d29bc4SBrooks Davis parse_with_reason(const std::string& status, const std::string& rest)
130*b0d29bc4SBrooks Davis {
131*b0d29bc4SBrooks Davis using engine::atf_result;
132*b0d29bc4SBrooks Davis
133*b0d29bc4SBrooks Davis if (rest.length() < 3 || rest.substr(0, 2) != ": ")
134*b0d29bc4SBrooks Davis throw engine::format_error(F("%s must be followed by ': <reason>'") %
135*b0d29bc4SBrooks Davis status);
136*b0d29bc4SBrooks Davis const std::string reason = rest.substr(2);
137*b0d29bc4SBrooks Davis INV(!reason.empty());
138*b0d29bc4SBrooks Davis
139*b0d29bc4SBrooks Davis if (status == "broken")
140*b0d29bc4SBrooks Davis return atf_result(atf_result::broken, reason);
141*b0d29bc4SBrooks Davis else if (status == "expected_death")
142*b0d29bc4SBrooks Davis return atf_result(atf_result::expected_death, reason);
143*b0d29bc4SBrooks Davis else if (status == "expected_failure")
144*b0d29bc4SBrooks Davis return atf_result(atf_result::expected_failure, reason);
145*b0d29bc4SBrooks Davis else if (status == "expected_timeout")
146*b0d29bc4SBrooks Davis return atf_result(atf_result::expected_timeout, reason);
147*b0d29bc4SBrooks Davis else if (status == "failed")
148*b0d29bc4SBrooks Davis return atf_result(atf_result::failed, reason);
149*b0d29bc4SBrooks Davis else if (status == "skipped")
150*b0d29bc4SBrooks Davis return atf_result(atf_result::skipped, reason);
151*b0d29bc4SBrooks Davis else
152*b0d29bc4SBrooks Davis PRE_MSG(false, "Unexpected status");
153*b0d29bc4SBrooks Davis }
154*b0d29bc4SBrooks Davis
155*b0d29bc4SBrooks Davis
156*b0d29bc4SBrooks Davis /// Converts a string to an integer.
157*b0d29bc4SBrooks Davis ///
158*b0d29bc4SBrooks Davis /// \param str The string containing the integer to convert.
159*b0d29bc4SBrooks Davis ///
160*b0d29bc4SBrooks Davis /// \return The converted integer; none if the parsing fails.
161*b0d29bc4SBrooks Davis static optional< int >
parse_int(const std::string & str)162*b0d29bc4SBrooks Davis parse_int(const std::string& str)
163*b0d29bc4SBrooks Davis {
164*b0d29bc4SBrooks Davis try {
165*b0d29bc4SBrooks Davis return utils::make_optional(text::to_type< int >(str));
166*b0d29bc4SBrooks Davis } catch (const text::value_error& e) {
167*b0d29bc4SBrooks Davis return none;
168*b0d29bc4SBrooks Davis }
169*b0d29bc4SBrooks Davis }
170*b0d29bc4SBrooks Davis
171*b0d29bc4SBrooks Davis
172*b0d29bc4SBrooks Davis /// Parses a test result that needs a reason and accepts an optional integer.
173*b0d29bc4SBrooks Davis ///
174*b0d29bc4SBrooks Davis /// \param status The result status name.
175*b0d29bc4SBrooks Davis /// \param rest The rest of the line after the status name.
176*b0d29bc4SBrooks Davis ///
177*b0d29bc4SBrooks Davis /// \return The parsed test result if the data is valid, or a broken result if
178*b0d29bc4SBrooks Davis /// the parsing failed.
179*b0d29bc4SBrooks Davis ///
180*b0d29bc4SBrooks Davis /// \pre status must be one of "expected_exit" or "expected_signal".
181*b0d29bc4SBrooks Davis static engine::atf_result
parse_with_reason_and_arg(const std::string & status,const std::string & rest)182*b0d29bc4SBrooks Davis parse_with_reason_and_arg(const std::string& status, const std::string& rest)
183*b0d29bc4SBrooks Davis {
184*b0d29bc4SBrooks Davis using engine::atf_result;
185*b0d29bc4SBrooks Davis
186*b0d29bc4SBrooks Davis std::string::size_type delim = rest.find_first_of(":(");
187*b0d29bc4SBrooks Davis if (delim == std::string::npos)
188*b0d29bc4SBrooks Davis throw engine::format_error(F("Invalid format for '%s' test case "
189*b0d29bc4SBrooks Davis "result; must be followed by '[(num)]: "
190*b0d29bc4SBrooks Davis "<reason>' but found '%s'") %
191*b0d29bc4SBrooks Davis status % rest);
192*b0d29bc4SBrooks Davis
193*b0d29bc4SBrooks Davis optional< int > arg;
194*b0d29bc4SBrooks Davis if (rest[delim] == '(') {
195*b0d29bc4SBrooks Davis const std::string::size_type delim2 = rest.find("):", delim);
196*b0d29bc4SBrooks Davis if (delim == std::string::npos)
197*b0d29bc4SBrooks Davis throw engine::format_error(F("Mismatched '(' in %s") % rest);
198*b0d29bc4SBrooks Davis
199*b0d29bc4SBrooks Davis const std::string argstr = rest.substr(delim + 1, delim2 - delim - 1);
200*b0d29bc4SBrooks Davis arg = parse_int(argstr);
201*b0d29bc4SBrooks Davis if (!arg)
202*b0d29bc4SBrooks Davis throw engine::format_error(F("Invalid integer argument '%s' to "
203*b0d29bc4SBrooks Davis "'%s' test case result") %
204*b0d29bc4SBrooks Davis argstr % status);
205*b0d29bc4SBrooks Davis delim = delim2 + 1;
206*b0d29bc4SBrooks Davis }
207*b0d29bc4SBrooks Davis
208*b0d29bc4SBrooks Davis const std::string reason = rest.substr(delim + 2);
209*b0d29bc4SBrooks Davis
210*b0d29bc4SBrooks Davis if (status == "expected_exit")
211*b0d29bc4SBrooks Davis return atf_result(atf_result::expected_exit, arg, reason);
212*b0d29bc4SBrooks Davis else if (status == "expected_signal")
213*b0d29bc4SBrooks Davis return atf_result(atf_result::expected_signal, arg, reason);
214*b0d29bc4SBrooks Davis else
215*b0d29bc4SBrooks Davis PRE_MSG(false, "Unexpected status");
216*b0d29bc4SBrooks Davis }
217*b0d29bc4SBrooks Davis
218*b0d29bc4SBrooks Davis
219*b0d29bc4SBrooks Davis /// Formats the termination status of a process to be used with validate_result.
220*b0d29bc4SBrooks Davis ///
221*b0d29bc4SBrooks Davis /// \param status The status to format.
222*b0d29bc4SBrooks Davis ///
223*b0d29bc4SBrooks Davis /// \return A string describing the status.
224*b0d29bc4SBrooks Davis static std::string
format_status(const process::status & status)225*b0d29bc4SBrooks Davis format_status(const process::status& status)
226*b0d29bc4SBrooks Davis {
227*b0d29bc4SBrooks Davis if (status.exited())
228*b0d29bc4SBrooks Davis return F("exited with code %s") % status.exitstatus();
229*b0d29bc4SBrooks Davis else if (status.signaled())
230*b0d29bc4SBrooks Davis return F("received signal %s%s") % status.termsig() %
231*b0d29bc4SBrooks Davis (status.coredump() ? " (core dumped)" : "");
232*b0d29bc4SBrooks Davis else
233*b0d29bc4SBrooks Davis return F("terminated in an unknown manner");
234*b0d29bc4SBrooks Davis }
235*b0d29bc4SBrooks Davis
236*b0d29bc4SBrooks Davis
237*b0d29bc4SBrooks Davis } // anonymous namespace
238*b0d29bc4SBrooks Davis
239*b0d29bc4SBrooks Davis
240*b0d29bc4SBrooks Davis /// Constructs a raw result with a type.
241*b0d29bc4SBrooks Davis ///
242*b0d29bc4SBrooks Davis /// The reason and the argument are left uninitialized.
243*b0d29bc4SBrooks Davis ///
244*b0d29bc4SBrooks Davis /// \param type_ The type of the result.
atf_result(const types type_)245*b0d29bc4SBrooks Davis engine::atf_result::atf_result(const types type_) :
246*b0d29bc4SBrooks Davis _type(type_)
247*b0d29bc4SBrooks Davis {
248*b0d29bc4SBrooks Davis }
249*b0d29bc4SBrooks Davis
250*b0d29bc4SBrooks Davis
251*b0d29bc4SBrooks Davis /// Constructs a raw result with a type and a reason.
252*b0d29bc4SBrooks Davis ///
253*b0d29bc4SBrooks Davis /// The argument is left uninitialized.
254*b0d29bc4SBrooks Davis ///
255*b0d29bc4SBrooks Davis /// \param type_ The type of the result.
256*b0d29bc4SBrooks Davis /// \param reason_ The reason for the result.
atf_result(const types type_,const std::string & reason_)257*b0d29bc4SBrooks Davis engine::atf_result::atf_result(const types type_, const std::string& reason_) :
258*b0d29bc4SBrooks Davis _type(type_), _reason(reason_)
259*b0d29bc4SBrooks Davis {
260*b0d29bc4SBrooks Davis }
261*b0d29bc4SBrooks Davis
262*b0d29bc4SBrooks Davis
263*b0d29bc4SBrooks Davis /// Constructs a raw result with a type, an optional argument and a reason.
264*b0d29bc4SBrooks Davis ///
265*b0d29bc4SBrooks Davis /// \param type_ The type of the result.
266*b0d29bc4SBrooks Davis /// \param argument_ The optional argument for the result.
267*b0d29bc4SBrooks Davis /// \param reason_ The reason for the result.
atf_result(const types type_,const utils::optional<int> & argument_,const std::string & reason_)268*b0d29bc4SBrooks Davis engine::atf_result::atf_result(const types type_,
269*b0d29bc4SBrooks Davis const utils::optional< int >& argument_,
270*b0d29bc4SBrooks Davis const std::string& reason_) :
271*b0d29bc4SBrooks Davis _type(type_), _argument(argument_), _reason(reason_)
272*b0d29bc4SBrooks Davis {
273*b0d29bc4SBrooks Davis }
274*b0d29bc4SBrooks Davis
275*b0d29bc4SBrooks Davis
276*b0d29bc4SBrooks Davis /// Parses an input stream to extract a test result.
277*b0d29bc4SBrooks Davis ///
278*b0d29bc4SBrooks Davis /// If the parsing fails for any reason, the test result is 'broken' and it
279*b0d29bc4SBrooks Davis /// contains the reason for the parsing failure. Test cases that report results
280*b0d29bc4SBrooks Davis /// in an inconsistent state cannot be trusted (e.g. the test program code may
281*b0d29bc4SBrooks Davis /// have a bug), and thus why they are reported as broken instead of just failed
282*b0d29bc4SBrooks Davis /// (which is a legitimate result for a test case).
283*b0d29bc4SBrooks Davis ///
284*b0d29bc4SBrooks Davis /// \param input The stream to read from.
285*b0d29bc4SBrooks Davis ///
286*b0d29bc4SBrooks Davis /// \return A generic representation of the result of the test case.
287*b0d29bc4SBrooks Davis ///
288*b0d29bc4SBrooks Davis /// \throw format_error If the input is invalid.
289*b0d29bc4SBrooks Davis engine::atf_result
parse(std::istream & input)290*b0d29bc4SBrooks Davis engine::atf_result::parse(std::istream& input)
291*b0d29bc4SBrooks Davis {
292*b0d29bc4SBrooks Davis const std::pair< size_t, std::string > data = read_lines(input);
293*b0d29bc4SBrooks Davis if (data.first == 0)
294*b0d29bc4SBrooks Davis throw format_error("Empty test result or no new line");
295*b0d29bc4SBrooks Davis else if (data.first > 1)
296*b0d29bc4SBrooks Davis throw format_error("Test result contains multiple lines: " +
297*b0d29bc4SBrooks Davis data.second);
298*b0d29bc4SBrooks Davis else {
299*b0d29bc4SBrooks Davis const std::string::size_type delim = data.second.find_first_not_of(
300*b0d29bc4SBrooks Davis "abcdefghijklmnopqrstuvwxyz_");
301*b0d29bc4SBrooks Davis const std::string status = data.second.substr(0, delim);
302*b0d29bc4SBrooks Davis const std::string rest = data.second.substr(status.length());
303*b0d29bc4SBrooks Davis
304*b0d29bc4SBrooks Davis if (status == "broken")
305*b0d29bc4SBrooks Davis return parse_with_reason(status, rest);
306*b0d29bc4SBrooks Davis else if (status == "expected_death")
307*b0d29bc4SBrooks Davis return parse_with_reason(status, rest);
308*b0d29bc4SBrooks Davis else if (status == "expected_exit")
309*b0d29bc4SBrooks Davis return parse_with_reason_and_arg(status, rest);
310*b0d29bc4SBrooks Davis else if (status == "expected_failure")
311*b0d29bc4SBrooks Davis return parse_with_reason(status, rest);
312*b0d29bc4SBrooks Davis else if (status == "expected_signal")
313*b0d29bc4SBrooks Davis return parse_with_reason_and_arg(status, rest);
314*b0d29bc4SBrooks Davis else if (status == "expected_timeout")
315*b0d29bc4SBrooks Davis return parse_with_reason(status, rest);
316*b0d29bc4SBrooks Davis else if (status == "failed")
317*b0d29bc4SBrooks Davis return parse_with_reason(status, rest);
318*b0d29bc4SBrooks Davis else if (status == "passed")
319*b0d29bc4SBrooks Davis return parse_without_reason(status, rest);
320*b0d29bc4SBrooks Davis else if (status == "skipped")
321*b0d29bc4SBrooks Davis return parse_with_reason(status, rest);
322*b0d29bc4SBrooks Davis else
323*b0d29bc4SBrooks Davis throw format_error(F("Unknown test result '%s'") % status);
324*b0d29bc4SBrooks Davis }
325*b0d29bc4SBrooks Davis }
326*b0d29bc4SBrooks Davis
327*b0d29bc4SBrooks Davis
328*b0d29bc4SBrooks Davis /// Loads a test case result from a file.
329*b0d29bc4SBrooks Davis ///
330*b0d29bc4SBrooks Davis /// \param file The file to parse.
331*b0d29bc4SBrooks Davis ///
332*b0d29bc4SBrooks Davis /// \return The parsed test case result if all goes well.
333*b0d29bc4SBrooks Davis ///
334*b0d29bc4SBrooks Davis /// \throw std::runtime_error If the file does not exist.
335*b0d29bc4SBrooks Davis /// \throw engine::format_error If the contents of the file are bogus.
336*b0d29bc4SBrooks Davis engine::atf_result
load(const fs::path & file)337*b0d29bc4SBrooks Davis engine::atf_result::load(const fs::path& file)
338*b0d29bc4SBrooks Davis {
339*b0d29bc4SBrooks Davis std::ifstream input(file.c_str());
340*b0d29bc4SBrooks Davis if (!input)
341*b0d29bc4SBrooks Davis throw std::runtime_error("Cannot open results file");
342*b0d29bc4SBrooks Davis else
343*b0d29bc4SBrooks Davis return parse(input);
344*b0d29bc4SBrooks Davis }
345*b0d29bc4SBrooks Davis
346*b0d29bc4SBrooks Davis
347*b0d29bc4SBrooks Davis /// Gets the type of the result.
348*b0d29bc4SBrooks Davis ///
349*b0d29bc4SBrooks Davis /// \return A result type.
350*b0d29bc4SBrooks Davis engine::atf_result::types
type(void) const351*b0d29bc4SBrooks Davis engine::atf_result::type(void) const
352*b0d29bc4SBrooks Davis {
353*b0d29bc4SBrooks Davis return _type;
354*b0d29bc4SBrooks Davis }
355*b0d29bc4SBrooks Davis
356*b0d29bc4SBrooks Davis
357*b0d29bc4SBrooks Davis /// Gets the optional argument of the result.
358*b0d29bc4SBrooks Davis ///
359*b0d29bc4SBrooks Davis /// \return The argument of the result if present; none otherwise.
360*b0d29bc4SBrooks Davis const optional< int >&
argument(void) const361*b0d29bc4SBrooks Davis engine::atf_result::argument(void) const
362*b0d29bc4SBrooks Davis {
363*b0d29bc4SBrooks Davis return _argument;
364*b0d29bc4SBrooks Davis }
365*b0d29bc4SBrooks Davis
366*b0d29bc4SBrooks Davis
367*b0d29bc4SBrooks Davis /// Gets the optional reason of the result.
368*b0d29bc4SBrooks Davis ///
369*b0d29bc4SBrooks Davis /// \return The reason of the result if present; none otherwise.
370*b0d29bc4SBrooks Davis const optional< std::string >&
reason(void) const371*b0d29bc4SBrooks Davis engine::atf_result::reason(void) const
372*b0d29bc4SBrooks Davis {
373*b0d29bc4SBrooks Davis return _reason;
374*b0d29bc4SBrooks Davis }
375*b0d29bc4SBrooks Davis
376*b0d29bc4SBrooks Davis
377*b0d29bc4SBrooks Davis /// Checks whether the result should be reported as good or not.
378*b0d29bc4SBrooks Davis ///
379*b0d29bc4SBrooks Davis /// \return True if the result can be considered "good", false otherwise.
380*b0d29bc4SBrooks Davis bool
good(void) const381*b0d29bc4SBrooks Davis engine::atf_result::good(void) const
382*b0d29bc4SBrooks Davis {
383*b0d29bc4SBrooks Davis switch (_type) {
384*b0d29bc4SBrooks Davis case atf_result::expected_death:
385*b0d29bc4SBrooks Davis case atf_result::expected_exit:
386*b0d29bc4SBrooks Davis case atf_result::expected_failure:
387*b0d29bc4SBrooks Davis case atf_result::expected_signal:
388*b0d29bc4SBrooks Davis case atf_result::expected_timeout:
389*b0d29bc4SBrooks Davis case atf_result::passed:
390*b0d29bc4SBrooks Davis case atf_result::skipped:
391*b0d29bc4SBrooks Davis return true;
392*b0d29bc4SBrooks Davis
393*b0d29bc4SBrooks Davis case atf_result::broken:
394*b0d29bc4SBrooks Davis case atf_result::failed:
395*b0d29bc4SBrooks Davis return false;
396*b0d29bc4SBrooks Davis
397*b0d29bc4SBrooks Davis default:
398*b0d29bc4SBrooks Davis UNREACHABLE;
399*b0d29bc4SBrooks Davis }
400*b0d29bc4SBrooks Davis }
401*b0d29bc4SBrooks Davis
402*b0d29bc4SBrooks Davis
403*b0d29bc4SBrooks Davis /// Reinterprets a raw result based on the termination status of the test case.
404*b0d29bc4SBrooks Davis ///
405*b0d29bc4SBrooks Davis /// This reinterpretation ensures that the termination conditions of the program
406*b0d29bc4SBrooks Davis /// match what is expected of the paticular result reported by the test program.
407*b0d29bc4SBrooks Davis /// If such conditions do not match, the test program is considered bogus and is
408*b0d29bc4SBrooks Davis /// thus reported as broken.
409*b0d29bc4SBrooks Davis ///
410*b0d29bc4SBrooks Davis /// This is just a helper function for calculate_result(); the real result of
411*b0d29bc4SBrooks Davis /// the test case cannot be inferred from apply() only.
412*b0d29bc4SBrooks Davis ///
413*b0d29bc4SBrooks Davis /// \param status The exit status of the test program, or none if the test
414*b0d29bc4SBrooks Davis /// program timed out.
415*b0d29bc4SBrooks Davis ///
416*b0d29bc4SBrooks Davis /// \result The adjusted result. The original result is transformed into broken
417*b0d29bc4SBrooks Davis /// if the exit status of the program does not match our expectations.
418*b0d29bc4SBrooks Davis engine::atf_result
apply(const optional<process::status> & status) const419*b0d29bc4SBrooks Davis engine::atf_result::apply(const optional< process::status >& status)
420*b0d29bc4SBrooks Davis const
421*b0d29bc4SBrooks Davis {
422*b0d29bc4SBrooks Davis if (!status) {
423*b0d29bc4SBrooks Davis if (_type != atf_result::expected_timeout)
424*b0d29bc4SBrooks Davis return atf_result(atf_result::broken, "Test case body timed out");
425*b0d29bc4SBrooks Davis else
426*b0d29bc4SBrooks Davis return *this;
427*b0d29bc4SBrooks Davis }
428*b0d29bc4SBrooks Davis
429*b0d29bc4SBrooks Davis INV(status);
430*b0d29bc4SBrooks Davis switch (_type) {
431*b0d29bc4SBrooks Davis case atf_result::broken:
432*b0d29bc4SBrooks Davis return *this;
433*b0d29bc4SBrooks Davis
434*b0d29bc4SBrooks Davis case atf_result::expected_death:
435*b0d29bc4SBrooks Davis return *this;
436*b0d29bc4SBrooks Davis
437*b0d29bc4SBrooks Davis case atf_result::expected_exit:
438*b0d29bc4SBrooks Davis if (status.get().exited()) {
439*b0d29bc4SBrooks Davis if (_argument) {
440*b0d29bc4SBrooks Davis if (_argument.get() == status.get().exitstatus())
441*b0d29bc4SBrooks Davis return *this;
442*b0d29bc4SBrooks Davis else
443*b0d29bc4SBrooks Davis return atf_result(
444*b0d29bc4SBrooks Davis atf_result::failed,
445*b0d29bc4SBrooks Davis F("Test case expected to exit with code %s but got "
446*b0d29bc4SBrooks Davis "code %s") %
447*b0d29bc4SBrooks Davis _argument.get() % status.get().exitstatus());
448*b0d29bc4SBrooks Davis } else
449*b0d29bc4SBrooks Davis return *this;
450*b0d29bc4SBrooks Davis } else
451*b0d29bc4SBrooks Davis return atf_result(atf_result::broken, "Expected clean exit but " +
452*b0d29bc4SBrooks Davis format_status(status.get()));
453*b0d29bc4SBrooks Davis
454*b0d29bc4SBrooks Davis case atf_result::expected_failure:
455*b0d29bc4SBrooks Davis if (status.get().exited() && status.get().exitstatus() == EXIT_SUCCESS)
456*b0d29bc4SBrooks Davis return *this;
457*b0d29bc4SBrooks Davis else
458*b0d29bc4SBrooks Davis return atf_result(atf_result::broken, "Expected failure should "
459*b0d29bc4SBrooks Davis "have reported success but " +
460*b0d29bc4SBrooks Davis format_status(status.get()));
461*b0d29bc4SBrooks Davis
462*b0d29bc4SBrooks Davis case atf_result::expected_signal:
463*b0d29bc4SBrooks Davis if (status.get().signaled()) {
464*b0d29bc4SBrooks Davis if (_argument) {
465*b0d29bc4SBrooks Davis if (_argument.get() == status.get().termsig())
466*b0d29bc4SBrooks Davis return *this;
467*b0d29bc4SBrooks Davis else
468*b0d29bc4SBrooks Davis return atf_result(
469*b0d29bc4SBrooks Davis atf_result::failed,
470*b0d29bc4SBrooks Davis F("Test case expected to receive signal %s but "
471*b0d29bc4SBrooks Davis "got %s") %
472*b0d29bc4SBrooks Davis _argument.get() % status.get().termsig());
473*b0d29bc4SBrooks Davis } else
474*b0d29bc4SBrooks Davis return *this;
475*b0d29bc4SBrooks Davis } else
476*b0d29bc4SBrooks Davis return atf_result(atf_result::broken, "Expected signal but " +
477*b0d29bc4SBrooks Davis format_status(status.get()));
478*b0d29bc4SBrooks Davis
479*b0d29bc4SBrooks Davis case atf_result::expected_timeout:
480*b0d29bc4SBrooks Davis return atf_result(atf_result::broken, "Expected timeout but " +
481*b0d29bc4SBrooks Davis format_status(status.get()));
482*b0d29bc4SBrooks Davis
483*b0d29bc4SBrooks Davis case atf_result::failed:
484*b0d29bc4SBrooks Davis if (status.get().exited() && status.get().exitstatus() == EXIT_FAILURE)
485*b0d29bc4SBrooks Davis return *this;
486*b0d29bc4SBrooks Davis else
487*b0d29bc4SBrooks Davis return atf_result(atf_result::broken, "Failed test case should "
488*b0d29bc4SBrooks Davis "have reported failure but " +
489*b0d29bc4SBrooks Davis format_status(status.get()));
490*b0d29bc4SBrooks Davis
491*b0d29bc4SBrooks Davis case atf_result::passed:
492*b0d29bc4SBrooks Davis if (status.get().exited() && status.get().exitstatus() == EXIT_SUCCESS)
493*b0d29bc4SBrooks Davis return *this;
494*b0d29bc4SBrooks Davis else
495*b0d29bc4SBrooks Davis return atf_result(atf_result::broken, "Passed test case should "
496*b0d29bc4SBrooks Davis "have reported success but " +
497*b0d29bc4SBrooks Davis format_status(status.get()));
498*b0d29bc4SBrooks Davis
499*b0d29bc4SBrooks Davis case atf_result::skipped:
500*b0d29bc4SBrooks Davis if (status.get().exited() && status.get().exitstatus() == EXIT_SUCCESS)
501*b0d29bc4SBrooks Davis return *this;
502*b0d29bc4SBrooks Davis else
503*b0d29bc4SBrooks Davis return atf_result(atf_result::broken, "Skipped test case should "
504*b0d29bc4SBrooks Davis "have reported success but " +
505*b0d29bc4SBrooks Davis format_status(status.get()));
506*b0d29bc4SBrooks Davis }
507*b0d29bc4SBrooks Davis
508*b0d29bc4SBrooks Davis UNREACHABLE;
509*b0d29bc4SBrooks Davis }
510*b0d29bc4SBrooks Davis
511*b0d29bc4SBrooks Davis
512*b0d29bc4SBrooks Davis /// Converts an internal result to the interface-agnostic representation.
513*b0d29bc4SBrooks Davis ///
514*b0d29bc4SBrooks Davis /// \return A generic result instance representing this result.
515*b0d29bc4SBrooks Davis model::test_result
externalize(void) const516*b0d29bc4SBrooks Davis engine::atf_result::externalize(void) const
517*b0d29bc4SBrooks Davis {
518*b0d29bc4SBrooks Davis switch (_type) {
519*b0d29bc4SBrooks Davis case atf_result::broken:
520*b0d29bc4SBrooks Davis return model::test_result(model::test_result_broken, _reason.get());
521*b0d29bc4SBrooks Davis
522*b0d29bc4SBrooks Davis case atf_result::expected_death:
523*b0d29bc4SBrooks Davis case atf_result::expected_exit:
524*b0d29bc4SBrooks Davis case atf_result::expected_failure:
525*b0d29bc4SBrooks Davis case atf_result::expected_signal:
526*b0d29bc4SBrooks Davis case atf_result::expected_timeout:
527*b0d29bc4SBrooks Davis return model::test_result(model::test_result_expected_failure,
528*b0d29bc4SBrooks Davis _reason.get());
529*b0d29bc4SBrooks Davis
530*b0d29bc4SBrooks Davis case atf_result::failed:
531*b0d29bc4SBrooks Davis return model::test_result(model::test_result_failed, _reason.get());
532*b0d29bc4SBrooks Davis
533*b0d29bc4SBrooks Davis case atf_result::passed:
534*b0d29bc4SBrooks Davis return model::test_result(model::test_result_passed);
535*b0d29bc4SBrooks Davis
536*b0d29bc4SBrooks Davis case atf_result::skipped:
537*b0d29bc4SBrooks Davis return model::test_result(model::test_result_skipped, _reason.get());
538*b0d29bc4SBrooks Davis
539*b0d29bc4SBrooks Davis default:
540*b0d29bc4SBrooks Davis UNREACHABLE;
541*b0d29bc4SBrooks Davis }
542*b0d29bc4SBrooks Davis }
543*b0d29bc4SBrooks Davis
544*b0d29bc4SBrooks Davis
545*b0d29bc4SBrooks Davis /// Compares two raw results for equality.
546*b0d29bc4SBrooks Davis ///
547*b0d29bc4SBrooks Davis /// \param other The result to compare to.
548*b0d29bc4SBrooks Davis ///
549*b0d29bc4SBrooks Davis /// \return True if the two raw results are equal; false otherwise.
550*b0d29bc4SBrooks Davis bool
operator ==(const atf_result & other) const551*b0d29bc4SBrooks Davis engine::atf_result::operator==(const atf_result& other) const
552*b0d29bc4SBrooks Davis {
553*b0d29bc4SBrooks Davis return _type == other._type && _argument == other._argument &&
554*b0d29bc4SBrooks Davis _reason == other._reason;
555*b0d29bc4SBrooks Davis }
556*b0d29bc4SBrooks Davis
557*b0d29bc4SBrooks Davis
558*b0d29bc4SBrooks Davis /// Compares two raw results for inequality.
559*b0d29bc4SBrooks Davis ///
560*b0d29bc4SBrooks Davis /// \param other The result to compare to.
561*b0d29bc4SBrooks Davis ///
562*b0d29bc4SBrooks Davis /// \return True if the two raw results are different; false otherwise.
563*b0d29bc4SBrooks Davis bool
operator !=(const atf_result & other) const564*b0d29bc4SBrooks Davis engine::atf_result::operator!=(const atf_result& other) const
565*b0d29bc4SBrooks Davis {
566*b0d29bc4SBrooks Davis return !(*this == other);
567*b0d29bc4SBrooks Davis }
568*b0d29bc4SBrooks Davis
569*b0d29bc4SBrooks Davis
570*b0d29bc4SBrooks Davis /// Injects the object into a stream.
571*b0d29bc4SBrooks Davis ///
572*b0d29bc4SBrooks Davis /// \param output The stream into which to inject the object.
573*b0d29bc4SBrooks Davis /// \param object The object to format.
574*b0d29bc4SBrooks Davis ///
575*b0d29bc4SBrooks Davis /// \return The output stream.
576*b0d29bc4SBrooks Davis std::ostream&
operator <<(std::ostream & output,const atf_result & object)577*b0d29bc4SBrooks Davis engine::operator<<(std::ostream& output, const atf_result& object)
578*b0d29bc4SBrooks Davis {
579*b0d29bc4SBrooks Davis std::string result_name;
580*b0d29bc4SBrooks Davis switch (object.type()) {
581*b0d29bc4SBrooks Davis case atf_result::broken: result_name = "broken"; break;
582*b0d29bc4SBrooks Davis case atf_result::expected_death: result_name = "expected_death"; break;
583*b0d29bc4SBrooks Davis case atf_result::expected_exit: result_name = "expected_exit"; break;
584*b0d29bc4SBrooks Davis case atf_result::expected_failure: result_name = "expected_failure"; break;
585*b0d29bc4SBrooks Davis case atf_result::expected_signal: result_name = "expected_signal"; break;
586*b0d29bc4SBrooks Davis case atf_result::expected_timeout: result_name = "expected_timeout"; break;
587*b0d29bc4SBrooks Davis case atf_result::failed: result_name = "failed"; break;
588*b0d29bc4SBrooks Davis case atf_result::passed: result_name = "passed"; break;
589*b0d29bc4SBrooks Davis case atf_result::skipped: result_name = "skipped"; break;
590*b0d29bc4SBrooks Davis }
591*b0d29bc4SBrooks Davis
592*b0d29bc4SBrooks Davis const optional< int >& argument = object.argument();
593*b0d29bc4SBrooks Davis
594*b0d29bc4SBrooks Davis const optional< std::string >& reason = object.reason();
595*b0d29bc4SBrooks Davis
596*b0d29bc4SBrooks Davis output << F("model::test_result{type=%s, argument=%s, reason=%s}")
597*b0d29bc4SBrooks Davis % text::quote(result_name, '\'')
598*b0d29bc4SBrooks Davis % (argument ? (F("%s") % argument.get()).str() : "none")
599*b0d29bc4SBrooks Davis % (reason ? text::quote(reason.get(), '\'') : "none");
600*b0d29bc4SBrooks Davis
601*b0d29bc4SBrooks Davis return output;
602*b0d29bc4SBrooks Davis }
603*b0d29bc4SBrooks Davis
604*b0d29bc4SBrooks Davis
605*b0d29bc4SBrooks Davis /// Calculates the user-visible result of a test case.
606*b0d29bc4SBrooks Davis ///
607*b0d29bc4SBrooks Davis /// This function needs to perform magic to ensure that what the test case
608*b0d29bc4SBrooks Davis /// reports as its result is what the user should really see: i.e. it adjusts
609*b0d29bc4SBrooks Davis /// the reported status of the test to the exit conditions of its body and
610*b0d29bc4SBrooks Davis /// cleanup parts.
611*b0d29bc4SBrooks Davis ///
612*b0d29bc4SBrooks Davis /// \param body_status The termination status of the process that executed
613*b0d29bc4SBrooks Davis /// the body of the test. None if the body timed out.
614*b0d29bc4SBrooks Davis /// \param results_file The path to the results file that the test case body is
615*b0d29bc4SBrooks Davis /// supposed to have created.
616*b0d29bc4SBrooks Davis ///
617*b0d29bc4SBrooks Davis /// \return The calculated test case result.
618*b0d29bc4SBrooks Davis model::test_result
calculate_atf_result(const optional<process::status> & body_status,const fs::path & results_file)619*b0d29bc4SBrooks Davis engine::calculate_atf_result(const optional< process::status >& body_status,
620*b0d29bc4SBrooks Davis const fs::path& results_file)
621*b0d29bc4SBrooks Davis {
622*b0d29bc4SBrooks Davis using engine::atf_result;
623*b0d29bc4SBrooks Davis
624*b0d29bc4SBrooks Davis atf_result result(atf_result::broken, "Unknown result");
625*b0d29bc4SBrooks Davis try {
626*b0d29bc4SBrooks Davis result = atf_result::load(results_file);
627*b0d29bc4SBrooks Davis } catch (const engine::format_error& error) {
628*b0d29bc4SBrooks Davis result = atf_result(atf_result::broken, error.what());
629*b0d29bc4SBrooks Davis } catch (const std::runtime_error& error) {
630*b0d29bc4SBrooks Davis if (body_status)
631*b0d29bc4SBrooks Davis result = atf_result(
632*b0d29bc4SBrooks Davis atf_result::broken, F("Premature exit; test case %s") %
633*b0d29bc4SBrooks Davis format_status(body_status.get()));
634*b0d29bc4SBrooks Davis else {
635*b0d29bc4SBrooks Davis // The test case timed out. apply() handles this case later.
636*b0d29bc4SBrooks Davis }
637*b0d29bc4SBrooks Davis }
638*b0d29bc4SBrooks Davis
639*b0d29bc4SBrooks Davis result = result.apply(body_status);
640*b0d29bc4SBrooks Davis
641*b0d29bc4SBrooks Davis return result.externalize();
642*b0d29bc4SBrooks Davis }
643