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