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