# Ddev Pc Test

> ddev 工作流步骤：当 spec 或 detail 文档确认后，判断修改是否可在 PC 上写测试 demo 直接验证，并生成测试用例文档与 demo 代码。触发词："写测试""PC 测试""测试 demo""模拟协议测试""测试用例""写个测试脚本验证"。在 ddev-spec 之后或 ddev-detail 之后使用；不可在无 spec 文档的情况下直接使用。

- Skill: `docevilock/ddev-pc-test` (Agent Skill)
- Install (CLI): `npx skillmds@latest add docevilock/ddev-pc-test`
- Raw SKILL.md: https://api.skillmd.com/api/skills/docevilock/ddev-pc-test/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: docevilock (https://skillmd.com/u/docevilock)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/docevilock/ddev-pc-test

---


# PC 可测试性分析与测试用例编写

## 作用

在 spec 或 detail 文档确认后、编码实现前，判断本次修改是否可以在 PC 上通过测试 demo 直接验证。如果可以，产出测试设计文档和可执行的测试 demo 代码。

本 skill 不负责"编码实现"，也不负责"上板实测"。它只负责回答两个问题：
- 能测吗？（可测试性判断）
- 怎么测？（测试用例 + demo 代码）

## 触发位置

本 skill 可在两个位置触发：
- spec 文档确认后：若改动简单、从 spec 图就能判断接口和协议，可跳过 detail 直接出测试
- detail 文档确认后：若需要完整结构体、数据流、状态机信息才能设计测试，则在 detail 之后进入

不可在无 spec 文档的情况下直接使用本 skill。必须先有明确的改动边界和接口定义。

## 基本流程

1. 读取已确认的 spec 文档（如果有 detail 文档也一并读取）。
2. **API 签名发现（强制）**：从固件源码中搜索本次改动涉及的所有真实接口签名。**在完成此步骤之前，不得开始判断可测试性或生成任何代码。** 详见下方"API 签名发现"章节。
3. 判断本次修改是否满足 PC 可测试条件（见下方"可测试性判断标准"）。
4. 如果不可测试，直接输出结论和原因，不生成任何产物。
5. 如果可以测试，生成两类产物：
   - 测试设计文档 → 落到 `docs/plans/YY-MM-DD_<topic>/detail/tests/`
   - 测试 demo 代码 → 落到仓库根目录 `test/YY-MM-DD-<feature>/`

## API 签名发现（强制）

在判断可测试性之前，必须先从固件源码中提取本次改动涉及的所有真实接口签名。**禁止凭空发明函数名、参数类型、结构体字段或枚举值。**

### 搜索范围

从 spec/detail 文档中提取所有涉及的模块、接口、数据结构名称，然后搜索：

- 函数声明（含参数类型、返回值）
- 结构体定义（`typedef struct` / `struct`）
- 枚举定义（`enum` / `typedef enum`）
- 类型别名（`typedef`）
- 错误码 / 状态码宏定义
- 协议帧结构体 / 缓冲区格式

### 搜索方法（优先级递减）

1. `grep` 按函数名/结构体名模式搜索头文件（`*.h`）中的声明，`Read` 获取精确定义、完整签名和所在文件行号
2. `grep` 按模块名搜索该模块所有相关符号和上下文
3. `grep` 搜索 `.c` 文件中的函数实现，`Read` 批量查看多个相关符号的完整源码

### 产出：API 签名清单

在测试总览文档（`<name>-overview.md`）中必须包含 API 签名清单，格式如下：

```markdown
## 被测 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，由脚本逐字段比对
- 协议帧逐字节比对，不接受"大致一样"

## 自检

测试产物生成完毕后，必须逐项核对以下清单：

1. **API 签名覆盖**：spec/detail 文档中涉及的所有接口是否都已在 API 签名清单中列出？
2. **签名一致性**：生成的 demo 代码中的每个函数调用、结构体引用、枚举值是否都能在签名清单中找到对应项？是否与固件源码一致？
3. **无自造签名**：demo 代码中是否存在签名清单中没有的、自行发明的函数名或类型名？
4. **协议帧对齐**：如果涉及协议帧，测试脚本中的帧构造/解析是否与固件源码中的帧结构体定义逐字段匹配？
5. **占位符扫描**：测试文档和代码中是否出现 "TBD"、"TODO"、"自行实现" 等占位符？
6. **用例覆盖**：每个测试用例是否都引用了具体的被测 API（函数名/结构体名）？

如有任一项不通过，必须在修复后再进入下一步。

## 完成后

测试设计文档和 demo 代码完成后：

- 如果下一步是编码实现，进入 `ddev-plan` 拆解执行计划
- 测试 demo 必须作为验收证据之一：在 `ddev-gate` 阶段必须跑通，不通过不算验收完成

