diff --git a/docs/README.md b/docs/README.md index 9e820128..26bcaea2 100644 --- a/docs/README.md +++ b/docs/README.md @@ -187,7 +187,7 @@ docs/ | [`项目审查与优化建议报告.md`](审查记录/项目审查与优化建议报告.md) | 项目全景审查:文档/代码/架构/可靠性问题清单(D-1~D-7、N-xx、Px-xx) | | [`项目审计改进清单.md`](审查记录/项目审计改进清单.md) | 审计待办清单(P0~P3,带状态与日期) | | [`下一步任务规划.md`](审查记录/下一步任务规划.md) | **下一步任务排序(阶段 A/B/C/D)+ 诊断基线**——只保留待办,完成即删;每轮迭代后更新 | -| [`测试与架构改进建议.md`](审查记录/测试与架构改进建议.md) | **测试充分性 + 架构改进专项复审**(2026-10-02,纯建议不改码):L0.5 覆盖率缺口、孤儿源文件、错误通道不对称等 S1~S14 实施建议 | +| [`测试与架构改进建议.md`](审查记录/测试与架构改进建议.md) | **测试充分性 + 架构改进专项待办**——只保留未闭环项(S12 断言宏、S1-c 可选单测、A-4 采集链路测试约束、A-5 残余 CI 体积门禁);已落地条目证据见各模块文档 | ### 8.2 环境与部署 diff --git a/docs/审查记录/测试与架构改进建议.md b/docs/审查记录/测试与架构改进建议.md index 5fd13a5b..e9e58863 100644 --- a/docs/审查记录/测试与架构改进建议.md +++ b/docs/审查记录/测试与架构改进建议.md @@ -1,617 +1,35 @@ -# 测试体系与整体架构改进建议(2026-10-02,2026-10-04 状态更新) +# 测试体系与架构改进建议(待办) -> **状态速览(2026-10-04)**:14 条建议(S1~S14)**已落地 12 条**(S1-a / S1-b、S2~S11、S13、S14); -> **待做 2 条**:**S12**(模块级轻量断言宏,第 4 波按需试点)、**S1-c**(可选,`sys_log` / `time_sync` 单测)。 -> 落地带来的体系变化:**L0.5 由 1 个模块 → 3 个**(看门狗 / `sys_clock` / `cfg_file`)、 -> **L0 钩子 15 → 18**、**板端套件 4 → 5**(新增 SD2506 RTC)、CI 已补产物存档与汇总行。 -> 各节内已用 **✅ / ◐ / ⬜** 就地标注;本文仍是**建议性文档**,未改动任何生产代码。 - -> **用途**:对现有**测试体系**与**整体架构**做一次独立复审,回答两个问题—— -> ① 测试是否足够?② 架构还有什么可改进的地方? -> **性质**:**纯建议文档,不改任何代码**。每条给出「现状证据 → 风险 → 实施路径 → 完成判据」。 +> **用途**:记录**尚未落地**的测试充分性与架构层面改进项,与 `下一步任务规划.md` 的分阶段 +> 任务互补(后者管排序,本文件管**测试充分性 / 架构**这一专项的剩余项)。 > **上游**:`项目审查与优化建议报告.md`(P0~P3 全量池)、`项目审计改进清单.md`(待办)、 > `错误码传播审计.md`(E-xx)、`待处理内容.md`(登记簿)、`下一步任务规划.md`(排序)。 -> **本文不重复已登记条目**——只补充**测试充分性**与**架构层面**的新发现,或对既有条目 -> 给出**新的实施角度**。凡与上游重叠的,标注上游编号并注明本文增量。 -> **审计方法**:源码逐文件走读(`App/`、`test/`、`Drivers/BSP/`)+ 测试资产量化统计 + -> CI/pre-commit 门禁链路核查 + 与六份测试文档交叉比对。 - ---- - -## 0. 结论速览 - -### 0.1 测试体系评价:**分层设计优秀,但"中间层"严重偏薄** - -| 维度 | 评价 | 一句话 | -|---|---|---| -| 分层模型(L0~L3) | ⭐⭐⭐⭐⭐ | 四层划分清晰、门禁决策表明确,**优于绝大多数同规模嵌入式项目** | -| 板端套件(L1) | ⭐⭐⭐⭐⭐ | **5 套件** + 222 个断言点,覆盖驱动功能面完整(SD2506 RTC 已补套件汇总行) | -| 静态门禁(L0) | ⭐⭐⭐⭐⭐ | **18 个钩子**、含一致性/规范/乱码/元数据 + 3 个主机单测钩子,防线密不透风 | -| **主机侧纯逻辑单测(L0.5)** | ⭐⭐⭐⭐☆ | **已覆盖 3 个模块**(看门狗 / `sys_clock` / `cfg_file`);适格对象还剩 `sys_log` / `time_sync` | -| UI 回归(L3 门禁) | ⭐⭐⭐⭐☆ | 文本层 + 像素层 + 池水位三层断言,思路先进 | -| CI | ⭐⭐⭐⭐☆ | 跑 L0 + L0.5;**已补产物存档 + 汇总行**;仍**无结果趋势、无覆盖率** | -| 可观测性(运行时) | ⭐⭐⭐⭐☆ | 栈水位探针 + 链路时延探针 + syslog,台架能力强 | -| **验证债** | ⭐⭐⭐⭐☆ | 台架窗口已清掉 S7 / S9 / S11 三项;剩余验证债仅 N-3(L5 基线,待 PC peer) | - -**核心判断(2026-10-04 更新)**:项目的测试**广度**(驱动功能面)和**静态深度**(门禁)都很强, -L0.5 这一短板**已被 S1 补齐大半**——`sys_clock` / `cfg_file` 两个高价值模块落地后, -**首跑各抓出一处生产缺陷**(Zeller 星期错误、外科手术保存死码 P0),验证了"L0.5 是性价比最高 -的改进方向"这一判断。剩余适格对象(`sys_log` / `time_sync`)价值递减,可按需补(S1-c)。 - -### 0.2 架构评价:**分层与解耦做得好,但存在"三个不对称"** - -| 不对称 | 表现 | 影响 | -|---|---|---| -| **功能不对称** | 网络栈极度成熟,而 ADC/RS485 采集链路是空桩 | DTU 核心职责未实现 | -| **纵深防御不对称** | 上层(网络/日志/配置)防御完备,下层(NAND 错误通道)散乱 | 底层故障难定位 | -| **测试不对称** | 驱动面覆盖好,纯逻辑面几乎裸奔 | 回归依赖真机,反馈慢 | - -### 0.3 建议优先级(与上游阶段 A/B/C/D 对齐) - -``` -【S 级:本周可做,零风险,收益大】 ← 本文新增 【2026-10-04 状态】 - S1 扩充 L0.5 单测(sys_clock 日期算术、cfg_file 解析器) ✅ S1-a / S1-b 已落地(S1-c 可选) - S2 建立 L0.5 适格模块台账 ✅ 已落地 - S3 CI 补产物存档 + 单测结果汇总 ✅ 已落地 - S8 清理孤儿源文件(crc.c / ringbuf.c) ✅ 已落地(文件已删) - -【A 级:上游已列,本文给增量角度】 - A1 P0-1 真机验证(上游 A1) A2 P1-8 网络重建恢复(上游 A2) ◐ 正常路径已实测零回归;负向场景见 N-16 - A3 E-34 CH395F 超时(上游 A3) A4 E-04 remo_send 返回值(上游 A4) ✅ 2026-10-03 - -【B 级:核心功能补齐(上游 B,需产品定义)】 ← 当前最高优先 - B1 ADC 链路 B2 RS485 链路 B3 E-59 门禁固化 - -【C 级:结构债(上游 C)】 - C1 pending 二期 C2 GD5F 错误通道 ✅ 2026-10-03 C3 日志管线 C4 defaultTask 栈(S-1,需尽快) -``` - ---- - -## 1. 测试体系:量化现状 - -### 1.1 测试资产盘点(2026-10-02 实测) - -**板端套件(L1)——5 套件,222 个 `TEST_CHECK` 断言点**(2026-10-04 复核): - -| 套件 | 文件 | 断言点 | Test_Guide 用例数 | 覆盖对象 | -|---|---|---|---|---| -| CH395F 硬件层 | `ch395f_test_task.c` | 52 | 34(TC-NET-xxx) | CH395F 寄存器/命令/中断 | -| CH395F NET 层 | `net_test_task.c` | 38 | (同上,Phase 6~11) | TCP 监听/连接/收发 | -| GD5F | `gd5f_test_task.c` | 76 | 45(TC-GD5F-xxx) | NAND 驱动 10 阶段 | -| STORAGE | `storage_test_task.c` | 38 | (TC-STO-01~04) | FTL + FatFS 集成 | -| SD2506 RTC | `sd2506_test_task.c` | 18 | 9(TC-RTC-xxx) | RTC 读写/闹钟 | -| **合计** | | **222** | | | - -> ⚠️ 注意:断言点数(222)**大于**文档用例数(91)——因为一个 TC 常有多个 `TEST_CHECK`。 -> 这是正常的,但**反向不成立**:文档存在而代码未实现的用例无法从断言点数量看出, -> 需靠 §1.3 的"覆盖矩阵核查"专项检查(本文建议 S4,见 §3.4)。 - -**主机侧(L0.5)——已覆盖 3 个模块**(2026-10-04 复核): - -| 模块 | 位置 | 用例数 | 被测对象 | -|---|---|---|---| -| 看门狗判定状态机 | `test/host/watchdog/` | 39 | `App/util/task_watchdog.c` 位图/掩码/节流/回绕 | -| 系统时钟换算 | `test/host/sys_clock/` | 20(100 断言点) | `App/util/sys_clock.c` 闰年/月末/回绕/星期 | -| 配置引擎解析 | `test/host/cfg_file/` | 20(139 断言点) | `App/cfg/cfg_file.c` INI 解析/转义/长度校验/迁移 | -| **其余适格模块** | — | **0** | `sys_log` / `time_sync`(可选,S1-c) | - -**静态门禁(L0)——18 个钩子**(2026-10-04 实测): - -`trailing-whitespace` / `end-of-file-fixer` / `check-added-large-files` / `check-merge-conflict` / -`check-yaml` / `gitlint` / `cppcheck` / **`test-cppcheck`** / `no-mojibake` / `coding-standard` / -`doc-links` / `task-table` / `mem-numbers` / `cubemx-survival` / `watchdog-unit-test` / -**`sys-clock-unit-test`** / **`cfg-file-unit-test`** / `test-suite-switch` - -**UI 回归(L3 门禁)**:`check_menu_rows.py`(菜单 8 状态像素 + 池基线)+ -`check_content_pages.py`(11 内容页 + 文本层 50 条字形模板)+ `check_remo_sink.py`(远显推流)。 - -**CI(Gitea Actions)**:2 个 job —— `l0-gates`(pre-commit 全量)、`l05-host-unit-tests`(遍历 `run_unit_test.py`); -两 job 均已**归档日志产物 + 打印汇总行**(S3,2026-10-02)。**注意 CI 不跑 Keil、不守体积门禁**(S14)。 - -### 1.2 覆盖率缺口矩阵(按"适格性"排序) - -> 判据:**无副作用 + 可离线复现 + 边界密集** = L0.5 适格(见`测试开发标准流程.md` §4.2)。 - -| 候选模块 | 规模 | 边界密度 | 现测试手段 | L0.5 价值 | 备注 | -|---|---|---|---|---|---| -| ~~**`App/util/sys_clock.c`**~~ | 479 行 | **极高** | ✅ **L0.5 已覆盖**(`test/host/sys_clock/`,20 用例) | ⭐⭐⭐⭐⭐ | 闰年/日期换算/星期/49.7 天回绕——**S1-a 已落地,首测抓出 Zeller 星期缺陷** | -| ~~**`App/cfg/cfg_file.c`**~~ | 1250 行 | **高** | ✅ **L0.5 已覆盖**(`test/host/cfg_file/`,20 用例) | ⭐⭐⭐⭐⭐ | INI 解析/转义/长度校验/迁移——**S1-b 已落地,首测抓出外科手术保存死码 P0** | -| ~~**`App/util/crc.c`**~~ | 120 行 | 中 | ✅ **已删除**(S8,2026-10-02) | ⭐⭐⭐ | 查表正确性可用标准向量验证;但**零调用者**——已按 S8 删除 | -| `Drivers/BSP/RS485/rs485.c` | 181 行 | 高 | **无**(任务为空桩) | ⭐⭐⭐⭐ | 帧解析状态机适格;但**功能未实现**(上游 B2) | -| `App/sync/time_sync.c` | 217 行 | 中 | 仅手工 `time_sync_server.py` | ⭐⭐⭐ | 协议编解码适格,与网络耦合需判 | -| `App/log/sys_log_mcu.c` | 359 行 | 中 | 仅 `@test` 标记 | ⭐⭐⭐ | 定长 64B 记录编解码适格;落盘部分需桩 | -| `Drivers/BSP/TPAFE5160/tpafe5160.c` | 356 行 | 中 | **无**(任务为空桩) | ⭐⭐ | 寄存器序列为主,纯逻辑面小 | -| ~~`App/util/ringbuf.c`~~ | 154 行 | 中 | ✅ **已删除**(S8,2026-10-02) | ⭐⭐ | 零调用者——已按 S8 删除 | - -**结论(2026-10-04 更新)**:**原列的两个最高价值模块(`sys_clock`、`cfg_file`)均已落地 L0.5** -(S1-a / S1-b),合计约 1730 行核心逻辑已脱离"只能靠真机"的状态;且两者**首跑各抓出一处 -生产缺陷**,是本轮测试体系建设中收益最直接的两笔。剩余适格对象(`sys_log` / `time_sync`) -价值递减,列入 S1-c 可选。 - -### 1.3 测试体系的结构性问题 - -#### 问题 T-1:L0.5 覆盖率过低,回归反馈闭环过长 🔴 → ✅ **已改善(2026-10-02 落地 S1-a / S1-b)** - -> **2026-10-04 更新**:L0.5 已由 1 个模块扩至 **3 个**(看门狗 / `sys_clock` / `cfg_file`), -> 本节所述"这两类模块只能靠真机回归"的缺口**已消除**;下述代价量化保留作立项依据与决策记录。 - -**现状(2026-10-02 初判)**:全项目仅看门狗 1 个模块有主机侧单测。`sys_clock.c`(479 行日期算术)、 -`cfg_file.c`(1250 行解析器)这类**边界密集、纯逻辑、零硬件**的模块, -改动后**只能靠真机 `@test` 才能发现问题**。 - -**代价量化**(以 `sys_clock.c` 为例): - -| 手段 | 单次反馈耗时 | 需要硬件 | 能覆盖的边界 | -|---|---|---|---| -| 真机 `@test` | 烧录 ~7s + 等静默 8s + 采集 ≈ **30s~2min** | ✅ 需板+串口+ST-LINK | 只测"当前日期"这一个点 | -| L0.5 单测 | 编译+运行 ≈ **2~5s** | ❌ 不需要 | 可穷举闰年/月末/回绕/2038 边界 | - -**关键**:`sys_clock.c` 里的 `unix_to_sd2506()` 涉及闰年判断、`mdays` 月份表、 -Unix 秒 → 年月日换算、49.7 天 tick 回绕惰性再基准化——这些**用真机只能测"今天", -用单测可以测一万个日期**。而历史事故(`AGENTS.md` 记载的"换算时不得再加 2000, -否则年份虚增导致 Unix 秒数超 uint32 回绕")**正是这类边界 bug**。 - -**建议**:见 §3.1(S1)。 - -#### 问题 T-2:CI 无产物存档、无趋势、无覆盖率 🟡 → ✅ **已改善(2026-10-02 落地 S3)** - -**现状**:CI 只做"通过/失败"判定,**不保存任何产物**: -- `pre-commit run --all-files` 的完整输出不入档(失败后只能靠 Gitea 日志页滚动查找); -- L0.5 单测输出不入档; -- 无任何历史趋势(例如"单测数从 39 涨到多少""门禁耗时变化")。 - -**风险**:CI 失败时**排障成本高**(本次 T-6 排障就是靠直查 MySQL + 手工解压 `actions_log` 才定位的, -见 `Gitea_Runner部署指南.md` §5.1.1)。且"测试数量是否在增长"无人可见。 - -**建议**:见 §3.3(S3)。 - -#### 问题 T-3:`test/` 代码完全豁免静态检查 🟡 → ✅ **已落地(2026-10-03,S5)** - -**现状**(`测试开发标准流程.md` §3.3 明示):`test/` 在 pre-commit 范围之外, -"上表约定属**自律**要求"。而 `test/board/` 下是 **6300+ 行 C 代码**(实测 `.c`+`.h` 合计 6,313 行),且**编入固件**。 - -**风险**:测试代码也是代码——`net_test_task.c` 曾出现"`s_rx_buf` 使用先于声明" -(`测试开发标准流程.md` 变更记录 2026-09-15),说明测试代码质量并非自动达标。 -豁免 cppcheck 意味着测试代码的**未初始化变量/越界/空指针**不会被静态发现。 - -**建议**:见 §3.5(S5)。注意**不宜**直接纳入现有钩子(会引入大量存量告警), -宜用**独立、宽松**的钩子(只报 error 级、不含 style)。 - -#### 问题 T-4:测试固件构建未纳入闭环(= 上游 P1-10,本文给增量)🟡 → ✅ **已落地(2026-10-02,S6)** - -**上游已登记** P1-10"测试固件构建闭环"。**本文增量**: -`测试开发标准流程.md` 变更记录 2026-09-15 记载 **CH395F 套件 7 个配置 -"自 2026-08(88160a5)起不可编译"**,且"128KB 静态 RAM 预算溢出" -(HW 全阶段固件超 8.7KB、NET 固件超 ~28KB)。 - -**这说明**:测试套件的**构建可编译性没有独立门禁**——产品构建 `@build` 天天跑, -但**测试构建可能长期处于"编不过"状态而无人知**,直到某次要用才发现(正是 09-15 的情况)。 - -**建议**:见 §3.6(S6)——把"测试套件构建"做成**可选的编译验证**,而非依赖人记得。 - -#### 问题 T-5:真机回归的"判定强度"仍可提升 🟡 → ✅ **已落地(2026-10-02,S7)** - -**现状**:`verify_log.py` 已支持 `--require-suite-summary`(T-2,要求汇总行 + `N>0` + `failed=0`), -这是很好的设计。但**产品构建回归**(非套件)仍是纯 Required 子串匹配 + -Forbidden `FAIL;ERROR;HardFault;assert`。 - -**盲区**:产品构建回归**不校验** syslog 中的自检结果。 -项目已有 `sys_log` 引擎写 `/log/selftest.log`(`运行与自检记录设计说明.md`), -但 `@test` 闭环**没有读取它**——自检失败(例如"时钟芯片异常")只在设备日志里静静躺着。 - -**建议**:见 §3.7(S7)——让 `@test` 闭环消费 `selftest.log` 的判定结果。 - ---- - -## 2. 整体架构:改进点 - -### 2.1 架构现状(客观评价) - -**做得好的**(无需改动): - -| 项 | 证据 | -|---|---| -| 六层单向依赖 | `项目架构总览.md` §1,跨层依赖有明确表 | -| 就绪屏障替代魔法延时 | `sys_events.h` + `osFlagsNoClear` 语义修正(含事故复盘) | -| 单写者收敛 | 日志(`console_log` / `sys_log`)、配置(`cfg_file` 属主=defaultTask)、SPI(netMsgQueue 串行化) | -| 平台后端抽象 | `cfg_file` 的"共享核心 + 双端后端"(MCU=FreeRTOS 堆/模拟器=宿主堆) | -| 双端共享源码 | UI / `net_cfg` / `cfg_file` 同时编 MCU 与模拟器 | -| 可观测性 | 栈水位探针、链路时延探针(DWT)、syslog 双记录、自检上报 | -| CubeMX 生存保障 | `check_cubemx_survival.py`(18 项清单 + 哈希基线) | - -### 2.2 改进点 A-1:孤儿源文件(认知负担 + 误导)🟡 → ✅ **已闭环(2026-10-02,S8)** - -**证据**(2026-10-02 实测): - -``` -App/util/crc.c 120 行 → 零 `#include "crc.h"`,不在 Keil 工程 -App/util/ringbuf.c 154 行 → 零 `#include "ringbuf.h"`,不在 Keil 工程 -``` - -- `crc.c` 提供 `crc16_modbus()` / `crc8()`,但**唯一使用者 `net_cfg_legacy.c` - 自己复制了一份 `s_crc16_modbus()`**(第 43/55 行),并未调用 `crc.c`。 -- `ringbuf.c` 是通用环形缓冲,但 `console_log.c` **自己内联实现了环形缓冲** - (`s_con_ring` + head/tail,第 39~42 行),未复用。 - -**风险**: -1. **误导**:新读者看到 `App/util/crc.c` 会以为 CRC 有统一实现,实际各写各的; -2. **维护负担**:`app_main.c` 的 `/log` 无调用者却需为它做"是否要测/是否要改"的决策; -3. 上游 E-15 已提"ringbuf 定去留——**已确认零调用者,倾向删除**",本文**用实测佐证该结论** - 并**追加 `crc.c`**。 - -**建议**:见 §4.1(S8)。 - -> ✅ **2026-10-02 落地**:`App/util/crc.{c,h}`、`App/util/ringbuf.{c,h}` 已删除, -> `App/util/README.md` 引用同步清理,引用关系图重跑;上游 E-15 随之销项。上述"证据"保留作背景。 - -### 2.3 改进点 A-2:错误码通道的"下层不对称"🔴 → ✅ **已闭环(2026-10-03,S9)** - -> **2026-10-04 更新**:GD5F 错误通道族(E-10 / E-42 / E-53 / E-54 / E-56 / E-57)已按下方 -> **四要素骨架**完成统一,GD5F 套件回归 **16/16 PASS**(见 §4.2)。本节保留作架构分析。 - -**上游已登记**(`错误码传播审计.md` 第二档 #1:E-10/E-42/E-53/E-54/E-56/E-57 GD5F 错误通道族)。 -**本文增量**:给出**架构层面的根本原因**与**统一方案骨架**。 - -**根本原因分析**:`ch395f.c`(1330 行) 与 `gd5f2gq5ue.c`(1496 行) 是**同代驱动**, -但只有 CH395F 形成了"错误分类 + 传播 + 审计"的完整体系(有 `错误码传播审计.md`、 -`CH395F_Trap_Records.md` 737 行);GD5F 侧的错误处理散落在各函数内, -`Trap_Records` 仅 198 行。**差异不在驱动复杂度,而在是否建立了统一的错误通道约定**。 - -**建议骨架**(供 C2 参考): - -``` -统一错误通道四要素(以 CH395F 为范式): - ① 分类:区分「超时 / 真故障 / 参数错」——不得混为一个返回码 - ② 透传:底层错误码逐层上报,不吞不猜(禁 `(void)ret`) - ③ 分级:可恢复(重试)/ 不可恢复(上报自检 + 降级)显式判定 - ④ 留痕:不可恢复项写 syslog selftest,可恢复项计数并在 UI/日志可见 -``` - -**关键纪律**(呼应 `AGENTS.md` 已有铁律):**自愈逻辑严禁把"名字/参数类错误码" -归入"卷损坏触发集"**——`.fschk` 无限重格式化事故即此类(2026-09-20)。 - -### 2.4 改进点 A-3:启动段无看门狗保护的窗口 🟡 → ✅ **已闭环(2026-10-02,S10)** - -**上游已登记**(N-03b,视为"显式代价")。**本文增量**:量化并给出**风险接受条件**。 - -**现状**:`app_main_init` + `defaultTask` bring-up 段(约 `sd2506_init` → `f_mount` → `net_cfg_load`) -无 IWDG 保护。该段死机表现 = "无输出 + LED 不闪"(N-03b 原文)。 - -**风险量化**:这段代码涉及 -- `sd2506_init` / `sys_clock_init`(I2C1,有超时) -- `gd5f2gq5ue_init`(**BBT 全片扫描**,最耗时) -- `f_mount` / 可能的 `f_mkfs`(FAT32 格式化) -- `net_cfg_load`(NAND 读 + INI 解析) - -**建议**:不强行加 IWDG(会与"BBT 扫描耗时不确定"冲突),而是 -① **加启动段进度日志**(每步 `DBG_INFO`,使"卡在哪一步"可见); -② **为长操作加显式看门狗喂狗点**(若将来启用启动期 IWDG); -③ 文档明确"启动段死机的判读方法"。 - -> 检查现状:`app_main.c` 是否每步有日志——若有则可直接销项。见 §4.3(S10)。 > -> ✅ **2026-10-02 落地**:`app_main_init` 已补 3 条、`sd2506_init` 已补 6 条启动日志 -> (Flash +904 B);`项目启动顺序管理.md` §6.8 新增"启动段死机判读法"——**每步均有可区分日志行**。 - -### 2.5 改进点 A-4:ADC/RS485 采集链路缺失(核心功能空洞)🔴 - -**上游已列**(P1-1、阶段 B)。**本文增量**:从**测试可验证性**角度补充设计约束。 - -**现状**:`adc_task.c` / `rs485_task.c` 均为 43 行空桩(`osDelay` 死循环); -`App/protocol/` 目录**只有 README 与 `.gitkeep`**(为协议解析预留但未实现)。 - -**测试可验证性约束**(建议纳入 B1/B2 设计): - -1. **协议解析必须与硬件 I/O 分离**——`App/protocol/` 的解析函数应接受 - `(const uint8_t *buf, size_t len)` 返回解析结果,使它能成为 L0.5 的**天然适格对象** - (参考 `cfg_file` 的"核心 + 后端"模式); -2. **采集缓冲必须双缓冲 + 序号**(上游 E-49/E-50)——否则消费者读到撕裂数据, - **且撕裂在单测里测不出来**(时序相关),必须在设计上消除; -3. **DE 收尾必须有超时路径覆盖**(RS485 半双工的特有陷阱)。 - -### 2.6 改进点 A-5:内存预算的"预算—实际"闭环可再收紧 🟡 → ◐ **部分闭环(S11 已落地;堆告警已补,栈风险升级)** - -**现状**:已有 `ram_report.py`(.map 静态)+ `stack_report.py`(静态调用图 × 运行时水位) -+ `mem-numbers` pre-commit 钩子(校验文档数字与 .map 一致)。**这套机制很先进**。 - -**缺口(2026-10-04 更新)**: -1. `mem-numbers` **在 CI 上被跳过**(无 `.map`)——意味着**CI 不能守体积门禁** - (已由 S14 显式提示,**未消除**); -2. ~~`configTOTAL_HEAP_SIZE`(48KB)无运行时余量告警~~ → ✅ **已补(S11,2026-10-03)**: - 堆低水位告警已落地(阈值 4096 B + 写 syslog selftest);V5 30 min 长跑**未触发**, - 最新基线 `free=8,664 / min_ever=4,728 B`(2026-10-04 重采,较 09-21 的 4,848 B 再降 120 B); -3. ⚠️ **上游 S-1 已升级为"需尽快处理"**:2026-10-04 产品构建实测 defaultTask 栈 - **3664 / 4096(余量 432 B ≈ 10%,远低于 30% 判据)**,较 2026-09-21 的 2872 B 明显上升; - 处置方向:栈 4096→4608(CCM 堆余量可覆盖 +512 B)或 GC 链局部削减。 - -**建议**:见 §4.4(S11)——**堆余量低水位告警**(复用既有 `stack_probe_poll` 模式)。**已落地**。 - -### 2.7 改进点 A-6:`configASSERT` 已改造但覆盖不足 🟡 → ⬜ **待做(S12)** - -**现状**(N-03a 已修):`configASSERT` 已从"关中断死循环"改为 -`vApplicationAssertFailed(__FILE__, __LINE__)` → 打印后复位。**这是很好的改进**。 - -**缺口**:`configASSERT` **只覆盖 FreeRTOS 内部断言**。 -项目自研代码**无统一断言宏**——例如 `net_socket.c` 的参数校验靠 `if + return -1`, -`cfg_file.c` 的边界靠手工检查。**没有轻量断言**意味着"不该发生的情况"静默通过。 - -**建议**:见 §4.5(S12)——引入**可选、可编译掉**的模块级断言宏 -(生产构建零开销,测试构建启用),与 `test_common.h` 的 `TEST_CHECK` 呼应。 - -### 2.8 改进点 A-7:模拟器差距清单未成体系 🟡 → ✅ **已闭环(2026-10-02,S13)** - -**现状**:多处提及"模拟器差距清单 +1"(N-4 二期门禁),例如 -"net_socket_sim.c 无法建模 pending"。但**没有一份汇总的"模拟器差距清单"文档**—— -差距散落在各文档的只言片语中。 - -**风险**:模拟器回归通过 ≠ 真机通过,但**差异有多大、差在哪,无人能一眼说清**。 -这会让"模拟器能否作为 L2 门禁"的判断失去依据。 - -**建议**:见 §4.6(S13)——建立**单一差距清单文档**。 +> **只保留待办**:**已完成条目直接删除,不留变更记录**(历史见 `git log`)。 +> 已完成事项的**证据与结论沉淀在模块文档**,本文件不再重复——对应关系: +> `测试说明/系统时钟单元测试说明.md`(S1-a)、`测试说明/配置文件单元测试说明.md`(S1-b)、 +> `测试说明/测试开发标准流程.md` §4.2.1(L0.5 适格台账)、`测试说明/模拟器差距清单.md`(S13)、 +> `测试说明/实机测试实施指南.md`(S4/S5/S7/S9/S11)、`问题记录/GD5F2GQ5UE_Trap_Records.md`(S9)。 +> +> **编号约定**:**S1~S14 / T-1~T-5 / A-1~A-7** 是 2026-10-02 版复审的**条目编号**—— +> 编号**不随条目删除而回收**;其他文档若引用这些编号,其证据见上列模块文档与 `git log`。 --- -## 3. 测试体系改进实施建议 +## 1. 未闭环一览 -### 3.1 S1:扩充 L0.5 主机侧单测(**最高性价比**)⭐⭐⭐⭐⭐ → ✅ **S1-a / S1-b 已落地;S1-c 可选** - -**目标**:把至少 2 个高价值纯逻辑模块纳入 L0.5,使"改这些模块"不再需要真机才能回归。 - -**实施路径**(完全复用既有范式,见 `看门狗单元测试说明.md`): - -**S1-a:`sys_clock.c` 日期算术单测**(推荐首个,边界最密、零依赖)—— ✅ **已完成(2026-10-02)** - -| 步骤 | 内容 | -|---|---| -| 1 | 确认 `sys_clock.c` 的硬件依赖面(`sd2506_get_time/set_time` + `HAL_GetTick` + 临界区宏 + `DBG_*`) | -| 2 | **全部依赖都是外部符号/宏 → 直接强符号覆盖桩(看门狗范式),无需平台层抽取**——比预期更干净,**生产代码零改动**(Zeller 修复除外,见下) | -| 3 | 新建 `test/host/sys_clock/run_unit_test.py`(声明式,≈50 行)+ `sys_clock_test.c` + `stub/` 4 个头 | -| 4 | 用例设计(**边界穷举**):闰年(2000 闰 / 2024 闰)· 月末(1/31、2/28、2/29、4/30)· 世纪边界 · 2000 最早年/2038 上限(`0x7FFFFFFF`)· **30 天惰性再基准化 + 亚秒余数结转 + 49.7 天 tick 回绕** · 星期(`rtc_calc_week`)16 个已知日期对照 · RTC 读失败/越界/写回失败三故障路径 | -| 5 | 挂 pre-commit 钩子 `sys-clock-unit-test`(改动 `sys_clock.c`/`sd2506.h` 时自动跑,秒级) | -| 6 | 在 `.gitea/workflows/ci.yml` 无需改动——`l05` job 遍历 `test/host/**/run_unit_test.py`,**自动纳入** | - -**完成判据**:`python test/host/sys_clock/run_unit_test.py` 退出码 0(**100/100 PASS**); -`@build` 0/0(已验);CI 的 `l05-host-unit-tests` 日志出现该运行器且 PASS(待推送后复验)。 - -> ⚠️ **重大副产物**:本单测**首跑即抓出一处生产缺陷**——`rtc_calc_week()` / -> `unix_to_sd2506()` 的 Zeller 公式错误,使 **1970~2099 全部日期的 `week` 字段皆为 -> 错值**(无常数偏移,错误率 ~78%;时间数值本身正确,故此前未暴露)。已修复并 -> 经 Python `datetime` 独立核对 47,482 天零错误。详见 -> [`../测试说明/系统时钟单元测试说明.md`](../测试说明/系统时钟单元测试说明.md) §5。 -> 修复使 Flash 减少 112 B(删除重复内联公式),内存快照已同步 -> (`check_mem_numbers.py --write`)。 - -**S1-b:`cfg_file.c` INI 解析器单测**(价值同样高,但需先确认抽象质量)—— ✅ **已完成(2026-10-02)** - -| 步骤 | 内容 | -|---|---| -| 1 | **利好**:`cfg_file` 已有 `cfg_store_alloc/free` 平台后端(`cfg_file_store.h`),MCU=FreeRTOS 堆、模拟器=宿主堆——**天然适格** | -| 2 | 确认文件 I/O 是否也已抽象(预计 `cfg_file` 的核心是"文本 ↔ 结构"纯变换,I/O 在后端) | -| 3 | 新建 `test/host/cfg_file/`,用桩后端提供内存虚拟文件 | -| 4 | 用例设计:INI 正常解析 · 空文件/缺段/重复键 · 键值转义 · **长度上限**(`CFG_FILE_MAX_LEN`)· **段名长度**(上游 E-16 相关)· 旧档 NCFG 迁移路径(`net_cfg_legacy`)· 畸形输入不崩溃 | -| 5 | 挂钩子 `cfg-file-unit-test` | - -> ⚠️ **注意**:`cfg_file.c` 1250 行,**不建议一次全测**。优先覆盖**解析器 + -> 边界校验**(P0-1 堆分配路径已由真机验证,见上游 A1),不做"为覆盖率而测"。 - -**S1-c(可选):`sys_log` 记录编解码、`time_sync` 协议帧** - -同上范式。优先级低于 S1-a/S1-b,可在 S1-a 落地后按剩余收益评估。 - -**⚠️ 关键约束**(重申 `测试开发标准流程.md` §4.2): -- 平台层抽取必须**零行为变化、零额外开销**(MCU 默认实现即原逻辑); -- 测试钩子用 `#ifdef <模块>_UNIT_TEST` 守卫,**MCU 构建零符号**; -- 单测**不能替代**板端验证——真机寄存器/复位原因/时序仍须 L1/L2。 - -### 3.2 S2:为 L0.5 建立"适格模块清单"(防遗漏)⭐⭐ → ✅ **已落地(2026-10-02)** - -**问题**:现在只有"看门狗"一个先例,**下一个该测谁**靠临时判断。 - -**建议**:在 `测试开发标准流程.md` 增补一节「L0.5 适格模块台账」: - -| 模块 | 状态 | 备注 | -|---|---|---| -| `task_watchdog.c` | ✅ 已覆盖(39 用例) | 范式 | -| `sys_clock.c` | ✅ 已覆盖(20 用例 / 100 断言点,2026-10-02 S1-a) | **首测即抓出 Zeller 星期缺陷并修复**,见 `系统时钟单元测试说明.md` §5 | -| `cfg_file.c` | ⬜ 待做(S1-b) | 已有后端抽象 | -| `sys_log_mcu.c` | ⬜ 候选 | 记录编解码 | -| `time_sync.c` | ⬜ 候选 | 协议帧 | -| `rs485.c` | ⬜ 待功能实现 | 上游 B2 | -| `crc.c` / `ringbuf.c` | ✅ 已删除(2026-10-02 S8) | 孤儿文件,已确认无被引 | -| `tpafe5160.c` | ❌ 不适格 | 寄存器序列为主 | -| 其余驱动 | ❌ 不适格 | 需硬件时序 | - -**完成判据**:文档有台账,新增模块时按表判断是否需 L0.5。 - -### 3.3 S3:CI 补产物存档与结果汇总 🟡 → ✅ **已落地(2026-10-02)** - -**目标**:让 CI 失败**可远程排障**、让测试规模**可趋势观察**。 - -**实施路径**: - -| 步骤 | 内容 | 备注 | -|---|---|---| -| 1 | 两个 job 各加"失败时/总是"上传日志产物 | Gitea Actions 支持 `actions/upload-artifact`;**需确认本实例的 action 拉取可用**(内网可能不通 github,见 `CI配置指南.md` 的"内网 action"章节) | -| 2 | 若 action 不可用,**降级方案**:把关键输出 `tee` 到一个固定路径并 `::notice::` 打印摘要 | 零依赖 | -| 3 | L0.5 job 末尾打印**汇总行**(如 `L0.5: 1 runner(s), 39/39 PASS`) | 简单有效,便于一眼看规模 | -| 4 | (可选,后续)收集 `[STACK]` 水位与 `.map` 数字形成趋势 | 依赖 runner 有 Keil | - -**完成判据**:CI 失败时,**不查 MySQL / 不解压 `actions_log`** 即可从 Gitea 页面 -看到完整输出与摘要。 - -> **本文增量**:本项目 CI 是**自建 Gitea 内网实例**,"能否拉外层 action"是现实约束—— -> 建议**优先做零依赖方案**(步骤 2/3),而不是先引入 `upload-artifact`。 - -### 3.4 S4:复核"文档用例 ↔ 代码断言"覆盖矩阵 ✅ **已落地(2026-10-03)** - -**问题**:文档声称 91 个 TC、代码有 222 个断言点,但**两者是否一一对应无人校验**。 -`测试开发标准流程.md` §2.3 铁律 3 要求"每条设计原则必须有对应用例", -但**没有机械校验**。 - -**建议**:新增一个**轻量校验脚本**(类似 `check_doc_links.py` 的形态): - -``` -scripts/check_test_traceability.py - 输入:test/board/*_test_task.c 的 TC 注释 + docs/测试说明/*Test_Guide.md 的 TC 编号 - 输出:① 文档有、代码无(用例未实现) ② 代码有、文档无(用例未登记) - 门禁:默认仅告警(warning),不阻断提交 -``` - -**⚠️ 慎重**:**不建议**立即做成阻断型门禁——存量差异可能较多, -宜先**输出报告**,人工确认后再决定是否收紧。这与 `doc-links` 的演进路径一致。 - -**完成判据**:能一键列出双向差异清单;差异收敛后可考虑升为门禁。 - -> **✅ 2026-10-03 落地**:`scripts/check_test_traceability.py` 已实现并跑通, -> **默认仅告警(exit 0)**。当前输出 **30 处差异**(26 处"文档有、代码无" + -> 4 处"代码有、文档无":`TC-GD5F-1100/1101/1102`、`TC-STO-06`)。**暂不升门禁**。 - -### 3.5 S5:给 `test/` 代码加"宽松"静态检查 ✅ **已落地(2026-10-03)** - -**目标**:抓 `test/board/` 6300+ 行固件测试代码的**真实缺陷**(未初始化/越界/空指针), -但不引入存量 style 噪音。 - -**实施路径**: - -| 步骤 | 内容 | -|---|---| -| 1 | 先跑一次基线:`cppcheck --enable=warning --error-exitcode=1 test/`,**统计存量告警数** | -| 2 | 若存量可控(建议 <20 条),加 `test-cppcheck` 钩子,**只开 `warning`**(不含 performance/portability/style) | -| 3 | 若存量较多,先建 **suppress 清单**(明确每条的豁免理由),再挂钩子 | -| 4 | **务必**排除模拟器桩/第三方头(`test/host/watchdog/stub/` 等) | - -**完成判据**:`test-cppcheck` 钩子在 CI 上 PASS;新增测试代码的越界/未初始化被自动拦截。 - -> **风险提示**:`test/` 代码豁免是**既有决策**(`测试开发标准流程.md` §3.3)。 -> 本建议**不是推翻它**,而是"在不产生大量噪音的前提下,补上致命缺陷的检查"。 -> 若基线告警过多,**保持现状亦可接受**——此时应把该决策写得更明确("理由:存量告警 N 条")。 - -> **✅ 2026-10-03 落地**:走**路径 2**——基线实测**0 条 warning**(存量可控), -> 已加 `test-cppcheck` 钩子(`.pre-commit-config.yaml`,`--enable=warning` -> + 排除 `test/host/*/stub/` 模拟器桩)。`pre-commit run test-cppcheck --all-files` -> **PASS**;新增 `test/board/` 代码的未初始化/越界/空指针现被自动拦截。 - -### 3.6 S6:测试构建可编译性纳入验证(= 上游 P1-10 增量)🟡 → ✅ **已落地(2026-10-02,方案 A)** - -**目标**:防止"测试套件长期编不过而无人知"(2026-08~09 CH395F 套件实例)。 - -**实施路径**(三选一,按成本递增): - -| 方案 | 做法 | 成本 | 效果 | +| 编号 | 事项 | 状态 | 详述 | |---|---|---|---| -| A(最轻) | `test/run_all_suites.py --dry-run` 基础上加 `--check-build`:仅改宏 + `@build`,**不烧录** | 低 | 每次回归窗口自动验证 5 套件可编译 | -| B | 新增 `@build-suites` 命令:循环 5 套件各跑一次 `@build`(含 RAM 溢出检查) | 中 | 一条命令全验 | -| C | CI 加 job(需 runner 有 Keil) | 高 | 自动守门,但当前 runner 无 Keil | - -**建议**:**先做 A**(`run_all_suites.py` 已有改宏 + `try/finally` 还原的骨架, -加 `--check-build` 是自然扩展),成本最低、收益明确。 - -**注意**:RAM 溢出(P1-9:HW 全阶段固件超 8.7KB)意味着**即使能编译也可能链接失败**—— -方案 A 的 `@build` 会自然暴露 `L6406E`,这正是想要的。 - -> ✅ **2026-10-02 落地(走方案 A)**:`test/run_all_suites.py` 新增 `--check-build` -> ——每轮只改宏 + `@build`、**不烧录不采集**;编译通过即 PASS,失败即判定"该套件编译不过"。 -> 该模式**不需要串口/烧录工具**,可在纯离机环境运行(`--check-build` 下不强求 `run_boot_test.py` 存在)。 - -### 3.7 S7:真机回归增加"自检结果"判定 🟡 → ✅ **已落地(2026-10-02)** - -**目标**:让 `@test` 闭环不仅看串口标记,**还看设备的自检结论**。 - -**现状**:`sys_log` 引擎写 `/log/selftest.log`(`运行与自检记录设计说明.md`), -但 `verify_log.py` 只解析串口日志。 - -**实施路径**: - -| 步骤 | 内容 | -|---|---| -| 1 | 确认 `selftest.log` 的记录格式(定长 64B,应有"项 / 结果 / 时间"字段) | -| 2 | `verify_log.py` 增加 `--require-selftest-pass`:要求串口日志中出现自检汇总 - (或由 `serial_tool` 通过 FTP 取回 `selftest.log` 后校验) | -| 3 | 判定语义建议:**自检有 no-go 项 → FAIL**;有 warn 项 → 打印但 PASS | - -**完成判据**:`@test` 能捕获"时钟芯片异常""存储降级"等自检失败, -而不是只捕获 `FAIL` 字符串。 - -> **注**:`sys_clock_init` 在 RTC 故障时返回 -1 并触发"时钟芯片异常"自检 -> (`项目架构总览.md` §3)——这正是当前闭环**捕获不到**的典型场景。 - -> ✅ **2026-10-02 落地**:`@test` 闭环已消费自检结论,真机全闭环 **`@test` PASS 7/7**。 -> 完成判据达成——"时钟芯片异常""存储降级"等自检失败现可被闭环捕获。 +| **S12** | 模块级轻量断言宏 | ⬜ 待做(第 4 波按需) | §2.1 | +| **S1-c** | `sys_log` / `time_sync` 主机侧单测 | ⬜ 可选(价值递减) | §2.2 | +| **A-4** | ADC / RS485 采集链路的**测试可验证性约束** | ⏸ 待 B1/B2 落地时执行 | §2.3 | +| **A-5 残余** | CI 体积门禁盲区(`mem-numbers` 在 CI 被跳过) | ⬜ 未消除 | §2.4 | --- -## 4. 架构改进实施建议 +## 2. 待办详述 -### 4.1 S8(原 A-1):清理孤儿源文件 ⭐⭐⭐ → ✅ **已落地(2026-10-02)** - -**动作**:二选一,**必须做决策**(现状"留着不管"是最差选项)。 - -| 方案 | 适用 | 动作 | -|---|---|---| -| **删除**(推荐) | 确认无调用者且无预留计划 | 删 `App/util/crc.c`、`App/util/crc.h`、`App/util/ringbuf.c`、`App/util/ringbuf.h`;同步清理 `App/util/README.md` 的引用 | -| 保留并复用 | 有明确复用计划 | ① 让 `console_log.c` 改用 `ringbuf`(**需评估:会改动已稳定的日志路径,风险不低**)② 让 `net_cfg_legacy.c` 删掉自有 CRC 改用 `crc.c`(**收益小、动 legacy 代码**) | - -**建议**:**删除**。理由: -- 上游 E-15 已倾向删除 `ringbuf`; -- `console_log` 的内联缓冲已经**稳定运行且经过事故验证**(单写者 + 丢弃计数), - 为"复用"去改它是**负收益**; -- `crc.c` 的被复用场景(legacy 迁移)是一次性代码,不值得建立统一 CRC 模块。 - -**完成判据**:文件不存在;`App/util/README.md` 同步;`@build` 0/0(本就不参与构建,应无影响); -全库无 `crc.h` / `ringbuf.h` 引用(E-15 销项)。 - -### 4.2 S9(原 A-2):统一 GD5F 错误通道(= 上游 C2 骨架)✅ **已闭环(2026-10-03)** - -见 §2.3 的四要素骨架。**实施顺序建议**: - -1. **先建分类函数**(`wait_busy` 区分超时/真故障)——这是所有下游判断的基础; -2. **再修最危险的**:`bbt_scan` 失败**放弃持久化**(保护好副本)——数据安全优先; -3. **然后补 init 全链路检查 + 重试**; -4. **最后**回读 B0h 校验 ECC; -5. 全程在 `GD5F2GQ5UE_Trap_Records.md` 留痕,并**同步 `GD5F2GQ5UE_Test_Guide.md`**。 - -**完成判据**:E-10/E-42/E-53/E-54/E-56/E-57 全部销项;GD5F 套件回归 PASS。 - -### 4.3 S10(原 A-3):启动段可观测性 🟡 → ✅ **已落地(2026-10-02)** - -**先核查现状**:`app_main.c` 的 `app_main_init` 是否每步都有 `DBG_INFO`。 - -| 核查结果 | 动作 | -|---|---| -| 已每步有日志 | **直接销项**,仅在文档注明"启动段死机可通过最后一条日志定位" | -| 部分有日志 | 补齐缺失步骤的日志(**零风险改动**) | -| 无日志 | 补全(注意日志字符串**必须纯 ASCII**,`AGENTS.md` 铁律) | - -**完成判据**:`app_main_init` 每一步都有可区分的日志行; -文档(`项目启动顺序管理.md` 或 `看门狗设计说明.md`)明确"启动段死机判读法"。 - -> ✅ **2026-10-02 落地**:`app_main_init` 补 3 条、`sd2506_init` 补 6 条启动日志(Flash +904 B); -> `项目启动顺序管理.md` §6.8 新增"启动段死机判读法"。判据达成。 - -### 4.4 S11(原 A-5):堆余量低水位告警 🟡 → ✅ **已落地(2026-10-03)** - -**动机**:实测 `min_ever=4,848 B`(初始 48KB 堆),**余量已不到 10%** -(2026-10-04 重采:`min_ever=4,728 B`,再降 120 B)。 -未来新增任务/缓冲时,**分配失败可能在深夜台架长跑时才暴露**。 - -**实施路径**(复用既有 `stack_probe_poll` 模式,`default_task.c` 内): - -| 步骤 | 内容 | -|---|---| -| 1 | 用 `xPortGetMinimumEverFreeHeapSize()` 取历史最低水位 | -| 2 | 在 `defaultTask` 的低频循环(如 LED 闪烁节拍)里检查 | -| 3 | 低于阈值(建议 `4096 B`)时 `DBG_ERROR("[HEAP] low water: %u B")` + **写 syslog selftest** | -| 4 | 阈值设为编译期常量,便于按新基线调整 | - -**成本**:约 15 行代码 + 1 个常量,**零额外缓冲**。 - -**完成判据**:台架长跑下能观察到堆低水位告警(若有);文档记录阈值与基线。 - -> ✅ **2026-10-03 落地**:堆低水位告警已在 `defaultTask` 低频循环实现(阈值 4096 B + 写 syslog selftest); -> V5 30 min 长跑**未触发**告警,基线 `min_ever=4,728 B`(2026-10-04 重采)。判据达成。 - -### 4.5 S12(原 A-6):模块级轻量断言宏 🟡 → ⬜ **待做(第 4 波按需)** +### 2.1 S12:模块级轻量断言宏 ⬜ **动机**:`configASSERT` 只覆盖 FreeRTOS 内部;自研代码的"不该发生"靠散落的 `if`。 @@ -629,7 +47,7 @@ scripts/check_test_traceability.py ``` **⚠️ 关键纪律**: -- **不得**在中断/高频繁路径使用(`DBG_ERROR` 有代价); +- **不得**在中断 / 高频路径使用(`DBG_ERROR` 有代价); - **不得**替代正常的错误返回(断言是"内部不变量",不是"外部输入校验"); - 生产构建 `APP_ASSERT_ENABLE` **关闭**——与 `ENABLE_DESTRUCTIVE_TESTS` 同级策略。 @@ -637,112 +55,72 @@ scripts/check_test_traceability.py **完成判据**:宏存在 + 生产构建 `@build` 0/0 且 `.map` 无新增符号;试点处有实际使用。 -### 4.6 S13(原 A-7):建立模拟器差距清单文档 🟡 → ✅ **已落地(2026-10-02)** +### 2.2 S1-c(可选):`sys_log` / `time_sync` 主机侧单测 ⬜ -**动机**:模拟器回归是 L3 门禁,但"通过 ≠ 真机通过"。差异**散落各处**: +**范围**:`App/log/sys_log_mcu.c`(定长 64B 记录编解码;落盘部分需桩)、 +`App/sync/time_sync.c`(协议帧编解码;与网络耦合,需先判定可解耦面)。 -| 已知差距 | 出处 | -|---|---| -| `net_socket_sim.c` 无法建模 pending 机制 | N-4 二期门禁 | -| (其它) | 需系统性收集 | +**为何低优先**:两者边界密度低于 `sys_clock` / `cfg_file`,且已有真机路径覆盖 +(`@test` 标记 / `time_sync_server.py`)。**按剩余收益评估后再决定是否做**, +范式完全复用 `看门狗单元测试说明.md`。 -**建议**:新建 `[计划] docs/UI设计/模拟器差距清单.md`(或放 `docs/测试说明/`), -按格式登记: +**完成判据**:`test/host/<模块>/run_unit_test.py` 退出码 0 + 挂 pre-commit 钩子。 -| # | 差距 | 模拟器行为 | 真机行为 | 影响 | 回归时是否需真机补测 | -|---|---|---|---|---|---| +### 2.3 A-4:ADC / RS485 采集链路的测试可验证性约束 ⏸ -**完成判据**:文档建立且至少收录已知差距;UI 回归流程文档引用它。 +**背景**:`adc_task.c` / `rs485_task.c` 仍是空桩,`App/protocol/` 只有 README 与 `.gitkeep`。 +本项不是"缺陷",而是**对上游 B1 / B2 的设计约束**——应在动手前明确,否则后续无法补测。 -> ✅ **已完成(2026-10-02)**:落地为 [`../测试说明/模拟器差距清单.md`](../测试说明/模拟器差距清单.md) -> (放 `docs/测试说明/`——它是跨子系统的测试资产,比 `UI设计/` 更贴切)。 -> 收录 **6 条网络**(N-1~N-6,含 pending 缺失、代次号恒 0、9 个 MCU 函数 sim 未实现)+ -> **4 条日志/配置**(L-1~L-4,含 mtime 粒度、堆后端差异)+ **7 条硬件专属**(H-1~H-7)+ -> **4 条"有意对齐"**(防误判)。新增 **§3 六条"必须回真机"决策规则**(把该建议从"登记册" -> 提升为"可执行判据")。`simulator/README.md` §五与 `docs/README.md` §5 已交叉引用; -> 登记约定写入 §4(新增 `sim/` 后端差异必须登记)。 +**约束**(建议纳入 B1 / B2 设计): -### 4.7 S14:CI 与体积门禁的关系澄清 🟡 → ✅ **已落地(2026-10-02)** +1. **协议解析必须与硬件 I/O 分离**——`App/protocol/` 的解析函数应接受 + `(const uint8_t *buf, size_t len)` 并返回解析结果,使其成为 L0.5 的**天然适格对象** + (参考 `cfg_file` 的"核心 + 后端"模式); +2. **采集缓冲必须双缓冲 + 序号**(上游 E-49 / E-50)——否则消费者读到撕裂数据, + 且**撕裂在单测里测不出来**(时序相关),必须在设计上消除; +3. **DE 收尾必须有超时路径覆盖**(RS485 半双工特有陷阱)。 -**现状**:`mem-numbers` 在 CI 上被跳过(无 `.map`)。 -**这导致一个认知风险**:看到 CI 全绿可能误以为"体积也守住了"。 +**完成判据**:B1 / B2 设计文档体现上述三条;协议解析函数有对应 L0.5 单测。 -**建议**: -1. **文档层面**:在 `CI配置指南.md` **显著位置**声明 - "**CI 不守体积门禁**,改动内存结构后必须在本地跑 `ci_local.py` 或 `@build`"; -2. **CI 层面(可选)**:在 job summary 打印 - `::notice::mem-numbers skipped (no .map) -- size gate NOT enforced in CI`—— - 已在 workflow 有类似逻辑(`SKIP=mem-numbers`),**只需把跳过原因显式提示出来**。 +### 2.4 A-5 残余:CI 体积门禁盲区 ⬜ -**完成判据**:CI 日志中有明确的"体积门禁未执行"提示;文档有醒目声明。 +**现状**:`mem-numbers` 钩子**在 CI 上被跳过**(CI 无 `.map`)→ **CI 不守体积门禁**。 +S14 已在 workflow 显式提示"体积门禁未执行"(`::notice::`)并在 `CI配置指南.md` 声明, +但**盲区本身未消除**——看到 CI 全绿仍可能误以为体积也守住了。 + +**可选处置**: +- 让 runner 具备 Keil 后,在 CI 增 `@build` job(成本高,需授权与许可证); +- 或把"体积校验"固化为**提交前必跑的本地门禁**(`scripts/ci_local.py`,当前已是事实标准)。 + +**完成判据**:存在**强制的**体积校验路径(CI 侧或本地侧),且文档明确其边界。 + +> **注**:本项与上游 `下一步任务规划.md` §2 C4(**S-1:defaultTask 栈 3664/4096 ≈ 10%**, +> 需尽快处理)是同一话题的两面——S-1 是**具体风险**,本项是**门禁缺口**。 --- -## 5. 建议实施路线图 +## 3. 沿用基线(供后续接入参考) -### 5.1 排期建议(与上游阶段 A/B/C/D 并行) +### 3.1 错误码通道四要素(CH395F 范式) -| 阶段 | 本文编号 | 内容 | 依赖 | 预估工作量 | -|---|---|---|---|---| -| **第 1 波**(零风险、高收益) | S8 | 清理孤儿源文件 | 无 | 小 | ✅ 2026-10-02 | -| | S1-a | `sys_clock` L0.5 单测 | 无 | 中 | ✅ 2026-10-02(**抓出并修复 Zeller 缺陷**) | -| | S3 | CI 产物存档(零依赖方案) | 无 | 小 | ✅ 2026-10-02 | -| | S14 | CI/体积门禁澄清 | 无 | 小 | ✅ 2026-10-02 | -| **第 2 波**(补测试能力) | S1-b | `cfg_file` L0.5 单测 | 无 | 中 | ✅ 2026-10-02(**抓出并修复外科手术保存死码 P0**) | -| | S2 | L0.5 适格台账 | S1-a 经验 | 小 | ✅ 2026-10-02 | -| | S10 | 启动段可观测性核查 | 无 | 小 | ✅ 2026-10-02(补 `sd2506_init` 6 条 + `app_main` 3 条;Flash +904 B) | -| | S6 | 测试构建可编译性(方案 A) | 无 | 小 | ✅ 2026-10-02(`run_all_suites.py --check-build`) | -| **第 3 波**(架构加固) | S7 | 真机回归加自检判定 | 无 | 中 | ✅ 2026-10-02(`@test` PASS 7/7) | -| | S11 | 堆低水位告警 | 无 | 小 | ✅ 2026-10-03(V5 长跑未触发,基线 min_ever=4728 B) | -| | S9 | GD5F 错误通道(= 上游 C2) | 无 | 大 | ✅ 2026-10-03(台架套件回归 16/16) | -| | S5 | `test/` 宽松静态检查 | 先跑基线 | 小~中 | ✅ 2026-10-03(`test-cppcheck` 钩子,`--enable=warning`) | -| | S4 | 用例追溯脚本 | 无 | 中 | ✅ 2026-10-03(`scripts/check_test_traceability.py`,默认告警) | -| **第 4 波**(按需) | S12 | 模块级断言宏 | 试点验证 | 小(试点) | ⬜ **待做** | -| | S13 | 模拟器差距清单 | 无 | 小 | ✅ 2026-10-02 | +> **来源**:`错误码传播审计.md`。GD5F 错误通道统一(E-10 / E-42 / E-53 / E-54 / E-56 / E-57) +> 即按此骨架落地(2026-10-03,套件回归 16/16 PASS)。**后续任何驱动 / 子系统建立错误通道时应沿用。** -> **"⏸ 判据需真机"说明**(2026-10-02 补;**2026-10-03 台架窗口三项全部闭环**):S7 / S11 / S9 的**代码可离线编写**,但其**完成判据** -> 均要求真机观测(`@test` 捕获自检失败 / 台架长跑观察到堆告警 / GD5F 套件回归 PASS)。 -> 在无 MCU 的环境下实施会留下"已改未验证"的债,故押后至有实机的窗口。 -> ✅ **2026-10-03 台架**:S7(`@test` PASS 7/7)、S11(V5 长跑未触发,基线 `min_ever=4728 B`)、S9(GD5F 套件 16/16 PASS)全部闭环。 +``` +① 分类:区分「超时 / 真故障 / 参数错」——不得混为一个返回码 +② 透传:底层错误码逐层上报,不吞不猜(禁 `(void)ret`) +③ 分级:可恢复(重试)/ 不可恢复(上报自检 + 降级)显式判定 +④ 留痕:不可恢复项写 syslog selftest,可恢复项计数并在 UI / 日志可见 +``` -### 5.2 与上游任务的关系 - -| 关系 | 说明 | -|---|---| -| **独立可做** | S1/S2/S3/S4/S5/S8/S10/S11/S13/S14——**不依赖产品定义、不依赖上游阶段**(**✅ 已全部落地**) | -| **与上游 A 并行** | S7(自检判定)与 A1(P0-1 真机验证)**同一轮台架窗口**做最划算(**✅ S7 已落地,`@test` 7/7**) | -| **是上游 C 的骨架** | S9 就是上游 C2;§2.3 的四要素可直接作为 C2 的实施框架(**✅ 已落地,2026-10-03**) | -| **是上游 B 的前置** | S1-b(`cfg_file` 单测经验)+ §2.5 的测试可验证性约束,**应在 B1/B2 动手前明确**(◐ 经验已具备,约束待 B1/B2 落地时执行) | +**关键纪律**(呼应 `AGENTS.md` 已有铁律):**自愈逻辑严禁把"名字 / 参数类错误码" +归入"卷损坏触发集"**——`.fschk` 无限重格式化事故即此类(2026-09-20)。 --- -## 6. 一句话总结 +## 4. 维护约定 -> **测试体系的短板不在"广度"而在"中间层"**——初版时 L0.5 只有 1 个模块; -> 经 S1-a / S1-b 落地,`sys_clock.c`(479 行日期算术)与 `cfg_file.c`(1250 行解析器) -> 这两类"边界密集、零硬件、秒级可测"的模块**已能在主机侧秒级回归**, -> 且**首跑各抓出一处生产缺陷**(Zeller 星期错误、外科手术保存死码 P0)—— -> **补齐 S1 确实是投入产出比最高的一步**。L0.5 现覆盖 3 个模块(看门狗 / `sys_clock` / `cfg_file`)。 -> -> **架构的主缺口是"功能不对称"**(网络极成熟 vs 采集链路为空桩)——**至今未变,是当前最高优先**; -> 而**"纵深不对称"**(上层防御完备 vs NAND 错误通道散乱)**已由 S9 收口** -> (GD5F 错误通道统一,2026-10-03)。本文补充的**测试可验证性约束**与**四要素实施骨架** -> 仍对上游 B1/B2 有效。 -> -> 另有 **3 项低风险卫生改进**已全部执行:清理孤儿文件(S8)、 -> CI 产物存档(S3)、CI 体积门禁澄清(S14)。 -> -> **唯一待做**:S12(模块级轻量断言宏,第 4 波按需试点);S1-c(`sys_log` / `time_sync` 单测)为可选项。 - ---- - -## 变更记录 - -| 日期 | 变更 | -|---|---| -| 2026-10-04 | **状态更新(全量复核)**:逐条核对 S1~S14 落地情况并就地标注(✅/◐/⬜)。§0.1 评价表刷新(**L0.5 1→3 模块**、**L0 钩子 15→18**、**板端 4→5 套件**、CI 已补产物存档与汇总行、验证债大幅下降);§0.3 优先级、§1.1 资产盘点、§1.2 缺口矩阵、§1.3 问题 T-1~T-5、§2.2~§2.8 各改进点、§3.1~§3.7、§4.1~§4.7、§5.1 路线图、§5.2、§6 同步。**修正交叉引用错位**:T-3/T-4/T-5 与 A-1/A-3/A-5/A-6/A-7 指向 §3.x/§4.x 的节号此前**整体偏移一节**,已逐一订正。§2.6 更新为 2026-10-04 重采数据(`min_ever=4,728 B`;**上游 S-1 升级:defaultTask 3664/4096 ≈ 10%**)。**结论:14 条建议已落地 12 条,仅 S12 待做。** | -| 2026-10-03 | 实施记录(台架窗口):**S7 已完成**(`@test` PASS 7/7);**S11 已完成**(堆低水位告警,V5 30 min 长跑未触发,基线 `min_ever=4728 B`);**S9 已完成**(GD5F 错误通道统一,套件回归 16/16 PASS);**S5 已完成**(`test-cppcheck` 钩子,基线 0 warning);**S4 已完成**(`scripts/check_test_traceability.py`,默认告警,当前 30 处差异)。§3.4 / §3.5 与 §5.1 状态已更新 | -| 2026-10-02 | 实施记录(第 2 波):**S1-b 已完成**(`cfg_file` L0.5 单测 20 用例 / 139 断言点 + 钩子 `cfg-file-unit-test`;**首跑抓出"外科手术保存死码"P0**,`s_encode` 返回值 `==0` 恒假 → 已存在配置文件的保存静默不落盘,修 3 处为 `>=0`,记入 E-63);**S2 已完成**(§4.2.1 L0.5 适格台账);**S10 已完成**(补 `sd2506_init` 6 条 + `app_main` 3 条启动日志,Flash +904 B,`项目启动顺序管理.md` §6.8 新增"启动段死机判读法");**S13 已完成**(`docs/测试说明/模拟器差距清单.md`,6 网络 + 4 日志配置 + 7 硬件 + 4 有意对齐,含 §3 六条"必须回真机"规则)。§5.1 路线图状态已更新,S7/S11/S9 标注"判据需真机"押后 | -| 2026-10-02 | 实施记录(第 1 波前半):**S8 已完成**(删除孤儿 `crc.c/h`、`ringbuf.c/h`,重跑引用关系图);**S1-a 已完成**(`sys_clock` L0.5 单测 20 用例 / 100 断言点 + 钩子 `sys-clock-unit-test`;**首跑抓出 Zeller 星期缺陷并修复**,Flash −112 B)。§3.1 S1-a 步骤表与 §5.1 路线图已更新状态 | -| 2026-10-02 | 实施记录(第 1 波后半):**S3 已完成**(CI 两 job 产物存档 + 汇总行,零依赖方案);**S14 已完成**(CI 体积门禁盲区显式提示 + `CI配置指南.md` §0.0 声明) | -| 2026-10-02 | 初版:测试体系量化盘点(4 套件 222 断言点 / L0.5 仅 1 模块 / 15 钩子)+ 5 个测试结构性问题 + 7 个架构改进点 + 14 条实施建议(S1~S14)+ 4 波路线图。所有论断基于当日源码实测(孤儿文件核查、断言点统计、CI workflow 走读)。**纯建议,未改任何代码** | +- 本文件**只保留未闭环项**——任一项完成后**直接删除**,**不留变更记录**(历史见 `git log`)。 +- 已完成条目的**证据与结论沉淀到模块文档**,本文件不再重复。 +- 新增发现按既有惯例分流:能立即做的就地登记;需排期的进 `项目审计改进清单.md` / + `待处理内容.md` / `下一步任务规划.md`。 diff --git a/docs/测试说明/实机测试实施指南.md b/docs/测试说明/实机测试实施指南.md index 1bbaf5e7..86562095 100644 --- a/docs/测试说明/实机测试实施指南.md +++ b/docs/测试说明/实机测试实施指南.md @@ -5,7 +5,7 @@ > **已完成事项**(S7、A1~A4)的实施细节与证据**不在本文**,见对应模块文档(§10 状态与去向)。 > **前置铁律**:本文所有步骤**均在接上 MCU / 串口 / ST-LINK 之后**执行; > 无实机时**只读本文,不执行任何烧录**。 -> **上游**:`错误码传播审计.md`(E-xx)· `审查记录/测试与架构改进建议.md`(S7/S9/S11/S5/S4)· +> **上游**:`错误码传播审计.md`(E-xx)· `审查记录/测试与架构改进建议.md`(专项待办)· > `审查记录/下一步任务规划.md`(任务排序)· `待处理内容.md`(登记簿)· > `子系统设计/看门狗设计说明.md`(§10 台架步骤)。 > **约定**:本文事项完成后**先销上游登记**,再在文末变更记录留一行; @@ -191,7 +191,7 @@ $cli = if ($env:DTU_STLINK_CLI) { $env:DTU_STLINK_CLI } else { (Get-Command ST-L > **顺带修复(编排器缺陷,非本次代码引入)**:`run_all_suites.py` 原对所有套件统一 `--forbidden "HardFault;assert;[ERR]"`,而 GD5F 的 `TC-GD5F-704`(CCM 非 DMA 可达缓冲)会**预期**打出 `[ERR][NAND] RX DMA buffer not DMA-reachable ... fallback poll`(用例 PASS)→ GD5F 套件**永远被判 FAIL**(自 `2055de0` 起)。按 `verify_log.py` 的说明改为**按套件覆盖 `--forbidden`**(GD5F = `HardFault;assert`),修复后 PASS。 > **来源**:`错误码传播审计.md` 第二档 #1(E-10/E-42/E-53/E-54/E-56/E-57); -> `测试与架构改进建议.md` §2.3 / §4.2 四要素骨架。 +> `测试与架构改进建议.md` §3.1 四要素骨架(设计基线)。 **现状证据**:`ch395f.c`(1330 行) 与 `gd5f2gq5ue.c`(1496 行) 是同代驱动,但**只有 CH395F** 形成了"分类 + 传播 + 审计"完整体系;GD5F 错误处理散落各函数,`Trap_Records` 仅 198 行。 @@ -432,13 +432,13 @@ CH395F / STORAGE / GD5F,未含 RTC;怀疑 RTC 构建下 `testTask` 不被创 | 本文项 | 上游文档 | 下游落地文档 | |---|---|---| -| S7 自检判定 | `审查记录/测试与架构改进建议.md` §3.7 | `测试说明/自动化测试流程.md`(参数已落地,`4a4cd29`) | +| S7 自检判定 | —(原复审项,已闭环 2026-10-02) | `测试说明/自动化测试流程.md`(参数已落地,`4a4cd29`) | | V2/V4/V5 | `子系统设计/看门狗设计说明.md` §10 | `子系统设计/看门狗设计说明.md` 变更记录(2026-10-03) | -| S11 堆告警 | `测试与架构改进建议.md` §4.4 | `内存管理/RAM使用分析与优化方案.md`(基线更新) | +| S11 堆告警 | —(原复审项,已闭环 2026-10-03) | `内存管理/RAM使用分析与优化方案.md`(基线更新) | | S9 GD5F | `问题记录/错误码传播审计.md` 第二档 #1 | `问题记录/GD5F2GQ5UE_Trap_Records.md`、`测试说明/GD5F2GQ5UE_Test_Guide.md` | | N-2 日志静默 | `待处理内容.md` N-2 | `问题记录/错误码传播审计.md` E-02 | | P1-8 网络重建 | `审查记录/项目审计改进清单.md` P1-8 | `网络/Net层阻塞模型架构设计.md` | -| S5/S4 | `测试与架构改进建议.md` §3.4/§3.5 | `测试说明/测试开发标准流程.md` | +| S5/S4 | —(原复审项,已闭环 2026-10-03) | `测试说明/测试开发标准流程.md` | | 模拟器差距 | `测试说明/模拟器差距清单.md` §3 决策规则 | 本文为"需回真机"项的执行面 | > **模拟器差距清单 §3 的六条"必须回真机"规则**决定"哪些改动**必须在本规划内验证—— diff --git a/docs/测试说明/模拟器差距清单.md b/docs/测试说明/模拟器差距清单.md index 1f0736d7..05d8f79b 100644 --- a/docs/测试说明/模拟器差距清单.md +++ b/docs/测试说明/模拟器差距清单.md @@ -3,7 +3,7 @@ > **用途**:把散落在各子系统文档里的"模拟器无法建模 X"汇成**单一事实源**,回答一个关键问题—— > **模拟器回归通过 ≠ 真机通过,那么差在哪?差多少?哪些必须真机补测?** > **性质**:**登记册**(living document)。新增模拟器后端差异时必须在此登记一行,不得只写在别处。 -> **上游依据**:`审查记录/测试与架构改进建议.md` §2.8 / §4.6(S13)、`审查记录/下一步任务规划.md` §3.1(N-4 二期门禁)。 +> **上游依据**:`审查记录/下一步任务规划.md` §3.1(N-4 二期门禁)。 > **相关**:`UI设计/LVGL模拟器部署与使用说明.md`(部署与使用)、`simulator/README.md`(目录与架构)、 > `测试说明/测试开发标准流程.md`(L3 门禁在测试分层中的位置)。 diff --git a/docs/测试说明/测试开发标准流程.md b/docs/测试说明/测试开发标准流程.md index 3ce0b467..25a879ad 100644 --- a/docs/测试说明/测试开发标准流程.md +++ b/docs/测试说明/测试开发标准流程.md @@ -232,7 +232,7 @@ static void phaseN_run(void) { **解决的问题**:测试套件的**构建可编译性没有独立门禁**——产品构建 `@build` 天天跑, 但测试构建可能长期"编不过"而无人知,直到某次要用才发现(2026-08~09 CH395F 套件 -7 个配置实例,见 `测试与架构改进建议.md` 问题 T-4)。 +7 个配置实例)。 ```powershell & '.venv\Scripts\python.exe' test/run_all_suites.py --check-build diff --git a/docs/测试说明/系统时钟单元测试说明.md b/docs/测试说明/系统时钟单元测试说明.md index 52b5348d..9e89cdda 100644 --- a/docs/测试说明/系统时钟单元测试说明.md +++ b/docs/测试说明/系统时钟单元测试说明.md @@ -8,8 +8,7 @@ > **关联文档**: > [`../../AGENTS.md`](../../AGENTS.md)(平台约束)、 > [`测试开发标准流程.md`](测试开发标准流程.md)(测试分层与门禁总览)、 -> [`看门狗单元测试说明.md`](看门狗单元测试说明.md)(同层范本)、 -> [`../审查记录/测试与架构改进建议.md`](../审查记录/测试与架构改进建议.md)(S1-a) +> [`看门狗单元测试说明.md`](看门狗单元测试说明.md)(同层范本) --- diff --git a/docs/测试说明/配置文件单元测试说明.md b/docs/测试说明/配置文件单元测试说明.md index 7ebc38e2..27201587 100644 --- a/docs/测试说明/配置文件单元测试说明.md +++ b/docs/测试说明/配置文件单元测试说明.md @@ -10,8 +10,7 @@ > [`测试开发标准流程.md`](测试开发标准流程.md)(测试分层与门禁总览)、 > [`看门狗单元测试说明.md`](看门狗单元测试说明.md)、 > [`系统时钟单元测试说明.md`](系统时钟单元测试说明.md)(同层范本)、 -> [`../子系统设计/配置文件设计说明.md`](../子系统设计/配置文件设计说明.md)(模块设计)、 -> [`../审查记录/测试与架构改进建议.md`](../审查记录/测试与架构改进建议.md)(S1-b) +> [`../子系统设计/配置文件设计说明.md`](../子系统设计/配置文件设计说明.md)(模块设计) ---