Re: GDBus API Questions; was: GDBus/GVariant plans for next GLib release
- From: Mikkel Kamstrup Erlandsen <mikkel kamstrup gmail com>
- To: David Zeuthen <david fubar dk>
- Cc: gtk-devel-list gnome org
- Subject: Re: GDBus API Questions; was: GDBus/GVariant plans for next GLib release
- Date: Wed, 28 Oct 2009 00:19:53 +0100
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]