Testing the Kernel⚓︎
Abstract
unCORE provides unit- and integration-tests. Integration tests are found under code/uncore/tests/
. Unit-test are located in the kernel source code as part of the kernel's library. Note that linting (the kernel but also all other parts of this project) is an important part of code quality enforcement. Hence, we lint the whole codebase during CI.
Unit Tests⚓︎
Unit tests for the kernel are associated with lib.rs
and not with main.rs
. Unit tests are declared via the #[test_case]
directive above the test:
A simple test runner implementation (located in code/uncore/src/library/test.rs
) executes all tests one after another when the unit-test binary is run in QEMU. Conditional compilation (with #[cfg(test)]
) indicates code that only runs when the unit-test binary is created. Because the library part of unCORE runs the unit tests, it has a pseudo entry function that acts like main()
:
/// The unit-test entry point of `lib.rs`. This function
/// is run when unit tests for `lib.rs` are run.
#[cfg(all(target_arch = "riscv64", test))]
#[riscv_rt::entry]
fn riscv64_entry() -> ! { ... }
To run unit tests, use cargo run -- u-test
.
Integration Tests⚓︎
Integration tests reside under code/uncore/tests/
. They test bigger parts of the whole kernel to make sure all parts work together nicely. Some integration tests do not use a test harness.
To run integration tests, use cargo run -- i-test
.
Running "Unit-Tests" Inside an Integration Test
If you want to run "unit-tests" inside an integration test, you require a test runner. The library part of unCORE provides such a runner:
You can then call __test_runner();
to run all tests marked with #[test_case]
.
How Test are Implemented⚓︎
Running kernel tests is a bit more tricky than you might think. We will need to run them inside QEMU, and on top of that, cargo
does not (yet) provide a nice interface to list the files it created for the tests. The trick is to supply the --no-run
and --message-output=json
flags when running cargo test ...
and then parse the binary file paths with jq
. These file paths can then be used in conjunction with QEMU. Relying on special files like .cargo/config.toml
would be infeasible as they introduce other pitfalls and have critical downsides (like forcing the whole workspace to a target).
Continuous Integration (CI)⚓︎
Continuous Integration (CI) is a critical part of modern software development. This project uses GitHub Actions. When you open a PR or when pushing on a branch, [GitHub Actions] run to check and test your code. These checks consist of linting as well as unit- and integration tests.
Praise be Linters
A linter that is probably going to be very annoying, nerve-wrecking, but also essential in the end is clippy
. You may have noticed the linting targets in code/Cargo.toml
. unCORE's configuration enables various linting targets for the whole kernel. If you do not want clippy
to eat you alive wheh checking a merge request (GitHub calls them "Pull Request"), fix the lints locally. You can run cargo run -- check
to check for all kinds of linting issues.