1 /* GLib testing framework examples and tests
3 * Copyright (C) 2008-2010 Red Hat, Inc.
4 * Copyright (C) 2021 Frederic Martinsons
6 * SPDX-License-Identifier: LGPL-2.1-or-later
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General
19 * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
21 * Author: David Zeuthen <davidz@redhat.com>
22 * Author: Frederic Martinsons <frederic.martinsons@gmail.com>
28 #include "gdbus-tests.h"
30 /* ---------------------------------------------------------------------------------------------------- */
31 /* Test that g_bus_own_name() works correctly */
32 /* ---------------------------------------------------------------------------------------------------- */
36 gboolean expect_null_connection;
37 guint num_bus_acquired;
41 GMainContext *main_context; /* (unowned) */
45 own_name_data_free_func (OwnNameData *data)
47 data->num_free_func++;
48 g_main_context_wakeup (data->main_context);
52 bus_acquired_handler (GDBusConnection *connection,
56 OwnNameData *data = user_data;
57 g_dbus_connection_set_exit_on_close (connection, FALSE);
58 data->num_bus_acquired += 1;
59 g_main_context_wakeup (data->main_context);
63 name_acquired_handler (GDBusConnection *connection,
67 OwnNameData *data = user_data;
68 data->num_acquired += 1;
69 g_main_context_wakeup (data->main_context);
73 name_lost_handler (GDBusConnection *connection,
77 OwnNameData *data = user_data;
78 if (data->expect_null_connection)
80 g_assert (connection == NULL);
84 g_assert (connection != NULL);
85 g_dbus_connection_set_exit_on_close (connection, FALSE);
88 g_main_context_wakeup (data->main_context);
92 test_bus_own_name (void)
101 gboolean name_has_owner_reply;
104 GMainContext *main_context = NULL; /* use the global default for now */
107 name = "org.gtk.GDBus.Name1";
110 * First check that name_lost_handler() is invoked if there is no bus.
112 * Also make sure name_lost_handler() isn't invoked when unowning the name.
114 data.num_bus_acquired = 0;
115 data.num_free_func = 0;
116 data.num_acquired = 0;
118 data.expect_null_connection = TRUE;
119 data.main_context = main_context;
120 id = g_bus_own_name (G_BUS_TYPE_SESSION,
122 G_BUS_NAME_OWNER_FLAGS_NONE,
123 bus_acquired_handler,
124 name_acquired_handler,
127 (GDestroyNotify) own_name_data_free_func);
128 g_assert_cmpint (data.num_bus_acquired, ==, 0);
129 g_assert_cmpint (data.num_acquired, ==, 0);
130 g_assert_cmpint (data.num_lost, ==, 0);
132 while (data.num_lost < 1)
133 g_main_context_iteration (main_context, TRUE);
135 g_assert_cmpint (data.num_bus_acquired, ==, 0);
136 g_assert_cmpint (data.num_acquired, ==, 0);
137 g_assert_cmpint (data.num_lost, ==, 1);
138 g_bus_unown_name (id);
139 g_assert_cmpint (data.num_acquired, ==, 0);
140 g_assert_cmpint (data.num_lost, ==, 1);
141 g_assert_cmpint (data.num_free_func, ==, 1);
144 * Bring up a bus, then own a name and check bus_acquired_handler() then name_acquired_handler() is invoked.
147 data.num_bus_acquired = 0;
148 data.num_acquired = 0;
150 data.expect_null_connection = FALSE;
151 id = g_bus_own_name (G_BUS_TYPE_SESSION,
153 G_BUS_NAME_OWNER_FLAGS_NONE,
154 bus_acquired_handler,
155 name_acquired_handler,
158 (GDestroyNotify) own_name_data_free_func);
159 g_assert_cmpint (data.num_bus_acquired, ==, 0);
160 g_assert_cmpint (data.num_acquired, ==, 0);
161 g_assert_cmpint (data.num_lost, ==, 0);
163 while (data.num_bus_acquired < 1)
164 g_main_context_iteration (main_context, TRUE);
166 g_assert_cmpint (data.num_bus_acquired, ==, 1);
167 g_assert_cmpint (data.num_acquired, ==, 0);
168 g_assert_cmpint (data.num_lost, ==, 0);
170 while (data.num_acquired < 1)
171 g_main_context_iteration (main_context, TRUE);
173 g_assert_cmpint (data.num_bus_acquired, ==, 1);
174 g_assert_cmpint (data.num_acquired, ==, 1);
175 g_assert_cmpint (data.num_lost, ==, 0);
178 * Check that the name was actually acquired.
180 c = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
181 g_assert (c != NULL);
182 g_assert (!g_dbus_connection_is_closed (c));
183 result = g_dbus_connection_call_sync (c,
184 "org.freedesktop.DBus", /* bus name */
185 "/org/freedesktop/DBus", /* object path */
186 "org.freedesktop.DBus", /* interface name */
187 "NameHasOwner", /* method name */
188 g_variant_new ("(s)", name),
189 G_VARIANT_TYPE ("(b)"),
190 G_DBUS_CALL_FLAGS_NONE,
194 g_assert_no_error (error);
195 g_assert (result != NULL);
196 g_variant_get (result, "(b)", &name_has_owner_reply);
197 g_assert (name_has_owner_reply);
198 g_variant_unref (result);
201 * Stop owning the name - this should invoke our free func
203 g_bus_unown_name (id);
204 while (data.num_free_func < 2)
205 g_main_context_iteration (main_context, TRUE);
206 g_assert_cmpint (data.num_free_func, ==, 2);
209 * Check that the name was actually released.
211 result = g_dbus_connection_call_sync (c,
212 "org.freedesktop.DBus", /* bus name */
213 "/org/freedesktop/DBus", /* object path */
214 "org.freedesktop.DBus", /* interface name */
215 "NameHasOwner", /* method name */
216 g_variant_new ("(s)", name),
217 G_VARIANT_TYPE ("(b)"),
218 G_DBUS_CALL_FLAGS_NONE,
222 g_assert_no_error (error);
223 g_assert (result != NULL);
224 g_variant_get (result, "(b)", &name_has_owner_reply);
225 g_assert (!name_has_owner_reply);
226 g_variant_unref (result);
228 /* Now try owning the name and then immediately decide to unown the name */
229 g_assert_cmpint (data.num_bus_acquired, ==, 1);
230 g_assert_cmpint (data.num_acquired, ==, 1);
231 g_assert_cmpint (data.num_lost, ==, 0);
232 g_assert_cmpint (data.num_free_func, ==, 2);
233 id = g_bus_own_name (G_BUS_TYPE_SESSION,
235 G_BUS_NAME_OWNER_FLAGS_NONE,
236 bus_acquired_handler,
237 name_acquired_handler,
240 (GDestroyNotify) own_name_data_free_func);
241 g_assert_cmpint (data.num_bus_acquired, ==, 1);
242 g_assert_cmpint (data.num_acquired, ==, 1);
243 g_assert_cmpint (data.num_lost, ==, 0);
244 g_assert_cmpint (data.num_free_func, ==, 2);
245 g_bus_unown_name (id);
246 g_assert_cmpint (data.num_bus_acquired, ==, 1);
247 g_assert_cmpint (data.num_acquired, ==, 1);
248 g_assert_cmpint (data.num_lost, ==, 0);
249 g_assert_cmpint (data.num_free_func, ==, 2);
251 /* the GDestroyNotify is called in idle because the bus is acquired in idle */
252 while (data.num_free_func < 3)
253 g_main_context_iteration (main_context, TRUE);
255 g_assert_cmpint (data.num_free_func, ==, 3);
258 * Own the name again.
260 data.num_bus_acquired = 0;
261 data.num_acquired = 0;
263 data.expect_null_connection = FALSE;
264 id = g_bus_own_name_with_closures (G_BUS_TYPE_SESSION,
266 G_BUS_NAME_OWNER_FLAGS_NONE,
267 g_cclosure_new (G_CALLBACK (bus_acquired_handler),
270 g_cclosure_new (G_CALLBACK (name_acquired_handler),
273 g_cclosure_new (G_CALLBACK (name_lost_handler),
275 (GClosureNotify) own_name_data_free_func));
276 g_assert_cmpint (data.num_bus_acquired, ==, 0);
277 g_assert_cmpint (data.num_acquired, ==, 0);
278 g_assert_cmpint (data.num_lost, ==, 0);
280 while (data.num_bus_acquired < 1)
281 g_main_context_iteration (main_context, TRUE);
283 g_assert_cmpint (data.num_bus_acquired, ==, 1);
284 g_assert_cmpint (data.num_acquired, ==, 0);
285 g_assert_cmpint (data.num_lost, ==, 0);
287 while (data.num_acquired < 1)
288 g_main_context_iteration (main_context, TRUE);
290 g_assert_cmpint (data.num_bus_acquired, ==, 1);
291 g_assert_cmpint (data.num_acquired, ==, 1);
292 g_assert_cmpint (data.num_lost, ==, 0);
295 * Try owning the name with another object on the same connection - this should
296 * fail because we already own the name.
298 data2.num_free_func = 0;
299 data2.num_bus_acquired = 0;
300 data2.num_acquired = 0;
302 data2.expect_null_connection = FALSE;
303 data2.main_context = main_context;
304 id2 = g_bus_own_name (G_BUS_TYPE_SESSION,
306 G_BUS_NAME_OWNER_FLAGS_NONE,
307 bus_acquired_handler,
308 name_acquired_handler,
311 (GDestroyNotify) own_name_data_free_func);
312 g_assert_cmpint (data2.num_bus_acquired, ==, 0);
313 g_assert_cmpint (data2.num_acquired, ==, 0);
314 g_assert_cmpint (data2.num_lost, ==, 0);
316 while (data2.num_bus_acquired < 1)
317 g_main_context_iteration (main_context, TRUE);
319 g_assert_cmpint (data2.num_bus_acquired, ==, 1);
320 g_assert_cmpint (data2.num_acquired, ==, 0);
321 g_assert_cmpint (data2.num_lost, ==, 0);
323 while (data2.num_lost < 1)
324 g_main_context_iteration (main_context, TRUE);
326 g_assert_cmpint (data2.num_bus_acquired, ==, 1);
327 g_assert_cmpint (data2.num_acquired, ==, 0);
328 g_assert_cmpint (data2.num_lost, ==, 1);
330 g_bus_unown_name (id2);
331 while (data2.num_free_func < 1)
332 g_main_context_iteration (main_context, TRUE);
334 g_assert_cmpint (data2.num_bus_acquired, ==, 1);
335 g_assert_cmpint (data2.num_acquired, ==, 0);
336 g_assert_cmpint (data2.num_lost, ==, 1);
337 g_assert_cmpint (data2.num_free_func, ==, 1);
340 * Create a secondary (e.g. private) connection and try owning the name on that
341 * connection. This should fail both with and without _REPLACE because we
342 * didn't specify ALLOW_REPLACEMENT.
344 c2 = _g_bus_get_priv (G_BUS_TYPE_SESSION, NULL, NULL);
345 g_assert (c2 != NULL);
346 g_assert (!g_dbus_connection_is_closed (c2));
347 /* first without _REPLACE */
348 data2.num_bus_acquired = 0;
349 data2.num_acquired = 0;
351 data2.expect_null_connection = FALSE;
352 data2.num_free_func = 0;
353 id2 = g_bus_own_name_on_connection (c2,
355 G_BUS_NAME_OWNER_FLAGS_NONE,
356 name_acquired_handler,
359 (GDestroyNotify) own_name_data_free_func);
360 g_assert_cmpint (data2.num_bus_acquired, ==, 0);
361 g_assert_cmpint (data2.num_acquired, ==, 0);
362 g_assert_cmpint (data2.num_lost, ==, 0);
364 while (data2.num_lost < 1)
365 g_main_context_iteration (main_context, TRUE);
367 g_assert_cmpint (data2.num_bus_acquired, ==, 0);
368 g_assert_cmpint (data2.num_acquired, ==, 0);
369 g_assert_cmpint (data2.num_lost, ==, 1);
371 g_bus_unown_name (id2);
372 while (data2.num_free_func < 1)
373 g_main_context_iteration (main_context, TRUE);
375 g_assert_cmpint (data2.num_bus_acquired, ==, 0);
376 g_assert_cmpint (data2.num_acquired, ==, 0);
377 g_assert_cmpint (data2.num_lost, ==, 1);
378 g_assert_cmpint (data2.num_free_func, ==, 1);
379 /* then with _REPLACE */
380 data2.num_bus_acquired = 0;
381 data2.num_acquired = 0;
383 data2.expect_null_connection = FALSE;
384 data2.num_free_func = 0;
385 id2 = g_bus_own_name_on_connection (c2,
387 G_BUS_NAME_OWNER_FLAGS_REPLACE,
388 name_acquired_handler,
391 (GDestroyNotify) own_name_data_free_func);
392 g_assert_cmpint (data2.num_bus_acquired, ==, 0);
393 g_assert_cmpint (data2.num_acquired, ==, 0);
394 g_assert_cmpint (data2.num_lost, ==, 0);
396 while (data2.num_lost < 1)
397 g_main_context_iteration (main_context, TRUE);
399 g_assert_cmpint (data2.num_bus_acquired, ==, 0);
400 g_assert_cmpint (data2.num_acquired, ==, 0);
401 g_assert_cmpint (data2.num_lost, ==, 1);
403 g_bus_unown_name (id2);
404 while (data2.num_free_func < 1)
405 g_main_context_iteration (main_context, TRUE);
407 g_assert_cmpint (data2.num_bus_acquired, ==, 0);
408 g_assert_cmpint (data2.num_acquired, ==, 0);
409 g_assert_cmpint (data2.num_lost, ==, 1);
410 g_assert_cmpint (data2.num_free_func, ==, 1);
413 * Stop owning the name and grab it again with _ALLOW_REPLACEMENT.
415 data.expect_null_connection = FALSE;
416 g_bus_unown_name (id);
417 while (data.num_bus_acquired < 1 || data.num_free_func < 4)
418 g_main_context_iteration (main_context, TRUE);
420 g_assert_cmpint (data.num_bus_acquired, ==, 1);
421 g_assert_cmpint (data.num_acquired, ==, 1);
422 g_assert_cmpint (data.num_free_func, ==, 4);
424 data.num_bus_acquired = 0;
425 data.num_acquired = 0;
427 data.expect_null_connection = FALSE;
428 id = g_bus_own_name (G_BUS_TYPE_SESSION,
430 G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT,
431 bus_acquired_handler,
432 name_acquired_handler,
435 (GDestroyNotify) own_name_data_free_func);
436 g_assert_cmpint (data.num_bus_acquired, ==, 0);
437 g_assert_cmpint (data.num_acquired, ==, 0);
438 g_assert_cmpint (data.num_lost, ==, 0);
440 while (data.num_bus_acquired < 1)
441 g_main_context_iteration (main_context, TRUE);
443 g_assert_cmpint (data.num_bus_acquired, ==, 1);
444 g_assert_cmpint (data.num_acquired, ==, 0);
445 g_assert_cmpint (data.num_lost, ==, 0);
447 while (data.num_acquired < 1)
448 g_main_context_iteration (main_context, TRUE);
450 g_assert_cmpint (data.num_bus_acquired, ==, 1);
451 g_assert_cmpint (data.num_acquired, ==, 1);
452 g_assert_cmpint (data.num_lost, ==, 0);
455 * Now try to grab the name from the secondary connection.
458 /* first without _REPLACE - this won't make us acquire the name */
459 data2.num_bus_acquired = 0;
460 data2.num_acquired = 0;
462 data2.expect_null_connection = FALSE;
463 data2.num_free_func = 0;
464 id2 = g_bus_own_name_on_connection (c2,
466 G_BUS_NAME_OWNER_FLAGS_NONE,
467 name_acquired_handler,
470 (GDestroyNotify) own_name_data_free_func);
471 g_assert_cmpint (data2.num_bus_acquired, ==, 0);
472 g_assert_cmpint (data2.num_acquired, ==, 0);
473 g_assert_cmpint (data2.num_lost, ==, 0);
475 while (data2.num_lost < 1)
476 g_main_context_iteration (main_context, TRUE);
478 g_assert_cmpint (data2.num_bus_acquired, ==, 0);
479 g_assert_cmpint (data2.num_acquired, ==, 0);
480 g_assert_cmpint (data2.num_lost, ==, 1);
482 g_bus_unown_name (id2);
483 while (data2.num_free_func < 1)
484 g_main_context_iteration (main_context, TRUE);
486 g_assert_cmpint (data2.num_bus_acquired, ==, 0);
487 g_assert_cmpint (data2.num_acquired, ==, 0);
488 g_assert_cmpint (data2.num_lost, ==, 1);
489 g_assert_cmpint (data2.num_free_func, ==, 1);
490 /* then with _REPLACE - here we should acquire the name - e.g. owner should lose it
491 * and owner2 should acquire it */
492 data2.num_bus_acquired = 0;
493 data2.num_acquired = 0;
495 data2.expect_null_connection = FALSE;
496 data2.num_free_func = 0;
497 id2 = g_bus_own_name_on_connection (c2,
499 G_BUS_NAME_OWNER_FLAGS_REPLACE,
500 name_acquired_handler,
503 (GDestroyNotify) own_name_data_free_func);
504 g_assert_cmpint (data.num_acquired, ==, 1);
505 g_assert_cmpint (data.num_lost, ==, 0);
506 g_assert_cmpint (data2.num_acquired, ==, 0);
507 g_assert_cmpint (data2.num_lost, ==, 0);
509 /* wait for handlers for both owner and owner2 to fire */
510 while (data.num_lost == 0 || data2.num_acquired == 0)
511 g_main_context_iteration (main_context, TRUE);
513 g_assert_cmpint (data.num_acquired, ==, 1);
514 g_assert_cmpint (data.num_lost, ==, 1);
515 g_assert_cmpint (data2.num_acquired, ==, 1);
516 g_assert_cmpint (data2.num_lost, ==, 0);
517 g_assert_cmpint (data2.num_bus_acquired, ==, 0);
519 /* ok, make owner2 release the name - then wait for owner to automagically reacquire it */
520 g_bus_unown_name (id2);
521 while (data.num_acquired < 2 || data2.num_free_func < 1)
522 g_main_context_iteration (main_context, TRUE);
524 g_assert_cmpint (data2.num_free_func, ==, 1);
525 g_assert_cmpint (data.num_acquired, ==, 2);
526 g_assert_cmpint (data.num_lost, ==, 1);
529 * Finally, nuke the bus and check name_lost_handler() is invoked.
532 data.expect_null_connection = TRUE;
534 while (data.num_lost != 2)
535 g_main_context_iteration (main_context, TRUE);
537 g_assert_cmpint (data.num_acquired, ==, 2);
538 g_assert_cmpint (data.num_lost, ==, 2);
540 g_bus_unown_name (id);
541 while (data.num_free_func < 5)
542 g_main_context_iteration (main_context, TRUE);
544 g_assert_cmpint (data.num_free_func, ==, 5);
552 /* ---------------------------------------------------------------------------------------------------- */
553 /* Test that g_bus_watch_name() works correctly */
554 /* ---------------------------------------------------------------------------------------------------- */
558 gboolean expect_null_connection;
564 GMainContext *main_context; /* (unowned), for the main test thread */
570 GDBusConnection *connection;
574 gboolean name_acquired;
576 gboolean unwatch_early;
579 GMainContext *thread_context; /* (unowned), only accessed from watcher_thread() */
580 } WatchNameThreadData;
583 watch_name_data_free_func (WatchNameData *data)
585 data->num_free_func++;
586 g_main_context_wakeup (data->main_context);
590 w_bus_acquired_handler (GDBusConnection *connection,
597 w_name_acquired_handler (GDBusConnection *connection,
601 OwnNameData *data = user_data;
602 data->num_acquired += 1;
603 g_main_context_wakeup (data->main_context);
607 w_name_lost_handler (GDBusConnection *connection,
611 OwnNameData *data = user_data;
613 g_main_context_wakeup (data->main_context);
617 name_appeared_handler (GDBusConnection *connection,
619 const gchar *name_owner,
622 WatchNameData *data = user_data;
624 if (data->expect_null_connection)
626 g_assert (connection == NULL);
630 g_assert (connection != NULL);
631 g_dbus_connection_set_exit_on_close (connection, FALSE);
633 data->num_appeared += 1;
634 g_main_context_wakeup (data->main_context);
638 name_vanished_handler (GDBusConnection *connection,
642 WatchNameData *data = user_data;
644 if (data->expect_null_connection)
646 g_assert (connection == NULL);
650 g_assert (connection != NULL);
651 g_dbus_connection_set_exit_on_close (connection, FALSE);
653 data->num_vanished += 1;
654 g_main_context_wakeup (data->main_context);
660 gboolean watch_with_closures;
661 gboolean existing_service;
664 static const WatchNameTest watch_no_closures_no_flags = {
665 .watcher_flags = G_BUS_NAME_WATCHER_FLAGS_NONE,
666 .watch_with_closures = FALSE,
667 .existing_service = FALSE
670 static const WatchNameTest watch_no_closures_flags_auto_start = {
671 .watcher_flags = G_BUS_NAME_WATCHER_FLAGS_AUTO_START,
672 .watch_with_closures = FALSE,
673 .existing_service = FALSE
676 static const WatchNameTest watch_no_closures_flags_auto_start_service_exist = {
677 .watcher_flags = G_BUS_NAME_WATCHER_FLAGS_AUTO_START,
678 .watch_with_closures = FALSE,
679 .existing_service = TRUE
682 static const WatchNameTest watch_closures_no_flags = {
683 .watcher_flags = G_BUS_NAME_WATCHER_FLAGS_NONE,
684 .watch_with_closures = TRUE,
685 .existing_service = FALSE
688 static const WatchNameTest watch_closures_flags_auto_start = {
689 .watcher_flags = G_BUS_NAME_WATCHER_FLAGS_AUTO_START,
690 .watch_with_closures = TRUE,
691 .existing_service = FALSE
695 stop_service (GDBusConnection *connection,
698 GError *error = NULL;
699 GDBusProxy *proxy = NULL;
700 GVariant *result = NULL;
701 GMainContext *main_context = NULL; /* use the global default for now */
703 data->num_vanished = 0;
705 proxy = g_dbus_proxy_new_sync (connection,
706 G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
708 "org.gtk.GDBus.FakeService",
709 "/org/gtk/GDBus/FakeService",
710 "org.gtk.GDBus.FakeService",
713 g_assert_no_error (error);
715 result = g_dbus_proxy_call_sync (proxy,
718 G_DBUS_CALL_FLAGS_NO_AUTO_START,
722 g_assert_no_error (error);
723 g_object_unref (proxy);
725 g_variant_unref (result);
726 while (data->num_vanished == 0)
727 g_main_context_iteration (main_context, TRUE);
731 test_bus_watch_name (gconstpointer d)
734 OwnNameData own_data;
737 GDBusConnection *connection;
738 const WatchNameTest *watch_name_test;
740 GMainContext *main_context = NULL; /* use the global default for now */
742 watch_name_test = (WatchNameTest *) d;
744 if (watch_name_test->existing_service)
746 name = "org.gtk.GDBus.FakeService";
750 name = "org.gtk.GDBus.Name1";
754 * First check that name_vanished_handler() is invoked if there is no bus.
756 * Also make sure name_vanished_handler() isn't invoked when unwatching the name.
758 data.num_free_func = 0;
759 data.num_appeared = 0;
760 data.num_vanished = 0;
761 data.expect_null_connection = TRUE;
762 data.main_context = main_context;
763 id = g_bus_watch_name (G_BUS_TYPE_SESSION,
765 watch_name_test->watcher_flags,
766 name_appeared_handler,
767 name_vanished_handler,
769 (GDestroyNotify) watch_name_data_free_func);
770 g_assert_cmpint (data.num_appeared, ==, 0);
771 g_assert_cmpint (data.num_vanished, ==, 0);
773 while (data.num_vanished < 1)
774 g_main_context_iteration (main_context, TRUE);
776 g_assert_cmpint (data.num_appeared, ==, 0);
777 g_assert_cmpint (data.num_vanished, ==, 1);
779 g_bus_unwatch_name (id);
780 while (data.num_free_func < 1)
781 g_main_context_iteration (main_context, TRUE);
783 g_assert_cmpint (data.num_appeared, ==, 0);
784 g_assert_cmpint (data.num_vanished, ==, 1);
785 g_assert_cmpint (data.num_free_func, ==, 1);
786 data.num_free_func = 0;
789 * Now bring up a bus, own a name, and then start watching it.
793 own_data.num_free_func = 0;
794 own_data.num_acquired = 0;
795 own_data.num_lost = 0;
796 data.expect_null_connection = FALSE;
797 own_data.main_context = main_context;
798 owner_id = g_bus_own_name (G_BUS_TYPE_SESSION,
800 G_BUS_NAME_OWNER_FLAGS_NONE,
801 w_bus_acquired_handler,
802 w_name_acquired_handler,
805 (GDestroyNotify) own_name_data_free_func);
807 while (own_data.num_acquired < 1)
808 g_main_context_iteration (main_context, TRUE);
810 g_assert_cmpint (own_data.num_acquired, ==, 1);
811 g_assert_cmpint (own_data.num_lost, ==, 0);
813 connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
814 g_assert (connection != NULL);
816 /* now watch the name */
817 data.num_appeared = 0;
818 data.num_vanished = 0;
819 if (watch_name_test->watch_with_closures)
821 id = g_bus_watch_name_on_connection_with_closures (connection,
823 watch_name_test->watcher_flags,
824 g_cclosure_new (G_CALLBACK (name_appeared_handler),
827 g_cclosure_new (G_CALLBACK (name_vanished_handler),
829 (GClosureNotify) watch_name_data_free_func));
833 id = g_bus_watch_name_on_connection (connection,
835 watch_name_test->watcher_flags,
836 name_appeared_handler,
837 name_vanished_handler,
839 (GDestroyNotify) watch_name_data_free_func);
841 g_assert_cmpint (data.num_appeared, ==, 0);
842 g_assert_cmpint (data.num_vanished, ==, 0);
844 while (data.num_appeared < 1)
845 g_main_context_iteration (main_context, TRUE);
847 g_assert_cmpint (data.num_appeared, ==, 1);
848 g_assert_cmpint (data.num_vanished, ==, 0);
853 g_bus_unwatch_name (id);
854 while (data.num_free_func < 1)
855 g_main_context_iteration (main_context, TRUE);
857 g_assert_cmpint (data.num_free_func, ==, 1);
860 g_bus_unown_name (owner_id);
861 while (own_data.num_free_func < 1)
862 g_main_context_iteration (main_context, TRUE);
864 g_assert_cmpint (own_data.num_acquired, ==, 1);
865 g_assert_cmpint (own_data.num_free_func, ==, 1);
866 own_data.num_free_func = 0;
868 * Create a watcher and then make a name be owned.
870 * This should trigger name_appeared_handler() ...
873 data.num_appeared = 0;
874 data.num_vanished = 0;
875 data.num_free_func = 0;
876 if (watch_name_test->watch_with_closures)
878 id = g_bus_watch_name_with_closures (G_BUS_TYPE_SESSION,
880 watch_name_test->watcher_flags,
881 g_cclosure_new (G_CALLBACK (name_appeared_handler),
884 g_cclosure_new (G_CALLBACK (name_vanished_handler),
886 (GClosureNotify) watch_name_data_free_func));
890 id = g_bus_watch_name (G_BUS_TYPE_SESSION,
892 watch_name_test->watcher_flags,
893 name_appeared_handler,
894 name_vanished_handler,
896 (GDestroyNotify) watch_name_data_free_func);
899 g_assert_cmpint (data.num_appeared, ==, 0);
900 g_assert_cmpint (data.num_vanished, ==, 0);
902 while (data.num_appeared == 0 && data.num_vanished == 0)
903 g_main_context_iteration (main_context, TRUE);
905 if (watch_name_test->existing_service)
907 g_assert_cmpint (data.num_appeared, ==, 1);
908 g_assert_cmpint (data.num_vanished, ==, 0);
912 g_assert_cmpint (data.num_appeared, ==, 0);
913 g_assert_cmpint (data.num_vanished, ==, 1);
916 if (!watch_name_test->existing_service)
919 own_data.num_acquired = 0;
920 own_data.num_lost = 0;
921 own_data.expect_null_connection = FALSE;
922 own_data.main_context = main_context;
923 owner_id = g_bus_own_name (G_BUS_TYPE_SESSION,
925 G_BUS_NAME_OWNER_FLAGS_NONE,
926 w_bus_acquired_handler,
927 w_name_acquired_handler,
930 (GDestroyNotify) own_name_data_free_func);
932 while (own_data.num_acquired == 0 || data.num_appeared == 0)
933 g_main_context_iteration (main_context, TRUE);
935 g_assert_cmpint (own_data.num_acquired, ==, 1);
936 g_assert_cmpint (own_data.num_lost, ==, 0);
937 g_assert_cmpint (data.num_appeared, ==, 1);
938 g_assert_cmpint (data.num_vanished, ==, 1);
941 data.expect_null_connection = TRUE;
942 if (watch_name_test->existing_service)
944 data.expect_null_connection = FALSE;
945 stop_service (connection, &data);
947 g_object_unref (connection);
949 * Nuke the bus and check that the name vanishes and is lost.
952 if (!watch_name_test->existing_service)
954 while (own_data.num_lost < 1 || data.num_vanished < 2)
955 g_main_context_iteration (main_context, TRUE);
956 g_assert_cmpint (own_data.num_lost, ==, 1);
957 g_assert_cmpint (data.num_vanished, ==, 2);
961 g_assert_cmpint (own_data.num_lost, ==, 0);
962 g_assert_cmpint (data.num_vanished, ==, 1);
965 g_bus_unwatch_name (id);
966 while (data.num_free_func < 1)
967 g_main_context_iteration (main_context, TRUE);
969 g_assert_cmpint (data.num_free_func, ==, 1);
971 if (!watch_name_test->existing_service)
973 g_bus_unown_name (owner_id);
974 while (own_data.num_free_func < 1)
975 g_main_context_iteration (main_context, TRUE);
977 g_assert_cmpint (own_data.num_free_func, ==, 1);
982 /* ---------------------------------------------------------------------------------------------------- */
984 /* Called in the same thread as watcher_thread() */
986 t_watch_name_data_free_func (WatchNameThreadData *thread_data)
988 thread_data->data.num_free_func++;
990 g_assert_true (g_main_context_is_owner (thread_data->thread_context));
991 g_main_context_wakeup (thread_data->thread_context);
994 /* Called in the same thread as watcher_thread() */
996 t_name_appeared_handler (GDBusConnection *connection,
998 const gchar *name_owner,
1001 WatchNameThreadData *thread_data = user_data;
1002 thread_data->data.num_appeared += 1;
1004 g_assert_true (g_main_context_is_owner (thread_data->thread_context));
1005 g_main_context_wakeup (thread_data->thread_context);
1008 /* Called in the same thread as watcher_thread() */
1010 t_name_vanished_handler (GDBusConnection *connection,
1014 WatchNameThreadData *thread_data = user_data;
1015 thread_data->data.num_vanished += 1;
1017 g_assert_true (g_main_context_is_owner (thread_data->thread_context));
1018 g_main_context_wakeup (thread_data->thread_context);
1021 /* Called in the thread which constructed the GDBusConnection */
1023 connection_closed_cb (GDBusConnection *connection,
1024 gboolean remote_peer_vanished,
1028 WatchNameThreadData *thread_data = (WatchNameThreadData *) user_data;
1029 if (thread_data->unwatch_early)
1031 g_mutex_lock (&thread_data->mutex);
1032 g_bus_unwatch_name (g_atomic_int_get (&thread_data->watch_id));
1033 g_atomic_int_set (&thread_data->watch_id, 0);
1034 g_cond_signal (&thread_data->cond);
1035 g_mutex_unlock (&thread_data->mutex);
1040 watcher_thread (gpointer user_data)
1042 WatchNameThreadData *thread_data = user_data;
1043 GMainContext *thread_context;
1045 thread_context = g_main_context_new ();
1046 thread_data->thread_context = thread_context;
1047 g_main_context_push_thread_default (thread_context);
1049 // Notify that the thread has started
1050 g_mutex_lock (&thread_data->cond_mutex);
1051 g_atomic_int_set (&thread_data->started, TRUE);
1052 g_cond_signal (&thread_data->cond);
1053 g_mutex_unlock (&thread_data->cond_mutex);
1055 // Wait for the main thread to own the name before watching it
1056 g_mutex_lock (&thread_data->cond_mutex);
1057 while (!g_atomic_int_get (&thread_data->name_acquired))
1058 g_cond_wait (&thread_data->cond, &thread_data->cond_mutex);
1059 g_mutex_unlock (&thread_data->cond_mutex);
1061 thread_data->data.num_appeared = 0;
1062 thread_data->data.num_vanished = 0;
1063 thread_data->data.num_free_func = 0;
1064 // g_signal_connect_after is important to have default handler be called before our code
1065 g_signal_connect_after (thread_data->connection, "closed", G_CALLBACK (connection_closed_cb), thread_data);
1067 g_mutex_lock (&thread_data->mutex);
1068 thread_data->watch_id = g_bus_watch_name_on_connection (thread_data->connection,
1069 "org.gtk.GDBus.Name1",
1070 G_BUS_NAME_WATCHER_FLAGS_NONE,
1071 t_name_appeared_handler,
1072 t_name_vanished_handler,
1074 (GDestroyNotify) t_watch_name_data_free_func);
1075 g_mutex_unlock (&thread_data->mutex);
1077 g_assert_cmpint (thread_data->data.num_appeared, ==, 0);
1078 g_assert_cmpint (thread_data->data.num_vanished, ==, 0);
1079 while (thread_data->data.num_appeared == 0)
1080 g_main_context_iteration (thread_context, TRUE);
1081 g_assert_cmpint (thread_data->data.num_appeared, ==, 1);
1082 g_assert_cmpint (thread_data->data.num_vanished, ==, 0);
1083 thread_data->data.num_appeared = 0;
1085 /* Close the connection and:
1086 * - check that we had received a vanished event even begin in different thread
1087 * - or check that unwatching the bus when a vanished had been scheduled
1088 * make it correctly unscheduled (unwatch_early condition)
1090 g_dbus_connection_close_sync (thread_data->connection, NULL, NULL);
1091 if (thread_data->unwatch_early)
1093 // Wait for the main thread to iterate in order to have close connection handled
1094 g_mutex_lock (&thread_data->mutex);
1095 while (g_atomic_int_get (&thread_data->watch_id) != 0)
1096 g_cond_wait (&thread_data->cond, &thread_data->mutex);
1097 g_mutex_unlock (&thread_data->mutex);
1099 while (thread_data->data.num_free_func == 0)
1100 g_main_context_iteration (thread_context, TRUE);
1101 g_assert_cmpint (thread_data->data.num_vanished, ==, 0);
1102 g_assert_cmpint (thread_data->data.num_appeared, ==, 0);
1103 g_assert_cmpint (thread_data->data.num_free_func, ==, 1);
1107 while (thread_data->data.num_vanished == 0)
1110 * Close of connection is treated in the context of the thread which
1111 * creates the connection. We must run iteration on it (to have the 'closed'
1112 * signal handled) and also run current thread loop to have name_vanished
1115 g_main_context_iteration (thread_context, TRUE);
1117 g_assert_cmpint (thread_data->data.num_vanished, ==, 1);
1118 g_assert_cmpint (thread_data->data.num_appeared, ==, 0);
1119 g_mutex_lock (&thread_data->mutex);
1120 g_bus_unwatch_name (g_atomic_int_get (&thread_data->watch_id));
1121 g_atomic_int_set (&thread_data->watch_id, 0);
1122 g_mutex_unlock (&thread_data->mutex);
1123 while (thread_data->data.num_free_func == 0)
1124 g_main_context_iteration (thread_context, TRUE);
1125 g_assert_cmpint (thread_data->data.num_free_func, ==, 1);
1128 g_mutex_lock (&thread_data->cond_mutex);
1129 thread_data->ended = TRUE;
1130 g_main_context_wakeup (NULL);
1131 g_cond_signal (&thread_data->cond);
1132 g_mutex_unlock (&thread_data->cond_mutex);
1134 g_signal_handlers_disconnect_by_func (thread_data->connection, connection_closed_cb, thread_data);
1135 g_object_unref (thread_data->connection);
1136 g_main_context_pop_thread_default (thread_context);
1137 g_main_context_unref (thread_context);
1139 g_mutex_lock (&thread_data->mutex);
1140 g_assert_cmpint (thread_data->watch_id, ==, 0);
1141 g_mutex_unlock (&thread_data->mutex);
1146 watch_with_different_context (gboolean unwatch_early)
1148 OwnNameData own_data;
1149 WatchNameThreadData thread_data;
1150 GDBusConnection *connection;
1153 GMainContext *main_context = NULL; /* use the global default for now */
1157 connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
1158 g_assert (connection != NULL);
1160 g_mutex_init (&thread_data.mutex);
1161 g_mutex_init (&thread_data.cond_mutex);
1162 g_cond_init (&thread_data.cond);
1163 thread_data.started = FALSE;
1164 thread_data.name_acquired = FALSE;
1165 thread_data.ended = FALSE;
1166 thread_data.connection = g_object_ref (connection);
1167 thread_data.unwatch_early = unwatch_early;
1169 // Create a thread which will watch a name and wait for it to be ready
1170 g_mutex_lock (&thread_data.cond_mutex);
1171 watcher = g_thread_new ("watcher", watcher_thread, &thread_data);
1172 while (!g_atomic_int_get (&thread_data.started))
1173 g_cond_wait (&thread_data.cond, &thread_data.cond_mutex);
1174 g_mutex_unlock (&thread_data.cond_mutex);
1176 own_data.num_acquired = 0;
1177 own_data.num_lost = 0;
1178 own_data.num_free_func = 0;
1179 own_data.expect_null_connection = FALSE;
1180 own_data.main_context = main_context;
1181 // Own the name to avoid direct name vanished in watcher thread
1182 id = g_bus_own_name_on_connection (connection,
1183 "org.gtk.GDBus.Name1",
1184 G_BUS_NAME_OWNER_FLAGS_REPLACE,
1185 w_name_acquired_handler,
1186 w_name_lost_handler,
1188 (GDestroyNotify) own_name_data_free_func);
1189 while (own_data.num_acquired == 0)
1190 g_main_context_iteration (main_context, TRUE);
1191 g_assert_cmpint (own_data.num_acquired, ==, 1);
1192 g_assert_cmpint (own_data.num_lost, ==, 0);
1194 // Wake the thread for it to begin watch
1195 g_mutex_lock (&thread_data.cond_mutex);
1196 g_atomic_int_set (&thread_data.name_acquired, TRUE);
1197 g_cond_signal (&thread_data.cond);
1198 g_mutex_unlock (&thread_data.cond_mutex);
1200 // Iterate the loop until thread is waking us up
1201 while (!thread_data.ended)
1202 g_main_context_iteration (main_context, TRUE);
1204 g_thread_join (watcher);
1206 g_bus_unown_name (id);
1207 while (own_data.num_free_func == 0)
1208 g_main_context_iteration (main_context, TRUE);
1209 g_assert_cmpint (own_data.num_free_func, ==, 1);
1211 g_mutex_clear (&thread_data.mutex);
1212 g_mutex_clear (&thread_data.cond_mutex);
1213 g_cond_clear (&thread_data.cond);
1215 session_bus_stop ();
1216 g_assert_true (g_dbus_connection_is_closed (connection));
1217 g_object_unref (connection);
1218 session_bus_down ();
1222 test_bus_watch_different_context (void)
1224 watch_with_different_context (FALSE);
1227 /* ---------------------------------------------------------------------------------------------------- */
1230 test_bus_unwatch_early (void)
1232 g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/604");
1233 watch_with_different_context (TRUE);
1236 /* ---------------------------------------------------------------------------------------------------- */
1239 test_validate_names (void)
1247 const gchar *string;
1249 { 1, 0, 1, "valid.well_known.name"},
1250 { 1, 0, 0, "valid.well-known.name"},
1251 { 1, 1, 0, ":valid.unique.name"},
1252 { 0, 0, 0, "invalid.5well_known.name"},
1253 { 0, 0, 0, "4invalid.5well_known.name"},
1254 { 1, 1, 0, ":4valid.5unique.name"},
1256 { 1, 0, 1, "very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.name1"}, /* 255 */
1257 { 0, 0, 0, "very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.name12"}, /* 256 - too long! */
1258 { 0, 0, 0, ".starts.with.a.dot"},
1259 { 0, 0, 0, "contains.invalid;.characters"},
1260 { 0, 0, 0, "contains.inva/lid.characters"},
1261 { 0, 0, 0, "contains.inva[lid.characters"},
1262 { 0, 0, 0, "contains.inva]lid.characters"},
1263 { 0, 0, 0, "contains.inva_æøå_lid.characters"},
1267 for (n = 0; n < G_N_ELEMENTS (names); n++)
1270 g_assert (g_dbus_is_name (names[n].string));
1272 g_assert (!g_dbus_is_name (names[n].string));
1274 if (names[n].unique)
1275 g_assert (g_dbus_is_unique_name (names[n].string));
1277 g_assert (!g_dbus_is_unique_name (names[n].string));
1279 if (names[n].interface)
1281 g_assert (g_dbus_is_interface_name (names[n].string));
1282 g_assert (g_dbus_is_error_name (names[n].string));
1286 g_assert (!g_dbus_is_interface_name (names[n].string));
1287 g_assert (!g_dbus_is_error_name (names[n].string));
1293 assert_cmp_escaped_object_path (const gchar *s,
1294 const gchar *correct_escaped)
1299 escaped = g_dbus_escape_object_path (s);
1300 g_assert_cmpstr (escaped, ==, correct_escaped);
1303 escaped = g_dbus_escape_object_path_bytestring ((const guint8 *) s);
1304 g_assert_cmpstr (escaped, ==, correct_escaped);
1306 unescaped = g_dbus_unescape_object_path (escaped);
1307 g_assert_cmpstr ((const gchar *) unescaped, ==, s);
1314 test_escape_object_path (void)
1316 assert_cmp_escaped_object_path ("Foo42", "Foo42");
1317 assert_cmp_escaped_object_path ("foo.bar.baz", "foo_2ebar_2ebaz");
1318 assert_cmp_escaped_object_path ("foo_bar_baz", "foo_5fbar_5fbaz");
1319 assert_cmp_escaped_object_path ("_", "_5f");
1320 assert_cmp_escaped_object_path ("__", "_5f_5f");
1321 assert_cmp_escaped_object_path ("", "_");
1322 assert_cmp_escaped_object_path (":1.42", "_3a1_2e42");
1323 assert_cmp_escaped_object_path ("a/b", "a_2fb");
1324 assert_cmp_escaped_object_path (" ", "_20");
1325 assert_cmp_escaped_object_path ("\n", "_0a");
1327 g_assert_null (g_dbus_unescape_object_path ("_ii"));
1328 g_assert_null (g_dbus_unescape_object_path ("döner"));
1329 g_assert_null (g_dbus_unescape_object_path ("_00"));
1330 g_assert_null (g_dbus_unescape_object_path ("_61"));
1331 g_assert_null (g_dbus_unescape_object_path ("_ga"));
1332 g_assert_null (g_dbus_unescape_object_path ("_ag"));
1335 /* ---------------------------------------------------------------------------------------------------- */
1343 g_test_init (&argc, &argv, G_TEST_OPTION_ISOLATE_DIRS, NULL);
1345 g_test_dbus_unset ();
1347 g_test_add_func ("/gdbus/validate-names", test_validate_names);
1348 g_test_add_func ("/gdbus/bus-own-name", test_bus_own_name);
1349 g_test_add_data_func ("/gdbus/bus-watch-name",
1350 &watch_no_closures_no_flags,
1351 test_bus_watch_name);
1352 g_test_add_data_func ("/gdbus/bus-watch-name-auto-start",
1353 &watch_no_closures_flags_auto_start,
1354 test_bus_watch_name);
1355 g_test_add_data_func ("/gdbus/bus-watch-name-auto-start-service-exist",
1356 &watch_no_closures_flags_auto_start_service_exist,
1357 test_bus_watch_name);
1358 g_test_add_data_func ("/gdbus/bus-watch-name-closures",
1359 &watch_closures_no_flags,
1360 test_bus_watch_name);
1361 g_test_add_data_func ("/gdbus/bus-watch-name-closures-auto-start",
1362 &watch_closures_flags_auto_start,
1363 test_bus_watch_name);
1364 g_test_add_func ("/gdbus/bus-watch-different-context", test_bus_watch_different_context);
1365 g_test_add_func ("/gdbus/bus-unwatch-early", test_bus_unwatch_early);
1366 g_test_add_func ("/gdbus/escape-object-path", test_escape_object_path);