docs(门禁): 主机侧单测覆盖面改写成口径陈述,第三处计数不同源一并修
「22 根专属钩子」是**口径**不是缺口:test/host 实测 42 个目录去掉 common/__pycache__ = 40 套带 run_unit_test.py,配置里带 -unit-test 后缀的只有 22 根 ⇒ 其余 18 套 (含 ui_sink、console_log、net_socket、lftpd 三兄弟、sys_log_mcu、iec104_serv、 task_prof…)没有独立 files: 触发面,但每次提交都被 test-coverage 总闸全量真跑, 失败照样拦——执行面无洞的证据核到源码:check_test_coverage.py:134 用 rglob 发现 全部 40 套,:44-46 的退出码语义写死「有单测失败即 1」。缺的只是秒级归因。 补钩子的判据也现成:pre-commit 4.x 移除了 always: true,没有 files: 触发面就等于 永不跑,所以**不能**靠"补第 23 根"解决,要补就得先给该模块划触发面。 顺带查出同类的第三处不同源(也在 docs/门禁说明/ 内):host-unit-tests.md 自己还写 着「16 个钩子 / 另有 12 套 / 总闸跑 28 套」,三个数全部过期,已改成 22 / 18 / 40, §1 表补齐 2026-10-25 增补的六行,§6 第 4 条重写成覆盖面台账,§9 留变更记录。 另用 git log -S 核实原句「23 个 / 含 ui-sink 七个」是双重错:ui-sink-unit-test、 iec104-serv-unit-test、console-log-unit-test 在配置历史里**从未存在**过。 页数不变(26 篇),钩子数不变(声明 50 / 实跑 50 / 专属单测钩子 22)。
This commit is contained in:
@@ -160,6 +160,8 @@ test/host/cfg_file/cfg_file_test.c
|
||||
| **TC-CONFIG-053** | 变更后重载 + 挂回调段差异触发回调 | 变更响应 |
|
||||
| **TC-CONFIG-055** | `(size, mtime)` 令牌不变 → 不重载(令牌 = 变更信号) | 令牌契约 |
|
||||
| **TC-CONFIG-054** | poll 检出超长 → 工厂默认 + 重建模板 + 留痕;**不触发回调** | 在线修复通道保留 |
|
||||
| **TC-CONFIG-056** | `[comm]` 段容量钉:段数上限 8、键数上限 16 写进断言(改上限必须同批改本行与用例) | 段/键上限是配置引擎的公开契约,不能靠"跑起来看看" |
|
||||
| **TC-CONFIG-057** | 段/键写满后再新增 → 拒绝保存且原文件不动(满则拒,不是静默截断) | 溢出语义与 TC-CONFIG-043 同源,覆盖 `[comm]` 新增段 |
|
||||
|
||||
**当前结果**:139/139 PASSED(`failed=0`)。
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# 主机侧单测(16 个 `*-unit-test` 钩子)— 同一机制的 16 个实例
|
||||
# 主机侧单测(22 个 `*-unit-test` 钩子)— 同一机制的 22 个实例
|
||||
|
||||
> 一句话:把**不依赖硬件的逻辑层**拿到 PC 上编译并跑断言(每个模块一个 `test/host/<模块>/run_unit_test.py`)。
|
||||
> 它是"回归最快的两层之一"(静态层 + 逻辑层),但**不等于真机正确**。
|
||||
|
||||
- 16 个钩子各有一个 `files:` 白名单(改动被测模块或测试目录才跑)、`pass_filenames: false`;
|
||||
- 22 个钩子各有一个 `files:` 白名单(改动被测模块或测试目录才跑)、`pass_filenames: false`;
|
||||
远端 CI 只跑 L0 静态门禁 + L0.5 主机侧单测(见 `docs/CI/README.md`)。
|
||||
|
||||
## 1. 它在查什么(16 个实例)
|
||||
## 1. 它在查什么(22 个实例)
|
||||
|
||||
| 钩子 | 被测对象 |
|
||||
|---|---|
|
||||
@@ -26,6 +26,12 @@
|
||||
| `iec104-unit-test` | IEC104 协议栈(ASDU / 链路 / 端口表) |
|
||||
| `soe-unit-test` | SOE 动作记录编解码 `App/log/soe_rec.c`、`soe_store.c` |
|
||||
| `nand-port-unit-test` | dhara NAND 移植层 `Drivers/BSP/GD5F2GQ5UE/nand_ftl.c` |
|
||||
| `adc-cal-unit-test` | 通道标定层 `App/algo/adc_cal.c`(2026-10-25 挂闸,下同) |
|
||||
| `prot-dim-unit-test` | 量纲回归:标定 → 相量 → 定值 → 判据链的单位与倍率一致性 |
|
||||
| `meas-unit-test` | L2.5 遥测快照层 `App/algo/meas.c`(200 ms 双区,104 与 UI 共用上游) |
|
||||
| `run-par-unit-test` | 运行参数段 `App/algo/run_par.c`(遥测死区与系数) |
|
||||
| `vol-idle-sync-unit-test` | 卷空闲同步 `App/fs/vol_idle_sync.c`(空闲窗口内的 f_sync 语义) |
|
||||
| `ui-data-unit-test` | UI 数据快照层 `Drivers/BSP/UI/ui_data.c` 与各页写入方 |
|
||||
|
||||
共同机制:`run_unit_test.py` 在宿主编译器上编译被测 `.c` + 该目录的 `stub/` 替身,跑断言并汇总
|
||||
用例数(各模块的 `TC-HOST-<模块码>` 号段见 `docs/编号规范.md`)。
|
||||
@@ -42,7 +48,7 @@
|
||||
| `nand-port`(dhara 移植层两条硬约束) | 149 断言 | 整页扫描的空闲判定(页首 64 B 全 0xFF 而第 1000 字节有数据等形态)+ **与旧算法对照**(旧实现只看页首,同一页答"空闲")。这类缺陷真机极难复现,发作后只表现为"某次复位后丢数据" |
|
||||
|
||||
- 运行器统一用 MSVC `cl.exe`(远端 CI 的 L0.5 层);公共骨架是
|
||||
`test/host/common/unit_test_harness.py` —— **改动这个骨架会同时触发 16 个钩子**
|
||||
`test/host/common/unit_test_harness.py` —— **改动这个骨架会同时触发 22 个钩子**
|
||||
(每个钩子的 `files:` 都把它写进触发面,这是有意的)。
|
||||
|
||||
## 2. 为什么要它
|
||||
@@ -55,7 +61,7 @@
|
||||
|
||||
## 3. 触发面
|
||||
|
||||
- 只有**改动该被测模块或该测试目录**时对应钩子才跑(16 个钩子各管一块,改日志不会触发 16 份编译)。
|
||||
- 只有**改动该被测模块或该测试目录**时对应钩子才跑(22 个钩子各管一块,改日志不会触发 22 份编译)。
|
||||
- 单独跑:`& '.venv\Scripts\python.exe' test/host/<模块>/run_unit_test.py`。
|
||||
|
||||
## 4. 报错怎么读
|
||||
@@ -81,12 +87,25 @@
|
||||
行为不同"。桩签名改了而测试没跟着改,编译器未必报错(尤其桩返回宽类型)。
|
||||
3. `include_dirs` 与源文件清单是**手写**的:新依赖一个目录就会假失败(见 §4 第 2 类),反过来,
|
||||
漏了某个真实依赖可能让测试**编过却少编了一个翻译单元**(少测而不自知)。
|
||||
4. **专属钩子不是唯一防线**(2026-10-24 更正,原写"没有对应钩子的模块 = 零回归",如今不成立):上表 16
|
||||
个模块各有专属钩子;仓库另有 12 套(P1 新增)没有专属钩子,改坏了由 `test-coverage` **总闸**兜
|
||||
住——它跑全部 28 套,并守"已覆盖行只增不减"。总闸的触发面是 `App/`、`Drivers/BSP/`、
|
||||
`test/host/`、覆盖率脚本与基线,**外加门禁自身**(`.pre-commit-config.yaml`)——不带上自身
|
||||
就等于谁改坏这根闸的 `files:` 模式,它就再也不跑(同 `lv-conf-sync`/`bsp-err` 的既有做法)。
|
||||
纯 UI/文档改动不触发它,那时确实没有可回归的行为。
|
||||
4. **专属钩子不是唯一防线,而「没有专属钩子」也不等于「没被跑」**。2026-10-26 按 git 历史与实测重新
|
||||
清点(上一版写「16 个模块有专属钩子、另有 12 套、总闸跑 28 套」——三个数都随 2026-10-25 的
|
||||
六根新钩子与后续新增套件过期了,正是本目录反复在防的"文档与机器面不同码"):
|
||||
- **22** 根专属钩子 = 有独立 `files:` 触发面、需要**秒级归因**的那个子集;
|
||||
- `test/host/` 下带 `run_unit_test.py` 的套件实测 **40** 套(42 个目录,除 `common` 与
|
||||
`__pycache__`)⇒ **没有专属钩子的是 18 套**:`cfg_file_store_mcu`、`console_log`、
|
||||
`ftp_cfg`、`iec104_serv`、`lftpd`、`lftpd_inet`、`lftpd_io`、`net_cfg`、`net_events`、
|
||||
`net_listener`、`net_pending`、`net_select`、`net_socket`、`prot_set_store`、`soe_store`、
|
||||
`sys_log_mcu`、`task_prof`、`ui_sink`;
|
||||
- 这 18 套每次提交都被 `test-coverage` **总闸**真跑:发现规则是
|
||||
`HOST_TEST_DIR.rglob("run_unit_test.py")`(与 `scripts/ci_local.py` 的 L0.5 同一口径),
|
||||
退出码语义写死「0 = 全部单测通过且覆盖率相对基线没回退 / 1 = 有单测失败或覆盖率回退」⇒
|
||||
**失败照样拦提交**。差的只是归因粒度(要在总闸输出里认出是哪套)与耗时(40 套全量比单根慢得多);
|
||||
- 要不要给某套补第 23 根,判据现成:pre-commit 4.x 没有 `always: true`,**该模块有没有自己的
|
||||
`files:` 触发面**决定补了会不会真跑;只为凑数加一根,等于把「快归因面」和「全量把关」两件事混成
|
||||
一件,还要让钩子计数再改一次;
|
||||
- 总闸的触发面是 `App/`、`Drivers/BSP/`、`test/host/`、覆盖率脚本与基线,**外加门禁自身**
|
||||
(`.pre-commit-config.yaml`)——不带上自身就等于谁改坏这根闸的 `files:` 模式它就再也不跑
|
||||
(同 `lv-conf-sync` / `bsp-err` 的既有做法)。纯 UI/文档改动不触发它,那时确实没有可回归的行为。
|
||||
5. 时序与并发不在范围:中断抢占、DMA 完成回调、临界区、真机 tick 抖动都照不到(历史上最贵的
|
||||
几个坑正是这类,见 `docs/问题记录/*_Trap_Records.md`)。
|
||||
6. 断言写的是"作者当时想到的分支":**行 / 函数覆盖有度量了**(`test-coverage` 总闸出
|
||||
@@ -109,4 +128,5 @@
|
||||
|
||||
| 日期 | 变更 |
|
||||
|---|---|
|
||||
| 2026-10-26 | **数字与机器面对齐**(task-18 复核带出):实例 16 → **22**(2026-10-25 由 3528644e / 48bc50e7 / ffd462ef / 8c726986 / 201d7132 / 484d0682 各挂一根,§1 表补齐 6 行);§6 第 4 条「12 套无专属钩子 / 总闸 28 套」按实测更正为 **18 套 / 40 套**,并给出「没有专属钩子 ≠ 没被跑」的证据(总闸 rglob 发现规则 + 退出码语义)与补钩子的判据;顺带纠正 AGENTS 的「23 个 / 含 ui-sink」——`ui-sink-unit-test` 在配置历史里从未存在 |
|
||||
| 2026-10-23 | 首篇逐项文档(16 个实例清单 + 今天两个 runner 需补 include 目录的实测) |
|
||||
|
||||
Reference in New Issue
Block a user