ESPectre 固件 CI 决策记录:移除 QEMU smoke 测试,以产品构建矩阵与宿主机测试作为固件质量主信号
【免费下载链接】espectreWi-Fi CSI motion sensing for ESP32. C++ SDK, ESPHome, Native, and Matter frontends, browser tools, and a CLI for the full device lifecycle. GPLv3 and commercial licensing.项目地址: https://gitcode.com/GitHub_Trending/es/espectre
本文是 ESPectre 项目架构决策记录(ADR)的解读文章,对应仓库文档 docs/adr/2026-07-18-remove-qemu-smoke-tests-from-firmware-ci.md(Status: Accepted,2026-07-18 记录,2026-08-26 更新)。文章围绕该 ADR 的决策脉络展开,并结合当前仓库中 CI 工作流、构建脚本与宿主机测试的实际状态,说明"用模拟器做冒烟验证"这条路线为何被放弃、代价是什么、以及 ESPectre 最终用哪些机制替代了它。读完你可以掌握一套可复用的判断框架:在什么条件下仿真冒烟测试会变成 CI 的"虚假安全感",以及如何用构建矩阵 + 宿主机测试 + 定向硬件测试重新组织固件验证策略。
决策背景:QEMU 冒烟测试曾想验证什么
当本决策做出时,ESPectre 需要为ESPHome、Native、Matter以及当时的Streamer前端分别在其支持的目标芯片矩阵上构建固件。每个前端的芯片矩阵并不相同,而 QEMU 冒烟覆盖把这个 CI 面进一步放大:额外的矩阵分支、产物处理、日志上传以及大量前端专属的 workaround(workaround 指为了迁就模拟器而专门写的分支代码或配置)。
QEMU 冒烟测试设计上只能提供有限的启动验证,ADR 明确列出了其预期信号:
- 合并后的 flash 镜像结构有效;
- bootloader 能启动;
- 分区表可读;
- 应用程序镜像能被加载;
- 在后续硬件初始化之前,可能打印出一条最小启动标记(startup marker)。
换句话说,QEMU 只能回答"镜像烧进去之后能不能跑过最开头一小段",而这恰恰不是 ESPectre 的主要产品风险所在。
核心矛盾:QEMU 无法覆盖 ESPectre 真正的风险面
ESPectre 是一个Wi-Fi CSI 运动感知项目(C++ SDK + ESPHome/Native/Matter 前端 + 浏览器工具 + CLI),其前端强依赖硬件路径,而 Espressif QEMU 并不能完整模拟这些路径。ADR 逐条列出了缺口:
- Wi-Fi PHY 与 modem-clock 的 bring-up;
- 蓝牙控制器与 NimBLE;
- Matter-over-BLE 配网(commissioning);
- ESPHome Improv / BLE 配网流程;
- 全部已发布芯片的完整覆盖。
其中"完整芯片覆盖"尤为关键:QEMU 只覆盖了已发布芯片的一个子集,于是 CI 上出现了一个不对称、且可能误导人的质量信号——某个芯片在模拟器里"启动了",很容易被误读为整个产品矩阵都是健康的。
ADR 还记录了本地验证得出的具体失败证据:
- Native 产品固件在 ESP32-C3 上:QEMU 下只运行到蓝牙控制器初始化,随后断言(assert),前端根本来不及执行任何真实的 BLE 回退逻辑;
- 原 Streamer 前端:可以走到早期启动标记,但随后在 QEMU 未建模的 Wi-Fi PHY 路径上失败;
- QEMU 从未触及CSI 感知、配网、Wi-Fi 关联、BLE、Matter 配网这些真正的运行时价值点。
对照当前源码结构也可以印证这一判断:ESPectre 的传感核心是真实 RF 数据通路,例如 csi_capture_service.cpp 与 wifi_csi_interface.h 直接对接硬件 CSI 回调,csi_pipeline.cpp 负责帧解析与归一化。这些路径的可用性本质上取决于真实 Wi-Fi PHY 行为,属于 QEMU 无法建模的部分——从源码结构看,仿真环境即便能启动镜像,也无法对这些代码产生任何有价值的验证。
决策内容:删除 QEMU 冒烟测试,但保留产品构建矩阵
决策本身非常干脆:从固件 CI 中移除 QEMU 冒烟测试。具体包含四项动作:
- 删除 QEMU 专属的工作流分支与日志产物;
- 删除 QEMU 专属的 helper action 与配置;
- 删除仅为支持 QEMU 冒烟路径而存在的代码或配置改动;
- 保留每个受维护前端在 CI 中的支持产品构建矩阵。
同时,ADR 重新划定了 CI 的职责边界,CI 仍然负责:
- 构建所有受支持的固件目标;
- 为 snapshot 与 release 工作流产出可发布的产物;
- 验证已被宿主机(host-side)测试覆盖的非固件测试面。
这一点在当前仓库中可以直接验证。搜索整个仓库,QEMU 相关字样仅出现在 ADR 目录的索引 docs/adr/README.md 中,.github 下不存在任何 QEMU 工作流或脚本残留,说明"删除 QEMU 专属分支与 helper"的动作已经完整落地。
决策落地后的 CI 现状(源码佐证)
决策之后,.github/workflows/ci.yml 的结构与 ADR 的职责划分完全吻合:
宿主机测试作业(对应"非固件测试面"):
test-cpp:调用 test/cpp/run_coverage.sh 以--ci模式运行 C++ 单元测试并产出覆盖率(ci.yml);test-python:运行 Python 产品与性能测试(./test/python/run_coverage.sh),并复用 NPZ 缓存以控制测试时长。
固件构建矩阵作业(对应"构建所有受支持目标 + 产出可发布产物"):
build-esphome:ESP32 / S3 / S2 / C3 / C5 / C6 共 6 个芯片,逐个执行esphome compile,产出 factory 与 OTA 镜像(ci.yml);build-matter:ESP32 / C3 / C5 / C6 / S3 共 5 个芯片(ci.yml);build-native:ESP32 / C3 / C5 / C6 / S3 / S2 共 6 个芯片(ci.yml)。
三个前端的芯片矩阵各不相同,正好印证 ADR 中"exact chip matrix differs by frontend"的描述——这也是当初 QEMU 冒烟覆盖"只覆盖子集"会造成误导的原因。
产物完整性校验:Native 的构建脚本 .github/scripts/build_native_firmware.sh 展示了"以产品构建本身作为产物完整性检查"的落地方式:在 ESP-IDF Docker 容器(espressif/idf:v5.5.5)中执行./espectre native build,随后用 esptoolmerge-bin把各段合并为可烧录镜像,再生成 SPDX SBOM 等合规工件。镜像能否被 esptool 正确合并、分区布局是否自洽,本身就是一次对"镜像结构有效"的强校验,且完全发生在真实目标工具链上,不依赖模拟器。
矩阵完整性闸门:snapshot 与 release 工作流在发布前调用 build_firmware_manifest.py,并以--require-complete-matrix强制要求固件矩阵必须齐全(见 .github/workflows/snapshot.yml 与 .github/workflows/release.yml)。这意味着"CI 绿"现在等价于"所有受支持前端 × 所有目标芯片都真实构建成功",而非"模拟器部分启动了"。
被否决的替代方案
ADR 记录了三类替代方案及其否决理由,这部分对同类项目最有借鉴价值:
方案一:只为部分前端保留 QEMU
被否决。原 Streamer 前端虽然能提供很窄的早启动信号,但该信号价值太小,不足以支撑一套定制 CI 路径,且它根本不覆盖真实 Wi-Fi 与 CSI 行为。换句话说:为一个"几乎测不到东西"的路径维护专属 CI 成本,性价比为负。
方案二:只为部分芯片保留 QEMU
被否决。部分芯片覆盖既增加维护成本,又会让状态检查看起来"比实际更具代表性"。这是对"不对称质量信号"的直接回应——宁可没有,也不要一个会撒谎的部分信号。
方案三:把 QEMU 降级为不阻塞的信息性作业(advisory job)
被否决。即便只作参考信息,它仍然消耗 CI 时间、增加工作流复杂度,并持续诱导未来出现"仅为迁就 QEMU 而产生的代码或配置漂移"。ADR 明确反对这种"留着以后也许有用"的妥协。
后果评估:收益、代价与缓解措施
收益(决策带来):
- 固件 CI 更短、更简单;
- 前端专属的 CI 例外与产物更少;
- 生产固件不再有"围绕模拟器限制来塑形"的压力;
- 信号更清晰:CI 通过 = 产品矩阵构建成功,而不是"模拟器部分启动了"。
代价(决策付出):
- CI 不再拥有基于模拟器的早启动 sanity check;
- 部分镜像组装类回归可能比以往更晚被发现。
缓解措施(如何对冲代价):
- 保持宿主机测试强健(对应现状中的 test-cpp / test-python 覆盖,历史沿革可参考 docs/CHANGELOG.md 中 2.5.0 时期"迁移到以宿主机 CMake/CTest 覆盖为主的 CI"的说明);
- 以产品构建本身作为主要产物完整性检查(对应 build-* 矩阵 + esptool merge + 矩阵完整性闸门);
- 涉及射频行为时,优先做定向的硬件冒烟测试,而不是回退到模拟器。
从该 ADR 提炼的可复用工程经验
如果把这份 ADR 抽象成方法论,可以归纳为四条,适用于任何"固件 + 多前端 + 多芯片"的 CI 设计:
- 先定义冒烟测试要防的真实风险。启动类冒烟只覆盖"镜像结构 / bootloader / 分区表"这一层;如果产品的最高风险在射频、配网与运行时数据通路,仿真的启动信号就是弱信号,不应作为质量门禁的依据。
- 警惕"部分覆盖"制造的假象。只覆盖部分芯片、部分前端的模拟器测试,会让整体状态看起来比实际更健康,这种信号比没有信号更有害。
- 产物构建就是最强的镜像完整性测试。用真实工具链合并镜像、校验分区与产物清单,能在不需要运行时的情况下完成大部分"镜像能不能用"的验证——build_firmware_manifest.py 的
--require-complete-matrix就是这一思路的落地。 - 否决"留着当参考"的中间态。不阻塞的信息性作业仍会持续消耗资源并诱发迁就性代码漂移;要么它值得投入,要么就彻底移除。
相关文档与文件索引
- 决策原文:docs/adr/2026-07-18-remove-qemu-smoke-tests-from-firmware-ci.md
- ADR 目录与索引规范:docs/adr/README.md
- CI 工作流(含宿主机测试与构建矩阵):.github/workflows/ci.yml
- 快照发布工作流:.github/workflows/snapshot.yml
- 正式发布工作流:.github/workflows/release.yml
- Native 固件构建脚本:.github/scripts/build_native_firmware.sh
- 固件清单与矩阵完整性校验:.github/scripts/build_firmware_manifest.py
- 宿主机测试入口:test/cpp/run_coverage.sh、test/python/run_coverage.sh
- 变更历史:docs/CHANGELOG.md
【免费下载链接】espectreWi-Fi CSI motion sensing for ESP32. C++ SDK, ESPHome, Native, and Matter frontends, browser tools, and a CLI for the full device lifecycle. GPLv3 and commercial licensing.项目地址: https://gitcode.com/GitHub_Trending/es/espectre
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考