os/test/
macros.rs

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
18// 添加公有构造函数,便于宏/其它代码显式构造
19impl 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);
29/// 安全地记录一个失败的断言。
30///
31/// 此函数是 `kassert!` 宏的后端,负责处理 unsafe 的全局可变状态访问。
32/// 它会原子地更新失败列表,防止并发访问时的数据竞争。
33///
34/// # Arguments
35///
36/// * `assertion`: 要记录的 `FailedAssertion` 实例。
37pub 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/// 判断条件是否为真,如果为假则记录一个失败的断言。
55#[macro_export]
56macro_rules! kassert {
57    ($cond:expr) => {{
58        if !$cond {
59            // 使用 $crate 确保宏在任何模块中都能正确找到路径
60            // 在安全上下文中先构造值,避免在 unsafe 块内展开 metavariables
61            let fa =
62                $crate::test::macros::FailedAssertion::new(stringify!($cond), file!(), line!());
63            // 调用安全封装函数(内部负责 unsafe)
64            $crate::test::macros::record_failed_assertion(fa);
65        }
66    }};
67}
68
69/// 定义一个早期测试(在内存管理等核心服务初始化前运行)。
70///
71/// 测试函数会被放入一个自定义的链接段 `.early_test_entry` 中,
72/// 以便在启动早期被统一执行。
73#[macro_export]
74macro_rules! early_test {
75    // 匹配一个函数名和一个代码块
76    ($func_name:ident,$body:block) => {
77        // 使用 paste::paste! 来组合标识符,确保函数名唯一
78        paste::paste! {
79            // 生成一个内部函数,避免命名冲突
80            #[doc = concat!("Early test case: ", stringify!($func_name))]
81            #[allow(dead_code)] // 函数不是直接调用的,所以允许未使用
82            fn [<early_test_ $func_name>]() {
83                // 打印测试开始信息,此时 console 应该已经可以工作
84                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            // 将函数指针放入自定义的链接器段
90            #[used] // 确保编译器不会优化掉这个静态变量
91            #[unsafe(link_section = ".early_test_entry")]
92            static [<EARLY_TEST_ENTRY_ $func_name:upper>]: fn() = [<early_test_$func_name>];
93        }
94    };
95}
96/// 定义一个标准的测试用例。
97///
98/// 提供两种语法:
99/// 1. 带环境:`test_case!(test_name, env_variable, { code });`
100/// 2. 不带环境:`test_case!(test_name, { code });`
101#[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}
170//test_case!的普通函数
171fn 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    // 执行测试函数
191    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
217//early_test!的执行函数
218pub fn run_early_tests() {
219    // 从链接器脚本中定义的符号获取段的起始和结束地址
220    unsafe extern "C" {
221        static __early_test_start: extern "C" fn();
222        static __early_test_end: extern "C" fn();
223    }
224
225    // 创建一个指向函数指针的切片
226    // 安全性:我们假设链接器脚本正确创建了这些符号,并且它们对齐了函数指针。
227    // 段中的每个项都是一个 `fn()` 类型的指针。
228    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    // 计算测试数量
232    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    // 遍历并执行所有测试函数
244    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}