Files
STM32F4-Base/test/host/README.md
T
wandering1 dafcf8098b test(b6): 台架 @test 闭环复测收口 + 启动行门控修复 + PC 104 主站
B6 台架 @test 闭环复测(B4/B5 验收窗口)完成,DoD 全判据过。

门控修复(首跑 FAIL 实锤的 B5 遗留竞态):
- 现象:protect task start 锚点行字节级交错损坏
  (st[art: cycle=220ms elm=70,两次开机同形复现)
- 根因:启动行在控制台写者注册前打印走无锁裸发,与
  defaultTask 启动日志交错——违反 2026-10-10 门控纪律
  (控制台单写者日志设计说明.md §3.4,adcTask 同款先例)
- 修复:启动锚点行改 dbg_console_writer_ready() 首拍补打

台架与工具:
- .serialtest/config.json:Required 增 protect task start /
  i104 listening on 2404 / [STACK] protect|iec104|heap
- test/host/iec104_master.py:纯标准库 104 主站客户端
  (STARTDT→总召四组+actterm→C_SE 两段写触发 YX15/16 变位
  COT=3→恢复复归→YK11 两段正答→C_RD 读回),往返 18/18
  PASS(d3107f3 与 B7+fix 两版固件)
- 复测 @test PASS:锚点完好 +577ms、9/9 Required、零 [ERR]、
  回读一致;protect free_min 1220(59%)、iec104 2136(83%)、
  新 56KB 堆 min_ever=10000(N-33 抬栈背书就绪)

证据与文档回填:
- boot_log_data 四份快照(含缺陷复现 MISS 件)+ README 登记
- 保护规划 §7 B6 行(B5 台架遗留闭环);控制台单写者 §3.4
  纪律示例 + §11 变更记录;IEC104 §7/§8 主站工具就绪;
  W1 卡 B6 ✅ + N-33 背书 + B7 台架腿注;登记簿 N-33
- 内存台账:+16 B 构建后 ram_baseline/check_mem_numbers 回填

验证:@build 0 错 0 警(Code=205516);ci_local 全绿
(L0 30 钩子 + L0.5 13 套件 826 断言)。
2026-10-12 16:31:33 +08:00

126 lines
9.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# test/host/ — 主机端测试脚本
> PC 侧的测试驱动与判读脚本(Python,跑在开发机上,经串口/网络与目标板交互)。
## 文件一览
### 流程与判读
| 脚本 | 作用 |
|---|---|
| `run_boot_test.py` | **`@test` 一键闭环**:开串口+清日志 → 烧录 → wait-idle → `verify_log` 校验(退出码 0=PASS) |
| `verify_log.py` | **日志标记校验**:Required / Forbidden 标记 → PASS / FAIL;可选 `--require-suite-summary` 校验套件汇总行(`=== <名称>: N/M PASSED (failed=0) ===` 且 `N>0`),抓"没跑到汇总行/断言数为 0"的盲区(T-2) |
| `e02_probe.py` | E-02 专项探针(错误码传播审计) |
### 网络 / FTP
| 脚本 | 作用 |
|---|---|
| `ch395f_socket_test.py` | Socket 通信测试(回显服务器 / 文件收发) |
| `ftp_test.py` | FTP 基本功能测试 |
| `ftp_up_dl_test.py` | FTP 上传下载测试 |
| `ftp_speed_test.py` | FTP 传输速度测试 |
| `ftp_capture.py` | FTP 流量抓取 |
| `ftp_regression.py` | FTP 回归套件 |
| `iec104_master.py` | **IEC 60870-5-104 主站客户端(B6)**:连设备 :2404 跑 STARTDT → 总召(actcon/四组背景扫描/actterm)→ 经 C_SE 两段写临时压低 OC1 定值确定性触发 YX16 变位(COT=3)→ 恢复 → YK11 两段式 → C_RD 读回;纯标准库,输出纯 ASCII 可直接重定向归档为台架证据(判据 exit 0/1/2) |
### 时间 / 时延
| 脚本 | 作用 |
|---|---|
| `latency_peer.py` | 链路时延测试的 PC 对端(peer) |
| `time_sync_server.py` | 时间同步推送服务(对时源) |
### 主机侧单元测试(纯逻辑,无硬件)
| 文件 | 作用 |
|---|---|
| `common/unit_test_harness.py` | **通用单测 harness**(L0.5):探测 MSVC → 拼 `INCLUDE`/`LIB` → 编译 → 运行 → 判定退出码。各模块运行器只做**声明式**配置(`TestBuild`),不含工具链细节 |
| `watchdog/run_unit_test.py` | **看门狗判定状态机单测**:声明 `TestBuild` 后调 harness,用 MSVC 离线编译真实 `App/util/task_watchdog.c`(可观测桩驱动),39 用例穷举边界。零硬件、秒级(退出码 0=PASS/1=FAIL/2=环境错误) |
| `watchdog/watchdog_test.c` | 测试单元本体(`#include` 生产源码 + 平台钩子桩) |
| `watchdog/stub/` | 桩头文件(`main.h`/`iwdg.h`/`dbg_log.h`),覆盖 MCU 版以在 PC 上编译 |
| `sys_clock/run_unit_test.py` | **系统时钟换算单测**:离线编译真实 `App/util/sys_clock.c`,以可控时钟 + 阴影 RTC 桩驱动,100 断言点覆盖闰年/月末/世纪/2038 上限/星期/30 天惰性再基准化/RTC 故障降级 |
| `sys_clock/sys_clock_test.c` | 测试单元本体(`#include` 生产源码 + 强符号桩;**不改生产代码**) |
| `sys_clock/stub/` | 桩头文件(`main.h`/`FreeRTOS.h`/`task.h`/`dbg_log.h`),覆盖 MCU 版以在 PC 上编译 |
| `cfg_file/run_unit_test.py` | **配置引擎单测**:离线编译真实 `App/cfg/cfg_file.c`,以**内存虚拟文件系统**桩驱动,149 断言点覆盖 INI 解析/四种值类型编解码/段注册校验/load-save-poll 契约/超长守卫/故障注入 |
| `cfg_file/cfg_file_test.c` | 测试单元本体(`#include` 生产源码 + `cfg_store_*` 内存 FS 后端桩;**不改生产代码**) |
| `cfg_file/stub/` | 桩头文件(`dbg_log.h`),覆盖 MCU 版以在 PC 上编译 |
| `phasor/run_unit_test.py` | **相量层(L2)单测**:离线编译真实 `App/algo/phasor.c` + `sample_ring.c`(**无需任何桩**,纯算法),57 用例覆盖傅氏系数表 / 幅值·相位 / 谐波抑制与提取 / 频偏 / 采样环 / 双区原子交换 |
| `phasor/phasor_test.c` | 测试单元本体(`#include` 两个生产源码 + 合成任意波形) |
| `adc_input/run_unit_test.py` | **ADC 输入层(L1)单测**:离线编译真实 `App/algo/adc_input.c`,**101** 用例给每个 Rank 注入互异码值**逐通道钉死映射**,并覆盖 DMA 挂载失败 / **乒乓半区选择(撕裂帧防线)** / 超拍丢帧 / 停摆 / HAL 回调句柄守卫 / **停摆拍陈旧帧** / 参数错等真机难复现分支 |
| `adc_input/adc_input_test.c` | 测试单元本体(`#include` 生产源码 + HAL / TPAFE 可注入桩) |
| `adc_input/stub/` | 桩头文件(`adc.h` / `tpafe5160.h`) |
| `vol_lock/run_unit_test.py` | **卷锁单测**:离线编译真实 `App/fs/vol_lock.c`,33 断言点覆盖创建 / 获取 / 释放契约(**fail-open 不变量**:创建失败仍返回 1 且零阻塞)/ 幂等 / 超时透传 |
| `vol_lock/vol_lock_test.c` | 测试单元本体(`#include` 生产源码 + CMSIS 互斥桩) |
| `vol_lock/stub/` | 桩头文件(`cmsis_os.h`) |
| `time_sync/run_unit_test.py` | **时间同步单测**:离线编译真实 `App/sync/time_sync.c`,80 断言点覆盖分片喂狗延时 / 监听退避 / 时间包重组(半包·粘连·EOF·真错·预算耗尽·tick 回绕)/ 任务主循环(**N-17 的全部逻辑贡献者**) |
| `time_sync/time_sync_test.c` | 测试单元本体(`#include` 生产源码 + 网络 / 事件 / RTOS 可脚本化桩 + `longjmp` 跳出 `for(;;)` 任务循环) |
| `time_sync/stub/` | 桩头文件(`FreeRTOS.h` / `task.h` / `cmsis_os2.h` / `net_socket.h` / `sys_clock.h` / `sys_events.h` / `dbg_log.h`) |
| `iec104/run_unit_test.py` | **IEC 60870-5-104 协议栈单测(B4)**:离线编译真实 `App/protocol/iec104/` 三模块 + 数据源(prot_set/prot_elm/phasor/sample_ring),**82** 用例覆盖 CP56 往返 / APCI 半帧·粘帧·坏帧 / STARTDT 握手 / 总召分片+RBT / COV 死区 / k=12 窗满 / w=8 触发 S / t1·t2·t3 / N(R) 倒退判故障 / 遥控·定值两段式与 PN / C_CS / 未知类型·地址错误应答 |
> 详见 `docs/测试说明/看门狗单元测试说明.md`、`docs/测试说明/系统时钟单元测试说明.md`、
> `docs/测试说明/配置文件单元测试说明.md`、`docs/测试说明/ADC输入层单元测试说明.md`、
> `docs/测试说明/相量层单元测试说明.md`、`docs/测试说明/卷锁单元测试说明.md`、
> `docs/测试说明/时间同步单元测试说明.md`;
> harness 用法见 [`common/README.md`](common/README.md)。
> 已挂 pre-commit 钩子 `watchdog-unit-test`、`sys-clock-unit-test`、`cfg-file-unit-test`、
> `phasor-unit-test`、`adc-input-unit-test`、`vol-lock-unit-test`、`time-sync-unit-test`。
## 运行方式
```bash
# @test 一键闭环(推荐,需真机)
& '.venv\Scripts\python.exe' .dsh/skills/serial-boot-test/scripts/run_boot_test.py
# 测试套件构建的回归:要求套件跑到汇总行且断言数 > 0(T-2)
& '.venv\Scripts\python.exe' .dsh/skills/serial-boot-test/scripts/run_boot_test.py `
--required "=== Storage Tests:" --forbidden "ERROR;HardFault;[ERR]" `
--require-suite-summary "Storage Tests"
# 看门狗判定状态机单测(纯逻辑,无需真机)
& '.venv\Scripts\python.exe' test/host/watchdog/run_unit_test.py
# 系统时钟换算单测(纯逻辑,无需真机)
& '.venv\Scripts\python.exe' test/host/sys_clock/run_unit_test.py
# 配置引擎单测(纯逻辑,无需真机)
& '.venv\Scripts\python.exe' test/host/cfg_file/run_unit_test.py
# 相量层(L2)单测(纯逻辑,无需真机)
& '.venv\Scripts\python.exe' test/host/phasor/run_unit_test.py
# ADC 输入层(L1)单测(纯逻辑,无需真机)
& '.venv\Scripts\python.exe' test/host/adc_input/run_unit_test.py
# 卷锁单测(纯逻辑,无需真机)
& '.venv\Scripts\python.exe' test/host/vol_lock/run_unit_test.py
# 时间同步单测(纯逻辑,无需真机)
& '.venv\Scripts\python.exe' test/host/time_sync/run_unit_test.py
```
> 串口端口配置在 `.serialtest/config.json`(单一配置源,当前 COM6/115200)。
## ⚠️ 注意
- **PYTHONPATH/路径敏感**:脚本按仓库根相对路径读写,**从仓库根执行**
- 本目录**被 pre-commit 门禁的 cppcheck / 代码规范排除**(`test/` 豁免);
但各模块的 `run_unit_test.py` 自身**作为独立钩子**参与门禁(见上,共 7 个)
- 台架探针族(TCP 复连 / FTP 中止等)在 `MDK-ARM/*.py`,与本目录分工不同
- 全部主机侧单测(`watchdog/`、`sys_clock/`、`cfg_file/`、`phasor/`、`adc_input/`、
`vol_lock/`、`time_sync/`)都需要本机装有 MSVC(VS2019/2022/2026 的 C++ 工具链);
无则脚本报环境错误(退出码 2)
## 相关文档
- `docs/测试说明/自动化测试流程.md` — `@test` 细节与失败模式
- `docs/测试说明/测试开发标准流程.md` — 测试分层与门禁
- `docs/测试说明/看门狗单元测试说明.md` — 主机侧单测(用例 / 平台层 / 扩展方法)
- `docs/测试说明/系统时钟单元测试说明.md` — 系统时钟换算单测(用例 / 桩 / Zeller 缺陷记录)
- `docs/测试说明/配置文件单元测试说明.md` — 配置引擎单测(内存 FS 桩 / 抓出的 P0)
- `docs/测试说明/ADC输入层单元测试说明.md` — ADC 输入层单测(逐 Rank 钉死映射)
- `docs/测试说明/相量层单元测试说明.md` — 相量层单测(傅氏系数 / 双区交换)
- `docs/测试说明/卷锁单元测试说明.md` — 卷锁单测(fail-open 不变量)
- `docs/测试说明/时间同步单元测试说明.md` — 时间同步单测(N-17 逻辑贡献者)
- `docs/测试说明/CH395F_Test_Guide.md`、`FTP_Test_Guide.md`