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