autoupdate
[gnulib.git] / tests / test-pipe.c
index 3018186..2dcab58 100644 (file)
@@ -1,5 +1,5 @@
 /* Test of create_pipe_bidi/wait_subprocess.
-   Copyright (C) 2009 Free Software Foundation, Inc.
+   Copyright (C) 2009, 2010 Free Software Foundation, Inc.
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
 #include "wait-process.h"
 
 #include <errno.h>
-#include <fcntl.h>
 #include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <unistd.h>
 
 /* Depending on arguments, this test intentionally closes stderr or
-   starts life with stderr closed.  So, the error messages might not
-   always print, but at least the exit status will be reliable.  */
-#define ASSERT(expr) \
-  do                                                                         \
-    {                                                                        \
-      if (!(expr))                                                           \
-        {                                                                    \
-          fprintf (stderr, "%s:%d: assertion failed\n", __FILE__, __LINE__); \
-          fflush (stderr);                                                   \
-          abort ();                                                          \
-        }                                                                    \
-    }                                                                        \
-  while (0)
+   starts life with stderr closed.  So, we arrange to have fd 10
+   (outside the range of interesting fd's during the test) set up to
+   duplicate the original stderr.  */
+
+#define BACKUP_STDERR_FILENO 10
+#define ASSERT_STREAM myerr
+#include "macros.h"
+
+static FILE *myerr;
+
+/* Code executed by the child process.  argv[1] = "child".  */
+static int
+child_main (int argc, char *argv[])
+{
+  char buffer[2] = { 's', 't' };
+  int fd;
+  int ret;
+
+  ASSERT (argc == 3);
+
+  /* Read one byte from fd 0, and write its value plus one to fd 1.
+     fd 2 should be closed iff the argument is 1.  Check that no other file
+     descriptors leaked.  */
+
+  ASSERT (read (STDIN_FILENO, buffer, 2) == 1);
+
+  buffer[0]++;
+  ASSERT (write (STDOUT_FILENO, buffer, 1) == 1);
+
+  errno = 0;
+  ret = dup2 (STDERR_FILENO, STDERR_FILENO);
+  switch (atoi (argv[2]))
+    {
+    case 0:
+      /* Expect fd 2 is open.  */
+      ASSERT (ret == STDERR_FILENO);
+      break;
+    case 1:
+      /* Expect fd 2 is closed.  */
+      ASSERT (ret == -1);
+      ASSERT (errno == EBADF);
+      break;
+    default:
+      ASSERT (false);
+    }
+
+  for (fd = 3; fd < 7; fd++)
+    {
+      errno = 0;
+      ASSERT (close (fd) == -1);
+      ASSERT (errno == EBADF);
+    }
+
+  return 0;
+}
 
 /* Create a bi-directional pipe to a test child, and validate that the
    child program returns the expected output.  The child is the same
@@ -50,47 +92,50 @@ static void
 test_pipe (const char *argv0, bool stderr_closed)
 {
   int fd[2];
-  const char *argv[3];
+  char *argv[4];
   pid_t pid;
-  char buffer[2] = { 'a', 'a' };
+  char buffer[2] = { 'a', 't' };
 
   /* Set up child.  */
-  argv[0] = argv0;
-  argv[1] = stderr_closed ? "9" : "8";
-  argv[2] = NULL;
-  pid = create_pipe_bidi(argv0, argv0, (char **) argv,
-                         false, true, true, fd);
+  argv[0] = (char *) argv0;
+  argv[1] = (char *) "child";
+  argv[2] = (char *) (stderr_closed ? "1" : "0");
+  argv[3] = NULL;
+  pid = create_pipe_bidi (argv0, argv0, argv, false, true, true, fd);
   ASSERT (0 <= pid);
   ASSERT (STDERR_FILENO < fd[0]);
   ASSERT (STDERR_FILENO < fd[1]);
 
   /* Push child's input.  */
   ASSERT (write (fd[1], buffer, 1) == 1);
+  ASSERT (close (fd[1]) == 0);
 
   /* Get child's output.  */
   ASSERT (read (fd[0], buffer, 2) == 1);
 
   /* Wait for child.  */
-  ASSERT (wait_subprocess (pid, argv0, true, false, false, true, NULL) == 0);
+  ASSERT (wait_subprocess (pid, argv0, true, false, true, true, NULL) == 0);
   ASSERT (close (fd[0]) == 0);
-  ASSERT (close (fd[1]) == 0);
 
   /* Check the result.  */
   ASSERT (buffer[0] == 'b');
-  ASSERT (buffer[1] == 'a');
+  ASSERT (buffer[1] == 't');
 }
 
-int
-main (int argc, const char *argv[])
+/* Code executed by the parent process.  */
+static int
+parent_main (int argc, char *argv[])
 {
-  int i;
   int test;
+  int fd;
+
   ASSERT (argc == 2);
+
+  /* Selectively close various standard fds, to verify the child process is
+     not impacted by this.  */
   test = atoi (argv[1]);
   switch (test)
     {
-      /* Driver cases.  Selectively close various standard fds, to
-         ensure the child process is not impacted by this.  */
     case 0:
       break;
     case 1:
@@ -119,42 +164,41 @@ main (int argc, const char *argv[])
       close (1);
       close (2);
       break;
-
-      /* Slave cases.  Read one byte from fd 0, and write its value
-         plus one to fd 1.  fd 2 should be closed iff the argument is
-         9.  Check that no other fd's leaked.  */
-    case 8:
-    case 9:
-      {
-        char buffer[1];
-        ASSERT (read (STDIN_FILENO, buffer, 1) == 1);
-        buffer[0]++;
-        ASSERT (write (STDOUT_FILENO, buffer, 1) == 1);
-        errno = 0;
-        i = fcntl (STDERR_FILENO, F_GETFL);
-        if (test == 8)
-          ASSERT (0 <= i);
-        else
-          {
-            ASSERT (i < 0);
-            ASSERT (errno == EBADF);
-          }
-        for (i = 3; i < 7; i++)
-          {
-            errno = 0;
-            ASSERT (close (i) == -1);
-            ASSERT (errno == EBADF);
-          }
-        return 0;
-      }
     default:
-      ASSERT (0);
+      ASSERT (false);
     }
-  /* All remaining code is for the driver.  Plug any leaks inherited
-     from outside world before starting, so that child has a clean
-     slate (at least for the fds that we might be manipulating).  */
-  for (i = 3; i < 7; i++)
-    close (i);
-  test_pipe (argv[0], 3 < test);
+
+  /* Plug any file descriptor leaks inherited from outside world before
+     starting, so that child has a clean slate (at least for the fds that we
+     might be manipulating).  */
+  for (fd = 3; fd < 7; fd++)
+    close (fd);
+
+  test_pipe (argv[0], test >= 4);
+
   return 0;
 }
+
+int
+main (int argc, char *argv[])
+{
+  if (argc < 2)
+    {
+      fprintf (stderr, "%s: need arguments\n", argv[0]);
+      return 2;
+    }
+  if (strcmp (argv[1], "child") == 0)
+    {
+      /* fd 2 might be closed, but fd BACKUP_STDERR_FILENO is the original
+         stderr.  */
+      myerr = fdopen (BACKUP_STDERR_FILENO, "w");
+      if (!myerr)
+        return 2;
+      return child_main (argc, argv);
+    }
+  /* We might close fd 2 later, so save it in fd 10.  */
+  if (dup2 (STDERR_FILENO, BACKUP_STDERR_FILENO) != BACKUP_STDERR_FILENO
+      || (myerr = fdopen (BACKUP_STDERR_FILENO, "w")) == NULL)
+    return 2;
+  return parent_main (argc, argv);
+}