Branch data Line data Source code
1 : : /* GLib testing framework examples and tests
2 : : *
3 : : * Copyright (C) 2008-2010 Red Hat, Inc.
4 : : *
5 : : * SPDX-License-Identifier: LGPL-2.1-or-later
6 : : *
7 : : * This library is free software; you can redistribute it and/or
8 : : * modify it under the terms of the GNU Lesser General Public
9 : : * License as published by the Free Software Foundation; either
10 : : * version 2.1 of the License, or (at your option) any later version.
11 : : *
12 : : * This library is distributed in the hope that it will be useful,
13 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 : : * Lesser General Public License for more details.
16 : : *
17 : : * You should have received a copy of the GNU Lesser General
18 : : * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
19 : : *
20 : : * Author: David Zeuthen <davidz@redhat.com>
21 : : */
22 : :
23 : : #include <gio/gio.h>
24 : : #include <unistd.h>
25 : : #include <string.h>
26 : :
27 : : #include "gdbus-tests.h"
28 : :
29 : : /* all tests rely on a shared mainloop */
30 : : static GMainLoop *loop = NULL;
31 : :
32 : : static GDBusConnection *c = NULL;
33 : :
34 : : /* ---------------------------------------------------------------------------------------------------- */
35 : : /* Test that we can export objects, the hierarchy is correct and the right handlers are invoked */
36 : : /* ---------------------------------------------------------------------------------------------------- */
37 : :
38 : : static const GDBusArgInfo foo_method1_in_args =
39 : : {
40 : : -1,
41 : : "an_input_string",
42 : : "s",
43 : : NULL
44 : : };
45 : : static const GDBusArgInfo * const foo_method1_in_arg_pointers[] = {&foo_method1_in_args, NULL};
46 : :
47 : : static const GDBusArgInfo foo_method1_out_args =
48 : : {
49 : : -1,
50 : : "an_output_string",
51 : : "s",
52 : : NULL
53 : : };
54 : : static const GDBusArgInfo * const foo_method1_out_arg_pointers[] = {&foo_method1_out_args, NULL};
55 : :
56 : : static const GDBusMethodInfo foo_method_info_method1 =
57 : : {
58 : : -1,
59 : : "Method1",
60 : : (GDBusArgInfo **) &foo_method1_in_arg_pointers,
61 : : (GDBusArgInfo **) &foo_method1_out_arg_pointers,
62 : : NULL
63 : : };
64 : : static const GDBusMethodInfo foo_method_info_method2 =
65 : : {
66 : : -1,
67 : : "Method2",
68 : : NULL,
69 : : NULL,
70 : : NULL
71 : : };
72 : : static const GDBusMethodInfo * const foo_method_info_pointers[] = {&foo_method_info_method1, &foo_method_info_method2, NULL};
73 : :
74 : : static const GDBusSignalInfo foo_signal_info =
75 : : {
76 : : -1,
77 : : "SignalAlpha",
78 : : NULL,
79 : : NULL
80 : : };
81 : : static const GDBusSignalInfo * const foo_signal_info_pointers[] = {&foo_signal_info, NULL};
82 : :
83 : : static const GDBusPropertyInfo foo_property_info[] =
84 : : {
85 : : {
86 : : -1,
87 : : "PropertyUno",
88 : : "s",
89 : : G_DBUS_PROPERTY_INFO_FLAGS_READABLE | G_DBUS_PROPERTY_INFO_FLAGS_WRITABLE,
90 : : NULL
91 : : },
92 : : {
93 : : -1,
94 : : "NotWritable",
95 : : "s",
96 : : G_DBUS_PROPERTY_INFO_FLAGS_READABLE,
97 : : NULL
98 : : },
99 : : {
100 : : -1,
101 : : "NotReadable",
102 : : "s",
103 : : G_DBUS_PROPERTY_INFO_FLAGS_WRITABLE,
104 : : NULL
105 : : }
106 : : };
107 : : static const GDBusPropertyInfo * const foo_property_info_pointers[] =
108 : : {
109 : : &foo_property_info[0],
110 : : &foo_property_info[1],
111 : : &foo_property_info[2],
112 : : NULL
113 : : };
114 : :
115 : : static const GDBusInterfaceInfo foo_interface_info =
116 : : {
117 : : -1,
118 : : "org.example.Foo",
119 : : (GDBusMethodInfo **) &foo_method_info_pointers,
120 : : (GDBusSignalInfo **) &foo_signal_info_pointers,
121 : : (GDBusPropertyInfo **)&foo_property_info_pointers,
122 : : NULL,
123 : : };
124 : :
125 : : /* Foo2 is just Foo without the properties */
126 : : static const GDBusInterfaceInfo foo2_interface_info =
127 : : {
128 : : -1,
129 : : "org.example.Foo2",
130 : : (GDBusMethodInfo **) &foo_method_info_pointers,
131 : : (GDBusSignalInfo **) &foo_signal_info_pointers,
132 : : NULL,
133 : : NULL
134 : : };
135 : :
136 : : static void
137 : 206 : foo_method_call (GDBusConnection *connection,
138 : : const gchar *sender,
139 : : const gchar *object_path,
140 : : const gchar *interface_name,
141 : : const gchar *method_name,
142 : : GVariant *parameters,
143 : : GDBusMethodInvocation *invocation,
144 : : gpointer user_data)
145 : : {
146 [ + + ]: 206 : if (g_strcmp0 (method_name, "Method1") == 0)
147 : : {
148 : : const gchar *input;
149 : : gchar *output;
150 : 203 : g_variant_get (parameters, "(&s)", &input);
151 : 203 : output = g_strdup_printf ("You passed the string '%s'. Jolly good!", input);
152 : 203 : g_dbus_method_invocation_return_value (invocation, g_variant_new ("(s)", output));
153 : 203 : g_free (output);
154 : : }
155 : : else
156 : : {
157 : 3 : g_dbus_method_invocation_return_dbus_error (invocation,
158 : : "org.example.SomeError",
159 : : "How do you like them apples, buddy!");
160 : : }
161 : 206 : }
162 : :
163 : : static GVariant *
164 : 9 : foo_get_property (GDBusConnection *connection,
165 : : const gchar *sender,
166 : : const gchar *object_path,
167 : : const gchar *interface_name,
168 : : const gchar *property_name,
169 : : GError **error,
170 : : gpointer user_data)
171 : : {
172 : : GVariant *ret;
173 : : gchar *s;
174 : 9 : s = g_strdup_printf ("Property '%s' Is What It Is!", property_name);
175 : 9 : ret = g_variant_new_string (s);
176 : 9 : g_free (s);
177 : 9 : return ret;
178 : : }
179 : :
180 : : static gboolean
181 : 3 : foo_set_property (GDBusConnection *connection,
182 : : const gchar *sender,
183 : : const gchar *object_path,
184 : : const gchar *interface_name,
185 : : const gchar *property_name,
186 : : GVariant *value,
187 : : GError **error,
188 : : gpointer user_data)
189 : : {
190 : : gchar *s;
191 : 3 : s = g_variant_print (value, TRUE);
192 : 3 : g_set_error (error,
193 : : G_DBUS_ERROR,
194 : : G_DBUS_ERROR_SPAWN_FILE_INVALID,
195 : : "Returning some error instead of writing the value '%s' to the property '%s'",
196 : : s, property_name);
197 : 3 : g_free (s);
198 : 3 : return FALSE;
199 : : }
200 : :
201 : : static const GDBusInterfaceVTable foo_vtable =
202 : : {
203 : : foo_method_call,
204 : : foo_get_property,
205 : : foo_set_property,
206 : : { 0 },
207 : : };
208 : :
209 : : /* -------------------- */
210 : :
211 : : static const GDBusMethodInfo bar_method_info[] =
212 : : {
213 : : {
214 : : -1,
215 : : "MethodA",
216 : : NULL,
217 : : NULL,
218 : : NULL
219 : : },
220 : : {
221 : : -1,
222 : : "MethodB",
223 : : NULL,
224 : : NULL,
225 : : NULL
226 : : }
227 : : };
228 : : static const GDBusMethodInfo * const bar_method_info_pointers[] = {&bar_method_info[0], &bar_method_info[1], NULL};
229 : :
230 : : static const GDBusSignalInfo bar_signal_info[] =
231 : : {
232 : : {
233 : : -1,
234 : : "SignalMars",
235 : : NULL,
236 : : NULL
237 : : }
238 : : };
239 : : static const GDBusSignalInfo * const bar_signal_info_pointers[] = {&bar_signal_info[0], NULL};
240 : :
241 : : static const GDBusPropertyInfo bar_property_info[] =
242 : : {
243 : : {
244 : : -1,
245 : : "PropertyDuo",
246 : : "s",
247 : : G_DBUS_PROPERTY_INFO_FLAGS_READABLE,
248 : : NULL
249 : : }
250 : : };
251 : : static const GDBusPropertyInfo * const bar_property_info_pointers[] = {&bar_property_info[0], NULL};
252 : :
253 : : static const GDBusInterfaceInfo bar_interface_info =
254 : : {
255 : : -1,
256 : : "org.example.Bar",
257 : : (GDBusMethodInfo **) bar_method_info_pointers,
258 : : (GDBusSignalInfo **) bar_signal_info_pointers,
259 : : (GDBusPropertyInfo **) bar_property_info_pointers,
260 : : NULL,
261 : : };
262 : :
263 : : /* -------------------- */
264 : :
265 : : static const GDBusMethodInfo dyna_method_info[] =
266 : : {
267 : : {
268 : : -1,
269 : : "DynaCyber",
270 : : NULL,
271 : : NULL,
272 : : NULL
273 : : }
274 : : };
275 : : static const GDBusMethodInfo * const dyna_method_info_pointers[] = {&dyna_method_info[0], NULL};
276 : :
277 : : static const GDBusInterfaceInfo dyna_interface_info =
278 : : {
279 : : -1,
280 : : "org.example.Dyna",
281 : : (GDBusMethodInfo **) dyna_method_info_pointers,
282 : : NULL, /* no signals */
283 : : NULL, /* no properties */
284 : : NULL,
285 : : };
286 : :
287 : : static void
288 : 1 : dyna_cyber (GDBusConnection *connection,
289 : : const gchar *sender,
290 : : const gchar *object_path,
291 : : const gchar *interface_name,
292 : : const gchar *method_name,
293 : : GVariant *parameters,
294 : : GDBusMethodInvocation *invocation,
295 : : gpointer user_data)
296 : : {
297 : 1 : GPtrArray *data = user_data;
298 : : gchar *node_name;
299 : : guint n;
300 : :
301 : 1 : node_name = strrchr (object_path, '/') + 1;
302 : :
303 : : /* Add new node if it is not already known */
304 [ + + ]: 4 : for (n = 0; n < data->len ; n++)
305 : : {
306 [ - + ]: 3 : if (g_strcmp0 (g_ptr_array_index (data, n), node_name) == 0)
307 : 0 : goto out;
308 : : }
309 : 1 : g_ptr_array_add (data, g_strdup(node_name));
310 : :
311 : 1 : out:
312 : 1 : g_dbus_method_invocation_return_value (invocation, NULL);
313 : 1 : }
314 : :
315 : : static const GDBusInterfaceVTable dyna_interface_vtable =
316 : : {
317 : : dyna_cyber,
318 : : NULL,
319 : : NULL,
320 : : { 0 }
321 : : };
322 : :
323 : : /* ---------------------------------------------------------------------------------------------------- */
324 : :
325 : : static void
326 : 34 : introspect_callback (GDBusProxy *proxy,
327 : : GAsyncResult *res,
328 : : gpointer user_data)
329 : : {
330 : 34 : gchar **xml_data = user_data;
331 : : GVariant *result;
332 : : GError *error;
333 : :
334 : 34 : error = NULL;
335 : 34 : result = g_dbus_proxy_call_finish (proxy,
336 : : res,
337 : : &error);
338 : 34 : g_assert_no_error (error);
339 : 34 : g_assert_nonnull (result);
340 : 34 : g_variant_get (result, "(s)", xml_data);
341 : 34 : g_variant_unref (result);
342 : :
343 : 34 : g_main_loop_quit (loop);
344 : 34 : }
345 : :
346 : : static gint
347 : 51 : compare_strings (gconstpointer a,
348 : : gconstpointer b)
349 : : {
350 : 51 : const gchar *sa = a;
351 : 51 : const gchar *sb = b;
352 : :
353 : : /* Array terminator must sort last */
354 [ - + ]: 51 : if (sa == NULL)
355 : 0 : return 1;
356 [ + + ]: 51 : if (sb == NULL)
357 : 14 : return -1;
358 : :
359 : 37 : return strcmp (sa, sb);
360 : : }
361 : :
362 : : static gchar **
363 : 11 : get_nodes_at (GDBusConnection *c,
364 : : const gchar *object_path)
365 : : {
366 : : GError *error;
367 : : GDBusProxy *proxy;
368 : : gchar *xml_data;
369 : : GPtrArray *p;
370 : : GDBusNodeInfo *node_info;
371 : : guint n;
372 : :
373 : 11 : error = NULL;
374 : 11 : proxy = g_dbus_proxy_new_sync (c,
375 : : G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
376 : : G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
377 : : NULL,
378 : : g_dbus_connection_get_unique_name (c),
379 : : object_path,
380 : : "org.freedesktop.DBus.Introspectable",
381 : : NULL,
382 : : &error);
383 : 11 : g_assert_no_error (error);
384 : 11 : g_assert_nonnull (proxy);
385 : :
386 : : /* do this async to avoid libdbus-1 deadlocks */
387 : 11 : xml_data = NULL;
388 : 11 : g_dbus_proxy_call (proxy,
389 : : "Introspect",
390 : : NULL,
391 : : G_DBUS_CALL_FLAGS_NONE,
392 : : -1,
393 : : NULL,
394 : : (GAsyncReadyCallback) introspect_callback,
395 : : &xml_data);
396 : 11 : g_main_loop_run (loop);
397 : 11 : g_assert_nonnull (xml_data);
398 : :
399 : 11 : node_info = g_dbus_node_info_new_for_xml (xml_data, &error);
400 : 11 : g_assert_no_error (error);
401 : 11 : g_assert_nonnull (node_info);
402 : :
403 : 11 : p = g_ptr_array_new ();
404 [ + - + + ]: 40 : for (n = 0; node_info->nodes != NULL && node_info->nodes[n] != NULL; n++)
405 : : {
406 : 29 : const GDBusNodeInfo *sub_node_info = node_info->nodes[n];
407 : 58 : g_ptr_array_add (p, g_strdup (sub_node_info->path));
408 : : }
409 : 11 : g_ptr_array_add (p, NULL);
410 : :
411 : 11 : g_object_unref (proxy);
412 : 11 : g_free (xml_data);
413 : 11 : g_dbus_node_info_unref (node_info);
414 : :
415 : : /* Nodes are semantically unordered; sort array so tests can rely on order */
416 : 11 : g_ptr_array_sort_values (p, compare_strings);
417 : :
418 : 11 : return (gchar **) g_ptr_array_free (p, FALSE);
419 : : }
420 : :
421 : : static gboolean
422 : 8 : has_interface (GDBusConnection *c,
423 : : const gchar *object_path,
424 : : const gchar *interface_name)
425 : : {
426 : : GError *error;
427 : : GDBusProxy *proxy;
428 : : gchar *xml_data;
429 : : GDBusNodeInfo *node_info;
430 : : gboolean ret;
431 : :
432 : 8 : error = NULL;
433 : 8 : proxy = g_dbus_proxy_new_sync (c,
434 : : G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
435 : : G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
436 : : NULL,
437 : : g_dbus_connection_get_unique_name (c),
438 : : object_path,
439 : : "org.freedesktop.DBus.Introspectable",
440 : : NULL,
441 : : &error);
442 : 8 : g_assert_no_error (error);
443 : 8 : g_assert_nonnull (proxy);
444 : :
445 : : /* do this async to avoid libdbus-1 deadlocks */
446 : 8 : xml_data = NULL;
447 : 8 : g_dbus_proxy_call (proxy,
448 : : "Introspect",
449 : : NULL,
450 : : G_DBUS_CALL_FLAGS_NONE,
451 : : -1,
452 : : NULL,
453 : : (GAsyncReadyCallback) introspect_callback,
454 : : &xml_data);
455 : 8 : g_main_loop_run (loop);
456 : 8 : g_assert_nonnull (xml_data);
457 : :
458 : 8 : node_info = g_dbus_node_info_new_for_xml (xml_data, &error);
459 : 8 : g_assert_no_error (error);
460 : 8 : g_assert_nonnull (node_info);
461 : :
462 : 8 : ret = (g_dbus_node_info_lookup_interface (node_info, interface_name) != NULL);
463 : :
464 : 8 : g_object_unref (proxy);
465 : 8 : g_free (xml_data);
466 : 8 : g_dbus_node_info_unref (node_info);
467 : :
468 : 8 : return ret;
469 : : }
470 : :
471 : : static guint
472 : 14 : count_interfaces (GDBusConnection *c,
473 : : const gchar *object_path)
474 : : {
475 : : GError *error;
476 : : GDBusProxy *proxy;
477 : : gchar *xml_data;
478 : : GDBusNodeInfo *node_info;
479 : : guint ret;
480 : :
481 : 14 : error = NULL;
482 : 14 : proxy = g_dbus_proxy_new_sync (c,
483 : : G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
484 : : G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
485 : : NULL,
486 : : g_dbus_connection_get_unique_name (c),
487 : : object_path,
488 : : "org.freedesktop.DBus.Introspectable",
489 : : NULL,
490 : : &error);
491 : 14 : g_assert_no_error (error);
492 : 14 : g_assert_nonnull (proxy);
493 : :
494 : : /* do this async to avoid libdbus-1 deadlocks */
495 : 14 : xml_data = NULL;
496 : 14 : g_dbus_proxy_call (proxy,
497 : : "Introspect",
498 : : NULL,
499 : : G_DBUS_CALL_FLAGS_NONE,
500 : : -1,
501 : : NULL,
502 : : (GAsyncReadyCallback) introspect_callback,
503 : : &xml_data);
504 : 14 : g_main_loop_run (loop);
505 : 14 : g_assert_nonnull (xml_data);
506 : :
507 : 14 : node_info = g_dbus_node_info_new_for_xml (xml_data, &error);
508 : 14 : g_assert_no_error (error);
509 : 14 : g_assert_nonnull (node_info);
510 : :
511 : 14 : ret = 0;
512 [ + - + + ]: 60 : while (node_info->interfaces != NULL && node_info->interfaces[ret] != NULL)
513 : 46 : ret++;
514 : :
515 : 14 : g_object_unref (proxy);
516 : 14 : g_free (xml_data);
517 : 14 : g_dbus_node_info_unref (node_info);
518 : :
519 : 14 : return ret;
520 : : }
521 : :
522 : : static void
523 : 1 : dyna_create_callback (GDBusProxy *proxy,
524 : : GAsyncResult *res,
525 : : gpointer user_data)
526 : : {
527 : : GVariant *result;
528 : : GError *error;
529 : :
530 : 1 : error = NULL;
531 : 1 : result = g_dbus_proxy_call_finish (proxy,
532 : : res,
533 : : &error);
534 : 1 : g_assert_no_error (error);
535 : 1 : g_assert_nonnull (result);
536 : 1 : g_variant_unref (result);
537 : :
538 : 1 : g_main_loop_quit (loop);
539 : 1 : }
540 : :
541 : : /* Dynamically create @object_name under /foo/dyna */
542 : : static void
543 : 1 : dyna_create (GDBusConnection *c,
544 : : const gchar *object_name)
545 : : {
546 : : GError *error;
547 : : GDBusProxy *proxy;
548 : : gchar *object_path;
549 : :
550 : 1 : object_path = g_strconcat ("/foo/dyna/", object_name, NULL);
551 : :
552 : 1 : error = NULL;
553 : 1 : proxy = g_dbus_proxy_new_sync (c,
554 : : G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
555 : : G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
556 : : NULL,
557 : : g_dbus_connection_get_unique_name (c),
558 : : object_path,
559 : : "org.example.Dyna",
560 : : NULL,
561 : : &error);
562 : 1 : g_assert_no_error (error);
563 : 1 : g_assert_nonnull (proxy);
564 : :
565 : : /* do this async to avoid libdbus-1 deadlocks */
566 : 1 : g_dbus_proxy_call (proxy,
567 : : "DynaCyber",
568 : : g_variant_new ("()"),
569 : : G_DBUS_CALL_FLAGS_NONE,
570 : : -1,
571 : : NULL,
572 : : (GAsyncReadyCallback) dyna_create_callback,
573 : : NULL);
574 : 1 : g_main_loop_run (loop);
575 : :
576 : 1 : g_assert_no_error (error);
577 : :
578 : 1 : g_object_unref (proxy);
579 : 1 : g_free (object_path);
580 : :
581 : 1 : return;
582 : : }
583 : :
584 : : typedef struct
585 : : {
586 : : guint num_unregistered_calls;
587 : : guint num_unregistered_subtree_calls;
588 : : guint num_subtree_nodes;
589 : : } ObjectRegistrationData;
590 : :
591 : : static void
592 : 219 : on_object_unregistered (gpointer user_data)
593 : : {
594 : 219 : ObjectRegistrationData *data = user_data;
595 : :
596 : 219 : data->num_unregistered_calls++;
597 : 219 : }
598 : :
599 : : static void
600 : 276 : on_subtree_unregistered (gpointer user_data)
601 : : {
602 : 276 : ObjectRegistrationData *data = user_data;
603 : :
604 : 276 : data->num_unregistered_subtree_calls++;
605 : 276 : }
606 : :
607 : : /* -------------------- */
608 : :
609 : : static gchar **
610 : 174 : subtree_enumerate (GDBusConnection *connection,
611 : : const gchar *sender,
612 : : const gchar *object_path,
613 : : gpointer user_data)
614 : : {
615 : 174 : ObjectRegistrationData *data = user_data;
616 : : GPtrArray *p;
617 : : gchar **nodes;
618 : : guint n;
619 : :
620 : 174 : p = g_ptr_array_new ();
621 : :
622 [ + + ]: 531 : for (n = 0; n < data->num_subtree_nodes; n++)
623 : : {
624 : 357 : g_ptr_array_add (p, g_strdup_printf ("vp%d", n));
625 : 357 : g_ptr_array_add (p, g_strdup_printf ("evp%d", n));
626 : : }
627 : 174 : g_ptr_array_add (p, NULL);
628 : :
629 : 174 : nodes = (gchar **) g_ptr_array_free (p, FALSE);
630 : :
631 : 174 : return nodes;
632 : : }
633 : :
634 : : /* Only allows certain objects, and aborts on unknowns */
635 : : static GDBusInterfaceInfo **
636 : 174 : subtree_introspect (GDBusConnection *connection,
637 : : const gchar *sender,
638 : : const gchar *object_path,
639 : : const gchar *node,
640 : : gpointer user_data)
641 : : {
642 : 174 : const GDBusInterfaceInfo *interfaces[2] = {
643 : : NULL /* filled in below */, NULL
644 : : };
645 : :
646 : : /* VPs implement the Foo interface, EVPs implement the Bar interface. The root
647 : : * does not implement any interfaces
648 : : */
649 [ + + ]: 174 : if (node == NULL)
650 : : {
651 : 4 : return NULL;
652 : : }
653 [ + - - + : 170 : else if (g_str_has_prefix (node, "vp"))
+ - + + ]
654 : : {
655 : 166 : interfaces[0] = &foo_interface_info;
656 : : }
657 [ + - - + : 4 : else if (g_str_has_prefix (node, "evp"))
+ - + - ]
658 : : {
659 : 4 : interfaces[0] = &bar_interface_info;
660 : : }
661 : : else
662 : : {
663 : : g_assert_not_reached ();
664 : : }
665 : :
666 : 170 : return g_memdup2 (interfaces, 2 * sizeof (void *));
667 : : }
668 : :
669 : : static const GDBusInterfaceVTable *
670 : 160 : subtree_dispatch (GDBusConnection *connection,
671 : : const gchar *sender,
672 : : const gchar *object_path,
673 : : const gchar *interface_name,
674 : : const gchar *node,
675 : : gpointer *out_user_data,
676 : : gpointer user_data)
677 : : {
678 [ + - ]: 160 : if (g_strcmp0 (interface_name, "org.example.Foo") == 0)
679 : 160 : return &foo_vtable;
680 : : else
681 : 0 : return NULL;
682 : : }
683 : :
684 : : static const GDBusSubtreeVTable subtree_vtable =
685 : : {
686 : : subtree_enumerate,
687 : : subtree_introspect,
688 : : subtree_dispatch,
689 : : { 0 }
690 : : };
691 : :
692 : : /* -------------------- */
693 : :
694 : : static gchar **
695 : 8 : dynamic_subtree_enumerate (GDBusConnection *connection,
696 : : const gchar *sender,
697 : : const gchar *object_path,
698 : : gpointer user_data)
699 : : {
700 : 8 : GPtrArray *data = user_data;
701 : 8 : gchar **nodes = g_new (gchar*, data->len + 1);
702 : : guint n;
703 : :
704 [ + + ]: 28 : for (n = 0; n < data->len; n++)
705 : : {
706 : 40 : nodes[n] = g_strdup (g_ptr_array_index (data, n));
707 : : }
708 : 8 : nodes[data->len] = NULL;
709 : :
710 : 8 : return nodes;
711 : : }
712 : :
713 : : /* Allow all objects to be introspected */
714 : : static GDBusInterfaceInfo **
715 : 9 : dynamic_subtree_introspect (GDBusConnection *connection,
716 : : const gchar *sender,
717 : : const gchar *object_path,
718 : : const gchar *node,
719 : : gpointer user_data)
720 : : {
721 : 9 : const GDBusInterfaceInfo *interfaces[2] = { &dyna_interface_info, NULL };
722 : :
723 : 9 : return g_memdup2 (interfaces, 2 * sizeof (void *));
724 : : }
725 : :
726 : : static const GDBusInterfaceVTable *
727 : 1 : dynamic_subtree_dispatch (GDBusConnection *connection,
728 : : const gchar *sender,
729 : : const gchar *object_path,
730 : : const gchar *interface_name,
731 : : const gchar *node,
732 : : gpointer *out_user_data,
733 : : gpointer user_data)
734 : : {
735 : 1 : *out_user_data = user_data;
736 : 1 : return &dyna_interface_vtable;
737 : : }
738 : :
739 : : static const GDBusSubtreeVTable dynamic_subtree_vtable =
740 : : {
741 : : dynamic_subtree_enumerate,
742 : : dynamic_subtree_introspect,
743 : : dynamic_subtree_dispatch,
744 : : { 0 }
745 : : };
746 : :
747 : : /* -------------------- */
748 : :
749 : : typedef struct
750 : : {
751 : : const gchar *object_path;
752 : : gboolean check_remote_errors;
753 : : } TestDispatchThreadFuncArgs;
754 : :
755 : : static gpointer
756 : 3 : test_dispatch_thread_func (gpointer user_data)
757 : : {
758 : 3 : TestDispatchThreadFuncArgs *args = user_data;
759 : 3 : const gchar *object_path = args->object_path;
760 : : GDBusProxy *foo_proxy;
761 : : GVariant *value;
762 : : GVariant *inner;
763 : : GError *error;
764 : : gchar *s;
765 : : const gchar *value_str;
766 : :
767 : 3 : foo_proxy = g_dbus_proxy_new_sync (c,
768 : : G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS |
769 : : G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
770 : : NULL,
771 : : g_dbus_connection_get_unique_name (c),
772 : : object_path,
773 : : "org.example.Foo",
774 : : NULL,
775 : : &error);
776 : 3 : g_assert_nonnull (foo_proxy);
777 : :
778 : : /* generic interfaces */
779 : 3 : error = NULL;
780 : 3 : value = g_dbus_proxy_call_sync (foo_proxy,
781 : : "org.freedesktop.DBus.Peer.Ping",
782 : : NULL,
783 : : G_DBUS_CALL_FLAGS_NONE,
784 : : -1,
785 : : NULL,
786 : : &error);
787 : 3 : g_assert_no_error (error);
788 : 3 : g_assert_nonnull (value);
789 : 3 : g_variant_unref (value);
790 : :
791 : : /* user methods */
792 : 3 : error = NULL;
793 : 3 : value = g_dbus_proxy_call_sync (foo_proxy,
794 : : "Method1",
795 : : g_variant_new ("(s)", "winwinwin"),
796 : : G_DBUS_CALL_FLAGS_NONE,
797 : : -1,
798 : : NULL,
799 : : &error);
800 : 3 : g_assert_no_error (error);
801 : 3 : g_assert_nonnull (value);
802 : 3 : g_assert_true (g_variant_is_of_type (value, G_VARIANT_TYPE ("(s)")));
803 : 3 : g_variant_get (value, "(&s)", &value_str);
804 : 3 : g_assert_cmpstr (value_str, ==, "You passed the string 'winwinwin'. Jolly good!");
805 : 3 : g_variant_unref (value);
806 : :
807 : 3 : error = NULL;
808 : 3 : value = g_dbus_proxy_call_sync (foo_proxy,
809 : : "Method2",
810 : : NULL,
811 : : G_DBUS_CALL_FLAGS_NONE,
812 : : -1,
813 : : NULL,
814 : : &error);
815 : 3 : g_assert_error (error, G_IO_ERROR, G_IO_ERROR_DBUS_ERROR);
816 : 3 : g_assert_cmpstr (error->message, ==, "GDBus.Error:org.example.SomeError: How do you like them apples, buddy!");
817 : 3 : g_error_free (error);
818 : 3 : g_assert_null (value);
819 : :
820 : 3 : error = NULL;
821 : 3 : value = g_dbus_proxy_call_sync (foo_proxy,
822 : : "Method2",
823 : : g_variant_new ("(s)", "failfailfail"),
824 : : G_DBUS_CALL_FLAGS_NONE,
825 : : -1,
826 : : NULL,
827 : : &error);
828 : 3 : g_assert_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS);
829 : 3 : g_assert_cmpstr (error->message, ==, "GDBus.Error:org.freedesktop.DBus.Error.InvalidArgs: Type of message, “(s)”, does not match expected type “()”");
830 : 3 : g_error_free (error);
831 : 3 : g_assert_null (value);
832 : :
833 : 3 : error = NULL;
834 : 3 : value = g_dbus_proxy_call_sync (foo_proxy,
835 : : "NonExistantMethod",
836 : : NULL,
837 : : G_DBUS_CALL_FLAGS_NONE,
838 : : -1,
839 : : NULL,
840 : : &error);
841 : 3 : g_assert_error (error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD);
842 : 3 : g_assert_cmpstr (error->message, ==, "GDBus.Error:org.freedesktop.DBus.Error.UnknownMethod: No such method “NonExistantMethod”");
843 : 3 : g_error_free (error);
844 : 3 : g_assert_null (value);
845 : :
846 : 3 : error = NULL;
847 : 3 : value = g_dbus_proxy_call_sync (foo_proxy,
848 : : "org.example.FooXYZ.NonExistant",
849 : : NULL,
850 : : G_DBUS_CALL_FLAGS_NONE,
851 : : -1,
852 : : NULL,
853 : : &error);
854 : 3 : g_assert_error (error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD);
855 : 3 : g_error_free (error);
856 : 3 : g_assert_null (value);
857 : :
858 : : /* user properties */
859 : 3 : error = NULL;
860 : 3 : value = g_dbus_proxy_call_sync (foo_proxy,
861 : : "org.freedesktop.DBus.Properties.Get",
862 : : g_variant_new ("(ss)",
863 : : "org.example.Foo",
864 : : "PropertyUno"),
865 : : G_DBUS_CALL_FLAGS_NONE,
866 : : -1,
867 : : NULL,
868 : : &error);
869 : 3 : g_assert_no_error (error);
870 : 3 : g_assert_nonnull (value);
871 : 3 : g_assert_true (g_variant_is_of_type (value, G_VARIANT_TYPE ("(v)")));
872 : 3 : g_variant_get (value, "(v)", &inner);
873 : 3 : g_assert_true (g_variant_is_of_type (inner, G_VARIANT_TYPE_STRING));
874 : 3 : g_assert_cmpstr (g_variant_get_string (inner, NULL), ==, "Property 'PropertyUno' Is What It Is!");
875 : 3 : g_variant_unref (value);
876 : 3 : g_variant_unref (inner);
877 : :
878 : 3 : error = NULL;
879 : 3 : value = g_dbus_proxy_call_sync (foo_proxy,
880 : : "org.freedesktop.DBus.Properties.Get",
881 : : g_variant_new ("(ss)",
882 : : "org.example.Foo",
883 : : "ThisDoesntExist"),
884 : : G_DBUS_CALL_FLAGS_NONE,
885 : : -1,
886 : : NULL,
887 : : &error);
888 : 3 : g_assert_null (value);
889 : 3 : g_assert_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS);
890 : 3 : g_assert_cmpstr (error->message, ==, "GDBus.Error:org.freedesktop.DBus.Error.InvalidArgs: No such property “ThisDoesntExist”");
891 : 3 : g_error_free (error);
892 : :
893 : 3 : error = NULL;
894 : 3 : value = g_dbus_proxy_call_sync (foo_proxy,
895 : : "org.freedesktop.DBus.Properties.Get",
896 : : g_variant_new ("(ss)",
897 : : "org.example.Foo",
898 : : "NotReadable"),
899 : : G_DBUS_CALL_FLAGS_NONE,
900 : : -1,
901 : : NULL,
902 : : &error);
903 : 3 : g_assert_null (value);
904 : 3 : g_assert_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS);
905 : 3 : g_assert_cmpstr (error->message, ==, "GDBus.Error:org.freedesktop.DBus.Error.InvalidArgs: Property “NotReadable” is not readable");
906 : 3 : g_error_free (error);
907 : :
908 : 3 : error = NULL;
909 : 3 : value = g_dbus_proxy_call_sync (foo_proxy,
910 : : "org.freedesktop.DBus.Properties.Set",
911 : : g_variant_new ("(ssv)",
912 : : "org.example.Foo",
913 : : "NotReadable",
914 : : g_variant_new_string ("But Writable you are!")),
915 : : G_DBUS_CALL_FLAGS_NONE,
916 : : -1,
917 : : NULL,
918 : : &error);
919 : 3 : g_assert_null (value);
920 [ + + ]: 3 : if (args->check_remote_errors)
921 : : {
922 : : /* _with_closures variant doesn't support customizing error data. */
923 : 2 : g_assert_error (error, G_DBUS_ERROR, G_DBUS_ERROR_SPAWN_FILE_INVALID);
924 : 2 : g_assert_cmpstr (error->message, ==, "GDBus.Error:org.freedesktop.DBus.Error.Spawn.FileInvalid: Returning some error instead of writing the value ''But Writable you are!'' to the property 'NotReadable'");
925 : : }
926 : 3 : g_assert_true (error != NULL && error->domain == G_DBUS_ERROR);
927 : 3 : g_error_free (error);
928 : :
929 : 3 : error = NULL;
930 : 3 : value = g_dbus_proxy_call_sync (foo_proxy,
931 : : "org.freedesktop.DBus.Properties.Set",
932 : : g_variant_new ("(ssv)",
933 : : "org.example.Foo",
934 : : "NotWritable",
935 : : g_variant_new_uint32 (42)),
936 : : G_DBUS_CALL_FLAGS_NONE,
937 : : -1,
938 : : NULL,
939 : : &error);
940 : 3 : g_assert_null (value);
941 : 3 : g_assert_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS);
942 : 3 : g_assert_cmpstr (error->message, ==, "GDBus.Error:org.freedesktop.DBus.Error.InvalidArgs: Property “NotWritable” is not writable");
943 : 3 : g_error_free (error);
944 : :
945 : 3 : error = NULL;
946 : 3 : value = g_dbus_proxy_call_sync (foo_proxy,
947 : : "org.freedesktop.DBus.Properties.GetAll",
948 : : g_variant_new ("(s)",
949 : : "org.example.Foo"),
950 : : G_DBUS_CALL_FLAGS_NONE,
951 : : -1,
952 : : NULL,
953 : : &error);
954 : 3 : g_assert_no_error (error);
955 : 3 : g_assert_nonnull (value);
956 : 3 : g_assert_true (g_variant_is_of_type (value, G_VARIANT_TYPE ("(a{sv})")));
957 : 3 : s = g_variant_print (value, TRUE);
958 : 3 : g_assert_cmpstr (s, ==, "({'PropertyUno': <\"Property 'PropertyUno' Is What It Is!\">, 'NotWritable': <\"Property 'NotWritable' Is What It Is!\">},)");
959 : 3 : g_free (s);
960 : 3 : g_variant_unref (value);
961 : :
962 : 3 : g_object_unref (foo_proxy);
963 : :
964 : 3 : g_main_loop_quit (loop);
965 : 3 : return NULL;
966 : : }
967 : :
968 : : static void
969 : 3 : test_dispatch (const gchar *object_path,
970 : : gboolean check_remote_errors)
971 : : {
972 : : GThread *thread;
973 : :
974 : 3 : TestDispatchThreadFuncArgs args = {object_path, check_remote_errors};
975 : :
976 : : /* run this in a thread to avoid deadlocks */
977 : 3 : thread = g_thread_new ("test_dispatch",
978 : : test_dispatch_thread_func,
979 : : (gpointer) &args);
980 : 3 : g_main_loop_run (loop);
981 : 3 : g_thread_join (thread);
982 : 3 : }
983 : :
984 : : static void
985 : 1 : test_object_registration (void)
986 : : {
987 : : GError *error;
988 : : ObjectRegistrationData data;
989 : : GPtrArray *dyna_data;
990 : : gchar **nodes;
991 : : guint boss_foo_reg_id;
992 : : guint boss_bar_reg_id;
993 : : guint worker1_foo_reg_id;
994 : : guint worker1p1_foo_reg_id;
995 : : guint worker2_bar_reg_id;
996 : : guint intern1_foo_reg_id;
997 : : guint intern2_bar_reg_id;
998 : : guint intern2_foo_reg_id;
999 : : guint intern3_bar_reg_id;
1000 : : guint registration_id;
1001 : : guint subtree_registration_id;
1002 : : guint non_subtree_object_path_foo_reg_id;
1003 : : guint non_subtree_object_path_bar_reg_id;
1004 : : guint dyna_subtree_registration_id;
1005 : : guint num_successful_registrations;
1006 : : guint num_failed_registrations;
1007 : :
1008 : 1 : data.num_unregistered_calls = 0;
1009 : 1 : data.num_unregistered_subtree_calls = 0;
1010 : 1 : data.num_subtree_nodes = 0;
1011 : :
1012 : 1 : num_successful_registrations = 0;
1013 : 1 : num_failed_registrations = 0;
1014 : :
1015 : 1 : error = NULL;
1016 : 1 : c = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
1017 : 1 : g_assert_no_error (error);
1018 : 1 : g_assert_nonnull (c);
1019 : :
1020 : 1 : registration_id = g_dbus_connection_register_object (c,
1021 : : "/foo/boss",
1022 : : (GDBusInterfaceInfo *) &foo_interface_info,
1023 : : &foo_vtable,
1024 : : &data,
1025 : : on_object_unregistered,
1026 : : &error);
1027 : 1 : g_assert_no_error (error);
1028 : 1 : g_assert_cmpuint (registration_id, >, 0);
1029 : 1 : boss_foo_reg_id = registration_id;
1030 : 1 : num_successful_registrations++;
1031 : :
1032 : 1 : registration_id = g_dbus_connection_register_object (c,
1033 : : "/foo/boss",
1034 : : (GDBusInterfaceInfo *) &bar_interface_info,
1035 : : NULL,
1036 : : &data,
1037 : : on_object_unregistered,
1038 : : &error);
1039 : 1 : g_assert_no_error (error);
1040 : 1 : g_assert_cmpuint (registration_id, >, 0);
1041 : 1 : boss_bar_reg_id = registration_id;
1042 : 1 : num_successful_registrations++;
1043 : :
1044 : 1 : registration_id = g_dbus_connection_register_object (c,
1045 : : "/foo/boss/worker1",
1046 : : (GDBusInterfaceInfo *) &foo_interface_info,
1047 : : NULL,
1048 : : &data,
1049 : : on_object_unregistered,
1050 : : &error);
1051 : 1 : g_assert_no_error (error);
1052 : 1 : g_assert_cmpuint (registration_id, >, 0);
1053 : 1 : worker1_foo_reg_id = registration_id;
1054 : 1 : num_successful_registrations++;
1055 : :
1056 : 1 : registration_id = g_dbus_connection_register_object (c,
1057 : : "/foo/boss/worker1p1",
1058 : : (GDBusInterfaceInfo *) &foo_interface_info,
1059 : : NULL,
1060 : : &data,
1061 : : on_object_unregistered,
1062 : : &error);
1063 : 1 : g_assert_no_error (error);
1064 : 1 : g_assert_cmpuint (registration_id, >, 0);
1065 : 1 : worker1p1_foo_reg_id = registration_id;
1066 : 1 : num_successful_registrations++;
1067 : :
1068 : 1 : registration_id = g_dbus_connection_register_object (c,
1069 : : "/foo/boss/worker2",
1070 : : (GDBusInterfaceInfo *) &bar_interface_info,
1071 : : NULL,
1072 : : &data,
1073 : : on_object_unregistered,
1074 : : &error);
1075 : 1 : g_assert_no_error (error);
1076 : 1 : g_assert_cmpuint (registration_id, >, 0);
1077 : 1 : worker2_bar_reg_id = registration_id;
1078 : 1 : num_successful_registrations++;
1079 : :
1080 : 1 : registration_id = g_dbus_connection_register_object (c,
1081 : : "/foo/boss/interns/intern1",
1082 : : (GDBusInterfaceInfo *) &foo_interface_info,
1083 : : NULL,
1084 : : &data,
1085 : : on_object_unregistered,
1086 : : &error);
1087 : 1 : g_assert_no_error (error);
1088 : 1 : g_assert_cmpuint (registration_id, >, 0);
1089 : 1 : intern1_foo_reg_id = registration_id;
1090 : 1 : num_successful_registrations++;
1091 : :
1092 : : /* ... and try again at another path */
1093 : 1 : registration_id = g_dbus_connection_register_object (c,
1094 : : "/foo/boss/interns/intern2",
1095 : : (GDBusInterfaceInfo *) &bar_interface_info,
1096 : : NULL,
1097 : : &data,
1098 : : on_object_unregistered,
1099 : : &error);
1100 : 1 : g_assert_no_error (error);
1101 : 1 : g_assert_cmpuint (registration_id, >, 0);
1102 : 1 : intern2_bar_reg_id = registration_id;
1103 : 1 : num_successful_registrations++;
1104 : :
1105 : : /* register at the same path/interface - this should fail and result in an
1106 : : * immediate unregistration (so the user_data isn’t leaked) */
1107 : 1 : registration_id = g_dbus_connection_register_object (c,
1108 : : "/foo/boss/interns/intern2",
1109 : : (GDBusInterfaceInfo *) &bar_interface_info,
1110 : : NULL,
1111 : : &data,
1112 : : on_object_unregistered,
1113 : : &error);
1114 : 1 : g_assert_error (error, G_IO_ERROR, G_IO_ERROR_EXISTS);
1115 : 1 : g_assert_false (g_dbus_error_is_remote_error (error));
1116 : 1 : g_error_free (error);
1117 : 1 : error = NULL;
1118 : 1 : g_assert_cmpuint (registration_id, ==, 0);
1119 : 1 : g_assert_cmpint (data.num_unregistered_calls, ==, 1);
1120 : 1 : num_failed_registrations++;
1121 : :
1122 : : /* register at different interface - shouldn't fail */
1123 : 1 : registration_id = g_dbus_connection_register_object (c,
1124 : : "/foo/boss/interns/intern2",
1125 : : (GDBusInterfaceInfo *) &foo_interface_info,
1126 : : NULL,
1127 : : &data,
1128 : : on_object_unregistered,
1129 : : &error);
1130 : 1 : g_assert_no_error (error);
1131 : 1 : g_assert_cmpuint (registration_id, >, 0);
1132 : 1 : intern2_foo_reg_id = registration_id;
1133 : 1 : num_successful_registrations++;
1134 : :
1135 : : /* unregister it via the id */
1136 : 1 : g_assert_true (g_dbus_connection_unregister_object (c, registration_id));
1137 : 1 : g_main_context_iteration (NULL, FALSE);
1138 : 1 : g_assert_cmpint (data.num_unregistered_calls, ==, 2);
1139 : 1 : intern2_foo_reg_id = 0;
1140 : :
1141 : : /* register it back */
1142 : 1 : registration_id = g_dbus_connection_register_object (c,
1143 : : "/foo/boss/interns/intern2",
1144 : : (GDBusInterfaceInfo *) &foo_interface_info,
1145 : : NULL,
1146 : : &data,
1147 : : on_object_unregistered,
1148 : : &error);
1149 : 1 : g_assert_no_error (error);
1150 : 1 : g_assert_cmpuint (registration_id, >, 0);
1151 : 1 : intern2_foo_reg_id = registration_id;
1152 : 1 : num_successful_registrations++;
1153 : :
1154 : 1 : registration_id = g_dbus_connection_register_object (c,
1155 : : "/foo/boss/interns/intern3",
1156 : : (GDBusInterfaceInfo *) &bar_interface_info,
1157 : : NULL,
1158 : : &data,
1159 : : on_object_unregistered,
1160 : : &error);
1161 : 1 : g_assert_no_error (error);
1162 : 1 : g_assert_cmpuint (registration_id, >, 0);
1163 : 1 : intern3_bar_reg_id = registration_id;
1164 : 1 : num_successful_registrations++;
1165 : :
1166 : : /* now register a whole subtree at /foo/boss/executives */
1167 : 1 : subtree_registration_id = g_dbus_connection_register_subtree (c,
1168 : : "/foo/boss/executives",
1169 : : &subtree_vtable,
1170 : : G_DBUS_SUBTREE_FLAGS_NONE,
1171 : : &data,
1172 : : on_subtree_unregistered,
1173 : : &error);
1174 : 1 : g_assert_no_error (error);
1175 : 1 : g_assert_cmpuint (subtree_registration_id, >, 0);
1176 : : /* try registering it again.. this should fail */
1177 : 1 : registration_id = g_dbus_connection_register_subtree (c,
1178 : : "/foo/boss/executives",
1179 : : &subtree_vtable,
1180 : : G_DBUS_SUBTREE_FLAGS_NONE,
1181 : : &data,
1182 : : on_subtree_unregistered,
1183 : : &error);
1184 : 1 : g_assert_error (error, G_IO_ERROR, G_IO_ERROR_EXISTS);
1185 : 1 : g_assert_false (g_dbus_error_is_remote_error (error));
1186 : 1 : g_error_free (error);
1187 : 1 : error = NULL;
1188 : 1 : g_assert_cmpuint (registration_id, ==, 0);
1189 : 1 : g_assert_cmpint (data.num_unregistered_subtree_calls, ==, 1);
1190 : :
1191 : : /* unregister it, then register it again */
1192 : 1 : g_assert_true (g_dbus_connection_unregister_subtree (c, subtree_registration_id));
1193 : 1 : g_main_context_iteration (NULL, FALSE);
1194 : 1 : g_assert_cmpint (data.num_unregistered_subtree_calls, ==, 2);
1195 : 1 : subtree_registration_id = g_dbus_connection_register_subtree (c,
1196 : : "/foo/boss/executives",
1197 : : &subtree_vtable,
1198 : : G_DBUS_SUBTREE_FLAGS_NONE,
1199 : : &data,
1200 : : on_subtree_unregistered,
1201 : : &error);
1202 : 1 : g_assert_no_error (error);
1203 : 1 : g_assert_cmpuint (subtree_registration_id, >, 0);
1204 : :
1205 : : /* try to register something under /foo/boss/executives - this should work
1206 : : * because registered subtrees and registered objects can coexist.
1207 : : *
1208 : : * Make the exported object implement *two* interfaces so we can check
1209 : : * that the right introspection handler is invoked.
1210 : : */
1211 : 1 : registration_id = g_dbus_connection_register_object (c,
1212 : : "/foo/boss/executives/non_subtree_object",
1213 : : (GDBusInterfaceInfo *) &bar_interface_info,
1214 : : NULL,
1215 : : &data,
1216 : : on_object_unregistered,
1217 : : &error);
1218 : 1 : g_assert_no_error (error);
1219 : 1 : g_assert_cmpuint (registration_id, >, 0);
1220 : 1 : non_subtree_object_path_bar_reg_id = registration_id;
1221 : 1 : num_successful_registrations++;
1222 : 1 : registration_id = g_dbus_connection_register_object (c,
1223 : : "/foo/boss/executives/non_subtree_object",
1224 : : (GDBusInterfaceInfo *) &foo_interface_info,
1225 : : NULL,
1226 : : &data,
1227 : : on_object_unregistered,
1228 : : &error);
1229 : 1 : g_assert_no_error (error);
1230 : 1 : g_assert_cmpuint (registration_id, >, 0);
1231 : 1 : non_subtree_object_path_foo_reg_id = registration_id;
1232 : 1 : num_successful_registrations++;
1233 : :
1234 : : /* now register a dynamic subtree, spawning objects as they are called */
1235 : 1 : dyna_data = g_ptr_array_new_with_free_func (g_free);
1236 : 1 : dyna_subtree_registration_id = g_dbus_connection_register_subtree (c,
1237 : : "/foo/dyna",
1238 : : &dynamic_subtree_vtable,
1239 : : G_DBUS_SUBTREE_FLAGS_DISPATCH_TO_UNENUMERATED_NODES,
1240 : : dyna_data,
1241 : : (GDestroyNotify)g_ptr_array_unref,
1242 : : &error);
1243 : 1 : g_assert_no_error (error);
1244 : 1 : g_assert_cmpuint (dyna_subtree_registration_id, >, 0);
1245 : :
1246 : : /* First assert that we have no nodes in the dynamic subtree */
1247 : 1 : nodes = get_nodes_at (c, "/foo/dyna");
1248 : 1 : g_assert_nonnull (nodes);
1249 : 1 : g_assert_cmpint (g_strv_length (nodes), ==, 0);
1250 : 1 : g_strfreev (nodes);
1251 : 1 : g_assert_cmpint (count_interfaces (c, "/foo/dyna"), ==, 4);
1252 : :
1253 : : /* Install three nodes in the dynamic subtree via the dyna_data backdoor and
1254 : : * assert that they show up correctly in the introspection data */
1255 : 1 : g_ptr_array_add (dyna_data, g_strdup ("lol"));
1256 : 1 : g_ptr_array_add (dyna_data, g_strdup ("cat"));
1257 : 1 : g_ptr_array_add (dyna_data, g_strdup ("cheezburger"));
1258 : 1 : nodes = get_nodes_at (c, "/foo/dyna");
1259 : 1 : g_assert_nonnull (nodes);
1260 : 1 : g_assert_cmpint (g_strv_length (nodes), ==, 3);
1261 : 1 : g_assert_cmpstr (nodes[0], ==, "cat");
1262 : 1 : g_assert_cmpstr (nodes[1], ==, "cheezburger");
1263 : 1 : g_assert_cmpstr (nodes[2], ==, "lol");
1264 : 1 : g_strfreev (nodes);
1265 : 1 : g_assert_cmpint (count_interfaces (c, "/foo/dyna/lol"), ==, 4);
1266 : 1 : g_assert_cmpint (count_interfaces (c, "/foo/dyna/cat"), ==, 4);
1267 : 1 : g_assert_cmpint (count_interfaces (c, "/foo/dyna/cheezburger"), ==, 4);
1268 : :
1269 : : /* Call a non-existing object path and assert that it has been created */
1270 : 1 : dyna_create (c, "dynamicallycreated");
1271 : 1 : nodes = get_nodes_at (c, "/foo/dyna");
1272 : 1 : g_assert_nonnull (nodes);
1273 : 1 : g_assert_cmpint (g_strv_length (nodes), ==, 4);
1274 : 1 : g_assert_cmpstr (nodes[0], ==, "cat");
1275 : 1 : g_assert_cmpstr (nodes[1], ==, "cheezburger");
1276 : 1 : g_assert_cmpstr (nodes[2], ==, "dynamicallycreated");
1277 : 1 : g_assert_cmpstr (nodes[3], ==, "lol");
1278 : 1 : g_strfreev (nodes);
1279 : 1 : g_assert_cmpint (count_interfaces (c, "/foo/dyna/dynamicallycreated"), ==, 4);
1280 : :
1281 : : /* now check that the object hierarchy is properly generated... yes, it's a bit
1282 : : * perverse that we round-trip to the bus to introspect ourselves ;-)
1283 : : */
1284 : 1 : nodes = get_nodes_at (c, "/");
1285 : 1 : g_assert_nonnull (nodes);
1286 : 1 : g_assert_cmpint (g_strv_length (nodes), ==, 1);
1287 : 1 : g_assert_cmpstr (nodes[0], ==, "foo");
1288 : 1 : g_strfreev (nodes);
1289 : 1 : g_assert_cmpint (count_interfaces (c, "/"), ==, 0);
1290 : :
1291 : 1 : nodes = get_nodes_at (c, "/foo");
1292 : 1 : g_assert_nonnull (nodes);
1293 : 1 : g_assert_cmpint (g_strv_length (nodes), ==, 2);
1294 : 1 : g_assert_cmpstr (nodes[0], ==, "boss");
1295 : 1 : g_assert_cmpstr (nodes[1], ==, "dyna");
1296 : 1 : g_strfreev (nodes);
1297 : 1 : g_assert_cmpint (count_interfaces (c, "/foo"), ==, 0);
1298 : :
1299 : 1 : nodes = get_nodes_at (c, "/foo/boss");
1300 : 1 : g_assert_nonnull (nodes);
1301 : 1 : g_assert_cmpint (g_strv_length (nodes), ==, 5);
1302 : 1 : g_assert_true (g_strv_contains ((const gchar* const *) nodes, "worker1"));
1303 : 1 : g_assert_true (g_strv_contains ((const gchar* const *) nodes, "worker1p1"));
1304 : 1 : g_assert_true (g_strv_contains ((const gchar* const *) nodes, "worker2"));
1305 : 1 : g_assert_true (g_strv_contains ((const gchar* const *) nodes, "interns"));
1306 : 1 : g_assert_true (g_strv_contains ((const gchar* const *) nodes, "executives"));
1307 : 1 : g_strfreev (nodes);
1308 : : /* any registered object always implement org.freedesktop.DBus.[Peer,Introspectable,Properties] */
1309 : 1 : g_assert_cmpint (count_interfaces (c, "/foo/boss"), ==, 5);
1310 : 1 : g_assert_true (has_interface (c, "/foo/boss", foo_interface_info.name));
1311 : 1 : g_assert_true (has_interface (c, "/foo/boss", bar_interface_info.name));
1312 : :
1313 : : /* check subtree nodes - we should have only non_subtree_object in /foo/boss/executives
1314 : : * because data.num_subtree_nodes is 0
1315 : : */
1316 : 1 : nodes = get_nodes_at (c, "/foo/boss/executives");
1317 : 1 : g_assert_nonnull (nodes);
1318 : 1 : g_assert_true (g_strv_contains ((const gchar* const *) nodes, "non_subtree_object"));
1319 : 1 : g_assert_cmpint (g_strv_length (nodes), ==, 1);
1320 : 1 : g_strfreev (nodes);
1321 : 1 : g_assert_cmpint (count_interfaces (c, "/foo/boss/executives"), ==, 0);
1322 : :
1323 : : /* now change data.num_subtree_nodes and check */
1324 : 1 : data.num_subtree_nodes = 2;
1325 : 1 : nodes = get_nodes_at (c, "/foo/boss/executives");
1326 : 1 : g_assert_nonnull (nodes);
1327 : 1 : g_assert_cmpint (g_strv_length (nodes), ==, 5);
1328 : 1 : g_assert_true (g_strv_contains ((const gchar* const *) nodes, "non_subtree_object"));
1329 : 1 : g_assert_true (g_strv_contains ((const gchar* const *) nodes, "vp0"));
1330 : 1 : g_assert_true (g_strv_contains ((const gchar* const *) nodes, "vp1"));
1331 : 1 : g_assert_true (g_strv_contains ((const gchar* const *) nodes, "evp0"));
1332 : 1 : g_assert_true (g_strv_contains ((const gchar* const *) nodes, "evp1"));
1333 : : /* check that /foo/boss/executives/non_subtree_object is not handled by the
1334 : : * subtree handlers - we can do this because objects from subtree handlers
1335 : : * has exactly one interface and non_subtree_object has two
1336 : : */
1337 : 1 : g_assert_cmpint (count_interfaces (c, "/foo/boss/executives/non_subtree_object"), ==, 5);
1338 : 1 : g_assert_true (has_interface (c, "/foo/boss/executives/non_subtree_object", foo_interface_info.name));
1339 : 1 : g_assert_true (has_interface (c, "/foo/boss/executives/non_subtree_object", bar_interface_info.name));
1340 : : /* check that the vp and evp objects are handled by the subtree handlers */
1341 : 1 : g_assert_cmpint (count_interfaces (c, "/foo/boss/executives/vp0"), ==, 4);
1342 : 1 : g_assert_cmpint (count_interfaces (c, "/foo/boss/executives/vp1"), ==, 4);
1343 : 1 : g_assert_cmpint (count_interfaces (c, "/foo/boss/executives/evp0"), ==, 4);
1344 : 1 : g_assert_cmpint (count_interfaces (c, "/foo/boss/executives/evp1"), ==, 4);
1345 : 1 : g_assert_true (has_interface (c, "/foo/boss/executives/vp0", foo_interface_info.name));
1346 : 1 : g_assert_true (has_interface (c, "/foo/boss/executives/vp1", foo_interface_info.name));
1347 : 1 : g_assert_true (has_interface (c, "/foo/boss/executives/evp0", bar_interface_info.name));
1348 : 1 : g_assert_true (has_interface (c, "/foo/boss/executives/evp1", bar_interface_info.name));
1349 : 1 : g_strfreev (nodes);
1350 : 1 : data.num_subtree_nodes = 3;
1351 : 1 : nodes = get_nodes_at (c, "/foo/boss/executives");
1352 : 1 : g_assert_nonnull (nodes);
1353 : 1 : g_assert_cmpint (g_strv_length (nodes), ==, 7);
1354 : 1 : g_assert_true (g_strv_contains ((const gchar* const *) nodes, "non_subtree_object"));
1355 : 1 : g_assert_true (g_strv_contains ((const gchar* const *) nodes, "vp0"));
1356 : 1 : g_assert_true (g_strv_contains ((const gchar* const *) nodes, "vp1"));
1357 : 1 : g_assert_true (g_strv_contains ((const gchar* const *) nodes, "vp2"));
1358 : 1 : g_assert_true (g_strv_contains ((const gchar* const *) nodes, "evp0"));
1359 : 1 : g_assert_true (g_strv_contains ((const gchar* const *) nodes, "evp1"));
1360 : 1 : g_assert_true (g_strv_contains ((const gchar* const *) nodes, "evp2"));
1361 : 1 : g_strfreev (nodes);
1362 : :
1363 : : /* This is to check that a bug (rather, class of bugs) in gdbusconnection.c's
1364 : : *
1365 : : * g_dbus_connection_list_registered_unlocked()
1366 : : *
1367 : : * where /foo/boss/worker1 reported a child '1', is now fixed.
1368 : : */
1369 : 1 : nodes = get_nodes_at (c, "/foo/boss/worker1");
1370 : 1 : g_assert_nonnull (nodes);
1371 : 1 : g_assert_cmpint (g_strv_length (nodes), ==, 0);
1372 : 1 : g_strfreev (nodes);
1373 : :
1374 : : /* check that calls are properly dispatched to the functions in foo_vtable for objects
1375 : : * implementing the org.example.Foo interface
1376 : : *
1377 : : * We do this for both a regular registered object (/foo/boss) and also for an object
1378 : : * registered through the subtree mechanism.
1379 : : */
1380 : 1 : test_dispatch ("/foo/boss", TRUE);
1381 : 1 : test_dispatch ("/foo/boss/executives/vp0", TRUE);
1382 : :
1383 : : /* To prevent from exiting and attaching a D-Bus tool like D-Feet; uncomment: */
1384 : : #if 0
1385 : : g_debug ("Point D-feet or other tool at: %s", g_test_dbus_get_temporary_address());
1386 : : g_main_loop_run (loop);
1387 : : #endif
1388 : :
1389 : : /* check that unregistering the subtree handler works */
1390 : 1 : g_assert_cmpint (data.num_unregistered_subtree_calls, ==, 2);
1391 : 1 : g_assert_true (g_dbus_connection_unregister_subtree (c, subtree_registration_id));
1392 : 1 : g_main_context_iteration (NULL, FALSE);
1393 : 1 : g_assert_cmpint (data.num_unregistered_subtree_calls, ==, 3);
1394 : 1 : nodes = get_nodes_at (c, "/foo/boss/executives");
1395 : 1 : g_assert_nonnull (nodes);
1396 : 1 : g_assert_cmpint (g_strv_length (nodes), ==, 1);
1397 : 1 : g_assert_true (g_strv_contains ((const gchar* const *) nodes, "non_subtree_object"));
1398 : 1 : g_strfreev (nodes);
1399 : :
1400 : 1 : g_assert_true (g_dbus_connection_unregister_object (c, boss_foo_reg_id));
1401 : 1 : g_assert_true (g_dbus_connection_unregister_object (c, boss_bar_reg_id));
1402 : 1 : g_assert_true (g_dbus_connection_unregister_object (c, worker1_foo_reg_id));
1403 : 1 : g_assert_true (g_dbus_connection_unregister_object (c, worker1p1_foo_reg_id));
1404 : 1 : g_assert_true (g_dbus_connection_unregister_object (c, worker2_bar_reg_id));
1405 : 1 : g_assert_true (g_dbus_connection_unregister_object (c, intern1_foo_reg_id));
1406 : 1 : g_assert_true (g_dbus_connection_unregister_object (c, intern2_bar_reg_id));
1407 : 1 : g_assert_true (g_dbus_connection_unregister_object (c, intern2_foo_reg_id));
1408 : 1 : g_assert_true (g_dbus_connection_unregister_object (c, intern3_bar_reg_id));
1409 : 1 : g_assert_true (g_dbus_connection_unregister_object (c, non_subtree_object_path_bar_reg_id));
1410 : 1 : g_assert_true (g_dbus_connection_unregister_object (c, non_subtree_object_path_foo_reg_id));
1411 : :
1412 : 1 : g_main_context_iteration (NULL, FALSE);
1413 : 1 : g_assert_cmpint (data.num_unregistered_calls, ==, num_successful_registrations + num_failed_registrations);
1414 : :
1415 : : /* check that we no longer export any objects - TODO: it looks like there's a bug in
1416 : : * libdbus-1 here: libdbus still reports the '/foo' object; so disable the test for now
1417 : : */
1418 : : #if 0
1419 : : nodes = get_nodes_at (c, "/");
1420 : : g_assert_nonnull (nodes);
1421 : : g_assert_cmpint (g_strv_length (nodes), ==, 0);
1422 : : g_strfreev (nodes);
1423 : : #endif
1424 : :
1425 : 1 : g_object_unref (c);
1426 : 1 : }
1427 : :
1428 : : static void
1429 : 1 : test_object_registration_with_closures (void)
1430 : : {
1431 : : GError *error;
1432 : : guint registration_id;
1433 : :
1434 : 1 : error = NULL;
1435 : 1 : c = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
1436 : 1 : g_assert_no_error (error);
1437 : 1 : g_assert_nonnull (c);
1438 : :
1439 : 1 : registration_id = g_dbus_connection_register_object_with_closures (c,
1440 : : "/foo/boss",
1441 : : (GDBusInterfaceInfo *) &foo_interface_info,
1442 : : g_cclosure_new (G_CALLBACK (foo_method_call), NULL, NULL),
1443 : : g_cclosure_new (G_CALLBACK (foo_get_property), NULL, NULL),
1444 : : g_cclosure_new (G_CALLBACK (foo_set_property), NULL, NULL),
1445 : : &error);
1446 : 1 : g_assert_no_error (error);
1447 : 1 : g_assert_cmpuint (registration_id, >, 0);
1448 : :
1449 : 1 : test_dispatch ("/foo/boss", FALSE);
1450 : :
1451 : 1 : g_assert_true (g_dbus_connection_unregister_object (c, registration_id));
1452 : :
1453 : 1 : g_object_unref (c);
1454 : 1 : }
1455 : :
1456 : : static const GDBusInterfaceInfo test_interface_info1 =
1457 : : {
1458 : : -1,
1459 : : "org.example.Foo",
1460 : : (GDBusMethodInfo **) NULL,
1461 : : (GDBusSignalInfo **) NULL,
1462 : : (GDBusPropertyInfo **) NULL,
1463 : : NULL,
1464 : : };
1465 : :
1466 : : static const GDBusInterfaceInfo test_interface_info2 =
1467 : : {
1468 : : -1,
1469 : : "org.freedesktop.DBus.Properties",
1470 : : (GDBusMethodInfo **) NULL,
1471 : : (GDBusSignalInfo **) NULL,
1472 : : (GDBusPropertyInfo **) NULL,
1473 : : NULL,
1474 : : };
1475 : :
1476 : : static void
1477 : 1 : check_interfaces (GDBusConnection *c,
1478 : : const gchar *object_path,
1479 : : const gchar **interfaces)
1480 : : {
1481 : : GError *error;
1482 : : GDBusProxy *proxy;
1483 : : gchar *xml_data;
1484 : : GDBusNodeInfo *node_info;
1485 : : gint i, j;
1486 : :
1487 : 1 : error = NULL;
1488 : 1 : proxy = g_dbus_proxy_new_sync (c,
1489 : : G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
1490 : : G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
1491 : : NULL,
1492 : : g_dbus_connection_get_unique_name (c),
1493 : : object_path,
1494 : : "org.freedesktop.DBus.Introspectable",
1495 : : NULL,
1496 : : &error);
1497 : 1 : g_assert_no_error (error);
1498 : 1 : g_assert_nonnull (proxy);
1499 : :
1500 : : /* do this async to avoid libdbus-1 deadlocks */
1501 : 1 : xml_data = NULL;
1502 : 1 : g_dbus_proxy_call (proxy,
1503 : : "Introspect",
1504 : : NULL,
1505 : : G_DBUS_CALL_FLAGS_NONE,
1506 : : -1,
1507 : : NULL,
1508 : : (GAsyncReadyCallback) introspect_callback,
1509 : : &xml_data);
1510 : 1 : g_main_loop_run (loop);
1511 : 1 : g_assert_nonnull (xml_data);
1512 : :
1513 : 1 : node_info = g_dbus_node_info_new_for_xml (xml_data, &error);
1514 : 1 : g_assert_no_error (error);
1515 : 1 : g_assert_nonnull (node_info);
1516 : :
1517 : 1 : g_assert_nonnull (node_info->interfaces);
1518 [ + + ]: 5 : for (i = 0; node_info->interfaces[i]; i++) ;
1519 : : #if 0
1520 : : if (g_strv_length ((gchar**)interfaces) != i - 1)
1521 : : {
1522 : : g_printerr ("expected ");
1523 : : for (i = 0; interfaces[i]; i++)
1524 : : g_printerr ("%s ", interfaces[i]);
1525 : : g_printerr ("\ngot ");
1526 : : for (i = 0; node_info->interfaces[i]; i++)
1527 : : g_printerr ("%s ", node_info->interfaces[i]->name);
1528 : : g_printerr ("\n");
1529 : : }
1530 : : #endif
1531 : 1 : g_assert_cmpint (g_strv_length ((gchar**)interfaces), ==, i - 1);
1532 : :
1533 [ + + ]: 4 : for (i = 0; interfaces[i]; i++)
1534 : : {
1535 [ + - ]: 8 : for (j = 0; node_info->interfaces[j]; j++)
1536 : : {
1537 [ + + ]: 8 : if (strcmp (interfaces[i], node_info->interfaces[j]->name) == 0)
1538 : 3 : goto found;
1539 : : }
1540 : :
1541 : : g_assert_not_reached ();
1542 : :
1543 : 3 : found: ;
1544 : : }
1545 : :
1546 : 1 : g_object_unref (proxy);
1547 : 1 : g_free (xml_data);
1548 : 1 : g_dbus_node_info_unref (node_info);
1549 : 1 : }
1550 : :
1551 : : static void
1552 : 1 : test_registered_interfaces (void)
1553 : : {
1554 : : GError *error;
1555 : : guint id1, id2;
1556 : 1 : const gchar *interfaces[] = {
1557 : : "org.example.Foo",
1558 : : "org.freedesktop.DBus.Properties",
1559 : : "org.freedesktop.DBus.Introspectable",
1560 : : NULL,
1561 : : };
1562 : :
1563 : 1 : error = NULL;
1564 : 1 : c = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
1565 : 1 : g_assert_no_error (error);
1566 : 1 : g_assert_nonnull (c);
1567 : :
1568 : 1 : id1 = g_dbus_connection_register_object (c,
1569 : : "/test",
1570 : : (GDBusInterfaceInfo *) &test_interface_info1,
1571 : : NULL,
1572 : : NULL,
1573 : : NULL,
1574 : : &error);
1575 : 1 : g_assert_no_error (error);
1576 : 1 : g_assert_cmpuint (id1, >, 0);
1577 : 1 : id2 = g_dbus_connection_register_object (c,
1578 : : "/test",
1579 : : (GDBusInterfaceInfo *) &test_interface_info2,
1580 : : NULL,
1581 : : NULL,
1582 : : NULL,
1583 : : &error);
1584 : 1 : g_assert_no_error (error);
1585 : 1 : g_assert_cmpuint (id2, >, 0);
1586 : :
1587 : 1 : check_interfaces (c, "/test", interfaces);
1588 : :
1589 : 1 : g_assert_true (g_dbus_connection_unregister_object (c, id1));
1590 : 1 : g_assert_true (g_dbus_connection_unregister_object (c, id2));
1591 : 1 : g_object_unref (c);
1592 : 1 : }
1593 : :
1594 : :
1595 : : /* ---------------------------------------------------------------------------------------------------- */
1596 : :
1597 : : static void
1598 : 5 : test_async_method_call (GDBusConnection *connection,
1599 : : const gchar *sender,
1600 : : const gchar *object_path,
1601 : : const gchar *interface_name,
1602 : : const gchar *method_name,
1603 : : GVariant *parameters,
1604 : : GDBusMethodInvocation *invocation,
1605 : : gpointer user_data)
1606 : : {
1607 : : const GDBusPropertyInfo *property;
1608 : :
1609 : : /* Strictly speaking, this function should also expect to receive
1610 : : * method calls not on the org.freedesktop.DBus.Properties interface,
1611 : : * but we don't do any during this testcase, so assert that.
1612 : : */
1613 : 5 : g_assert_cmpstr (interface_name, ==, "org.freedesktop.DBus.Properties");
1614 : 5 : g_assert_null (g_dbus_method_invocation_get_method_info (invocation));
1615 : :
1616 : 5 : property = g_dbus_method_invocation_get_property_info (invocation);
1617 : :
1618 : : /* We should never be seeing any property calls on the com.example.Bar
1619 : : * interface because it doesn't export any properties.
1620 : : *
1621 : : * In each case below make sure the interface is org.example.Foo.
1622 : : */
1623 : :
1624 : : /* Do a whole lot of asserts to make sure that invalid calls are still
1625 : : * getting properly rejected by GDBusConnection and that our
1626 : : * environment is as we expect it to be.
1627 : : */
1628 [ + + ]: 5 : if (g_str_equal (method_name, "Get"))
1629 : : {
1630 : : const gchar *iface_name, *prop_name;
1631 : :
1632 : 2 : g_variant_get (parameters, "(&s&s)", &iface_name, &prop_name);
1633 : 2 : g_assert_cmpstr (iface_name, ==, "org.example.Foo");
1634 : 2 : g_assert_nonnull (property);
1635 : 2 : g_assert_cmpstr (prop_name, ==, property->name);
1636 : 2 : g_assert_true (property->flags & G_DBUS_PROPERTY_INFO_FLAGS_READABLE);
1637 : 2 : g_dbus_method_invocation_return_value (invocation, g_variant_new ("(v)", g_variant_new_string (prop_name)));
1638 : : }
1639 : :
1640 [ + + ]: 3 : else if (g_str_equal (method_name, "Set"))
1641 : : {
1642 : : const gchar *iface_name, *prop_name;
1643 : : GVariant *value;
1644 : :
1645 : 2 : g_variant_get (parameters, "(&s&sv)", &iface_name, &prop_name, &value);
1646 : 2 : g_assert_cmpstr (iface_name, ==, "org.example.Foo");
1647 : 2 : g_assert_nonnull (property);
1648 : 2 : g_assert_cmpstr (prop_name, ==, property->name);
1649 : 2 : g_assert_true (property->flags & G_DBUS_PROPERTY_INFO_FLAGS_WRITABLE);
1650 : 2 : g_assert_true (g_variant_is_of_type (value, G_VARIANT_TYPE (property->signature)));
1651 : 2 : g_dbus_method_invocation_return_value (invocation, g_variant_new ("()"));
1652 : 2 : g_variant_unref (value);
1653 : : }
1654 : :
1655 [ + - ]: 1 : else if (g_str_equal (method_name, "GetAll"))
1656 : : {
1657 : : const gchar *iface_name;
1658 : :
1659 : 1 : g_variant_get (parameters, "(&s)", &iface_name);
1660 : 1 : g_assert_cmpstr (iface_name, ==, "org.example.Foo");
1661 : 1 : g_assert_null (property);
1662 : 1 : g_dbus_method_invocation_return_value (invocation,
1663 : : g_variant_new_parsed ("({ 'PropertyUno': < 'uno' >,"
1664 : : " 'NotWritable': < 'notwrite' > },)"));
1665 : : }
1666 : :
1667 : : else
1668 : : g_assert_not_reached ();
1669 : 5 : }
1670 : :
1671 : : static gint outstanding_cases;
1672 : :
1673 : : static void
1674 : 20 : ensure_result_cb (GObject *source,
1675 : : GAsyncResult *result,
1676 : : gpointer user_data)
1677 : : {
1678 : 20 : GDBusConnection *connection = G_DBUS_CONNECTION (source);
1679 : : GVariant *reply;
1680 : :
1681 : 20 : reply = g_dbus_connection_call_finish (connection, result, NULL);
1682 : :
1683 [ + + ]: 20 : if (user_data == NULL)
1684 : : {
1685 : : /* Expected an error */
1686 : 14 : g_assert_null (reply);
1687 : : }
1688 : : else
1689 : : {
1690 : : /* Expected a reply of a particular format. */
1691 : : gchar *str;
1692 : :
1693 : 6 : g_assert_nonnull (reply);
1694 : 6 : str = g_variant_print (reply, TRUE);
1695 : 6 : g_assert_cmpstr (str, ==, (const gchar *) user_data);
1696 : 6 : g_free (str);
1697 : :
1698 : 6 : g_variant_unref (reply);
1699 : : }
1700 : :
1701 : 20 : g_assert_cmpint (outstanding_cases, >, 0);
1702 : 20 : outstanding_cases--;
1703 : 20 : }
1704 : :
1705 : : static void
1706 : 20 : test_async_case (GDBusConnection *connection,
1707 : : const gchar *expected_reply,
1708 : : const gchar *method,
1709 : : const gchar *format_string,
1710 : : ...)
1711 : : {
1712 : : va_list ap;
1713 : :
1714 : 20 : va_start (ap, format_string);
1715 : :
1716 : 20 : g_dbus_connection_call (connection, g_dbus_connection_get_unique_name (connection), "/foo",
1717 : : "org.freedesktop.DBus.Properties", method, g_variant_new_va (format_string, NULL, &ap),
1718 : : NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, ensure_result_cb, (gpointer) expected_reply);
1719 : :
1720 : 20 : va_end (ap);
1721 : :
1722 : 20 : outstanding_cases++;
1723 : 20 : }
1724 : :
1725 : : static void
1726 : 1 : test_async_properties (void)
1727 : : {
1728 : 1 : GError *error = NULL;
1729 : : guint registration_id, registration_id2;
1730 : : static const GDBusInterfaceVTable vtable = {
1731 : : test_async_method_call, NULL, NULL, { 0 }
1732 : : };
1733 : :
1734 : 1 : c = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
1735 : 1 : g_assert_no_error (error);
1736 : 1 : g_assert_nonnull (c);
1737 : :
1738 : 1 : registration_id = g_dbus_connection_register_object (c,
1739 : : "/foo",
1740 : : (GDBusInterfaceInfo *) &foo_interface_info,
1741 : : &vtable, NULL, NULL, &error);
1742 : 1 : g_assert_no_error (error);
1743 : 1 : g_assert_cmpuint (registration_id, !=, 0);
1744 : 1 : registration_id2 = g_dbus_connection_register_object (c,
1745 : : "/foo",
1746 : : (GDBusInterfaceInfo *) &foo2_interface_info,
1747 : : &vtable, NULL, NULL, &error);
1748 : 1 : g_assert_no_error (error);
1749 : 1 : g_assert_cmpuint (registration_id, !=, 0);
1750 : :
1751 : 1 : test_async_case (c, NULL, "random", "()");
1752 : :
1753 : : /* Test a variety of error cases */
1754 : 1 : test_async_case (c, NULL, "Get", "(si)", "wrong signature", 5);
1755 : 1 : test_async_case (c, NULL, "Get", "(ss)", "org.example.WrongInterface", "zzz");
1756 : 1 : test_async_case (c, NULL, "Get", "(ss)", "org.example.Foo", "NoSuchProperty");
1757 : 1 : test_async_case (c, NULL, "Get", "(ss)", "org.example.Foo", "NotReadable");
1758 : :
1759 : 1 : test_async_case (c, NULL, "Set", "(si)", "wrong signature", 5);
1760 : 1 : test_async_case (c, NULL, "Set", "(ssv)", "org.example.WrongInterface", "zzz", g_variant_new_string (""));
1761 : 1 : test_async_case (c, NULL, "Set", "(ssv)", "org.example.Foo", "NoSuchProperty", g_variant_new_string (""));
1762 : 1 : test_async_case (c, NULL, "Set", "(ssv)", "org.example.Foo", "NotWritable", g_variant_new_string (""));
1763 : 1 : test_async_case (c, NULL, "Set", "(ssv)", "org.example.Foo", "PropertyUno", g_variant_new_object_path ("/wrong"));
1764 : :
1765 : 1 : test_async_case (c, NULL, "GetAll", "(si)", "wrong signature", 5);
1766 : 1 : test_async_case (c, NULL, "GetAll", "(s)", "org.example.WrongInterface");
1767 : :
1768 : : /* Make sure that we get no unexpected async property calls for com.example.Foo2 */
1769 : 1 : test_async_case (c, NULL, "Get", "(ss)", "org.example.Foo2", "zzz");
1770 : 1 : test_async_case (c, NULL, "Set", "(ssv)", "org.example.Foo2", "zzz", g_variant_new_string (""));
1771 : 1 : test_async_case (c, "(@a{sv} {},)", "GetAll", "(s)", "org.example.Foo2");
1772 : :
1773 : : /* Now do the proper things */
1774 : 1 : test_async_case (c, "(<'PropertyUno'>,)", "Get", "(ss)", "org.example.Foo", "PropertyUno");
1775 : 1 : test_async_case (c, "(<'NotWritable'>,)", "Get", "(ss)", "org.example.Foo", "NotWritable");
1776 : 1 : test_async_case (c, "()", "Set", "(ssv)", "org.example.Foo", "PropertyUno", g_variant_new_string (""));
1777 : 1 : test_async_case (c, "()", "Set", "(ssv)", "org.example.Foo", "NotReadable", g_variant_new_string (""));
1778 : 1 : test_async_case (c, "({'PropertyUno': <'uno'>, 'NotWritable': <'notwrite'>},)", "GetAll", "(s)", "org.example.Foo");
1779 : :
1780 [ + + ]: 18 : while (outstanding_cases)
1781 : 17 : g_main_context_iteration (NULL, TRUE);
1782 : :
1783 : 1 : g_dbus_connection_unregister_object (c, registration_id);
1784 : 1 : g_dbus_connection_unregister_object (c, registration_id2);
1785 : 1 : g_object_unref (c);
1786 : 1 : }
1787 : :
1788 : : typedef struct
1789 : : {
1790 : : GDBusConnection *connection; /* (owned) */
1791 : : guint registration_id;
1792 : : guint subtree_registration_id;
1793 : : } ThreadedUnregistrationData;
1794 : :
1795 : : static gpointer
1796 : 479 : unregister_thread_cb (gpointer user_data)
1797 : : {
1798 : 479 : ThreadedUnregistrationData *data = user_data;
1799 : :
1800 : : /* Sleeping here makes the race more likely to be hit, as it balances the
1801 : : * time taken to set up the thread and unregister, with the time taken to
1802 : : * make and handle the D-Bus call. This will likely change with future kernel
1803 : : * versions, but there isn’t a more deterministic synchronisation point that
1804 : : * I can think of to use instead. */
1805 : 479 : usleep (330);
1806 : :
1807 [ + + ]: 479 : if (data->registration_id > 0)
1808 : 206 : g_assert_true (g_dbus_connection_unregister_object (data->connection, data->registration_id));
1809 : :
1810 [ + + ]: 479 : if (data->subtree_registration_id > 0)
1811 : 273 : g_assert_true (g_dbus_connection_unregister_subtree (data->connection, data->subtree_registration_id));
1812 : :
1813 : 479 : return NULL;
1814 : : }
1815 : :
1816 : : static void
1817 : 479 : async_result_cb (GObject *source_object,
1818 : : GAsyncResult *result,
1819 : : gpointer user_data)
1820 : : {
1821 : 479 : GAsyncResult **result_out = user_data;
1822 : :
1823 : 479 : *result_out = g_object_ref (result);
1824 : 479 : g_main_context_wakeup (NULL);
1825 : 479 : }
1826 : :
1827 : : /* Returns %TRUE if this iteration resolved the race with the unregistration
1828 : : * first, %FALSE if the call handler was invoked first. */
1829 : : static gboolean
1830 : 479 : test_threaded_unregistration_iteration (gboolean subtree)
1831 : : {
1832 : 479 : ThreadedUnregistrationData data = { NULL, 0, 0 };
1833 : 479 : ObjectRegistrationData object_registration_data = { 0, 0, 2 };
1834 : 479 : GError *local_error = NULL;
1835 : 479 : GThread *unregister_thread = NULL;
1836 : : const gchar *object_path;
1837 : 479 : GVariant *value = NULL;
1838 : : const gchar *value_str;
1839 : 479 : GAsyncResult *call_result = NULL;
1840 : : gboolean unregistration_was_first;
1841 : :
1842 : 479 : data.connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &local_error);
1843 : 479 : g_assert_no_error (local_error);
1844 : 479 : g_assert_nonnull (data.connection);
1845 : :
1846 : : /* Register an object or a subtree */
1847 [ + + ]: 479 : if (!subtree)
1848 : : {
1849 : 206 : data.registration_id = g_dbus_connection_register_object (data.connection,
1850 : : "/foo/boss",
1851 : : (GDBusInterfaceInfo *) &foo_interface_info,
1852 : : &foo_vtable,
1853 : : &object_registration_data,
1854 : : on_object_unregistered,
1855 : : &local_error);
1856 : 206 : g_assert_no_error (local_error);
1857 : 206 : g_assert_cmpint (data.registration_id, >, 0);
1858 : :
1859 : 206 : object_path = "/foo/boss";
1860 : : }
1861 : : else
1862 : : {
1863 : 273 : data.subtree_registration_id = g_dbus_connection_register_subtree (data.connection,
1864 : : "/foo/boss/executives",
1865 : : &subtree_vtable,
1866 : : G_DBUS_SUBTREE_FLAGS_NONE,
1867 : : &object_registration_data,
1868 : : on_subtree_unregistered,
1869 : : &local_error);
1870 : 273 : g_assert_no_error (local_error);
1871 : 273 : g_assert_cmpint (data.subtree_registration_id, >, 0);
1872 : :
1873 : 273 : object_path = "/foo/boss/executives/vp0";
1874 : : }
1875 : :
1876 : : /* Allow the registrations to go through. */
1877 : 479 : g_main_context_iteration (NULL, FALSE);
1878 : :
1879 : : /* Spawn a thread to unregister the object/subtree. This will race with
1880 : : * the call we subsequently make. */
1881 : 479 : unregister_thread = g_thread_new ("unregister-object",
1882 : : unregister_thread_cb, &data);
1883 : :
1884 : : /* Call a method on the object (or an object in the subtree). The callback
1885 : : * will be invoked in this main context. */
1886 : 479 : g_dbus_connection_call (data.connection,
1887 : : g_dbus_connection_get_unique_name (data.connection),
1888 : : object_path,
1889 : : "org.example.Foo",
1890 : : "Method1",
1891 : : g_variant_new ("(s)", "winwinwin"),
1892 : : NULL,
1893 : : G_DBUS_CALL_FLAGS_NONE,
1894 : : -1,
1895 : : NULL,
1896 : : async_result_cb,
1897 : : &call_result);
1898 : :
1899 [ + + ]: 1749 : while (call_result == NULL)
1900 : 1270 : g_main_context_iteration (NULL, TRUE);
1901 : :
1902 : 479 : value = g_dbus_connection_call_finish (data.connection, call_result, &local_error);
1903 : :
1904 : : /* The result of the method could either be an error (that the object doesn’t
1905 : : * exist) or a valid result, depending on how the thread was scheduled
1906 : : * relative to the call. */
1907 : 479 : unregistration_was_first = (value == NULL);
1908 [ + + ]: 479 : if (value != NULL)
1909 : : {
1910 : 200 : g_assert_no_error (local_error);
1911 : 200 : g_assert_true (g_variant_is_of_type (value, G_VARIANT_TYPE ("(s)")));
1912 : 200 : g_variant_get (value, "(&s)", &value_str);
1913 : 200 : g_assert_cmpstr (value_str, ==, "You passed the string 'winwinwin'. Jolly good!");
1914 : : }
1915 : : else
1916 : : {
1917 : 279 : g_assert_null (value);
1918 : 279 : g_assert_error (local_error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD);
1919 : : }
1920 : :
1921 : 479 : g_clear_pointer (&value, g_variant_unref);
1922 : 479 : g_clear_error (&local_error);
1923 : :
1924 : : /* Tidy up. */
1925 : 479 : g_thread_join (g_steal_pointer (&unregister_thread));
1926 : :
1927 : 479 : g_clear_object (&call_result);
1928 : 479 : g_clear_object (&data.connection);
1929 : :
1930 : 479 : return unregistration_was_first;
1931 : : }
1932 : :
1933 : : static void
1934 : 2 : test_threaded_unregistration (gconstpointer test_data)
1935 : : {
1936 : 2 : gboolean subtree = GPOINTER_TO_INT (test_data);
1937 : : guint i;
1938 : 2 : guint n_iterations_unregistration_first = 0;
1939 : 2 : guint n_iterations_call_first = 0;
1940 : :
1941 : 2 : g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/2400");
1942 : 2 : g_test_summary ("Test that object/subtree unregistration from one thread "
1943 : : "doesn’t cause problems when racing with method callbacks "
1944 : : "in another thread for that object or subtree");
1945 : :
1946 : : /* Run iterations of the test until it’s likely we’ve hit the race. Limit the
1947 : : * number of iterations so the test doesn’t run forever if not. The choice of
1948 : : * 100 is arbitrary. */
1949 [ + - + + : 481 : for (i = 0; i < 1000 && (n_iterations_unregistration_first < 100 || n_iterations_call_first < 100); i++)
+ + ]
1950 : : {
1951 [ + + ]: 479 : if (test_threaded_unregistration_iteration (subtree))
1952 : 279 : n_iterations_unregistration_first++;
1953 : : else
1954 : 200 : n_iterations_call_first++;
1955 : : }
1956 : :
1957 : : /* If the condition below is met, we probably failed to reproduce the race.
1958 : : * Don’t fail the test, though, as we can’t always control whether we hit the
1959 : : * race, and spurious test failures are annoying. */
1960 [ + - - + ]: 2 : if (n_iterations_unregistration_first < 100 ||
1961 : : n_iterations_call_first < 100)
1962 : 0 : g_test_skip_printf ("Failed to reproduce race (%u iterations with unregistration first, %u with call first); skipping test",
1963 : : n_iterations_unregistration_first, n_iterations_call_first);
1964 : 2 : }
1965 : :
1966 : : /* ---------------------------------------------------------------------------------------------------- */
1967 : :
1968 : : int
1969 : 1 : main (int argc,
1970 : : char *argv[])
1971 : : {
1972 : : gint ret;
1973 : :
1974 : 1 : g_test_init (&argc, &argv, G_TEST_OPTION_ISOLATE_DIRS, NULL);
1975 : :
1976 : : /* all the tests rely on a shared main loop */
1977 : 1 : loop = g_main_loop_new (NULL, FALSE);
1978 : :
1979 : 1 : g_test_add_func ("/gdbus/object-registration", test_object_registration);
1980 : 1 : g_test_add_func ("/gdbus/object-registration-with-closures", test_object_registration_with_closures);
1981 : 1 : g_test_add_func ("/gdbus/registered-interfaces", test_registered_interfaces);
1982 : 1 : g_test_add_func ("/gdbus/async-properties", test_async_properties);
1983 : 1 : g_test_add_data_func ("/gdbus/threaded-unregistration/object", GINT_TO_POINTER (FALSE), test_threaded_unregistration);
1984 : 1 : g_test_add_data_func ("/gdbus/threaded-unregistration/subtree", GINT_TO_POINTER (TRUE), test_threaded_unregistration);
1985 : :
1986 : : /* TODO: check that we spit out correct introspection data */
1987 : : /* TODO: check that registering a whole subtree works */
1988 : :
1989 : 1 : ret = session_bus_run ();
1990 : :
1991 : 1 : g_main_loop_unref (loop);
1992 : :
1993 : 1 : return ret;
1994 : : }
|