Merge branch '976-disable-assert-checks' into 'master'
[glib.git] / glib / tests / spawn-singlethread.c
blob909f702fc0033cabd2efbb214c13a0afc1443875
1 /*
2 * Copyright (C) 2011 Red Hat, Inc.
4 * This work is provided "as is"; redistribution and modification
5 * in whole or in part, in any medium, physical or electronic is
6 * permitted without restriction.
8 * This work is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 * In no event shall the authors or contributors be liable for any
13 * direct, indirect, incidental, special, exemplary, or consequential
14 * damages (including, but not limited to, procurement of substitute
15 * goods or services; loss of use, data, or profits; or business
16 * interruption) however caused and on any theory of liability, whether
17 * in contract, strict liability, or tort (including negligence or
18 * otherwise) arising in any way out of the use of this software, even
19 * if advised of the possibility of such damage.
21 * Author: Colin Walters <walters@verbum.org>
24 #include "config.h"
26 #include <glib.h>
27 #include <string.h>
28 #include <fcntl.h>
30 #ifdef G_OS_UNIX
31 #include <glib-unix.h>
32 #endif
34 #ifdef G_OS_WIN32
35 #include <io.h>
36 #define LINEEND "\r\n"
37 #else
38 #define LINEEND "\n"
39 #endif
41 /* MinGW builds are likely done using a BASH-style shell, so run the
42 * normal script there, as on non-Windows builds, as it is more likely
43 * that one will run 'make check' in such shells to test the code
45 #if defined (G_OS_WIN32) && defined (_MSC_VER)
46 #define SCRIPT_EXT ".bat"
47 #else
48 #define SCRIPT_EXT
49 #endif
51 static char *echo_prog_path;
52 static char *echo_script_path;
54 typedef struct {
55 GMainLoop *loop;
56 gboolean child_exited;
57 gboolean stdout_done;
58 GString *stdout_buf;
59 } SpawnAsyncMultithreadedData;
61 static gboolean
62 on_child_exited (GPid pid,
63 gint status,
64 gpointer datap)
66 SpawnAsyncMultithreadedData *data = datap;
68 data->child_exited = TRUE;
69 if (data->child_exited && data->stdout_done)
70 g_main_loop_quit (data->loop);
72 return G_SOURCE_REMOVE;
75 static gboolean
76 on_child_stdout (GIOChannel *channel,
77 GIOCondition condition,
78 gpointer datap)
80 char buf[1024];
81 GError *error = NULL;
82 gsize bytes_read;
83 SpawnAsyncMultithreadedData *data = datap;
85 if (condition & G_IO_IN)
87 GIOStatus status;
88 status = g_io_channel_read_chars (channel, buf, sizeof (buf), &bytes_read, &error);
89 g_assert_no_error (error);
90 g_string_append_len (data->stdout_buf, buf, (gssize) bytes_read);
91 if (status == G_IO_STATUS_EOF)
92 data->stdout_done = TRUE;
94 if (condition & G_IO_HUP)
95 data->stdout_done = TRUE;
96 if (condition & G_IO_ERR)
97 g_error ("Error reading from child stdin");
99 if (data->child_exited && data->stdout_done)
100 g_main_loop_quit (data->loop);
102 return !data->stdout_done;
105 static void
106 test_spawn_async (void)
108 int tnum = 1;
109 GError *error = NULL;
110 GPtrArray *argv;
111 char *arg;
112 GPid pid;
113 GMainContext *context;
114 GMainLoop *loop;
115 GIOChannel *channel;
116 GSource *source;
117 int child_stdout_fd;
118 SpawnAsyncMultithreadedData data;
120 context = g_main_context_new ();
121 loop = g_main_loop_new (context, TRUE);
123 arg = g_strdup_printf ("thread %d", tnum);
125 argv = g_ptr_array_new ();
126 g_ptr_array_add (argv, echo_prog_path);
127 g_ptr_array_add (argv, arg);
128 g_ptr_array_add (argv, NULL);
130 g_spawn_async_with_pipes (NULL, (char**)argv->pdata, NULL, G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL, &pid, NULL,
131 &child_stdout_fd, NULL, &error);
132 g_assert_no_error (error);
133 g_ptr_array_free (argv, TRUE);
135 data.loop = loop;
136 data.stdout_done = FALSE;
137 data.child_exited = FALSE;
138 data.stdout_buf = g_string_new (0);
140 source = g_child_watch_source_new (pid);
141 g_source_set_callback (source, (GSourceFunc)on_child_exited, &data, NULL);
142 g_source_attach (source, context);
143 g_source_unref (source);
145 channel = g_io_channel_unix_new (child_stdout_fd);
146 source = g_io_create_watch (channel, G_IO_IN | G_IO_HUP | G_IO_ERR);
147 g_source_set_callback (source, (GSourceFunc)on_child_stdout, &data, NULL);
148 g_source_attach (source, context);
149 g_source_unref (source);
151 g_main_loop_run (loop);
153 g_assert (data.child_exited);
154 g_assert (data.stdout_done);
155 g_assert_cmpstr (data.stdout_buf->str, ==, arg);
156 g_string_free (data.stdout_buf, TRUE);
158 g_io_channel_unref (channel);
159 g_main_context_unref (context);
160 g_main_loop_unref (loop);
162 g_free (arg);
165 /* Windows close() causes failure through the Invalid Parameter Handler
166 * Routine if the file descriptor does not exist.
168 static void
169 sane_close (int fd)
171 if (fd >= 0)
172 close (fd);
175 /* Test g_spawn_async_with_fds() with a variety of different inputs */
176 static void
177 test_spawn_async_with_fds (void)
179 int tnum = 1;
180 GPtrArray *argv;
181 char *arg;
182 int i;
184 /* Each test has 3 variable parameters: stdin, stdout, stderr */
185 enum fd_type {
186 NO_FD, /* pass fd -1 (unset) */
187 FD_NEGATIVE, /* pass fd of negative value (equivalent to unset) */
188 PIPE, /* pass fd of new/unique pipe */
189 STDOUT_PIPE, /* pass the same pipe as stdout */
190 } tests[][3] = {
191 { NO_FD, NO_FD, NO_FD }, /* Test with no fds passed */
192 { NO_FD, FD_NEGATIVE, NO_FD }, /* Test another negative fd value */
193 { PIPE, PIPE, PIPE }, /* Test with unique fds passed */
194 { NO_FD, PIPE, STDOUT_PIPE }, /* Test the same fd for stdout + stderr */
197 arg = g_strdup_printf ("thread %d", tnum);
199 argv = g_ptr_array_new ();
200 g_ptr_array_add (argv, echo_prog_path);
201 g_ptr_array_add (argv, arg);
202 g_ptr_array_add (argv, NULL);
204 for (i = 0; i < G_N_ELEMENTS (tests); i++)
206 GError *error = NULL;
207 GPid pid;
208 GMainContext *context;
209 GMainLoop *loop;
210 GIOChannel *channel = NULL;
211 GSource *source;
212 SpawnAsyncMultithreadedData data;
213 enum fd_type *fd_info = tests[i];
214 gint test_pipe[3][2];
215 int j;
217 for (j = 0; j < 3; j++)
219 switch (fd_info[j])
221 case NO_FD:
222 test_pipe[j][0] = -1;
223 test_pipe[j][1] = -1;
224 break;
225 case FD_NEGATIVE:
226 test_pipe[j][0] = -5;
227 test_pipe[j][1] = -5;
228 break;
229 case PIPE:
230 #ifdef G_OS_UNIX
231 g_unix_open_pipe (test_pipe[j], FD_CLOEXEC, &error);
232 g_assert_no_error (error);
233 #else
234 g_assert_cmpint (_pipe (test_pipe[j], 4096, _O_BINARY), >=, 0);
235 #endif
236 break;
237 case STDOUT_PIPE:
238 g_assert_cmpint (j, ==, 2); /* only works for stderr */
239 test_pipe[j][0] = test_pipe[1][0];
240 test_pipe[j][1] = test_pipe[1][1];
241 break;
242 default:
243 g_assert_not_reached ();
247 context = g_main_context_new ();
248 loop = g_main_loop_new (context, TRUE);
250 g_spawn_async_with_fds (NULL, (char**)argv->pdata, NULL,
251 G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL, &pid,
252 test_pipe[0][0], test_pipe[1][1], test_pipe[2][1],
253 &error);
254 g_assert_no_error (error);
255 sane_close (test_pipe[0][0]);
256 sane_close (test_pipe[1][1]);
257 if (fd_info[2] != STDOUT_PIPE)
258 sane_close (test_pipe[2][1]);
260 data.loop = loop;
261 data.stdout_done = FALSE;
262 data.child_exited = FALSE;
263 data.stdout_buf = g_string_new (0);
265 source = g_child_watch_source_new (pid);
266 g_source_set_callback (source, (GSourceFunc)on_child_exited, &data, NULL);
267 g_source_attach (source, context);
268 g_source_unref (source);
270 if (test_pipe[1][0] >= 0)
272 channel = g_io_channel_unix_new (test_pipe[1][0]);
273 source = g_io_create_watch (channel, G_IO_IN | G_IO_HUP | G_IO_ERR);
274 g_source_set_callback (source, (GSourceFunc)on_child_stdout,
275 &data, NULL);
276 g_source_attach (source, context);
277 g_source_unref (source);
279 else
281 /* Don't check stdout data if we didn't pass a fd */
282 data.stdout_done = TRUE;
285 g_main_loop_run (loop);
287 g_assert_true (data.child_exited);
289 if (test_pipe[1][0] >= 0)
291 /* Check for echo on stdout */
292 g_assert_true (data.stdout_done);
293 g_assert_cmpstr (data.stdout_buf->str, ==, arg);
294 g_io_channel_unref (channel);
296 g_string_free (data.stdout_buf, TRUE);
298 g_main_context_unref (context);
299 g_main_loop_unref (loop);
300 sane_close (test_pipe[0][1]);
301 sane_close (test_pipe[1][0]);
302 if (fd_info[2] != STDOUT_PIPE)
303 sane_close (test_pipe[2][0]);
306 g_ptr_array_free (argv, TRUE);
307 g_free (arg);
310 static void
311 test_spawn_sync (void)
313 int tnum = 1;
314 GError *error = NULL;
315 GPtrArray *argv;
316 char *arg;
317 char *stdout_str;
318 int estatus;
320 arg = g_strdup_printf ("thread %d", tnum);
322 argv = g_ptr_array_new ();
323 g_ptr_array_add (argv, echo_prog_path);
324 g_ptr_array_add (argv, arg);
325 g_ptr_array_add (argv, NULL);
327 g_spawn_sync (NULL, (char**)argv->pdata, NULL, 0, NULL, NULL, &stdout_str, NULL, &estatus, &error);
328 g_assert_no_error (error);
329 g_assert_cmpstr (arg, ==, stdout_str);
330 g_free (arg);
331 g_free (stdout_str);
332 g_ptr_array_free (argv, TRUE);
335 /* Like test_spawn_sync but uses spawn flags that trigger the optimized
336 * posix_spawn codepath.
338 static void
339 test_posix_spawn (void)
341 int tnum = 1;
342 GError *error = NULL;
343 GPtrArray *argv;
344 char *arg;
345 char *stdout_str;
346 int estatus;
347 GSpawnFlags flags = G_SPAWN_CLOEXEC_PIPES | G_SPAWN_LEAVE_DESCRIPTORS_OPEN;
349 arg = g_strdup_printf ("thread %d", tnum);
351 argv = g_ptr_array_new ();
352 g_ptr_array_add (argv, echo_prog_path);
353 g_ptr_array_add (argv, arg);
354 g_ptr_array_add (argv, NULL);
356 g_spawn_sync (NULL, (char**)argv->pdata, NULL, flags, NULL, NULL, &stdout_str, NULL, &estatus, &error);
357 g_assert_no_error (error);
358 g_assert_cmpstr (arg, ==, stdout_str);
359 g_free (arg);
360 g_free (stdout_str);
361 g_ptr_array_free (argv, TRUE);
364 static void
365 test_spawn_script (void)
367 GError *error = NULL;
368 GPtrArray *argv;
369 char *stdout_str;
370 int estatus;
372 argv = g_ptr_array_new ();
373 g_ptr_array_add (argv, echo_script_path);
374 g_ptr_array_add (argv, NULL);
376 g_spawn_sync (NULL, (char**)argv->pdata, NULL, 0, NULL, NULL, &stdout_str, NULL, &estatus, &error);
377 g_assert_no_error (error);
378 g_assert_cmpstr ("echo" LINEEND, ==, stdout_str);
379 g_free (stdout_str);
380 g_ptr_array_free (argv, TRUE);
383 /* Test that spawning a non-existent executable returns %G_SPAWN_ERROR_NOENT. */
384 static void
385 test_spawn_nonexistent (void)
387 GError *error = NULL;
388 GPtrArray *argv = NULL;
389 gchar *stdout_str = NULL;
390 gint exit_status = -1;
392 argv = g_ptr_array_new ();
393 g_ptr_array_add (argv, "this does not exist");
394 g_ptr_array_add (argv, NULL);
396 g_spawn_sync (NULL, (char**) argv->pdata, NULL, 0, NULL, NULL, &stdout_str,
397 NULL, &exit_status, &error);
398 g_assert_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_NOENT);
399 g_assert_null (stdout_str);
400 g_assert_cmpint (exit_status, ==, -1);
402 g_ptr_array_free (argv, TRUE);
404 g_clear_error (&error);
408 main (int argc,
409 char *argv[])
411 char *dirname;
412 int ret;
414 g_test_init (&argc, &argv, NULL);
416 dirname = g_path_get_dirname (argv[0]);
417 echo_prog_path = g_build_filename (dirname, "test-spawn-echo" EXEEXT, NULL);
418 if (!g_file_test (echo_prog_path, G_FILE_TEST_EXISTS))
420 g_free (echo_prog_path);
421 echo_prog_path = g_build_filename (dirname, "lt-test-spawn-echo" EXEEXT, NULL);
423 echo_script_path = g_build_filename (dirname, "echo-script" SCRIPT_EXT, NULL);
424 if (!g_file_test (echo_script_path, G_FILE_TEST_EXISTS))
426 g_free (echo_script_path);
427 echo_script_path = g_test_build_filename (G_TEST_DIST, "echo-script" SCRIPT_EXT, NULL);
429 g_free (dirname);
431 g_assert (g_file_test (echo_prog_path, G_FILE_TEST_EXISTS));
432 g_assert (g_file_test (echo_script_path, G_FILE_TEST_EXISTS));
434 g_test_add_func ("/gthread/spawn-single-sync", test_spawn_sync);
435 g_test_add_func ("/gthread/spawn-single-async", test_spawn_async);
436 g_test_add_func ("/gthread/spawn-single-async-with-fds", test_spawn_async_with_fds);
437 g_test_add_func ("/gthread/spawn-script", test_spawn_script);
438 g_test_add_func ("/gthread/spawn/nonexistent", test_spawn_nonexistent);
439 g_test_add_func ("/gthread/spawn-posix-spawn", test_posix_spawn);
441 ret = g_test_run();
443 g_free (echo_script_path);
444 g_free (echo_prog_path);
446 return ret;