#include <gio/gio.h>
+#ifdef G_OS_UNIX
+#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
+#endif
#include "gdbus-sessionbus.h"
static gint appeared;
static gint disappeared;
static gint changed;
-static gboolean died;
-static gboolean timed_out;
-GPid pid;
static void
name_appeared (GDBusConnection *connection,
g_main_loop_quit (loop);
}
-static gboolean
-start_application (gpointer data)
+#ifdef G_OS_UNIX
+void
+child_setup_pipe (gpointer user_data)
{
- gchar *argv[] = { "./testapp", NULL };
+ int *fds = user_data;
- g_assert (g_spawn_async (NULL, argv, NULL, 0, NULL, NULL, &pid, NULL));
+ close (fds[0]);
+ dup2 (fds[1], 3);
+ g_setenv ("_G_TEST_SLAVE_FD", "3", TRUE);
+ close (fds[1]);
+}
+#endif
- return FALSE;
+static gboolean
+spawn_async_with_monitor_pipe (const gchar *argv[], GPid *pid, int *fd)
+{
+#ifdef G_OS_UNIX
+ int fds[2];
+ gboolean result;
+
+ pipe (fds);
+
+ result = g_spawn_async (NULL, (char**)argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD, child_setup_pipe, &fds, pid, NULL);
+ close (fds[1]);
+ *fd = fds[0];
+ return result;
+#else
+ *fd = -1;
+ return g_spawn_async (NULL, argv, 0, NULL, NULL, pid, NULL);
+#endif
}
static gboolean
-run_application_sync (gpointer data)
+start_application (GPid *pid, int *fd)
{
- GMainLoop *loop = data;
+ const gchar *argv[] = { "./testapp", NULL };
- g_assert (g_spawn_command_line_sync ("./testapp", NULL, NULL, NULL, NULL));
+ g_assert (spawn_async_with_monitor_pipe (argv, pid, fd));
- if (loop)
- g_main_loop_quit (loop);
+ return FALSE;
+}
+
+typedef struct {
+ GMainContext *context;
+ GSource *child_watch;
+ GSource *timeout;
+ GPid pid;
+ int fd;
+
+ gboolean child_exited;
+ GMainLoop *loop;
+} AwaitChildTerminationData;
+
+static void
+on_child_termination_exited (GPid pid,
+ gint status,
+ gpointer user_data)
+{
+ AwaitChildTerminationData *data = user_data;
+ data->child_exited = TRUE;
+ g_spawn_close_pid (data->pid);
+ g_main_loop_quit (data->loop);
+}
+static gboolean
+on_child_termination_timeout (gpointer user_data)
+{
+ AwaitChildTerminationData *data = user_data;
+ g_main_loop_quit (data->loop);
return FALSE;
}
+static void
+await_child_termination_init (AwaitChildTerminationData *data,
+ GPid pid,
+ int fd)
+{
+ data->context = g_main_context_get_thread_default ();
+ data->child_exited = FALSE;
+ data->pid = pid;
+ data->fd = fd;
+}
+
+static void
+await_child_termination_terminate (AwaitChildTerminationData *data)
+{
+#ifdef G_OS_UNIX
+ kill (data->pid, SIGTERM);
+ close (data->fd);
+#endif
+}
+
static gboolean
-timeout (gpointer data)
+await_child_termination_run (AwaitChildTerminationData *data)
{
- GMainLoop *loop = data;
+ GSource *timeout_source;
+ GSource *child_watch_source;
- timed_out = TRUE;
+ data->loop = g_main_loop_new (data->context, FALSE);
- g_main_loop_quit (loop);
+ child_watch_source = g_child_watch_source_new (data->pid);
+ g_source_set_callback (child_watch_source, (GSourceFunc) on_child_termination_exited, data, NULL);
+ g_source_attach (child_watch_source, data->context);
+ g_source_unref (child_watch_source);
+
+ timeout_source = g_timeout_source_new_seconds (5);
+ g_source_set_callback (timeout_source, on_child_termination_timeout, data, NULL);
+ g_source_attach (timeout_source, data->context);
+ g_source_unref (timeout_source);
+
+ g_main_loop_run (data->loop);
+
+ g_source_destroy (child_watch_source);
+ g_source_destroy (timeout_source);
+
+ g_main_loop_unref (data->loop);
- return TRUE;
+ return data->child_exited;
}
-/* This test starts an application, checks that its name appears
- * on the bus, then starts it again and checks that the second
- * instance exits right away.
- */
static void
-test_unique (void)
+terminate_child_sync (GPid pid, int fd)
{
+ AwaitChildTerminationData data;
+
+ await_child_termination_init (&data, pid, fd);
+ await_child_termination_terminate (&data);
+ await_child_termination_run (&data);
+}
+
+typedef void (*RunWithApplicationFunc) (void);
+
+typedef struct {
GMainLoop *loop;
- gint watch;
- guint id1, id2, id3;
+ RunWithApplicationFunc func;
+ guint timeout_id;
+} RunWithAppNameAppearedData;
- appeared = 0;
- timed_out = FALSE;
+static void
+on_run_with_application_name_appeared (GDBusConnection *connection,
+ const gchar *name,
+ const gchar *name_owner,
+ gpointer user_data)
+{
+ RunWithAppNameAppearedData *data = user_data;
+
+ data->func ();
+
+ g_main_loop_quit (data->loop);
+}
+
+static gboolean
+on_run_with_application_timeout (gpointer user_data)
+{
+ RunWithAppNameAppearedData *data = user_data;
+ data->timeout_id = 0;
+ g_error ("Timed out starting testapp");
+ return FALSE;
+}
+
+static void
+run_with_application (RunWithApplicationFunc test_func)
+{
+ GMainLoop *loop;
+ RunWithAppNameAppearedData data;
+ gint watch;
+ GPid main_pid;
+ gint main_fd;
loop = g_main_loop_new (NULL, FALSE);
- id1 = g_timeout_add (5000, timeout, loop);
+
+ data.timeout_id = 0;
+ data.func = test_func;
+ data.loop = loop;
watch = g_bus_watch_name (G_BUS_TYPE_SESSION,
"org.gtk.test.app",
0,
- name_appeared,
+ on_run_with_application_name_appeared,
NULL,
- loop,
+ &data,
NULL);
- id2 = g_timeout_add (0, start_application, loop);
+ data.timeout_id = g_timeout_add_seconds (5, on_run_with_application_timeout, &data);
- g_main_loop_run (loop);
-
- g_assert_cmpint (appeared, ==, 1);
-
- id3 = g_timeout_add (0, run_application_sync, loop);
+ start_application (&main_pid, &main_fd);
g_main_loop_run (loop);
- g_assert_cmpint (appeared, ==, 1);
- g_assert_cmpint (timed_out, ==, FALSE);
+ if (data.timeout_id)
+ {
+ g_source_remove (data.timeout_id);
+ data.timeout_id = 0;
+ }
- g_bus_unwatch_name (watch);
+ g_main_loop_unref (loop);
- kill (pid, SIGTERM);
+ g_bus_unwatch_name (watch);
- g_main_loop_unref (loop);
- g_source_remove (id1);
- g_source_remove (id2);
- g_source_remove (id3);
+ terminate_child_sync (main_pid, main_fd);
}
-static gboolean
-quit_app (gpointer data)
+/* This test starts an application, checks that its name appears
+ * on the bus, then starts it again and checks that the second
+ * instance exits right away.
+ */
+static void
+test_unique_on_app_appeared (void)
{
- GDBusConnection *connection;
- GVariant *res;
+ GPid sub_pid;
+ int sub_fd;
+ int watch;
+ AwaitChildTerminationData data;
- connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
- res = g_dbus_connection_call_sync (connection,
- "org.gtk.test.app",
- "/org/gtk/test/app",
- "org.gtk.Application",
- "Quit",
- g_variant_new ("(u)", 0),
- NULL,
- G_DBUS_CALL_FLAGS_NONE,
- -1,
- NULL,
- NULL);
- if (res)
- g_variant_unref (res);
+ appeared = 0;
+ disappeared = 0;
- return FALSE;
+ watch = g_bus_watch_name (G_BUS_TYPE_SESSION,
+ "org.gtk.test.app",
+ 0,
+ name_appeared,
+ name_disappeared,
+ NULL,
+ NULL);
+
+ start_application (&sub_pid, &sub_fd);
+ await_child_termination_init (&data, sub_pid, sub_fd);
+ await_child_termination_run (&data);
+
+ g_bus_unwatch_name (watch);
+
+ g_assert_cmpint (appeared, ==, 1);
+ g_assert_cmpint (disappeared, ==, 0);
}
static void
-child_is_dead (GPid pid,
- gint status,
- gpointer data)
+test_unique (void)
{
- GMainLoop *loop = data;
-
- died++;
+ run_with_application (test_unique_on_app_appeared);
+}
- g_assert (WIFEXITED (status) && WEXITSTATUS(status) == 0);
+static void
+on_name_disappeared_quit (GDBusConnection *connection,
+ const gchar *name,
+ gpointer user_data)
+{
+ GMainLoop *loop = user_data;
- if (loop)
- g_main_loop_quit (loop);
+ g_main_loop_quit (loop);
}
-/* This test start an application, checks that its name appears on
- * the bus, then calls Quit, and verifies that the name disappears
- * and the application exits.
+/* This test starts an application, checks that its name appears on
+ * the bus, then calls Quit, and verifies that the name disappears and
+ * the application exits.
*/
static void
-test_quit (void)
+test_quit_on_app_appeared (void)
{
GMainLoop *loop;
- gint watch;
- guint id1, id2, id3;
- gchar *argv[] = { "./testapp", NULL };
-
- appeared = 0;
- disappeared = 0;
- died = FALSE;
- timed_out = FALSE;
+ int quit_disappeared_watch;
+ GDBusConnection *connection;
loop = g_main_loop_new (NULL, FALSE);
- watch = g_bus_watch_name (G_BUS_TYPE_SESSION,
+ quit_disappeared_watch = g_bus_watch_name (G_BUS_TYPE_SESSION,
"org.gtk.test.app",
0,
- name_appeared,
- name_disappeared,
NULL,
+ on_name_disappeared_quit,
+ loop,
NULL);
- g_assert (g_spawn_async (NULL, argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL, &pid, NULL));
-
- id1 = g_child_watch_add (pid, child_is_dead, loop);
-
- id2 = g_timeout_add (500, quit_app, NULL);
-
- id3 = g_timeout_add (5000, timeout, loop);
+ connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
+ g_dbus_connection_call (connection,
+ "org.gtk.test.app",
+ "/org/gtk/test/app",
+ "org.gtk.Application",
+ "Quit",
+ g_variant_new ("(u)", 0),
+ NULL,
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ NULL,
+ NULL, NULL);
g_main_loop_run (loop);
- g_assert_cmpint (timed_out, ==, FALSE);
- g_assert_cmpint (appeared, ==, 1);
- g_assert_cmpint (disappeared, >=, 1);
- g_assert_cmpint (died, ==, TRUE);
- g_bus_unwatch_name (watch);
+ g_bus_unwatch_name (quit_disappeared_watch);
g_main_loop_unref (loop);
- g_source_remove (id1);
- g_source_remove (id2);
- g_source_remove (id3);
+}
+
+static void
+test_quit (void)
+{
+ run_with_application (test_quit_on_app_appeared);
}
static gboolean
return strv;
}
-/* This test start an application, waits for its name to appear on
- * the bus, then calls ListActions, and verifies that it gets the expected
- * actions back.
- */
static void
-test_list_actions (void)
+test_list_actions_on_app_appeared (void)
{
- GMainLoop *loop;
- gchar *argv[] = { "./testapp", NULL };
gchar **actions;
- gint watch;
-
- appeared = 0;
-
- loop = g_main_loop_new (NULL, FALSE);
- watch = g_bus_watch_name (G_BUS_TYPE_SESSION,
- "org.gtk.test.app",
- 0,
- name_appeared,
- NULL,
- loop,
- NULL);
-
- g_assert (g_spawn_async (NULL, argv, NULL, 0, NULL, NULL, &pid, NULL));
- if (!appeared)
- g_main_loop_run (loop);
- g_main_loop_unref (loop);
actions = list_actions ();
g_assert (_g_strv_has_string ((const char *const *)actions, "action2"));
g_strfreev (actions);
+}
- kill (pid, SIGTERM);
-
- g_bus_unwatch_name (watch);
+/* This test start an application, waits for its name to appear on
+ * the bus, then calls ListActions, and verifies that it gets the expected
+ * actions back.
+ */
+static void
+test_list_actions (void)
+{
+ run_with_application (test_list_actions_on_app_appeared);
}
static gboolean
-invoke_action (gpointer data)
+invoke_action (gpointer user_data)
{
- const gchar *action = data;
+ const gchar *action = user_data;
GDBusConnection *connection;
GVariant *res;
return FALSE;
}
-static void
-exit_with_code_1 (GPid pid,
- gint status,
- gpointer data)
-{
- GMainLoop *loop = data;
-
- died++;
-
- g_assert (WIFEXITED (status) && WEXITSTATUS(status) == 1);
-
- if (loop)
- g_main_loop_quit (loop);
-}
-
/* This test starts an application, waits for it to appear,
* then invokes 'action1' and checks that it causes the application
* to exit with an exit code of 1.
test_invoke (void)
{
GMainLoop *loop;
- gint watch;
- gchar *argv[] = { "./testapp", NULL };
- guint id1, id2, id3;
-
- appeared = 0;
- disappeared = 0;
- died = FALSE;
- timed_out = FALSE;
+ int quit_disappeared_watch;
loop = g_main_loop_new (NULL, FALSE);
- watch = g_bus_watch_name (G_BUS_TYPE_SESSION,
- "org.gtk.test.app",
- 0,
- name_appeared,
- name_disappeared,
- NULL,
- NULL);
- g_assert (g_spawn_async (NULL, argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL, &pid, NULL));
+ quit_disappeared_watch = g_bus_watch_name (G_BUS_TYPE_SESSION,
+ "org.gtk.test.app",
+ 0,
+ NULL,
+ on_name_disappeared_quit,
+ loop,
+ NULL);
- id1 = g_child_watch_add (pid, exit_with_code_1, loop);
-
- id2 = g_timeout_add (500, invoke_action, "action1");
-
- id3 = g_timeout_add (5000, timeout, loop);
+ g_timeout_add (0, invoke_action, "action1");
g_main_loop_run (loop);
- g_assert_cmpint (timed_out, ==, FALSE);
- g_assert_cmpint (appeared, >=, 1);
- g_assert_cmpint (disappeared, >=, 1);
- g_assert_cmpint (died, ==, TRUE);
- g_bus_unwatch_name (watch);
- g_main_loop_unref (loop);
- g_source_remove (id1);
- g_source_remove (id2);
- g_source_remove (id3);
-
- kill (pid, SIGTERM);
+ g_bus_unwatch_name (quit_disappeared_watch);
}
static void
-test_remote (void)
+test_remote_on_application_appeared (void)
{
- GMainLoop *loop;
- gint watch;
- GPid pid1, pid2;
- gchar *argv[] = { "./testapp", NULL, NULL };
-
- appeared = 0;
- timed_out = FALSE;
-
- loop = g_main_loop_new (NULL, FALSE);
- g_timeout_add (5000, timeout, loop);
-
- watch = g_bus_watch_name (G_BUS_TYPE_SESSION,
- "org.gtk.test.app",
- 0,
- name_appeared,
- NULL,
- loop,
- NULL);
-
- g_assert (g_spawn_async (NULL, argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL, &pid1, NULL));
- g_child_watch_add (pid1, exit_with_code_1, loop);
-
- g_main_loop_run (loop);
-
- g_assert_cmpint (appeared, ==, 1);
-
- argv[1] = "--non-unique";
- g_assert (g_spawn_async (NULL, argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL, &pid2, NULL));
+ GPid sub_pid;
+ int sub_fd;
+ AwaitChildTerminationData data;
+ gchar *argv[] = { "./testapp", "--non-unique", NULL };
- g_main_loop_run (loop);
-
- g_assert_cmpint (appeared, ==, 1);
- g_assert_cmpint (timed_out, ==, FALSE);
+ spawn_async_with_monitor_pipe ((const char **) argv, &sub_pid, &sub_fd);
- g_main_loop_unref (loop);
- g_bus_unwatch_name (watch);
+ await_child_termination_init (&data, sub_pid, sub_fd);
+ await_child_termination_run (&data);
+}
- kill (pid1, SIGTERM);
- kill (pid2, SIGTERM);
+static void
+test_remote (void)
+{
+ run_with_application (test_remote_on_application_appeared);
}
static void
}
static void
-test_change_action (void)
+test_change_action_on_application_appeared (void)
{
GMainLoop *loop;
- gint watch;
guint id;
- GPid pid1;
- gchar *argv[] = { "./testapp", NULL, NULL };
GDBusConnection *connection;
- appeared = 0;
- changed = 0;
- timed_out = FALSE;
-
loop = g_main_loop_new (NULL, FALSE);
- g_timeout_add (5000, timeout, loop);
-
- watch = g_bus_watch_name (G_BUS_TYPE_SESSION,
- "org.gtk.test.app",
- 0,
- name_appeared,
- NULL,
- loop,
- NULL);
-
- g_assert (g_spawn_async (NULL, argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL, &pid1, NULL));
- g_main_loop_run (loop);
-
- g_assert_cmpint (appeared, ==, 1);
connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
id = g_dbus_connection_signal_subscribe (connection,
loop,
NULL);
- g_timeout_add (1000, invoke_action, "action2");
+ g_timeout_add (0, invoke_action, "action2");
g_main_loop_run (loop);
g_assert_cmpint (changed, >, 0);
- g_assert_cmpint (timed_out, ==, FALSE);
g_dbus_connection_signal_unsubscribe (connection, id);
g_object_unref (connection);
g_main_loop_unref (loop);
- g_bus_unwatch_name (watch);
+}
- kill (pid1, SIGTERM);
+static void
+test_change_action (void)
+{
+ run_with_application (test_change_action_on_application_appeared);
}
int