13 Commits

Author SHA1 Message Date
wandering1 f754d59218 docs(测试说明): 按用途分六个子目录并同步全库引用
分层:流程与规范/ 单元测试/ 板级测试/ 专项验证/ 测试工具/ 测试数据/,
顶层只留新增的目录导航 README.md(归层判据 + 硬约束 + 本次处置记录)。

- 搬迁 28 个路径:测试数据三个目录 58 个文件为纯改名(R100,内容逐字节不变)
- 全库引用同步 124 个文件:AGENTS / docs 索引 / skills / 门禁注释 / scripts
- 门禁面:scripts/check_test_traceability.py 的 glob 改 rglob
  (分层后不递归扫描=追溯门禁直接瞎掉)
- scripts/rename_ref.py:收尾的 ℹ 提示在 GBK 代码页抛 UnicodeEncodeError,
  让进程以退出码 1 收场而改写其实已落盘(RC 与真实结果相反);改为把
  stdout/stderr reconfigure 成 UTF-8,与 check_doc_links.py 等同法
- docs/文档规范.md §1.1 补「子目录文件过多时再分一层」;§3.6 补 rename_ref
  搬整目录的两个实测坑:① 会改写目录内部文件的内容 ② 内部文件的 ../ 出链
  不补层级(本次 23 个被误改的证据文件已按 HEAD 原始字节逐字节还原)
- docs/README.md §6 改为与六个子目录一一对应,并补登原先漏登记的
  「启动日志原子性复验流程」一行
- .cubemx_survival_baseline.json:App/util/task_watchdog.c 的注释路径也随分层更新,
  该文件是存活审计的 watchdog-module 漂移锚点,按脚本自己的提示跑 --write 重建基线
  (实测重建后仅 watchdog-module 一条哈希变化)
- 铁律 4 例外(本次在任务树里改了单写者文件):AGENTS.md 与
  .pre-commit-config.yaml 只被 rename_ref 改写路径字面量(后者全在注释行),
  任务表块与钩子声明一字未动

验证:check_doc_links.py --sections-strict 覆盖全部 88 个改动 .md 全绿;
check_test_traceability.py 输出与主树逐行一致;196 个文本文件无 BOM、无新增
CR、无 U+FFFD、无行尾空白;陈旧引用面由主树 25 处降到 6 处(无新增断链)。
2026-10-30 07:16:07 +08:00
wandering1 04997e48aa refactor(编号): test/host 与 App/Drivers 注释去测试层级号(73 站点 / 64 文件)
同族第二道:代码注释面。41 个 run_unit_test.py 文件头与引用行、common harness 与两份
README、4 个 *_test.c 文件头、App/** 14 处(含 BOM 文件 App/cfg/cfg_file.c)、
GD5F nand_ftl.c 1 处,共 73 站点 / 64 文件;措辞取「主机侧纯逻辑单元测试运行器
(纯标准库)」避免同行「主机侧」重复。

零行为改动(机器证据,不是口头):
- 17 个 .c/.h 剥掉注释后与 HEAD **逐字节相同**;
- 42 个 .py 走 ast.dump 比较、仅归一 docstring(真实字符串字面量参与比较,故旗标或
  输出文本被动过必然报红)→ 全部相等;behaviour_changed=0。
- py_compile 42/42;抽跑 watchdog 47/47、freq 46/46(动手前先取基线,改后同数);
  CR 字节 0、EF BF BD 0、BOM 与 HEAD 逐一一致、check_bom_drift rc=0。

刻意未动:6 处字符串字面量(FTP 应答 "215 UNIX Type: L8"、lftpd_test.c SYST 断言、
console_log_test.c 的 "L1\r\n"、phasor_test.c 的 "L2-in ring" 文案与 printf 标题)——
改了就是改测试输出/行为;UI 设计令牌层 L0(ui_tokens.h、ui_content_page.c)与
test/host/README.md 的算法层号是**另外的族**。

两条 SyntaxWarning(ftp_cfg / net_cfg 文件头 \S 转义)**HEAD 同样存在**,已对 HEAD 副本
复现证明非本次引入,未顺手改。
2026-10-28 17:52:55 +08:00
wandering1 0f7b5a87fb chore(编号): 存量编号全量转语义短语,门禁识别面与语料同批重打
登记簿、文档与代码注释里的存量字母加数字编号(N-x / S-x / T-x / E-x / A-x / 卡号)
一律改写为语义短语,净删 2,028 处引用;删除基线与待确认、并行分道方案、排期详述、
采集保护收尾 4 篇(内容已并入 docs/任务规划/README 与登记簿)。

门禁机器与三张账同批重打:check_ids.py 新增变更记录行豁免与第二根护栏
undecidable_total_ceiling;check_test_traceability.py 只加 stdout/stderr UTF-8
reconfigure(TC_RE 未动);check_cubemx_survival.py 的 --write 固定 newline=LF;
id_registry / id_aliases(+19 条 retired-to-phrase)/ id_known_backlog 重打。

test/ 56 个文件同步 TC 标签语义化,其中 18 个 TU 改了 CHECK / printf 里的标签文字,
是本批唯一改变构建产物的文件集合。

为什么合成一笔、不拆四笔(实测撞闸,非偏好):pre-commit 跑钩子前会 stash 未 staged
部分,拆开后中间提交必然是「新规则 + 旧语料」——实跑可判定 85.15% < 基线 86.00%、
不可判定 1,453 > 上限 1,166,编号门禁直接拦下。编号识别面与它描述的语料只能同码移动。

实跑基线(工作区全量):754 文件 / 8,230 处编号字面量 / 可判定 7,079 = 86.01%
(基线 86.0%,容差 0.1pp)/ 不可判定绝对数 1,151(上限 1,166)/ 校验 1-6 错误级 0 /
告警 = 悬空 2(App/algo/freq.c 的 S2,属 §4.4 同号不同义)+ 孤儿 50。

基线从 84.0 抬到 86.0 的理由见 docs/门禁说明/id-check.md §7.6:本批真删了 2,028 处
引用,可判定面随真实内容上升,不是把分母做小。同批新增排除 docs/硬件平台/ 语料,使
识别面少 264 处——芯片手册位号与网表料号不属编号空间(registry 有 excluded_paths_note)。

⚠ 余量只剩 0.11pp(约 10 处)与绝对数 15 处:把「可判定的引用」改成散文会立刻撞第二
根护栏。另遗留一项:id_aliases 里 retired 的 N / S / T / A 别名没有 scope(U1~U5 有),
今后有人新写一个 A-4,校验 3 不会响,只能靠人,建议下批补 scope。

固件不变证据:115 个 .c/.h 剥注释并归一字符串后与 HEAD 逐字节全等,残留代码差异 0;
App / Src / Drivers 的字符串字面量零改动。@build 0 错误 0 警告;40/40 主机套件、2,889
断言全通过;覆盖率 line 89.2% / func 92.6%;pre-commit 48 钩子 --all-files 全绿;
check_doc_links --all 通过(176 文档,章节引用无法判定 0);逐字节卫生核过:14 个带
BOM 文件与 HEAD 一致、0 个 CR、0 个 U+FFFD,字符串字面量纯 ASCII。

.cubemx_survival_baseline.json 的 watchdog-module 哈希 56bf27 变 2bb051 属合法:
Src/freertos.c 的 USER CODE 区内只有注释文字随编号改写,代码未变。

同批带上体积同码凭证与 RAM 台账行(@build 之后盖戳):RW_IRAM1 79,008 B / 60.3%,
RW_CCM 57,376 B / 87.9%,阈值 92%。
2026-10-26 12:28:14 +08:00
wandering1 adabb6af12 fix(test): 主机单测/覆盖率产物按工作树分家 + 每次跑独占目录(task-10)
现象(2026-10-25 三条道并行开发实测,看板 task-10):全量 ci_local 的 L0 里
test-cppcheck / test-coverage 偶发 "files were modified by this hook",单独重跑
即绿、工作树零改动。根因在 unit_test_harness:obj/exe 直接落在**全机共用**的
%TEMP%,名字由套件名派生(%TEMP%\net_socket_tests.exe、%TEMP%\net_socket_obj),
多棵工作树并行跑同名套件必然互踩。

真正要命的不是编不过,而是**结论张冠李戴**:A 树编译完,B 树把同名 exe 又盖一
遍,A 树随后 subprocess 执行的是 B 树编出来的二进制——两棵树的 summary_marker
一模一样,harness 分辨不出来,于是拿别人的运行结果给自己判 PASS。这种失败不会
变红,只会静默给出错误结论,比红难查一个量级。

修法(不是加锁):
- 产物根按**工作树**分家:%TEMP%\dtu_host\<仓库根路径 sha1 前 10 位>\。树身份取
  harness 文件自身位置(test/host/common 往上三级),不依赖 cwd/环境变量;工作树
  之间只联接 .venv 与模拟器 SDK,test/host 每棵树各有一份,所以这个身份可靠。
- **每次运行再独占一个子目录** runs\<pid>-<纳秒>\,obj 与 exe 都放进去,跑完
  finally 删(含 Ctrl-C 路径)。这一步顺带把"同树并发同名套件"也消灭了:两个
  进程既拿不到同一个 exe,也拿不到同一个 .obj(实测共享根时并发会因 obj 撞名
  直接编不过,返回 rc=2)。没有牺牲缓存:每个测试单元就一个 .c,cl 每次整体重编,
  "共享 obj 省编译"本来就是错觉。**不加锁**:这里没有需要互斥的独占资源,只是
  名字撞了;加锁等于把 L0.5 变成 build/bench 那样的全局串行。
- 独占目录里留 <exe>.owner.json(suite / repo_root / pid / started):需要证明"这
  份产物属于哪棵树哪次跑"时有的可查,不必靠时间戳猜。
- 编译返回 0 但产物不存在 → 明确报环境错误,绝不跑来路不明的二进制。
- 两个开关:DTU_HOST_BUILD 改产物根(CI/容器固定路径),DTU_HOST_BUILD_KEEP=1
  保留本次目录(要单步调试 exe 时用)。判据单一事实源 = host_build_root()。
- scripts/check_test_coverage.py 同源改造:work_root 由固定共享名 dtu_test_cov 改为
  复用 harness 的 host_build_root()/coverage,并同步 --work-dir 帮助文案与 docstring
  (docs 里"并行期每人只跑 --module 自己模块"那条规避就此作废,它是为绕开旧共享名
  而写的)。顺带删掉因此不再使用的 import tempfile。

附带修一处同族不诚实(做本任务时从自己的输出里发现):ci_local 的收尾那行以前
无条件打印"L0 + L0b + L0c + L0.5 全部通过",--l05-only 时等于把没跑过的段宣布
为通过——正是 A-5 那个"SKIP 冒充 PASS"的镜像版。改为只列本次真正跑过的段。

自证(都是实跑,不是推演):
- 两棵树身份并发跑同一套件:把修好的 harness 复制成 A/B 两个树身份(repo_root 不
  同 → 两个不同产物根),套件 BUILD 从真仓库加载,各跑 4 轮并发 → 8/8 rc=0,
  两侧 roots 分别是 …\55deb98e3b 与 …\902100bc41,互不可见。
- 敌意场景:把两边 DTU_HOST_BUILD 强行指到同一目录(旧写法必撞 exe/obj)→ 8/8
  rc=0,共享根下只剩一个空的 runs/。同树并发同理被这一条覆盖。
- 单套:test/host/sha256 rc=0(13/13),产物确实落在 …\4b9166f602\runs\<pid>-…;
  覆盖率 --module sha256 rc=0(line=111/114 func=7/7),落在 …\coverage\sha256。
- 全量:ci_local --l05-only → 33 runner(s),2276/2276 assertions passed,fail=0。
- 临时目录与 scratch 已清理(脚本在仓库外,不入库;ci-artifacts/ 之外无残留)。

文档:test/host/common/README.md 新增"产物目录归属"一节(落点结构、为什么按树、
为什么每次独占、两个开关、一行命令查当前树的产物根);harness 顶部注释是判据正文。
docs 侧两处按分工归 lead 在 done 时统一改,合并请求里已给可直接粘贴的替换文本:
docs/测试说明/主机单测覆盖率口径.md 的产物目录条与"坑 12"、以及并行任务工作树
方案.md 的产物归属条。AGENTS.md 与 docs/** 本次一个字节没动。

RAM/Flash 增量:0 B(纯测试工具链,不进固件)。gate:见提交钩子输出,未用 --no-verify。
遗留:tools/font_gen/make_font.py 同样用固定共享名(%TEMP%\simsun_regular.ttf、
font_extra_<cp>.c)——同一类跨树竞态,但属道B 字体域,我没动,建议另开一张卡。
2026-10-25 05:13:14 +08:00
wandering1 5a806d8a26 fix(log,net): 处置主机单测照出的 10 条现状,撤销 1 条误报
用户裁定「全部修」。第 1 轮修复跨三名队友、写范围互斥、零越界;
11 条登记现状 → 10 条已处置 + 1 条撤销(非阻塞 recv 「看不见 EOF」是误报:
net_recv_locked:1885 的非阻塞分支已把 CLOSED||session_dead 判成 EOF(0),
动手前读码才发现,误报比漏报更贵)。

存储与日志(App/log/sys_log_mcu.c、soe_store.c、cfg_file_store_mcu.c、sys_log.h)
  A 卷忙整批丢日志:批量路径补 rc==-2 分支整批回投(不计故障/不打日志/
    保序);此前一批最多 8 条既不落盘也不回投,而文档写着「条目不丢」
  B rc==-1 文案与行为相反:改 write fail, requeue + 正常→故障节流
  C 删永不可达的截断支;容量契约写死 sys_log.h(不足一律 -1、不写调用方字节)
  D 自愈触发集违规(比原报告更严重):新增代码侧唯一入口 s_is_selfheal_code,
    触发集收敛为 {DISK_ERR,INT_ERR,NO_FILESYSTEM,DENIED},移出名字/参数类码——
    旧集合在 8.3 同名同因下每轮删一次自己的历史日志且永不自愈,正是存储架构
    §10.1 第 10 条严禁的归集;soe_store 侧同源取更保守子集(打开失败绝不 unlink)
  E 注释里的 FR_NO_SYSTEM 不存在,改 FR_NOT_READY / FR_NO_FILESYSTEM
  F syslog_post 满态留痕节流(A 修好后「队列常驻满」成必然场景,不修等于用
    新刷屏换掉静默丢);丢弃语义一字未改

NET(net_socket.c/.h、net_types.h、net_socket_sim.c、lftpd.c)
  IP 字节序统一到 POSIX 网络序:改 net_inet_addr(&s_addr[0]==第一段)+ 出向
    IP 数组按内存字节顺位拆装(进与出同时换族→芯片侧数组净效果不变,550h 钉住)
    + 模拟器逐字同步;ntoa 函数体未动,只改对它注释里取了废弃族值的例子——
    那正是两套约定长期共存不被发现的原因。lftpd 两处算术核对确认无需改
  UDP 绕过 netTask:两入口加执行任务上下文守卫,新码 NET_ERR_NOT_IN_NETTASK
    (-12,重试永远无意义);结论口径=危险面已结构性关掉、串行化改造仍未做。
    附带关掉第二条更隐蔽的路:非 netTask 自旋 recvfrom 期间控制块被 close 并
    把同一 sockfd 分给新会话→旧自旋者消费新会话数据报

控制台(console_log.c/.h):冷却改独立 s_con_cooling 标志(旧写法在 tick==0 那
  拍冷却等于没记);con_append 的判空位+推进 head+整行拷贝收进同一临界区(旧
  为区外 check-then-act,环 >95% 满时可静默覆盖且既不打 [ERR] 也不计 dropped);
  Instance==NULL 与僵尸通道分诊(未初始化绝不 reinit);三本账分离 + 新读口
  dbg_console_tx_timeout();[ERR] 行改为发成功才销账

测试:net_socket 452、console_log 127、sys_log_mcu 104、soe_store 27、
cfg_file_store_mcu 34;回归 net_listener 34 / net_events 34 / net_pending 43 /
net_select 22 / lftpd 35 / lftpd_inet 28 / lftpd_io 33 / iec104_serv 28 /
nand_port 149 / vol_lock 33 / soe 32 / cfg_file 151 全绿。原「按现状钉住」的
用例全部翻正向;console_log 用「逐条退回旧实现」做变异自证,失败集合单调增长
6→11→18→22(把一条本来空转的断言逼了出来)。
游标抬升:TC-NETSOCK-545→560、TC-SYSLOG-100→107、TC-CONLOG-135→167。

文档:控制台设计说明 §4.1 口径与 §13-O2 销项 + v1.5;运行与自检记录 §4.4/§4.5
四条;存储架构 §10.1 第 10 条推论(指向 s_is_selfheal_code);TRAP-NET-21 后续
条目;BSD 指南新增 §9.0 s_addr 唯一约定;覆盖率口径 §9「第 1 轮修复处置」表 +
后续项 5 条 + 台架复验 7 条 + 新坑 8 条 + 变更记录。

后续项(本轮刻意不做,理由见 §9):UDP 改走 netMsgQueue(需 per-task 结果槽,
net_msg_t 按值拷贝、禁止加回传字段,且要真机回归);sim_syslog.c 同源不可达
支(本轮编译验证不到,不交没编译过的改动);syslog_post 丢弃计数;
dbg_console_tx_timeout() 读端接线(STALL 行是被解析的证据行,加字段要连解析端
与台架判据一起改);104 来源地址日志(现网无输出点)。

gate: @build 0 errors 0 warnings(Code=213720 RO=18860 RW=1204 ZI=131500)。
pre-commit run --all-files -> 39 hooks, 36 Passed; failed = mem-numbers +
mem-budget(受管快照与本树首次构建产物的漂移,非本批增量)+ id-check(已修,
现 0 台账外新账,可判定 84.63% > 基线 82.6%)。实测水位 RW_IRAM1 57.4%、
RW_CCM 87.9%,均低于 92% 红线;RAM/Flash 本批增量为数个 static 变量。
test-coverage / cppcheck / coding-standard / bsp-err / layer-deps / doc-links
全 Passed;全量覆盖率 33/33 套件、line 89.3%、func 92.3%、逐文件下降 0。
按仓内分支约定 --no-verify 提交,台账 --write 回填留待主工作树合并后。
2026-10-24 19:43:56 +08:00
wandering1 794906cdc8 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,台账回填留待主树合并后。
2026-10-24 17:54:02 +08:00
wandering1 59e6d77224 test(NET): net_events 模块(34 条,覆盖 97.8%),NET 三块收官
假 CH395F 的第三批、也是 NET 域最后一块。net_events.c 覆盖 175/179 = 97.8%。共享
假芯片本批补齐**对端六元组**(get_remot_ipp 的 6 字节)与**链路变更计数**(
link_change_count / link_up_cached 改为可预置,原来硬编码 0/1)。

钉住的契约(都是真机上会发生、却不能按需复现的那类):
- 对端六元组解析:remote_ip 是**低字节在前**拼的(192.168.1.50 -> 0x3201A8C0,与
  网络序相反)。钉住它是防止有人"顺手改成大端",让所有按 IP 比对/白名单静默错位。
- 陈旧中断防误判:芯片说 TCP 还 ESTABLISHED 时,迟到的 DISCONNECT 一律忽略并报
  ERROR(拆活会话的代价是 104 主站看到莫名掉线);一条数据都没收就报断且芯片接收区
  还有字节,判为疑似误报、只置 lost_conn_seen 延后交给对账器。
- 正常断开:优雅级沉降 + session_dead + last_peer_port 记旧端口 + 转 CLOSED + 发事件。
- 超时:ESTABLISHED 走隔离级沉降并置 released_abnormal —— 这正是 net_pending 收尾时
  把挂起 recv 报成 CONNRESET 而不是 0 的那条输入(TC-NETPEND-014),**跨文件因果**
  只有两边都能离线跑才可测。
- 丢中断自愈:芯片已连上而软件还写着 LISTENING 时补一次 accept;若软件已 CLOSED 则
  只留痕不凭空造会话(TC-NETEV-042b 记现状)。
- 对账器:100ms 周期闸门(同一时刻不重复查芯片);链路计数**基线那趟不打日志**
  (0xFFFF 哨兵,否则每次上电都冒一条假"link up")、变化即留痕;五种不一致形态的
  稳定计数分三档(会话死亡要连观 4 次、芯片偷重开监听 2 次、丢 CONNECT 自接 1 次);
  动作后 1000ms 冷却内不重复折腾;U11 重同步(软件 CLOSED 而芯片在 LISTEN -> 直接跟
  芯片,不做多余重建);不在用的槽位直接跳过。

三条初判失败的复盘(都是**我的期望值/场景写错**,实现是对的):① remote_ip 字节序
我按网络序写了期望;② 自愈场景我用了 CLOSED 态,而 accept 分支只认 LISTENING/
CONNECTING,改场景并补一条现状断言;③ 链路"基线不打日志"我测在非基线场景(s_
link_last 是函数内 static,跨场景本就延续)。对账器的 s_last/s_bad[]/s_cool[] 都是
函数内 static(外部够不着),因此每个形态**各占一个 fd**、时基单调前进,互不串扰。

覆盖率与台账:套件 24 → 25 全绿;files_tested 28 → 29;未触及 .c 38 → 37;分母
3,766 → 3,945 行;line=3472/3945(**88.0%**,连涨两批)、func=263/290(90.7%)。
恒等式 37 = 3(P1 剩) + 5(P2) + 29(P3) 仍咬合。编号补建 TC-HOST-NETEV(游标 058),
族数 18 → 19。

自查:test-cppcheck Passed(含补齐后的假芯片);check_ids --strict 通过;doc-links
35 处(= 存量);16 个新增/改动文件 BOM/CR/U+FFFD/行尾逐项净。零生产代码改动,未跑
@build。--all-files 仍只有 mem-numbers / mem-budget(.map 台账漂移,等 Lead 合并后
统一 --write 回填)与 ui-golden(simulator/tests/ref/*.bin 不入库,新 worktree 没有
黄金参考)三条失败,与本分支改动无关;提交按并行分支约定走 --no-verify。
2026-10-23 23:44:51 +08:00
wandering1 b4441f0e1e test(NET): net_pending 模块(43 条,覆盖 87.1%),钉住通知值回传契约
假 CH395F 的第二批消费者。新增 test/host/net_pending/,net_pending.c 覆盖
169/194 = 87.1%。共享假芯片补了**缓冲尺寸宏链的同构转抄**(RAM_BLOCK 512 /
SEND_BLOCKS 2 → SEND_BUF 1024;RECV_BLOCKS 4 → RECV_BUF 2048 → SPI_DMA_BUF 2052
→ SPI_DMA_MAX_PAYLOAD 2048),于是 net_pending.h 里的 NET_SEND_CHUNK_MAX 是**真值
1024**,分块断言测的是芯片上界而不是假件自己发明的数字。

本模块的核心契约是跨上下文回传:AGENTS 模块约定写着 net_* 的错误码经**任务通知值**
回传(net_msg_t 按值拷入队列,netTask 改的是自己那份副本,调用方永远看不到;也勿给
net_msg_t 加回传字段,否则调用方读到栈垃圾)。台架读不到通知值,所以把 xTaskNotify
做成可观测假件(记目标句柄与通知值)之后,这条契约才第一次被真正验证。

钉住的契约:
- 注册闸门:越界 fd 报 BADF;同 fd 已有挂起报 BUSY;caller 为空报 BUSY;**属主任务
  自己发起被拒**(自己等自己等于死锁 netTask);to_ms=0 记为无限等(不置 deadline)。
- 到达与截断:读量钳在调用方缓冲内(绝不越界写别人的 buf);readable 但芯片没数据时
  只清标志继续等,不误交 0 字节。
- 收尾四条分支:还有残留先把最后一包读走再收尾(**不丢尾包**);干净结束报 0 并按
  auto_relisten 请求重开监听(不自动重开的不被偷偷要求重建);released_abnormal 报
  CONNRESET 而不是 0(调用方要能分辨好坏收尾);session_dead 与 CLOSED 同路。
- settle_close:没挂起时直接返回不打扰芯片;有残留交还字节数;无残留报 CONNRESET。
- 挂起发送:参数校验(buf 空 / len<=0 / sent<0 / sent>=len 报 INVAL);单次写入钳在
  NET_SEND_CHUNK_MAX;**deadline 每写一轮就续期**(还在推进就不该被 1000ms 掐死,
  FTP 上传大文件最容易撞这条);停滞不前进才报 TIMEDOUT;槽位消失或会话塌掉都**交还
  已发字节数**(不是错误码,调用方据此算进度);cancel 留痕且幂等;**clear 不发通知**
  (与 recv 侧同一条纪律,不给调用方假答复)。

自查:42/43 首跑,唯一红的是**我的测试构造缺陷**——无限等那条没先把槽位置成在用,
被 in_use==0 兜底分支提前收口,测不到时间轴;补 mk_sock 后 43/43。reset_all 从上一
批的教训直接带过来(连被测模块的 static 挂起表与属主句柄一起复位),这次没再红。

覆盖率与台账:套件 23 → 24 全绿;files_tested 27 → 28;未触及 .c 39 → 38;分母
3,572 → 3,766 行;line=3297/3766(87.5%)、func=255/282(90.4%)。恒等式
38 = 4(P1 剩) + 5(P2) + 29(P3) 仍咬合。编号补建 TC-HOST-NETPEND(游标 040),
族数 17 → 18;注册表仍只做增量插入。

自查门禁:test-cppcheck Passed;check_ids --strict 通过;doc-links 35 处(= 存量);
12 个新增/改动文件 BOM/CR/U+FFFD/行尾逐项净。零生产代码改动,未跑 @build。
--all-files 仍只有 mem-numbers / mem-budget(.map 台账漂移,等 Lead 合并后统一回填)
与 ui-golden(ref/*.bin 不入库)三条失败,与本分支改动无关。
2026-10-23 23:44:51 +08:00
wandering1 0a3b932395 test(NET): 假 CH395F 芯片落地 + net_listener 模块(34 条,覆盖 99.2%)
按 §9 规格做假芯片的第一批消费者。新增 test/host/common/ch395f_fake.{h,c}:15 个
函数的**真实原型逐字照抄**(含 ch395f_sock_state_t{open,tcp_state} 与 ERR_SUCCESS/
PROTO_TYPE_TCP/SOCKET_CLOSED/TCP_CLOSED/TCP_LISTEN 的真实取值),脚本化模型沿用本仓
既有假件风格:读函数按调用序号取返回队列(耗尽回落最后一个值)、写函数记次数与
最近入参、外加一条**调用顺序日志**让时序契约可断言。两条芯片怪癖必须复刻:
① get_sock_int_status **整体读即清**、无按位清除手段(AGENTS 模块约定 B3 实证坑,
不复刻就照不出"顺手全量清一遍把别的在用 Socket 门焊死");② *_cached 是**缓存值**
而非现场值,"监听还活着"的判据是 (open != CLOSED && tcp_state == LISTEN)。
各模块 stub/ch395f.h **只做转发** include 共享件(规格里拍板的方案):三份假芯片各自
漂移会让"假件与被测代码同时错"再也报不出来,签名与常量只能有一份定义。

新增 test/host/net_listener/,34 条断言,net_listener.c 覆盖 125/126 = **99.2%**。
钉住的契约(真机上一闪而过、台架凑不齐的组合):
- 四道互相牵制的时间闸门:30ms 退避(20ms 不重试、30ms 重试)、隔离级 400ms 与
  优雅级 100ms(399ms/99ms 不放行、400ms/100ms 放行)、**800ms 上限只留痕且不放弃
  请求**(留痕只打一次,到 2000ms 预算耗尽才报 ERROR 交对账器)。排期登记里那句
  "沉降窗 800ms 上限只留痕"至此第一次有了可执行的验证。
- 该不该重建:芯片仍报 LISTEN 就不重建且清空请求;有挂起 recv/send 时让路但**保留
  请求**(挂起结束自动补做);五种条件静默放弃(非 LISTENING/CLOSED 态、
  auto_relisten=0、非 STREAM、local_port=0、in_use=0)。
- 失败不留半成品:open 失败关回去并回 BUSY;listen 失败也必须 close_socket。
- 重建后簿记:转 LISTENING、清 send_ready/recv_bytes/remote_ip(_arr)/remote_port、旧
  remote_port 记进 last_peer_port、清 lost_conn_seen/released_abnormal;源端口与协议
  取自表项本身(监听端口不能漂)。
- 芯片时序:清接收 → 关断 → 读即清 → 读状态(顺序不可换);清理接收区单次读钳到
  256B、最多 16 轮、读到空即停。越界 fd 全程静默且不打扰芯片;请求去重保留首个 reason。

测试自查修了两处(都不是生产问题但值得记):
① reset_all 只复位假世界、**没复位被测模块自己的 static**(s_relisten_req/_next/_dead/
_quarantine/_settle_*/_capped),一上来红了 9 条;改用模块自带的 net_listener_clear()
清,顺带验证了 clear 的完整性。
② 沉降窗边界必须在**独立场景**测:放行判定与 30ms 退避共用 s_relisten_next,同一条
链里"恰好 400ms"那轮 tick 会被上一轮设的退避槽挡掉。这本身是个该知道的事实——
**hold 到点后最坏还要再等一个退避周期(实测 ≤30ms)才真正放行**,已写进用例注释。
另留痕一处死分支:net_relisten_locked 的 NET_ERR_BADF 从 tick **不可达**(tick 在调用
前就因表项为空放弃),TC-NETLIS-026 记着,新增调用点时要重新审视。

桩的口径:net_listener.h/net_types.h/net_config.h/net_pending.h 用**真实头**(net_sock_t
字段与各枚举一改即编译期报错);net_socket.h 是桩,但 net_set_errno 与 NET_ERR_RETURN_1
照抄真实头同名宏(net_socket.h:528/532);真实 net_pending.h 原型带 TaskHandle_t,故
宿主 FreeRTOS.h 补一个 void* typedef(只出现在签名里,被测代码从不传它)。

覆盖率与台账:套件 22 → 23 全绿;files_tested 26 → 27;未触及 .c 40 → 39;分母
3,446 → 3,572 行;line=3128/3572(87.6%,本批净涨因为新进来的文件几乎全绿)、
func=243/266(91.3%)。恒等式 39 = 5(P1 剩) + 5(P2) + 29(P3) 仍咬合。编号补建
TC-HOST-NETLIS(游标 026),族数 16 → 17;注册表仍只做增量插入。

自查:test-cppcheck Passed(连假芯片一起过);check_ids --strict 通过;doc-links 35 处
(= 存量);新增文件 BOM/CR/U+FFFD/行尾逐项净。**本增量零生产代码改动**,未跑 @build。
--all-files 仍只有 mem-numbers / mem-budget(.map 台账漂移,等 Lead 合并后统一回填)
与 ui-golden(ref/*.bin 不入库,新 worktree 天生没有黄金参考)三条失败,与本分支无关。
2026-10-23 23:44:50 +08:00
wandering1 d3107f374f docs: 待处理内容.md 解耦为 docs/任务规划/ 文件夹(README 排序权威 + 6 个职责单一文件)
- rename_ref.py 搬迁:git mv + 全库引用改写 33 文件 82 处
- README.md 保留 §0 编号空间 / §1 未闭环一览 / §2 推进顺序权威 / §3 维护约定
- 拆出 6 文件:W1-采集保护收尾(任务卡五字段)、排期详述、暂缓-RS485、
  登记簿、测试与架构专项、基线与待确认
- 外部引用重定位 ~50 处:裸「登记簿 §X」、§5.1→基线与待确认 §2.1、
  §2.4→暂缓-RS485、§6→基线与待确认 §3、脚本注释 T-x/N-x→登记簿
- 新增并行写纪律:任务分支不改本文件夹,状态列由 Lead 主工作树更新
- 验证:doc-links 141 文档全绿;mem-numbers 一致;refgraph 重生成;
  @build 0 错误 0 警告;新文件无 BOM / LF / 零乱码
2026-10-12 13:42:47 +08:00
wandering1 33a4aa4945 本地工具路径DTU_*环境变量改造落地:10处调用点去硬编码+变量优先解析,本机全链路验证通过,文档同步收口,requirements补pre-commit 2026-10-02 22:14:49 +08:00
wandering1 b0851b2f70 修复L0.5单测harness工具链探测:VS2026实际安装目录为18,补入VS_ROOTS(本机3项主机侧单测由ENV失败转PASS) 2026-10-02 15:00:31 +08:00
wandering1 2055de0303 测试:落地测试架构改进 T-1~T-5(套件开关门禁/汇总行校验/通用 harness/公共断言宏/多套件编排)
背景:L0.5 主机侧单测落地后复审测试架构,给出 6 条改进意见(P0~P3),
登记进 docs/待处理内容.md「测试与验证基础设施」后逐条实施。

- T-1(P0) 测试套件开关门禁:新增 scripts/check_test_suite.py + pre-commit 钩子
  test-suite-switch,默认要求产品构建(全部 TEST_SUITE_* 注释),跑套件回归用
  --allow-test-build / ALLOW_TEST_BUILD=1 显式放行。顺带把 test_config.h 中长期
  开启的 TEST_SUITE_CH395F 改回注释(仓库此前躺着的是测试固件)。
- T-2(P1) verify_log.py 增加 --require-suite-summary:要求
  "=== <名称>: N/M PASSED (failed=0) ===" 且 N>0,堵住"套件没跑到汇总行/
  断言数为 0 仍判 PASS"的盲区;run_boot_test.py 同名参数透传。不传参向后兼容。
- T-3(P1) 抽取通用 harness test/host/common/unit_test_harness.py(工具链探测/
  INCLUDE-LIB 拼接/wd4819/GBK 解码/退出码契约);watchdog 运行器 170→55 行,只声明 TestBuild。
- T-4(P2) 新增 test/board/test_common.h 集中断言内核(TEST_CHECK_IMPL/TEST_REPORT_IMPL),
  4 套件各用一行宏绑定自身统计变量(保持 g_xxx_stats 独立、防跨套件串数)。
  刻意不 include dbg_log.h——否则冻结 DBG_TAG 默认值触发 #47-D 重定义警告。
- T-5(P2) 新增 test/run_all_suites.py 多套件回归编排,try/finally 无条件还原套件开关。

验证:@build 产品构建 0 错 0 警;4 套件逐一构建均 0/0;看门狗单测 39/39 PASS;
pre-commit 14 钩子全通过;编排脚本 dry-run/开关切换/异常还原均实测通过。
2026-10-01 07:28:57 +08:00