Platform Testing
Platform testing is supported through the patina_sdk::test
module, which provides a testing framework similar to
the typical Rust testing framework. The key difference is that instead of tests being collected and executed on the
host system, they are collected and executed via a component (patina_sdk::test::TestRunner
) provided by the same
crate. The platform must register this component with the Patina DXE Core, which will then dispatch the component
to run all registered tests.
Note: The most up-to-date documentation on the
patina_sdk::test
module can be found on crates.io. For convenience, some high-level concepts are summarized below.
Writing On-Platform Tests
Writing a test to be run on-platform is as simple as setting the patina_test
attribute on a function with the
following interface, where ...
can be any number of parameters that implement the Param
trait from
patina_sdk::component::*
:
#![allow(unused)] fn main() { use patina_sdk::test::{Result, patina_test}; #[patina_test] fn my_test(...) -> Result { todo!() } }
On-platform tests are not just for component testing; they can also be used for testing general-purpose code on a
platform. Any function tagged with #[patina_test]
will be collected and executed on a platform. The test runner
can filter out tests, but you should also be conscious of when tests should run. Using cfg_attr
paired with the
skip
attribute is a great way to have tests ignored for reasons like host architecture or feature flags.
Note:
patina_sdk::test::Result
is simplycore::result::Result<(), &'static str>
, and you can use that instead.
This example shows how to use the skip
attribute paired with cfg_attr
to skip a test.
#![allow(unused)] fn main() { use patina_sdk::boot_services::StandardBootServices; #[patina_test] #[cfg_attr(target_arch = "aarch64", skip)] fn my_test(bs: StandardBootServices) -> Result { todo!() } }
Next is the should_fail
attribute, which allows you to specify that a test should fail. It can also specify the
expected failure message.
#![allow(unused)] fn main() { #[patina_test] #[should_fail] fn my_test1() -> Result { todo!() } #[patina_test] #[should_fail = "Failed for this reason"] fn my_test2() -> Result { todo!() } }
Running On-Platform Tests
Running all these tests on a platform is as easy as instantiating the test runner component and registering it with the Patina DXE Core:
#![allow(unused)] fn main() { let test_runner = TestRunner::default(); Core::default() .init_memory() .with_component(test_runner) .start() .unwrap(); }
This will execute all tests marked with the patina_test
attribute across all crates used to compile this binary.
Due to this fact, we have some configuration options with the test component. The most important customization is
the with_filter
function, which allows you to filter down the tests to run. The logic behind this is similar to
the filtering provided by cargo test
. That is to say, if you pass it a filter of X64
, it will only run tests
with X64
in their name. The function name is <module_path>::<name>
. You can call with_filter
multiple times.
The next customization is debug_mode
which enables logging during test execution (false by default). The final
customization is fail_fast
which will immediately exit the test harness as soon as a single test fails (false by
default). These two customizations can only be called once. Subsequent calls will overwrite the previous value.
#![allow(unused)] fn main() { let test_runner = TestRunnerComponent::default() .with_filter("X64") .debug_mode(true) .fail_fast(true); Core::default() .init_memory() .with_component(test_runner) .start() .unwrap(); }
--- config: layout: elk look: handDrawn --- graph TD A[Register TestRunner Component] --> B[DXE Core Dispatches TestRunner] B --> C[Collect and Run Tests] C --> D[Report Results]