Branch data Line data Source code
1 : : /* GLib testing framework examples and tests
2 : : *
3 : : * Copyright (C) 2008-2010 Red Hat, Inc.
4 : : *
5 : : * SPDX-License-Identifier: LGPL-2.1-or-later
6 : : *
7 : : * This library is free software; you can redistribute it and/or
8 : : * modify it under the terms of the GNU Lesser General Public
9 : : * License as published by the Free Software Foundation; either
10 : : * version 2.1 of the License, or (at your option) any later version.
11 : : *
12 : : * This library is distributed in the hope that it will be useful,
13 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 : : * Lesser General Public License for more details.
16 : : *
17 : : * You should have received a copy of the GNU Lesser General
18 : : * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
19 : : *
20 : : * Author: David Zeuthen <davidz@redhat.com>
21 : : */
22 : :
23 : : #include <gio/gio.h>
24 : : #include <unistd.h>
25 : : #include <string.h>
26 : :
27 : : #include "gdbus-tests.h"
28 : :
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 : 6 : 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 : 6 : error = NULL;
46 : 6 : 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 : 6 : g_assert_no_error (error);
54 : 6 : g_assert_nonnull (result);
55 : 6 : g_assert_cmpstr (g_variant_get_type_string (result), ==, "(s)");
56 : 6 : g_variant_get (result, "(&s)", &str);
57 : 6 : g_assert_cmpstr (str, ==, "You greeted me with 'Hey'. Thanks!");
58 : 6 : g_variant_unref (result);
59 : :
60 : : /* Check that we can completely recover the returned error */
61 : 6 : 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 : 6 : g_assert_error (error, G_IO_ERROR, G_IO_ERROR_DBUS_ERROR);
69 : 6 : g_assert_true (g_dbus_error_is_remote_error (error));
70 : 6 : g_assert_true (g_dbus_error_is_remote_error (error));
71 : 6 : g_assert_null (result);
72 : 6 : dbus_error_name = g_dbus_error_get_remote_error (error);
73 : 6 : g_assert_cmpstr (dbus_error_name, ==, "com.example.TestException");
74 : 6 : g_free (dbus_error_name);
75 : 6 : g_assert_true (g_dbus_error_strip_remote_error (error));
76 : 6 : g_assert_cmpstr (error->message, ==, "Yo is not a proper greeting");
77 : 6 : 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 : 6 : error = NULL;
85 : 6 : 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 : 6 : g_assert_error (error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT);
93 : 6 : g_assert_false (g_dbus_error_is_remote_error (error));
94 : 6 : g_assert_null (result);
95 : 6 : g_clear_error (&error);
96 : :
97 : : /* Check that proxy-default timeouts work. */
98 : 6 : g_assert_cmpint (g_dbus_proxy_get_default_timeout (proxy), ==, -1);
99 : :
100 : : /* the default timeout is 25000 msec so this should work */
101 : 6 : 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 : 6 : g_assert_no_error (error);
109 : 6 : g_assert_nonnull (result);
110 : 6 : g_assert_cmpstr (g_variant_get_type_string (result), ==, "()");
111 : 6 : 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 : 6 : g_dbus_proxy_set_default_timeout (proxy, 250);
117 : 6 : g_assert_cmpint (g_dbus_proxy_get_default_timeout (proxy), ==, 250);
118 : 6 : 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 : 6 : g_assert_error (error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT);
126 : 6 : g_assert_false (g_dbus_error_is_remote_error (error));
127 : 6 : g_assert_null (result);
128 : 6 : g_clear_error (&error);
129 : :
130 : : /* clean up after ourselves */
131 : 6 : g_dbus_proxy_set_default_timeout (proxy, -1);
132 : 6 : }
133 : :
134 : : static gboolean
135 : 3 : strv_equal (gchar **strv, ...)
136 : : {
137 : : gsize count;
138 : : va_list list;
139 : : const gchar *str;
140 : : gboolean res;
141 : :
142 : 3 : res = TRUE;
143 : 3 : count = 0;
144 : 3 : va_start (list, strv);
145 : : while (1)
146 : : {
147 : 75 : str = va_arg (list, const gchar *);
148 [ + + ]: 75 : if (str == NULL)
149 : 3 : break;
150 [ - + ]: 72 : if (g_strcmp0 (str, strv[count]) != 0)
151 : : {
152 : 0 : res = FALSE;
153 : 0 : break;
154 : : }
155 : 72 : count++;
156 : : }
157 : 3 : va_end (list);
158 : :
159 [ + - ]: 3 : if (res)
160 : 3 : res = g_strv_length (strv) == count;
161 : :
162 : 3 : return res;
163 : : }
164 : :
165 : : /* ---------------------------------------------------------------------------------------------------- */
166 : : /* Test that the property aspects of GDBusProxy works */
167 : : /* ---------------------------------------------------------------------------------------------------- */
168 : :
169 : : static void
170 : 4 : 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 : 4 : error = NULL;
181 : :
182 [ + + ]: 4 : if (g_dbus_proxy_get_flags (proxy) & G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES)
183 : : {
184 : 1 : g_assert_null (g_dbus_proxy_get_cached_property_names (proxy));
185 : 1 : return;
186 : : }
187 : :
188 : : /*
189 : : * Check that we can list all cached properties.
190 : : */
191 : 3 : names = g_dbus_proxy_get_cached_property_names (proxy);
192 : :
193 : 3 : 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 : 3 : 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 : 3 : variant = g_dbus_proxy_get_cached_property (proxy, "y");
228 : 3 : g_assert_nonnull (variant);
229 : 3 : g_assert_cmpint (g_variant_get_byte (variant), ==, 1);
230 : 3 : g_variant_unref (variant);
231 : 3 : variant = g_dbus_proxy_get_cached_property (proxy, "o");
232 : 3 : g_assert_nonnull (variant);
233 : 3 : g_assert_cmpstr (g_variant_get_string (variant, NULL), ==, "/some/path");
234 : 3 : 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 : 3 : variant2 = g_variant_new_byte (42);
241 : 3 : 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 : 3 : g_assert_no_error (error);
251 : 3 : g_assert_nonnull (result);
252 : 3 : g_assert_cmpstr (g_variant_get_type_string (result), ==, "()");
253 : 3 : g_variant_unref (result);
254 : 3 : _g_assert_signal_received (proxy, "g-properties-changed");
255 : 3 : variant = g_dbus_proxy_get_cached_property (proxy, "y");
256 : 3 : g_assert_nonnull (variant);
257 : 3 : g_assert_cmpint (g_variant_get_byte (variant), ==, 42);
258 : 3 : g_variant_unref (variant);
259 : :
260 : 3 : g_dbus_proxy_set_cached_property (proxy, "y", g_variant_new_byte (142));
261 : 3 : variant = g_dbus_proxy_get_cached_property (proxy, "y");
262 : 3 : g_assert_nonnull (variant);
263 : 3 : g_assert_cmpint (g_variant_get_byte (variant), ==, 142);
264 : 3 : g_variant_unref (variant);
265 : :
266 : 3 : g_dbus_proxy_set_cached_property (proxy, "y", NULL);
267 : 3 : variant = g_dbus_proxy_get_cached_property (proxy, "y");
268 : 3 : 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 : 3 : variant = g_dbus_proxy_get_cached_property (proxy, "PropertyThatWillBeInvalidated");
275 : 3 : g_assert_nonnull (variant);
276 : 3 : g_assert_cmpstr (g_variant_get_string (variant, NULL), ==, "InitialValue");
277 : 3 : 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 : 3 : 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 : 3 : g_assert_no_error (error);
297 : 3 : g_assert_nonnull (result);
298 : 3 : g_assert_cmpstr (g_variant_get_type_string (result), ==, "()");
299 : 3 : g_variant_unref (result);
300 : : /* ... hence we wait for the g-properties-changed signal to be delivered */
301 : 3 : _g_assert_signal_received (proxy, "g-properties-changed");
302 : : /* ... and now we finally, check that the cached value has been invalidated */
303 : 3 : variant = g_dbus_proxy_get_cached_property (proxy, "PropertyThatWillBeInvalidated");
304 : 3 : 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 : 3 : error = NULL;
308 : 3 : 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 : 3 : g_assert_no_error (error);
317 : :
318 : 3 : name_owner = g_dbus_proxy_get_name_owner (proxy2);
319 : 3 : g_assert_nonnull (name_owner);
320 : 3 : g_free (name_owner);
321 : :
322 : 3 : variant = g_dbus_proxy_get_cached_property (proxy2, "PropertyThatWillBeInvalidated");
323 : 3 : g_assert_nonnull (variant);
324 : 3 : g_assert_cmpstr (g_variant_get_string (variant, NULL), ==, "OMGInvalidated"); /* from previous test */
325 : 3 : g_variant_unref (variant);
326 : :
327 : 3 : 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 : 3 : g_assert_no_error (error);
335 : 3 : g_assert_nonnull (result);
336 : 3 : g_assert_cmpstr (g_variant_get_type_string (result), ==, "()");
337 : 3 : g_variant_unref (result);
338 : :
339 : : /* this time we should get the ::g-properties-changed _with_ the value */
340 : 3 : _g_assert_signal_received (proxy2, "g-properties-changed");
341 : :
342 : 3 : variant = g_dbus_proxy_get_cached_property (proxy2, "PropertyThatWillBeInvalidated");
343 : 3 : g_assert_nonnull (variant);
344 : 3 : g_assert_cmpstr (g_variant_get_string (variant, NULL), ==, "OMGInvalidated2");
345 : 3 : g_variant_unref (variant);
346 : :
347 : 3 : g_object_unref (proxy2);
348 : : }
349 : :
350 : : /* ---------------------------------------------------------------------------------------------------- */
351 : : /* Test that the signal aspects of GDBusProxy works */
352 : : /* ---------------------------------------------------------------------------------------------------- */
353 : :
354 : : static void
355 : 12 : 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 : 12 : GString *s = user_data;
362 : :
363 : 12 : g_assert_cmpstr (signal_name, ==, "TestSignal");
364 : 12 : g_assert_cmpstr (g_variant_get_type_string (parameters), ==, "(sov)");
365 : :
366 : 12 : g_variant_print_string (parameters, s, TRUE);
367 : 12 : }
368 : :
369 : : typedef struct
370 : : {
371 : : GMainLoop *internal_loop;
372 : : GString *s;
373 : : } TestSignalData;
374 : :
375 : : static void
376 : 6 : test_proxy_signals_on_emit_signal_cb (GDBusProxy *proxy,
377 : : GAsyncResult *res,
378 : : gpointer user_data)
379 : : {
380 : 6 : TestSignalData *data = user_data;
381 : : GError *error;
382 : : GVariant *result;
383 : :
384 : 6 : error = NULL;
385 : 6 : result = g_dbus_proxy_call_finish (proxy,
386 : : res,
387 : : &error);
388 : 6 : g_assert_no_error (error);
389 : 6 : g_assert_nonnull (result);
390 : 6 : g_assert_cmpstr (g_variant_get_type_string (result), ==, "()");
391 : 6 : g_variant_unref (result);
392 : :
393 : : /* check that the signal was received before we got the method result */
394 : 6 : g_assert_cmpuint (strlen (data->s->str), >, 0);
395 : :
396 : : /* break out of the loop */
397 : 6 : g_main_loop_quit (data->internal_loop);
398 : 6 : }
399 : :
400 : : static void
401 : 6 : test_signals (GDBusProxy *proxy)
402 : : {
403 : : GError *error;
404 : : GString *s;
405 : : gulong signal_handler_id;
406 : : TestSignalData data;
407 : : GVariant *result;
408 : :
409 : 6 : 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 : 6 : s = g_string_new (NULL);
418 : 6 : signal_handler_id = g_signal_connect (proxy,
419 : : "g-signal",
420 : : G_CALLBACK (test_proxy_signals_on_signal),
421 : : s);
422 : :
423 : 6 : 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 : 6 : g_assert_no_error (error);
433 : 6 : g_assert_nonnull (result);
434 : 6 : g_assert_cmpstr (g_variant_get_type_string (result), ==, "()");
435 : 6 : g_variant_unref (result);
436 : : /* check that we haven't received the signal just yet */
437 : 6 : g_assert_cmpuint (strlen (s->str), ==, 0);
438 : : /* and now wait for the signal */
439 : 6 : _g_assert_signal_received (proxy, "g-signal");
440 : 6 : g_assert_cmpstr (s->str,
441 : : ==,
442 : : "('Accept the next proposition you hear .. in bed!', objectpath '/some/path/in/bed', <'a variant'>)");
443 : 6 : g_signal_handler_disconnect (proxy, signal_handler_id);
444 : 6 : g_string_free (s, TRUE);
445 : :
446 : : /*
447 : : * Now do this async to check the signal is received before the method returns.
448 : : */
449 : 6 : s = g_string_new (NULL);
450 : 6 : data.internal_loop = g_main_loop_new (NULL, FALSE);
451 : 6 : data.s = s;
452 : 6 : signal_handler_id = g_signal_connect (proxy,
453 : : "g-signal",
454 : : G_CALLBACK (test_proxy_signals_on_signal),
455 : : s);
456 : 6 : 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 : 6 : g_main_loop_run (data.internal_loop);
467 : 6 : g_main_loop_unref (data.internal_loop);
468 : 6 : g_assert_cmpstr (s->str,
469 : : ==,
470 : : "('You will make a great programmer .. in bed!', objectpath '/some/other/path/in/bed', <'a variant'>)");
471 : 6 : g_signal_handler_disconnect (proxy, signal_handler_id);
472 : 6 : g_string_free (s, TRUE);
473 : 6 : }
474 : :
475 : : /* ---------------------------------------------------------------------------------------------------- */
476 : :
477 : : static void
478 : 3 : test_bogus_method_return (GDBusProxy *proxy)
479 : : {
480 : 3 : GError *error = NULL;
481 : : GVariant *result;
482 : :
483 : 3 : result = g_dbus_proxy_call_sync (proxy,
484 : : "PairReturn",
485 : : NULL,
486 : : G_DBUS_CALL_FLAGS_NONE,
487 : : -1,
488 : : NULL,
489 : : &error);
490 : 3 : g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT);
491 : 3 : g_error_free (error);
492 : 3 : g_assert_null (result);
493 : 3 : }
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 : 3 : 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 : 3 : g_dbus_proxy_set_cached_property (proxy, "y", g_variant_new_string ("error_me_out!"));
610 : 3 : g_dbus_proxy_set_cached_property (proxy, "y", g_variant_new_byte (42));
611 : 3 : g_dbus_proxy_set_cached_property (proxy, "does-not-exist", g_variant_new_string ("something"));
612 : 3 : g_dbus_proxy_set_cached_property (proxy, "does-not-exist", NULL);
613 : :
614 : : /* Now repeat the method tests, with an expected interface set */
615 : 3 : g_dbus_proxy_set_interface_info (proxy, frob_dbus_interface_info);
616 : 3 : test_methods (proxy);
617 : 3 : test_signals (proxy);
618 : :
619 : : /* And also where we deliberately set the expected interface definition incorrectly */
620 : 3 : 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 [ + - ]: 3 : if (g_test_undefined ())
627 : : {
628 : : /* Also check that we complain if setting a cached property of the wrong type */
629 : 3 : 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 : 3 : value = g_variant_ref_sink (g_variant_new_string ("error_me_out!"));
632 : 3 : g_dbus_proxy_set_cached_property (proxy, "y", value);
633 : 3 : g_variant_unref (value);
634 : 3 : g_test_assert_expected_messages ();
635 : : }
636 : :
637 : : /* this should work, however (since the type is correct) */
638 : 3 : g_dbus_proxy_set_cached_property (proxy, "y", g_variant_new_byte (42));
639 : :
640 [ + - ]: 3 : 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 : 3 : 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 : 3 : value = g_dbus_proxy_get_cached_property (proxy, "i");
648 : 3 : 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 : 3 : value = g_dbus_proxy_get_cached_property (proxy, "d");
659 : 3 : g_assert_nonnull (value);
660 : 3 : g_assert_true (g_variant_is_of_type (value, G_VARIANT_TYPE_DOUBLE));
661 : 3 : g_assert_cmpfloat (g_variant_get_double (value), ==, 7.5);
662 : 3 : g_variant_unref (value);
663 : : /* update it via the cached property... */
664 : 3 : g_dbus_proxy_set_cached_property (proxy, "d", g_variant_new_double (75.0));
665 : : /* ... and finally check that it has changed */
666 : 3 : value = g_dbus_proxy_get_cached_property (proxy, "d");
667 : 3 : g_assert_nonnull (value);
668 : 3 : g_assert_true (g_variant_is_of_type (value, G_VARIANT_TYPE_DOUBLE));
669 : 3 : g_assert_cmpfloat (g_variant_get_double (value), ==, 75.0);
670 : 3 : g_variant_unref (value);
671 : : /* now update it via the D-Bus interface... */
672 : 3 : error = NULL;
673 : 3 : 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 : 3 : g_assert_no_error (error);
678 : 3 : g_assert_nonnull (value);
679 : 3 : g_assert_cmpstr (g_variant_get_type_string (value), ==, "()");
680 : 3 : g_variant_unref (value);
681 : : /* ...ensure we receive the ::PropertiesChanged signal... */
682 : 3 : _g_assert_signal_received (proxy, "g-properties-changed");
683 : : /* ... and finally check that it has changed */
684 : 3 : value = g_dbus_proxy_get_cached_property (proxy, "d");
685 : 3 : g_assert_nonnull (value);
686 : 3 : g_assert_true (g_variant_is_of_type (value, G_VARIANT_TYPE_DOUBLE));
687 : 3 : g_assert_cmpfloat (g_variant_get_double (value), ==, 85.0);
688 : 3 : g_variant_unref (value);
689 : 3 : }
690 : :
691 : : static void
692 : 3 : 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 : 3 : connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
704 : :
705 : 3 : g_assert_true (g_dbus_proxy_get_connection (proxy) == connection);
706 : 3 : g_assert_null (g_dbus_proxy_get_interface_info (proxy));
707 : 3 : g_assert_cmpstr (g_dbus_proxy_get_name (proxy), ==, "com.example.TestService");
708 : 3 : g_assert_cmpstr (g_dbus_proxy_get_object_path (proxy), ==, "/com/example/TestObject");
709 : 3 : g_assert_cmpstr (g_dbus_proxy_get_interface_name (proxy), ==, "com.example.Frob");
710 : 3 : g_assert_cmpint (g_dbus_proxy_get_default_timeout (proxy), ==, -1);
711 : :
712 : 3 : 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 : 3 : g_assert_true (conn == connection);
723 : 3 : g_assert_null (info);
724 : 3 : g_assert_cmpint (flags, ==, g_dbus_proxy_get_flags (proxy));
725 : 3 : g_assert_cmpstr (name, ==, "com.example.TestService");
726 : 3 : g_assert_cmpstr (path, ==, "/com/example/TestObject");
727 : 3 : g_assert_cmpstr (interface, ==, "com.example.Frob");
728 : 3 : g_assert_cmpint (timeout, ==, -1);
729 : :
730 : 3 : g_object_unref (conn);
731 : 3 : g_free (name);
732 : 3 : g_free (path);
733 : 3 : g_free (interface);
734 : :
735 : 3 : g_object_unref (connection);
736 : 3 : }
737 : :
738 : : static void
739 : 3 : name_disappeared_cb (GDBusConnection *connection,
740 : : const gchar *name,
741 : : gpointer user_data)
742 : : {
743 : 3 : gboolean *name_disappeared = user_data;
744 : 3 : *name_disappeared = TRUE;
745 : 3 : g_main_context_wakeup (NULL);
746 : 3 : }
747 : :
748 : : static void
749 : 3 : kill_test_service (GDBusConnection *connection)
750 : : {
751 : : #ifdef G_OS_UNIX
752 : : guint pid;
753 : : GVariant *ret;
754 : 3 : GError *error = NULL;
755 : 3 : const gchar *name = "com.example.TestService";
756 : : guint watch_id;
757 : 3 : gboolean name_disappeared = FALSE;
758 : :
759 : 3 : 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 : 3 : g_variant_get (ret, "(u)", &pid);
771 : 3 : g_variant_unref (ret);
772 : :
773 : : /* Watch the name and wait until it’s disappeared. */
774 : 3 : 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 : 3 : kill (pid, SIGTERM);
779 : :
780 [ + + ]: 12 : while (!name_disappeared)
781 : 9 : g_main_context_iteration (NULL, TRUE);
782 : :
783 : 3 : g_bus_unwatch_name (watch_id);
784 : : #else
785 : : g_warning ("Can't kill com.example.TestService");
786 : : #endif
787 : 3 : }
788 : :
789 : : static void
790 : 2 : test_proxy_with_flags (GDBusProxyFlags flags)
791 : : {
792 : : GDBusProxy *proxy;
793 : : GDBusConnection *connection;
794 : : GError *error;
795 : : gchar *owner;
796 : :
797 : 2 : error = NULL;
798 : 2 : connection = g_bus_get_sync (G_BUS_TYPE_SESSION,
799 : : NULL,
800 : : &error);
801 : 2 : g_assert_no_error (error);
802 : 2 : error = NULL;
803 : 2 : 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 : 2 : g_assert_no_error (error);
812 : :
813 : : /* this is safe; we explicitly kill the service later on */
814 : 2 : g_assert_true (g_spawn_command_line_async (g_test_get_filename (G_TEST_BUILT, "gdbus-testserver", NULL), NULL));
815 : :
816 : 2 : _g_assert_property_notify (proxy, "g-name-owner");
817 : :
818 : 2 : test_basic (proxy);
819 : 2 : test_methods (proxy);
820 : 2 : test_properties (proxy);
821 : 2 : test_signals (proxy);
822 : 2 : test_expected_interface (proxy);
823 : :
824 : 2 : kill_test_service (connection);
825 : :
826 : 2 : owner = g_dbus_proxy_get_name_owner (proxy);
827 : 2 : g_assert_null (owner);
828 : 2 : g_free (owner);
829 : :
830 : 2 : g_object_unref (proxy);
831 : 2 : g_object_unref (connection);
832 : 2 : }
833 : :
834 : : static void
835 : 1 : test_proxy (void)
836 : : {
837 : 1 : test_proxy_with_flags (G_DBUS_PROXY_FLAGS_NONE);
838 : 1 : }
839 : :
840 : : /* ---------------------------------------------------------------------------------------------------- */
841 : :
842 : : static void
843 : 1 : proxy_ready (GObject *source,
844 : : GAsyncResult *result,
845 : : gpointer user_data)
846 : : {
847 : : GDBusProxy *proxy;
848 : : GError *error;
849 : : gchar *owner;
850 : :
851 : 1 : error = NULL;
852 : 1 : proxy = g_dbus_proxy_new_for_bus_finish (result, &error);
853 : 1 : g_assert_no_error (error);
854 : :
855 : 1 : owner = g_dbus_proxy_get_name_owner (proxy);
856 : 1 : g_assert_null (owner);
857 : 1 : g_free (owner);
858 : :
859 : : /* this is safe; we explicitly kill the service later on */
860 : 1 : g_assert_true (g_spawn_command_line_async (g_test_get_filename (G_TEST_BUILT, "gdbus-testserver", NULL), NULL));
861 : :
862 : 1 : _g_assert_property_notify (proxy, "g-name-owner");
863 : :
864 : 1 : test_basic (proxy);
865 : 1 : test_methods (proxy);
866 : 1 : test_properties (proxy);
867 : 1 : test_signals (proxy);
868 : 1 : test_expected_interface (proxy);
869 : :
870 : 1 : kill_test_service (g_dbus_proxy_get_connection (proxy));
871 : 1 : g_object_unref (proxy);
872 : 1 : g_main_loop_quit (loop);
873 : 1 : }
874 : :
875 : : static gboolean
876 : 0 : fail_test (gpointer user_data)
877 : : {
878 : : g_assert_not_reached ();
879 : : }
880 : :
881 : : static void
882 : 1 : test_async (void)
883 : : {
884 : : guint id;
885 : :
886 : 1 : 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 : 1 : id = g_timeout_add (10000, fail_test, NULL);
897 : 1 : g_main_loop_run (loop);
898 : :
899 : 1 : g_source_remove (id);
900 : 1 : }
901 : :
902 : : static void
903 : 1 : test_no_properties (void)
904 : : {
905 : : GDBusProxy *proxy;
906 : : GError *error;
907 : :
908 : 1 : error = NULL;
909 : 1 : 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 : 1 : g_assert_no_error (error);
918 : :
919 : 1 : test_properties (proxy);
920 : :
921 : 1 : g_object_unref (proxy);
922 : 1 : }
923 : :
924 : : static void
925 : 1 : check_error (GObject *source,
926 : : GAsyncResult *result,
927 : : gpointer user_data)
928 : : {
929 : 1 : GError *error = NULL;
930 : : GVariant *reply;
931 : :
932 : 1 : reply = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), result, &error);
933 : 1 : g_assert_error (error, G_IO_ERROR, G_IO_ERROR_FAILED);
934 : 1 : g_assert_null (reply);
935 : 1 : g_error_free (error);
936 : :
937 : 1 : g_main_loop_quit (loop);
938 : 1 : }
939 : :
940 : : static void
941 : 1 : test_wellknown_noauto (void)
942 : : {
943 : 1 : GError *error = NULL;
944 : : GDBusProxy *proxy;
945 : : guint id;
946 : :
947 : 1 : 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 : 1 : g_assert_no_error (error);
952 : 1 : g_assert_nonnull (proxy);
953 : :
954 : 1 : g_dbus_proxy_call (proxy, "method", NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, check_error, NULL);
955 : 1 : id = g_timeout_add (10000, fail_test, NULL);
956 : 1 : g_main_loop_run (loop);
957 : 1 : g_object_unref (proxy);
958 : 1 : g_source_remove (id);
959 : 1 : }
960 : :
961 : : typedef enum {
962 : : ADD_MATCH,
963 : : REMOVE_MATCH,
964 : : } AddOrRemove;
965 : :
966 : : static void
967 : 2 : add_or_remove_match_rule (GDBusConnection *connection,
968 : : AddOrRemove add_or_remove,
969 : : GVariant *match_rule)
970 : : {
971 : 2 : GDBusMessage *message = NULL;
972 : 2 : GError *error = NULL;
973 : :
974 [ + + ]: 2 : 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 : 2 : g_dbus_message_set_body (message, match_rule);
979 : 2 : g_dbus_connection_send_message (connection,
980 : : message,
981 : : G_DBUS_SEND_MESSAGE_FLAGS_NONE,
982 : : NULL,
983 : : &error);
984 : 2 : g_assert_no_error (error);
985 : 2 : g_clear_object (&message);
986 : 2 : }
987 : :
988 : : static void
989 : 1 : test_proxy_no_match_rule (void)
990 : : {
991 : 1 : GDBusConnection *connection = NULL;
992 : 1 : GVariant *match_rule = NULL;
993 : :
994 : 1 : g_test_summary ("Test that G_DBUS_PROXY_FLAGS_NO_MATCH_RULE works");
995 : 1 : g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/1109");
996 : :
997 : 1 : connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
998 : :
999 : : /* Add a custom match rule which matches everything. */
1000 : 1 : match_rule = g_variant_ref_sink (g_variant_new ("(s)", "type='signal'"));
1001 : 1 : add_or_remove_match_rule (connection, ADD_MATCH, match_rule);
1002 : :
1003 : : /* Run the tests. */
1004 : 1 : test_proxy_with_flags (G_DBUS_PROXY_FLAGS_NO_MATCH_RULE);
1005 : :
1006 : : /* Remove the match rule again. */
1007 : 1 : add_or_remove_match_rule (connection, REMOVE_MATCH, match_rule);
1008 : :
1009 : 1 : g_clear_pointer (&match_rule, g_variant_unref);
1010 : 1 : g_clear_object (&connection);
1011 : 1 : }
1012 : :
1013 : : int
1014 : 1 : main (int argc,
1015 : : char *argv[])
1016 : : {
1017 : : gint ret;
1018 : 1 : GDBusNodeInfo *introspection_data = NULL;
1019 : :
1020 : 1 : g_test_init (&argc, &argv, G_TEST_OPTION_ISOLATE_DIRS, NULL);
1021 : :
1022 : 1 : introspection_data = g_dbus_node_info_new_for_xml (frob_dbus_interface_xml, NULL);
1023 : 1 : g_assert_nonnull (introspection_data);
1024 : 1 : frob_dbus_interface_info = introspection_data->interfaces[0];
1025 : :
1026 : : /* all the tests rely on a shared main loop */
1027 : 1 : loop = g_main_loop_new (NULL, FALSE);
1028 : :
1029 : 1 : g_test_add_func ("/gdbus/proxy", test_proxy);
1030 : 1 : g_test_add_func ("/gdbus/proxy/no-properties", test_no_properties);
1031 : 1 : g_test_add_func ("/gdbus/proxy/wellknown-noauto", test_wellknown_noauto);
1032 : 1 : g_test_add_func ("/gdbus/proxy/async", test_async);
1033 : 1 : g_test_add_func ("/gdbus/proxy/no-match-rule", test_proxy_no_match_rule);
1034 : :
1035 : 1 : ret = session_bus_run();
1036 : :
1037 : 1 : g_dbus_node_info_unref (introspection_data);
1038 : 1 : g_main_loop_unref (loop);
1039 : :
1040 : 1 : return ret;
1041 : : }
|