Пример #1
0
Файл: env.c Проект: namore/kyua
/// Sets an environment variable.
///
/// \param name Name of the environment variable to set.
/// \param value Value to be set.
///
/// \return An error object.
kyua_error_t
kyua_env_set(const char* name, const char* value)
{
    kyua_error_t error;

#if defined(HAVE_SETENV)
    if (setenv(name, value, 1) == -1)
        error = kyua_libc_error_new(
            errno, "Failed to set environment variable %s to %s", name, value);
    else
        error = kyua_error_ok();
#elif defined(HAVE_PUTENV)
    const size_t length = strlen(name) + strlen(value) + 2;
    char* buffer = (char*)malloc(length);
    if (buffer == NULL)
        error = kyua_oom_error_new();
    else {
        const size_t printed_length = snprintf(buffer, length, "%s=%s", name,
                                               value);
        assert(length == printed_length + 1);
        if (putenv(buffer) == -1) {
            error = kyua_libc_error_new(
                errno, "Failed to set environment variable %s to %s",
                name, value);
            free(buffer);
        } else
            error = kyua_error_ok();
    }
#else
#   error "Don't know how to set an environment variable."
#endif

    return error;
}
Пример #2
0
/// Generates a string from a format string and its replacements.
///
/// \param [out] output Pointer to the dynamically-allocated string with the
///     result of the operation.  The caller must release this with free().
/// \param format The formatting string.
/// \param ap List of to apply to the formatting string.
///
/// \return OK if the string could be formatted; an error otherwise.
kyua_error_t
kyua_text_vprintf(char** output, const char* format, va_list ap)
{
    va_list ap2;

    va_copy(ap2, ap);
    const int length = calculate_length(format, ap2);
    va_end(ap2);
    if (length < 0)
        return kyua_libc_error_new(errno, "Could not calculate length of "
                                   "string with format '%s'", format);

    char* buffer = (char*)malloc(length + 1);
    if (buffer == NULL)
        return kyua_oom_error_new();

    va_copy(ap2, ap);
    const int printed_length = vsnprintf(buffer, length + 1, format, ap2);
    va_end(ap2);
    assert(printed_length == length);
    if (printed_length < 0) {
        free(buffer);
        return kyua_libc_error_new(errno, "Could generate string with format "
                                   "'%s'", format);
    }

    *output = buffer;
    return kyua_error_ok();
}
Пример #3
0
/// 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();
}
Пример #4
0
/// Rewrites the test cases list from the input to the output.
///
/// \param [in,out] input Stream from which to read the test program's test
///     cases list.  The current location must be after the header and at the
///     first identifier (if any).
/// \param [out] output Stream to which to write the generic list.
///
/// \return An error object.
static kyua_error_t
parse_tests(FILE* input, FILE* output)
{
    char line[512];  // It's ugly to have a limit, but it's easier this way.

    if (fgets_no_newline(line, sizeof(line), input) == NULL) {
        return fgets_error(input, "Empty test cases list");
    }

    kyua_error_t error;

    do {
        char* key = NULL; char* value = NULL;
        error = parse_property(line, &key, &value);
        if (kyua_error_is_set(error))
            break;

        if (strcmp(key, "ident") == 0) {
            error = parse_test_case(input, output, value);
        } else {
            error = kyua_generic_error_new("Expected ident property, got %s",
                                           key);
        }
    } while (!kyua_error_is_set(error) &&
             fgets_no_newline(line, sizeof(line), input) != NULL);

    if (!kyua_error_is_set(error)) {
        if (ferror(input))
            error = kyua_libc_error_new(errno, "fgets failed");
        else
            assert(feof(input));
    }

    return error;
}
Пример #5
0
/// 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;
}
Пример #6
0
ATF_TC_BODY(libc_error_format__args, tc)
{
    kyua_error_t error = kyua_libc_error_new(EPERM, "%s message %d", "A", 123);
    char buffer[1024];
    kyua_error_format(error, buffer, sizeof(buffer));
    ATF_REQUIRE(strstr(buffer, strerror(EPERM)) != NULL);
    ATF_REQUIRE(strstr(buffer, "A message 123") != NULL);
    kyua_error_free(error);
}
Пример #7
0
ATF_TC_BODY(libc_error_format__plain, tc)
{
    kyua_error_t error = kyua_libc_error_new(ENOMEM, "Test message");
    char buffer[1024];
    kyua_error_format(error, buffer, sizeof(buffer));
    ATF_REQUIRE(strstr(buffer, strerror(ENOMEM)) != NULL);
    ATF_REQUIRE(strstr(buffer, "Test message") != NULL);
    kyua_error_free(error);
}
Пример #8
0
/// Generates an error for the case where fgets() returns NULL.
///
/// \param input Stream on which fgets() returned an error.
/// \param message Error message.
///
/// \return An error object with the error message and any relevant details.
static kyua_error_t
fgets_error(FILE* input, const char* message)
{
    if (feof(input)) {
        return kyua_generic_error_new("%s: unexpected EOF", message);
    } else {
        assert(ferror(input));
        return kyua_libc_error_new(errno, "%s", message);
    }
}
Пример #9
0
Файл: env.c Проект: namore/kyua
/// Unsets an environment variable.
///
/// \param name Name of the environment variable to unset.
///
/// \return An error object.
kyua_error_t
kyua_env_unset(const char* name)
{
#if defined(HAVE_UNSETENV)
    if (unsetenv(name) == -1)
        return kyua_libc_error_new(
            errno, "Failed to unset environment variable %s", name);
    else
        return kyua_error_ok();
#elif defined(HAVE_PUTENV)
    return kyua_env_set(name, "");
#else
#   error "Don't know how to unset an environment variable."
#endif
}
Пример #10
0
/// Creates a file with the result of the test.
///
/// \param path Path to the file to be created.
/// \param type The type of the result.
///
/// \return An error object.
kyua_error_t
kyua_result_write(const char* path, const enum kyua_result_type_t type)
{
    assert(type == KYUA_RESULT_PASSED);

    FILE* file = fopen(path, "w");
    if (file == NULL)
        return kyua_libc_error_new(errno, "Cannot create result file '%s'",
                                   path);

    fprintf(file, "%s\n", type_to_name[type]);

    fclose(file);

    return kyua_error_ok();
}
Пример #11
0
/// Dumps the contents of the input file into the output.
///
/// \param input File from which to read.
/// \param output File to which to write.
///
/// \return An error if there is a problem.
static kyua_error_t
dump_file(FILE* input, FILE* output)
{
    char buffer[1024];
    size_t length;

    while ((length = fread(buffer, 1, sizeof(buffer), input)) > 0) {
        if (fwrite(buffer, 1, length, output) != length) {
            return kyua_generic_error_new("Failed to write to output file");
        }
    }
    if (ferror(input))
        return kyua_libc_error_new(errno, "Failed to read test cases list");

    return kyua_error_ok();
}
Пример #12
0
/// Reads an ATF test cases list and prints a Kyua definition.
///
/// \param fd A file descriptor from which to read the test cases list of a test
///     program.  Should be connected to the stdout of the latter.  This
///     function grabs ownership of the descriptor and releases it in all cases.
/// \param [in,out] output File to which to write the Kyua definition.
///
/// \return OK if the parsing succeeds; an error otherwise.  Note that, if there
/// is an error, the output may not be consistent and should not be used.
kyua_error_t
atf_list_parse(const int fd, FILE* output)
{
    kyua_error_t error;

    FILE* input = fdopen(fd, "r");
    if (input == NULL) {
        error = kyua_libc_error_new(errno, "fdopen(%d) failed", fd);
        close(fd);
    } else {
        error = parse_header(input);
        if (!kyua_error_is_set(error)) {
            error = parse_tests(input, output);
        }
        fclose(input);
    }

    return error;
}
Пример #13
0
/// Creates a file within the work directory.
///
/// \param work_directory Path to the work directory.
/// \param name Name of the file to create.
/// \param mode Mode of the file, as specified by fopen(3).
/// \param [out] file Pointer to the created stream.
///
/// \return An error if there is a problem.
static kyua_error_t
create_file_in_work_directory(const char* work_directory, const char* name,
                              const char* mode, FILE** file)
{
    char* path;
    kyua_error_t error = kyua_fs_concat(&path, work_directory, name, NULL);
    if (kyua_error_is_set(error))
        goto out;

    FILE* tmp_file = fopen(path, mode);
    if (tmp_file == NULL) {
        error = kyua_libc_error_new(errno, "Failed to create %s", path);
        goto out_path;
    }

    *file = tmp_file;

    assert(!kyua_error_is_set(error));
out_path:
    free(path);
out:
    return error;
}
Пример #14
0
/// Creates a file with the result of the test.
///
/// \param path Path to the file to be created.
/// \param type The type of the result.
/// \param reason Textual explanation of the reason behind the result.  Must be
///     NULL with KYUA_RESULT_PASSED, or else non-NULL.
///
/// \return An error object.
kyua_error_t
kyua_result_write_with_reason(const char* path,
                              const enum kyua_result_type_t type,
                              const char* reason, ...)
{
    assert(type != KYUA_RESULT_PASSED);
    assert(reason != NULL);

    FILE* file = fopen(path, "w");
    if (file == NULL)
        return kyua_libc_error_new(errno, "Cannot create result file '%s'",
                                   path);

    char buffer[1024];
    va_list ap;
    va_start(ap, reason);
    (void)vsnprintf(buffer, sizeof(buffer), reason, ap);
    va_end(ap);
    fprintf(file, "%s: %s\n", type_to_name[type], buffer);

    fclose(file);

    return kyua_error_ok();
}
Пример #15
0
ATF_TC_BODY(libc_error_errno, tc)
{
    kyua_error_t error = kyua_libc_error_new(EPERM, "Doesn't matter");
    ATF_REQUIRE_EQ(EPERM, kyua_libc_error_errno(error));
    kyua_error_free(error);
}
Пример #16
0
ATF_TC_BODY(libc_error_type, tc)
{
    kyua_error_t error = kyua_libc_error_new(ENOMEM, "Nothing");
    ATF_REQUIRE(kyua_error_is_type(error, kyua_libc_error_type));
    kyua_error_free(error);
}
Пример #17
0
/// Lists the test cases in a test program.
///
/// \param test_program Path to the test program for which to list the test
///     cases.  Should be absolute.
/// \param run_params Execution parameters to configure the test process.
///
/// \return An error if the listing fails; OK otherwise.
static kyua_error_t
list_test_cases(const char* test_program, const kyua_run_params_t* run_params)
{
    kyua_error_t error;

    char* work_directory;
    error = kyua_run_work_directory_enter(WORKDIR_TEMPLATE,
                                          run_params->unprivileged_user,
                                          run_params->unprivileged_group,
                                          &work_directory);
    if (kyua_error_is_set(error))
        goto out;
    kyua_run_params_t real_run_params = *run_params;
    real_run_params.work_directory = work_directory;

    int stdout_fds[2];
    if (pipe(stdout_fds) == -1) {
        error = kyua_libc_error_new(errno, "pipe failed");
        goto out_work_directory;
    }

    pid_t pid;
    error = kyua_run_fork(&real_run_params, &pid);
    if (!kyua_error_is_set(error) && pid == 0) {
        run_list(test_program, stdout_fds);
    }
    assert(pid != -1 && pid != 0);
    if (kyua_error_is_set(error))
        goto out_stdout_fds;

    FILE* tmp_output = NULL;  // Initialize to shut up gcc warning.
    error = create_file_in_work_directory(real_run_params.work_directory,
                                          "list.txt", "w+", &tmp_output);
    if (kyua_error_is_set(error))
        goto out_stdout_fds;

    close(stdout_fds[1]); stdout_fds[1] = -1;
    kyua_error_t parse_error = atf_list_parse(stdout_fds[0], tmp_output);
    stdout_fds[0] = -1;  // Guaranteed closed by atf_list_parse.
    // Delay reporting of parse errors to later.  If we detect a problem while
    // waiting for the test program, we know that the parsing has most likely
    // failed and therefore the error with the program is more important for
    // reporting purposes.

    int status; bool timed_out;
    error = kyua_run_wait(pid, &status, &timed_out);
    if (kyua_error_is_set(error))
        goto out_tmp_output;
    if (!WIFEXITED(status) || WEXITSTATUS(status) != EXIT_SUCCESS) {
        error = kyua_generic_error_new("Test program list did not return "
                                       "success");
        goto out_tmp_output;
    }

    error = kyua_error_subsume(error, parse_error);
    if (!kyua_error_is_set(error)) {
        rewind(tmp_output);
        error = dump_file(tmp_output, stdout);
    }

out_tmp_output:
    fclose(tmp_output);
out_stdout_fds:
    if (stdout_fds[0] != -1)
        close(stdout_fds[0]);
    if (stdout_fds[1] != -1)
        close(stdout_fds[1]);
out_work_directory:
    error = kyua_error_subsume(error,
        kyua_run_work_directory_leave(&work_directory));
out:
    return error;
}