Refactor test code.
[gnulib.git] / tests / test-pipe.c
1 /* Test of create_pipe_bidi/wait_subprocess.
2    Copyright (C) 2009 Free Software Foundation, Inc.
3
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 3, or (at your option)
7    any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program; if not, write to the Free Software Foundation,
16    Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
17
18 #include <config.h>
19
20 #include "pipe.h"
21 #include "wait-process.h"
22
23 #include <errno.h>
24 #include <fcntl.h>
25 #include <stdbool.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29
30 /* Depending on arguments, this test intentionally closes stderr or
31    starts life with stderr closed.  So, the error messages might not
32    always print, but at least the exit status will be reliable.  */
33 #define ASSERT(expr) \
34   do                                                                         \
35     {                                                                        \
36       if (!(expr))                                                           \
37         {                                                                    \
38           fprintf (stderr, "%s:%d: assertion failed\n", __FILE__, __LINE__); \
39           fflush (stderr);                                                   \
40           abort ();                                                          \
41         }                                                                    \
42     }                                                                        \
43   while (0)
44
45 /* Code executed by the child process.  argv[1] = "child".  */
46 static int
47 child_main (int argc, char *argv[])
48 {
49   char buffer[1];
50   int i;
51   int fd;
52
53   ASSERT (argc == 3);
54
55   /* Read one byte from fd 0, and write its value plus one to fd 1.
56      fd 2 should be closed iff the argument is 1.  Check that no other file
57      descriptors leaked.  */
58
59   ASSERT (read (STDIN_FILENO, buffer, 1) == 1);
60
61   buffer[0]++;
62   ASSERT (write (STDOUT_FILENO, buffer, 1) == 1);
63
64   errno = 0;
65 #ifdef F_GETFL
66   /* Try to keep stderr open for better diagnostics.  */
67   i = fcntl (STDERR_FILENO, F_GETFL);
68 #else
69   /* But allow compilation on mingw.  You might need to disable this code for
70      debugging failures.  */
71   i = close (STDERR_FILENO);
72 #endif
73   switch (atoi (argv[2]))
74     {
75     case 0:
76       /* Expect fd 2 was open.  */
77       ASSERT (i >= 0);
78       break;
79     case 1:
80       /* Expect fd 2 was closed.  */
81       ASSERT (i < 0);
82       ASSERT (errno == EBADF);
83       break;
84     default:
85       ASSERT (false);
86     }
87
88   for (fd = 3; fd < 7; fd++)
89     {
90       errno = 0;
91       ASSERT (close (fd) == -1);
92       ASSERT (errno == EBADF);
93     }
94
95   return 0;
96 }
97
98 /* Create a bi-directional pipe to a test child, and validate that the
99    child program returns the expected output.  The child is the same
100    program as the parent ARGV0, but with different arguments.
101    STDERR_CLOSED is true if we have already closed fd 2.  */
102 static void
103 test_pipe (const char *argv0, bool stderr_closed)
104 {
105   int fd[2];
106   char *argv[4];
107   pid_t pid;
108   char buffer[2] = { 'a', 't' };
109
110   /* Set up child.  */
111   argv[0] = (char *) argv0;
112   argv[1] = (char *) "child";
113   argv[2] = (char *) (stderr_closed ? "1" : "0");
114   argv[3] = NULL;
115   pid = create_pipe_bidi (argv0, argv0, argv, false, true, true, fd);
116   ASSERT (0 <= pid);
117   ASSERT (STDERR_FILENO < fd[0]);
118   ASSERT (STDERR_FILENO < fd[1]);
119
120   /* Push child's input.  */
121   ASSERT (write (fd[1], buffer, 1) == 1);
122
123   /* Get child's output.  */
124   ASSERT (read (fd[0], buffer, 2) == 1);
125
126   /* Wait for child.  */
127   ASSERT (wait_subprocess (pid, argv0, true, false, true, true, NULL) == 0);
128   ASSERT (close (fd[0]) == 0);
129   ASSERT (close (fd[1]) == 0);
130
131   /* Check the result.  */
132   ASSERT (buffer[0] == 'b');
133   ASSERT (buffer[1] == 't');
134 }
135
136 /* Code executed by the parent process.  */
137 static int
138 parent_main (int argc, char *argv[])
139 {
140   int test;
141   int fd;
142
143   ASSERT (argc == 2);
144
145   /* Selectively close various standard fds, to verify the child process is
146      not impacted by this.  */
147   test = atoi (argv[1]);
148   switch (test)
149     {
150     case 0:
151       break;
152     case 1:
153       close (0);
154       break;
155     case 2:
156       close (1);
157       break;
158     case 3:
159       close (0);
160       close (1);
161       break;
162     case 4:
163       close (2);
164       break;
165     case 5:
166       close (0);
167       close (2);
168       break;
169     case 6:
170       close (1);
171       close (2);
172       break;
173     case 7:
174       close (0);
175       close (1);
176       close (2);
177       break;
178     default:
179       ASSERT (false);
180     }
181
182   /* Plug any file descriptor leaks inherited from outside world before
183      starting, so that child has a clean slate (at least for the fds that we
184      might be manipulating).  */
185   for (fd = 3; fd < 7; fd++)
186     close (fd);
187
188   test_pipe (argv[0], test >= 4);
189
190   return 0;
191 }
192
193 int
194 main (int argc, char *argv[])
195 {
196   ASSERT (argc >= 2);
197   if (strcmp (argv[1], "child") == 0)
198     return child_main (argc, argv);
199   else
200     return parent_main (argc, argv);
201 }