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

source code of gtk/subprojects/glib/gio/tests/gdbus-export.c