blob: 4e3fa03bc1868d5540102f943917f286bf351884 [file] [log] [blame]
#![no_std]
use core::ptr::addr_of_mut;
use intrusive_collections::{intrusive_adapter, LinkedList, LinkedListLink};
pub use pw_bytes;
intrusive_adapter!(pub TestDescAndFnAdapter<'a> = &'a TestDescAndFn: TestDescAndFn { link: LinkedListLink });
static mut TEST_LIST: Option<LinkedList<TestDescAndFnAdapter>> = None;
// All accesses to test list go through this function. This gives us a
// single point of ownership of TEST_LIST and keeps us from leaking references
// to it.
fn access_test_list<F>(callback: F)
where
F: FnOnce(&mut LinkedList<TestDescAndFnAdapter>),
{
// Safety: Tests are single threaded for now. This assumption needs to be
// revisited.
let test_list: &mut Option<LinkedList<TestDescAndFnAdapter>> =
unsafe { addr_of_mut!(TEST_LIST).as_mut().unwrap_unchecked() };
let list = test_list.get_or_insert_with(|| LinkedList::new(TestDescAndFnAdapter::new()));
callback(list)
}
pub fn add_test(test: &'static mut TestDescAndFn) {
access_test_list(|test_list| test_list.push_back(test))
}
pub fn for_each_test<F>(mut callback: F)
where
F: FnMut(&TestDescAndFn),
{
access_test_list(|test_list| {
for test in test_list.iter() {
callback(test);
}
});
}
pub struct TestError {
pub file: &'static str,
pub line: u32,
pub message: &'static str,
}
pub type Result<T> = core::result::Result<T, TestError>;
pub enum TestFn {
StaticTestFn(fn() -> Result<()>),
}
pub struct TestDesc {
pub name: &'static str,
}
pub struct TestDescAndFn {
pub desc: TestDesc,
pub test_fn: TestFn,
pub link: LinkedListLink,
}
impl TestDescAndFn {
pub const fn new(desc: TestDesc, test_fn: TestFn) -> Self {
Self {
desc,
test_fn,
link: LinkedListLink::new(),
}
}
}
// We're marking these as send and sync so that we can declare statics with.
// them. They're not actually Send and Sync because they contain linked list
// pointers but in practice tests are single threaded and these are never sent
// between threads.
//
// A better pattern here must be worked out with intrusive lists of static data
// (for statically declared threads for instance) so we'll revisit this later.
unsafe impl Send for TestDescAndFn {}
unsafe impl Sync for TestDescAndFn {}
#[macro_export]
macro_rules! assert_eq {
($a:expr, $b:expr) => {
if $a != $b {
return Err(unittest::TestError {
file: file!(),
line: line!(),
message: unittest::pw_bytes::concat_static_strs!(
"assert_eq!(",
stringify!($a),
", ",
stringify!($b),
") failed"
),
});
}
};
}