第四节 组件测试和模块测试
Rust 工具链本身对测试的支持很方便,下面将基于这一有利条件,对内核的组件和模块进行功能测试验证。组件测试相当于 crate 级的测试,直接在 crate 根目录下建立 tests 模块,对组件进行基于公开接口的黑盒测试;模块测试对应于单元测试,在模块内建立子模块tests,对模块的内部方法进行白盒测试。在 Makefile 中,预留了一个test 命令入口,用于启动上述测试。
我们需要注意一个容易忽略的事实,通过 make run
编译并运行内核,与通过 make test
编译并执行测试,这二者的编译和运行环境是截然不同的,前者的编译目标是 RiscV64 体系结构,并通过 qemu-riscv64 模拟器运行;后者则是针对本地的 x86_64 体系结构,在本地 Linux 上作为应用直接运行(Linux on x86_64 这应该是我们大多数人的本地开发环境)。因此,通过 Rust 工具链进行测试是有局限的,测试的功能与实际将要运行的功能可能存在差异,测试时还有可能需要一些 dummy 测试桩的辅助。我们需要清醒的保持这一认识,避免在后面的实验开发和测试中陷入困惑。
下面对现有的工程项目进行一些改造,满足测试的需要:
-
Makefile 中 test 命令
调用
cargo test
启动测试:ifeq ($(filter $(MAKECMDGOALS),test),) # not run `cargo test` RUSTFLAGS := -C link-arg=-T$(LD_SCRIPT) -C link-arg=-no-pie export RUSTFLAGS endif test: cargo test --workspace --exclude "axorigin" -- --nocapture
测试针对整个工程 workspace 下包含的所有组件,只是排除 axorigin 应用本身。我们希望测试用例中能够通过 println! 直接输出信息到屏幕,所以指定了
--nocapture
参数。特别需要注意的是上面的 1~4 行,环境变量
RUSTFLAGS
不能出现在make test
的过程中,原因前面已经说了,测试时是基于 x86_64 的体系结构,这个环境变量却要求使用面向 RiscV64 的 linker.lds 链接脚本和参数。如果不注意这一点,就会出现如下的错误:= note: /usr/bin/ld: cannot represent machine `riscv' collect2: error: ld returned 1 exit status error: could not compile `axhal` (lib test) due to previous error
x86 工具链的链接器在解析 linker.lds 第一行时就发现错误,它不能识别
OUTPUT_ARCH(riscv)
,直接报错退出了。 -
工程 Workspace 级的 Cargo.toml
[workspace] resolver = "2" members = [ "axorigin", "axhal", ] [profile.release] lto = true
以后增加新组件时,需要扩展 members 列表。
-
改造 axhal 组件的代码组织结构
组件 axhal 负责屏蔽体系结构的差异,所以它对
make test
所要求的另类环境最为敏感。首先改造 crate 的 lib.rs:
// axhal/src/lib.rs #![no_std] #[cfg(target_arch = "riscv64")] mod riscv64; #[cfg(target_arch = "riscv64")] pub use self::riscv64::*; // axhal/src/riscv64.rs mod lang_items; mod boot; pub mod console; unsafe extern "C" fn rust_entry(_hartid: usize, _dtb: usize) { extern "C" { fn main(hartid: usize, dtb: usize); } main(_hartid, _dtb); }
原来 lib.rs 只是针对 RriscV64 的实现,现在下移到 riscv64.rs 模块文件中;相应的,lang_items.rs、boot.rs和 console.rs 三个文件,也移到到目录 riscv64 下。这样只有当目标体系结构是 RriscV64 时,才会引用这些实现,因而执行测试时不会编译它们。
改造 axhal 的 Cargo.toml:
[target.'cfg(target_arch = "riscv64")'.dependencies] sbi-rt = { version = "0.0.2", features = ["legacy"] }
把 [dependencies] 改成带体系结构条件的形式,避免在执行测试时引入 sbi-rt 这个无效引用。
用 git show --stat
看一下改造后的情况:
Cargo.toml | 10 ++++++++++
Makefile | 7 ++++++-
axhal/Cargo.toml | 2 +-
axhal/src/lib.rs | 15 ++++-----------
axhal/src/riscv64.rs | 10 ++++++++++
axhal/src/{ => riscv64}/boot.rs | 0
axhal/src/{ => riscv64}/console.rs | 0
axhal/src/{ => riscv64}/lang_items.rs | 0
8 files changed, 31 insertions(+), 13 deletions(-)
分别执行 make run
和 make test
,验证我们的成果。
前者的输出没有变化,而执行后者时,显示如下:
cargo test --workspace --exclude "axorigin" -- --nocapture Compiling axhal v0.1.0 (/home/cloud/gitStudy/arceos_tutorial/axhal) Finished test [unoptimized + debuginfo] target(s) in 0.20s Running unittests src/lib.rs (target/debug/deps/axhal-0770fa3f3cc1ea75)
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Doc-tests axhal
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
目前还没有写任何测试用例,测试框架只是空转一次。
下节开始,我们将使用测试用例来驱动内核的开发。