Wire -c/--config-file's effective config into the self-test suite
run_self_tests() now takes the same effective AppConfig any other command
gets and stashes it into a new g_test_app_config global (tests/support/
fixtures.h) before Catch2 runs anything. test_rootless_run.cpp's own
run_in_fixture() reads a copy of it instead of a hardcoded default
AppConfig{}, so -c actually reaches that test's container creation.
Verified: a config with every unshare-*/with-* key set to false, used via
-c <file> -t -- "[integration][net]~[root]", flips all 3 of that file's
container-creating tests to failing -- including the nohup-straggler
regression test, since with neither a pid namespace nor a cgroup nothing
reaps the backgrounded process -- while [unit] and non-networking
[integration] tests are completely unaffected, as expected.
This commit is contained in:
@@ -355,28 +355,52 @@ Source layout (all under `src/`):
|
|||||||
right after the `stop_tap_relay()` loop) for `--clean-processes`'s own
|
right after the `stop_tap_relay()` loop) for `--clean-processes`'s own
|
||||||
crash-orphan sweep (`network_tap_relay.h`'s `clean_stale_tap_relays()`,
|
crash-orphan sweep (`network_tap_relay.h`'s `clean_stale_tap_relays()`,
|
||||||
see below).
|
see below).
|
||||||
- `self_test.{h,cpp}` — `run_self_tests(args)` implements `-t/--test`
|
- `self_test.{h,cpp}` — `run_self_tests(args, config)` implements
|
||||||
(`args` is `ParsedArgs::test_args`, `cli_args.h` — everything on the
|
`-t/--test` (`args` is `ParsedArgs::test_args`, `cli_args.h` — everything
|
||||||
command line after `-t`, captured the same trailing-argv way `-r`/`-x`
|
on the command line after `-t`, captured the same trailing-argv way
|
||||||
capture their own command). Purely plumbing: builds a synthetic argv
|
`-r`/`-x` capture their own command; `config` is the same effective,
|
||||||
(`{"slocker-lite -t"} + args`) and hands it straight to Catch2's
|
`-c/--config-file`-resolved `AppConfig` any other command gets, threaded
|
||||||
`Catch::Session().run(argc, argv)` — no test logic of its own lives here
|
through from `dispatch_command()`'s own `Mode::test` case). Mostly
|
||||||
at all, that's all under `tests/` (see below); this file is `#if
|
plumbing: builds a synthetic argv (`{"slocker-lite -t"} + args`) and
|
||||||
|
hands it straight to Catch2's `Catch::Session().run(argc, argv)` — no
|
||||||
|
test logic of its own lives here at all, that's all under `tests/` (see
|
||||||
|
below) — but first stashes `config` into `tests/support/fixtures.h`'s
|
||||||
|
own `g_test_app_config` global, *before* Catch2 ever runs a single
|
||||||
|
`TEST_CASE`. **Why**: without this, test code that creates a real
|
||||||
|
container (`test_rootless_run.cpp`'s own `run_in_fixture()`, below) had
|
||||||
|
no way to reflect `-c`'s settings at all — it always built a fresh,
|
||||||
|
hardcoded default `AppConfig{}` for every container regardless of what
|
||||||
|
`-c`/the real config file said, since Catch2 `TEST_CASE`s are just plain
|
||||||
|
functions with no way to receive parameters from the harness that invoked
|
||||||
|
them. Confirmed by direct testing (per the user's own request): a
|
||||||
|
hand-written config with every `unshare-*`/`with-*` key set to `false`,
|
||||||
|
used via `-c <that file> -t -- "[integration][net]~[root]"`, correctly
|
||||||
|
flips all 3 of `test_rootless_run.cpp`'s container-creating tests to
|
||||||
|
failing (the two namespace-isolation checks, since nothing's actually
|
||||||
|
isolated anymore, and the nohup-straggler regression test, since with no
|
||||||
|
pid namespace *and* no cgroup — rootless, on this dev machine — nothing
|
||||||
|
reaps the backgrounded process) while leaving every other category
|
||||||
|
(`[unit]`, `[integration]~[net]`) completely unaffected, exactly as
|
||||||
|
expected. This file is `#if
|
||||||
ENABLE_TESTS`-guarded (`config.h`, from Meson's `enable_tests` option,
|
ENABLE_TESTS`-guarded (`config.h`, from Meson's `enable_tests` option,
|
||||||
default on — the same macro that already gated whether `catch2_dep` gets
|
default on — the same macro that already gated whether `catch2_dep` gets
|
||||||
linked at all) so a `-Denable_tests=false` build prints a clear "not
|
linked at all) so a `-Denable_tests=false` build prints a clear "not
|
||||||
compiled into this build" message and returns nonzero instead of
|
compiled into this build" message and returns nonzero instead of
|
||||||
failing to link. `-t`'s own leftover-args capture requires a literal
|
failing to link — the `#include "fixtures.h"`/`g_test_app_config` usage
|
||||||
`--` before any Catch2 option that looks like one of slocker-lite's own
|
is confined to the `#if ENABLE_TESTS` branch specifically because
|
||||||
(`-r/--reporter` collides with `-r/--run` — `-c/--section` no longer
|
`tests/support/fixtures.cpp` (where it's defined) is itself only
|
||||||
collides with anything now that `--cleanup` dropped its own `-c` short
|
compiled into the binary when `enable_tests` is on (`meson.build`'s
|
||||||
form) — a bare tag expression like `-t -- "[unit]"` needs it
|
`test_sources`), so referencing it unconditionally would break that
|
||||||
too by convention, though `getopt_long`'s own permutation happens to let
|
build with a link error. `-t`'s own leftover-args capture requires a
|
||||||
a `-t "[unit]"` without `--` work anyway, since `"[unit]"` doesn't start
|
literal `--` before any Catch2 option that looks like one of
|
||||||
with `-`. Distinct from the Meson-driven fixture smoke test under
|
slocker-lite's own (`-r/--reporter` collides with `-r/--run`;
|
||||||
`tests/` (`tests/gen_fixture.py`/`tests/run_test.py`, described in
|
`-c/--section` again collides too, now with `-c/--config-file`) — a bare
|
||||||
"Build & test commands" below), which stays a separate, always-on,
|
tag expression like `-t -- "[unit]"` needs it too by convention, though
|
||||||
Python-driven mount/unmount/cleanup check.
|
`getopt_long`'s own permutation happens to let a `-t "[unit]"` without
|
||||||
|
`--` work anyway, since `"[unit]"` doesn't start with `-`. Distinct from
|
||||||
|
the Meson-driven fixture smoke test under `tests/` (`tests/gen_fixture.py`/
|
||||||
|
`tests/run_test.py`, described in "Build & test commands" below), which
|
||||||
|
stays a separate, always-on, Python-driven mount/unmount/cleanup check.
|
||||||
|
|
||||||
**Test organization under `tests/`** (all in-process — every category
|
**Test organization under `tests/`** (all in-process — every category
|
||||||
calls this project's own already-header-exposed functions directly, no
|
calls this project's own already-header-exposed functions directly, no
|
||||||
@@ -500,7 +524,19 @@ Source layout (all under `src/`):
|
|||||||
expected shutdown `SIGTERM` via the inherited handler and report a
|
expected shutdown `SIGTERM` via the inherited handler and report a
|
||||||
spurious failure) — fixed the same way, resetting `SIGTERM` to
|
spurious failure) — fixed the same way, resetting `SIGTERM` to
|
||||||
`SIG_DFL` right before forking the straggler.
|
`SIG_DFL` right before forking the straggler.
|
||||||
- `tests/support/fixtures.{h,cpp}` — `find_busybox_fixture()` (searches
|
- `tests/support/fixtures.{h,cpp}` — `g_test_app_config` (a plain `AppConfig`
|
||||||
|
global, default-constructed) is set once by `run_self_tests()`
|
||||||
|
(`self_test.cpp`, see above) from the current `-t` run's own effective,
|
||||||
|
possibly `-c/--config-file`-resolved config, before Catch2 runs anything;
|
||||||
|
`test_rootless_run.cpp`'s own `run_in_fixture()` reads a copy of it for
|
||||||
|
every container it creates instead of a hardcoded default, so `-c`
|
||||||
|
actually reaches that test's own namespace-policy resolution. Declared
|
||||||
|
here (not `self_test.{h,cpp}`) specifically because `self_test.cpp` is
|
||||||
|
always compiled (test or not), while this file — and thus this
|
||||||
|
global's actual definition — only exists in the binary at all when
|
||||||
|
`enable_tests` is on; `self_test.cpp`'s own `#include "fixtures.h"`/
|
||||||
|
write to it is confined to its `#if ENABLE_TESTS` branch for exactly
|
||||||
|
that reason. `find_busybox_fixture()` (searches
|
||||||
`images/busybox.tar` relative to cwd, this project's own established
|
`images/busybox.tar` relative to cwd, this project's own established
|
||||||
manual-testing convention; `nullopt` if absent, so `[net]` tests
|
manual-testing convention; `nullopt` if absent, so `[net]` tests
|
||||||
`SKIP()` rather than fail — see `tests/setup-tests.py`, below),
|
`SKIP()` rather than fail — see `tests/setup-tests.py`, below),
|
||||||
|
|||||||
+1
-1
@@ -889,7 +889,7 @@ int dispatch_command(const ParsedArgs& args, const std::filesystem::path& config
|
|||||||
case Mode::clean_processes:
|
case Mode::clean_processes:
|
||||||
return clean_processes_command();
|
return clean_processes_command();
|
||||||
case Mode::test:
|
case Mode::test:
|
||||||
return run_self_tests(args.test_args);
|
return run_self_tests(args.test_args, config);
|
||||||
case Mode::exec:
|
case Mode::exec:
|
||||||
return exec_in_session(*args.exec_pid, args.command, args.user_flag, args.group_flag);
|
return exec_in_session(*args.exec_pid, args.command, args.user_flag, args.group_flag);
|
||||||
case Mode::kill:
|
case Mode::kill:
|
||||||
|
|||||||
+11
-1
@@ -20,12 +20,21 @@
|
|||||||
|
|
||||||
#if ENABLE_TESTS
|
#if ENABLE_TESTS
|
||||||
#include <catch2/catch_session.hpp>
|
#include <catch2/catch_session.hpp>
|
||||||
|
|
||||||
|
#include "fixtures.h"
|
||||||
#else
|
#else
|
||||||
#include <fmt/core.h>
|
#include <fmt/core.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int run_self_tests(const std::vector<std::string>& args) {
|
int run_self_tests(const std::vector<std::string>& args, const AppConfig& config) {
|
||||||
#if ENABLE_TESTS
|
#if ENABLE_TESTS
|
||||||
|
// Stashed into a shared test-support global (tests/support/fixtures.h)
|
||||||
|
// *before* Catch2 ever runs a TEST_CASE, so test code that creates a
|
||||||
|
// real container (test_rootless_run.cpp's own run_in_fixture()) can
|
||||||
|
// consult the same effective, -c/--config-file-resolved settings a
|
||||||
|
// real -r/--run invocation would, instead of hardcoded defaults.
|
||||||
|
g_test_app_config = config;
|
||||||
|
|
||||||
// args[0] is conventionally the program name in Catch2's eyes (used in
|
// args[0] is conventionally the program name in Catch2's eyes (used in
|
||||||
// some of its own diagnostic/help output) -- it never sees the real
|
// some of its own diagnostic/help output) -- it never sees the real
|
||||||
// argv[0], since ParsedArgs::test_args only ever holds what came after
|
// argv[0], since ParsedArgs::test_args only ever holds what came after
|
||||||
@@ -45,6 +54,7 @@ int run_self_tests(const std::vector<std::string>& args) {
|
|||||||
return session.run(static_cast<int>(argv.size()), argv.data());
|
return session.run(static_cast<int>(argv.size()), argv.data());
|
||||||
#else
|
#else
|
||||||
(void)args;
|
(void)args;
|
||||||
|
(void)config;
|
||||||
fmt::print(stderr,
|
fmt::print(stderr,
|
||||||
"tests were not compiled into this build (reconfigure with "
|
"tests were not compiled into this build (reconfigure with "
|
||||||
"-Denable_tests=true and rebuild)\n");
|
"-Denable_tests=true and rebuild)\n");
|
||||||
|
|||||||
+12
-2
@@ -19,16 +19,26 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "config_file.h"
|
||||||
|
|
||||||
// Implements -t/--test, this project's own built-in Catch2-driven test
|
// Implements -t/--test, this project's own built-in Catch2-driven test
|
||||||
// suite (distinct from the Meson-driven fixture smoke test under tests/,
|
// suite (distinct from the Meson-driven fixture smoke test under tests/,
|
||||||
// which stays a separate, always-on mount/unmount/cleanup check).
|
// which stays a separate, always-on mount/unmount/cleanup check).
|
||||||
// `args` is `ParsedArgs::test_args` (cli_args.h) -- everything on the
|
// `args` is `ParsedArgs::test_args` (cli_args.h) -- everything on the
|
||||||
// command line after `-t`, forwarded to Catch2's own Session::run()
|
// command line after `-t`, forwarded to Catch2's own Session::run()
|
||||||
// unmodified (tag expressions, --list-tests, --reporter, etc.); a bare
|
// unmodified (tag expressions, --list-tests, --reporter, etc.); a bare
|
||||||
// `-t` (empty `args`) runs every registered TEST_CASE. Only compiled to
|
// `-t` (empty `args`) runs every registered TEST_CASE. `config` is the
|
||||||
|
// same effective (`-c/--config-file`-resolved) AppConfig any other command
|
||||||
|
// gets, given to test code so a test that creates a real container can
|
||||||
|
// reflect the same global settings a real `-r/--run` would (see
|
||||||
|
// `tests/support/fixtures.h`'s `g_test_app_config`, which this stashes it
|
||||||
|
// into) -- letting e.g. `-c <all-unshare-off.yaml> -t -- "[integration][net]"`
|
||||||
|
// exercise what happens with pid/cgroup isolation actually disabled,
|
||||||
|
// instead of every test always creating containers under hardcoded
|
||||||
|
// defaults regardless of `-c`. Only compiled to
|
||||||
// actually run Catch2 when this build has ENABLE_TESTS set (config.h,
|
// actually run Catch2 when this build has ENABLE_TESTS set (config.h,
|
||||||
// from Meson's `enable_tests` option, default on) -- otherwise prints a
|
// from Meson's `enable_tests` option, default on) -- otherwise prints a
|
||||||
// clear "not compiled into this build" message and returns nonzero,
|
// clear "not compiled into this build" message and returns nonzero,
|
||||||
// since a -Denable_tests=false build has no TEST_CASEs (or Catch2 itself)
|
// since a -Denable_tests=false build has no TEST_CASEs (or Catch2 itself)
|
||||||
// linked in at all.
|
// linked in at all.
|
||||||
int run_self_tests(const std::vector<std::string>& args);
|
int run_self_tests(const std::vector<std::string>& args, const AppConfig& config);
|
||||||
|
|||||||
@@ -125,7 +125,12 @@ std::string run_in_fixture(const std::filesystem::path& image, const std::vector
|
|||||||
args.mode_arg = image.string();
|
args.mode_arg = image.string();
|
||||||
args.command = command;
|
args.command = command;
|
||||||
|
|
||||||
AppConfig config;
|
// A fresh copy of the current -t run's effective config (g_test_app_config,
|
||||||
|
// fixtures.h -- set once by run_self_tests() from whatever main() resolved,
|
||||||
|
// respecting -c/--config-file) rather than a hardcoded AppConfig{}, so this
|
||||||
|
// helper's containers reflect the same global settings a real -r/--run
|
||||||
|
// invocation would.
|
||||||
|
AppConfig config = g_test_app_config;
|
||||||
CapturedStdout capture;
|
CapturedStdout capture;
|
||||||
// config_path is only ever consulted by modes that read/write a config
|
// config_path is only ever consulted by modes that read/write a config
|
||||||
// file (-w/--write-config, -v/--volume, -n/--network); Mode::run
|
// file (-w/--write-config, -v/--volume, -n/--network); Mode::run
|
||||||
|
|||||||
@@ -26,6 +26,8 @@
|
|||||||
#include <system_error>
|
#include <system_error>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
AppConfig g_test_app_config;
|
||||||
|
|
||||||
std::optional<std::filesystem::path> find_busybox_fixture() {
|
std::optional<std::filesystem::path> find_busybox_fixture() {
|
||||||
std::error_code ec;
|
std::error_code ec;
|
||||||
auto path = std::filesystem::current_path(ec) / "images" / "busybox.tar";
|
auto path = std::filesystem::current_path(ec) / "images" / "busybox.tar";
|
||||||
|
|||||||
@@ -20,6 +20,20 @@
|
|||||||
#include <optional>
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
#include "config_file.h"
|
||||||
|
|
||||||
|
// The effective AppConfig for the current -t/--test run -- set once by
|
||||||
|
// run_self_tests() (self_test.cpp) from whatever main() resolved (respecting
|
||||||
|
// -c/--config-file, same as any other command), before Catch2 ever runs a
|
||||||
|
// TEST_CASE. Test code that creates a real container (test_rootless_run.cpp's
|
||||||
|
// own run_in_fixture()) reads this instead of hardcoding a fresh, default
|
||||||
|
// AppConfig{}, so e.g. `-c <all-unshare-off.yaml> -t -- "[integration][net]"`
|
||||||
|
// actually exercises what happens with pid/cgroup isolation disabled. Plain
|
||||||
|
// default-constructed AppConfig{} otherwise (matching every test's prior,
|
||||||
|
// unconditional behavior) -- a bare `-t` with no `-c` and no real config file
|
||||||
|
// changes nothing.
|
||||||
|
extern AppConfig g_test_app_config;
|
||||||
|
|
||||||
// Path to a real, runnable OCI Image Layout tar (something with an actual
|
// Path to a real, runnable OCI Image Layout tar (something with an actual
|
||||||
// /bin/sh, unlike tests/gen_fixture.py's minimal single-file fixture used
|
// /bin/sh, unlike tests/gen_fixture.py's minimal single-file fixture used
|
||||||
// by the plain mount/unmount smoke test) for [integration][net] tests that
|
// by the plain mount/unmount smoke test) for [integration][net] tests that
|
||||||
|
|||||||
Reference in New Issue
Block a user