[glib/gdbus] First stab at proxy for C object mapping



commit 88d1c84e0e737c545da886d344eaeafec7e0be3b
Author: David Zeuthen <davidz redhat com>
Date:   Thu Apr 23 17:11:36 2009 -0400

    First stab at proxy for C object mapping
    
    Right now we only handle primitive types but it should be
    straightforward to add support for GHashTable, GPtrArray and GArray
    for containers (need to register GPtrArray and GArray with the type
    system though).
    
    For D-Bus variants, the solution is probably something like
    EggDBusVariant.
    
    Until the "write a server" bits are there, we're going to use a Python
    server for testing (repurposing the test service from early EggDBus
    development).
---
 docs/reference/gdbus/gdbus-docs.xml     |    1 +
 docs/reference/gdbus/gdbus-sections.txt |   25 +
 docs/reference/gdbus/gdbus.types        |    1 +
 gdbus/Makefile.am                       |    4 +-
 gdbus/example-own-name.c                |    2 +
 gdbus/gdbus.h                           |    1 +
 gdbus/gdbus.symbols                     |   16 +
 gdbus/gdbusconnection.c                 |  127 ++++-
 gdbus/gdbusconnection.h                 |    6 +-
 gdbus/gdbusproxy.c                      |  953 +++++++++++++++++++++++++++++++
 gdbus/gdbusproxy.h                      |  118 ++++
 gdbus/gdbustypes.h                      |    1 +
 gdbus/tests/Makefile.am                 |   12 +-
 gdbus/tests/connection.c                |  925 +------------------------------
 gdbus/tests/names.c                     |  653 +++++++++++++++++++++
 gdbus/tests/proxy.c                     |  186 ++++++
 gdbus/tests/sessionbus.c                |  335 +++++++++++
 gdbus/tests/sessionbus.h                |   38 ++
 gdbus/tests/tests.h                     |   43 ++
 gdbus/tests/testserver.py               |  420 ++++++++++++++
 20 files changed, 2931 insertions(+), 936 deletions(-)

diff --git a/docs/reference/gdbus/gdbus-docs.xml b/docs/reference/gdbus/gdbus-docs.xml
index 2192a12..0d31e06 100644
--- a/docs/reference/gdbus/gdbus-docs.xml
+++ b/docs/reference/gdbus/gdbus-docs.xml
@@ -31,6 +31,7 @@
     </chapter>
     <chapter id="cmapping">
         <title>C Object Mapping</title>
+        <xi:include href="xml/gdbusproxy.xml"/>
     </chapter>
   </part>
 
diff --git a/docs/reference/gdbus/gdbus-sections.txt b/docs/reference/gdbus/gdbus-sections.txt
index 3ca5edf..18fbc4b 100644
--- a/docs/reference/gdbus/gdbus-sections.txt
+++ b/docs/reference/gdbus/gdbus-sections.txt
@@ -64,6 +64,8 @@ g_dbus_connection_get_dbus_1_connection
 g_dbus_connection_send_dbus_1_message
 g_dbus_connection_send_dbus_1_message_with_reply
 g_dbus_connection_send_dbus_1_message_with_reply_finish
+g_dbus_connection_send_dbus_1_message_block
+g_dbus_connection_send_dbus_1_message_cancel
 <SUBSECTION Standard>
 G_DBUS_CONNECTION
 G_IS_DBUS_CONNECTION
@@ -110,3 +112,26 @@ g_bus_own_name
 g_bus_unown_name
 </SECTION>
 
+<SECTION>
+<FILE>gdbusproxy</FILE>
+<TITLE>GDBusProxy</TITLE>
+GDBusProxy
+GDBusProxyClass
+g_dbus_proxy_new
+g_dbus_proxy_get_connection
+g_dbus_proxy_get_interface_name
+g_dbus_proxy_get_name
+g_dbus_proxy_get_object_path
+g_dbus_proxy_invoke_method
+g_dbus_proxy_invoke_method_finish
+g_dbus_proxy_invoke_method_sync
+<SUBSECTION Standard>
+G_DBUS_PROXY
+G_IS_DBUS_PROXY
+G_TYPE_DBUS_PROXY
+g_dbus_proxy_get_type
+G_DBUS_PROXY_CLASS
+G_IS_DBUS_PROXY_CLASS
+G_DBUS_PROXY_GET_CLASS
+</SECTION>
+
diff --git a/docs/reference/gdbus/gdbus.types b/docs/reference/gdbus/gdbus.types
index 5052bf9..d4f00a6 100644
--- a/docs/reference/gdbus/gdbus.types
+++ b/docs/reference/gdbus/gdbus.types
@@ -4,3 +4,4 @@ g_bus_name_watcher_get_type
 g_bus_type_get_type
 g_bus_name_owner_flags_get_type
 g_dbus_error_get_type
+g_dbus_proxy_get_type
diff --git a/gdbus/Makefile.am b/gdbus/Makefile.am
index 52578bc..bb246f1 100644
--- a/gdbus/Makefile.am
+++ b/gdbus/Makefile.am
@@ -2,7 +2,7 @@ include $(top_srcdir)/Makefile.decl
 
 NULL =
 
-SUBDIRS=tests
+SUBDIRS=. tests
 
 if OS_WIN32_AND_DLL_COMPILATION
 if MS_LIB_AVAILABLE
@@ -83,6 +83,7 @@ gdbus_headers =				\
 	gbusnamewatcher.h		\
 	gdbusnamewatching.h		\
 	gdbusnameowning.h		\
+	gdbusproxy.h			\
 	$(NULL)
 
 libgdbus_2_0_la_SOURCES =						\
@@ -99,6 +100,7 @@ libgdbus_2_0_la_SOURCES =						\
 	gdbusnamewatching.h		gdbusnamewatching.c		\
 	gdbusnameowning.h		gdbusnameowning.c		\
 	gdbusprivate.h			gdbusprivate.c			\
+	gdbusproxy.h			gdbusproxy.c			\
 	gdbusalias.h 							\
 	gdbusaliasdef.c							\
 	$(NULL)
diff --git a/gdbus/example-own-name.c b/gdbus/example-own-name.c
index 11071c9..950e8b8 100644
--- a/gdbus/example-own-name.c
+++ b/gdbus/example-own-name.c
@@ -39,6 +39,8 @@ main (int argc, char *argv[])
 
   error = NULL;
   opt_name = NULL;
+  opt_replace = FALSE;
+  opt_allow_replacement = FALSE;
   opt_context = g_option_context_new ("g_bus_own_name() example");
   g_option_context_add_main_entries (opt_context, opt_entries, NULL);
   if (!g_option_context_parse (opt_context, &argc, &argv, &error))
diff --git a/gdbus/gdbus.h b/gdbus/gdbus.h
index d77f088..d4c2926 100644
--- a/gdbus/gdbus.h
+++ b/gdbus/gdbus.h
@@ -34,6 +34,7 @@
 #include <gdbus/gbusnamewatcher.h>
 #include <gdbus/gdbusnamewatching.h>
 #include <gdbus/gdbusnameowning.h>
+#include <gdbus/gdbusproxy.h>
 
 #undef __G_DBUS_D_DBUS_H_INSIDE__
 
diff --git a/gdbus/gdbus.symbols b/gdbus/gdbus.symbols
index 13819ff..90835c7 100644
--- a/gdbus/gdbus.symbols
+++ b/gdbus/gdbus.symbols
@@ -30,6 +30,8 @@ g_dbus_connection_get_dbus_1_connection
 g_dbus_connection_send_dbus_1_message
 g_dbus_connection_send_dbus_1_message_with_reply
 g_dbus_connection_send_dbus_1_message_with_reply_finish
+g_dbus_connection_send_dbus_1_message_block
+g_dbus_connection_send_dbus_1_message_cancel
 #endif
 #endif
 
@@ -102,3 +104,17 @@ g_bus_own_name
 g_bus_unown_name
 #endif
 #endif
+
+#if IN_HEADER(__G_DBUS_PROXY_H__)
+#if IN_FILE(__G_DBUS_PROXY_C__)
+g_dbus_proxy_get_type G_GNUC_CONST
+g_dbus_proxy_new
+g_dbus_proxy_get_connection
+g_dbus_proxy_get_interface_name
+g_dbus_proxy_get_name
+g_dbus_proxy_get_object_path
+g_dbus_proxy_invoke_method
+g_dbus_proxy_invoke_method_finish
+g_dbus_proxy_invoke_method_sync
+#endif
+#endif
diff --git a/gdbus/gdbusconnection.c b/gdbus/gdbusconnection.c
index 1f598d1..c722456 100644
--- a/gdbus/gdbusconnection.c
+++ b/gdbus/gdbusconnection.c
@@ -64,6 +64,9 @@ struct _GDBusConnectionPrivate
   /* these variables are used to store async callbacks for users calling _new()  */
   gboolean   in_init_phase;
   GPtrArray *init_phase_pending_simple_async_results;
+
+  guint global_pending_call_id;
+  GHashTable *pending_call_id_to_simple;
 };
 
 enum
@@ -130,6 +133,8 @@ g_dbus_connection_finalize (GObject *object)
 
   g_dbus_connection_set_dbus_1_connection (connection, NULL);
 
+  g_hash_table_destroy (connection->priv->pending_call_id_to_simple);
+
   if (G_OBJECT_CLASS (g_dbus_connection_parent_class)->finalize != NULL)
     G_OBJECT_CLASS (g_dbus_connection_parent_class)->finalize (object);
 }
@@ -368,6 +373,9 @@ g_dbus_connection_init (GDBusConnection *connection)
   connection->priv = G_TYPE_INSTANCE_GET_PRIVATE (connection, G_TYPE_DBUS_CONNECTION, GDBusConnectionPrivate);
   connection->priv->in_init_phase = TRUE;
   connection->priv->init_phase_pending_simple_async_results = g_ptr_array_new ();
+
+  connection->priv->global_pending_call_id = 1;
+  connection->priv->pending_call_id_to_simple = g_hash_table_new (g_direct_hash, g_direct_equal);
 }
 
 /**
@@ -1105,12 +1113,17 @@ send_dbus_1_message_with_reply_cb (DBusPendingCall *pending_call,
                                    void            *user_data)
 {
   GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
+  GDBusConnection *connection;
   GCancellable *cancellable;
   gulong cancellable_handler_id;
   DBusMessage *reply;
+  guint pending_call_id;
 
   G_LOCK (connection_lock);
   cancellable_handler_id = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (simple), "cancellable-handler-id"));
+  pending_call_id = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (simple), "pending-id"));
+  connection = G_DBUS_CONNECTION (g_async_result_get_source_object (G_ASYNC_RESULT (simple)));
+  g_hash_table_remove (connection->priv->pending_call_id_to_simple, GUINT_TO_POINTER (pending_call_id));
   G_UNLOCK (connection_lock);
 
   cancellable = g_object_get_data (G_OBJECT (simple), "cancellable");
@@ -1132,6 +1145,7 @@ send_dbus_1_message_with_reply_cb (DBusPendingCall *pending_call,
       g_simple_async_result_set_op_res_gpointer (simple, reply, (GDestroyNotify) dbus_message_unref);
     }
   g_simple_async_result_complete (simple);
+  g_object_unref (connection);
   g_object_unref (simple);
 }
 
@@ -1157,6 +1171,80 @@ send_dbus_1_message_with_reply_cancelled_cb (GCancellable *cancellable,
 }
 
 /**
+ * g_dbus_connection_send_dbus_1_message_cancel:
+ * @connection: A #GDBusConnection.
+ * @pending_call_id: A pending call id obtained from g_dbus_connection_send_dbus_1_message_with_reply().
+ *
+ * Cancels the pending call specified by @pending_call_id.
+ *
+ * The #GAsyncReadyCallback passed to g_dbus_connection_send_dbus_1_message_with_reply() will
+ * be invoked in idle (e.g. after this function returns).
+ **/
+void
+g_dbus_connection_send_dbus_1_message_cancel (GDBusConnection *connection,
+                                              guint            pending_call_id)
+{
+  GSimpleAsyncResult *simple;
+  DBusPendingCall *pending_call;
+
+  g_return_if_fail (G_IS_DBUS_CONNECTION (connection));
+  g_return_if_fail (pending_call_id != 0);
+
+  G_LOCK (connection_lock);
+  simple = g_hash_table_lookup (connection->priv->pending_call_id_to_simple, GUINT_TO_POINTER (pending_call_id));
+  if (simple == NULL)
+    {
+      g_warning ("Cannot cancel; no pending call with id %d", pending_call_id);
+      goto out;
+    }
+
+  pending_call = g_object_get_data (G_OBJECT (simple), "dbus-1-pending-call");
+  dbus_pending_call_cancel (pending_call);
+
+  g_idle_add (send_dbus_1_message_with_reply_cancelled_in_idle, simple);
+
+ out:
+  G_UNLOCK (connection_lock);
+}
+
+/**
+ * g_dbus_connection_send_dbus_1_message_block:
+ * @connection: A #GDBusConnection.
+ * @pending_call_id: A pending call id obtained from g_dbus_connection_send_dbus_1_message_with_reply().
+ *
+ * Blocks until the pending call specified by @pending_call_id is done. This will
+ * not block in the main loop. You are guaranteed that the #GAsyncReadyCallback passed to
+ * g_dbus_connection_send_dbus_1_message_with_reply() is invoked before this function returns.
+ **/
+void
+g_dbus_connection_send_dbus_1_message_block (GDBusConnection *connection,
+                                             guint            pending_call_id)
+{
+  GSimpleAsyncResult *simple;
+  DBusPendingCall *pending_call;
+
+  g_return_if_fail (G_IS_DBUS_CONNECTION (connection));
+  g_return_if_fail (pending_call_id != 0);
+
+  G_LOCK (connection_lock);
+  simple = g_hash_table_lookup (connection->priv->pending_call_id_to_simple, GUINT_TO_POINTER (pending_call_id));
+  if (simple == NULL)
+    {
+      g_warning ("Cannot block; no pending call with id %d", pending_call_id);
+      G_UNLOCK (connection_lock);
+      goto out;
+    }
+
+  pending_call = g_object_get_data (G_OBJECT (simple), "dbus-1-pending-call");
+  dbus_pending_call_ref (pending_call);
+  G_UNLOCK (connection_lock);
+  dbus_pending_call_block (pending_call);
+  dbus_pending_call_unref (pending_call);
+ out:
+  ;
+}
+
+/**
  * g_dbus_connection_send_dbus_1_message_with_reply:
  * @connection: A #GDBusConnection.
  * @message: A #DBusMessage
@@ -1176,8 +1264,11 @@ send_dbus_1_message_with_reply_cancelled_cb (GCancellable *cancellable,
  * closed - sending the message will fail with %G_DBUS_ERROR_FAILED.
  *
  * This function is intended for use by object mappings only.
+ *
+ * Returns: An pending call id (never 0) for the message that can be used in
+ * g_dbus_connection_send_dbus_1_message_cancel() or g_dbus_connection_send_dbus_1_message_block().
  **/
-void
+guint
 g_dbus_connection_send_dbus_1_message_with_reply (GDBusConnection    *connection,
                                                   DBusMessage        *message,
                                                   gint                timeout_msec,
@@ -1185,14 +1276,17 @@ g_dbus_connection_send_dbus_1_message_with_reply (GDBusConnection    *connection
                                                   GAsyncReadyCallback callback,
                                                   gpointer            user_data)
 {
+  guint pending_call_id;
   GSimpleAsyncResult *simple;
   DBusPendingCall *pending_call;
   gulong cancellable_handler_id;
 
-  g_return_if_fail (G_IS_DBUS_CONNECTION (connection));
-  g_return_if_fail (callback != NULL);
-  g_return_if_fail (message != NULL);
-  g_return_if_fail (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_METHOD_CALL);
+  g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), 0);
+  g_return_val_if_fail (callback != NULL, 0);
+  g_return_val_if_fail (message != NULL, 0);
+  g_return_val_if_fail (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_METHOD_CALL, 0);
+
+  G_LOCK (connection_lock);
 
   simple = g_simple_async_result_new (G_OBJECT (connection),
                                       callback,
@@ -1228,6 +1322,17 @@ g_dbus_connection_send_dbus_1_message_with_reply (GDBusConnection    *connection
                                         timeout_msec))
     _g_dbus_oom ();
 
+  if (pending_call == NULL)
+    {
+      g_simple_async_result_set_error (simple,
+                                       G_DBUS_ERROR,
+                                       G_DBUS_ERROR_FAILED,
+                                       _("Not connected"));
+      g_simple_async_result_complete_in_idle (simple);
+      g_object_unref (simple);
+      goto out;
+    }
+
   g_object_set_data_full (G_OBJECT (simple),
                           "dbus-1-pending-call",
                           pending_call,
@@ -1248,7 +1353,6 @@ g_dbus_connection_send_dbus_1_message_with_reply (GDBusConnection    *connection
       /* use the lock to ensure cancellable-handler-id is set on simple before trying to get it
        * in send_dbus_1_message_with_reply_cb()
        */
-      G_LOCK (connection_lock);
       cancellable_handler_id = g_cancellable_connect (cancellable,
                                                       G_CALLBACK (send_dbus_1_message_with_reply_cancelled_cb),
                                                       simple,
@@ -1256,11 +1360,18 @@ g_dbus_connection_send_dbus_1_message_with_reply (GDBusConnection    *connection
       g_object_set_data (G_OBJECT (simple),
                          "cancellable-handler-id",
                          GUINT_TO_POINTER (cancellable_handler_id));
-      G_UNLOCK (connection_lock);
     }
 
  out:
-  ;
+  pending_call_id = connection->priv->global_pending_call_id++; /* TODO: uh uh, handle collisions */
+  g_hash_table_insert (connection->priv->pending_call_id_to_simple,
+                       GUINT_TO_POINTER (pending_call_id),
+                       simple);
+  g_object_set_data (G_OBJECT (simple),
+                     "pending-id",
+                     GUINT_TO_POINTER (pending_call_id));
+  G_UNLOCK (connection_lock);
+  return pending_call_id;
 }
 
 /**
diff --git a/gdbus/gdbusconnection.h b/gdbus/gdbusconnection.h
index 87e0f4d..75199cb 100644
--- a/gdbus/gdbusconnection.h
+++ b/gdbus/gdbusconnection.h
@@ -113,12 +113,16 @@ void             g_dbus_connection_set_exit_on_close          (GDBusConnection
 DBusConnection  *g_dbus_connection_get_dbus_1_connection                 (GDBusConnection    *connection);
 void             g_dbus_connection_send_dbus_1_message                   (GDBusConnection    *connection,
                                                                           DBusMessage        *message);
-void             g_dbus_connection_send_dbus_1_message_with_reply        (GDBusConnection    *connection,
+guint            g_dbus_connection_send_dbus_1_message_with_reply        (GDBusConnection    *connection,
                                                                           DBusMessage        *message,
                                                                           gint                timeout_msec,
                                                                           GCancellable       *cancellable,
                                                                           GAsyncReadyCallback callback,
                                                                           gpointer            user_data);
+void             g_dbus_connection_send_dbus_1_message_block             (GDBusConnection    *connection,
+                                                                          guint               pending_call_id);
+void             g_dbus_connection_send_dbus_1_message_cancel            (GDBusConnection    *connection,
+                                                                          guint               pending_call_id);
 DBusMessage     *g_dbus_connection_send_dbus_1_message_with_reply_finish (GDBusConnection    *connection,
                                                                           GAsyncResult       *res,
                                                                           GError            **error);
diff --git a/gdbus/gdbusproxy.c b/gdbus/gdbusproxy.c
new file mode 100644
index 0000000..fd1d092
--- /dev/null
+++ b/gdbus/gdbusproxy.c
@@ -0,0 +1,953 @@
+/* GDBus - GLib D-Bus Library
+ *
+ * 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 "config.h"
+
+#include <stdlib.h>
+
+#include <glib/gi18n.h>
+
+#include "gdbusproxy.h"
+#include "gdbusenumtypes.h"
+#include "gdbusconnection.h"
+#include "gdbuserror.h"
+#include "gdbusprivate.h"
+
+#include "gdbusalias.h"
+
+/**
+ * SECTION:gdbusproxy
+ * @short_description: Proxy Class
+ * @include: gdbus/gdbus.h
+ *
+ * #GDBusProxy is a...
+ */
+
+struct _GDBusProxyPrivate
+{
+  GDBusConnection *connection;
+  gchar *name;
+  gchar *object_path;
+  gchar *interface_name;
+};
+
+enum
+{
+  PROP_0,
+  PROP_G_DBUS_PROXY_CONNECTION,
+  PROP_G_DBUS_PROXY_NAME,
+  PROP_G_DBUS_PROXY_OBJECT_PATH,
+  PROP_G_DBUS_PROXY_INTERFACE_NAME,
+};
+
+G_DEFINE_TYPE (GDBusProxy, g_dbus_proxy, G_TYPE_OBJECT);
+
+static void
+g_dbus_proxy_finalize (GObject *object)
+{
+  GDBusProxy *proxy = G_DBUS_PROXY (object);
+
+  g_object_unref (proxy->priv->connection);
+  g_free (proxy->priv->name);
+  g_free (proxy->priv->object_path);
+  g_free (proxy->priv->interface_name);
+
+  if (G_OBJECT_CLASS (g_dbus_proxy_parent_class)->finalize != NULL)
+    G_OBJECT_CLASS (g_dbus_proxy_parent_class)->finalize (object);
+}
+
+static void
+g_dbus_proxy_get_property (GObject    *object,
+                           guint       prop_id,
+                           GValue     *value,
+                           GParamSpec *pspec)
+{
+  GDBusProxy *proxy = G_DBUS_PROXY (object);
+
+  switch (prop_id)
+    {
+    case PROP_G_DBUS_PROXY_CONNECTION:
+      g_value_set_object (value, proxy->priv->connection);
+      break;
+
+    case PROP_G_DBUS_PROXY_NAME:
+      g_value_set_string (value, proxy->priv->name);
+      break;
+
+    case PROP_G_DBUS_PROXY_OBJECT_PATH:
+      g_value_set_string (value, proxy->priv->object_path);
+      break;
+
+    case PROP_G_DBUS_PROXY_INTERFACE_NAME:
+      g_value_set_string (value, proxy->priv->interface_name);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+g_dbus_proxy_set_property (GObject      *object,
+                           guint         prop_id,
+                           const GValue *value,
+                           GParamSpec   *pspec)
+{
+  GDBusProxy *proxy = G_DBUS_PROXY (object);
+
+  switch (prop_id)
+    {
+    case PROP_G_DBUS_PROXY_CONNECTION:
+      proxy->priv->connection = g_value_dup_object (value);
+      break;
+
+    case PROP_G_DBUS_PROXY_NAME:
+      proxy->priv->name = g_value_dup_string (value);
+      break;
+
+    case PROP_G_DBUS_PROXY_OBJECT_PATH:
+      proxy->priv->object_path = g_value_dup_string (value);
+      break;
+
+    case PROP_G_DBUS_PROXY_INTERFACE_NAME:
+      proxy->priv->interface_name = g_value_dup_string (value);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+g_dbus_proxy_constructed (GObject *object)
+{
+  //GDBusProxy *proxy = G_DBUS_PROXY (object);
+
+  /* TODO: Start loading all properties */
+
+  if (G_OBJECT_CLASS (g_dbus_proxy_parent_class)->constructed != NULL)
+    G_OBJECT_CLASS (g_dbus_proxy_parent_class)->constructed (object);
+}
+
+static void
+g_dbus_proxy_class_init (GDBusProxyClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+  gobject_class->finalize     = g_dbus_proxy_finalize;
+  gobject_class->set_property = g_dbus_proxy_set_property;
+  gobject_class->get_property = g_dbus_proxy_get_property;
+  gobject_class->constructed  = g_dbus_proxy_constructed;
+
+  /* all property names are prefixed to avoid collisions with D-Bus property names */
+
+  /**
+   * GDBusProxy:g-dbus-proxy-connection:
+   *
+   * The @GDBusConnection the proxy is for.
+   */
+  g_object_class_install_property (gobject_class,
+                                   PROP_G_DBUS_PROXY_CONNECTION,
+                                   g_param_spec_object ("g-dbus-proxy-connection",
+                                                        _("g-dbus-proxy-connection"),
+                                                        _("The connection the proxy is for"),
+                                                        G_TYPE_DBUS_CONNECTION,
+                                                        G_PARAM_READABLE |
+                                                        G_PARAM_WRITABLE |
+                                                        G_PARAM_CONSTRUCT_ONLY |
+                                                        G_PARAM_STATIC_NAME |
+                                                        G_PARAM_STATIC_BLURB |
+                                                        G_PARAM_STATIC_NICK));
+
+  /**
+   * GDBusProxy:g-dbus-proxy-name:
+   *
+   * The bus name the proxy is for.
+   */
+  g_object_class_install_property (gobject_class,
+                                   PROP_G_DBUS_PROXY_NAME,
+                                   g_param_spec_string ("g-dbus-proxy-name",
+                                                        _("g-dbus-proxy-name"),
+                                                        _("The bus name the proxy is for"),
+                                                        NULL,
+                                                        G_PARAM_READABLE |
+                                                        G_PARAM_WRITABLE |
+                                                        G_PARAM_CONSTRUCT_ONLY |
+                                                        G_PARAM_STATIC_NAME |
+                                                        G_PARAM_STATIC_BLURB |
+                                                        G_PARAM_STATIC_NICK));
+
+  /**
+   * GDBusProxy:g-dbus-proxy-object-path:
+   *
+   * The object path the proxy is for.
+   */
+  g_object_class_install_property (gobject_class,
+                                   PROP_G_DBUS_PROXY_OBJECT_PATH,
+                                   g_param_spec_string ("g-dbus-proxy-object-path",
+                                                        _("g-dbus-proxy-object-path"),
+                                                        _("The object path the proxy is for"),
+                                                        NULL,
+                                                        G_PARAM_READABLE |
+                                                        G_PARAM_WRITABLE |
+                                                        G_PARAM_CONSTRUCT_ONLY |
+                                                        G_PARAM_STATIC_NAME |
+                                                        G_PARAM_STATIC_BLURB |
+                                                        G_PARAM_STATIC_NICK));
+
+  /**
+   * GDBusProxy:g-dbus-proxy-interface-name:
+   *
+   * The D-Bus interface name the proxy is for.
+   */
+  g_object_class_install_property (gobject_class,
+                                   PROP_G_DBUS_PROXY_INTERFACE_NAME,
+                                   g_param_spec_string ("g-dbus-proxy-interface-name",
+                                                        _("g-dbus-proxy-interface-name"),
+                                                        _("The D-Bus interface name the proxy is for"),
+                                                        NULL,
+                                                        G_PARAM_READABLE |
+                                                        G_PARAM_WRITABLE |
+                                                        G_PARAM_CONSTRUCT_ONLY |
+                                                        G_PARAM_STATIC_NAME |
+                                                        G_PARAM_STATIC_BLURB |
+                                                        G_PARAM_STATIC_NICK));
+
+  g_type_class_add_private (klass, sizeof (GDBusProxyPrivate));
+}
+
+static void
+g_dbus_proxy_init (GDBusProxy *proxy)
+{
+  proxy->priv = G_TYPE_INSTANCE_GET_PRIVATE (proxy, G_TYPE_DBUS_PROXY, GDBusProxyPrivate);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+/**
+ * g_dbus_proxy_new:
+ * @connection: A #GDBusConnection.
+ * @name: A bus name.
+ * @object_path: An object path.
+ * @interface_name: A D-Bus interface name.
+ *
+ * Creates a proxy for accessing @interface_name on the remote at
+ * @object_path owned by @name at @connection.
+ *
+ * Returns: A #GDBusProxy. Free with g_object_unref().
+ **/
+GDBusProxy *
+g_dbus_proxy_new (GDBusConnection     *connection,
+                  const gchar         *name,
+                  const gchar         *object_path,
+                  const gchar         *interface_name)
+{
+  GDBusProxy *proxy;
+
+  /* TODO: need to take a GAsyncReadyCallback for when properties are loaded */
+
+  g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL);
+  g_return_val_if_fail (name != NULL, NULL);
+  g_return_val_if_fail (object_path != NULL, NULL);
+  g_return_val_if_fail (interface_name, NULL);
+
+  proxy = G_DBUS_PROXY (g_object_new (G_TYPE_DBUS_PROXY,
+                                      "g-dbus-proxy-name", name,
+                                      "g-dbus-proxy-connection", connection,
+                                      "g-dbus-proxy-object-path", object_path,
+                                      "g-dbus-proxy-interface-name", interface_name,
+                                      NULL));
+
+  return proxy;
+}
+/* ---------------------------------------------------------------------------------------------------- */
+
+/**
+ * g_dbus_proxy_get_connection:
+ * @proxy: A #GDBusProxy.
+ *
+ * Gets the connection the proxy is for.
+ *
+ * Returns: A #GDBusConnection owned by @proxy. Do not free.
+ **/
+GDBusConnection *
+g_dbus_proxy_get_connection (GDBusProxy *proxy)
+{
+  g_return_val_if_fail (G_IS_DBUS_PROXY (proxy), NULL);
+  return proxy->priv->connection;
+}
+
+/**
+ * g_dbus_proxy_get_name:
+ * @proxy: A #GDBusProxy.
+ *
+ * Gets the bus name the proxy is for.
+ *
+ * Returns: A string owned by @proxy. Do not free.
+ **/
+const gchar *
+g_dbus_proxy_get_name (GDBusProxy *proxy)
+{
+  g_return_val_if_fail (G_IS_DBUS_PROXY (proxy), NULL);
+  return proxy->priv->name;
+}
+
+/**
+ * g_dbus_proxy_get_object_path:
+ * @proxy: A #GDBusProxy.
+ *
+ * Gets the object path the proxy is for.
+ *
+ * Returns: A string owned by @proxy. Do not free.
+ **/
+const gchar *
+g_dbus_proxy_get_object_path (GDBusProxy *proxy)
+{
+  g_return_val_if_fail (G_IS_DBUS_PROXY (proxy), NULL);
+  return proxy->priv->object_path;
+}
+
+/**
+ * g_dbus_proxy_get_interface_name:
+ * @proxy: A #GDBusProxy.
+ *
+ * Gets the D-Bus interface name the proxy is for.
+ *
+ * Returns: A string owned by @proxy. Do not free.
+ **/
+const gchar *
+g_dbus_proxy_get_interface_name (GDBusProxy *proxy)
+{
+  g_return_val_if_fail (G_IS_DBUS_PROXY (proxy), NULL);
+  return proxy->priv->interface_name;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+/**
+ * ForeachCallback:
+ * @signature: Signature for single complete D-Bus type.
+ * @type: The #GType corresponding to @signature.
+ * @va_args: A #va_list you can use va_arg() on to extract the value.
+ *
+ * Callback function used in foreach_signature_and_gtype().
+ *
+ * Returns: %TRUE to short-circuit iteration, %FALSE to continue iterating.
+ */
+typedef gboolean (*ForeachCallback) (guint        arg_num,
+                                     const gchar *signature,
+                                     GType        type,
+                                     va_list      va_args,
+                                     gpointer     user_data);
+
+/**
+ * foreach_signature_and_gtype:
+ * @signature: A D-Bus signature of zero or more single complete types.
+ * @first_type: First #GType to match up.
+ * @va_args: The value of the first in argument, followed by zero or more (type, value) pairs, followed
+ * by #G_TYPE_INVALID.
+ * @callback: Callback function.
+ * @user_data: User data to pass to @callback.
+ *
+ * Splits up @signature into single complete types, matches each single complete type with
+ * the #GType<!-- -->s and values from @first_type, @va_args and invokes @callback for each
+ * matching pair.
+ *
+ * Returns: %TRUE if @callback short-circuited the matching, %FALSE otherwise.
+ */
+static gboolean
+foreach_signature_and_gtype (const gchar    *signature,
+                             GType           first_type,
+                             va_list         va_args,
+                             ForeachCallback callback,
+                             gpointer        user_data)
+{
+  DBusSignatureIter iter;
+  gchar *element_signature;
+  gboolean ret;
+  GType gtype;
+  guint arg_num;
+
+  g_assert (dbus_signature_validate (signature, NULL));
+
+  ret = FALSE;
+
+  if (strlen (signature) == 0)
+      goto out;
+
+  g_assert (first_type != G_TYPE_INVALID);
+
+  dbus_signature_iter_init (&iter, signature);
+
+  gtype = first_type;
+  arg_num = 0;
+  do
+    {
+      if (arg_num > 0)
+        gtype = va_arg (va_args, GType);
+
+      element_signature = dbus_signature_iter_get_signature (&iter);
+
+      ret = callback (arg_num,
+                      element_signature,
+                      gtype,
+                      va_args,
+                      user_data);
+      dbus_free (element_signature);
+
+      if (ret)
+        goto out;
+
+      arg_num++;
+    }
+  while (dbus_signature_iter_next (&iter));
+
+ out:
+  return ret;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static gboolean
+append_values_cb (guint        arg_num,
+                  const gchar *signature,
+                  GType        type,
+                  va_list      va_args,
+                  gpointer     user_data)
+{
+  DBusMessageIter *iter = user_data;
+  guchar           v_byte;
+  dbus_uint16_t    v_uint16;
+  dbus_uint32_t    v_uint32;
+  dbus_uint64_t    v_uint64;
+  gdouble          v_double;
+  dbus_bool_t      v_boolean;
+  const gchar     *v_string;
+
+  switch (signature[0])
+    {
+    case DBUS_TYPE_BYTE:
+      if (type == G_TYPE_UCHAR || type == G_TYPE_CHAR)
+        v_byte = va_arg (va_args, guint);
+      else
+        goto mismatch;
+      dbus_message_iter_append_basic (iter, signature[0], &v_byte);
+      break;
+
+    case DBUS_TYPE_BOOLEAN:
+      if (type == G_TYPE_BOOLEAN)
+        v_boolean = va_arg (va_args, gboolean);
+      else
+        goto mismatch;
+      dbus_message_iter_append_basic (iter, signature[0], &v_boolean);
+      break;
+
+    case DBUS_TYPE_INT16:
+    case DBUS_TYPE_UINT16:
+      if (type == G_TYPE_INT || type == G_TYPE_UINT)
+        v_uint16 = va_arg (va_args, guint);
+      else
+        goto mismatch;
+      dbus_message_iter_append_basic (iter, signature[0], &v_uint16);
+      break;
+
+    case DBUS_TYPE_INT32:
+    case DBUS_TYPE_UINT32:
+      if (type == G_TYPE_INT || type == G_TYPE_UINT)
+        v_uint32 = va_arg (va_args, guint);
+      else
+        goto mismatch;
+      dbus_message_iter_append_basic (iter, signature[0], &v_uint32);
+      break;
+
+    case DBUS_TYPE_INT64:
+    case DBUS_TYPE_UINT64:
+      if (type == G_TYPE_INT64 || type == G_TYPE_UINT64)
+        v_uint64 = va_arg (va_args, guint64);
+      else
+        goto mismatch;
+      dbus_message_iter_append_basic (iter, signature[0], &v_uint64);
+      break;
+
+    case DBUS_TYPE_DOUBLE:
+      if (type == G_TYPE_DOUBLE)
+        v_double = va_arg (va_args, gdouble);
+      else
+        goto mismatch;
+      dbus_message_iter_append_basic (iter, signature[0], &v_double);
+      break;
+
+    case DBUS_TYPE_STRING:
+    case DBUS_TYPE_OBJECT_PATH:
+    case DBUS_TYPE_SIGNATURE:
+      if (type == G_TYPE_STRING)
+        v_string = (gchar *) va_arg (va_args, gchar*);
+      else
+        goto mismatch;
+      dbus_message_iter_append_basic (iter, signature[0], &v_string);
+      break;
+
+    default:
+      g_warning ("Don't know how to append D-Bus type-code %c while processing in-arg %d",
+                 signature[0],
+                 arg_num);
+      goto error;
+    }
+
+  return FALSE;
+
+ mismatch:
+  g_warning ("D-Bus signature %s and GType %s are not compatible for in-arg %d",
+             signature,
+             g_type_name (type),
+             arg_num);
+ error:
+  return TRUE;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static gboolean
+extract_values_cb (guint        arg_num,
+                   const gchar *signature,
+                   GType        type,
+                   va_list      va_args,
+                   gpointer     user_data)
+{
+  DBusMessageIter *iter = user_data;
+  guchar           v_byte;
+  dbus_uint16_t    v_uint16;
+  dbus_uint32_t    v_uint32;
+  dbus_uint64_t    v_uint64;
+  gdouble          v_double;
+  dbus_bool_t      v_boolean;
+  const gchar     *v_string;
+  gpointer         p;
+
+  switch (signature[0])
+    {
+    case DBUS_TYPE_BYTE:
+      if (type != G_TYPE_UCHAR && type != G_TYPE_CHAR)
+        goto mismatch;
+      dbus_message_iter_get_basic (iter, &v_byte);
+      p = va_arg (va_args, gpointer);
+      *((guchar *) p) = v_byte;
+      break;
+
+    case DBUS_TYPE_BOOLEAN:
+      if (type != G_TYPE_BOOLEAN)
+        goto mismatch;
+      dbus_message_iter_get_basic (iter, &v_boolean);
+      p = va_arg (va_args, gpointer);
+      *((gboolean *) p) = v_boolean;
+      break;
+
+    case DBUS_TYPE_INT16:
+    case DBUS_TYPE_UINT16:
+      if (type != G_TYPE_INT && type != G_TYPE_UINT)
+        goto mismatch;
+      dbus_message_iter_get_basic (iter, &v_uint16);
+      p = va_arg (va_args, gpointer);
+      *((guint *) p) = v_uint16;
+      break;
+
+    case DBUS_TYPE_INT32:
+    case DBUS_TYPE_UINT32:
+      if (type != G_TYPE_INT && type != G_TYPE_UINT)
+        goto mismatch;
+      dbus_message_iter_get_basic (iter, &v_uint32);
+      p = va_arg (va_args, gpointer);
+      *((guint *) p) = v_uint32;
+      break;
+
+    case DBUS_TYPE_INT64:
+    case DBUS_TYPE_UINT64:
+      if (type != G_TYPE_INT64 && type != G_TYPE_UINT64)
+        goto mismatch;
+      dbus_message_iter_get_basic (iter, &v_uint64);
+      p = va_arg (va_args, gpointer);
+      *((guint64 *) p) = v_uint64;
+      break;
+
+    case DBUS_TYPE_DOUBLE:
+      if (type != G_TYPE_DOUBLE)
+        goto mismatch;
+      dbus_message_iter_get_basic (iter, &v_double);
+      p = va_arg (va_args, gpointer);
+      *((gdouble *) p) = v_double;
+      break;
+
+    case DBUS_TYPE_STRING:
+    case DBUS_TYPE_OBJECT_PATH:
+    case DBUS_TYPE_SIGNATURE:
+      if (type != G_TYPE_STRING)
+        goto mismatch;
+      dbus_message_iter_get_basic (iter, &v_string);
+      p = va_arg (va_args, gpointer);
+      *((gchar **) p) = g_strdup (v_string);
+      break;
+
+    default:
+      g_warning ("Don't know how to extract D-Bus type-code %c for out-arg %d",
+                 signature[0],
+                 arg_num);
+      goto error;
+    }
+
+  dbus_message_iter_next (iter);
+
+  return FALSE;
+
+ mismatch:
+  g_warning ("D-Bus signature %s and GType %s are not compatible for out-arg %d",
+             signature,
+             g_type_name (type),
+             arg_num);
+ error:
+  return TRUE;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+invoke_method_cb (GDBusConnection *connection,
+                  GAsyncResult    *result,
+                  gpointer         user_data)
+{
+  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
+  DBusMessage *reply;
+  GError *error;
+
+  error = NULL;
+  reply = g_dbus_connection_send_dbus_1_message_with_reply_finish (connection,
+                                                                   result,
+                                                                   &error);
+
+  if (reply == NULL)
+    {
+      g_simple_async_result_set_from_error (simple, error);
+      g_error_free (error);
+    }
+  else
+    {
+      g_simple_async_result_set_op_res_gpointer (simple,
+                                                 reply,
+                                                 (GDestroyNotify) dbus_message_unref);
+    }
+
+  g_simple_async_result_complete (simple);
+  g_object_unref (simple);
+}
+
+static guint
+g_dbus_proxy_invoke_method_valist (GDBusProxy          *proxy,
+                                   const gchar         *name,
+                                   const gchar         *signature,
+                                   guint                timeout_msec,
+                                   GCancellable        *cancellable,
+                                   GAsyncReadyCallback  callback,
+                                   gpointer             user_data,
+                                   GType                first_in_arg_type,
+                                   va_list              va_args)
+{
+  DBusMessage *message;
+  DBusMessageIter iter;
+  GSimpleAsyncResult *simple;
+  guint pending_call_id;
+
+  pending_call_id = 0;
+
+  simple = g_simple_async_result_new (G_OBJECT (proxy),
+                                      callback,
+                                      user_data,
+                                      g_dbus_proxy_invoke_method);
+
+  if ((message = dbus_message_new_method_call (proxy->priv->name,
+                                               proxy->priv->object_path,
+                                               proxy->priv->interface_name,
+                                               name)) == NULL)
+    _g_dbus_oom ();
+
+  /* append args to message */
+  dbus_message_iter_init_append (message, &iter);
+  if (foreach_signature_and_gtype (signature,
+                                   first_in_arg_type,
+                                   va_args,
+                                   append_values_cb,
+                                   &iter))
+    goto out;
+
+  pending_call_id = g_dbus_connection_send_dbus_1_message_with_reply (proxy->priv->connection,
+                                                                      message,
+                                                                      timeout_msec,
+                                                                      cancellable,
+                                                                      (GAsyncReadyCallback) invoke_method_cb,
+                                                                      simple);
+  dbus_message_unref (message);
+
+ out:
+  return pending_call_id;
+}
+
+static gboolean
+g_dbus_proxy_invoke_method_finish_valist (GDBusProxy          *proxy,
+                                          const gchar         *signature,
+                                          GAsyncResult        *res,
+                                          GError             **error,
+                                          GType                first_out_arg_type,
+                                          va_list              va_args)
+{
+  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
+  DBusMessage *reply;
+  DBusMessageIter reply_iter;
+  DBusError dbus_error;
+  gboolean ret;
+
+  g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == g_dbus_proxy_invoke_method);
+
+  ret = FALSE;
+  if (g_simple_async_result_propagate_error (simple, error))
+    goto out;
+
+  reply = g_simple_async_result_get_op_res_gpointer (simple);
+  dbus_error_init (&dbus_error);
+
+  /* Translate to GError if the reply is an error */
+  if (dbus_set_error_from_message (&dbus_error, reply))
+    {
+      /* TODO: hmm, gotta get caller to pass error_types or... better to rethink GDBusError helpers */
+      g_dbus_error_set_dbus_error (error,
+                                   &dbus_error,
+                                   NULL,
+                                   NULL);
+      dbus_error_free (&dbus_error);
+      goto out;
+    }
+
+  /* extract values from message */
+  dbus_message_iter_init (reply, &reply_iter);
+  if (foreach_signature_and_gtype (signature,
+                                   first_out_arg_type,
+                                   va_args,
+                                   extract_values_cb,
+                                   &reply_iter))
+    {
+      g_set_error (error,
+                   G_DBUS_ERROR,
+                   G_DBUS_ERROR_FAILED,
+                   _("Error extracting values from D-Bus message. This is a bug in the application"));
+      ret = FALSE;
+      goto out;
+    }
+
+  ret = TRUE;
+
+ out:
+  return ret;
+}
+
+/**
+ * g_dbus_proxy_invoke_method:
+ * @proxy: A #GDBusProxy.
+ * @name: The name of the method to invoke.
+ * @signature: The D-Bus signature for all arguments being passed to the method.
+ * @timeout_msec: Timeout in milliseconds or -1 for default timeout.
+ * @cancellable: A #GCancellable or %NULL.
+ * @callback: The callback function to invoke when the reply is ready or %NULL.
+ * @user_data: User data to pass to @callback.
+ * @first_in_arg_type: The #GType of the first argument to pass or #G_TYPE_INVALID if there are no arguments.
+ * @...: The value of the first in argument, followed by zero or more (type, value) pairs, followed by #G_TYPE_INVALID.
+ *
+ * Invokes the method @name on the interface and remote object represented by @proxy. This
+ * is an asynchronous operation and when the reply is ready @callback will be invoked (on
+ * the main thread) and you can get the result by calling g_dbus_proxy_invoke_method_finish().
+ *
+ * Note that @signature and and the supplied (type, value) pairs must match as described in
+ * chapter TODO_SECTION_EXPLAINING_DBUS_TO_GTYPE_OBJECT_MAPPING.
+ *
+ * Returns: An pending call id (never 0) for the message that can be used in
+ * g_dbus_connection_send_dbus_1_message_cancel() or g_dbus_connection_send_dbus_1_message_block().
+ **/
+guint
+g_dbus_proxy_invoke_method (GDBusProxy          *proxy,
+                            const gchar         *name,
+                            const gchar         *signature,
+                            guint                timeout_msec,
+                            GCancellable        *cancellable,
+                            GAsyncReadyCallback  callback,
+                            gpointer             user_data,
+                            GType                first_in_arg_type,
+                            ...)
+{
+  va_list va_args;
+  guint pending_call_id;
+
+  va_start (va_args, first_in_arg_type);
+  pending_call_id = g_dbus_proxy_invoke_method_valist (proxy,
+                                                       name,
+                                                       signature,
+                                                       timeout_msec,
+                                                       cancellable,
+                                                       callback,
+                                                       user_data,
+                                                       first_in_arg_type,
+                                                       va_args);
+  va_end (va_args);
+
+  return pending_call_id;
+}
+
+/**
+ * g_dbus_proxy_invoke_method_finish:
+ * @proxy: A #GDBusProxy.
+ * @signature: The D-Bus signature for all return values.
+ * @res: A #GAsyncResult obtained from the #GAsyncReadyCallback function passed to g_dbus_proxy_invoke_method().
+ * @error: Return location for error or %NULL.
+ * @first_out_arg_type: The #GType of the first return value or #G_TYPE_INVALID if there are no return values.
+ * @...: The location to store of the first return value, followed by zero or more (type, value) pairs,
+ * followed by #G_TYPE_INVALID.
+ *
+ * Finishes invoking a method on @proxy initiated with g_dbus_proxy_invoke_method().
+ *
+ * Note that @signature and and the supplied (type, return value) pairs must match as described in
+ * chapter TODO_SECTION_EXPLAINING_DBUS_TO_GTYPE_OBJECT_MAPPING.
+ *
+ * Returns: %TRUE if the method invocation succeeded, %FALSE is @error is set.
+ **/
+gboolean
+g_dbus_proxy_invoke_method_finish (GDBusProxy          *proxy,
+                                   const gchar         *signature,
+                                   GAsyncResult        *res,
+                                   GError             **error,
+                                   GType                first_out_arg_type,
+                                   ...)
+{
+  gboolean ret;
+  va_list va_args;
+
+  va_start (va_args, first_out_arg_type);
+  ret = g_dbus_proxy_invoke_method_finish_valist (proxy,
+                                                  signature,
+                                                  res,
+                                                  error,
+                                                  first_out_arg_type,
+                                                  va_args);
+  va_end (va_args);
+
+  return ret;
+}
+
+static void
+invoke_method_sync_cb (GDBusConnection *connection,
+                       GAsyncResult    *res,
+                       gpointer         user_data)
+{
+  GAsyncResult **out_res = user_data;
+  g_assert (out_res != NULL);
+  *out_res = g_object_ref (res);
+}
+
+/**
+ * g_dbus_proxy_invoke_method_sync:
+ * @proxy: A #GDBusProxy.
+ * @name: The name of the method to invoke.
+ * @in_signature: The D-Bus signature for all arguments being passed to the method.
+ * @out_signature: The D-Bus signature for all return values.
+ * @timeout_msec: Timeout in milliseconds or -1 for default timeout.
+ * @cancellable: A #GCancellable or %NULL.
+ * @error: Return location for @error or %NULL.
+ * @first_in_arg_type: The #GType of the first argument to pass or #G_TYPE_INVALID if there are no arguments.
+ * @...: The value of the first argument to pass (if any), followed by zero or more (type, value) pairs,
+ * followed by #G_TYPE_INVALID. Then one or more pairs for the #GType and return location for the return
+ * values, then terminated by #G_TYPE_INVALID.
+ *
+ * Synchronously invokes the method @name on the interface and remote
+ * object represented by @proxy.
+ *
+ * Returns: %TRUE if the call succeeded, %FALSE if @error is set.
+ **/
+gboolean
+g_dbus_proxy_invoke_method_sync (GDBusProxy          *proxy,
+                                 const gchar         *name,
+                                 const gchar         *in_signature,
+                                 const gchar         *out_signature,
+                                 guint                timeout_msec,
+                                 GCancellable        *cancellable,
+                                 GError             **error,
+                                 GType                first_in_arg_type,
+                                 ...)
+{
+  va_list va_args;
+  guint pending_call_id;
+  GAsyncResult *res;
+  GType first_out_arg_type;
+  gboolean ret;
+
+  ret = FALSE;
+
+  va_start (va_args, first_in_arg_type);
+  pending_call_id = g_dbus_proxy_invoke_method_valist (proxy,
+                                                       name,
+                                                       in_signature,
+                                                       timeout_msec,
+                                                       cancellable,
+                                                       (GAsyncReadyCallback) invoke_method_sync_cb,
+                                                       &res,
+                                                       first_in_arg_type,
+                                                       va_args);
+  /* check if caller messed up */
+  if (pending_call_id == 0)
+    {
+      g_set_error (error,
+                   G_DBUS_ERROR,
+                   G_DBUS_ERROR_FAILED,
+                   _("Error appending values to D-Bus message. This is a bug in the application"));
+      goto out;
+    }
+
+  g_assert (va_arg (va_args, GType) == G_TYPE_INVALID);
+  first_out_arg_type = va_arg (va_args, GType);
+
+  res = NULL;
+  g_dbus_connection_send_dbus_1_message_block (proxy->priv->connection,
+                                               pending_call_id);
+
+  g_assert (res != NULL);
+
+  ret = g_dbus_proxy_invoke_method_finish_valist (proxy,
+                                                  out_signature,
+                                                  res,
+                                                  error,
+                                                  first_out_arg_type,
+                                                  va_args);
+  g_object_unref (res);
+
+ out:
+  va_end (va_args);
+
+  return ret;
+}
+
+#define __G_DBUS_PROXY_C__
+#include "gdbusaliasdef.c"
diff --git a/gdbus/gdbusproxy.h b/gdbus/gdbusproxy.h
new file mode 100644
index 0000000..7a56db4
--- /dev/null
+++ b/gdbus/gdbusproxy.h
@@ -0,0 +1,118 @@
+/* GDBus - GLib D-Bus Library
+ *
+ * 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>
+ */
+
+#if !defined (__G_DBUS_G_DBUS_H_INSIDE__) && !defined (G_DBUS_COMPILATION)
+#error "Only <gdbus/gdbus.h> can be included directly."
+#endif
+
+#ifndef __G_DBUS_PROXY_H__
+#define __G_DBUS_PROXY_H__
+
+#include <gdbus/gdbustypes.h>
+#include <dbus/dbus.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_DBUS_PROXY         (g_dbus_proxy_get_type ())
+#define G_DBUS_PROXY(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_DBUS_PROXY, GDBusProxy))
+#define G_DBUS_PROXY_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_DBUS_PROXY, GDBusProxyClass))
+#define G_DBUS_PROXY_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_DBUS_PROXY, GDBusProxyClass))
+#define G_IS_DBUS_PROXY(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_DBUS_PROXY))
+#define G_IS_DBUS_PROXY_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_DBUS_PROXY))
+
+typedef struct _GDBusProxyClass   GDBusProxyClass;
+typedef struct _GDBusProxyPrivate GDBusProxyPrivate;
+
+/**
+ * GDBusProxy:
+ *
+ * The #GDBusProxy structure contains only private data and
+ * should only be accessed using the provided API.
+ */
+struct _GDBusProxy
+{
+  /*< private >*/
+  GObject parent_instance;
+  GDBusProxyPrivate *priv;
+};
+
+/**
+ * GDBusProxyClass:
+ *
+ * Class structure for #GDBusProxy.
+ */
+struct _GDBusProxyClass
+{
+  /*< private >*/
+  GObjectClass parent_class;
+
+  /*< public >*/
+
+  /*< private >*/
+  /* Padding for future expansion */
+  void (*_g_reserved1) (void);
+  void (*_g_reserved2) (void);
+  void (*_g_reserved3) (void);
+  void (*_g_reserved4) (void);
+  void (*_g_reserved5) (void);
+  void (*_g_reserved6) (void);
+  void (*_g_reserved7) (void);
+  void (*_g_reserved8) (void);
+};
+
+GType            g_dbus_proxy_get_type             (void) G_GNUC_CONST;
+GDBusProxy      *g_dbus_proxy_new                  (GDBusConnection     *connection,
+                                                    const gchar         *name,
+                                                    const gchar         *object_path,
+                                                    const gchar         *interface_name);
+GDBusConnection *g_dbus_proxy_get_connection       (GDBusProxy          *proxy);
+const gchar     *g_dbus_proxy_get_name             (GDBusProxy          *proxy);
+const gchar     *g_dbus_proxy_get_object_path      (GDBusProxy          *proxy);
+const gchar     *g_dbus_proxy_get_interface_name   (GDBusProxy          *proxy);
+guint            g_dbus_proxy_invoke_method        (GDBusProxy          *proxy,
+                                                    const gchar         *name,
+                                                    const gchar         *signature,
+                                                    guint                timeout_msec,
+                                                    GCancellable        *cancellable,
+                                                    GAsyncReadyCallback  callback,
+                                                    gpointer             user_data,
+                                                    GType                first_in_arg_type,
+                                                    ...);
+gboolean         g_dbus_proxy_invoke_method_finish (GDBusProxy          *proxy,
+                                                    const gchar         *signature,
+                                                    GAsyncResult        *res,
+                                                    GError             **error,
+                                                    GType                first_out_arg_type,
+                                                    ...);
+gboolean         g_dbus_proxy_invoke_method_sync   (GDBusProxy          *proxy,
+                                                    const gchar         *name,
+                                                    const gchar         *in_signature,
+                                                    const gchar         *out_signature,
+                                                    guint                timeout_msec,
+                                                    GCancellable        *cancellable,
+                                                    GError             **error,
+                                                    GType                first_in_arg_type,
+                                                    ...);
+
+G_END_DECLS
+
+#endif /* __G_DBUS_PROXY_H__ */
diff --git a/gdbus/gdbustypes.h b/gdbus/gdbustypes.h
index 508613e..f1a4451 100644
--- a/gdbus/gdbustypes.h
+++ b/gdbus/gdbustypes.h
@@ -34,6 +34,7 @@ G_BEGIN_DECLS
 typedef struct _GDBusConnection       GDBusConnection;
 typedef struct _GBusNameOwner         GBusNameOwner;
 typedef struct _GBusNameWatcher       GBusNameWatcher;
+typedef struct _GDBusProxy            GDBusProxy;
 
 G_END_DECLS
 
diff --git a/gdbus/tests/Makefile.am b/gdbus/tests/Makefile.am
index 1a6d855..e17a07e 100644
--- a/gdbus/tests/Makefile.am
+++ b/gdbus/tests/Makefile.am
@@ -14,7 +14,7 @@ INCLUDES =                      \
 	$(DBUS_CFLAGS)		\
 	$(NULL)
 
-check_PROGRAMS = $(TEST_PROGS)
+noinst_PROGRAMS = $(TEST_PROGS)
 progs_ldadd     =                                       \
         $(top_builddir)/glib/libglib-2.0.la             \
         $(top_builddir)/gobject/libgobject-2.0.la       \
@@ -24,7 +24,15 @@ progs_ldadd     =                                       \
 
 TEST_PROGS +=				\
         connection			\
+	names				\
+	proxy				\
 	$(NULL)
 
-connection_SOURCES = connection.c
+connection_SOURCES = connection.c sessionbus.c sessionbus.h tests.h
 connection_LDADD = $(progs_ldadd)
+
+names_SOURCES = names.c sessionbus.c sessionbus.h tests.h
+names_LDADD = $(progs_ldadd)
+
+proxy_SOURCES = proxy.c sessionbus.c sessionbus.h tests.h
+proxy_LDADD = $(progs_ldadd)
diff --git a/gdbus/tests/connection.c b/gdbus/tests/connection.c
index c65513f..3997adc 100644
--- a/gdbus/tests/connection.c
+++ b/gdbus/tests/connection.c
@@ -21,352 +21,13 @@
  */
 
 #include <gdbus/gdbus.h>
-
-#include <stdlib.h>
-#include <string.h>
 #include <unistd.h>
-#include <errno.h>
-#include <sys/types.h>
-#include <signal.h>
-#include <stdio.h>
 
-/* 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)
+#include "tests.h"
 
 /* all tests rely on a shared mainloop */
 static GMainLoop *loop = NULL;
 
-#if 0
-static gboolean
-wakeup_mainloop (gpointer user_data)
-{
-  GMainLoop *sleep_loop = user_data;
-  g_main_loop_quit (sleep_loop);
-  return FALSE;
-}
-
-static void
-sleep_in_mainloop (guint msec)
-{
-  GMainLoop *sleep_loop;
-  sleep_loop = g_main_loop_new (NULL, FALSE);
-  g_timeout_add (msec, wakeup_mainloop, sleep_loop);
-  g_main_loop_run (sleep_loop);
-  g_main_loop_unref (sleep_loop);
-}
-#endif
-
-/* ---------------------------------------------------------------------------------------------------- */
-/* 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];
-  guint 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];
-
-static const gchar *
-session_bus_up_with_address (const gchar *given_address)
-{
-  gchar *address;
-  int stdout_fd;
-  GError *error;
-  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) != 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,
-                                 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));
-
-  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 (argv[2]);
-  g_free (config_file_name);
-  return address;
-}
-
-static 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;
-
-static 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;
-}
-
-static 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;
-}
-
-static 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;
-    }
-}
-
 /* ---------------------------------------------------------------------------------------------------- */
 /* Message Bus tests - check all aspects of connecting and reconnecting to bus instances                */
 /* ---------------------------------------------------------------------------------------------------- */
@@ -773,588 +434,6 @@ test_dbus_connection_send (void)
 }
 
 /* ---------------------------------------------------------------------------------------------------- */
-/* Test that GBusNameOwner works correctly */
-/* ---------------------------------------------------------------------------------------------------- */
-
-static void
-on_name_acquired_expect_failure (GBusNameOwner *owner,
-                                 gpointer       user_data)
-{
-  g_assert_not_reached ();
-}
-
-static void
-on_name_lost_expect_failure (GBusNameOwner *owner,
-                             gpointer       user_data)
-{
-  g_assert_not_reached ();
-}
-
-static void
-on_owner_cb_expect_failure (GBusNameOwner *owner,
-                            GAsyncResult  *result,
-                            gpointer       user_data)
-{
-  gboolean *val = user_data;
-  gboolean ret;
-  GError *error;
-
-  error = NULL;
-  ret = g_bus_name_owner_new_finish (owner,
-                                     result,
-                                     &error);
-  _g_assert_error_domain (error, G_DBUS_ERROR);
-  g_error_free (error);
-  g_assert (!ret);
-  g_assert (!g_bus_name_owner_get_owns_name (owner));
-
-  *val = TRUE;
-  g_main_loop_quit (loop);
-}
-
-/* ---------------------------------------------------------------------------------------------------- */
-
-static void
-on_name_acquired_expect_success (GBusNameOwner *owner,
-                                 gpointer       user_data)
-{
-  gboolean *val2 = user_data;
-
-  *val2 = TRUE;
-}
-
-static void
-on_name_lost_expect_success (GBusNameOwner *owner,
-                             gpointer       user_data)
-{
-  g_assert_not_reached ();
-}
-
-static void
-on_owner_cb_expect_success (GBusNameOwner *owner,
-                            GAsyncResult  *result,
-                            gpointer       user_data)
-{
-  gboolean *val = user_data;
-  gboolean ret;
-  GError *error;
-
-  error = NULL;
-  ret = g_bus_name_owner_new_finish (owner,
-                                     result,
-                                     &error);
-  g_assert_no_error (error);
-  g_assert (ret);
-  g_assert (g_bus_name_owner_get_owns_name (owner));
-
-  *val = TRUE;
-  g_main_loop_quit (loop);
-}
-
-/* ---------------------------------------------------------------------------------------------------- */
-
-static void
-on_name_lost_bringing_down_the_bus (GBusNameOwner *owner,
-                                    gpointer       user_data)
-{
-  gboolean *val = user_data;
-
-  *val = TRUE;
-
-  g_main_loop_quit (loop);
-}
-
-/* ---------------------------------------------------------------------------------------------------- */
-
-static void
-on_name_acquired_bringing_up_the_bus (GBusNameOwner *owner,
-                                      gpointer       user_data)
-{
-  gboolean *val = user_data;
-
-  *val = TRUE;
-
-  g_main_loop_quit (loop);
-}
-
-/* ---------------------------------------------------------------------------------------------------- */
-
-static void
-on_owner_cb_expect_failure2 (GBusNameOwner *owner,
-                             GAsyncResult  *result,
-                             gpointer       user_data)
-{
-  gboolean *val = user_data;
-  gboolean ret;
-  GError *error;
-
-  error = NULL;
-  ret = g_bus_name_owner_new_finish (owner,
-                                     result,
-                                     &error);
-  g_assert_no_error (error);
-  g_assert (ret);
-  g_assert (!g_bus_name_owner_get_owns_name (owner));
-
-  *val = TRUE;
-  g_main_loop_quit (loop);
-}
-
-/* ---------------------------------------------------------------------------------------------------- */
-static void
-on_owner_for_connection_cb_expect_failure (GBusNameOwner *owner,
-                                           GAsyncResult  *result,
-                                           gpointer       user_data)
-{
-  gboolean *val = user_data;
-  gboolean ret;
-  GError *error;
-
-  error = NULL;
-  ret = g_bus_name_owner_new_for_connection_finish (owner,
-                                                    result,
-                                                    &error);
-  g_assert_no_error (error);
-  g_assert (ret);
-  g_assert (!g_bus_name_owner_get_owns_name (owner));
-
-  *val = TRUE;
-  g_main_loop_quit (loop);
-}
-/* ---------------------------------------------------------------------------------------------------- */
-
-static void
-on_name_acquired_for_o2 (GBusNameOwner *owner,
-                         gpointer       user_data)
-{
-  gboolean *val = user_data;
-
-  *val = TRUE;
-
-  g_main_loop_quit (loop);
-}
-
-/* ---------------------------------------------------------------------------------------------------- */
-
-static void
-on_name_lost_for_o2 (GBusNameOwner *owner,
-                     gpointer       user_data)
-{
-  gboolean *val = user_data;
-
-  *val = TRUE;
-
-  g_main_loop_quit (loop);
-}
-
-/* ---------------------------------------------------------------------------------------------------- */
-
-static void
-test_bus_name_owner (void)
-{
-  GDBusConnection *private_connection;
-  GBusNameOwner *o;
-  GBusNameOwner *o2;
-  gboolean val;
-  gboolean val2;
-
-  /* first try to own a name when there is no bus */
-  val = FALSE;
-  o = g_bus_name_owner_new (G_BUS_TYPE_SESSION,
-                            "org.gtk.Test.Name1",
-                            G_BUS_NAME_OWNER_FLAGS_NONE,
-                            NULL,
-                            (GAsyncReadyCallback) on_owner_cb_expect_failure,
-                            &val);
-  g_signal_connect (o, "name-acquired", G_CALLBACK (on_name_acquired_expect_failure), NULL);
-  g_signal_connect (o, "name-lost", G_CALLBACK (on_name_lost_expect_failure), NULL);
-  g_main_loop_run (loop);
-  g_assert (val);
-  g_object_unref (o);
-
-  /* try again, this time with a bus instance - the ::name-acquired signal should fire before the async reply */
-  session_bus_up ();
-  val = FALSE;
-  val2 = FALSE;
-  o = g_bus_name_owner_new (G_BUS_TYPE_SESSION,
-                            "org.gtk.Test.Name1",
-                            G_BUS_NAME_OWNER_FLAGS_NONE,
-                            NULL,
-                            (GAsyncReadyCallback) on_owner_cb_expect_success,
-                            &val);
-  g_signal_connect (o, "name-acquired", G_CALLBACK (on_name_acquired_expect_success), &val2);
-  g_signal_connect (o, "name-lost", G_CALLBACK (on_name_lost_expect_success), NULL);
-  g_main_loop_run (loop);
-  g_assert (val);
-  g_assert (val2);
-
-  /* try owning the same name again... this should not fail, we should get the same object
-   * as o because of singleton handling
-   */
-  o2 = g_bus_name_owner_new (G_BUS_TYPE_SESSION,
-                             "org.gtk.Test.Name1",
-                             G_BUS_NAME_OWNER_FLAGS_NONE,
-                             NULL,
-                             (GAsyncReadyCallback) on_owner_cb_expect_success,
-                             &val);
-  g_main_loop_run (loop);
-  g_assert (val);
-  g_assert (o2 == o);
-  g_object_unref (o2);
-
-  /* now kill o.. and then attempt to own the same name again.. */
-  g_object_unref (o);
-  val = FALSE;
-  val2 = FALSE;
-  o = g_bus_name_owner_new (G_BUS_TYPE_SESSION,
-                            "org.gtk.Test.Name1",
-                            G_BUS_NAME_OWNER_FLAGS_NONE,
-                            NULL,
-                            (GAsyncReadyCallback) on_owner_cb_expect_success,
-                            &val);
-  g_signal_connect (o, "name-acquired", G_CALLBACK (on_name_acquired_expect_success), &val2);
-  g_main_loop_run (loop);
-  g_assert (val);
-  g_assert (val2);
-
-  /* now bring down the bus.. this should trigger the ::name-lost signal */
-  val = FALSE;
-  g_signal_connect (o, "name-lost", G_CALLBACK (on_name_lost_bringing_down_the_bus), &val);
-  g_dbus_connection_set_exit_on_close (g_bus_name_owner_get_connection (o), FALSE);
-  session_bus_down ();
-  g_main_loop_run (loop);
-  g_assert (val);
-
-  /* bring the bus back up... then we should get the ::name-acquired signal
-   *
-   * Can't use existing signal handler since it doesn't quit the main loop.
-   */
-  val = FALSE;
-  g_signal_handlers_disconnect_by_func (o, on_name_acquired_expect_success, &val2);
-  g_signal_connect (o, "name-acquired", G_CALLBACK (on_name_acquired_bringing_up_the_bus), &val);
-  session_bus_up ();
-  g_main_loop_run (loop);
-  g_assert (val);
-
-  /* Create a private connection and a bus name owner for that connection, o2, that tries to own
-   * the name with _REPLACE and _ALLOW_REPLACEMENT. This should fail because o already owns the name
-   * and didn't specify ALLOW_REPLACEMENT.
-   */
-  private_connection = g_dbus_connection_bus_get_private (G_BUS_TYPE_SESSION,
-                                                          NULL,                            /* cancellable */
-                                                          (GAsyncReadyCallback) get_connection_callback_private,
-                                                          NULL);                           /* user_data */
-  g_main_loop_run (loop);
-  val = FALSE;
-  o2 = g_bus_name_owner_new_for_connection (private_connection,
-                                            "org.gtk.Test.Name1",
-                                            G_BUS_NAME_OWNER_FLAGS_REPLACE |
-                                            G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT,
-                                            NULL,
-                                            (GAsyncReadyCallback) on_owner_for_connection_cb_expect_failure,
-                                            &val);
-  g_object_unref (private_connection);
-  g_main_loop_run (loop);
-  g_assert (!g_bus_name_owner_get_owns_name (o2));
-  g_assert (val);
-
-  /* OK, so o is owning the name and o2 is in queue. Kill o and check that o2 acquires the name. */
-  val = FALSE;
-  g_signal_connect (o2, "name-acquired", G_CALLBACK (on_name_acquired_for_o2), &val);
-  g_object_unref (o);
-  g_main_loop_run (loop);
-  g_assert (g_bus_name_owner_get_owns_name (o2));
-  g_assert (val);
-
-  /* Since o2 specified ALLOW_REPLACEMENT, check that o can claim the name only when using REPLACE */
-  val = FALSE;
-  o = g_bus_name_owner_new (G_BUS_TYPE_SESSION,
-                            "org.gtk.Test.Name1",
-                            G_BUS_NAME_OWNER_FLAGS_NONE,
-                            NULL,
-                            (GAsyncReadyCallback) on_owner_cb_expect_failure2,
-                            &val);
-  g_main_loop_run (loop);
-  g_assert (val);
-  g_assert (!g_bus_name_owner_get_owns_name (o));
-  g_assert (g_bus_name_owner_get_owns_name (o2));
-  g_object_unref (o);
-  /* and now with REPLACE */
-  val = FALSE;
-  o = g_bus_name_owner_new (G_BUS_TYPE_SESSION,
-                            "org.gtk.Test.Name1",
-                            G_BUS_NAME_OWNER_FLAGS_REPLACE,
-                            NULL,
-                            (GAsyncReadyCallback) on_owner_cb_expect_success,
-                            &val);
-  g_main_loop_run (loop);
-  g_assert (val);
-  g_assert (g_bus_name_owner_get_owns_name (o));
-  /* we might not have gotten the signal for o2 yet */
-  if (g_bus_name_owner_get_owns_name (o2))
-    {
-      val = FALSE;
-      g_signal_connect (o2, "name-lost", G_CALLBACK (on_name_lost_for_o2), &val);
-      g_main_loop_run (loop);
-      g_assert (val);
-      g_assert (!g_bus_name_owner_get_owns_name (o2));
-    }
-  /* make sure we don't exit when bringing down the bus */
-  g_dbus_connection_set_exit_on_close (g_bus_name_owner_get_connection (o), FALSE);
-  g_dbus_connection_set_exit_on_close (g_bus_name_owner_get_connection (o2), FALSE);
-  g_object_unref (o2);
-  g_object_unref (o);
-
-  session_bus_down ();
-}
-
-/* ---------------------------------------------------------------------------------------------------- */
-/* Test that GBusNameWatcher works correctly */
-/* ---------------------------------------------------------------------------------------------------- */
-
-static void
-bnw_cb_expect_error (GBusNameWatcher *watcher,
-                     GAsyncResult    *res,
-                     gpointer         user_data)
-{
-  GError *error;
-  gboolean ret;
-
-  error = NULL;
-  ret = g_bus_name_watcher_new_finish (watcher,
-                                       res,
-                                       &error);
-  _g_assert_error_domain (error, G_DBUS_ERROR);
-  g_error_free (error);
-  g_assert (!ret);
-  g_assert_cmpstr (g_bus_name_watcher_get_name_owner (watcher), ==, NULL);
-
-  g_main_loop_quit (loop);
-}
-
-/* ---------------------------------------------------------------------------------------------------- */
-
-static void
-bnw_cb_expect_null_owner (GBusNameWatcher *watcher,
-                          GAsyncResult    *res,
-                          gpointer         user_data)
-{
-  GError *error;
-  gboolean ret;
-
-  error = NULL;
-  ret = g_bus_name_watcher_new_finish (watcher,
-                                       res,
-                                       &error);
-  _g_assert_error_domain (error, G_DBUS_ERROR);
-  g_assert (!ret);
-  g_assert_cmpstr (g_bus_name_watcher_get_name_owner (watcher), ==, NULL);
-
-  g_main_loop_quit (loop);
-}
-
-/* ---------------------------------------------------------------------------------------------------- */
-
-static void
-bnw_on_owner_cb_expect_success (GBusNameOwner *owner,
-                                GAsyncResult  *result,
-                                gpointer       user_data)
-{
-  gboolean ret;
-  GError *error;
-
-  error = NULL;
-  ret = g_bus_name_owner_new_finish (owner,
-                                     result,
-                                     &error);
-  g_assert_no_error (error);
-  g_assert (ret);
-  g_assert (g_bus_name_owner_get_owns_name (owner));
-
-  g_main_loop_quit (loop);
-}
-
-/* ---------------------------------------------------------------------------------------------------- */
-
-static void
-bnw_cb_expect_owner (GBusNameWatcher *watcher,
-                     GAsyncResult    *res,
-                     gpointer         user_data)
-{
-  GError *error;
-  gboolean ret;
-
-  error = NULL;
-  ret = g_bus_name_watcher_new_finish (watcher,
-                                       res,
-                                       &error);
-  g_assert_no_error (error);
-  g_assert (ret);
-  g_assert_cmpstr (g_bus_name_watcher_get_name_owner (watcher), !=, NULL);
-
-  g_main_loop_quit (loop);
-}
-
-/* ---------------------------------------------------------------------------------------------------- */
-
-static void
-bnw_on_name_vanished (GBusNameWatcher *watcher,
-                      gpointer         user_data)
-{
-  g_main_loop_quit (loop);
-}
-
-/* ---------------------------------------------------------------------------------------------------- */
-
-static void
-bnw_on_name_appeared (GBusNameWatcher *watcher,
-                      gpointer         user_data)
-{
-  g_main_loop_quit (loop);
-}
-
-/* ---------------------------------------------------------------------------------------------------- */
-
-static void
-bnw_cb_singleton (GBusNameWatcher *watcher,
-                  GAsyncResult    *res,
-                  gpointer         user_data)
-{
-  gboolean *val = user_data;
-  GError *error;
-  gboolean ret;
-
-  error = NULL;
-  ret = g_bus_name_watcher_new_finish (watcher,
-                                       res,
-                                       &error);
-  g_assert_no_error (error);
-  g_assert (ret);
-  g_assert_cmpstr (g_bus_name_watcher_get_name_owner (watcher), !=, NULL);
-
-  *val = TRUE;
-
-  g_main_loop_quit (loop);
-}
-
-/* ---------------------------------------------------------------------------------------------------- */
-
-static void
-test_bus_name_watcher (void)
-{
-  GBusNameWatcher *w;
-  GBusNameWatcher *w2;
-  GBusNameOwner *o;
-  gboolean val;
-
-  /* if no session bus is available, expect the callback to expect an error */
-  w = g_bus_name_watcher_new (G_BUS_TYPE_SESSION,
-                              "org.gtk.Test.Name1",
-                              NULL,
-                              (GAsyncReadyCallback) bnw_cb_expect_error,
-                              NULL);
-  g_main_loop_run (loop);
-  g_dbus_connection_set_exit_on_close (g_bus_name_watcher_get_connection (w), FALSE);
-  g_object_unref (w);
-
-  /* if a session bus is available, expect the callback to fail */
-  session_bus_up ();
-  w = g_bus_name_watcher_new (G_BUS_TYPE_SESSION,
-                              "org.gtk.Test.Name1",
-                              NULL,
-                              (GAsyncReadyCallback) bnw_cb_expect_null_owner,
-                              NULL);
-  g_main_loop_run (loop);
-  g_dbus_connection_set_exit_on_close (g_bus_name_watcher_get_connection (w), FALSE);
-  g_object_unref (w);
-
-  /* now start owning a name */
-  o = g_bus_name_owner_new (G_BUS_TYPE_SESSION,
-                            "org.gtk.Test.Name1",
-                            G_BUS_NAME_OWNER_FLAGS_NONE,
-                            NULL,
-                            (GAsyncReadyCallback) bnw_on_owner_cb_expect_success,
-                            NULL);
-  g_main_loop_run (loop);
-
-  /* now we should expect to get the name owner in the callback when constructing the watcher */
-  w = g_bus_name_watcher_new (G_BUS_TYPE_SESSION,
-                              "org.gtk.Test.Name1",
-                              NULL,
-                              (GAsyncReadyCallback) bnw_cb_expect_owner,
-                              NULL);
-  g_main_loop_run (loop);
-  g_assert_cmpstr (g_bus_name_watcher_get_name_owner (w), !=, NULL);
-
-  /* now stop owning the name and check that the watcher emits the ::name-vanished signal */
-  g_signal_connect (w, "name-vanished", G_CALLBACK (bnw_on_name_vanished), NULL);
-  g_object_unref (o);
-  g_main_loop_run (loop);
-  g_assert_cmpstr (g_bus_name_watcher_get_name_owner (w), ==, NULL);
-  g_signal_handlers_disconnect_by_func (w, bnw_on_name_vanished, NULL);
-
-  /* start owning the name and check that the watcher emits the ::name-appeared signal */
-  g_signal_connect (w, "name-appeared", G_CALLBACK (bnw_on_name_appeared), NULL);
-  o = g_bus_name_owner_new (G_BUS_TYPE_SESSION,
-                            "org.gtk.Test.Name1",
-                            G_BUS_NAME_OWNER_FLAGS_NONE,
-                            NULL,
-                            NULL,
-                            NULL);
-  g_main_loop_run (loop);
-  g_assert_cmpstr (g_bus_name_watcher_get_name_owner (w), !=, NULL);
-  g_signal_handlers_disconnect_by_func (w, bnw_on_name_appeared, NULL);
-
-  /* now check that we get ::name-vanished when the bus goes away */
-  g_signal_connect (w, "name-vanished", G_CALLBACK (bnw_on_name_vanished), NULL);
-  g_dbus_connection_set_exit_on_close (g_bus_name_watcher_get_connection (w), FALSE);
-  session_bus_down ();
-  g_main_loop_run (loop);
-  g_assert_cmpstr (g_bus_name_watcher_get_name_owner (w), ==, NULL);
-  g_signal_handlers_disconnect_by_func (w, bnw_on_name_vanished, NULL);
-
-  /* and ::name-appeared when the bus comes back up (this works because
-   * GBusNameOwner will claim the name on reconnection)
-   */
-  g_signal_connect (w, "name-appeared", G_CALLBACK (bnw_on_name_appeared), NULL);
-  session_bus_up ();
-  g_main_loop_run (loop);
-  g_assert_cmpstr (g_bus_name_watcher_get_name_owner (w), !=, NULL);
-  g_signal_handlers_disconnect_by_func (w, bnw_on_name_appeared, NULL);
-
-  /* check that singleton handling works, e.g. a new watcher for the _same_
-   * connection and name gives the same object
-   */
-  val = FALSE;
-  w2 = g_bus_name_watcher_new (G_BUS_TYPE_SESSION,
-                               "org.gtk.Test.Name1",
-                               NULL,
-                               (GAsyncReadyCallback) bnw_cb_singleton,
-                               &val);
-  g_assert (w == w2);
-  g_assert (!val);
-  g_main_loop_run (loop);
-  g_assert (val);
-  g_object_unref (w2);
-  w2 = g_bus_name_watcher_new (G_BUS_TYPE_SESSION,
-                               "org.gtk.Test.Name2",
-                               NULL,
-                               NULL,
-                               NULL);
-  g_assert (w != w2);
-  g_object_unref (w2);
-
-  /* stop watching */
-  g_object_unref (w);
-
-  /* kill owner */
-  g_object_unref (o);
-
-  /* tear down bus */
-  session_bus_down ();
-}
-
-/* ---------------------------------------------------------------------------------------------------- */
 
 int
 main (int   argc,
@@ -1374,7 +453,5 @@ main (int   argc,
 
   g_test_add_func ("/gdbus/message-bus-connections", test_message_bus_connections);
   g_test_add_func ("/gdbus/dbus-connection-send", test_dbus_connection_send);
-  g_test_add_func ("/gdbus/bus-name-owner", test_bus_name_owner);
-  g_test_add_func ("/gdbus/bus-name-watcher", test_bus_name_watcher);
   return g_test_run();
 }
diff --git a/gdbus/tests/names.c b/gdbus/tests/names.c
new file mode 100644
index 0000000..f446f1f
--- /dev/null
+++ b/gdbus/tests/names.c
@@ -0,0 +1,653 @@
+/* GLib testing framework examples and tests
+ *
+ * Copyright (C) 2008-2009 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: David Zeuthen <davidz redhat com>
+ */
+
+#include <gdbus/gdbus.h>
+#include <unistd.h>
+
+#include "tests.h"
+
+/* all tests rely on a shared mainloop */
+static GMainLoop *loop = NULL;
+
+/* ---------------------------------------------------------------------------------------------------- */
+/* Test that GBusNameOwner works correctly */
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+on_name_acquired_expect_failure (GBusNameOwner *owner,
+                                 gpointer       user_data)
+{
+  g_assert_not_reached ();
+}
+
+static void
+on_name_lost_expect_failure (GBusNameOwner *owner,
+                             gpointer       user_data)
+{
+  g_assert_not_reached ();
+}
+
+static void
+on_owner_cb_expect_failure (GBusNameOwner *owner,
+                            GAsyncResult  *result,
+                            gpointer       user_data)
+{
+  gboolean *val = user_data;
+  gboolean ret;
+  GError *error;
+
+  error = NULL;
+  ret = g_bus_name_owner_new_finish (owner,
+                                     result,
+                                     &error);
+  _g_assert_error_domain (error, G_DBUS_ERROR);
+  g_error_free (error);
+  g_assert (!ret);
+  g_assert (!g_bus_name_owner_get_owns_name (owner));
+
+  *val = TRUE;
+  g_main_loop_quit (loop);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+on_name_acquired_expect_success (GBusNameOwner *owner,
+                                 gpointer       user_data)
+{
+  gboolean *val2 = user_data;
+
+  *val2 = TRUE;
+}
+
+static void
+on_name_lost_expect_success (GBusNameOwner *owner,
+                             gpointer       user_data)
+{
+  g_assert_not_reached ();
+}
+
+static void
+on_owner_cb_expect_success (GBusNameOwner *owner,
+                            GAsyncResult  *result,
+                            gpointer       user_data)
+{
+  gboolean *val = user_data;
+  gboolean ret;
+  GError *error;
+
+  error = NULL;
+  ret = g_bus_name_owner_new_finish (owner,
+                                     result,
+                                     &error);
+  g_assert_no_error (error);
+  g_assert (ret);
+  g_assert (g_bus_name_owner_get_owns_name (owner));
+
+  *val = TRUE;
+  g_main_loop_quit (loop);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+on_name_lost_bringing_down_the_bus (GBusNameOwner *owner,
+                                    gpointer       user_data)
+{
+  gboolean *val = user_data;
+
+  *val = TRUE;
+
+  g_main_loop_quit (loop);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+on_name_acquired_bringing_up_the_bus (GBusNameOwner *owner,
+                                      gpointer       user_data)
+{
+  gboolean *val = user_data;
+
+  *val = TRUE;
+
+  g_main_loop_quit (loop);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+on_owner_cb_expect_failure2 (GBusNameOwner *owner,
+                             GAsyncResult  *result,
+                             gpointer       user_data)
+{
+  gboolean *val = user_data;
+  gboolean ret;
+  GError *error;
+
+  error = NULL;
+  ret = g_bus_name_owner_new_finish (owner,
+                                     result,
+                                     &error);
+  g_assert_no_error (error);
+  g_assert (ret);
+  g_assert (!g_bus_name_owner_get_owns_name (owner));
+
+  *val = TRUE;
+  g_main_loop_quit (loop);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+static void
+on_owner_for_connection_cb_expect_failure (GBusNameOwner *owner,
+                                           GAsyncResult  *result,
+                                           gpointer       user_data)
+{
+  gboolean *val = user_data;
+  gboolean ret;
+  GError *error;
+
+  error = NULL;
+  ret = g_bus_name_owner_new_for_connection_finish (owner,
+                                                    result,
+                                                    &error);
+  g_assert_no_error (error);
+  g_assert (ret);
+  g_assert (!g_bus_name_owner_get_owns_name (owner));
+
+  *val = TRUE;
+  g_main_loop_quit (loop);
+}
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+on_name_acquired_for_o2 (GBusNameOwner *owner,
+                         gpointer       user_data)
+{
+  gboolean *val = user_data;
+
+  *val = TRUE;
+
+  g_main_loop_quit (loop);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+on_name_lost_for_o2 (GBusNameOwner *owner,
+                     gpointer       user_data)
+{
+  gboolean *val = user_data;
+
+  *val = TRUE;
+
+  g_main_loop_quit (loop);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+get_connection_callback_private (GDBusConnection *connection,
+                                 GAsyncResult    *result,
+                                 gpointer         user_data)
+{
+  GError *error;
+  gboolean ret;
+
+  error = NULL;
+  ret = g_dbus_connection_bus_get_private_finish (connection,
+                                                  result,
+                                                  &error);
+  g_assert_no_error (error);
+  g_assert (ret);
+  g_main_loop_quit (loop);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+test_bus_name_owner (void)
+{
+  GDBusConnection *private_connection;
+  GBusNameOwner *o;
+  GBusNameOwner *o2;
+  gboolean val;
+  gboolean val2;
+
+  /* first try to own a name when there is no bus */
+  val = FALSE;
+  o = g_bus_name_owner_new (G_BUS_TYPE_SESSION,
+                            "org.gtk.Test.Name1",
+                            G_BUS_NAME_OWNER_FLAGS_NONE,
+                            NULL,
+                            (GAsyncReadyCallback) on_owner_cb_expect_failure,
+                            &val);
+  g_signal_connect (o, "name-acquired", G_CALLBACK (on_name_acquired_expect_failure), NULL);
+  g_signal_connect (o, "name-lost", G_CALLBACK (on_name_lost_expect_failure), NULL);
+  g_main_loop_run (loop);
+  g_assert (val);
+  g_object_unref (o);
+
+  /* try again, this time with a bus instance - the ::name-acquired signal should fire before the async reply */
+  session_bus_up ();
+  val = FALSE;
+  val2 = FALSE;
+  o = g_bus_name_owner_new (G_BUS_TYPE_SESSION,
+                            "org.gtk.Test.Name1",
+                            G_BUS_NAME_OWNER_FLAGS_NONE,
+                            NULL,
+                            (GAsyncReadyCallback) on_owner_cb_expect_success,
+                            &val);
+  g_signal_connect (o, "name-acquired", G_CALLBACK (on_name_acquired_expect_success), &val2);
+  g_signal_connect (o, "name-lost", G_CALLBACK (on_name_lost_expect_success), NULL);
+  g_main_loop_run (loop);
+  g_assert (val);
+  g_assert (val2);
+
+  /* try owning the same name again... this should not fail, we should get the same object
+   * as o because of singleton handling
+   */
+  o2 = g_bus_name_owner_new (G_BUS_TYPE_SESSION,
+                             "org.gtk.Test.Name1",
+                             G_BUS_NAME_OWNER_FLAGS_NONE,
+                             NULL,
+                             (GAsyncReadyCallback) on_owner_cb_expect_success,
+                             &val);
+  g_main_loop_run (loop);
+  g_assert (val);
+  g_assert (o2 == o);
+  g_object_unref (o2);
+
+  /* now kill o.. and then attempt to own the same name again.. */
+  g_object_unref (o);
+  val = FALSE;
+  val2 = FALSE;
+  o = g_bus_name_owner_new (G_BUS_TYPE_SESSION,
+                            "org.gtk.Test.Name1",
+                            G_BUS_NAME_OWNER_FLAGS_NONE,
+                            NULL,
+                            (GAsyncReadyCallback) on_owner_cb_expect_success,
+                            &val);
+  g_signal_connect (o, "name-acquired", G_CALLBACK (on_name_acquired_expect_success), &val2);
+  g_main_loop_run (loop);
+  g_assert (val);
+  g_assert (val2);
+
+  /* now bring down the bus.. this should trigger the ::name-lost signal */
+  val = FALSE;
+  g_signal_connect (o, "name-lost", G_CALLBACK (on_name_lost_bringing_down_the_bus), &val);
+  g_dbus_connection_set_exit_on_close (g_bus_name_owner_get_connection (o), FALSE);
+  session_bus_down ();
+  g_main_loop_run (loop);
+  g_assert (val);
+
+  /* bring the bus back up... then we should get the ::name-acquired signal
+   *
+   * Can't use existing signal handler since it doesn't quit the main loop.
+   */
+  val = FALSE;
+  g_signal_handlers_disconnect_by_func (o, on_name_acquired_expect_success, &val2);
+  g_signal_connect (o, "name-acquired", G_CALLBACK (on_name_acquired_bringing_up_the_bus), &val);
+  session_bus_up ();
+  g_main_loop_run (loop);
+  g_assert (val);
+
+  /* Create a private connection and a bus name owner for that connection, o2, that tries to own
+   * the name with _REPLACE and _ALLOW_REPLACEMENT. This should fail because o already owns the name
+   * and didn't specify ALLOW_REPLACEMENT.
+   */
+  private_connection = g_dbus_connection_bus_get_private (G_BUS_TYPE_SESSION,
+                                                          NULL,                            /* cancellable */
+                                                          (GAsyncReadyCallback) get_connection_callback_private,
+                                                          NULL);                           /* user_data */
+  g_main_loop_run (loop);
+  val = FALSE;
+  o2 = g_bus_name_owner_new_for_connection (private_connection,
+                                            "org.gtk.Test.Name1",
+                                            G_BUS_NAME_OWNER_FLAGS_REPLACE |
+                                            G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT,
+                                            NULL,
+                                            (GAsyncReadyCallback) on_owner_for_connection_cb_expect_failure,
+                                            &val);
+  g_object_unref (private_connection);
+  g_main_loop_run (loop);
+  g_assert (!g_bus_name_owner_get_owns_name (o2));
+  g_assert (val);
+
+  /* OK, so o is owning the name and o2 is in queue. Kill o and check that o2 acquires the name. */
+  val = FALSE;
+  g_signal_connect (o2, "name-acquired", G_CALLBACK (on_name_acquired_for_o2), &val);
+  g_object_unref (o);
+  g_main_loop_run (loop);
+  g_assert (g_bus_name_owner_get_owns_name (o2));
+  g_assert (val);
+
+  /* Since o2 specified ALLOW_REPLACEMENT, check that o can claim the name only when using REPLACE */
+  val = FALSE;
+  o = g_bus_name_owner_new (G_BUS_TYPE_SESSION,
+                            "org.gtk.Test.Name1",
+                            G_BUS_NAME_OWNER_FLAGS_NONE,
+                            NULL,
+                            (GAsyncReadyCallback) on_owner_cb_expect_failure2,
+                            &val);
+  g_main_loop_run (loop);
+  g_assert (val);
+  g_assert (!g_bus_name_owner_get_owns_name (o));
+  g_assert (g_bus_name_owner_get_owns_name (o2));
+  g_object_unref (o);
+  /* and now with REPLACE */
+  val = FALSE;
+  o = g_bus_name_owner_new (G_BUS_TYPE_SESSION,
+                            "org.gtk.Test.Name1",
+                            G_BUS_NAME_OWNER_FLAGS_REPLACE,
+                            NULL,
+                            (GAsyncReadyCallback) on_owner_cb_expect_success,
+                            &val);
+  g_main_loop_run (loop);
+  g_assert (val);
+  g_assert (g_bus_name_owner_get_owns_name (o));
+  /* we might not have gotten the signal for o2 yet */
+  if (g_bus_name_owner_get_owns_name (o2))
+    {
+      val = FALSE;
+      g_signal_connect (o2, "name-lost", G_CALLBACK (on_name_lost_for_o2), &val);
+      g_main_loop_run (loop);
+      g_assert (val);
+      g_assert (!g_bus_name_owner_get_owns_name (o2));
+    }
+  /* make sure we don't exit when bringing down the bus */
+  g_dbus_connection_set_exit_on_close (g_bus_name_owner_get_connection (o), FALSE);
+  g_dbus_connection_set_exit_on_close (g_bus_name_owner_get_connection (o2), FALSE);
+  g_object_unref (o2);
+  g_object_unref (o);
+
+  session_bus_down ();
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+/* Test that GBusNameWatcher works correctly */
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+bnw_cb_expect_error (GBusNameWatcher *watcher,
+                     GAsyncResult    *res,
+                     gpointer         user_data)
+{
+  GError *error;
+  gboolean ret;
+
+  error = NULL;
+  ret = g_bus_name_watcher_new_finish (watcher,
+                                       res,
+                                       &error);
+  _g_assert_error_domain (error, G_DBUS_ERROR);
+  g_error_free (error);
+  g_assert (!ret);
+  g_assert_cmpstr (g_bus_name_watcher_get_name_owner (watcher), ==, NULL);
+
+  g_main_loop_quit (loop);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+bnw_cb_expect_null_owner (GBusNameWatcher *watcher,
+                          GAsyncResult    *res,
+                          gpointer         user_data)
+{
+  GError *error;
+  gboolean ret;
+
+  error = NULL;
+  ret = g_bus_name_watcher_new_finish (watcher,
+                                       res,
+                                       &error);
+  _g_assert_error_domain (error, G_DBUS_ERROR);
+  g_assert (!ret);
+  g_assert_cmpstr (g_bus_name_watcher_get_name_owner (watcher), ==, NULL);
+
+  g_main_loop_quit (loop);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+bnw_on_owner_cb_expect_success (GBusNameOwner *owner,
+                                GAsyncResult  *result,
+                                gpointer       user_data)
+{
+  gboolean ret;
+  GError *error;
+
+  error = NULL;
+  ret = g_bus_name_owner_new_finish (owner,
+                                     result,
+                                     &error);
+  g_assert_no_error (error);
+  g_assert (ret);
+  g_assert (g_bus_name_owner_get_owns_name (owner));
+
+  g_main_loop_quit (loop);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+bnw_cb_expect_owner (GBusNameWatcher *watcher,
+                     GAsyncResult    *res,
+                     gpointer         user_data)
+{
+  GError *error;
+  gboolean ret;
+
+  error = NULL;
+  ret = g_bus_name_watcher_new_finish (watcher,
+                                       res,
+                                       &error);
+  g_assert_no_error (error);
+  g_assert (ret);
+  g_assert_cmpstr (g_bus_name_watcher_get_name_owner (watcher), !=, NULL);
+
+  g_main_loop_quit (loop);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+bnw_on_name_vanished (GBusNameWatcher *watcher,
+                      gpointer         user_data)
+{
+  g_main_loop_quit (loop);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+bnw_on_name_appeared (GBusNameWatcher *watcher,
+                      gpointer         user_data)
+{
+  g_main_loop_quit (loop);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+bnw_cb_singleton (GBusNameWatcher *watcher,
+                  GAsyncResult    *res,
+                  gpointer         user_data)
+{
+  gboolean *val = user_data;
+  GError *error;
+  gboolean ret;
+
+  error = NULL;
+  ret = g_bus_name_watcher_new_finish (watcher,
+                                       res,
+                                       &error);
+  g_assert_no_error (error);
+  g_assert (ret);
+  g_assert_cmpstr (g_bus_name_watcher_get_name_owner (watcher), !=, NULL);
+
+  *val = TRUE;
+
+  g_main_loop_quit (loop);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+test_bus_name_watcher (void)
+{
+  GBusNameWatcher *w;
+  GBusNameWatcher *w2;
+  GBusNameOwner *o;
+  gboolean val;
+
+  /* if no session bus is available, expect the callback to expect an error */
+  w = g_bus_name_watcher_new (G_BUS_TYPE_SESSION,
+                              "org.gtk.Test.Name1",
+                              NULL,
+                              (GAsyncReadyCallback) bnw_cb_expect_error,
+                              NULL);
+  g_main_loop_run (loop);
+  g_dbus_connection_set_exit_on_close (g_bus_name_watcher_get_connection (w), FALSE);
+  g_object_unref (w);
+
+  /* if a session bus is available, expect the callback to fail */
+  session_bus_up ();
+  w = g_bus_name_watcher_new (G_BUS_TYPE_SESSION,
+                              "org.gtk.Test.Name1",
+                              NULL,
+                              (GAsyncReadyCallback) bnw_cb_expect_null_owner,
+                              NULL);
+  g_main_loop_run (loop);
+  g_dbus_connection_set_exit_on_close (g_bus_name_watcher_get_connection (w), FALSE);
+  g_object_unref (w);
+
+  /* now start owning a name */
+  o = g_bus_name_owner_new (G_BUS_TYPE_SESSION,
+                            "org.gtk.Test.Name1",
+                            G_BUS_NAME_OWNER_FLAGS_NONE,
+                            NULL,
+                            (GAsyncReadyCallback) bnw_on_owner_cb_expect_success,
+                            NULL);
+  g_main_loop_run (loop);
+
+  /* now we should expect to get the name owner in the callback when constructing the watcher */
+  w = g_bus_name_watcher_new (G_BUS_TYPE_SESSION,
+                              "org.gtk.Test.Name1",
+                              NULL,
+                              (GAsyncReadyCallback) bnw_cb_expect_owner,
+                              NULL);
+  g_main_loop_run (loop);
+  g_assert_cmpstr (g_bus_name_watcher_get_name_owner (w), !=, NULL);
+
+  /* now stop owning the name and check that the watcher emits the ::name-vanished signal */
+  g_signal_connect (w, "name-vanished", G_CALLBACK (bnw_on_name_vanished), NULL);
+  g_object_unref (o);
+  g_main_loop_run (loop);
+  g_assert_cmpstr (g_bus_name_watcher_get_name_owner (w), ==, NULL);
+  g_signal_handlers_disconnect_by_func (w, bnw_on_name_vanished, NULL);
+
+  /* start owning the name and check that the watcher emits the ::name-appeared signal */
+  g_signal_connect (w, "name-appeared", G_CALLBACK (bnw_on_name_appeared), NULL);
+  o = g_bus_name_owner_new (G_BUS_TYPE_SESSION,
+                            "org.gtk.Test.Name1",
+                            G_BUS_NAME_OWNER_FLAGS_NONE,
+                            NULL,
+                            NULL,
+                            NULL);
+  g_main_loop_run (loop);
+  g_assert_cmpstr (g_bus_name_watcher_get_name_owner (w), !=, NULL);
+  g_signal_handlers_disconnect_by_func (w, bnw_on_name_appeared, NULL);
+
+  /* now check that we get ::name-vanished when the bus goes away */
+  g_signal_connect (w, "name-vanished", G_CALLBACK (bnw_on_name_vanished), NULL);
+  g_dbus_connection_set_exit_on_close (g_bus_name_watcher_get_connection (w), FALSE);
+  session_bus_down ();
+  g_main_loop_run (loop);
+  g_assert_cmpstr (g_bus_name_watcher_get_name_owner (w), ==, NULL);
+  g_signal_handlers_disconnect_by_func (w, bnw_on_name_vanished, NULL);
+
+  /* and ::name-appeared when the bus comes back up (this works because
+   * GBusNameOwner will claim the name on reconnection)
+   */
+  g_signal_connect (w, "name-appeared", G_CALLBACK (bnw_on_name_appeared), NULL);
+  session_bus_up ();
+  g_main_loop_run (loop);
+  g_assert_cmpstr (g_bus_name_watcher_get_name_owner (w), !=, NULL);
+  g_signal_handlers_disconnect_by_func (w, bnw_on_name_appeared, NULL);
+
+  /* check that singleton handling works, e.g. a new watcher for the _same_
+   * connection and name gives the same object
+   */
+  val = FALSE;
+  w2 = g_bus_name_watcher_new (G_BUS_TYPE_SESSION,
+                               "org.gtk.Test.Name1",
+                               NULL,
+                               (GAsyncReadyCallback) bnw_cb_singleton,
+                               &val);
+  g_assert (w == w2);
+  g_assert (!val);
+  g_main_loop_run (loop);
+  g_assert (val);
+  g_object_unref (w2);
+  w2 = g_bus_name_watcher_new (G_BUS_TYPE_SESSION,
+                               "org.gtk.Test.Name2",
+                               NULL,
+                               NULL,
+                               NULL);
+  g_assert (w != w2);
+  g_object_unref (w2);
+
+  /* stop watching */
+  g_object_unref (w);
+
+  /* kill owner */
+  g_object_unref (o);
+
+  /* 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/bus-name-owner", test_bus_name_owner);
+  g_test_add_func ("/gdbus/bus-name-watcher", test_bus_name_watcher);
+  return g_test_run();
+}
diff --git a/gdbus/tests/proxy.c b/gdbus/tests/proxy.c
new file mode 100644
index 0000000..d24d4df
--- /dev/null
+++ b/gdbus/tests/proxy.c
@@ -0,0 +1,186 @@
+/* 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 <gdbus/gdbus.h>
+#include <unistd.h>
+
+#include "tests.h"
+
+/* all tests rely on a shared mainloop */
+static GMainLoop *loop = NULL;
+
+/* ---------------------------------------------------------------------------------------------------- */
+/* Test GDBusProxy */
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+proxy_on_name_appeared (GDBusConnection *connection,
+                        const gchar     *name,
+                        const gchar     *name_owner,
+                        gpointer         user_data)
+{
+  GDBusProxy *frob;
+  GError     *error;
+  gboolean   ret;
+  gchar      v_byte;
+  gboolean   v_boolean;
+  gint       v_int16;
+  guint      v_uint16;
+  gint       v_int32;
+  guint      v_uint32;
+  gint64     v_int64;
+  guint64    v_uint64;
+  gdouble    v_double;
+  gchar     *v_string;
+  gchar     *v_object_path;
+  gchar     *v_signature;
+
+  error = NULL;
+
+  frob = g_dbus_proxy_new (connection,
+                           name,
+                           "/com/example/TestObject",
+                           "com.example.Frob");
+
+  ret = g_dbus_proxy_invoke_method_sync (frob,
+                                         "HelloWorld",
+                                         "s", "s",
+                                         -1, NULL, &error,
+                                         G_TYPE_STRING, "Hello World!", G_TYPE_INVALID,
+                                         G_TYPE_STRING, &v_string, G_TYPE_INVALID);
+  g_assert_no_error (error);
+  g_assert (ret);
+  g_assert_cmpstr (v_string, ==, "You greeted me with 'Hello World!'. Thanks!");
+  g_free (v_string);
+
+  ret = g_dbus_proxy_invoke_method_sync (frob,
+                                         "TestPrimitiveTypes",
+                                         "ybnqiuxtdsog", "ybnqiuxtdsog",
+                                         -1, NULL, &error,
+                                         /* in values */
+                                         G_TYPE_CHAR, 1,
+                                         G_TYPE_BOOLEAN, TRUE,
+                                         G_TYPE_INT, 2,                  /* gint16 */
+                                         G_TYPE_UINT, 3,                 /* guint16 */
+                                         G_TYPE_INT, 4,
+                                         G_TYPE_UINT, 5,
+                                         G_TYPE_INT64, (gint64) 6,
+                                         G_TYPE_UINT64, (guint64) 7,
+                                         G_TYPE_DOUBLE, 8.1,
+                                         G_TYPE_STRING, "a string",
+                                         G_TYPE_STRING, "/foo",          /* an object path */
+                                         G_TYPE_STRING, "ass",           /* a signature */
+                                         G_TYPE_INVALID,
+                                         /* out values */
+                                         G_TYPE_CHAR, &v_byte,
+                                         G_TYPE_BOOLEAN, &v_boolean,
+                                         G_TYPE_INT, &v_int16,           /* gint16 */
+                                         G_TYPE_UINT, &v_uint16,         /* guint16 */
+                                         G_TYPE_INT, &v_int32,
+                                         G_TYPE_UINT, &v_uint32,
+                                         G_TYPE_INT64, &v_int64,
+                                         G_TYPE_UINT64, &v_uint64,
+                                         G_TYPE_DOUBLE, &v_double,
+                                         G_TYPE_STRING, &v_string,
+                                         G_TYPE_STRING, &v_object_path,  /* an object path */
+                                         G_TYPE_STRING, &v_signature,    /* a signature */
+                                         G_TYPE_INVALID);
+  g_assert_no_error (error);
+  g_assert (ret);
+  g_assert_cmpint   (v_byte, ==, 1 + 1);
+  g_assert          (!v_boolean);
+  g_assert_cmpint   (v_int16, ==, 2 + 1);
+  g_assert_cmpint   (v_uint16, ==, 3 + 1);
+  g_assert_cmpint   (v_int32, ==, 4 + 1);
+  g_assert_cmpint   (v_uint32, ==, 5 + 1);
+  g_assert_cmpint   (v_int64, ==, 6 + 1);
+  g_assert_cmpint   (v_uint64, ==, 7 + 1);
+  g_assert_cmpfloat (v_double, ==, -8.1 + 0.123);
+  g_assert_cmpstr   (v_string, ==, "a stringa string");
+  g_assert_cmpstr   (v_object_path, ==, "/foo/modified");
+  g_assert_cmpstr   (v_signature, ==, "assass");
+  g_free (v_string);
+  g_free (v_object_path);
+  g_free (v_signature);
+
+  g_object_unref (frob);
+
+  g_main_loop_quit (loop);
+}
+
+static void
+proxy_on_name_vanished (GDBusConnection *connection,
+                        const gchar     *name,
+                        gpointer         user_data)
+{
+}
+
+static void
+test_proxy (void)
+{
+  guint watcher_id;
+
+  session_bus_up ();
+
+  watcher_id = g_bus_watch_name (G_BUS_TYPE_SESSION,
+                                 "com.example.TestService",
+                                 proxy_on_name_appeared,
+                                 proxy_on_name_vanished,
+                                 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 (200 * 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);
+
+  g_bus_unwatch_name (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/gdbus/tests/sessionbus.c b/gdbus/tests/sessionbus.c
new file mode 100644
index 0000000..b4a466e
--- /dev/null
+++ b/gdbus/tests/sessionbus.c
@@ -0,0 +1,335 @@
+/* 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];
+  guint 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;
+  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) != 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,
+                                 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));
+
+  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 (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/gdbus/tests/sessionbus.h b/gdbus/tests/sessionbus.h
new file mode 100644
index 0000000..38229a6
--- /dev/null
+++ b/gdbus/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 <gdbus/gdbus.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/gdbus/tests/tests.h b/gdbus/tests/tests.h
new file mode 100644
index 0000000..d533240
--- /dev/null
+++ b/gdbus/tests/tests.h
@@ -0,0 +1,43 @@
+/* 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 <gdbus/gdbus.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)
+
+G_END_DECLS
+
+#endif /* __TESTS_H__ */
diff --git a/gdbus/tests/testserver.py b/gdbus/tests/testserver.py
new file mode 100755
index 0000000..9c30999
--- /dev/null
+++ b/gdbus/tests/testserver.py
@@ -0,0 +1,420 @@
+#!/usr/bin/python
+
+import gobject
+
+import dbus
+import dbus.service
+import dbus.mainloop.glib
+
+class TestException(dbus.DBusException):
+    _dbus_error_name = 'com.example.TestException'
+
+
+class TestService(dbus.service.Object):
+
+    # $ dbus-send --session --print-reply --dest=com.example.TestService /com/example/TestService com.example.TestService.HelloWorld string:Hi!
+    @dbus.service.method("com.example.Frob",
+                         in_signature='s', out_signature='s')
+    def HelloWorld(self, hello_message):
+        if str(hello_message) == 'Yo':
+            raise TestException('Yo is not a proper greeting')
+        else:
+            return "You greeted me with '%s'. Thanks!"%(str(hello_message))
+
+    # $ dbus-send --session --print-reply --dest=com.example.TestService /com/example/TestService com.example.TestService.TestPrimitiveTypes byte:1 boolean:false int16:160 uint16:161 int32:320 uint32:321 int64:640 uint64:641 double:4.5 string:foo objpath:/foo
+    @dbus.service.method("com.example.Frob",
+                         in_signature='ybnqiuxtdsog', out_signature='ybnqiuxtdsog')
+    def TestPrimitiveTypes(self, val_byte, val_boolean, val_int16, val_uint16, val_int32, val_uint32, val_int64, val_uint64, val_double, val_string, val_objpath, val_signature):
+        return val_byte + 1, not val_boolean, val_int16 + 1, val_uint16 + 1, val_int32 + 1, val_uint32 + 1, val_int64 + 1, val_uint64 + 1, -val_double + 0.123, val_string * 2, val_objpath + "/modified", val_signature * 2
+
+    # $ dbus-send --session --print-reply --dest=com.example.TestService /com/example/TestService com.example.TestService.TestArrayOfPrimitiveTypes array:byte:1,2 array:boolean:false,true array:int16:160,170 array:uint16:161,171 array:int32:320,330 array:uint32:321,331 array:int64:640,650 array:uint64:641,651 array:double:4.5,5.5 array:string:foo,foo2 array:objpath:/foo,/foo2
+    @dbus.service.method("com.example.Frob",
+                         in_signature='ayabanaqaiauaxatadasao', out_signature='ayabanaqaiauaxatadasao')
+    def TestArrayOfPrimitiveTypes(self, val_byte, val_boolean, val_int16, val_uint16, val_int32, val_uint32, val_int64, val_uint64, val_double, val_string, val_objpath):
+        return val_byte*2, val_boolean*2, val_int16*2, val_uint16*2, val_int32*2, val_uint32*2, val_int64*2, val_uint64*2, val_double*2, val_string*2, val_objpath*2
+
+    # no support in dbus-send
+    @dbus.service.method("com.example.Frob",
+                         in_signature='(ii)(s(ii))', out_signature='(ii)(s(ii))')
+    def TestStructureTypes(self, s1, s2):
+        (x, y) = s1;
+        (desc, (x1, y1)) = s2;
+        return (x + 1, y + 1), ("replaced desc", (x1 + 2, y1 + 2))
+
+    # no support in dbus-send
+    @dbus.service.method("com.example.Frob",
+                         in_signature='a(s(ii)s)', out_signature='a(s(ii)s)')
+    def TestArrayOfStructureTypes(self, a):
+        r = []
+        n = 0
+        for i in a:
+            (d, (x, y), e) = i;
+            r.append(("%s_%d"%(d,n), (x + 1, y + 1), "%s_%d"%(e,n)))
+            n+=1
+        return r
+
+    # no support in dbus-send
+    @dbus.service.method("com.example.Frob",
+                         in_signature  = 'a{ss}a{oo}a{ii}a{yy}a{nn}a{qq}a{uu}a{bb}a{xx}a{tt}a{dd}',
+                         out_signature = 'a{ss}a{oo}a{ii}a{yy}a{nn}a{qq}a{uu}a{bb}a{xx}a{tt}a{dd}')
+    def TestHashTables(self, hss, hoo, hii, hyy, hnn, hqq, huu, hbb, hxx, htt, hdd):
+
+        ret_hss = {}
+        for i in hss:
+            ret_hss[i + "mod"] = hss[i]*2
+
+        ret_hoo = {}
+        for i in hoo:
+            ret_hoo[i + "/mod"] = hoo[i] + "/mod2"
+
+        ret_hii = {}
+        for i in hii:
+            ret_hii[i*2] = hii[i]*3
+
+        ret_hyy = {}
+        for i in hyy:
+            ret_hyy[i*2] = (hyy[i]*3) & 255
+
+        ret_hnn = {}
+        for i in hnn:
+            ret_hnn[i*2] = hnn[i]*3
+
+        ret_hqq = {}
+        for i in hqq:
+            ret_hqq[i*2] = hqq[i]*3
+
+        ret_huu = {}
+        for i in huu:
+            ret_huu[i*2] = huu[i]*3
+
+        ret_hbb = {}
+        for i in hbb:
+            ret_hbb[i] = True
+
+        ret_hxx = {}
+        for i in hxx:
+            ret_hxx[i + 2] = hxx[i] + 1
+
+        ret_htt = {}
+        for i in htt:
+            ret_htt[i + 2] = htt[i] + 1
+
+        ret_hdd = {}
+        for i in hdd:
+            ret_hdd[i + 2.5] = hdd[i] + 5.0
+
+        return ret_hss, ret_hoo, ret_hii, ret_hyy, ret_hnn, ret_hqq, ret_huu, ret_hbb, ret_hxx, ret_htt, ret_hdd
+
+    # no support in dbus-send
+    @dbus.service.method("com.example.Frob",
+                         in_signature  = 'a{s(ii)}',
+                         out_signature = 'a{s(s(ii))}')
+    def TestHashTableOfStructures(self, hash_of_points):
+        ret = {}
+        for s in hash_of_points:
+            (x, y) = hash_of_points[s]
+            ret[s + "_new"] = (s, (x + 100, y + 200))
+        return ret
+
+    # no support in dbus-send
+    @dbus.service.method("com.example.Frob",
+                         in_signature  = 'a{sas}a{sao}a{sai}a{say}a{san}a{saq}a{sau}a{sab}a{sax}a{sat}a{sad}a{sa(ii)}',
+                         out_signature = 'a{sas}a{sao}a{sai}a{say}a{san}a{saq}a{sau}a{sab}a{sax}a{sat}a{sad}a{sa(ii)}')
+    def TestHashTablesOfArrays(self, hsas, hsao, hsai, hsay, hsan, hsaq, hsau, hsab, hsax, hsat, hsad, hash_of_point_arrays):
+
+        ret_hsas = {}
+        for i in hsas:
+            ret_hsas[i] = hsas[i]*2
+
+        ret_hsao = {}
+        for i in hsao:
+            ret_hsao[i] = hsao[i]*2
+
+        ret_hsai = {"k" : [1, 2]}
+        ret_hsay = {"k" : [3, 4]}
+        ret_hsan = {"k" : [5, 6]}
+        ret_hsaq = {"k" : [7, 8]}
+        ret_hsau = {"k" : [9, 10]}
+        ret_hsab = {"k" : [True, False, True]}
+        ret_hsax = {"k" : [11, 12]}
+        ret_hsat = {"k" : [13, 14]}
+        ret_hsad = {"k" : [15.5, 16.5]}
+
+        ret_hash_of_point_arrays = {}
+        for s in hash_of_point_arrays:
+            point_array = hash_of_point_arrays[s]
+            new_point_array = []
+            for point in point_array:
+                (x, y) = point
+                new_point_array.append ((x + 100, y + 200))
+            ret_hash_of_point_arrays[s + "_new"] = new_point_array
+
+        return ret_hsas, ret_hsao, ret_hsai, ret_hsay, ret_hsan, ret_hsaq, ret_hsau, ret_hsab, ret_hsax, ret_hsat, ret_hsad, ret_hash_of_point_arrays
+
+    # no support in dbus-send
+    @dbus.service.method("com.example.Frob",
+                         in_signature  = 'a{sa{s(ii)}}',
+                         out_signature = 'a{sa{s(ii)}}')
+    def TestHashTableOfHashTablesOfStructures(self, hash_of_hash_of_points):
+
+        ret = {}
+        for h in hash_of_hash_of_points:
+            hash_of_points = hash_of_hash_of_points[h]
+            newh = {}
+            for s in hash_of_points:
+                (x, y) = hash_of_points[s]
+                newh[s + "_new_new"] = (x + 100, y + 200)
+            ret[h + "_new"] = newh
+
+        return ret
+
+    # no support in dbus-send
+    @dbus.service.method("com.example.Frob",
+                         in_signature  = 'aa{s(ii)}',
+                         out_signature = 'aa{s(ii)}')
+    def TestArrayOfHashTablesOfStructures(self, list_of_hash_of_points):
+
+        ret = []
+        for hash_of_points in list_of_hash_of_points:
+            newh = {}
+            for s in hash_of_points:
+                (x, y) = hash_of_points[s]
+                newh[s + "_new_new"] = (x + 100, y + 200)
+            ret.append(newh)
+
+        return ret
+
+    # no support in dbus-send
+    @dbus.service.method("com.example.Frob",
+                         in_signature  = 'aasaa(ii)aaoaaiaayaanaaqaauaabaaxaataadaaas',
+                         out_signature = 'aasaa(ii)aaoaaiaayaanaaqaauaabaaxaataadaaas')
+    def TestArrayOfArrays(self, aas, aastruct, aao, aai, aay, aan, aaq, aau, aab, aax, aat, aad, aaas):
+
+        ret_aas = aas*2;
+        ret_aastruct = aastruct*2;
+        ret_aao = aao*2;
+        ret_aai = aai*2;
+        ret_aay = aay*2;
+        ret_aan = aan*2;
+        ret_aaq = aaq*2;
+        ret_aau = aau*2;
+        ret_aab = aab*2;
+        ret_aax = aax*2;
+        ret_aat = aat*2;
+        ret_aad = aad*2;
+        ret_aaas = aaas*2
+
+        return ret_aas, ret_aastruct, ret_aao, ret_aai, ret_aay, ret_aan, ret_aaq, ret_aau, ret_aab, ret_aax, ret_aat, ret_aad, ret_aaas
+
+    # dbus-send --print-reply --session --dest=com.example.TestService /com/example/TestService com.example.TestService.TestVariantReturn string:o
+    @dbus.service.method("com.example.Frob",
+                         in_signature  = 's',
+                         out_signature = 'v')
+    def TestVariantReturn(self, desired_variant):
+
+        if desired_variant == "y":
+            ret = dbus.Byte(1)
+        elif desired_variant == "b":
+            ret = True
+        elif desired_variant == "n":
+            ret = dbus.Int16(2)
+        elif desired_variant == "q":
+            ret = dbus.UInt16(3)
+        elif desired_variant == "i":
+            ret = dbus.Int32(4)
+        elif desired_variant == "u":
+            ret = dbus.UInt32(5)
+        elif desired_variant == "x":
+            ret = dbus.Int64(6)
+        elif desired_variant == "t":
+            ret = dbus.UInt64(7)
+        elif desired_variant == "d":
+            ret = 7.5
+        elif desired_variant == "s":
+            ret = "a string"
+        elif desired_variant == "o":
+            ret = dbus.ObjectPath("/some/object/path")
+        elif desired_variant == "ai":
+            ret = []
+            ret.append(dbus.Int32(4))
+            ret.append(dbus.Int32(5))
+        elif desired_variant == "as":
+            ret = []
+            ret.append("a string")
+            ret.append("another string")
+        elif desired_variant == "point":
+            ret = (1, 2)
+        elif desired_variant == "dpoint":
+            ret = ("the desc", (3, 4))
+        elif desired_variant == "hash_of_points":
+            ret = {}
+            ret["key0"] = (1, 2)
+            ret["key1"] = (3, 4)
+            ret["key2"] = (5, 6)
+            ret["key3"] = (7, 8)
+        elif desired_variant == "unregistered_struct":
+            ret = ("string",
+                   dbus.Byte(2),
+                   dbus.Int16(3),
+                   4,
+                   [0, 1, 2, 3],
+                   {"rip" : 2, "rap" : 4, "rup" : 6},
+                   [(1, 2), (1, 2), (1, 2)],
+                   [[1, 2, 3], [4, 5, 6], [7, 8, 9]],
+                   [True, False, False],
+                   [[True, True], [False, True]],
+                   [dbus.Byte(0xca), dbus.Byte(0xfe), dbus.Byte(0xba), dbus.Byte(0xbe), dbus.Byte(0x00), dbus.Byte(0xde), dbus.Byte(0xad), dbus.Byte(0xbe), dbus.Byte(0xef)],
+                   {"huey" : (2, 1), "dewey" : (4, 2), "louie" : (6, 3)},
+                   {"innie" : [10, 11], "minnie" : [12, 13], "minie" : [14, 15], "moe" : [16, 17]},
+                   {dbus.Byte(0xa0) : [10, 11], dbus.Byte(0xa1) : [12, 13], dbus.Byte(0xa2) : [14, 15]})
+        else:
+            raise TestException("unknown desired variant '%s'"%desired_variant)
+
+        return ret
+
+    # $ dbus-send --print-reply --session --dest=com.example.TestService /com/example/TestService com.example.TestService.TestVariantReturn2 string:one
+    @dbus.service.method("com.example.Frob",
+                         in_signature  = 's',
+                         out_signature = 'ava{sv}(siivx)')
+    def TestVariantReturn2(self, desired_variant):
+
+        ret_av = []
+        ret_hsv = {}
+
+        if desired_variant == "one":
+            ret_av.append (dbus.Byte(1))
+            ret_av.append ("a string")
+            ret_av.append ((1, 2))
+            ret_av.append ([dbus.Int16(100), dbus.Int16(100), dbus.Int16(100)])
+            ret_av.append ({"rip" : 2, "rap" : 4, "rup" : 6})
+            ret_struct = ("ok, steady", 10, 11, "one", 2)
+        elif desired_variant == "two":
+            ret_hsv["1st"] = dbus.Byte(1)
+            ret_hsv["2nd"] = "a string"
+            ret_hsv["3rd"] = (1, 2)
+            ret_hsv["4th"] = [dbus.Int16(100), dbus.Int16(100), dbus.Int16(100)]
+            ret_hsv["5th"] = {"rip" : 2, "rap" : 4, "rup" : 6}
+            ret_struct = ("anger. fear. acceptance. CA$H!!", 10, 11, {"foo": 42, "bar": 43}, 2)
+        else:
+            raise TestException("unknown desired variant '%s'"%desired_variant)
+
+        return ret_av, ret_hsv, ret_struct
+
+    # $ dbus-send --print-reply --session --dest=com.example.TestService /com/example/TestService com.example.TestService.TestPassVariant variant:string:dbus-rocks
+    @dbus.service.method("com.example.Frob",
+                         in_signature  = 'v',
+                         out_signature = 's')
+    def TestPassVariant(self, variant):
+
+        return dbus.String(variant) + "-and-a-bag-of-chips"
+
+    # $ dbus-send --print-reply --session --dest=com.example.TestService /com/example/TestService com.example.TestService.TestPassVariant variant:string:dbus-rocks
+    @dbus.service.method("com.example.Frob",
+                         in_signature  = 'ava{sv}a{sav}(siivx)',
+                         out_signature = 's')
+    def TestPassVariant2(self, array_of_variants, dict_of_string_to_variants, dict_of_string_to_array_of_variants, struct_with_variant):
+
+        return dbus.String(array_of_variants) + dbus.String(dict_of_string_to_variants) + dbus.String(dict_of_string_to_array_of_variants) + dbus.String(struct_with_variant)
+
+    @dbus.service.method("org.freedesktop.DBus.Properties",
+                         in_signature  = 'ss',
+                         out_signature = 'v')
+    def Get(self, interface_name, property_name):
+
+        if interface_name == "com.example.Frob":
+            return self.frob_props[property_name]
+        elif interface_name == "com.example.Tweak":
+            return self.tweak_props[property_name]
+        else:
+            raise TestException("No such interface " + interface_name)
+
+    @dbus.service.method("org.freedesktop.DBus.Properties",
+                         in_signature  = 's',
+                         out_signature = 'a{sv}')
+    def GetAll(self, interface_name):
+        if interface_name == "com.example.Frob":
+            return self.frob_props
+        elif interface_name == "com.example.Tweak":
+            return self.tweak_props
+        else:
+            raise TestException("No such interface " + interface_name)
+
+    @dbus.service.method("com.example.Tweak",
+                         in_signature='s', out_signature='s')
+    def ICanHazGreetingz(self, hello_message):
+        return "Word. You haz greetz '%s'. KTHXBYE!"%(str(hello_message))
+
+    @dbus.service.method("com.example.Tweak",
+                         out_signature='s')
+    def GetServerUniqueName(self):
+        return session_bus.get_unique_name()
+
+
+
+
+
+    @dbus.service.signal("com.example.Tweak")
+    def NewzNotifz(self, newz):
+        pass
+
+    @dbus.service.method("com.example.Tweak",
+                         in_signature='s')
+    def BroadcastzNewz(self, newz_to_pass):
+        self.NewzNotifz("Word. Broadcastz '%s'. KTHXBYE!"%(str(newz_to_pass)))
+
+
+    @dbus.service.method("com.example.Twiddle",
+                         in_signature='s')
+    def BroadcastzNewzOnTwiddle(self, newz_to_pass):
+        string = "Sez '%s'. KTHXBYE!"%(str(newz_to_pass))
+        # manually construct the message since dbus-python doesn't appear
+        # to support identical signal names on different interfaces...
+        #
+        message = dbus.lowlevel.SignalMessage("/com/example/TestObject", "com.example.Twiddle", "NewzNotifz")
+        message.append(string)
+        session_bus.send_message(message)
+
+
+
+if __name__ == '__main__':
+    dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+    session_bus = dbus.SessionBus()
+    name = dbus.service.BusName("com.example.TestService", session_bus)
+
+    obj = TestService(session_bus, '/com/example/TestObject')
+
+    #print "Our unique name is %s"%(session_bus.get_unique_name())
+
+    obj.frob_props = {}
+    obj.frob_props["y"] = dbus.Byte(1)
+    obj.frob_props["b"] = dbus.Boolean(True)
+    obj.frob_props["n"] = dbus.Int16(2)
+    obj.frob_props["q"] = dbus.UInt16(3)
+    obj.frob_props["i"] = dbus.Int32(4)
+    obj.frob_props["u"] = dbus.UInt32(5)
+    obj.frob_props["x"] = dbus.Int64(6)
+    obj.frob_props["t"] = dbus.UInt64(7)
+    obj.frob_props["d"] = dbus.Double(7.5)
+    obj.frob_props["s"] = dbus.String("a string")
+    obj.frob_props["o"] = dbus.ObjectPath("/some/path")
+    obj.frob_props["ay"] = [dbus.Byte(1), dbus.Byte(11)]
+    obj.frob_props["ab"] = [dbus.Boolean(True), dbus.Boolean(False)]
+    obj.frob_props["an"] = [dbus.Int16(2), dbus.Int16(12)]
+    obj.frob_props["aq"] = [dbus.UInt16(3), dbus.UInt16(13)]
+    obj.frob_props["ai"] = [dbus.Int32(4), dbus.Int32(14)]
+    obj.frob_props["au"] = [dbus.UInt32(5), dbus.UInt32(15)]
+    obj.frob_props["ax"] = [dbus.Int64(6), dbus.Int64(16)]
+    obj.frob_props["at"] = [dbus.UInt64(7), dbus.UInt64(17)]
+    obj.frob_props["ad"] = [dbus.Double(7.5), dbus.Double(17.5)]
+    obj.frob_props["as"] = [dbus.String("a string"), dbus.String("another string")]
+    obj.frob_props["ao"] = [dbus.ObjectPath("/some/path"), dbus.ObjectPath("/another/path")]
+    obj.frob_props["foo"] = "a frobbed string"
+
+    obj.tweak_props = {}
+    obj.tweak_props["foo"] = "a tweaked string"
+    obj.tweak_props["bar"] = [1, 2]
+    obj.tweak_props["baz"] = (3, 4)
+
+    mainloop = gobject.MainLoop()
+    mainloop.run()



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]