More test coverage for settings backends
[platform/upstream/glib.git] / gio / tests / gdbus-export.c
index cfefe71..0d4d94b 100644 (file)
@@ -122,6 +122,15 @@ static const GDBusInterfaceInfo foo_interface_info =
   NULL,
 };
 
+/* Foo2 is just Foo without the properties */
+static const GDBusInterfaceInfo foo2_interface_info =
+{
+  -1,
+  "org.example.Foo2",
+  (GDBusMethodInfo **) &foo_method_info_pointers,
+  (GDBusSignalInfo **) &foo_signal_info_pointers,
+};
+
 static void
 foo_method_call (GDBusConnection       *connection,
                  const gchar           *sender,
@@ -137,7 +146,7 @@ foo_method_call (GDBusConnection       *connection,
       const gchar *input;
       gchar *output;
       g_variant_get (parameters, "(&s)", &input);
-      output = g_strdup_printf ("You passed the string `%s'. Jolly good!", input);
+      output = g_strdup_printf ("You passed the string '%s'. Jolly good!", input);
       g_dbus_method_invocation_return_value (invocation, g_variant_new ("(s)", output));
       g_free (output);
     }
@@ -160,7 +169,7 @@ foo_get_property (GDBusConnection       *connection,
 {
   GVariant *ret;
   gchar *s;
-  s = g_strdup_printf ("Property `%s' Is What It Is!", property_name);
+  s = g_strdup_printf ("Property '%s' Is What It Is!", property_name);
   ret = g_variant_new_string (s);
   g_free (s);
   return ret;
@@ -181,7 +190,7 @@ foo_set_property (GDBusConnection       *connection,
   g_set_error (error,
                G_DBUS_ERROR,
                G_DBUS_ERROR_SPAWN_FILE_INVALID,
-               "Returning some error instead of writing the value `%s' to the property `%s'",
+               "Returning some error instead of writing the value '%s' to the property '%s'",
                property_name, s);
   g_free (s);
   return FALSE;
@@ -628,7 +637,11 @@ subtree_introspect (GDBusConnection       *connection,
   /* VPs implement the Foo interface, EVPs implement the Bar interface. The root
    * does not implement any interfaces
    */
-  if (g_str_has_prefix (node, "vp"))
+  if (node == NULL)
+    {
+      return NULL;
+    }
+  else if (g_str_has_prefix (node, "vp"))
     {
       interfaces[0] = &foo_interface_info;
     }
@@ -636,10 +649,6 @@ subtree_introspect (GDBusConnection       *connection,
     {
       interfaces[0] = &bar_interface_info;
     }
-  else if (g_strcmp0 (node, "/") == 0)
-    {
-      return NULL;
-    }
   else
     {
       g_assert_not_reached ();
@@ -774,7 +783,7 @@ test_dispatch_thread_func (gpointer user_data)
   g_assert (value != NULL);
   g_assert (g_variant_is_of_type (value, G_VARIANT_TYPE ("(s)")));
   g_variant_get (value, "(&s)", &value_str);
-  g_assert_cmpstr (value_str, ==, "You passed the string `winwinwin'. Jolly good!");
+  g_assert_cmpstr (value_str, ==, "You passed the string 'winwinwin'. Jolly good!");
   g_variant_unref (value);
 
   error = NULL;
@@ -799,7 +808,7 @@ test_dispatch_thread_func (gpointer user_data)
                                   NULL,
                                   &error);
   g_assert_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS);
-  g_assert_cmpstr (error->message, ==, "GDBus.Error:org.freedesktop.DBus.Error.InvalidArgs: Type of message, `(s)', does not match expected type `()'");
+  g_assert_cmpstr (error->message, ==, "GDBus.Error:org.freedesktop.DBus.Error.InvalidArgs: Type of message, '(s)', does not match expected type '()'");
   g_error_free (error);
   g_assert (value == NULL);
 
@@ -812,7 +821,7 @@ test_dispatch_thread_func (gpointer user_data)
                                   NULL,
                                   &error);
   g_assert_error (error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD);
-  g_assert_cmpstr (error->message, ==, "GDBus.Error:org.freedesktop.DBus.Error.UnknownMethod: No such method `NonExistantMethod'");
+  g_assert_cmpstr (error->message, ==, "GDBus.Error:org.freedesktop.DBus.Error.UnknownMethod: No such method 'NonExistantMethod'");
   g_error_free (error);
   g_assert (value == NULL);
 
@@ -844,8 +853,9 @@ test_dispatch_thread_func (gpointer user_data)
   g_assert (g_variant_is_of_type (value, G_VARIANT_TYPE ("(v)")));
   g_variant_get (value, "(v)", &inner);
   g_assert (g_variant_is_of_type (inner, G_VARIANT_TYPE_STRING));
-  g_assert_cmpstr (g_variant_get_string (inner, NULL), ==, "Property `PropertyUno' Is What It Is!");
+  g_assert_cmpstr (g_variant_get_string (inner, NULL), ==, "Property 'PropertyUno' Is What It Is!");
   g_variant_unref (value);
+  g_variant_unref (inner);
 
   error = NULL;
   value = g_dbus_proxy_call_sync (foo_proxy,
@@ -859,7 +869,7 @@ test_dispatch_thread_func (gpointer user_data)
                                   &error);
   g_assert (value == NULL);
   g_assert_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS);
-  g_assert_cmpstr (error->message, ==, "GDBus.Error:org.freedesktop.DBus.Error.InvalidArgs: No such property `ThisDoesntExist'");
+  g_assert_cmpstr (error->message, ==, "GDBus.Error:org.freedesktop.DBus.Error.InvalidArgs: No such property 'ThisDoesntExist'");
   g_error_free (error);
 
   error = NULL;
@@ -874,7 +884,7 @@ test_dispatch_thread_func (gpointer user_data)
                                   &error);
   g_assert (value == NULL);
   g_assert_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS);
-  g_assert_cmpstr (error->message, ==, "GDBus.Error:org.freedesktop.DBus.Error.InvalidArgs: Property `NotReadable' is not readable");
+  g_assert_cmpstr (error->message, ==, "GDBus.Error:org.freedesktop.DBus.Error.InvalidArgs: Property 'NotReadable' is not readable");
   g_error_free (error);
 
   error = NULL;
@@ -890,7 +900,7 @@ test_dispatch_thread_func (gpointer user_data)
                                   &error);
   g_assert (value == NULL);
   g_assert_error (error, G_DBUS_ERROR, G_DBUS_ERROR_SPAWN_FILE_INVALID);
-  g_assert_cmpstr (error->message, ==, "GDBus.Error:org.freedesktop.DBus.Error.Spawn.FileInvalid: Returning some error instead of writing the value `NotReadable' to the property `'But Writable you are!''");
+  g_assert_cmpstr (error->message, ==, "GDBus.Error:org.freedesktop.DBus.Error.Spawn.FileInvalid: Returning some error instead of writing the value 'NotReadable' to the property ''But Writable you are!''");
   g_error_free (error);
 
   error = NULL;
@@ -906,7 +916,7 @@ test_dispatch_thread_func (gpointer user_data)
                                   &error);
   g_assert (value == NULL);
   g_assert_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS);
-  g_assert_cmpstr (error->message, ==, "GDBus.Error:org.freedesktop.DBus.Error.InvalidArgs: Property `NotWritable' is not writable");
+  g_assert_cmpstr (error->message, ==, "GDBus.Error:org.freedesktop.DBus.Error.InvalidArgs: Property 'NotWritable' is not writable");
   g_error_free (error);
 
   error = NULL;
@@ -922,7 +932,7 @@ test_dispatch_thread_func (gpointer user_data)
   g_assert (value != NULL);
   g_assert (g_variant_is_of_type (value, G_VARIANT_TYPE ("(a{sv})")));
   s = g_variant_print (value, TRUE);
-  g_assert_cmpstr (s, ==, "({'PropertyUno': <\"Property `PropertyUno' Is What It Is!\">, 'NotWritable': <\"Property `NotWritable' Is What It Is!\">},)");
+  g_assert_cmpstr (s, ==, "({'PropertyUno': <\"Property 'PropertyUno' Is What It Is!\">, 'NotWritable': <\"Property 'NotWritable' Is What It Is!\">},)");
   g_free (s);
   g_variant_unref (value);
 
@@ -936,16 +946,11 @@ static void
 test_dispatch (const gchar *object_path)
 {
   GThread *thread;
-  GError *error;
 
   /* run this in a thread to avoid deadlocks */
-  error = NULL;
-  thread = g_thread_create (test_dispatch_thread_func,
-                            (gpointer) object_path,
-                            TRUE,
-                            &error);
-  g_assert_no_error (error);
-  g_assert (thread != NULL);
+  thread = g_thread_new ("test_dispatch",
+                         test_dispatch_thread_func,
+                         (gpointer) object_path);
   g_main_loop_run (loop);
   g_thread_join (thread);
 }
@@ -986,7 +991,7 @@ test_object_registration (void)
 
   registration_id = g_dbus_connection_register_object (c,
                                                        "/foo/boss",
-                                                       &foo_interface_info,
+                                                       (GDBusInterfaceInfo *) &foo_interface_info,
                                                        &foo_vtable,
                                                        &data,
                                                        on_object_unregistered,
@@ -998,7 +1003,7 @@ test_object_registration (void)
 
   registration_id = g_dbus_connection_register_object (c,
                                                        "/foo/boss",
-                                                       &bar_interface_info,
+                                                       (GDBusInterfaceInfo *) &bar_interface_info,
                                                        NULL,
                                                        &data,
                                                        on_object_unregistered,
@@ -1010,7 +1015,7 @@ test_object_registration (void)
 
   registration_id = g_dbus_connection_register_object (c,
                                                        "/foo/boss/worker1",
-                                                       &foo_interface_info,
+                                                       (GDBusInterfaceInfo *) &foo_interface_info,
                                                        NULL,
                                                        &data,
                                                        on_object_unregistered,
@@ -1022,7 +1027,7 @@ test_object_registration (void)
 
   registration_id = g_dbus_connection_register_object (c,
                                                        "/foo/boss/worker1p1",
-                                                       &foo_interface_info,
+                                                       (GDBusInterfaceInfo *) &foo_interface_info,
                                                        NULL,
                                                        &data,
                                                        on_object_unregistered,
@@ -1034,7 +1039,7 @@ test_object_registration (void)
 
   registration_id = g_dbus_connection_register_object (c,
                                                        "/foo/boss/worker2",
-                                                       &bar_interface_info,
+                                                       (GDBusInterfaceInfo *) &bar_interface_info,
                                                        NULL,
                                                        &data,
                                                        on_object_unregistered,
@@ -1046,7 +1051,7 @@ test_object_registration (void)
 
   registration_id = g_dbus_connection_register_object (c,
                                                        "/foo/boss/interns/intern1",
-                                                       &foo_interface_info,
+                                                       (GDBusInterfaceInfo *) &foo_interface_info,
                                                        NULL,
                                                        &data,
                                                        on_object_unregistered,
@@ -1059,7 +1064,7 @@ test_object_registration (void)
   /* ... and try again at another path */
   registration_id = g_dbus_connection_register_object (c,
                                                        "/foo/boss/interns/intern2",
-                                                       &bar_interface_info,
+                                                       (GDBusInterfaceInfo *) &bar_interface_info,
                                                        NULL,
                                                        &data,
                                                        on_object_unregistered,
@@ -1072,7 +1077,7 @@ test_object_registration (void)
   /* register at the same path/interface - this should fail */
   registration_id = g_dbus_connection_register_object (c,
                                                        "/foo/boss/interns/intern2",
-                                                       &bar_interface_info,
+                                                       (GDBusInterfaceInfo *) &bar_interface_info,
                                                        NULL,
                                                        &data,
                                                        on_object_unregistered,
@@ -1086,7 +1091,7 @@ test_object_registration (void)
   /* register at different interface - shouldn't fail */
   registration_id = g_dbus_connection_register_object (c,
                                                        "/foo/boss/interns/intern2",
-                                                       &foo_interface_info,
+                                                       (GDBusInterfaceInfo *) &foo_interface_info,
                                                        NULL,
                                                        &data,
                                                        on_object_unregistered,
@@ -1098,13 +1103,14 @@ test_object_registration (void)
 
   /* unregister it via the id */
   g_assert (g_dbus_connection_unregister_object (c, registration_id));
+  g_main_context_iteration (NULL, FALSE);
   g_assert_cmpint (data.num_unregistered_calls, ==, 1);
   intern2_foo_reg_id = 0;
 
   /* register it back */
   registration_id = g_dbus_connection_register_object (c,
                                                        "/foo/boss/interns/intern2",
-                                                       &foo_interface_info,
+                                                       (GDBusInterfaceInfo *) &foo_interface_info,
                                                        NULL,
                                                        &data,
                                                        on_object_unregistered,
@@ -1116,7 +1122,7 @@ test_object_registration (void)
 
   registration_id = g_dbus_connection_register_object (c,
                                                        "/foo/boss/interns/intern3",
-                                                       &bar_interface_info,
+                                                       (GDBusInterfaceInfo *) &bar_interface_info,
                                                        NULL,
                                                        &data,
                                                        on_object_unregistered,
@@ -1153,6 +1159,7 @@ test_object_registration (void)
   /* unregister it, then register it again */
   g_assert_cmpint (data.num_unregistered_subtree_calls, ==, 0);
   g_assert (g_dbus_connection_unregister_subtree (c, subtree_registration_id));
+  g_main_context_iteration (NULL, FALSE);
   g_assert_cmpint (data.num_unregistered_subtree_calls, ==, 1);
   subtree_registration_id = g_dbus_connection_register_subtree (c,
                                                                 "/foo/boss/executives",
@@ -1172,7 +1179,7 @@ test_object_registration (void)
    */
   registration_id = g_dbus_connection_register_object (c,
                                                        "/foo/boss/executives/non_subtree_object",
-                                                       &bar_interface_info,
+                                                       (GDBusInterfaceInfo *) &bar_interface_info,
                                                        NULL,
                                                        &data,
                                                        on_object_unregistered,
@@ -1183,7 +1190,7 @@ test_object_registration (void)
   num_successful_registrations++;
   registration_id = g_dbus_connection_register_object (c,
                                                        "/foo/boss/executives/non_subtree_object",
-                                                       &foo_interface_info,
+                                                       (GDBusInterfaceInfo *) &foo_interface_info,
                                                        NULL,
                                                        &data,
                                                        on_object_unregistered,
@@ -1194,7 +1201,7 @@ test_object_registration (void)
   num_successful_registrations++;
 
   /* now register a dynamic subtree, spawning objects as they are called */
-  dyna_data = g_ptr_array_new ();
+  dyna_data = g_ptr_array_new_with_free_func (g_free);
   dyna_subtree_registration_id = g_dbus_connection_register_subtree (c,
                                                                      "/foo/dyna",
                                                                      &dynamic_subtree_vtable,
@@ -1214,9 +1221,9 @@ test_object_registration (void)
 
   /* Install three nodes in the dynamic subtree via the dyna_data backdoor and
    * assert that they show up correctly in the introspection data */
-  g_ptr_array_add (dyna_data, "lol");
-  g_ptr_array_add (dyna_data, "cat");
-  g_ptr_array_add (dyna_data, "cheezburger");
+  g_ptr_array_add (dyna_data, g_strdup ("lol"));
+  g_ptr_array_add (dyna_data, g_strdup ("cat"));
+  g_ptr_array_add (dyna_data, g_strdup ("cheezburger"));
   nodes = get_nodes_at (c, "/foo/dyna");
   g_assert (nodes != NULL);
   g_assert_cmpint (g_strv_length (nodes), ==, 3);
@@ -1344,13 +1351,14 @@ test_object_registration (void)
 
   /* To prevent from exiting and attaching a D-Bus tool like D-Feet; uncomment: */
 #if 0
-  g_debug ("Point D-feet or other tool at: %s", session_bus_get_temporary_address());
+  g_debug ("Point D-feet or other tool at: %s", g_test_dbus_get_temporary_address());
   g_main_loop_run (loop);
 #endif
 
   /* check that unregistering the subtree handler works */
   g_assert_cmpint (data.num_unregistered_subtree_calls, ==, 1);
   g_assert (g_dbus_connection_unregister_subtree (c, subtree_registration_id));
+  g_main_context_iteration (NULL, FALSE);
   g_assert_cmpint (data.num_unregistered_subtree_calls, ==, 2);
   nodes = get_nodes_at (c, "/foo/boss/executives");
   g_assert (nodes != NULL);
@@ -1370,6 +1378,7 @@ test_object_registration (void)
   g_assert (g_dbus_connection_unregister_object (c, non_subtree_object_path_bar_reg_id));
   g_assert (g_dbus_connection_unregister_object (c, non_subtree_object_path_foo_reg_id));
 
+  g_main_context_iteration (NULL, FALSE);
   g_assert_cmpint (data.num_unregistered_calls, ==, num_successful_registrations);
 
   /* check that we no longer export any objects - TODO: it looks like there's a bug in
@@ -1385,6 +1394,337 @@ test_object_registration (void)
   g_object_unref (c);
 }
 
+static const GDBusInterfaceInfo test_interface_info1 =
+{
+  -1,
+  "org.example.Foo",
+  (GDBusMethodInfo **) NULL,
+  (GDBusSignalInfo **) NULL,
+  (GDBusPropertyInfo **) NULL,
+  NULL,
+};
+
+static const GDBusInterfaceInfo test_interface_info2 =
+{
+  -1,
+  "org.freedesktop.DBus.Properties",
+  (GDBusMethodInfo **) NULL,
+  (GDBusSignalInfo **) NULL,
+  (GDBusPropertyInfo **) NULL,
+  NULL,
+};
+
+static void
+check_interfaces (GDBusConnection  *c,
+                  const gchar      *object_path,
+                  const gchar     **interfaces)
+{
+  GError *error;
+  GDBusProxy *proxy;
+  gchar *xml_data;
+  GDBusNodeInfo *node_info;
+  gint i, j;
+
+  error = NULL;
+  proxy = g_dbus_proxy_new_sync (c,
+                                 G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
+                                 G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
+                                 NULL,
+                                 g_dbus_connection_get_unique_name (c),
+                                 object_path,
+                                 "org.freedesktop.DBus.Introspectable",
+                                 NULL,
+                                 &error);
+  g_assert_no_error (error);
+  g_assert (proxy != NULL);
+
+  /* do this async to avoid libdbus-1 deadlocks */
+  xml_data = NULL;
+  g_dbus_proxy_call (proxy,
+                     "Introspect",
+                     NULL,
+                     G_DBUS_CALL_FLAGS_NONE,
+                     -1,
+                     NULL,
+                     (GAsyncReadyCallback) introspect_callback,
+                     &xml_data);
+  g_main_loop_run (loop);
+  g_assert (xml_data != NULL);
+
+  node_info = g_dbus_node_info_new_for_xml (xml_data, &error);
+  g_assert_no_error (error);
+  g_assert (node_info != NULL);
+
+  g_assert (node_info->interfaces != NULL);
+  for (i = 0; node_info->interfaces[i]; i++) ;
+#if 0
+  if (g_strv_length ((gchar**)interfaces) != i - 1)
+    {
+      g_print ("expected ");
+      for (i = 0; interfaces[i]; i++)
+        g_print ("%s ", interfaces[i]);
+      g_print ("\ngot ");
+      for (i = 0; node_info->interfaces[i]; i++)
+        g_print ("%s ", node_info->interfaces[i]->name);
+      g_print ("\n");
+    }
+#endif
+  g_assert_cmpint (g_strv_length ((gchar**)interfaces), ==, i - 1);
+
+  for (i = 0; interfaces[i]; i++)
+    {
+      for (j = 0; node_info->interfaces[j]; j++)
+        {
+          if (strcmp (interfaces[i], node_info->interfaces[j]->name) == 0)
+            goto found;
+        }
+
+      g_assert_not_reached ();
+
+ found: ;
+    }
+
+  g_object_unref (proxy);
+  g_free (xml_data);
+  g_dbus_node_info_unref (node_info);
+}
+
+static void
+test_registered_interfaces (void)
+{
+  GError *error;
+  guint id1, id2;
+  const gchar *interfaces[] = {
+    "org.example.Foo",
+    "org.freedesktop.DBus.Properties",
+    "org.freedesktop.DBus.Introspectable",
+    NULL,
+  };
+
+  error = NULL;
+  c = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
+  g_assert_no_error (error);
+  g_assert (c != NULL);
+
+  id1 = g_dbus_connection_register_object (c,
+                                           "/test",
+                                           (GDBusInterfaceInfo *) &test_interface_info1,
+                                           NULL,
+                                           NULL,
+                                           NULL,
+                                           &error);
+  g_assert_no_error (error);
+  g_assert (id1 > 0);
+  id2 = g_dbus_connection_register_object (c,
+                                           "/test",
+                                           (GDBusInterfaceInfo *) &test_interface_info2,
+                                           NULL,
+                                           NULL,
+                                           NULL,
+                                           &error);
+  g_assert_no_error (error);
+  g_assert (id2 > 0);
+
+  check_interfaces (c, "/test", interfaces);
+
+  g_assert (g_dbus_connection_unregister_object (c, id1));
+  g_assert (g_dbus_connection_unregister_object (c, id2));
+  g_object_unref (c);
+}
+
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+test_async_method_call (GDBusConnection       *connection,
+                        const gchar           *sender,
+                        const gchar           *object_path,
+                        const gchar           *interface_name,
+                        const gchar           *method_name,
+                        GVariant              *parameters,
+                        GDBusMethodInvocation *invocation,
+                        gpointer               user_data)
+{
+  const GDBusPropertyInfo *property;
+
+  /* Strictly speaking, this function should also expect to receive
+   * method calls not on the org.freedesktop.DBus.Properties interface,
+   * but we don't do any during this testcase, so assert that.
+   */
+  g_assert_cmpstr (interface_name, ==, "org.freedesktop.DBus.Properties");
+  g_assert (g_dbus_method_invocation_get_method_info (invocation) == NULL);
+
+  property = g_dbus_method_invocation_get_property_info (invocation);
+
+  /* We should never be seeing any property calls on the com.example.Bar
+   * interface because it doesn't export any properties.
+   *
+   * In each case below make sure the interface is org.example.Foo.
+   */
+
+  /* Do a whole lot of asserts to make sure that invalid calls are still
+   * getting properly rejected by GDBusConnection and that our
+   * environment is as we expect it to be.
+   */
+  if (g_str_equal (method_name, "Get"))
+    {
+      const gchar *iface_name, *prop_name;
+
+      g_variant_get (parameters, "(&s&s)", &iface_name, &prop_name);
+      g_assert_cmpstr (iface_name, ==, "org.example.Foo");
+      g_assert (property != NULL);
+      g_assert_cmpstr (prop_name, ==, property->name);
+      g_assert (property->flags & G_DBUS_PROPERTY_INFO_FLAGS_READABLE);
+      g_dbus_method_invocation_return_value (invocation, g_variant_new ("(v)", g_variant_new_string (prop_name)));
+    }
+
+  else if (g_str_equal (method_name, "Set"))
+    {
+      const gchar *iface_name, *prop_name;
+      GVariant *value;
+
+      g_variant_get (parameters, "(&s&sv)", &iface_name, &prop_name, &value);
+      g_assert_cmpstr (iface_name, ==, "org.example.Foo");
+      g_assert (property != NULL);
+      g_assert_cmpstr (prop_name, ==, property->name);
+      g_assert (property->flags & G_DBUS_PROPERTY_INFO_FLAGS_WRITABLE);
+      g_assert (g_variant_is_of_type (value, G_VARIANT_TYPE (property->signature)));
+      g_dbus_method_invocation_return_value (invocation, g_variant_new ("()"));
+      g_variant_unref (value);
+    }
+
+  else if (g_str_equal (method_name, "GetAll"))
+    {
+      const gchar *iface_name;
+
+      g_variant_get (parameters, "(&s)", &iface_name);
+      g_assert_cmpstr (iface_name, ==, "org.example.Foo");
+      g_assert (property == NULL);
+      g_dbus_method_invocation_return_value (invocation,
+                                             g_variant_new_parsed ("({ 'PropertyUno': < 'uno' >,"
+                                                                   "   'NotWritable': < 'notwrite' > },)"));
+    }
+
+  else
+    g_assert_not_reached ();
+}
+
+static gint outstanding_cases;
+
+static void
+ensure_result_cb (GObject      *source,
+                  GAsyncResult *result,
+                  gpointer      user_data)
+{
+  GDBusConnection *connection = G_DBUS_CONNECTION (source);
+  GVariant *reply;
+
+  reply = g_dbus_connection_call_finish (connection, result, NULL);
+
+  if (user_data == NULL)
+    {
+      /* Expected an error */
+      g_assert (reply == NULL);
+    }
+  else
+    {
+      /* Expected a reply of a particular format. */
+      gchar *str;
+
+      g_assert (reply != NULL);
+      str = g_variant_print (reply, TRUE);
+      g_assert_cmpstr (str, ==, (const gchar *) user_data);
+      g_free (str);
+
+      g_variant_unref (reply);
+    }
+
+  g_assert_cmpint (outstanding_cases, >, 0);
+  outstanding_cases--;
+}
+
+static void
+test_async_case (GDBusConnection *connection,
+                 const gchar     *expected_reply,
+                 const gchar     *method,
+                 const gchar     *format_string,
+                 ...)
+{
+  va_list ap;
+
+  va_start (ap, format_string);
+
+  g_dbus_connection_call (connection, g_dbus_connection_get_unique_name (connection), "/foo",
+                          "org.freedesktop.DBus.Properties", method, g_variant_new_va (format_string, NULL, &ap),
+                          NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, ensure_result_cb, (gpointer) expected_reply);
+
+  va_end (ap);
+
+  outstanding_cases++;
+}
+
+static void
+test_async_properties (void)
+{
+  GError *error = NULL;
+  guint registration_id, registration_id2;
+  static const GDBusInterfaceVTable vtable = {
+    test_async_method_call, NULL, NULL
+  };
+
+  c = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
+  g_assert_no_error (error);
+  g_assert (c != NULL);
+
+  registration_id = g_dbus_connection_register_object (c,
+                                                       "/foo",
+                                                       (GDBusInterfaceInfo *) &foo_interface_info,
+                                                       &vtable, NULL, NULL, &error);
+  g_assert_no_error (error);
+  g_assert (registration_id);
+  registration_id2 = g_dbus_connection_register_object (c,
+                                                        "/foo",
+                                                        (GDBusInterfaceInfo *) &foo2_interface_info,
+                                                        &vtable, NULL, NULL, &error);
+  g_assert_no_error (error);
+  g_assert (registration_id);
+
+  test_async_case (c, NULL, "random", "()");
+
+  /* Test a variety of error cases */
+  test_async_case (c, NULL, "Get", "(si)", "wrong signature", 5);
+  test_async_case (c, NULL, "Get", "(ss)", "org.example.WrongInterface", "zzz");
+  test_async_case (c, NULL, "Get", "(ss)", "org.example.Foo", "NoSuchProperty");
+  test_async_case (c, NULL, "Get", "(ss)", "org.example.Foo", "NotReadable");
+
+  test_async_case (c, NULL, "Set", "(si)", "wrong signature", 5);
+  test_async_case (c, NULL, "Set", "(ssv)", "org.example.WrongInterface", "zzz", g_variant_new_string (""));
+  test_async_case (c, NULL, "Set", "(ssv)", "org.example.Foo", "NoSuchProperty", g_variant_new_string (""));
+  test_async_case (c, NULL, "Set", "(ssv)", "org.example.Foo", "NotWritable", g_variant_new_string (""));
+  test_async_case (c, NULL, "Set", "(ssv)", "org.example.Foo", "PropertyUno", g_variant_new_object_path ("/wrong"));
+
+  test_async_case (c, NULL, "GetAll", "(si)", "wrong signature", 5);
+  test_async_case (c, NULL, "GetAll", "(s)", "org.example.WrongInterface");
+
+  /* Make sure that we get no unexpected async property calls for com.example.Foo2 */
+  test_async_case (c, NULL, "Get", "(ss)", "org.example.Foo2", "zzz");
+  test_async_case (c, NULL, "Set", "(ssv)", "org.example.Foo2", "zzz", g_variant_new_string (""));
+  test_async_case (c, "(@a{sv} {},)", "GetAll", "(s)", "org.example.Foo2");
+
+  /* Now do the proper things */
+  test_async_case (c, "(<'PropertyUno'>,)", "Get", "(ss)", "org.example.Foo", "PropertyUno");
+  test_async_case (c, "(<'NotWritable'>,)", "Get", "(ss)", "org.example.Foo", "NotWritable");
+  test_async_case (c, "()", "Set", "(ssv)", "org.example.Foo", "PropertyUno", g_variant_new_string (""));
+  test_async_case (c, "()", "Set", "(ssv)", "org.example.Foo", "NotReadable", g_variant_new_string (""));
+  test_async_case (c, "({'PropertyUno': <'uno'>, 'NotWritable': <'notwrite'>},)", "GetAll", "(s)", "org.example.Foo");
+
+  while (outstanding_cases)
+    g_main_context_iteration (NULL, TRUE);
+
+  g_dbus_connection_unregister_object (c, registration_id);
+  g_dbus_connection_unregister_object (c, registration_id2);
+  g_object_unref (c);
+}
 
 /* ---------------------------------------------------------------------------------------------------- */
 
@@ -1394,33 +1734,21 @@ main (int   argc,
 {
   gint ret;
 
-  g_type_init ();
   g_test_init (&argc, &argv, NULL);
 
   /* all the tests rely on a shared main loop */
   loop = g_main_loop_new (NULL, FALSE);
 
-  /* all the tests use a session bus with a well-known address that we can bring up and down
-   * using session_bus_up() and session_bus_down().
-   */
-  g_unsetenv ("DISPLAY");
-  g_setenv ("DBUS_SESSION_BUS_ADDRESS", session_bus_get_temporary_address (), TRUE);
-
-  session_bus_up ();
-
-  /* TODO: wait a bit for the bus to come up.. ideally session_bus_up() won't return
-   * until one can connect to the bus but that's not how things work right now
-   */
-  usleep (500 * 1000);
-
   g_test_add_func ("/gdbus/object-registration", test_object_registration);
+  g_test_add_func ("/gdbus/registered-interfaces", test_registered_interfaces);
+  g_test_add_func ("/gdbus/async-properties", test_async_properties);
+
   /* TODO: check that we spit out correct introspection data */
   /* TODO: check that registering a whole subtree works */
 
-  ret = g_test_run();
+  ret = session_bus_run ();
 
-  /* tear down bus */
-  session_bus_down ();
+  g_main_loop_unref (loop);
 
   return ret;
 }