use crate::sync::SyncUnsafe; #[repr(C)] pub struct TestEntry { pub name: &'static str, pub func: fn(), } struct CurrentTest { num: usize, name: &'static str, } static CURRENT_TEST: SyncUnsafe = SyncUnsafe::new(CurrentTest { num: 0, name: "" }); pub fn current() -> (usize, &'static str) { unsafe { let ct = &*CURRENT_TEST.get(); (ct.num, ct.name) } } fn set_current(num: usize, name: &'static str) { unsafe { let ct = &mut *CURRENT_TEST.get(); ct.num = num; ct.name = name; } } fn test_entries() -> &'static [TestEntry] { #[allow(improper_ctypes)] unsafe extern "C" { static __kernel_tests_start: TestEntry; static __kernel_tests_end: TestEntry; } unsafe { let start = &raw const __kernel_tests_start; let end = &raw const __kernel_tests_end; let count = (end as usize - start as usize) / core::mem::size_of::(); core::slice::from_raw_parts(start, count) } } const PREFIX: &str = "lancer_kernel::tests::"; fn strip_prefix(name: &str) -> &str { match name.strip_prefix(PREFIX) { Some(rest) => rest, None => name, } } pub fn run_all() -> ! { let entries = test_entries(); crate::kprintln!("KTAP version 1"); crate::kprintln!("1..{}", entries.len()); entries.iter().enumerate().for_each(|(i, entry)| { let num = i + 1; let name = strip_prefix(entry.name); set_current(num, name); (entry.func)(); crate::kprintln!("ok {} - {}", num, name); }); crate::klog!("test", "all {} passed", entries.len()); crate::proc::report_refcount_audit(); crate::arch::gdt::verify_stack_guards(); crate::klog!("test", "stack guard canaries intact"); crate::qemu::exit(crate::qemu::ExitCode::Success); } #[macro_export] macro_rules! kernel_test { (fn $name:ident() $body:block) => { fn $name() $body const _: () = { #[used] #[unsafe(link_section = ".kernel_tests")] static ENTRY: $crate::test_harness::TestEntry = $crate::test_harness::TestEntry { name: concat!(module_path!(), "::", stringify!($name)), func: $name, }; }; }; }