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