summaryrefslogtreecommitdiff
path: root/gio/tests/gdbus-connection.c
diff options
context:
space:
mode:
Diffstat (limited to 'gio/tests/gdbus-connection.c')
-rw-r--r--gio/tests/gdbus-connection.c653
1 files changed, 653 insertions, 0 deletions
diff --git a/gio/tests/gdbus-connection.c b/gio/tests/gdbus-connection.c
new file mode 100644
index 000000000..1174d6f29
--- /dev/null
+++ b/gio/tests/gdbus-connection.c
@@ -0,0 +1,653 @@
+/* GLib testing framework examples and tests
+ *
+ * Copyright (C) 2008-2009 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: David Zeuthen <davidz@redhat.com>
+ */
+
+#include <gio/gio.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "gdbus-tests.h"
+
+/* all tests rely on a shared mainloop */
+static GMainLoop *loop = NULL;
+
+/* ---------------------------------------------------------------------------------------------------- */
+/* Connection life-cycle testing */
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+test_connection_life_cycle (void)
+{
+ GDBusConnection *c;
+ GDBusConnection *c2;
+ GError *error;
+
+ error = NULL;
+
+ /*
+ * Check for correct behavior when no bus is present
+ *
+ */
+ c = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
+ _g_assert_error_domain (error, G_IO_ERROR);
+ g_assert (!g_dbus_error_is_remote_error (error));
+ g_assert (c == NULL);
+ g_error_free (error);
+ error = NULL;
+
+ /*
+ * Check for correct behavior when a bus is present
+ */
+ session_bus_up ();
+ /* case 1 */
+ c = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
+ g_assert_no_error (error);
+ g_assert (c != NULL);
+ g_assert (!g_dbus_connection_is_closed (c));
+
+ /*
+ * Check that singleton handling work
+ */
+ c2 = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
+ g_assert_no_error (error);
+ g_assert (c2 != NULL);
+ g_assert (c == c2);
+ g_object_unref (c2);
+
+ /*
+ * Check that private connections work
+ */
+ c2 = _g_bus_get_priv (G_BUS_TYPE_SESSION, NULL, &error);
+ g_assert_no_error (error);
+ g_assert (c2 != NULL);
+ g_assert (c != c2);
+ g_object_unref (c2);
+
+ c2 = _g_bus_get_priv (G_BUS_TYPE_SESSION, NULL, &error);
+ g_assert_no_error (error);
+ g_assert (c2 != NULL);
+ g_assert (!g_dbus_connection_is_closed (c2));
+ g_dbus_connection_close (c2);
+ _g_assert_signal_received (c2, "closed");
+ g_assert (g_dbus_connection_is_closed (c2));
+ g_object_unref (c2);
+
+ /*
+ * Check for correct behavior when the bus goes away
+ *
+ */
+ g_assert (!g_dbus_connection_is_closed (c));
+ g_dbus_connection_set_exit_on_close (c, FALSE);
+ session_bus_down ();
+ if (!g_dbus_connection_is_closed (c))
+ _g_assert_signal_received (c, "closed");
+ g_assert (g_dbus_connection_is_closed (c));
+
+ _g_object_wait_for_single_ref (c);
+ g_object_unref (c);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+/* Test that sending and receiving messages work as expected */
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+msg_cb_expect_error_disconnected (GDBusConnection *connection,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GError *error;
+ GVariant *result;
+
+ error = NULL;
+ result = g_dbus_connection_invoke_method_finish (connection,
+ res,
+ &error);
+ g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CLOSED);
+ g_assert (!g_dbus_error_is_remote_error (error));
+ g_error_free (error);
+ g_assert (result == NULL);
+
+ g_main_loop_quit (loop);
+}
+
+static void
+msg_cb_expect_error_unknown_method (GDBusConnection *connection,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GError *error;
+ GVariant *result;
+
+ error = NULL;
+ result = g_dbus_connection_invoke_method_finish (connection,
+ res,
+ &error);
+ g_assert_error (error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD);
+ g_assert (g_dbus_error_is_remote_error (error));
+ g_assert (result == NULL);
+
+ g_main_loop_quit (loop);
+}
+
+static void
+msg_cb_expect_success (GDBusConnection *connection,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GError *error;
+ GVariant *result;
+
+ error = NULL;
+ result = g_dbus_connection_invoke_method_finish (connection,
+ res,
+ &error);
+ g_assert_no_error (error);
+ g_assert (result != NULL);
+ g_variant_unref (result);
+
+ g_main_loop_quit (loop);
+}
+
+static void
+msg_cb_expect_error_cancelled (GDBusConnection *connection,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GError *error;
+ GVariant *result;
+
+ error = NULL;
+ result = g_dbus_connection_invoke_method_finish (connection,
+ res,
+ &error);
+ g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED);
+ g_assert (!g_dbus_error_is_remote_error (error));
+ g_error_free (error);
+ g_assert (result == NULL);
+
+ g_main_loop_quit (loop);
+}
+
+static void
+msg_cb_expect_error_cancelled_2 (GDBusConnection *connection,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GError *error;
+ GVariant *result;
+
+ error = NULL;
+ result = g_dbus_connection_invoke_method_finish (connection,
+ res,
+ &error);
+ g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED);
+ g_assert (!g_dbus_error_is_remote_error (error));
+ g_error_free (error);
+ g_assert (result == NULL);
+
+ g_main_loop_quit (loop);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+test_connection_send (void)
+{
+ GDBusConnection *c;
+ GCancellable *ca;
+
+ session_bus_up ();
+
+ /* First, get an unopened connection */
+ c = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
+ g_assert (c != NULL);
+ g_assert (!g_dbus_connection_is_closed (c));
+
+ /*
+ * Check that we never actually send a message if the GCancellable
+ * is already cancelled - i.e. we should get #G_IO_ERROR_CANCELLED
+ * when the actual connection is not up.
+ */
+ ca = g_cancellable_new ();
+ g_cancellable_cancel (ca);
+ g_dbus_connection_invoke_method (c,
+ "org.freedesktop.DBus", /* bus_name */
+ "/org/freedesktop/DBus", /* object path */
+ "org.freedesktop.DBus", /* interface name */
+ "GetId", /* method name */
+ NULL,
+ G_DBUS_INVOKE_METHOD_FLAGS_NONE,
+ -1,
+ ca,
+ (GAsyncReadyCallback) msg_cb_expect_error_cancelled,
+ NULL);
+ g_main_loop_run (loop);
+ g_object_unref (ca);
+
+ /*
+ * Check that we get a reply to the GetId() method call.
+ */
+ g_dbus_connection_invoke_method (c,
+ "org.freedesktop.DBus", /* bus_name */
+ "/org/freedesktop/DBus", /* object path */
+ "org.freedesktop.DBus", /* interface name */
+ "GetId", /* method name */
+ NULL,
+ G_DBUS_INVOKE_METHOD_FLAGS_NONE,
+ -1,
+ NULL,
+ (GAsyncReadyCallback) msg_cb_expect_success,
+ NULL);
+ g_main_loop_run (loop);
+
+ /*
+ * Check that we get an error reply to the NonExistantMethod() method call.
+ */
+ g_dbus_connection_invoke_method (c,
+ "org.freedesktop.DBus", /* bus_name */
+ "/org/freedesktop/DBus", /* object path */
+ "org.freedesktop.DBus", /* interface name */
+ "NonExistantMethod", /* method name */
+ NULL,
+ G_DBUS_INVOKE_METHOD_FLAGS_NONE,
+ -1,
+ NULL,
+ (GAsyncReadyCallback) msg_cb_expect_error_unknown_method,
+ NULL);
+ g_main_loop_run (loop);
+
+ /*
+ * Check that cancellation works when the message is already in flight.
+ */
+ ca = g_cancellable_new ();
+ g_dbus_connection_invoke_method (c,
+ "org.freedesktop.DBus", /* bus_name */
+ "/org/freedesktop/DBus", /* object path */
+ "org.freedesktop.DBus", /* interface name */
+ "GetId", /* method name */
+ NULL,
+ G_DBUS_INVOKE_METHOD_FLAGS_NONE,
+ -1,
+ ca,
+ (GAsyncReadyCallback) msg_cb_expect_error_cancelled_2,
+ NULL);
+ g_cancellable_cancel (ca);
+ g_main_loop_run (loop);
+ g_object_unref (ca);
+
+ /*
+ * Check that we get an error when sending to a connection that is disconnected.
+ */
+ g_dbus_connection_set_exit_on_close (c, FALSE);
+ session_bus_down ();
+ _g_assert_signal_received (c, "closed");
+ g_assert (g_dbus_connection_is_closed (c));
+
+ g_dbus_connection_invoke_method (c,
+ "org.freedesktop.DBus", /* bus_name */
+ "/org/freedesktop/DBus", /* object path */
+ "org.freedesktop.DBus", /* interface name */
+ "GetId", /* method name */
+ NULL,
+ G_DBUS_INVOKE_METHOD_FLAGS_NONE,
+ -1,
+ NULL,
+ (GAsyncReadyCallback) msg_cb_expect_error_disconnected,
+ NULL);
+ g_main_loop_run (loop);
+
+ _g_object_wait_for_single_ref (c);
+ g_object_unref (c);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+/* Connection signal tests */
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+test_connection_signal_handler (GDBusConnection *connection,
+ const gchar *sender_name,
+ const gchar *object_path,
+ const gchar *interface_name,
+ const gchar *signal_name,
+ GVariant *parameters,
+ gpointer user_data)
+{
+ gint *counter = user_data;
+ *counter += 1;
+
+ /*g_debug ("in test_connection_signal_handler (sender=%s path=%s interface=%s member=%s)",
+ sender_name,
+ object_path,
+ interface_name,
+ signal_name);*/
+
+ g_main_loop_quit (loop);
+}
+
+static gboolean
+test_connection_signal_quit_mainloop (gpointer user_data)
+{
+ gboolean *quit_mainloop_fired = user_data;
+ *quit_mainloop_fired = TRUE;
+ g_main_loop_quit (loop);
+ return TRUE;
+}
+
+static void
+test_connection_signals (void)
+{
+ GDBusConnection *c1;
+ GDBusConnection *c2;
+ GDBusConnection *c3;
+ guint s1;
+ guint s2;
+ guint s3;
+ gint count_s1;
+ gint count_s2;
+ gint count_name_owner_changed;
+ GError *error;
+ gboolean ret;
+ GVariant *result;
+
+ error = NULL;
+
+ /*
+ * Bring up first separate connections
+ */
+ session_bus_up ();
+ /* if running with dbus-monitor, it claims the name :1.0 - so if we don't run with the monitor
+ * emulate this
+ */
+ if (g_getenv ("G_DBUS_MONITOR") == NULL)
+ {
+ c1 = _g_bus_get_priv (G_BUS_TYPE_SESSION, NULL, NULL);
+ g_assert (c1 != NULL);
+ g_assert (!g_dbus_connection_is_closed (c1));
+ g_object_unref (c1);
+ }
+ c1 = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
+ g_assert (c1 != NULL);
+ g_assert (!g_dbus_connection_is_closed (c1));
+ g_assert_cmpstr (g_dbus_connection_get_unique_name (c1), ==, ":1.1");
+
+ /*
+ * Install two signal handlers for the first connection
+ *
+ * - Listen to the signal "Foo" from :1.2 (e.g. c2)
+ * - Listen to the signal "Foo" from anyone (e.g. both c2 and c3)
+ *
+ * and then count how many times this signal handler was invoked.
+ */
+ s1 = g_dbus_connection_signal_subscribe (c1,
+ ":1.2",
+ "org.gtk.GDBus.ExampleInterface",
+ "Foo",
+ "/org/gtk/GDBus/ExampleInterface",
+ NULL,
+ test_connection_signal_handler,
+ &count_s1,
+ NULL);
+ s2 = g_dbus_connection_signal_subscribe (c1,
+ NULL, /* match any sender */
+ "org.gtk.GDBus.ExampleInterface",
+ "Foo",
+ "/org/gtk/GDBus/ExampleInterface",
+ NULL,
+ test_connection_signal_handler,
+ &count_s2,
+ NULL);
+ s3 = g_dbus_connection_signal_subscribe (c1,
+ "org.freedesktop.DBus", /* sender */
+ "org.freedesktop.DBus", /* interface */
+ "NameOwnerChanged", /* member */
+ "/org/freedesktop/DBus", /* path */
+ NULL,
+ test_connection_signal_handler,
+ &count_name_owner_changed,
+ NULL);
+ g_assert (s1 != 0);
+ g_assert (s2 != 0);
+ g_assert (s3 != 0);
+
+ count_s1 = 0;
+ count_s2 = 0;
+ count_name_owner_changed = 0;
+
+ /*
+ * Bring up two other connections
+ */
+ c2 = _g_bus_get_priv (G_BUS_TYPE_SESSION, NULL, NULL);
+ g_assert (c2 != NULL);
+ g_assert (!g_dbus_connection_is_closed (c2));
+ g_assert_cmpstr (g_dbus_connection_get_unique_name (c2), ==, ":1.2");
+ c3 = _g_bus_get_priv (G_BUS_TYPE_SESSION, NULL, NULL);
+ g_assert (c3 != NULL);
+ g_assert (!g_dbus_connection_is_closed (c3));
+ g_assert_cmpstr (g_dbus_connection_get_unique_name (c3), ==, ":1.3");
+
+ /*
+ * Make c2 emit "Foo" - we should catch it twice
+ *
+ * Note that there is no way to be sure that the signal subscriptions
+ * on c1 are effective yet - for all we know, the AddMatch() messages
+ * could sit waiting in a buffer somewhere between this process and
+ * the message bus. And emitting signals on c2 (a completely other
+ * socket!) will not necessarily change this.
+ *
+ * To ensure this is not the case, do a synchronous call on c1.
+ */
+ result = g_dbus_connection_invoke_method_sync (c1,
+ "org.freedesktop.DBus", /* bus name */
+ "/org/freedesktop/DBus", /* object path */
+ "org.freedesktop.DBus", /* interface name */
+ "GetId", /* method name */
+ NULL, /* parameters */
+ G_DBUS_INVOKE_METHOD_FLAGS_NONE,
+ -1,
+ NULL,
+ &error);
+ g_assert_no_error (error);
+ g_assert (result != NULL);
+ g_variant_unref (result);
+ /* now, emit the signal on c2 */
+ ret = g_dbus_connection_emit_signal (c2,
+ NULL, /* destination bus name */
+ "/org/gtk/GDBus/ExampleInterface",
+ "org.gtk.GDBus.ExampleInterface",
+ "Foo",
+ NULL,
+ &error);
+ g_assert_no_error (error);
+ g_assert (ret);
+ while (!(count_s1 == 1 && count_s2 == 1))
+ g_main_loop_run (loop);
+ g_assert_cmpint (count_s1, ==, 1);
+ g_assert_cmpint (count_s2, ==, 1);
+
+ /*
+ * Make c3 emit "Foo" - we should catch it only once
+ */
+ ret = g_dbus_connection_emit_signal (c3,
+ NULL, /* destination bus name */
+ "/org/gtk/GDBus/ExampleInterface",
+ "org.gtk.GDBus.ExampleInterface",
+ "Foo",
+ NULL,
+ &error);
+ g_assert_no_error (error);
+ g_assert (ret);
+ while (!(count_s1 == 1 && count_s2 == 2))
+ g_main_loop_run (loop);
+ g_assert_cmpint (count_s1, ==, 1);
+ g_assert_cmpint (count_s2, ==, 2);
+
+ /*
+ * Also to check the total amount of NameOwnerChanged signals - use a 5 second ceiling
+ * to avoid spinning forever
+ */
+ gboolean quit_mainloop_fired;
+ guint quit_mainloop_id;
+ quit_mainloop_fired = FALSE;
+ quit_mainloop_id = g_timeout_add (5000, test_connection_signal_quit_mainloop, &quit_mainloop_fired);
+ while (count_name_owner_changed != 2 && !quit_mainloop_fired)
+ g_main_loop_run (loop);
+ g_source_remove (quit_mainloop_id);
+ g_assert_cmpint (count_s1, ==, 1);
+ g_assert_cmpint (count_s2, ==, 2);
+ g_assert_cmpint (count_name_owner_changed, ==, 2);
+
+ g_dbus_connection_signal_unsubscribe (c1, s1);
+ g_dbus_connection_signal_unsubscribe (c1, s2);
+ g_dbus_connection_signal_unsubscribe (c1, s3);
+
+ _g_object_wait_for_single_ref (c1);
+ _g_object_wait_for_single_ref (c2);
+ _g_object_wait_for_single_ref (c3);
+
+ g_object_unref (c1);
+ g_object_unref (c2);
+ g_object_unref (c3);
+
+ session_bus_down ();
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+typedef struct
+{
+ guint num_handled;
+ guint32 serial;
+} FilterData;
+
+static gboolean
+filter_func (GDBusConnection *connection,
+ GDBusMessage *message,
+ gpointer user_data)
+{
+ FilterData *data = user_data;
+ guint32 reply_serial;
+
+ reply_serial = g_dbus_message_get_reply_serial (message);
+ if (reply_serial == data->serial)
+ data->num_handled += 1;
+
+ return FALSE;
+}
+
+static void
+test_connection_filter (void)
+{
+ GDBusConnection *c;
+ FilterData data;
+ GDBusMessage *m;
+ GDBusMessage *r;
+ GError *error;
+ guint filter_id;
+
+ memset (&data, '\0', sizeof (FilterData));
+
+ session_bus_up ();
+
+ error = NULL;
+ c = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
+ g_assert_no_error (error);
+ g_assert (c != NULL);
+
+ filter_id = g_dbus_connection_add_filter (c,
+ filter_func,
+ &data,
+ NULL);
+
+ m = g_dbus_message_new_method_call ("org.freedesktop.DBus", /* name */
+ "/org/freedesktop/DBus", /* path */
+ "org.freedesktop.DBus", /* interface */
+ "GetNameOwner");
+ g_dbus_message_set_body (m, g_variant_new ("(s)", "org.freedesktop.DBus"));
+ error = NULL;
+ g_dbus_connection_send_message (c, m, &data.serial, &error);
+ g_assert_no_error (error);
+
+ while (data.num_handled == 0)
+ g_thread_yield ();
+
+ g_dbus_connection_send_message (c, m, &data.serial, &error);
+ g_assert_no_error (error);
+
+ while (data.num_handled == 1)
+ g_thread_yield ();
+
+ r = g_dbus_connection_send_message_with_reply_sync (c,
+ m,
+ -1,
+ &data.serial,
+ NULL, /* GCancellable */
+ &error);
+ g_assert_no_error (error);
+ g_assert (r != NULL);
+ g_object_unref (r);
+ g_assert_cmpint (data.num_handled, ==, 3);
+
+ g_dbus_connection_remove_filter (c, filter_id);
+
+ r = g_dbus_connection_send_message_with_reply_sync (c,
+ m,
+ -1,
+ &data.serial,
+ NULL, /* GCancellable */
+ &error);
+ g_assert_no_error (error);
+ g_assert (r != NULL);
+ g_object_unref (r);
+ g_assert_cmpint (data.num_handled, ==, 3);
+
+ _g_object_wait_for_single_ref (c);
+ g_object_unref (c);
+ g_object_unref (m);
+
+ session_bus_down ();
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+int
+main (int argc,
+ char *argv[])
+{
+ g_type_init ();
+ g_test_init (&argc, &argv, NULL);
+
+ /* all the tests rely on a shared main loop */
+ loop = g_main_loop_new (NULL, FALSE);
+
+ /* all the tests use a session bus with a well-known address that we can bring up and down
+ * using session_bus_up() and session_bus_down().
+ */
+ g_unsetenv ("DISPLAY");
+ g_setenv ("DBUS_SESSION_BUS_ADDRESS", session_bus_get_temporary_address (), TRUE);
+
+ g_test_add_func ("/gdbus/connection-life-cycle", test_connection_life_cycle);
+ g_test_add_func ("/gdbus/connection-send", test_connection_send);
+ g_test_add_func ("/gdbus/connection-signals", test_connection_signals);
+ g_test_add_func ("/gdbus/connection-filter", test_connection_filter);
+ return g_test_run();
+}