Skip to content

tests/int: revamp (drop runc(), use run -N, add asserts) - #5429

Open
kolyshkin wants to merge 9 commits into
opencontainers:mainfrom
kolyshkin:int-shim
Open

tests/int: revamp (drop runc(), use run -N, add asserts)#5429
kolyshkin wants to merge 9 commits into
opencontainers:mainfrom
kolyshkin:int-shim

Conversation

@kolyshkin

@kolyshkin kolyshkin commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

This supersedes #4946, taking a different approach to the problems
discussed there (see this comment and below).

The end result is that integration tests call runc the way any other
command is called, and check its output with helpers that say what went
wrong:

run -0 runc start ctr
assert_output --partial "something"

instead of the current

runc start ctr
[ "$status" -eq 0 ]
[[ "$output" == *"something"* ]]

where runc is a bash function hiding a call to bats' run helper, and
a failed comparison only shows the expression, not the values.

What a failure looks like now

To see this in practice, four deliberate bugs were introduced (runc -v
printing spec :, runc list -q adding a trailing space to the ID, a
reworded hook error, and runc delete --force failing on a missing
container), and the tests were run with and without this PR:

Bug Before Now
list -q adds a trailing space 20 lines of every runc call made so far, ending with an output that looks exactly right expected: 'test_box1'
actual: 'test_box1 '
spec : instead of spec: the failed expression, plus the whole runc -v output to eyeball expected: 'spec: [0-9]+\.[0-9]+\.[0-9]+'
actual: 'spec : 1.3.0'
reworded hook error ditto, with the message to compare by hand expected: 'error running createRuntime hook #1:'
actual: '...failure in createRuntime hook #1...'
delete --force fails [ "$status" -eq 0 ]' failed -- but after which call? run -0 runc delete --force notexists' failed, expected exit code 0, got 1

The trailing space one is the point of the whole exercise. Before:

not ok 1 list
# (in test file tests/integration/list.bats, line 37)
#   `[ "${lines[0]}" = "test_box1" ]' failed
# runc spec --rootless (status=0)
#
# runc run -d --console-socket /tmp/.../tty/sock test_box1 (status=0)
#
[... 10 more lines of the preceding commands and their output ...]
# runc list -q (status=0)
# test_box1
# test_box2
# test_box3

The expected value is not shown, the actual one looks correct, and the
difference is invisible. Now:

not ok 1 list
# (in test file tests/integration/list.bats, line 32)
#   `assert_line --index 0 "test_box1"' failed
# -- line 0 does not match (exact) --
# expected: 'test_box1'
#   actual: 'test_box1 '
# --

@kolyshkin
kolyshkin marked this pull request as draft August 28, 2026 06:08
@kolyshkin kolyshkin changed the title tests/int: add runc wrapper script and assert helper tests/int: use bats' run helper for runc, add assert Aug 28, 2026
@kolyshkin
kolyshkin force-pushed the int-shim branch 2 times, most recently from 9357195 to 580166d Compare August 28, 2026 06:47
@kolyshkin kolyshkin changed the title tests/int: use bats' run helper for runc, add assert tests/int: revamp (add assert, drop function runc, use run -N, etc.) Aug 28, 2026
@kolyshkin
kolyshkin force-pushed the int-shim branch 2 times, most recently from e73b626 to 5fb7f31 Compare August 28, 2026 07:53
@kolyshkin kolyshkin changed the title tests/int: revamp (add assert, drop function runc, use run -N, etc.) tests/int: revamp (add asserts, drop function runc, use run -N) Aug 28, 2026
@kolyshkin
kolyshkin force-pushed the int-shim branch 2 times, most recently from 87cf0b4 to 5fb7f31 Compare August 28, 2026 08:40
@kolyshkin
kolyshkin marked this pull request as ready for review August 28, 2026 08:40
@kolyshkin
kolyshkin force-pushed the int-shim branch 2 times, most recently from d9deecf to 6bd8318 Compare August 29, 2026 02:37
@kolyshkin kolyshkin changed the title tests/int: revamp (add asserts, drop function runc, use run -N) tests/int: revamp (add asserts, drop function runc, use run -N) Aug 29, 2026
@kolyshkin kolyshkin changed the title tests/int: revamp (add asserts, drop function runc, use run -N) tests/int: revamp (drop runc(), use run -N, add asserts) Aug 29, 2026
@kolyshkin
kolyshkin force-pushed the int-shim branch 2 times, most recently from 4d160e8 to a5cbc11 Compare August 29, 2026 02:47
@kolyshkin
kolyshkin requested review from lifubang and rata and removed request for rata September 1, 2026 00:07
@kolyshkin
kolyshkin force-pushed the int-shim branch 2 times, most recently from 1bf73d7 to 60daed2 Compare September 3, 2026 22:03
@kolyshkin

Copy link
Copy Markdown
Contributor Author

@AkihiroSuda @cyphar @thaJeztah @lifubang @rata PTAL (this changes our bats tests considerably, and so I have to rebase this manually every time we merge something involving bats tests). If you agree with the concept and the implementation, can we please merge this to reduce my load?

One note on reimplementing asserts vs using bats' own -- unfortunately, they don't come with bats-core and the installation is a real PITA, on the other hand the re-implementation is pretty straightforward and probably wouldn't change much. Yet if you don't like it, I can remove the last few commits which add/use asserts.

When runc is used as the right hand side of a pipe, it runs in a
subshell, so the $status and $output set by bats' run helper (called
from the runc wrapper) do not propagate to the test. As a result, the
checks that follow such a call silently test the values left over from
the previous runc call.

Use a heredoc redirection instead of piping the heredoc in.

Signed-off-by: Kir Kolyshkin <[email protected]>
We already have a function called runc, and adding a variable with the
same name, while not technically incorrect, does not help readability.

Fixes: a38f42a
Signed-off-by: Kir Kolyshkin <[email protected]>
Until now, runc in integration tests was a bash function, wrapping the
actual binary to add the flags all tests need (--root and, sometimes,
--systemd-cgroup).

A function can not be used where a real binary is required, which is why
we also had setup_runc_cmdline and RUNC_CMDLINE, used by the tests that
run runc under taskset or in a separate mount namespace.

Add tests/integration/bin/runc, a small wrapper script doing the same
thing, and prepend its directory to PATH. This way runc can be used both
directly and after commands like taskset, nsenter, or timeout, and
RUNC_CMDLINE is no longer needed.

Note that the runc bash function still takes precedence for a plain
"runc ..." call in tests, so their behavior is unchanged.

Signed-off-by: Kir Kolyshkin <[email protected]>
With this option, bats prints the value of $output for a failed test,
which is exactly what the sane_run wrapper does manually (and what the
next commit removes it for).

The option requires bats v1.7.0, so bump the minimum version. Our CI
uses v1.12.0 on GHA and builds the same version from source on EL8;
other platforms use their distro package, which is expected to be new
enough (and bats_require_minimum_version makes it obvious if it isn't).

Signed-off-by: Kir Kolyshkin <[email protected]>
Now that runc is a real binary (a wrapper script in PATH), tests can
call it the way any other command is called, using bats' run helper
directly:

	run -0 runc start ctr

instead of the old

	runc start ctr
	[ "$status" -eq 0 ]

where runc was a bash function hiding a call to run. The exit code check
is now part of the call: -N for an expected exit code, ! for "any
failure", and no argument where the exit code is checked later or not at
all.

This has to be done in one go: while the runc function exists, "run runc"
would call it, resulting in a nested run.

Other changes made necessary or possible by this:

  - The runc and sane_run functions are gone. The output of a failed
    command is now printed by bats itself, see the previous commit.

  - __runc (a raw runc call, not using run) is now simply runc.

Signed-off-by: Kir Kolyshkin <[email protected]>
A fair number of runc calls do not check the exit code at all, although
they should. Fix that.

Note that two of the checks added to update.bats expect an error (exit
code 1), as the test cases there are about runc rejecting a combination
of options.

The remaining calls that do not check the exit code are the ones that
check $status themselves (in an if, or against different values), the
one in a background subshell (where a failure can not be detected), and
the one that is explicitly documented as being allowed to fail.

Signed-off-by: Kir Kolyshkin <[email protected]>
When a test fails on a string comparison, all we see is the failed
expression, which usually means adding some debug output and rerunning
the test to find out what the actual value was.

The bats-assert library solves this, but it (together with its
bats-support dependency) has to be installed separately on every distro
we test on, which is more trouble than it's worth for a few functions.

Add assert_output, refute_output, and assert_line, implementing a subset
of the bats-assert API (so that the tests read the same way, and this
code can be dropped should bats-assert ever be added as a dependency),
and use them for the comparisons against an exact value. Now, when such
a check fails, the test log shows what was expected and what was there:

	-- output does not match (exact) --
	expected: '/override'
	  actual: $'\0/override'
	--

Signed-off-by: Kir Kolyshkin <[email protected]>
The point of the assert helpers is that a failed test says what it
wanted and what it got, so leaving most comparisons as bare [[ ]]
defeats it.

Convert the rest of the checks on $output and $lines. Those using a glob
to look for a substring become --partial, the rest become --regexp (the
patterns are translated to extended regular expressions, and anchored
where the glob was).

The few remaining [[ ]] are not assertions: one is an if condition, and
another one is the left hand side of ||, offering an alternative to the
assert that follows it.

Signed-off-by: Kir Kolyshkin <[email protected]>
The "Writing integration tests" section only pointed at helpers.bash,
which does not say much about how a test is supposed to be written.

Now that tests call runc via bats' run helper and check the output with
assert_output and friends, document that: how the expected exit code is
given to run, which assertions are available, and why they are preferred
over a bare test expression.

Signed-off-by: Kir Kolyshkin <[email protected]>
@thaJeztah

Copy link
Copy Markdown
Member

Sorry, looks like I missed the previous ping; are the failures expected, or something broken in the current PR?

Comment thread tests/integration/cpu_affinity.bats Outdated
Comment on lines +118 to +114
sane_run taskset -c "$first" "${RUNC_CMDLINE[@]}" run ctr
sane_run taskset -c "$first" runc run ctr

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like that it looks and feels as a direct invocation of runc, but this may also be a potential downside; would a case where .. for some reason .. runc is invoked directly be immediately apparent?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(in that case, we could rename the script to something more clear to be the wrapper script)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants