test(host): P2 覆盖率批次,5 套主机单测与共享假 FatFs

P2 五个文件全部进分母(三名队友并行,写范围互斥、零越界):
  net_socket 420 条 / 881-883=99.8%、52-52 函数(S1~S6 分片)
  sys_log_mcu 75 条 / 310-313=99.0%、12-12
  console_log 104 条 / 188-189=99.5%、15-15
  cfg_file_store_mcu 34 条 / 97-97=100%、10-10
  soe_store 26 条 / 78-79=98.7%、4-4
合计 659 条断言,P2 本体 1554-1561=99.6%、93-93 函数。

账:套件 28→33 全绿;files_tested 32→37;未触及 112→107(.c 34→29,
恒等式 29 = 0(P1剩) + 0(P2) + 29(P3) 咬合,§9 终点判据达成);分母
4684→6245 插桩行;line 85.8%→89.3%;func 90.1%→92.2%(这批 5 个文件
全都以 ≥98.7% 进分母,故未摊薄)。--update-baseline 前后逐文件比对:
下降 0 个文件。

基建:新建 test/host/common/fatfs_fake.{h,c}(以真实 ff.h 为声明源、只
定义三文件实际调用的 9 个 f_*,缺哪个链接期响;卷忙/短写/FR_DENIED
可注入;短名违规分档);扩展 ch395f_fake 为纯新增(g_fx/g_fxchip 另开
一份世界 + ext_reset 挂进既有复位,既有三套面一字不动),并按真头修正
两个错值常量(SOCKET_OPEN 0x01→0x05、TCP_ESTABLISHED 0x05→0x04),
另在 net_socket runner 加真头/假件同名常量文本核对闸(负例验证过)。
存量回归:net_listener 34、net_events 34、net_pending 43、net_select 22、
nand_port 149、lftpd_io 33、vol_lock 33、soe 32、cfg_file 151 全绿。

编号:补 TC-HOST-{NETSOCK,SYSLOG,CFGFST,SOESTORE,CONLOG} 五族(22→27,
先补族再引号);net_socket 模块码由自取的两字母 NS 正名为 NETSOCK;
id-check 增量拦截无台账外新账,可判定比例 83.27%(基线 82.6%)。

文档:覆盖率口径 §9 新增「P2 落地」(含两套分母口径差、10 条现状表、
12 条可复用手法与并发红线),§8 第 1 条改「清单已吃穿」,补变更记录。
本提交未改任何生产代码(App/Drivers/Src/Lib/MDK-ARM 零触碰)。

10 条现状已逐条登记在 §9(含 sys_log_mcu.c:509~537 卷忙整批丢日志、
net_socket.c 同文件两套 IP 字节序、UDP 两 API 绕过 netTask),已获指示
全部修复,另开一轮(含测试字面量统一纯 ASCII)。

gate: pre-commit run --all-files → 39 hooks, 38 Passed; only mem-numbers
Failed(本工作树从未 @build,无 .map 事实源,属分支期已知陷阱);
mem-budget Passed;test-coverage 与 id-check Passed。RAM/Flash 增量 = 0
(纯测试批次)。按仓内分支约定走 --no-verify,台账回填留待主树合并后。
This commit is contained in:
2026-10-24 17:54:02 +08:00
parent 4ac869058d
commit 794906cdc8
46 changed files with 10821 additions and 39 deletions
@@ -208,10 +208,13 @@
**下一步(按性价比排序)**:
1. **扩大覆盖面**:按 **§9 的建桩优先级清单**吃。**P1 已吃完**(2026-10-24 合入主干,13 个 .c 全部
进分母,`files_tested` 19 → 32);下一步是 **P2 的 5 个 / 2,226 行**(`net_socket.c` 1,251 行需分片、
`sys_log_mcu`+`cfg_file_store_mcu`+`soe_store` 共用一套假 FatFs、`console_log`)。P3 的 29 个 /
4,814 行**刻意不做**并已写明理由。比把已覆盖文件从 85% 推到 95% 值钱得多。
1. **扩大覆盖面**:按 **§9 的建桩优先级清单**吃 —— **清单已吃穿**:P1 13 个 `.c`(`files_tested` 19 → 32)
与 P2 5 个 `.c`(`files_tested` 32 → **37**,实测见 §9「P2 落地」)都已进分母,**剩下的 29 个 `.c`
全部是 P3「刻意不做」**、每条都能在 P3 表里找到归属(本节终点判据就此达成)。于是这一条**没有下一批可排**,
只剩两类事:① 把 §9 登记为本批「现状」的条目按用户裁定落成 fix(体量最大的一块是
`App/log/sys_log_mcu.c` 卷忙时整批丢弃日志);② 真想把百分比再往上推,就只能回头补**已进分母文件**的
未测分支(缺口最大的三块:`lftpd_io.c` 53.5%、`net_pending.c` 87.1%、`lftpd.c` 64.3%)——
按 §5 那仍是自分数、不是 KPI,性价比低于 ①。
2. **不做**:第二条覆盖率通道(gcc/WSL、`dotnet-coverage`、OpenCppCoverage 三条路都不走,用户裁定
2026-10-23)。要重开这个裁定,前提是能同时给出"两份分母怎么对齐"的答案,否则只是多一本对不上的账。
@@ -415,6 +418,137 @@ uint16_t ch395f_link_change_count(void);
3. **`console_log.c`(265)** —— 单写者契约 K1/K2 已经写成表,照抄成用例最省力:
`docs/子系统设计/控制台单写者日志设计说明.md`。
### P2 落地(2026-10-24,5 个文件全部进分母,套件 28 → 33 全绿)
三个模块码并行落地(`net_socket` 分片吞、存储三件套共用新建假 FatFs、`console_log` 独立),
**新增 5 套 659 条断言**:`net_socket` 420、`console_log` 104、`sys_log_mcu` 75、`cfg_file_store_mcu` 34、`soe_store` 26。
| 新纳入分母的 `.c` | 插桩行 / 函数 | 断言 | 套件目录 |
|---|---|---|---|
| `net_socket.c` | **881/883 = 99.8%**、52/52 | 420 | `test/host/net_socket/` |
| `sys_log_mcu.c` | **310/313 = 99.0%**、12/12 | 75 | `test/host/sys_log_mcu/` |
| `console_log.c` | **188/189 = 99.5%**、15/15 | 104 | `test/host/console_log/` |
| `cfg_file_store_mcu.c` | **97/97 = 100%**、10/10 | 34 | `test/host/cfg_file_store_mcu/` |
| `soe_store.c` | **78/79 = 98.7%**、4/4 | 26 | `test/host/soe_store/` |
| **P2 合计** | **1,554/1,561 = 99.6%**、93/93 | 659 | — |
**账**:套件 28 → **33** 全绿、`files_tested` 32 → **37**、未触及 **112 → 107**(`.c` **34 → 29**、`.h` 仍 78)、
分母 4,684 → **6,245** 插桩行,`line=4020/4684(85.8%)` → **`5574/6245(89.3%)`**、
`func=300/333(90.1%)` → **`393/426(92.2%)`**。恒等式跟着更新且仍然咬合:`29 = 0(P1 剩) + 0(P2) + 29(P3)`,
并且**本节终点判据(下面「顺序建议与记账」第 3 条)就此达成**:剩下的 29 条 `.c` 逐条都能在 P3 那张表里找到归属
(芯片驱动 5 + UI 17 + 任务编排 6 + 内核前装配 1 = 29),没有一条是"既没进分母、又说不清为什么不测"的真窟窿。
⚠ **这批罕见地没有摊薄、反而 +3.5pp** —— 不是因为更勤奋,而是 5 个文件**全都以 ≥98.7% 进分母**
(P1 那批是 `lftpd_io.c` 53.5%、`lftpd.c` 64.3% 这种"半份进分母")。**别把它读成"百分比才是 KPI"**:
按 §5 口径这批该盯的仍是 `files_tested` 32→37 与未触及 `.c` 34→29 这两个数,89.3% 只是自分数。
**两套分母别再混用**(写给下次估工作量的人):本节清单里的"有效代码行"是**去注释/去空行**的静态计数,
插桩分母只数 mscoverage 认为可执行的行——`net_socket.c` 清单 1,251 / 插桩 **883**、`sys_log_mcu.c` 458 / **313**、
`console_log.c` 265 / **189**、`cfg_file_store_mcu.c` 129 / **97**、`soe_store.c` 123 / **79**。
按 1,251 估进度会把 99.8% 读成 70.4%。另:`for (;;)` 的收尾大括号会被算进可执行行
(`soe_store.c:202` 就是这种非真语句),别当成漏测去"补"。
**画像期两处口径被落地推翻**(就地更正,不回改上文原话):① "⚠ **没有 `ch395f.h`**——NET 域那批必须新建它"
已由 P1 建好(`test/host/net_listener/stub/ch395f.h` 转发到共享假芯片);② "FatFs 侧**不建假头**,沿用真实
`ff.h` + 测试单元内假 diskio"——P2 走的是**另一条并存的路**:`test/host/common/fatfs_fake.{h,c}` 以真实
`Lib/FatFs/ff.h` 为**声明源**、在 `fatfs_fake.c` 里**定义** `f_*`(签名一漂移就编译期报错),
提供内存卷 + 可注入返回码/短写/`FR_DENIED`。两条路的共同点是**都不自造假头**。假件**刻意只提供三个生产文件
实际调用的那 9 个 `f_*`**(`f_close f_lseek f_mkdir f_open f_read f_rename f_stat f_unlink f_write`),
不给 `f_mount/f_mkfs/f_opendir/f_readdir/f_sync` —— 于是"自愈逻辑把名字/参数类错误码升级成卷操作"这类违规
会在**链接期**就响,而不是靠评审眼神(`docs/架构设计/STM32F4-Base存储架构说明.md` §10.1 陷阱二的机器化兜底)。
**编号**:`TC-HOST-*` 族 22 → **27**(本批补 `NETSOCK`/`SYSLOG`/`CFGFST`/`SOESTORE`/`CONLOG`,
游标分别 `TC-NETSOCK-545`/`TC-SYSLOG-100`/`TC-CFGFST-053`/`TC-SOESTORE-024`/`TC-CONLOG-135`,
属主登记处 `test/host/<目录>/*`,`cursor_owner_only`/`allow_orphans`/`subitem_ok` 照 B 层口径)。
**先补族再引号**这条链本批又走了一遍,而且踩在收口前:`net_socket` 起初自取两字母码 `TC-NS-*`(465 处),
那会整批落进通用 `TC` 族(属主 `docs/测试说明/*_Test_Guide.md`、**无** `allow_orphans`)
= 458 条台账外新账直接拦提交;正名为 `TC-NETSOCK-` 是 bytes 级替换(465 处 / 0 残留 / 兄弟目录 `git diff` 全空 /
复跑 420 全绿)。**写死成纪律**:队友起手就按 Lead 给的模块码写号,两字母不可检索的码(`NS`)不要取。
本批**未**给新套件单挂 `*-unit-test` 钩子(与 P1 那 12 套同口径):聚合闸 `test-coverage` 的触发面
`^(App/|Drivers/BSP/|test/host/|…)` 已自动发现新模块并按基线守回退。
**假件常量必须与真头逐字对齐(本批最值钱的机制结论)**:共享假芯片里 `CH395F_SOCKET_OPEN` 曾是 `0x01U`
(真头 `Drivers/BSP/CH395F/ch395f.h`:220 = **`0x05U`**、`docs/芯片手册/CH395F.md`:527)、
`CH395F_TCP_ESTABLISHED` 曾是 `0x05U`(真头:229 = **`0x04U`**、手册:539)。
**三套存量单测一条都没红**——host 编译里生产码看到的也是假宏,宏对宏自洽 → 错在"值层",
表现正是"把断言写在假件规则上而门禁毫无反应"。修法不是改断言而是**加机械闸**:
`test/host/net_socket/run_unit_test.py` 在编译之前做真头/假件同名 `CH395F_*` 常量的文本核对
(真头 106 项 / 假件 32 项 / 同名 22 项,只比纯字面量取值),并做过负例验证:把值改回 `0x01U` →
输出 `[DRIFT] CH395F_SOCKET_OPEN 真=0x05 假=0x01` 且 rc=1 拒绝编译。跨头 `_Static_assert` 做不到,
文本核对是目前唯一能常驻的拦法。**推论**:任何"照抄真头常量"的共享假件都该配这样一根闸。
**两条 FatFs 实证陷阱都做了正反两面**:`FA_OPEN_APPEND` 不含 `FA_WRITE`(缺写位 → `f_write` 恒
`FR_DENIED` 的对照保真断言 3 处)与"`FR_OK` + 短写"判失败必须校验写回字节数;短名违规**分档**
(体 > 8 / 扩展 > 3 / 非法字符 / 以点开头 → `FR_INVALID_NAME`;含 `:` → `FR_INVALID_DRIVE`),
`FF_USE_LFN=0` 下 `.fschk` 走的正是"扩展域 5 字符超限"这条真实路径;自愈只动自己的文件
(反复失败 20 轮后 `/log` 目录与邻居文件未被触碰)。
**本批照出的现状(只登记、未改生产码;逐条已上报用户待裁定)**:
| # | 位置 | 现象与影响 | 钉住它的用例 |
|---|---|---|---|
| 1 | `App/log/sys_log_mcu.c:509~537` | **疑似数据丢失(建议按 P1 定级)**:批量路径 `done = n` 只有 `rc==-3`/`rc==-1` 两个分支会改它,**`rc==-2`(卷忙)没有分支** → 紧随其后的 `for (i = done; i < n; …)` 回投循环一次都不执行 → 最多 8 条日志既不落盘也不回投、不计故障、不打日志。同段 `:531` 注释与 `docs/子系统设计/运行与自检记录设计说明.md` §4.4 都写"条目不丢(旧实现 -2 丢那一条)"→ **现状比旧实现更糟**(旧丢 1 条、现一次丢 8 条)。对照:**逐条慢路径遇 `-2` 会正确回投尾部**,同一错误码在两条路径行为相反。触发面常见:卷锁 v1.2 之后 ftpTask 传文件期间 defaultTask 每 2 s 排空一次 | TC-SYSLOG-035 / 035b / 035c |
| 2 | `App/log/sys_log_mcu.c:527~529` | `rc==-1` 打 `DBG_ERROR("drop %lu entries …")`,但该分支 `done=0` → 整批其实**全部回投**、一条没丢 → 文案与行为相反,且持续写故障时每轮 drain 都重投重试一遍 | TC-SYSLOG-036 |
| 3 | `App/log/sys_log_mcu.c:616` vs `627~628` | 守卫写成 `desc_len <= SYSLOG_DESC_MAX` 就 `-1`,而 `dl` 恒 ≤ 28 → 截断分支**永不可达**。无害(宁可拒绝不越界),但"传小缓冲能拿到截断内容"的直觉是错的 | TC-SYSLOG-098 / 057 |
| 4 | `Drivers/BSP/NET/net_socket.c:2825` / `:2869` / `:3080` | **同一文件两套 IP 字节序约定,`inet_addr` 与 `ntoa`/`getsockname` 不互逆**:`net_inet_addr` 小端组装后又 `NET_HTONL` → `0xC0A80164`;`net_inet_ntoa` 交换后高段在前 → 只能正确还原 `getsockname` 那族(`0x6401A8C0`);`net_connect_locked:1571` 用 `>>24` 取首段 → 只配 `inet_addr` 那族。**为什么长期不被看见**:`ntoa` 自己 `:2865~2867` 那段 E13 修复注释里的例子「192.168.1.100 → 0xC0A80164 → "192.168.1.100"」取的是**另一族**的值(交换宏下实算 `"100.1.168.192"`)。前提已核清:`Drivers/BSP/NET/net_types.h`:56 是 `#ifdef __BIG_ENDIAN` 才恒等,STM32 与 x86/MSVC 都走交换分支 → **不存在"主机语义≠目标语义"**。当前无调用者跨族(`lftpd.c`:864 走 `getpeername→ntoa` 同族恰好正确),属埋在库 API 面上的地雷;统一必须跨 `inet_addr`/`ntoa`/`getsockname`/`connect` 解析/`lftpd.c`:548 一起过 | TC-NETSOCK-100b / 100c / 100d、107f、430h(钉的是**具体数值**,统一时会五条一起红并互相指认,不会被误读成单点回归) |
| 5 | `Drivers/BSP/NET/net_socket.c:2408` / `:2537` | **UDP 两个公开 API 绕过 netTask 串行化**:`net_sendto`/`net_recvfrom` 直接 `net_poll()`(`:2482`/`:2501`/`:2563`)+ `ch395f_write_send_buf`(`:2487`)/`ch395f_read_recv_buf`(`:2575`/`:2604`),**全程不经 `netMsgQueue`** → 与 AGENTS「所有 `net_*` 经 netTask 串行执行」及 `docs/问题记录/CH395F_Trap_Records.md` TRAP-NET-14 正面冲突。全仓无生产调用者(只有 `test/board` 与模拟器)所以未爆。建议:改走消息队列,或在 `net_socket.h` 显式标注"未串行化,仅限 netTask" | TC-NETSOCK-440 |
| 6 | `App/log/soe_store.c` 函数头 ④ | 注释写"打开/写入结构性错误 → `unlink` 重试一次(同 run.log)",实现里 `f_open` 失败**直接 return -1、不重试**(只有 `f_write` 失败才重试);且 `run.log` 侧 `s_append_rec` 的重试码集合还含 `FR_INVALID_NAME`/`FR_NO_PATH` → 两套自愈集合不一致。属"注释夸大实现"还是"实现少一半"待裁 | TC-SOESTORE-022 |
| 7 | `App/cfg/cfg_file_store_mcu.c:50` | 函数头写"未挂载时 `f_open` 返回 `FR_NO_SYSTEM`"——**`FRESULT` 里没有这个码**(`Lib/FatFs/ff.h` 共 20 个码,未挂载实际是 `FR_NOT_READY`/`FR_NO_FILESYSTEM`)。只在注释里不参与编译,危害是后人照它写判据会写出永不成立的分支 | —(注释面) |
| 8 | `App/log/console_log.c:317` vs `:285` | 冷却起点用 `s_con_cool_until = (uint32_t)HAL_GetTick()`,而判据拿 `!= 0U` 当哨兵 → 故障恰好落在 `HAL_GetTick()==0` 那一拍(开机头 1 ms,或 49.7 天回绕到 0)时**冷却等于没记**,下一轮 drain 立刻再耗一次 100 ms 块超时。撞的正是控制台域那条已登记的"背靠背块超时拖长主循环 → 喂狗节奏饥饿"。建议:另用一个"在冷却"标志,别拿 tick 值当哨兵 | TC-CONLOG-081 / 082(按现状钉) |
| 9 | `App/log/console_log.c:394~412` | `con_append` 的**空位判据在 `taskENTER_CRITICAL` 之外**(check-then-act)。生产者按 `docs/子系统设计/控制台单写者日志设计说明.md` §3.1 是"全部任务 + 内核前主线程 + ISR",而同文档 §4.1 的 head/tail 行写作"单生产者/单写者"——**两处口径互斥,后者需要更正**。多生产者可在环 >95% 满时同时过判据 → `head` 各自推进后合计越过容量 → `con_used()` 回绕变小 → 写者少发一段,被覆盖的行**既不打 `[ERR]` 也不进 `dropped`**(静默损坏;索引恒取模,不会越界)。**这条没有用例、只报推断**:单线程宿主构造不出真抢占点(把注入点挂在 `taskENTER_CRITICAL` 上已在区内,不算抢占点) | —(无用例,仅登记) |
| 10 | `App/log/console_log.c:181~183` + `:306~331` | `huart1.Instance == NULL`(HAL 早期 / 断言-故障上报路径的专用短路,返回 `HAL_ERROR`)在 drain 里被当成**僵尸通道**走 `con_uart_reinit()` → `MX_USART1_UART_Init()`(含 MspInit 的 RCC/GPIO 重配)。当前接线顺序(`MX_USART1_UART_Init` 早于 `dbg_console_writer_attach`)挡住了,但"未初始化"与"通道卡死"共用一条自愈支路,attach 时机一旦被提前就会在 HAL 未就绪时重配外设 | TC-CONLOG-103(钉现状) |
**给下一批的可复用手法与坑**(完整清单写在各套件 `.c` 文件头,这里只留跨模块通用的):
1. **测试 TU 里 `#define fputc <改名符号>` 再纳入生产源**:`App/log/console_log.c`:492 的
`int fputc(int, FILE*)` 与宿主 CRT 撞符号(`libucrt.lib(fputc.obj) error LNK2005` + `LNK1169`)。
改名纳入后函数体与行覆盖一字不动;顺带实测确认**宿主 printf 不走用户 `fputc`**(测试输出不会被吞进环形缓冲)。
2. **钩寄存器读点不必改生产码**:判据里的掩码宏是桩宏 → 定义成**函数调用**就钩住每一次 `u->SR` 位读;
`#define DWT (con_dwt_touch(), &g_dwt)` 让 `CYCCNT` 每次访问自走。于是
`CON_TX_TIMEOUT_CYC = SystemCoreClock/10` 这类编译期表达式原样不动,也能把"自旋几次超时"变成可断言量。
⚠ 结构体成员访问本身(`u->SR`/`u->DR`)钩不到,别绕进去。
3. **跑飞保险要挂在被测模块的天然计数点上**:`net_send_locked`/`net_sendto`/`net_recvfrom` 的等待环里
**没有 `osDelay`**,只给 `osDelay` 加保险挡不住这些死循环(实测第一次跑就真卡死)。挂在 `net_poll()`
每轮必读的 `HAL_GPIO_ReadPin`(INT#)上,附带让 `NET_SEND_SPIN_MS` 这类有界自旋预算**第一次可量测**。
同一纪律的另一半:假内核对 `xTaskNotifyWait(portMAX_DELAY)` 泵 50 轮仍无通知就 `exit(2)` 打 `FATAL` ——
它当场抓出一处把 `net_close` 返回语义判错的用例,而不是让门禁卡死。
4. **未插桩全绿、插桩后 `0xC0000005`** 基本都是测试自己的越界读:假 UART 的上线字节日志是二进制缓冲没补
`NUL` 就被 `strstr` 读穿;以及把 192 B 栈数组当 2048 B 源传进 `con_append`(它只钳写入长度、不钳读取范围)。
守则:凡是把缓冲当 C 字符串读的地方先补 `NUL` 或改 `memcmp` 定长比。
5. **注入槽的"未命中"不能用默认值判**:`FF_FAKE_ACT_RC` 恰好 `= 0` = 变量默认值 → `f_open` 误走注入分支、
返回"`FR_OK` 但没建句柄",表现是十几条断言莫名红。要显式 `hit` 标志 + `act` 双条件。同类:假卷锁/注入若按
"第 N 次调用"取值,会被场景前几步消耗掉下标 → 注入看着设了其实没生效;改成"接下来 N 次返回忙"计数器 +
`skip` 参数才能表达"取锁前后各 `f_stat` 一次"这种锁后二次校验分支。
6. **假卷每文件簇池必须大于生产单文件容量上限**(`run.log`/`soe.log` = 1024×64 = 65,536 B)。原来给 2,048 →
"容量满 `unlink` 重建"场景里追加位落在池外、`f_write` 恒 0 字节短写,**轮转分支根本跑不到**(一片红且看不出根因)。
7. **共享调用日志会被长轮询挤掉**:假芯片 `g_chip.log` 只有 96 格,一次 `sock_reset` 的几十轮把更早的顺序信息
挤出可查范围 → 顺序断言假绿/假红。做顺序断言前先 `clear_log`、用 `log_find` 查。
8. **电平型条件位 ≠ 一次性脉冲**:`SINT` 的 `SENBUF_FREE` 在多块发送时从第二块起还要再看到,一次性位图会让
"写完一块再也等不到空闲"变成假死;同类 `rst_keep_open`(造"永远关不掉"的芯片)用来量上层 600 ms 复位收口,
否则那两条超时分支永远量不到。**新旋钮必须默认关**,存量套件行为才逐字不变。
9. **读操作也会占槽**:`net_get_errno()` 内部同样走 `net_errno_slot()`,首次读就分配槽位并进一次临界区 →
"只有写才占槽"这类直觉断言会红。这是真实语义,不是缺陷。
10. **注释与字符串的两颗哑雷**:块注释文本里写 `hton*/ntoh*` 里的 `*/` 会**提前闭合注释**,后面中文裸露成代码,
刷出几十条 `C3872『U+3001 不允许作标识符』`,长得像编码乱码;字符串字面量里内嵌 ASCII 双引号(强调中文词)
→ 字符串提前截断,报一串 `C2143`,根因完全看不出来。要引用一律全角「」。另:用 heredoc /
`python - <<PY` 写 C 文件会吃掉 `\n`、`\xef` 这类转义(落盘成真换行)→ 一律直写文件再编译验证。
11. **共享假件扩展的纪律**(本批验证有效):**只加不改**,且**不往既有状态结构体里加字段** —— 另开
`g_fx`/`g_fxchip` 一份扩展世界,新增 `ch395f_fake_ext_reset()` 由既有 `ch395f_fake_reset()` 末尾调用,
既有三套的复位面与断言面一字不动、新状态也不会场景间残留。改完必跑存量回归(本批 net_listener 34/34、
net_events 34/34、net_pending 43/43、net_select 22/22;nand_port 149/149、lftpd_io 33/33、vol_lock 33/33、
soe 32/32、cfg_file 151/151)。
12. **覆盖率中间产物根目录是固定共享的** `%TEMP%/dtu_test_cov/<模块名>`:并行期每人只跑 `--module <自己模块>`
(子目录互不冲突);**绝不加 `--clean`**(它 `rmtree` 整棵共享根,会把队友正在编译的目录连根删掉,
表现为"莫名其妙的编译失败、重跑就好");**不带 `--module` 的全量闸只在收口时由 Lead 单跑**。
另注:逐套 `run_unit_test.py` 写的是固定 `tempfile/<suite>_obj` 与 `<suite>.exe`,所以**同一套件同一时刻
只能一人跑**(不同套件互不干扰)。
**两处口径问答(免得下次重新推导)**:① 子集数不能和并集基线直接比 —— `soe_store` 单套对
`App/log/soe_rec.c` 只到 78/87(89.7%),而台账是全部 33 套的**并集** 81/87(93.1%);本批
`--update-baseline` 前后逐文件比对**下降文件数 = 0**,所以那不是回退。判据只有一条:全量闸的并集 ≥ 基线。
② `console_log.c` 与 `soe_store.c` 那 1~2 行未覆盖都不是漏测:`console_log.c:352` 是 `snprintf` 溢出后的
钳位(`note[64]`,最长 42 B → 数学上到不了)、`soe_store.c:202` 是 `for (;;)` 收尾大括号。
### P3|**明确不做**(把理由写死,免得下次又被列回待办:29 个 `.c` = 4,814 行)
| 类别 | 体量 | 为什么不做 | 那它靠什么把关 |
@@ -462,3 +596,13 @@ uint16_t ch395f_link_change_count(void);
逻辑度=1−硬件符号密度 / 要桩的外部头 / 可复用现有桩 / fix 提交数),分为 **P1 13 个 2,617 行、
P2 5 个 2,226 行、P3 29 个 4,814 行明确不做**(四条对得上:13+5+29=47、三段相加=9,657);
§8 下一步第 1 条改为指向本节 |
| 2026-10-24 | **P2 落地(三名队友并行,本分支)**:新增 5 套 **659 条断言**(`net_socket` 420 /
`console_log` 104 / `sys_log_mcu` 75 / `cfg_file_store_mcu` 34 / `soe_store` 26),套件 28 → **33** 全绿、
`files_tested` 32 → **37**、未触及 `.c` **34 → 29**(恒等式 `29 = 0(P1) + 0(P2) + 29(P3)`,清单吃穿)、
分母 4,684 → **6,245**、`line` 85.8% → **89.3%**、`func` 90.1% → **92.2%**(这批**没有摊薄**:5 个文件
全都以 ≥98.7% 进分母)。新建共享假 FatFs `test/host/common/fatfs_fake.{h,c}`、共享假 CH395F 纯扩展
(两个与真头不符的常量改成真值 + 加真头/假件常量文本核对闸,负例验证过)。补 `TC-HOST-*` 五族
(22 → 27)与 `scripts/test_coverage_baseline.json` 重登(逐文件比对:下降 0 个)。照出 **10 条现状**
(重点:`sys_log_mcu.c:509~537` 卷忙整批丢日志、`net_socket.c` 同文件两套 IP 字节序、UDP 两个公开 API
绕过 netTask、`console_log.c` 冷却哨兵与空位判据),**生产码一行未改**,逐条登记在 §9「P2 落地」表内待裁定;
§8 第 1 条同步改为「清单已吃穿」 |