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