glib/gio/tests/gsubprocess.c
Philip Withnall 66ab836f5a gsubprocess: Fix a critical calling communicate() with no pipes
If calling g_subprocess_communicate() on a GSubprocess with no
stdout/stderr pipe, a critical warning would be emitted from
g_memory_output_stream_steal_as_bytes(), as it would be called on a NULL
output stream.

Fix that, improve the relevant GIR annotations, and expand the unit
tests to cover it (and various other combinations of flags).

Signed-off-by: Philip Withnall <withnall@endlessm.com>

https://bugzilla.gnome.org/show_bug.cgi?id=793331
2018-02-13 16:27:32 +00:00

1544 lines
45 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include <gio/gio.h>
#include <string.h>
#ifdef G_OS_UNIX
#include <sys/wait.h>
#include <glib-unix.h>
#include <gio/gunixinputstream.h>
#include <gio/gfiledescriptorbased.h>
#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;
const gchar *id;
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);
id = g_subprocess_get_identifier (proc);
g_assert (id != NULL);
g_subprocess_wait_check (proc, NULL, error);
g_assert_no_error (local_error);
g_assert (g_subprocess_get_successful (proc));
g_object_unref (proc);
}
static void
check_ready (GObject *source,
GAsyncResult *res,
gpointer user_data)
{
gboolean ret;
GError *error = NULL;
ret = g_subprocess_wait_check_finish (G_SUBPROCESS (source),
res,
&error);
g_assert (ret);
g_assert_no_error (error);
g_object_unref (source);
}
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_async (proc, NULL, check_ready, NULL);
}
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);
}
typedef struct {
GMainLoop *loop;
GCancellable *cancellable;
gboolean cb_called;
} TestExit1CancelData;
static gboolean
test_exit1_cancel_idle_quit_cb (gpointer user_data)
{
GMainLoop *loop = user_data;
g_main_loop_quit (loop);
return G_SOURCE_REMOVE;
}
static void
test_exit1_cancel_wait_check_cb (GObject *source,
GAsyncResult *result,
gpointer user_data)
{
GSubprocess *subprocess = G_SUBPROCESS (source);
TestExit1CancelData *data = user_data;
gboolean ret;
GError *error = NULL;
g_assert_false (data->cb_called);
data->cb_called = TRUE;
ret = g_subprocess_wait_check_finish (subprocess, result, &error);
g_assert (!ret);
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED);
g_clear_error (&error);
g_idle_add (test_exit1_cancel_idle_quit_cb, data->loop);
}
static void
test_exit1_cancel (void)
{
GError *local_error = NULL;
GError **error = &local_error;
GPtrArray *args;
GSubprocess *proc;
TestExit1CancelData data = { 0 };
g_test_bug ("786456");
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);
data.loop = g_main_loop_new (NULL, FALSE);
data.cancellable = g_cancellable_new ();
g_subprocess_wait_check_async (proc, data.cancellable, test_exit1_cancel_wait_check_cb, &data);
g_subprocess_wait_check (proc, NULL, error);
g_assert_error (local_error, G_SPAWN_EXIT_ERROR, 1);
g_clear_error (error);
g_cancellable_cancel (data.cancellable);
g_main_loop_run (data.loop);
g_object_unref (proc);
g_main_loop_unref (data.loop);
g_clear_object (&data.cancellable);
}
static void
test_exit1_cancel_in_cb_wait_check_cb (GObject *source,
GAsyncResult *result,
gpointer user_data)
{
GSubprocess *subprocess = G_SUBPROCESS (source);
TestExit1CancelData *data = user_data;
gboolean ret;
GError *error = NULL;
g_assert_false (data->cb_called);
data->cb_called = TRUE;
ret = g_subprocess_wait_check_finish (subprocess, result, &error);
g_assert (!ret);
g_assert_error (error, G_SPAWN_EXIT_ERROR, 1);
g_clear_error (&error);
g_cancellable_cancel (data->cancellable);
g_idle_add (test_exit1_cancel_idle_quit_cb, data->loop);
}
static void
test_exit1_cancel_in_cb (void)
{
GError *local_error = NULL;
GError **error = &local_error;
GPtrArray *args;
GSubprocess *proc;
TestExit1CancelData data = { 0 };
g_test_bug ("786456");
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);
data.loop = g_main_loop_new (NULL, FALSE);
data.cancellable = g_cancellable_new ();
g_subprocess_wait_check_async (proc, data.cancellable, test_exit1_cancel_in_cb_wait_check_cb, &data);
g_subprocess_wait_check (proc, NULL, error);
g_assert_error (local_error, G_SPAWN_EXIT_ERROR, 1);
g_clear_error (error);
g_main_loop_run (data.loop);
g_object_unref (proc);
g_main_loop_unref (data.loop);
g_clear_object (&data.cancellable);
}
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_cmpmem (g_bytes_get_data (output_buf, NULL),
g_bytes_get_size (output_buf),
"hello, world!", 13);
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 {
GSubprocessFlags flags;
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_bytes = NULL, *stderr_bytes = NULL;
char *stdout_str = NULL, *stderr_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, &stderr_str, &data->error);
else
(void) g_subprocess_communicate_finish ((GSubprocess*)proc, result,
&stdout_bytes, &stderr_bytes, &data->error);
if (data->error)
return;
if (data->flags & G_SUBPROCESS_FLAGS_STDOUT_PIPE)
{
if (data->is_utf8)
{
stdout_data = (guint8*)stdout_str;
stdout_len = strlen (stdout_str);
}
else
stdout_data = g_bytes_get_data (stdout_bytes, &stdout_len);
g_assert_cmpmem (stdout_data, stdout_len, "# hello world\n", 14);
}
else
{
g_assert_null (stdout_str);
g_assert_null (stdout_bytes);
}
if (data->flags & G_SUBPROCESS_FLAGS_STDERR_PIPE)
{
if (data->is_utf8)
g_assert_nonnull (stderr_str);
else
g_assert_nonnull (stderr_bytes);
}
else
{
g_assert_null (stderr_str);
g_assert_null (stderr_bytes);
}
g_clear_pointer (&stdout_bytes, g_bytes_unref);
g_clear_pointer (&stderr_bytes, g_bytes_unref);
g_free (stdout_str);
g_free (stderr_str);
}
/* Test g_subprocess_communicate_async() works correctly with a variety of flags,
* as passed in via @test_data. */
static void
test_communicate_async (gconstpointer test_data)
{
GSubprocessFlags flags = GPOINTER_TO_INT (test_data);
GError *error = NULL;
GPtrArray *args;
TestAsyncCommunicateData data = { flags, 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 | flags,
&error);
g_assert_no_error (error);
g_ptr_array_free (args, TRUE);
/* Include a leading hash and trailing newline so that if this gets onto the
* tests stdout, it doesnt mess up TAP output. */
hellostring = "# hello world\n";
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);
}
/* Test g_subprocess_communicate() works correctly with a variety of flags,
* as passed in via @test_data. */
static void
test_communicate (gconstpointer test_data)
{
GSubprocessFlags flags = GPOINTER_TO_INT (test_data);
GError *error = NULL;
GPtrArray *args;
GSubprocess *proc;
GCancellable *cancellable = NULL;
GBytes *input;
const gchar *hellostring;
GBytes *stdout_bytes, *stderr_bytes;
const gchar *stdout_data;
gsize stdout_len;
args = get_test_subprocess_args ("cat", NULL);
proc = g_subprocess_newv ((const gchar* const*)args->pdata,
G_SUBPROCESS_FLAGS_STDIN_PIPE | flags,
&error);
g_assert_no_error (error);
g_ptr_array_free (args, TRUE);
/* Include a leading hash and trailing newline so that if this gets onto the
* tests stdout, it doesnt mess up TAP output. */
hellostring = "# hello world\n";
input = g_bytes_new_static (hellostring, strlen (hellostring));
g_subprocess_communicate (proc, input, cancellable, &stdout_bytes, &stderr_bytes, &error);
g_assert_no_error (error);
if (flags & G_SUBPROCESS_FLAGS_STDOUT_PIPE)
{
stdout_data = g_bytes_get_data (stdout_bytes, &stdout_len);
g_assert_cmpmem (stdout_data, stdout_len, "# hello world\n", 14);
}
else
g_assert_null (stdout_bytes);
if (flags & G_SUBPROCESS_FLAGS_STDERR_PIPE)
g_assert_nonnull (stderr_bytes);
else
g_assert_null (stderr_bytes);
g_bytes_unref (input);
g_clear_pointer (&stdout_bytes, g_bytes_unref);
g_clear_pointer (&stderr_bytes, g_bytes_unref);
g_object_unref (proc);
}
/* Test g_subprocess_communicate_utf8_async() works correctly with a variety of
* flags, as passed in via @test_data. */
static void
test_communicate_utf8_async (gconstpointer test_data)
{
GSubprocessFlags flags = GPOINTER_TO_INT (test_data);
GError *error = NULL;
GPtrArray *args;
TestAsyncCommunicateData data = { flags, 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 | flags,
&error);
g_assert_no_error (error);
g_ptr_array_free (args, TRUE);
data.is_utf8 = TRUE;
g_subprocess_communicate_utf8_async (proc, "# hello world\n",
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);
}
/* Test g_subprocess_communicate_utf8() works correctly with a variety of flags,
* as passed in via @test_data. */
static void
test_communicate_utf8 (gconstpointer test_data)
{
GSubprocessFlags flags = GPOINTER_TO_INT (test_data);
GError *error = NULL;
GPtrArray *args;
GSubprocess *proc;
GCancellable *cancellable = NULL;
const gchar *stdin_buf;
gchar *stdout_buf, *stderr_buf;
args = get_test_subprocess_args ("cat", NULL);
proc = g_subprocess_newv ((const gchar* const*)args->pdata,
G_SUBPROCESS_FLAGS_STDIN_PIPE | flags,
&error);
g_assert_no_error (error);
g_ptr_array_free (args, TRUE);
/* Include a leading hash and trailing newline so that if this gets onto the
* tests stdout, it doesnt mess up TAP output. */
stdin_buf = "# hello world\n";
g_subprocess_communicate_utf8 (proc, stdin_buf, cancellable, &stdout_buf, &stderr_buf, &error);
g_assert_no_error (error);
if (flags & G_SUBPROCESS_FLAGS_STDOUT_PIPE)
g_assert_cmpstr (stdout_buf, ==, "# hello world\n");
else
g_assert_null (stdout_buf);
if (flags & G_SUBPROCESS_FLAGS_STDERR_PIPE)
g_assert_nonnull (stderr_buf);
else g_assert_null (stderr_buf);
g_free (stdout_buf);
g_free (stderr_buf);
g_object_unref (proc);
}
static void
test_communicate_nothing (void)
{
GError *error = NULL;
GPtrArray *args;
GSubprocess *proc;
GCancellable *cancellable = NULL;
gchar *stdout_buf;
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
| G_SUBPROCESS_FLAGS_STDERR_MERGE,
&error);
g_assert_no_error (error);
g_ptr_array_free (args, TRUE);
g_subprocess_communicate_utf8 (proc, "", cancellable, &stdout_buf, NULL, &error);
g_assert_no_error (error);
g_assert_cmpstr (stdout_buf, ==, "");
g_free (stdout_buf);
g_object_unref (proc);
}
static void
test_communicate_utf8_invalid (void)
{
GSubprocessFlags flags = G_SUBPROCESS_FLAGS_STDOUT_PIPE;
GError *error = NULL;
GPtrArray *args;
TestAsyncCommunicateData data = { flags, 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 | flags,
&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);
}
#ifdef G_OS_UNIX
static gboolean
send_signal (gpointer user_data)
{
GSubprocess *proc = user_data;
g_subprocess_send_signal (proc, SIGKILL);
return FALSE;
}
static void
test_signal (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_signal, proc);
g_main_loop_run (loop);
g_main_loop_unref (loop);
g_object_unref (proc);
}
#endif
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);
g_object_unref (launcher);
}
/* Test that explicitly inheriting and modifying the parent process
* environment works. */
static void
test_env_inherit (void)
{
GError *local_error = NULL;
GError **error = &local_error;
GSubprocessLauncher *launcher;
GSubprocess *proc;
GPtrArray *args;
GInputStream *stdout;
gchar *result;
gchar **split;
g_setenv ("TEST_ENV_INHERIT1", "1", TRUE);
g_setenv ("TEST_ENV_INHERIT2", "2", TRUE);
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, NULL);
g_subprocess_launcher_setenv (launcher, "TWO", "2", TRUE);
g_subprocess_launcher_unsetenv (launcher, "TEST_ENV_INHERIT1");
g_assert_null (g_subprocess_launcher_getenv (launcher, "TEST_ENV_INHERIT1"));
g_assert_cmpstr (g_subprocess_launcher_getenv (launcher, "TEST_ENV_INHERIT2"), ==, "2");
g_assert_cmpstr (g_subprocess_launcher_getenv (launcher, "TWO"), ==, "2");
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_null (g_environ_getenv (split, "TEST_ENV_INHERIT1"));
g_assert_cmpstr (g_environ_getenv (split, "TEST_ENV_INHERIT2"), ==, "2");
g_assert_cmpstr (g_environ_getenv (split, "TWO"), ==, "2");
g_strfreev (split);
g_free (result);
g_object_unref (proc);
g_object_unref (launcher);
}
static void
test_cwd (void)
{
GError *local_error = NULL;
GError **error = &local_error;
GSubprocessLauncher *launcher;
GSubprocess *proc;
GPtrArray *args;
GInputStream *stdout;
gchar *result;
const char *basename;
args = get_test_subprocess_args ("cwd", NULL);
launcher = g_subprocess_launcher_new (G_SUBPROCESS_FLAGS_STDOUT_PIPE);
g_subprocess_launcher_set_flags (launcher, G_SUBPROCESS_FLAGS_STDOUT_PIPE);
g_subprocess_launcher_set_cwd (launcher, "/tmp");
proc = g_subprocess_launcher_spawnv (launcher, (const char * const *)args->pdata, 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);
basename = g_strrstr (result, "/");
g_assert (basename != NULL);
g_assert_cmpstr (basename, ==, "/tmp" LINEEND);
g_free (result);
g_object_unref (proc);
g_object_unref (launcher);
}
#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
static void
test_launcher_environment (void)
{
GSubprocessLauncher *launcher;
GError *error = NULL;
GSubprocess *proc;
GPtrArray *args;
gchar *out;
g_setenv ("A", "B", TRUE);
g_setenv ("C", "D", TRUE);
launcher = g_subprocess_launcher_new (G_SUBPROCESS_FLAGS_STDOUT_PIPE);
/* unset a variable */
g_subprocess_launcher_unsetenv (launcher, "A");
/* and set a diffferent one */
g_subprocess_launcher_setenv (launcher, "E", "F", TRUE);
args = get_test_subprocess_args ("printenv", "A", "C", "E", NULL);
proc = g_subprocess_launcher_spawnv (launcher, (const gchar **) args->pdata, &error);
g_assert_no_error (error);
g_assert (proc);
g_subprocess_communicate_utf8 (proc, NULL, NULL, &out, NULL, &error);
g_assert_no_error (error);
g_assert_cmpstr (out, ==, "C=D\nE=F\n");
g_free (out);
g_object_unref (proc);
g_object_unref (launcher);
g_ptr_array_unref (args);
}
int
main (int argc, char **argv)
{
const struct
{
const gchar *subtest;
GSubprocessFlags flags;
}
flags_vectors[] =
{
{ "", G_SUBPROCESS_FLAGS_STDOUT_PIPE | G_SUBPROCESS_FLAGS_STDERR_MERGE },
{ "/no-pipes", G_SUBPROCESS_FLAGS_NONE },
{ "/separate-stderr", G_SUBPROCESS_FLAGS_STDOUT_PIPE | G_SUBPROCESS_FLAGS_STDERR_PIPE },
{ "/stdout-only", G_SUBPROCESS_FLAGS_STDOUT_PIPE },
{ "/stderr-only", G_SUBPROCESS_FLAGS_STDERR_PIPE },
{ "/stdout-silence", G_SUBPROCESS_FLAGS_STDOUT_SILENCE },
};
gsize i;
g_test_init (&argc, &argv, NULL);
g_test_bug_base ("https://bugzilla.gnome.org/");
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);
g_test_add_func ("/gsubprocess/signal", test_signal);
#endif
g_test_add_func ("/gsubprocess/exit1", test_exit1);
g_test_add_func ("/gsubprocess/exit1/cancel", test_exit1_cancel);
g_test_add_func ("/gsubprocess/exit1/cancel_in_cb", test_exit1_cancel_in_cb);
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);
/* Add various tests for g_subprocess_communicate() with different flags. */
for (i = 0; i < G_N_ELEMENTS (flags_vectors); i++)
{
gchar *test_path = NULL;
test_path = g_strdup_printf ("/gsubprocess/communicate%s", flags_vectors[i].subtest);
g_test_add_data_func (test_path, GINT_TO_POINTER (flags_vectors[i].flags),
test_communicate);
g_free (test_path);
test_path = g_strdup_printf ("/gsubprocess/communicate/async%s", flags_vectors[i].subtest);
g_test_add_data_func (test_path, GINT_TO_POINTER (flags_vectors[i].flags),
test_communicate_async);
g_free (test_path);
test_path = g_strdup_printf ("/gsubprocess/communicate/utf8%s", flags_vectors[i].subtest);
g_test_add_data_func (test_path, GINT_TO_POINTER (flags_vectors[i].flags),
test_communicate_utf8);
g_free (test_path);
test_path = g_strdup_printf ("/gsubprocess/communicate/utf8/async%s", flags_vectors[i].subtest);
g_test_add_data_func (test_path, GINT_TO_POINTER (flags_vectors[i].flags),
test_communicate_utf8_async);
g_free (test_path);
}
g_test_add_func ("/gsubprocess/communicate/utf8/invalid", test_communicate_utf8_invalid);
g_test_add_func ("/gsubprocess/communicate/nothing", test_communicate_nothing);
g_test_add_func ("/gsubprocess/terminate", test_terminate);
g_test_add_func ("/gsubprocess/env", test_env);
g_test_add_func ("/gsubprocess/env/inherit", test_env_inherit);
g_test_add_func ("/gsubprocess/cwd", test_cwd);
#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
g_test_add_func ("/gsubprocess/launcher-environment", test_launcher_environment);
return g_test_run ();
}