freebsd-skq/contrib/atf/atf-c/check.c

485 lines
12 KiB
C
Raw Normal View History

Import atf-0.21: Released on October 23rd, 2014. * Restored the atf(7) manual page to serve as a reference to all the other manual pages shipped by ATF. * Added the -s flag to atf-sh to support specifying the shell interpreter to be used. * Removed ATF_WORKDIR. The only remaining consumers have been converted to use the standard TMPDIR environment variable. As a benefit, and because Kyua forces the TMPDIR to live within the test case's work directory, any stale files left behind by ATF will be automatically cleaned up. * Documented the environment variables recognized by each component in the relevant manual pages. This information was lost with the atf-config(1) removal. * Added a new "require.diskspace" metadata property to test cases so that they can specify the minimum amount of disk space required for the test to run. * Renamed the atf-{c,c++,sh}-api(3) manual pages to atf-{c,c++,sh}(3) for discoverability purposes. Symbolic links are provided for the time being to still make the old names visible. * Issue #5: Recommend the (expected, actual) idiom for calls to the test macros in the manual pages. * Issue #7: Stopped catching unhandled exceptions in atf-c++ tests. This propagates the crash to the caller, which in turn allows it to obtain proper debugging information. In particular, Kyua should now be able to extract a stacktrace pinpointing the problem. * Issue #8: Fixed atf-c/macros_test:use test failures spotted by the clang that ships with FreeBSD 11.0-CURRENT. * Issue #12: Improved documentation of atf-sh(3) and atf-check(1) by better explaining how they relate to each other. * Issue #14: Stopped setting 'set -e' in atf-sh. This setting was initially added as a way to enable a "strict" mode in the library and to make test cases fail fast when they run unprotected commands. However, doing so in the library is surprising as the responsibility of enabling 'set -e' should be on the user's code. Also, 'set -e' introduces inconsistent behavior on subshells and users do not expect that. * Issue #15: Fixed atf_utils_{fork,wait} to support nested calls. * Issue #16: Fixed test failures (by removing a long-standing hack) on systems that lack \e support in printf(1). * Issue #19: Removed stale references to atf-config and atf-run.
2014-10-30 17:14:07 +00:00
/* Copyright (c) 2008 The NetBSD Foundation, 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:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. 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.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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
Import atf-0.21: Released on October 23rd, 2014. * Restored the atf(7) manual page to serve as a reference to all the other manual pages shipped by ATF. * Added the -s flag to atf-sh to support specifying the shell interpreter to be used. * Removed ATF_WORKDIR. The only remaining consumers have been converted to use the standard TMPDIR environment variable. As a benefit, and because Kyua forces the TMPDIR to live within the test case's work directory, any stale files left behind by ATF will be automatically cleaned up. * Documented the environment variables recognized by each component in the relevant manual pages. This information was lost with the atf-config(1) removal. * Added a new "require.diskspace" metadata property to test cases so that they can specify the minimum amount of disk space required for the test to run. * Renamed the atf-{c,c++,sh}-api(3) manual pages to atf-{c,c++,sh}(3) for discoverability purposes. Symbolic links are provided for the time being to still make the old names visible. * Issue #5: Recommend the (expected, actual) idiom for calls to the test macros in the manual pages. * Issue #7: Stopped catching unhandled exceptions in atf-c++ tests. This propagates the crash to the caller, which in turn allows it to obtain proper debugging information. In particular, Kyua should now be able to extract a stacktrace pinpointing the problem. * Issue #8: Fixed atf-c/macros_test:use test failures spotted by the clang that ships with FreeBSD 11.0-CURRENT. * Issue #12: Improved documentation of atf-sh(3) and atf-check(1) by better explaining how they relate to each other. * Issue #14: Stopped setting 'set -e' in atf-sh. This setting was initially added as a way to enable a "strict" mode in the library and to make test cases fail fast when they run unprotected commands. However, doing so in the library is surprising as the responsibility of enabling 'set -e' should be on the user's code. Also, 'set -e' introduces inconsistent behavior on subshells and users do not expect that. * Issue #15: Fixed atf_utils_{fork,wait} to support nested calls. * Issue #16: Fixed test failures (by removing a long-standing hack) on systems that lack \e support in printf(1). * Issue #19: Removed stale references to atf-config and atf-run.
2014-10-30 17:14:07 +00:00
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
#include "atf-c/check.h"
#include <sys/wait.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "atf-c/build.h"
#include "atf-c/defs.h"
Import atf-0.21: Released on October 23rd, 2014. * Restored the atf(7) manual page to serve as a reference to all the other manual pages shipped by ATF. * Added the -s flag to atf-sh to support specifying the shell interpreter to be used. * Removed ATF_WORKDIR. The only remaining consumers have been converted to use the standard TMPDIR environment variable. As a benefit, and because Kyua forces the TMPDIR to live within the test case's work directory, any stale files left behind by ATF will be automatically cleaned up. * Documented the environment variables recognized by each component in the relevant manual pages. This information was lost with the atf-config(1) removal. * Added a new "require.diskspace" metadata property to test cases so that they can specify the minimum amount of disk space required for the test to run. * Renamed the atf-{c,c++,sh}-api(3) manual pages to atf-{c,c++,sh}(3) for discoverability purposes. Symbolic links are provided for the time being to still make the old names visible. * Issue #5: Recommend the (expected, actual) idiom for calls to the test macros in the manual pages. * Issue #7: Stopped catching unhandled exceptions in atf-c++ tests. This propagates the crash to the caller, which in turn allows it to obtain proper debugging information. In particular, Kyua should now be able to extract a stacktrace pinpointing the problem. * Issue #8: Fixed atf-c/macros_test:use test failures spotted by the clang that ships with FreeBSD 11.0-CURRENT. * Issue #12: Improved documentation of atf-sh(3) and atf-check(1) by better explaining how they relate to each other. * Issue #14: Stopped setting 'set -e' in atf-sh. This setting was initially added as a way to enable a "strict" mode in the library and to make test cases fail fast when they run unprotected commands. However, doing so in the library is surprising as the responsibility of enabling 'set -e' should be on the user's code. Also, 'set -e' introduces inconsistent behavior on subshells and users do not expect that. * Issue #15: Fixed atf_utils_{fork,wait} to support nested calls. * Issue #16: Fixed test failures (by removing a long-standing hack) on systems that lack \e support in printf(1). * Issue #19: Removed stale references to atf-config and atf-run.
2014-10-30 17:14:07 +00:00
#include "atf-c/detail/dynstr.h"
#include "atf-c/detail/env.h"
#include "atf-c/detail/fs.h"
#include "atf-c/detail/list.h"
#include "atf-c/detail/process.h"
#include "atf-c/detail/sanity.h"
#include "atf-c/error.h"
#include "atf-c/utils.h"
/* ---------------------------------------------------------------------
* Auxiliary functions.
* --------------------------------------------------------------------- */
static
atf_error_t
create_tmpdir(atf_fs_path_t *dir)
{
atf_error_t err;
err = atf_fs_path_init_fmt(dir, "%s/check.XXXXXX",
Import atf-0.21: Released on October 23rd, 2014. * Restored the atf(7) manual page to serve as a reference to all the other manual pages shipped by ATF. * Added the -s flag to atf-sh to support specifying the shell interpreter to be used. * Removed ATF_WORKDIR. The only remaining consumers have been converted to use the standard TMPDIR environment variable. As a benefit, and because Kyua forces the TMPDIR to live within the test case's work directory, any stale files left behind by ATF will be automatically cleaned up. * Documented the environment variables recognized by each component in the relevant manual pages. This information was lost with the atf-config(1) removal. * Added a new "require.diskspace" metadata property to test cases so that they can specify the minimum amount of disk space required for the test to run. * Renamed the atf-{c,c++,sh}-api(3) manual pages to atf-{c,c++,sh}(3) for discoverability purposes. Symbolic links are provided for the time being to still make the old names visible. * Issue #5: Recommend the (expected, actual) idiom for calls to the test macros in the manual pages. * Issue #7: Stopped catching unhandled exceptions in atf-c++ tests. This propagates the crash to the caller, which in turn allows it to obtain proper debugging information. In particular, Kyua should now be able to extract a stacktrace pinpointing the problem. * Issue #8: Fixed atf-c/macros_test:use test failures spotted by the clang that ships with FreeBSD 11.0-CURRENT. * Issue #12: Improved documentation of atf-sh(3) and atf-check(1) by better explaining how they relate to each other. * Issue #14: Stopped setting 'set -e' in atf-sh. This setting was initially added as a way to enable a "strict" mode in the library and to make test cases fail fast when they run unprotected commands. However, doing so in the library is surprising as the responsibility of enabling 'set -e' should be on the user's code. Also, 'set -e' introduces inconsistent behavior on subshells and users do not expect that. * Issue #15: Fixed atf_utils_{fork,wait} to support nested calls. * Issue #16: Fixed test failures (by removing a long-standing hack) on systems that lack \e support in printf(1). * Issue #19: Removed stale references to atf-config and atf-run.
2014-10-30 17:14:07 +00:00
atf_env_get_with_default("TMPDIR", "/tmp"));
if (atf_is_error(err))
goto out;
err = atf_fs_mkdtemp(dir);
if (atf_is_error(err)) {
atf_fs_path_fini(dir);
goto out;
}
INV(!atf_is_error(err));
out:
return err;
}
static
void
cleanup_tmpdir(const atf_fs_path_t *dir, const atf_fs_path_t *outfile,
const atf_fs_path_t *errfile)
{
{
atf_error_t err = atf_fs_unlink(outfile);
if (atf_is_error(err)) {
INV(atf_error_is(err, "libc") &&
atf_libc_error_code(err) == ENOENT);
atf_error_free(err);
} else
INV(!atf_is_error(err));
}
{
atf_error_t err = atf_fs_unlink(errfile);
if (atf_is_error(err)) {
INV(atf_error_is(err, "libc") &&
atf_libc_error_code(err) == ENOENT);
atf_error_free(err);
} else
INV(!atf_is_error(err));
}
{
atf_error_t err = atf_fs_rmdir(dir);
INV(!atf_is_error(err));
}
}
static
int
const_execvp(const char *file, const char *const *argv)
{
#define UNCONST(a) ((void *)(unsigned long)(const void *)(a))
return execvp(file, UNCONST(argv));
#undef UNCONST
}
static
atf_error_t
init_sb(const atf_fs_path_t *path, atf_process_stream_t *sb)
{
atf_error_t err;
if (path == NULL)
err = atf_process_stream_init_inherit(sb);
else
err = atf_process_stream_init_redirect_path(sb, path);
return err;
}
static
atf_error_t
init_sbs(const atf_fs_path_t *outfile, atf_process_stream_t *outsb,
const atf_fs_path_t *errfile, atf_process_stream_t *errsb)
{
atf_error_t err;
err = init_sb(outfile, outsb);
if (atf_is_error(err))
goto out;
err = init_sb(errfile, errsb);
if (atf_is_error(err)) {
atf_process_stream_fini(outsb);
goto out;
}
out:
return err;
}
struct exec_data {
const char *const *m_argv;
};
static void exec_child(void *) ATF_DEFS_ATTRIBUTE_NORETURN;
static
void
exec_child(void *v)
{
struct exec_data *ea = v;
const_execvp(ea->m_argv[0], ea->m_argv);
fprintf(stderr, "execvp(%s) failed: %s\n", ea->m_argv[0], strerror(errno));
exit(127);
}
static
atf_error_t
fork_and_wait(const char *const *argv, const atf_fs_path_t *outfile,
const atf_fs_path_t *errfile, atf_process_status_t *status)
{
atf_error_t err;
atf_process_child_t child;
atf_process_stream_t outsb, errsb;
struct exec_data ea = { argv };
err = init_sbs(outfile, &outsb, errfile, &errsb);
if (atf_is_error(err))
goto out;
err = atf_process_fork(&child, exec_child, &outsb, &errsb, &ea);
if (atf_is_error(err))
goto out_sbs;
err = atf_process_child_wait(&child, status);
out_sbs:
atf_process_stream_fini(&errsb);
atf_process_stream_fini(&outsb);
out:
return err;
}
static
void
update_success_from_status(const char *progname,
const atf_process_status_t *status, bool *success)
{
bool s = atf_process_status_exited(status) &&
atf_process_status_exitstatus(status) == EXIT_SUCCESS;
if (atf_process_status_exited(status)) {
if (atf_process_status_exitstatus(status) == EXIT_SUCCESS)
INV(s);
else {
INV(!s);
fprintf(stderr, "%s failed with exit code %d\n", progname,
atf_process_status_exitstatus(status));
}
} else if (atf_process_status_signaled(status)) {
INV(!s);
fprintf(stderr, "%s failed due to signal %d%s\n", progname,
atf_process_status_termsig(status),
atf_process_status_coredump(status) ? " (core dumped)" : "");
} else {
INV(!s);
fprintf(stderr, "%s failed due to unknown reason\n", progname);
}
*success = s;
}
static
atf_error_t
array_to_list(const char *const *a, atf_list_t *l)
{
atf_error_t err;
err = atf_list_init(l);
if (atf_is_error(err))
goto out;
while (*a != NULL) {
char *item = strdup(*a);
if (item == NULL) {
err = atf_no_memory_error();
goto out;
}
err = atf_list_append(l, item, true);
if (atf_is_error(err))
goto out;
a++;
}
out:
return err;
}
static void
print_array(const char *const *array, const char *pfx)
{
const char *const *ptr;
printf("%s", pfx);
for (ptr = array; *ptr != NULL; ptr++)
printf(" %s", *ptr);
printf("\n");
}
static
atf_error_t
check_build_run(const char *const *argv, bool *success)
{
atf_error_t err;
atf_process_status_t status;
print_array(argv, ">");
err = fork_and_wait(argv, NULL, NULL, &status);
if (atf_is_error(err))
goto out;
update_success_from_status(argv[0], &status, success);
atf_process_status_fini(&status);
INV(!atf_is_error(err));
out:
return err;
}
/* ---------------------------------------------------------------------
* The "atf_check_result" type.
* --------------------------------------------------------------------- */
struct atf_check_result_impl {
atf_list_t m_argv;
atf_fs_path_t m_dir;
atf_fs_path_t m_stdout;
atf_fs_path_t m_stderr;
atf_process_status_t m_status;
};
static
atf_error_t
atf_check_result_init(atf_check_result_t *r, const char *const *argv,
const atf_fs_path_t *dir)
{
atf_error_t err;
r->pimpl = malloc(sizeof(struct atf_check_result_impl));
if (r->pimpl == NULL)
return atf_no_memory_error();
err = array_to_list(argv, &r->pimpl->m_argv);
if (atf_is_error(err))
goto out;
err = atf_fs_path_copy(&r->pimpl->m_dir, dir);
if (atf_is_error(err))
goto err_argv;
err = atf_fs_path_init_fmt(&r->pimpl->m_stdout, "%s/stdout",
atf_fs_path_cstring(dir));
if (atf_is_error(err))
goto err_dir;
err = atf_fs_path_init_fmt(&r->pimpl->m_stderr, "%s/stderr",
atf_fs_path_cstring(dir));
if (atf_is_error(err))
goto err_stdout;
INV(!atf_is_error(err));
goto out;
err_stdout:
atf_fs_path_fini(&r->pimpl->m_stdout);
err_dir:
atf_fs_path_fini(&r->pimpl->m_dir);
err_argv:
atf_list_fini(&r->pimpl->m_argv);
out:
return err;
}
void
atf_check_result_fini(atf_check_result_t *r)
{
atf_process_status_fini(&r->pimpl->m_status);
cleanup_tmpdir(&r->pimpl->m_dir, &r->pimpl->m_stdout,
&r->pimpl->m_stderr);
atf_fs_path_fini(&r->pimpl->m_stdout);
atf_fs_path_fini(&r->pimpl->m_stderr);
atf_fs_path_fini(&r->pimpl->m_dir);
atf_list_fini(&r->pimpl->m_argv);
free(r->pimpl);
}
const char *
atf_check_result_stdout(const atf_check_result_t *r)
{
return atf_fs_path_cstring(&r->pimpl->m_stdout);
}
const char *
atf_check_result_stderr(const atf_check_result_t *r)
{
return atf_fs_path_cstring(&r->pimpl->m_stderr);
}
bool
atf_check_result_exited(const atf_check_result_t *r)
{
return atf_process_status_exited(&r->pimpl->m_status);
}
int
atf_check_result_exitcode(const atf_check_result_t *r)
{
return atf_process_status_exitstatus(&r->pimpl->m_status);
}
bool
atf_check_result_signaled(const atf_check_result_t *r)
{
return atf_process_status_signaled(&r->pimpl->m_status);
}
int
atf_check_result_termsig(const atf_check_result_t *r)
{
return atf_process_status_termsig(&r->pimpl->m_status);
}
/* ---------------------------------------------------------------------
* Free functions.
* --------------------------------------------------------------------- */
/* XXX: This function shouldn't be in this module. It messes with stdout
* and stderr, and it provides a very high-end interface. This belongs,
* probably, somewhere related to test cases (such as in the tc module). */
atf_error_t
atf_check_build_c_o(const char *sfile,
const char *ofile,
const char *const optargs[],
bool *success)
{
atf_error_t err;
char **argv;
err = atf_build_c_o(sfile, ofile, optargs, &argv);
if (atf_is_error(err))
goto out;
err = check_build_run((const char *const *)argv, success);
atf_utils_free_charpp(argv);
out:
return err;
}
atf_error_t
atf_check_build_cpp(const char *sfile,
const char *ofile,
const char *const optargs[],
bool *success)
{
atf_error_t err;
char **argv;
err = atf_build_cpp(sfile, ofile, optargs, &argv);
if (atf_is_error(err))
goto out;
err = check_build_run((const char *const *)argv, success);
atf_utils_free_charpp(argv);
out:
return err;
}
atf_error_t
atf_check_build_cxx_o(const char *sfile,
const char *ofile,
const char *const optargs[],
bool *success)
{
atf_error_t err;
char **argv;
err = atf_build_cxx_o(sfile, ofile, optargs, &argv);
if (atf_is_error(err))
goto out;
err = check_build_run((const char *const *)argv, success);
atf_utils_free_charpp(argv);
out:
return err;
}
atf_error_t
atf_check_exec_array(const char *const *argv, atf_check_result_t *r)
{
atf_error_t err;
atf_fs_path_t dir;
err = create_tmpdir(&dir);
if (atf_is_error(err))
goto out;
err = atf_check_result_init(r, argv, &dir);
if (atf_is_error(err)) {
atf_error_t err2 = atf_fs_rmdir(&dir);
INV(!atf_is_error(err2));
goto out;
}
err = fork_and_wait(argv, &r->pimpl->m_stdout, &r->pimpl->m_stderr,
&r->pimpl->m_status);
if (atf_is_error(err)) {
atf_check_result_fini(r);
goto out;
}
INV(!atf_is_error(err));
atf_fs_path_fini(&dir);
out:
return err;
}