1 /* GLib testing framework examples and tests
2 *
3 * Copyright (C) 2008-2010 Red Hat, Inc.
4 *
5 * SPDX-License-Identifier: LGPL-2.1-or-later
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General
18 * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
19 *
20 * Author: David Zeuthen <davidz@redhat.com>
21 */
22
23 #include <gio/gio.h>
24 #include <unistd.h>
25 #include <string.h>
26
27 #include "gdbus-tests.h"
28
29 /* all tests rely on a shared mainloop */
30 static GMainLoop *loop = NULL;
31
32 /* ---------------------------------------------------------------------------------------------------- */
33 /* Test that the method aspects of GDBusProxy works */
34 /* ---------------------------------------------------------------------------------------------------- */
35
36 static void
37 test_methods (GDBusProxy *proxy)
38 {
39 GVariant *result;
40 GError *error;
41 const gchar *str;
42 gchar *dbus_error_name;
43
44 /* check that we can invoke a method */
45 error = NULL;
46 result = g_dbus_proxy_call_sync (proxy,
47 "HelloWorld",
48 g_variant_new ("(s)", "Hey"),
49 G_DBUS_CALL_FLAGS_NONE,
50 -1,
51 NULL,
52 &error);
53 g_assert_no_error (error);
54 g_assert_nonnull (result);
55 g_assert_cmpstr (g_variant_get_type_string (result), ==, "(s)");
56 g_variant_get (result, "(&s)", &str);
57 g_assert_cmpstr (str, ==, "You greeted me with 'Hey'. Thanks!");
58 g_variant_unref (result);
59
60 /* Check that we can completely recover the returned error */
61 result = g_dbus_proxy_call_sync (proxy,
62 "HelloWorld",
63 g_variant_new ("(s)", "Yo"),
64 G_DBUS_CALL_FLAGS_NONE,
65 -1,
66 NULL,
67 &error);
68 g_assert_error (error, G_IO_ERROR, G_IO_ERROR_DBUS_ERROR);
69 g_assert_true (g_dbus_error_is_remote_error (error));
70 g_assert_true (g_dbus_error_is_remote_error (error));
71 g_assert_null (result);
72 dbus_error_name = g_dbus_error_get_remote_error (error);
73 g_assert_cmpstr (dbus_error_name, ==, "com.example.TestException");
74 g_free (dbus_error_name);
75 g_assert_true (g_dbus_error_strip_remote_error (error));
76 g_assert_cmpstr (error->message, ==, "Yo is not a proper greeting");
77 g_clear_error (&error);
78
79 /* Check that we get a timeout if the method handling is taking longer than
80 * timeout. We use such a long sleep because on slow machines, if the
81 * sleep isn't much longer than the timeout and we're doing a parallel
82 * build, there's no guarantee we'll be scheduled in the window between
83 * the timeout being hit and the sleep finishing. */
84 error = NULL;
85 result = g_dbus_proxy_call_sync (proxy,
86 "Sleep",
87 g_variant_new ("(i)", 10000 /* msec */),
88 G_DBUS_CALL_FLAGS_NONE,
89 100 /* msec */,
90 NULL,
91 &error);
92 g_assert_error (error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT);
93 g_assert_false (g_dbus_error_is_remote_error (error));
94 g_assert_null (result);
95 g_clear_error (&error);
96
97 /* Check that proxy-default timeouts work. */
98 g_assert_cmpint (g_dbus_proxy_get_default_timeout (proxy), ==, -1);
99
100 /* the default timeout is 25000 msec so this should work */
101 result = g_dbus_proxy_call_sync (proxy,
102 "Sleep",
103 g_variant_new ("(i)", 500 /* msec */),
104 G_DBUS_CALL_FLAGS_NONE,
105 -1, /* use proxy default (e.g. -1 -> e.g. 25000 msec) */
106 NULL,
107 &error);
108 g_assert_no_error (error);
109 g_assert_nonnull (result);
110 g_assert_cmpstr (g_variant_get_type_string (result), ==, "()");
111 g_variant_unref (result);
112
113 /* Now set the proxy-default timeout to 250 msec and try the 10000 msec
114 * call - this should FAIL. Again, we use such a long sleep because on slow
115 * machines there's no guarantee we'll be scheduled when we want to be. */
116 g_dbus_proxy_set_default_timeout (proxy, 250);
117 g_assert_cmpint (g_dbus_proxy_get_default_timeout (proxy), ==, 250);
118 result = g_dbus_proxy_call_sync (proxy,
119 "Sleep",
120 g_variant_new ("(i)", 10000 /* msec */),
121 G_DBUS_CALL_FLAGS_NONE,
122 -1, /* use proxy default (e.g. 250 msec) */
123 NULL,
124 &error);
125 g_assert_error (error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT);
126 g_assert_false (g_dbus_error_is_remote_error (error));
127 g_assert_null (result);
128 g_clear_error (&error);
129
130 /* clean up after ourselves */
131 g_dbus_proxy_set_default_timeout (proxy, -1);
132 }
133
134 static gboolean
135 strv_equal (gchar **strv, ...)
136 {
137 gsize count;
138 va_list list;
139 const gchar *str;
140 gboolean res;
141
142 res = TRUE;
143 count = 0;
144 va_start (list, strv);
145 while (1)
146 {
147 str = va_arg (list, const gchar *);
148 if (str == NULL)
149 break;
150 if (g_strcmp0 (str, strv[count]) != 0)
151 {
152 res = FALSE;
153 break;
154 }
155 count++;
156 }
157 va_end (list);
158
159 if (res)
160 res = g_strv_length (strv) == count;
161
162 return res;
163 }
164
165 /* ---------------------------------------------------------------------------------------------------- */
166 /* Test that the property aspects of GDBusProxy works */
167 /* ---------------------------------------------------------------------------------------------------- */
168
169 static void
170 test_properties (GDBusProxy *proxy)
171 {
172 GError *error;
173 GVariant *variant;
174 GVariant *variant2;
175 GVariant *result;
176 gchar **names;
177 gchar *name_owner;
178 GDBusProxy *proxy2;
179
180 error = NULL;
181
182 if (g_dbus_proxy_get_flags (proxy) & G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES)
183 {
184 g_assert_null (g_dbus_proxy_get_cached_property_names (proxy));
185 return;
186 }
187
188 /*
189 * Check that we can list all cached properties.
190 */
191 names = g_dbus_proxy_get_cached_property_names (proxy);
192
193 g_assert_true (strv_equal (names,
194 "PropertyThatWillBeInvalidated",
195 "ab",
196 "ad",
197 "ai",
198 "an",
199 "ao",
200 "aq",
201 "as",
202 "at",
203 "au",
204 "ax",
205 "ay",
206 "b",
207 "d",
208 "foo",
209 "i",
210 "n",
211 "o",
212 "q",
213 "s",
214 "t",
215 "u",
216 "x",
217 "y",
218 NULL));
219
220 g_strfreev (names);
221
222 /*
223 * Check that we can read cached properties.
224 *
225 * No need to test all properties - GVariant has already been tested
226 */
227 variant = g_dbus_proxy_get_cached_property (proxy, "y");
228 g_assert_nonnull (variant);
229 g_assert_cmpint (g_variant_get_byte (variant), ==, 1);
230 g_variant_unref (variant);
231 variant = g_dbus_proxy_get_cached_property (proxy, "o");
232 g_assert_nonnull (variant);
233 g_assert_cmpstr (g_variant_get_string (variant, NULL), ==, "/some/path");
234 g_variant_unref (variant);
235
236 /*
237 * Now ask the service to change a property and check that #GDBusProxy::g-property-changed
238 * is received. Also check that the cache is updated.
239 */
240 variant2 = g_variant_new_byte (42);
241 result = g_dbus_proxy_call_sync (proxy,
242 "FrobSetProperty",
243 g_variant_new ("(sv)",
244 "y",
245 variant2),
246 G_DBUS_CALL_FLAGS_NONE,
247 -1,
248 NULL,
249 &error);
250 g_assert_no_error (error);
251 g_assert_nonnull (result);
252 g_assert_cmpstr (g_variant_get_type_string (result), ==, "()");
253 g_variant_unref (result);
254 _g_assert_signal_received (proxy, "g-properties-changed");
255 variant = g_dbus_proxy_get_cached_property (proxy, "y");
256 g_assert_nonnull (variant);
257 g_assert_cmpint (g_variant_get_byte (variant), ==, 42);
258 g_variant_unref (variant);
259
260 g_dbus_proxy_set_cached_property (proxy, "y", g_variant_new_byte (142));
261 variant = g_dbus_proxy_get_cached_property (proxy, "y");
262 g_assert_nonnull (variant);
263 g_assert_cmpint (g_variant_get_byte (variant), ==, 142);
264 g_variant_unref (variant);
265
266 g_dbus_proxy_set_cached_property (proxy, "y", NULL);
267 variant = g_dbus_proxy_get_cached_property (proxy, "y");
268 g_assert_null (variant);
269
270 /* Check that the invalidation feature of the PropertiesChanged()
271 * signal works... First, check that we have a cached value of the
272 * property (from the initial GetAll() call)
273 */
274 variant = g_dbus_proxy_get_cached_property (proxy, "PropertyThatWillBeInvalidated");
275 g_assert_nonnull (variant);
276 g_assert_cmpstr (g_variant_get_string (variant, NULL), ==, "InitialValue");
277 g_variant_unref (variant);
278 /* now ask to invalidate the property - this causes a
279 *
280 * PropertiesChanaged("com.example.Frob",
281 * {},
282 * ["PropertyThatWillBeInvalidated")
283 *
284 * signal to be emitted. This is received before the method reply
285 * for FrobInvalidateProperty *but* since the proxy was created in
286 * the same thread as we're doing this synchronous call, we'll get
287 * the method reply before...
288 */
289 result = g_dbus_proxy_call_sync (proxy,
290 "FrobInvalidateProperty",
291 g_variant_new ("(s)", "OMGInvalidated"),
292 G_DBUS_CALL_FLAGS_NONE,
293 -1,
294 NULL,
295 &error);
296 g_assert_no_error (error);
297 g_assert_nonnull (result);
298 g_assert_cmpstr (g_variant_get_type_string (result), ==, "()");
299 g_variant_unref (result);
300 /* ... hence we wait for the g-properties-changed signal to be delivered */
301 _g_assert_signal_received (proxy, "g-properties-changed");
302 /* ... and now we finally, check that the cached value has been invalidated */
303 variant = g_dbus_proxy_get_cached_property (proxy, "PropertyThatWillBeInvalidated");
304 g_assert_null (variant);
305
306 /* Now test that G_DBUS_PROXY_FLAGS_GET_INVALIDATED_PROPERTIES works - we need a new proxy for that */
307 error = NULL;
308 proxy2 = g_dbus_proxy_new_sync (g_dbus_proxy_get_connection (proxy),
309 G_DBUS_PROXY_FLAGS_GET_INVALIDATED_PROPERTIES,
310 NULL, /* GDBusInterfaceInfo */
311 "com.example.TestService", /* name */
312 "/com/example/TestObject", /* object path */
313 "com.example.Frob", /* interface */
314 NULL, /* GCancellable */
315 &error);
316 g_assert_no_error (error);
317
318 name_owner = g_dbus_proxy_get_name_owner (proxy2);
319 g_assert_nonnull (name_owner);
320 g_free (name_owner);
321
322 variant = g_dbus_proxy_get_cached_property (proxy2, "PropertyThatWillBeInvalidated");
323 g_assert_nonnull (variant);
324 g_assert_cmpstr (g_variant_get_string (variant, NULL), ==, "OMGInvalidated"); /* from previous test */
325 g_variant_unref (variant);
326
327 result = g_dbus_proxy_call_sync (proxy2,
328 "FrobInvalidateProperty",
329 g_variant_new ("(s)", "OMGInvalidated2"),
330 G_DBUS_CALL_FLAGS_NONE,
331 -1,
332 NULL,
333 &error);
334 g_assert_no_error (error);
335 g_assert_nonnull (result);
336 g_assert_cmpstr (g_variant_get_type_string (result), ==, "()");
337 g_variant_unref (result);
338
339 /* this time we should get the ::g-properties-changed _with_ the value */
340 _g_assert_signal_received (proxy2, "g-properties-changed");
341
342 variant = g_dbus_proxy_get_cached_property (proxy2, "PropertyThatWillBeInvalidated");
343 g_assert_nonnull (variant);
344 g_assert_cmpstr (g_variant_get_string (variant, NULL), ==, "OMGInvalidated2");
345 g_variant_unref (variant);
346
347 g_object_unref (proxy2);
348 }
349
350 /* ---------------------------------------------------------------------------------------------------- */
351 /* Test that the signal aspects of GDBusProxy works */
352 /* ---------------------------------------------------------------------------------------------------- */
353
354 static void
355 test_proxy_signals_on_signal (GDBusProxy *proxy,
356 const gchar *sender_name,
357 const gchar *signal_name,
358 GVariant *parameters,
359 gpointer user_data)
360 {
361 GString *s = user_data;
362
363 g_assert_cmpstr (signal_name, ==, "TestSignal");
364 g_assert_cmpstr (g_variant_get_type_string (parameters), ==, "(sov)");
365
366 g_variant_print_string (parameters, s, TRUE);
367 }
368
369 typedef struct
370 {
371 GMainLoop *internal_loop;
372 GString *s;
373 } TestSignalData;
374
375 static void
376 test_proxy_signals_on_emit_signal_cb (GDBusProxy *proxy,
377 GAsyncResult *res,
378 gpointer user_data)
379 {
380 TestSignalData *data = user_data;
381 GError *error;
382 GVariant *result;
383
384 error = NULL;
385 result = g_dbus_proxy_call_finish (proxy,
386 res,
387 &error);
388 g_assert_no_error (error);
389 g_assert_nonnull (result);
390 g_assert_cmpstr (g_variant_get_type_string (result), ==, "()");
391 g_variant_unref (result);
392
393 /* check that the signal was received before we got the method result */
394 g_assert_cmpuint (strlen (data->s->str), >, 0);
395
396 /* break out of the loop */
397 g_main_loop_quit (data->internal_loop);
398 }
399
400 static void
401 test_signals (GDBusProxy *proxy)
402 {
403 GError *error;
404 GString *s;
405 gulong signal_handler_id;
406 TestSignalData data;
407 GVariant *result;
408
409 error = NULL;
410
411 /*
412 * Ask the service to emit a signal and check that we receive it.
413 *
414 * Note that blocking calls don't block in the mainloop so wait for the signal (which
415 * is dispatched before the method reply)
416 */
417 s = g_string_new (NULL);
418 signal_handler_id = g_signal_connect (proxy,
419 "g-signal",
420 G_CALLBACK (test_proxy_signals_on_signal),
421 s);
422
423 result = g_dbus_proxy_call_sync (proxy,
424 "EmitSignal",
425 g_variant_new ("(so)",
426 "Accept the next proposition you hear",
427 "/some/path"),
428 G_DBUS_CALL_FLAGS_NONE,
429 -1,
430 NULL,
431 &error);
432 g_assert_no_error (error);
433 g_assert_nonnull (result);
434 g_assert_cmpstr (g_variant_get_type_string (result), ==, "()");
435 g_variant_unref (result);
436 /* check that we haven't received the signal just yet */
437 g_assert_cmpuint (strlen (s->str), ==, 0);
438 /* and now wait for the signal */
439 _g_assert_signal_received (proxy, "g-signal");
440 g_assert_cmpstr (s->str,
441 ==,
442 "('Accept the next proposition you hear .. in bed!', objectpath '/some/path/in/bed', <'a variant'>)");
443 g_signal_handler_disconnect (proxy, signal_handler_id);
444 g_string_free (s, TRUE);
445
446 /*
447 * Now do this async to check the signal is received before the method returns.
448 */
449 s = g_string_new (NULL);
450 data.internal_loop = g_main_loop_new (NULL, FALSE);
451 data.s = s;
452 signal_handler_id = g_signal_connect (proxy,
453 "g-signal",
454 G_CALLBACK (test_proxy_signals_on_signal),
455 s);
456 g_dbus_proxy_call (proxy,
457 "EmitSignal",
458 g_variant_new ("(so)",
459 "You will make a great programmer",
460 "/some/other/path"),
461 G_DBUS_CALL_FLAGS_NONE,
462 -1,
463 NULL,
464 (GAsyncReadyCallback) test_proxy_signals_on_emit_signal_cb,
465 &data);
466 g_main_loop_run (data.internal_loop);
467 g_main_loop_unref (data.internal_loop);
468 g_assert_cmpstr (s->str,
469 ==,
470 "('You will make a great programmer .. in bed!', objectpath '/some/other/path/in/bed', <'a variant'>)");
471 g_signal_handler_disconnect (proxy, signal_handler_id);
472 g_string_free (s, TRUE);
473 }
474
475 /* ---------------------------------------------------------------------------------------------------- */
476
477 static void
478 test_bogus_method_return (GDBusProxy *proxy)
479 {
480 GError *error = NULL;
481 GVariant *result;
482
483 result = g_dbus_proxy_call_sync (proxy,
484 "PairReturn",
485 NULL,
486 G_DBUS_CALL_FLAGS_NONE,
487 -1,
488 NULL,
489 &error);
490 g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT);
491 g_error_free (error);
492 g_assert_null (result);
493 }
494
495 #if 0 /* Disabled: see https://bugzilla.gnome.org/show_bug.cgi?id=658999 */
496 static void
497 test_bogus_signal (GDBusProxy *proxy)
498 {
499 GError *error = NULL;
500 GVariant *result;
501 GDBusInterfaceInfo *old_iface_info;
502
503 result = g_dbus_proxy_call_sync (proxy,
504 "EmitSignal2",
505 NULL,
506 G_DBUS_CALL_FLAGS_NONE,
507 -1,
508 NULL,
509 &error);
510 g_assert_no_error (error);
511 g_assert_nonnull (result);
512 g_assert_cmpstr (g_variant_get_type_string (result), ==, "()");
513 g_variant_unref (result);
514
515 if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDOUT | G_TEST_TRAP_SILENCE_STDERR))
516 {
517 /* and now wait for the signal that will never arrive */
518 _g_assert_signal_received (proxy, "g-signal");
519 }
520 g_test_trap_assert_stderr ("*Dropping signal TestSignal2 of type (i) since the type from the expected interface is (u)*");
521 g_test_trap_assert_failed();
522
523 /* Our main branch will also do g_warning() when running the mainloop so
524 * temporarily remove the expected interface
525 */
526 old_iface_info = g_dbus_proxy_get_interface_info (proxy);
527 g_dbus_proxy_set_interface_info (proxy, NULL);
528 _g_assert_signal_received (proxy, "g-signal");
529 g_dbus_proxy_set_interface_info (proxy, old_iface_info);
530 }
531
532 static void
533 test_bogus_property (GDBusProxy *proxy)
534 {
535 GError *error = NULL;
536 GVariant *result;
537 GDBusInterfaceInfo *old_iface_info;
538
539 /* Make the service emit a PropertiesChanged signal for property 'i' of type 'i' - since
540 * our introspection data has this as type 'u' we should get a warning on stderr.
541 */
542 result = g_dbus_proxy_call_sync (proxy,
543 "FrobSetProperty",
544 g_variant_new ("(sv)",
545 "i", g_variant_new_int32 (42)),
546 G_DBUS_CALL_FLAGS_NONE,
547 -1,
548 NULL,
549 &error);
550 g_assert_no_error (error);
551 g_assert_nonnull (result);
552 g_assert_cmpstr (g_variant_get_type_string (result), ==, "()");
553 g_variant_unref (result);
554
555 if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDOUT | G_TEST_TRAP_SILENCE_STDERR))
556 {
557 /* and now wait for the signal that will never arrive */
558 _g_assert_signal_received (proxy, "g-properties-changed");
559 }
560 g_test_trap_assert_stderr ("*Received property i with type i does not match expected type u in the expected interface*");
561 g_test_trap_assert_failed();
562
563 /* Our main branch will also do g_warning() when running the mainloop so
564 * temporarily remove the expected interface
565 */
566 old_iface_info = g_dbus_proxy_get_interface_info (proxy);
567 g_dbus_proxy_set_interface_info (proxy, NULL);
568 _g_assert_signal_received (proxy, "g-properties-changed");
569 g_dbus_proxy_set_interface_info (proxy, old_iface_info);
570 }
571 #endif /* Disabled: see https://bugzilla.gnome.org/show_bug.cgi?id=658999 */
572
573 /* ---------------------------------------------------------------------------------------------------- */
574
575 static const gchar *frob_dbus_interface_xml =
576 "<node>"
577 " <interface name='com.example.Frob'>"
578 /* PairReturn() is deliberately different from gdbus-testserver's definition */
579 " <method name='PairReturn'>"
580 " <arg type='u' name='somenumber' direction='in'/>"
581 " <arg type='s' name='somestring' direction='out'/>"
582 " </method>"
583 " <method name='HelloWorld'>"
584 " <arg type='s' name='somestring' direction='in'/>"
585 " <arg type='s' name='somestring' direction='out'/>"
586 " </method>"
587 " <method name='Sleep'>"
588 " <arg type='i' name='timeout' direction='in'/>"
589 " </method>"
590 /* We deliberately only mention a single property here */
591 " <property name='y' type='y' access='readwrite'/>"
592 /* The 'i' property is deliberately different from gdbus-testserver's definition */
593 " <property name='i' type='u' access='readwrite'/>"
594 /* ::TestSignal2 is deliberately different from gdbus-testserver's definition */
595 " <signal name='TestSignal2'>"
596 " <arg type='u' name='somenumber'/>"
597 " </signal>"
598 " </interface>"
599 "</node>";
600 static GDBusInterfaceInfo *frob_dbus_interface_info;
601
602 static void
603 test_expected_interface (GDBusProxy *proxy)
604 {
605 GVariant *value;
606 GError *error;
607
608 /* This is obviously wrong but expected interface is not set so we don't fail... */
609 g_dbus_proxy_set_cached_property (proxy, "y", g_variant_new_string ("error_me_out!"));
610 g_dbus_proxy_set_cached_property (proxy, "y", g_variant_new_byte (42));
611 g_dbus_proxy_set_cached_property (proxy, "does-not-exist", g_variant_new_string ("something"));
612 g_dbus_proxy_set_cached_property (proxy, "does-not-exist", NULL);
613
614 /* Now repeat the method tests, with an expected interface set */
615 g_dbus_proxy_set_interface_info (proxy, frob_dbus_interface_info);
616 test_methods (proxy);
617 test_signals (proxy);
618
619 /* And also where we deliberately set the expected interface definition incorrectly */
620 test_bogus_method_return (proxy);
621 /* Disabled: see https://bugzilla.gnome.org/show_bug.cgi?id=658999
622 test_bogus_signal (proxy);
623 test_bogus_property (proxy);
624 */
625
626 if (g_test_undefined ())
627 {
628 /* Also check that we complain if setting a cached property of the wrong type */
629 g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING,
630 "*Trying to set property y of type s but according to the expected interface the type is y*");
631 value = g_variant_ref_sink (g_variant_new_string ("error_me_out!"));
632 g_dbus_proxy_set_cached_property (proxy, "y", value);
633 g_variant_unref (value);
634 g_test_assert_expected_messages ();
635 }
636
637 /* this should work, however (since the type is correct) */
638 g_dbus_proxy_set_cached_property (proxy, "y", g_variant_new_byte (42));
639
640 if (g_test_undefined ())
641 {
642 /* Try to get the value of a property where the type we expect is different from
643 * what we have in our cache (e.g. what the service returned)
644 */
645 g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING,
646 "*Trying to get property i with type i but according to the expected interface the type is u*");
647 value = g_dbus_proxy_get_cached_property (proxy, "i");
648 g_test_assert_expected_messages ();
649 }
650
651 /* Even if a property does not exist in expected_interface, looking it
652 * up, or setting it, should never fail. Because it could be that the
653 * property has been added to the service but the GDBusInterfaceInfo*
654 * passed to g_dbus_proxy_set_interface_info() just haven't been updated.
655 *
656 * See https://bugzilla.gnome.org/show_bug.cgi?id=660886
657 */
658 value = g_dbus_proxy_get_cached_property (proxy, "d");
659 g_assert_nonnull (value);
660 g_assert_true (g_variant_is_of_type (value, G_VARIANT_TYPE_DOUBLE));
661 g_assert_cmpfloat (g_variant_get_double (value), ==, 7.5);
662 g_variant_unref (value);
663 /* update it via the cached property... */
664 g_dbus_proxy_set_cached_property (proxy, "d", g_variant_new_double (75.0));
665 /* ... and finally check that it has changed */
666 value = g_dbus_proxy_get_cached_property (proxy, "d");
667 g_assert_nonnull (value);
668 g_assert_true (g_variant_is_of_type (value, G_VARIANT_TYPE_DOUBLE));
669 g_assert_cmpfloat (g_variant_get_double (value), ==, 75.0);
670 g_variant_unref (value);
671 /* now update it via the D-Bus interface... */
672 error = NULL;
673 value = g_dbus_proxy_call_sync (proxy, "FrobSetProperty",
674 g_variant_new ("(sv)", "d", g_variant_new_double (85.0)),
675 G_DBUS_CALL_FLAGS_NONE,
676 -1, NULL, &error);
677 g_assert_no_error (error);
678 g_assert_nonnull (value);
679 g_assert_cmpstr (g_variant_get_type_string (value), ==, "()");
680 g_variant_unref (value);
681 /* ...ensure we receive the ::PropertiesChanged signal... */
682 _g_assert_signal_received (proxy, "g-properties-changed");
683 /* ... and finally check that it has changed */
684 value = g_dbus_proxy_get_cached_property (proxy, "d");
685 g_assert_nonnull (value);
686 g_assert_true (g_variant_is_of_type (value, G_VARIANT_TYPE_DOUBLE));
687 g_assert_cmpfloat (g_variant_get_double (value), ==, 85.0);
688 g_variant_unref (value);
689 }
690
691 static void
692 test_basic (GDBusProxy *proxy)
693 {
694 GDBusConnection *connection;
695 GDBusConnection *conn;
696 GDBusProxyFlags flags;
697 GDBusInterfaceInfo *info;
698 gchar *name;
699 gchar *path;
700 gchar *interface;
701 gint timeout;
702
703 connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
704
705 g_assert_true (g_dbus_proxy_get_connection (proxy) == connection);
706 g_assert_null (g_dbus_proxy_get_interface_info (proxy));
707 g_assert_cmpstr (g_dbus_proxy_get_name (proxy), ==, "com.example.TestService");
708 g_assert_cmpstr (g_dbus_proxy_get_object_path (proxy), ==, "/com/example/TestObject");
709 g_assert_cmpstr (g_dbus_proxy_get_interface_name (proxy), ==, "com.example.Frob");
710 g_assert_cmpint (g_dbus_proxy_get_default_timeout (proxy), ==, -1);
711
712 g_object_get (proxy,
713 "g-connection", &conn,
714 "g-interface-info", &info,
715 "g-flags", &flags,
716 "g-name", &name,
717 "g-object-path", &path,
718 "g-interface-name", &interface,
719 "g-default-timeout", &timeout,
720 NULL);
721
722 g_assert_true (conn == connection);
723 g_assert_null (info);
724 g_assert_cmpint (flags, ==, g_dbus_proxy_get_flags (proxy));
725 g_assert_cmpstr (name, ==, "com.example.TestService");
726 g_assert_cmpstr (path, ==, "/com/example/TestObject");
727 g_assert_cmpstr (interface, ==, "com.example.Frob");
728 g_assert_cmpint (timeout, ==, -1);
729
730 g_object_unref (conn);
731 g_free (name);
732 g_free (path);
733 g_free (interface);
734
735 g_object_unref (connection);
736 }
737
738 static void
739 name_disappeared_cb (GDBusConnection *connection,
740 const gchar *name,
741 gpointer user_data)
742 {
743 gboolean *name_disappeared = user_data;
744 *name_disappeared = TRUE;
745 g_main_context_wakeup (NULL);
746 }
747
748 static void
749 kill_test_service (GDBusConnection *connection)
750 {
751 #ifdef G_OS_UNIX
752 guint pid;
753 GVariant *ret;
754 GError *error = NULL;
755 const gchar *name = "com.example.TestService";
756 guint watch_id;
757 gboolean name_disappeared = FALSE;
758
759 ret = g_dbus_connection_call_sync (connection,
760 "org.freedesktop.DBus",
761 "/org/freedesktop/DBus",
762 "org.freedesktop.DBus",
763 "GetConnectionUnixProcessID",
764 g_variant_new ("(s)", name),
765 NULL,
766 G_DBUS_CALL_FLAGS_NONE,
767 -1,
768 NULL,
769 &error);
770 g_variant_get (ret, "(u)", &pid);
771 g_variant_unref (ret);
772
773 /* Watch the name and wait until it’s disappeared. */
774 watch_id = g_bus_watch_name_on_connection (connection, name,
775 G_BUS_NAME_WATCHER_FLAGS_NONE,
776 NULL, name_disappeared_cb,
777 &name_disappeared, NULL);
778 kill (pid, SIGTERM);
779
780 while (!name_disappeared)
781 g_main_context_iteration (NULL, TRUE);
782
783 g_bus_unwatch_name (watch_id);
784 #else
785 g_warning ("Can't kill com.example.TestService");
786 #endif
787 }
788
789 static void
790 test_proxy_with_flags (GDBusProxyFlags flags)
791 {
792 GDBusProxy *proxy;
793 GDBusConnection *connection;
794 GError *error;
795 gchar *owner;
796
797 error = NULL;
798 connection = g_bus_get_sync (G_BUS_TYPE_SESSION,
799 NULL,
800 &error);
801 g_assert_no_error (error);
802 error = NULL;
803 proxy = g_dbus_proxy_new_sync (connection,
804 flags,
805 NULL, /* GDBusInterfaceInfo */
806 "com.example.TestService", /* name */
807 "/com/example/TestObject", /* object path */
808 "com.example.Frob", /* interface */
809 NULL, /* GCancellable */
810 &error);
811 g_assert_no_error (error);
812
813 /* this is safe; we explicitly kill the service later on */
814 g_assert_true (g_spawn_command_line_async (g_test_get_filename (G_TEST_BUILT, "gdbus-testserver", NULL), NULL));
815
816 _g_assert_property_notify (proxy, "g-name-owner");
817
818 test_basic (proxy);
819 test_methods (proxy);
820 test_properties (proxy);
821 test_signals (proxy);
822 test_expected_interface (proxy);
823
824 kill_test_service (connection);
825
826 owner = g_dbus_proxy_get_name_owner (proxy);
827 g_assert_null (owner);
828 g_free (owner);
829
830 g_object_unref (proxy);
831 g_object_unref (connection);
832 }
833
834 static void
835 test_proxy (void)
836 {
837 test_proxy_with_flags (G_DBUS_PROXY_FLAGS_NONE);
838 }
839
840 /* ---------------------------------------------------------------------------------------------------- */
841
842 static void
843 proxy_ready (GObject *source,
844 GAsyncResult *result,
845 gpointer user_data)
846 {
847 GDBusProxy *proxy;
848 GError *error;
849 gchar *owner;
850
851 error = NULL;
852 proxy = g_dbus_proxy_new_for_bus_finish (result, &error);
853 g_assert_no_error (error);
854
855 owner = g_dbus_proxy_get_name_owner (proxy);
856 g_assert_null (owner);
857 g_free (owner);
858
859 /* this is safe; we explicitly kill the service later on */
860 g_assert_true (g_spawn_command_line_async (g_test_get_filename (G_TEST_BUILT, "gdbus-testserver", NULL), NULL));
861
862 _g_assert_property_notify (proxy, "g-name-owner");
863
864 test_basic (proxy);
865 test_methods (proxy);
866 test_properties (proxy);
867 test_signals (proxy);
868 test_expected_interface (proxy);
869
870 kill_test_service (g_dbus_proxy_get_connection (proxy));
871 g_object_unref (proxy);
872 g_main_loop_quit (loop);
873 }
874
875 static gboolean
876 fail_test (gpointer user_data)
877 {
878 g_assert_not_reached ();
879 }
880
881 static void
882 test_async (void)
883 {
884 guint id;
885
886 g_dbus_proxy_new_for_bus (G_BUS_TYPE_SESSION,
887 G_DBUS_PROXY_FLAGS_NONE,
888 NULL, /* GDBusInterfaceInfo */
889 "com.example.TestService", /* name */
890 "/com/example/TestObject", /* object path */
891 "com.example.Frob", /* interface */
892 NULL, /* GCancellable */
893 proxy_ready,
894 NULL);
895
896 id = g_timeout_add (10000, fail_test, NULL);
897 g_main_loop_run (loop);
898
899 g_source_remove (id);
900 }
901
902 static void
903 test_no_properties (void)
904 {
905 GDBusProxy *proxy;
906 GError *error;
907
908 error = NULL;
909 proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
910 G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
911 NULL, /* GDBusInterfaceInfo */
912 "com.example.TestService", /* name */
913 "/com/example/TestObject", /* object path */
914 "com.example.Frob", /* interface */
915 NULL, /* GCancellable */
916 &error);
917 g_assert_no_error (error);
918
919 test_properties (proxy);
920
921 g_object_unref (proxy);
922 }
923
924 static void
925 check_error (GObject *source,
926 GAsyncResult *result,
927 gpointer user_data)
928 {
929 GError *error = NULL;
930 GVariant *reply;
931
932 reply = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), result, &error);
933 g_assert_error (error, G_IO_ERROR, G_IO_ERROR_FAILED);
934 g_assert_null (reply);
935 g_error_free (error);
936
937 g_main_loop_quit (loop);
938 }
939
940 static void
941 test_wellknown_noauto (void)
942 {
943 GError *error = NULL;
944 GDBusProxy *proxy;
945 guint id;
946
947 proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
948 G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
949 NULL, "some.name.that.does.not.exist",
950 "/", "some.interface", NULL, &error);
951 g_assert_no_error (error);
952 g_assert_nonnull (proxy);
953
954 g_dbus_proxy_call (proxy, "method", NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, check_error, NULL);
955 id = g_timeout_add (10000, fail_test, NULL);
956 g_main_loop_run (loop);
957 g_object_unref (proxy);
958 g_source_remove (id);
959 }
960
961 typedef enum {
962 ADD_MATCH,
963 REMOVE_MATCH,
964 } AddOrRemove;
965
966 static void
967 add_or_remove_match_rule (GDBusConnection *connection,
968 AddOrRemove add_or_remove,
969 GVariant *match_rule)
970 {
971 GDBusMessage *message = NULL;
972 GError *error = NULL;
973
974 message = g_dbus_message_new_method_call ("org.freedesktop.DBus", /* name */
975 "/org/freedesktop/DBus", /* path */
976 "org.freedesktop.DBus", /* interface */
977 (add_or_remove == ADD_MATCH) ? "AddMatch" : "RemoveMatch");
978 g_dbus_message_set_body (message, match_rule);
979 g_dbus_connection_send_message (connection,
980 message,
981 G_DBUS_SEND_MESSAGE_FLAGS_NONE,
982 NULL,
983 &error);
984 g_assert_no_error (error);
985 g_clear_object (&message);
986 }
987
988 static void
989 test_proxy_no_match_rule (void)
990 {
991 GDBusConnection *connection = NULL;
992 GVariant *match_rule = NULL;
993
994 g_test_summary ("Test that G_DBUS_PROXY_FLAGS_NO_MATCH_RULE works");
995 g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/1109");
996
997 connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
998
999 /* Add a custom match rule which matches everything. */
1000 match_rule = g_variant_ref_sink (g_variant_new ("(s)", "type='signal'"));
1001 add_or_remove_match_rule (connection, ADD_MATCH, match_rule);
1002
1003 /* Run the tests. */
1004 test_proxy_with_flags (G_DBUS_PROXY_FLAGS_NO_MATCH_RULE);
1005
1006 /* Remove the match rule again. */
1007 add_or_remove_match_rule (connection, REMOVE_MATCH, match_rule);
1008
1009 g_clear_pointer (&match_rule, g_variant_unref);
1010 g_clear_object (&connection);
1011 }
1012
1013 int
1014 main (int argc,
1015 char *argv[])
1016 {
1017 gint ret;
1018 GDBusNodeInfo *introspection_data = NULL;
1019
1020 g_test_init (&argc, &argv, G_TEST_OPTION_ISOLATE_DIRS, NULL);
1021
1022 introspection_data = g_dbus_node_info_new_for_xml (frob_dbus_interface_xml, NULL);
1023 g_assert_nonnull (introspection_data);
1024 frob_dbus_interface_info = introspection_data->interfaces[0];
1025
1026 /* all the tests rely on a shared main loop */
1027 loop = g_main_loop_new (NULL, FALSE);
1028
1029 g_test_add_func ("/gdbus/proxy", test_proxy);
1030 g_test_add_func ("/gdbus/proxy/no-properties", test_no_properties);
1031 g_test_add_func ("/gdbus/proxy/wellknown-noauto", test_wellknown_noauto);
1032 g_test_add_func ("/gdbus/proxy/async", test_async);
1033 g_test_add_func ("/gdbus/proxy/no-match-rule", test_proxy_no_match_rule);
1034
1035 ret = session_bus_run();
1036
1037 g_dbus_node_info_unref (introspection_data);
1038 g_main_loop_unref (loop);
1039
1040 return ret;
1041 }