The unified diff between revisions [f48a12ad..] and [3ba2af4a..] is displayed below. It can also be downloaded as a raw diff.
#
#
# patch "atf-c/check.c"
# from [351dd5ace1742377ed11540e62ebd926fb8aa884]
# to [ef0e40037178101675afe9ba3769897a24bbce19]
#
# patch "atf-c/process.c"
# from [951565dc23d58942899eb2c68a9e9b9d1b48928e]
# to [0abd2229aaa6a55aa99fb4ac23fd7f285862e12e]
#
# patch "atf-c/process.h"
# from [8265e8436a6c94dcf2e843ba31df7d99c3772b82]
# to [038a4ff06acb2132257bdabc9c3b387ce50ecb4c]
#
# patch "atf-c/tc.c"
# from [8990a94abfa2417ef6f1f4ec78556cc3f35353af]
# to [bcb9833d27cdd68cff6f1b58c0018f7f30c79866]
#
# patch "atf-c++/process.cpp"
# from [1c1397ab2ab18e0b6fcf8d6fbe7f2c3e991b9bb7]
# to [aaec03f9fdb8cd8769b0c897e7498e07089add79]
#
# patch "tests/atf/atf-c/t_process.c"
# from [70c2e3a828220c193cf33aee22cb769e09cb4891]
# to [4e54aa872f22559d6dc2d819ebf3191e2deb6389]
#
# patch "tests/atf/atf-c/t_sanity.c"
# from [f198b24a03b91d99d034dd4e6c8b629b56e470a8]
# to [f357d5b3f5ad0742a725b18970a3e60d40549bc5]
#
# patch "tests/atf/atf-c/t_signals.c"
# from [5db33b1465a5d1185ecf442e6cfe8f5d24a2eb52]
# to [de2770b573a42267cff064efa928e170bc154dc0]
#
============================================================
--- atf-c/check.c 351dd5ace1742377ed11540e62ebd926fb8aa884
+++ atf-c/check.c ef0e40037178101675afe9ba3769897a24bbce19
@@ -119,7 +119,7 @@ fork_and_wait(const char *const *argv, i
int status;
pid_t pid;
- err = atf_process_fork(&pid);
+ err = atf_process_oldfork(&pid);
if (atf_is_error(err))
goto out;
============================================================
--- atf-c/process.c 951565dc23d58942899eb2c68a9e9b9d1b48928e
+++ atf-c/process.c 0abd2229aaa6a55aa99fb4ac23fd7f285862e12e
@@ -31,14 +31,20 @@
#include <sys/wait.h>
#include <errno.h>
+#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
+#include "atf-c/defs.h"
#include "atf-c/error.h"
#include "atf-c/process.h"
#include "atf-c/sanity.h"
+/* This prototype is not in the header file because this is a private
+ * function; however, we need to access it during testing. */
+atf_error_t atf_process_status_init(atf_process_status_t *, int);
+
/* ---------------------------------------------------------------------
* The "child" error type.
* --------------------------------------------------------------------- */
@@ -78,24 +84,440 @@ child_error(const char *cmd, int state)
}
/* ---------------------------------------------------------------------
+ * The "stream_prepare" auxiliary type.
+ * --------------------------------------------------------------------- */
+
+struct stream_prepare {
+ const atf_process_stream_t *m_sb;
+
+ bool m_pipefds_ok;
+ int m_pipefds[2];
+};
+typedef struct stream_prepare stream_prepare_t;
+
+static
+atf_error_t
+stream_prepare_init(stream_prepare_t *sp, const atf_process_stream_t *sb)
+{
+ atf_error_t err;
+
+ const int type = atf_process_stream_type(sb);
+
+ sp->m_sb = sb;
+ sp->m_pipefds_ok = false;
+
+ if (type == atf_process_stream_type_capture) {
+ if (pipe(sp->m_pipefds) == -1)
+ err = atf_libc_error(errno, "Failed to create pipe");
+ else {
+ err = atf_no_error();
+ sp->m_pipefds_ok = true;
+ }
+ } else
+ err = atf_no_error();
+
+ return err;
+}
+
+static
+void
+stream_prepare_fini(stream_prepare_t *sp)
+{
+ if (sp->m_pipefds_ok) {
+ close(sp->m_pipefds[0]);
+ close(sp->m_pipefds[1]);
+ }
+}
+
+/* ---------------------------------------------------------------------
+ * The "atf_process_stream" type.
+ * --------------------------------------------------------------------- */
+
+const int atf_process_stream_type_capture = 1;
+const int atf_process_stream_type_inherit = 2;
+const int atf_process_stream_type_redirect_fd = 3;
+const int atf_process_stream_type_redirect_path = 4;
+
+static
+bool
+stream_is_valid(const atf_process_stream_t *sb)
+{
+ return (sb->m_type == atf_process_stream_type_capture) ||
+ (sb->m_type == atf_process_stream_type_inherit) ||
+ (sb->m_type == atf_process_stream_type_redirect_fd) ||
+ (sb->m_type == atf_process_stream_type_redirect_path);
+}
+
+atf_error_t
+atf_process_stream_init_capture(atf_process_stream_t *sb)
+{
+ atf_object_init(&sb->m_object);
+
+ sb->m_type = atf_process_stream_type_capture;
+
+ POST(stream_is_valid(sb));
+ return atf_no_error();
+}
+
+atf_error_t
+atf_process_stream_init_inherit(atf_process_stream_t *sb)
+{
+ atf_object_init(&sb->m_object);
+
+ sb->m_type = atf_process_stream_type_inherit;
+
+ POST(stream_is_valid(sb));
+ return atf_no_error();
+}
+
+atf_error_t
+atf_process_stream_init_redirect_fd(atf_process_stream_t *sb,
+ const int fd)
+{
+ atf_object_init(&sb->m_object);
+
+ sb->m_type = atf_process_stream_type_redirect_fd;
+ sb->m_fd = fd;
+
+ POST(stream_is_valid(sb));
+ return atf_no_error();
+}
+
+atf_error_t
+atf_process_stream_init_redirect_path(atf_process_stream_t *sb,
+ const atf_fs_path_t *path)
+{
+ atf_object_init(&sb->m_object);
+
+ sb->m_type = atf_process_stream_type_redirect_path;
+ sb->m_path = path;
+
+ POST(stream_is_valid(sb));
+ return atf_no_error();
+}
+
+void
+atf_process_stream_fini(atf_process_stream_t *sb)
+{
+ PRE(stream_is_valid(sb));
+
+ atf_object_fini(&sb->m_object);
+}
+
+int
+atf_process_stream_type(const atf_process_stream_t *sb)
+{
+ PRE(stream_is_valid(sb));
+
+ return sb->m_type;
+}
+
+/* ---------------------------------------------------------------------
+ * The "atf_process_status" type.
+ * --------------------------------------------------------------------- */
+
+atf_error_t
+atf_process_status_init(atf_process_status_t *s, int status)
+{
+ atf_object_init(&s->m_object);
+
+ s->m_status = status;
+
+ return atf_no_error();
+}
+
+void
+atf_process_status_fini(atf_process_status_t *s)
+{
+ atf_object_fini(&s->m_object);
+}
+
+bool
+atf_process_status_exited(const atf_process_status_t *s)
+{
+ int mutable_status = s->m_status;
+ return WIFEXITED(mutable_status);
+}
+
+int
+atf_process_status_exitstatus(const atf_process_status_t *s)
+{
+ PRE(atf_process_status_exited(s));
+ int mutable_status = s->m_status;
+ return WEXITSTATUS(mutable_status);
+}
+
+bool
+atf_process_status_signaled(const atf_process_status_t *s)
+{
+ int mutable_status = s->m_status;
+ return WIFSIGNALED(mutable_status);
+}
+
+int
+atf_process_status_termsig(const atf_process_status_t *s)
+{
+ PRE(atf_process_status_signaled(s));
+ int mutable_status = s->m_status;
+ return WTERMSIG(mutable_status);
+}
+
+bool
+atf_process_status_coredump(const atf_process_status_t *s)
+{
+ PRE(atf_process_status_signaled(s));
+#if defined(WCOREDUMP)
+ int mutable_status = s->m_status;
+ return WCOREDUMP(mutable_status);
+#else
+ return false;
+#endif
+}
+
+/* ---------------------------------------------------------------------
+ * The "atf_process_child" type.
+ * --------------------------------------------------------------------- */
+
+static
+atf_error_t
+atf_process_child_init(atf_process_child_t *c)
+{
+ atf_object_init(&c->m_object);
+
+ c->m_pid = 0;
+ c->m_stdout = -1;
+ c->m_stderr = -1;
+
+ return atf_no_error();
+}
+
+static
+void
+atf_process_child_fini(atf_process_child_t *c)
+{
+ atf_object_fini(&c->m_object);
+}
+
+atf_error_t
+atf_process_child_wait(atf_process_child_t *c, atf_process_status_t *s)
+{
+ atf_error_t err;
+ int status;
+
+ if (waitpid(c->m_pid, &status, 0) == -1)
+ err = atf_libc_error(errno, "Failed waiting for process %d",
+ c->m_pid);
+ else {
+ atf_process_child_fini(c);
+ err = atf_process_status_init(s, status);
+ }
+
+ return err;
+}
+
+int
+atf_process_child_stdout(atf_process_child_t *c)
+{
+ PRE(c->m_stdout != -1);
+ return c->m_stdout;
+}
+
+int
+atf_process_child_stderr(atf_process_child_t *c)
+{
+ PRE(c->m_stderr != -1);
+ return c->m_stderr;
+}
+
+/* ---------------------------------------------------------------------
* Free functions.
* --------------------------------------------------------------------- */
+static
atf_error_t
-atf_process_fork(pid_t *outpid)
+safe_dup(const int oldfd, const int newfd)
{
atf_error_t err;
+
+ if (oldfd != newfd) {
+ if (dup2(oldfd, newfd) == -1) {
+ err = atf_libc_error(errno, "Could not allocate file descriptor");
+ } else {
+ close(oldfd);
+ err = atf_no_error();
+ }
+ } else
+ err = atf_no_error();
+
+ return err;
+}
+
+static
+atf_error_t
+child_connect(const stream_prepare_t *sp, int procfd)
+{
+ atf_error_t err;
+ const int type = atf_process_stream_type(sp->m_sb);
+
+ if (type == atf_process_stream_type_capture) {
+ close(sp->m_pipefds[0]);
+ err = safe_dup(sp->m_pipefds[1], procfd);
+ } else if (type == atf_process_stream_type_inherit) {
+ err = atf_no_error();
+ } else if (type == atf_process_stream_type_redirect_fd) {
+ err = safe_dup(sp->m_sb->m_fd, procfd);
+ } else if (type == atf_process_stream_type_redirect_path) {
+ int aux = open(atf_fs_path_cstring(sp->m_sb->m_path),
+ O_WRONLY | O_CREAT | O_TRUNC, 0644);
+ if (aux == -1)
+ err = atf_libc_error(errno, "Could not create %s",
+ atf_fs_path_cstring(sp->m_sb->m_path));
+ else {
+ err = safe_dup(aux, procfd);
+ if (atf_is_error(err))
+ close(aux);
+ }
+ } else {
+ UNREACHABLE;
+ err = atf_no_error();
+ }
+
+ return err;
+}
+
+static
+void
+parent_connect(const stream_prepare_t *sp, int *fd)
+{
+ const int type = atf_process_stream_type(sp->m_sb);
+
+ if (type == atf_process_stream_type_capture) {
+ close(sp->m_pipefds[1]);
+ *fd = sp->m_pipefds[0];
+ } else if (type == atf_process_stream_type_inherit) {
+ /* Do nothing. */
+ } else if (type == atf_process_stream_type_redirect_fd) {
+ /* Do nothing. */
+ } else if (type == atf_process_stream_type_redirect_path) {
+ /* Do nothing. */
+ } else {
+ UNREACHABLE;
+ }
+}
+
+static
+atf_error_t
+do_parent(atf_process_child_t *c,
+ const pid_t pid,
+ const stream_prepare_t *outsp,
+ const stream_prepare_t *errsp)
+{
+ atf_error_t err;
+
+ err = atf_process_child_init(c);
+ if (atf_is_error(err))
+ goto out;
+
+ c->m_pid = pid;
+
+ parent_connect(outsp, &c->m_stdout);
+ parent_connect(errsp, &c->m_stderr);
+
+out:
+ return err;
+}
+
+static
+void
+do_child(atf_error_t (*)(const void *),
+ const void *,
+ const stream_prepare_t *,
+ const stream_prepare_t *) ATF_DEFS_ATTRIBUTE_NORETURN;
+
+static
+void
+do_child(atf_error_t (*start)(const void *),
+ const void *v,
+ const stream_prepare_t *outsp,
+ const stream_prepare_t *errsp)
+{
+ atf_error_t err;
+
+ atf_reset_exit_checks();
+ err = child_connect(outsp, STDOUT_FILENO);
+ if (atf_is_error(err))
+ goto out;
+
+ err = child_connect(errsp, STDERR_FILENO);
+ if (atf_is_error(err))
+ goto out;
+
+ err = start(v);
+
+out:
+ if (atf_is_error(err)) {
+ char buf[1024];
+
+ atf_error_format(err, buf, sizeof(buf));
+ fprintf(stderr, "Unhandled error: %s\n", buf);
+ atf_error_free(err);
+
+ exit(EXIT_FAILURE);
+ } else
+ exit(EXIT_SUCCESS);
+}
+
+atf_error_t
+atf_process_oldfork(pid_t *pid)
+{
+ *pid = fork();
+ return atf_no_error();
+}
+
+atf_error_t
+atf_process_fork(atf_process_child_t *c,
+ atf_error_t (*start)(const void *),
+ const atf_process_stream_t *outsb,
+ const atf_process_stream_t *errsb,
+ const void *v)
+{
+ atf_error_t err;
+ stream_prepare_t outsp;
+ stream_prepare_t errsp;
pid_t pid;
+ err = stream_prepare_init(&outsp, outsb);
+ if (atf_is_error(err))
+ goto out;
+
+ err = stream_prepare_init(&errsp, errsb);
+ if (atf_is_error(err))
+ goto err_outpipe;
+
pid = fork();
if (pid == -1) {
err = atf_libc_error(errno, "Failed to fork");
- goto out;
+ goto err_errpipe;
}
- *outpid = pid;
- err = atf_no_error();
+ if (pid == 0) {
+ do_child(start, v, &outsp, &errsp);
+ UNREACHABLE;
+ abort();
+ err = atf_no_error();
+ } else {
+ err = do_parent(c, pid, &outsp, &errsp);
+ if (atf_is_error(err))
+ goto err_errpipe;
+ }
+ goto out;
+
+err_errpipe:
+ stream_prepare_fini(&errsp);
+err_outpipe:
+ stream_prepare_fini(&outsp);
+
out:
return err;
}
============================================================
--- atf-c/process.h 8265e8436a6c94dcf2e843ba31df7d99c3772b82
+++ atf-c/process.h 038a4ff06acb2132257bdabc9c3b387ce50ecb4c
@@ -32,13 +32,92 @@
#include <sys/types.h>
+#include <stdbool.h>
+
#include <atf-c/error_fwd.h>
+#include <atf-c/fs.h>
+#include <atf-c/object.h>
/* ---------------------------------------------------------------------
+ * The "atf_process_stream" type.
+ * --------------------------------------------------------------------- */
+
+struct atf_process_stream {
+ atf_object_t m_object;
+
+ int m_type;
+
+ // Valid if m_type == redirect_fd.
+ int m_fd;
+
+ // Valid if m_type == redirect_path.
+ const atf_fs_path_t *m_path;
+};
+typedef struct atf_process_stream atf_process_stream_t;
+
+extern const int atf_process_stream_type_capture;
+extern const int atf_process_stream_type_inherit;
+extern const int atf_process_stream_type_redirect_fd;
+extern const int atf_process_stream_type_redirect_path;
+
+atf_error_t atf_process_stream_init_capture(atf_process_stream_t *);
+atf_error_t atf_process_stream_init_inherit(atf_process_stream_t *);
+atf_error_t atf_process_stream_init_redirect_fd(atf_process_stream_t *,
+ const int fd);
+atf_error_t atf_process_stream_init_redirect_path(atf_process_stream_t *,
+ const atf_fs_path_t *);
+void atf_process_stream_fini(atf_process_stream_t *);
+
+int atf_process_stream_type(const atf_process_stream_t *);
+
+/* ---------------------------------------------------------------------
+ * The "atf_process_status" type.
+ * --------------------------------------------------------------------- */
+
+struct atf_process_status {
+ atf_object_t m_object;
+
+ int m_status;
+};
+typedef struct atf_process_status atf_process_status_t;
+
+void atf_process_status_fini(atf_process_status_t *);
+
+bool atf_process_status_exited(const atf_process_status_t *);
+int atf_process_status_exitstatus(const atf_process_status_t *);
+bool atf_process_status_signaled(const atf_process_status_t *);
+int atf_process_status_termsig(const atf_process_status_t *);
+bool atf_process_status_coredump(const atf_process_status_t *);
+
+/* ---------------------------------------------------------------------
+ * The "atf_process_child" type.
+ * --------------------------------------------------------------------- */
+
+struct atf_process_child {
+ atf_object_t m_object;
+
+ pid_t m_pid;
+
+ int m_stdout;
+ int m_stderr;
+};
+typedef struct atf_process_child atf_process_child_t;
+
+atf_error_t atf_process_child_wait(atf_process_child_t *,
+ atf_process_status_t *);
+int atf_process_child_stdout(atf_process_child_t *);
+int atf_process_child_stderr(atf_process_child_t *);
+
+/* ---------------------------------------------------------------------
* Free functions.
* --------------------------------------------------------------------- */
-atf_error_t atf_process_fork(pid_t *);
-atf_error_t atf_process_system(const char *);
+atf_error_t atf_process_oldfork(pid_t *pid); // XXX Kill this.
+atf_error_t atf_process_fork(atf_process_child_t *,
+ atf_error_t (*)(const void *),
+ const atf_process_stream_t *,
+ const atf_process_stream_t *,
+ const void *);
+atf_error_t atf_process_system(const char *); // XXX Kill this.
#endif /* !defined(ATF_C_PROCESS_H) */
============================================================
--- atf-c/tc.c 8990a94abfa2417ef6f1f4ec78556cc3f35353af
+++ atf-c/tc.c bcb9833d27cdd68cff6f1b58c0018f7f30c79866
@@ -453,7 +453,7 @@ fork_body(const atf_tc_t *tc, int fdout,
atf_error_t err;
pid_t pid;
- err = atf_process_fork(&pid);
+ err = atf_process_oldfork(&pid);
if (atf_is_error(err))
goto out;
@@ -480,7 +480,7 @@ fork_cleanup(const atf_tc_t *tc, int fdo
if (tc->m_cleanup == NULL)
err = atf_no_error();
else {
- err = atf_process_fork(&pid);
+ err = atf_process_oldfork(&pid);
if (atf_is_error(err))
goto out;
============================================================
--- atf-c++/process.cpp 1c1397ab2ab18e0b6fcf8d6fbe7f2c3e991b9bb7
+++ atf-c++/process.cpp aaec03f9fdb8cd8769b0c897e7498e07089add79
@@ -48,7 +48,7 @@ impl::fork(void)
atf_error_t err;
pid_t pid;
- err = atf_process_fork(&pid);
+ err = atf_process_oldfork(&pid);
if (atf_is_error(err))
throw_atf_error(err);
============================================================
--- 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);
============================================================
--- tests/atf/atf-c/t_sanity.c f198b24a03b91d99d034dd4e6c8b629b56e470a8
+++ tests/atf/atf-c/t_sanity.c f357d5b3f5ad0742a725b18970a3e60d40549bc5
@@ -79,7 +79,7 @@ do_test(enum type t, bool cond)
ATF_REQUIRE(pipe(fds) != -1);
- RE(atf_process_fork(&pid));
+ RE(atf_process_oldfork(&pid));
if (pid == 0) {
ATF_REQUIRE(dup2(fds[1], STDERR_FILENO) != -1);
close(fds[0]);
============================================================
--- tests/atf/atf-c/t_signals.c 5db33b1465a5d1185ecf442e6cfe8f5d24a2eb52
+++ tests/atf/atf-c/t_signals.c de2770b573a42267cff064efa928e170bc154dc0
@@ -253,7 +253,7 @@ ATF_TC_BODY(signal_reset, tc)
ATF_TC_BODY(signal_reset, tc)
{
pid_t pid;
- RE(atf_process_fork(&pid));
+ RE(atf_process_oldfork(&pid));
if (pid == 0) {
struct sigaction sa;