KUnit Design
High Level Structure
KUnit in practice is divided into two components: the KUnit kernel (more often referred to as the KUnit kernel, or just the kernel), and kunit.py. Most of KUnit, including all the test cases, all of the test libraries, and even the test runner itself are all part of the Linux kernel.
User Mode Linux
User Mode Linux, or UML, is a way to compile and run the Linux kernel as a normal program that can run in userspace without the help of a VM or any virtualization support.
UML is an architecture arch/um/ that maps all the architecture specific elements, which would normally map to low level hardware features, to the POSIX interface allowing the Linux kernel to be compiled and run as a normal userspace program.
For more information on UML, see: http://user-mode-linux.sourceforge.net/
KUnit Kernel
Most of the complexity of KUnit lies in the Linux kernel itself. Within the KUnit kernel there are several parts:
The Test Runner which schedules and runs all the tests, which is composed of:
The Linux initcall subsystem which invokes:
The Test Case Runner which handles sandboxing and running individual test cases.
The Test Library which provide wrappers around the API provided by the Test Case Runner to test cases to communicate with it.
The Linux kernel itself is much more complicated than this, but a full overview is outside of the scope of this document.
Test Runner
The test runner in KUnit is more or less the KUnit kernel itself. Each kunit
suite, KUnit’s name
for a test suite, registers its test case runner as an init call with the Linux
initcall system with kunit_test_suite(suite)
. When the kernel boots up, it runs
through all the initcalls to initialize all its subsystems; the last initcalls
it executes are the test case runners.
Test Case Runner
Each test case runner is associated with a kunit suite. When the test case runner is invoked, it runs through each test case and for each test case, does the following:
Register a failure handler with the UML trap subsytem.
Run the init function provided by the
kunit_suite
.Run the test case.
Run the exit function provided by the
kunit_suite
.Deallocate all the test allocated resources.
Report test results.
All this happens in lib/kunit/test.c.
Test Library
Currently, the test runner provides the function kunit_do_assertion
for the
test case to communicate a failed assertion:
void kunit_do_assertion(struct kunit *test,
struct kunit_assert *assert,
bool pass,
const char *fmt, ...);
It takes a
whether the expectation/assertion failed or not
log message to show on failure represented as a C format string.
The function definition can be found here: https://git.kernel.org/pub/scm/linux/kernel/git/shuah/linux-kselftest.git/tree/lib/kunit/test.c?h=test&id=741a98d022362c90609ac9dcd8ad56314d8e68b3#n152
kunit.py
Currently the main purpose kunit.py serves is just to make to process of building the kernel and running tests easier, but it also serves as a place where workarounds can be added in. For example, there are some bugs (for example, if the UML kernel crashes, it will corrupt the user’s terminal settings) with the UML user interface that are much easier to work around outside of the kernel than fix in the kernel itself. One of the function it serves is to parse the kernel output and print out the summarized results in a nice, human readable format.