1 // Copyright 2012 Google Inc.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
8 // * Redistributions of source code must retain the above copyright
9 //   notice, this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above copyright
11 //   notice, this list of conditions and the following disclaimer in the
12 //   documentation and/or other materials provided with the distribution.
13 // * Neither the name of Google Inc. nor the names of its contributors
14 //   may be used to endorse or promote products derived from this software
15 //   without specific prior written permission.
16 //
17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 
29 #include "atf_result.h"
30 
31 #include <sys/types.h>
32 #include <sys/wait.h>
33 
34 #include <assert.h>
35 #include <errno.h>
36 #include <limits.h>
37 #include <stdbool.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 
42 #include "error.h"
43 #include "result.h"
44 
45 
46 // Enumeration of the different result types returned by an ATF test case.
47 enum atf_status {
48     ATF_STATUS_EXPECTED_DEATH,
49     ATF_STATUS_EXPECTED_EXIT,
50     ATF_STATUS_EXPECTED_FAILURE,
51     ATF_STATUS_EXPECTED_SIGNAL,
52     ATF_STATUS_EXPECTED_TIMEOUT,
53     ATF_STATUS_FAILED,
54     ATF_STATUS_PASSED,
55     ATF_STATUS_SKIPPED,
56 
57     // The broken status below is never returned by the test cases themselves.
58     // We use it internally to pass around problems detected while dealing with
59     // the test case itself (like an invalid result file).
60     ATF_STATUS_BROKEN,
61 };
62 
63 
64 /// Magic number representing a missing argument to the test result status.
65 ///
66 /// Use this to specify that an expected_exit or expected_signal result accepts
67 /// any exit code or signal, respectively.
68 #define NO_STATUS_ARG -1
69 
70 
71 /// Removes a trailing newline from a string (supposedly read by fgets(3)).
72 ///
73 /// \param [in,out] str The string to remove the trailing newline from.
74 ///
75 /// \return True if there was a newline character; false otherwise.
76 static bool
77 trim_newline(char* str)
78 {
79     const size_t length = strlen(str);
80     if (length == 0) {
81         return false;
82     } else {
83         if (str[length - 1] == '\n') {
84             str[length - 1] = '\0';
85             return true;
86         } else {
87             return false;
88         }
89     }
90 }
91 
92 
93 /// Force read on stream to see if we are really at EOF.
94 ///
95 /// A call to fgets(3) will not return EOF when it returns valid data.  But
96 /// because of our semantics below, we need to be able to tell if more lines are
97 /// available before actually reading them.
98 ///
99 /// \param input The stream to check for EOF.
100 ///
101 /// \return True if the stream is not at EOF yet; false otherwise.
102 static bool
103 is_really_eof(FILE* input)
104 {
105     const int ch = getc(input);
106     const bool real_eof = feof(input);
107     (void)ungetc(ch, input);
108     return real_eof;
109 }
110 
111 
112 /// Parses the optional argument to a result status.
113 ///
114 /// \param str Pointer to the argument.  May be \0 in those cases where the
115 ///     status does not have any argument.
116 /// \param [out] status_arg Value of the parsed argument.
117 ///
118 /// \return OK if the argument exists and is valid, or if it does not exist; an
119 /// error otherwise.
120 static kyua_error_t
121 parse_status_arg(const char* str, int* status_arg)
122 {
123     if (*str == '\0') {
124         *status_arg = NO_STATUS_ARG;
125         return kyua_error_ok();
126     }
127 
128     const size_t length = strlen(str);
129     if (*str != '(' || *(str + length - 1) != ')')
130         return kyua_generic_error_new("Invalid status argument %s", str);
131     const char* const arg = str + 1;
132 
133     char* endptr;
134     const long value = strtol(arg, &endptr, 10);
135     if (arg[0] == '\0' || endptr != str + length - 1)
136         return kyua_generic_error_new("Invalid status argument %s: not a "
137                                       "number", str);
138     if (errno == ERANGE && (value == LONG_MAX || value == LONG_MIN))
139         return kyua_generic_error_new("Invalid status argument %s: out of "
140                                       "range", str);
141     if (value < INT_MIN || value > INT_MAX)
142         return kyua_generic_error_new("Invalid status argument %s: out of "
143                                       "range", str);
144 
145     *status_arg = (int)value;
146     return kyua_error_ok();
147 }
148 
149 
150 /// Parses a textual result status.
151 ///
152 /// \param str The text to parse.
153 /// \param [out] status Status type if the input is valid.
154 /// \param [out] status_arg Optional integral argument to the status.
155 /// \param [out] need_reason Whether the detected status requires a reason.
156 ///
157 /// \return An error if the status is not valid.
158 static kyua_error_t
159 parse_status(const char* str, enum atf_status* status, int* status_arg,
160              bool* need_reason)
161 {
162     if (strcmp(str, "passed") == 0) {
163         *status = ATF_STATUS_PASSED;
164         *need_reason = false;
165         return kyua_error_ok();
166     } else if (strcmp(str, "failed") == 0) {
167         *status = ATF_STATUS_FAILED;
168         *need_reason = true;
169         return kyua_error_ok();
170     } else if (strcmp(str, "skipped") == 0) {
171         *status = ATF_STATUS_SKIPPED;
172         *need_reason = true;
173         return kyua_error_ok();
174     } else if (strcmp(str, "expected_death") == 0) {
175         *status = ATF_STATUS_EXPECTED_DEATH;
176         *need_reason = true;
177         return kyua_error_ok();
178     } else if (strncmp(str, "expected_exit", 13) == 0) {
179         *status = ATF_STATUS_EXPECTED_EXIT;
180         *need_reason = true;
181         return parse_status_arg(str + 13, status_arg);
182     } else if (strcmp(str, "expected_failure") == 0) {
183         *status = ATF_STATUS_EXPECTED_FAILURE;
184         *need_reason = true;
185         return kyua_error_ok();
186     } else if (strncmp(str, "expected_signal", 15) == 0){
187         *status = ATF_STATUS_EXPECTED_SIGNAL;
188         *need_reason = true;
189         return parse_status_arg(str + 15, status_arg);
190     } else if (strcmp(str, "expected_timeout") == 0) {
191         *status = ATF_STATUS_EXPECTED_TIMEOUT;
192         *need_reason = true;
193         return kyua_error_ok();
194     } else {
195         return kyua_generic_error_new("Unknown test case result status %s",
196                                       str);
197     }
198 }
199 
200 
201 /// Advances a pointer to a buffer to its end.
202 ///
203 /// \param [in,out] buffer Current buffer contents; updated on exit to point to
204 ///     the termination character.
205 /// \param [in,out] buffer_size Current buffer size; updated on exit to account
206 ///     for the decreased capacity due to the pointer increase.
207 static void
208 advance(char** buffer, size_t* buffer_size)
209 {
210     const size_t increment = strlen(*buffer);
211     *buffer += increment;
212     *buffer_size -= increment;
213 }
214 
215 
216 /// Extracts the result reason from the input file.
217 ///
218 /// \pre This can only be called for those result types that require a reason.
219 ///
220 /// \param [in,out] input The file from which to read.
221 /// \param first_line The first line of the reason.  Because this is part of the
222 ///     same line in which the result status is printed, this line has already
223 ///     been read by the caller and thus must be provided here.
224 /// \param [out] output Buffer to which to write the full reason.
225 /// \param output_size Size of the output buffer.
226 ///
227 /// \return An error if there was no reason in the input or if there is a
228 /// problem reading it.
229 static kyua_error_t
230 read_reason(FILE* input, const char* first_line, char* output,
231             size_t output_size)
232 {
233     if (first_line == NULL || *first_line == '\0')
234         return kyua_generic_error_new("Test case should have reported a "
235                                       "failure reason but didn't");
236 
237     snprintf(output, output_size, "%s", first_line);
238     advance(&output, &output_size);
239 
240     bool had_newline = true;
241     while (!is_really_eof(input)) {
242         if (had_newline) {
243             snprintf(output, output_size, "<<NEWLINE>>");
244             advance(&output, &output_size);
245         }
246 
247         if (fgets(output, output_size, input) == NULL) {
248             assert(ferror(input));
249             return kyua_libc_error_new(errno, "Failed to read reason from "
250                                        "result file");
251         }
252         had_newline = trim_newline(output);
253         advance(&output, &output_size);
254     }
255 
256     return kyua_error_ok();
257 }
258 
259 
260 /// Parses a results file written by an ATF test case.
261 ///
262 /// \param input_name Path to the result file to parse.
263 /// \param [out] status Type of result.
264 /// \param [out] status_arg Optional integral argument to the status.
265 /// \param [out] reason Textual explanation of the result, if any.
266 /// \param reason_size Length of the reason output buffer.
267 ///
268 /// \return An error if the input_name file has an invalid syntax; OK otherwise.
269 static kyua_error_t
270 read_atf_result(const char* input_name, enum atf_status* status,
271                 int* status_arg, char* const reason, const size_t reason_size)
272 {
273     kyua_error_t error = kyua_error_ok();
274 
275     FILE* input = fopen(input_name, "r");
276     if (input == NULL) {
277         error = kyua_generic_error_new("Premature exit");
278         goto out;
279     }
280 
281     char line[1024];
282     if (fgets(line, sizeof(line), input) == NULL) {
283         if (ferror(input)) {
284             error = kyua_libc_error_new(errno, "Failed to read result from "
285                                         "file %s", input_name);
286             goto out_input;
287         } else {
288             assert(feof(input));
289             error = kyua_generic_error_new("Empty result file %s", input_name);
290             goto out_input;
291         }
292     }
293 
294     if (!trim_newline(line)) {
295         error = kyua_generic_error_new("Missing newline in result file");
296         goto out_input;
297     }
298 
299     char* reason_start = strstr(line, ": ");
300     if (reason_start != NULL) {
301         *reason_start = '\0';
302         *(reason_start + 1) = '\0';
303         reason_start += 2;
304     }
305 
306     bool need_reason = false;  // Initialize to shut up gcc warning.
307     error = parse_status(line, status, status_arg, &need_reason);
308     if (kyua_error_is_set(error))
309         goto out_input;
310 
311     if (need_reason) {
312         error = read_reason(input, reason_start, reason, reason_size);
313     } else {
314         if (reason_start != NULL || !is_really_eof(input)) {
315             error = kyua_generic_error_new("Found unexpected reason in passed "
316                                            "test result");
317             goto out_input;
318         }
319         reason[0] = '\0';
320     }
321 
322 out_input:
323     fclose(input);
324 out:
325     return error;
326 }
327 
328 
329 /// Writes a generic result file for an ATF broken result.
330 ///
331 /// \param reason Textual explanation of the result.
332 /// \param status Exit code of the test program as returned by wait().
333 /// \param output Path to the generic result file to create.
334 /// \param [out] success Whether the result should be considered a success or
335 ///     not; e.g. passed and skipped are successful, but failed is not.
336 ///
337 /// \return An error if the conversion fails; OK otherwise.
338 static kyua_error_t
339 convert_broken(const char* reason, int status, const char* output,
340                bool* success)
341 {
342     if (WIFEXITED(status)) {
343         *success = false;
344         return kyua_result_write(
345             output, KYUA_RESULT_BROKEN, "%s; test case exited with code %d",
346             reason, WEXITSTATUS(status));
347     } else {
348         assert(WIFSIGNALED(status));
349         *success = false;
350         return kyua_result_write(
351             output, KYUA_RESULT_BROKEN, "%s; test case received signal %d%s",
352             reason, WTERMSIG(status),
353             WCOREDUMP(status) ? " (core dumped)" : "");
354     }
355 }
356 
357 
358 /// Writes a generic result file for an ATF expected_death result.
359 ///
360 /// \param reason Textual explanation of the result.
361 /// \param status Exit code of the test program as returned by wait().
362 /// \param output Path to the generic result file to create.
363 /// \param [out] success Whether the result should be considered a success or
364 ///     not; e.g. passed and skipped are successful, but failed is not.
365 ///
366 /// \return An error if the conversion fails; OK otherwise.
367 static kyua_error_t
368 convert_expected_death(const char* reason, int status, const char* output,
369                        bool* success)
370 {
371     if (WIFEXITED(status) && WEXITSTATUS(status) == EXIT_SUCCESS) {
372         *success = false;
373         return kyua_result_write(
374             output, KYUA_RESULT_FAILED, "Test case expected to die but exited "
375             "successfully");
376     } else {
377         *success = true;
378         return kyua_result_write(
379             output, KYUA_RESULT_EXPECTED_FAILURE, "%s", reason);
380     }
381 }
382 
383 
384 /// Writes a generic result file for an ATF expected_exit result
385 ///
386 /// \param status_arg Optional integral argument to the status.
387 /// \param reason Textual explanation of the result.
388 /// \param status Exit code of the test program as returned by wait().
389 /// \param output Path to the generic result file to create.
390 /// \param [out] success Whether the result should be considered a success or
391 ///     not; e.g. passed and skipped are successful, but failed is not.
392 ///
393 /// \return An error if the conversion fails; OK otherwise.
394 static kyua_error_t
395 convert_expected_exit(const int status_arg, const char* reason, int status,
396                       const char* output, bool* success)
397 {
398     if (WIFEXITED(status)) {
399         if (status_arg == NO_STATUS_ARG || status_arg == WEXITSTATUS(status)) {
400             *success = true;
401             return kyua_result_write(
402                 output, KYUA_RESULT_EXPECTED_FAILURE, "%s", reason);
403         } else {
404             *success = false;
405             return kyua_result_write(
406                 output, KYUA_RESULT_FAILED, "Test case expected to exit with "
407                 "code %d but got code %d", status_arg, WEXITSTATUS(status));
408         }
409     } else {
410         assert(WIFSIGNALED(status));
411         *success = false;
412         return kyua_result_write(
413             output, KYUA_RESULT_FAILED, "Test case expected to exit normally "
414             "but received signal %d%s", WTERMSIG(status),
415             WCOREDUMP(status) ? " (core dumped)" : "");
416     }
417 }
418 
419 
420 /// Writes a generic result file for an ATF expected_failure result.
421 ///
422 /// \param reason Textual explanation of the result.
423 /// \param status Exit code of the test program as returned by wait().
424 /// \param output Path to the generic result file to create.
425 /// \param [out] success Whether the result should be considered a success or
426 ///     not; e.g. passed and skipped are successful, but failed is not.
427 ///
428 /// \return An error if the conversion fails; OK otherwise.
429 static kyua_error_t
430 convert_expected_failure(const char* reason, int status, const char* output,
431                          bool* success)
432 {
433     if (WIFEXITED(status)) {
434         if (WEXITSTATUS(status) == EXIT_SUCCESS) {
435             *success = true;
436             return kyua_result_write(
437                 output, KYUA_RESULT_EXPECTED_FAILURE, "%s", reason);
438         } else {
439             *success = false;
440             return kyua_result_write(
441                 output, KYUA_RESULT_FAILED, "Test case expected a failure but "
442                 "exited with error code %d", WEXITSTATUS(status));
443         }
444     } else {
445         assert(WIFSIGNALED(status));
446         *success = false;
447         return kyua_result_write(
448             output, KYUA_RESULT_FAILED, "Test case expected a failure but "
449             "received signal %d%s", WTERMSIG(status),
450             WCOREDUMP(status) ? " (core dumped)" : "");
451     }
452 }
453 
454 
455 /// Writes a generic result file for an ATF expected_signal result.
456 ///
457 /// \param status_arg Optional integral argument to the status.
458 /// \param reason Textual explanation of the result.
459 /// \param status Exit code of the test program as returned by wait().
460 /// \param output Path to the generic result file to create.
461 /// \param [out] success Whether the result should be considered a success or
462 ///     not; e.g. passed and skipped are successful, but failed is not.
463 ///
464 /// \return An error if the conversion fails; OK otherwise.
465 static kyua_error_t
466 convert_expected_signal(const int status_arg, const char* reason, int status,
467                         const char* output, bool* success)
468 {
469     if (WIFSIGNALED(status)) {
470         if (status_arg == NO_STATUS_ARG || status_arg == WTERMSIG(status)) {
471             *success = true;
472             return kyua_result_write(
473                 output, KYUA_RESULT_EXPECTED_FAILURE, "%s", reason);
474         } else {
475             *success = false;
476             return kyua_result_write(
477                 output, KYUA_RESULT_FAILED, "Test case expected to receive "
478                 "signal %d but got %d", status_arg, WTERMSIG(status));
479         }
480     } else {
481         assert(WIFEXITED(status));
482         *success = false;
483         return kyua_result_write(
484             output, KYUA_RESULT_FAILED, "Test case expected to receive a "
485             "signal but exited with code %d", WEXITSTATUS(status));
486     }
487 }
488 
489 
490 /// Writes a generic result file for an ATF expected_timeout result.
491 ///
492 /// \param status Exit code of the test program as returned by wait().
493 /// \param output Path to the generic result file to create.
494 /// \param [out] success Whether the result should be considered a success or
495 ///     not; e.g. passed and skipped are successful, but failed is not.
496 ///
497 /// \return An error if the conversion fails; OK otherwise.
498 static kyua_error_t
499 convert_expected_timeout(int status, const char* output, bool* success)
500 {
501     if (WIFEXITED(status)) {
502         *success = false;
503         return kyua_result_write(
504             output, KYUA_RESULT_FAILED, "Test case expected to time out but "
505             "exited with code %d", WEXITSTATUS(status));
506     } else {
507         assert(WIFSIGNALED(status));
508         *success = false;
509         return kyua_result_write(
510             output, KYUA_RESULT_FAILED, "Test case expected to time out but "
511             "received signal %d%s", WTERMSIG(status),
512             WCOREDUMP(status) ? " (core dumped)" : "");
513     }
514 }
515 
516 
517 /// Writes a generic result file for an ATF failed result.
518 ///
519 /// \param reason Textual explanation of the result.
520 /// \param status Exit code of the test program as returned by wait().
521 /// \param output Path to the generic result file to create.
522 /// \param [out] success Whether the result should be considered a success or
523 ///     not; e.g. passed and skipped are successful, but failed is not.
524 ///
525 /// \return An error if the conversion fails; OK otherwise.
526 static kyua_error_t
527 convert_failed(const char* reason, int status, const char* output,
528                bool* success)
529 {
530     if (WIFEXITED(status)) {
531         if (WEXITSTATUS(status) == EXIT_SUCCESS) {
532             *success = false;
533             return kyua_result_write(
534                 output, KYUA_RESULT_BROKEN, "Test case reported a failed "
535                 "result but exited with a successful exit code");
536         } else {
537             *success = false;
538             return kyua_result_write(
539                 output, KYUA_RESULT_FAILED, "%s", reason);
540         }
541     } else {
542         assert(WIFSIGNALED(status));
543         *success = false;
544         return kyua_result_write(
545             output, KYUA_RESULT_BROKEN, "Test case reported a failed result "
546             "but received signal %d%s", WTERMSIG(status),
547             WCOREDUMP(status) ? " (core dumped)" : "");
548     }
549 }
550 
551 
552 /// Writes a generic result file for an ATF passed result.
553 ///
554 /// \param status Exit code of the test program as returned by wait().
555 /// \param output Path to the generic result file to create.
556 /// \param [out] success Whether the result should be considered a success or
557 ///     not; e.g. passed and skipped are successful, but failed is not.
558 ///
559 /// \return An error if the conversion fails; OK otherwise.
560 static kyua_error_t
561 convert_passed(int status, const char* output, bool* success)
562 {
563     if (WIFEXITED(status)) {
564         if (WEXITSTATUS(status) == EXIT_SUCCESS) {
565             *success = true;
566             return kyua_result_write(output, KYUA_RESULT_PASSED, NULL);
567         } else {
568             *success = false;
569             return kyua_result_write(
570                 output, KYUA_RESULT_BROKEN, "Test case reported a passed "
571                 "result but returned a non-zero exit code %d",
572                 WEXITSTATUS(status));
573         }
574     } else {
575         assert(WIFSIGNALED(status));
576         *success = false;
577         return kyua_result_write(
578             output, KYUA_RESULT_BROKEN, "Test case reported a passed result "
579             "but received signal %d%s", WTERMSIG(status),
580             WCOREDUMP(status) ? " (core dumped)" : "");
581     }
582 }
583 
584 
585 /// Writes a generic result file for an ATF skipped result.
586 ///
587 /// \param reason Textual explanation of the result.
588 /// \param status Exit code of the test program as returned by wait().
589 /// \param output Path to the generic result file to create.
590 /// \param [out] success Whether the result should be considered a success or
591 ///     not; e.g. passed and skipped are successful, but failed is not.
592 ///
593 /// \return An error if the conversion fails; OK otherwise.
594 static kyua_error_t
595 convert_skipped(const char* reason, int status, const char* output,
596                 bool* success)
597 {
598     if (WIFEXITED(status)) {
599         if (WEXITSTATUS(status) == EXIT_SUCCESS) {
600             *success = true;
601             return kyua_result_write(output, KYUA_RESULT_SKIPPED, "%s", reason);
602         } else {
603             *success = false;
604             return kyua_result_write(
605                 output, KYUA_RESULT_BROKEN, "Test case reported a skipped "
606                 "result but returned a non-zero exit code %d",
607                 WEXITSTATUS(status));
608         }
609     } else {
610         *success = false;
611         assert(WIFSIGNALED(status));
612         return kyua_result_write(
613             output, KYUA_RESULT_BROKEN, "Test case reported a skipped result "
614             "but received signal %d%s", WTERMSIG(status),
615             WCOREDUMP(status) ? " (core dumped)" : "");
616     }
617 }
618 
619 
620 /// Writes a generic result file based on an ATF result and an exit code.
621 ///
622 /// \param status Type of the ATF result.
623 /// \param status_arg Optional integral argument to the status.
624 /// \param reason Textual explanation of the result.
625 /// \param wait_status Exit code of the test program as returned by wait().
626 /// \param timed_out Whether the test program timed out or not.
627 /// \param output Path to the generic result file to create.
628 /// \param [out] success Whether the result should be considered a success or
629 ///     not; e.g. passed and skipped are successful, but failed is not.
630 ///
631 /// \return An error if the conversion fails; OK otherwise.
632 static kyua_error_t
633 convert_result(const enum atf_status status, const int status_arg,
634                const char* reason, const int wait_status, const bool timed_out,
635                const char* output, bool* success)
636 {
637     if (timed_out) {
638         if (status == ATF_STATUS_EXPECTED_TIMEOUT) {
639             *success = true;
640             return kyua_result_write(
641                 output, KYUA_RESULT_EXPECTED_FAILURE, "%s", reason);
642         } else {
643             assert(status == ATF_STATUS_BROKEN);
644             *success = false;
645             return kyua_result_write(
646                 output, KYUA_RESULT_BROKEN, "Test case body timed out");
647         }
648     }
649 
650     switch (status) {
651     case ATF_STATUS_BROKEN:
652         return convert_broken(reason, wait_status, output, success);
653 
654     case ATF_STATUS_EXPECTED_DEATH:
655         return convert_expected_death(reason, wait_status, output, success);
656 
657     case ATF_STATUS_EXPECTED_EXIT:
658         return convert_expected_exit(status_arg, reason, wait_status, output,
659                                      success);
660 
661     case ATF_STATUS_EXPECTED_FAILURE:
662         return convert_expected_failure(reason, wait_status, output, success);
663 
664     case ATF_STATUS_EXPECTED_SIGNAL:
665         return convert_expected_signal(status_arg, reason, wait_status, output,
666                                        success);
667 
668     case ATF_STATUS_EXPECTED_TIMEOUT:
669         return convert_expected_timeout(wait_status, output, success);
670 
671     case ATF_STATUS_FAILED:
672         return convert_failed(reason, wait_status, output, success);
673 
674     case ATF_STATUS_PASSED:
675         return convert_passed(wait_status, output, success);
676 
677     case ATF_STATUS_SKIPPED:
678         return convert_skipped(reason, wait_status, output, success);
679     }
680 
681     assert(false);
682 #if defined(__minix) && defined(NDEBUG)
683     abort();
684 #endif /* defined(__minix) && !defined(NDEBUG) */
685 }
686 
687 
688 /// Writes a generic result file based on an ATF result file and an exit code.
689 ///
690 /// \param input_name Path to the ATF result file to parse.
691 /// \param output_name Path to the generic result file to create.
692 /// \param wait_status Exit code of the test program as returned by wait().
693 /// \param timed_out Whether the test program timed out or not.
694 /// \param [out] success Whether the result should be considered a success or
695 ///     not; e.g. passed and skipped are successful, but failed is not.
696 ///
697 /// \return An error if the conversion fails; OK otherwise.
698 kyua_error_t
699 kyua_atf_result_rewrite(const char* input_name, const char* output_name,
700                         const int wait_status, const bool timed_out,
701                         bool* success)
702 {
703     enum atf_status status; int status_arg; char reason[1024];
704     status = ATF_STATUS_BROKEN;  // Initialize to shut up gcc warning.
705     const kyua_error_t error = read_atf_result(input_name, &status, &status_arg,
706                                                reason, sizeof(reason));
707     if (kyua_error_is_set(error)) {
708         // Errors while parsing the ATF result file can often be attributed to
709         // the result file being bogus.  Therefore, just mark the test case as
710         // broken, because it possibly is.
711         status = ATF_STATUS_BROKEN;
712         kyua_error_format(error, reason, sizeof(reason));
713         kyua_error_free(error);
714     }
715 
716     // Errors converting the loaded result to the final result file are not due
717     // to a bad test program: they are because our own code fails (e.g. cannot
718     // create the output file).  These need to be returned to the caller.
719     return convert_result(status, status_arg, reason, wait_status, timed_out,
720                           output_name, success);
721 }
722 
723 
724 /// Creates a result file for a failed cleanup routine.
725 ///
726 /// This function is supposed to be invoked after the body has had a chance to
727 /// create its own result file, and only if the body has terminated with a
728 /// non-failure result.
729 ///
730 /// \param output_name Path to the generic result file to create.
731 /// \param wait_status Exit code of the test program as returned by wait().
732 /// \param timed_out Whether the test program timed out or not.
733 /// \param [out] success Whether the result should be considered a success or
734 ///     not; i.e. a clean exit is successful, but anything else is a failure.
735 ///
736 /// \return An error if there is a problem writing the result; OK otherwise.
737 kyua_error_t
738 kyua_atf_result_cleanup_rewrite(const char* output_name, int wait_status,
739                                 const bool timed_out, bool* success)
740 {
741     if (timed_out) {
742         *success = false;
743         return kyua_result_write(
744             output_name, KYUA_RESULT_BROKEN, "Test case cleanup timed out");
745     } else {
746         if (WIFEXITED(wait_status)) {
747             if (WEXITSTATUS(wait_status) == EXIT_SUCCESS) {
748                 *success = true;
749                 // Reuse the result file created by the body.  I.e. avoid
750                 // creating a new file here.
751                 return kyua_error_ok();
752             } else {
753                 *success = false;
754                 return kyua_result_write(
755                     output_name, KYUA_RESULT_BROKEN, "Test case cleanup exited "
756                     "with code %d", WEXITSTATUS(wait_status));
757             }
758         } else {
759             *success = false;
760             return kyua_result_write(
761                 output_name, KYUA_RESULT_BROKEN, "Test case cleanup received "
762                 "signal %d%s", WTERMSIG(wait_status),
763                 WCOREDUMP(wait_status) ? " (core dumped)" : "");
764         }
765     }
766 }
767