test-pipe: make a bit more robust.
[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 #if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
24 /* Get declarations of the Win32 API functions.  */
25 # define WIN32_LEAN_AND_MEAN
26 # include <windows.h>
27 #endif
28
29 #include <errno.h>
30 #include <fcntl.h>
31 #include <stdbool.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35
36 /* Depending on arguments, this test intentionally closes stderr or
37    starts life with stderr closed.  So, we arrange to have fd 10
38    (outside the range of interesting fd's during the test) set up to
39    duplicate the original stderr.  */
40
41 static FILE *myerr;
42
43 #define ASSERT(expr) \
44   do                                                                         \
45     {                                                                        \
46       if (!(expr))                                                           \
47         {                                                                    \
48           fprintf (myerr, "%s:%d: assertion failed\n", __FILE__, __LINE__);  \
49           fflush (myerr);                                                    \
50           abort ();                                                          \
51         }                                                                    \
52     }                                                                        \
53   while (0)
54
55 /* Code executed by the child process.  argv[1] = "child".  */
56 static int
57 child_main (int argc, char *argv[])
58 {
59   char buffer[2] = { 's', 't' };
60   int fd;
61
62   ASSERT (argc == 3);
63
64   /* Read one byte from fd 0, and write its value plus one to fd 1.
65      fd 2 should be closed iff the argument is 1.  Check that no other file
66      descriptors leaked.  */
67
68   ASSERT (read (STDIN_FILENO, buffer, 2) == 1);
69
70   buffer[0]++;
71   ASSERT (write (STDOUT_FILENO, buffer, 1) == 1);
72
73 #if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
74   /* On Win32, the initial state of unassigned standard file descriptors is
75      that they are open but point to an INVALID_HANDLE_VALUE.  Thus
76      close (STDERR_FILENO) would always succeed.  */
77   switch (atoi (argv[2]))
78     {
79     case 0:
80       /* Expect fd 2 is open to a valid handle.  */
81       ASSERT ((HANDLE) _get_osfhandle (STDERR_FILENO) != INVALID_HANDLE_VALUE);
82       break;
83     case 1:
84       /* Expect fd 2 is pointing to INVALID_HANDLE_VALUE.  */
85       ASSERT ((HANDLE) _get_osfhandle (STDERR_FILENO) == INVALID_HANDLE_VALUE);
86       break;
87     default:
88       ASSERT (false);
89     }
90 #elif defined F_GETFL
91   /* On Unix, the initial state of unassigned standard file descriptors is
92      that they are closed.  */
93   {
94     int ret;
95     errno = 0;
96     ret = fcntl (STDERR_FILENO, F_GETFL);
97     switch (atoi (argv[2]))
98       {
99       case 0:
100         /* Expect fd 2 is open.  */
101         ASSERT (ret >= 0);
102         break;
103       case 1:
104         /* Expect fd 2 is closed.  */
105         ASSERT (ret < 0);
106         ASSERT (errno == EBADF);
107         break;
108       default:
109         ASSERT (false);
110       }
111   }
112 #endif
113
114   for (fd = 3; fd < 7; fd++)
115     {
116       errno = 0;
117       ASSERT (close (fd) == -1);
118       ASSERT (errno == EBADF);
119     }
120
121   return 0;
122 }
123
124 /* Create a bi-directional pipe to a test child, and validate that the
125    child program returns the expected output.  The child is the same
126    program as the parent ARGV0, but with different arguments.
127    STDERR_CLOSED is true if we have already closed fd 2.  */
128 static void
129 test_pipe (const char *argv0, bool stderr_closed)
130 {
131   int fd[2];
132   char *argv[4];
133   pid_t pid;
134   char buffer[2] = { 'a', 't' };
135
136   /* Set up child.  */
137   argv[0] = (char *) argv0;
138   argv[1] = (char *) "child";
139   argv[2] = (char *) (stderr_closed ? "1" : "0");
140   argv[3] = NULL;
141   pid = create_pipe_bidi (argv0, argv0, argv, false, true, true, fd);
142   ASSERT (0 <= pid);
143   ASSERT (STDERR_FILENO < fd[0]);
144   ASSERT (STDERR_FILENO < fd[1]);
145
146   /* Push child's input.  */
147   ASSERT (write (fd[1], buffer, 1) == 1);
148   ASSERT (close (fd[1]) == 0);
149
150   /* Get child's output.  */
151   ASSERT (read (fd[0], buffer, 2) == 1);
152
153   /* Wait for child.  */
154   ASSERT (wait_subprocess (pid, argv0, true, false, true, true, NULL) == 0);
155   ASSERT (close (fd[0]) == 0);
156
157   /* Check the result.  */
158   ASSERT (buffer[0] == 'b');
159   ASSERT (buffer[1] == 't');
160 }
161
162 /* Code executed by the parent process.  */
163 static int
164 parent_main (int argc, char *argv[])
165 {
166   int test;
167   int fd;
168
169   ASSERT (argc == 2);
170
171   /* Selectively close various standard fds, to verify the child process is
172      not impacted by this.  */
173   test = atoi (argv[1]);
174   switch (test)
175     {
176     case 0:
177       break;
178     case 1:
179       close (0);
180       break;
181     case 2:
182       close (1);
183       break;
184     case 3:
185       close (0);
186       close (1);
187       break;
188     case 4:
189       close (2);
190       break;
191     case 5:
192       close (0);
193       close (2);
194       break;
195     case 6:
196       close (1);
197       close (2);
198       break;
199     case 7:
200       close (0);
201       close (1);
202       close (2);
203       break;
204     default:
205       ASSERT (false);
206     }
207
208   /* Plug any file descriptor leaks inherited from outside world before
209      starting, so that child has a clean slate (at least for the fds that we
210      might be manipulating).  */
211   for (fd = 3; fd < 7; fd++)
212     close (fd);
213
214   test_pipe (argv[0], test >= 4);
215
216   return 0;
217 }
218
219 int
220 main (int argc, char *argv[])
221 {
222   if (argc < 2)
223     {
224       fprintf (stderr, "%s: need arguments\n", argv[0]);
225       return 2;
226     }
227   if (strcmp (argv[1], "child") == 0)
228     {
229       /* fd 2 might be closed, but fd 10 is the original stderr.  */
230       myerr = fdopen (10, "w");
231       if (!myerr)
232         return 2;
233       return child_main (argc, argv);
234     }
235   /* We might close fd 2 later, so save it in fd 10.  */
236   if (dup2 (STDERR_FILENO, 10) != 10
237       || (myerr = fdopen (10, "w")) == NULL)
238     return 2;
239   return parent_main (argc, argv);
240 }