summaryrefslogtreecommitdiff
path: root/tests/tap/process.c
diff options
context:
space:
mode:
Diffstat (limited to 'tests/tap/process.c')
-rw-r--r--tests/tap/process.c100
1 files changed, 100 insertions, 0 deletions
diff --git a/tests/tap/process.c b/tests/tap/process.c
new file mode 100644
index 0000000..16154c7
--- /dev/null
+++ b/tests/tap/process.c
@@ -0,0 +1,100 @@
+/*
+ * Utility functions for tests that use subprocesses.
+ *
+ * Provides utility functions for subprocess manipulation. Currently, only
+ * one utility function is provided: is_function_output, which runs a function
+ * in a subprocess and checks its output and exit status against expected
+ * values.
+ *
+ * Copyright 2009, 2010 Board of Trustees, Leland Stanford Jr. University
+ * Copyright (c) 2004, 2005, 2006
+ * by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
+ * 2002, 2003 by The Internet Software Consortium and Rich Salz
+ *
+ * See LICENSE for licensing terms.
+ */
+
+#include <config.h>
+#include <portable/system.h>
+
+#include <sys/wait.h>
+
+#include <tests/tap/basic.h>
+#include <tests/tap/process.h>
+#include <util/xmalloc.h>
+
+
+/*
+ * Given a function, an expected exit status, and expected output, runs that
+ * function in a subprocess, capturing stdout and stderr via a pipe, and
+ * compare the combination of stdout and stderr with the expected output and
+ * the exit status with the expected status. Expects the function to always
+ * exit (not die from a signal).
+ */
+void
+is_function_output(test_function_type function, int status, const char *output,
+ const char *format, ...)
+{
+ int fds[2];
+ pid_t child;
+ char *buf, *msg;
+ ssize_t count, ret, buflen;
+ int rval;
+ va_list args;
+
+ /* Flush stdout before we start to avoid odd forking issues. */
+ fflush(stdout);
+
+ /* Set up the pipe and call the function, collecting its output. */
+ if (pipe(fds) == -1)
+ sysbail("can't create pipe");
+ child = fork();
+ if (child == (pid_t) -1) {
+ sysbail("can't fork");
+ } else if (child == 0) {
+ /* In child. Set up our stdout and stderr. */
+ close(fds[0]);
+ if (dup2(fds[1], 1) == -1)
+ _exit(255);
+ if (dup2(fds[1], 2) == -1)
+ _exit(255);
+
+ /* Now, run the function and exit successfully if it returns. */
+ (*function)();
+ fflush(stdout);
+ _exit(0);
+ } else {
+ /*
+ * In the parent; close the extra file descriptor, read the output if
+ * any, and then collect the exit status.
+ */
+ close(fds[1]);
+ buflen = BUFSIZ;
+ buf = xmalloc(buflen);
+ count = 0;
+ do {
+ ret = read(fds[0], buf + count, buflen - count - 1);
+ if (ret > 0)
+ count += ret;
+ if (count >= buflen - 1) {
+ buflen += BUFSIZ;
+ buf = xrealloc(buf, buflen);
+ }
+ } while (ret > 0);
+ buf[count < 0 ? 0 : count] = '\0';
+ if (waitpid(child, &rval, 0) == (pid_t) -1)
+ sysbail("waitpid failed");
+ }
+
+ /* Now, check the results against what we expected. */
+ va_start(args, format);
+ if (xvasprintf(&msg, format, args) < 0)
+ bail("cannot format test description");
+ va_end(args);
+ ok(WIFEXITED(rval), "%s (exited)", msg);
+ is_int(status, WEXITSTATUS(rval), "%s (status)", msg);
+ is_string(output, buf, "%s (output)", msg);
+ free(buf);
+ free(msg);
+}