Next Generation WASM Microkernel Operating System
at trap_handler 172 lines 5.3 kB view raw
1// Copyright 2025 Jonas Kruckenberg 2// 3// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or 4// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or 5// http://opensource.org/licenses/MIT>, at your option. This file may not be 6// copied, modified, or distributed except according to those terms. 7 8mod args; 9mod printer; 10mod smoke; 11mod spectest; 12mod wast; 13 14use crate::tests::args::Arguments; 15use crate::tests::printer::Printer; 16use crate::{arch, state}; 17use alloc::boxed::Box; 18use alloc::sync::Arc; 19use core::any::Any; 20use core::ptr::addr_of; 21use core::sync::atomic::{AtomicU64, Ordering}; 22use core::{hint, slice}; 23use futures::FutureExt; 24use futures::future::try_join_all; 25use ktest::Test; 26 27/// The outcome of performing a single test. 28pub enum Outcome { 29 /// The test passed. 30 Passed, 31 /// The test failed. 32 Failed(Box<dyn Any + Send + 'static>), 33 /// The test was ignored. 34 Ignored, 35} 36 37/// Conclusion of running the whole test suite 38pub struct Conclusion { 39 /// Number of tests and benchmarks that were filtered out (either by the 40 /// filter-in pattern or by `--skip` arguments). 41 pub num_filtered_out: AtomicU64, 42 43 /// Number of passed tests. 44 pub num_passed: AtomicU64, 45 46 /// Number of failed tests and benchmarks. 47 pub num_failed: AtomicU64, 48 49 /// Number of ignored tests and benchmarks. 50 pub num_ignored: AtomicU64, 51 52 /// Number of benchmarks that successfully ran. 53 pub num_measured: AtomicU64, 54} 55 56impl Conclusion { 57 /// Exits the application with error code 101 if there were any failures. 58 /// Otherwise, returns normally. **This will not run any destructors.** 59 /// Consider using [`Self::exit_code`] instead for a proper program cleanup. 60 pub fn exit_if_failed(&self) { 61 if self.has_failed() { 62 arch::exit(101) 63 } 64 } 65 66 /// Returns whether there have been any failures. 67 pub fn has_failed(&self) -> bool { 68 self.num_failed.load(Ordering::Acquire) > 0 69 } 70 71 fn empty() -> Self { 72 Self { 73 num_filtered_out: AtomicU64::new(0), 74 num_passed: AtomicU64::new(0), 75 num_failed: AtomicU64::new(0), 76 num_ignored: AtomicU64::new(0), 77 num_measured: AtomicU64::new(0), 78 } 79 } 80} 81 82pub async fn run_tests(global: &'static state::Global) -> Conclusion { 83 let chosen = global.device_tree.find_by_path("/chosen").unwrap(); 84 let args = if let Some(prop) = chosen.property("bootargs") { 85 let str = prop.as_str().unwrap(); 86 Arguments::from_str(str) 87 } else { 88 Arguments::default() 89 }; 90 91 let tests = all_tests(); 92 93 // Create printer which is used for all output. 94 let printer = Printer::new(args.format); 95 96 // If `--list` is specified, just print the list and return. 97 if args.list { 98 printer.print_list(tests, args.ignored); 99 return Conclusion::empty(); 100 } 101 102 // Print number of tests 103 printer.print_title(tests.len() as u64); 104 105 let printer = Arc::new(printer); 106 let conclusion = Arc::new(Conclusion::empty()); 107 108 let tests = tests.iter().map(|test| { 109 if args.is_ignored(test) { 110 printer.print_test(&test.info); 111 conclusion.num_ignored.fetch_add(1, Ordering::Release); 112 futures::future::Either::Left(core::future::ready(Ok(()))) 113 } else { 114 let printer = printer.clone(); 115 let conclusion = conclusion.clone(); 116 117 let h = global 118 .executor 119 .try_spawn(async move { 120 // Print `test foo ...`, run the test, then print the outcome in 121 // the same line. 122 printer.print_test(&test.info); 123 (test.run)().await; 124 }) 125 .unwrap() 126 .inspect(move |res| match res { 127 Ok(_) => { 128 conclusion.num_passed.fetch_add(1, Ordering::Release); 129 } 130 Err(_) => { 131 conclusion.num_failed.fetch_add(1, Ordering::Release); 132 } 133 }); 134 135 futures::future::Either::Right(h) 136 } 137 }); 138 139 // we handle test failures through `Conclusion` so it is safe to ignore the result here 140 let _ = try_join_all(tests).await; 141 142 printer.print_summary(&conclusion); 143 144 Arc::into_inner(conclusion).unwrap() 145} 146 147pub fn all_tests() -> &'static [Test] { 148 #[used(linker)] 149 #[unsafe(link_section = "k23_tests")] 150 static mut LINKME_PLEASE: [Test; 0] = []; 151 152 unsafe extern "C" { 153 #[allow(improper_ctypes)] 154 static __start_k23_tests: Test; 155 #[allow(improper_ctypes)] 156 static __stop_k23_tests: Test; 157 } 158 159 let start = addr_of!(__start_k23_tests); 160 let stop = addr_of!(__stop_k23_tests); 161 162 let stride = size_of::<Test>(); 163 let byte_offset = stop as usize - start as usize; 164 let len = match byte_offset.checked_div(stride) { 165 Some(len) => len, 166 // The #[distributed_slice] call checks `size_of::<T>() > 0` before 167 // using the unsafe `private_new`. 168 None => unsafe { hint::unreachable_unchecked() }, 169 }; 170 171 unsafe { slice::from_raw_parts(start, len) } 172}