DXE Core Testing
Writing DXE Core tests follows all the same principles defined in the Testing chapter, so if you
have not reviewed it yet, please do so before continuing. One of the reasons that patina
is split into multiple crates and merged the patina_dxe_core
umbrella crate is to support code separation and ease of
unit testing. Support crates (in the crates/*
folder) should not contain any static data used by the core. Instead,
they should provide the generic implementation details that the core uses to function. This simplifies unit tests, code
coverage, and the future possibility of extracting functionality to be used in additional cores (such as PEI, MM, etc).
The DXE Core supports all 4 types of testing mentioned in the Testing chapter; this includes on-platform unit tests.
Any function with the patina_test
attribute will be consolidated and executed on any platform that uses the
TestRunnerComponent
(unless specifically filtered out by the platform).
Testing with Global State
The standout difference between typical testing as described in the testing chapter, is that the DXE core has multiple static pieces of data that are referenced throughout the codebase. Since unit tests are ran in parallel, this means that multiple tests may be manipulating this static data at the same time. This will lead to either dead-locks, panics, or the static data being in an unexpected state for the test.
To help with this issue in the patina_dxe_core
crate, a test_support
module was added to make writing tests more convenient. The most important functionality in the module is the
with_global_lock
function which takes your test closure / function as a parameter. This function locks a private
global mutex, ensuring you have exclusive access to all statics within the DXE Core.
It is the responsibility of the test writer to reset the global state to meet their expectations. It is not the responsibility of the test writer to clear the global state once the test is finished.
Examples
Example 1
#![allow(unused)] fn main() { use crate::test_support::{with_global_lock, init_test_gcd}; #[test] fn test_that_uses_gcd() { with_global_lock(|| { init_test_gcd(None); todo!("Finish the test"); }); } }
Example 2
#![allow(unused)] fn main() { use crate::test_support::{with_global_lock, init_test_gcd}; fn with_gcd<F: Fn()>(gcd_size: Option<usize>, f: F) { with_global_lock(|| { init_test_gcd(gcd_size); f(); }) } #[test] fn test1() { with_gcd(None, || { todo!("Write the test"); }); } #[test] fn test2() { with_gcd(Some(0x1000), || { todo!("Write the test"); }); } }