1 /* GIO testing utilities
2 *
3 * Copyright (C) 2008-2010 Red Hat, Inc.
4 * Copyright (C) 2012 Collabora Ltd. <http://www.collabora.co.uk/>
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General
17 * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
18 *
19 * Authors: David Zeuthen <davidz@redhat.com>
20 * Xavier Claessens <xavier.claessens@collabora.co.uk>
21 */
22
23 #include "config.h"
24
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <errno.h>
28 #include <string.h>
29 #include <gstdio.h>
30 #ifdef G_OS_UNIX
31 #include <unistd.h>
32 #endif
33 #ifdef G_OS_WIN32
34 #include <io.h>
35 #include <fcntl.h>
36 #include <windows.h>
37 #endif
38
39 #include <glib.h>
40
41 #include "gdbusconnection.h"
42 #include "gdbusprivate.h"
43 #include "gfile.h"
44 #include "gioenumtypes.h"
45 #include "gtestdbus.h"
46
47 #include "glibintl.h"
48
49 #ifdef G_OS_UNIX
50 #include "glib-unix.h"
51 #endif
52
53 /* -------------------------------------------------------------------------- */
54 /* Utility: Wait until object has a single ref */
55
56 typedef struct
57 {
58 GMainLoop *loop;
59 gboolean timed_out;
60 } WeakNotifyData;
61
62 static gboolean
on_weak_notify_timeout(gpointer user_data)63 on_weak_notify_timeout (gpointer user_data)
64 {
65 WeakNotifyData *data = user_data;
66 data->timed_out = TRUE;
67 g_main_loop_quit (data->loop);
68 return FALSE;
69 }
70
71 static gboolean
unref_on_idle(gpointer object)72 unref_on_idle (gpointer object)
73 {
74 g_object_unref (object);
75 return FALSE;
76 }
77
78 static gboolean
_g_object_unref_and_wait_weak_notify(gpointer object)79 _g_object_unref_and_wait_weak_notify (gpointer object)
80 {
81 WeakNotifyData data;
82 guint timeout_id;
83
84 data.loop = g_main_loop_new (NULL, FALSE);
85 data.timed_out = FALSE;
86
87 g_object_weak_ref (object, (GWeakNotify) g_main_loop_quit, data.loop);
88
89 /* Drop the strong ref held by the caller in an idle callback. This is to
90 * make sure the mainloop is already running when weak notify happens (when
91 * all other strong ref holders have dropped theirs). */
92 g_idle_add (unref_on_idle, object);
93
94 /* Make sure we don't block forever */
95 timeout_id = g_timeout_add (30 * 1000, on_weak_notify_timeout, &data);
96
97 g_main_loop_run (data.loop);
98
99 if (data.timed_out)
100 {
101 g_warning ("Weak notify timeout, object ref_count=%d",
102 G_OBJECT (object)->ref_count);
103 }
104 else
105 {
106 g_source_remove (timeout_id);
107 }
108
109 g_main_loop_unref (data.loop);
110 return data.timed_out;
111 }
112
113 /* -------------------------------------------------------------------------- */
114 /* Utilities to cleanup the mess in the case unit test process crash */
115
116 #ifdef G_OS_WIN32
117
118 /* This could be interesting to expose in public API */
119 static void
_g_test_watcher_add_pid(GPid pid)120 _g_test_watcher_add_pid (GPid pid)
121 {
122 static gsize started = 0;
123 HANDLE job;
124
125 if (g_once_init_enter (&started))
126 {
127 JOBOBJECT_EXTENDED_LIMIT_INFORMATION info;
128
129 job = CreateJobObjectW (NULL, NULL);
130 memset (&info, 0, sizeof (info));
131 info.BasicLimitInformation.LimitFlags = 0x2000 /* JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE */;
132
133 if (!SetInformationJobObject(job, JobObjectExtendedLimitInformation, &info, sizeof (info)))
134 g_warning ("Can't enable JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE: %s", g_win32_error_message (GetLastError()));
135
136 g_once_init_leave (&started,(gsize)job);
137 }
138
139 job = (HANDLE)started;
140
141 if (!AssignProcessToJobObject(job, pid))
142 g_warning ("Can't assign process to job: %s", g_win32_error_message (GetLastError()));
143 }
144
145 static void
_g_test_watcher_remove_pid(GPid pid)146 _g_test_watcher_remove_pid (GPid pid)
147 {
148 /* No need to unassign the process from the job object as the process
149 will be killed anyway */
150 }
151
152 #else
153
154 #define ADD_PID_FORMAT "add pid %d\n"
155 #define REMOVE_PID_FORMAT "remove pid %d\n"
156
157 static void
watch_parent(gint fd)158 watch_parent (gint fd)
159 {
160 GIOChannel *channel;
161 GPollFD fds[1];
162 GArray *pids_to_kill;
163
164 channel = g_io_channel_unix_new (fd);
165
166 fds[0].fd = fd;
167 fds[0].events = G_IO_HUP | G_IO_IN;
168 fds[0].revents = 0;
169
170 pids_to_kill = g_array_new (FALSE, FALSE, sizeof (guint));
171
172 do
173 {
174 gint num_events;
175 gchar *command = NULL;
176 guint pid;
177 guint n;
178 GError *error = NULL;
179
180 num_events = g_poll (fds, 1, -1);
181 if (num_events == 0)
182 continue;
183
184 if (fds[0].revents & G_IO_HUP)
185 {
186 /* Parent quit, cleanup the mess and exit */
187 for (n = 0; n < pids_to_kill->len; n++)
188 {
189 pid = g_array_index (pids_to_kill, guint, n);
190 g_printerr ("cleaning up pid %d\n", pid);
191 kill (pid, SIGTERM);
192 }
193
194 g_array_unref (pids_to_kill);
195 g_io_channel_shutdown (channel, FALSE, &error);
196 g_assert_no_error (error);
197 g_io_channel_unref (channel);
198
199 exit (0);
200 }
201
202 /* Read the command from the input */
203 g_io_channel_read_line (channel, &command, NULL, NULL, &error);
204 g_assert_no_error (error);
205
206 /* Check for known commands */
207 if (sscanf (command, ADD_PID_FORMAT, &pid) == 1)
208 {
209 g_array_append_val (pids_to_kill, pid);
210 }
211 else if (sscanf (command, REMOVE_PID_FORMAT, &pid) == 1)
212 {
213 for (n = 0; n < pids_to_kill->len; n++)
214 {
215 if (g_array_index (pids_to_kill, guint, n) == pid)
216 {
217 g_array_remove_index (pids_to_kill, n);
218 pid = 0;
219 break;
220 }
221 }
222 if (pid != 0)
223 {
224 g_warning ("unknown pid %d to remove", pid);
225 }
226 }
227 else
228 {
229 g_warning ("unknown command from parent '%s'", command);
230 }
231
232 g_free (command);
233 }
234 while (TRUE);
235 }
236
237 static GIOChannel *
watcher_init(void)238 watcher_init (void)
239 {
240 static gsize started = 0;
241 static GIOChannel *channel = NULL;
242 int errsv;
243
244 if (g_once_init_enter (&started))
245 {
246 gint pipe_fds[2];
247
248 /* fork a child to clean up when we are killed */
249 if (pipe (pipe_fds) != 0)
250 {
251 errsv = errno;
252 g_warning ("pipe() failed: %s", g_strerror (errsv));
253 g_assert_not_reached ();
254 }
255
256 /* flush streams to avoid buffers being duplicated in the child and
257 * flushed by both the child and parent later
258 *
259 * FIXME: This is a workaround for the fact that watch_parent() uses
260 * non-async-signal-safe API. See
261 * https://gitlab.gnome.org/GNOME/glib/-/issues/2322#note_1034330
262 */
263 fflush (stdout);
264 fflush (stderr);
265
266 switch (fork ())
267 {
268 case -1:
269 errsv = errno;
270 g_warning ("fork() failed: %s", g_strerror (errsv));
271 g_assert_not_reached ();
272 break;
273
274 case 0:
275 /* child */
276 close (pipe_fds[1]);
277 watch_parent (pipe_fds[0]);
278 break;
279
280 default:
281 /* parent */
282 close (pipe_fds[0]);
283 channel = g_io_channel_unix_new (pipe_fds[1]);
284 }
285
286 g_once_init_leave (&started, 1);
287 }
288
289 return channel;
290 }
291
292 static void
watcher_send_command(const gchar * command)293 watcher_send_command (const gchar *command)
294 {
295 GIOChannel *channel;
296 GError *error = NULL;
297 GIOStatus status;
298
299 channel = watcher_init ();
300
301 do
302 status = g_io_channel_write_chars (channel, command, -1, NULL, &error);
303 while (status == G_IO_STATUS_AGAIN);
304 g_assert_no_error (error);
305
306 g_io_channel_flush (channel, &error);
307 g_assert_no_error (error);
308 }
309
310 /* This could be interesting to expose in public API */
311 static void
_g_test_watcher_add_pid(GPid pid)312 _g_test_watcher_add_pid (GPid pid)
313 {
314 gchar *command;
315
316 command = g_strdup_printf (ADD_PID_FORMAT, (guint) pid);
317 watcher_send_command (command);
318 g_free (command);
319 }
320
321 static void
_g_test_watcher_remove_pid(GPid pid)322 _g_test_watcher_remove_pid (GPid pid)
323 {
324 gchar *command;
325
326 command = g_strdup_printf (REMOVE_PID_FORMAT, (guint) pid);
327 watcher_send_command (command);
328 g_free (command);
329 }
330
331 #endif
332
333 /* -------------------------------------------------------------------------- */
334 /* GTestDBus object implementation */
335
336 /**
337 * SECTION:gtestdbus
338 * @short_description: D-Bus testing helper
339 * @include: gio/gio.h
340 *
341 * A helper class for testing code which uses D-Bus without touching the user's
342 * session bus.
343 *
344 * Note that #GTestDBus modifies the user’s environment, calling setenv().
345 * This is not thread-safe, so all #GTestDBus calls should be completed before
346 * threads are spawned, or should have appropriate locking to ensure no access
347 * conflicts to environment variables shared between #GTestDBus and other
348 * threads.
349 *
350 * ## Creating unit tests using GTestDBus
351 *
352 * Testing of D-Bus services can be tricky because normally we only ever run
353 * D-Bus services over an existing instance of the D-Bus daemon thus we
354 * usually don't activate D-Bus services that are not yet installed into the
355 * target system. The #GTestDBus object makes this easier for us by taking care
356 * of the lower level tasks such as running a private D-Bus daemon and looking
357 * up uninstalled services in customizable locations, typically in your source
358 * code tree.
359 *
360 * The first thing you will need is a separate service description file for the
361 * D-Bus daemon. Typically a `services` subdirectory of your `tests` directory
362 * is a good place to put this file.
363 *
364 * The service file should list your service along with an absolute path to the
365 * uninstalled service executable in your source tree. Using autotools we would
366 * achieve this by adding a file such as `my-server.service.in` in the services
367 * directory and have it processed by configure.
368 * |[
369 * [D-BUS Service]
370 * Name=org.gtk.GDBus.Examples.ObjectManager
371 * Exec=@abs_top_builddir@/gio/tests/gdbus-example-objectmanager-server
372 * ]|
373 * You will also need to indicate this service directory in your test
374 * fixtures, so you will need to pass the path while compiling your
375 * test cases. Typically this is done with autotools with an added
376 * preprocessor flag specified to compile your tests such as:
377 * |[
378 * -DTEST_SERVICES=\""$(abs_top_builddir)/tests/services"\"
379 * ]|
380 * Once you have a service definition file which is local to your source tree,
381 * you can proceed to set up a GTest fixture using the #GTestDBus scaffolding.
382 *
383 * An example of a test fixture for D-Bus services can be found
384 * here:
385 * [gdbus-test-fixture.c](https://gitlab.gnome.org/GNOME/glib/-/blob/HEAD/gio/tests/gdbus-test-fixture.c)
386 *
387 * Note that these examples only deal with isolating the D-Bus aspect of your
388 * service. To successfully run isolated unit tests on your service you may need
389 * some additional modifications to your test case fixture. For example; if your
390 * service uses GSettings and installs a schema then it is important that your test service
391 * not load the schema in the ordinary installed location (chances are that your service
392 * and schema files are not yet installed, or worse; there is an older version of the
393 * schema file sitting in the install location).
394 *
395 * Most of the time we can work around these obstacles using the
396 * environment. Since the environment is inherited by the D-Bus daemon
397 * created by #GTestDBus and then in turn inherited by any services the
398 * D-Bus daemon activates, using the setup routine for your fixture is
399 * a practical place to help sandbox your runtime environment. For the
400 * rather typical GSettings case we can work around this by setting
401 * `GSETTINGS_SCHEMA_DIR` to the in tree directory holding your schemas
402 * in the above fixture_setup() routine.
403 *
404 * The GSettings schemas need to be locally pre-compiled for this to work. This can be achieved
405 * by compiling the schemas locally as a step before running test cases, an autotools setup might
406 * do the following in the directory holding schemas:
407 * |[
408 * all-am:
409 * $(GLIB_COMPILE_SCHEMAS) .
410 *
411 * CLEANFILES += gschemas.compiled
412 * ]|
413 */
414
415 typedef struct _GTestDBusClass GTestDBusClass;
416 typedef struct _GTestDBusPrivate GTestDBusPrivate;
417
418 /**
419 * GTestDBus:
420 *
421 * The #GTestDBus structure contains only private data and
422 * should only be accessed using the provided API.
423 *
424 * Since: 2.34
425 */
426 struct _GTestDBus {
427 GObject parent;
428
429 GTestDBusPrivate *priv;
430 };
431
432 struct _GTestDBusClass {
433 GObjectClass parent_class;
434 };
435
436 struct _GTestDBusPrivate
437 {
438 GTestDBusFlags flags;
439 GPtrArray *service_dirs;
440 GPid bus_pid;
441 gchar *bus_address;
442 gboolean up;
443 };
444
445 enum
446 {
447 PROP_0,
448 PROP_FLAGS,
449 };
450
G_DEFINE_TYPE_WITH_PRIVATE(GTestDBus,g_test_dbus,G_TYPE_OBJECT)451 G_DEFINE_TYPE_WITH_PRIVATE (GTestDBus, g_test_dbus, G_TYPE_OBJECT)
452
453 static void
454 g_test_dbus_init (GTestDBus *self)
455 {
456 self->priv = g_test_dbus_get_instance_private (self);
457 self->priv->service_dirs = g_ptr_array_new_with_free_func (g_free);
458 }
459
460 static void
g_test_dbus_dispose(GObject * object)461 g_test_dbus_dispose (GObject *object)
462 {
463 GTestDBus *self = (GTestDBus *) object;
464
465 if (self->priv->up)
466 g_test_dbus_down (self);
467
468 G_OBJECT_CLASS (g_test_dbus_parent_class)->dispose (object);
469 }
470
471 static void
g_test_dbus_finalize(GObject * object)472 g_test_dbus_finalize (GObject *object)
473 {
474 GTestDBus *self = (GTestDBus *) object;
475
476 g_ptr_array_unref (self->priv->service_dirs);
477 g_free (self->priv->bus_address);
478
479 G_OBJECT_CLASS (g_test_dbus_parent_class)->finalize (object);
480 }
481
482 static void
g_test_dbus_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)483 g_test_dbus_get_property (GObject *object,
484 guint property_id,
485 GValue *value,
486 GParamSpec *pspec)
487 {
488 GTestDBus *self = (GTestDBus *) object;
489
490 switch (property_id)
491 {
492 case PROP_FLAGS:
493 g_value_set_flags (value, g_test_dbus_get_flags (self));
494 break;
495 default:
496 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
497 break;
498 }
499 }
500
501 static void
g_test_dbus_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)502 g_test_dbus_set_property (GObject *object,
503 guint property_id,
504 const GValue *value,
505 GParamSpec *pspec)
506 {
507 GTestDBus *self = (GTestDBus *) object;
508
509 switch (property_id)
510 {
511 case PROP_FLAGS:
512 self->priv->flags = g_value_get_flags (value);
513 break;
514 default:
515 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
516 break;
517 }
518 }
519
520 static void
g_test_dbus_class_init(GTestDBusClass * klass)521 g_test_dbus_class_init (GTestDBusClass *klass)
522 {
523 GObjectClass *object_class = G_OBJECT_CLASS (klass);
524
525 object_class->dispose = g_test_dbus_dispose;
526 object_class->finalize = g_test_dbus_finalize;
527 object_class->get_property = g_test_dbus_get_property;
528 object_class->set_property = g_test_dbus_set_property;
529
530 /**
531 * GTestDBus:flags:
532 *
533 * #GTestDBusFlags specifying the behaviour of the D-Bus session.
534 *
535 * Since: 2.34
536 */
537 g_object_class_install_property (object_class, PROP_FLAGS,
538 g_param_spec_flags ("flags",
539 P_("D-Bus session flags"),
540 P_("Flags specifying the behaviour of the D-Bus session"),
541 G_TYPE_TEST_DBUS_FLAGS, G_TEST_DBUS_NONE,
542 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
543 G_PARAM_STATIC_STRINGS));
544
545 }
546
547 static gchar *
write_config_file(GTestDBus * self)548 write_config_file (GTestDBus *self)
549 {
550 GString *contents;
551 gint fd;
552 guint i;
553 GError *error = NULL;
554 gchar *path = NULL;
555
556 fd = g_file_open_tmp ("g-test-dbus-XXXXXX", &path, &error);
557 g_assert_no_error (error);
558
559 contents = g_string_new (NULL);
560 g_string_append (contents,
561 "<busconfig>\n"
562 " <type>session</type>\n"
563 #ifdef G_OS_WIN32
564 " <listen>nonce-tcp:</listen>\n"
565 #else
566 " <listen>unix:tmpdir=/tmp</listen>\n"
567 #endif
568 );
569
570 for (i = 0; i < self->priv->service_dirs->len; i++)
571 {
572 const gchar *dir_path = g_ptr_array_index (self->priv->service_dirs, i);
573
574 g_string_append_printf (contents,
575 " <servicedir>%s</servicedir>\n", dir_path);
576 }
577
578 g_string_append (contents,
579 " <policy context=\"default\">\n"
580 " <!-- Allow everything to be sent -->\n"
581 " <allow send_destination=\"*\" eavesdrop=\"true\"/>\n"
582 " <!-- Allow everything to be received -->\n"
583 " <allow eavesdrop=\"true\"/>\n"
584 " <!-- Allow anyone to own anything -->\n"
585 " <allow own=\"*\"/>\n"
586 " </policy>\n"
587 "</busconfig>\n");
588
589 close (fd);
590 g_file_set_contents_full (path, contents->str, contents->len,
591 G_FILE_SET_CONTENTS_NONE,
592 0600, &error);
593 g_assert_no_error (error);
594
595 g_string_free (contents, TRUE);
596
597 return path;
598 }
599
600 static gboolean
make_pipe(gint pipe_fds[2],GError ** error)601 make_pipe (gint pipe_fds[2],
602 GError **error)
603 {
604 #if defined(G_OS_UNIX)
605 return g_unix_open_pipe (pipe_fds, FD_CLOEXEC, error);
606 #elif defined(G_OS_WIN32)
607 if (_pipe (pipe_fds, 4096, _O_BINARY) < 0)
608 {
609 int errsv = errno;
610
611 g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
612 _("Failed to create pipe for communicating with child process (%s)"),
613 g_strerror (errsv));
614 return FALSE;
615 }
616 return TRUE;
617 #else
618 g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
619 _("Pipes are not supported in this platform"));
620 return FALSE;
621 #endif
622 }
623
624 static void
start_daemon(GTestDBus * self)625 start_daemon (GTestDBus *self)
626 {
627 const gchar *argv[] = {"dbus-daemon", "--print-address", "--config-file=foo", NULL};
628 gint pipe_fds[2] = {-1, -1};
629 gchar *config_path;
630 gchar *config_arg;
631 gchar *print_address;
632 GIOChannel *channel;
633 gsize termpos;
634 GError *error = NULL;
635
636 if (g_getenv ("G_TEST_DBUS_DAEMON") != NULL)
637 argv[0] = (gchar *)g_getenv ("G_TEST_DBUS_DAEMON");
638
639 make_pipe (pipe_fds, &error);
640 g_assert_no_error (error);
641
642 print_address = g_strdup_printf ("--print-address=%d", pipe_fds[1]);
643 argv[1] = print_address;
644 g_assert_no_error (error);
645
646 /* Write config file and set its path in argv */
647 config_path = write_config_file (self);
648 config_arg = g_strdup_printf ("--config-file=%s", config_path);
649 argv[2] = config_arg;
650
651 /* Spawn dbus-daemon */
652 g_spawn_async_with_pipes_and_fds (NULL,
653 argv,
654 NULL,
655 /* We Need this to get the pid returned on win32 */
656 G_SPAWN_DO_NOT_REAP_CHILD |
657 G_SPAWN_SEARCH_PATH |
658 /* dbus-daemon will not abuse our descriptors, and
659 * passing this means we can use posix_spawn() for speed */
660 G_SPAWN_LEAVE_DESCRIPTORS_OPEN,
661 NULL, NULL,
662 -1, -1, -1,
663 &pipe_fds[1], &pipe_fds[1], 1,
664 &self->priv->bus_pid,
665 NULL, NULL, NULL,
666 &error);
667 g_assert_no_error (error);
668
669 _g_test_watcher_add_pid (self->priv->bus_pid);
670
671 /* Read bus address from pipe */
672 channel = g_io_channel_unix_new (pipe_fds[0]);
673 pipe_fds[0] = -1;
674 g_io_channel_set_close_on_unref (channel, TRUE);
675 g_io_channel_read_line (channel, &self->priv->bus_address, NULL,
676 &termpos, &error);
677 g_assert_no_error (error);
678 self->priv->bus_address[termpos] = '\0';
679 close (pipe_fds[1]);
680 pipe_fds[1] = -1;
681
682 /* start dbus-monitor */
683 if (g_getenv ("G_DBUS_MONITOR") != NULL)
684 {
685 gchar *command;
686
687 command = g_strdup_printf ("dbus-monitor --address %s",
688 self->priv->bus_address);
689 g_spawn_command_line_async (command, NULL);
690 g_free (command);
691
692 g_usleep (500 * 1000);
693 }
694
695 /* Cleanup */
696 g_io_channel_shutdown (channel, FALSE, &error);
697 g_assert_no_error (error);
698 g_io_channel_unref (channel);
699
700 /* Don't use g_file_delete since it calls into gvfs */
701 if (g_unlink (config_path) != 0)
702 g_assert_not_reached ();
703
704 g_free (print_address);
705 g_free (config_path);
706 g_free (config_arg);
707 }
708
709 static void
stop_daemon(GTestDBus * self)710 stop_daemon (GTestDBus *self)
711 {
712 #ifdef G_OS_WIN32
713 if (!TerminateProcess (self->priv->bus_pid, 0))
714 g_warning ("Can't terminate process: %s", g_win32_error_message (GetLastError()));
715 #else
716 kill (self->priv->bus_pid, SIGTERM);
717 #endif
718 _g_test_watcher_remove_pid (self->priv->bus_pid);
719 g_spawn_close_pid (self->priv->bus_pid);
720 self->priv->bus_pid = 0;
721
722 g_free (self->priv->bus_address);
723 self->priv->bus_address = NULL;
724 }
725
726 /**
727 * g_test_dbus_new:
728 * @flags: a #GTestDBusFlags
729 *
730 * Create a new #GTestDBus object.
731 *
732 * Returns: (transfer full): a new #GTestDBus.
733 */
734 GTestDBus *
g_test_dbus_new(GTestDBusFlags flags)735 g_test_dbus_new (GTestDBusFlags flags)
736 {
737 return g_object_new (G_TYPE_TEST_DBUS,
738 "flags", flags,
739 NULL);
740 }
741
742 /**
743 * g_test_dbus_get_flags:
744 * @self: a #GTestDBus
745 *
746 * Get the flags of the #GTestDBus object.
747 *
748 * Returns: the value of #GTestDBus:flags property
749 */
750 GTestDBusFlags
g_test_dbus_get_flags(GTestDBus * self)751 g_test_dbus_get_flags (GTestDBus *self)
752 {
753 g_return_val_if_fail (G_IS_TEST_DBUS (self), G_TEST_DBUS_NONE);
754
755 return self->priv->flags;
756 }
757
758 /**
759 * g_test_dbus_get_bus_address:
760 * @self: a #GTestDBus
761 *
762 * Get the address on which dbus-daemon is running. If g_test_dbus_up() has not
763 * been called yet, %NULL is returned. This can be used with
764 * g_dbus_connection_new_for_address().
765 *
766 * Returns: (nullable): the address of the bus, or %NULL.
767 */
768 const gchar *
g_test_dbus_get_bus_address(GTestDBus * self)769 g_test_dbus_get_bus_address (GTestDBus *self)
770 {
771 g_return_val_if_fail (G_IS_TEST_DBUS (self), NULL);
772
773 return self->priv->bus_address;
774 }
775
776 /**
777 * g_test_dbus_add_service_dir:
778 * @self: a #GTestDBus
779 * @path: path to a directory containing .service files
780 *
781 * Add a path where dbus-daemon will look up .service files. This can't be
782 * called after g_test_dbus_up().
783 */
784 void
g_test_dbus_add_service_dir(GTestDBus * self,const gchar * path)785 g_test_dbus_add_service_dir (GTestDBus *self,
786 const gchar *path)
787 {
788 g_return_if_fail (G_IS_TEST_DBUS (self));
789 g_return_if_fail (self->priv->bus_address == NULL);
790
791 g_ptr_array_add (self->priv->service_dirs, g_strdup (path));
792 }
793
794 /**
795 * g_test_dbus_up:
796 * @self: a #GTestDBus
797 *
798 * Start a dbus-daemon instance and set DBUS_SESSION_BUS_ADDRESS. After this
799 * call, it is safe for unit tests to start sending messages on the session bus.
800 *
801 * If this function is called from setup callback of g_test_add(),
802 * g_test_dbus_down() must be called in its teardown callback.
803 *
804 * If this function is called from unit test's main(), then g_test_dbus_down()
805 * must be called after g_test_run().
806 */
807 void
g_test_dbus_up(GTestDBus * self)808 g_test_dbus_up (GTestDBus *self)
809 {
810 g_return_if_fail (G_IS_TEST_DBUS (self));
811 g_return_if_fail (self->priv->bus_address == NULL);
812 g_return_if_fail (!self->priv->up);
813
814 start_daemon (self);
815
816 g_test_dbus_unset ();
817 g_setenv ("DBUS_SESSION_BUS_ADDRESS", self->priv->bus_address, TRUE);
818 self->priv->up = TRUE;
819 }
820
821
822 /**
823 * g_test_dbus_stop:
824 * @self: a #GTestDBus
825 *
826 * Stop the session bus started by g_test_dbus_up().
827 *
828 * Unlike g_test_dbus_down(), this won't verify the #GDBusConnection
829 * singleton returned by g_bus_get() or g_bus_get_sync() is destroyed. Unit
830 * tests wanting to verify behaviour after the session bus has been stopped
831 * can use this function but should still call g_test_dbus_down() when done.
832 */
833 void
g_test_dbus_stop(GTestDBus * self)834 g_test_dbus_stop (GTestDBus *self)
835 {
836 g_return_if_fail (G_IS_TEST_DBUS (self));
837 g_return_if_fail (self->priv->bus_address != NULL);
838
839 stop_daemon (self);
840 }
841
842 /**
843 * g_test_dbus_down:
844 * @self: a #GTestDBus
845 *
846 * Stop the session bus started by g_test_dbus_up().
847 *
848 * This will wait for the singleton returned by g_bus_get() or g_bus_get_sync()
849 * to be destroyed. This is done to ensure that the next unit test won't get a
850 * leaked singleton from this test.
851 */
852 void
g_test_dbus_down(GTestDBus * self)853 g_test_dbus_down (GTestDBus *self)
854 {
855 GDBusConnection *connection;
856
857 g_return_if_fail (G_IS_TEST_DBUS (self));
858 g_return_if_fail (self->priv->up);
859
860 connection = _g_bus_get_singleton_if_exists (G_BUS_TYPE_SESSION);
861 if (connection != NULL)
862 g_dbus_connection_set_exit_on_close (connection, FALSE);
863
864 if (self->priv->bus_address != NULL)
865 stop_daemon (self);
866
867 if (connection != NULL)
868 _g_object_unref_and_wait_weak_notify (connection);
869
870 g_test_dbus_unset ();
871 _g_bus_forget_singleton (G_BUS_TYPE_SESSION);
872 self->priv->up = FALSE;
873 }
874
875 /**
876 * g_test_dbus_unset:
877 *
878 * Unset DISPLAY and DBUS_SESSION_BUS_ADDRESS env variables to ensure the test
879 * won't use user's session bus.
880 *
881 * This is useful for unit tests that want to verify behaviour when no session
882 * bus is running. It is not necessary to call this if unit test already calls
883 * g_test_dbus_up() before acquiring the session bus.
884 */
885 void
g_test_dbus_unset(void)886 g_test_dbus_unset (void)
887 {
888 g_unsetenv ("DISPLAY");
889 g_unsetenv ("DBUS_SESSION_BUS_ADDRESS");
890 g_unsetenv ("DBUS_STARTER_ADDRESS");
891 g_unsetenv ("DBUS_STARTER_BUS_TYPE");
892 /* avoid using XDG_RUNTIME_DIR/bus */
893 g_unsetenv ("XDG_RUNTIME_DIR");
894 }
895