| #![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" |
| ), |
| }); |
| } |
| }; |
| } |