Kernel Test Harness
What is it?
The kernel test harness runs unit tests inside the real kernel at boot and reports the results over the serial line, where scripts/test.sh reads them. Beyond plain assertions, it gives tests the tools to reach code a normal boot never exercises: forced allocation failures, ordered two-thread interleavings, large scratch buffers and per-test leak detection. It exists because many error paths were untested simply because no infrastructure could drive them.
How does it work?
Test files under src/kernel/test/ register suites with a category. At boot, the runner in test_runner.c runs every suite in the selected category, cleans up after each one, checks the heap for leaks, and prints a summary line. On the host, scripts/test.sh builds the image, boots it in QEMU, and parses that line.
sequenceDiagram
participant Host as scripts/test.sh
participant Boot as Bootloader (boot.conf)
participant Runner as test_runner.c
participant Suite as test_*.c suite
Host->>Boot: SUITE=<cat> patched into test_suite=
Boot->>Runner: category filter in boot_info
loop every registered suite in the category
Runner->>Suite: run body
Suite-->>Runner: assertions, skips, pending
Runner->>Runner: drain cleanup actions, compare heap usage
end
Runner-->>Host: "=== N tests passed, ... leaked ... ===" on serial
- Categories.
test_category_tintest.hlists 17 categories, fromTEST_CAT_MMtoTEST_CAT_QUOTA. Each has amake test-<cat>target. - Cleanup registry.
test_add_action()pushes a cleanup callback onto a per-suite LIFO stack that the runner drains after the suite body, whether it passed or failed. Scratch buffers and log suppression are built on it. - Fault injection. Countdown setters in
heap.hmake the Nthkmallocreturn NULL; the same shape extends to the PMM, the VMM and user-copy helpers, and can be scoped to one task. - Race barrier.
race_barrier.hholds two threads at a rendezvous point and releases them in a chosen order, so a test can aim at one interleaving instead of hoping a race happens. The ordering is reliable on one CPU but best-effort across CPUs; the header explains how to add a join or a completion flag when a test needs strict cross-CPU order. - Leak detection. The runner compares heap usage before and after each suite (after the cleanup drain). An unexplained difference is counted as a leak, and the host treats any leak as a failure.
What are its interfaces?
| Interface | Purpose |
|---|---|
test_suite_register_cat(name, fn, cat) |
Register a suite in a category (test.h) |
TEST_ASSERT, TEST_ASSERT_EQ(a, b, msg), TEST_ASSERT_NULL and friends |
Assertions that record file and line on failure |
TEST_SKIP(msg) |
Record a skip (for absent hardware); it does not leave the suite, so return after it |
TEST_PENDING(cond, msg) |
Mark a contract for a feature that is not implemented yet; shows [STUB] and counts as pending, not failed |
test_add_action(fn, ctx) |
Register a cleanup that runs after the suite |
TEST_EXPECT_LEAK(bytes, reason), TEST_LEAK_IGNORE(reason) |
Declare an intentional allocation that outlives the suite |
kmalloc_fail_countdown_set(n), kmalloc_fail_next() |
Force an allocation failure (heap.h) |
TEST_SCRATCH_KBUF(name, size) |
Auto-freed scratch buffer larger than the kernel stack allows (scratch.h) |
TEST_KLOG_SUPPRESS(subsystem) |
Quiet a subsystem's expected log noise for one test (klog_suppress.h) |
test_suite= and test_quiet= in boot.conf |
Category filter and verbosity, parsed by the bootloader |
Test files must not call live boot infrastructure (panic, boot_progress, a subsystem's _init); the rule and its opt-out are in the Test Code Policy.
How do I use it?
bash scripts/test.sh # every category
bash scripts/test.sh SUITE=mm # one category
make test-mm # the same, as a make target
bash scripts/test.sh QUIET=1 # summary only
CI_PARITY=1 bash scripts/test.sh # the QEMU package and TCG engine CI uses
The run ends with the runner's summary line, printed by test_runner.c in this form:
=== N tests passed, 0 failed, S skipped, P pending, L leaked, Q quota-leaked (T s) ===
A minimal suite looks like this:
static void test_widget_rejects_null(void) {
TEST_ASSERT_EQ(widget_open(NULL), STATUS_INVALID_PARAMETER, "NULL name refused");
}
void test_register_widget(void) {
test_suite_register_cat("widget_rejects_null", test_widget_rejects_null, TEST_CAT_OB);
}
Live suite and assertion counts per file are on the generated Kernel Test Coverage page.
What is not implemented yet?
- The audit of the leak failures that per-test leak detection first surfaced is closed on QEMU (KVM, TCG) and WHPX, but its VirtualBox and bare-metal legs need an operator: Audit and Classify the Leak Failures.
Every other section of the roadmap file has shipped.
How does it compare with Windows 11 and Linux?
Linux has in-kernel unit testing through KUnit, fault injection through failslab and fail_page_alloc, and leak reporting through kmemleak, which scans periodically rather than per test. Windows offers Driver Verifier's low-resources simulation for fault injection, but its kernel test infrastructure is not public. This harness combines all of these in one boot: task-scoped fault injection across several allocators, an ordered race barrier, and a leak count that fails the run in CI.