1 | #include <gio/gio.h> |
2 | #include <string.h> |
3 | |
4 | #ifdef G_OS_UNIX |
5 | #include <sys/wait.h> |
6 | #include <glib-unix.h> |
7 | #include <gio/gunixinputstream.h> |
8 | #include <gio/gfiledescriptorbased.h> |
9 | #include <unistd.h> |
10 | #include <fcntl.h> |
11 | #endif |
12 | |
13 | /* We write 2^1 + 2^2 ... + 2^10 or 2047 copies of "Hello World!\n" |
14 | * ultimately |
15 | */ |
16 | #define TOTAL_HELLOS 2047 |
17 | #define HELLO_WORLD "hello world!\n" |
18 | |
19 | #ifdef G_OS_WIN32 |
20 | #define LINEEND "\r\n" |
21 | #define EXEEXT ".exe" |
22 | #define SPLICELEN (TOTAL_HELLOS * (strlen (HELLO_WORLD) + 1)) /* because \r */ |
23 | #else |
24 | #define LINEEND "\n" |
25 | #define EXEEXT |
26 | #define SPLICELEN (TOTAL_HELLOS * strlen (HELLO_WORLD)) |
27 | #endif |
28 | |
29 | static GPtrArray * |
30 | get_test_subprocess_args (const char *mode, |
31 | ...) G_GNUC_NULL_TERMINATED; |
32 | |
33 | static GPtrArray * |
34 | get_test_subprocess_args (const char *mode, |
35 | ...) |
36 | { |
37 | GPtrArray *ret; |
38 | char *path; |
39 | const char *binname; |
40 | va_list args; |
41 | gpointer arg; |
42 | |
43 | ret = g_ptr_array_new_with_free_func (element_free_func: g_free); |
44 | |
45 | #ifdef G_OS_WIN32 |
46 | binname = "gsubprocess-testprog.exe" ; |
47 | #else |
48 | binname = "gsubprocess-testprog" ; |
49 | #endif |
50 | |
51 | path = g_test_build_filename (file_type: G_TEST_BUILT, first_path: binname, NULL); |
52 | g_ptr_array_add (array: ret, data: path); |
53 | g_ptr_array_add (array: ret, data: g_strdup (str: mode)); |
54 | |
55 | va_start (args, mode); |
56 | while ((arg = va_arg (args, gpointer)) != NULL) |
57 | g_ptr_array_add (array: ret, data: g_strdup (str: arg)); |
58 | va_end (args); |
59 | |
60 | g_ptr_array_add (array: ret, NULL); |
61 | return ret; |
62 | } |
63 | |
64 | static void |
65 | test_noop (void) |
66 | { |
67 | GError *local_error = NULL; |
68 | GError **error = &local_error; |
69 | GPtrArray *args; |
70 | GSubprocess *proc; |
71 | |
72 | args = get_test_subprocess_args (mode: "noop" , NULL); |
73 | proc = g_subprocess_newv (argv: (const gchar * const *) args->pdata, flags: G_SUBPROCESS_FLAGS_NONE, error); |
74 | g_ptr_array_free (array: args, TRUE); |
75 | g_assert_no_error (local_error); |
76 | |
77 | g_subprocess_wait_check (subprocess: proc, NULL, error); |
78 | g_assert_no_error (local_error); |
79 | g_assert_true (g_subprocess_get_successful (proc)); |
80 | |
81 | g_object_unref (object: proc); |
82 | } |
83 | |
84 | static void |
85 | check_ready (GObject *source, |
86 | GAsyncResult *res, |
87 | gpointer user_data) |
88 | { |
89 | gboolean ret; |
90 | GError *error = NULL; |
91 | |
92 | ret = g_subprocess_wait_check_finish (G_SUBPROCESS (source), |
93 | result: res, |
94 | error: &error); |
95 | g_assert_true (ret); |
96 | g_assert_no_error (error); |
97 | |
98 | g_object_unref (object: source); |
99 | } |
100 | |
101 | static void |
102 | test_noop_all_to_null (void) |
103 | { |
104 | GError *local_error = NULL; |
105 | GError **error = &local_error; |
106 | GPtrArray *args; |
107 | GSubprocess *proc; |
108 | |
109 | args = get_test_subprocess_args (mode: "noop" , NULL); |
110 | proc = g_subprocess_newv (argv: (const gchar * const *) args->pdata, |
111 | flags: G_SUBPROCESS_FLAGS_STDOUT_SILENCE | G_SUBPROCESS_FLAGS_STDERR_SILENCE, |
112 | error); |
113 | g_ptr_array_free (array: args, TRUE); |
114 | g_assert_no_error (local_error); |
115 | |
116 | g_subprocess_wait_check_async (subprocess: proc, NULL, callback: check_ready, NULL); |
117 | } |
118 | |
119 | static void |
120 | test_noop_no_wait (void) |
121 | { |
122 | GError *local_error = NULL; |
123 | GError **error = &local_error; |
124 | GPtrArray *args; |
125 | GSubprocess *proc; |
126 | |
127 | args = get_test_subprocess_args (mode: "noop" , NULL); |
128 | proc = g_subprocess_newv (argv: (const gchar * const *) args->pdata, flags: G_SUBPROCESS_FLAGS_NONE, error); |
129 | g_ptr_array_free (array: args, TRUE); |
130 | g_assert_no_error (local_error); |
131 | |
132 | g_object_unref (object: proc); |
133 | } |
134 | |
135 | static void |
136 | test_noop_stdin_inherit (void) |
137 | { |
138 | GError *local_error = NULL; |
139 | GError **error = &local_error; |
140 | GPtrArray *args; |
141 | GSubprocess *proc; |
142 | |
143 | args = get_test_subprocess_args (mode: "noop" , NULL); |
144 | proc = g_subprocess_newv (argv: (const gchar * const *) args->pdata, flags: G_SUBPROCESS_FLAGS_STDIN_INHERIT, error); |
145 | g_ptr_array_free (array: args, TRUE); |
146 | g_assert_no_error (local_error); |
147 | |
148 | g_subprocess_wait_check (subprocess: proc, NULL, error); |
149 | g_assert_no_error (local_error); |
150 | |
151 | g_object_unref (object: proc); |
152 | } |
153 | |
154 | #ifdef G_OS_UNIX |
155 | static void |
156 | test_search_path (void) |
157 | { |
158 | GError *local_error = NULL; |
159 | GError **error = &local_error; |
160 | GSubprocess *proc; |
161 | |
162 | proc = g_subprocess_new (flags: G_SUBPROCESS_FLAGS_NONE, error, argv0: "true" , NULL); |
163 | g_assert_no_error (local_error); |
164 | |
165 | g_subprocess_wait_check (subprocess: proc, NULL, error); |
166 | g_assert_no_error (local_error); |
167 | |
168 | g_object_unref (object: proc); |
169 | } |
170 | #endif |
171 | |
172 | static void |
173 | test_exit1 (void) |
174 | { |
175 | GError *local_error = NULL; |
176 | GError **error = &local_error; |
177 | GPtrArray *args; |
178 | GSubprocess *proc; |
179 | |
180 | args = get_test_subprocess_args (mode: "exit1" , NULL); |
181 | proc = g_subprocess_newv (argv: (const gchar * const *) args->pdata, flags: G_SUBPROCESS_FLAGS_NONE, error); |
182 | g_ptr_array_free (array: args, TRUE); |
183 | g_assert_no_error (local_error); |
184 | |
185 | g_subprocess_wait_check (subprocess: proc, NULL, error); |
186 | g_assert_error (local_error, G_SPAWN_EXIT_ERROR, 1); |
187 | g_clear_error (err: error); |
188 | |
189 | g_object_unref (object: proc); |
190 | } |
191 | |
192 | typedef struct { |
193 | GMainLoop *loop; |
194 | GCancellable *cancellable; |
195 | gboolean cb_called; |
196 | } TestExit1CancelData; |
197 | |
198 | static gboolean |
199 | test_exit1_cancel_idle_quit_cb (gpointer user_data) |
200 | { |
201 | GMainLoop *loop = user_data; |
202 | g_main_loop_quit (loop); |
203 | return G_SOURCE_REMOVE; |
204 | } |
205 | |
206 | static void |
207 | test_exit1_cancel_wait_check_cb (GObject *source, |
208 | GAsyncResult *result, |
209 | gpointer user_data) |
210 | { |
211 | GSubprocess *subprocess = G_SUBPROCESS (source); |
212 | TestExit1CancelData *data = user_data; |
213 | gboolean ret; |
214 | GError *error = NULL; |
215 | |
216 | g_assert_false (data->cb_called); |
217 | data->cb_called = TRUE; |
218 | |
219 | ret = g_subprocess_wait_check_finish (subprocess, result, error: &error); |
220 | g_assert_false (ret); |
221 | g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED); |
222 | g_clear_error (err: &error); |
223 | |
224 | g_idle_add (function: test_exit1_cancel_idle_quit_cb, data: data->loop); |
225 | } |
226 | |
227 | static void |
228 | test_exit1_cancel (void) |
229 | { |
230 | GError *local_error = NULL; |
231 | GError **error = &local_error; |
232 | GPtrArray *args; |
233 | GSubprocess *proc; |
234 | TestExit1CancelData data = { 0 }; |
235 | |
236 | g_test_bug (bug_uri_snippet: "786456" ); |
237 | |
238 | args = get_test_subprocess_args (mode: "exit1" , NULL); |
239 | proc = g_subprocess_newv (argv: (const gchar * const *) args->pdata, flags: G_SUBPROCESS_FLAGS_NONE, error); |
240 | g_ptr_array_free (array: args, TRUE); |
241 | g_assert_no_error (local_error); |
242 | |
243 | data.loop = g_main_loop_new (NULL, FALSE); |
244 | data.cancellable = g_cancellable_new (); |
245 | g_subprocess_wait_check_async (subprocess: proc, cancellable: data.cancellable, callback: test_exit1_cancel_wait_check_cb, user_data: &data); |
246 | |
247 | g_subprocess_wait_check (subprocess: proc, NULL, error); |
248 | g_assert_error (local_error, G_SPAWN_EXIT_ERROR, 1); |
249 | g_clear_error (err: error); |
250 | |
251 | g_cancellable_cancel (cancellable: data.cancellable); |
252 | g_main_loop_run (loop: data.loop); |
253 | |
254 | g_object_unref (object: proc); |
255 | g_main_loop_unref (loop: data.loop); |
256 | g_clear_object (&data.cancellable); |
257 | } |
258 | |
259 | static void |
260 | test_exit1_cancel_in_cb_wait_check_cb (GObject *source, |
261 | GAsyncResult *result, |
262 | gpointer user_data) |
263 | { |
264 | GSubprocess *subprocess = G_SUBPROCESS (source); |
265 | TestExit1CancelData *data = user_data; |
266 | gboolean ret; |
267 | GError *error = NULL; |
268 | |
269 | g_assert_false (data->cb_called); |
270 | data->cb_called = TRUE; |
271 | |
272 | ret = g_subprocess_wait_check_finish (subprocess, result, error: &error); |
273 | g_assert_false (ret); |
274 | g_assert_error (error, G_SPAWN_EXIT_ERROR, 1); |
275 | g_clear_error (err: &error); |
276 | |
277 | g_cancellable_cancel (cancellable: data->cancellable); |
278 | |
279 | g_idle_add (function: test_exit1_cancel_idle_quit_cb, data: data->loop); |
280 | } |
281 | |
282 | static void |
283 | test_exit1_cancel_in_cb (void) |
284 | { |
285 | GError *local_error = NULL; |
286 | GError **error = &local_error; |
287 | GPtrArray *args; |
288 | GSubprocess *proc; |
289 | TestExit1CancelData data = { 0 }; |
290 | |
291 | g_test_bug (bug_uri_snippet: "786456" ); |
292 | |
293 | args = get_test_subprocess_args (mode: "exit1" , NULL); |
294 | proc = g_subprocess_newv (argv: (const gchar * const *) args->pdata, flags: G_SUBPROCESS_FLAGS_NONE, error); |
295 | g_ptr_array_free (array: args, TRUE); |
296 | g_assert_no_error (local_error); |
297 | |
298 | data.loop = g_main_loop_new (NULL, FALSE); |
299 | data.cancellable = g_cancellable_new (); |
300 | g_subprocess_wait_check_async (subprocess: proc, cancellable: data.cancellable, callback: test_exit1_cancel_in_cb_wait_check_cb, user_data: &data); |
301 | |
302 | g_subprocess_wait_check (subprocess: proc, NULL, error); |
303 | g_assert_error (local_error, G_SPAWN_EXIT_ERROR, 1); |
304 | g_clear_error (err: error); |
305 | |
306 | g_main_loop_run (loop: data.loop); |
307 | |
308 | g_object_unref (object: proc); |
309 | g_main_loop_unref (loop: data.loop); |
310 | g_clear_object (&data.cancellable); |
311 | } |
312 | |
313 | static gchar * |
314 | splice_to_string (GInputStream *stream, |
315 | GError **error) |
316 | { |
317 | GMemoryOutputStream *buffer = NULL; |
318 | char *ret = NULL; |
319 | |
320 | buffer = (GMemoryOutputStream*)g_memory_output_stream_new (NULL, size: 0, realloc_function: g_realloc, destroy_function: g_free); |
321 | if (g_output_stream_splice (stream: (GOutputStream*)buffer, source: stream, flags: 0, NULL, error) < 0) |
322 | goto out; |
323 | |
324 | if (!g_output_stream_write (stream: (GOutputStream*)buffer, buffer: "\0" , count: 1, NULL, error)) |
325 | goto out; |
326 | |
327 | if (!g_output_stream_close (stream: (GOutputStream*)buffer, NULL, error)) |
328 | goto out; |
329 | |
330 | ret = g_memory_output_stream_steal_data (ostream: buffer); |
331 | out: |
332 | g_clear_object (&buffer); |
333 | return ret; |
334 | } |
335 | |
336 | static void |
337 | test_echo1 (void) |
338 | { |
339 | GError *local_error = NULL; |
340 | GError **error = &local_error; |
341 | GSubprocess *proc; |
342 | GPtrArray *args; |
343 | GInputStream *stdout_stream; |
344 | gchar *result; |
345 | |
346 | args = get_test_subprocess_args (mode: "echo" , "hello" , "world!" , NULL); |
347 | proc = g_subprocess_newv (argv: (const gchar * const *) args->pdata, flags: G_SUBPROCESS_FLAGS_STDOUT_PIPE, error); |
348 | g_ptr_array_free (array: args, TRUE); |
349 | g_assert_no_error (local_error); |
350 | |
351 | stdout_stream = g_subprocess_get_stdout_pipe (subprocess: proc); |
352 | |
353 | result = splice_to_string (stream: stdout_stream, error); |
354 | g_assert_no_error (local_error); |
355 | |
356 | g_assert_cmpstr (result, ==, "hello" LINEEND "world!" LINEEND); |
357 | |
358 | g_free (mem: result); |
359 | g_object_unref (object: proc); |
360 | } |
361 | |
362 | #ifdef G_OS_UNIX |
363 | static void |
364 | test_echo_merged (void) |
365 | { |
366 | GError *local_error = NULL; |
367 | GError **error = &local_error; |
368 | GSubprocess *proc; |
369 | GPtrArray *args; |
370 | GInputStream *stdout_stream; |
371 | gchar *result; |
372 | |
373 | args = get_test_subprocess_args (mode: "echo-stdout-and-stderr" , "merge" , "this" , NULL); |
374 | proc = g_subprocess_newv (argv: (const gchar * const *) args->pdata, |
375 | flags: G_SUBPROCESS_FLAGS_STDOUT_PIPE | G_SUBPROCESS_FLAGS_STDERR_MERGE, |
376 | error); |
377 | g_ptr_array_free (array: args, TRUE); |
378 | g_assert_no_error (local_error); |
379 | |
380 | stdout_stream = g_subprocess_get_stdout_pipe (subprocess: proc); |
381 | result = splice_to_string (stream: stdout_stream, error); |
382 | g_assert_no_error (local_error); |
383 | |
384 | g_assert_cmpstr (result, ==, "merge\nmerge\nthis\nthis\n" ); |
385 | |
386 | g_free (mem: result); |
387 | g_object_unref (object: proc); |
388 | } |
389 | #endif |
390 | |
391 | typedef struct { |
392 | guint events_pending; |
393 | GMainLoop *loop; |
394 | } TestCatData; |
395 | |
396 | static void |
397 | test_cat_on_input_splice_complete (GObject *object, |
398 | GAsyncResult *result, |
399 | gpointer user_data) |
400 | { |
401 | TestCatData *data = user_data; |
402 | GError *error = NULL; |
403 | |
404 | (void)g_output_stream_splice_finish (stream: (GOutputStream*)object, result, error: &error); |
405 | g_assert_no_error (error); |
406 | |
407 | data->events_pending--; |
408 | if (data->events_pending == 0) |
409 | g_main_loop_quit (loop: data->loop); |
410 | } |
411 | |
412 | static void |
413 | test_cat_utf8 (void) |
414 | { |
415 | GError *local_error = NULL; |
416 | GError **error = &local_error; |
417 | GSubprocess *proc; |
418 | GPtrArray *args; |
419 | GBytes *input_buf; |
420 | GBytes *output_buf; |
421 | GInputStream *input_buf_stream = NULL; |
422 | GOutputStream *output_buf_stream = NULL; |
423 | GOutputStream *stdin_stream = NULL; |
424 | GInputStream *stdout_stream = NULL; |
425 | TestCatData data; |
426 | |
427 | memset (s: &data, c: 0, n: sizeof (data)); |
428 | data.loop = g_main_loop_new (NULL, TRUE); |
429 | |
430 | args = get_test_subprocess_args (mode: "cat" , NULL); |
431 | proc = g_subprocess_newv (argv: (const gchar * const *) args->pdata, |
432 | flags: G_SUBPROCESS_FLAGS_STDIN_PIPE | G_SUBPROCESS_FLAGS_STDOUT_PIPE, |
433 | error); |
434 | g_ptr_array_free (array: args, TRUE); |
435 | g_assert_no_error (local_error); |
436 | |
437 | stdin_stream = g_subprocess_get_stdin_pipe (subprocess: proc); |
438 | stdout_stream = g_subprocess_get_stdout_pipe (subprocess: proc); |
439 | |
440 | input_buf = g_bytes_new_static (data: "hello, world!" , size: strlen (s: "hello, world!" )); |
441 | input_buf_stream = g_memory_input_stream_new_from_bytes (bytes: input_buf); |
442 | g_bytes_unref (bytes: input_buf); |
443 | |
444 | output_buf_stream = g_memory_output_stream_new (NULL, size: 0, realloc_function: g_realloc, destroy_function: g_free); |
445 | |
446 | g_output_stream_splice_async (stream: stdin_stream, source: input_buf_stream, flags: G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE | G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET, |
447 | G_PRIORITY_DEFAULT, NULL, callback: test_cat_on_input_splice_complete, |
448 | user_data: &data); |
449 | data.events_pending++; |
450 | g_output_stream_splice_async (stream: output_buf_stream, source: stdout_stream, flags: G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE | G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET, |
451 | G_PRIORITY_DEFAULT, NULL, callback: test_cat_on_input_splice_complete, |
452 | user_data: &data); |
453 | data.events_pending++; |
454 | |
455 | g_main_loop_run (loop: data.loop); |
456 | |
457 | g_subprocess_wait_check (subprocess: proc, NULL, error); |
458 | g_assert_no_error (local_error); |
459 | |
460 | output_buf = g_memory_output_stream_steal_as_bytes (ostream: (GMemoryOutputStream*)output_buf_stream); |
461 | |
462 | g_assert_cmpmem (g_bytes_get_data (output_buf, NULL), |
463 | g_bytes_get_size (output_buf), |
464 | "hello, world!" , 13); |
465 | |
466 | g_bytes_unref (bytes: output_buf); |
467 | g_main_loop_unref (loop: data.loop); |
468 | g_object_unref (object: input_buf_stream); |
469 | g_object_unref (object: output_buf_stream); |
470 | g_object_unref (object: proc); |
471 | } |
472 | |
473 | static gpointer |
474 | cancel_soon (gpointer user_data) |
475 | { |
476 | GCancellable *cancellable = user_data; |
477 | |
478 | g_usleep (G_TIME_SPAN_SECOND); |
479 | g_cancellable_cancel (cancellable); |
480 | g_object_unref (object: cancellable); |
481 | |
482 | return NULL; |
483 | } |
484 | |
485 | static void |
486 | test_cat_eof (void) |
487 | { |
488 | GCancellable *cancellable; |
489 | GError *error = NULL; |
490 | GSubprocess *cat; |
491 | gboolean result; |
492 | gchar buffer; |
493 | gssize s; |
494 | |
495 | #ifdef G_OS_WIN32 |
496 | g_test_skip ("This test has not been ported to Win32" ); |
497 | return; |
498 | #endif |
499 | |
500 | /* Spawn 'cat' */ |
501 | cat = g_subprocess_new (flags: G_SUBPROCESS_FLAGS_STDIN_PIPE | G_SUBPROCESS_FLAGS_STDOUT_PIPE, error: &error, argv0: "cat" , NULL); |
502 | g_assert_no_error (error); |
503 | g_assert_nonnull (cat); |
504 | |
505 | /* Make sure that reading stdout blocks (until we cancel) */ |
506 | cancellable = g_cancellable_new (); |
507 | g_thread_unref (thread: g_thread_new (name: "cancel thread" , func: cancel_soon, g_object_ref (cancellable))); |
508 | s = g_input_stream_read (stream: g_subprocess_get_stdout_pipe (subprocess: cat), buffer: &buffer, count: sizeof buffer, cancellable, error: &error); |
509 | g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED); |
510 | g_assert_cmpint (s, ==, -1); |
511 | g_object_unref (object: cancellable); |
512 | g_clear_error (err: &error); |
513 | |
514 | /* Close the stream (EOF on cat's stdin) */ |
515 | result = g_output_stream_close (stream: g_subprocess_get_stdin_pipe (subprocess: cat), NULL, error: &error); |
516 | g_assert_no_error (error); |
517 | g_assert_true (result); |
518 | |
519 | /* Now check that reading cat's stdout gets us an EOF (since it quit) */ |
520 | s = g_input_stream_read (stream: g_subprocess_get_stdout_pipe (subprocess: cat), buffer: &buffer, count: sizeof buffer, NULL, error: &error); |
521 | g_assert_no_error (error); |
522 | g_assert_false (s); |
523 | |
524 | /* Check that the process has exited as a result of the EOF */ |
525 | result = g_subprocess_wait (subprocess: cat, NULL, error: &error); |
526 | g_assert_no_error (error); |
527 | g_assert_true (g_subprocess_get_if_exited (cat)); |
528 | g_assert_cmpint (g_subprocess_get_exit_status (cat), ==, 0); |
529 | g_assert_true (result); |
530 | |
531 | g_object_unref (object: cat); |
532 | } |
533 | |
534 | typedef struct { |
535 | guint events_pending; |
536 | gboolean caught_error; |
537 | GError *error; |
538 | GMainLoop *loop; |
539 | |
540 | gint counter; |
541 | GOutputStream *first_stdin; |
542 | } TestMultiSpliceData; |
543 | |
544 | static void |
545 | on_one_multi_splice_done (GObject *obj, |
546 | GAsyncResult *res, |
547 | gpointer user_data) |
548 | { |
549 | TestMultiSpliceData *data = user_data; |
550 | |
551 | if (!data->caught_error) |
552 | { |
553 | if (g_output_stream_splice_finish (stream: (GOutputStream*)obj, result: res, error: &data->error) < 0) |
554 | data->caught_error = TRUE; |
555 | } |
556 | |
557 | data->events_pending--; |
558 | if (data->events_pending == 0) |
559 | g_main_loop_quit (loop: data->loop); |
560 | } |
561 | |
562 | static gboolean |
563 | on_idle_multisplice (gpointer user_data) |
564 | { |
565 | TestMultiSpliceData *data = user_data; |
566 | |
567 | if (data->counter >= TOTAL_HELLOS || data->caught_error) |
568 | { |
569 | if (!g_output_stream_close (stream: data->first_stdin, NULL, error: &data->error)) |
570 | data->caught_error = TRUE; |
571 | data->events_pending--; |
572 | if (data->events_pending == 0) |
573 | { |
574 | g_main_loop_quit (loop: data->loop); |
575 | } |
576 | return FALSE; |
577 | } |
578 | else |
579 | { |
580 | int i; |
581 | for (i = 0; i < data->counter; i++) |
582 | { |
583 | gsize bytes_written; |
584 | if (!g_output_stream_write_all (stream: data->first_stdin, HELLO_WORLD, |
585 | count: strlen (HELLO_WORLD), bytes_written: &bytes_written, |
586 | NULL, error: &data->error)) |
587 | { |
588 | data->caught_error = TRUE; |
589 | return FALSE; |
590 | } |
591 | } |
592 | data->counter *= 2; |
593 | return TRUE; |
594 | } |
595 | } |
596 | |
597 | static void |
598 | on_subprocess_exited (GObject *object, |
599 | GAsyncResult *result, |
600 | gpointer user_data) |
601 | { |
602 | GSubprocess *subprocess = G_SUBPROCESS (object); |
603 | TestMultiSpliceData *data = user_data; |
604 | GError *error = NULL; |
605 | |
606 | if (!g_subprocess_wait_finish (subprocess, result, error: &error)) |
607 | { |
608 | if (!data->caught_error) |
609 | { |
610 | data->caught_error = TRUE; |
611 | g_propagate_error (dest: &data->error, src: error); |
612 | } |
613 | } |
614 | g_spawn_check_exit_status (exit_status: g_subprocess_get_exit_status (subprocess), error: &error); |
615 | g_assert_no_error (error); |
616 | data->events_pending--; |
617 | if (data->events_pending == 0) |
618 | g_main_loop_quit (loop: data->loop); |
619 | } |
620 | |
621 | static void |
622 | test_multi_1 (void) |
623 | { |
624 | GError *local_error = NULL; |
625 | GError **error = &local_error; |
626 | GPtrArray *args; |
627 | GSubprocessLauncher *launcher; |
628 | GSubprocess *first; |
629 | GSubprocess *second; |
630 | GSubprocess *third; |
631 | GOutputStream *first_stdin; |
632 | GInputStream *first_stdout; |
633 | GOutputStream *second_stdin; |
634 | GInputStream *second_stdout; |
635 | GOutputStream *third_stdin; |
636 | GInputStream *third_stdout; |
637 | GOutputStream *membuf; |
638 | TestMultiSpliceData data; |
639 | int splice_flags = G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE | G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET; |
640 | |
641 | args = get_test_subprocess_args (mode: "cat" , NULL); |
642 | launcher = g_subprocess_launcher_new (flags: G_SUBPROCESS_FLAGS_STDIN_PIPE | G_SUBPROCESS_FLAGS_STDOUT_PIPE); |
643 | first = g_subprocess_launcher_spawnv (self: launcher, argv: (const gchar * const *) args->pdata, error); |
644 | g_assert_no_error (local_error); |
645 | second = g_subprocess_launcher_spawnv (self: launcher, argv: (const gchar * const *) args->pdata, error); |
646 | g_assert_no_error (local_error); |
647 | third = g_subprocess_launcher_spawnv (self: launcher, argv: (const gchar * const *) args->pdata, error); |
648 | g_assert_no_error (local_error); |
649 | |
650 | g_ptr_array_free (array: args, TRUE); |
651 | |
652 | membuf = g_memory_output_stream_new (NULL, size: 0, realloc_function: g_realloc, destroy_function: g_free); |
653 | |
654 | first_stdin = g_subprocess_get_stdin_pipe (subprocess: first); |
655 | first_stdout = g_subprocess_get_stdout_pipe (subprocess: first); |
656 | second_stdin = g_subprocess_get_stdin_pipe (subprocess: second); |
657 | second_stdout = g_subprocess_get_stdout_pipe (subprocess: second); |
658 | third_stdin = g_subprocess_get_stdin_pipe (subprocess: third); |
659 | third_stdout = g_subprocess_get_stdout_pipe (subprocess: third); |
660 | |
661 | memset (s: &data, c: 0, n: sizeof (data)); |
662 | data.loop = g_main_loop_new (NULL, TRUE); |
663 | data.counter = 1; |
664 | data.first_stdin = first_stdin; |
665 | |
666 | data.events_pending++; |
667 | g_output_stream_splice_async (stream: second_stdin, source: first_stdout, flags: splice_flags, G_PRIORITY_DEFAULT, |
668 | NULL, callback: on_one_multi_splice_done, user_data: &data); |
669 | data.events_pending++; |
670 | g_output_stream_splice_async (stream: third_stdin, source: second_stdout, flags: splice_flags, G_PRIORITY_DEFAULT, |
671 | NULL, callback: on_one_multi_splice_done, user_data: &data); |
672 | data.events_pending++; |
673 | g_output_stream_splice_async (stream: membuf, source: third_stdout, flags: splice_flags, G_PRIORITY_DEFAULT, |
674 | NULL, callback: on_one_multi_splice_done, user_data: &data); |
675 | |
676 | data.events_pending++; |
677 | g_timeout_add (interval: 250, function: on_idle_multisplice, data: &data); |
678 | |
679 | data.events_pending++; |
680 | g_subprocess_wait_async (subprocess: first, NULL, callback: on_subprocess_exited, user_data: &data); |
681 | data.events_pending++; |
682 | g_subprocess_wait_async (subprocess: second, NULL, callback: on_subprocess_exited, user_data: &data); |
683 | data.events_pending++; |
684 | g_subprocess_wait_async (subprocess: third, NULL, callback: on_subprocess_exited, user_data: &data); |
685 | |
686 | g_main_loop_run (loop: data.loop); |
687 | |
688 | g_assert_false (data.caught_error); |
689 | g_assert_no_error (data.error); |
690 | |
691 | g_assert_cmpint (g_memory_output_stream_get_data_size ((GMemoryOutputStream*)membuf), ==, SPLICELEN); |
692 | |
693 | g_main_loop_unref (loop: data.loop); |
694 | g_object_unref (object: membuf); |
695 | g_object_unref (object: launcher); |
696 | g_object_unref (object: first); |
697 | g_object_unref (object: second); |
698 | g_object_unref (object: third); |
699 | } |
700 | |
701 | typedef struct { |
702 | GSubprocessFlags flags; |
703 | gboolean is_utf8; |
704 | gboolean running; |
705 | GError *error; |
706 | } TestAsyncCommunicateData; |
707 | |
708 | static void |
709 | on_communicate_complete (GObject *proc, |
710 | GAsyncResult *result, |
711 | gpointer user_data) |
712 | { |
713 | TestAsyncCommunicateData *data = user_data; |
714 | GBytes *stdout_bytes = NULL, *stderr_bytes = NULL; |
715 | char *stdout_str = NULL, *stderr_str = NULL; |
716 | const guint8 *stdout_data; |
717 | gsize stdout_len; |
718 | |
719 | data->running = FALSE; |
720 | if (data->is_utf8) |
721 | (void) g_subprocess_communicate_utf8_finish (subprocess: (GSubprocess*)proc, result, |
722 | stdout_buf: &stdout_str, stderr_buf: &stderr_str, error: &data->error); |
723 | else |
724 | (void) g_subprocess_communicate_finish (subprocess: (GSubprocess*)proc, result, |
725 | stdout_buf: &stdout_bytes, stderr_buf: &stderr_bytes, error: &data->error); |
726 | if (data->error) |
727 | return; |
728 | |
729 | if (data->flags & G_SUBPROCESS_FLAGS_STDOUT_PIPE) |
730 | { |
731 | if (data->is_utf8) |
732 | { |
733 | stdout_data = (guint8*)stdout_str; |
734 | stdout_len = strlen (s: stdout_str); |
735 | } |
736 | else |
737 | stdout_data = g_bytes_get_data (bytes: stdout_bytes, size: &stdout_len); |
738 | |
739 | g_assert_cmpmem (stdout_data, stdout_len, "# hello world" LINEEND, 13 + strlen (LINEEND)); |
740 | } |
741 | else |
742 | { |
743 | g_assert_null (stdout_str); |
744 | g_assert_null (stdout_bytes); |
745 | } |
746 | |
747 | if (data->flags & G_SUBPROCESS_FLAGS_STDERR_PIPE) |
748 | { |
749 | if (data->is_utf8) |
750 | g_assert_nonnull (stderr_str); |
751 | else |
752 | g_assert_nonnull (stderr_bytes); |
753 | } |
754 | else |
755 | { |
756 | g_assert_null (stderr_str); |
757 | g_assert_null (stderr_bytes); |
758 | } |
759 | |
760 | g_clear_pointer (&stdout_bytes, g_bytes_unref); |
761 | g_clear_pointer (&stderr_bytes, g_bytes_unref); |
762 | g_free (mem: stdout_str); |
763 | g_free (mem: stderr_str); |
764 | } |
765 | |
766 | /* Test g_subprocess_communicate_async() works correctly with a variety of flags, |
767 | * as passed in via @test_data. */ |
768 | static void |
769 | test_communicate_async (gconstpointer test_data) |
770 | { |
771 | GSubprocessFlags flags = GPOINTER_TO_INT (test_data); |
772 | GError *error = NULL; |
773 | GPtrArray *args; |
774 | TestAsyncCommunicateData data = { flags, 0, }; |
775 | GSubprocess *proc; |
776 | GCancellable *cancellable = NULL; |
777 | GBytes *input; |
778 | const char *hellostring; |
779 | |
780 | args = get_test_subprocess_args (mode: "cat" , NULL); |
781 | proc = g_subprocess_newv (argv: (const gchar* const*)args->pdata, |
782 | flags: G_SUBPROCESS_FLAGS_STDIN_PIPE | flags, |
783 | error: &error); |
784 | g_assert_no_error (error); |
785 | g_ptr_array_free (array: args, TRUE); |
786 | |
787 | /* Include a leading hash and trailing newline so that if this gets onto the |
788 | * test’s stdout, it doesn’t mess up TAP output. */ |
789 | hellostring = "# hello world\n" ; |
790 | input = g_bytes_new_static (data: hellostring, size: strlen (s: hellostring)); |
791 | |
792 | g_subprocess_communicate_async (subprocess: proc, stdin_buf: input, |
793 | cancellable, |
794 | callback: on_communicate_complete, |
795 | user_data: &data); |
796 | |
797 | data.running = TRUE; |
798 | while (data.running) |
799 | g_main_context_iteration (NULL, TRUE); |
800 | |
801 | g_assert_no_error (data.error); |
802 | |
803 | g_bytes_unref (bytes: input); |
804 | g_object_unref (object: proc); |
805 | } |
806 | |
807 | /* Test g_subprocess_communicate() works correctly with a variety of flags, |
808 | * as passed in via @test_data. */ |
809 | static void |
810 | test_communicate (gconstpointer test_data) |
811 | { |
812 | GSubprocessFlags flags = GPOINTER_TO_INT (test_data); |
813 | GError *error = NULL; |
814 | GPtrArray *args; |
815 | GSubprocess *proc; |
816 | GCancellable *cancellable = NULL; |
817 | GBytes *input; |
818 | const gchar *hellostring; |
819 | GBytes *stdout_bytes, *stderr_bytes; |
820 | const gchar *stdout_data; |
821 | gsize stdout_len; |
822 | |
823 | args = get_test_subprocess_args (mode: "cat" , NULL); |
824 | proc = g_subprocess_newv (argv: (const gchar* const*)args->pdata, |
825 | flags: G_SUBPROCESS_FLAGS_STDIN_PIPE | flags, |
826 | error: &error); |
827 | g_assert_no_error (error); |
828 | g_ptr_array_free (array: args, TRUE); |
829 | |
830 | /* Include a leading hash and trailing newline so that if this gets onto the |
831 | * test’s stdout, it doesn’t mess up TAP output. */ |
832 | hellostring = "# hello world\n" ; |
833 | input = g_bytes_new_static (data: hellostring, size: strlen (s: hellostring)); |
834 | |
835 | g_subprocess_communicate (subprocess: proc, stdin_buf: input, cancellable, stdout_buf: &stdout_bytes, stderr_buf: &stderr_bytes, error: &error); |
836 | g_assert_no_error (error); |
837 | |
838 | if (flags & G_SUBPROCESS_FLAGS_STDOUT_PIPE) |
839 | { |
840 | stdout_data = g_bytes_get_data (bytes: stdout_bytes, size: &stdout_len); |
841 | g_assert_cmpmem (stdout_data, stdout_len, "# hello world" LINEEND, 13 + strlen (LINEEND)); |
842 | } |
843 | else |
844 | g_assert_null (stdout_bytes); |
845 | if (flags & G_SUBPROCESS_FLAGS_STDERR_PIPE) |
846 | g_assert_nonnull (stderr_bytes); |
847 | else |
848 | g_assert_null (stderr_bytes); |
849 | |
850 | g_bytes_unref (bytes: input); |
851 | g_clear_pointer (&stdout_bytes, g_bytes_unref); |
852 | g_clear_pointer (&stderr_bytes, g_bytes_unref); |
853 | g_object_unref (object: proc); |
854 | } |
855 | |
856 | typedef struct { |
857 | GSubprocess *proc; |
858 | GCancellable *cancellable; |
859 | gboolean is_utf8; |
860 | gboolean running; |
861 | GError *error; |
862 | } TestCancelledCommunicateData; |
863 | |
864 | static gboolean |
865 | on_test_communicate_cancelled_idle (gpointer user_data) |
866 | { |
867 | TestCancelledCommunicateData *data = user_data; |
868 | GBytes *input; |
869 | const gchar *hellostring; |
870 | GBytes *stdout_bytes = NULL, *stderr_bytes = NULL; |
871 | gchar *stdout_buf = NULL, *stderr_buf = NULL; |
872 | |
873 | /* Include a leading hash and trailing newline so that if this gets onto the |
874 | * test’s stdout, it doesn’t mess up TAP output. */ |
875 | hellostring = "# hello world\n" ; |
876 | input = g_bytes_new_static (data: hellostring, size: strlen (s: hellostring)); |
877 | |
878 | if (data->is_utf8) |
879 | g_subprocess_communicate_utf8 (subprocess: data->proc, stdin_buf: hellostring, cancellable: data->cancellable, |
880 | stdout_buf: &stdout_buf, stderr_buf: &stderr_buf, error: &data->error); |
881 | else |
882 | g_subprocess_communicate (subprocess: data->proc, stdin_buf: input, cancellable: data->cancellable, stdout_buf: &stdout_bytes, |
883 | stderr_buf: &stderr_bytes, error: &data->error); |
884 | |
885 | data->running = FALSE; |
886 | |
887 | if (data->is_utf8) |
888 | { |
889 | g_assert_null (stdout_buf); |
890 | g_assert_null (stderr_buf); |
891 | } |
892 | else |
893 | { |
894 | g_assert_null (stdout_bytes); |
895 | g_assert_null (stderr_bytes); |
896 | } |
897 | |
898 | g_bytes_unref (bytes: input); |
899 | |
900 | return G_SOURCE_REMOVE; |
901 | } |
902 | |
903 | /* Test g_subprocess_communicate() can be cancelled correctly */ |
904 | static void |
905 | test_communicate_cancelled (gconstpointer test_data) |
906 | { |
907 | GSubprocessFlags flags = GPOINTER_TO_INT (test_data); |
908 | GPtrArray *args; |
909 | GSubprocess *proc; |
910 | GCancellable *cancellable = NULL; |
911 | GError *error = NULL; |
912 | TestCancelledCommunicateData data = { 0 }; |
913 | |
914 | args = get_test_subprocess_args (mode: "cat" , NULL); |
915 | proc = g_subprocess_newv (argv: (const gchar* const*)args->pdata, |
916 | flags: G_SUBPROCESS_FLAGS_STDIN_PIPE | flags, |
917 | error: &error); |
918 | g_assert_no_error (error); |
919 | g_ptr_array_free (array: args, TRUE); |
920 | |
921 | cancellable = g_cancellable_new (); |
922 | |
923 | data.proc = proc; |
924 | data.cancellable = cancellable; |
925 | data.error = error; |
926 | |
927 | g_cancellable_cancel (cancellable); |
928 | g_idle_add (function: on_test_communicate_cancelled_idle, data: &data); |
929 | |
930 | data.running = TRUE; |
931 | while (data.running) |
932 | g_main_context_iteration (NULL, TRUE); |
933 | |
934 | g_assert_error (data.error, G_IO_ERROR, G_IO_ERROR_CANCELLED); |
935 | g_clear_error (err: &data.error); |
936 | |
937 | g_object_unref (object: cancellable); |
938 | g_object_unref (object: proc); |
939 | } |
940 | |
941 | static void |
942 | on_communicate_cancelled_complete (GObject *proc, |
943 | GAsyncResult *result, |
944 | gpointer user_data) |
945 | { |
946 | TestAsyncCommunicateData *data = user_data; |
947 | GBytes *stdout_bytes = NULL, *stderr_bytes = NULL; |
948 | char *stdout_str = NULL, *stderr_str = NULL; |
949 | |
950 | data->running = FALSE; |
951 | if (data->is_utf8) |
952 | (void) g_subprocess_communicate_utf8_finish (subprocess: (GSubprocess*)proc, result, |
953 | stdout_buf: &stdout_str, stderr_buf: &stderr_str, error: &data->error); |
954 | else |
955 | (void) g_subprocess_communicate_finish (subprocess: (GSubprocess*)proc, result, |
956 | stdout_buf: &stdout_bytes, stderr_buf: &stderr_bytes, error: &data->error); |
957 | |
958 | if (data->is_utf8) |
959 | { |
960 | g_assert_null (stdout_str); |
961 | g_assert_null (stderr_str); |
962 | } |
963 | else |
964 | { |
965 | g_assert_null (stdout_bytes); |
966 | g_assert_null (stderr_bytes); |
967 | } |
968 | } |
969 | |
970 | /* Test g_subprocess_communicate_async() can be cancelled correctly, |
971 | * as passed in via @test_data. */ |
972 | static void |
973 | test_communicate_cancelled_async (gconstpointer test_data) |
974 | { |
975 | GSubprocessFlags flags = GPOINTER_TO_INT (test_data); |
976 | GError *error = NULL; |
977 | GPtrArray *args; |
978 | TestAsyncCommunicateData data = { 0 }; |
979 | GSubprocess *proc; |
980 | GCancellable *cancellable = NULL; |
981 | GBytes *input; |
982 | const char *hellostring; |
983 | |
984 | args = get_test_subprocess_args (mode: "cat" , NULL); |
985 | proc = g_subprocess_newv (argv: (const gchar* const*)args->pdata, |
986 | flags: G_SUBPROCESS_FLAGS_STDIN_PIPE | flags, |
987 | error: &error); |
988 | g_assert_no_error (error); |
989 | g_ptr_array_free (array: args, TRUE); |
990 | |
991 | /* Include a leading hash and trailing newline so that if this gets onto the |
992 | * test’s stdout, it doesn’t mess up TAP output. */ |
993 | hellostring = "# hello world\n" ; |
994 | input = g_bytes_new_static (data: hellostring, size: strlen (s: hellostring)); |
995 | |
996 | cancellable = g_cancellable_new (); |
997 | |
998 | g_subprocess_communicate_async (subprocess: proc, stdin_buf: input, |
999 | cancellable, |
1000 | callback: on_communicate_cancelled_complete, |
1001 | user_data: &data); |
1002 | |
1003 | g_cancellable_cancel (cancellable); |
1004 | |
1005 | data.running = TRUE; |
1006 | while (data.running) |
1007 | g_main_context_iteration (NULL, TRUE); |
1008 | |
1009 | g_assert_error (data.error, G_IO_ERROR, G_IO_ERROR_CANCELLED); |
1010 | g_clear_error (err: &data.error); |
1011 | |
1012 | g_bytes_unref (bytes: input); |
1013 | g_object_unref (object: cancellable); |
1014 | g_object_unref (object: proc); |
1015 | } |
1016 | |
1017 | /* Test g_subprocess_communicate_utf8_async() works correctly with a variety of |
1018 | * flags, as passed in via @test_data. */ |
1019 | static void |
1020 | test_communicate_utf8_async (gconstpointer test_data) |
1021 | { |
1022 | GSubprocessFlags flags = GPOINTER_TO_INT (test_data); |
1023 | GError *error = NULL; |
1024 | GPtrArray *args; |
1025 | TestAsyncCommunicateData data = { flags, 0, }; |
1026 | GSubprocess *proc; |
1027 | GCancellable *cancellable = NULL; |
1028 | |
1029 | args = get_test_subprocess_args (mode: "cat" , NULL); |
1030 | proc = g_subprocess_newv (argv: (const gchar* const*)args->pdata, |
1031 | flags: G_SUBPROCESS_FLAGS_STDIN_PIPE | flags, |
1032 | error: &error); |
1033 | g_assert_no_error (error); |
1034 | g_ptr_array_free (array: args, TRUE); |
1035 | |
1036 | data.is_utf8 = TRUE; |
1037 | g_subprocess_communicate_utf8_async (subprocess: proc, stdin_buf: "# hello world\n" , |
1038 | cancellable, |
1039 | callback: on_communicate_complete, |
1040 | user_data: &data); |
1041 | |
1042 | data.running = TRUE; |
1043 | while (data.running) |
1044 | g_main_context_iteration (NULL, TRUE); |
1045 | |
1046 | g_assert_no_error (data.error); |
1047 | |
1048 | g_object_unref (object: proc); |
1049 | } |
1050 | |
1051 | /* Test g_subprocess_communicate_utf8_async() can be cancelled correctly. */ |
1052 | static void |
1053 | test_communicate_utf8_cancelled_async (gconstpointer test_data) |
1054 | { |
1055 | GSubprocessFlags flags = GPOINTER_TO_INT (test_data); |
1056 | GError *error = NULL; |
1057 | GPtrArray *args; |
1058 | TestAsyncCommunicateData data = { flags, 0, }; |
1059 | GSubprocess *proc; |
1060 | GCancellable *cancellable = NULL; |
1061 | |
1062 | args = get_test_subprocess_args (mode: "cat" , NULL); |
1063 | proc = g_subprocess_newv (argv: (const gchar* const*)args->pdata, |
1064 | flags: G_SUBPROCESS_FLAGS_STDIN_PIPE | flags, |
1065 | error: &error); |
1066 | g_assert_no_error (error); |
1067 | g_ptr_array_free (array: args, TRUE); |
1068 | |
1069 | cancellable = g_cancellable_new (); |
1070 | data.is_utf8 = TRUE; |
1071 | g_subprocess_communicate_utf8_async (subprocess: proc, stdin_buf: "# hello world\n" , |
1072 | cancellable, |
1073 | callback: on_communicate_cancelled_complete, |
1074 | user_data: &data); |
1075 | |
1076 | g_cancellable_cancel (cancellable); |
1077 | |
1078 | data.running = TRUE; |
1079 | while (data.running) |
1080 | g_main_context_iteration (NULL, TRUE); |
1081 | |
1082 | g_assert_error (data.error, G_IO_ERROR, G_IO_ERROR_CANCELLED); |
1083 | g_clear_error (err: &data.error); |
1084 | |
1085 | g_object_unref (object: cancellable); |
1086 | g_object_unref (object: proc); |
1087 | } |
1088 | |
1089 | /* Test g_subprocess_communicate_utf8() works correctly with a variety of flags, |
1090 | * as passed in via @test_data. */ |
1091 | static void |
1092 | test_communicate_utf8 (gconstpointer test_data) |
1093 | { |
1094 | GSubprocessFlags flags = GPOINTER_TO_INT (test_data); |
1095 | GError *error = NULL; |
1096 | GPtrArray *args; |
1097 | GSubprocess *proc; |
1098 | GCancellable *cancellable = NULL; |
1099 | const gchar *stdin_buf; |
1100 | gchar *stdout_buf, *stderr_buf; |
1101 | |
1102 | args = get_test_subprocess_args (mode: "cat" , NULL); |
1103 | proc = g_subprocess_newv (argv: (const gchar* const*)args->pdata, |
1104 | flags: G_SUBPROCESS_FLAGS_STDIN_PIPE | flags, |
1105 | error: &error); |
1106 | g_assert_no_error (error); |
1107 | g_ptr_array_free (array: args, TRUE); |
1108 | |
1109 | /* Include a leading hash and trailing newline so that if this gets onto the |
1110 | * test’s stdout, it doesn’t mess up TAP output. */ |
1111 | stdin_buf = "# hello world\n" ; |
1112 | |
1113 | g_subprocess_communicate_utf8 (subprocess: proc, stdin_buf, cancellable, stdout_buf: &stdout_buf, stderr_buf: &stderr_buf, error: &error); |
1114 | g_assert_no_error (error); |
1115 | |
1116 | if (flags & G_SUBPROCESS_FLAGS_STDOUT_PIPE) |
1117 | g_assert_cmpstr (stdout_buf, ==, "# hello world" LINEEND); |
1118 | else |
1119 | g_assert_null (stdout_buf); |
1120 | if (flags & G_SUBPROCESS_FLAGS_STDERR_PIPE) |
1121 | g_assert_nonnull (stderr_buf); |
1122 | else g_assert_null (stderr_buf); |
1123 | |
1124 | g_free (mem: stdout_buf); |
1125 | g_free (mem: stderr_buf); |
1126 | g_object_unref (object: proc); |
1127 | } |
1128 | |
1129 | /* Test g_subprocess_communicate_utf8() can be cancelled correctly */ |
1130 | static void |
1131 | test_communicate_utf8_cancelled (gconstpointer test_data) |
1132 | { |
1133 | GSubprocessFlags flags = GPOINTER_TO_INT (test_data); |
1134 | GPtrArray *args; |
1135 | GSubprocess *proc; |
1136 | GCancellable *cancellable = NULL; |
1137 | GError *error = NULL; |
1138 | TestCancelledCommunicateData data = { 0 }; |
1139 | |
1140 | args = get_test_subprocess_args (mode: "cat" , NULL); |
1141 | proc = g_subprocess_newv (argv: (const gchar* const*)args->pdata, |
1142 | flags: G_SUBPROCESS_FLAGS_STDIN_PIPE | flags, |
1143 | error: &error); |
1144 | g_assert_no_error (error); |
1145 | g_ptr_array_free (array: args, TRUE); |
1146 | |
1147 | cancellable = g_cancellable_new (); |
1148 | |
1149 | data.proc = proc; |
1150 | data.cancellable = cancellable; |
1151 | data.error = error; |
1152 | |
1153 | g_cancellable_cancel (cancellable); |
1154 | g_idle_add (function: on_test_communicate_cancelled_idle, data: &data); |
1155 | |
1156 | data.is_utf8 = TRUE; |
1157 | data.running = TRUE; |
1158 | while (data.running) |
1159 | g_main_context_iteration (NULL, TRUE); |
1160 | |
1161 | g_assert_error (data.error, G_IO_ERROR, G_IO_ERROR_CANCELLED); |
1162 | g_clear_error (err: &data.error); |
1163 | |
1164 | g_object_unref (object: cancellable); |
1165 | g_object_unref (object: proc); |
1166 | } |
1167 | |
1168 | static void |
1169 | test_communicate_nothing (void) |
1170 | { |
1171 | GError *error = NULL; |
1172 | GPtrArray *args; |
1173 | GSubprocess *proc; |
1174 | GCancellable *cancellable = NULL; |
1175 | gchar *stdout_buf; |
1176 | |
1177 | args = get_test_subprocess_args (mode: "cat" , NULL); |
1178 | proc = g_subprocess_newv (argv: (const gchar* const*)args->pdata, |
1179 | flags: G_SUBPROCESS_FLAGS_STDIN_PIPE |
1180 | | G_SUBPROCESS_FLAGS_STDOUT_PIPE |
1181 | | G_SUBPROCESS_FLAGS_STDERR_MERGE, |
1182 | error: &error); |
1183 | g_assert_no_error (error); |
1184 | g_ptr_array_free (array: args, TRUE); |
1185 | |
1186 | g_subprocess_communicate_utf8 (subprocess: proc, stdin_buf: "" , cancellable, stdout_buf: &stdout_buf, NULL, error: &error); |
1187 | g_assert_no_error (error); |
1188 | |
1189 | g_assert_cmpstr (stdout_buf, ==, "" ); |
1190 | |
1191 | g_free (mem: stdout_buf); |
1192 | |
1193 | g_object_unref (object: proc); |
1194 | } |
1195 | |
1196 | static void |
1197 | test_communicate_utf8_async_invalid (void) |
1198 | { |
1199 | GSubprocessFlags flags = G_SUBPROCESS_FLAGS_STDOUT_PIPE; |
1200 | GError *error = NULL; |
1201 | GPtrArray *args; |
1202 | TestAsyncCommunicateData data = { flags, 0, }; |
1203 | GSubprocess *proc; |
1204 | GCancellable *cancellable = NULL; |
1205 | |
1206 | args = get_test_subprocess_args (mode: "cat" , NULL); |
1207 | proc = g_subprocess_newv (argv: (const gchar* const*)args->pdata, |
1208 | flags: G_SUBPROCESS_FLAGS_STDIN_PIPE | flags, |
1209 | error: &error); |
1210 | g_assert_no_error (error); |
1211 | g_ptr_array_free (array: args, TRUE); |
1212 | |
1213 | data.is_utf8 = TRUE; |
1214 | g_subprocess_communicate_utf8_async (subprocess: proc, stdin_buf: "\xFF\xFF" , |
1215 | cancellable, |
1216 | callback: on_communicate_complete, |
1217 | user_data: &data); |
1218 | |
1219 | data.running = TRUE; |
1220 | while (data.running) |
1221 | g_main_context_iteration (NULL, TRUE); |
1222 | |
1223 | g_assert_error (data.error, G_IO_ERROR, G_IO_ERROR_FAILED); |
1224 | g_error_free (error: data.error); |
1225 | |
1226 | g_object_unref (object: proc); |
1227 | } |
1228 | |
1229 | /* Test that invalid UTF-8 received using g_subprocess_communicate_utf8() |
1230 | * results in an error. */ |
1231 | static void |
1232 | test_communicate_utf8_invalid (void) |
1233 | { |
1234 | GSubprocessFlags flags = G_SUBPROCESS_FLAGS_STDOUT_PIPE; |
1235 | GError *local_error = NULL; |
1236 | gboolean ret; |
1237 | GPtrArray *args; |
1238 | gchar *stdout_str = NULL, *stderr_str = NULL; |
1239 | GSubprocess *proc; |
1240 | |
1241 | args = get_test_subprocess_args (mode: "cat" , NULL); |
1242 | proc = g_subprocess_newv (argv: (const gchar* const*)args->pdata, |
1243 | flags: G_SUBPROCESS_FLAGS_STDIN_PIPE | flags, |
1244 | error: &local_error); |
1245 | g_assert_no_error (local_error); |
1246 | g_ptr_array_free (array: args, TRUE); |
1247 | |
1248 | ret = g_subprocess_communicate_utf8 (subprocess: proc, stdin_buf: "\xFF\xFF" , NULL, |
1249 | stdout_buf: &stdout_str, stderr_buf: &stderr_str, error: &local_error); |
1250 | g_assert_error (local_error, G_IO_ERROR, G_IO_ERROR_FAILED); |
1251 | g_error_free (error: local_error); |
1252 | g_assert_false (ret); |
1253 | |
1254 | g_assert_null (stdout_str); |
1255 | g_assert_null (stderr_str); |
1256 | |
1257 | g_object_unref (object: proc); |
1258 | } |
1259 | |
1260 | static gboolean |
1261 | send_terminate (gpointer user_data) |
1262 | { |
1263 | GSubprocess *proc = user_data; |
1264 | |
1265 | g_subprocess_force_exit (subprocess: proc); |
1266 | |
1267 | return FALSE; |
1268 | } |
1269 | |
1270 | static void |
1271 | on_request_quit_exited (GObject *object, |
1272 | GAsyncResult *result, |
1273 | gpointer user_data) |
1274 | { |
1275 | GSubprocess *subprocess = G_SUBPROCESS (object); |
1276 | GError *error = NULL; |
1277 | |
1278 | g_subprocess_wait_finish (subprocess, result, error: &error); |
1279 | g_assert_no_error (error); |
1280 | #ifdef G_OS_UNIX |
1281 | g_assert_true (g_subprocess_get_if_signaled (subprocess)); |
1282 | g_assert_cmpint (g_subprocess_get_term_sig (subprocess), ==, 9); |
1283 | #endif |
1284 | g_spawn_check_exit_status (exit_status: g_subprocess_get_status (subprocess), error: &error); |
1285 | g_assert_nonnull (error); |
1286 | g_clear_error (err: &error); |
1287 | |
1288 | g_main_loop_quit (loop: (GMainLoop*)user_data); |
1289 | } |
1290 | |
1291 | static void |
1292 | test_terminate (void) |
1293 | { |
1294 | GError *local_error = NULL; |
1295 | GError **error = &local_error; |
1296 | GSubprocess *proc; |
1297 | GPtrArray *args; |
1298 | GMainLoop *loop; |
1299 | const gchar *id; |
1300 | |
1301 | args = get_test_subprocess_args (mode: "sleep-forever" , NULL); |
1302 | proc = g_subprocess_newv (argv: (const gchar * const *) args->pdata, flags: G_SUBPROCESS_FLAGS_NONE, error); |
1303 | g_ptr_array_free (array: args, TRUE); |
1304 | g_assert_no_error (local_error); |
1305 | |
1306 | id = g_subprocess_get_identifier (subprocess: proc); |
1307 | g_assert_nonnull (id); |
1308 | |
1309 | loop = g_main_loop_new (NULL, TRUE); |
1310 | |
1311 | g_subprocess_wait_async (subprocess: proc, NULL, callback: on_request_quit_exited, user_data: loop); |
1312 | |
1313 | g_timeout_add_seconds (interval: 3, function: send_terminate, data: proc); |
1314 | |
1315 | g_main_loop_run (loop); |
1316 | |
1317 | g_main_loop_unref (loop); |
1318 | g_object_unref (object: proc); |
1319 | } |
1320 | |
1321 | #ifdef G_OS_UNIX |
1322 | static gboolean |
1323 | send_signal (gpointer user_data) |
1324 | { |
1325 | GSubprocess *proc = user_data; |
1326 | |
1327 | g_subprocess_send_signal (subprocess: proc, SIGKILL); |
1328 | |
1329 | return FALSE; |
1330 | } |
1331 | |
1332 | static void |
1333 | test_signal (void) |
1334 | { |
1335 | GError *local_error = NULL; |
1336 | GError **error = &local_error; |
1337 | GSubprocess *proc; |
1338 | GPtrArray *args; |
1339 | GMainLoop *loop; |
1340 | |
1341 | args = get_test_subprocess_args (mode: "sleep-forever" , NULL); |
1342 | proc = g_subprocess_newv (argv: (const gchar * const *) args->pdata, flags: G_SUBPROCESS_FLAGS_NONE, error); |
1343 | g_ptr_array_free (array: args, TRUE); |
1344 | g_assert_no_error (local_error); |
1345 | |
1346 | loop = g_main_loop_new (NULL, TRUE); |
1347 | |
1348 | g_subprocess_wait_async (subprocess: proc, NULL, callback: on_request_quit_exited, user_data: loop); |
1349 | |
1350 | g_timeout_add_seconds (interval: 3, function: send_signal, data: proc); |
1351 | |
1352 | g_main_loop_run (loop); |
1353 | |
1354 | g_main_loop_unref (loop); |
1355 | g_object_unref (object: proc); |
1356 | } |
1357 | #endif |
1358 | |
1359 | static void |
1360 | test_env (void) |
1361 | { |
1362 | GError *local_error = NULL; |
1363 | GError **error = &local_error; |
1364 | GSubprocessLauncher *launcher; |
1365 | GSubprocess *proc; |
1366 | GPtrArray *args; |
1367 | GInputStream *stdout_stream; |
1368 | gchar *result; |
1369 | gchar *envp[] = { NULL, "ONE=1" , "TWO=1" , "THREE=3" , "FOUR=1" , NULL }; |
1370 | gchar **split; |
1371 | |
1372 | envp[0] = g_strdup_printf (format: "PATH=%s" , g_getenv (variable: "PATH" )); |
1373 | args = get_test_subprocess_args (mode: "env" , NULL); |
1374 | launcher = g_subprocess_launcher_new (flags: G_SUBPROCESS_FLAGS_NONE); |
1375 | g_subprocess_launcher_set_flags (self: launcher, flags: G_SUBPROCESS_FLAGS_STDOUT_PIPE); |
1376 | g_subprocess_launcher_set_environ (self: launcher, env: envp); |
1377 | g_subprocess_launcher_setenv (self: launcher, variable: "TWO" , value: "2" , TRUE); |
1378 | g_subprocess_launcher_setenv (self: launcher, variable: "THREE" , value: "1" , FALSE); |
1379 | g_subprocess_launcher_unsetenv (self: launcher, variable: "FOUR" ); |
1380 | |
1381 | g_assert_null (g_subprocess_launcher_getenv (launcher, "FOUR" )); |
1382 | |
1383 | proc = g_subprocess_launcher_spawn (self: launcher, error, argv0: args->pdata[0], "env" , NULL); |
1384 | g_ptr_array_free (array: args, TRUE); |
1385 | g_assert_no_error (local_error); |
1386 | g_free (mem: envp[0]); |
1387 | |
1388 | stdout_stream = g_subprocess_get_stdout_pipe (subprocess: proc); |
1389 | |
1390 | result = splice_to_string (stream: stdout_stream, error); |
1391 | split = g_strsplit (string: result, LINEEND, max_tokens: -1); |
1392 | g_assert_cmpstr (g_environ_getenv (split, "ONE" ), ==, "1" ); |
1393 | g_assert_cmpstr (g_environ_getenv (split, "TWO" ), ==, "2" ); |
1394 | g_assert_cmpstr (g_environ_getenv (split, "THREE" ), ==, "3" ); |
1395 | g_assert_null (g_environ_getenv (split, "FOUR" )); |
1396 | |
1397 | g_strfreev (str_array: split); |
1398 | g_free (mem: result); |
1399 | g_object_unref (object: proc); |
1400 | g_object_unref (object: launcher); |
1401 | } |
1402 | |
1403 | /* Test that explicitly inheriting and modifying the parent process’ |
1404 | * environment works. */ |
1405 | static void |
1406 | test_env_inherit (void) |
1407 | { |
1408 | GError *local_error = NULL; |
1409 | GError **error = &local_error; |
1410 | GSubprocessLauncher *launcher; |
1411 | GSubprocess *proc; |
1412 | GPtrArray *args; |
1413 | GInputStream *stdout_stream; |
1414 | gchar *result; |
1415 | gchar **split; |
1416 | |
1417 | g_setenv (variable: "TEST_ENV_INHERIT1" , value: "1" , TRUE); |
1418 | g_setenv (variable: "TEST_ENV_INHERIT2" , value: "2" , TRUE); |
1419 | |
1420 | args = get_test_subprocess_args (mode: "env" , NULL); |
1421 | launcher = g_subprocess_launcher_new (flags: G_SUBPROCESS_FLAGS_NONE); |
1422 | g_subprocess_launcher_set_flags (self: launcher, flags: G_SUBPROCESS_FLAGS_STDOUT_PIPE); |
1423 | g_subprocess_launcher_set_environ (self: launcher, NULL); |
1424 | g_subprocess_launcher_setenv (self: launcher, variable: "TWO" , value: "2" , TRUE); |
1425 | g_subprocess_launcher_unsetenv (self: launcher, variable: "TEST_ENV_INHERIT1" ); |
1426 | |
1427 | g_assert_null (g_subprocess_launcher_getenv (launcher, "TEST_ENV_INHERIT1" )); |
1428 | g_assert_cmpstr (g_subprocess_launcher_getenv (launcher, "TEST_ENV_INHERIT2" ), ==, "2" ); |
1429 | g_assert_cmpstr (g_subprocess_launcher_getenv (launcher, "TWO" ), ==, "2" ); |
1430 | |
1431 | proc = g_subprocess_launcher_spawn (self: launcher, error, argv0: args->pdata[0], "env" , NULL); |
1432 | g_ptr_array_free (array: args, TRUE); |
1433 | g_assert_no_error (local_error); |
1434 | |
1435 | stdout_stream = g_subprocess_get_stdout_pipe (subprocess: proc); |
1436 | |
1437 | result = splice_to_string (stream: stdout_stream, error); |
1438 | split = g_strsplit (string: result, LINEEND, max_tokens: -1); |
1439 | g_assert_null (g_environ_getenv (split, "TEST_ENV_INHERIT1" )); |
1440 | g_assert_cmpstr (g_environ_getenv (split, "TEST_ENV_INHERIT2" ), ==, "2" ); |
1441 | g_assert_cmpstr (g_environ_getenv (split, "TWO" ), ==, "2" ); |
1442 | |
1443 | g_strfreev (str_array: split); |
1444 | g_free (mem: result); |
1445 | g_object_unref (object: proc); |
1446 | g_object_unref (object: launcher); |
1447 | } |
1448 | |
1449 | static void |
1450 | test_cwd (void) |
1451 | { |
1452 | GError *local_error = NULL; |
1453 | GError **error = &local_error; |
1454 | GSubprocessLauncher *launcher; |
1455 | GSubprocess *proc; |
1456 | GPtrArray *args; |
1457 | GInputStream *stdout_stream; |
1458 | gchar *result; |
1459 | const char *basename; |
1460 | gchar *tmp_lineend; |
1461 | const gchar *tmp_lineend_basename; |
1462 | |
1463 | args = get_test_subprocess_args (mode: "cwd" , NULL); |
1464 | launcher = g_subprocess_launcher_new (flags: G_SUBPROCESS_FLAGS_STDOUT_PIPE); |
1465 | g_subprocess_launcher_set_flags (self: launcher, flags: G_SUBPROCESS_FLAGS_STDOUT_PIPE); |
1466 | g_subprocess_launcher_set_cwd (self: launcher, cwd: g_get_tmp_dir ()); |
1467 | tmp_lineend = g_strdup_printf (format: "%s%s" , g_get_tmp_dir (), LINEEND); |
1468 | tmp_lineend_basename = g_strrstr (haystack: tmp_lineend, G_DIR_SEPARATOR_S); |
1469 | |
1470 | proc = g_subprocess_launcher_spawnv (self: launcher, argv: (const char * const *)args->pdata, error); |
1471 | g_ptr_array_free (array: args, TRUE); |
1472 | g_assert_no_error (local_error); |
1473 | |
1474 | stdout_stream = g_subprocess_get_stdout_pipe (subprocess: proc); |
1475 | |
1476 | result = splice_to_string (stream: stdout_stream, error); |
1477 | |
1478 | basename = g_strrstr (haystack: result, G_DIR_SEPARATOR_S); |
1479 | g_assert_nonnull (basename); |
1480 | g_assert_cmpstr (basename, ==, tmp_lineend_basename); |
1481 | g_free (mem: tmp_lineend); |
1482 | |
1483 | g_free (mem: result); |
1484 | g_object_unref (object: proc); |
1485 | g_object_unref (object: launcher); |
1486 | } |
1487 | #ifdef G_OS_UNIX |
1488 | |
1489 | static void |
1490 | test_subprocess_launcher_close (void) |
1491 | { |
1492 | GError *local_error = NULL; |
1493 | GError **error = &local_error; |
1494 | GSubprocessLauncher *launcher; |
1495 | GSubprocess *proc; |
1496 | GPtrArray *args; |
1497 | int fd, fd2; |
1498 | gboolean is_open; |
1499 | |
1500 | /* Open two arbitrary FDs. One of them, @fd, will be transferred to the |
1501 | * launcher, and the other’s FD integer will be used as its target FD, giving |
1502 | * the mapping `fd → fd2` if a child process were to be spawned. |
1503 | * |
1504 | * The launcher will then be closed, which should close @fd but *not* @fd2, |
1505 | * as the value of @fd2 is only valid as an FD in a child process. (A child |
1506 | * process is not actually spawned in this test.) |
1507 | */ |
1508 | fd = dup (fd: 0); |
1509 | fd2 = dup (fd: 0); |
1510 | launcher = g_subprocess_launcher_new (flags: G_SUBPROCESS_FLAGS_NONE); |
1511 | g_subprocess_launcher_take_fd (self: launcher, source_fd: fd, target_fd: fd2); |
1512 | |
1513 | is_open = fcntl (fd: fd, F_GETFD) != -1; |
1514 | g_assert_true (is_open); |
1515 | is_open = fcntl (fd: fd2, F_GETFD) != -1; |
1516 | g_assert_true (is_open); |
1517 | |
1518 | g_subprocess_launcher_close (self: launcher); |
1519 | |
1520 | is_open = fcntl (fd: fd, F_GETFD) != -1; |
1521 | g_assert_false (is_open); |
1522 | is_open = fcntl (fd: fd2, F_GETFD) != -1; |
1523 | g_assert_true (is_open); |
1524 | |
1525 | /* Now test that actually trying to spawn the child gives %G_IO_ERROR_CLOSED, |
1526 | * as g_subprocess_launcher_close() has been called. */ |
1527 | args = get_test_subprocess_args (mode: "cat" , NULL); |
1528 | proc = g_subprocess_launcher_spawnv (self: launcher, argv: (const gchar * const *) args->pdata, error); |
1529 | g_ptr_array_free (array: args, TRUE); |
1530 | g_assert_null (proc); |
1531 | g_assert_error (local_error, G_IO_ERROR, G_IO_ERROR_CLOSED); |
1532 | g_clear_error (err: error); |
1533 | |
1534 | close (fd: fd2); |
1535 | g_object_unref (object: launcher); |
1536 | } |
1537 | |
1538 | static void |
1539 | test_stdout_file (void) |
1540 | { |
1541 | GError *local_error = NULL; |
1542 | GError **error = &local_error; |
1543 | GSubprocessLauncher *launcher; |
1544 | GSubprocess *proc; |
1545 | GPtrArray *args; |
1546 | GFile *tmpfile; |
1547 | GFileIOStream *iostream; |
1548 | GOutputStream *stdin_stream; |
1549 | const char *test_data = "this is some test data\n" ; |
1550 | char *tmp_contents; |
1551 | char *tmp_file_path; |
1552 | |
1553 | tmpfile = g_file_new_tmp (tmpl: "gsubprocessXXXXXX" , iostream: &iostream, error); |
1554 | g_assert_no_error (local_error); |
1555 | g_clear_object (&iostream); |
1556 | |
1557 | tmp_file_path = g_file_get_path (file: tmpfile); |
1558 | |
1559 | args = get_test_subprocess_args (mode: "cat" , NULL); |
1560 | launcher = g_subprocess_launcher_new (flags: G_SUBPROCESS_FLAGS_STDIN_PIPE); |
1561 | g_subprocess_launcher_set_stdout_file_path (self: launcher, path: tmp_file_path); |
1562 | proc = g_subprocess_launcher_spawnv (self: launcher, argv: (const gchar * const *) args->pdata, error); |
1563 | g_ptr_array_free (array: args, TRUE); |
1564 | g_assert_no_error (local_error); |
1565 | |
1566 | stdin_stream = g_subprocess_get_stdin_pipe (subprocess: proc); |
1567 | |
1568 | g_output_stream_write_all (stream: stdin_stream, buffer: test_data, count: strlen (s: test_data), NULL, NULL, error); |
1569 | g_assert_no_error (local_error); |
1570 | |
1571 | g_output_stream_close (stream: stdin_stream, NULL, error); |
1572 | g_assert_no_error (local_error); |
1573 | |
1574 | g_subprocess_wait_check (subprocess: proc, NULL, error); |
1575 | |
1576 | g_object_unref (object: launcher); |
1577 | g_object_unref (object: proc); |
1578 | |
1579 | g_file_load_contents (file: tmpfile, NULL, contents: &tmp_contents, NULL, NULL, error); |
1580 | g_assert_no_error (local_error); |
1581 | |
1582 | g_assert_cmpstr (test_data, ==, tmp_contents); |
1583 | g_free (mem: tmp_contents); |
1584 | |
1585 | (void) g_file_delete (file: tmpfile, NULL, NULL); |
1586 | g_object_unref (object: tmpfile); |
1587 | g_free (mem: tmp_file_path); |
1588 | } |
1589 | |
1590 | static void |
1591 | test_stdout_fd (void) |
1592 | { |
1593 | GError *local_error = NULL; |
1594 | GError **error = &local_error; |
1595 | GSubprocessLauncher *launcher; |
1596 | GSubprocess *proc; |
1597 | GPtrArray *args; |
1598 | GFile *tmpfile; |
1599 | GFileIOStream *iostream; |
1600 | GFileDescriptorBased *descriptor_stream; |
1601 | GOutputStream *stdin_stream; |
1602 | const char *test_data = "this is some test data\n" ; |
1603 | char *tmp_contents; |
1604 | |
1605 | tmpfile = g_file_new_tmp (tmpl: "gsubprocessXXXXXX" , iostream: &iostream, error); |
1606 | g_assert_no_error (local_error); |
1607 | |
1608 | args = get_test_subprocess_args (mode: "cat" , NULL); |
1609 | launcher = g_subprocess_launcher_new (flags: G_SUBPROCESS_FLAGS_STDIN_PIPE); |
1610 | descriptor_stream = G_FILE_DESCRIPTOR_BASED (g_io_stream_get_output_stream (G_IO_STREAM (iostream))); |
1611 | g_subprocess_launcher_take_stdout_fd (self: launcher, fd: dup (fd: g_file_descriptor_based_get_fd (fd_based: descriptor_stream))); |
1612 | proc = g_subprocess_launcher_spawnv (self: launcher, argv: (const gchar * const *) args->pdata, error); |
1613 | g_ptr_array_free (array: args, TRUE); |
1614 | g_assert_no_error (local_error); |
1615 | |
1616 | g_clear_object (&iostream); |
1617 | |
1618 | stdin_stream = g_subprocess_get_stdin_pipe (subprocess: proc); |
1619 | |
1620 | g_output_stream_write_all (stream: stdin_stream, buffer: test_data, count: strlen (s: test_data), NULL, NULL, error); |
1621 | g_assert_no_error (local_error); |
1622 | |
1623 | g_output_stream_close (stream: stdin_stream, NULL, error); |
1624 | g_assert_no_error (local_error); |
1625 | |
1626 | g_subprocess_wait_check (subprocess: proc, NULL, error); |
1627 | |
1628 | g_object_unref (object: launcher); |
1629 | g_object_unref (object: proc); |
1630 | |
1631 | g_file_load_contents (file: tmpfile, NULL, contents: &tmp_contents, NULL, NULL, error); |
1632 | g_assert_no_error (local_error); |
1633 | |
1634 | g_assert_cmpstr (test_data, ==, tmp_contents); |
1635 | g_free (mem: tmp_contents); |
1636 | |
1637 | (void) g_file_delete (file: tmpfile, NULL, NULL); |
1638 | g_object_unref (object: tmpfile); |
1639 | } |
1640 | |
1641 | static void |
1642 | child_setup (gpointer user_data) |
1643 | { |
1644 | dup2 (GPOINTER_TO_INT (user_data), fd2: 1); |
1645 | } |
1646 | |
1647 | static void |
1648 | test_child_setup (void) |
1649 | { |
1650 | GError *local_error = NULL; |
1651 | GError **error = &local_error; |
1652 | GSubprocessLauncher *launcher; |
1653 | GSubprocess *proc; |
1654 | GPtrArray *args; |
1655 | GFile *tmpfile; |
1656 | GFileIOStream *iostream; |
1657 | GOutputStream *stdin_stream; |
1658 | const char *test_data = "this is some test data\n" ; |
1659 | char *tmp_contents; |
1660 | int fd; |
1661 | |
1662 | tmpfile = g_file_new_tmp (tmpl: "gsubprocessXXXXXX" , iostream: &iostream, error); |
1663 | g_assert_no_error (local_error); |
1664 | |
1665 | fd = g_file_descriptor_based_get_fd (G_FILE_DESCRIPTOR_BASED (g_io_stream_get_output_stream (G_IO_STREAM (iostream)))); |
1666 | |
1667 | args = get_test_subprocess_args (mode: "cat" , NULL); |
1668 | launcher = g_subprocess_launcher_new (flags: G_SUBPROCESS_FLAGS_STDIN_PIPE); |
1669 | g_subprocess_launcher_set_child_setup (self: launcher, child_setup, GINT_TO_POINTER (fd), NULL); |
1670 | proc = g_subprocess_launcher_spawnv (self: launcher, argv: (const gchar * const *) args->pdata, error); |
1671 | g_ptr_array_free (array: args, TRUE); |
1672 | g_assert_no_error (local_error); |
1673 | |
1674 | g_clear_object (&iostream); |
1675 | |
1676 | stdin_stream = g_subprocess_get_stdin_pipe (subprocess: proc); |
1677 | |
1678 | g_output_stream_write_all (stream: stdin_stream, buffer: test_data, count: strlen (s: test_data), NULL, NULL, error); |
1679 | g_assert_no_error (local_error); |
1680 | |
1681 | g_output_stream_close (stream: stdin_stream, NULL, error); |
1682 | g_assert_no_error (local_error); |
1683 | |
1684 | g_subprocess_wait_check (subprocess: proc, NULL, error); |
1685 | |
1686 | g_object_unref (object: launcher); |
1687 | g_object_unref (object: proc); |
1688 | |
1689 | g_file_load_contents (file: tmpfile, NULL, contents: &tmp_contents, NULL, NULL, error); |
1690 | g_assert_no_error (local_error); |
1691 | |
1692 | g_assert_cmpstr (test_data, ==, tmp_contents); |
1693 | g_free (mem: tmp_contents); |
1694 | |
1695 | (void) g_file_delete (file: tmpfile, NULL, NULL); |
1696 | g_object_unref (object: tmpfile); |
1697 | } |
1698 | |
1699 | static void |
1700 | test_pass_fd (void) |
1701 | { |
1702 | GError *local_error = NULL; |
1703 | GError **error = &local_error; |
1704 | GInputStream *child_input; |
1705 | GDataInputStream *child_datainput; |
1706 | GSubprocessLauncher *launcher; |
1707 | GSubprocess *proc; |
1708 | GPtrArray *args; |
1709 | int basic_pipefds[2]; |
1710 | int needdup_pipefds[2]; |
1711 | char *buf; |
1712 | gsize len; |
1713 | char *basic_fd_str; |
1714 | char *needdup_fd_str; |
1715 | |
1716 | g_unix_open_pipe (fds: basic_pipefds, FD_CLOEXEC, error); |
1717 | g_assert_no_error (local_error); |
1718 | g_unix_open_pipe (fds: needdup_pipefds, FD_CLOEXEC, error); |
1719 | g_assert_no_error (local_error); |
1720 | |
1721 | basic_fd_str = g_strdup_printf (format: "%d" , basic_pipefds[1]); |
1722 | needdup_fd_str = g_strdup_printf (format: "%d" , needdup_pipefds[1] + 1); |
1723 | |
1724 | args = get_test_subprocess_args (mode: "write-to-fds" , basic_fd_str, needdup_fd_str, NULL); |
1725 | launcher = g_subprocess_launcher_new (flags: G_SUBPROCESS_FLAGS_NONE); |
1726 | g_subprocess_launcher_take_fd (self: launcher, source_fd: basic_pipefds[1], target_fd: basic_pipefds[1]); |
1727 | g_subprocess_launcher_take_fd (self: launcher, source_fd: needdup_pipefds[1], target_fd: needdup_pipefds[1] + 1); |
1728 | proc = g_subprocess_launcher_spawnv (self: launcher, argv: (const gchar * const *) args->pdata, error); |
1729 | g_ptr_array_free (array: args, TRUE); |
1730 | g_assert_no_error (local_error); |
1731 | |
1732 | g_free (mem: basic_fd_str); |
1733 | g_free (mem: needdup_fd_str); |
1734 | |
1735 | child_input = g_unix_input_stream_new (fd: basic_pipefds[0], TRUE); |
1736 | child_datainput = g_data_input_stream_new (base_stream: child_input); |
1737 | buf = g_data_input_stream_read_line_utf8 (stream: child_datainput, length: &len, NULL, error); |
1738 | g_assert_no_error (local_error); |
1739 | g_assert_cmpstr (buf, ==, "hello world" ); |
1740 | g_object_unref (object: child_datainput); |
1741 | g_object_unref (object: child_input); |
1742 | g_free (mem: buf); |
1743 | |
1744 | child_input = g_unix_input_stream_new (fd: needdup_pipefds[0], TRUE); |
1745 | child_datainput = g_data_input_stream_new (base_stream: child_input); |
1746 | buf = g_data_input_stream_read_line_utf8 (stream: child_datainput, length: &len, NULL, error); |
1747 | g_assert_no_error (local_error); |
1748 | g_assert_cmpstr (buf, ==, "hello world" ); |
1749 | g_free (mem: buf); |
1750 | g_object_unref (object: child_datainput); |
1751 | g_object_unref (object: child_input); |
1752 | |
1753 | g_object_unref (object: launcher); |
1754 | g_object_unref (object: proc); |
1755 | } |
1756 | |
1757 | #endif |
1758 | |
1759 | static void |
1760 | test_launcher_environment (void) |
1761 | { |
1762 | GSubprocessLauncher *launcher; |
1763 | GError *error = NULL; |
1764 | GSubprocess *proc; |
1765 | GPtrArray *args; |
1766 | gchar *out; |
1767 | |
1768 | g_setenv (variable: "A" , value: "B" , TRUE); |
1769 | g_setenv (variable: "C" , value: "D" , TRUE); |
1770 | |
1771 | launcher = g_subprocess_launcher_new (flags: G_SUBPROCESS_FLAGS_STDOUT_PIPE); |
1772 | |
1773 | /* unset a variable */ |
1774 | g_subprocess_launcher_unsetenv (self: launcher, variable: "A" ); |
1775 | |
1776 | /* and set a different one */ |
1777 | g_subprocess_launcher_setenv (self: launcher, variable: "E" , value: "F" , TRUE); |
1778 | |
1779 | args = get_test_subprocess_args (mode: "printenv" , "A" , "C" , "E" , NULL); |
1780 | proc = g_subprocess_launcher_spawnv (self: launcher, argv: (const gchar **) args->pdata, error: &error); |
1781 | g_assert_no_error (error); |
1782 | g_assert_nonnull (proc); |
1783 | |
1784 | g_subprocess_communicate_utf8 (subprocess: proc, NULL, NULL, stdout_buf: &out, NULL, error: &error); |
1785 | g_assert_no_error (error); |
1786 | |
1787 | g_assert_cmpstr (out, ==, "C=D" LINEEND "E=F" LINEEND); |
1788 | g_free (mem: out); |
1789 | |
1790 | g_object_unref (object: proc); |
1791 | g_object_unref (object: launcher); |
1792 | g_ptr_array_unref (array: args); |
1793 | } |
1794 | |
1795 | int |
1796 | main (int argc, char **argv) |
1797 | { |
1798 | const struct |
1799 | { |
1800 | const gchar *subtest; |
1801 | GSubprocessFlags flags; |
1802 | } |
1803 | flags_vectors[] = |
1804 | { |
1805 | { "" , G_SUBPROCESS_FLAGS_STDOUT_PIPE | G_SUBPROCESS_FLAGS_STDERR_MERGE }, |
1806 | { "/no-pipes" , G_SUBPROCESS_FLAGS_NONE }, |
1807 | { "/separate-stderr" , G_SUBPROCESS_FLAGS_STDOUT_PIPE | G_SUBPROCESS_FLAGS_STDERR_PIPE }, |
1808 | { "/stdout-only" , G_SUBPROCESS_FLAGS_STDOUT_PIPE }, |
1809 | { "/stderr-only" , G_SUBPROCESS_FLAGS_STDERR_PIPE }, |
1810 | { "/stdout-silence" , G_SUBPROCESS_FLAGS_STDOUT_SILENCE }, |
1811 | }; |
1812 | gsize i; |
1813 | |
1814 | g_test_init (argc: &argc, argv: &argv, NULL); |
1815 | g_test_bug_base (uri_pattern: "https://bugzilla.gnome.org/" ); |
1816 | |
1817 | g_test_add_func (testpath: "/gsubprocess/noop" , test_func: test_noop); |
1818 | g_test_add_func (testpath: "/gsubprocess/noop-all-to-null" , test_func: test_noop_all_to_null); |
1819 | g_test_add_func (testpath: "/gsubprocess/noop-no-wait" , test_func: test_noop_no_wait); |
1820 | g_test_add_func (testpath: "/gsubprocess/noop-stdin-inherit" , test_func: test_noop_stdin_inherit); |
1821 | #ifdef G_OS_UNIX |
1822 | g_test_add_func (testpath: "/gsubprocess/search-path" , test_func: test_search_path); |
1823 | g_test_add_func (testpath: "/gsubprocess/signal" , test_func: test_signal); |
1824 | #endif |
1825 | g_test_add_func (testpath: "/gsubprocess/exit1" , test_func: test_exit1); |
1826 | g_test_add_func (testpath: "/gsubprocess/exit1/cancel" , test_func: test_exit1_cancel); |
1827 | g_test_add_func (testpath: "/gsubprocess/exit1/cancel_in_cb" , test_func: test_exit1_cancel_in_cb); |
1828 | g_test_add_func (testpath: "/gsubprocess/echo1" , test_func: test_echo1); |
1829 | #ifdef G_OS_UNIX |
1830 | g_test_add_func (testpath: "/gsubprocess/echo-merged" , test_func: test_echo_merged); |
1831 | #endif |
1832 | g_test_add_func (testpath: "/gsubprocess/cat-utf8" , test_func: test_cat_utf8); |
1833 | g_test_add_func (testpath: "/gsubprocess/cat-eof" , test_func: test_cat_eof); |
1834 | g_test_add_func (testpath: "/gsubprocess/multi1" , test_func: test_multi_1); |
1835 | |
1836 | /* Add various tests for g_subprocess_communicate() with different flags. */ |
1837 | for (i = 0; i < G_N_ELEMENTS (flags_vectors); i++) |
1838 | { |
1839 | gchar *test_path = NULL; |
1840 | |
1841 | test_path = g_strdup_printf (format: "/gsubprocess/communicate%s" , flags_vectors[i].subtest); |
1842 | g_test_add_data_func (testpath: test_path, GINT_TO_POINTER (flags_vectors[i].flags), |
1843 | test_func: test_communicate); |
1844 | g_free (mem: test_path); |
1845 | |
1846 | test_path = g_strdup_printf (format: "/gsubprocess/communicate/cancelled%s" , flags_vectors[i].subtest); |
1847 | g_test_add_data_func (testpath: test_path, GINT_TO_POINTER (flags_vectors[i].flags), |
1848 | test_func: test_communicate_cancelled); |
1849 | g_free (mem: test_path); |
1850 | |
1851 | test_path = g_strdup_printf (format: "/gsubprocess/communicate/async%s" , flags_vectors[i].subtest); |
1852 | g_test_add_data_func (testpath: test_path, GINT_TO_POINTER (flags_vectors[i].flags), |
1853 | test_func: test_communicate_async); |
1854 | g_free (mem: test_path); |
1855 | |
1856 | test_path = g_strdup_printf (format: "/gsubprocess/communicate/async/cancelled%s" , flags_vectors[i].subtest); |
1857 | g_test_add_data_func (testpath: test_path, GINT_TO_POINTER (flags_vectors[i].flags), |
1858 | test_func: test_communicate_cancelled_async); |
1859 | g_free (mem: test_path); |
1860 | |
1861 | test_path = g_strdup_printf (format: "/gsubprocess/communicate/utf8%s" , flags_vectors[i].subtest); |
1862 | g_test_add_data_func (testpath: test_path, GINT_TO_POINTER (flags_vectors[i].flags), |
1863 | test_func: test_communicate_utf8); |
1864 | g_free (mem: test_path); |
1865 | |
1866 | test_path = g_strdup_printf (format: "/gsubprocess/communicate/utf8/cancelled%s" , flags_vectors[i].subtest); |
1867 | g_test_add_data_func (testpath: test_path, GINT_TO_POINTER (flags_vectors[i].flags), |
1868 | test_func: test_communicate_utf8_cancelled); |
1869 | g_free (mem: test_path); |
1870 | |
1871 | test_path = g_strdup_printf (format: "/gsubprocess/communicate/utf8/async%s" , flags_vectors[i].subtest); |
1872 | g_test_add_data_func (testpath: test_path, GINT_TO_POINTER (flags_vectors[i].flags), |
1873 | test_func: test_communicate_utf8_async); |
1874 | g_free (mem: test_path); |
1875 | |
1876 | test_path = g_strdup_printf (format: "/gsubprocess/communicate/utf8/async/cancelled%s" , flags_vectors[i].subtest); |
1877 | g_test_add_data_func (testpath: test_path, GINT_TO_POINTER (flags_vectors[i].flags), |
1878 | test_func: test_communicate_utf8_cancelled_async); |
1879 | g_free (mem: test_path); |
1880 | } |
1881 | |
1882 | g_test_add_func (testpath: "/gsubprocess/communicate/utf8/async/invalid" , test_func: test_communicate_utf8_async_invalid); |
1883 | g_test_add_func (testpath: "/gsubprocess/communicate/utf8/invalid" , test_func: test_communicate_utf8_invalid); |
1884 | g_test_add_func (testpath: "/gsubprocess/communicate/nothing" , test_func: test_communicate_nothing); |
1885 | g_test_add_func (testpath: "/gsubprocess/terminate" , test_func: test_terminate); |
1886 | g_test_add_func (testpath: "/gsubprocess/env" , test_func: test_env); |
1887 | g_test_add_func (testpath: "/gsubprocess/env/inherit" , test_func: test_env_inherit); |
1888 | g_test_add_func (testpath: "/gsubprocess/cwd" , test_func: test_cwd); |
1889 | #ifdef G_OS_UNIX |
1890 | g_test_add_func (testpath: "/gsubprocess/launcher-close" , test_func: test_subprocess_launcher_close); |
1891 | g_test_add_func (testpath: "/gsubprocess/stdout-file" , test_func: test_stdout_file); |
1892 | g_test_add_func (testpath: "/gsubprocess/stdout-fd" , test_func: test_stdout_fd); |
1893 | g_test_add_func (testpath: "/gsubprocess/child-setup" , test_func: test_child_setup); |
1894 | g_test_add_func (testpath: "/gsubprocess/pass-fd" , test_func: test_pass_fd); |
1895 | #endif |
1896 | g_test_add_func (testpath: "/gsubprocess/launcher-environment" , test_func: test_launcher_environment); |
1897 | |
1898 | return g_test_run (); |
1899 | } |
1900 | |