Re: GDBus API Questions; was: GDBus/GVariant plans for next GLib release



2009/10/27 Mikkel Kamstrup Erlandsen <mikkel kamstrup gmail com>:
> 2009/10/27 David Zeuthen <david fubar dk>:
>> Hey Mikkel,
>>
>> On Mon, 2009-10-26 at 23:52 +0100, Mikkel Kamstrup Erlandsen wrote:
>>> > I just looked over the newly introduced
>>> > g_dbus_connection_register_subtree() and related data structures, and
>>> > I think it will fit very nicely with what I am going to need. All in
>>> > all it looks really sweet, good work.
>>> >
>>> > One thing though is that as I read it objects in a subtree must be
>>> > known before method calls are accepted to them? For my use case in
>>> > Zeitgeist I was hoping that I could completely get rid of a "Manager"
>>> > type of interface, and just implicitly create objects in the tree
>>> > whenever calls where made to them. This does not look possible as it
>>> > stands?
>>> >
>>> > Maybe allowing '*' as a wildcard node name in the subtree enumeration function?
>>
>> I'm actually a bit wary of introducing this kind of functionality -
>> mainly, I guess, because it screws with the notion that a D-Bus service
>> export a set of objects. In particular it makes it hard to
>> debug/introspect the service - for example, in extreme abuses of such a
>> feature (not the use-case you are suggesting though), you can't really
>> use e.g. d-feet to get an idea of what kind of objects are exported and
>> known by the service.
>>
>> The subtree functionality is really just for performance hacks - the
>> intended use is to avoid creating a huge amount of objects. For example,
>> one use case is export the subtree /org/foo/Project/processes/<pid>
>> where <pid> is, say, a UNIX process id. With the subtree handler, no
>> object creation over is necessary.
>>
>> Anyway, your original use case does seem sound and reasonable - it
>> reduces overhead insofar that the client saves a round-trip to a
>> hypothetical Manager.CreateObject() method that would be needed if we
>> didn't have this. It does make it less intuitive insofar that remote
>> object creation is this magical thing with automatically appearing
>> nodes... but I guess that's fine.
>
> Not to mention the built in race condition you have if the exported
> objects can also be deleted via the manager. All client apps would
> have to precede all calls to an object by a Create() message (and it
> would still be racy). Anyway, enough about this :-) I think it is
> probably a fragment of my REST/web background where such patterns are
> more common.
>
>>> I had a stab at this myself. The wildcard idea seemed like a bad one,
>>> so I instead added another gboolean param to
>>> g_dbus_connection_register_subtree(), @is_dynamic.
>>>
>>> If is_dynamic is TRUE then objects need not be in the enumerated list
>>> of objects in order to be introspected and dispatched. Pretty simple.
>>>
>>> No matter the simplicity I still managed to screw up one of the unit
>>> tests. I will fix it and add some specific tests for the dynamic case
>>> if you give the "go" for this David.
>>
>> Sounds good to me. I'd prefer a GDBusSubtreeFlags flag enumeration with
>> a G_DBUS_SUBTREE_FLAGS_DISPATCH_TO_UNENUMERATED_NODES member instead of
>> @is_dynamic. Just for future proofing and all. Maybe it would also be
>> nice to pass a "gboolean enumerated" to @introspect and @dispatch that
>> indicates whether the node was enumerated (or not).
>
> That sounds like a better idea. I'll have another stab at this tonight
> if the kids allow me :-)

They did :-)

Attached is a series of patches (0001 should be identical to my
previous) implementing what you describe, except adding the gboolean
enumerated arg to @introspect and @dispatch. I will get around to
that.

The failing unit tests I described was really just me b0rking up the
linking between some installed gdbus components and my devel version.
I have them running now[1] and I added some testing for the dynamic
objects as well (see 0003).

-- 
Cheers,
Mikkel

[1]: One caveat is that I see some errors about refcount on GVariants
from the /gdbus/method-calls-in-thread test every now and then, but it
runs mostly. Looks like a race, but it is unrelated to my hacks.
You've probably already seen it yourself.
From d569ee5b89b8865747a153fb3848764c52a4c865 Mon Sep 17 00:00:00 2001
From: Mikkel Kamstrup Erlandsen <mikkel kamstrup gmail com>
Date: Tue, 27 Oct 2009 20:45:34 +0100
Subject: [PATCH 1/3] Add a @is_dynamic gboolean parameter to g_dbus_connection_register_subtree(). This will be reworked to use an enumeration/flag instead for better future extensibility

---
 gdbus/example-subtree.c |    1 +
 gdbus/gdbusconnection.c |   24 ++++++++++++++++++------
 gdbus/gdbusconnection.h |    1 +
 gdbus/tests/export.c    |    3 +++
 4 files changed, 23 insertions(+), 6 deletions(-)

diff --git a/gdbus/example-subtree.c b/gdbus/example-subtree.c
index 041a8f4..f870589 100644
--- a/gdbus/example-subtree.c
+++ b/gdbus/example-subtree.c
@@ -332,6 +332,7 @@ on_name_acquired (GDBusConnection *connection,
   registration_id = g_dbus_connection_register_subtree (connection,
                                                        "/org/gtk/GDBus/TestSubtree/Devices",
                                                         &subtree_vtable,
+                                                        FALSE, /* is_dynamic */
                                                         NULL,  /* user_data */
                                                         NULL,  /* user_data_free_func */
                                                         NULL); /* GError** */
diff --git a/gdbus/gdbusconnection.c b/gdbus/gdbusconnection.c
index aa21213..d9099c0 100644
--- a/gdbus/gdbusconnection.c
+++ b/gdbus/gdbusconnection.c
@@ -3620,6 +3620,7 @@ struct ExportedSubtree
   gchar                    *object_path;
   GDBusConnection          *connection;
   const GDBusSubtreeVTable *vtable;
+  gboolean                  is_dynamic;
 
   GMainContext             *context;
   gpointer                  user_data;
@@ -3685,6 +3686,8 @@ handle_subtree_introspect (DBusConnection  *connection,
 
   //g_debug ("in handle_subtree_introspect for %s", requested_object_path);
 
+  /* Strictly we don't need the children in dynamic mode, but we avoid the
+   * conditionals to preserve code clarity */
   children = es->vtable->enumerate (es->connection,
                                     es->user_data,
                                     sender,
@@ -3693,8 +3696,9 @@ handle_subtree_introspect (DBusConnection  *connection,
   if (!is_root)
     {
       requested_node = strrchr (requested_object_path, '/') + 1;
-      /* skip if requested node is not part of children */
-      if (!_g_strv_has_string ((const gchar * const *) children, requested_node))
+
+      /* Assert existence of object if we are not dynamic */
+      if (!es->is_dynamic && !_g_strv_has_string ((const gchar * const *) children, requested_node))
         goto out;
     }
   else
@@ -3825,8 +3829,9 @@ handle_subtree_method_invocation (DBusConnection *connection,
   if (!is_root)
     {
       requested_node = strrchr (requested_object_path, '/') + 1;
-      /* skip if requested node is not part of children */
-      if (!_g_strv_has_string ((const gchar * const *) children, requested_node))
+
+      /* If not dynamic, skip if requested node is not part of children */
+      if (!es->is_dynamic && !_g_strv_has_string ((const gchar * const *) children, requested_node))
         goto out;
     }
   else
@@ -4048,6 +4053,9 @@ static const DBusObjectPathVTable dbus_1_subtree_vtable =
  * @connection: A #GDBusConnection.
  * @object_path: The object path to register the subtree at.
  * @vtable: A #GDBusSubtreeVTable to enumerate, introspect and dispatch nodes in the subtree.
+ * @is_dynamic: If %TRUE method calls to objects not in the enumerated range
+ *              will still be dispatched. This is useful if you want to
+ *              dynamically spawn objects in the subtree.
  * @user_data: Data to pass to functions in @vtable.
  * @user_data_free_func: Function to call when the subtree is unregistered.
  * @error: Return location for error or %NULL.
@@ -4059,8 +4067,9 @@ static const DBusObjectPathVTable dbus_1_subtree_vtable =
  * by @object_path.
  *
  * When handling remote calls into any node in the subtree, first the
- * @enumerate and @introspection function is used to check if the node
- * exists and whether it supports the requested method. If so, the
+ * @enumerate function is used to check if the node exists. If the node exists
+ * or @is_dynamic is set to %TRUE the @introspection function is used to
+ * check if the node supports the requested method. If so, the
  * @dispatch function is used to determine where to dispatch the
  * call. The collected #GDBusInterfaceVTable and #gpointer will be
  * used to call into the interface vtable for processing the request.
@@ -4087,6 +4096,7 @@ guint
 g_dbus_connection_register_subtree (GDBusConnection            *connection,
                                     const gchar                *object_path,
                                     const GDBusSubtreeVTable   *vtable,
+                                    gboolean                    is_dynamic,
                                     gpointer                    user_data,
                                     GDestroyNotify              user_data_free_func,
                                     GError                    **error)
@@ -4099,6 +4109,7 @@ g_dbus_connection_register_subtree (GDBusConnection            *connection,
   g_return_val_if_fail (!g_dbus_connection_get_is_disconnected (connection), 0);
   g_return_val_if_fail (object_path != NULL, 0);
   g_return_val_if_fail (vtable != NULL, 0);
+  g_return_val_if_fail (error == NULL || *error == NULL, 0);
 
   ret = 0;
 
@@ -4146,6 +4157,7 @@ g_dbus_connection_register_subtree (GDBusConnection            *connection,
     }
 
   es->vtable = vtable;
+  es->is_dynamic = is_dynamic;
   es->id = _global_subtree_registration_id++; /* TODO: overflow etc. */
   es->user_data = user_data;
   es->user_data_free_func = user_data_free_func;
diff --git a/gdbus/gdbusconnection.h b/gdbus/gdbusconnection.h
index da07e14..cd49a07 100644
--- a/gdbus/gdbusconnection.h
+++ b/gdbus/gdbusconnection.h
@@ -318,6 +318,7 @@ struct _GDBusSubtreeVTable
 guint            g_dbus_connection_register_subtree           (GDBusConnection            *connection,
                                                                const gchar                *object_path,
                                                                const GDBusSubtreeVTable   *vtable,
+                                                               gboolean                    is_dynamic,
                                                                gpointer                    user_data,
                                                                GDestroyNotify              user_data_free_func,
                                                                GError                    **error);
diff --git a/gdbus/tests/export.c b/gdbus/tests/export.c
index 5a678e1..f868d54 100644
--- a/gdbus/tests/export.c
+++ b/gdbus/tests/export.c
@@ -649,6 +649,7 @@ test_object_registration (void)
   subtree_registration_id = g_dbus_connection_register_subtree (c,
                                                                 "/foo/boss/executives",
                                                                 &subtree_vtable,
+                                                                FALSE,
                                                                 &data,
                                                                 on_subtree_unregistered,
                                                                 &error);
@@ -658,6 +659,7 @@ test_object_registration (void)
   registration_id = g_dbus_connection_register_subtree (c,
                                                         "/foo/boss/executives",
                                                         &subtree_vtable,
+                                                        FALSE,
                                                         &data,
                                                         on_subtree_unregistered,
                                                         &error);
@@ -674,6 +676,7 @@ test_object_registration (void)
   subtree_registration_id = g_dbus_connection_register_subtree (c,
                                                                 "/foo/boss/executives",
                                                                 &subtree_vtable,
+                                                                FALSE,
                                                                 &data,
                                                                 on_subtree_unregistered,
                                                                 &error);
-- 
1.6.3.3

From 6ab58f1031fcde74c70ed60de5443ab851c174b1 Mon Sep 17 00:00:00 2001
From: Mikkel Kamstrup Erlandsen <mikkel kamstrup gmail com>
Date: Tue, 27 Oct 2009 21:48:31 +0100
Subject: [PATCH 2/3] implement GDBusSubtreeFlags enumeration

Use it for g_dbus_connection_register_subtree() arg in stead of is_dynamic.
Also add the new enumeration to the gtk-doc sections
---
 docs/reference/gdbus/gdbus-standalone-sections.txt |    1 +
 gdbus/gdbusconnection.c                            |   27 ++++++++++---------
 gdbus/gdbusconnection.h                            |    2 +-
 gdbus/gdbusenums.h                                 |   15 +++++++++++
 gdbus/tests/export.c                               |    6 ++--
 5 files changed, 34 insertions(+), 17 deletions(-)

diff --git a/docs/reference/gdbus/gdbus-standalone-sections.txt b/docs/reference/gdbus/gdbus-standalone-sections.txt
index 442fee0..f763906 100644
--- a/docs/reference/gdbus/gdbus-standalone-sections.txt
+++ b/docs/reference/gdbus/gdbus-standalone-sections.txt
@@ -37,6 +37,7 @@ GDBusSubtreeVTable
 GDBusSubtreeEnumerateFunc
 GDBusSubtreeIntrospectFunc
 GDBusSubtreeDispatchFunc
+GDBusSubtreeFlags
 g_dbus_connection_register_subtree
 g_dbus_connection_unregister_subtree
 <SUBSECTION Standard>
diff --git a/gdbus/gdbusconnection.c b/gdbus/gdbusconnection.c
index d9099c0..8a175d6 100644
--- a/gdbus/gdbusconnection.c
+++ b/gdbus/gdbusconnection.c
@@ -3620,7 +3620,7 @@ struct ExportedSubtree
   gchar                    *object_path;
   GDBusConnection          *connection;
   const GDBusSubtreeVTable *vtable;
-  gboolean                  is_dynamic;
+  GDBusSubtreeFlags         flags;
 
   GMainContext             *context;
   gpointer                  user_data;
@@ -3698,7 +3698,8 @@ handle_subtree_introspect (DBusConnection  *connection,
       requested_node = strrchr (requested_object_path, '/') + 1;
 
       /* Assert existence of object if we are not dynamic */
-      if (!es->is_dynamic && !_g_strv_has_string ((const gchar * const *) children, requested_node))
+      if (!(es->flags & G_DBUS_SUBTREE_FLAGS_DISPATCH_TO_UNENUMERATED_NODES) &&
+          !_g_strv_has_string ((const gchar * const *) children, requested_node))
         goto out;
     }
   else
@@ -3831,7 +3832,8 @@ handle_subtree_method_invocation (DBusConnection *connection,
       requested_node = strrchr (requested_object_path, '/') + 1;
 
       /* If not dynamic, skip if requested node is not part of children */
-      if (!es->is_dynamic && !_g_strv_has_string ((const gchar * const *) children, requested_node))
+      if (!(es->flags & G_DBUS_SUBTREE_FLAGS_DISPATCH_TO_UNENUMERATED_NODES) &&
+          !_g_strv_has_string ((const gchar * const *) children, requested_node))
         goto out;
     }
   else
@@ -4053,9 +4055,7 @@ static const DBusObjectPathVTable dbus_1_subtree_vtable =
  * @connection: A #GDBusConnection.
  * @object_path: The object path to register the subtree at.
  * @vtable: A #GDBusSubtreeVTable to enumerate, introspect and dispatch nodes in the subtree.
- * @is_dynamic: If %TRUE method calls to objects not in the enumerated range
- *              will still be dispatched. This is useful if you want to
- *              dynamically spawn objects in the subtree.
+ * @flags: Flags used to fine tune the behavior of the subtree
  * @user_data: Data to pass to functions in @vtable.
  * @user_data_free_func: Function to call when the subtree is unregistered.
  * @error: Return location for error or %NULL.
@@ -4068,11 +4068,12 @@ static const DBusObjectPathVTable dbus_1_subtree_vtable =
  *
  * When handling remote calls into any node in the subtree, first the
  * @enumerate function is used to check if the node exists. If the node exists
- * or @is_dynamic is set to %TRUE the @introspection function is used to
- * check if the node supports the requested method. If so, the
- * @dispatch function is used to determine where to dispatch the
- * call. The collected #GDBusInterfaceVTable and #gpointer will be
- * used to call into the interface vtable for processing the request.
+ * or the #G_DBUS_SUBTREE_FLAGS_DISPATCH_TO_UNENUMERATED_NODES flag is set
+ * the @introspection function is used to check if the node supports the
+ * requested method. If so, the @dispatch function is used to determine
+ * where to dispatch the call. The collected #GDBusInterfaceVTable and
+ * #gpointer will be used to call into the interface vtable for processing
+ * the request.
  *
  * All calls into user-provided code will be invoked in the <link
  * linkend="g-main-context-push-thread-default">thread-default main
@@ -4096,7 +4097,7 @@ guint
 g_dbus_connection_register_subtree (GDBusConnection            *connection,
                                     const gchar                *object_path,
                                     const GDBusSubtreeVTable   *vtable,
-                                    gboolean                    is_dynamic,
+                                    GDBusSubtreeFlags           flags,
                                     gpointer                    user_data,
                                     GDestroyNotify              user_data_free_func,
                                     GError                    **error)
@@ -4157,7 +4158,7 @@ g_dbus_connection_register_subtree (GDBusConnection            *connection,
     }
 
   es->vtable = vtable;
-  es->is_dynamic = is_dynamic;
+  es->flags = flags;
   es->id = _global_subtree_registration_id++; /* TODO: overflow etc. */
   es->user_data = user_data;
   es->user_data_free_func = user_data_free_func;
diff --git a/gdbus/gdbusconnection.h b/gdbus/gdbusconnection.h
index cd49a07..4f517a3 100644
--- a/gdbus/gdbusconnection.h
+++ b/gdbus/gdbusconnection.h
@@ -318,7 +318,7 @@ struct _GDBusSubtreeVTable
 guint            g_dbus_connection_register_subtree           (GDBusConnection            *connection,
                                                                const gchar                *object_path,
                                                                const GDBusSubtreeVTable   *vtable,
-                                                               gboolean                    is_dynamic,
+                                                               GDBusSubtreeFlags           flags,
                                                                gpointer                    user_data,
                                                                GDestroyNotify              user_data_free_func,
                                                                GError                    **error);
diff --git a/gdbus/gdbusenums.h b/gdbus/gdbusenums.h
index ab37e16..de43ec0 100644
--- a/gdbus/gdbusenums.h
+++ b/gdbus/gdbusenums.h
@@ -257,6 +257,21 @@ typedef enum
   G_DBUS_PROPERTY_INFO_FLAGS_WRITABLE = (1<<1),
 } GDBusPropertyInfoFlags;
 
+/**
+ * GDBusSubtreeFlags:
+ * @G_DBUS_SUBTREE_FLAGS_NONE: No flags set.
+ * @G_DBUS_SUBTREE_FLAGS_DISPATCH_TO_UNENUMERATED_NODES: Method calls to objects not in the enumerated range
+ *                                                       will still be dispatched. This is useful if you want
+ *                                                       to dynamically spawn objects in the subtree.
+ *
+ * Flags passed to g_dbus_connection_register_subtree().
+ */
+typedef enum
+{
+  G_DBUS_SUBTREE_FLAGS_NONE = 0,                                /*< nick=none >*/
+  G_DBUS_SUBTREE_FLAGS_DISPATCH_TO_UNENUMERATED_NODES = (1<<0), /*< nick=dispatch-to-unenumerated-nodes >*/
+} GDBusSubtreeFlags;
+
 G_END_DECLS
 
 #endif /* __G_DBUS_ENUMS_H__ */
diff --git a/gdbus/tests/export.c b/gdbus/tests/export.c
index f868d54..e6e6d3e 100644
--- a/gdbus/tests/export.c
+++ b/gdbus/tests/export.c
@@ -649,7 +649,7 @@ test_object_registration (void)
   subtree_registration_id = g_dbus_connection_register_subtree (c,
                                                                 "/foo/boss/executives",
                                                                 &subtree_vtable,
-                                                                FALSE,
+                                                                G_DBUS_SUBTREE_FLAGS_NONE,
                                                                 &data,
                                                                 on_subtree_unregistered,
                                                                 &error);
@@ -659,7 +659,7 @@ test_object_registration (void)
   registration_id = g_dbus_connection_register_subtree (c,
                                                         "/foo/boss/executives",
                                                         &subtree_vtable,
-                                                        FALSE,
+                                                        G_DBUS_SUBTREE_FLAGS_NONE,
                                                         &data,
                                                         on_subtree_unregistered,
                                                         &error);
@@ -676,7 +676,7 @@ test_object_registration (void)
   subtree_registration_id = g_dbus_connection_register_subtree (c,
                                                                 "/foo/boss/executives",
                                                                 &subtree_vtable,
-                                                                FALSE,
+                                                                G_DBUS_SUBTREE_FLAGS_NONE,
                                                                 &data,
                                                                 on_subtree_unregistered,
                                                                 &error);
-- 
1.6.3.3

From ba058721ffe3f7395f0c0e65462ff1be125777e4 Mon Sep 17 00:00:00 2001
From: Mikkel Kamstrup Erlandsen <mikkel kamstrup gmail com>
Date: Wed, 28 Oct 2009 00:06:51 +0100
Subject: [PATCH 3/3] test dynamic objects in a subtree

Implement some test for dynamically spawning objects in a subtree
by setting the G_DBUS_SUBTREE_FLAGS_DISPATCH_TO_UNENUMERATED_NODES flag
---
 gdbus/tests/export.c |  169 +++++++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 168 insertions(+), 1 deletions(-)

diff --git a/gdbus/tests/export.c b/gdbus/tests/export.c
index e6e6d3e..be911e2 100644
--- a/gdbus/tests/export.c
+++ b/gdbus/tests/export.c
@@ -131,6 +131,73 @@ static const GDBusInterfaceInfo bar_interface_info =
   NULL,
 };
 
+/* -------------------- */
+
+static const GDBusMethodInfo dyna_method_info[] =
+{
+  {
+    "DynaCyber",
+    "", 0, NULL,
+    "", 0, NULL,
+    NULL
+  }
+};
+
+static const GDBusSignalInfo dyna_signal_info[] =
+{
+  
+};
+
+static const GDBusPropertyInfo dyna_property_info[] =
+{
+  
+};
+
+static const GDBusInterfaceInfo dyna_interface_info =
+{
+  "org.example.Dyna",
+  1,    /* 1 method*/
+  dyna_method_info,
+  0,    /* 0 signals */
+  NULL,
+  0,    /* 0 properties */
+  NULL,
+  NULL,
+};
+
+static void
+dyna_cyber (GDBusConnection *connection,
+            gpointer user_data,
+            const gchar *sender,
+            const gchar *object_path,
+            const gchar *interface_name,
+            const gchar *method_name,
+            GVariant *parameters,
+            GDBusMethodInvocation *invocation)
+{
+  GPtrArray *data = user_data;
+  char *node_name = strrchr (object_path, '/') + 1;
+  guint n;
+  
+  /* Add new node if it is not already known */  
+  for (n = 0; n < data->len ; n++)
+    {
+      if (g_strcmp0 (g_ptr_array_index (data, n), node_name) == 0)
+        goto out;
+    }
+  g_ptr_array_add (data, g_strdup(node_name));
+
+  out:
+    g_dbus_method_invocation_return_value (invocation, NULL);
+}
+
+static const GDBusInterfaceVTable dyna_interface_vtable =
+{
+  dyna_cyber,
+  NULL,
+  NULL
+};
+
 /* ---------------------------------------------------------------------------------------------------- */
 
 static void
@@ -381,6 +448,7 @@ subtree_enumerate (GDBusConnection       *connection,
   return nodes;
 }
 
+/* Only allows certain objects, and aborts on unknowns */
 static GPtrArray *
 subtree_introspect (GDBusConnection       *connection,
                     gpointer               user_data,
@@ -435,12 +503,71 @@ static const GDBusSubtreeVTable subtree_vtable =
 
 /* -------------------- */
 
+static gchar **
+dynamic_subtree_enumerate (GDBusConnection       *connection,
+                           gpointer               user_data,
+                           const gchar           *sender,
+                           const gchar           *object_path)
+{
+  GPtrArray *data = user_data;
+  gchar **nodes = g_new (gchar*, data->len + 1);
+  guint n;
+  
+  for (n = 0; n < data->len; n++)
+    {
+      nodes[n] = g_strdup (g_ptr_array_index (data, n));
+    }
+  nodes[data->len] = NULL;
+
+  return nodes;
+}
+
+/* Allow all objects to be introspected */
+static GPtrArray *
+dynamic_subtree_introspect (GDBusConnection       *connection,
+                            gpointer               user_data,
+                            const gchar           *sender,
+                            const gchar           *object_path,
+                            const gchar           *node)
+{
+  GPtrArray *interfaces;
+
+  /* All nodes (including the root node) implements the Dyna interface */
+  interfaces = g_ptr_array_new ();
+  g_ptr_array_add (interfaces, (gpointer) &dyna_interface_info);
+
+  return interfaces;
+}
+
+static const GDBusInterfaceVTable *
+dynamic_subtree_dispatch (GDBusConnection             *connection,
+                          gpointer                     user_data,
+                          const gchar                 *sender,
+                          const gchar                 *object_path,
+                          const gchar                 *interface_name,
+                          const gchar                 *node,
+                          gpointer                    *out_user_data)
+{
+  *out_user_data = user_data;
+  return &dyna_interface_vtable;
+}
+
+static const GDBusSubtreeVTable dynamic_subtree_vtable =
+{
+  dynamic_subtree_enumerate,
+  dynamic_subtree_introspect,
+  dynamic_subtree_dispatch
+};
+
+/* -------------------- */
+
 static void
 test_object_registration (void)
 {
   GDBusConnection *c;
   GError *error;
   ObjectRegistrationData data;
+  GPtrArray *dyna_data;
   gchar **nodes;
   guint boss_foo_reg_id;
   guint boss_bar_reg_id;
@@ -454,6 +581,7 @@ test_object_registration (void)
   guint subtree_registration_id;
   guint non_subtree_object_path_foo_reg_id;
   guint non_subtree_object_path_bar_reg_id;
+  guint dyna_subtree_registration_id;
   guint num_successful_registrations;
   DBusConnection *dc;
   DBusError dbus_error;
@@ -714,7 +842,41 @@ test_object_registration (void)
   non_subtree_object_path_foo_reg_id = registration_id;
   num_successful_registrations++;
 
+  /* now register a dynamic subtree, spawning objects as they are called */
+  dyna_data = g_ptr_array_new ();
+  dyna_subtree_registration_id = g_dbus_connection_register_subtree (c,
+                                                                     "/foo/dyna",
+                                                                     &dynamic_subtree_vtable,
+                                                                     G_DBUS_SUBTREE_FLAGS_DISPATCH_TO_UNENUMERATED_NODES,
+                                                                     dyna_data,
+                                                                     (GDestroyNotify)g_ptr_array_unref,
+                                                                     &error);
+  g_assert_no_error (error);
+  g_assert (dyna_subtree_registration_id > 0);
 
+  /* First assert that we have no nodes in the dynamic subtree */
+  nodes = get_nodes_at (c, "/foo/dyna");
+  g_assert (nodes != NULL);
+  g_assert_cmpint (g_strv_length (nodes), ==, 0);
+  g_strfreev (nodes);
+  g_assert_cmpint (count_interfaces (c, "/foo/dyna"), ==, 4);
+
+  /* Install three nodes in the dynamic subtree via the 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");
+  nodes = get_nodes_at (c, "/foo/dyna");
+  g_assert (nodes != NULL);
+  g_assert_cmpint (g_strv_length (nodes), ==, 3);
+  g_assert_cmpstr (nodes[0], ==, "lol");
+  g_assert_cmpstr (nodes[1], ==, "cat");
+  g_assert_cmpstr (nodes[2], ==, "cheezburger");
+  g_strfreev (nodes);
+  g_assert_cmpint (count_interfaces (c, "/foo/dyna/lol"), ==, 4);
+  g_assert_cmpint (count_interfaces (c, "/foo/dyna/cat"), ==, 4);
+  g_assert_cmpint (count_interfaces (c, "/foo/dyna/cheezburger"), ==, 4);
+  
   /* now check that the object hierarachy is properly generated... yes, it's a bit
    * perverse that we round-trip to the bus to introspect ourselves ;-)
    */
@@ -727,8 +889,9 @@ test_object_registration (void)
 
   nodes = get_nodes_at (c, "/foo");
   g_assert (nodes != NULL);
-  g_assert_cmpint (g_strv_length (nodes), ==, 1);
+  g_assert_cmpint (g_strv_length (nodes), ==, 2);
   g_assert_cmpstr (nodes[0], ==, "boss");
+  g_assert_cmpstr (nodes[1], ==, "dyna");
   g_strfreev (nodes);
   g_assert_cmpint (count_interfaces (c, "/foo"), ==, 0);
 
@@ -842,6 +1005,10 @@ test_object_registration (void)
   g_strfreev (nodes);
 #endif
 
+  /* To prevent from exiting and attaching a DBus tool like D-Feet; uncomment: */
+  /*g_info ("Point D-feet or other tool at: %s", session_bus_get_temporary_address());
+  g_main_loop_run (loop);*/
+  
   g_object_unref (c);
 }
 
-- 
1.6.3.3



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