xref: /libuv/test/test-stdio-over-pipes.c (revision 011a1ac1)
1 /* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
2  *
3  * Permission is hereby granted, free of charge, to any person obtaining a copy
4  * of this software and associated documentation files (the "Software"), to
5  * deal in the Software without restriction, including without limitation the
6  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7  * sell copies of the Software, and to permit persons to whom the Software is
8  * furnished to do so, subject to the following conditions:
9  *
10  * The above copyright notice and this permission notice shall be included in
11  * all copies or substantial portions of the Software.
12  *
13  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
19  * IN THE SOFTWARE.
20  */
21 
22 #include "uv.h"
23 #include "task.h"
24 
25 #include <stdlib.h>
26 #include <string.h>
27 
28 
29 static char exepath[1024];
30 static size_t exepath_size = 1024;
31 static char* args[3];
32 static uv_process_options_t options;
33 static int close_cb_called;
34 static int exit_cb_called;
35 static int on_read_cb_called;
36 static int after_write_cb_called;
37 static uv_pipe_t in;
38 static uv_pipe_t out;
39 static uv_loop_t* loop;
40 #define OUTPUT_SIZE 1024
41 static char output[OUTPUT_SIZE];
42 static int output_used;
43 
44 
close_cb(uv_handle_t * handle)45 static void close_cb(uv_handle_t* handle) {
46   close_cb_called++;
47 }
48 
49 
exit_cb(uv_process_t * process,int64_t exit_status,int term_signal)50 static void exit_cb(uv_process_t* process,
51                     int64_t exit_status,
52                     int term_signal) {
53   printf("exit_cb\n");
54   exit_cb_called++;
55   ASSERT_OK(exit_status);
56   ASSERT_OK(term_signal);
57   uv_close((uv_handle_t*)process, close_cb);
58   uv_close((uv_handle_t*)&in, close_cb);
59   uv_close((uv_handle_t*)&out, close_cb);
60 }
61 
62 
init_process_options(char * test,uv_exit_cb exit_cb)63 static void init_process_options(char* test, uv_exit_cb exit_cb) {
64   int r = uv_exepath(exepath, &exepath_size);
65   ASSERT_OK(r);
66   exepath[exepath_size] = '\0';
67   args[0] = exepath;
68   args[1] = test;
69   args[2] = NULL;
70   options.file = exepath;
71   options.args = args;
72   options.exit_cb = exit_cb;
73 }
74 
75 
on_alloc(uv_handle_t * handle,size_t suggested_size,uv_buf_t * buf)76 static void on_alloc(uv_handle_t* handle,
77                      size_t suggested_size,
78                      uv_buf_t* buf) {
79   buf->base = output + output_used;
80   buf->len = OUTPUT_SIZE - output_used;
81 }
82 
83 
after_write(uv_write_t * req,int status)84 static void after_write(uv_write_t* req, int status) {
85   if (status) {
86     fprintf(stderr, "uv_write error: %s\n", uv_strerror(status));
87     ASSERT(0);
88   }
89 
90   /* Free the read/write buffer and the request */
91   free(req);
92 
93   after_write_cb_called++;
94 }
95 
96 
on_read(uv_stream_t * pipe,ssize_t nread,const uv_buf_t * rdbuf)97 static void on_read(uv_stream_t* pipe, ssize_t nread, const uv_buf_t* rdbuf) {
98   uv_write_t* req;
99   uv_buf_t wrbuf;
100   int r;
101 
102   ASSERT(nread > 0 || nread == UV_EOF);
103 
104   if (nread > 0) {
105     output_used += nread;
106     if (output_used % 12 == 0) {
107       ASSERT_OK(memcmp("hello world\n", output, 12));
108       wrbuf = uv_buf_init(output, 12);
109       req = malloc(sizeof(*req));
110       r = uv_write(req, (uv_stream_t*) &in, &wrbuf, 1, after_write);
111       ASSERT_OK(r);
112     }
113   }
114 
115   on_read_cb_called++;
116 }
117 
118 
test_stdio_over_pipes(int overlapped)119 static void test_stdio_over_pipes(int overlapped) {
120   int r;
121   uv_process_t process;
122   uv_stdio_container_t stdio[3];
123 
124   loop = uv_default_loop();
125 
126   init_process_options("stdio_over_pipes_helper", exit_cb);
127 
128   uv_pipe_init(loop, &out, 0);
129   uv_pipe_init(loop, &in, 0);
130 
131   options.stdio = stdio;
132   options.stdio[0].flags = UV_CREATE_PIPE | UV_READABLE_PIPE |
133       (overlapped ?  UV_OVERLAPPED_PIPE : 0);
134   options.stdio[0].data.stream = (uv_stream_t*) &in;
135   options.stdio[1].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE |
136       (overlapped ? UV_OVERLAPPED_PIPE : 0);
137   options.stdio[1].data.stream = (uv_stream_t*) &out;
138   options.stdio[2].flags = UV_INHERIT_FD;
139   options.stdio[2].data.fd = 2;
140   options.stdio_count = 3;
141 
142   r = uv_spawn(loop, &process, &options);
143   ASSERT_OK(r);
144 
145   r = uv_read_start((uv_stream_t*) &out, on_alloc, on_read);
146   ASSERT_OK(r);
147 
148   r = uv_run(loop, UV_RUN_DEFAULT);
149   ASSERT_OK(r);
150 
151   ASSERT_GT(on_read_cb_called, 1);
152   ASSERT_EQ(2, after_write_cb_called);
153   ASSERT_EQ(1, exit_cb_called);
154   ASSERT_EQ(3, close_cb_called);
155   ASSERT_OK(memcmp("hello world\nhello world\n", output, 24));
156   ASSERT_EQ(24, output_used);
157 
158   MAKE_VALGRIND_HAPPY(loop);
159 }
160 
TEST_IMPL(stdio_over_pipes)161 TEST_IMPL(stdio_over_pipes) {
162   test_stdio_over_pipes(0);
163   return 0;
164 }
165 
TEST_IMPL(stdio_emulate_iocp)166 TEST_IMPL(stdio_emulate_iocp) {
167   test_stdio_over_pipes(1);
168   return 0;
169 }
170 
171 
172 /* Everything here runs in a child process. */
173 
174 static int on_pipe_read_called;
175 static int after_write_called;
176 static uv_pipe_t stdin_pipe1;
177 static uv_pipe_t stdout_pipe1;
178 static uv_pipe_t stdin_pipe2;
179 static uv_pipe_t stdout_pipe2;
180 
on_pipe_read(uv_stream_t * pipe,ssize_t nread,const uv_buf_t * buf)181 static void on_pipe_read(uv_stream_t* pipe, ssize_t nread, const uv_buf_t* buf) {
182   ASSERT_GT(nread, 0);
183   ASSERT_OK(memcmp("hello world\n", buf->base, nread));
184   on_pipe_read_called++;
185 
186   free(buf->base);
187 
188   uv_read_stop(pipe);
189 }
190 
191 
after_pipe_write(uv_write_t * req,int status)192 static void after_pipe_write(uv_write_t* req, int status) {
193   ASSERT_OK(status);
194   after_write_called++;
195 }
196 
197 
on_read_alloc(uv_handle_t * handle,size_t suggested_size,uv_buf_t * buf)198 static void on_read_alloc(uv_handle_t* handle,
199                           size_t suggested_size,
200                           uv_buf_t* buf) {
201   buf->base = malloc(suggested_size);
202   buf->len = suggested_size;
203 }
204 
205 
stdio_over_pipes_helper(void)206 int stdio_over_pipes_helper(void) {
207   /* Write several buffers to test that the write order is preserved. */
208   char* buffers[] = {
209     "he",
210     "ll",
211     "o ",
212     "wo",
213     "rl",
214     "d",
215     "\n"
216   };
217 
218   uv_write_t write_req[ARRAY_SIZE(buffers)];
219   uv_buf_t buf[ARRAY_SIZE(buffers)];
220   unsigned int i;
221   int j;
222   int r;
223   uv_loop_t* loop = uv_default_loop();
224 
225   ASSERT_EQ(UV_NAMED_PIPE, uv_guess_handle(0));
226   ASSERT_EQ(UV_NAMED_PIPE, uv_guess_handle(1));
227 
228   r = uv_pipe_init(loop, &stdin_pipe1, 0);
229   ASSERT_OK(r);
230   r = uv_pipe_init(loop, &stdout_pipe1, 0);
231   ASSERT_OK(r);
232   r = uv_pipe_init(loop, &stdin_pipe2, 0);
233   ASSERT_OK(r);
234   r = uv_pipe_init(loop, &stdout_pipe2, 0);
235   ASSERT_OK(r);
236 
237   uv_pipe_open(&stdin_pipe1, 0);
238   uv_pipe_open(&stdout_pipe1, 1);
239   uv_pipe_open(&stdin_pipe2, 0);
240   uv_pipe_open(&stdout_pipe2, 1);
241 
242   for (j = 0; j < 2; j++) {
243     /* Unref both stdio handles to make sure that all writes complete. */
244     uv_unref((uv_handle_t*) &stdin_pipe1);
245     uv_unref((uv_handle_t*) &stdout_pipe1);
246     uv_unref((uv_handle_t*) &stdin_pipe2);
247     uv_unref((uv_handle_t*) &stdout_pipe2);
248 
249     for (i = 0; i < ARRAY_SIZE(buffers); i++) {
250       buf[i] = uv_buf_init((char*) buffers[i], strlen(buffers[i]));
251     }
252 
253     for (i = 0; i < ARRAY_SIZE(buffers); i++) {
254       r = uv_write(&write_req[i],
255                    (uv_stream_t*) (j == 0 ? &stdout_pipe1 : &stdout_pipe2),
256                    &buf[i],
257                    1,
258                    after_pipe_write);
259       ASSERT_OK(r);
260     }
261 
262     notify_parent_process();
263     uv_run(loop, UV_RUN_DEFAULT);
264 
265     ASSERT_EQ(after_write_called, 7 * (j + 1));
266     ASSERT_EQ(on_pipe_read_called, j);
267     ASSERT_OK(close_cb_called);
268 
269     uv_ref((uv_handle_t*) &stdout_pipe1);
270     uv_ref((uv_handle_t*) &stdin_pipe1);
271     uv_ref((uv_handle_t*) &stdout_pipe2);
272     uv_ref((uv_handle_t*) &stdin_pipe2);
273 
274     r = uv_read_start((uv_stream_t*) (j == 0 ? &stdin_pipe1 : &stdin_pipe2),
275                       on_read_alloc,
276                       on_pipe_read);
277     ASSERT_OK(r);
278 
279     uv_run(loop, UV_RUN_DEFAULT);
280 
281     ASSERT_EQ(after_write_called, 7 * (j + 1));
282     ASSERT_EQ(on_pipe_read_called, j + 1);
283     ASSERT_OK(close_cb_called);
284   }
285 
286   uv_close((uv_handle_t*)&stdin_pipe1, close_cb);
287   uv_close((uv_handle_t*)&stdout_pipe1, close_cb);
288   uv_close((uv_handle_t*)&stdin_pipe2, close_cb);
289   uv_close((uv_handle_t*)&stdout_pipe2, close_cb);
290 
291   uv_run(loop, UV_RUN_DEFAULT);
292 
293   ASSERT_EQ(14, after_write_called);
294   ASSERT_EQ(2, on_pipe_read_called);
295   ASSERT_EQ(4, close_cb_called);
296 
297   MAKE_VALGRIND_HAPPY(loop);
298   return 0;
299 }
300