[evolution-data-server/mmeeks-gdbus-import] add the tests
- From: Michael Meeks <michael src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evolution-data-server/mmeeks-gdbus-import] add the tests
- Date: Fri, 26 Feb 2010 11:09:11 +0000 (UTC)
commit 6bb725ff168c36ece73f69a087da2b712a6474a8
Author: Michael Meeks <michael meeks novell com>
Date: Fri Feb 26 11:03:23 2010 +0000
add the tests
Makefile.am | 4 +-
edbus/ebitlock.c | 2 +-
edbus/edbusenumtypes.c | 2 +-
edbus/edbusenumtypes.c.template | 2 +-
edbus/tests/Makefile.am | 51 ++
edbus/tests/connection.c | 486 +++++++++++++++++
edbus/tests/error.c | 198 +++++++
edbus/tests/export.c | 1118 +++++++++++++++++++++++++++++++++++++++
edbus/tests/introspection.c | 163 ++++++
edbus/tests/names.c | 651 +++++++++++++++++++++++
edbus/tests/peer.c | 448 ++++++++++++++++
edbus/tests/proxy.c | 387 ++++++++++++++
edbus/tests/sessionbus.c | 342 ++++++++++++
edbus/tests/sessionbus.h | 38 ++
edbus/tests/tests.c | 131 +++++
edbus/tests/tests.h | 117 ++++
edbus/tests/threading.c | 529 ++++++++++++++++++
17 files changed, 4664 insertions(+), 5 deletions(-)
---
diff --git a/Makefile.am b/Makefile.am
index 0c00c9c..d1c567b 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -6,8 +6,8 @@ if ENABLE_CALENDAR
CALENDAR_DIR = calendar
endif
-SUBDIRS = win32 libedataserver libebackend servers camel addressbook $(CALENDAR_DIR) libedataserverui docs art po edbus
-DIST_SUBDIRS = win32 libedataserver libebackend servers camel addressbook calendar libedataserverui docs art po edbus
+SUBDIRS = win32 edbus libedataserver libebackend servers camel addressbook $(CALENDAR_DIR) libedataserverui docs art po
+DIST_SUBDIRS = win32 edbus libedataserver libebackend servers camel addressbook calendar libedataserverui docs art po
changelogs = \
ChangeLog
diff --git a/edbus/ebitlock.c b/edbus/ebitlock.c
index a1dcaa5..1d245ea 100644
--- a/edbus/ebitlock.c
+++ b/edbus/ebitlock.c
@@ -38,7 +38,7 @@ static GSList *g_futex_address_list = NULL;
static GMutex *g_futex_mutex = NULL;
#endif
-#warning Need to initialize me ...
+/* called by gdbus_threads_init */
extern void _g_futex_thread_init(void);
void
diff --git a/edbus/edbusenumtypes.c b/edbus/edbusenumtypes.c
index 39fb906..a853fa9 100644
--- a/edbus/edbusenumtypes.c
+++ b/edbus/edbusenumtypes.c
@@ -1,7 +1,7 @@
/* Generated data (by glib-mkenums) */
-#include <gdbus.h>
+#include <edbus.h>
/* enumerations from "evariant.h" */
GType
diff --git a/edbus/edbusenumtypes.c.template b/edbus/edbusenumtypes.c.template
index 317fa22..935f3e0 100644
--- a/edbus/edbusenumtypes.c.template
+++ b/edbus/edbusenumtypes.c.template
@@ -1,5 +1,5 @@
/*** BEGIN file-header ***/
-#include <gdbus.h>
+#include <edbus.h>
/*** END file-header ***/
diff --git a/edbus/tests/Makefile.am b/edbus/tests/Makefile.am
new file mode 100644
index 0000000..765a7f1
--- /dev/null
+++ b/edbus/tests/Makefile.am
@@ -0,0 +1,51 @@
+include $(srcdir)/Makefile.decl
+
+NULL =
+
+INCLUDES = \
+ -g \
+ -I$(top_srcdir) \
+ $(EDBUS_CFLAGS) \
+ $(NULL)
+
+noinst_PROGRAMS = $(TEST_PROGS)
+progs_ldadd = \
+ $(EDBUS_LIBS) \
+ $(top_builddir)/edbus/libedbus.la \
+ $(NULL)
+
+TEST_PROGS += connection
+TEST_PROGS += names
+TEST_PROGS += proxy
+TEST_PROGS += introspection
+TEST_PROGS += threading
+TEST_PROGS += export
+TEST_PROGS += error
+TEST_PROGS += peer
+
+connection_SOURCES = connection.c sessionbus.c sessionbus.h tests.h tests.c
+connection_LDADD = $(progs_ldadd)
+
+names_SOURCES = names.c sessionbus.c sessionbus.h tests.h tests.c
+names_LDADD = $(progs_ldadd)
+
+proxy_SOURCES = proxy.c sessionbus.c sessionbus.h tests.h tests.c
+proxy_LDADD = $(progs_ldadd)
+
+introspection_SOURCES = introspection.c sessionbus.c sessionbus.h tests.h tests.c
+introspection_LDADD = $(progs_ldadd)
+
+threading_SOURCES = threading.c sessionbus.c sessionbus.h tests.h tests.c
+threading_LDADD = $(progs_ldadd)
+
+export_SOURCES = export.c sessionbus.c sessionbus.h tests.h tests.c
+export_CFLAGS = $(DBUS1_CFLAGS)
+export_LDADD = $(progs_ldadd)
+
+error_SOURCES = error.c sessionbus.c sessionbus.h tests.h tests.c
+error_CFLAGS = $(DBUS1_CFLAGS)
+error_LDADD = $(progs_ldadd)
+
+peer_SOURCES = peer.c sessionbus.c sessionbus.h tests.h tests.c
+peer_CFLAGS = $(DBUS1_CFLAGS)
+peer_LDADD = $(progs_ldadd)
diff --git a/edbus/tests/connection.c b/edbus/tests/connection.c
new file mode 100644
index 0000000..709b575
--- /dev/null
+++ b/edbus/tests/connection.c
@@ -0,0 +1,486 @@
+/* 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 <edbus/edbus.h>
+#include <unistd.h>
+
+#include "tests.h"
+
+/* all tests rely on a shared mainloop */
+static GMainLoop *loop = NULL;
+
+/* ---------------------------------------------------------------------------------------------------- */
+/* Connection life-cycle testing */
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+test_connection_life_cycle (void)
+{
+ EDBusConnection *c;
+ EDBusConnection *c2;
+ GError *error;
+
+ error = NULL;
+
+ /**
+ * Check for correct behavior when no bus is present
+ *
+ */
+ c = e_dbus_connection_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
+ g_assert_error (error, E_DBUS_ERROR, E_DBUS_ERROR_FILE_NOT_FOUND);
+ g_assert (!e_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 = e_dbus_connection_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
+ g_assert_no_error (error);
+ g_assert (c != NULL);
+ g_assert (!e_dbus_connection_get_is_disconnected (c));
+
+ /**
+ * Check that singleton handling work
+ */
+ c2 = e_dbus_connection_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 = e_dbus_connection_bus_get_private_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 for correct behavior when the bus goes away
+ *
+ */
+ e_dbus_connection_set_exit_on_disconnect (c, FALSE);
+ session_bus_down ();
+ _g_assert_signal_received (c, "disconnected");
+ g_assert (e_dbus_connection_get_is_disconnected (c));
+ g_object_unref (c);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+/* Test that sending and receiving messages work as expected */
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+msg_cb_expect_error_disconnected (EDBusConnection *connection,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GError *error;
+ EVariant *result;
+
+ error = NULL;
+ result = e_dbus_connection_invoke_method_finish (connection,
+ res,
+ &error);
+ g_assert_error (error, E_DBUS_ERROR, E_DBUS_ERROR_DISCONNECTED);
+ g_assert (!e_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 (EDBusConnection *connection,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GError *error;
+ EVariant *result;
+
+ error = NULL;
+ result = e_dbus_connection_invoke_method_finish (connection,
+ res,
+ &error);
+ g_assert_error (error, E_DBUS_ERROR, E_DBUS_ERROR_UNKNOWN_METHOD);
+ g_assert (e_dbus_error_is_remote_error (error));
+ g_assert (result == NULL);
+
+ g_main_loop_quit (loop);
+}
+
+static void
+msg_cb_expect_success (EDBusConnection *connection,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GError *error;
+ EVariant *result;
+
+ error = NULL;
+ result = e_dbus_connection_invoke_method_finish (connection,
+ res,
+ &error);
+ g_assert_no_error (error);
+ g_assert (result != NULL);
+ e_variant_unref (result);
+
+ g_main_loop_quit (loop);
+}
+
+static void
+msg_cb_expect_error_cancelled (EDBusConnection *connection,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GError *error;
+ EVariant *result;
+
+ error = NULL;
+ result = e_dbus_connection_invoke_method_finish (connection,
+ res,
+ &error);
+ g_assert_error (error, E_DBUS_ERROR, E_DBUS_ERROR_CANCELLED);
+ g_assert (!e_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)
+{
+ EDBusConnection *c;
+ GCancellable *ca;
+
+ session_bus_up ();
+
+ /* First, get an unopened connection */
+ c = e_dbus_connection_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
+ g_assert (c != NULL);
+ g_assert (!e_dbus_connection_get_is_disconnected (c));
+
+ /**
+ * Check that we never actually send a message if the GCancellable is already cancelled - i.e.
+ * we should get #E_DBUS_ERROR_CANCELLED instead of #E_DBUS_ERROR_FAILED even when the actual
+ * connection is not up.
+ */
+ ca = g_cancellable_new ();
+ g_cancellable_cancel (ca);
+ e_dbus_connection_invoke_method (c,
+ "org.freedesktop.DBus", /* bus_name */
+ "/org/freedesktop/DBus", /* object path */
+ "org.freedesktop.DBus", /* interface name */
+ "GetId", /* method name */
+ NULL,
+ -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.
+ */
+ e_dbus_connection_invoke_method (c,
+ "org.freedesktop.DBus", /* bus_name */
+ "/org/freedesktop/DBus", /* object path */
+ "org.freedesktop.DBus", /* interface name */
+ "GetId", /* method name */
+ NULL,
+ -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.
+ */
+ e_dbus_connection_invoke_method (c,
+ "org.freedesktop.DBus", /* bus_name */
+ "/org/freedesktop/DBus", /* object path */
+ "org.freedesktop.DBus", /* interface name */
+ "NonExistantMethod", /* method name */
+ NULL,
+ -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 ();
+ e_dbus_connection_invoke_method (c,
+ "org.freedesktop.DBus", /* bus_name */
+ "/org/freedesktop/DBus", /* object path */
+ "org.freedesktop.DBus", /* interface name */
+ "GetId", /* method name */
+ NULL,
+ -1,
+ ca,
+ (GAsyncReadyCallback) msg_cb_expect_error_cancelled,
+ 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.
+ */
+ e_dbus_connection_set_exit_on_disconnect (c, FALSE);
+ session_bus_down ();
+ _g_assert_signal_received (c, "disconnected");
+ g_assert (e_dbus_connection_get_is_disconnected (c));
+
+ e_dbus_connection_invoke_method (c,
+ "org.freedesktop.DBus", /* bus_name */
+ "/org/freedesktop/DBus", /* object path */
+ "org.freedesktop.DBus", /* interface name */
+ "GetId", /* method name */
+ NULL,
+ -1,
+ NULL,
+ (GAsyncReadyCallback) msg_cb_expect_error_disconnected,
+ NULL);
+ g_main_loop_run (loop);
+
+ g_object_unref (c);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+/* Connection signal tests */
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+test_connection_signal_handler (EDBusConnection *connection,
+ const gchar *sender_name,
+ const gchar *object_path,
+ const gchar *interface_name,
+ const gchar *signal_name,
+ EVariant *parameters,
+ gpointer user_data)
+{
+ gint *counter = user_data;
+ *counter += 1;
+ g_main_loop_quit (loop);
+}
+
+static gboolean
+test_connection_signal_quit_mainloop (gpointer user_data)
+{
+ g_main_loop_quit (loop);
+ return FALSE;
+}
+
+static void
+test_connection_signals (void)
+{
+ EDBusConnection *c1;
+ EDBusConnection *c2;
+ EDBusConnection *c3;
+ guint s1;
+ guint s2;
+ guint s3;
+ gint count_s1;
+ gint count_s2;
+ gint count_name_owner_changed;
+ GError *error;
+ gboolean ret;
+
+ 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 ("E_DBUS_MONITOR") == NULL)
+ {
+ c1 = e_dbus_connection_bus_get_private_sync (G_BUS_TYPE_SESSION, NULL, NULL);
+ g_assert (c1 != NULL);
+ g_assert (!e_dbus_connection_get_is_disconnected (c1));
+ g_object_unref (c1);
+ }
+ c1 = e_dbus_connection_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
+ g_assert (c1 != NULL);
+ g_assert (!e_dbus_connection_get_is_disconnected (c1));
+ g_assert_cmpstr (e_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 = e_dbus_connection_signal_subscribe (c1,
+ ":1.2",
+ "org.gtk.EDBus.ExampleInterface",
+ "Foo",
+ "/org/gtk/EDBus/ExampleInterface",
+ NULL,
+ test_connection_signal_handler,
+ &count_s1,
+ NULL);
+ s2 = e_dbus_connection_signal_subscribe (c1,
+ NULL, /* match any sender */
+ "org.gtk.EDBus.ExampleInterface",
+ "Foo",
+ "/org/gtk/EDBus/ExampleInterface",
+ NULL,
+ test_connection_signal_handler,
+ &count_s2,
+ NULL);
+ s3 = e_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 = e_dbus_connection_bus_get_private_sync (G_BUS_TYPE_SESSION, NULL, NULL);
+ g_assert (c2 != NULL);
+ g_assert (!e_dbus_connection_get_is_disconnected (c2));
+ g_assert_cmpstr (e_dbus_connection_get_unique_name (c2), ==, ":1.2");
+ c3 = e_dbus_connection_bus_get_private_sync (G_BUS_TYPE_SESSION, NULL, NULL);
+ g_assert (c3 != NULL);
+ g_assert (!e_dbus_connection_get_is_disconnected (c3));
+ g_assert_cmpstr (e_dbus_connection_get_unique_name (c3), ==, ":1.3");
+
+ /**
+ * Make c2 emit "Foo" - we should catch it twice
+ */
+ ret = e_dbus_connection_emit_signal (c2,
+ NULL, /* destination bus name */
+ "/org/gtk/EDBus/ExampleInterface",
+ "org.gtk.EDBus.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 = e_dbus_connection_emit_signal (c3,
+ NULL, /* destination bus name */
+ "/org/gtk/EDBus/ExampleInterface",
+ "org.gtk.EDBus.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);
+
+ /**
+ * Tool around in the mainloop to avoid race conditions and also to check the
+ * total amount of NameOwnerChanged signals
+ */
+ g_timeout_add (500, test_connection_signal_quit_mainloop, NULL);
+ g_main_loop_run (loop);
+ g_assert_cmpint (count_s1, ==, 1);
+ g_assert_cmpint (count_s2, ==, 2);
+ g_assert_cmpint (count_name_owner_changed, ==, 2);
+
+ /**
+ * Now bring down the session bus and check we get the :disconnected signal from each connection.
+ */
+ session_bus_down ();
+ e_dbus_connection_set_exit_on_disconnect (c1, FALSE);
+ e_dbus_connection_set_exit_on_disconnect (c2, FALSE);
+ e_dbus_connection_set_exit_on_disconnect (c3, FALSE);
+ if (!e_dbus_connection_get_is_disconnected (c1))
+ _g_assert_signal_received (c1, "disconnected");
+ if (!e_dbus_connection_get_is_disconnected (c2))
+ _g_assert_signal_received (c2, "disconnected");
+ if (!e_dbus_connection_get_is_disconnected (c3))
+ _g_assert_signal_received (c3, "disconnected");
+
+ e_dbus_connection_signal_unsubscribe (c1, s1);
+ e_dbus_connection_signal_unsubscribe (c1, s2);
+ e_dbus_connection_signal_unsubscribe (c1, s3);
+ g_object_unref (c1);
+ g_object_unref (c2);
+ g_object_unref (c3);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+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);
+ return g_test_run();
+}
diff --git a/edbus/tests/error.c b/edbus/tests/error.c
new file mode 100644
index 0000000..5914f36
--- /dev/null
+++ b/edbus/tests/error.c
@@ -0,0 +1,198 @@
+/* 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 <edbus/edbus.h>
+#include <unistd.h>
+#include <string.h>
+
+/* ---------------------------------------------------------------------------------------------------- */
+/* Test that registered errors are properly mapped */
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+check_registered_error (const gchar *given_dbus_error_name,
+ GQuark error_domain,
+ gint error_code)
+{
+ GError *error;
+ gchar *dbus_error_name;
+
+ error = e_dbus_error_new_for_dbus_error (given_dbus_error_name, "test message");
+ g_assert_error (error, error_domain, error_code);
+ g_assert (e_dbus_error_is_remote_error (error));
+ g_assert (e_dbus_error_strip_remote_error (error));
+ g_assert_cmpstr (error->message, ==, "test message");
+ dbus_error_name = e_dbus_error_get_remote_error (error);
+ g_assert_cmpstr (dbus_error_name, ==, given_dbus_error_name);
+ g_free (dbus_error_name);
+ g_error_free (error);
+}
+
+static void
+test_registered_errors (void)
+{
+ /* Here we check that we are able to map to GError and back for registered
+ * errors.
+ *
+ * For example, if "org.freedesktop.DBus.Error.AddressInUse" is
+ * associated with (E_DBUS_ERROR, E_DBUS_ERROR_DBUS_FAILED), check
+ * that
+ *
+ * - Creating a GError for e.g. "org.freedesktop.DBus.Error.AddressInUse"
+ * has (error_domain, code) == (E_DBUS_ERROR, E_DBUS_ERROR_DBUS_FAILED)
+ *
+ * - That it is possible to recover e.g. "org.freedesktop.DBus.Error.AddressInUse"
+ * as the D-Bus error name when dealing with an error with (error_domain, code) ==
+ * (E_DBUS_ERROR, E_DBUS_ERROR_DBUS_FAILED)
+ *
+ * We just check a couple of well-known errors.
+ */
+ check_registered_error ("org.freedesktop.DBus.Error.Failed",
+ E_DBUS_ERROR,
+ E_DBUS_ERROR_DBUS_FAILED);
+ check_registered_error ("org.freedesktop.DBus.Error.AddressInUse",
+ E_DBUS_ERROR,
+ E_DBUS_ERROR_ADDRESS_IN_USE);
+ check_registered_error ("org.freedesktop.DBus.Error.UnknownMethod",
+ E_DBUS_ERROR,
+ E_DBUS_ERROR_UNKNOWN_METHOD);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+check_unregistered_error (const gchar *given_dbus_error_name)
+{
+ GError *error;
+ gchar *dbus_error_name;
+
+ error = e_dbus_error_new_for_dbus_error (given_dbus_error_name, "test message");
+ g_assert_error (error, E_DBUS_ERROR, E_DBUS_ERROR_REMOTE_ERROR);
+ g_assert (e_dbus_error_is_remote_error (error));
+ dbus_error_name = e_dbus_error_get_remote_error (error);
+ g_assert_cmpstr (dbus_error_name, ==, given_dbus_error_name);
+ g_free (dbus_error_name);
+
+ /* strip the message */
+ g_assert (e_dbus_error_strip_remote_error (error));
+ g_assert_cmpstr (error->message, ==, "test message");
+
+ /* check that we can no longer recover the D-Bus error name */
+ g_assert (e_dbus_error_get_remote_error (error) == NULL);
+
+ g_error_free (error);
+
+}
+
+static void
+test_unregistered_errors (void)
+{
+ /* Here we check that we are able to map to GError and back for unregistered
+ * errors.
+ *
+ * For example, if "com.example.Error.Failed" is not registered, then check
+ *
+ * - Creating a GError for e.g. "com.example.Error.Failed" has (error_domain, code) ==
+ * (E_DBUS_ERROR, E_DBUS_ERROR_REMOTE_ERROR)
+ *
+ * - That it is possible to recover e.g. "com.example.Error.Failed" from that
+ * GError.
+ *
+ * We just check a couple of random errors.
+ */
+
+ check_unregistered_error ("com.example.Error.Failed");
+ check_unregistered_error ("foobar.buh");
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+check_transparent_gerror (GQuark error_domain,
+ gint error_code)
+{
+ GError *error;
+ gchar *given_dbus_error_name;
+ gchar *dbus_error_name;
+
+ error = g_error_new (error_domain, error_code, "test message");
+ given_dbus_error_name = e_dbus_error_encode_gerror (error);
+ g_assert (g_str_has_prefix (given_dbus_error_name, "org.gtk.EDBus.UnmappedGError.Quark"));
+ g_error_free (error);
+
+ error = e_dbus_error_new_for_dbus_error (given_dbus_error_name, "test message");
+ g_assert_error (error, error_domain, error_code);
+ g_assert (e_dbus_error_is_remote_error (error));
+ dbus_error_name = e_dbus_error_get_remote_error (error);
+ g_assert_cmpstr (dbus_error_name, ==, given_dbus_error_name);
+ g_free (dbus_error_name);
+ g_free (given_dbus_error_name);
+
+ /* strip the message */
+ g_assert (e_dbus_error_strip_remote_error (error));
+ g_assert_cmpstr (error->message, ==, "test message");
+
+ /* check that we can no longer recover the D-Bus error name */
+ g_assert (e_dbus_error_get_remote_error (error) == NULL);
+
+ g_error_free (error);
+}
+
+static void
+test_transparent_gerror (void)
+{
+ /* Here we check that we are able to transparent pass unregistered GError's
+ * over the wire.
+ *
+ * For example, if G_IO_ERROR_FAILED is not registered, then check
+ *
+ * - e_dbus_error_encode_gerror() returns something of the form
+ * org.gtk.EDBus.UnmappedGError.Quark_HEXENCODED_QUARK_NAME_.Code_ERROR_CODE
+ *
+ * - mapping back the D-Bus error name gives us G_IO_ERROR_FAILED
+ *
+ * - That it is possible to recover the D-Bus error name from the
+ * GError.
+ *
+ * We just check a couple of random errors.
+ */
+
+ check_transparent_gerror (G_IO_ERROR, G_IO_ERROR_FAILED);
+ check_transparent_gerror (G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_PARSE);
+}
+
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+int
+main (int argc,
+ char *argv[])
+{
+ g_type_init ();
+ g_test_init (&argc, &argv, NULL);
+
+ g_test_add_func ("/gdbus/registered-errors", test_registered_errors);
+ g_test_add_func ("/gdbus/unregistered-errors", test_unregistered_errors);
+ g_test_add_func ("/gdbus/transparent-gerror", test_transparent_gerror);
+
+ return g_test_run();
+}
diff --git a/edbus/tests/export.c b/edbus/tests/export.c
new file mode 100644
index 0000000..bb4c9ad
--- /dev/null
+++ b/edbus/tests/export.c
@@ -0,0 +1,1118 @@
+/* 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 <edbus/edbus.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <dbus/dbus.h>
+
+#include "tests.h"
+
+/* all tests rely on a shared mainloop */
+static GMainLoop *loop = NULL;
+
+/* ---------------------------------------------------------------------------------------------------- */
+/* Test that we can export objects, the hierarchy is correct and the right handlers are invoked */
+/* ---------------------------------------------------------------------------------------------------- */
+
+static const EDBusMethodInfo foo_method_info[] =
+{
+ {
+ "Method1",
+ "", 0, NULL,
+ "", 0, NULL,
+ NULL
+ },
+ {
+ "Method2",
+ "", 0, NULL,
+ "", 0, NULL,
+ NULL
+ }
+};
+
+static const EDBusSignalInfo foo_signal_info[] =
+{
+ {
+ "SignalAlpha",
+ "", 0, NULL,
+ NULL
+ }
+};
+
+static const EDBusPropertyInfo foo_property_info[] =
+{
+ {
+ "PropertyUno",
+ "s",
+ E_DBUS_PROPERTY_INFO_FLAGS_READABLE,
+ NULL
+ }
+};
+
+static const EDBusInterfaceInfo foo_interface_info =
+{
+ "org.example.Foo",
+ 2,
+ foo_method_info,
+ 1,
+ foo_signal_info,
+ 1,
+ foo_property_info,
+ NULL,
+};
+
+/* -------------------- */
+
+static const EDBusMethodInfo bar_method_info[] =
+{
+ {
+ "MethodA",
+ "", 0, NULL,
+ "", 0, NULL,
+ NULL
+ },
+ {
+ "MethodB",
+ "", 0, NULL,
+ "", 0, NULL,
+ NULL
+ }
+};
+
+static const EDBusSignalInfo bar_signal_info[] =
+{
+ {
+ "SignalMars",
+ "", 0, NULL,
+ NULL
+ }
+};
+
+static const EDBusPropertyInfo bar_property_info[] =
+{
+ {
+ "PropertyDuo",
+ "s",
+ E_DBUS_PROPERTY_INFO_FLAGS_READABLE,
+ NULL
+ }
+};
+
+static const EDBusInterfaceInfo bar_interface_info =
+{
+ "org.example.Bar",
+ 2,
+ bar_method_info,
+ 1,
+ bar_signal_info,
+ 1,
+ bar_property_info,
+ NULL,
+};
+
+/* -------------------- */
+
+static const EDBusMethodInfo dyna_method_info[] =
+{
+ {
+ "DynaCyber",
+ "", 0, NULL,
+ "", 0, NULL,
+ NULL
+ }
+};
+
+static const EDBusInterfaceInfo dyna_interface_info =
+{
+ "org.example.Dyna",
+ 1, /* 1 method*/
+ dyna_method_info,
+ 0, /* 0 signals */
+ NULL,
+ 0, /* 0 properties */
+ NULL,
+ NULL,
+};
+
+static void
+dyna_cyber (EDBusConnection *connection,
+ gpointer user_data,
+ const gchar *sender,
+ const gchar *object_path,
+ const gchar *interface_name,
+ const gchar *method_name,
+ EVariant *parameters,
+ EDBusMethodInvocation *invocation)
+{
+ GPtrArray *data = user_data;
+ gchar *node_name;
+ guint n;
+
+ node_name = strrchr (object_path, '/') + 1;
+
+ /* Add new node if it is not already known */
+ for (n = 0; n < data->len ; n++)
+ {
+ if (g_strcmp0 (g_ptr_array_index (data, n), node_name) == 0)
+ goto out;
+ }
+ g_ptr_array_add (data, g_strdup(node_name));
+
+ out:
+ e_dbus_method_invocation_return_value (invocation, NULL);
+}
+
+static const EDBusInterfaceVTable dyna_interface_vtable =
+{
+ dyna_cyber,
+ NULL,
+ NULL
+};
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+introspect_callback (EDBusProxy *proxy,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ const gchar *s;
+ gchar **xml_data = user_data;
+ EVariant *result;
+ GError *error;
+
+ error = NULL;
+ result = e_dbus_proxy_invoke_method_finish (proxy,
+ res,
+ &error);
+ g_assert_no_error (error);
+ g_assert (result != NULL);
+ e_variant_get (result, "(s)", &s);
+ *xml_data = g_strdup (s);
+ e_variant_unref (result);
+
+ g_main_loop_quit (loop);
+}
+
+static gchar **
+get_nodes_at (EDBusConnection *c,
+ const gchar *object_path)
+{
+ GError *error;
+ EDBusProxy *proxy;
+ gchar *xml_data;
+ GPtrArray *p;
+ EDBusNodeInfo *node_info;
+ guint n;
+
+ error = NULL;
+ proxy = e_dbus_proxy_new_sync (c,
+ E_TYPE_DBUS_PROXY,
+ E_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
+ E_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
+ e_dbus_connection_get_unique_name (c),
+ object_path,
+ "org.freedesktop.DBus.Introspectable",
+ NULL,
+ &error);
+ g_assert_no_error (error);
+ g_assert (proxy != NULL);
+
+ /* do this async to avoid libdbus-1 deadlocks */
+ xml_data = NULL;
+ e_dbus_proxy_invoke_method (proxy,
+ "Introspect",
+ NULL,
+ -1,
+ NULL,
+ (GAsyncReadyCallback) introspect_callback,
+ &xml_data);
+ g_main_loop_run (loop);
+ g_assert (xml_data != NULL);
+
+ node_info = e_dbus_node_info_new_for_xml (xml_data, &error);
+ g_assert_no_error (error);
+ g_assert (node_info != NULL);
+
+ p = g_ptr_array_new ();
+ for (n = 0; n < node_info->num_nodes; n++)
+ {
+ const EDBusNodeInfo *sub_node_info = node_info->nodes + n;
+ g_ptr_array_add (p, g_strdup (sub_node_info->path));
+ }
+ g_ptr_array_add (p, NULL);
+
+ g_object_unref (proxy);
+ g_free (xml_data);
+ e_dbus_node_info_free (node_info);
+
+ return (gchar **) g_ptr_array_free (p, FALSE);
+}
+
+static gboolean
+has_interface (EDBusConnection *c,
+ const gchar *object_path,
+ const gchar *interface_name)
+{
+ GError *error;
+ EDBusProxy *proxy;
+ gchar *xml_data;
+ EDBusNodeInfo *node_info;
+ gboolean ret;
+
+ error = NULL;
+ proxy = e_dbus_proxy_new_sync (c,
+ E_TYPE_DBUS_PROXY,
+ E_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
+ E_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
+ e_dbus_connection_get_unique_name (c),
+ object_path,
+ "org.freedesktop.DBus.Introspectable",
+ NULL,
+ &error);
+ g_assert_no_error (error);
+ g_assert (proxy != NULL);
+
+ /* do this async to avoid libdbus-1 deadlocks */
+ xml_data = NULL;
+ e_dbus_proxy_invoke_method (proxy,
+ "Introspect",
+ NULL,
+ -1,
+ NULL,
+ (GAsyncReadyCallback) introspect_callback,
+ &xml_data);
+ g_main_loop_run (loop);
+ g_assert (xml_data != NULL);
+
+ node_info = e_dbus_node_info_new_for_xml (xml_data, &error);
+ g_assert_no_error (error);
+ g_assert (node_info != NULL);
+
+ ret = (e_dbus_node_info_lookup_interface (node_info, interface_name) != NULL);
+
+ g_object_unref (proxy);
+ g_free (xml_data);
+ e_dbus_node_info_free (node_info);
+
+ return ret;
+}
+
+static guint
+count_interfaces (EDBusConnection *c,
+ const gchar *object_path)
+{
+ GError *error;
+ EDBusProxy *proxy;
+ gchar *xml_data;
+ EDBusNodeInfo *node_info;
+ guint ret;
+
+ error = NULL;
+ proxy = e_dbus_proxy_new_sync (c,
+ E_TYPE_DBUS_PROXY,
+ E_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
+ E_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
+ e_dbus_connection_get_unique_name (c),
+ object_path,
+ "org.freedesktop.DBus.Introspectable",
+ NULL,
+ &error);
+ g_assert_no_error (error);
+ g_assert (proxy != NULL);
+
+ /* do this async to avoid libdbus-1 deadlocks */
+ xml_data = NULL;
+ e_dbus_proxy_invoke_method (proxy,
+ "Introspect",
+ NULL,
+ -1,
+ NULL,
+ (GAsyncReadyCallback) introspect_callback,
+ &xml_data);
+ g_main_loop_run (loop);
+ g_assert (xml_data != NULL);
+
+ node_info = e_dbus_node_info_new_for_xml (xml_data, &error);
+ g_assert_no_error (error);
+ g_assert (node_info != NULL);
+
+ ret = node_info->num_interfaces;
+
+ g_object_unref (proxy);
+ g_free (xml_data);
+ e_dbus_node_info_free (node_info);
+
+ return ret;
+}
+
+static void
+dyna_create_callback (EDBusProxy *proxy,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ EVariant *result;
+ GError *error;
+
+ error = NULL;
+ result = e_dbus_proxy_invoke_method_finish (proxy,
+ res,
+ &error);
+ g_assert_no_error (error);
+ g_assert (result != NULL);
+ e_variant_unref (result);
+
+ g_main_loop_quit (loop);
+}
+
+/* Dynamically create @object_name under /foo/dyna */
+static void
+dyna_create (EDBusConnection *c,
+ const gchar *object_name)
+{
+ GError *error;
+ EDBusProxy *proxy;
+ gchar *object_path;
+
+ object_path = g_strconcat ("/foo/dyna/", object_name, NULL);
+
+ error = NULL;
+ proxy = e_dbus_proxy_new_sync (c,
+ E_TYPE_DBUS_PROXY,
+ E_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
+ E_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
+ e_dbus_connection_get_unique_name (c),
+ object_path,
+ "org.example.Dyna",
+ NULL,
+ &error);
+ g_assert_no_error (error);
+ g_assert (proxy != NULL);
+
+ /* do this async to avoid libdbus-1 deadlocks */
+ e_dbus_proxy_invoke_method (proxy,
+ "DynaCyber",
+ e_variant_new ("()"),
+ -1,
+ NULL,
+ (GAsyncReadyCallback) dyna_create_callback,
+ NULL);
+ g_main_loop_run (loop);
+
+ g_assert_no_error (error);
+
+ g_object_unref (proxy);
+ g_free (object_path);
+
+ return;
+}
+
+typedef struct
+{
+ guint num_unregistered_calls;
+ guint num_unregistered_subtree_calls;
+ guint num_subtree_nodes;
+} ObjectRegistrationData;
+
+static void
+on_object_unregistered (gpointer user_data)
+{
+ ObjectRegistrationData *data = user_data;
+
+ data->num_unregistered_calls++;
+}
+
+static void
+on_subtree_unregistered (gpointer user_data)
+{
+ ObjectRegistrationData *data = user_data;
+
+ data->num_unregistered_subtree_calls++;
+}
+
+static gboolean
+_g_strv_has_string (const gchar* const * haystack,
+ const gchar *needle)
+{
+ guint n;
+
+ for (n = 0; haystack != NULL && haystack[n] != NULL; n++)
+ {
+ if (g_strcmp0 (haystack[n], needle) == 0)
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static DBusHandlerResult
+dc_message_func (DBusConnection *connection,
+ DBusMessage *message,
+ void *user_data)
+{
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+/* -------------------- */
+
+static gchar **
+subtree_enumerate (EDBusConnection *connection,
+ gpointer user_data,
+ const gchar *sender,
+ const gchar *object_path)
+{
+ ObjectRegistrationData *data = user_data;
+ GPtrArray *p;
+ gchar **nodes;
+ guint n;
+
+ p = g_ptr_array_new ();
+
+ for (n = 0; n < data->num_subtree_nodes; n++)
+ {
+ g_ptr_array_add (p, g_strdup_printf ("vp%d", n));
+ g_ptr_array_add (p, g_strdup_printf ("evp%d", n));
+ }
+ g_ptr_array_add (p, NULL);
+
+ nodes = (gchar **) g_ptr_array_free (p, FALSE);
+
+ return nodes;
+}
+
+/* Only allows certain objects, and aborts on unknowns */
+static GPtrArray *
+subtree_introspect (EDBusConnection *connection,
+ gpointer user_data,
+ const gchar *sender,
+ const gchar *object_path,
+ const gchar *node)
+{
+ GPtrArray *interfaces;
+
+ /* VPs implement the Foo interface, EVPs implement the Bar interface. The root
+ * does not implement any interfaces
+ */
+ interfaces = g_ptr_array_new ();
+ if (g_str_has_prefix (node, "vp"))
+ {
+ g_ptr_array_add (interfaces, (gpointer) &foo_interface_info);
+ }
+ else if (g_str_has_prefix (node, "evp"))
+ {
+ g_ptr_array_add (interfaces, (gpointer) &bar_interface_info);
+ }
+ else if (g_strcmp0 (node, "/") == 0)
+ {
+ /* do nothing */
+ }
+ else
+ {
+ g_assert_not_reached ();
+ }
+
+ return interfaces;
+}
+
+static const EDBusInterfaceVTable *
+subtree_dispatch (EDBusConnection *connection,
+ gpointer user_data,
+ const gchar *sender,
+ const gchar *object_path,
+ const gchar *interface_name,
+ const gchar *node,
+ gpointer *out_user_data)
+{
+ return NULL;
+}
+
+static const EDBusSubtreeVTable subtree_vtable =
+{
+ subtree_enumerate,
+ subtree_introspect,
+ subtree_dispatch
+};
+
+/* -------------------- */
+
+static gchar **
+dynamic_subtree_enumerate (EDBusConnection *connection,
+ gpointer user_data,
+ const gchar *sender,
+ const gchar *object_path)
+{
+ GPtrArray *data = user_data;
+ gchar **nodes = g_new (gchar*, data->len + 1);
+ guint n;
+
+ for (n = 0; n < data->len; n++)
+ {
+ nodes[n] = g_strdup (g_ptr_array_index (data, n));
+ }
+ nodes[data->len] = NULL;
+
+ return nodes;
+}
+
+/* Allow all objects to be introspected */
+static GPtrArray *
+dynamic_subtree_introspect (EDBusConnection *connection,
+ gpointer user_data,
+ const gchar *sender,
+ const gchar *object_path,
+ const gchar *node)
+{
+ GPtrArray *interfaces;
+
+ /* All nodes (including the root node) implements the Dyna interface */
+ interfaces = g_ptr_array_new ();
+ g_ptr_array_add (interfaces, (gpointer) &dyna_interface_info);
+
+ return interfaces;
+}
+
+static const EDBusInterfaceVTable *
+dynamic_subtree_dispatch (EDBusConnection *connection,
+ gpointer user_data,
+ const gchar *sender,
+ const gchar *object_path,
+ const gchar *interface_name,
+ const gchar *node,
+ gpointer *out_user_data)
+{
+ *out_user_data = user_data;
+ return &dyna_interface_vtable;
+}
+
+static const EDBusSubtreeVTable dynamic_subtree_vtable =
+{
+ dynamic_subtree_enumerate,
+ dynamic_subtree_introspect,
+ dynamic_subtree_dispatch
+};
+
+/* -------------------- */
+
+static void
+test_object_registration (void)
+{
+ EDBusConnection *c;
+ GError *error;
+ ObjectRegistrationData data;
+ GPtrArray *dyna_data;
+ gchar **nodes;
+ guint boss_foo_reg_id;
+ guint boss_bar_reg_id;
+ guint worker1_foo_reg_id;
+ guint worker2_bar_reg_id;
+ guint intern1_foo_reg_id;
+ guint intern2_bar_reg_id;
+ guint intern2_foo_reg_id;
+ guint intern3_bar_reg_id;
+ guint registration_id;
+ guint subtree_registration_id;
+ guint non_subtree_object_path_foo_reg_id;
+ guint non_subtree_object_path_bar_reg_id;
+ guint dyna_subtree_registration_id;
+ guint num_successful_registrations;
+ DBusConnection *dc;
+ DBusError dbus_error;
+ DBusObjectPathVTable dc_obj_vtable =
+ {
+ NULL,
+ dc_message_func,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+ };
+
+ data.num_unregistered_calls = 0;
+ data.num_unregistered_subtree_calls = 0;
+ data.num_subtree_nodes = 0;
+
+ num_successful_registrations = 0;
+
+ error = NULL;
+ c = e_dbus_connection_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
+ g_assert_no_error (error);
+ g_assert (c != NULL);
+
+ registration_id = e_dbus_connection_register_object (c,
+ "/foo/boss",
+ foo_interface_info.name,
+ &foo_interface_info,
+ NULL,
+ &data,
+ on_object_unregistered,
+ &error);
+ g_assert_no_error (error);
+ g_assert (registration_id > 0);
+ boss_foo_reg_id = registration_id;
+ num_successful_registrations++;
+
+ registration_id = e_dbus_connection_register_object (c,
+ "/foo/boss",
+ bar_interface_info.name,
+ &bar_interface_info,
+ NULL,
+ &data,
+ on_object_unregistered,
+ &error);
+ g_assert_no_error (error);
+ g_assert (registration_id > 0);
+ boss_bar_reg_id = registration_id;
+ num_successful_registrations++;
+
+ registration_id = e_dbus_connection_register_object (c,
+ "/foo/boss/worker1",
+ foo_interface_info.name,
+ &foo_interface_info,
+ NULL,
+ &data,
+ on_object_unregistered,
+ &error);
+ g_assert_no_error (error);
+ g_assert (registration_id > 0);
+ worker1_foo_reg_id = registration_id;
+ num_successful_registrations++;
+
+ registration_id = e_dbus_connection_register_object (c,
+ "/foo/boss/worker2",
+ bar_interface_info.name,
+ &bar_interface_info,
+ NULL,
+ &data,
+ on_object_unregistered,
+ &error);
+ g_assert_no_error (error);
+ g_assert (registration_id > 0);
+ worker2_bar_reg_id = registration_id;
+ num_successful_registrations++;
+
+ registration_id = e_dbus_connection_register_object (c,
+ "/foo/boss/interns/intern1",
+ foo_interface_info.name,
+ &foo_interface_info,
+ NULL,
+ &data,
+ on_object_unregistered,
+ &error);
+ g_assert_no_error (error);
+ g_assert (registration_id > 0);
+ intern1_foo_reg_id = registration_id;
+ num_successful_registrations++;
+
+ /* Now check we get an error if trying to register a path already registered by another D-Bus binding
+ *
+ * To check this we need to pretend, for a while, that we're another binding.
+ */
+ dbus_error_init (&dbus_error);
+ dc = dbus_bus_get (DBUS_BUS_SESSION, &dbus_error);
+ g_assert (!dbus_error_is_set (&dbus_error));
+ g_assert (dc != NULL);
+ g_assert (dbus_connection_try_register_object_path (dc,
+ "/foo/boss/interns/other_intern",
+ &dc_obj_vtable,
+ NULL,
+ &dbus_error));
+ registration_id = e_dbus_connection_register_object (c,
+ "/foo/boss/interns/other_intern",
+ bar_interface_info.name,
+ &bar_interface_info,
+ NULL,
+ &data,
+ on_object_unregistered,
+ &error);
+ g_assert_error (error, E_DBUS_ERROR, E_DBUS_ERROR_OBJECT_PATH_IN_USE);
+ g_assert (!e_dbus_error_is_remote_error (error));
+ g_error_free (error);
+ error = NULL;
+ g_assert (registration_id == 0);
+
+ /* ... and try again at another path */
+ registration_id = e_dbus_connection_register_object (c,
+ "/foo/boss/interns/intern2",
+ bar_interface_info.name,
+ &bar_interface_info,
+ NULL,
+ &data,
+ on_object_unregistered,
+ &error);
+ g_assert_no_error (error);
+ g_assert (registration_id > 0);
+ intern2_bar_reg_id = registration_id;
+ num_successful_registrations++;
+
+ /* register at the same path/interface - this should fail */
+ registration_id = e_dbus_connection_register_object (c,
+ "/foo/boss/interns/intern2",
+ bar_interface_info.name,
+ &bar_interface_info,
+ NULL,
+ &data,
+ on_object_unregistered,
+ &error);
+ g_assert_error (error, E_DBUS_ERROR, E_DBUS_ERROR_OBJECT_PATH_IN_USE);
+ g_assert (!e_dbus_error_is_remote_error (error));
+ g_error_free (error);
+ error = NULL;
+ g_assert (registration_id == 0);
+
+ /* register at different interface - shouldn't fail */
+ registration_id = e_dbus_connection_register_object (c,
+ "/foo/boss/interns/intern2",
+ foo_interface_info.name,
+ &foo_interface_info,
+ NULL,
+ &data,
+ on_object_unregistered,
+ &error);
+ g_assert_no_error (error);
+ g_assert (registration_id > 0);
+ intern2_foo_reg_id = registration_id;
+ num_successful_registrations++;
+
+ /* unregister it via the id */
+ g_assert (e_dbus_connection_unregister_object (c, registration_id));
+ g_assert_cmpint (data.num_unregistered_calls, ==, 1);
+ intern2_foo_reg_id = 0;
+
+ /* register it back */
+ registration_id = e_dbus_connection_register_object (c,
+ "/foo/boss/interns/intern2",
+ foo_interface_info.name,
+ &foo_interface_info,
+ NULL,
+ &data,
+ on_object_unregistered,
+ &error);
+ g_assert_no_error (error);
+ g_assert (registration_id > 0);
+ intern2_foo_reg_id = registration_id;
+ num_successful_registrations++;
+
+ registration_id = e_dbus_connection_register_object (c,
+ "/foo/boss/interns/intern3",
+ bar_interface_info.name,
+ &bar_interface_info,
+ NULL,
+ &data,
+ on_object_unregistered,
+ &error);
+ g_assert_no_error (error);
+ g_assert (registration_id > 0);
+ intern3_bar_reg_id = registration_id;
+ num_successful_registrations++;
+
+ /* now register a whole subtree at /foo/boss/executives */
+ subtree_registration_id = e_dbus_connection_register_subtree (c,
+ "/foo/boss/executives",
+ &subtree_vtable,
+ E_DBUS_SUBTREE_FLAGS_NONE,
+ &data,
+ on_subtree_unregistered,
+ &error);
+ g_assert_no_error (error);
+ g_assert (subtree_registration_id > 0);
+ /* try registering it again.. this should fail */
+ registration_id = e_dbus_connection_register_subtree (c,
+ "/foo/boss/executives",
+ &subtree_vtable,
+ E_DBUS_SUBTREE_FLAGS_NONE,
+ &data,
+ on_subtree_unregistered,
+ &error);
+ g_assert_error (error, E_DBUS_ERROR, E_DBUS_ERROR_OBJECT_PATH_IN_USE);
+ g_assert (!e_dbus_error_is_remote_error (error));
+ g_error_free (error);
+ error = NULL;
+ g_assert (registration_id == 0);
+
+ /* unregister it, then register it again */
+ g_assert_cmpint (data.num_unregistered_subtree_calls, ==, 0);
+ g_assert (e_dbus_connection_unregister_subtree (c, subtree_registration_id));
+ g_assert_cmpint (data.num_unregistered_subtree_calls, ==, 1);
+ subtree_registration_id = e_dbus_connection_register_subtree (c,
+ "/foo/boss/executives",
+ &subtree_vtable,
+ E_DBUS_SUBTREE_FLAGS_NONE,
+ &data,
+ on_subtree_unregistered,
+ &error);
+ g_assert_no_error (error);
+ g_assert (subtree_registration_id > 0);
+
+ /* try to register something under /foo/boss/executives - this should work
+ * because registered subtrees and registered objects can coexist.
+ *
+ * Make the exported object implement *two* interfaces so we can check
+ * that the right introspection handler is invoked.
+ */
+ registration_id = e_dbus_connection_register_object (c,
+ "/foo/boss/executives/non_subtree_object",
+ bar_interface_info.name,
+ &bar_interface_info,
+ NULL,
+ &data,
+ on_object_unregistered,
+ &error);
+ g_assert_no_error (error);
+ g_assert (registration_id > 0);
+ non_subtree_object_path_bar_reg_id = registration_id;
+ num_successful_registrations++;
+ registration_id = e_dbus_connection_register_object (c,
+ "/foo/boss/executives/non_subtree_object",
+ foo_interface_info.name,
+ &foo_interface_info,
+ NULL,
+ &data,
+ on_object_unregistered,
+ &error);
+ g_assert_no_error (error);
+ g_assert (registration_id > 0);
+ non_subtree_object_path_foo_reg_id = registration_id;
+ num_successful_registrations++;
+
+ /* now register a dynamic subtree, spawning objects as they are called */
+ dyna_data = g_ptr_array_new ();
+ dyna_subtree_registration_id = e_dbus_connection_register_subtree (c,
+ "/foo/dyna",
+ &dynamic_subtree_vtable,
+ E_DBUS_SUBTREE_FLAGS_DISPATCH_TO_UNENUMERATED_NODES,
+ dyna_data,
+ (GDestroyNotify)g_ptr_array_unref,
+ &error);
+ g_assert_no_error (error);
+ g_assert (dyna_subtree_registration_id > 0);
+
+ /* First assert that we have no nodes in the dynamic subtree */
+ nodes = get_nodes_at (c, "/foo/dyna");
+ g_assert (nodes != NULL);
+ g_assert_cmpint (g_strv_length (nodes), ==, 0);
+ g_strfreev (nodes);
+ g_assert_cmpint (count_interfaces (c, "/foo/dyna"), ==, 4);
+
+ /* Install three nodes in the dynamic subtree via the dyna_data backdoor and
+ * assert that they show up correctly in the introspection data */
+ g_ptr_array_add (dyna_data, (gpointer)"lol");
+ g_ptr_array_add (dyna_data, (gpointer)"cat");
+ g_ptr_array_add (dyna_data, (gpointer)"cheezburger");
+ nodes = get_nodes_at (c, "/foo/dyna");
+ g_assert (nodes != NULL);
+ g_assert_cmpint (g_strv_length (nodes), ==, 3);
+ g_assert_cmpstr (nodes[0], ==, "lol");
+ g_assert_cmpstr (nodes[1], ==, "cat");
+ g_assert_cmpstr (nodes[2], ==, "cheezburger");
+ g_strfreev (nodes);
+ g_assert_cmpint (count_interfaces (c, "/foo/dyna/lol"), ==, 4);
+ g_assert_cmpint (count_interfaces (c, "/foo/dyna/cat"), ==, 4);
+ g_assert_cmpint (count_interfaces (c, "/foo/dyna/cheezburger"), ==, 4);
+
+ /* Call a non-existing object path and assert that it has been created */
+ dyna_create (c, "dynamicallycreated");
+ nodes = get_nodes_at (c, "/foo/dyna");
+ g_assert (nodes != NULL);
+ g_assert_cmpint (g_strv_length (nodes), ==, 4);
+ g_assert_cmpstr (nodes[0], ==, "lol");
+ g_assert_cmpstr (nodes[1], ==, "cat");
+ g_assert_cmpstr (nodes[2], ==, "cheezburger");
+ g_assert_cmpstr (nodes[3], ==, "dynamicallycreated");
+ g_strfreev (nodes);
+ g_assert_cmpint (count_interfaces (c, "/foo/dyna/dynamicallycreated"), ==, 4);
+
+
+ /* now check that the object hierarachy is properly generated... yes, it's a bit
+ * perverse that we round-trip to the bus to introspect ourselves ;-)
+ */
+ nodes = get_nodes_at (c, "/");
+ g_assert (nodes != NULL);
+ g_assert_cmpint (g_strv_length (nodes), ==, 1);
+ g_assert_cmpstr (nodes[0], ==, "foo");
+ g_strfreev (nodes);
+ g_assert_cmpint (count_interfaces (c, "/"), ==, 0);
+
+ nodes = get_nodes_at (c, "/foo");
+ g_assert (nodes != NULL);
+ g_assert_cmpint (g_strv_length (nodes), ==, 2);
+ g_assert_cmpstr (nodes[0], ==, "boss");
+ g_assert_cmpstr (nodes[1], ==, "dyna");
+ g_strfreev (nodes);
+ g_assert_cmpint (count_interfaces (c, "/foo"), ==, 0);
+
+ nodes = get_nodes_at (c, "/foo/boss");
+ g_assert (nodes != NULL);
+ g_assert_cmpint (g_strv_length (nodes), ==, 4);
+ g_assert (_g_strv_has_string ((const gchar* const *) nodes, "worker1"));
+ g_assert (_g_strv_has_string ((const gchar* const *) nodes, "worker2"));
+ g_assert (_g_strv_has_string ((const gchar* const *) nodes, "interns"));
+ g_assert (_g_strv_has_string ((const gchar* const *) nodes, "executives"));
+ g_strfreev (nodes);
+ /* any registered object always implement org.freedesktop.DBus.[Peer,Introspectable,Properties] */
+ g_assert_cmpint (count_interfaces (c, "/foo/boss"), ==, 5);
+ g_assert (has_interface (c, "/foo/boss", foo_interface_info.name));
+ g_assert (has_interface (c, "/foo/boss", bar_interface_info.name));
+
+ /* check subtree nodes - we should have only non_subtree_object in /foo/boss/executives
+ * because data.num_subtree_nodes is 0
+ */
+ nodes = get_nodes_at (c, "/foo/boss/executives");
+ g_assert (nodes != NULL);
+ g_assert (_g_strv_has_string ((const gchar* const *) nodes, "non_subtree_object"));
+ g_assert_cmpint (g_strv_length (nodes), ==, 1);
+ g_strfreev (nodes);
+ g_assert_cmpint (count_interfaces (c, "/foo/boss/executives"), ==, 0);
+
+ /* now change data.num_subtree_nodes and check */
+ data.num_subtree_nodes = 2;
+ nodes = get_nodes_at (c, "/foo/boss/executives");
+ g_assert (nodes != NULL);
+ g_assert_cmpint (g_strv_length (nodes), ==, 5);
+ g_assert (_g_strv_has_string ((const gchar* const *) nodes, "non_subtree_object"));
+ g_assert (_g_strv_has_string ((const gchar* const *) nodes, "vp0"));
+ g_assert (_g_strv_has_string ((const gchar* const *) nodes, "vp1"));
+ g_assert (_g_strv_has_string ((const gchar* const *) nodes, "evp0"));
+ g_assert (_g_strv_has_string ((const gchar* const *) nodes, "evp1"));
+ /* check that /foo/boss/executives/non_subtree_object is not handled by the
+ * subtree handlers - we can do this because objects from subtree handlers
+ * has exactly one interface and non_subtree_object has two
+ */
+ g_assert_cmpint (count_interfaces (c, "/foo/boss/executives/non_subtree_object"), ==, 5);
+ g_assert (has_interface (c, "/foo/boss/executives/non_subtree_object", foo_interface_info.name));
+ g_assert (has_interface (c, "/foo/boss/executives/non_subtree_object", bar_interface_info.name));
+ /* check that the vp and evp objects are handled by the subtree handlers */
+ g_assert_cmpint (count_interfaces (c, "/foo/boss/executives/vp0"), ==, 4);
+ g_assert_cmpint (count_interfaces (c, "/foo/boss/executives/vp1"), ==, 4);
+ g_assert_cmpint (count_interfaces (c, "/foo/boss/executives/evp0"), ==, 4);
+ g_assert_cmpint (count_interfaces (c, "/foo/boss/executives/evp1"), ==, 4);
+ g_assert (has_interface (c, "/foo/boss/executives/vp0", foo_interface_info.name));
+ g_assert (has_interface (c, "/foo/boss/executives/vp1", foo_interface_info.name));
+ g_assert (has_interface (c, "/foo/boss/executives/evp0", bar_interface_info.name));
+ g_assert (has_interface (c, "/foo/boss/executives/evp1", bar_interface_info.name));
+ g_strfreev (nodes);
+ data.num_subtree_nodes = 3;
+ nodes = get_nodes_at (c, "/foo/boss/executives");
+ g_assert (nodes != NULL);
+ g_assert_cmpint (g_strv_length (nodes), ==, 7);
+ g_assert (_g_strv_has_string ((const gchar* const *) nodes, "non_subtree_object"));
+ g_assert (_g_strv_has_string ((const gchar* const *) nodes, "vp0"));
+ g_assert (_g_strv_has_string ((const gchar* const *) nodes, "vp1"));
+ g_assert (_g_strv_has_string ((const gchar* const *) nodes, "vp2"));
+ g_assert (_g_strv_has_string ((const gchar* const *) nodes, "evp0"));
+ g_assert (_g_strv_has_string ((const gchar* const *) nodes, "evp1"));
+ g_assert (_g_strv_has_string ((const gchar* const *) nodes, "evp2"));
+ g_strfreev (nodes);
+
+ /* check that unregistering the subtree handler works */
+ g_assert_cmpint (data.num_unregistered_subtree_calls, ==, 1);
+ g_assert (e_dbus_connection_unregister_subtree (c, subtree_registration_id));
+ g_assert_cmpint (data.num_unregistered_subtree_calls, ==, 2);
+ nodes = get_nodes_at (c, "/foo/boss/executives");
+ g_assert (nodes != NULL);
+ g_assert_cmpint (g_strv_length (nodes), ==, 1);
+ g_assert (_g_strv_has_string ((const gchar* const *) nodes, "non_subtree_object"));
+ g_strfreev (nodes);
+
+ /* check we catch the object from the other "binding" */
+ nodes = get_nodes_at (c, "/foo/boss/interns");
+ g_assert (nodes != NULL);
+ g_assert_cmpint (g_strv_length (nodes), ==, 4);
+ g_assert (_g_strv_has_string ((const gchar* const *) nodes, "intern1"));
+ g_assert (_g_strv_has_string ((const gchar* const *) nodes, "intern2"));
+ g_assert (_g_strv_has_string ((const gchar* const *) nodes, "intern3"));
+ g_assert (_g_strv_has_string ((const gchar* const *) nodes, "other_intern")); /* from the other "binding" */
+ g_strfreev (nodes);
+
+ g_assert (e_dbus_connection_unregister_object (c, boss_foo_reg_id));
+ g_assert (e_dbus_connection_unregister_object (c, boss_bar_reg_id));
+ g_assert (e_dbus_connection_unregister_object (c, worker1_foo_reg_id));
+ g_assert (e_dbus_connection_unregister_object (c, worker2_bar_reg_id));
+ g_assert (e_dbus_connection_unregister_object (c, intern1_foo_reg_id));
+ g_assert (e_dbus_connection_unregister_object (c, intern2_bar_reg_id));
+ g_assert (e_dbus_connection_unregister_object (c, intern2_foo_reg_id));
+ g_assert (e_dbus_connection_unregister_object (c, intern3_bar_reg_id));
+ g_assert (e_dbus_connection_unregister_object (c, non_subtree_object_path_bar_reg_id));
+ g_assert (e_dbus_connection_unregister_object (c, non_subtree_object_path_foo_reg_id));
+
+ g_assert_cmpint (data.num_unregistered_calls, ==, num_successful_registrations);
+
+ /* Shutdown the other "binding" */
+ g_assert (dbus_connection_unregister_object_path (dc, "/foo/boss/interns/other_intern"));
+ dbus_connection_unref (dc);
+
+ /* check that we no longer export any objects - TODO: it looks like there's a bug in
+ * libdbus-1 here: libdbus still reports the '/foo' object; so disable the test for now
+ */
+#if 0
+ nodes = get_nodes_at (c, "/");
+ g_assert (nodes != NULL);
+ g_assert_cmpint (g_strv_length (nodes), ==, 0);
+ g_strfreev (nodes);
+#endif
+
+ /* To prevent from exiting and attaching a DBus tool like D-Feet; uncomment: */
+ /*g_info ("Point D-feet or other tool at: %s", session_bus_get_temporary_address());
+ g_main_loop_run (loop);*/
+
+ g_object_unref (c);
+}
+
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+int
+main (int argc,
+ char *argv[])
+{
+ gint ret;
+
+ 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);
+
+ session_bus_up ();
+
+ /* TODO: wait a bit for the bus to come up.. ideally session_bus_up() won't return
+ * until one can connect to the bus but that's not how things work right now
+ */
+ usleep (500 * 1000);
+
+ g_test_add_func ("/gdbus/object-registration", test_object_registration);
+ /* TODO: check that we spit out correct introspection data */
+ /* TODO: check that registering a whole subtree works */
+
+ ret = g_test_run();
+
+ /* tear down bus */
+ session_bus_down ();
+
+ return ret;
+}
diff --git a/edbus/tests/introspection.c b/edbus/tests/introspection.c
new file mode 100644
index 0000000..388139c
--- /dev/null
+++ b/edbus/tests/introspection.c
@@ -0,0 +1,163 @@
+/* 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 <edbus/edbus.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "tests.h"
+
+/* all tests rely on a shared mainloop */
+static GMainLoop *loop = NULL;
+
+/* ---------------------------------------------------------------------------------------------------- */
+/* Test introspection parser */
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+introspection_on_proxy_appeared (EDBusConnection *connection,
+ const gchar *name,
+ const gchar *name_owner,
+ EDBusProxy *proxy,
+ gpointer user_data)
+{
+ GError *error;
+ const gchar *xml_data;
+ EDBusNodeInfo *node_info;
+ const EDBusInterfaceInfo *interface_info;
+ const EDBusMethodInfo *method_info;
+ const EDBusSignalInfo *signal_info;
+ EVariant *result;
+
+ error = NULL;
+
+ /**
+ * Invoke Introspect(), then parse the output.
+ */
+ result = e_dbus_proxy_invoke_method_sync (proxy,
+ "org.freedesktop.DBus.Introspectable.Introspect",
+ NULL,
+ -1,
+ NULL,
+ &error);
+ g_assert_no_error (error);
+ g_assert (result != NULL);
+ e_variant_get (result, "(s)", &xml_data);
+
+ node_info = e_dbus_node_info_new_for_xml (xml_data, &error);
+ g_assert_no_error (error);
+ g_assert (node_info != NULL);
+
+ /* for now we only check a couple of things. TODO: check more things */
+
+ interface_info = e_dbus_node_info_lookup_interface (node_info, "com.example.NonExistantInterface");
+ g_assert (interface_info == NULL);
+
+ interface_info = e_dbus_node_info_lookup_interface (node_info, "org.freedesktop.DBus.Introspectable");
+ g_assert (interface_info != NULL);
+ method_info = e_dbus_interface_info_lookup_method (interface_info, "NonExistantMethod");
+ g_assert (method_info == NULL);
+ method_info = e_dbus_interface_info_lookup_method (interface_info, "Introspect");
+ g_assert (method_info != NULL);
+ g_assert_cmpstr (method_info->in_signature, ==, "");
+ g_assert_cmpint (method_info->in_num_args, ==, 0);
+ g_assert (method_info->in_args == NULL);
+ g_assert_cmpstr (method_info->out_signature, ==, "s");
+ g_assert_cmpint (method_info->out_num_args, ==, 1);
+ g_assert (method_info->out_args != NULL);
+ g_assert (method_info->out_args[0].name != NULL);
+ g_assert_cmpstr (method_info->out_args[0].signature, ==, "s");
+
+ interface_info = e_dbus_node_info_lookup_interface (node_info, "com.example.Frob");
+ g_assert (interface_info != NULL);
+ signal_info = e_dbus_interface_info_lookup_signal (interface_info, "TestSignal");
+ g_assert (signal_info != NULL);
+ g_assert_cmpstr (signal_info->signature, ==, "sov");
+
+ e_dbus_node_info_free (node_info);
+ e_variant_unref (result);
+
+ g_main_loop_quit (loop);
+}
+
+static void
+introspection_on_proxy_vanished (EDBusConnection *connection,
+ const gchar *name,
+ gpointer user_data)
+{
+}
+
+static void
+test_introspection_parser (void)
+{
+ guint watcher_id;
+
+ session_bus_up ();
+
+ watcher_id = e_bus_watch_proxy (G_BUS_TYPE_SESSION,
+ "com.example.TestService",
+ "/com/example/TestObject",
+ "com.example.Frob",
+ E_TYPE_DBUS_PROXY,
+ E_DBUS_PROXY_FLAGS_NONE,
+ introspection_on_proxy_appeared,
+ introspection_on_proxy_vanished,
+ NULL,
+ NULL);
+
+ /* TODO: wait a bit for the bus to come up.. ideally session_bus_up() won't return
+ * until one can connect to the bus but that's not how things work right now
+ */
+ usleep (500 * 1000);
+ /* this is safe; testserver will exit once the bus goes away */
+ g_assert (g_spawn_command_line_async ("./testserver.py", NULL));
+
+ g_main_loop_run (loop);
+
+ e_bus_unwatch_proxy (watcher_id);
+
+ /* tear down bus */
+ 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/introspection-parser", test_introspection_parser);
+ return g_test_run();
+}
diff --git a/edbus/tests/names.c b/edbus/tests/names.c
new file mode 100644
index 0000000..e2e53ce
--- /dev/null
+++ b/edbus/tests/names.c
@@ -0,0 +1,651 @@
+/* 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 <edbus/edbus.h>
+#include <unistd.h>
+
+#include "tests.h"
+
+/* all tests rely on a shared mainloop */
+static GMainLoop *loop;
+
+/* ---------------------------------------------------------------------------------------------------- */
+/* Test that e_bus_own_name() works correctly */
+/* ---------------------------------------------------------------------------------------------------- */
+
+typedef struct
+{
+ GMainLoop *loop;
+ gboolean expect_null_connection;
+ guint num_acquired;
+ guint num_lost;
+ guint num_free_func;
+} OwnNameData;
+
+static void
+own_name_data_free_func (OwnNameData *data)
+{
+ data->num_free_func++;
+ g_main_loop_quit (loop);
+}
+
+static void
+name_acquired_handler (EDBusConnection *connection,
+ const gchar *name,
+ gpointer user_data)
+{
+ OwnNameData *data = user_data;
+ e_dbus_connection_set_exit_on_disconnect (connection, FALSE);
+ data->num_acquired += 1;
+ g_main_loop_quit (loop);
+}
+
+static void
+name_lost_handler (EDBusConnection *connection,
+ const gchar *name,
+ gpointer user_data)
+{
+ OwnNameData *data = user_data;
+ if (data->expect_null_connection)
+ {
+ g_assert (connection == NULL);
+ }
+ else
+ {
+ g_assert (connection != NULL);
+ e_dbus_connection_set_exit_on_disconnect (connection, FALSE);
+ }
+ data->num_lost += 1;
+ g_main_loop_quit (loop);
+}
+
+static void
+test_bus_own_name (void)
+{
+ guint id;
+ guint id2;
+ OwnNameData data;
+ OwnNameData data2;
+ const gchar *name;
+ EDBusConnection *c;
+ GError *error;
+ gboolean name_has_owner_reply;
+ EDBusConnection *c2;
+ EVariant *result;
+
+ error = NULL;
+ name = "org.gtk.EDBus.Name1";
+
+ /**
+ * First check that name_lost_handler() is invoked if there is no bus.
+ *
+ * Also make sure name_lost_handler() isn't invoked when unowning the name.
+ */
+ data.num_free_func = 0;
+ data.num_acquired = 0;
+ data.num_lost = 0;
+ data.expect_null_connection = TRUE;
+ id = e_bus_own_name (G_BUS_TYPE_SESSION,
+ name,
+ G_BUS_NAME_OWNER_FLAGS_NONE,
+ name_acquired_handler,
+ name_lost_handler,
+ &data,
+ (GDestroyNotify) own_name_data_free_func);
+ g_assert_cmpint (data.num_acquired, ==, 0);
+ g_assert_cmpint (data.num_lost, ==, 0);
+ g_main_loop_run (loop);
+ g_assert_cmpint (data.num_acquired, ==, 0);
+ g_assert_cmpint (data.num_lost, ==, 1);
+ e_bus_unown_name (id);
+ g_assert_cmpint (data.num_acquired, ==, 0);
+ g_assert_cmpint (data.num_lost, ==, 1);
+ g_main_loop_run (loop);
+ g_assert_cmpint (data.num_free_func, ==, 1);
+
+ /**
+ * Bring up a bus, then own a name and check name_acquired_handler() is invoked.
+ */
+ session_bus_up ();
+ data.num_acquired = 0;
+ data.num_lost = 0;
+ data.expect_null_connection = FALSE;
+ id = e_bus_own_name (G_BUS_TYPE_SESSION,
+ name,
+ G_BUS_NAME_OWNER_FLAGS_NONE,
+ name_acquired_handler,
+ name_lost_handler,
+ &data,
+ (GDestroyNotify) own_name_data_free_func);
+ g_assert_cmpint (data.num_acquired, ==, 0);
+ g_assert_cmpint (data.num_lost, ==, 0);
+ g_main_loop_run (loop);
+ g_assert_cmpint (data.num_acquired, ==, 1);
+ g_assert_cmpint (data.num_lost, ==, 0);
+
+ /**
+ * Check that the name was actually acquired.
+ */
+ c = e_dbus_connection_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
+ g_assert (c != NULL);
+ g_assert (!e_dbus_connection_get_is_disconnected (c));
+ result = e_dbus_connection_invoke_method_sync (c,
+ "org.freedesktop.DBus", /* bus name */
+ "/org/freedesktop/DBus", /* object path */
+ "org.freedesktop.DBus", /* interface name */
+ "NameHasOwner", /* method name */
+ e_variant_new ("(s)", name),
+ -1,
+ NULL,
+ &error);
+ g_assert_no_error (error);
+ g_assert (result != NULL);
+ e_variant_get (result, "(b)", &name_has_owner_reply);
+ g_assert (name_has_owner_reply);
+ e_variant_unref (result);
+
+ /**
+ * Stop owning the name - this should trigger name_lost_handler()
+ * (in an idle handler) from e_bus_unown_name().
+ */
+ data.expect_null_connection = FALSE;
+ e_bus_unown_name (id);
+ g_main_loop_run (loop);
+ g_assert_cmpint (data.num_acquired, ==, 1);
+ g_assert_cmpint (data.num_lost, ==, 1);
+ g_assert_cmpint (data.num_free_func, ==, 2);
+
+ /**
+ * Check that the name was actually relased.
+ */
+ result = e_dbus_connection_invoke_method_sync (c,
+ "org.freedesktop.DBus", /* bus name */
+ "/org/freedesktop/DBus", /* object path */
+ "org.freedesktop.DBus", /* interface name */
+ "NameHasOwner", /* method name */
+ e_variant_new ("(s)", name),
+ -1,
+ NULL,
+ &error);
+ g_assert_no_error (error);
+ g_assert (result != NULL);
+ e_variant_get (result, "(b)", &name_has_owner_reply);
+ g_assert (!name_has_owner_reply);
+ e_variant_unref (result);
+
+ /**
+ * Own the name again.
+ */
+ data.num_acquired = 0;
+ data.num_lost = 0;
+ data.expect_null_connection = FALSE;
+ id = e_bus_own_name (G_BUS_TYPE_SESSION,
+ name,
+ G_BUS_NAME_OWNER_FLAGS_NONE,
+ name_acquired_handler,
+ name_lost_handler,
+ &data,
+ (GDestroyNotify) own_name_data_free_func);
+ g_assert_cmpint (data.num_acquired, ==, 0);
+ g_assert_cmpint (data.num_lost, ==, 0);
+ g_main_loop_run (loop);
+ g_assert_cmpint (data.num_acquired, ==, 1);
+ g_assert_cmpint (data.num_lost, ==, 0);
+
+ /**
+ * Try owning the name with another object on the same connection - this should
+ * fail because we already own the name.
+ */
+ data2.num_free_func = 0;
+ data2.num_acquired = 0;
+ data2.num_lost = 0;
+ data2.expect_null_connection = FALSE;
+ id2 = e_bus_own_name (G_BUS_TYPE_SESSION,
+ name,
+ G_BUS_NAME_OWNER_FLAGS_NONE,
+ name_acquired_handler,
+ name_lost_handler,
+ &data2,
+ (GDestroyNotify) own_name_data_free_func);
+ g_assert_cmpint (data2.num_acquired, ==, 0);
+ g_assert_cmpint (data2.num_lost, ==, 0);
+ g_main_loop_run (loop);
+ g_assert_cmpint (data2.num_acquired, ==, 0);
+ g_assert_cmpint (data2.num_lost, ==, 1);
+ e_bus_unown_name (id2);
+ g_assert_cmpint (data2.num_acquired, ==, 0);
+ g_assert_cmpint (data2.num_lost, ==, 1);
+ g_main_loop_run (loop);
+ g_assert_cmpint (data2.num_free_func, ==, 1);
+
+ /**
+ * Create a secondary (e.g. private) connection and try owning the name on that
+ * connection. This should fail both with and without _REPLACE because we
+ * didn't specify ALLOW_REPLACEMENT.
+ */
+ c2 = e_dbus_connection_bus_get_private_sync (G_BUS_TYPE_SESSION, NULL, NULL);
+ g_assert (c2 != NULL);
+ g_assert (!e_dbus_connection_get_is_disconnected (c2));
+ /* first without _REPLACE */
+ data2.num_acquired = 0;
+ data2.num_lost = 0;
+ data2.expect_null_connection = FALSE;
+ data2.num_free_func = 0;
+ id2 = e_bus_own_name_on_connection (c2,
+ name,
+ G_BUS_NAME_OWNER_FLAGS_NONE,
+ name_acquired_handler,
+ name_lost_handler,
+ &data2,
+ (GDestroyNotify) own_name_data_free_func);
+ g_assert_cmpint (data2.num_acquired, ==, 0);
+ g_assert_cmpint (data2.num_lost, ==, 0);
+ g_main_loop_run (loop);
+ g_assert_cmpint (data2.num_acquired, ==, 0);
+ g_assert_cmpint (data2.num_lost, ==, 1);
+ e_bus_unown_name (id2);
+ g_assert_cmpint (data2.num_acquired, ==, 0);
+ g_assert_cmpint (data2.num_lost, ==, 1);
+ g_main_loop_run (loop);
+ g_assert_cmpint (data2.num_free_func, ==, 1);
+ /* then with _REPLACE */
+ data2.num_acquired = 0;
+ data2.num_lost = 0;
+ data2.expect_null_connection = FALSE;
+ data2.num_free_func = 0;
+ id2 = e_bus_own_name_on_connection (c2,
+ name,
+ G_BUS_NAME_OWNER_FLAGS_REPLACE,
+ name_acquired_handler,
+ name_lost_handler,
+ &data2,
+ (GDestroyNotify) own_name_data_free_func);
+ g_assert_cmpint (data2.num_acquired, ==, 0);
+ g_assert_cmpint (data2.num_lost, ==, 0);
+ g_main_loop_run (loop);
+ g_assert_cmpint (data2.num_acquired, ==, 0);
+ g_assert_cmpint (data2.num_lost, ==, 1);
+ e_bus_unown_name (id2);
+ g_assert_cmpint (data2.num_acquired, ==, 0);
+ g_assert_cmpint (data2.num_lost, ==, 1);
+ g_main_loop_run (loop);
+ g_assert_cmpint (data2.num_free_func, ==, 1);
+
+ /**
+ * Stop owning the name and grab it again with _ALLOW_REPLACEMENT.
+ */
+ data.expect_null_connection = FALSE;
+ e_bus_unown_name (id);
+ g_main_loop_run (loop);
+ g_assert_cmpint (data.num_acquired, ==, 1);
+ g_assert_cmpint (data.num_lost, ==, 1);
+ g_assert_cmpint (data.num_free_func, ==, 3);
+ /* grab it again */
+ data.num_acquired = 0;
+ data.num_lost = 0;
+ data.expect_null_connection = FALSE;
+ id = e_bus_own_name (G_BUS_TYPE_SESSION,
+ name,
+ G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT,
+ name_acquired_handler,
+ name_lost_handler,
+ &data,
+ (GDestroyNotify) own_name_data_free_func);
+ g_assert_cmpint (data.num_acquired, ==, 0);
+ g_assert_cmpint (data.num_lost, ==, 0);
+ g_main_loop_run (loop);
+ g_assert_cmpint (data.num_acquired, ==, 1);
+ g_assert_cmpint (data.num_lost, ==, 0);
+
+ /**
+ * Now try to grab the name from the secondary connection.
+ *
+ */
+ /* first without _REPLACE - this won't make us acquire the name */
+ data2.num_acquired = 0;
+ data2.num_lost = 0;
+ data2.expect_null_connection = FALSE;
+ data2.num_free_func = 0;
+ id2 = e_bus_own_name_on_connection (c2,
+ name,
+ G_BUS_NAME_OWNER_FLAGS_NONE,
+ name_acquired_handler,
+ name_lost_handler,
+ &data2,
+ (GDestroyNotify) own_name_data_free_func);
+ g_assert_cmpint (data2.num_acquired, ==, 0);
+ g_assert_cmpint (data2.num_lost, ==, 0);
+ g_main_loop_run (loop);
+ g_assert_cmpint (data2.num_acquired, ==, 0);
+ g_assert_cmpint (data2.num_lost, ==, 1);
+ e_bus_unown_name (id2);
+ g_assert_cmpint (data2.num_acquired, ==, 0);
+ g_assert_cmpint (data2.num_lost, ==, 1);
+ g_main_loop_run (loop);
+ g_assert_cmpint (data2.num_free_func, ==, 1);
+ /* then with _REPLACE - here we should acquire the name - e.g. owner should lose it
+ * and owner2 should acquire it */
+ data2.num_acquired = 0;
+ data2.num_lost = 0;
+ data2.expect_null_connection = FALSE;
+ data2.num_free_func = 0;
+ id2 = e_bus_own_name_on_connection (c2,
+ name,
+ G_BUS_NAME_OWNER_FLAGS_REPLACE,
+ name_acquired_handler,
+ name_lost_handler,
+ &data2,
+ (GDestroyNotify) own_name_data_free_func);
+ g_assert_cmpint (data.num_acquired, ==, 1);
+ g_assert_cmpint (data.num_lost, ==, 0);
+ g_assert_cmpint (data2.num_acquired, ==, 0);
+ g_assert_cmpint (data2.num_lost, ==, 0);
+ /* wait for handlers for both owner and owner2 to fire */
+ g_main_loop_run (loop);
+ g_main_loop_run (loop);
+ g_assert_cmpint (data.num_acquired, ==, 1);
+ g_assert_cmpint (data.num_lost, ==, 1);
+ g_assert_cmpint (data2.num_acquired, ==, 1);
+ g_assert_cmpint (data2.num_lost, ==, 0);
+ /* ok, make owner2 release the name - then wait for owner to automagically reacquire it */
+ e_bus_unown_name (id2);
+ g_main_loop_run (loop);
+ g_assert_cmpint (data2.num_acquired, ==, 1);
+ g_assert_cmpint (data2.num_lost, ==, 1);
+ g_main_loop_run (loop);
+ g_assert_cmpint (data2.num_free_func, ==, 1);
+ g_assert_cmpint (data.num_acquired, ==, 2);
+ g_assert_cmpint (data.num_lost, ==, 1);
+
+ /**
+ * Finally, nuke the bus and check name_lost_handler() is invoked.
+ *
+ */
+ data.expect_null_connection = TRUE;
+ session_bus_down ();
+ g_main_loop_run (loop);
+ g_assert_cmpint (data.num_acquired, ==, 2);
+ g_assert_cmpint (data.num_lost, ==, 2);
+ e_bus_unown_name (id);
+ g_main_loop_run (loop);
+ g_assert_cmpint (data.num_free_func, ==, 4);
+
+ g_object_unref (c);
+ g_object_unref (c2);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+/* Test that e_bus_watch_name() works correctly */
+/* ---------------------------------------------------------------------------------------------------- */
+
+typedef struct
+{
+ gboolean expect_null_connection;
+ guint num_acquired;
+ guint num_lost;
+ guint num_appeared;
+ guint num_vanished;
+ guint num_free_func;
+} WatchNameData;
+
+static void
+watch_name_data_free_func (WatchNameData *data)
+{
+ data->num_free_func++;
+ g_main_loop_quit (loop);
+}
+
+static void
+w_name_acquired_handler (EDBusConnection *connection,
+ const gchar *name,
+ gpointer user_data)
+{
+ WatchNameData *data = user_data;
+ data->num_acquired += 1;
+ g_main_loop_quit (loop);
+}
+
+static void
+w_name_lost_handler (EDBusConnection *connection,
+ const gchar *name,
+ gpointer user_data)
+{
+ WatchNameData *data = user_data;
+ data->num_lost += 1;
+ g_main_loop_quit (loop);
+}
+
+static void
+name_appeared_handler (EDBusConnection *connection,
+ const gchar *name,
+ const gchar *name_owner,
+ gpointer user_data)
+{
+ WatchNameData *data = user_data;
+ if (data->expect_null_connection)
+ {
+ g_assert (connection == NULL);
+ }
+ else
+ {
+ g_assert (connection != NULL);
+ e_dbus_connection_set_exit_on_disconnect (connection, FALSE);
+ }
+ data->num_appeared += 1;
+ g_main_loop_quit (loop);
+}
+
+static void
+name_vanished_handler (EDBusConnection *connection,
+ const gchar *name,
+ gpointer user_data)
+{
+ WatchNameData *data = user_data;
+ if (data->expect_null_connection)
+ {
+ g_assert (connection == NULL);
+ }
+ else
+ {
+ g_assert (connection != NULL);
+ e_dbus_connection_set_exit_on_disconnect (connection, FALSE);
+ }
+ data->num_vanished += 1;
+ g_main_loop_quit (loop);
+}
+
+static void
+test_bus_watch_name (void)
+{
+ WatchNameData data;
+ guint id;
+ guint owner_id;
+
+ /**
+ * First check that name_vanished_handler() is invoked if there is no bus.
+ *
+ * Also make sure name_vanished_handler() isn't invoked when unwatching the name.
+ */
+ data.num_free_func = 0;
+ data.num_appeared = 0;
+ data.num_vanished = 0;
+ data.expect_null_connection = TRUE;
+ id = e_bus_watch_name (G_BUS_TYPE_SESSION,
+ "org.gtk.EDBus.Name1",
+ name_appeared_handler,
+ name_vanished_handler,
+ &data,
+ (GDestroyNotify) watch_name_data_free_func);
+ g_assert_cmpint (data.num_appeared, ==, 0);
+ g_assert_cmpint (data.num_vanished, ==, 0);
+ g_main_loop_run (loop);
+ g_assert_cmpint (data.num_appeared, ==, 0);
+ g_assert_cmpint (data.num_vanished, ==, 1);
+ e_bus_unwatch_name (id);
+ g_assert_cmpint (data.num_appeared, ==, 0);
+ g_assert_cmpint (data.num_vanished, ==, 1);
+ g_main_loop_run (loop);
+ g_assert_cmpint (data.num_free_func, ==, 1);
+
+ /**
+ * Now bring up a bus, own a name, and then start watching it.
+ */
+ session_bus_up ();
+ /* own the name */
+ data.num_free_func = 0;
+ data.num_acquired = 0;
+ data.num_lost = 0;
+ data.expect_null_connection = FALSE;
+ owner_id = e_bus_own_name (G_BUS_TYPE_SESSION,
+ "org.gtk.EDBus.Name1",
+ G_BUS_NAME_OWNER_FLAGS_NONE,
+ w_name_acquired_handler,
+ w_name_lost_handler,
+ &data,
+ (GDestroyNotify) watch_name_data_free_func);
+ g_main_loop_run (loop);
+ g_assert_cmpint (data.num_acquired, ==, 1);
+ g_assert_cmpint (data.num_lost, ==, 0);
+ /* now watch the name */
+ data.num_appeared = 0;
+ data.num_vanished = 0;
+ id = e_bus_watch_name (G_BUS_TYPE_SESSION,
+ "org.gtk.EDBus.Name1",
+ name_appeared_handler,
+ name_vanished_handler,
+ &data,
+ (GDestroyNotify) watch_name_data_free_func);
+ g_assert_cmpint (data.num_appeared, ==, 0);
+ g_assert_cmpint (data.num_vanished, ==, 0);
+ g_main_loop_run (loop);
+ g_assert_cmpint (data.num_appeared, ==, 1);
+ g_assert_cmpint (data.num_vanished, ==, 0);
+
+ /**
+ * Unwatch the name - this should trigger name_vanished_handler() because of this
+ * guarantee
+ *
+ * If the name being watched currently has an owner the name (e.g. @name_appeared_handler
+ * was the last handler to be invoked), then @name_vanished_handler will be invoked
+ * before this function returns.
+ *
+ * in e_bus_unwatch_name().
+ */
+ e_bus_unwatch_name (id);
+ g_main_loop_run (loop);
+ g_assert_cmpint (data.num_appeared, ==, 1);
+ g_assert_cmpint (data.num_vanished, ==, 1);
+ g_assert_cmpint (data.num_free_func, ==, 1);
+
+ /* unown the name */
+ e_bus_unown_name (owner_id);
+ g_main_loop_run (loop);
+ g_assert_cmpint (data.num_acquired, ==, 1);
+ g_assert_cmpint (data.num_lost, ==, 1);
+ g_assert_cmpint (data.num_free_func, ==, 2);
+
+ /**
+ * Create a watcher and then make a name be owned.
+ *
+ * This should trigger name_appeared_handler() ...
+ */
+ /* watch the name */
+ data.num_appeared = 0;
+ data.num_vanished = 0;
+ data.num_free_func = 0;
+ id = e_bus_watch_name (G_BUS_TYPE_SESSION,
+ "org.gtk.EDBus.Name1",
+ name_appeared_handler,
+ name_vanished_handler,
+ &data,
+ (GDestroyNotify) watch_name_data_free_func);
+ g_assert_cmpint (data.num_appeared, ==, 0);
+ g_assert_cmpint (data.num_vanished, ==, 0);
+ g_main_loop_run (loop);
+ g_assert_cmpint (data.num_appeared, ==, 0);
+ g_assert_cmpint (data.num_vanished, ==, 1);
+
+ /* own the name */
+ data.num_acquired = 0;
+ data.num_lost = 0;
+ data.expect_null_connection = FALSE;
+ owner_id = e_bus_own_name (G_BUS_TYPE_SESSION,
+ "org.gtk.EDBus.Name1",
+ G_BUS_NAME_OWNER_FLAGS_NONE,
+ w_name_acquired_handler,
+ w_name_lost_handler,
+ &data,
+ (GDestroyNotify) watch_name_data_free_func);
+ g_main_loop_run (loop);
+ g_main_loop_run (loop);
+ g_assert_cmpint (data.num_acquired, ==, 1);
+ g_assert_cmpint (data.num_lost, ==, 0);
+ g_assert_cmpint (data.num_appeared, ==, 1);
+ g_assert_cmpint (data.num_vanished, ==, 1);
+
+ /**
+ * Nuke the bus and check that the name vanishes and is lost.
+ */
+ data.expect_null_connection = TRUE;
+ session_bus_down ();
+ g_main_loop_run (loop);
+ g_assert_cmpint (data.num_lost, ==, 1);
+ g_assert_cmpint (data.num_vanished, ==, 2);
+
+ e_bus_unwatch_name (id);
+ g_main_loop_run (loop);
+ g_assert_cmpint (data.num_free_func, ==, 1);
+
+ e_bus_unown_name (owner_id);
+ g_main_loop_run (loop);
+ g_assert_cmpint (data.num_free_func, ==, 2);
+
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+int
+main (int argc,
+ char *argv[])
+{
+ gint ret;
+
+ g_type_init ();
+ g_test_init (&argc, &argv, NULL);
+
+ 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/bus-own-name", test_bus_own_name);
+ g_test_add_func ("/gdbus/bus-watch-name", test_bus_watch_name);
+
+ ret = g_test_run();
+
+ g_main_loop_unref (loop);
+
+ return ret;
+}
diff --git a/edbus/tests/peer.c b/edbus/tests/peer.c
new file mode 100644
index 0000000..3c2855e
--- /dev/null
+++ b/edbus/tests/peer.c
@@ -0,0 +1,448 @@
+/* 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 <edbus/edbus.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "tests.h"
+
+static gchar *test_address = NULL;
+static GMainLoop *loop = NULL;
+
+/* ---------------------------------------------------------------------------------------------------- */
+/* Test that peer-to-peer connections work */
+/* ---------------------------------------------------------------------------------------------------- */
+
+
+typedef struct
+{
+ gboolean accept_connection;
+ gint num_connection_attempts;
+ GPtrArray *current_connections;
+ guint num_method_calls;
+} PeerData;
+
+static const EDBusArgInfo test_interface_hello_peer_method_in_args[] =
+{
+ {"greeting", "s", NULL}
+};
+
+static const EDBusArgInfo test_interface_hello_peer_method_out_args[] =
+{
+ {"response", "s", NULL}
+};
+
+static const EDBusMethodInfo test_interface_method_info[] =
+{
+ {
+ "HelloPeer",
+ "s", 1, test_interface_hello_peer_method_in_args,
+ "s", 1, test_interface_hello_peer_method_out_args,
+ NULL
+ },
+ {
+ "EmitSignal",
+ "", 0, NULL,
+ "", 0, NULL,
+ NULL
+ }
+};
+
+static const EDBusArgInfo test_interface_peer_signal_args[] =
+{
+ {"a_string", "s", NULL}
+};
+
+static const EDBusSignalInfo test_interface_signal_info[] =
+{
+ {
+ "PeerSignal",
+ "s", 1, test_interface_peer_signal_args,
+ NULL
+ }
+};
+
+static const EDBusPropertyInfo test_interface_property_info[] =
+{
+ {
+ "PeerProperty",
+ "s", E_DBUS_PROPERTY_INFO_FLAGS_READABLE,
+ NULL
+ }
+};
+
+static const EDBusInterfaceInfo test_interface_introspection_data =
+{
+ "org.gtk.EDBus.PeerTestInterface",
+ 2, test_interface_method_info,
+ 1, test_interface_signal_info,
+ 1, test_interface_property_info,
+ NULL,
+};
+
+static void
+test_interface_method_call (EDBusConnection *connection,
+ gpointer user_data,
+ const gchar *sender,
+ const gchar *object_path,
+ const gchar *interface_name,
+ const gchar *method_name,
+ EVariant *parameters,
+ EDBusMethodInvocation *invocation)
+{
+ PeerData *data = user_data;
+
+ g_assert_cmpstr (object_path, ==, "/org/gtk/EDBus/PeerTestObject");
+ g_assert_cmpstr (interface_name, ==, "org.gtk.EDBus.PeerTestInterface");
+
+ if (g_strcmp0 (method_name, "HelloPeer") == 0)
+ {
+ const gchar *greeting;
+ gchar *response;
+
+ e_variant_get (parameters, "(s)", &greeting);
+
+ response = g_strdup_printf ("You greeted me with '%s'.",
+ greeting);
+ e_dbus_method_invocation_return_value (invocation,
+ e_variant_new ("(s)", response));
+ g_free (response);
+ }
+ else if (g_strcmp0 (method_name, "EmitSignal") == 0)
+ {
+ GError *error;
+
+ error = NULL;
+ e_dbus_connection_emit_signal (connection,
+ NULL,
+ "/org/gtk/EDBus/PeerTestObject",
+ "org.gtk.EDBus.PeerTestInterface",
+ "PeerSignal",
+ NULL,
+ &error);
+ g_assert_no_error (error);
+ e_dbus_method_invocation_return_value (invocation, NULL);
+ }
+ else
+ {
+ g_assert_not_reached ();
+ }
+
+ data->num_method_calls++;
+}
+
+static EVariant *
+test_interface_get_property (EDBusConnection *connection,
+ gpointer user_data,
+ const gchar *sender,
+ const gchar *object_path,
+ const gchar *interface_name,
+ const gchar *property_name,
+ GError **error)
+{
+ g_assert_cmpstr (object_path, ==, "/org/gtk/EDBus/PeerTestObject");
+ g_assert_cmpstr (interface_name, ==, "org.gtk.EDBus.PeerTestInterface");
+ g_assert_cmpstr (property_name, ==, "PeerProperty");
+
+ return e_variant_new_string ("ThePropertyValue");
+}
+
+
+static const EDBusInterfaceVTable test_interface_vtable =
+{
+ test_interface_method_call,
+ test_interface_get_property,
+ NULL /* set_property */
+};
+
+static void
+on_new_connection (EDBusServer *server,
+ EDBusConnection *connection,
+ gpointer user_data)
+{
+ PeerData *data = user_data;
+
+ data->num_connection_attempts++;
+
+ if (data->accept_connection)
+ {
+ GError *error;
+ guint reg_id;
+
+ g_ptr_array_add (data->current_connections, g_object_ref (connection));
+
+ /* export object on the newly established connection */
+ error = NULL;
+ reg_id = e_dbus_connection_register_object (connection,
+ "/org/gtk/EDBus/PeerTestObject",
+ "org.gtk.EDBus.PeerTestInterface",
+ &test_interface_introspection_data,
+ &test_interface_vtable,
+ data,
+ NULL, /* GDestroyNotify for data */
+ &error);
+ g_assert_no_error (error);
+ g_assert (reg_id > 0);
+ }
+ else
+ {
+ /* don't ref the connection */
+ }
+
+ g_main_loop_quit (loop);
+}
+
+static void
+new_proxy_cb (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ EDBusProxy **proxy = user_data;
+ GError *error;
+
+ error = NULL;
+ *proxy = e_dbus_proxy_new_finish (res, &error);
+ g_assert_no_error (error);
+ g_assert (*proxy != NULL);
+
+ g_main_loop_quit (loop);
+}
+
+static void
+hello_peer_cb (EDBusProxy *proxy,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GError *error;
+ EVariant *result;
+ const gchar *s;
+
+ error = NULL;
+ result = e_dbus_proxy_invoke_method_finish (proxy, res, &error);
+ g_assert_no_error (error);
+ g_assert (result != NULL);
+ e_variant_get (result, "(s)", &s);
+ g_assert_cmpstr (s, ==, "You greeted me with 'Hey Peer!'.");
+ e_variant_unref (result);
+
+ g_main_loop_quit (loop);
+}
+
+static void
+on_proxy_signal_received (EDBusProxy *proxy,
+ gchar *sender_name,
+ gchar *signal_name,
+ EVariant *parameters,
+ gpointer user_data)
+{
+ g_assert (sender_name == NULL);
+ g_assert_cmpstr (signal_name, ==, "PeerSignal");
+ g_main_loop_quit (loop);
+}
+
+static void
+test_peer (void)
+{
+ EDBusServer *server;
+ EDBusConnection *c;
+ EDBusConnection *c2;
+ EDBusProxy *proxy;
+ GError *error;
+ PeerData data;
+ EVariant *value;
+
+ error = NULL;
+ data.num_connection_attempts = 0;
+ data.current_connections = g_ptr_array_new_with_free_func (g_object_unref);
+ data.num_method_calls = 0;
+
+ /* first try to connect when there is no server */
+ c = e_dbus_connection_new_sync (test_address,
+ NULL,
+ &error);
+ g_assert_error (error, E_DBUS_ERROR, E_DBUS_ERROR_FILE_NOT_FOUND);
+ g_assert (!e_dbus_error_is_remote_error (error));
+ g_clear_error (&error);
+ g_assert (c == NULL);
+
+ /* bring up a server */
+ server = e_dbus_server_new (test_address, &error);
+ g_assert_no_error (error);
+ g_assert (server != NULL);
+
+ g_signal_connect (server,
+ "new-connection",
+ G_CALLBACK (on_new_connection),
+ &data);
+
+ /* bring up a connection and accept it */
+ data.accept_connection = TRUE;
+ c = e_dbus_connection_new_sync (test_address,
+ NULL,
+ &error);
+ g_assert_no_error (error);
+ g_assert (c != NULL);
+ g_main_loop_run (loop);
+ g_assert_cmpint (data.current_connections->len, ==, 1);
+ g_assert_cmpint (data.num_connection_attempts, ==, 1);
+ g_assert (e_dbus_connection_get_bus_type (c) == G_BUS_TYPE_NONE);
+ g_assert (e_dbus_connection_get_unique_name (c) == NULL);
+ g_assert_cmpstr (e_dbus_connection_get_address (c), ==, test_address);
+
+ /* check that we create a proxy, read properties, receive signals and invoke the HelloPeer() method
+ *
+ * Need to do this async to avoid deadlock.
+ */
+ proxy = NULL;
+ e_dbus_proxy_new (c,
+ E_TYPE_DBUS_PROXY,
+ E_DBUS_PROXY_FLAGS_NONE,
+ NULL, /* bus_name */
+ "/org/gtk/EDBus/PeerTestObject",
+ "org.gtk.EDBus.PeerTestInterface",
+ NULL, /* GCancellable */
+ (GAsyncReadyCallback) new_proxy_cb,
+ &proxy);
+ g_main_loop_run (loop);
+ g_assert (proxy != NULL);
+ value = e_dbus_proxy_get_cached_property (proxy, "PeerProperty", &error);
+ g_assert_cmpstr (e_variant_get_string (value, NULL), ==, "ThePropertyValue");
+
+ /* try invoking a method - again, async */
+ e_dbus_proxy_invoke_method (proxy,
+ "HelloPeer",
+ e_variant_new ("(s)", "Hey Peer!"),
+ -1,
+ NULL, /* GCancellable */
+ (GAsyncReadyCallback) hello_peer_cb,
+ NULL); /* user_data */
+ g_main_loop_run (loop);
+ g_assert_cmpint (data.num_method_calls, ==, 1);
+
+ /* make the other peer emit a signal - catch it */
+ g_signal_connect (proxy,
+ "g-signal",
+ G_CALLBACK (on_proxy_signal_received),
+ NULL);
+ e_dbus_proxy_invoke_method (proxy,
+ "EmitSignal",
+ NULL, /* no arguments */
+ -1,
+ NULL, /* GCancellable */
+ NULL, /* GAsyncReadyCallback - we don't care about the result */
+ NULL); /* user_data */
+ g_main_loop_run (loop);
+ g_object_unref (proxy);
+ g_assert_cmpint (data.num_method_calls, ==, 2);
+
+ /* bring up a connection - don't accept it
+ *
+ * Note that the client will get the connection - but will be disconnected immediately
+ */
+ data.accept_connection = FALSE;
+ c2 = e_dbus_connection_new_sync (test_address,
+ NULL,
+ &error);
+ g_assert_no_error (error);
+ g_assert (c2 != NULL);
+ g_assert (!e_dbus_connection_get_is_disconnected (c2));
+ g_main_loop_run (loop);
+ g_assert_cmpint (data.current_connections->len, ==, 1);
+ g_assert_cmpint (data.num_connection_attempts, ==, 2);
+ _g_assert_signal_received (c2, "disconnected");
+ g_assert (e_dbus_connection_get_is_disconnected (c2));
+ g_object_unref (c2);
+
+ /* bring up a connection - accept it.. then disconnect from the client side - check
+ * that the server side gets the disconnect signal.
+ */
+ data.accept_connection = TRUE;
+ c2 = e_dbus_connection_new_sync (test_address,
+ NULL,
+ &error);
+ g_assert_no_error (error);
+ g_assert (c2 != NULL);
+ g_assert (!e_dbus_connection_get_is_disconnected (c2));
+ g_main_loop_run (loop);
+ g_assert_cmpint (data.current_connections->len, ==, 2);
+ g_assert_cmpint (data.num_connection_attempts, ==, 3);
+ g_assert (!e_dbus_connection_get_is_disconnected (E_DBUS_CONNECTION (data.current_connections->pdata[1])));
+ g_object_unref (c2);
+ _g_assert_signal_received (E_DBUS_CONNECTION (data.current_connections->pdata[1]), "disconnected");
+ g_assert (e_dbus_connection_get_is_disconnected (E_DBUS_CONNECTION (data.current_connections->pdata[1])));
+ g_ptr_array_set_size (data.current_connections, 1); /* remove disconnected connection object */
+
+ /* unref the server and stop listening for new connections
+ *
+ * This won't bring down the established connections - check that c is still connected
+ * by invoking a method
+ */
+ g_object_unref (server);
+ e_dbus_proxy_invoke_method (proxy,
+ "HelloPeer",
+ e_variant_new ("(s)", "Hey Peer!"),
+ -1,
+ NULL, /* GCancellable */
+ (GAsyncReadyCallback) hello_peer_cb,
+ NULL); /* user_data */
+ g_main_loop_run (loop);
+ g_assert_cmpint (data.num_method_calls, ==, 3);
+
+ /* now disconnect from the server side - check that the client side gets the signal */
+ g_assert_cmpint (data.current_connections->len, ==, 1);
+ g_assert (E_DBUS_CONNECTION (data.current_connections->pdata[0]) != c);
+ e_dbus_connection_disconnect (E_DBUS_CONNECTION (data.current_connections->pdata[0]));
+ g_assert (!e_dbus_connection_get_is_disconnected (c));
+ _g_assert_signal_received (c, "disconnected");
+ g_assert (e_dbus_connection_get_is_disconnected (c));
+ g_object_unref (c);
+
+ g_ptr_array_unref (data.current_connections);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+int
+main (int argc,
+ char *argv[])
+{
+ gint ret;
+
+ g_type_init ();
+ g_thread_init (NULL);
+ g_test_init (&argc, &argv, NULL);
+
+ test_address = g_strdup_printf ("unix:path=/tmp/gdbus-test-pid-%d", getpid ());
+
+ /* all the tests rely on a shared main loop */
+ loop = g_main_loop_new (NULL, FALSE);
+
+ g_test_add_func ("/gdbus/peer-to-peer", test_peer);
+
+ ret = g_test_run();
+
+ g_free (test_address);
+ g_main_loop_unref (loop);
+
+ return ret;
+}
diff --git a/edbus/tests/proxy.c b/edbus/tests/proxy.c
new file mode 100644
index 0000000..575ce8d
--- /dev/null
+++ b/edbus/tests/proxy.c
@@ -0,0 +1,387 @@
+/* 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 <edbus/edbus.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "tests.h"
+
+/* all tests rely on a shared mainloop */
+static GMainLoop *loop = NULL;
+
+/* ---------------------------------------------------------------------------------------------------- */
+/* Test that the method aspects of EDBusProxy works */
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+test_methods (EDBusConnection *connection,
+ const gchar *name,
+ const gchar *name_owner,
+ EDBusProxy *proxy)
+{
+ EVariant *result;
+ GError *error;
+ const gchar *str;
+ gchar *dbus_error_name;
+
+ /* check that we can invoke a method */
+ error = NULL;
+ result = e_dbus_proxy_invoke_method_sync (proxy,
+ "HelloWorld",
+ e_variant_new ("(s)", "Hey"),
+ -1,
+ NULL,
+ &error);
+ g_assert_no_error (error);
+ g_assert (result != NULL);
+ g_assert_cmpstr (e_variant_get_type_string (result), ==, "(s)");
+ e_variant_get (result, "(s)", &str);
+ g_assert_cmpstr (str, ==, "You greeted me with 'Hey'. Thanks!");
+ e_variant_unref (result);
+
+ /* Check that we can completely recover the returned error */
+ result = e_dbus_proxy_invoke_method_sync (proxy,
+ "HelloWorld",
+ e_variant_new ("(s)", "Yo"),
+ -1,
+ NULL,
+ &error);
+ g_assert_error (error, E_DBUS_ERROR, E_DBUS_ERROR_REMOTE_ERROR);
+ g_assert (e_dbus_error_is_remote_error (error));
+ g_assert (e_dbus_error_is_remote_error (error));
+ g_assert (result == NULL);
+ dbus_error_name = e_dbus_error_get_remote_error (error);
+ g_assert_cmpstr (dbus_error_name, ==, "com.example.TestException");
+ g_free (dbus_error_name);
+ g_assert (e_dbus_error_strip_remote_error (error));
+ g_assert_cmpstr (error->message, ==, "Yo is not a proper greeting");
+ g_clear_error (&error);
+
+ /* Check that we get a timeout if the method handling is taking longer than timeout */
+ error = NULL;
+ result = e_dbus_proxy_invoke_method_sync (proxy,
+ "Sleep",
+ e_variant_new ("(i)", 500 /* msec */),
+ 100 /* msec */,
+ NULL,
+ &error);
+ g_assert_error (error, E_DBUS_ERROR, E_DBUS_ERROR_NO_REPLY);
+ g_assert (e_dbus_error_is_remote_error (error));
+ g_assert (result == NULL);
+ g_clear_error (&error);
+
+ /* Check that proxy-default timeouts work. */
+ g_assert_cmpint (e_dbus_proxy_get_default_timeout (proxy), ==, -1);
+
+ /* the default timeout is 25000 msec so this should work */
+ result = e_dbus_proxy_invoke_method_sync (proxy,
+ "Sleep",
+ e_variant_new ("(i)", 500 /* msec */),
+ -1, /* use proxy default (e.g. -1 -> e.g. 25000 msec) */
+ NULL,
+ &error);
+ g_assert_no_error (error);
+ g_assert (result != NULL);
+ g_assert_cmpstr (e_variant_get_type_string (result), ==, "()");
+ e_variant_unref (result);
+
+ /* now set the proxy-default timeout to 250 msec and try the 500 msec call - this should FAIL */
+ e_dbus_proxy_set_default_timeout (proxy, 250);
+ g_assert_cmpint (e_dbus_proxy_get_default_timeout (proxy), ==, 250);
+ result = e_dbus_proxy_invoke_method_sync (proxy,
+ "Sleep",
+ e_variant_new ("(i)", 500 /* msec */),
+ -1, /* use proxy default (e.g. 250 msec) */
+ NULL,
+ &error);
+ g_assert_error (error, E_DBUS_ERROR, E_DBUS_ERROR_NO_REPLY);
+ g_assert (e_dbus_error_is_remote_error (error));
+ g_assert (result == NULL);
+ g_clear_error (&error);
+
+ /* clean up after ourselves */
+ e_dbus_proxy_set_default_timeout (proxy, -1);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+/* Test that the property aspects of EDBusProxy works */
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+test_properties (EDBusConnection *connection,
+ const gchar *name,
+ const gchar *name_owner,
+ EDBusProxy *proxy)
+{
+ GError *error;
+ EVariant *variant;
+ EVariant *variant2;
+ EVariant *result;
+
+ error = NULL;
+
+ /**
+ * Check that we can read cached properties.
+ *
+ * No need to test all properties - EVariant has already been tested
+ */
+ variant = e_dbus_proxy_get_cached_property (proxy, "y", &error);
+ g_assert_no_error (error);
+ g_assert (variant != NULL);
+ g_assert_cmpint (e_variant_get_byte (variant), ==, 1);
+ e_variant_unref (variant);
+ variant = e_dbus_proxy_get_cached_property (proxy, "o", &error);
+ g_assert_no_error (error);
+ g_assert (variant != NULL);
+ g_assert_cmpstr (e_variant_get_string (variant, NULL), ==, "/some/path");
+ e_variant_unref (variant);
+
+ /**
+ * Now ask the service to change a property and check that #EDBusProxy::g-property-changed
+ * is received. Also check that the cache is updated.
+ */
+ variant2 = e_variant_new_byte (42);
+ result = e_dbus_proxy_invoke_method_sync (proxy,
+ "FrobSetProperty",
+ e_variant_new ("(sv)",
+ "y",
+ variant2),
+ -1,
+ NULL,
+ &error);
+ g_assert_no_error (error);
+ g_assert (result != NULL);
+ g_assert_cmpstr (e_variant_get_type_string (result), ==, "()");
+ e_variant_unref (result);
+ _g_assert_signal_received (proxy, "g-properties-changed");
+ variant = e_dbus_proxy_get_cached_property (proxy, "y", &error);
+ g_assert_no_error (error);
+ g_assert (variant != NULL);
+ g_assert_cmpint (e_variant_get_byte (variant), ==, 42);
+ e_variant_unref (variant);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+/* Test that the signal aspects of EDBusProxy works */
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+test_proxy_signals_on_signal (EDBusProxy *proxy,
+ const gchar *sender_name,
+ const gchar *signal_name,
+ EVariant *parameters,
+ gpointer user_data)
+{
+ GString *s = user_data;
+
+ g_assert_cmpstr (signal_name, ==, "TestSignal");
+ g_assert_cmpstr (e_variant_get_type_string (parameters), ==, "(sov)");
+
+ e_variant_print_string (parameters, s, TRUE);
+}
+
+typedef struct
+{
+ GMainLoop *internal_loop;
+ GString *s;
+} TestSignalData;
+
+static void
+test_proxy_signals_on_emit_signal_cb (EDBusProxy *proxy,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ TestSignalData *data = user_data;
+ GError *error;
+ EVariant *result;
+
+ error = NULL;
+ result = e_dbus_proxy_invoke_method_finish (proxy,
+ res,
+ &error);
+ g_assert_no_error (error);
+ g_assert (result != NULL);
+ g_assert_cmpstr (e_variant_get_type_string (result), ==, "()");
+ e_variant_unref (result);
+
+ /* check that the signal was recieved before we got the method result */
+ g_assert (strlen (data->s->str) > 0);
+
+ /* break out of the loop */
+ g_main_loop_quit (data->internal_loop);
+}
+
+static void
+test_signals (EDBusConnection *connection,
+ const gchar *name,
+ const gchar *name_owner,
+ EDBusProxy *proxy)
+{
+ GError *error;
+ GString *s;
+ gulong signal_handler_id;
+ TestSignalData data;
+ EVariant *result;
+
+ error = NULL;
+
+ /**
+ * Ask the service to emit a signal and check that we receive it.
+ *
+ * Note that blocking calls don't block in the mainloop so wait for the signal (which
+ * is dispatched before the method reply)
+ */
+ s = g_string_new (NULL);
+ signal_handler_id = g_signal_connect (proxy,
+ "g-signal",
+ G_CALLBACK (test_proxy_signals_on_signal),
+ s);
+
+ result = e_dbus_proxy_invoke_method_sync (proxy,
+ "EmitSignal",
+ e_variant_new ("(so)",
+ "Accept the next proposition you hear",
+ "/some/path"),
+ -1,
+ NULL,
+ &error);
+ g_assert_no_error (error);
+ g_assert (result != NULL);
+ g_assert_cmpstr (e_variant_get_type_string (result), ==, "()");
+ e_variant_unref (result);
+ /* check that we haven't received the signal just yet */
+ g_assert (strlen (s->str) == 0);
+ /* and now wait for the signal */
+ _g_assert_signal_received (proxy, "g-signal");
+ g_assert_cmpstr (s->str,
+ ==,
+ "(\"Accept the next proposition you hear .. in bed!\", objectpath \"/some/path/in/bed\", <\"a variant\">)");
+ g_signal_handler_disconnect (proxy, signal_handler_id);
+ g_string_free (s, TRUE);
+
+ /**
+ * Now do this async to check the signal is received before the method returns.
+ */
+ s = g_string_new (NULL);
+ data.internal_loop = g_main_loop_new (NULL, FALSE);
+ data.s = s;
+ signal_handler_id = g_signal_connect (proxy,
+ "g-signal",
+ G_CALLBACK (test_proxy_signals_on_signal),
+ s);
+ e_dbus_proxy_invoke_method (proxy,
+ "EmitSignal",
+ e_variant_new ("(so)",
+ "You will make a great programmer",
+ "/some/other/path"),
+ -1,
+ NULL,
+ (GAsyncReadyCallback) test_proxy_signals_on_emit_signal_cb,
+ &data);
+ g_main_loop_run (data.internal_loop);
+ g_main_loop_unref (data.internal_loop);
+ g_assert_cmpstr (s->str,
+ ==,
+ "(\"You will make a great programmer .. in bed!\", objectpath \"/some/other/path/in/bed\", <\"a variant\">)");
+ g_signal_handler_disconnect (proxy, signal_handler_id);
+ g_string_free (s, TRUE);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+on_proxy_appeared (EDBusConnection *connection,
+ const gchar *name,
+ const gchar *name_owner,
+ EDBusProxy *proxy,
+ gpointer user_data)
+{
+ test_methods (connection, name, name_owner, proxy);
+ test_properties (connection, name, name_owner, proxy);
+ test_signals (connection, name, name_owner, proxy);
+
+ g_main_loop_quit (loop);
+}
+
+static void
+on_proxy_vanished (EDBusConnection *connection,
+ const gchar *name,
+ gpointer user_data)
+{
+}
+
+static void
+test_proxy (void)
+{
+ guint watcher_id;
+
+ session_bus_up ();
+
+ /* TODO: wait a bit for the bus to come up.. ideally session_bus_up() won't return
+ * until one can connect to the bus but that's not how things work right now
+ */
+ usleep (500 * 1000);
+
+ watcher_id = e_bus_watch_proxy (G_BUS_TYPE_SESSION,
+ "com.example.TestService",
+ "/com/example/TestObject",
+ "com.example.Frob",
+ E_TYPE_DBUS_PROXY,
+ E_DBUS_PROXY_FLAGS_NONE,
+ on_proxy_appeared,
+ on_proxy_vanished,
+ NULL,
+ NULL);
+
+ /* this is safe; testserver will exit once the bus goes away */
+ g_assert (g_spawn_command_line_async ("./testserver.py", NULL));
+
+ g_main_loop_run (loop);
+
+ e_bus_unwatch_proxy (watcher_id);
+
+ /* tear down bus */
+ 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/proxy", test_proxy);
+ return g_test_run();
+}
diff --git a/edbus/tests/sessionbus.c b/edbus/tests/sessionbus.c
new file mode 100644
index 0000000..3dde5d2
--- /dev/null
+++ b/edbus/tests/sessionbus.c
@@ -0,0 +1,342 @@
+/* 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 <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <signal.h>
+#include <stdio.h>
+
+#include "sessionbus.h"
+
+/* ---------------------------------------------------------------------------------------------------- */
+/* Utilities for bringing up and tearing down session message bus instances */
+
+static void
+watch_parent (gint fd)
+{
+ GPollFD fds[1];
+ gint num_events;
+ gchar buf[512];
+ gint bytes_read;
+ GArray *buses_to_kill_array;
+
+ fds[0].fd = fd;
+ fds[0].events = G_IO_HUP | G_IO_IN;
+ fds[0].revents = 0;
+
+ buses_to_kill_array = g_array_new (FALSE, TRUE, sizeof (guint));
+
+ do
+ {
+ guint pid;
+ guint n;
+
+ num_events = g_poll (fds, 1, -1);
+ if (num_events == 0)
+ continue;
+
+ if (fds[0].revents == G_IO_HUP)
+ {
+ for (n = 0; n < buses_to_kill_array->len; n++)
+ {
+ pid = g_array_index (buses_to_kill_array, guint, n);
+ g_print ("cleaning up bus with pid %d\n", pid);
+ kill (pid, SIGTERM);
+ }
+ g_array_free (buses_to_kill_array, TRUE);
+ exit (0);
+ }
+
+ //g_debug ("data from parent");
+
+ memset (buf, '\0', sizeof buf);
+ again:
+ bytes_read = read (fds[0].fd, buf, sizeof buf);
+ if (bytes_read < 0 && (errno == EAGAIN || errno == EINTR))
+ goto again;
+
+ if (sscanf (buf, "add %d\n", &pid) == 1)
+ {
+ g_array_append_val (buses_to_kill_array, pid);
+ }
+ else if (sscanf (buf, "remove %d\n", &pid) == 1)
+ {
+ for (n = 0; n < buses_to_kill_array->len; n++)
+ {
+ if (g_array_index (buses_to_kill_array, guint, n) == pid)
+ {
+ g_array_remove_index (buses_to_kill_array, n);
+ pid = 0;
+ break;
+ }
+ }
+ if (pid != 0)
+ {
+ g_warning ("unknown pid %d to remove", pid);
+ }
+ }
+ else
+ {
+ g_warning ("unknown command from parent '%s'", buf);
+ }
+ }
+ while (TRUE);
+
+}
+
+static GHashTable *session_bus_address_to_pid = NULL;
+static gint pipe_fds[2];
+
+const gchar *
+session_bus_up_with_address (const gchar *given_address)
+{
+ gchar *address;
+ int stdout_fd;
+ GError *error;
+ const gchar *argv[] = {"dbus-daemon", "--print-address", "--config-file=foo", NULL};
+ GPid pid;
+ gchar buf[512];
+ ssize_t bytes_read;
+ gchar *config_file_name;
+ gint config_file_fd;
+ GString *config_file_contents;
+
+ address = NULL;
+ error = NULL;
+ config_file_name = NULL;
+ config_file_fd = -1;
+ argv[2] = NULL;
+
+ config_file_fd = g_file_open_tmp ("g-dbus-tests-XXXXXX",
+ &config_file_name,
+ &error);
+ if (config_file_fd < 0)
+ {
+ g_warning ("Error creating temporary config file: %s", error->message);
+ g_error_free (error);
+ goto out;
+ }
+
+ config_file_contents = g_string_new (NULL);
+ g_string_append (config_file_contents, "<busconfig>\n");
+ g_string_append (config_file_contents, " <type>session</type>\n");
+ g_string_append_printf (config_file_contents, " <listen>%s</listen>\n", given_address);
+ g_string_append (config_file_contents,
+ " <policy context=\"default\">\n"
+ " <!-- Allow everything to be sent -->\n"
+ " <allow send_destination=\"*\" eavesdrop=\"true\"/>\n"
+ " <!-- Allow everything to be received -->\n"
+ " <allow eavesdrop=\"true\"/>\n"
+ " <!-- Allow anyone to own anything -->\n"
+ " <allow own=\"*\"/>\n"
+ " </policy>\n");
+ g_string_append (config_file_contents, "</busconfig>\n");
+
+ if (write (config_file_fd, config_file_contents->str, config_file_contents->len) != (gssize) config_file_contents->len)
+ {
+ g_warning ("Error writing %d bytes to config file: %m", (gint) config_file_contents->len);
+ g_string_free (config_file_contents, TRUE);
+ goto out;
+ }
+ g_string_free (config_file_contents, TRUE);
+
+ argv[2] = g_strdup_printf ("--config-file=%s", config_file_name);
+
+ if (session_bus_address_to_pid == NULL)
+ {
+ /* keep a mapping from session bus address to the pid */
+ session_bus_address_to_pid = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+
+ /* fork a child to clean up session buses when we are killed */
+ if (pipe (pipe_fds) != 0)
+ {
+ g_warning ("pipe() failed: %m");
+ g_assert_not_reached ();
+ }
+ switch (fork ())
+ {
+ case -1:
+ g_warning ("fork() failed: %m");
+ g_assert_not_reached ();
+ break;
+
+ case 0:
+ /* child */
+ close (pipe_fds[1]);
+ watch_parent (pipe_fds[0]);
+ break;
+
+ default:
+ /* parent */
+ close (pipe_fds[0]);
+ break;
+ }
+
+ //atexit (cleanup_session_buses);
+ /* TODO: need to handle the cases where we crash */
+ }
+ else
+ {
+ /* check if we already have a bus running for this address */
+ if (g_hash_table_lookup (session_bus_address_to_pid, given_address) != NULL)
+ {
+ g_warning ("Already have a bus instance for the given address %s", given_address);
+ goto out;
+ }
+ }
+
+ if (!g_spawn_async_with_pipes (NULL,
+ (char **)argv,
+ NULL,
+ G_SPAWN_SEARCH_PATH,
+ NULL,
+ NULL,
+ &pid,
+ NULL,
+ &stdout_fd,
+ NULL,
+ &error))
+ {
+ g_warning ("Error spawning dbus-daemon: %s", error->message);
+ g_error_free (error);
+ goto out;
+ }
+
+ memset (buf, '\0', sizeof buf);
+ again:
+ bytes_read = read (stdout_fd, buf, sizeof buf);
+ if (bytes_read < 0 && (errno == EAGAIN || errno == EINTR))
+ goto again;
+ close (stdout_fd);
+
+ if (bytes_read == 0 || bytes_read == sizeof buf)
+ {
+ g_warning ("Error reading address from dbus daemon, %d bytes read", (gint) bytes_read);
+ kill (SIGTERM, pid);
+ goto out;
+ }
+
+ address = g_strdup (buf);
+ g_strstrip (address);
+
+ /* write the pid to the child so it can kill it when we die */
+ g_snprintf (buf, sizeof buf, "add %d\n", (guint) pid);
+ write (pipe_fds[1], buf, strlen (buf));
+
+ /* start dbus-monitor */
+ if (g_getenv ("E_DBUS_MONITOR") != NULL)
+ {
+ g_spawn_command_line_async ("dbus-monitor --session", NULL);
+ usleep (500 * 1000);
+ }
+
+ g_hash_table_insert (session_bus_address_to_pid, address, GUINT_TO_POINTER (pid));
+
+ out:
+ if (config_file_fd > 0)
+ {
+ if (close (config_file_fd) != 0)
+ {
+ g_warning ("Error closing fd for config file %s: %m", config_file_name);
+ }
+ g_assert (config_file_name != NULL);
+ if (unlink (config_file_name) != 0)
+ {
+ g_warning ("Error unlinking config file %s: %m", config_file_name);
+ }
+ }
+ g_free ((char *)argv[2]);
+ g_free (config_file_name);
+ return address;
+}
+
+void
+session_bus_down_with_address (const gchar *address)
+{
+ gpointer value;
+ GPid pid;
+ gchar buf[512];
+
+ g_assert (address != NULL);
+ g_assert (session_bus_address_to_pid != NULL);
+
+ value = g_hash_table_lookup (session_bus_address_to_pid, address);
+ g_assert (value != NULL);
+
+ pid = GPOINTER_TO_UINT (g_hash_table_lookup (session_bus_address_to_pid, address));
+
+ kill (pid, SIGTERM);
+
+ /* write the pid to the child so it won't kill it when we die */
+ g_snprintf (buf, sizeof buf, "remove %d\n", (guint) pid);
+ write (pipe_fds[1], buf, strlen (buf));
+
+ g_hash_table_remove (session_bus_address_to_pid, address);
+}
+
+static gchar *temporary_address = NULL;
+static gchar *temporary_address_used_by_bus = NULL;
+
+const gchar *
+session_bus_get_temporary_address (void)
+{
+ if (temporary_address == NULL)
+ {
+ /* TODO: maybe use a more random name etc etc */
+ temporary_address = g_strdup_printf ("unix:path=/tmp/g-dbus-tests-pid-%d", getpid ());
+ }
+
+ return temporary_address;
+}
+
+const gchar *
+session_bus_up (void)
+{
+ if (temporary_address_used_by_bus != NULL)
+ {
+ g_warning ("There is already a session bus up");
+ goto out;
+ }
+
+ temporary_address_used_by_bus = g_strdup (session_bus_up_with_address (session_bus_get_temporary_address ()));
+
+ out:
+ return temporary_address_used_by_bus;
+}
+
+void
+session_bus_down (void)
+{
+ if (temporary_address_used_by_bus == NULL)
+ {
+ g_warning ("There is not a session bus up");
+ }
+ else
+ {
+ session_bus_down_with_address (temporary_address_used_by_bus);
+ g_free (temporary_address_used_by_bus);
+ temporary_address_used_by_bus = NULL;
+ }
+}
diff --git a/edbus/tests/sessionbus.h b/edbus/tests/sessionbus.h
new file mode 100644
index 0000000..e9c4e73
--- /dev/null
+++ b/edbus/tests/sessionbus.h
@@ -0,0 +1,38 @@
+/* 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>
+ */
+
+#ifndef __SESSION_BUS_H__
+#define __SESSION_BUS_H__
+
+#include <edbus/edbus.h>
+
+G_BEGIN_DECLS
+
+const gchar *session_bus_up_with_address (const gchar *given_address);
+void session_bus_down_with_address (const gchar *address);
+const gchar *session_bus_get_temporary_address (void);
+const gchar *session_bus_up (void);
+void session_bus_down (void);
+
+G_END_DECLS
+
+#endif /* __SESSION_BUS_H__ */
diff --git a/edbus/tests/tests.c b/edbus/tests/tests.c
new file mode 100644
index 0000000..03c1c8e
--- /dev/null
+++ b/edbus/tests/tests.c
@@ -0,0 +1,131 @@
+/* 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 <edbus/edbus.h>
+#include <unistd.h>
+
+#include "tests.h"
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+typedef struct
+{
+ GMainLoop *loop;
+ gboolean timed_out;
+} PropertyNotifyData;
+
+static void
+on_property_notify (GObject *object,
+ GParamSpec *pspec,
+ gpointer user_data)
+{
+ PropertyNotifyData *data = user_data;
+ g_main_loop_quit (data->loop);
+}
+
+static gboolean
+on_property_notify_timeout (gpointer user_data)
+{
+ PropertyNotifyData *data = user_data;
+ data->timed_out = TRUE;
+ g_main_loop_quit (data->loop);
+ return TRUE;
+}
+
+gboolean
+_g_assert_property_notify_run (gpointer object,
+ const gchar *property_name)
+{
+ gchar *s;
+ gulong handler_id;
+ guint timeout_id;
+ PropertyNotifyData data;
+
+ data.loop = g_main_loop_new (NULL, FALSE);
+ data.timed_out = FALSE;
+ s = g_strdup_printf ("notify::%s", property_name);
+ handler_id = g_signal_connect (object,
+ s,
+ G_CALLBACK (on_property_notify),
+ &data);
+ g_free (s);
+ timeout_id = g_timeout_add (5 * 1000,
+ on_property_notify_timeout,
+ &data);
+ g_main_loop_run (data.loop);
+ g_signal_handler_disconnect (object, handler_id);
+ g_source_remove (timeout_id);
+ g_main_loop_unref (data.loop);
+
+ return data.timed_out;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+typedef struct
+{
+ GMainLoop *loop;
+ gboolean timed_out;
+} SignalReceivedData;
+
+static void
+on_signal_received (gpointer user_data)
+{
+ SignalReceivedData *data = user_data;
+ g_main_loop_quit (data->loop);
+}
+
+static gboolean
+on_signal_received_timeout (gpointer user_data)
+{
+ SignalReceivedData *data = user_data;
+ data->timed_out = TRUE;
+ g_main_loop_quit (data->loop);
+ return TRUE;
+}
+
+gboolean
+_g_assert_signal_received_run (gpointer object,
+ const gchar *signal_name)
+{
+ gulong handler_id;
+ guint timeout_id;
+ SignalReceivedData data;
+
+ data.loop = g_main_loop_new (NULL, FALSE);
+ data.timed_out = FALSE;
+ handler_id = g_signal_connect_swapped (object,
+ signal_name,
+ G_CALLBACK (on_signal_received),
+ &data);
+ timeout_id = g_timeout_add (5 * 1000,
+ on_signal_received_timeout,
+ &data);
+ g_main_loop_run (data.loop);
+ g_signal_handler_disconnect (object, handler_id);
+ g_source_remove (timeout_id);
+ g_main_loop_unref (data.loop);
+
+ return data.timed_out;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
diff --git a/edbus/tests/tests.h b/edbus/tests/tests.h
new file mode 100644
index 0000000..9ade394
--- /dev/null
+++ b/edbus/tests/tests.h
@@ -0,0 +1,117 @@
+/* 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>
+ */
+
+#ifndef __TESTS_H__
+#define __TESTS_H__
+
+#include <edbus/edbus.h>
+#include "sessionbus.h"
+
+G_BEGIN_DECLS
+
+/* TODO: clean up and move to gtestutils.c
+ *
+ * This is needed because libdbus-1 does not give predictable error messages - e.g. you
+ * get a different error message on connecting to a bus if the socket file is there vs
+ * if the socket file is missing.
+ */
+
+#define _g_assert_error_domain(err, dom) do { if (!err || (err)->domain != dom) \
+ g_assertion_message_error (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \
+ #err, err, dom, -1); } while (0)
+
+#define _g_assert_property_notify(object, property_name) \
+ do \
+ { \
+ if (!G_IS_OBJECT (object)) \
+ { \
+ g_assertion_message (G_LOG_DOMAIN, \
+ __FILE__, \
+ __LINE__, \
+ G_STRFUNC, \
+ "Not a GObject instance"); \
+ } \
+ if (g_object_class_find_property (G_OBJECT_GET_CLASS (object), \
+ property_name) == NULL) \
+ { \
+ g_assertion_message (G_LOG_DOMAIN, \
+ __FILE__, \
+ __LINE__, \
+ G_STRFUNC, \
+ "Property " property_name " does not " \
+ "exist on object"); \
+ } \
+ if (_g_assert_property_notify_run (object, property_name)) \
+ { \
+ g_assertion_message (G_LOG_DOMAIN, \
+ __FILE__, \
+ __LINE__, \
+ G_STRFUNC, \
+ "Timed out waiting for notification " \
+ "on property " property_name); \
+ } \
+ } \
+ while (FALSE)
+
+#define _g_assert_signal_received(object, signal_name) \
+ do \
+ { \
+ if (!G_IS_OBJECT (object)) \
+ { \
+ g_assertion_message (G_LOG_DOMAIN, \
+ __FILE__, \
+ __LINE__, \
+ G_STRFUNC, \
+ "Not a GObject instance"); \
+ } \
+ if (g_signal_lookup (signal_name, \
+ G_TYPE_FROM_INSTANCE (object)) == 0) \
+ { \
+ g_assertion_message (G_LOG_DOMAIN, \
+ __FILE__, \
+ __LINE__, \
+ G_STRFUNC, \
+ "Signal " signal_name " does not " \
+ "exist on object"); \
+ } \
+ if (_g_assert_signal_received_run (object, signal_name)) \
+ { \
+ g_assertion_message (G_LOG_DOMAIN, \
+ __FILE__, \
+ __LINE__, \
+ G_STRFUNC, \
+ "Timed out waiting for signal " \
+ signal_name); \
+ } \
+ } \
+ while (FALSE)
+
+gboolean _g_assert_property_notify_run (gpointer object,
+ const gchar *property_name);
+
+
+gboolean _g_assert_signal_received_run (gpointer object,
+ const gchar *signal_name);
+
+G_END_DECLS
+
+#endif /* __TESTS_H__ */
diff --git a/edbus/tests/threading.c b/edbus/tests/threading.c
new file mode 100644
index 0000000..82d5d3f
--- /dev/null
+++ b/edbus/tests/threading.c
@@ -0,0 +1,529 @@
+/* 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 <edbus/edbus.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "tests.h"
+
+/* all tests rely on a global connection */
+static EDBusConnection *c = NULL;
+
+/* all tests rely on a shared mainloop */
+static GMainLoop *loop = NULL;
+
+/* ---------------------------------------------------------------------------------------------------- */
+/* Ensure that signal and method replies are delivered in the right thread */
+/* ---------------------------------------------------------------------------------------------------- */
+
+typedef struct {
+ GThread *thread;
+ GMainLoop *thread_loop;
+ guint signal_count;
+} DeliveryData;
+
+static void
+msg_cb_expect_success (EDBusConnection *connection,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ DeliveryData *data = user_data;
+ GError *error;
+ EVariant *result;
+
+ error = NULL;
+ result = e_dbus_connection_invoke_method_finish (connection,
+ res,
+ &error);
+ g_assert_no_error (error);
+ g_assert (result != NULL);
+ e_variant_unref (result);
+
+ g_assert (g_thread_self () == data->thread);
+
+ g_main_loop_quit (data->thread_loop);
+}
+
+static void
+msg_cb_expect_error_cancelled (EDBusConnection *connection,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ DeliveryData *data = user_data;
+ GError *error;
+ EVariant *result;
+
+ error = NULL;
+ result = e_dbus_connection_invoke_method_finish (connection,
+ res,
+ &error);
+ g_assert_error (error, E_DBUS_ERROR, E_DBUS_ERROR_CANCELLED);
+ g_assert (!e_dbus_error_is_remote_error (error));
+ g_error_free (error);
+ g_assert (result == NULL);
+
+ g_assert (g_thread_self () == data->thread);
+
+ g_main_loop_quit (data->thread_loop);
+}
+
+static void
+signal_handler (EDBusConnection *connection,
+ const gchar *sender_name,
+ const gchar *object_path,
+ const gchar *interface_name,
+ const gchar *signal_name,
+ EVariant *parameters,
+ gpointer user_data)
+{
+ DeliveryData *data = user_data;
+
+ g_assert (g_thread_self () == data->thread);
+
+ data->signal_count++;
+
+ g_main_loop_quit (data->thread_loop);
+}
+
+static gpointer
+test_delivery_in_thread_func (gpointer _data)
+{
+ GMainLoop *thread_loop;
+ GMainContext *thread_context;
+ DeliveryData data;
+ GCancellable *ca;
+ guint subscription_id;
+ EDBusConnection *priv_c;
+ GError *error;
+
+ error = NULL;
+
+ thread_context = g_main_context_new ();
+ thread_loop = g_main_loop_new (thread_context, FALSE);
+ g_main_context_push_thread_default (thread_context);
+
+ data.thread = g_thread_self ();
+ data.thread_loop = thread_loop;
+ data.signal_count = 0;
+
+ /* ---------------------------------------------------------------------------------------------------- */
+
+ /**
+ * Check that we get a reply to the GetId() method call.
+ */
+ e_dbus_connection_invoke_method (c,
+ "org.freedesktop.DBus", /* bus_name */
+ "/org/freedesktop/DBus", /* object path */
+ "org.freedesktop.DBus", /* interface name */
+ "GetId", /* method name */
+ NULL,
+ -1,
+ NULL,
+ (GAsyncReadyCallback) msg_cb_expect_success,
+ &data);
+ g_main_loop_run (thread_loop);
+
+ /**
+ * Check that we never actually send a message if the GCancellable is already cancelled - i.e.
+ * we should get #E_DBUS_ERROR_CANCELLED instead of #E_DBUS_ERROR_FAILED even when the actual
+ * connection is not up.
+ */
+ ca = g_cancellable_new ();
+ g_cancellable_cancel (ca);
+ e_dbus_connection_invoke_method (c,
+ "org.freedesktop.DBus", /* bus_name */
+ "/org/freedesktop/DBus", /* object path */
+ "org.freedesktop.DBus", /* interface name */
+ "GetId", /* method name */
+ NULL,
+ -1,
+ ca,
+ (GAsyncReadyCallback) msg_cb_expect_error_cancelled,
+ &data);
+ g_main_loop_run (thread_loop);
+ g_object_unref (ca);
+
+ /**
+ * Check that cancellation works when the message is already in flight.
+ */
+ ca = g_cancellable_new ();
+ e_dbus_connection_invoke_method (c,
+ "org.freedesktop.DBus", /* bus_name */
+ "/org/freedesktop/DBus", /* object path */
+ "org.freedesktop.DBus", /* interface name */
+ "GetId", /* method name */
+ NULL,
+ -1,
+ ca,
+ (GAsyncReadyCallback) msg_cb_expect_error_cancelled,
+ &data);
+ g_cancellable_cancel (ca);
+ g_main_loop_run (thread_loop);
+ g_object_unref (ca);
+
+ /**
+ * Check that signals are delivered to the correct thread.
+ *
+ * First we subscribe to the signal, the we create a a private connection. This should
+ * cause a NameOwnerChanged message. Then we tear it down. This should cause another
+ * NameOwnerChanged message.
+ */
+ subscription_id = e_dbus_connection_signal_subscribe (c,
+ "org.freedesktop.DBus", /* sender */
+ "org.freedesktop.DBus", /* interface */
+ "NameOwnerChanged", /* member */
+ "/org/freedesktop/DBus", /* path */
+ NULL,
+ signal_handler,
+ &data,
+ NULL);
+ g_assert (subscription_id != 0);
+ g_assert (data.signal_count == 0);
+
+ priv_c = e_dbus_connection_bus_get_private_sync (G_BUS_TYPE_SESSION, NULL, &error);
+ g_assert_no_error (error);
+ g_assert (priv_c != NULL);
+
+ g_main_loop_run (thread_loop);
+ g_assert (data.signal_count == 1);
+
+ g_object_unref (priv_c);
+
+ g_main_loop_run (thread_loop);
+ g_assert (data.signal_count == 2);
+
+ e_dbus_connection_signal_unsubscribe (c, subscription_id);
+
+ /* ---------------------------------------------------------------------------------------------------- */
+
+ g_main_context_pop_thread_default (thread_context);
+ g_main_loop_unref (thread_loop);
+ g_main_context_unref (thread_context);
+
+ g_main_loop_quit (loop);
+
+ return NULL;
+}
+
+static void
+test_delivery_in_thread (void)
+{
+ GError *error;
+ GThread *thread;
+
+ error = NULL;
+ thread = g_thread_create (test_delivery_in_thread_func,
+ NULL,
+ TRUE,
+ &error);
+ g_assert_no_error (error);
+ g_assert (thread != NULL);
+
+ /* run the event loop - it is needed to dispatch D-Bus messages */
+ g_main_loop_run (loop);
+
+ g_thread_join (thread);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+typedef struct {
+ EDBusProxy *proxy;
+ gint msec;
+ guint num;
+ gboolean async;
+
+ GMainLoop *thread_loop;
+ GThread *thread;
+
+ gboolean done;
+} SyncThreadData;
+
+static void
+sleep_cb (EDBusProxy *proxy,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ SyncThreadData *data = user_data;
+ GError *error;
+ EVariant *result;
+
+ error = NULL;
+ result = e_dbus_proxy_invoke_method_finish (proxy,
+ res,
+ &error);
+ g_assert_no_error (error);
+ g_assert (result != NULL);
+ g_assert_cmpstr (e_variant_get_type_string (result), ==, "()");
+ e_variant_unref (result);
+
+ g_assert (data->thread == g_thread_self ());
+
+ g_main_loop_quit (data->thread_loop);
+
+ //g_debug ("async cb (%p)", g_thread_self ());
+}
+
+static gpointer
+test_sleep_in_thread_func (gpointer _data)
+{
+ SyncThreadData *data = _data;
+ GMainContext *thread_context;
+ guint n;
+
+ thread_context = g_main_context_new ();
+ data->thread_loop = g_main_loop_new (thread_context, FALSE);
+ g_main_context_push_thread_default (thread_context);
+
+ data->thread = g_thread_self ();
+
+ for (n = 0; n < data->num; n++)
+ {
+ if (data->async)
+ {
+ //g_debug ("invoking async (%p)", g_thread_self ());
+ e_dbus_proxy_invoke_method (data->proxy,
+ "Sleep",
+ e_variant_new ("(i)", data->msec),
+ -1,
+ NULL,
+ (GAsyncReadyCallback) sleep_cb,
+ data);
+ g_main_loop_run (data->thread_loop);
+ //g_debug ("done invoking async (%p)", g_thread_self ());
+ }
+ else
+ {
+ GError *error;
+ EVariant *result;
+
+ error = NULL;
+ //g_debug ("invoking sync (%p)", g_thread_self ());
+ result = e_dbus_proxy_invoke_method_sync (data->proxy,
+ "Sleep",
+ e_variant_new ("(i)", data->msec),
+ -1,
+ NULL,
+ &error);
+ //g_debug ("done invoking sync (%p)", g_thread_self ());
+ g_assert_no_error (error);
+ g_assert (result != NULL);
+ g_assert_cmpstr (e_variant_get_type_string (result), ==, "()");
+ e_variant_unref (result);
+ }
+ }
+
+ g_main_context_pop_thread_default (thread_context);
+ g_main_loop_unref (data->thread_loop);
+ g_main_context_unref (thread_context);
+
+ data->done = TRUE;
+ g_main_loop_quit (loop);
+
+ return NULL;
+}
+
+static void
+on_proxy_appeared (EDBusConnection *connection,
+ const gchar *name,
+ const gchar *name_owner,
+ EDBusProxy *proxy,
+ gpointer user_data)
+{
+ guint n;
+
+ /**
+ * Check that multiple threads can do calls without interferring with
+ * each other. We do this by creating three threads that call the
+ * Sleep() method on the server (which handles it asynchronously, e.g.
+ * it won't block other requests) with different sleep durations and
+ * a number of times. We do this so each set of calls add up to 4000
+ * milliseconds.
+ *
+ * We run this test twice - first with async calls in each thread, then
+ * again with sync calls
+ */
+
+ /* TODO: The sync calls are broken - this is likely a libdbus-1 bug. Need
+ * to investigate. So only do the async calls for now.
+ */
+ for (n = 0; n < /* 2 */ 1; n++)
+ {
+ gboolean do_async;
+ GThread *thread1;
+ GThread *thread2;
+ GThread *thread3;
+ SyncThreadData data1;
+ SyncThreadData data2;
+ SyncThreadData data3;
+ GError *error;
+ GTimeVal start_time;
+ GTimeVal end_time;
+ guint elapsed_msec;
+
+ error = NULL;
+ do_async = (n == 0);
+
+ g_get_current_time (&start_time);
+
+ data1.proxy = proxy;
+ data1.msec = 40;
+ data1.num = 100;
+ data1.async = do_async;
+ data1.done = FALSE;
+ thread1 = g_thread_create (test_sleep_in_thread_func,
+ &data1,
+ TRUE,
+ &error);
+ g_assert_no_error (error);
+ g_assert (thread1 != NULL);
+
+ data2.proxy = proxy;
+ data2.msec = 20;
+ data2.num = 200;
+ data2.async = do_async;
+ data2.done = FALSE;
+ thread2 = g_thread_create (test_sleep_in_thread_func,
+ &data2,
+ TRUE,
+ &error);
+ g_assert_no_error (error);
+ g_assert (thread2 != NULL);
+
+ data3.proxy = proxy;
+ data3.msec = 100;
+ data3.num = 40;
+ data3.async = do_async;
+ data3.done = FALSE;
+ thread3 = g_thread_create (test_sleep_in_thread_func,
+ &data3,
+ TRUE,
+ &error);
+ g_assert_no_error (error);
+ g_assert (thread3 != NULL);
+
+ /* we handle messages in the main loop - threads will quit it when they are done */
+ while (!(data1.done && data2.done && data3.done))
+ g_main_loop_run (loop);
+
+ g_thread_join (thread1);
+ g_thread_join (thread2);
+ g_thread_join (thread3);
+
+ g_get_current_time (&end_time);
+
+ elapsed_msec = ((end_time.tv_sec * G_USEC_PER_SEC + end_time.tv_usec) -
+ (start_time.tv_sec * G_USEC_PER_SEC + start_time.tv_usec)) / 1000;
+
+ //g_debug ("Elapsed time for %s = %d msec", n == 0 ? "async" : "sync", elapsed_msec);
+
+ /* elapsed_msec should be 4000 msec + change for overhead */
+ g_assert_cmpint (elapsed_msec, >=, 4000);
+ g_assert_cmpint (elapsed_msec, <, 5000);
+ }
+
+ g_main_loop_quit (loop);
+}
+
+static void
+on_proxy_vanished (EDBusConnection *connection,
+ const gchar *name,
+ gpointer user_data)
+{
+}
+
+static void
+test_method_calls_in_thread (void)
+{
+ guint watcher_id;
+
+ watcher_id = e_bus_watch_proxy (G_BUS_TYPE_SESSION,
+ "com.example.TestService",
+ "/com/example/TestObject",
+ "com.example.Frob",
+ E_TYPE_DBUS_PROXY,
+ E_DBUS_PROXY_FLAGS_NONE,
+ on_proxy_appeared,
+ on_proxy_vanished,
+ NULL,
+ NULL);
+
+ g_main_loop_run (loop);
+
+ e_bus_unwatch_proxy (watcher_id);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+int
+main (int argc,
+ char *argv[])
+{
+ GError *error;
+ gint ret;
+
+ g_type_init ();
+ g_thread_init (NULL);
+ e_dbus_threads_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);
+
+ session_bus_up ();
+
+ /* TODO: wait a bit for the bus to come up.. ideally session_bus_up() won't return
+ * until one can connect to the bus but that's not how things work right now
+ */
+ usleep (500 * 1000);
+
+ /* this is safe; testserver will exit once the bus goes away */
+ g_assert (g_spawn_command_line_async ("./testserver.py", NULL));
+
+ /* wait for the service to come up */
+ usleep (500 * 1000);
+
+ /* Create the connection in the main thread */
+ error = NULL;
+ c = e_dbus_connection_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
+ g_assert_no_error (error);
+ g_assert (c != NULL);
+
+ g_test_add_func ("/gdbus/delivery-in-thread", test_delivery_in_thread);
+ g_test_add_func ("/gdbus/method-calls-in-thread", test_method_calls_in_thread);
+
+ ret = g_test_run();
+
+ g_object_unref (c);
+
+ /* tear down bus */
+ session_bus_down ();
+
+ return ret;
+}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]