Rust嵌入式测试框架embedded-test的使用,高效轻量级的嵌入式系统测试工具
以下是基于您提供内容的完整示例和说明:
Rust嵌入式测试框架embedded-test的使用,高效轻量级的嵌入式系统测试工具
框架概述
embedded-test是为嵌入式系统(riscv、arm和xtensa)设计的测试框架,与probe-rs工具配合使用,可在实际设备上运行集成测试。测试流程包括:
- 通过probe-rs run烧录测试程序
- 通过半主机模式获取测试信息
- 逐个执行测试用例(每次重置设备)
- 报告最终结果
核心特性
- 隔离测试:每个用例独立运行,自动重置设备
- 状态传递:支持初始化函数传递测试状态
- 异步支持:可选embassy特性支持异步测试
- 丰富属性:支持should_panic/ignore/timeout等测试属性
完整配置示例
- Cargo.toml配置:
[package]
name = "embedded-test-demo"
version = "0.1.0"
[dev-dependencies]
embedded-test = { version = "0.6.0", features = ["log"] }
rtt-target = { version = "0.3.1" }
[[test]]
name = "hw_test"
harness = false
- build.rs配置:
fn main() {
println!("cargo::rustc-link-arg-tests=-Tembedded-test.x");
}
- 测试代码示例(main.rs):
#![no_std]
#![no_main]
use core::panic::PanicInfo;
#[panic_handler]
fn panic(_info: &PanicInfo) -> ! {
loop {}
}
#[cfg(test)]
#[embedded_test::tests]
mod tests {
use cortex_m::asm;
// 同步初始化函数
#[init]
fn init() -> u32 {
42 // 返回测试用的魔数
}
// 基础测试用例
#[test]
fn basic_check() {
assert_eq!(2 + 2, 4);
}
// 带状态的测试
#[test]
fn state_test(input: u32) {
assert_eq!(input, 42);
}
// 日志测试(需要log特性)
#[test]
#[cfg(feature = "log")]
fn test_with_logging() {
rtt_target::rtt_init_print!();
log::info!("Test log output");
assert!(true);
}
// 超时测试(10秒超时)
#[test]
#[timeout(10)]
fn long_running_test() {
for _ in 0..100_000 {
asm::nop();
}
}
// 预期panic的测试
#[test]
#[should_panic]
fn expected_failure() {
panic!("This should be caught");
}
}
- 运行配置(.cargo/config.toml):
[target.thumbv7em-none-eabihf]
runner = "probe-rs run --chip STM32F767ZITx"
执行流程
- 安装工具链:
cargo install probe-rs-tools
rustup target add thumbv7em-none-eabihf
- 运行测试:
cargo test --test hw_test --features log
特性说明
特性名 | 作用描述 |
---|---|
defmt | 通过defmt输出测试结果 |
embassy | 启用异步测试支持 |
xtensa-semihosting | 为XTensa架构启用半主机模式 |
该框架特别适合需要硬件在环测试的嵌入式开发场景,通过实际硬件验证确保代码可靠性。
1 回复
Rust嵌入式测试框架embedded-test的使用
介绍
embedded-test
是一个专为Rust嵌入式开发设计的高效、轻量级测试框架。它为嵌入式系统提供了简单易用的测试工具,特别适合在资源受限的环境中进行单元测试和集成测试。
主要特点:
- 轻量级设计,适合资源受限的嵌入式环境
- 支持裸机(no_std)环境
- 提供基本的断言功能
- 可定制的测试输出
- 与cargo test集成
使用方法
1. 添加依赖
首先在Cargo.toml
中添加依赖:
[dev-dependencies]
embedded-test = "0.1"
2. 基本测试示例
#![no_std]
#![no_main]
use embedded_test::runner::test_runner;
use embedded_test::println;
#[test_case]
fn test_addition() {
assert_eq!(2 + 2, 4); // 基本数学运算测试
}
#[test_case]
fn test_memory_allocation() {
let x = 42; // 简单内存分配测试
assert_ne!(x, 0);
}
#[entry]
fn main() -> ! {
test_runner(); // 运行所有测试用例
loop {}
}
3. 自定义测试运行器
use embedded_test::{
runner::TestRunner,
printer::SerialPortPrinter,
TestResult
};
// 自定义测试运行器,使用串口输出结果
fn custom_test_runner(tests: &[&dyn Fn() -> TestResult]) {
let mut printer = SerialPortPrinter::new(0xE0000000 as *mut u8);
let mut runner = TestRunner::new(&mut printer);
runner.run_tests(tests); // 运行所有测试
}
#[entry]
fn main() -> ! {
// 指定要运行的测试用例
custom_test_runner(&[&test_addition, &test_memory_allocation]);
loop {}
}
4. 硬件相关测试示例
#[test_case]
fn test_led_blink() {
use embedded_hal::digital::v2::OutputPin;
let mut led = Led::new();
led.set_high().unwrap(); // 点亮LED
assert_eq!(led.is_high(), true); // 验证LED状态
led.set_low().unwrap(); // 关闭LED
assert_eq!(led.is_low(), true); // 再次验证LED状态
}
#[test_case]
fn test_adc_reading() {
let adc = Adc::new();
// 读取ADC通道0的值
let reading = adc.read(Channel::Channel0).unwrap();
// 验证ADC读数在合理范围内
assert!(reading > 0 && reading < 4096, "ADC reading out of range");
}
5. 测试组织
mod sensor_tests {
use super::*;
#[test_case]
fn test_temperature_sensor() {
let temp = read_temperature(); // 读取温度传感器
// 验证温度在合理范围内
assert!(temp > -40.0 && temp < 85.0, "Temperature out of expected range");
}
}
mod communication_tests {
use super::*;
#[test_case]
fn test_i2c_communication() {
let mut i2c = I2c::new();
// 测试I2C通信
let result = i2c.write(0x42, &[0x01, 0x02]);
assert!(result.is_ok(), "I2C communication failed");
}
}
高级用法
1. 测试夹具
// 测试夹具结构体
struct TestFixture {
peripheral: SomePeripheral,
}
impl TestFixture {
fn new() -> Self {
Self {
peripheral: SomePeripheral::initialize(), // 初始化外设
}
}
}
#[test_case]
fn test_with_fixture() {
let fixture = TestFixture::new(); // 创建测试夹具
// 使用夹具中的外设进行操作
let result = fixture.peripheral.do_something();
assert!(result.is_ok()); // 验证操作结果
}
2. 模拟时间测试
#[test_case]
fn test_timeout() {
use embedded_time::duration::Milliseconds;
let start = get_system_tick(); // 获取开始时间
delay(Milliseconds(500)); // 延迟500ms
let elapsed = get_system_tick() - start; // 计算经过时间
// 验证延迟时间在合理范围内
assert!(
elapsed >= Milliseconds(500) && elapsed <= Milliseconds(520),
"Delay not within expected range"
);
}
3. 测试失败处理
#[test_case]
fn test_error_handling() {
let result = risky_operation(); // 执行可能失败的操作
if let Err(e) = result {
println!("Test failed with error: {:?}", e); // 输出错误信息
assert!(false, "Operation failed with error"); // 标记测试失败
}
}
完整示例demo
下面是一个完整的嵌入式测试项目示例,展示了如何使用embedded-test
框架:
#![no_std]
#![no_main]
use embedded_test::{
runner::test_runner,
println,
TestResult
};
use cortex_m_rt::entry;
use panic_halt as _;
// 简单数学运算测试
#[test_case]
fn test_math_operations() {
assert_eq!(1 + 1, 2);
assert_ne!(5 * 5, 10);
}
// 硬件抽象层测试
#[test_case]
fn test_hal_operations() {
// 假设我们有一个模拟的GPIO实现
let mut pin = MockGpio::new();
pin.set_high().unwrap();
assert!(pin.is_high().unwrap());
pin.set_low().unwrap();
assert!(pin.is_low().unwrap());
}
// 自定义测试运行器
fn custom_test_runner(tests: &[&dyn Fn() -> TestResult]) {
println!("Starting {} tests...", tests.len());
for (i, test) in tests.iter().enumerate() {
println!("Running test #{}", i + 1);
match test() {
TestResult::Passed => println!("Test #{} passed", i + 1),
TestResult::Failed(msg) => println!("Test #{} failed: {}", i + 1, msg),
}
}
println!("All tests completed");
}
#[entry]
fn main() -> ! {
// 使用默认测试运行器
// test_runner();
// 或者使用自定义测试运行器
custom_test_runner(&[&test_math_operations, &test_hal_operations]);
loop {}
}
// 模拟GPIO实现
struct MockGpio {
state: bool,
}
impl MockGpio {
fn new() -> Self {
Self { state: false }
}
fn set_high(&mut self) -> Result<(), &'static str> {
self.state = true;
Ok(())
}
fn set_low(&mut self) -> Result<(), &'static str> {
self.state = false;
Ok(())
}
fn is_high(&self) -> Result<bool, &'static str> {
Ok(self.state)
}
fn is_low(&self) -> Result<bool, &'static str> {
Ok(!self.state)
}
}
注意事项
- 在嵌入式环境中,确保测试不会影响实际的硬件状态
- 考虑内存限制,避免在测试中使用过多内存
- 对于时间敏感的测试,可能需要调整容忍范围
- 某些测试可能需要特定的硬件设置才能运行
embedded-test
为嵌入式Rust开发提供了简单有效的测试解决方案,特别适合在持续集成环境中验证嵌入式系统的功能。