2009/10/27 Mikkel Kamstrup Erlandsen <mikkel.kamst...@gmail.com>: > 2009/10/27 David Zeuthen <da...@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.kamst...@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.kamst...@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.kamst...@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
_______________________________________________ gtk-devel-list mailing list gtk-devel-list@gnome.org http://mail.gnome.org/mailman/listinfo/gtk-devel-list