aboutsummaryrefslogtreecommitdiff
path: root/support/tst-support_capture_subprocess.c
diff options
context:
space:
mode:
authorFlorian Weimer <fweimer@redhat.com>2017-06-02 11:59:28 +0200
committerFlorian Weimer <fweimer@redhat.com>2017-06-02 11:59:28 +0200
commit91b6eb1140eda6bab324821ee3785e5d0ca155b8 (patch)
treec8b630c412611a9b9f5e600e8824661f403bfa7f /support/tst-support_capture_subprocess.c
parent09103e40252454e906a0b8543a142fc96b4c17c1 (diff)
downloadglibc-91b6eb1140eda6bab324821ee3785e5d0ca155b8.zip
glibc-91b6eb1140eda6bab324821ee3785e5d0ca155b8.tar.gz
glibc-91b6eb1140eda6bab324821ee3785e5d0ca155b8.tar.bz2
Add internal facility for dynamic array handling
This is intended as a type-safe alternative to obstacks and hand-written realloc constructs. The implementation avoids writing function pointers to the heap.
Diffstat (limited to 'support/tst-support_capture_subprocess.c')
-rw-r--r--support/tst-support_capture_subprocess.c188
1 files changed, 188 insertions, 0 deletions
diff --git a/support/tst-support_capture_subprocess.c b/support/tst-support_capture_subprocess.c
new file mode 100644
index 0000000..5672fba
--- /dev/null
+++ b/support/tst-support_capture_subprocess.c
@@ -0,0 +1,188 @@
+/* Test capturing output from a subprocess.
+ Copyright (C) 2017 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <http://www.gnu.org/licenses/>. */
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <support/capture_subprocess.h>
+#include <support/check.h>
+#include <support/support.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+/* Write one byte at *P to FD and advance *P. Do nothing if *P is
+ '\0'. */
+static void
+transfer (const unsigned char **p, int fd)
+{
+ if (**p != '\0')
+ {
+ TEST_VERIFY (write (fd, *p, 1) == 1);
+ ++*p;
+ }
+}
+
+/* Determine the order in which stdout and stderr are written. */
+enum write_mode { out_first, err_first, interleave,
+ write_mode_last = interleave };
+
+/* Describe what to write in the subprocess. */
+struct test
+{
+ char *out;
+ char *err;
+ enum write_mode write_mode;
+ int signal;
+ int status;
+};
+
+/* For use with support_capture_subprocess. */
+static void
+callback (void *closure)
+{
+ const struct test *test = closure;
+ bool mode_ok = false;
+ switch (test->write_mode)
+ {
+ case out_first:
+ TEST_VERIFY (fputs (test->out, stdout) >= 0);
+ TEST_VERIFY (fflush (stdout) == 0);
+ TEST_VERIFY (fputs (test->err, stderr) >= 0);
+ TEST_VERIFY (fflush (stderr) == 0);
+ mode_ok = true;
+ break;
+ case err_first:
+ TEST_VERIFY (fputs (test->err, stderr) >= 0);
+ TEST_VERIFY (fflush (stderr) == 0);
+ TEST_VERIFY (fputs (test->out, stdout) >= 0);
+ TEST_VERIFY (fflush (stdout) == 0);
+ mode_ok = true;
+ break;
+ case interleave:
+ {
+ const unsigned char *pout = (const unsigned char *) test->out;
+ const unsigned char *perr = (const unsigned char *) test->err;
+ do
+ {
+ transfer (&pout, STDOUT_FILENO);
+ transfer (&perr, STDERR_FILENO);
+ }
+ while (*pout != '\0' || *perr != '\0');
+ }
+ mode_ok = true;
+ break;
+ }
+ TEST_VERIFY (mode_ok);
+
+ if (test->signal != 0)
+ raise (test->signal);
+ exit (test->status);
+}
+
+/* Create a heap-allocated random string of letters. */
+static char *
+random_string (size_t length)
+{
+ char *result = xmalloc (length + 1);
+ for (size_t i = 0; i < length; ++i)
+ result[i] = 'a' + (rand () % 26);
+ result[length] = '\0';
+ return result;
+}
+
+/* Check that the specific stream from the captured subprocess matches
+ expectations. */
+static void
+check_stream (const char *what, const struct xmemstream *stream,
+ const char *expected)
+{
+ if (strcmp (stream->buffer, expected) != 0)
+ {
+ support_record_failure ();
+ printf ("error: captured %s data incorrect\n"
+ " expected: %s\n"
+ " actual: %s\n",
+ what, expected, stream->buffer);
+ }
+ if (stream->length != strlen (expected))
+ {
+ support_record_failure ();
+ printf ("error: captured %s data length incorrect\n"
+ " expected: %zu\n"
+ " actual: %zu\n",
+ what, strlen (expected), stream->length);
+ }
+}
+
+static int
+do_test (void)
+{
+ const int lengths[] = {0, 1, 17, 512, 20000, -1};
+
+ /* Test multiple combinations of support_capture_subprocess.
+
+ length_idx_stdout: Index into the lengths array above,
+ controls how many bytes are written by the subprocess to
+ standard output.
+ length_idx_stderr: Same for standard error.
+ write_mode: How standard output and standard error writes are
+ ordered.
+ signal: Exit with no signal if zero, with SIGTERM if one.
+ status: Process exit status: 0 if zero, 3 if one. */
+ for (int length_idx_stdout = 0; lengths[length_idx_stdout] >= 0;
+ ++length_idx_stdout)
+ for (int length_idx_stderr = 0; lengths[length_idx_stderr] >= 0;
+ ++length_idx_stderr)
+ for (int write_mode = 0; write_mode < write_mode_last; ++write_mode)
+ for (int signal = 0; signal < 2; ++signal)
+ for (int status = 0; status < 2; ++status)
+ {
+ struct test test =
+ {
+ .out = random_string (lengths[length_idx_stdout]),
+ .err = random_string (lengths[length_idx_stderr]),
+ .write_mode = write_mode,
+ .signal = signal * SIGTERM, /* 0 or SIGTERM. */
+ .status = status * 3, /* 0 or 3. */
+ };
+ TEST_VERIFY (strlen (test.out) == lengths[length_idx_stdout]);
+ TEST_VERIFY (strlen (test.err) == lengths[length_idx_stderr]);
+
+ struct support_capture_subprocess result
+ = support_capture_subprocess (callback, &test);
+ check_stream ("stdout", &result.out, test.out);
+ check_stream ("stderr", &result.err, test.err);
+ if (test.signal != 0)
+ {
+ TEST_VERIFY (WIFSIGNALED (result.status));
+ TEST_VERIFY (WTERMSIG (result.status) == test.signal);
+ }
+ else
+ {
+ TEST_VERIFY (WIFEXITED (result.status));
+ TEST_VERIFY (WEXITSTATUS (result.status) == test.status);
+ }
+ support_capture_subprocess_free (&result);
+ free (test.out);
+ free (test.err);
+ }
+ return 0;
+}
+
+#include <support/test-driver.c>