深入解析Phil-opp的Blog OS项目:实现裸机环境下的测试框架

深入解析Phil-opp的Blog OS项目:实现裸机环境下的测试框架

blog_os Writing an OS in Rust blog_os 项目地址: https://gitcode.com/gh_mirrors/bl/blog_os

引言

在操作系统开发中,测试是一个至关重要的环节。本文将深入探讨如何在Phil-opp的Blog OS项目中实现一个裸机环境下的测试框架。这个项目是一个用Rust编写的简单操作系统内核,运行在裸机环境中,没有标准库支持。

Rust测试框架的挑战

标准测试框架的限制

Rust内置的测试框架依赖于标准库,这对于我们的no_std内核来说是个问题。当我们尝试在项目中运行cargo test时,会遇到找不到test库的错误,因为该库需要标准库支持。

自定义测试框架解决方案

幸运的是,Rust提供了custom_test_frameworks特性,允许我们替换默认的测试框架。这个特性不需要外部库,因此可以在#[no_std]环境中工作。

实现步骤:

  1. main.rs中添加特性声明
  2. 定义测试运行器函数
  3. 使用#[test_case]属性标记测试函数
#![feature(custom_test_frameworks)]
#![test_runner(crate::test_runner)]

#[cfg(test)]
pub fn test_runner(tests: &[&dyn Fn()]) {
    println!("Running {} tests", tests.len());
    for test in tests {
        test();
    }
}

测试执行流程

入口点调整

由于我们使用自定义入口点_start,需要调整测试执行流程:

  1. 使用reexport_test_harness_main属性重命名生成的测试主函数
  2. _start函数中调用重命名后的测试主函数
#![reexport_test_harness_main = "test_main"]

#[no_mangle]
pub extern "C" fn _start() -> ! {
    #[cfg(test)]
    test_main();
    
    loop {}
}

QEMU退出机制

isa-debug-exit设备

为了在测试完成后自动退出QEMU,我们使用QEMU的isa-debug-exit设备:

  1. Cargo.toml中配置QEMU参数
  2. 实现端口I/O操作来与设备通信
[package.metadata.bootimage]
test-args = ["-device", "isa-debug-exit,iobase=0xf4,iosize=0x04"]

退出代码实现

定义退出代码枚举并使用x86_64库进行端口操作:

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u32)]
pub enum QemuExitCode {
    Success = 0x10,
    Failed = 0x11,
}

pub fn exit_qemu(exit_code: QemuExitCode) {
    unsafe {
        let mut port = Port::new(0xf4);
        port.write(exit_code as u32);
    }
}

串口输出

16550 UART实现

为了在测试后查看输出,我们实现串口输出:

  1. 添加uart_16550依赖
  2. 创建串口模块并初始化
lazy_static! {
    pub static ref SERIAL1: Mutex<SerialPort> = {
        let mut serial_port = unsafe { SerialPort::new(0x3F8) };
        serial_port.init();
        Mutex::new(serial_port)
    };
}

输出宏

为了方便使用,实现serial_print!serial_println!宏:

#[macro_export]
macro_rules! serial_print {
    ($($arg:tt)*) => {
        $crate::serial::_print(format_args!($($arg)*));
    };
}

完整测试框架

测试运行器改进

整合所有功能后的测试运行器:

pub fn test_runner(tests: &[&dyn Testable]) {
    serial_println!("Running {} tests", tests.len());
    for test in tests {
        test.run();
    }
    exit_qemu(QemuExitCode::Success);
}

测试特征

定义Testable特征统一测试接口:

pub trait Testable {
    fn run(&self);
}

impl<T> Testable for T
where
    T: Fn(),
{
    fn run(&self) {
        serial_print!("{}...\t", core::any::type_name::<T>());
        self();
        serial_println!("[ok]");
    }
}

实际应用

示例测试

#[test_case]
fn trivial_assertion() {
    assert_eq!(1, 1);
}

#[test_case]
fn test_breakpoint_exception() {
    x86_64::instructions::interrupts::int3();
}

异常处理测试

#[test_case]
fn test_breakpoint_exception() {
    // 触发断点异常
    x86_64::instructions::interrupts::int3();
}

结论

通过本文,我们实现了一个完整的裸机测试框架,具有以下特点:

  1. 不依赖标准库的自定义测试框架
  2. 测试完成后自动退出QEMU
  3. 通过串口输出测试结果
  4. 统一的测试接口和漂亮的输出格式

这个框架为后续操作系统开发提供了可靠的测试基础,使得我们能够在不依赖标准库的情况下进行单元测试和集成测试。

blog_os Writing an OS in Rust blog_os 项目地址: https://gitcode.com/gh_mirrors/bl/blog_os

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

袁立春Spencer

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值