# 嵌入式测试开发标准流程（基于 STM32F4 + CH395F 经验）

> 适用范围：STM32 裸机/FreeRTOS 项目，Keil MDK-ARM v5 (ARMCC)，串口日志输出测试结果。

---

## 1. 测试规划（文档先行）

在 `docs/` 下创建测试规范文档，定义以下结构：

### 1.1 阶段概览表

| 阶段 ID | 类型 | 耗时 | 入口 | 出口 |
|---------|------|------|------|------|
| TM-PHY-01 | 独立运行 | ~1s | 宏启用 | 串口汇总 |

### 1.2 每个测试用例

| 字段 | 说明 |
|------|------|
| **ID** | `TC-NET-NNN`，唯一编号 |
| **优先级** | P0（核心）/ P1（重要） |
| **类型** | 功能测试 / 边界测试 / 负向测试 / 压力测试 / 恢复测试 |
| **标题** | 一句话描述 |
| **前置条件** | 硬件状态、PC 端命令、依赖的其他用例 |
| **测试步骤** | 编号操作序列 |
| **成功标准** | 精确到"返回值 == 0x00"、"延迟 >= 60000ms" |
| **失败标准** | 每种失败对应的现象 |
| **覆盖原则** | 对应哪个设计原则 |

关键原则：**先写判定标准，再写测试代码**，避免"测了但不知道算不算过"。

---

## 2. 测试框架搭建

### 2.1 目录结构

```
test/
  ch395f_test.h           # 宏定义 + TEST_CHECK/TEST_REPORT + test_stats_t
  ch395f_test_task.h      # 仅导出 StartCh395fTestTask
  ch395f_test_task.c      # 全 Phase 实现
```

### 2.2 模板文件

#### `ch395f_test.h` — 宏+类型

```c
// 阶段使能开关（取消注释即启用）
#define ENABLE_PHASE1_TESTS

// 测试统计
typedef struct {
    uint16_t total;
    uint16_t passed;
    uint16_t failed;
} test_stats_t;

extern test_stats_t g_test_stats;

#define TEST_CHECK(cond, fmt, ...) do { \
    g_test_stats.total++; \
    if (cond) { \
        g_test_stats.passed++; \
        DBG_INFO("[PASS] " fmt, ##__VA_ARGS__); \
    } else { \
        g_test_stats.failed++; \
        DBG_ERROR("[FAIL] " fmt, ##__VA_ARGS__); \
    } \
} while (0)

#define TEST_REPORT(name) do { \
    DBG_INFO("=== %s: %d/%d PASSED (failed=%d) ===", \
             name, g_test_stats.passed, g_test_stats.total, g_test_stats.failed); \
} while (0)
```

#### `ch395f_test_task.c` — 每个 Phase 的模板

```c
#ifdef ENABLE_PHASEX_TESTS
/*
 * Phase X — 功能说明
 *
 * 测试目的：（概括）
 * 前置条件：（硬件/软件/PC 端）
 * 通过：（整体判定条件）
 * 失败：（整体判定条件）
 */
static void phaseX_run(void) {
    DBG_INFO("=== CH395F Phase X Tests ===");

    /* ---- TC-NET-NNN: 用例标题 ---- */
    {
        // 原理说明
        // 通过：精确条件
        // 失败：精确条件
        ret = some_api();
        DBG_INFO("          expect: ...");
        DBG_INFO("          actual: ...");
        TEST_CHECK(ret == EXPECTED, "description");
    }

    TEST_REPORT("Phase X");
}
#endif
```

---

## 3. 编码规范

### 3.1 DBG_INFO 三行输出

每个判定点输出三行，一眼看出"期望什么、拿了什么、过没过"：

```
  [P1-01] ch395f_check_exist()
          expect: 0x00 (~0x57 = 0xA8)
          actual: 0x00
  [PASS] P1-01 check_exist = 0x00 (expect 0x00)
```

### 3.2 硬编码值加注释

```c
// 好：
ch395f_status_t ret = ch395f_check_exist();
// SPI 写入 0x06 (CMD_CHECK_EXIST) + 0x57 (测试字节)
// 成功：回复 == ~0x57 == 0xA8（CH395F_ERR_SUCCESS = 0x00）
// 失败：回复 != 0xA8（CH395F_STATUS_NOT_DETECTED = 0xFF）

// 不好：
ch395f_status_t ret = ch395f_check_exist();
TEST_CHECK(ret == 0, "check_exist OK");
```

### 3.3 无中文字符串

ARMCC v5 不识别 UTF-8 多字节字符。`DBG_INFO`/`DBG_ERROR` 中只写 ASCII。注释可以写中文。

### 3.4 栈安全

大缓冲区（如 64KB）用 `static` 全局，不放任务栈：

```c
#define TEST_BUF_SIZE       65536
static uint8_t s_rx_buf[TEST_BUF_SIZE];
```

### 3.5 条件编译消除未使用变量警告

```c
static uint8_t s_rx_buf[TEST_BUF_SIZE];
#if defined(ENABLE_PHASE5_TESTS) || defined(ENABLE_PHASE8_TESTS)
static uint8_t s_tx_buf[TEST_BUF_SIZE];
#endif
```

---

## 4. 统一入口

### 4.1 所有测试在 FreeRTOS 任务中串行执行

```
ch395fTestTask:
  1. 等待 g_net_ready（netTask 初始化完成）
  2. 阻塞执行 Phase 1/2/3/6/9（内部循环，无需外部连接）
  3. 创建 TCP 监听 Socket（端口 8080，单连接模式）
  4. 事件驱动循环 Phase 4/5/7/8/10（等待 PC 连接）
```

### 4.2 不要在 main.c 中直接调用测试

main.c 只做硬件初始化和启动 OS，不包含任何测试逻辑。

### 4.3 测试文件的手动注册

新测试文件需：
1. 添加到 `test/` 目录
2. 修改 `MDK-ARM/STM32F407-Demo.uvprojx` 添加文件引用和 IncludePath

---

## 5. 文档与代码同步

- 文档中的入口函数名、宏名、流程描述必须与代码一致
- 代码变更后立即更新文档，否则文档两天内就会失效
- 测试用例的通过/失败标准在**文档和代码注释中都写清楚**
- 文档使用标准 Markdown 表格，避免复杂嵌套导致渲染异常

---

## 6. 编译验证

### 6.1 底线

**`0 Error(s), 0 Warning(s)`**，不可妥协。

### 6.2 常见警告处理

| 警告 | 原因 | 解决 |
|------|------|------|
| `#177-D: variable was declared but never referenced` | 条件编译导致 | 加 `#ifdef` 包裹变量声明 |
| `#870-D: invalid multibyte character sequence` | DBG_INFO 中有中文 | 改为纯 ASCII |
| 隐式类型转换 | 参数类型不匹配 | 加显式 `(uint8_t)` 等 cast |

### 6.3 构建命令

```bat
MDK-ARM\build.bat
:: 或项目根目录执行 @build
```

退出码：0 = 成功 | 1 = 有警告（不通过）| 2+ = 错误

---

## 7. 迭代节奏

```
文档（规划 + 判定标准）
  → 代码（添加 Phase 实现）
  → @build（0 Error(s), 0 Warning(s)）
  → 烧录验证（串口观察 PASS/FAIL）
  → 根据实际结果修正判定标准
  → 更新文档（保持同步）
  → 下一阶段
```

---

## 附录：本项目的测试文件清单

| 文件 | 作用 |
|------|------|
| `test/ch395f_test.h` | 测试宏、类型定义、Phase 使能开关 |
| `test/ch395f_test_task.h` | 导出 `StartCh395fTestTask` |
| `test/ch395f_test_task.c` | 全 10 个 Phase 实现 |
| `docs/CH395F_Test_Guide.md` | 测试规范、用例表格、通过/失败标准 |
| `docs/CH395F_Trap_Records.md` | 已知硬件/软件陷阱及修复 |
