Buhe Blog - Microsoft Internet Explorer

OS 简单教程番外1:给内核添加测试框架


给内核添加测试框架

为了方便开发我们给内核添加测试框架,rust 本来就有测试框架,可以利用起来

// in src/main.rs

#![feature(custom_test_frameworks)]
#![test_runner(crate::test_runner)]

#![reexport_test_harness_main = "test_main"]

#[cfg(test)]
fn test_runner(tests: &[&dyn Fn()]) {
   println!("Running {} tests", tests.len());
   for test in tests {
       test();
  }
}
  • #![feature(custom_test_frameworks)] 是自定义测试框架
  • #![test_runner(crate::test_runner)] 是用 test_runner 做为测试 runner
  • 入口本来是测试框架自己生成的 _start ,我们指定 test_main
  • 最后在我们的 rust_main 中调用 test_main
#[no_mangle]
extern "C" fn rust_main() -> ! {
   clear_bss();
   heap::init();
   trap::init();
   task::init();

   #[cfg(test)]
   test_main();

   task::run();
}

现在试试用 cargo test,可惜不行,因为我们要在 k210 上执行,所以改造 Makefile 吧。

test: env
@echo Platform: $(BOARD)
@cd ../user && make build
$(eval override KERNEL_ELF := $(shell cargo test --no-run --message-format=json | jq -r "select(.profile.test == true) | .filenames[]"))
@$(OBJCOPY) $(KERNEL_ELF) --strip-all -O binary $(KERNEL_BIN)
(which $(K210-BURNER)) || (cd .. && git clone https://github.com/sipeed/kflash.py.git && mv kflash.py tools)
@cp $(BOOTLOADER) $(BOOTLOADER).copy
@dd if=$(KERNEL_BIN) of=$(BOOTLOADER).copy bs=$(K210_BOOTLOADER_SIZE) seek=1
@mv $(BOOTLOADER).copy $(KERNEL_BIN)
@sudo chmod 777 $(K210-SERIALPORT)
python3 $(K210-BURNER) -p $(K210-SERIALPORT) -b 1500000 $(KERNEL_BIN)
python3 -m serial.tools.miniterm --eol LF --dtr 0 --rts 0 --filter direct $(K210-SERIALPORT) 115200
  • 先 build 应用程序,和直接 run 一样
  • $(eval override KERNEL_ELF := $(shell cargo test –no-run –message-format=json | jq -r “select(.profile.test == true) | .filenames[]”)) 这句不同,这句的意思是:
    • 安装 jq,brew install jq
    • 执行 cargo test –no-run –message-format=json ,带测试编译,不执行,结果 json 展示。
    • 因为二进制文件带编译 hash 每次都不一样,我们用 –message-format=json ,然后用 jq 解析 json
    • 最后赋值 KERNEL_ELF
  • 后面同 build 接着就可以添加测试用例了,然后 make test 执行
#[test_case]
pub fn heap_test() {
   use alloc::boxed::Box;
   use alloc::vec::Vec;
   extern "C" {
       fn sbss();
       fn ebss();
  }
   let bss_range = sbss as usize..ebss as usize;
   let a = Box::new(5);
   assert_eq!(*a, 5);
   assert!(bss_range.contains(&(a.as_ref() as *const _ as usize)));
   drop(a);
   let mut v: Vec<usize> = Vec::new();
   for i in 0..500 {
       v.push(i);
  }
   for i in 0..500 {
       assert_eq!(v[i], i);
  }
   assert!(bss_range.contains(&(v.as_ptr() as usize)));
   drop(v);
   println!("heap_test passed!");
}

可以看到,make test 结果在 k210 上执行测试用例,而 make run 和以前一样。

[rustsbi] RustSBI version 0.2.0-alpha.3
.______       __   __     _______.___________. _______..______   __
|   _ \     | | | |   /       |           | /       ||   _ \ | |
| |_) |   | | | |   |   (----`---| |----`|   (----`| |_) || |
|     /     | | | |   \   \       | |     \   \   |   _ < | |
| |\ \----.| `--' |.----)   |     | | .----)   |   | |_) || |
| _| `._____| \______/ |_______/       |__| |_______/   |______/ |__|

[rustsbi] Platform: K210 (Version 0.2.0)
[rustsbi] misa: RV64ACDFIMSU
[rustsbi] mideleg: 0x22
[rustsbi] medeleg: 0x1ab
[rustsbi] Kernel entry: 0x80020000
[kernel] app_0 [0x80035018, 0x80035938)
Running 1 tests
heap_test passed!
Hello OS from app
[kernel] Application exited with code 0

代码请参考: https://github.com/buhe/bugu/tree/5-test