1#![allow(dead_code)]
2use crate::{
3 arch::intr::{
4 are_interrupts_enabled, disable_interrupts, enable_interrupts, read_and_enable_interrupts,
5 restore_interrupts,
6 },
7 println,
8};
9use core::sync::atomic::{AtomicUsize, Ordering};
10
11#[derive(Copy, Clone, Debug)]
12pub struct FailedAssertion {
13 pub cond: &'static str,
14 pub file: &'static str,
15 pub line: u32,
16}
17
18impl FailedAssertion {
20 pub const fn new(cond: &'static str, file: &'static str, line: u32) -> Self {
21 Self { cond, file, line }
22 }
23}
24
25pub static mut FAILED_LIST: [Option<FailedAssertion>; 32] = [None; 32];
26pub const FAILED_LIST_CAPACITY: usize = 32;
27pub static mut FAILED_INDEX: usize = 0;
28pub static TEST_FAILED: AtomicUsize = AtomicUsize::new(0);
29pub fn record_failed_assertion(assertion: FailedAssertion) {
38 let index = TEST_FAILED.fetch_add(1, core::sync::atomic::Ordering::SeqCst);
39 if index < FAILED_LIST_CAPACITY {
40 unsafe {
41 FAILED_LIST[index] = Some(assertion);
42 if index + 1 > FAILED_INDEX {
43 FAILED_INDEX = index + 1;
44 }
45 }
46 } else {
47 println!(
48 "\x1b[91m[warn] Failed assertion list is full (capacity {}). Cannot record: {}\x1b[0m",
49 FAILED_LIST_CAPACITY, assertion.cond
50 );
51 }
52}
53
54#[macro_export]
56macro_rules! kassert {
57 ($cond:expr) => {{
58 if !$cond {
59 let fa =
62 $crate::test::macros::FailedAssertion::new(stringify!($cond), file!(), line!());
63 $crate::test::macros::record_failed_assertion(fa);
65 }
66 }};
67}
68
69#[macro_export]
74macro_rules! early_test {
75 ($func_name:ident,$body:block) => {
77 paste::paste! {
79 #[doc = concat!("Early test case: ", stringify!($func_name))]
81 #[allow(dead_code)] fn [<early_test_ $func_name>]() {
83 println!("\x1b[36m[early_test] Running: {}\x1b[0m\n", stringify!($func_name));
85 $body
86 println!("\x1b[36m[early_test] Passed: {}\x1b[0m\n", stringify!($func_name));
87 }
88
89 #[used] #[unsafe(link_section = ".early_test_entry")]
92 static [<EARLY_TEST_ENTRY_ $func_name:upper>]: fn() = [<early_test_$func_name>];
93 }
94 };
95}
96#[macro_export]
102macro_rules! test_case {
103 (
104 $func_name:ident,
105 ($env:ident),
106 $body:block
107 ) => {
108 #[cfg(test)]
109 #[test]
110 fn $func_name() {
111
112 let _guard = $crate::test::guard::TestEnvGuard::enter($env);
113
114 println!("\x1b[33m[Running test: {} (with env)]\x1b[0m", stringify!($name));
115 let failed_before = $crate::test::macros::TEST_FAILED.load(core::sync::atomic::Ordering::SeqCst);
116
117 $body
118
119 let failed_after = $crate::test::macros::TEST_FAILED.load(core::sync::atomic::Ordering::SeqCst);
120 if failed_after == failed_before {
121 println!("\x1b[32m[ok] {}\x1b[0m", stringify!($name));
122 } else {
123 println!("\x1b[91m[failed] {}\x1b[0m", stringify!($name));
124 }
125 }
126 };
127 (
128 $func_name:ident,
129 $body:block
130 ) => {
131 #[doc = concat!("Test case: ", stringify!($func_name))]
132 #[test_case]
133 fn $func_name() {
134 $crate::println!("\x1b[33m=======================================\x1b[0m");
135 $crate::println!(
136 "\x1b[33mRunning test: {}::{}\x1b[0m",
137 module_path!(),
138 stringify!($func_name)
139 );
140
141 let failed_before = $crate::test::macros::TEST_FAILED.load(core::sync::atomic::Ordering::SeqCst);
142
143 $body
144
145 let failed_after = $crate::test::macros::TEST_FAILED.load(core::sync::atomic::Ordering::SeqCst);
146 let failed_count = failed_after - failed_before;
147
148 unsafe {
149 for i in failed_before..$crate::test::macros::FAILED_INDEX {
150 if let Some(fail) = $crate::test::macros::FAILED_LIST[i] {
151 $crate::println!(
152 "\x1b[31mFailed assertion: {} at {}:{}\x1b[0m",
153 fail.cond, fail.file, fail.line
154 );
155 }
156 }
157 }
158
159 if failed_count == 0 {
160 $crate::println!("\x1b[32m[ok] Test passed\x1b[0m\n");
161 } else {
162 $crate::println!(
163 "\x1b[91m[failed] Test failed with {} failed assertions\x1b[0m\n",
164 failed_count
165 );
166 }
167 }
168 };
169}
170fn run_test(test_name: &str, env_name: Option<&str>, test_fn: impl FnOnce()) {
172 println!("\x1b[33m=======================================\x1b[0m");
173 if let Some(env) = env_name {
174 println!(
175 "\x1b[33mRunning test: {}::{} (with env: {})\x1b[0m",
176 module_path!(),
177 test_name,
178 env
179 );
180 } else {
181 println!(
182 "\x1b[33mRunning test: {}::{}\x1b[0m",
183 module_path!(),
184 test_name
185 );
186 }
187
188 let failed_before = TEST_FAILED.load(core::sync::atomic::Ordering::SeqCst);
189
190 test_fn();
192
193 let failed_after = TEST_FAILED.load(core::sync::atomic::Ordering::SeqCst);
194 let failed_count = failed_after - failed_before;
195
196 unsafe {
197 for i in failed_before..FAILED_INDEX {
198 if let Some(fail) = &FAILED_LIST[i] {
199 println!(
200 "\x1b[31mFailed assertion: {} at {}:{}\x1b[0m",
201 fail.cond, fail.file, fail.line
202 );
203 }
204 }
205 }
206
207 if failed_count == 0 {
208 println!("\x1b[32m[ok] Test passed\x1b[0m\n");
209 } else {
210 println!(
211 "\x1b[91m[failed] Test failed with {} failed assertions\x1b[0m\n",
212 failed_count
213 );
214 }
215}
216
217pub fn run_early_tests() {
219 unsafe extern "C" {
221 static __early_test_start: extern "C" fn();
222 static __early_test_end: extern "C" fn();
223 }
224
225 let start = unsafe { &__early_test_start as *const _ as *const extern "C" fn() };
229 let end = unsafe { &__early_test_end as *const _ as *const extern "C" fn() };
230
231 let count = unsafe { end.offset_from(start) } as usize;
233 if count == 0 {
234 println!("\x1b[36m[early_test] No early tests to run.\x1b[0m");
235 return;
236 }
237
238 println!(
239 "\n\x1b[36m--- Running {} early tests (pre-mm) ---\x1b[0m",
240 count
241 );
242
243 for i in 0..count {
245 let test_fn = unsafe { *start.add(i) };
246 test_fn();
247 }
248
249 println!("\x1b[36m--- Early tests finished ---\x1b[0m\n");
250}