| name | ddev-pc-test |
| description | ddev 工作流步骤:当 spec 或 detail 文档确认后,判断修改是否可在 PC 上写测试 demo 直接验证,并生成测试用例文档与 demo 代码。触发词:"写测试""PC 测试""测试 demo""模拟协议测试""测试用例""写个测试脚本验证"。在 ddev-spec 之后或 ddev-detail 之后使用;不可在无 spec 文档的情况下直接使用。 |
PC 可测试性分析与测试用例编写
作用
在 spec 或 detail 文档确认后、编码实现前,判断本次修改是否可以在 PC 上通过测试 demo 直接验证。如果可以,产出测试设计文档和可执行的测试 demo 代码。
本 skill 不负责"编码实现",也不负责"上板实测"。它只负责回答两个问题:
- 能测吗?(可测试性判断)
- 怎么测?(测试用例 + demo 代码)
触发位置
本 skill 可在两个位置触发:
- spec 文档确认后:若改动简单、从 spec 图就能判断接口和协议,可跳过 detail 直接出测试
- detail 文档确认后:若需要完整结构体、数据流、状态机信息才能设计测试,则在 detail 之后进入
不可在无 spec 文档的情况下直接使用本 skill。必须先有明确的改动边界和接口定义。
基本流程
- 读取已确认的 spec 文档(如果有 detail 文档也一并读取)。
- API 签名发现(强制):从固件源码中搜索本次改动涉及的所有真实接口签名。在完成此步骤之前,不得开始判断可测试性或生成任何代码。 详见下方"API 签名发现"章节。
- 判断本次修改是否满足 PC 可测试条件(见下方"可测试性判断标准")。
- 如果不可测试,直接输出结论和原因,不生成任何产物。
- 如果可以测试,生成两类产物:
- 测试设计文档 → 落到
docs/plans/YY-MM-DD_<topic>/detail/tests/
- 测试 demo 代码 → 落到仓库根目录
test/YY-MM-DD-<feature>/
API 签名发现(强制)
在判断可测试性之前,必须先从固件源码中提取本次改动涉及的所有真实接口签名。禁止凭空发明函数名、参数类型、结构体字段或枚举值。
搜索范围
从 spec/detail 文档中提取所有涉及的模块、接口、数据结构名称,然后搜索:
- 函数声明(含参数类型、返回值)
- 结构体定义(
typedef struct / struct)
- 枚举定义(
enum / typedef enum)
- 类型别名(
typedef)
- 错误码 / 状态码宏定义
- 协议帧结构体 / 缓冲区格式
搜索方法(优先级递减)
优先使用 CodeGraph 结构化查询:
codegraph_search 按函数名/结构体名搜索 → codegraph_node 获取精确定义、完整签名和所在文件行号
codegraph_context 按模块名获取该模块所有相关符号和上下文
codegraph_explore 批量查看多个相关符号的完整源码
CodeGraph 未初始化时回退方案:
grep 按函数名/结构体名模式搜索头文件(*.h)中的声明
Read 打开搜索到的头文件,确认精确的函数签名和结构体定义
grep 搜索 .c 文件中的函数实现确认签名一致性
产出:API 签名清单
在测试总览文档(<name>-overview.md)中必须包含 API 签名清单,格式如下:
## 被测 API 签名清单
| 接口 | 签名 | 所在文件 |
|------|------|----------|
| sn_read | `int sn_read(char *buf, size_t len)` | `src/module/sn.c:42` |
| ota_frame_parse | `ota_ret_t ota_frame_parse(const uint8_t *data, ota_frame_t *out)` | `include/ota/ota_proto.h:88` |
| ota_frame_t | `typedef struct { uint8_t cmd; uint16_t len; uint8_t payload[256]; } ota_frame_t` | `include/ota/ota_types.h:15` |
每个条目必须包含:精确的 C 函数签名(含参数名和类型)、结构体/枚举的完整定义、所在文件和行号。
强制约束
- 后续所有测试 demo 代码中的函数调用、结构体引用、typedef、枚举值必须与签名清单完全一致
- 如果固件源码中不存在某个 spec 提及的接口,必须先确认 spec 是否需要修正,不得自行编造
- 测试脚本中对端模拟器的发包/收包格式必须匹配真实协议帧结构体定义
- 未在签名清单中列出的符号不得出现在测试代码中
可测试性判断标准
以下条件必须全部满足,才算"可以在 PC 上测试":
- 存在明确的输入/输出接口,可以脱离硬件直接在 PC 上构造和验证
- 被测代码不依赖板级外设寄存器、中断、DMA、GPIO、定时器等必须硬件的抽象
- 被测逻辑可以通过模拟(mock/stub)或协议脚本足以覆盖关键路径
- 测试场景可以用脚本(Python / shell / Lua 等)构造完整的输入序列和预期输出
- 被测代码可以在 PC 上编译并链接(或被测逻辑可以用等价脚本在 PC 上模拟)
如果有一项不满足,就判定为"不可 PC 测试",不生成产物。
常见不可测试场景
- 依赖特定 MCU 外设(RTC 闹钟、ADC 采样、PWM 输出、SPI/I2C 驱动)
- 中断服务例程与硬件时序紧耦合
- 需要真实的射频收发、BLE 扫描、WiFi 连接
- 依赖硬件唯一 ID、efuse、安全元件
常见可测试场景
- 协议解析、帧封装/解封装、校验算法
- 状态机、业务逻辑、条件分发
- OTA 协议、分包/组包、版本协商(Python 模拟上位机 + 真实固件 + demo 接收端)
- 编解码、压缩/解压、格式转换
- 配置解析、参数校验、错误码映射
- 日志解析、命令行协议
测试设计文档
测试设计文档落到 docs/plans/YY-MM-DD_<topic>/detail/tests/,按需生成以下文件:
detail/tests/
├── <name>-overview.md # 测试总览:可测试性结论、覆盖范围、阅读顺序
├── <name>-cases.md # 测试用例:每个用例含输入、预期输出、覆盖点
├── <name>-env.md # 测试环境:需要什么工具、脚本、固件产物、通信方式
总览文档最低要求
<name>-overview.md 至少包含:
- 可测试性判断结论及依据
- 本次测试覆盖哪些接口/状态/路径
- 本次测试不覆盖什么(硬件依赖部分明确排除)
- 测试架构图(ASCII 图:谁模拟什么、谁被测、数据怎么流转)
- 子文档索引和阅读顺序
⚠️ 硬门禁:画图前必须先加载 ddev-diagram
在任何 ASCII 图动笔之前,必须执行 Skill("ddev-diagram") 加载绘制规范。加载完成前禁止写任何 ASCII 图。
测试用例文档最低要求
<name>-cases.md 至少包含:
- 每个测试用例的唯一编号(如 TC-01)
- 测试目标(测什么接口/路径/状态)
- 被测 API:引用 API 签名清单中的具体函数名/结构体名(如
sn_read、ota_frame_parse),必须与签名清单一一对应
- 前置条件(需要什么环境、什么固件产物)
- 输入数据(具体值,不含糊)
- 预期输出(具体值,可自动比对)
- 覆盖的 spec/detail 文档引用(能追溯到哪张图、哪个结构体)
测试环境文档最低要求
<name>-env.md 至少包含:
- 需要安装的工具(Python 版本、pip 包、串口工具等)
- 需要编译的固件产物和产物路径
- 通信方式(串口、socket、pipe)及端口/地址
- 启动顺序和清理步骤
- 超时时间、重试策略
测试 demo 代码
测试 demo 代码落到仓库根目录 test/YY-MM-DD-<feature>/,目录结构建议:
test/25-06-18-ota-protocol/
├── README.md # 本目录说明:测什么、怎么跑、依赖什么
├── host_sim/
│ └── ota_host.py # Python 模拟上位机:发包、收响应、校验
├── device_demo/
│ └── ota_device_demo.c # 设备端 demo:接收、解析、回复(可 PC 编译)
├── fixtures/
│ └── test_fw.bin # 测试用固件产物(小尺寸占位)
└── run_test.ps1 # Windows 一键运行脚本
demo 代码原则
- 必须使用真实固件 API 签名:demo 代码中的所有函数调用、结构体引用、typedef、枚举值必须与 API 签名发现步骤产出的签名清单完全一致,不得自造函数名、参数类型或返回值
- 协议帧必须匹配真实格式:如果测试涉及协议帧构造/解析,必须与固件源码中的帧结构体定义逐字节对齐,不得自行设计帧格式
- 尽最大努力模拟真实场景:用真实编译产出的固件推理,脚本模拟对端行为
- 输入输出必须可自动比对,禁止只打印到终端让人眼看
- demo 代码本身不应引入被测模块的外部依赖(头文件除外)
- demo 应包含明确的通过/失败输出和退出码
- 如果被测目标是嵌入式固件模块,优先用 Python 脚本作为对端模拟器
- README 必须写清楚:依赖、编译方法、运行命令、预期输出
测试自动比对
每个测试用例的预期输出必须可编程比对。推荐做法:
- Python 脚本中直接
assert 关键字段
- demo 输出结构化的 JSON,由脚本逐字段比对
- 协议帧逐字节比对,不接受"大致一样"
自检
测试产物生成完毕后,必须逐项核对以下清单:
- API 签名覆盖:spec/detail 文档中涉及的所有接口是否都已在 API 签名清单中列出?
- 签名一致性:生成的 demo 代码中的每个函数调用、结构体引用、枚举值是否都能在签名清单中找到对应项?是否与固件源码一致?
- 无自造签名:demo 代码中是否存在签名清单中没有的、自行发明的函数名或类型名?
- 协议帧对齐:如果涉及协议帧,测试脚本中的帧构造/解析是否与固件源码中的帧结构体定义逐字段匹配?
- 占位符扫描:测试文档和代码中是否出现 "TBD"、"TODO"、"自行实现" 等占位符?
- 用例覆盖:每个测试用例是否都引用了具体的被测 API(函数名/结构体名)?
如有任一项不通过,必须在修复后再进入下一步。
完成后
测试设计文档和 demo 代码完成后:
- 如果下一步是编码实现,进入
ddev-plan 拆解执行计划
- 测试 demo 必须作为验收证据之一:在
ddev-gate 阶段必须跑通,不通过不算验收完成