diff --git a/tests/graphics.c b/tests/graphics.c
index 4969df6a04b1cf9adc25fff8630d5a6d5419b542..2eb4b280f86c81d8dc65d75ef8f0debc16925d42 100644
--- a/tests/graphics.c
+++ b/tests/graphics.c
@@ -44,12 +44,13 @@
 #include "test-utils.h"
 
 static const char *argv0;
+static gchar *global_sysroots;
 
 typedef struct
 {
   gchar *srcdir;
   gchar *builddir;
-  gchar *sysroots;
+  const gchar *sysroots;
   gchar *sysroot;
   gchar **fake_icds_envp;
 } Fixture;
@@ -82,7 +83,7 @@ setup (Fixture *f,
   if (f->builddir == NULL)
     f->builddir = g_path_get_dirname (argv0);
 
-  f->sysroots = g_build_filename (f->builddir, "sysroots", NULL);
+  f->sysroots = global_sysroots;
 
   if (g_chdir (f->srcdir) != 0)
     g_error ("chdir %s: %s", f->srcdir, g_strerror (errno));
@@ -193,7 +194,6 @@ teardown (Fixture *f,
   g_free (f->srcdir);
   g_free (f->builddir);
   g_free (f->sysroot);
-  g_free (f->sysroots);
   g_strfreev (f->fake_icds_envp);
 }
 
@@ -2036,9 +2036,10 @@ test_dri_with_env (Fixture *f,
   g_list_free_full (va_api, g_object_unref);
 
   /* Test relative path.
-   * Move to the build directory because otherwise we can't use the relative sysroots path */
-  if (g_chdir (f->builddir) != 0)
-    g_error ("chdir %s: %s", f->builddir, g_strerror (errno));
+   * Move to the sysroots path because otherwise we can't use the
+   * relative paths */
+  if (g_chdir (global_sysroots) != 0)
+    g_error ("chdir %s: %s", global_sysroots, g_strerror (errno));
   g_free (libgl);
   g_free (libgl2);
   g_free (libgl3);
@@ -2047,12 +2048,12 @@ test_dri_with_env (Fixture *f,
   g_free (libva3);
   g_free (libgl_combined);
   g_free (libva_combined);
-  libgl = g_build_filename ("sysroots", "no-os-release", "custom_path32", "dri", NULL);
-  libgl2 = g_build_filename ("sysroots", "no-os-release", "custom_path32_2", "dri", NULL);
-  libgl3 = g_build_filename ("sysroots", "no-os-release", "custom_path64", "dri", NULL);
-  libva = g_build_filename ("sysroots", "no-os-release", "custom_path32", "va", NULL);
-  libva2 = g_build_filename ("sysroots", "no-os-release", "custom_path32_2", "va", NULL);
-  libva3 = g_build_filename ("sysroots", "no-os-release", "custom_path64", "va", NULL);
+  libgl = g_build_filename ("no-os-release", "custom_path32", "dri", NULL);
+  libgl2 = g_build_filename ("no-os-release", "custom_path32_2", "dri", NULL);
+  libgl3 = g_build_filename ("no-os-release", "custom_path64", "dri", NULL);
+  libva = g_build_filename ("no-os-release", "custom_path32", "va", NULL);
+  libva2 = g_build_filename ("no-os-release", "custom_path32_2", "va", NULL);
+  libva3 = g_build_filename ("no-os-release", "custom_path64", "va", NULL);
   libgl_combined = g_strjoin (":", libgl, libgl2, libgl3, NULL);
   libva_combined = g_strjoin (":", libva, libva2, libva3, NULL);
   envp = g_environ_setenv (envp, "LIBGL_DRIVERS_PATH", libgl_combined, TRUE);
@@ -2285,7 +2286,7 @@ test_vdpau (Fixture *f,
       else
         {
           vdpau_path = g_build_filename (sysroot, test->vdpau_path_env, "vdpau", NULL);
-          vdpau_relative_path = g_build_filename ("sysroots", test->sysroot, test->vdpau_path_env,
+          vdpau_relative_path = g_build_filename (test->sysroot, test->vdpau_path_env,
                                                   "vdpau", NULL);
           envp = g_environ_setenv (envp, "VDPAU_DRIVER_PATH", vdpau_path, TRUE);
         }
@@ -2334,8 +2335,9 @@ test_vdpau (Fixture *f,
         {
           envp = g_environ_setenv (envp, "VDPAU_DRIVER_PATH", vdpau_relative_path, TRUE);
           /* Move to the build directory because otherwise we can't use the relative sysroots path */
-          if (g_chdir (f->builddir) != 0)
-            g_error ("chdir %s: %s", f->builddir, g_strerror (errno));
+          if (g_chdir (global_sysroots) != 0)
+            g_error ("chdir %s: %s", global_sysroots, g_strerror (errno));
+
           srt_system_info_set_environ (info, envp);
           vdpau = srt_system_info_list_vdpau_drivers (info, test->multiarch_tuple, SRT_DRIVER_FLAGS_NONE);
           check_list_suffixes (vdpau, test->vdpau_suffixes, SRT_GRAPHICS_VDPAU_MODULE);
@@ -2694,7 +2696,10 @@ int
 main (int argc,
       char **argv)
 {
+  int ret;
+
   argv0 = argv[0];
+  global_sysroots = _srt_global_setup_sysroots (argv0);
 
   g_test_init (&argc, &argv, NULL);
   g_test_add ("/graphics/object", Fixture, NULL,
@@ -2752,5 +2757,8 @@ main (int argc,
   g_test_add ("/graphics/glx/container", Fixture, NULL,
               setup, test_glx_container, teardown);
 
-  return g_test_run ();
+  ret = g_test_run ();
+  _srt_global_teardown_sysroots ();
+  g_clear_pointer (&global_sysroots, g_free);
+  return ret;
 }
diff --git a/tests/meson.build b/tests/meson.build
index 8969a5c0634e26344a62364fb0dc1c4a677028ae..7be9db8e74b57348de1cbdfab74c69cbf8c029d8 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -61,6 +61,10 @@ test_utils = static_library(
     'fake-home.c',
     'test-utils.c',
   ],
+  c_args : [
+    '-D_SRT_MULTIARCH="' + multiarch + '"',
+    '-D_SRT_PYTHON="' + python.path() + '"',
+  ],
   dependencies : [
     glib,
     gobject,
@@ -116,18 +120,7 @@ install_subdir('fake-steam-runtime', install_dir : tests_dir)
 install_subdir('input-monitor-outputs', install_dir : tests_dir)
 install_subdir('json-report', install_dir : tests_dir)
 
-meson.add_postconf_script(
-  python.path(),
-  join_paths(meson.current_source_dir(), 'generate-sysroots.py'),
-  join_paths(meson.current_build_dir(), 'sysroots'),
-)
-
-meson.add_install_script(
-  python.path(),
-  join_paths(meson.current_source_dir(), 'generate-sysroots.py'),
-  '--install',
-  join_paths(tests_dir, 'sysroots'),
-)
+install_data('generate-sysroots.py', install_dir : tests_dir)
 
 # These are all the same: they just exit 0.
 foreach helper : [
diff --git a/tests/system-info.c b/tests/system-info.c
index 63597d177eb4b2fb898b0237996ac073d46d9d67..8a9facc03093cbf8a49a8e275403c02f45d9bef0 100644
--- a/tests/system-info.c
+++ b/tests/system-info.c
@@ -46,12 +46,13 @@
 
 static const char *argv0;
 static gchar *fake_home_path;
+static gchar *global_sysroots;
 
 typedef struct
 {
   gchar *srcdir;
   gchar *builddir;
-  gchar *sysroots;
+  const gchar *sysroots;
 } Fixture;
 
 typedef struct
@@ -74,7 +75,7 @@ setup (Fixture *f,
   if (f->builddir == NULL)
     f->builddir = g_path_get_dirname (argv0);
 
-  f->sysroots = g_build_filename (f->builddir, "sysroots", NULL);
+  f->sysroots = global_sysroots;
 }
 
 static void
@@ -85,7 +86,6 @@ teardown (Fixture *f,
 
   g_free (f->srcdir);
   g_free (f->builddir);
-  g_free (f->sysroots);
 
   /* We expect that fake_home already cleaned this up, but just to be sure we
    * do it too */
@@ -3676,6 +3676,7 @@ main (int argc,
    * doesn't support a custom environ. */
   g_test_init (&argc, &argv, NULL);
   fake_home_path = _srt_global_setup_private_xdg_dirs ();
+  global_sysroots = _srt_global_setup_sysroots (argv0);
 
   g_test_add ("/system-info/object", Fixture, NULL,
               setup, test_object, teardown);
@@ -3762,5 +3763,8 @@ main (int argc,
   if (!_srt_global_teardown_private_xdg_dirs ())
     g_debug ("Unable to remove the fake home parent directory of: %s", fake_home_path);
 
+  _srt_global_teardown_sysroots ();
+  g_clear_pointer (&global_sysroots, g_free);
+
   return status;
 }
diff --git a/tests/test-utils.c b/tests/test-utils.c
index 7d8836d2e921efda6147aa1f27e598824d8b041d..0255d30d9e2e633d46f9e3a207cc7524b124c2b9 100644
--- a/tests/test-utils.c
+++ b/tests/test-utils.c
@@ -98,6 +98,83 @@ _srt_global_teardown_private_xdg_dirs (void)
   return result;
 }
 
+static gchar *sysroots_parent = NULL;
+
+/**
+ * _srt_global_setup_sysroots:
+ * @argv0: `argv[0]`
+ *
+ * Create mock sysroots in a temporary directory.
+ *
+ * Returns: (type filename) (transfer full): Absolute path to the newly created
+ *  sysroots directory.
+ */
+gchar *
+_srt_global_setup_sysroots (const char *argv0)
+{
+  g_autoptr(GError) error = NULL;
+  g_autofree gchar *srcdir = NULL;
+  g_autofree gchar *sysroots = NULL;
+  g_autofree gchar *generate_sysroots = NULL;
+  g_autofree gchar *out = NULL;
+  g_autofree gchar *err = NULL;
+  const char *argv[] =
+  {
+    _SRT_PYTHON,
+    "<generate_sysroots.py>",
+    "<sysroot>",
+    NULL
+  };
+  int wait_status;
+
+  g_return_val_if_fail (sysroots_parent == NULL, NULL);
+
+  /* Create a directory that we control, and then put the fake home
+   * directory inside it, so we can delete and recreate the fake home
+   * directory without being vulnerable to symlink attacks. */
+  sysroots_parent = g_dir_make_tmp ("srt-test-XXXXXX", &error);
+  g_assert_no_error (error);
+  sysroots = g_build_filename (sysroots_parent, "sysroots", NULL);
+
+  srcdir = g_strdup (g_getenv ("G_TEST_SRCDIR"));
+
+  if (srcdir == NULL)
+    srcdir = g_path_get_dirname (argv0);
+
+  generate_sysroots = g_build_filename (srcdir, "generate-sysroots.py", NULL);
+  argv[1] = generate_sysroots;
+  argv[2] = sysroots;
+
+  g_spawn_sync (NULL, (gchar **) argv, NULL, G_SPAWN_SEARCH_PATH,
+                NULL, NULL, &out, &err, &wait_status, &error);
+  g_assert_no_error (error);
+  g_test_message ("stdout from generate-sysroots.py:\n%s", out);
+  g_test_message ("stderr from generate-sysroots.py:\n%s", err);
+  g_assert_cmpint (wait_status, ==, 0);
+
+  return g_steal_pointer (&sysroots);
+}
+
+/**
+ * _srt_global_teardown_sysroots:
+ *
+ * Tear down the previously created temporary directory.
+ *
+ * Returns: %TRUE if no errors occurred removing the temporary directory.
+ */
+gboolean
+_srt_global_teardown_sysroots (void)
+{
+  gboolean result;
+  g_return_val_if_fail (sysroots_parent != NULL, FALSE);
+
+  result = _srt_rm_rf (sysroots_parent);
+  g_free (sysroots_parent);
+  sysroots_parent = NULL;
+
+  return result;
+}
+
 TestsOpenFdSet
 tests_check_fd_leaks_enter (void)
 {
diff --git a/tests/test-utils.h b/tests/test-utils.h
index 5a86dcae9efa4918b9f9b41931c1860fc903cbdb..3bcc6840e8a659f620d2fe1fa0c0aeb8c1c95154 100644
--- a/tests/test-utils.h
+++ b/tests/test-utils.h
@@ -66,3 +66,6 @@ gboolean _srt_global_teardown_private_xdg_dirs (void);
 typedef GHashTable *TestsOpenFdSet;
 TestsOpenFdSet tests_check_fd_leaks_enter (void);
 void tests_check_fd_leaks_leave (TestsOpenFdSet fds);
+
+gchar *_srt_global_setup_sysroots (const char *argv0);
+gboolean _srt_global_teardown_sysroots (void);
diff --git a/tests/xdg-portal.c b/tests/xdg-portal.c
index 305105435ed35cdc5177964cab01b702091ab9e1..40dc7f43fb66ca9cec05f925395a5746024006a6 100644
--- a/tests/xdg-portal.c
+++ b/tests/xdg-portal.c
@@ -37,6 +37,7 @@
 #include "test-utils.h"
 
 static const char *argv0;
+static gchar *global_sysroots;
 
 typedef struct
 {
@@ -210,7 +211,7 @@ test_check_xdg_portal (Fixture *f,
       srt_system_info_set_test_flags (info, t->test_flags);
       if (t->sysroot != NULL)
         {
-          sysroot = g_build_filename (f->builddir, "sysroots", t->sysroot, NULL);
+          sysroot = g_build_filename (global_sysroots, t->sysroot, NULL);
           srt_system_info_set_sysroot (info, sysroot);
         }
 
@@ -247,11 +248,17 @@ int
 main (int argc,
       char **argv)
 {
+  int ret;
+
   argv0 = argv[0];
+  global_sysroots = _srt_global_setup_sysroots (argv0);
 
   g_test_init (&argc, &argv, NULL);
   g_test_add ("/xdg-portal/test_check_xdg_portal", Fixture, NULL, setup,
               test_check_xdg_portal, teardown);
 
-  return g_test_run ();
+  ret = g_test_run ();
+  _srt_global_teardown_sysroots ();
+  g_clear_pointer (&global_sysroots, g_free);
+  return ret;
 }