summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon McVittie <smcv@collabora.com>2021-05-21 15:56:31 +0100
committerAlexander Larsson <alexander.larsson@gmail.com>2021-05-25 11:11:03 +0200
commit412c15772f794fd300991a91f2e0fe51f615bf79 (patch)
tree259bd54f4f05715fe6986665c3290b0b73e16df1
parentafd0cc4d184fe1b58d06587e865dc05fcfa38da7 (diff)
downloadflatpak-412c15772f794fd300991a91f2e0fe51f615bf79.tar.gz
portal: Add some test coverage
This exercises Spawn() and reproduces #4286. Signed-off-by: Simon McVittie <smcv@collabora.com>
-rw-r--r--tests/Makefile.am.inc16
-rw-r--r--tests/mock-flatpak.c87
-rw-r--r--tests/test-portal.c483
-rw-r--r--tests/testlib.c146
-rw-r--r--tests/testlib.h13
5 files changed, 743 insertions, 2 deletions
diff --git a/tests/Makefile.am.inc b/tests/Makefile.am.inc
index c9a73dd1..8adf2b3e 100644
--- a/tests/Makefile.am.inc
+++ b/tests/Makefile.am.inc
@@ -1,5 +1,6 @@
AM_TESTS_ENVIRONMENT = FLATPAK_TESTS_DEBUG=1 \
FLATPAK_CONFIG_DIR=/dev/null \
+ FLATPAK_PORTAL=$$(cd $(top_builddir) && pwd)/flatpak-portal \
FLATPAK_TRIGGERSDIR=$$(cd $(top_srcdir) && pwd)/triggers \
FLATPAK_VALIDATE_ICON=$$(cd $(top_builddir) && pwd)/flatpak-validate-icon \
FLATPAK_REVOKEFS_FUSE=$$(cd $(top_builddir) && pwd)/revokefs-fuse \
@@ -65,6 +66,7 @@ testcommon_CFLAGS = \
$(JSON_CFLAGS) \
$(APPSTREAM_GLIB_CFLAGS) \
-DFLATPAK_COMPILATION \
+ -DLIBEXECDIR=\"$(libexecdir)\" \
-I$(srcdir)/app \
-I$(builddir)/app \
$(NULL)
@@ -98,6 +100,14 @@ test_instance_SOURCES = \
tests/test-instance.c \
$(NULL)
+test_portal_CFLAGS = $(testcommon_CFLAGS)
+test_portal_LDADD = $(testcommon_LDADD) libtestlib.la
+test_portal_SOURCES = \
+ portal/flatpak-portal-dbus.c \
+ portal/flatpak-portal-dbus.h \
+ tests/test-portal.c \
+ $(NULL)
+
tests_hold_lock_CFLAGS = $(AM_CFLAGS) $(BASE_CFLAGS)
tests_hold_lock_LDADD = $(AM_LDADD) $(BASE_LIBS) libglnx.la
tests_hold_lock_SOURCES = tests/hold-lock.c
@@ -108,6 +118,10 @@ tests_httpcache_CFLAGS = $(AM_CFLAGS) $(BASE_CFLAGS) $(OSTREE_CFLAGS) $(SOUP_CFL
tests_httpcache_LDADD = $(AM_LDADD) $(BASE_LIBS) $(OSTREE_LIBS) $(SOUP_LIBS) $(JSON_LIBS) $(APPSTREAM_GLIB_LIBS) \
libflatpak-common.la libflatpak-common-base.la libglnx.la
+tests_mock_flatpak_CFLAGS = $(testcommon_CFLAGS)
+tests_mock_flatpak_LDADD = $(testcommon_LDADD) libtestlib.la
+tests_mock_flatpak_SOURCES = tests/mock-flatpak.c
+
tests_test_update_portal_CFLAGS = $(AM_CFLAGS) $(BASE_CFLAGS) \
-DFLATPAK_COMPILATION \
-DLOCALEDIR=\"$(localedir)\"
@@ -288,6 +302,7 @@ test_programs = \
test-context \
test-exports \
test-instance \
+ test-portal \
testcommon \
testlibrary \
$(NULL)
@@ -296,6 +311,7 @@ test_extra_programs = \
tests/hold-lock \
tests/httpcache \
tests/list-unused \
+ tests/mock-flatpak \
tests/test-authenticator \
tests/test-portal-impl \
tests/test-update-portal \
diff --git a/tests/mock-flatpak.c b/tests/mock-flatpak.c
new file mode 100644
index 00000000..03ea3d2b
--- /dev/null
+++ b/tests/mock-flatpak.c
@@ -0,0 +1,87 @@
+/*
+ * Copyright © 2018-2021 Collabora Ltd.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "libglnx.h"
+
+#include <glib.h>
+#include <gio/gio.h>
+
+#include "flatpak-context-private.h"
+
+int
+main (int argc,
+ char **argv)
+{
+ int i;
+
+ g_debug ("This is a mock implementation of `flatpak run` for the portal");
+
+ for (i = 0; i < argc; i++)
+ g_print ("argv[%d] = %s\n", i, argv[i]);
+
+ for (i = 0; i < argc; i++)
+ {
+ if (g_str_has_prefix (argv[i], "--env-fd="))
+ {
+ g_autoptr(FlatpakContext) context = flatpak_context_new ();
+ const char *value = argv[i] + strlen ("--env-fd=");
+ g_autoptr(GError) error = NULL;
+ guint64 fd;
+ gchar *endptr;
+ GHashTableIter iter;
+ gpointer k, v;
+
+ fd = g_ascii_strtoull (value, &endptr, 10);
+
+ if (endptr == NULL || *endptr != '\0' || fd > G_MAXINT)
+ g_error ("Not a valid file descriptor: %s", value);
+
+ flatpak_context_parse_env_fd (context, (int) fd, &error);
+ g_assert_no_error (error);
+
+ g_hash_table_iter_init (&iter, context->env_vars);
+
+ while (g_hash_table_iter_next (&iter, &k, &v))
+ g_print ("env[%s] = %s\n", (const char *) k, (const char *) v);
+ }
+ }
+
+ for (i = 0; i < 256; i++)
+ {
+ struct stat stat_buf;
+
+ if (fstat (i, &stat_buf) < 0)
+ {
+ int saved_errno = errno;
+
+ g_assert_cmpint (saved_errno, ==, EBADF);
+ }
+ else
+ {
+ g_print ("fd[%d] = (dev=%" G_GUINT64_FORMAT " ino=%" G_GUINT64_FORMAT ")\n",
+ i,
+ (guint64) stat_buf.st_dev,
+ (guint64) stat_buf.st_ino);
+ }
+ }
+
+ return 0;
+}
diff --git a/tests/test-portal.c b/tests/test-portal.c
new file mode 100644
index 00000000..35789061
--- /dev/null
+++ b/tests/test-portal.c
@@ -0,0 +1,483 @@
+/*
+ * Copyright © 2018-2021 Collabora Ltd.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "libglnx.h"
+
+#include <glib.h>
+#include <gio/gio.h>
+#include <gio/gunixfdlist.h>
+
+#include "portal/flatpak-portal.h"
+#include "portal/flatpak-portal-dbus.h"
+#include "testlib.h"
+
+typedef struct
+{
+ TestsDBusDaemon dbus_daemon;
+ GSubprocess *portal;
+ gchar *portal_path;
+ gchar *mock_flatpak;
+ PortalFlatpak *proxy;
+ GDBusConnection *conn;
+} Fixture;
+
+typedef struct
+{
+ int dummy;
+} Config;
+
+static void
+setup (Fixture *f,
+ gconstpointer context G_GNUC_UNUSED)
+{
+ g_autoptr(GError) error = NULL;
+
+ tests_dbus_daemon_setup (&f->dbus_daemon);
+
+ f->portal_path = g_strdup (g_getenv ("FLATPAK_PORTAL"));
+
+ if (f->portal_path == NULL)
+ f->portal_path = g_strdup (LIBEXECDIR "/flatpak-portal");
+
+ f->mock_flatpak = g_test_build_filename (G_TEST_BUILT, "mock-flatpak", NULL);
+
+ f->conn = g_dbus_connection_new_for_address_sync (f->dbus_daemon.dbus_address,
+ (G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT |
+ G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION),
+ NULL, NULL, &error);
+ g_assert_no_error (error);
+ g_assert_nonnull (f->conn);
+}
+
+/* Don't corrupt TAP output if portal outputs on stdout */
+static void
+launcher_stdout_to_our_stderr (GSubprocessLauncher *launcher)
+{
+ glnx_autofd int stderr_copy = -1;
+
+ stderr_copy = dup (STDERR_FILENO);
+ g_assert_no_errno (stderr_copy);
+ g_assert_no_errno (fcntl (stderr_copy, F_SETFD, FD_CLOEXEC));
+ g_subprocess_launcher_take_stdout_fd (launcher, glnx_steal_fd (&stderr_copy));
+}
+
+static GSubprocessLauncher *
+fixture_make_launcher (Fixture *f)
+{
+ g_autoptr(GSubprocessLauncher) launcher = NULL;
+
+ launcher = g_subprocess_launcher_new (G_SUBPROCESS_FLAGS_NONE);
+ launcher_stdout_to_our_stderr (launcher);
+ g_subprocess_launcher_setenv (launcher,
+ "DBUS_SESSION_BUS_ADDRESS",
+ f->dbus_daemon.dbus_address,
+ TRUE);
+ g_subprocess_launcher_setenv (launcher,
+ "FLATPAK_PORTAL_MOCK_FLATPAK",
+ f->mock_flatpak,
+ TRUE);
+
+ return g_steal_pointer (&launcher);
+}
+
+static void
+name_appeared_cb (GDBusConnection *conn,
+ const char *name,
+ const char *owner,
+ gpointer user_data)
+{
+ gchar **name_owner_p = user_data;
+
+ g_clear_pointer (name_owner_p, g_free);
+ *name_owner_p = g_strdup (owner);
+}
+
+static void
+name_vanished_cb (GDBusConnection *conn,
+ const char *name,
+ gpointer user_data)
+{
+ gchar **name_owner_p = user_data;
+
+ g_clear_pointer (name_owner_p, g_free);
+ *name_owner_p = g_strdup ("");
+}
+
+static void
+name_owner_watch_removed_cb (gpointer user_data)
+{
+ gchar **name_owner_p = user_data;
+
+ g_clear_pointer (name_owner_p, g_free);
+}
+
+static void
+fixture_wait_for_name_to_be_owned (Fixture *f,
+ const char *name)
+{
+ g_autofree gchar *name_owner = NULL;
+ guint watch;
+
+ watch = g_bus_watch_name_on_connection (f->conn,
+ name,
+ G_BUS_NAME_WATCHER_FLAGS_NONE,
+ name_appeared_cb,
+ name_vanished_cb,
+ &name_owner,
+ name_owner_watch_removed_cb);
+
+ /* Wait for name to become owned */
+ while (name_owner == NULL || name_owner[0] == '\0')
+ g_main_context_iteration (NULL, TRUE);
+
+ g_bus_unwatch_name (watch);
+
+ /* Wait for watch to be cleaned up */
+ while (name_owner != NULL)
+ g_main_context_iteration (NULL, TRUE);
+}
+
+static void
+fixture_start_portal (Fixture *f)
+{
+ g_autoptr(GError) error = NULL;
+ g_autoptr(GSubprocessLauncher) launcher = NULL;
+
+ launcher = fixture_make_launcher (f);
+ f->portal = g_subprocess_launcher_spawn (launcher, &error,
+ f->portal_path,
+ NULL);
+ g_assert_no_error (error);
+ g_assert_nonnull (f->portal);
+
+ fixture_wait_for_name_to_be_owned (f, FLATPAK_PORTAL_BUS_NAME);
+
+ f->proxy = portal_flatpak_proxy_new_sync (f->conn,
+ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
+ FLATPAK_PORTAL_BUS_NAME,
+ FLATPAK_PORTAL_PATH,
+ NULL,
+ &error);
+ g_assert_no_error (error);
+ g_assert_nonnull (f->proxy);
+}
+
+static void
+test_help (Fixture *f,
+ gconstpointer context G_GNUC_UNUSED)
+{
+ g_autoptr(GSubprocessLauncher) launcher = NULL;
+ g_autofree gchar *stdout_buf;
+ g_autofree gchar *stderr_buf;
+ g_autoptr(GError) error = NULL;
+
+ /* Don't use fixture_make_launcher() here because we want to
+ * capture stdout */
+ launcher = g_subprocess_launcher_new (G_SUBPROCESS_FLAGS_STDOUT_PIPE |
+ G_SUBPROCESS_FLAGS_STDERR_PIPE);
+ g_subprocess_launcher_setenv (launcher,
+ "DBUS_SESSION_BUS_ADDRESS",
+ f->dbus_daemon.dbus_address,
+ TRUE);
+
+ f->portal = g_subprocess_launcher_spawn (launcher, &error,
+ f->portal_path,
+ "--help",
+ NULL);
+ g_assert_no_error (error);
+ g_assert_nonnull (f->portal);
+
+ g_subprocess_communicate_utf8 (f->portal, NULL, NULL, &stdout_buf,
+ &stderr_buf, &error);
+ g_assert_nonnull (stdout_buf);
+ g_assert_nonnull (stderr_buf);
+ g_test_message ("flatpak-portal --help: %s", stdout_buf);
+ g_assert_true (strstr (stdout_buf, "--replace") != NULL);
+
+ g_subprocess_wait_check (f->portal, NULL, &error);
+ g_assert_no_error (error);
+}
+
+static void
+count_successful_exit_cb (PortalFlatpak *proxy,
+ guint pid,
+ guint wait_status,
+ gpointer user_data)
+{
+ gsize *times_exited_p = user_data;
+
+ g_debug ("Process %u exited with wait status %u", pid, wait_status);
+ g_assert_true (WIFEXITED (wait_status));
+ g_assert_cmpuint (WEXITSTATUS (wait_status), ==, 0);
+ (*times_exited_p) += 1;
+}
+
+static void
+test_basic (Fixture *f,
+ gconstpointer context G_GNUC_UNUSED)
+{
+ g_autoptr(GError) error = NULL;
+ g_autoptr(GUnixFDList) fds_out = NULL;
+ guint pid;
+ gboolean ok;
+ const char * const argv[] = { "hello", NULL };
+ gsize times_exited = 0;
+ gulong handler_id;
+
+ fixture_start_portal (f);
+
+ /* We can't easily tell whether EXPOSE_PIDS ought to be set or not */
+ g_assert_cmpuint ((portal_flatpak_get_supports (f->proxy) &
+ (~FLATPAK_SPAWN_SUPPORT_FLAGS_EXPOSE_PIDS)), ==, 0);
+ g_assert_cmpuint (portal_flatpak_get_version (f->proxy), ==, 6);
+
+ handler_id = g_signal_connect (f->proxy, "spawn-exited",
+ G_CALLBACK (count_successful_exit_cb),
+ &times_exited);
+
+ ok = portal_flatpak_call_spawn_sync (f->proxy,
+ "/", /* cwd */
+ argv, /* argv */
+ g_variant_new ("a{uh}", NULL),
+ g_variant_new ("a{ss}", NULL),
+ FLATPAK_SPAWN_FLAGS_NONE,
+ g_variant_new ("a{sv}", NULL),
+ NULL, /* fd list */
+ &pid,
+ &fds_out,
+ NULL,
+ &error);
+ g_assert_no_error (error);
+ g_assert_true (ok);
+ g_assert_cmpuint (pid, >, 1);
+
+ while (times_exited == 0)
+ g_main_context_iteration (NULL, TRUE);
+
+ g_signal_handler_disconnect (f->proxy, handler_id);
+
+ if (fds_out != NULL)
+ g_assert_cmpint (g_unix_fd_list_get_length (fds_out), ==, 0);
+
+ g_subprocess_send_signal (f->portal, SIGTERM);
+ g_subprocess_wait (f->portal, NULL, &error);
+ g_assert_no_error (error);
+}
+
+static void
+test_fd_passing (Fixture *f,
+ gconstpointer context G_GNUC_UNUSED)
+{
+#define SOME_FDS 16
+ g_autoptr(GError) error = NULL;
+ char *tempfile_paths[SOME_FDS];
+ int tempfile_fds[SOME_FDS];
+ guint gap_size;
+ gsize times_exited = 0;
+ gulong handler_id;
+ gsize i;
+
+ fixture_start_portal (f);
+
+ handler_id = g_signal_connect (f->proxy, "spawn-exited",
+ G_CALLBACK (count_successful_exit_cb),
+ &times_exited);
+
+ for (i = 0; i < SOME_FDS; i++)
+ {
+ tempfile_paths[i] = g_strdup ("/tmp/flatpak-portal-test.XXXXXX");
+ tempfile_fds[i] = g_mkstemp (tempfile_paths[i]);
+ g_assert_no_errno (tempfile_fds[i]);
+ }
+
+ /* Using a non-contiguous block of fds can help to tickle bugs in the
+ * portal. */
+ for (gap_size = 0; gap_size < 128; gap_size += 16)
+ {
+ g_autoptr(GUnixFDList) fds_in = g_unix_fd_list_new ();
+ g_autoptr(GUnixFDList) fds_out = NULL;
+ g_auto(GVariantBuilder) fd_map_builder = {};
+ g_auto(GVariantBuilder) env_builder = {};
+ guint pid;
+ gboolean ok;
+ const char * const argv[] = { "hello", NULL };
+ g_autofree char *output = NULL;
+
+ g_variant_builder_init (&fd_map_builder, G_VARIANT_TYPE ("a{uh}"));
+ g_variant_builder_init (&env_builder, G_VARIANT_TYPE ("a{ss}"));
+ times_exited = 0;
+
+ g_variant_builder_add (&env_builder, "{ss}", "FOO", "bar");
+
+ for (i = 0; i < SOME_FDS; i++)
+ {
+ int handle = g_unix_fd_list_append (fds_in, tempfile_fds[i], &error);
+ guint32 desired_fd;
+
+ g_assert_no_error (error);
+ g_assert_cmpint (handle, >=, 0);
+
+ if (i <= STDERR_FILENO)
+ desired_fd = i;
+ else
+ desired_fd = i + gap_size;
+
+ g_variant_builder_add (&fd_map_builder, "{uh}",
+ desired_fd, (gint32) handle);
+ }
+
+ ok = portal_flatpak_call_spawn_sync (f->proxy,
+ "/", /* cwd */
+ argv, /* argv */
+ g_variant_builder_end (&fd_map_builder),
+ g_variant_builder_end (&env_builder),
+ FLATPAK_SPAWN_FLAGS_NONE,
+ g_variant_new ("a{sv}", NULL),
+ fds_in,
+ &pid,
+ &fds_out,
+ NULL,
+ &error);
+ g_assert_no_error (error);
+ g_assert_true (ok);
+ g_assert_cmpuint (pid, >, 1);
+
+ /* Wait for this one to exit */
+ while (times_exited == 0)
+ g_main_context_iteration (NULL, TRUE);
+
+ if (fds_out != NULL)
+ g_assert_cmpint (g_unix_fd_list_get_length (fds_out), ==, 0);
+
+ /* stdout from the portal should have ended up in temp file [1] */
+ g_assert_no_errno (lseek (tempfile_fds[1], 0, SEEK_SET));
+ output = glnx_fd_readall_utf8 (tempfile_fds[1], NULL, NULL, &error);
+ g_assert_no_error (error);
+ g_assert_nonnull (output);
+ g_assert_no_errno (lseek (tempfile_fds[1], 0, SEEK_SET));
+ g_assert_no_errno (ftruncate (tempfile_fds[1], 0));
+ g_test_message ("Output from mock Flatpak: %s", output);
+
+ if (strstr (output, "env[FOO] = bar") != NULL)
+ g_test_message ("Found env[FOO] = bar in output");
+ else
+ g_error ("env[FOO] = bar not found in \"%s\"", output);
+
+ for (i = 0; i < SOME_FDS; i++)
+ {
+ struct stat stat_buf;
+ g_autofree char *expected = NULL;
+ int desired_fd;
+
+ g_assert_no_errno (fstat (tempfile_fds[i], &stat_buf));
+
+ if (i <= STDERR_FILENO)
+ desired_fd = i;
+ else
+ desired_fd = i + gap_size;
+
+ expected = g_strdup_printf ("fd[%d] = (dev=%" G_GUINT64_FORMAT " ino=%" G_GUINT64_FORMAT ")",
+ desired_fd,
+ (guint64) stat_buf.st_dev,
+ (guint64) stat_buf.st_ino);
+
+ if (strstr (output, expected) != NULL)
+ g_test_message ("fd %d OK: \"%s\"", desired_fd, expected);
+ else
+ g_error ("\"%s\" not found in \"%s\"", expected, output);
+ }
+ }
+
+ g_signal_handler_disconnect (f->proxy, handler_id);
+
+ g_subprocess_send_signal (f->portal, SIGTERM);
+ g_subprocess_wait (f->portal, NULL, &error);
+ g_assert_no_error (error);
+
+ for (i = 0; i < SOME_FDS; i++)
+ {
+ g_assert_no_errno (unlink (tempfile_paths[i]));
+ glnx_close_fd (&tempfile_fds[i]);
+ g_free (tempfile_paths[i]);
+ }
+}
+
+static void
+test_replace (Fixture *f,
+ gconstpointer context G_GNUC_UNUSED)
+{
+ g_autoptr(GSubprocessLauncher) launcher = NULL;
+ g_autoptr(GError) error = NULL;
+ g_autoptr(GSubprocess) gets_replaced = NULL;
+
+ /* Not using fixture_start_portal() here because we want to --replace */
+ launcher = fixture_make_launcher (f);
+ gets_replaced = g_subprocess_launcher_spawn (launcher, &error,
+ f->portal_path,
+ "--replace",
+ NULL);
+ g_assert_no_error (error);
+ g_assert_nonnull (gets_replaced);
+
+ fixture_wait_for_name_to_be_owned (f, FLATPAK_PORTAL_BUS_NAME);
+
+ g_clear_object (&launcher);
+ launcher = fixture_make_launcher (f);
+ f->portal = g_subprocess_launcher_spawn (launcher, &error,
+ f->portal_path,
+ "--replace",
+ NULL);
+ g_assert_no_error (error);
+ g_assert_nonnull (f->portal);
+
+ /* f->portal replaces gets_replaced, which exits 0 */
+ g_subprocess_wait_check (gets_replaced, NULL, &error);
+ g_assert_no_error (error);
+
+ g_subprocess_send_signal (f->portal, SIGTERM);
+ g_subprocess_wait (f->portal, NULL, &error);
+ g_assert_no_error (error);
+}
+
+static void
+teardown (Fixture *f,
+ gconstpointer context G_GNUC_UNUSED)
+{
+ tests_dbus_daemon_teardown (&f->dbus_daemon);
+ g_clear_object (&f->portal);
+ g_free (f->portal_path);
+}
+
+int
+main (int argc,
+ char **argv)
+{
+ g_test_init (&argc, &argv, NULL);
+
+ g_test_add ("/help", Fixture, NULL, setup, test_help, teardown);
+ g_test_add ("/basic", Fixture, NULL, setup, test_basic, teardown);
+ g_test_add ("/fd-passing", Fixture, NULL, setup, test_fd_passing, teardown);
+ g_test_add ("/replace", Fixture, NULL, setup, test_replace, teardown);
+
+ return g_test_run ();
+}
diff --git a/tests/testlib.c b/tests/testlib.c
index c8f85446..28c97a4d 100644
--- a/tests/testlib.c
+++ b/tests/testlib.c
@@ -1,5 +1,5 @@
/*
- * Copyright © 2020-2021 Collabora Ltd.
+ * Copyright © 2018-2021 Collabora Ltd.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -95,3 +95,147 @@ isolated_test_dir_global_teardown (void)
g_free (isolated_test_dir);
isolated_test_dir = NULL;
}
+
+static void
+replace_tokens (const char *in_path,
+ const char *out_path)
+{
+ g_autoptr(GError) error = NULL;
+ g_autoptr(GString) buffer = NULL;
+ g_autofree char *contents = NULL;
+ const char *iter;
+
+ g_file_get_contents (in_path, &contents, NULL, &error);
+ g_assert_no_error (error);
+
+ buffer = g_string_new ("");
+ iter = contents;
+
+ while (iter[0] != '\0')
+ {
+ const char *first_at = strchr (iter, '@');
+ const char *second_at;
+
+ if (first_at == NULL)
+ {
+ /* no more @token@s, append [iter..end] and stop */
+ g_string_append (buffer, iter);
+ break;
+ }
+
+ second_at = strchr (first_at + 1, '@');
+
+ if (second_at == NULL)
+ g_error ("Unterminated @token@ in %s: %s", in_path, first_at);
+
+ /* append the literal text [iter..first_at - 1], if non-empty */
+ if (first_at != iter)
+ g_string_append_len (buffer, iter, first_at - iter);
+
+ /* append the replacement for [first_at..second_at] if known */
+ if (g_str_has_prefix (first_at, "@testdir@"))
+ {
+ g_autofree char *testdir = g_test_build_filename (G_TEST_DIST, ".", NULL);
+
+ g_string_append (buffer, testdir);
+ }
+ else
+ {
+ g_error ("Unknown @token@ in %s: %.*s",
+ in_path, (int) (second_at - first_at) + 1, first_at);
+ }
+
+ /* continue to process [second_at + 1..end] */
+ iter = second_at + 1;
+ }
+
+ g_file_set_contents (out_path, buffer->str, -1, &error);
+ g_assert_no_error (error);
+}
+
+void
+tests_dbus_daemon_setup (TestsDBusDaemon *self)
+{
+ g_autoptr(GSubprocessLauncher) launcher = NULL;
+ g_autoptr(GError) error = NULL;
+ g_autofree char *config_arg = NULL;
+ g_autofree char *session_conf = NULL;
+ g_autofree char *session_conf_in = NULL;
+ GInputStream *address_pipe;
+ gchar address_buffer[4096] = { 0 };
+ char *newline;
+
+ g_return_if_fail (self != NULL);
+ g_return_if_fail (self->dbus_daemon == NULL);
+ g_return_if_fail (self->dbus_address == NULL);
+ g_return_if_fail (self->temp_dir == NULL);
+
+ self->temp_dir = g_dir_make_tmp ("flatpak-test.XXXXXX", &error);
+ g_assert_no_error (error);
+ g_assert_nonnull (self->temp_dir);
+
+ session_conf_in = g_test_build_filename (G_TEST_DIST, "session.conf.in", NULL);
+ session_conf = g_build_filename (self->temp_dir, "test-bus.conf", NULL);
+ replace_tokens (session_conf_in, session_conf);
+ config_arg = g_strdup_printf ("--config-file=%s", session_conf);
+ launcher = g_subprocess_launcher_new (G_SUBPROCESS_FLAGS_STDOUT_PIPE);
+ self->dbus_daemon = g_subprocess_launcher_spawn (launcher, &error,
+ "dbus-daemon",
+ config_arg,
+ "--print-address=1",
+ "--nofork",
+ NULL);
+ g_assert_no_error (error);
+ g_assert_nonnull (self->dbus_daemon);
+
+ address_pipe = g_subprocess_get_stdout_pipe (self->dbus_daemon);
+ g_assert_nonnull (address_pipe);
+
+ /* Crash if it takes too long to get the address */
+ alarm (30);
+
+ while (strchr (address_buffer, '\n') == NULL)
+ {
+ if (strlen (address_buffer) >= sizeof (address_buffer) - 1)
+ g_error ("Read %" G_GSIZE_FORMAT " bytes from dbus-daemon with "
+ "no newline",
+ sizeof (address_buffer) - 1);
+
+ g_input_stream_read (address_pipe,
+ address_buffer + strlen (address_buffer),
+ sizeof (address_buffer) - strlen (address_buffer),
+ NULL, &error);
+ g_assert_no_error (error);
+ }
+
+ /* Disable alarm */
+ alarm (0);
+
+ newline = strchr (address_buffer, '\n');
+ g_assert_nonnull (newline);
+ *newline = '\0';
+ self->dbus_address = g_strdup (address_buffer);
+}
+
+void
+tests_dbus_daemon_teardown (TestsDBusDaemon *self)
+{
+ g_autoptr(GError) error = NULL;
+
+ if (self->dbus_daemon != NULL)
+ {
+ g_subprocess_send_signal (self->dbus_daemon, SIGTERM);
+ g_subprocess_wait (self->dbus_daemon, NULL, &error);
+ g_assert_no_error (error);
+ }
+
+ if (self->temp_dir != NULL)
+ {
+ glnx_shutil_rm_rf_at (AT_FDCWD, self->temp_dir, NULL, &error);
+ g_assert_no_error (error);
+ }
+
+ g_clear_object (&self->dbus_daemon);
+ g_clear_pointer (&self->dbus_address, g_free);
+ g_clear_pointer (&self->temp_dir, g_free);
+}
diff --git a/tests/testlib.h b/tests/testlib.h
index 80e73994..213939a2 100644
--- a/tests/testlib.h
+++ b/tests/testlib.h
@@ -1,5 +1,5 @@
/*
- * Copyright © 2020-2021 Collabora Ltd.
+ * Copyright © 2018-2021 Collabora Ltd.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -19,6 +19,7 @@
#define TESTLIB_H
#include <glib.h>
+#include <gio/gio.h>
#ifndef g_assert_no_errno
#define g_assert_no_errno(expr) \
@@ -31,4 +32,14 @@ extern char *isolated_test_dir;
void isolated_test_dir_global_setup (void);
void isolated_test_dir_global_teardown (void);
+typedef struct
+{
+ GSubprocess *dbus_daemon;
+ gchar *dbus_address;
+ gchar *temp_dir;
+} TestsDBusDaemon;
+
+void tests_dbus_daemon_setup (TestsDBusDaemon *self);
+void tests_dbus_daemon_teardown (TestsDBusDaemon *self);
+
#endif