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