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