test(APP/104): iec104_serv 模块(28 条,84.2%)——P1 十三块全部进分母

P1 收官批次。新增 test/host/iec104_serv/,iec104_serv.c 覆盖 117/139 = 84.2%。
做法:把生产源整个 #include 进 TU,直接驱动它的 7 个 static 辅助函数——**不跑
iec104_serv_task 的无限主循环**(那是 never-returning 的任务体,不适合单测;事件组
相关支路用 sys_evtHandle=NULL 走"不可用"分支)。链路层与 pts 层给假件:链路协议本身
有 test/host/iec104 的 97 条专测,本模块只管服务怎么活、怎么死、怎么重生。

钉住的契约:
- 建监听:2404 端口过 htons、绑 NET_INADDR_ANY;socket/bind/listen 任一失败都要把
  已经拿到的号关掉(漏关就是每次重试漏一个 socket 号,CH395F 只有 8 个)。
- **网络代次一变就拆掉旧连接与旧监听并重建**:现场改 IP 后端口必须在新地址上继续
  服务,这条不重建就是"设备换了 IP 之后 104 主站再也连不上"。
- accept 门控:监听未就绪不 accept;表项已失效时把监听号丢掉、下一轮重建。
- 单连接 socket 的断线语义:对端 FIN 时只关数据口,**监听必须留着**;数据号与监听
  号同一个时绝不关(关了就端口失联)。recv 返 -1 且 errno 是 WOULDBLOCK 是正常空转,
  不能当成错误丢连接;真错误则丢弃并**当场重建监听**,不留空窗。
- 判死用**连续**短写计数:一次成功即复位。写成累计就会把偶发拥塞误判成断线,主站
  看到的就是莫名掉线(连到 3 次才判死,I104_SEND_FAIL_MAX)。
- 32 分频探活:无可读数据时不是每轮都去收(省 CPU 预算),到点才收一次;对端 FIN 后
  复位计数器。协议状态掉回 IDLE 也要丢并重生(否则服务挂着但永不服务)。
- 时钟桥接两个溢出边界:epoch 秒超过 32 位一律拒绝且不碰时钟(截断写进 RTC 就是乱值);
  未对时(sys_clock_get 返回 UINT32_MAX)回 UINT64_MAX 哨兵而不是 0(否则对端会以为
  设备时间是 1970-01-01)。

主机侧兼容处理(写进桩注释):真实 App/util/sys_clock.h 会 include sd2506.h(RTC 芯片
驱动)一路拉到 HAL,PC 上编不动,故桩掉 sys_clock.h 但**两个签名与真实头逐字一致**;
104 的头散在 App 与 Drivers/BSP 的多个子目录(iec104_serv.h/iec104_link.h/iec104_pts.h/
task_watchdog.h/sys_events.h/prot_set.h/sd2506.h…),逐个手写 include 目录必漏且报错会
落在被测源上、极易误读成生产代码坏了——运行器改为**按目录实际是否含 .h 自动收集**。

自查:初跑红 2 条,都是我的场景问题(分频到点那轮 recv 返回 0 会走对端 FIN 支路,
应给 -1 + WOULDBLOCK;"重建失败丢监听号"必须同时让表项失效),改正后 28/28。

P1 收官合计:12 个新模块、**308 条断言**;套件 16 → **28** 全绿;files_tested 19 → **32**;
未触及 .c 47 → **34**;恒等式 34 = 0(P1 剩) + 5(P2) + 29(P3);分母 2,893 → **4,684** 行;
line=4020/4684(85.8%)、func=300/333(90.1%)。百分比"降"是摊薄不是回退(lftpd.c 64.3%、
lftpd_io.c 53.5% 这类只测主体的文件也进了分母),按 §5 口径该盯 files_tested/files_untested。
亮点:lftpd_inet.c **127/127 = 100%**、net_listener.c 99.2%、net_events.c 97.8%。
编号补建 TC-HOST-I104SRV(游标 041),TC-HOST 族数 20 → 22。§9 的落地进度表补齐并
统一为 NET 拆出的四块 ①②③④。

自查门禁:test-cppcheck Passed;check_ids --strict 通过;doc-links 35 处(= 存量);
10 个新增/改动文件 BOM/CR/U+FFFD/行尾逐项净。零生产代码改动,未跑 @build。
--all-files 仍只有 mem-numbers / mem-budget(.map 台账漂移,等 Lead 合并后统一回填)
与 ui-golden(ref/*.bin 不入库)三条失败,与本分支改动无关。
This commit is contained in:
2026-10-23 23:29:58 +08:00
parent a0c261b5ee
commit 957ec64645
10 changed files with 784 additions and 19 deletions
@@ -226,22 +226,25 @@
`test/host/lftpd`(35 条) | `lftpd_inet.c` **127/127 = 100%**、`lftpd_io.c` 85/159、
`lftpd.c` **304/473 = 64.3%**(控制通道闸门 + 17 条命令 + 被动模式 + 传输收尾都进来了;
还剩 `lftpd_start` 主循环与 `send_list`/`send_file` 的分批边界分支) |
| NET 四块之一:`net_select.c` | ✅ 完成 | `test/host/net_select`(21 条) | `net_select.c` 97/114 = **85.1%** |
| `lftpd.c` / `lftpd_inet.c` | ⬜ 未开始 | — | 需假 `net_socket` 层(`time_sync`/stub 那份可扩展) |
| NET 三块之一:`net_listener.c` | ✅ 完成(**125/126 = 99.2%**) |
| NET 拆出的四块 ①:`net_select.c` | ✅ 完成 | `test/host/net_select`(21 条) | `net_select.c` 97/114 = **85.1%** |
| NET 拆出的四块 ②:`net_listener.c` | ✅ 完成(**125/126 = 99.2%**) |
`test/host/net_listener`(34 条)+ 共享假芯片 `test/host/common/ch395f_fake.{h,c}` |
四道时间闸门全钉住(30ms 退避 / 400 与 100ms 两级沉降 / 800ms 上限只留痕 / 2000ms
预算交对账器),另有接收区 256B 钳位与 16 轮上限、"清接收→关断→读即清→读状态"的
调用顺序、五种静默放弃条件 |
| NET 三块之二:`net_pending.c` | ✅ 完成(**169/194 = 87.1%**) | `test/host/net_pending`(43 条) |
| NET 拆出的四块 ③:`net_pending.c` | ✅ 完成(**169/194 = 87.1%**) | `test/host/net_pending`(43 条) |
跨上下文契约(**错误码走任务通知值**,靠可观测的 `xTaskNotify` 假件才测得到)、
收尾四条分支(不丢尾包 / 干净报 0 / 异常释放报 CONNRESET / 槽位没了报 CONNRESET)、
发送分块钳在 `NET_SEND_CHUNK_MAX=1024`(真值,非假件自造)、`deadline` 每轮续期
(长文件不被 1000ms 掐死)、停滞超时、取消与 `clear` 不发通知的纪律 |
| NET 三块之三:`net_events.c` | ✅ 完成 | `test/host/net_events`(34 条) | 陈旧中断防误判 |
| NET 拆出的四块 ④:`net_events.c` | ✅ 完成 | `test/host/net_events`(34 条) | 陈旧中断防误判 |
(芯片说还在连接就不许拆活会话)、疑似误报延后、超时标异常释放(正是 `net_pending`
收尾报 CONNRESET 的那条输入,**跨文件因果两头都能离线跑才测得到**)、丢中断自愈,
以及对账器的 100ms 周期闸门 / 稳定计数 4·2·1 三档 / 1000ms 冷却 / U11 重同步 |
| 104 服务层:`iec104_serv.c` | ✅ 完成(**117/139 = 84.2%**) | `test/host/iec104_serv`(28 条) |
服务生命周期:代次一变就重建监听、accept 门控与表项失效自愈、单连接断线只关数据口
(与监听同号时绝不关)、连续短写判死(成功即复位)、32 分频探活、协议掉回 IDLE 丢弃
重生、时钟桥接两个溢出边界(>4G 秒拒绝、未对时回哨兵) |
本批照出的两处**现状**(只登记、**未改语义**;全库唯一调用点 `test/board/net_test_task.c`
传的是 NULL 与非零 tv,今天没有受害者):
@@ -267,6 +270,17 @@
风险在"以后有人拿它去尾部空白"。修它一行即可,但会改变 FTP 参数(含空格的文件名/
口令)的可见行为,需与 FTP 域一起过 → 现在只用 TC-LFTSTR-005 把现状钉成"改了会响"。
**P1 收官(2026-10-23 同日,13 个文件全部进分母)**:新增 12 个主机单测模块
(`net_cfg` / `ftp_cfg` / `prot_set_store` / `task_prof` / `lftpd_io` /
`net_select` / `net_listener` / `net_pending` / `net_events` / `lftpd_inet` /
`lftpd` / `iec104_serv`),共 **308 条断言**;套件 16 → **28** 全绿,
`files_tested` 19 → **32**,未触及 `.c` 47 → **34**(恒等式 `34 = 0(P1) + 5(P2) + 29(P3)`),
分母 2,893 → **4,684** 插桩行,`line=4020/4684(85.8%)`、`func=300/333(90.1%)`。
百分比从 88.9% 一路"降"到 85.8% —— **这仍然是摊薄不是回退**:`lftpd_io.c`(53.5%) 与
`lftpd.c`(64.3%) 这类只测了主体的文件也进了分母;按 §5 的口径,这一路该盯的是
`files_tested`(19→32)与 `files_untested`(125→112),而不是 `line_rate`。
其中 `lftpd_inet.c` 达到 **100%**(127/127),`net_listener.c` 99.2%、
`net_events.c` 97.8%。
### 三处死路径的处置(2026-10-23,用户逐条裁定后实施)
三处都不是改动引入的回归,而是**从没被执行到的分支**:生产路径上要么没有调用者、