Skip to content
Snippets Groups Projects
Commit 8a0a0f05 authored by Simon McVittie's avatar Simon McVittie
Browse files

tests: Add test coverage for pv_wrap_append_preload


This has almost full branch coverage. We don't currently cover the case
where STEAM_COMPAT_CLIENT_INSTALL_PATH is not set, but that's trivial
anyway (we treat all paths as not matching it).

Signed-off-by: default avatarSimon McVittie <smcv@collabora.com>
parent c9731195
No related branches found
No related tags found
1 merge request!337pressure-vessel: Remap preloadable modules better
......@@ -45,9 +45,16 @@ compiled_tests = [
'bwrap-lock',
'resolve-in-sysroot',
'wait-for-child-processes',
'wrap-setup',
'utils',
]
# The wrap-setup test fails to link unless -lelf is forced in, with undefined
# references to elf_version() etc. It isn't clear why... but this seems to work.
force_libelf = declare_dependency(
link_args : ['-Wl,--no-as-needed,-lelf,--as-needed'],
)
foreach test_name : compiled_tests
exe = executable(
'test-' + test_name,
......@@ -57,6 +64,7 @@ foreach test_name : compiled_tests
libglnx_dep,
test_utils_static_libsteamrt_dep,
pressure_vessel_wrap_lib_dep,
force_libelf,
],
include_directories : pv_include_dirs,
install : false,
......
/*
* Copyright © 2019-2021 Collabora Ltd.
*
* SPDX-License-Identifier: MIT
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <stdlib.h>
#include <unistd.h>
#include <glib.h>
#include <glib/gstdio.h>
#include "steam-runtime-tools/glib-backports-internal.h"
#include "steam-runtime-tools/utils-internal.h"
#include "libglnx/libglnx.h"
#include "tests/test-utils.h"
#include "wrap-setup.h"
#include "utils.h"
typedef struct
{
TestsOpenFdSet old_fds;
gchar *tmpdir;
gchar *mock_host;
gchar *mock_runtime;
gchar *var;
GStrv env;
} Fixture;
typedef struct
{
int unused;
} Config;
static int
open_or_die (const char *path,
int flags,
int mode)
{
glnx_autofd int fd = open (path, flags | O_CLOEXEC, mode);
if (fd >= 0)
return glnx_steal_fd (&fd);
else
g_error ("open(%s, 0x%x): %s", path, flags, g_strerror (errno));
}
static FlatpakExports *
fixture_create_exports (Fixture *f)
{
g_autoptr(FlatpakExports) exports = flatpak_exports_new ();
glnx_autofd int fd = open_or_die (f->mock_host, O_RDONLY | O_DIRECTORY, 0755);
flatpak_exports_take_host_fd (exports, glnx_steal_fd (&fd));
return g_steal_pointer (&exports);
}
static PvRuntime *
fixture_create_runtime (Fixture *f,
PvRuntimeFlags flags)
{
g_autoptr(GError) local_error = NULL;
g_autoptr(PvGraphicsProvider) graphics_provider = NULL;
g_autoptr(PvRuntime) runtime = NULL;
const char *gfx_in_container;
if (flags & PV_RUNTIME_FLAGS_FLATPAK_SUBSANDBOX)
gfx_in_container = "/run/parent";
else
gfx_in_container = "/run/host";
graphics_provider = pv_graphics_provider_new ("/", gfx_in_container,
&local_error);
g_assert_no_error (local_error);
g_assert_nonnull (graphics_provider);
runtime = pv_runtime_new (f->mock_runtime,
"mock_platform_1.0",
f->var,
NULL,
graphics_provider,
environ,
(flags
| PV_RUNTIME_FLAGS_VERBOSE
| PV_RUNTIME_FLAGS_SINGLE_THREAD),
&local_error);
g_assert_no_error (local_error);
g_assert_nonnull (runtime);
return g_steal_pointer (&runtime);
}
static void
setup (Fixture *f,
gconstpointer context)
{
static const char * const touch[] =
{
"app/lib/libpreloadA.so",
"home/me/libpreloadH.so",
"lib/libpreload-rootfs.so",
"steam/lib/gameoverlayrenderer.so",
"usr/lib/libpreloadU.so",
"usr/local/lib/libgtk3-nocsd.so.0",
};
g_autoptr(GError) local_error = NULL;
gsize i;
f->old_fds = tests_check_fd_leaks_enter ();
f->tmpdir = g_dir_make_tmp ("pressure-vessel-tests.XXXXXX", &local_error);
g_assert_no_error (local_error);
f->mock_host = g_build_filename (f->tmpdir, "host", NULL);
f->mock_runtime = g_build_filename (f->tmpdir, "runtime", NULL);
f->var = g_build_filename (f->tmpdir, "var", NULL);
g_assert_no_errno (g_mkdir (f->mock_host, 0755));
g_assert_no_errno (g_mkdir (f->mock_runtime, 0755));
g_assert_no_errno (g_mkdir (f->var, 0755));
for (i = 0; i < G_N_ELEMENTS (touch); i++)
{
g_autofree char *dir = g_path_get_dirname (touch[i]);
g_autofree char *full_dir = g_build_filename (f->mock_host, dir, NULL);
g_autofree char *full_path = g_build_filename (f->mock_host, touch[i], NULL);
g_test_message ("Creating %s", full_dir);
g_assert_no_errno (g_mkdir_with_parents (full_dir, 0755));
g_test_message ("Creating %s", full_path);
g_file_set_contents (full_path, "", 0, &local_error);
g_assert_no_error (local_error);
}
f->env = g_get_environ ();
f->env = g_environ_setenv (f->env, "STEAM_COMPAT_CLIENT_INSTALL_PATH",
"/steam", TRUE);
}
static void
teardown (Fixture *f,
gconstpointer context)
{
g_autoptr(GError) local_error = NULL;
if (f->tmpdir != NULL)
{
glnx_shutil_rm_rf_at (-1, f->tmpdir, NULL, &local_error);
g_assert_no_error (local_error);
}
tests_check_fd_leaks_leave (f->old_fds);
g_clear_pointer (&f->mock_host, g_free);
g_clear_pointer (&f->mock_runtime, g_free);
g_clear_pointer (&f->tmpdir, g_free);
g_clear_pointer (&f->var, g_free);
g_clear_pointer (&f->env, g_strfreev);
}
static void
populate_ld_preload (Fixture *f,
GPtrArray *argv,
PvAppendPreloadFlags flags,
PvRuntime *runtime,
FlatpakExports *exports)
{
static const struct
{
const char *string;
const char *warning;
} preloads[] =
{
{ "" },
{ "" },
{ "/app/lib/libpreloadA.so" },
{ "/lib/libpreload-rootfs.so" },
{ "/usr/lib/libpreloadU.so" },
{ "/home/me/libpreloadH.so" },
{ "/steam/lib/gameoverlayrenderer.so" },
{
"/usr/local/lib/libgtk3-nocsd.so.0",
.warning = "Disabling gtk3-nocsd LD_PRELOAD: it is known to cause crashes.",
},
{ "" },
};
gsize i;
for (i = 0; i < G_N_ELEMENTS (preloads); i++)
{
GLogLevelFlags old_fatal_mask = G_LOG_FATAL_MASK;
/* We expect a warning for libgtk3-nocsd.so.0, but the test framework
* makes warnings and critical warnings fatal, in addition to the
* usual fatal errors. Temporarily relax that to just critical
* warnings and fatal errors. */
if (preloads[i].warning != NULL)
{
old_fatal_mask = g_log_set_always_fatal (G_LOG_FATAL_MASK | G_LOG_LEVEL_CRITICAL);
#if GLIB_CHECK_VERSION(2, 34, 0)
/* We can't check for the message during unit testing when
* compiling with GLib 2.32 from scout, but we can check for it
* in developer builds against a newer GLib. Note that this
* assumes pressure-vessel doesn't define G_LOG_USE_STRUCTURED,
* but that's a GLib 2.50 feature which we are unlikely to use
* while we are still building against scout. */
g_test_expect_message ("pressure-vessel",
G_LOG_LEVEL_WARNING,
preloads[i].warning);
#endif
}
pv_wrap_append_preload (argv,
"LD_PRELOAD",
"--ld-preload",
preloads[i].string,
f->env,
flags,
runtime,
exports);
/* If we modified the fatal mask, put back the old value. */
if (preloads[i].warning != NULL)
{
#if GLIB_CHECK_VERSION(2, 34, 0)
g_test_assert_expected_messages ();
#endif
g_log_set_always_fatal (old_fatal_mask);
}
}
}
static void
test_remap_ld_preload (Fixture *f,
gconstpointer context)
{
g_autoptr(FlatpakExports) exports = fixture_create_exports (f);
g_autoptr(GPtrArray) argv = g_ptr_array_new_with_free_func (g_free);
g_autoptr(PvRuntime) runtime = fixture_create_runtime (f, PV_RUNTIME_FLAGS_NONE);
gsize i;
populate_ld_preload (f, argv, PV_APPEND_PRELOAD_FLAGS_NONE, runtime, exports);
i = 0;
g_assert_cmpuint (argv->len, >, i);
g_assert_cmpstr (g_ptr_array_index (argv, i++),
==, "--ld-preload=/app/lib/libpreloadA.so");
g_assert_cmpuint (argv->len, >, i);
g_assert_cmpstr (g_ptr_array_index (argv, i++),
==, "--ld-preload=/run/host/lib/libpreload-rootfs.so");
g_assert_cmpuint (argv->len, >, i);
g_assert_cmpstr (g_ptr_array_index (argv, i++),
==, "--ld-preload=/run/host/usr/lib/libpreloadU.so");
g_assert_cmpuint (argv->len, >, i);
g_assert_cmpstr (g_ptr_array_index (argv, i++),
==, "--ld-preload=/home/me/libpreloadH.so");
g_assert_cmpuint (argv->len, >, i);
g_assert_cmpstr (g_ptr_array_index (argv, i++),
==, "--ld-preload=/steam/lib/gameoverlayrenderer.so");
g_assert_cmpuint (argv->len, ==, i);
/* FlatpakExports never exports /app */
g_assert_false (flatpak_exports_path_is_visible (exports, "/app"));
g_assert_false (flatpak_exports_path_is_visible (exports, "/app/lib"));
g_assert_false (flatpak_exports_path_is_visible (exports, "/app/lib/libpreloadA.so"));
/* We don't always export /home etc. so we have to explicitly export
* this one */
g_assert_false (flatpak_exports_path_is_visible (exports, "/home"));
g_assert_false (flatpak_exports_path_is_visible (exports, "/home/me"));
g_assert_true (flatpak_exports_path_is_visible (exports, "/home/me/libpreloadH.so"));
/* FlatpakExports never exports /lib as /lib */
g_assert_false (flatpak_exports_path_is_visible (exports, "/lib"));
g_assert_false (flatpak_exports_path_is_visible (exports, "/lib/libpreload-rootfs.so"));
/* FlatpakExports never exports /usr as /usr */
g_assert_false (flatpak_exports_path_is_visible (exports, "/usr"));
g_assert_false (flatpak_exports_path_is_visible (exports, "/usr/lib"));
g_assert_false (flatpak_exports_path_is_visible (exports, "/usr/lib/libpreloadU.so"));
/* We assume STEAM_COMPAT_CLIENT_INSTALL_PATH is dealt with separately */
g_assert_false (flatpak_exports_path_is_visible (exports, "/steam"));
g_assert_false (flatpak_exports_path_is_visible (exports, "/steam/lib"));
g_assert_false (flatpak_exports_path_is_visible (exports, "/steam/lib/gameoverlayrenderer.so"));
}
static void
test_remap_ld_preload_flatpak (Fixture *f,
gconstpointer context)
{
g_autoptr(GPtrArray) argv = g_ptr_array_new_with_free_func (g_free);
g_autoptr(PvRuntime) runtime = fixture_create_runtime (f, PV_RUNTIME_FLAGS_FLATPAK_SUBSANDBOX);
gsize i;
populate_ld_preload (f, argv, PV_APPEND_PRELOAD_FLAGS_FLATPAK_SUBSANDBOX,
runtime, NULL);
i = 0;
g_assert_cmpuint (argv->len, >, i);
g_assert_cmpstr (g_ptr_array_index (argv, i++),
==, "--ld-preload=/run/parent/app/lib/libpreloadA.so");
g_assert_cmpuint (argv->len, >, i);
g_assert_cmpstr (g_ptr_array_index (argv, i++),
==, "--ld-preload=/run/parent/lib/libpreload-rootfs.so");
g_assert_cmpuint (argv->len, >, i);
g_assert_cmpstr (g_ptr_array_index (argv, i++),
==, "--ld-preload=/run/parent/usr/lib/libpreloadU.so");
g_assert_cmpuint (argv->len, >, i);
g_assert_cmpstr (g_ptr_array_index (argv, i++),
==, "--ld-preload=/home/me/libpreloadH.so");
g_assert_cmpuint (argv->len, >, i);
g_assert_cmpstr (g_ptr_array_index (argv, i++),
==, "--ld-preload=/steam/lib/gameoverlayrenderer.so");
g_assert_cmpuint (argv->len, ==, i);
}
/*
* In addition to testing the rare case where there's no runtime,
* this one also exercises PV_APPEND_PRELOAD_FLAGS_REMOVE_GAME_OVERLAY,
* which is the implementation of --remove-game-overlay.
*/
static void
test_remap_ld_preload_no_runtime (Fixture *f,
gconstpointer context)
{
g_autoptr(GPtrArray) argv = g_ptr_array_new_with_free_func (g_free);
g_autoptr(FlatpakExports) exports = fixture_create_exports (f);
gsize i;
populate_ld_preload (f, argv, PV_APPEND_PRELOAD_FLAGS_REMOVE_GAME_OVERLAY,
NULL, exports);
i = 0;
g_assert_cmpuint (argv->len, >, i);
g_assert_cmpstr (g_ptr_array_index (argv, i++),
==, "--ld-preload=/app/lib/libpreloadA.so");
g_assert_cmpuint (argv->len, >, i);
g_assert_cmpstr (g_ptr_array_index (argv, i++),
==, "--ld-preload=/lib/libpreload-rootfs.so");
g_assert_cmpuint (argv->len, >, i);
g_assert_cmpstr (g_ptr_array_index (argv, i++),
==, "--ld-preload=/usr/lib/libpreloadU.so");
g_assert_cmpuint (argv->len, >, i);
g_assert_cmpstr (g_ptr_array_index (argv, i++),
==, "--ld-preload=/home/me/libpreloadH.so");
g_assert_cmpuint (argv->len, ==, i);
/* FlatpakExports never exports /app */
g_assert_false (flatpak_exports_path_is_visible (exports, "/app"));
g_assert_false (flatpak_exports_path_is_visible (exports, "/app/lib"));
g_assert_false (flatpak_exports_path_is_visible (exports, "/app/lib/libpreloadA.so"));
/* We don't always export /home etc. so we have to explicitly export
* this one */
g_assert_false (flatpak_exports_path_is_visible (exports, "/home"));
g_assert_false (flatpak_exports_path_is_visible (exports, "/home/me"));
g_assert_true (flatpak_exports_path_is_visible (exports, "/home/me/libpreloadH.so"));
/* FlatpakExports never exports /lib as /lib */
g_assert_false (flatpak_exports_path_is_visible (exports, "/lib"));
g_assert_false (flatpak_exports_path_is_visible (exports, "/lib/libpreload-rootfs.so"));
/* FlatpakExports never exports /usr as /usr */
g_assert_false (flatpak_exports_path_is_visible (exports, "/usr"));
g_assert_false (flatpak_exports_path_is_visible (exports, "/usr/lib"));
g_assert_false (flatpak_exports_path_is_visible (exports, "/usr/lib/libpreloadU.so"));
}
static void
test_remap_ld_preload_flatpak_no_runtime (Fixture *f,
gconstpointer context)
{
g_autoptr(GPtrArray) argv = g_ptr_array_new_with_free_func (g_free);
gsize i;
populate_ld_preload (f, argv, PV_APPEND_PRELOAD_FLAGS_FLATPAK_SUBSANDBOX,
NULL, NULL);
i = 0;
g_assert_cmpuint (argv->len, >, i);
g_assert_cmpstr (g_ptr_array_index (argv, i++),
==, "--ld-preload=/app/lib/libpreloadA.so");
g_assert_cmpuint (argv->len, >, i);
g_assert_cmpstr (g_ptr_array_index (argv, i++),
==, "--ld-preload=/lib/libpreload-rootfs.so");
g_assert_cmpuint (argv->len, >, i);
g_assert_cmpstr (g_ptr_array_index (argv, i++),
==, "--ld-preload=/usr/lib/libpreloadU.so");
g_assert_cmpuint (argv->len, >, i);
g_assert_cmpstr (g_ptr_array_index (argv, i++),
==, "--ld-preload=/home/me/libpreloadH.so");
g_assert_cmpuint (argv->len, >, i);
g_assert_cmpstr (g_ptr_array_index (argv, i++),
==, "--ld-preload=/steam/lib/gameoverlayrenderer.so");
g_assert_cmpuint (argv->len, ==, i);
}
int
main (int argc,
char **argv)
{
_srt_setenv_disable_gio_modules ();
g_test_init (&argc, &argv, NULL);
g_test_add ("/remap-ld-preload", Fixture, NULL,
setup, test_remap_ld_preload, teardown);
g_test_add ("/remap-ld-preload-flatpak", Fixture, NULL,
setup, test_remap_ld_preload_flatpak, teardown);
g_test_add ("/remap-ld-preload-no-runtime", Fixture, NULL,
setup, test_remap_ld_preload_no_runtime, teardown);
g_test_add ("/remap-ld-preload-flatpak-no-runtime", Fixture, NULL,
setup, test_remap_ld_preload_flatpak_no_runtime, teardown);
return g_test_run ();
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment