Blame support/tst-support_capture_subprocess.c

Packit Service 82fcde
/* Test capturing output from a subprocess.
Packit Service 82fcde
   Copyright (C) 2017-2018 Free Software Foundation, Inc.
Packit Service 82fcde
   This file is part of the GNU C Library.
Packit Service 82fcde
Packit Service 82fcde
   The GNU C Library is free software; you can redistribute it and/or
Packit Service 82fcde
   modify it under the terms of the GNU Lesser General Public
Packit Service 82fcde
   License as published by the Free Software Foundation; either
Packit Service 82fcde
   version 2.1 of the License, or (at your option) any later version.
Packit Service 82fcde
Packit Service 82fcde
   The GNU C Library is distributed in the hope that it will be useful,
Packit Service 82fcde
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 82fcde
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit Service 82fcde
   Lesser General Public License for more details.
Packit Service 82fcde
Packit Service 82fcde
   You should have received a copy of the GNU Lesser General Public
Packit Service 82fcde
   License along with the GNU C Library; if not, see
Packit Service 82fcde
   <http://www.gnu.org/licenses/>.  */
Packit Service 82fcde
Packit Service 82fcde
#include <stdbool.h>
Packit Service 82fcde
#include <stdio.h>
Packit Service 82fcde
#include <stdlib.h>
Packit Service 82fcde
#include <string.h>
Packit Service 82fcde
#include <support/capture_subprocess.h>
Packit Service 82fcde
#include <support/check.h>
Packit Service 82fcde
#include <support/support.h>
Packit Service 82fcde
#include <sys/wait.h>
Packit Service 82fcde
#include <unistd.h>
Packit Service 82fcde
Packit Service 82fcde
/* Write one byte at *P to FD and advance *P.  Do nothing if *P is
Packit Service 82fcde
   '\0'.  */
Packit Service 82fcde
static void
Packit Service 82fcde
transfer (const unsigned char **p, int fd)
Packit Service 82fcde
{
Packit Service 82fcde
  if (**p != '\0')
Packit Service 82fcde
    {
Packit Service 82fcde
      TEST_VERIFY (write (fd, *p, 1) == 1);
Packit Service 82fcde
      ++*p;
Packit Service 82fcde
    }
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
/* Determine the order in which stdout and stderr are written.  */
Packit Service 82fcde
enum write_mode { out_first, err_first, interleave,
Packit Service 82fcde
                  write_mode_last =  interleave };
Packit Service 82fcde
Packit Service 82fcde
/* Describe what to write in the subprocess.  */
Packit Service 82fcde
struct test
Packit Service 82fcde
{
Packit Service 82fcde
  char *out;
Packit Service 82fcde
  char *err;
Packit Service 82fcde
  enum write_mode write_mode;
Packit Service 82fcde
  int signal;
Packit Service 82fcde
  int status;
Packit Service 82fcde
};
Packit Service 82fcde
Packit Service 1c5418
/* For use with support_capture_subprocess.  */
Packit Service 1c5418
static void
Packit Service 1c5418
callback (void *closure)
Packit Service 82fcde
{
Packit Service 1c5418
  const struct test *test = closure;
Packit Service 82fcde
  bool mode_ok = false;
Packit Service 82fcde
  switch (test->write_mode)
Packit Service 82fcde
    {
Packit Service 82fcde
    case out_first:
Packit Service 82fcde
      TEST_VERIFY (fputs (test->out, stdout) >= 0);
Packit Service 82fcde
      TEST_VERIFY (fflush (stdout) == 0);
Packit Service 82fcde
      TEST_VERIFY (fputs (test->err, stderr) >= 0);
Packit Service 82fcde
      TEST_VERIFY (fflush (stderr) == 0);
Packit Service 82fcde
      mode_ok = true;
Packit Service 82fcde
      break;
Packit Service 82fcde
    case err_first:
Packit Service 82fcde
      TEST_VERIFY (fputs (test->err, stderr) >= 0);
Packit Service 82fcde
      TEST_VERIFY (fflush (stderr) == 0);
Packit Service 82fcde
      TEST_VERIFY (fputs (test->out, stdout) >= 0);
Packit Service 82fcde
      TEST_VERIFY (fflush (stdout) == 0);
Packit Service 82fcde
      mode_ok = true;
Packit Service 82fcde
      break;
Packit Service 82fcde
    case interleave:
Packit Service 82fcde
      {
Packit Service 82fcde
        const unsigned char *pout = (const unsigned char *) test->out;
Packit Service 82fcde
        const unsigned char *perr = (const unsigned char *) test->err;
Packit Service 82fcde
        do
Packit Service 82fcde
          {
Packit Service 82fcde
            transfer (&pout, STDOUT_FILENO);
Packit Service 82fcde
            transfer (&perr, STDERR_FILENO);
Packit Service 82fcde
          }
Packit Service 82fcde
        while (*pout != '\0' || *perr != '\0');
Packit Service 82fcde
      }
Packit Service 82fcde
      mode_ok = true;
Packit Service 82fcde
      break;
Packit Service 82fcde
    }
Packit Service 82fcde
  TEST_VERIFY (mode_ok);
Packit Service 82fcde
Packit Service 82fcde
  if (test->signal != 0)
Packit Service 82fcde
    raise (test->signal);
Packit Service 82fcde
  exit (test->status);
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
/* Create a heap-allocated random string of letters.  */
Packit Service 82fcde
static char *
Packit Service 82fcde
random_string (size_t length)
Packit Service 82fcde
{
Packit Service 82fcde
  char *result = xmalloc (length + 1);
Packit Service 82fcde
  for (size_t i = 0; i < length; ++i)
Packit Service 82fcde
    result[i] = 'a' + (rand () % 26);
Packit Service 82fcde
  result[length] = '\0';
Packit Service 82fcde
  return result;
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
/* Check that the specific stream from the captured subprocess matches
Packit Service 82fcde
   expectations.  */
Packit Service 82fcde
static void
Packit Service 82fcde
check_stream (const char *what, const struct xmemstream *stream,
Packit Service 82fcde
              const char *expected)
Packit Service 82fcde
{
Packit Service 82fcde
  if (strcmp (stream->buffer, expected) != 0)
Packit Service 82fcde
    {
Packit Service 82fcde
      support_record_failure ();
Packit Service 82fcde
      printf ("error: captured %s data incorrect\n"
Packit Service 82fcde
              "  expected: %s\n"
Packit Service 82fcde
              "  actual:   %s\n",
Packit Service 82fcde
              what, expected, stream->buffer);
Packit Service 82fcde
    }
Packit Service 82fcde
  if (stream->length != strlen (expected))
Packit Service 82fcde
    {
Packit Service 82fcde
      support_record_failure ();
Packit Service 82fcde
      printf ("error: captured %s data length incorrect\n"
Packit Service 82fcde
              "  expected: %zu\n"
Packit Service 82fcde
              "  actual:   %zu\n",
Packit Service 82fcde
              what, strlen (expected), stream->length);
Packit Service 82fcde
    }
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
static int
Packit Service 1c5418
do_test (void)
Packit Service 82fcde
{
Packit Service 82fcde
  const int lengths[] = {0, 1, 17, 512, 20000, -1};
Packit Service 82fcde
Packit Service 1c5418
  /* Test multiple combinations of support_capture_subprocess.
Packit Service 82fcde
Packit Service 82fcde
     length_idx_stdout: Index into the lengths array above,
Packit Service 82fcde
       controls how many bytes are written by the subprocess to
Packit Service 82fcde
       standard output.
Packit Service 82fcde
     length_idx_stderr: Same for standard error.
Packit Service 82fcde
     write_mode: How standard output and standard error writes are
Packit Service 82fcde
       ordered.
Packit Service 82fcde
     signal: Exit with no signal if zero, with SIGTERM if one.
Packit Service 82fcde
     status: Process exit status: 0 if zero, 3 if one.  */
Packit Service 82fcde
  for (int length_idx_stdout = 0; lengths[length_idx_stdout] >= 0;
Packit Service 82fcde
       ++length_idx_stdout)
Packit Service 82fcde
    for (int length_idx_stderr = 0; lengths[length_idx_stderr] >= 0;
Packit Service 82fcde
         ++length_idx_stderr)
Packit Service 82fcde
      for (int write_mode = 0; write_mode < write_mode_last; ++write_mode)
Packit Service 82fcde
        for (int signal = 0; signal < 2; ++signal)
Packit Service 82fcde
          for (int status = 0; status < 2; ++status)
Packit Service 82fcde
            {
Packit Service 82fcde
              struct test test =
Packit Service 82fcde
                {
Packit Service 82fcde
                  .out = random_string (lengths[length_idx_stdout]),
Packit Service 82fcde
                  .err = random_string (lengths[length_idx_stderr]),
Packit Service 82fcde
                  .write_mode = write_mode,
Packit Service 82fcde
                  .signal = signal * SIGTERM, /* 0 or SIGTERM.  */
Packit Service 82fcde
                  .status = status * 3,       /* 0 or 3.  */
Packit Service 82fcde
                };
Packit Service 82fcde
              TEST_VERIFY (strlen (test.out) == lengths[length_idx_stdout]);
Packit Service 82fcde
              TEST_VERIFY (strlen (test.err) == lengths[length_idx_stderr]);
Packit Service 82fcde
Packit Service 1c5418
              struct support_capture_subprocess result
Packit Service 1c5418
                = support_capture_subprocess (callback, &test);
Packit Service 82fcde
              check_stream ("stdout", &result.out, test.out);
Packit Service 82fcde
              check_stream ("stderr", &result.err, test.err);
Packit Service 82fcde
              if (test.signal != 0)
Packit Service 82fcde
                {
Packit Service 82fcde
                  TEST_VERIFY (WIFSIGNALED (result.status));
Packit Service 82fcde
                  TEST_VERIFY (WTERMSIG (result.status) == test.signal);
Packit Service 82fcde
                }
Packit Service 82fcde
              else
Packit Service 82fcde
                {
Packit Service 82fcde
                  TEST_VERIFY (WIFEXITED (result.status));
Packit Service 82fcde
                  TEST_VERIFY (WEXITSTATUS (result.status) == test.status);
Packit Service 82fcde
                }
Packit Service 82fcde
              support_capture_subprocess_free (&result);
Packit Service 82fcde
              free (test.out);
Packit Service 82fcde
              free (test.err);
Packit Service 82fcde
            }
Packit Service 82fcde
  return 0;
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
#include <support/test-driver.c>