Duke-Tang/firmware-autotest

GitHub: Duke-Tang/firmware-autotest

基于Python和pytest的STM32 I2C传感器固件自动化测试框架,通过寄存器级模拟与故障注入验证驱动层错误恢复能力,支持CI驱动的确定性回归测试。

Stars: 0 | Forks: 0

# STM32 I2C 传感器固件自动化测试框架 一个基于 Python 的测试自动化框架,在寄存器级别模拟 STM32 I2C 温度传感器,其驱动层具有重试逻辑、故障注入压力测试和特性分析报告功能。 该项目旨在演示在无物理硬件可用时,固件自动化测试工程的最佳实践,包括:确定性压力测试、错误恢复逻辑的统计验证,以及由 CI 驱动的回归检查。 ## 为什么做这个项目 固件自动化测试工程师将大部分时间花在验证系统**在发生故障时**的行为,而不是在理想路径条件下的行为。本框架反映了真实的现实工作流: - 一个模拟器替代了 I2C 外设,因此测试可以在任何笔记本电脑或 CI 运行器上运行,无需真实硬件。 - 驱动层通过重试和指数退避机制封装了模拟器,提供了逼真的固件风格错误恢复能力。 - 测试使用带有固定种子的 RNG(随机数生成器)确定性地注入故障,因此相同的输入总是产生相同的判定结果——这是稳定 CI 所必需的。 - 特性分析运行器扫掠故障率范围,并生成人类可读的表格和机器可读的 JSON,用于下游分析。 ## 架构 ┌────────────────────┐ │ Application code │ read_temperature(), read_device_id() └─────────┬──────────┘ │ ┌─────────▼──────────┐ retry, backoff, statistics │ I2CDriver │ (device/i2c_driver.py) └─────────┬──────────┘ │ ┌─────────▼──────────┐ register-level emulation │ I2CSensorSim │ fault injection (NACK rate) │ │ (device/i2c_sensor_sim.py) └────────────────────┘ | 层级 | 文件 | 职责 | |--------------|---------------------------------|-----------------------------------------| | 应用层 | (调用方) | 调用高层驱动 API | | 驱动层 | `device/i2c_driver.py` | 重试、退避、错误统计 | | 硬件模拟层 | `device/i2c_sensor_sim.py` | 寄存器映射、故障注入、日志记录 | ## 项目结构 firmware-autotest/ ├── device/ # 固件侧模拟 + 驱动 │ ├── i2c_sensor_sim.py # TMP102 风格的 I2C 传感器模拟器 │ └── i2c_driver.py # 带有重试和统计功能的驱动 ├── tests/ # pytest 测试套件 │ ├── test_sensor_basic.py # 功能测试(5 个用例) │ └── test_sensor_stress.py # 故障注入 / 压力测试(5 个用例) ├── tools/ # 自动化测试工具 │ ├── run_characterization.py # 多场景特性分析运行器 │ └── log_parser.py # UART 风格日志解析器 ├── .github/workflows/ci.yml # GitHub Actions CI (pytest 矩阵) ├── requirements.txt └── README.md ## 快速开始 ``` git clone cd firmware-autotest pip install -r requirements.txt # 运行完整测试套件 pytest tests/ -v # 跨 7 个故障率 × 1000 次试验运行 characterization python3 tools/run_characterization.py --trials 1000 # 生成示例日志,然后解析它 python3 -c "from device.i2c_sensor_sim import I2CSensorSim; \ from device.i2c_driver import I2CDriver; \ d = I2CDriver(I2CSensorSim(fault_rate=0.2)); \ [d.read_temperature() for _ in range(50)]" python3 tools/log_parser.py sensor_run.log --json log_report.json ``` ## 测试覆盖率 10 个 pytest 用例,覆盖了标称行为和故障模式: **功能测试 (`test_sensor_basic.py`)** - 设备 ID 寄存器返回正确的常量 - 室温读数在合理的物理范围内 - 读取无效寄存器会引发 `ValueError` - 写入只读 ID 寄存器会引发 `PermissionError` - TX 计数器在每次事务处理时递增 **带故障注入的压力测试 (`test_sensor_stress.py`)** - 稳定的传感器:零故障 → 零重试(验证重试机制不会被轻易触发) - 不稳定的传感器:30% NACK 率 → 驱动程序恢复 ≥95% 的调用 - 损坏的传感器:100% NACK → 驱动程序在恰好 `max_retries + 1` 次后放弃 - 重试计数器与预期的尝试次数完全匹配 - `reset_stats()` 能正确地将所有计数器清零 所有测试在干净的机器上运行时间不超过一秒。 ## 特性分析结果 `tools/run_characterization.py` 扫掠故障率范围,并生成如下表格: Fault Rate | Trials | Success % | Fails | Avg Retry | Avg ms | p99 ms 0.00 | 1000 | 100.00% | 0 | 0.000 | 0.05 | 0.18 0.10 | 1000 | 100.00% | 0 | 0.101 | 0.34 | 3.68 0.30 | 1000 | 99.20% | 8 | 0.430 | 0.78 | 4.50 0.50 | 1000 | 93.50% | 65 | 1.005 | 1.62 | 8.10 0.70 | 1000 | 75.40% | 246 | 2.350 | 3.50 | 16.20 **结果解析:** 在 3 次重试的情况下,驱动程序可以完全恢复高达约 30% 的 NACK 率。超过约 50% 时,单靠重试是不够的——真实系统需要硬件级别的缓解措施(更长的总线拉高时间、更低的时钟频率或外设复位)。 同时也会生成 JSON 输出,供下游仪表盘或 CI 趋势跟踪摄取使用。 ## CI `.github/workflows/ci.yml` 会在每次推送和拉取请求时运行完整的测试套件,支持 Python 3.10 / 3.11 / 3.12。每次运行还会执行一项特性分析冒烟测试(每个场景 100 次试验),并将 JSON 报告作为构建产物上传。 ## 设计说明 - **为什么要有独立的驱动层?** 它将重试/超时策略与硬件细节隔离开来,因此相同的驱动程序可以位于真实的 STM32 HAL 或此模拟器之上。 - **为什么在压力测试中使用固定种子的 RNG?** 非确定性测试会导致 CI 不稳定。`random.seed(42)` 使故障模式可重现——相同的输入、相同的判定结果,每次运行都如此。 - **为什么使用指数退避?** 线性重试会持续冲击性能下降的总线;指数退避(1ms → 2ms → 4ms)为瞬态故障提供了清除时间。 - **为什么需要日志解析器?** 真实开发板上的真实固件仅暴露 UART 输出。基于正则表达式的解析器将非结构化日志转换为结构化指标,这是硬件在环自动化测试的标准模式。 ## 作者 Duke Tang — 作为固件自动化测试工程岗位的作品集项目而构建。
标签:ASM汇编, I2C协议, pytest, Python, STM32, 传感器仿真, 单元测试, 压力测试, 回归测试, 固件测试, 安全规则引擎, 寄存器级仿真, 嵌入式系统, 开源框架, 持续集成, 指数退避, 故障注入, 无后门, 测试框架, 硬件仿真, 确定性测试, 软件质量保证, 逆向工具, 错误恢复机制