The unified diff between revisions [f48a12ad..] and [3ba2af4a..] is displayed below. It can also be downloaded as a raw diff.
This diff has been restricted to the following files: 'tests/atf/atf-c/t_process.c'
#
#
# patch "tests/atf/atf-c/t_process.c"
# from [70c2e3a828220c193cf33aee22cb769e09cb4891]
# to [4e54aa872f22559d6dc2d819ebf3191e2deb6389]
#
============================================================
--- tests/atf/atf-c/t_process.c 70c2e3a828220c193cf33aee22cb769e09cb4891
+++ tests/atf/atf-c/t_process.c 4e54aa872f22559d6dc2d819ebf3191e2deb6389
@@ -27,37 +27,668 @@
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+#include <sys/types.h>
+#include <sys/resource.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
#include <atf-c.h>
+#include "atf-c/defs.h"
+#include "atf-c/io.h"
#include "atf-c/process.h"
+#include "atf-c/sanity.h"
#include "h_lib.h"
+atf_error_t atf_process_status_init(atf_process_status_t *, int);
+
/* ---------------------------------------------------------------------
- * Test cases for the free functions.
+ * Auxiliary functions for testing of 'atf_process_fork'.
* --------------------------------------------------------------------- */
-ATF_TC(fork);
-ATF_TC_HEAD(fork, tc)
+/*
+ * Testing of atf_process_fork is quite messy. We want to be able to test
+ * all the possible combinations of stdout and stderr behavior to ensure
+ * that the streams are manipulated correctly.
+ *
+ * To do this, the do_fork function is a wrapper for atf_process_fork that
+ * issues stream-specific hooks before fork, while the child is running and
+ * after the child terminates. We then provide test cases that just call
+ * do_fork with different hooks.
+ *
+ * The hooks are described by base_stream, and we then have one *_stream
+ * type for ever possible stream behavior.
+ */
+
+enum out_type { stdout_type, stderr_type };
+
+struct base_stream {
+ void (*init)(void *);
+ void (*process)(void *, atf_process_child_t *);
+ void (*fini)(void *);
+
+ atf_process_stream_t m_sb;
+ enum out_type m_type;
+};
+#define BASE_STREAM(ihook, phook, fhook, type) \
+ { .init = ihook, \
+ .process = phook, \
+ .fini = fhook, \
+ .m_type = type }
+
+static
+void
+check_file(const enum out_type type)
{
- atf_tc_set_md_var(tc, "descr", "Tests the atf_process_fork function");
+ switch (type) {
+ case stdout_type:
+ ATF_CHECK(grep_file("stdout", "stdout: msg"));
+ ATF_CHECK(!grep_file("stdout", "stderr: msg"));
+ break;
+ case stderr_type:
+ ATF_CHECK(grep_file("stderr", "stderr: msg"));
+ ATF_CHECK(!grep_file("stderr", "stdout: msg"));
+ break;
+ default:
+ UNREACHABLE;
+ }
}
-ATF_TC_BODY(fork, tc)
+
+struct capture_stream {
+ struct base_stream m_base;
+
+ atf_dynstr_t m_msg;
+};
+#define CAPTURE_STREAM(type) \
+ { .m_base = BASE_STREAM(capture_stream_init, \
+ capture_stream_process, \
+ capture_stream_fini, \
+ type) }
+
+static
+void
+capture_stream_init(void *v)
{
- atf_tc_skip("Unimplemented test case; process API not yet decided");
+ struct capture_stream *s = v;
+
+ RE(atf_process_stream_init_capture(&s->m_base.m_sb));
+ RE(atf_dynstr_init(&s->m_msg));
}
-ATF_TC(system);
-ATF_TC_HEAD(system, tc)
+static
+void
+capture_stream_process(void *v, atf_process_child_t *c)
{
- atf_tc_set_md_var(tc, "descr", "Tests the atf_process_system function");
+ struct capture_stream *s = v;
+
+ bool eof;
+ switch (s->m_base.m_type) {
+ case stdout_type:
+ RE(atf_io_readline(atf_process_child_stdout(c),
+ &s->m_msg, &eof));
+ ATF_CHECK(grep_string(&s->m_msg, "stdout: msg"));
+ ATF_CHECK(!grep_string(&s->m_msg, "stderr: msg"));
+ break;
+ case stderr_type:
+ RE(atf_io_readline(atf_process_child_stderr(c),
+ &s->m_msg, &eof));
+ ATF_CHECK(!grep_string(&s->m_msg, "stdout: msg"));
+ ATF_CHECK(grep_string(&s->m_msg, "stderr: msg"));
+ break;
+ default:
+ UNREACHABLE;
+ }
}
-ATF_TC_BODY(system, tc)
+
+static
+void
+capture_stream_fini(void *v)
{
- atf_tc_skip("Unimplemented test case; process API not yet decided");
+ struct capture_stream *s = v;
+
+ atf_dynstr_fini(&s->m_msg);
+ atf_process_stream_fini(&s->m_base.m_sb);
}
+struct inherit_stream {
+ struct base_stream m_base;
+ int m_fd;
+
+ int m_old_fd;
+};
+#define INHERIT_STREAM(type) \
+ { .m_base = BASE_STREAM(inherit_stream_init, \
+ NULL, \
+ inherit_stream_fini, \
+ type) }
+
+static
+void
+inherit_stream_init(void *v)
+{
+ struct inherit_stream *s = v;
+ const char *name;
+
+ RE(atf_process_stream_init_inherit(&s->m_base.m_sb));
+
+ switch (s->m_base.m_type) {
+ case stdout_type:
+ s->m_fd = STDOUT_FILENO;
+ name = "stdout";
+ break;
+ case stderr_type:
+ s->m_fd = STDERR_FILENO;
+ name = "stderr";
+ break;
+ default:
+ UNREACHABLE;
+ name = NULL;
+ }
+
+ s->m_old_fd = dup(s->m_fd);
+ ATF_REQUIRE(s->m_old_fd != -1);
+ ATF_REQUIRE(close(s->m_fd) != -1);
+ ATF_REQUIRE_EQ(open(name, O_WRONLY | O_CREAT | O_TRUNC, 0644),
+ s->m_fd);
+}
+
+static
+void
+inherit_stream_fini(void *v)
+{
+ struct inherit_stream *s = v;
+
+ ATF_REQUIRE(dup2(s->m_old_fd, s->m_fd) != -1);
+ ATF_REQUIRE(close(s->m_old_fd) != -1);
+
+ atf_process_stream_fini(&s->m_base.m_sb);
+
+ check_file(s->m_base.m_type);
+}
+
+struct redirect_fd_stream {
+ struct base_stream m_base;
+
+ int m_fd;
+};
+#define REDIRECT_FD_STREAM(type) \
+ { .m_base = BASE_STREAM(redirect_fd_stream_init, \
+ NULL, \
+ redirect_fd_stream_fini, \
+ type) }
+
+static
+void
+redirect_fd_stream_init(void *v)
+{
+ struct redirect_fd_stream *s = v;
+
+ switch (s->m_base.m_type) {
+ case stdout_type:
+ s->m_fd = open("stdout", O_WRONLY | O_CREAT | O_TRUNC, 0644);
+ break;
+ case stderr_type:
+ s->m_fd = open("stderr", O_WRONLY | O_CREAT | O_TRUNC, 0644);
+ break;
+ default:
+ UNREACHABLE;
+ }
+ ATF_REQUIRE(s->m_fd != -1);
+
+ RE(atf_process_stream_init_redirect_fd(&s->m_base.m_sb, s->m_fd));
+}
+
+static
+void
+redirect_fd_stream_fini(void *v)
+{
+ struct redirect_fd_stream *s = v;
+
+ ATF_REQUIRE(close(s->m_fd) != -1);
+
+ atf_process_stream_fini(&s->m_base.m_sb);
+
+ check_file(s->m_base.m_type);
+}
+
+struct redirect_path_stream {
+ struct base_stream m_base;
+
+ atf_fs_path_t m_path;
+};
+#define REDIRECT_PATH_STREAM(type) \
+ { .m_base = BASE_STREAM(redirect_path_stream_init, \
+ NULL, \
+ redirect_path_stream_fini, \
+ type) }
+
+static
+void
+redirect_path_stream_init(void *v)
+{
+ struct redirect_path_stream *s = v;
+
+ switch (s->m_base.m_type) {
+ case stdout_type:
+ RE(atf_fs_path_init_fmt(&s->m_path, "stdout"));
+ break;
+ case stderr_type:
+ RE(atf_fs_path_init_fmt(&s->m_path, "stderr"));
+ break;
+ default:
+ UNREACHABLE;
+ }
+
+ RE(atf_process_stream_init_redirect_path(&s->m_base.m_sb, &s->m_path));
+}
+
+static
+void
+redirect_path_stream_fini(void *v)
+{
+ struct redirect_path_stream *s = v;
+
+ atf_process_stream_fini(&s->m_base.m_sb);
+
+ atf_fs_path_fini(&s->m_path);
+
+ check_file(s->m_base.m_type);
+}
+
+static
+atf_error_t
+child_print(const void *v)
+{
+ const char *msg = v;
+
+ fprintf(stdout, "stdout: %s\n", msg);
+ fprintf(stderr, "stderr: %s\n", msg);
+
+ return atf_no_error();
+}
+
+static
+void
+do_fork(const struct base_stream *outfs, void *out,
+ const struct base_stream *errfs, void *err)
+{
+ atf_process_child_t child;
+ atf_process_status_t status;
+
+ outfs->init(out);
+ errfs->init(err);
+
+ RE(atf_process_fork(&child, child_print,
+ &outfs->m_sb, &errfs->m_sb, "msg"));
+ if (outfs->process != NULL)
+ outfs->process(out, &child);
+ if (errfs->process != NULL)
+ errfs->process(err, &child);
+ RE(atf_process_child_wait(&child, &status));
+
+ outfs->fini(out);
+ errfs->fini(err);
+
+ atf_process_status_fini(&status);
+}
+
/* ---------------------------------------------------------------------
+ * Test cases for the "stream" type.
+ * --------------------------------------------------------------------- */
+
+ATF_TC(stream_init_capture);
+ATF_TC_HEAD(stream_init_capture, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the "
+ "atf_process_stream_init_capture function");
+}
+ATF_TC_BODY(stream_init_capture, tc)
+{
+ atf_process_stream_t sb;
+
+ RE(atf_process_stream_init_capture(&sb));
+
+ ATF_CHECK_EQ(atf_process_stream_type(&sb),
+ atf_process_stream_type_capture);
+
+ atf_process_stream_fini(&sb);
+}
+
+ATF_TC(stream_init_inherit);
+ATF_TC_HEAD(stream_init_inherit, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the "
+ "atf_process_stream_init_inherit function");
+}
+ATF_TC_BODY(stream_init_inherit, tc)
+{
+ atf_process_stream_t sb;
+
+ RE(atf_process_stream_init_inherit(&sb));
+
+ ATF_CHECK_EQ(atf_process_stream_type(&sb),
+ atf_process_stream_type_inherit);
+
+ atf_process_stream_fini(&sb);
+}
+
+ATF_TC(stream_init_redirect_fd);
+ATF_TC_HEAD(stream_init_redirect_fd, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the "
+ "atf_process_stream_init_redirect_fd function");
+}
+ATF_TC_BODY(stream_init_redirect_fd, tc)
+{
+ atf_process_stream_t sb;
+
+ RE(atf_process_stream_init_redirect_fd(&sb, 1));
+
+ ATF_CHECK_EQ(atf_process_stream_type(&sb),
+ atf_process_stream_type_redirect_fd);
+
+ atf_process_stream_fini(&sb);
+}
+
+ATF_TC(stream_init_redirect_path);
+ATF_TC_HEAD(stream_init_redirect_path, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the "
+ "atf_process_stream_init_redirect_path function");
+}
+ATF_TC_BODY(stream_init_redirect_path, tc)
+{
+ atf_process_stream_t sb;
+ atf_fs_path_t path;
+
+ RE(atf_fs_path_init_fmt(&path, "foo"));
+ RE(atf_process_stream_init_redirect_path(&sb, &path));
+
+ ATF_CHECK_EQ(atf_process_stream_type(&sb),
+ atf_process_stream_type_redirect_path);
+
+ atf_process_stream_fini(&sb);
+ atf_fs_path_fini(&path);
+}
+
+/* ---------------------------------------------------------------------
+ * Test cases for the "status" type.
+ * --------------------------------------------------------------------- */
+
+static void child_exit_success(void) ATF_DEFS_ATTRIBUTE_NORETURN;
+static void child_exit_failure(void) ATF_DEFS_ATTRIBUTE_NORETURN;
+static void child_sigkill(void) ATF_DEFS_ATTRIBUTE_NORETURN;
+static void child_sigquit(void) ATF_DEFS_ATTRIBUTE_NORETURN;
+static void child_sigterm(void) ATF_DEFS_ATTRIBUTE_NORETURN;
+
+void
+child_exit_success(void)
+{
+ exit(EXIT_SUCCESS);
+}
+
+void
+child_exit_failure(void)
+{
+ exit(EXIT_FAILURE);
+}
+
+void
+child_sigkill(void)
+{
+ kill(getpid(), SIGKILL);
+ abort();
+}
+
+void
+child_sigquit(void)
+{
+ kill(getpid(), SIGQUIT);
+ abort();
+}
+
+void
+child_sigterm(void)
+{
+ kill(getpid(), SIGTERM);
+ abort();
+}
+
+static
+int
+fork_and_wait_child(void (*child_func)(void))
+{
+ pid_t pid;
+ int status;
+
+ pid = fork();
+ ATF_REQUIRE(pid != -1);
+ if (pid == 0) {
+ status = 0; /* Silence compiler warnings */
+ child_func();
+ UNREACHABLE;
+ abort();
+ } else {
+ ATF_REQUIRE(waitpid(pid, &status, 0) != 0);
+ }
+
+ return status;
+}
+
+ATF_TC(status_exited);
+ATF_TC_HEAD(status_exited, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the status type for processes "
+ "that exit cleanly");
+}
+ATF_TC_BODY(status_exited, tc)
+{
+ {
+ const int rawstatus = fork_and_wait_child(child_exit_success);
+ atf_process_status_t s;
+ RE(atf_process_status_init(&s, rawstatus));
+ ATF_CHECK(atf_process_status_exited(&s));
+ ATF_CHECK_EQ(atf_process_status_exitstatus(&s), EXIT_SUCCESS);
+ ATF_CHECK(!atf_process_status_signaled(&s));
+ atf_process_status_fini(&s);
+ }
+
+ {
+ const int rawstatus = fork_and_wait_child(child_exit_failure);
+ atf_process_status_t s;
+ RE(atf_process_status_init(&s, rawstatus));
+ ATF_CHECK(atf_process_status_exited(&s));
+ ATF_CHECK_EQ(atf_process_status_exitstatus(&s), EXIT_FAILURE);
+ ATF_CHECK(!atf_process_status_signaled(&s));
+ atf_process_status_fini(&s);
+ }
+}
+
+ATF_TC(status_signaled);
+ATF_TC_HEAD(status_signaled, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the status type for processes "
+ "that end due to a signal");
+}
+ATF_TC_BODY(status_signaled, tc)
+{
+ {
+ const int rawstatus = fork_and_wait_child(child_sigkill);
+ atf_process_status_t s;
+ RE(atf_process_status_init(&s, rawstatus));
+ ATF_CHECK(!atf_process_status_exited(&s));
+ ATF_CHECK(atf_process_status_signaled(&s));
+ ATF_CHECK_EQ(atf_process_status_termsig(&s), SIGKILL);
+ ATF_CHECK(!atf_process_status_coredump(&s));
+ atf_process_status_fini(&s);
+ }
+
+ {
+ const int rawstatus = fork_and_wait_child(child_sigterm);
+ atf_process_status_t s;
+ RE(atf_process_status_init(&s, rawstatus));
+ ATF_CHECK(!atf_process_status_exited(&s));
+ ATF_CHECK(atf_process_status_signaled(&s));
+ ATF_CHECK_EQ(atf_process_status_termsig(&s), SIGTERM);
+ ATF_CHECK(!atf_process_status_coredump(&s));
+ atf_process_status_fini(&s);
+ }
+}
+
+ATF_TC(status_coredump);
+ATF_TC_HEAD(status_coredump, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the status type for processes "
+ "that crash");
+}
+ATF_TC_BODY(status_coredump, tc)
+{
+ struct rlimit rl;
+ rl.rlim_cur = RLIM_INFINITY;
+ rl.rlim_max = RLIM_INFINITY;
+ if (setrlimit(RLIMIT_CORE, &rl) == -1)
+ atf_tc_skip("Cannot unlimit the core file size; check limits "
+ "manually");
+
+ const int rawstatus = fork_and_wait_child(child_sigquit);
+ atf_process_status_t s;
+ RE(atf_process_status_init(&s, rawstatus));
+ ATF_CHECK(!atf_process_status_exited(&s));
+ ATF_CHECK(atf_process_status_signaled(&s));
+ ATF_CHECK_EQ(atf_process_status_termsig(&s), SIGQUIT);
+ ATF_CHECK(atf_process_status_coredump(&s));
+ atf_process_status_fini(&s);
+}
+
+/* ---------------------------------------------------------------------
+ * Tests cases for the free functions.
+ * --------------------------------------------------------------------- */
+
+static const int exit_v_null = 1;
+static const int exit_v_notnull = 2;
+
+static
+atf_error_t
+child_cookie(const void *v)
+{
+ if (v == NULL)
+ exit(exit_v_null);
+ else
+ exit(exit_v_notnull);
+
+ UNREACHABLE;
+ return atf_no_error();
+}
+
+ATF_TC(fork_cookie);
+ATF_TC_HEAD(fork_cookie, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests forking a child, with "
+ "a null and non-null data cookie");
+}
+ATF_TC_BODY(fork_cookie, tc)
+{
+ atf_process_stream_t outsb, errsb;
+
+ RE(atf_process_stream_init_inherit(&outsb));
+ RE(atf_process_stream_init_inherit(&errsb));
+
+ {
+ atf_process_child_t child;
+ atf_process_status_t status;
+
+ RE(atf_process_fork(&child, child_cookie, &outsb, &errsb, NULL));
+ RE(atf_process_child_wait(&child, &status));
+
+ ATF_CHECK(atf_process_status_exited(&status));
+ ATF_CHECK_EQ(atf_process_status_exitstatus(&status), exit_v_null);
+
+ atf_process_status_fini(&status);
+ }
+
+ {
+ atf_process_child_t child;
+ atf_process_status_t status;
+
+ RE(atf_process_fork(&child, child_cookie, &outsb, &errsb, "cookie"));
+ RE(atf_process_child_wait(&child, &status));
+
+ ATF_CHECK(atf_process_status_exited(&status));
+ ATF_CHECK_EQ(atf_process_status_exitstatus(&status), exit_v_notnull);
+
+ atf_process_status_fini(&status);
+ }
+
+ atf_process_stream_fini(&errsb);
+ atf_process_stream_fini(&outsb);
+}
+
+#define TC_FORK_STREAMS(outlc, outuc, errlc, erruc) \
+ ATF_TC(fork_out ## outlc ## _err ## errlc); \
+ ATF_TC_HEAD(fork_out ## outlc ## _err ## errlc, tc) \
+ { \
+ atf_tc_set_md_var(tc, "descr", "Tests forking a child, with " \
+ "stdout " #outuc " and stderr " #erruc); \
+ } \
+ ATF_TC_BODY(fork_out ## outlc ## _err ## errlc, tc) \
+ { \
+ struct outlc ## _stream out = outuc ## _STREAM(stdout_type); \
+ struct errlc ## _stream err = erruc ## _STREAM(stderr_type); \
+ do_fork(&out.m_base, &out, &err.m_base, &err); \
+ }
+
+TC_FORK_STREAMS(capture, CAPTURE, inherit, INHERIT);
+
+ATF_TC(fork_outinherit_errinherit);
+ATF_TC_HEAD(fork_outinherit_errinherit, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests forking a child, with "
+ "inherited stdout and inherited stderr");
+}
+ATF_TC_BODY(fork_outinherit_errinherit, tc)
+{
+ struct inherit_stream out = INHERIT_STREAM(stdout_type);
+ struct inherit_stream err = INHERIT_STREAM(stderr_type);
+
+ do_fork(&out.m_base, &out, &err.m_base, &err);
+}
+
+ATF_TC(fork_outredirectfd_errinherit);
+ATF_TC_HEAD(fork_outredirectfd_errinherit, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests forking a child, with "
+ "inherited stdout and inherited stderr");
+}
+ATF_TC_BODY(fork_outredirectfd_errinherit, tc)
+{
+ struct redirect_fd_stream out = REDIRECT_FD_STREAM(stdout_type);
+ struct inherit_stream err = INHERIT_STREAM(stderr_type);
+
+ do_fork(&out.m_base, &out, &err.m_base, &err);
+}
+
+ATF_TC(fork_outredirectpath_errinherit);
+ATF_TC_HEAD(fork_outredirectpath_errinherit, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests forking a child, with "
+ "inherited stdout and inherited stderr");
+}
+ATF_TC_BODY(fork_outredirectpath_errinherit, tc)
+{
+ struct redirect_path_stream out = REDIRECT_PATH_STREAM(stdout_type);
+ struct inherit_stream err = INHERIT_STREAM(stderr_type);
+
+ do_fork(&out.m_base, &out, &err.m_base, &err);
+}
+
+/* ---------------------------------------------------------------------
* Tests cases for the header file.
* --------------------------------------------------------------------- */
@@ -69,9 +700,23 @@ ATF_TP_ADD_TCS(tp)
ATF_TP_ADD_TCS(tp)
{
+ /* Add the tests for the "stream" type. */
+ ATF_TP_ADD_TC(tp, stream_init_capture);
+ ATF_TP_ADD_TC(tp, stream_init_inherit);
+ ATF_TP_ADD_TC(tp, stream_init_redirect_fd);
+ ATF_TP_ADD_TC(tp, stream_init_redirect_path);
+
+ /* Add the tests for the "status" type. */
+ ATF_TP_ADD_TC(tp, status_exited);
+ ATF_TP_ADD_TC(tp, status_signaled);
+ ATF_TP_ADD_TC(tp, status_coredump);
+
/* Add the tests for the free functions. */
- ATF_TP_ADD_TC(tp, fork);
- ATF_TP_ADD_TC(tp, system);
+ ATF_TP_ADD_TC(tp, fork_cookie);
+ ATF_TP_ADD_TC(tp, fork_outcapture_errinherit);
+ ATF_TP_ADD_TC(tp, fork_outinherit_errinherit);
+ ATF_TP_ADD_TC(tp, fork_outredirectfd_errinherit);
+ ATF_TP_ADD_TC(tp, fork_outredirectpath_errinherit);
/* Add the test cases for the header file. */
ATF_TP_ADD_TC(tp, include);