#include #include #ifdef G_OS_UNIX #include #include #include #include #endif #ifdef G_OS_WIN32 #define LINEEND "\r\n" #define EXEEXT ".exe" #else #define LINEEND "\n" #define EXEEXT #endif static GPtrArray * get_test_subprocess_args (const char *mode, ...) G_GNUC_NULL_TERMINATED; static GPtrArray * get_test_subprocess_args (const char *mode, ...) { GPtrArray *ret; char *path; const char *binname; va_list args; gpointer arg; ret = g_ptr_array_new_with_free_func (g_free); #ifdef G_OS_WIN32 binname = "gsubprocess-testprog.exe"; #else binname = "gsubprocess-testprog"; #endif path = g_test_build_filename (G_TEST_BUILT, binname, NULL); g_ptr_array_add (ret, path); g_ptr_array_add (ret, g_strdup (mode)); va_start (args, mode); while ((arg = va_arg (args, gpointer)) != NULL) g_ptr_array_add (ret, g_strdup (arg)); va_end (args); g_ptr_array_add (ret, NULL); return ret; } static void test_noop (void) { GError *local_error = NULL; GError **error = &local_error; GPtrArray *args; GSubprocess *proc; args = get_test_subprocess_args ("noop", NULL); proc = g_subprocess_newv ((const gchar * const *) args->pdata, G_SUBPROCESS_FLAGS_NONE, error); g_ptr_array_free (args, TRUE); g_assert_no_error (local_error); g_subprocess_wait_check (proc, NULL, error); g_assert_no_error (local_error); g_object_unref (proc); } static void test_noop_all_to_null (void) { GError *local_error = NULL; GError **error = &local_error; GPtrArray *args; GSubprocess *proc; args = get_test_subprocess_args ("noop", NULL); proc = g_subprocess_newv ((const gchar * const *) args->pdata, G_SUBPROCESS_FLAGS_STDOUT_SILENCE | G_SUBPROCESS_FLAGS_STDERR_SILENCE, error); g_ptr_array_free (args, TRUE); g_assert_no_error (local_error); g_subprocess_wait_check (proc, NULL, error); g_assert_no_error (local_error); g_object_unref (proc); } static void test_noop_no_wait (void) { GError *local_error = NULL; GError **error = &local_error; GPtrArray *args; GSubprocess *proc; args = get_test_subprocess_args ("noop", NULL); proc = g_subprocess_newv ((const gchar * const *) args->pdata, G_SUBPROCESS_FLAGS_NONE, error); g_ptr_array_free (args, TRUE); g_assert_no_error (local_error); g_object_unref (proc); } static void test_noop_stdin_inherit (void) { GError *local_error = NULL; GError **error = &local_error; GPtrArray *args; GSubprocess *proc; args = get_test_subprocess_args ("noop", NULL); proc = g_subprocess_newv ((const gchar * const *) args->pdata, G_SUBPROCESS_FLAGS_STDIN_INHERIT, error); g_ptr_array_free (args, TRUE); g_assert_no_error (local_error); g_subprocess_wait_check (proc, NULL, error); g_assert_no_error (local_error); g_object_unref (proc); } #ifdef G_OS_UNIX static void test_search_path (void) { GError *local_error = NULL; GError **error = &local_error; GSubprocess *proc; proc = g_subprocess_new (G_SUBPROCESS_FLAGS_NONE, error, "true", NULL); g_assert_no_error (local_error); g_subprocess_wait_check (proc, NULL, error); g_assert_no_error (local_error); g_object_unref (proc); } #endif static void test_exit1 (void) { GError *local_error = NULL; GError **error = &local_error; GPtrArray *args; GSubprocess *proc; args = get_test_subprocess_args ("exit1", NULL); proc = g_subprocess_newv ((const gchar * const *) args->pdata, G_SUBPROCESS_FLAGS_NONE, error); g_ptr_array_free (args, TRUE); g_assert_no_error (local_error); g_subprocess_wait_check (proc, NULL, error); g_assert_error (local_error, G_SPAWN_EXIT_ERROR, 1); g_clear_error (error); g_object_unref (proc); } static gchar * splice_to_string (GInputStream *stream, GError **error) { GMemoryOutputStream *buffer = NULL; char *ret = NULL; buffer = (GMemoryOutputStream*)g_memory_output_stream_new (NULL, 0, g_realloc, g_free); if (g_output_stream_splice ((GOutputStream*)buffer, stream, 0, NULL, error) < 0) goto out; if (!g_output_stream_write ((GOutputStream*)buffer, "\0", 1, NULL, error)) goto out; if (!g_output_stream_close ((GOutputStream*)buffer, NULL, error)) goto out; ret = g_memory_output_stream_steal_data (buffer); out: g_clear_object (&buffer); return ret; } static void test_echo1 (void) { GError *local_error = NULL; GError **error = &local_error; GSubprocess *proc; GPtrArray *args; GInputStream *stdout; gchar *result; args = get_test_subprocess_args ("echo", "hello", "world!", NULL); proc = g_subprocess_newv ((const gchar * const *) args->pdata, G_SUBPROCESS_FLAGS_STDOUT_PIPE, error); g_ptr_array_free (args, TRUE); g_assert_no_error (local_error); stdout = g_subprocess_get_stdout_pipe (proc); result = splice_to_string (stdout, error); g_assert_no_error (local_error); g_assert_cmpstr (result, ==, "hello" LINEEND "world!" LINEEND); g_free (result); g_object_unref (proc); } #ifdef G_OS_UNIX static void test_echo_merged (void) { GError *local_error = NULL; GError **error = &local_error; GSubprocess *proc; GPtrArray *args; GInputStream *stdout; gchar *result; args = get_test_subprocess_args ("echo-stdout-and-stderr", "merge", "this", NULL); proc = g_subprocess_newv ((const gchar * const *) args->pdata, G_SUBPROCESS_FLAGS_STDOUT_PIPE | G_SUBPROCESS_FLAGS_STDERR_MERGE, error); g_ptr_array_free (args, TRUE); g_assert_no_error (local_error); stdout = g_subprocess_get_stdout_pipe (proc); result = splice_to_string (stdout, error); g_assert_no_error (local_error); g_assert_cmpstr (result, ==, "merge\nmerge\nthis\nthis\n"); g_free (result); g_object_unref (proc); } #endif typedef struct { guint events_pending; GMainLoop *loop; } TestCatData; static void test_cat_on_input_splice_complete (GObject *object, GAsyncResult *result, gpointer user_data) { TestCatData *data = user_data; GError *error = NULL; (void)g_output_stream_splice_finish ((GOutputStream*)object, result, &error); g_assert_no_error (error); data->events_pending--; if (data->events_pending == 0) g_main_loop_quit (data->loop); } static void test_cat_utf8 (void) { GError *local_error = NULL; GError **error = &local_error; GSubprocess *proc; GPtrArray *args; GBytes *input_buf; GBytes *output_buf; GInputStream *input_buf_stream = NULL; GOutputStream *output_buf_stream = NULL; GOutputStream *stdin_stream = NULL; GInputStream *stdout_stream = NULL; TestCatData data; memset (&data, 0, sizeof (data)); data.loop = g_main_loop_new (NULL, TRUE); args = get_test_subprocess_args ("cat", NULL); proc = g_subprocess_newv ((const gchar * const *) args->pdata, G_SUBPROCESS_FLAGS_STDIN_PIPE | G_SUBPROCESS_FLAGS_STDOUT_PIPE, error); g_ptr_array_free (args, TRUE); g_assert_no_error (local_error); stdin_stream = g_subprocess_get_stdin_pipe (proc); stdout_stream = g_subprocess_get_stdout_pipe (proc); input_buf = g_bytes_new_static ("hello, world!", strlen ("hello, world!")); input_buf_stream = g_memory_input_stream_new_from_bytes (input_buf); g_bytes_unref (input_buf); output_buf_stream = g_memory_output_stream_new (NULL, 0, g_realloc, g_free); g_output_stream_splice_async (stdin_stream, input_buf_stream, G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE | G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET, G_PRIORITY_DEFAULT, NULL, test_cat_on_input_splice_complete, &data); data.events_pending++; g_output_stream_splice_async (output_buf_stream, stdout_stream, G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE | G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET, G_PRIORITY_DEFAULT, NULL, test_cat_on_input_splice_complete, &data); data.events_pending++; g_main_loop_run (data.loop); g_subprocess_wait_check (proc, NULL, error); g_assert_no_error (local_error); output_buf = g_memory_output_stream_steal_as_bytes ((GMemoryOutputStream*)output_buf_stream); g_assert_cmpint (g_bytes_get_size (output_buf), ==, 13); g_assert_cmpint (memcmp (g_bytes_get_data (output_buf, NULL), "hello, world!", 13), ==, 0); g_bytes_unref (output_buf); g_main_loop_unref (data.loop); g_object_unref (input_buf_stream); g_object_unref (output_buf_stream); g_object_unref (proc); } static gpointer cancel_soon (gpointer user_data) { GCancellable *cancellable = user_data; g_usleep (G_TIME_SPAN_SECOND); g_cancellable_cancel (cancellable); g_object_unref (cancellable); return NULL; } static void test_cat_eof (void) { GCancellable *cancellable; GError *error = NULL; GSubprocess *cat; gboolean result; gchar buffer; gssize s; #ifdef G_OS_WIN32 g_test_skip ("This test has not been ported to Win32"); return; #endif /* Spawn 'cat' */ cat = g_subprocess_new (G_SUBPROCESS_FLAGS_STDIN_PIPE | G_SUBPROCESS_FLAGS_STDOUT_PIPE, &error, "cat", NULL); g_assert_no_error (error); g_assert (cat); /* Make sure that reading stdout blocks (until we cancel) */ cancellable = g_cancellable_new (); g_thread_unref (g_thread_new ("cancel thread", cancel_soon, g_object_ref (cancellable))); s = g_input_stream_read (g_subprocess_get_stdout_pipe (cat), &buffer, sizeof buffer, cancellable, &error); g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED); g_assert_cmpint (s, ==, -1); g_object_unref (cancellable); g_clear_error (&error); /* Close the stream (EOF on cat's stdin) */ result = g_output_stream_close (g_subprocess_get_stdin_pipe (cat), NULL, &error); g_assert_no_error (error); g_assert (result); /* Now check that reading cat's stdout gets us an EOF (since it quit) */ s = g_input_stream_read (g_subprocess_get_stdout_pipe (cat), &buffer, sizeof buffer, NULL, &error); g_assert_no_error (error); g_assert (!s); /* Check that the process has exited as a result of the EOF */ result = g_subprocess_wait (cat, NULL, &error); g_assert_no_error (error); g_assert (g_subprocess_get_if_exited (cat)); g_assert_cmpint (g_subprocess_get_exit_status (cat), ==, 0); g_assert (result); g_object_unref (cat); } typedef struct { guint events_pending; gboolean caught_error; GError *error; GMainLoop *loop; gint counter; GOutputStream *first_stdin; } TestMultiSpliceData; static void on_one_multi_splice_done (GObject *obj, GAsyncResult *res, gpointer user_data) { TestMultiSpliceData *data = user_data; if (!data->caught_error) { if (g_output_stream_splice_finish ((GOutputStream*)obj, res, &data->error) < 0) data->caught_error = TRUE; } data->events_pending--; if (data->events_pending == 0) g_main_loop_quit (data->loop); } static gboolean on_idle_multisplice (gpointer user_data) { TestMultiSpliceData *data = user_data; /* We write 2^1 + 2^2 ... + 2^10 or 2047 copies of "Hello World!\n" * ultimately */ if (data->counter >= 2047 || data->caught_error) { if (!g_output_stream_close (data->first_stdin, NULL, &data->error)) data->caught_error = TRUE; data->events_pending--; if (data->events_pending == 0) { g_main_loop_quit (data->loop); } return FALSE; } else { int i; for (i = 0; i < data->counter; i++) { gsize bytes_written; if (!g_output_stream_write_all (data->first_stdin, "hello world!\n", strlen ("hello world!\n"), &bytes_written, NULL, &data->error)) { data->caught_error = TRUE; return FALSE; } } data->counter *= 2; return TRUE; } } static void on_subprocess_exited (GObject *object, GAsyncResult *result, gpointer user_data) { GSubprocess *subprocess = G_SUBPROCESS (object); TestMultiSpliceData *data = user_data; GError *error = NULL; if (!g_subprocess_wait_finish (subprocess, result, &error)) { if (!data->caught_error) { data->caught_error = TRUE; g_propagate_error (&data->error, error); } } g_spawn_check_exit_status (g_subprocess_get_exit_status (subprocess), &error); g_assert_no_error (error); data->events_pending--; if (data->events_pending == 0) g_main_loop_quit (data->loop); } static void test_multi_1 (void) { GError *local_error = NULL; GError **error = &local_error; GPtrArray *args; GSubprocessLauncher *launcher; GSubprocess *first; GSubprocess *second; GSubprocess *third; GOutputStream *first_stdin; GInputStream *first_stdout; GOutputStream *second_stdin; GInputStream *second_stdout; GOutputStream *third_stdin; GInputStream *third_stdout; GOutputStream *membuf; TestMultiSpliceData data; int splice_flags = G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE | G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET; args = get_test_subprocess_args ("cat", NULL); launcher = g_subprocess_launcher_new (G_SUBPROCESS_FLAGS_STDIN_PIPE | G_SUBPROCESS_FLAGS_STDOUT_PIPE); first = g_subprocess_launcher_spawnv (launcher, (const gchar * const *) args->pdata, error); g_assert_no_error (local_error); second = g_subprocess_launcher_spawnv (launcher, (const gchar * const *) args->pdata, error); g_assert_no_error (local_error); third = g_subprocess_launcher_spawnv (launcher, (const gchar * const *) args->pdata, error); g_assert_no_error (local_error); g_ptr_array_free (args, TRUE); membuf = g_memory_output_stream_new (NULL, 0, g_realloc, g_free); first_stdin = g_subprocess_get_stdin_pipe (first); first_stdout = g_subprocess_get_stdout_pipe (first); second_stdin = g_subprocess_get_stdin_pipe (second); second_stdout = g_subprocess_get_stdout_pipe (second); third_stdin = g_subprocess_get_stdin_pipe (third); third_stdout = g_subprocess_get_stdout_pipe (third); memset (&data, 0, sizeof (data)); data.loop = g_main_loop_new (NULL, TRUE); data.counter = 1; data.first_stdin = first_stdin; data.events_pending++; g_output_stream_splice_async (second_stdin, first_stdout, splice_flags, G_PRIORITY_DEFAULT, NULL, on_one_multi_splice_done, &data); data.events_pending++; g_output_stream_splice_async (third_stdin, second_stdout, splice_flags, G_PRIORITY_DEFAULT, NULL, on_one_multi_splice_done, &data); data.events_pending++; g_output_stream_splice_async (membuf, third_stdout, splice_flags, G_PRIORITY_DEFAULT, NULL, on_one_multi_splice_done, &data); data.events_pending++; g_timeout_add (250, on_idle_multisplice, &data); data.events_pending++; g_subprocess_wait_async (first, NULL, on_subprocess_exited, &data); data.events_pending++; g_subprocess_wait_async (second, NULL, on_subprocess_exited, &data); data.events_pending++; g_subprocess_wait_async (third, NULL, on_subprocess_exited, &data); g_main_loop_run (data.loop); g_assert (!data.caught_error); g_assert_no_error (data.error); g_assert_cmpint (g_memory_output_stream_get_data_size ((GMemoryOutputStream*)membuf), ==, 26611); g_main_loop_unref (data.loop); g_object_unref (membuf); g_object_unref (launcher); g_object_unref (first); g_object_unref (second); g_object_unref (third); } typedef struct { gboolean is_utf8; gboolean running; GError *error; } TestAsyncCommunicateData; static void on_communicate_complete (GObject *proc, GAsyncResult *result, gpointer user_data) { TestAsyncCommunicateData *data = user_data; GBytes *stdout = NULL; char *stdout_str = NULL; const guint8 *stdout_data; gsize stdout_len; data->running = FALSE; if (data->is_utf8) (void) g_subprocess_communicate_utf8_finish ((GSubprocess*)proc, result, &stdout_str, NULL, &data->error); else (void) g_subprocess_communicate_finish ((GSubprocess*)proc, result, &stdout, NULL, &data->error); if (data->error) return; if (!data->is_utf8) { g_assert (stdout != NULL); stdout_data = g_bytes_get_data (stdout, &stdout_len); } else { g_assert (stdout_str != NULL); stdout_data = (guint8*)stdout_str; stdout_len = strlen (stdout_str); } g_assert_cmpint (stdout_len, ==, 11); g_assert (memcmp (stdout_data, "hello world", 11) == 0); if (stdout) g_bytes_unref (stdout); g_free (stdout_str); } static void test_communicate (void) { GError *error = NULL; GPtrArray *args; TestAsyncCommunicateData data = { 0, }; GSubprocess *proc; GCancellable *cancellable = NULL; GBytes *input; const char *hellostring; args = get_test_subprocess_args ("cat", NULL); proc = g_subprocess_newv ((const gchar* const*)args->pdata, G_SUBPROCESS_FLAGS_STDIN_PIPE | G_SUBPROCESS_FLAGS_STDOUT_PIPE, &error); g_assert_no_error (error); g_ptr_array_free (args, TRUE); hellostring = "hello world"; input = g_bytes_new_static (hellostring, strlen (hellostring)); g_subprocess_communicate_async (proc, input, cancellable, on_communicate_complete, &data); data.running = TRUE; while (data.running) g_main_context_iteration (NULL, TRUE); g_assert_no_error (data.error); g_bytes_unref (input); g_object_unref (proc); } static void test_communicate_utf8 (void) { GError *error = NULL; GPtrArray *args; TestAsyncCommunicateData data = { 0, }; GSubprocess *proc; GCancellable *cancellable = NULL; args = get_test_subprocess_args ("cat", NULL); proc = g_subprocess_newv ((const gchar* const*)args->pdata, G_SUBPROCESS_FLAGS_STDIN_PIPE | G_SUBPROCESS_FLAGS_STDOUT_PIPE, &error); g_assert_no_error (error); g_ptr_array_free (args, TRUE); data.is_utf8 = TRUE; g_subprocess_communicate_utf8_async (proc, "hello world", cancellable, on_communicate_complete, &data); data.running = TRUE; while (data.running) g_main_context_iteration (NULL, TRUE); g_assert_no_error (data.error); g_object_unref (proc); } static void test_communicate_utf8_invalid (void) { GError *error = NULL; GPtrArray *args; TestAsyncCommunicateData data = { 0, }; GSubprocess *proc; GCancellable *cancellable = NULL; args = get_test_subprocess_args ("cat", NULL); proc = g_subprocess_newv ((const gchar* const*)args->pdata, G_SUBPROCESS_FLAGS_STDIN_PIPE | G_SUBPROCESS_FLAGS_STDOUT_PIPE, &error); g_assert_no_error (error); g_ptr_array_free (args, TRUE); data.is_utf8 = TRUE; g_subprocess_communicate_utf8_async (proc, "\xFF\xFF", cancellable, on_communicate_complete, &data); data.running = TRUE; while (data.running) g_main_context_iteration (NULL, TRUE); g_assert_error (data.error, G_IO_ERROR, G_IO_ERROR_FAILED); g_error_free (data.error); g_object_unref (proc); } static gboolean send_terminate (gpointer user_data) { GSubprocess *proc = user_data; g_subprocess_force_exit (proc); return FALSE; } static void on_request_quit_exited (GObject *object, GAsyncResult *result, gpointer user_data) { GSubprocess *subprocess = G_SUBPROCESS (object); GError *error = NULL; g_subprocess_wait_finish (subprocess, result, &error); g_assert_no_error (error); #ifdef G_OS_UNIX g_assert (g_subprocess_get_if_signaled (subprocess)); g_assert (g_subprocess_get_term_sig (subprocess) == 9); #endif g_spawn_check_exit_status (g_subprocess_get_status (subprocess), &error); g_assert (error != NULL); g_clear_error (&error); g_main_loop_quit ((GMainLoop*)user_data); } static void test_terminate (void) { GError *local_error = NULL; GError **error = &local_error; GSubprocess *proc; GPtrArray *args; GMainLoop *loop; args = get_test_subprocess_args ("sleep-forever", NULL); proc = g_subprocess_newv ((const gchar * const *) args->pdata, G_SUBPROCESS_FLAGS_NONE, error); g_ptr_array_free (args, TRUE); g_assert_no_error (local_error); loop = g_main_loop_new (NULL, TRUE); g_subprocess_wait_async (proc, NULL, on_request_quit_exited, loop); g_timeout_add_seconds (3, send_terminate, proc); g_main_loop_run (loop); g_main_loop_unref (loop); g_object_unref (proc); } static void test_env (void) { GError *local_error = NULL; GError **error = &local_error; GSubprocessLauncher *launcher; GSubprocess *proc; GPtrArray *args; GInputStream *stdout; gchar *result; gchar *envp[] = { "ONE=1", "TWO=1", "THREE=3", "FOUR=1", NULL }; gchar **split; args = get_test_subprocess_args ("env", NULL); launcher = g_subprocess_launcher_new (G_SUBPROCESS_FLAGS_NONE); g_subprocess_launcher_set_flags (launcher, G_SUBPROCESS_FLAGS_STDOUT_PIPE); g_subprocess_launcher_set_environ (launcher, envp); g_subprocess_launcher_setenv (launcher, "TWO", "2", TRUE); g_subprocess_launcher_setenv (launcher, "THREE", "1", FALSE); g_subprocess_launcher_unsetenv (launcher, "FOUR"); g_assert_null (g_subprocess_launcher_getenv (launcher, "FOUR")); proc = g_subprocess_launcher_spawn (launcher, error, args->pdata[0], "env", NULL); g_ptr_array_free (args, TRUE); g_assert_no_error (local_error); stdout = g_subprocess_get_stdout_pipe (proc); result = splice_to_string (stdout, error); split = g_strsplit (result, "\n", -1); g_assert_cmpstr (g_environ_getenv (split, "ONE"), ==, "1"); g_assert_cmpstr (g_environ_getenv (split, "TWO"), ==, "2"); g_assert_cmpstr (g_environ_getenv (split, "THREE"), ==, "3"); g_assert_null (g_environ_getenv (split, "FOUR")); g_strfreev (split); g_free (result); g_object_unref (proc); } #ifdef G_OS_UNIX static void test_stdout_file (void) { GError *local_error = NULL; GError **error = &local_error; GSubprocessLauncher *launcher; GSubprocess *proc; GPtrArray *args; GFile *tmpfile; GFileIOStream *iostream; GOutputStream *stdin; const char *test_data = "this is some test data\n"; char *tmp_contents; char *tmp_file_path; tmpfile = g_file_new_tmp ("gsubprocessXXXXXX", &iostream, error); g_assert_no_error (local_error); g_clear_object (&iostream); tmp_file_path = g_file_get_path (tmpfile); args = get_test_subprocess_args ("cat", NULL); launcher = g_subprocess_launcher_new (G_SUBPROCESS_FLAGS_STDIN_PIPE); g_subprocess_launcher_set_stdout_file_path (launcher, tmp_file_path); proc = g_subprocess_launcher_spawnv (launcher, (const gchar * const *) args->pdata, error); g_ptr_array_free (args, TRUE); g_assert_no_error (local_error); stdin = g_subprocess_get_stdin_pipe (proc); g_output_stream_write_all (stdin, test_data, strlen (test_data), NULL, NULL, error); g_assert_no_error (local_error); g_output_stream_close (stdin, NULL, error); g_assert_no_error (local_error); g_subprocess_wait_check (proc, NULL, error); g_object_unref (launcher); g_object_unref (proc); g_file_load_contents (tmpfile, NULL, &tmp_contents, NULL, NULL, error); g_assert_no_error (local_error); g_assert_cmpstr (test_data, ==, tmp_contents); g_free (tmp_contents); (void) g_file_delete (tmpfile, NULL, NULL); g_object_unref (tmpfile); g_free (tmp_file_path); } static void test_stdout_fd (void) { GError *local_error = NULL; GError **error = &local_error; GSubprocessLauncher *launcher; GSubprocess *proc; GPtrArray *args; GFile *tmpfile; GFileIOStream *iostream; GFileDescriptorBased *descriptor_stream; GOutputStream *stdin; const char *test_data = "this is some test data\n"; char *tmp_contents; tmpfile = g_file_new_tmp ("gsubprocessXXXXXX", &iostream, error); g_assert_no_error (local_error); args = get_test_subprocess_args ("cat", NULL); launcher = g_subprocess_launcher_new (G_SUBPROCESS_FLAGS_STDIN_PIPE); descriptor_stream = G_FILE_DESCRIPTOR_BASED (g_io_stream_get_output_stream (G_IO_STREAM (iostream))); g_subprocess_launcher_take_stdout_fd (launcher, dup (g_file_descriptor_based_get_fd (descriptor_stream))); proc = g_subprocess_launcher_spawnv (launcher, (const gchar * const *) args->pdata, error); g_ptr_array_free (args, TRUE); g_assert_no_error (local_error); g_clear_object (&iostream); stdin = g_subprocess_get_stdin_pipe (proc); g_output_stream_write_all (stdin, test_data, strlen (test_data), NULL, NULL, error); g_assert_no_error (local_error); g_output_stream_close (stdin, NULL, error); g_assert_no_error (local_error); g_subprocess_wait_check (proc, NULL, error); g_object_unref (launcher); g_object_unref (proc); g_file_load_contents (tmpfile, NULL, &tmp_contents, NULL, NULL, error); g_assert_no_error (local_error); g_assert_cmpstr (test_data, ==, tmp_contents); g_free (tmp_contents); (void) g_file_delete (tmpfile, NULL, NULL); g_object_unref (tmpfile); } static void child_setup (gpointer user_data) { dup2 (GPOINTER_TO_INT (user_data), 1); } static void test_child_setup (void) { GError *local_error = NULL; GError **error = &local_error; GSubprocessLauncher *launcher; GSubprocess *proc; GPtrArray *args; GFile *tmpfile; GFileIOStream *iostream; GOutputStream *stdin; const char *test_data = "this is some test data\n"; char *tmp_contents; int fd; tmpfile = g_file_new_tmp ("gsubprocessXXXXXX", &iostream, error); g_assert_no_error (local_error); fd = g_file_descriptor_based_get_fd (G_FILE_DESCRIPTOR_BASED (g_io_stream_get_output_stream (G_IO_STREAM (iostream)))); args = get_test_subprocess_args ("cat", NULL); launcher = g_subprocess_launcher_new (G_SUBPROCESS_FLAGS_STDIN_PIPE); g_subprocess_launcher_set_child_setup (launcher, child_setup, GINT_TO_POINTER (fd), NULL); proc = g_subprocess_launcher_spawnv (launcher, (const gchar * const *) args->pdata, error); g_ptr_array_free (args, TRUE); g_assert_no_error (local_error); g_clear_object (&iostream); stdin = g_subprocess_get_stdin_pipe (proc); g_output_stream_write_all (stdin, test_data, strlen (test_data), NULL, NULL, error); g_assert_no_error (local_error); g_output_stream_close (stdin, NULL, error); g_assert_no_error (local_error); g_subprocess_wait_check (proc, NULL, error); g_object_unref (launcher); g_object_unref (proc); g_file_load_contents (tmpfile, NULL, &tmp_contents, NULL, NULL, error); g_assert_no_error (local_error); g_assert_cmpstr (test_data, ==, tmp_contents); g_free (tmp_contents); (void) g_file_delete (tmpfile, NULL, NULL); g_object_unref (tmpfile); } static void test_pass_fd (void) { GError *local_error = NULL; GError **error = &local_error; GInputStream *child_input; GDataInputStream *child_datainput; GSubprocessLauncher *launcher; GSubprocess *proc; GPtrArray *args; int basic_pipefds[2]; int needdup_pipefds[2]; char *buf; gsize len; char *basic_fd_str; char *needdup_fd_str; g_unix_open_pipe (basic_pipefds, FD_CLOEXEC, error); g_assert_no_error (local_error); g_unix_open_pipe (needdup_pipefds, FD_CLOEXEC, error); g_assert_no_error (local_error); basic_fd_str = g_strdup_printf ("%d", basic_pipefds[1]); needdup_fd_str = g_strdup_printf ("%d", needdup_pipefds[1] + 1); args = get_test_subprocess_args ("write-to-fds", basic_fd_str, needdup_fd_str, NULL); launcher = g_subprocess_launcher_new (G_SUBPROCESS_FLAGS_NONE); g_subprocess_launcher_take_fd (launcher, basic_pipefds[1], basic_pipefds[1]); g_subprocess_launcher_take_fd (launcher, needdup_pipefds[1], needdup_pipefds[1] + 1); proc = g_subprocess_launcher_spawnv (launcher, (const gchar * const *) args->pdata, error); g_ptr_array_free (args, TRUE); g_assert_no_error (local_error); g_free (basic_fd_str); g_free (needdup_fd_str); child_input = g_unix_input_stream_new (basic_pipefds[0], TRUE); child_datainput = g_data_input_stream_new (child_input); buf = g_data_input_stream_read_line_utf8 (child_datainput, &len, NULL, error); g_assert_no_error (local_error); g_assert_cmpstr (buf, ==, "hello world"); g_object_unref (child_datainput); g_object_unref (child_input); g_free (buf); child_input = g_unix_input_stream_new (needdup_pipefds[0], TRUE); child_datainput = g_data_input_stream_new (child_input); buf = g_data_input_stream_read_line_utf8 (child_datainput, &len, NULL, error); g_assert_no_error (local_error); g_assert_cmpstr (buf, ==, "hello world"); g_free (buf); g_object_unref (child_datainput); g_object_unref (child_input); g_object_unref (launcher); g_object_unref (proc); } #endif int main (int argc, char **argv) { g_test_init (&argc, &argv, NULL); g_test_add_func ("/gsubprocess/noop", test_noop); g_test_add_func ("/gsubprocess/noop-all-to-null", test_noop_all_to_null); g_test_add_func ("/gsubprocess/noop-no-wait", test_noop_no_wait); g_test_add_func ("/gsubprocess/noop-stdin-inherit", test_noop_stdin_inherit); #ifdef G_OS_UNIX g_test_add_func ("/gsubprocess/search-path", test_search_path); #endif g_test_add_func ("/gsubprocess/exit1", test_exit1); g_test_add_func ("/gsubprocess/echo1", test_echo1); #ifdef G_OS_UNIX g_test_add_func ("/gsubprocess/echo-merged", test_echo_merged); #endif g_test_add_func ("/gsubprocess/cat-utf8", test_cat_utf8); g_test_add_func ("/gsubprocess/cat-eof", test_cat_eof); g_test_add_func ("/gsubprocess/multi1", test_multi_1); g_test_add_func ("/gsubprocess/communicate", test_communicate); g_test_add_func ("/gsubprocess/communicate-utf8", test_communicate_utf8); g_test_add_func ("/gsubprocess/communicate-utf8-invalid", test_communicate_utf8_invalid); g_test_add_func ("/gsubprocess/terminate", test_terminate); g_test_add_func ("/gsubprocess/env", test_env); #ifdef G_OS_UNIX g_test_add_func ("/gsubprocess/stdout-file", test_stdout_file); g_test_add_func ("/gsubprocess/stdout-fd", test_stdout_fd); g_test_add_func ("/gsubprocess/child-setup", test_child_setup); g_test_add_func ("/gsubprocess/pass-fd", test_pass_fd); #endif return g_test_run (); }