123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202 |
- /* Test capturing output from a subprocess.
- Copyright (C) 2017-2019 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);
- /* Allowed output for support_capture_subprocess_check. */
- int check_allow = 0;
- if (lengths[length_idx_stdout] > 0)
- check_allow |= sc_allow_stdout;
- if (lengths[length_idx_stderr] > 0)
- check_allow |= sc_allow_stderr;
- if (check_allow == 0)
- check_allow = sc_allow_none;
- if (test.signal != 0)
- {
- TEST_VERIFY (WIFSIGNALED (result.status));
- TEST_VERIFY (WTERMSIG (result.status) == test.signal);
- support_capture_subprocess_check (&result, "signal",
- -SIGTERM, check_allow);
- }
- else
- {
- TEST_VERIFY (WIFEXITED (result.status));
- TEST_VERIFY (WEXITSTATUS (result.status) == test.status);
- support_capture_subprocess_check (&result, "exit",
- test.status, check_allow);
- }
- support_capture_subprocess_free (&result);
- free (test.out);
- free (test.err);
- }
- return 0;
- }
- #include <support/test-driver.c>
|