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 <sys/types.h> |
26 | |
27 | #include "gdbus-tests.h" |
28 | |
29 | /* all tests rely on a shared mainloop */ |
30 | static GMainLoop *loop = NULL; |
31 | |
32 | #if 0 |
33 | G_GNUC_UNUSED static void |
34 | _log (const gchar *format, ...) |
35 | { |
36 | GTimeVal now; |
37 | time_t now_time; |
38 | struct tm *now_tm; |
39 | gchar time_buf[128]; |
40 | gchar *str; |
41 | va_list var_args; |
42 | |
43 | va_start (var_args, format); |
44 | str = g_strdup_vprintf (format, var_args); |
45 | va_end (var_args); |
46 | |
47 | g_get_current_time (&now); |
48 | now_time = (time_t) now.tv_sec; |
49 | now_tm = localtime (&now_time); |
50 | strftime (time_buf, sizeof time_buf, "%H:%M:%S" , now_tm); |
51 | |
52 | g_printerr ("%s.%06d: %s\n" , |
53 | time_buf, (gint) now.tv_usec / 1000, |
54 | str); |
55 | g_free (str); |
56 | } |
57 | #else |
58 | #define _log(...) |
59 | #endif |
60 | |
61 | static gboolean |
62 | test_connection_quit_mainloop (gpointer user_data) |
63 | { |
64 | gboolean *quit_mainloop_fired = user_data; /* (atomic) */ |
65 | _log ("quit_mainloop_fired" ); |
66 | g_atomic_int_set (quit_mainloop_fired, TRUE); |
67 | g_main_loop_quit (loop); |
68 | return G_SOURCE_CONTINUE; |
69 | } |
70 | |
71 | /* ---------------------------------------------------------------------------------------------------- */ |
72 | /* Connection life-cycle testing */ |
73 | /* ---------------------------------------------------------------------------------------------------- */ |
74 | |
75 | static const GDBusInterfaceInfo boo_interface_info = |
76 | { |
77 | -1, |
78 | "org.example.Boo" , |
79 | (GDBusMethodInfo **) NULL, |
80 | (GDBusSignalInfo **) NULL, |
81 | (GDBusPropertyInfo **) NULL, |
82 | NULL, |
83 | }; |
84 | |
85 | static const GDBusInterfaceVTable boo_vtable = |
86 | { |
87 | NULL, /* _method_call */ |
88 | NULL, /* _get_property */ |
89 | NULL /* _set_property */ |
90 | }; |
91 | |
92 | /* Runs in a worker thread. */ |
93 | static GDBusMessage * |
94 | some_filter_func (GDBusConnection *connection, |
95 | GDBusMessage *message, |
96 | gboolean incoming, |
97 | gpointer user_data) |
98 | { |
99 | return message; |
100 | } |
101 | |
102 | static void |
103 | on_name_owner_changed (GDBusConnection *connection, |
104 | const gchar *sender_name, |
105 | const gchar *object_path, |
106 | const gchar *interface_name, |
107 | const gchar *signal_name, |
108 | GVariant *parameters, |
109 | gpointer user_data) |
110 | { |
111 | } |
112 | |
113 | static void |
114 | a_gdestroynotify_that_sets_a_gboolean_to_true_and_quits_loop (gpointer user_data) |
115 | { |
116 | gboolean *val = user_data; /* (atomic) */ |
117 | g_atomic_int_set (val, TRUE); |
118 | _log ("destroynotify fired for %p" , val); |
119 | g_main_loop_quit (loop); |
120 | } |
121 | |
122 | static void |
123 | test_connection_bus_failure (void) |
124 | { |
125 | GDBusConnection *c; |
126 | GError *error = NULL; |
127 | |
128 | /* |
129 | * Check for correct behavior when no bus is present |
130 | * |
131 | */ |
132 | c = g_bus_get_sync (bus_type: G_BUS_TYPE_SESSION, NULL, error: &error); |
133 | g_assert_nonnull (error); |
134 | g_assert_false (g_dbus_error_is_remote_error (error)); |
135 | g_assert_null (c); |
136 | g_error_free (error); |
137 | } |
138 | |
139 | static void |
140 | test_connection_life_cycle (void) |
141 | { |
142 | gboolean ret; |
143 | GDBusConnection *c; |
144 | GDBusConnection *c2; |
145 | GError *error; |
146 | gboolean on_signal_registration_freed_called; /* (atomic) */ |
147 | gboolean on_filter_freed_called; /* (atomic) */ |
148 | gboolean on_register_object_freed_called; /* (atomic) */ |
149 | gboolean quit_mainloop_fired; /* (atomic) */ |
150 | guint quit_mainloop_id; |
151 | guint registration_id; |
152 | |
153 | error = NULL; |
154 | |
155 | /* |
156 | * Check for correct behavior when a bus is present |
157 | */ |
158 | session_bus_up (); |
159 | /* case 1 */ |
160 | error = NULL; |
161 | c = g_bus_get_sync (bus_type: G_BUS_TYPE_SESSION, NULL, error: &error); |
162 | g_assert_no_error (error); |
163 | g_assert_nonnull (c); |
164 | g_assert_false (g_dbus_connection_is_closed (c)); |
165 | |
166 | /* |
167 | * Check that singleton handling work |
168 | */ |
169 | error = NULL; |
170 | c2 = g_bus_get_sync (bus_type: G_BUS_TYPE_SESSION, NULL, error: &error); |
171 | g_assert_no_error (error); |
172 | g_assert_nonnull (c2); |
173 | g_assert_true (c == c2); |
174 | g_object_unref (object: c2); |
175 | |
176 | /* |
177 | * Check that private connections work |
178 | */ |
179 | c2 = _g_bus_get_priv (bus_type: G_BUS_TYPE_SESSION, NULL, error: &error); |
180 | g_assert_no_error (error); |
181 | g_assert_nonnull (c2); |
182 | g_assert_true (c != c2); |
183 | g_object_unref (object: c2); |
184 | |
185 | c2 = _g_bus_get_priv (bus_type: G_BUS_TYPE_SESSION, NULL, error: &error); |
186 | g_assert_no_error (error); |
187 | g_assert_nonnull (c2); |
188 | g_assert_false (g_dbus_connection_is_closed (c2)); |
189 | ret = g_dbus_connection_close_sync (connection: c2, NULL, error: &error); |
190 | g_assert_no_error (error); |
191 | g_assert_true (ret); |
192 | _g_assert_signal_received (c2, "closed" ); |
193 | g_assert_true (g_dbus_connection_is_closed (c2)); |
194 | ret = g_dbus_connection_close_sync (connection: c2, NULL, error: &error); |
195 | g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CLOSED); |
196 | g_error_free (error); |
197 | g_assert_false (ret); |
198 | g_object_unref (object: c2); |
199 | |
200 | /* |
201 | * Check that the finalization code works |
202 | * |
203 | * (and that the GDestroyNotify for filters and objects and signal |
204 | * registrations are run as expected) |
205 | */ |
206 | error = NULL; |
207 | c2 = _g_bus_get_priv (bus_type: G_BUS_TYPE_SESSION, NULL, error: &error); |
208 | g_assert_no_error (error); |
209 | g_assert_nonnull (c2); |
210 | /* signal registration */ |
211 | g_atomic_int_set (&on_signal_registration_freed_called, FALSE); |
212 | g_dbus_connection_signal_subscribe (connection: c2, |
213 | sender: "org.freedesktop.DBus" , /* bus name */ |
214 | interface_name: "org.freedesktop.DBus" , /* interface */ |
215 | member: "NameOwnerChanged" , /* member */ |
216 | object_path: "/org/freesktop/DBus" , /* path */ |
217 | NULL, /* arg0 */ |
218 | flags: G_DBUS_SIGNAL_FLAGS_NONE, |
219 | callback: on_name_owner_changed, |
220 | user_data: (gpointer) &on_signal_registration_freed_called, |
221 | user_data_free_func: a_gdestroynotify_that_sets_a_gboolean_to_true_and_quits_loop); |
222 | /* filter func */ |
223 | g_atomic_int_set (&on_filter_freed_called, FALSE); |
224 | g_dbus_connection_add_filter (connection: c2, |
225 | filter_function: some_filter_func, |
226 | user_data: (gpointer) &on_filter_freed_called, |
227 | user_data_free_func: a_gdestroynotify_that_sets_a_gboolean_to_true_and_quits_loop); |
228 | /* object registration */ |
229 | g_atomic_int_set (&on_register_object_freed_called, FALSE); |
230 | error = NULL; |
231 | registration_id = g_dbus_connection_register_object (connection: c2, |
232 | object_path: "/foo" , |
233 | interface_info: (GDBusInterfaceInfo *) &boo_interface_info, |
234 | vtable: &boo_vtable, |
235 | user_data: (gpointer) &on_register_object_freed_called, |
236 | user_data_free_func: a_gdestroynotify_that_sets_a_gboolean_to_true_and_quits_loop, |
237 | error: &error); |
238 | g_assert_no_error (error); |
239 | g_assert_cmpuint (registration_id, >, 0); |
240 | /* ok, finalize the connection and check that all the GDestroyNotify functions are invoked as expected */ |
241 | g_object_unref (object: c2); |
242 | g_atomic_int_set (&quit_mainloop_fired, FALSE); |
243 | quit_mainloop_id = g_timeout_add (interval: 30000, function: test_connection_quit_mainloop, data: (gpointer) &quit_mainloop_fired); |
244 | _log ("destroynotifies for\n" |
245 | " register_object %p\n" |
246 | " filter %p\n" |
247 | " signal %p" , |
248 | &on_register_object_freed_called, |
249 | &on_filter_freed_called, |
250 | &on_signal_registration_freed_called); |
251 | while (TRUE) |
252 | { |
253 | if (g_atomic_int_get (&on_signal_registration_freed_called) && |
254 | g_atomic_int_get (&on_filter_freed_called) && |
255 | g_atomic_int_get (&on_register_object_freed_called)) |
256 | break; |
257 | if (g_atomic_int_get (&quit_mainloop_fired)) |
258 | break; |
259 | _log ("entering loop" ); |
260 | g_main_loop_run (loop); |
261 | _log ("exiting loop" ); |
262 | } |
263 | g_source_remove (tag: quit_mainloop_id); |
264 | g_assert_true (g_atomic_int_get (&on_signal_registration_freed_called)); |
265 | g_assert_true (g_atomic_int_get (&on_filter_freed_called)); |
266 | g_assert_true (g_atomic_int_get (&on_register_object_freed_called)); |
267 | g_assert_false (g_atomic_int_get (&quit_mainloop_fired)); |
268 | |
269 | /* |
270 | * Check for correct behavior when the bus goes away |
271 | * |
272 | */ |
273 | g_assert_false (g_dbus_connection_is_closed (c)); |
274 | g_dbus_connection_set_exit_on_close (connection: c, FALSE); |
275 | session_bus_stop (); |
276 | _g_assert_signal_received (c, "closed" ); |
277 | g_assert_true (g_dbus_connection_is_closed (c)); |
278 | g_object_unref (object: c); |
279 | |
280 | session_bus_down (); |
281 | } |
282 | |
283 | /* ---------------------------------------------------------------------------------------------------- */ |
284 | /* Test that sending and receiving messages work as expected */ |
285 | /* ---------------------------------------------------------------------------------------------------- */ |
286 | |
287 | static void |
288 | msg_cb_expect_error_disconnected (GDBusConnection *connection, |
289 | GAsyncResult *res, |
290 | gpointer user_data) |
291 | { |
292 | GError *error; |
293 | GVariant *result; |
294 | |
295 | /* Make sure gdbusconnection isn't holding @connection's lock. (#747349) */ |
296 | g_dbus_connection_get_last_serial (connection); |
297 | |
298 | error = NULL; |
299 | result = g_dbus_connection_call_finish (connection, |
300 | res, |
301 | error: &error); |
302 | g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CLOSED); |
303 | g_assert_false (g_dbus_error_is_remote_error (error)); |
304 | g_error_free (error); |
305 | g_assert_null (result); |
306 | |
307 | g_main_loop_quit (loop); |
308 | } |
309 | |
310 | static void |
311 | msg_cb_expect_error_unknown_method (GDBusConnection *connection, |
312 | GAsyncResult *res, |
313 | gpointer user_data) |
314 | { |
315 | GError *error; |
316 | GVariant *result; |
317 | |
318 | /* Make sure gdbusconnection isn't holding @connection's lock. (#747349) */ |
319 | g_dbus_connection_get_last_serial (connection); |
320 | |
321 | error = NULL; |
322 | result = g_dbus_connection_call_finish (connection, |
323 | res, |
324 | error: &error); |
325 | g_assert_error (error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD); |
326 | g_assert_true (g_dbus_error_is_remote_error (error)); |
327 | g_error_free (error); |
328 | g_assert_null (result); |
329 | |
330 | g_main_loop_quit (loop); |
331 | } |
332 | |
333 | static void |
334 | msg_cb_expect_success (GDBusConnection *connection, |
335 | GAsyncResult *res, |
336 | gpointer user_data) |
337 | { |
338 | GError *error; |
339 | GVariant *result; |
340 | |
341 | /* Make sure gdbusconnection isn't holding @connection's lock. (#747349) */ |
342 | g_dbus_connection_get_last_serial (connection); |
343 | |
344 | error = NULL; |
345 | result = g_dbus_connection_call_finish (connection, |
346 | res, |
347 | error: &error); |
348 | g_assert_no_error (error); |
349 | g_assert_nonnull (result); |
350 | g_variant_unref (value: result); |
351 | |
352 | g_main_loop_quit (loop); |
353 | } |
354 | |
355 | static void |
356 | msg_cb_expect_error_cancelled (GDBusConnection *connection, |
357 | GAsyncResult *res, |
358 | gpointer user_data) |
359 | { |
360 | GError *error; |
361 | GVariant *result; |
362 | |
363 | /* Make sure gdbusconnection isn't holding @connection's lock. (#747349) */ |
364 | g_dbus_connection_get_last_serial (connection); |
365 | |
366 | error = NULL; |
367 | result = g_dbus_connection_call_finish (connection, |
368 | res, |
369 | error: &error); |
370 | g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED); |
371 | g_assert_false (g_dbus_error_is_remote_error (error)); |
372 | g_error_free (error); |
373 | g_assert_null (result); |
374 | |
375 | g_main_loop_quit (loop); |
376 | } |
377 | |
378 | static void |
379 | msg_cb_expect_error_cancelled_2 (GDBusConnection *connection, |
380 | GAsyncResult *res, |
381 | gpointer user_data) |
382 | { |
383 | GError *error; |
384 | GVariant *result; |
385 | |
386 | /* Make sure gdbusconnection isn't holding @connection's lock. (#747349) */ |
387 | g_dbus_connection_get_last_serial (connection); |
388 | |
389 | error = NULL; |
390 | result = g_dbus_connection_call_finish (connection, |
391 | res, |
392 | error: &error); |
393 | g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED); |
394 | g_assert_false (g_dbus_error_is_remote_error (error)); |
395 | g_error_free (error); |
396 | g_assert_null (result); |
397 | |
398 | g_main_loop_quit (loop); |
399 | } |
400 | |
401 | /* ---------------------------------------------------------------------------------------------------- */ |
402 | |
403 | static void |
404 | test_connection_send (void) |
405 | { |
406 | GDBusConnection *c; |
407 | GCancellable *ca; |
408 | |
409 | session_bus_up (); |
410 | |
411 | /* First, get an unopened connection */ |
412 | c = g_bus_get_sync (bus_type: G_BUS_TYPE_SESSION, NULL, NULL); |
413 | g_assert_nonnull (c); |
414 | g_assert_false (g_dbus_connection_is_closed (c)); |
415 | |
416 | /* |
417 | * Check that we never actually send a message if the GCancellable |
418 | * is already cancelled - i.e. we should get #G_IO_ERROR_CANCELLED |
419 | * when the actual connection is not up. |
420 | */ |
421 | ca = g_cancellable_new (); |
422 | g_cancellable_cancel (cancellable: ca); |
423 | g_dbus_connection_call (connection: c, |
424 | bus_name: "org.freedesktop.DBus" , /* bus_name */ |
425 | object_path: "/org/freedesktop/DBus" , /* object path */ |
426 | interface_name: "org.freedesktop.DBus" , /* interface name */ |
427 | method_name: "GetId" , /* method name */ |
428 | NULL, NULL, |
429 | flags: G_DBUS_CALL_FLAGS_NONE, |
430 | timeout_msec: -1, |
431 | cancellable: ca, |
432 | callback: (GAsyncReadyCallback) msg_cb_expect_error_cancelled, |
433 | NULL); |
434 | g_main_loop_run (loop); |
435 | g_object_unref (object: ca); |
436 | |
437 | /* |
438 | * Check that we get a reply to the GetId() method call. |
439 | */ |
440 | g_dbus_connection_call (connection: c, |
441 | bus_name: "org.freedesktop.DBus" , /* bus_name */ |
442 | object_path: "/org/freedesktop/DBus" , /* object path */ |
443 | interface_name: "org.freedesktop.DBus" , /* interface name */ |
444 | method_name: "GetId" , /* method name */ |
445 | NULL, NULL, |
446 | flags: G_DBUS_CALL_FLAGS_NONE, |
447 | timeout_msec: -1, |
448 | NULL, |
449 | callback: (GAsyncReadyCallback) msg_cb_expect_success, |
450 | NULL); |
451 | g_main_loop_run (loop); |
452 | |
453 | /* |
454 | * Check that we get an error reply to the NonExistantMethod() method call. |
455 | */ |
456 | g_dbus_connection_call (connection: c, |
457 | bus_name: "org.freedesktop.DBus" , /* bus_name */ |
458 | object_path: "/org/freedesktop/DBus" , /* object path */ |
459 | interface_name: "org.freedesktop.DBus" , /* interface name */ |
460 | method_name: "NonExistantMethod" , /* method name */ |
461 | NULL, NULL, |
462 | flags: G_DBUS_CALL_FLAGS_NONE, |
463 | timeout_msec: -1, |
464 | NULL, |
465 | callback: (GAsyncReadyCallback) msg_cb_expect_error_unknown_method, |
466 | NULL); |
467 | g_main_loop_run (loop); |
468 | |
469 | /* |
470 | * Check that cancellation works when the message is already in flight. |
471 | */ |
472 | ca = g_cancellable_new (); |
473 | g_dbus_connection_call (connection: c, |
474 | bus_name: "org.freedesktop.DBus" , /* bus_name */ |
475 | object_path: "/org/freedesktop/DBus" , /* object path */ |
476 | interface_name: "org.freedesktop.DBus" , /* interface name */ |
477 | method_name: "GetId" , /* method name */ |
478 | NULL, NULL, |
479 | flags: G_DBUS_CALL_FLAGS_NONE, |
480 | timeout_msec: -1, |
481 | cancellable: ca, |
482 | callback: (GAsyncReadyCallback) msg_cb_expect_error_cancelled_2, |
483 | NULL); |
484 | g_cancellable_cancel (cancellable: ca); |
485 | g_main_loop_run (loop); |
486 | g_object_unref (object: ca); |
487 | |
488 | /* |
489 | * Check that we get an error when sending to a connection that is disconnected. |
490 | */ |
491 | g_dbus_connection_set_exit_on_close (connection: c, FALSE); |
492 | session_bus_stop (); |
493 | _g_assert_signal_received (c, "closed" ); |
494 | g_assert_true (g_dbus_connection_is_closed (c)); |
495 | |
496 | g_dbus_connection_call (connection: c, |
497 | bus_name: "org.freedesktop.DBus" , /* bus_name */ |
498 | object_path: "/org/freedesktop/DBus" , /* object path */ |
499 | interface_name: "org.freedesktop.DBus" , /* interface name */ |
500 | method_name: "GetId" , /* method name */ |
501 | NULL, NULL, |
502 | flags: G_DBUS_CALL_FLAGS_NONE, |
503 | timeout_msec: -1, |
504 | NULL, |
505 | callback: (GAsyncReadyCallback) msg_cb_expect_error_disconnected, |
506 | NULL); |
507 | g_main_loop_run (loop); |
508 | |
509 | g_object_unref (object: c); |
510 | |
511 | session_bus_down (); |
512 | } |
513 | |
514 | /* ---------------------------------------------------------------------------------------------------- */ |
515 | /* Connection signal tests */ |
516 | /* ---------------------------------------------------------------------------------------------------- */ |
517 | |
518 | static void |
519 | test_connection_signal_handler (GDBusConnection *connection, |
520 | const gchar *sender_name, |
521 | const gchar *object_path, |
522 | const gchar *interface_name, |
523 | const gchar *signal_name, |
524 | GVariant *parameters, |
525 | gpointer user_data) |
526 | { |
527 | gint *counter = user_data; |
528 | *counter += 1; |
529 | |
530 | /*g_debug ("in test_connection_signal_handler (sender=%s path=%s interface=%s member=%s)", |
531 | sender_name, |
532 | object_path, |
533 | interface_name, |
534 | signal_name);*/ |
535 | |
536 | g_main_loop_quit (loop); |
537 | } |
538 | |
539 | static void |
540 | test_connection_signals (void) |
541 | { |
542 | GDBusConnection *c1; |
543 | GDBusConnection *c2; |
544 | GDBusConnection *c3; |
545 | guint s1; |
546 | guint s1b; |
547 | guint s2; |
548 | guint s3; |
549 | gint count_s1; |
550 | gint count_s1b; |
551 | gint count_s2; |
552 | gint count_name_owner_changed; |
553 | GError *error; |
554 | gboolean ret; |
555 | GVariant *result; |
556 | gboolean quit_mainloop_fired; |
557 | guint quit_mainloop_id; |
558 | |
559 | error = NULL; |
560 | |
561 | /* |
562 | * Bring up first separate connections |
563 | */ |
564 | session_bus_up (); |
565 | /* if running with dbus-monitor, it claims the name :1.0 - so if we don't run with the monitor |
566 | * emulate this |
567 | */ |
568 | if (g_getenv (variable: "G_DBUS_MONITOR" ) == NULL) |
569 | { |
570 | c1 = _g_bus_get_priv (bus_type: G_BUS_TYPE_SESSION, NULL, NULL); |
571 | g_assert_nonnull (c1); |
572 | g_assert_false (g_dbus_connection_is_closed (c1)); |
573 | g_object_unref (object: c1); |
574 | } |
575 | c1 = g_bus_get_sync (bus_type: G_BUS_TYPE_SESSION, NULL, NULL); |
576 | g_assert_nonnull (c1); |
577 | g_assert_false (g_dbus_connection_is_closed (c1)); |
578 | g_assert_cmpstr (g_dbus_connection_get_unique_name (c1), ==, ":1.1" ); |
579 | |
580 | /* |
581 | * Install two signal handlers for the first connection |
582 | * |
583 | * - Listen to the signal "Foo" from :1.2 (e.g. c2) |
584 | * - Listen to the signal "Foo" from anyone (e.g. both c2 and c3) |
585 | * |
586 | * and then count how many times this signal handler was invoked. |
587 | */ |
588 | s1 = g_dbus_connection_signal_subscribe (connection: c1, |
589 | sender: ":1.2" , |
590 | interface_name: "org.gtk.GDBus.ExampleInterface" , |
591 | member: "Foo" , |
592 | object_path: "/org/gtk/GDBus/ExampleInterface" , |
593 | NULL, |
594 | flags: G_DBUS_SIGNAL_FLAGS_NONE, |
595 | callback: test_connection_signal_handler, |
596 | user_data: &count_s1, |
597 | NULL); |
598 | s2 = g_dbus_connection_signal_subscribe (connection: c1, |
599 | NULL, /* match any sender */ |
600 | interface_name: "org.gtk.GDBus.ExampleInterface" , |
601 | member: "Foo" , |
602 | object_path: "/org/gtk/GDBus/ExampleInterface" , |
603 | NULL, |
604 | flags: G_DBUS_SIGNAL_FLAGS_NONE, |
605 | callback: test_connection_signal_handler, |
606 | user_data: &count_s2, |
607 | NULL); |
608 | s3 = g_dbus_connection_signal_subscribe (connection: c1, |
609 | sender: "org.freedesktop.DBus" , /* sender */ |
610 | interface_name: "org.freedesktop.DBus" , /* interface */ |
611 | member: "NameOwnerChanged" , /* member */ |
612 | object_path: "/org/freedesktop/DBus" , /* path */ |
613 | NULL, |
614 | flags: G_DBUS_SIGNAL_FLAGS_NONE, |
615 | callback: test_connection_signal_handler, |
616 | user_data: &count_name_owner_changed, |
617 | NULL); |
618 | /* Note that s1b is *just like* s1 - this is to catch a bug where N |
619 | * subscriptions of the same rule causes N calls to each of the N |
620 | * subscriptions instead of just 1 call to each of the N subscriptions. |
621 | */ |
622 | s1b = g_dbus_connection_signal_subscribe (connection: c1, |
623 | sender: ":1.2" , |
624 | interface_name: "org.gtk.GDBus.ExampleInterface" , |
625 | member: "Foo" , |
626 | object_path: "/org/gtk/GDBus/ExampleInterface" , |
627 | NULL, |
628 | flags: G_DBUS_SIGNAL_FLAGS_NONE, |
629 | callback: test_connection_signal_handler, |
630 | user_data: &count_s1b, |
631 | NULL); |
632 | g_assert_cmpuint (s1, !=, 0); |
633 | g_assert_cmpuint (s1b, !=, 0); |
634 | g_assert_cmpuint (s2, !=, 0); |
635 | g_assert_cmpuint (s3, !=, 0); |
636 | |
637 | count_s1 = 0; |
638 | count_s1b = 0; |
639 | count_s2 = 0; |
640 | count_name_owner_changed = 0; |
641 | |
642 | /* |
643 | * Make c2 emit "Foo" - we should catch it twice |
644 | * |
645 | * Note that there is no way to be sure that the signal subscriptions |
646 | * on c1 are effective yet - for all we know, the AddMatch() messages |
647 | * could sit waiting in a buffer somewhere between this process and |
648 | * the message bus. And emitting signals on c2 (a completely other |
649 | * socket!) will not necessarily change this. |
650 | * |
651 | * To ensure this is not the case, do a synchronous call on c1. |
652 | */ |
653 | result = g_dbus_connection_call_sync (connection: c1, |
654 | bus_name: "org.freedesktop.DBus" , /* bus name */ |
655 | object_path: "/org/freedesktop/DBus" , /* object path */ |
656 | interface_name: "org.freedesktop.DBus" , /* interface name */ |
657 | method_name: "GetId" , /* method name */ |
658 | NULL, /* parameters */ |
659 | NULL, /* return type */ |
660 | flags: G_DBUS_CALL_FLAGS_NONE, |
661 | timeout_msec: -1, |
662 | NULL, |
663 | error: &error); |
664 | g_assert_no_error (error); |
665 | g_assert_nonnull (result); |
666 | g_variant_unref (value: result); |
667 | |
668 | /* |
669 | * Bring up two other connections |
670 | */ |
671 | c2 = _g_bus_get_priv (bus_type: G_BUS_TYPE_SESSION, NULL, NULL); |
672 | g_assert_nonnull (c2); |
673 | g_assert_false (g_dbus_connection_is_closed (c2)); |
674 | g_assert_cmpstr (g_dbus_connection_get_unique_name (c2), ==, ":1.2" ); |
675 | c3 = _g_bus_get_priv (bus_type: G_BUS_TYPE_SESSION, NULL, NULL); |
676 | g_assert_nonnull (c3); |
677 | g_assert_false (g_dbus_connection_is_closed (c3)); |
678 | g_assert_cmpstr (g_dbus_connection_get_unique_name (c3), ==, ":1.3" ); |
679 | |
680 | /* now, emit the signal on c2 */ |
681 | ret = g_dbus_connection_emit_signal (connection: c2, |
682 | NULL, /* destination bus name */ |
683 | object_path: "/org/gtk/GDBus/ExampleInterface" , |
684 | interface_name: "org.gtk.GDBus.ExampleInterface" , |
685 | signal_name: "Foo" , |
686 | NULL, |
687 | error: &error); |
688 | g_assert_no_error (error); |
689 | g_assert_true (ret); |
690 | while (!(count_s1 >= 1 && count_s2 >= 1)) |
691 | g_main_loop_run (loop); |
692 | g_assert_cmpint (count_s1, ==, 1); |
693 | g_assert_cmpint (count_s2, ==, 1); |
694 | |
695 | /* |
696 | * Make c3 emit "Foo" - we should catch it only once |
697 | */ |
698 | ret = g_dbus_connection_emit_signal (connection: c3, |
699 | NULL, /* destination bus name */ |
700 | object_path: "/org/gtk/GDBus/ExampleInterface" , |
701 | interface_name: "org.gtk.GDBus.ExampleInterface" , |
702 | signal_name: "Foo" , |
703 | NULL, |
704 | error: &error); |
705 | g_assert_no_error (error); |
706 | g_assert_true (ret); |
707 | while (!(count_s1 == 1 && count_s2 == 2)) |
708 | g_main_loop_run (loop); |
709 | g_assert_cmpint (count_s1, ==, 1); |
710 | g_assert_cmpint (count_s2, ==, 2); |
711 | |
712 | /* |
713 | * Also to check the total amount of NameOwnerChanged signals - use a 5 second ceiling |
714 | * to avoid spinning forever |
715 | */ |
716 | quit_mainloop_fired = FALSE; |
717 | quit_mainloop_id = g_timeout_add (interval: 30000, function: test_connection_quit_mainloop, data: &quit_mainloop_fired); |
718 | while (count_name_owner_changed < 2 && !quit_mainloop_fired) |
719 | g_main_loop_run (loop); |
720 | g_source_remove (tag: quit_mainloop_id); |
721 | g_assert_cmpint (count_s1, ==, 1); |
722 | g_assert_cmpint (count_s2, ==, 2); |
723 | g_assert_cmpint (count_name_owner_changed, ==, 2); |
724 | |
725 | g_dbus_connection_signal_unsubscribe (connection: c1, subscription_id: s1); |
726 | g_dbus_connection_signal_unsubscribe (connection: c1, subscription_id: s2); |
727 | g_dbus_connection_signal_unsubscribe (connection: c1, subscription_id: s3); |
728 | g_dbus_connection_signal_unsubscribe (connection: c1, subscription_id: s1b); |
729 | |
730 | g_object_unref (object: c1); |
731 | g_object_unref (object: c2); |
732 | g_object_unref (object: c3); |
733 | |
734 | session_bus_down (); |
735 | } |
736 | |
737 | static void |
738 | test_match_rule (GDBusConnection *connection, |
739 | GDBusSignalFlags flags, |
740 | gchar *arg0_rule, |
741 | gchar *arg0, |
742 | gboolean should_match) |
743 | { |
744 | guint subscription_ids[2]; |
745 | gint emissions = 0; |
746 | gint matches = 0; |
747 | GError *error = NULL; |
748 | |
749 | subscription_ids[0] = g_dbus_connection_signal_subscribe (connection, |
750 | NULL, interface_name: "org.gtk.ExampleInterface" , member: "Foo" , object_path: "/" , |
751 | NULL, |
752 | flags: G_DBUS_SIGNAL_FLAGS_NONE, |
753 | callback: test_connection_signal_handler, |
754 | user_data: &emissions, NULL); |
755 | subscription_ids[1] = g_dbus_connection_signal_subscribe (connection, |
756 | NULL, interface_name: "org.gtk.ExampleInterface" , member: "Foo" , object_path: "/" , |
757 | arg0: arg0_rule, |
758 | flags, |
759 | callback: test_connection_signal_handler, |
760 | user_data: &matches, NULL); |
761 | g_assert_cmpint (subscription_ids[0], !=, 0); |
762 | g_assert_cmpint (subscription_ids[1], !=, 0); |
763 | |
764 | g_dbus_connection_emit_signal (connection, |
765 | NULL, object_path: "/" , interface_name: "org.gtk.ExampleInterface" , |
766 | signal_name: "Foo" , parameters: g_variant_new (format_string: "(s)" , arg0), |
767 | error: &error); |
768 | g_assert_no_error (error); |
769 | |
770 | /* synchronously ping a non-existent method to make sure the signals are dispatched */ |
771 | g_dbus_connection_call_sync (connection, bus_name: "org.gtk.ExampleInterface" , object_path: "/" , interface_name: "org.gtk.ExampleInterface" , |
772 | method_name: "Bar" , parameters: g_variant_new (format_string: "()" ), G_VARIANT_TYPE_UNIT, flags: G_DBUS_CALL_FLAGS_NONE, |
773 | timeout_msec: -1, NULL, NULL); |
774 | |
775 | while (g_main_context_iteration (NULL, FALSE)) |
776 | ; |
777 | |
778 | g_assert_cmpint (emissions, ==, 1); |
779 | g_assert_cmpint (matches, ==, should_match ? 1 : 0); |
780 | |
781 | g_dbus_connection_signal_unsubscribe (connection, subscription_id: subscription_ids[0]); |
782 | g_dbus_connection_signal_unsubscribe (connection, subscription_id: subscription_ids[1]); |
783 | } |
784 | |
785 | static void |
786 | test_connection_signal_match_rules (void) |
787 | { |
788 | GDBusConnection *con; |
789 | |
790 | session_bus_up (); |
791 | con = g_bus_get_sync (bus_type: G_BUS_TYPE_SESSION, NULL, NULL); |
792 | |
793 | test_match_rule (connection: con, flags: G_DBUS_SIGNAL_FLAGS_NONE, arg0_rule: "foo" , arg0: "foo" , TRUE); |
794 | test_match_rule (connection: con, flags: G_DBUS_SIGNAL_FLAGS_NONE, arg0_rule: "foo" , arg0: "bar" , FALSE); |
795 | |
796 | test_match_rule (connection: con, flags: G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_NAMESPACE, arg0_rule: "org.gtk" , arg0: "" , FALSE); |
797 | test_match_rule (connection: con, flags: G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_NAMESPACE, arg0_rule: "org.gtk" , arg0: "org" , FALSE); |
798 | test_match_rule (connection: con, flags: G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_NAMESPACE, arg0_rule: "org.gtk" , arg0: "org.gtk" , TRUE); |
799 | test_match_rule (connection: con, flags: G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_NAMESPACE, arg0_rule: "org.gtk" , arg0: "org.gtk.Example" , TRUE); |
800 | test_match_rule (connection: con, flags: G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_NAMESPACE, arg0_rule: "org.gtk" , arg0: "org.gtk+" , FALSE); |
801 | |
802 | test_match_rule (connection: con, flags: G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_PATH, arg0_rule: "/" , arg0: "/" , TRUE); |
803 | test_match_rule (connection: con, flags: G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_PATH, arg0_rule: "/" , arg0: "" , FALSE); |
804 | test_match_rule (connection: con, flags: G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_PATH, arg0_rule: "/org/gtk/Example" , arg0: "/org/gtk/Example" , TRUE); |
805 | test_match_rule (connection: con, flags: G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_PATH, arg0_rule: "/org/gtk/" , arg0: "/org/gtk/Example" , TRUE); |
806 | test_match_rule (connection: con, flags: G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_PATH, arg0_rule: "/org/gtk/Example" , arg0: "/org/gtk/" , TRUE); |
807 | test_match_rule (connection: con, flags: G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_PATH, arg0_rule: "/org/gtk/Example" , arg0: "/org/gtk" , FALSE); |
808 | test_match_rule (connection: con, flags: G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_PATH, arg0_rule: "/org/gtk+" , arg0: "/org/gtk" , FALSE); |
809 | |
810 | g_object_unref (object: con); |
811 | session_bus_down (); |
812 | } |
813 | |
814 | /* ---------------------------------------------------------------------------------------------------- */ |
815 | |
816 | /* Accessed both from the test code and the filter function (in a worker thread) |
817 | * so all accesses must be atomic. */ |
818 | typedef struct |
819 | { |
820 | GAsyncQueue *incoming_queue; /* (element-type GDBusMessage) */ |
821 | guint num_outgoing; /* (atomic) */ |
822 | } FilterData; |
823 | |
824 | /* Runs in a worker thread. */ |
825 | static GDBusMessage * |
826 | filter_func (GDBusConnection *connection, |
827 | GDBusMessage *message, |
828 | gboolean incoming, |
829 | gpointer user_data) |
830 | { |
831 | FilterData *data = user_data; |
832 | |
833 | if (incoming) |
834 | g_async_queue_push (queue: data->incoming_queue, g_object_ref (message)); |
835 | else |
836 | g_atomic_int_inc (&data->num_outgoing); |
837 | |
838 | return message; |
839 | } |
840 | |
841 | static void |
842 | wait_for_filtered_reply (GAsyncQueue *incoming_queue, |
843 | guint32 expected_serial) |
844 | { |
845 | GDBusMessage *popped_message = NULL; |
846 | |
847 | while ((popped_message = g_async_queue_pop (queue: incoming_queue)) != NULL) |
848 | { |
849 | guint32 reply_serial = g_dbus_message_get_reply_serial (message: popped_message); |
850 | g_object_unref (object: popped_message); |
851 | if (reply_serial == expected_serial) |
852 | return; |
853 | } |
854 | |
855 | g_assert_not_reached (); |
856 | } |
857 | |
858 | typedef struct |
859 | { |
860 | gboolean alter_incoming; |
861 | gboolean alter_outgoing; |
862 | } FilterEffects; |
863 | |
864 | /* Runs in a worker thread. */ |
865 | static GDBusMessage * |
866 | other_filter_func (GDBusConnection *connection, |
867 | GDBusMessage *message, |
868 | gboolean incoming, |
869 | gpointer user_data) |
870 | { |
871 | const FilterEffects *effects = user_data; |
872 | GDBusMessage *ret; |
873 | gboolean alter; |
874 | |
875 | if (incoming) |
876 | alter = effects->alter_incoming; |
877 | else |
878 | alter = effects->alter_outgoing; |
879 | |
880 | if (alter) |
881 | { |
882 | GDBusMessage *copy; |
883 | GVariant *body; |
884 | gchar *s; |
885 | gchar *s2; |
886 | |
887 | copy = g_dbus_message_copy (message, NULL); |
888 | g_object_unref (object: message); |
889 | |
890 | body = g_dbus_message_get_body (message: copy); |
891 | g_variant_get (value: body, format_string: "(s)" , &s); |
892 | s2 = g_strdup_printf (format: "MOD: %s" , s); |
893 | g_dbus_message_set_body (message: copy, body: g_variant_new (format_string: "(s)" , s2)); |
894 | g_free (mem: s2); |
895 | g_free (mem: s); |
896 | |
897 | ret = copy; |
898 | } |
899 | else |
900 | { |
901 | ret = message; |
902 | } |
903 | |
904 | return ret; |
905 | } |
906 | |
907 | static void |
908 | test_connection_filter_name_owner_changed_signal_handler (GDBusConnection *connection, |
909 | const gchar *sender_name, |
910 | const gchar *object_path, |
911 | const gchar *interface_name, |
912 | const gchar *signal_name, |
913 | GVariant *parameters, |
914 | gpointer user_data) |
915 | { |
916 | const gchar *name; |
917 | const gchar *old_owner; |
918 | const gchar *new_owner; |
919 | |
920 | g_variant_get (value: parameters, |
921 | format_string: "(&s&s&s)" , |
922 | &name, |
923 | &old_owner, |
924 | &new_owner); |
925 | |
926 | if (g_strcmp0 (str1: name, str2: "com.example.TestService" ) == 0 && strlen (s: new_owner) > 0) |
927 | { |
928 | g_main_loop_quit (loop); |
929 | } |
930 | } |
931 | |
932 | static gboolean |
933 | test_connection_filter_on_timeout (gpointer user_data) |
934 | { |
935 | g_printerr (format: "Timeout waiting 30 sec on service\n" ); |
936 | g_assert_not_reached (); |
937 | return G_SOURCE_REMOVE; |
938 | } |
939 | |
940 | static void |
941 | test_connection_filter (void) |
942 | { |
943 | GDBusConnection *c; |
944 | FilterData data = { NULL, 0 }; |
945 | GDBusMessage *m; |
946 | GDBusMessage *m2; |
947 | GDBusMessage *r; |
948 | GError *error; |
949 | guint filter_id; |
950 | guint timeout_mainloop_id; |
951 | guint signal_handler_id; |
952 | FilterEffects effects; |
953 | GVariant *result; |
954 | const gchar *s; |
955 | guint32 serial_temp; |
956 | |
957 | session_bus_up (); |
958 | |
959 | error = NULL; |
960 | c = g_bus_get_sync (bus_type: G_BUS_TYPE_SESSION, NULL, error: &error); |
961 | g_assert_no_error (error); |
962 | g_assert_nonnull (c); |
963 | |
964 | data.incoming_queue = g_async_queue_new_full (item_free_func: g_object_unref); |
965 | data.num_outgoing = 0; |
966 | filter_id = g_dbus_connection_add_filter (connection: c, |
967 | filter_function: filter_func, |
968 | user_data: &data, |
969 | NULL); |
970 | |
971 | m = g_dbus_message_new_method_call (name: "org.freedesktop.DBus" , /* name */ |
972 | path: "/org/freedesktop/DBus" , /* path */ |
973 | interface_: "org.freedesktop.DBus" , /* interface */ |
974 | method: "GetNameOwner" ); |
975 | g_dbus_message_set_body (message: m, body: g_variant_new (format_string: "(s)" , "org.freedesktop.DBus" )); |
976 | error = NULL; |
977 | g_dbus_connection_send_message (connection: c, message: m, flags: G_DBUS_SEND_MESSAGE_FLAGS_NONE, out_serial: &serial_temp, error: &error); |
978 | g_assert_no_error (error); |
979 | |
980 | wait_for_filtered_reply (incoming_queue: data.incoming_queue, expected_serial: serial_temp); |
981 | |
982 | m2 = g_dbus_message_copy (message: m, error: &error); |
983 | g_assert_no_error (error); |
984 | g_dbus_connection_send_message (connection: c, message: m2, flags: G_DBUS_SEND_MESSAGE_FLAGS_NONE, out_serial: &serial_temp, error: &error); |
985 | g_object_unref (object: m2); |
986 | g_assert_no_error (error); |
987 | |
988 | wait_for_filtered_reply (incoming_queue: data.incoming_queue, expected_serial: serial_temp); |
989 | |
990 | m2 = g_dbus_message_copy (message: m, error: &error); |
991 | g_assert_no_error (error); |
992 | g_dbus_message_set_serial (message: m2, serial: serial_temp); |
993 | /* lock the message to test PRESERVE_SERIAL flag. */ |
994 | g_dbus_message_lock (message: m2); |
995 | g_dbus_connection_send_message (connection: c, message: m2, flags: G_DBUS_SEND_MESSAGE_FLAGS_PRESERVE_SERIAL, out_serial: &serial_temp, error: &error); |
996 | g_object_unref (object: m2); |
997 | g_assert_no_error (error); |
998 | |
999 | wait_for_filtered_reply (incoming_queue: data.incoming_queue, expected_serial: serial_temp); |
1000 | |
1001 | m2 = g_dbus_message_copy (message: m, error: &error); |
1002 | g_assert_no_error (error); |
1003 | r = g_dbus_connection_send_message_with_reply_sync (connection: c, |
1004 | message: m2, |
1005 | flags: G_DBUS_SEND_MESSAGE_FLAGS_NONE, |
1006 | timeout_msec: -1, |
1007 | out_serial: &serial_temp, |
1008 | NULL, /* GCancellable */ |
1009 | error: &error); |
1010 | g_object_unref (object: m2); |
1011 | g_assert_no_error (error); |
1012 | g_assert_nonnull (r); |
1013 | g_object_unref (object: r); |
1014 | |
1015 | wait_for_filtered_reply (incoming_queue: data.incoming_queue, expected_serial: serial_temp); |
1016 | g_assert_cmpint (g_async_queue_length (data.incoming_queue), ==, 0); |
1017 | |
1018 | g_dbus_connection_remove_filter (connection: c, filter_id); |
1019 | |
1020 | m2 = g_dbus_message_copy (message: m, error: &error); |
1021 | g_assert_no_error (error); |
1022 | r = g_dbus_connection_send_message_with_reply_sync (connection: c, |
1023 | message: m2, |
1024 | flags: G_DBUS_SEND_MESSAGE_FLAGS_NONE, |
1025 | timeout_msec: -1, |
1026 | out_serial: &serial_temp, |
1027 | NULL, /* GCancellable */ |
1028 | error: &error); |
1029 | g_object_unref (object: m2); |
1030 | g_assert_no_error (error); |
1031 | g_assert_nonnull (r); |
1032 | g_object_unref (object: r); |
1033 | g_assert_cmpint (g_async_queue_length (data.incoming_queue), ==, 0); |
1034 | g_assert_cmpint (g_atomic_int_get (&data.num_outgoing), ==, 4); |
1035 | |
1036 | /* wait for service to be available */ |
1037 | signal_handler_id = g_dbus_connection_signal_subscribe (connection: c, |
1038 | sender: "org.freedesktop.DBus" , /* sender */ |
1039 | interface_name: "org.freedesktop.DBus" , |
1040 | member: "NameOwnerChanged" , |
1041 | object_path: "/org/freedesktop/DBus" , |
1042 | NULL, /* arg0 */ |
1043 | flags: G_DBUS_SIGNAL_FLAGS_NONE, |
1044 | callback: test_connection_filter_name_owner_changed_signal_handler, |
1045 | NULL, |
1046 | NULL); |
1047 | g_assert_cmpint (signal_handler_id, !=, 0); |
1048 | |
1049 | /* this is safe; testserver will exit once the bus goes away */ |
1050 | g_assert_true (g_spawn_command_line_async (g_test_get_filename (G_TEST_BUILT, "gdbus-testserver" , NULL), NULL)); |
1051 | |
1052 | timeout_mainloop_id = g_timeout_add (interval: 30000, function: test_connection_filter_on_timeout, NULL); |
1053 | g_main_loop_run (loop); |
1054 | g_source_remove (tag: timeout_mainloop_id); |
1055 | g_dbus_connection_signal_unsubscribe (connection: c, subscription_id: signal_handler_id); |
1056 | |
1057 | /* now test some combinations... */ |
1058 | filter_id = g_dbus_connection_add_filter (connection: c, |
1059 | filter_function: other_filter_func, |
1060 | user_data: &effects, |
1061 | NULL); |
1062 | /* -- */ |
1063 | effects.alter_incoming = FALSE; |
1064 | effects.alter_outgoing = FALSE; |
1065 | error = NULL; |
1066 | result = g_dbus_connection_call_sync (connection: c, |
1067 | bus_name: "com.example.TestService" , /* bus name */ |
1068 | object_path: "/com/example/TestObject" , /* object path */ |
1069 | interface_name: "com.example.Frob" , /* interface name */ |
1070 | method_name: "HelloWorld" , /* method name */ |
1071 | parameters: g_variant_new (format_string: "(s)" , "Cat" ), /* parameters */ |
1072 | G_VARIANT_TYPE ("(s)" ), /* return type */ |
1073 | flags: G_DBUS_CALL_FLAGS_NONE, |
1074 | timeout_msec: -1, |
1075 | NULL, |
1076 | error: &error); |
1077 | g_assert_no_error (error); |
1078 | g_variant_get (value: result, format_string: "(&s)" , &s); |
1079 | g_assert_cmpstr (s, ==, "You greeted me with 'Cat'. Thanks!" ); |
1080 | g_variant_unref (value: result); |
1081 | /* -- */ |
1082 | effects.alter_incoming = TRUE; |
1083 | effects.alter_outgoing = TRUE; |
1084 | error = NULL; |
1085 | result = g_dbus_connection_call_sync (connection: c, |
1086 | bus_name: "com.example.TestService" , /* bus name */ |
1087 | object_path: "/com/example/TestObject" , /* object path */ |
1088 | interface_name: "com.example.Frob" , /* interface name */ |
1089 | method_name: "HelloWorld" , /* method name */ |
1090 | parameters: g_variant_new (format_string: "(s)" , "Cat" ), /* parameters */ |
1091 | G_VARIANT_TYPE ("(s)" ), /* return type */ |
1092 | flags: G_DBUS_CALL_FLAGS_NONE, |
1093 | timeout_msec: -1, |
1094 | NULL, |
1095 | error: &error); |
1096 | g_assert_no_error (error); |
1097 | g_variant_get (value: result, format_string: "(&s)" , &s); |
1098 | g_assert_cmpstr (s, ==, "MOD: You greeted me with 'MOD: Cat'. Thanks!" ); |
1099 | g_variant_unref (value: result); |
1100 | |
1101 | |
1102 | g_dbus_connection_remove_filter (connection: c, filter_id); |
1103 | |
1104 | g_object_unref (object: c); |
1105 | g_object_unref (object: m); |
1106 | g_async_queue_unref (queue: data.incoming_queue); |
1107 | |
1108 | session_bus_down (); |
1109 | } |
1110 | |
1111 | /* ---------------------------------------------------------------------------------------------------- */ |
1112 | |
1113 | #define NUM_THREADS 50 |
1114 | |
1115 | static void |
1116 | send_bogus_message (GDBusConnection *c, guint32 *out_serial) |
1117 | { |
1118 | GDBusMessage *m; |
1119 | GError *error; |
1120 | |
1121 | m = g_dbus_message_new_method_call (name: "org.freedesktop.DBus" , /* name */ |
1122 | path: "/org/freedesktop/DBus" , /* path */ |
1123 | interface_: "org.freedesktop.DBus" , /* interface */ |
1124 | method: "GetNameOwner" ); |
1125 | g_dbus_message_set_body (message: m, body: g_variant_new (format_string: "(s)" , "org.freedesktop.DBus" )); |
1126 | error = NULL; |
1127 | g_dbus_connection_send_message (connection: c, message: m, flags: G_DBUS_SEND_MESSAGE_FLAGS_NONE, out_serial, error: &error); |
1128 | g_assert_no_error (error); |
1129 | g_object_unref (object: m); |
1130 | } |
1131 | |
1132 | #define SLEEP_USEC (100 * 1000) |
1133 | |
1134 | static gpointer |
1135 | serials_thread_func (GDBusConnection *c) |
1136 | { |
1137 | guint32 message_serial; |
1138 | guint i; |
1139 | |
1140 | /* No calls on this thread yet */ |
1141 | g_assert_cmpint (g_dbus_connection_get_last_serial(c), ==, 0); |
1142 | |
1143 | /* Send a bogus message and store its serial */ |
1144 | message_serial = 0; |
1145 | send_bogus_message (c, out_serial: &message_serial); |
1146 | |
1147 | /* Give it some time to actually send the message out. 10 seconds |
1148 | * should be plenty, even on slow machines. */ |
1149 | for (i = 0; i < 10 * G_USEC_PER_SEC / SLEEP_USEC; i++) |
1150 | { |
1151 | if (g_dbus_connection_get_last_serial(connection: c) != 0) |
1152 | break; |
1153 | |
1154 | g_usleep (SLEEP_USEC); |
1155 | } |
1156 | |
1157 | g_assert_cmpint (g_dbus_connection_get_last_serial(c), !=, 0); |
1158 | g_assert_cmpint (g_dbus_connection_get_last_serial(c), ==, message_serial); |
1159 | |
1160 | return NULL; |
1161 | } |
1162 | |
1163 | static void |
1164 | test_connection_serials (void) |
1165 | { |
1166 | GDBusConnection *c; |
1167 | GError *error; |
1168 | GThread *pool[NUM_THREADS]; |
1169 | int i; |
1170 | |
1171 | session_bus_up (); |
1172 | |
1173 | error = NULL; |
1174 | c = g_bus_get_sync (bus_type: G_BUS_TYPE_SESSION, NULL, error: &error); |
1175 | g_assert_no_error (error); |
1176 | g_assert_nonnull (c); |
1177 | |
1178 | /* Status after initialization */ |
1179 | g_assert_cmpint (g_dbus_connection_get_last_serial (c), ==, 1); |
1180 | |
1181 | /* Send a bogus message */ |
1182 | send_bogus_message (c, NULL); |
1183 | g_assert_cmpint (g_dbus_connection_get_last_serial (c), ==, 2); |
1184 | |
1185 | /* Start the threads */ |
1186 | for (i = 0; i < NUM_THREADS; i++) |
1187 | pool[i] = g_thread_new (NULL, func: (GThreadFunc) serials_thread_func, data: c); |
1188 | |
1189 | /* Wait until threads are finished */ |
1190 | for (i = 0; i < NUM_THREADS; i++) |
1191 | g_thread_join (thread: pool[i]); |
1192 | |
1193 | /* No calls in between on this thread, should be the last value */ |
1194 | g_assert_cmpint (g_dbus_connection_get_last_serial (c), ==, 2); |
1195 | |
1196 | send_bogus_message (c, NULL); |
1197 | |
1198 | /* All above calls + calls in threads */ |
1199 | g_assert_cmpint (g_dbus_connection_get_last_serial (c), ==, 3 + NUM_THREADS); |
1200 | |
1201 | g_object_unref (object: c); |
1202 | |
1203 | session_bus_down (); |
1204 | } |
1205 | |
1206 | /* ---------------------------------------------------------------------------------------------------- */ |
1207 | |
1208 | static void |
1209 | test_connection_basic (void) |
1210 | { |
1211 | GDBusConnection *connection; |
1212 | GError *error; |
1213 | GDBusCapabilityFlags flags; |
1214 | GDBusConnectionFlags connection_flags; |
1215 | gchar *guid; |
1216 | gchar *name; |
1217 | gboolean closed; |
1218 | gboolean exit_on_close; |
1219 | GIOStream *stream; |
1220 | GCredentials *credentials; |
1221 | |
1222 | session_bus_up (); |
1223 | |
1224 | error = NULL; |
1225 | connection = g_bus_get_sync (bus_type: G_BUS_TYPE_SESSION, NULL, error: &error); |
1226 | g_assert_no_error (error); |
1227 | g_assert_nonnull (connection); |
1228 | |
1229 | flags = g_dbus_connection_get_capabilities (connection); |
1230 | g_assert_true (flags == G_DBUS_CAPABILITY_FLAGS_NONE || |
1231 | flags == G_DBUS_CAPABILITY_FLAGS_UNIX_FD_PASSING); |
1232 | |
1233 | connection_flags = g_dbus_connection_get_flags (connection); |
1234 | g_assert_cmpint (connection_flags, ==, |
1235 | G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT | |
1236 | G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION); |
1237 | |
1238 | credentials = g_dbus_connection_get_peer_credentials (connection); |
1239 | g_assert_null (credentials); |
1240 | |
1241 | g_object_get (object: connection, |
1242 | first_property_name: "stream" , &stream, |
1243 | "guid" , &guid, |
1244 | "unique-name" , &name, |
1245 | "closed" , &closed, |
1246 | "exit-on-close" , &exit_on_close, |
1247 | "capabilities" , &flags, |
1248 | NULL); |
1249 | |
1250 | g_assert_true (G_IS_IO_STREAM (stream)); |
1251 | g_assert_true (g_dbus_is_guid (guid)); |
1252 | g_assert_true (g_dbus_is_unique_name (name)); |
1253 | g_assert_false (closed); |
1254 | g_assert_true (exit_on_close); |
1255 | g_assert_true (flags == G_DBUS_CAPABILITY_FLAGS_NONE || |
1256 | flags == G_DBUS_CAPABILITY_FLAGS_UNIX_FD_PASSING); |
1257 | g_object_unref (object: stream); |
1258 | g_free (mem: name); |
1259 | g_free (mem: guid); |
1260 | |
1261 | g_object_unref (object: connection); |
1262 | |
1263 | session_bus_down (); |
1264 | } |
1265 | |
1266 | /* ---------------------------------------------------------------------------------------------------- */ |
1267 | |
1268 | int |
1269 | main (int argc, |
1270 | char *argv[]) |
1271 | { |
1272 | int ret; |
1273 | |
1274 | g_test_init (argc: &argc, argv: &argv, NULL); |
1275 | |
1276 | /* all the tests rely on a shared main loop */ |
1277 | loop = g_main_loop_new (NULL, FALSE); |
1278 | |
1279 | g_test_dbus_unset (); |
1280 | |
1281 | /* gdbus cleanup is pretty racy due to worker threads, so always do this test first */ |
1282 | g_test_add_func (testpath: "/gdbus/connection/bus-failure" , test_func: test_connection_bus_failure); |
1283 | |
1284 | g_test_add_func (testpath: "/gdbus/connection/basic" , test_func: test_connection_basic); |
1285 | g_test_add_func (testpath: "/gdbus/connection/life-cycle" , test_func: test_connection_life_cycle); |
1286 | g_test_add_func (testpath: "/gdbus/connection/send" , test_func: test_connection_send); |
1287 | g_test_add_func (testpath: "/gdbus/connection/signals" , test_func: test_connection_signals); |
1288 | g_test_add_func (testpath: "/gdbus/connection/signal-match-rules" , test_func: test_connection_signal_match_rules); |
1289 | g_test_add_func (testpath: "/gdbus/connection/filter" , test_func: test_connection_filter); |
1290 | g_test_add_func (testpath: "/gdbus/connection/serials" , test_func: test_connection_serials); |
1291 | ret = g_test_run(); |
1292 | |
1293 | g_main_loop_unref (loop); |
1294 | return ret; |
1295 | } |
1296 | |