3260d16f34
Change-Id: Ib64cef83a646bce2b0afa72b607fb9e5c306e859
766 lines
28 KiB
C
766 lines
28 KiB
C
// Copyright 2012 Google Inc.
|
|
// All rights reserved.
|
|
//
|
|
// Redistribution and use in source and binary forms, with or without
|
|
// modification, are permitted provided that the following conditions are
|
|
// met:
|
|
//
|
|
// * Redistributions of source code must retain the above copyright
|
|
// notice, this list of conditions and the following disclaimer.
|
|
// * Redistributions in binary form must reproduce the above copyright
|
|
// notice, this list of conditions and the following disclaimer in the
|
|
// documentation and/or other materials provided with the distribution.
|
|
// * Neither the name of Google Inc. nor the names of its contributors
|
|
// may be used to endorse or promote products derived from this software
|
|
// without specific prior written permission.
|
|
//
|
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
#include "atf_result.h"
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/wait.h>
|
|
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <limits.h>
|
|
#include <stdbool.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "error.h"
|
|
#include "result.h"
|
|
|
|
|
|
// Enumeration of the different result types returned by an ATF test case.
|
|
enum atf_status {
|
|
ATF_STATUS_EXPECTED_DEATH,
|
|
ATF_STATUS_EXPECTED_EXIT,
|
|
ATF_STATUS_EXPECTED_FAILURE,
|
|
ATF_STATUS_EXPECTED_SIGNAL,
|
|
ATF_STATUS_EXPECTED_TIMEOUT,
|
|
ATF_STATUS_FAILED,
|
|
ATF_STATUS_PASSED,
|
|
ATF_STATUS_SKIPPED,
|
|
|
|
// The broken status below is never returned by the test cases themselves.
|
|
// We use it internally to pass around problems detected while dealing with
|
|
// the test case itself (like an invalid result file).
|
|
ATF_STATUS_BROKEN,
|
|
};
|
|
|
|
|
|
/// Magic number representing a missing argument to the test result status.
|
|
///
|
|
/// Use this to specify that an expected_exit or expected_signal result accepts
|
|
/// any exit code or signal, respectively.
|
|
#define NO_STATUS_ARG -1
|
|
|
|
|
|
/// Removes a trailing newline from a string (supposedly read by fgets(3)).
|
|
///
|
|
/// \param [in,out] str The string to remove the trailing newline from.
|
|
///
|
|
/// \return True if there was a newline character; false otherwise.
|
|
static bool
|
|
trim_newline(char* str)
|
|
{
|
|
const size_t length = strlen(str);
|
|
if (length == 0) {
|
|
return false;
|
|
} else {
|
|
if (str[length - 1] == '\n') {
|
|
str[length - 1] = '\0';
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/// Force read on stream to see if we are really at EOF.
|
|
///
|
|
/// A call to fgets(3) will not return EOF when it returns valid data. But
|
|
/// because of our semantics below, we need to be able to tell if more lines are
|
|
/// available before actually reading them.
|
|
///
|
|
/// \param input The stream to check for EOF.
|
|
///
|
|
/// \return True if the stream is not at EOF yet; false otherwise.
|
|
static bool
|
|
is_really_eof(FILE* input)
|
|
{
|
|
const int ch = getc(input);
|
|
const bool real_eof = feof(input);
|
|
(void)ungetc(ch, input);
|
|
return real_eof;
|
|
}
|
|
|
|
|
|
/// Parses the optional argument to a result status.
|
|
///
|
|
/// \param str Pointer to the argument. May be \0 in those cases where the
|
|
/// status does not have any argument.
|
|
/// \param [out] status_arg Value of the parsed argument.
|
|
///
|
|
/// \return OK if the argument exists and is valid, or if it does not exist; an
|
|
/// error otherwise.
|
|
static kyua_error_t
|
|
parse_status_arg(const char* str, int* status_arg)
|
|
{
|
|
if (*str == '\0') {
|
|
*status_arg = NO_STATUS_ARG;
|
|
return kyua_error_ok();
|
|
}
|
|
|
|
const size_t length = strlen(str);
|
|
if (*str != '(' || *(str + length - 1) != ')')
|
|
return kyua_generic_error_new("Invalid status argument %s", str);
|
|
const char* const arg = str + 1;
|
|
|
|
char* endptr;
|
|
const long value = strtol(arg, &endptr, 10);
|
|
if (arg[0] == '\0' || endptr != str + length - 1)
|
|
return kyua_generic_error_new("Invalid status argument %s: not a "
|
|
"number", str);
|
|
if (errno == ERANGE && (value == LONG_MAX || value == LONG_MIN))
|
|
return kyua_generic_error_new("Invalid status argument %s: out of "
|
|
"range", str);
|
|
if (value < INT_MIN || value > INT_MAX)
|
|
return kyua_generic_error_new("Invalid status argument %s: out of "
|
|
"range", str);
|
|
|
|
*status_arg = (int)value;
|
|
return kyua_error_ok();
|
|
}
|
|
|
|
|
|
/// Parses a textual result status.
|
|
///
|
|
/// \param str The text to parse.
|
|
/// \param [out] status Status type if the input is valid.
|
|
/// \param [out] status_arg Optional integral argument to the status.
|
|
/// \param [out] need_reason Whether the detected status requires a reason.
|
|
///
|
|
/// \return An error if the status is not valid.
|
|
static kyua_error_t
|
|
parse_status(const char* str, enum atf_status* status, int* status_arg,
|
|
bool* need_reason)
|
|
{
|
|
if (strcmp(str, "passed") == 0) {
|
|
*status = ATF_STATUS_PASSED;
|
|
*need_reason = false;
|
|
return kyua_error_ok();
|
|
} else if (strcmp(str, "failed") == 0) {
|
|
*status = ATF_STATUS_FAILED;
|
|
*need_reason = true;
|
|
return kyua_error_ok();
|
|
} else if (strcmp(str, "skipped") == 0) {
|
|
*status = ATF_STATUS_SKIPPED;
|
|
*need_reason = true;
|
|
return kyua_error_ok();
|
|
} else if (strcmp(str, "expected_death") == 0) {
|
|
*status = ATF_STATUS_EXPECTED_DEATH;
|
|
*need_reason = true;
|
|
return kyua_error_ok();
|
|
} else if (strncmp(str, "expected_exit", 13) == 0) {
|
|
*status = ATF_STATUS_EXPECTED_EXIT;
|
|
*need_reason = true;
|
|
return parse_status_arg(str + 13, status_arg);
|
|
} else if (strcmp(str, "expected_failure") == 0) {
|
|
*status = ATF_STATUS_EXPECTED_FAILURE;
|
|
*need_reason = true;
|
|
return kyua_error_ok();
|
|
} else if (strncmp(str, "expected_signal", 15) == 0){
|
|
*status = ATF_STATUS_EXPECTED_SIGNAL;
|
|
*need_reason = true;
|
|
return parse_status_arg(str + 15, status_arg);
|
|
} else if (strcmp(str, "expected_timeout") == 0) {
|
|
*status = ATF_STATUS_EXPECTED_TIMEOUT;
|
|
*need_reason = true;
|
|
return kyua_error_ok();
|
|
} else {
|
|
return kyua_generic_error_new("Unknown test case result status %s",
|
|
str);
|
|
}
|
|
}
|
|
|
|
|
|
/// Advances a pointer to a buffer to its end.
|
|
///
|
|
/// \param [in,out] buffer Current buffer contents; updated on exit to point to
|
|
/// the termination character.
|
|
/// \param [in,out] buffer_size Current buffer size; updated on exit to account
|
|
/// for the decreased capacity due to the pointer increase.
|
|
static void
|
|
advance(char** buffer, size_t* buffer_size)
|
|
{
|
|
const size_t increment = strlen(*buffer);
|
|
*buffer += increment;
|
|
*buffer_size -= increment;
|
|
}
|
|
|
|
|
|
/// Extracts the result reason from the input file.
|
|
///
|
|
/// \pre This can only be called for those result types that require a reason.
|
|
///
|
|
/// \param [in,out] input The file from which to read.
|
|
/// \param first_line The first line of the reason. Because this is part of the
|
|
/// same line in which the result status is printed, this line has already
|
|
/// been read by the caller and thus must be provided here.
|
|
/// \param [out] output Buffer to which to write the full reason.
|
|
/// \param output_size Size of the output buffer.
|
|
///
|
|
/// \return An error if there was no reason in the input or if there is a
|
|
/// problem reading it.
|
|
static kyua_error_t
|
|
read_reason(FILE* input, const char* first_line, char* output,
|
|
size_t output_size)
|
|
{
|
|
if (first_line == NULL || *first_line == '\0')
|
|
return kyua_generic_error_new("Test case should have reported a "
|
|
"failure reason but didn't");
|
|
|
|
snprintf(output, output_size, "%s", first_line);
|
|
advance(&output, &output_size);
|
|
|
|
bool had_newline = true;
|
|
while (!is_really_eof(input)) {
|
|
if (had_newline) {
|
|
snprintf(output, output_size, "<<NEWLINE>>");
|
|
advance(&output, &output_size);
|
|
}
|
|
|
|
if (fgets(output, output_size, input) == NULL) {
|
|
assert(ferror(input));
|
|
return kyua_libc_error_new(errno, "Failed to read reason from "
|
|
"result file");
|
|
}
|
|
had_newline = trim_newline(output);
|
|
advance(&output, &output_size);
|
|
}
|
|
|
|
return kyua_error_ok();
|
|
}
|
|
|
|
|
|
/// Parses a results file written by an ATF test case.
|
|
///
|
|
/// \param input_name Path to the result file to parse.
|
|
/// \param [out] status Type of result.
|
|
/// \param [out] status_arg Optional integral argument to the status.
|
|
/// \param [out] reason Textual explanation of the result, if any.
|
|
/// \param reason_size Length of the reason output buffer.
|
|
///
|
|
/// \return An error if the input_name file has an invalid syntax; OK otherwise.
|
|
static kyua_error_t
|
|
read_atf_result(const char* input_name, enum atf_status* status,
|
|
int* status_arg, char* const reason, const size_t reason_size)
|
|
{
|
|
kyua_error_t error = kyua_error_ok();
|
|
|
|
FILE* input = fopen(input_name, "r");
|
|
if (input == NULL) {
|
|
error = kyua_generic_error_new("Premature exit");
|
|
goto out;
|
|
}
|
|
|
|
char line[1024];
|
|
if (fgets(line, sizeof(line), input) == NULL) {
|
|
if (ferror(input)) {
|
|
error = kyua_libc_error_new(errno, "Failed to read result from "
|
|
"file %s", input_name);
|
|
goto out_input;
|
|
} else {
|
|
assert(feof(input));
|
|
error = kyua_generic_error_new("Empty result file %s", input_name);
|
|
goto out_input;
|
|
}
|
|
}
|
|
|
|
if (!trim_newline(line)) {
|
|
error = kyua_generic_error_new("Missing newline in result file");
|
|
goto out_input;
|
|
}
|
|
|
|
char* reason_start = strstr(line, ": ");
|
|
if (reason_start != NULL) {
|
|
*reason_start = '\0';
|
|
*(reason_start + 1) = '\0';
|
|
reason_start += 2;
|
|
}
|
|
|
|
bool need_reason = false; // Initialize to shut up gcc warning.
|
|
error = parse_status(line, status, status_arg, &need_reason);
|
|
if (kyua_error_is_set(error))
|
|
goto out_input;
|
|
|
|
if (need_reason) {
|
|
error = read_reason(input, reason_start, reason, reason_size);
|
|
} else {
|
|
if (reason_start != NULL || !is_really_eof(input)) {
|
|
error = kyua_generic_error_new("Found unexpected reason in passed "
|
|
"test result");
|
|
goto out_input;
|
|
}
|
|
reason[0] = '\0';
|
|
}
|
|
|
|
out_input:
|
|
fclose(input);
|
|
out:
|
|
return error;
|
|
}
|
|
|
|
|
|
/// Writes a generic result file for an ATF broken result.
|
|
///
|
|
/// \param reason Textual explanation of the result.
|
|
/// \param status Exit code of the test program as returned by wait().
|
|
/// \param output Path to the generic result file to create.
|
|
/// \param [out] success Whether the result should be considered a success or
|
|
/// not; e.g. passed and skipped are successful, but failed is not.
|
|
///
|
|
/// \return An error if the conversion fails; OK otherwise.
|
|
static kyua_error_t
|
|
convert_broken(const char* reason, int status, const char* output,
|
|
bool* success)
|
|
{
|
|
if (WIFEXITED(status)) {
|
|
*success = false;
|
|
return kyua_result_write(
|
|
output, KYUA_RESULT_BROKEN, "%s; test case exited with code %d",
|
|
reason, WEXITSTATUS(status));
|
|
} else {
|
|
assert(WIFSIGNALED(status));
|
|
*success = false;
|
|
return kyua_result_write(
|
|
output, KYUA_RESULT_BROKEN, "%s; test case received signal %d%s",
|
|
reason, WTERMSIG(status),
|
|
WCOREDUMP(status) ? " (core dumped)" : "");
|
|
}
|
|
}
|
|
|
|
|
|
/// Writes a generic result file for an ATF expected_death result.
|
|
///
|
|
/// \param reason Textual explanation of the result.
|
|
/// \param status Exit code of the test program as returned by wait().
|
|
/// \param output Path to the generic result file to create.
|
|
/// \param [out] success Whether the result should be considered a success or
|
|
/// not; e.g. passed and skipped are successful, but failed is not.
|
|
///
|
|
/// \return An error if the conversion fails; OK otherwise.
|
|
static kyua_error_t
|
|
convert_expected_death(const char* reason, int status, const char* output,
|
|
bool* success)
|
|
{
|
|
if (WIFEXITED(status) && WEXITSTATUS(status) == EXIT_SUCCESS) {
|
|
*success = false;
|
|
return kyua_result_write(
|
|
output, KYUA_RESULT_FAILED, "Test case expected to die but exited "
|
|
"successfully");
|
|
} else {
|
|
*success = true;
|
|
return kyua_result_write(
|
|
output, KYUA_RESULT_EXPECTED_FAILURE, "%s", reason);
|
|
}
|
|
}
|
|
|
|
|
|
/// Writes a generic result file for an ATF expected_exit result
|
|
///
|
|
/// \param status_arg Optional integral argument to the status.
|
|
/// \param reason Textual explanation of the result.
|
|
/// \param status Exit code of the test program as returned by wait().
|
|
/// \param output Path to the generic result file to create.
|
|
/// \param [out] success Whether the result should be considered a success or
|
|
/// not; e.g. passed and skipped are successful, but failed is not.
|
|
///
|
|
/// \return An error if the conversion fails; OK otherwise.
|
|
static kyua_error_t
|
|
convert_expected_exit(const int status_arg, const char* reason, int status,
|
|
const char* output, bool* success)
|
|
{
|
|
if (WIFEXITED(status)) {
|
|
if (status_arg == NO_STATUS_ARG || status_arg == WEXITSTATUS(status)) {
|
|
*success = true;
|
|
return kyua_result_write(
|
|
output, KYUA_RESULT_EXPECTED_FAILURE, "%s", reason);
|
|
} else {
|
|
*success = false;
|
|
return kyua_result_write(
|
|
output, KYUA_RESULT_FAILED, "Test case expected to exit with "
|
|
"code %d but got code %d", status_arg, WEXITSTATUS(status));
|
|
}
|
|
} else {
|
|
assert(WIFSIGNALED(status));
|
|
*success = false;
|
|
return kyua_result_write(
|
|
output, KYUA_RESULT_FAILED, "Test case expected to exit normally "
|
|
"but received signal %d%s", WTERMSIG(status),
|
|
WCOREDUMP(status) ? " (core dumped)" : "");
|
|
}
|
|
}
|
|
|
|
|
|
/// Writes a generic result file for an ATF expected_failure result.
|
|
///
|
|
/// \param reason Textual explanation of the result.
|
|
/// \param status Exit code of the test program as returned by wait().
|
|
/// \param output Path to the generic result file to create.
|
|
/// \param [out] success Whether the result should be considered a success or
|
|
/// not; e.g. passed and skipped are successful, but failed is not.
|
|
///
|
|
/// \return An error if the conversion fails; OK otherwise.
|
|
static kyua_error_t
|
|
convert_expected_failure(const char* reason, int status, const char* output,
|
|
bool* success)
|
|
{
|
|
if (WIFEXITED(status)) {
|
|
if (WEXITSTATUS(status) == EXIT_SUCCESS) {
|
|
*success = true;
|
|
return kyua_result_write(
|
|
output, KYUA_RESULT_EXPECTED_FAILURE, "%s", reason);
|
|
} else {
|
|
*success = false;
|
|
return kyua_result_write(
|
|
output, KYUA_RESULT_FAILED, "Test case expected a failure but "
|
|
"exited with error code %d", WEXITSTATUS(status));
|
|
}
|
|
} else {
|
|
assert(WIFSIGNALED(status));
|
|
*success = false;
|
|
return kyua_result_write(
|
|
output, KYUA_RESULT_FAILED, "Test case expected a failure but "
|
|
"received signal %d%s", WTERMSIG(status),
|
|
WCOREDUMP(status) ? " (core dumped)" : "");
|
|
}
|
|
}
|
|
|
|
|
|
/// Writes a generic result file for an ATF expected_signal result.
|
|
///
|
|
/// \param status_arg Optional integral argument to the status.
|
|
/// \param reason Textual explanation of the result.
|
|
/// \param status Exit code of the test program as returned by wait().
|
|
/// \param output Path to the generic result file to create.
|
|
/// \param [out] success Whether the result should be considered a success or
|
|
/// not; e.g. passed and skipped are successful, but failed is not.
|
|
///
|
|
/// \return An error if the conversion fails; OK otherwise.
|
|
static kyua_error_t
|
|
convert_expected_signal(const int status_arg, const char* reason, int status,
|
|
const char* output, bool* success)
|
|
{
|
|
if (WIFSIGNALED(status)) {
|
|
if (status_arg == NO_STATUS_ARG || status_arg == WTERMSIG(status)) {
|
|
*success = true;
|
|
return kyua_result_write(
|
|
output, KYUA_RESULT_EXPECTED_FAILURE, "%s", reason);
|
|
} else {
|
|
*success = false;
|
|
return kyua_result_write(
|
|
output, KYUA_RESULT_FAILED, "Test case expected to receive "
|
|
"signal %d but got %d", status_arg, WTERMSIG(status));
|
|
}
|
|
} else {
|
|
assert(WIFEXITED(status));
|
|
*success = false;
|
|
return kyua_result_write(
|
|
output, KYUA_RESULT_FAILED, "Test case expected to receive a "
|
|
"signal but exited with code %d", WEXITSTATUS(status));
|
|
}
|
|
}
|
|
|
|
|
|
/// Writes a generic result file for an ATF expected_timeout result.
|
|
///
|
|
/// \param status Exit code of the test program as returned by wait().
|
|
/// \param output Path to the generic result file to create.
|
|
/// \param [out] success Whether the result should be considered a success or
|
|
/// not; e.g. passed and skipped are successful, but failed is not.
|
|
///
|
|
/// \return An error if the conversion fails; OK otherwise.
|
|
static kyua_error_t
|
|
convert_expected_timeout(int status, const char* output, bool* success)
|
|
{
|
|
if (WIFEXITED(status)) {
|
|
*success = false;
|
|
return kyua_result_write(
|
|
output, KYUA_RESULT_FAILED, "Test case expected to time out but "
|
|
"exited with code %d", WEXITSTATUS(status));
|
|
} else {
|
|
assert(WIFSIGNALED(status));
|
|
*success = false;
|
|
return kyua_result_write(
|
|
output, KYUA_RESULT_FAILED, "Test case expected to time out but "
|
|
"received signal %d%s", WTERMSIG(status),
|
|
WCOREDUMP(status) ? " (core dumped)" : "");
|
|
}
|
|
}
|
|
|
|
|
|
/// Writes a generic result file for an ATF failed result.
|
|
///
|
|
/// \param reason Textual explanation of the result.
|
|
/// \param status Exit code of the test program as returned by wait().
|
|
/// \param output Path to the generic result file to create.
|
|
/// \param [out] success Whether the result should be considered a success or
|
|
/// not; e.g. passed and skipped are successful, but failed is not.
|
|
///
|
|
/// \return An error if the conversion fails; OK otherwise.
|
|
static kyua_error_t
|
|
convert_failed(const char* reason, int status, const char* output,
|
|
bool* success)
|
|
{
|
|
if (WIFEXITED(status)) {
|
|
if (WEXITSTATUS(status) == EXIT_SUCCESS) {
|
|
*success = false;
|
|
return kyua_result_write(
|
|
output, KYUA_RESULT_BROKEN, "Test case reported a failed "
|
|
"result but exited with a successful exit code");
|
|
} else {
|
|
*success = false;
|
|
return kyua_result_write(
|
|
output, KYUA_RESULT_FAILED, "%s", reason);
|
|
}
|
|
} else {
|
|
assert(WIFSIGNALED(status));
|
|
*success = false;
|
|
return kyua_result_write(
|
|
output, KYUA_RESULT_BROKEN, "Test case reported a failed result "
|
|
"but received signal %d%s", WTERMSIG(status),
|
|
WCOREDUMP(status) ? " (core dumped)" : "");
|
|
}
|
|
}
|
|
|
|
|
|
/// Writes a generic result file for an ATF passed result.
|
|
///
|
|
/// \param status Exit code of the test program as returned by wait().
|
|
/// \param output Path to the generic result file to create.
|
|
/// \param [out] success Whether the result should be considered a success or
|
|
/// not; e.g. passed and skipped are successful, but failed is not.
|
|
///
|
|
/// \return An error if the conversion fails; OK otherwise.
|
|
static kyua_error_t
|
|
convert_passed(int status, const char* output, bool* success)
|
|
{
|
|
if (WIFEXITED(status)) {
|
|
if (WEXITSTATUS(status) == EXIT_SUCCESS) {
|
|
*success = true;
|
|
return kyua_result_write(output, KYUA_RESULT_PASSED, NULL);
|
|
} else {
|
|
*success = false;
|
|
return kyua_result_write(
|
|
output, KYUA_RESULT_BROKEN, "Test case reported a passed "
|
|
"result but returned a non-zero exit code %d",
|
|
WEXITSTATUS(status));
|
|
}
|
|
} else {
|
|
assert(WIFSIGNALED(status));
|
|
*success = false;
|
|
return kyua_result_write(
|
|
output, KYUA_RESULT_BROKEN, "Test case reported a passed result "
|
|
"but received signal %d%s", WTERMSIG(status),
|
|
WCOREDUMP(status) ? " (core dumped)" : "");
|
|
}
|
|
}
|
|
|
|
|
|
/// Writes a generic result file for an ATF skipped result.
|
|
///
|
|
/// \param reason Textual explanation of the result.
|
|
/// \param status Exit code of the test program as returned by wait().
|
|
/// \param output Path to the generic result file to create.
|
|
/// \param [out] success Whether the result should be considered a success or
|
|
/// not; e.g. passed and skipped are successful, but failed is not.
|
|
///
|
|
/// \return An error if the conversion fails; OK otherwise.
|
|
static kyua_error_t
|
|
convert_skipped(const char* reason, int status, const char* output,
|
|
bool* success)
|
|
{
|
|
if (WIFEXITED(status)) {
|
|
if (WEXITSTATUS(status) == EXIT_SUCCESS) {
|
|
*success = true;
|
|
return kyua_result_write(output, KYUA_RESULT_SKIPPED, "%s", reason);
|
|
} else {
|
|
*success = false;
|
|
return kyua_result_write(
|
|
output, KYUA_RESULT_BROKEN, "Test case reported a skipped "
|
|
"result but returned a non-zero exit code %d",
|
|
WEXITSTATUS(status));
|
|
}
|
|
} else {
|
|
*success = false;
|
|
assert(WIFSIGNALED(status));
|
|
return kyua_result_write(
|
|
output, KYUA_RESULT_BROKEN, "Test case reported a skipped result "
|
|
"but received signal %d%s", WTERMSIG(status),
|
|
WCOREDUMP(status) ? " (core dumped)" : "");
|
|
}
|
|
}
|
|
|
|
|
|
/// Writes a generic result file based on an ATF result and an exit code.
|
|
///
|
|
/// \param status Type of the ATF result.
|
|
/// \param status_arg Optional integral argument to the status.
|
|
/// \param reason Textual explanation of the result.
|
|
/// \param wait_status Exit code of the test program as returned by wait().
|
|
/// \param timed_out Whether the test program timed out or not.
|
|
/// \param output Path to the generic result file to create.
|
|
/// \param [out] success Whether the result should be considered a success or
|
|
/// not; e.g. passed and skipped are successful, but failed is not.
|
|
///
|
|
/// \return An error if the conversion fails; OK otherwise.
|
|
static kyua_error_t
|
|
convert_result(const enum atf_status status, const int status_arg,
|
|
const char* reason, const int wait_status, const bool timed_out,
|
|
const char* output, bool* success)
|
|
{
|
|
if (timed_out) {
|
|
if (status == ATF_STATUS_EXPECTED_TIMEOUT) {
|
|
*success = true;
|
|
return kyua_result_write(
|
|
output, KYUA_RESULT_EXPECTED_FAILURE, "%s", reason);
|
|
} else {
|
|
assert(status == ATF_STATUS_BROKEN);
|
|
*success = false;
|
|
return kyua_result_write(
|
|
output, KYUA_RESULT_BROKEN, "Test case body timed out");
|
|
}
|
|
}
|
|
|
|
switch (status) {
|
|
case ATF_STATUS_BROKEN:
|
|
return convert_broken(reason, wait_status, output, success);
|
|
|
|
case ATF_STATUS_EXPECTED_DEATH:
|
|
return convert_expected_death(reason, wait_status, output, success);
|
|
|
|
case ATF_STATUS_EXPECTED_EXIT:
|
|
return convert_expected_exit(status_arg, reason, wait_status, output,
|
|
success);
|
|
|
|
case ATF_STATUS_EXPECTED_FAILURE:
|
|
return convert_expected_failure(reason, wait_status, output, success);
|
|
|
|
case ATF_STATUS_EXPECTED_SIGNAL:
|
|
return convert_expected_signal(status_arg, reason, wait_status, output,
|
|
success);
|
|
|
|
case ATF_STATUS_EXPECTED_TIMEOUT:
|
|
return convert_expected_timeout(wait_status, output, success);
|
|
|
|
case ATF_STATUS_FAILED:
|
|
return convert_failed(reason, wait_status, output, success);
|
|
|
|
case ATF_STATUS_PASSED:
|
|
return convert_passed(wait_status, output, success);
|
|
|
|
case ATF_STATUS_SKIPPED:
|
|
return convert_skipped(reason, wait_status, output, success);
|
|
}
|
|
|
|
assert(false);
|
|
#if defined(__minix) && defined(NDEBUG)
|
|
abort();
|
|
#endif /* defined(__minix) && !defined(NDEBUG) */
|
|
}
|
|
|
|
|
|
/// Writes a generic result file based on an ATF result file and an exit code.
|
|
///
|
|
/// \param input_name Path to the ATF result file to parse.
|
|
/// \param output_name Path to the generic result file to create.
|
|
/// \param wait_status Exit code of the test program as returned by wait().
|
|
/// \param timed_out Whether the test program timed out or not.
|
|
/// \param [out] success Whether the result should be considered a success or
|
|
/// not; e.g. passed and skipped are successful, but failed is not.
|
|
///
|
|
/// \return An error if the conversion fails; OK otherwise.
|
|
kyua_error_t
|
|
kyua_atf_result_rewrite(const char* input_name, const char* output_name,
|
|
const int wait_status, const bool timed_out,
|
|
bool* success)
|
|
{
|
|
enum atf_status status; int status_arg; char reason[1024];
|
|
status = ATF_STATUS_BROKEN; // Initialize to shut up gcc warning.
|
|
const kyua_error_t error = read_atf_result(input_name, &status, &status_arg,
|
|
reason, sizeof(reason));
|
|
if (kyua_error_is_set(error)) {
|
|
// Errors while parsing the ATF result file can often be attributed to
|
|
// the result file being bogus. Therefore, just mark the test case as
|
|
// broken, because it possibly is.
|
|
status = ATF_STATUS_BROKEN;
|
|
kyua_error_format(error, reason, sizeof(reason));
|
|
kyua_error_free(error);
|
|
}
|
|
|
|
// Errors converting the loaded result to the final result file are not due
|
|
// to a bad test program: they are because our own code fails (e.g. cannot
|
|
// create the output file). These need to be returned to the caller.
|
|
return convert_result(status, status_arg, reason, wait_status, timed_out,
|
|
output_name, success);
|
|
}
|
|
|
|
|
|
/// Creates a result file for a failed cleanup routine.
|
|
///
|
|
/// This function is supposed to be invoked after the body has had a chance to
|
|
/// create its own result file, and only if the body has terminated with a
|
|
/// non-failure result.
|
|
///
|
|
/// \param output_name Path to the generic result file to create.
|
|
/// \param wait_status Exit code of the test program as returned by wait().
|
|
/// \param timed_out Whether the test program timed out or not.
|
|
/// \param [out] success Whether the result should be considered a success or
|
|
/// not; i.e. a clean exit is successful, but anything else is a failure.
|
|
///
|
|
/// \return An error if there is a problem writing the result; OK otherwise.
|
|
kyua_error_t
|
|
kyua_atf_result_cleanup_rewrite(const char* output_name, int wait_status,
|
|
const bool timed_out, bool* success)
|
|
{
|
|
if (timed_out) {
|
|
*success = false;
|
|
return kyua_result_write(
|
|
output_name, KYUA_RESULT_BROKEN, "Test case cleanup timed out");
|
|
} else {
|
|
if (WIFEXITED(wait_status)) {
|
|
if (WEXITSTATUS(wait_status) == EXIT_SUCCESS) {
|
|
*success = true;
|
|
// Reuse the result file created by the body. I.e. avoid
|
|
// creating a new file here.
|
|
return kyua_error_ok();
|
|
} else {
|
|
*success = false;
|
|
return kyua_result_write(
|
|
output_name, KYUA_RESULT_BROKEN, "Test case cleanup exited "
|
|
"with code %d", WEXITSTATUS(wait_status));
|
|
}
|
|
} else {
|
|
*success = false;
|
|
return kyua_result_write(
|
|
output_name, KYUA_RESULT_BROKEN, "Test case cleanup received "
|
|
"signal %d%s", WTERMSIG(wait_status),
|
|
WCOREDUMP(wait_status) ? " (core dumped)" : "");
|
|
}
|
|
}
|
|
}
|