d64bbdc57fbe5354690c44560449a06da6d7af5e
39 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
d64bbdc57f |
feat(wave): 故障录波数据面、接线与单通道回放页(软件闭环交付)
数据面(App/algo,纯逻辑,双端编译) - wave_ring:8 路 × 320 点列优先环(1 kHz,预触发 160 + 后触发 160),adcTask 唯一写者;触发/冻结/封存/冷却四态,冻结持续到落盘回执才解冻,滞留 2 s 自弃 兜底——下一次真故障无波可录比丢一段更贵 - wave_store:段头编解码 + 拉模式打包游标 + 自带无表位运算 CRC-16/MODBUS(带 锚点自检)+ 段名与整段长度等式;格式不依赖平台类型宽度 - 段头只存原始码值与封存瞬间生效系数,工程量只当单位标签、不进任何算式 - 三套主机单测(环 113 / 编解码 170 / 触发状态机 110),全部无桩、生产源原样 纳入;新文件补 stddef 是 ARMCC 与主机差异,主机侧照不出这条分叉 - test/host/wave_read.py 是按 §6.2 的独立第二实现(dump/fft/shape/plot),92 条 自洽断言;三份合成段互读双校验全过、哈希钉死,格式面双向闭合 - 三套新单测不加专属钩子,由覆盖率总闸全量真跑(沿用既有裁定) 接线(任务层) - adc_cal:新增生效系数的 32 位定点原值入口(与既有浮点入口共用生效视图), 段头按最低有效位精确记账,不经浮点回算 - adcTask:每拍压 8 路语义原始码值并推进状态机,封存那一拍同拍交棒封存瞬间事实; 台架定周期自抓走双层编译门,产品镜像里零存在;串口新增一条 wave: 周期行 - defaultTask:卷空闲后一次心跳整段搬完(try 锁 + 256 字节分片 + 一次完整开档到 关档),成败都回执;失败先删半成品再计错,段号只在成功时推进;持锁超 50 ms 补一条告警并记历史峰值(喂狗余量可核) - Lead 复核补第四道并发陷阱:采集任务优先级高于落盘道,搬段中途它可能正好把滞留 超时的本段自弃、甚至封出下一段——数据区校验和是对「实际读到的字节」算的, 半新半旧的段照样自洽,故关档后复检环侧身份判废:删文件、不补账、段号不推进 - Lead 复核抓到触发挂号不是原子操作(旧实现分三个字节写,消费者可在窗口里读到 上一段的触发源与元件号):已把载荷压进一个字节,越界当场拒且不记任何计数; 段文件字节布局零改动,三份合成段哈希逐字节不变 - protectTask:保护启动上升沿同点请求录波,一拍一次、元件号取最低位;判据本体 零改动 人机(160×160 单色屏) - 菜单树节点 26「手动录波」绑页面 + 记录查询组末新增「录波回放」叶,重跑生成器 得 43 节点 / 34 叶 / 30 已绑,页面映射 28 → 30 - 回放页一屏一路、触发点前后各一周波共 40 点、每点 3 像素阶梯线(只用水平与 垂直两种线段——单色屏斜线会被阈值裁掉);零点虚线、触发光标、窗口峰值、 单位徽标与形状受限徽标;空态与坏段各一句显式文案,绝不用半截波形冒充数据 - ui_data 新增录波注入通道(六件操作表、三态返回);执行侧胶水带段头缓存、 手动请求后连缓存一起作废、逐点走全仓唯一的码值换算入口 - 模拟器接真实环与编解码合成两段(含坏段与空态旗标),6 张新黄金参考,既有 60 张逐字节零漂移未用更新模式;字库 218 → 224 字 / 319 字形 - 两份图形配置里图表组件仍为关;零堆分配;页面池水位不变 - 回放读路径的 uiTask 阻塞上界按实码写清:一次渲染 = 段表刷新 + 未缓存段头最坏 22 次小读;已验段只剩一次开窗读 + 40 点解码;无实测数不编,实测归台架、 仪器用已在跑的单轮工作耗时探针,不新加埋点 台账与文档 - 内存台账:录波环与落盘/回放静态逐条入册,三档对照 81,536 → 88,296 → 89,936 字节(62.2% → 68.6%),累计 +8,400;预估 5.9 KB 与实测 +6,760 的差额点名; 散列文件头注释同步;任务栈表核对后无需抬栈;体积趋势行与同码凭证由权威构建 重打(0 错误 0 警告,四项体积与上一轮相同——本轮改动只有注释与声明位置) - 设计说明:§5 补第四条并发陷阱;§6.3 定字段口径(两份独立实现的共同依据); §7 回填实测;§9 周期行写实;§10 台架判据改两段式且不比绝对相位;§12 补读路径 风险行;抬头状态改成「代码面已交付、未做完的只有板级那一轮」 - 卡片:软件闭环一栏列证据;台架三条按用户裁定保持未完成,不做虚假销项 - 口径同步:数据可信、UI 补齐、页面补齐规划、产品需求、采集短板评测、任务规划 索引与两份 README;陈旧现状句清扫 16 处(带日期戳的历史取数行一律不动) - 门禁说明:nested-comment 记仓库根探测必须兼容 linked worktree 的 gitdir 文件 形态(修前该钩子在任务树里恒报环境错、静默不可用);coding-standard 记 「函数头注释超过 24 行就把函数功能挤出回看窗口」这条机制与本次现场 - 覆盖率基线登记两个新覆盖模块(套件 41 → 44 套);环单测重打后该文件有效行 227 → 226、覆盖行 226 → 225,比率仍 99.6%,是分母变小不是覆盖率回退 为什么是单一提交:文档链接、双端注册、生成器幂等(菜单与字库)、内存数字与 体积事实源、编号门禁这五道整仓门禁要求代码、页面、菜单树、字库、文档与台账 同处一个提交才自洽,按数据面/接线/人机/台账拆成四笔必然红其中至少四道 编号门禁新增行 14 处命中已按语义改写:魔数常量按字符命名、段名样例改由格式串 生成、频谱表头改 f / 2x / 5x 并加一行图例;改写后自洽断言 92 条仍全过 |
||
|
|
41670a1cc6 |
docs(gates): 主机单测套数复点同步——41 套 / 无专属钩子 19 套
第 41 套 boot_stats 由 728d6067(2026-10-27 11:29)入库,全仓写的 「40 套 / 18 套无专属钩子」从那天起过期;本轮按实测复点同步。 真值双源互证:test/host/*/run_unit_test.py 实数 41(43 个目录,除 common 与 __pycache__)= scripts/check_test_coverage.py 自报 suites=41/41。专属钩子 22 根(YAML 解析 id 以 -unit-test 结尾, 22 条全对得上有 runner 的目录)⇒ 无专属钩子 19 套 = 41 − 22。 改当前态共 18 处:AGENTS.md 提交门禁节三处、.pre-commit-config.yaml test-coverage 注释、docs/门禁说明/host-unit-tests.md §6 第 4 条六处 (无钩子名单首位补 boot_stats、目录数 42→43、裁定句改「裁定当时 18 套,2026-10-28 现值 19 套」)、docs/门禁说明/README.md、 docs/代码规范/pre-commit使用说明.md 两处、docs/编号规范.md、 scripts/README.md、docs/任务规划/分工说明-20261028 一条、 docs/测试说明/主机单测覆盖率口径.md 追加 2026-10-28 = 41 套 ~105 s 新实测点、test/host/sys_log_mcu/sys_log_mcu_test.c 注释行。 刻意不动:带日期戳的历史耗时与套数行(只追加新的实测点,不覆写)、 host-unit-tests.md 里「22 根 / 40 套 / 18 套无专属钩子」这句复核原文 (只在后面追加更正)、scripts/archive/id_gate/ 留档两处、 同数不同义的五处(40KB 级 / −20~40% / 不到 40 行 / 18 断言点 / 板端 RTC 套件)。钩子数 51、专属 22 根、提交期实跑 48 根本轮实测 未变,相关句一字未动。 门禁:doc-links --all --sections-strict / id-check-new --worktree / no-mojibake / bom-drift / validate-config 全 rc=0, test/host/sys_log_mcu 单测 rc=0;改后复扫「40 套」仅剩 3 处 (1 处刻意保留 + 2 处留档)、「42 个目录」0 处、「18 套」仅剩 2 处 (都带「当时」口径)。 |
||
|
|
295eb9911f |
ci(gates): 覆盖率总闸与全量 cppcheck 移到远端 CI 强制(提交期实跑 48 根)
用户裁定 2026-10-28:慢钩子不再压在每个 git commit 上,由远端 CI 强制;本地等价入口 = scripts/ci_local.py 的 manual 段。 实测三根耗时:cppcheck 20.5 s、test-cppcheck 38.1 s、 test-coverage 105.2 s,合计 ~165 s ≈ 一次带代码提交耗时的 三分之二(test-coverage 的 files: 覆盖 App/,改一行就跑光 40 套)。 - .pre-commit-config.yaml:cppcheck / test-cppcheck / test-coverage 加 stages: [manual] + 理由注释;替换掉 test-coverage 上方「没写 stages → 所有阶段都跑、输出出现两次」 的旧坑注释(固定 manual 后不再有这一坑)。 - .gitea/workflows/ci.yml:static-gates job 在静态门禁步之后 新增「手动阶段门禁(覆盖率总闸 + 全量 cppcheck)」步, if: always(),逐根 --hook-stage manual(pre_commit run 不支持 多 id),日志 ci-artifacts/manual-stage.log + 归档 + STEP_SUMMARY, rc≠0 即失败;run 块纯 ASCII。 - scripts/ci_local.py:新增 MANUAL_STAGE_HOOKS 与 run_manual_stage() (静态段之后默认跑),删 run_coverage() 死代码,--coverage 降级为兼容别名。 - AGENTS.md + 12 篇 docs/scripts/README:三数同源口径改为 声明 51 / 提交期 --all-files 实跑 47 / +gitlint = 48, 其余 3 根在 manual 阶段;历史测量数字只加「当时」不回改。 验证:pre-commit run --all-files = 47 Passed / 0 Failed rc=0; manual 三根 Passed(test-coverage line 89.8% func 93.2% 41/41 套); ci_local.py --static-only 全绿;check_doc_links --all --sections-strict rc=0、mojibake / bom-drift / id-check-new rc=0; ci.yml yaml.safe_load 通过。未改任何 .c/.h,故未跑 @build。 |
||
|
|
511fd30cc6 |
docs: 清理全仓变更记录流水(43 篇整节删 / 23 篇留一行 / 5 篇保留)
用户裁定:文档只写现状,历史流水一律删掉,过程查 git log。 - 43 篇「变更记录 / 修订记录」整节删除:继电保护规划 12.6 KB、产品 需求 11.8 KB、项目部署环境 5.5 KB、BSD Socket 指南 5.3 KB、架构 总览 5.0 KB、实机测试实施指南 3.8 KB 等 - 23 篇别处仍有指向:只留节名 + 一行「本文件不再记流水账,过程查 git log」,节号与锚点不位移(doc-links 不因此断链) - 5 篇整表保留(被当唯一证据 / 活台账引用):看门狗设计说明、配置 文件设计说明、RAM预算台账、内容页架构、本地工具路径环境变量方案 - 规范同步:文档规范 §5.1 改新裁定;测试文档模板规范删示例节与 登记句;测试开发标准流程 / UI 两篇 / 实机测试实施指南 / 四篇单测 说明 / 并行任务工作树方案 / 编号规范 的「登记变更记录」改「写进 正文」 - 技能同步:stale-doc-count-sweep / ui-verify / id-family 三个 SKILL 正文不再要求追加流水行(顺带消掉 SKILL.md:121「产品需求 §12」 失效引用) - 修 5 处悬空指针:采样有效性校验目录锚点、BSD 指南「文末变更 记录」、docs/README 过期描述、网络自愈架构两处指向面 72 篇文档净减 600 余行;check_doc_links --all --sections-strict 可判 定比例 61% → 78%、失效引用 0;静态门禁 passed=50 failed=0;gitlint 10 条历史提交信息 0 failed。 未提交他人在飞改动:.dsh/skills/keil-build/*(%SystemDrive% 守卫)。 |
||
|
|
dc214e1426 |
docs(UI): 内容页黄金参考当前态改报「60 张」,三数按双源实测同步
三数实测真值(每项两个独立来源互证): - 张数 60:simulator/tests/ref/ 入库 shot_*.bin 实数 60 = check_content_pages.py 的 PAGES 条目数 60(id 逐一对齐)。 - 已绑 28 叶 / 未绑 5 叶:ui_page_registry.h 的 UI_PAGE_MAP_COUNT 28 = ui_menu_tree.txt 剔掉文件头注释示例行后的 #page= 实数(不剔是 29)。 - 字体 218 唯一字 / 313 字形:ui_cn_chars.txt 唯一码点 218(ASCII 区 0 个,ASCII 0x20~0x7E 由生成器子集参数另计、不算进字数); ui_font_cn_12.c 的 .bitmap_index 条目 314 条含 notdef,剔掉 notdef → 313 = 218 + ASCII 95。 口径更正(数字一律不动):当前态计数单位由「页」改报「张」——张数 = 页面个数 + 分页页多拍的第 2 屏(runreport_p2 / sparam_p2 两张),两个数 不许互相冒充。同日先前那行「53 → 59 页」是页面个数口径、与判据本体 不同源,原文冻结不回改,只在各文件变更记录末行追加当日更正。 历史一律冻结:变更记录里的 27 / 40 / 47 / 53→60 / 59 与字库 122/217、 179/274、185/280、183/278 都是当日快照,只追加、不回改。 夹带说明:.pre-commit-config.yaml 一并入本笔——pre-commit 要求配置本身 已 staged 才允许提交;该文件的改动是 ui-golden 显示名(60 页→60 张)与 一条钩子计数口径注释(同日 51 → 52 → 51 净零,属另一类,已在此点名)。 |
||
|
|
599f05be22 |
refactor(编号): 测试验证层级 L0/L0.5/L1/L2/L3 → 语义名(批次 3d·文档层)
这族是验证层级:静态门禁 / 主机侧纯逻辑单测 / 板端测试套件 / 主机侧回归 / 人工目检。 实测它出现在 **97 个文件**(40+ 个 test/host 的 run_unit_test.py、.gitea workflow、 ci_local.py 的阶段标签、以及十来篇文档)。本批只做**文档层**,代码层单列一批(见下)。 改动:10 篇文档 110 行 + 三处收尾—— 测试开发标准流程 / 实机测试实施指南 / 主机单测覆盖率口径 / 门禁说明 README 与 host-unit-tests / CI README 与远程CI完整指南 / docs README / 测试与架构专项 / pre-commit 使用说明。 映射:L0 → 静态门禁、L0.5 → 主机侧纯逻辑单测、L1 → 板端测试套件、L2 → 主机侧回归、 L3 → 人工目检;`L0b`(ci_local 的内部阶段标签)在文档里写成「提交信息门禁」。 测试层级那张表的「层级」列被删掉(名称列已承载同一信息,留着就是两列重复)。 **未做(代码层,单列批次 3d-2)**:`scripts/ci_local.py` 的阶段标签、`.gitea` workflow、 `test/host/*/run_unit_test.py` 的输出前缀、以及代码头注释里的 L0/L0.5 —— 那些是**程序标识**, 改名要连带 CI 日志格式与文档里对 `[SUMMARY] L0: ...` 的引用,属跨端改动,需单独验证。 验证:三篇主文档的层号残留 = 0;doc-links 通过;mojibake / git diff --check 干净; 表格列数已收敛为 3 列(名称 / 载体 / 抓住什么)。 |
||
|
|
1668842d61 |
chore(id-check): 退役全库编号语料审计(钩子 52 → 51)
第三步(用户裁定跳过观察期直接做)。第一步 |
||
|
|
94b0d79026 |
feat(ui)+chore(docs): UI 补齐清单清空;数据可信销项与「卡」号清退
本笔含两组收尾工作(同一工作区内交错在 6 个共享文档/门禁配置里, 拆开会产生"文档声称的页数与树里黄金数不一致"的中间提交,故合为一笔): 一、UI 补齐(清单清空) - 新建 6 个内容页模块:ui_page_accident / oper / alarm / start / yk 复用 ui_w_records 共享组件(分页、空态"无记录"、按实例读盘缓存), 记录层与五个写入点已由道F task-10 / 道G task-12 落地,本批只补页面; ui_page_runreport 为 13 行 2 页只读统计,四个数逐个挂范围标签 (自电池周期 / 本次开机),boot_ready=0 显 "--" 不显 0。 - 数据面:ui_data_info_t 尾部追加 9 字段,ui_data_feed 用 boot_stats_get() + iec104_serv_get_stats() 纯搬运;模拟器填确定值假数据 (差异登记 docs/测试说明/模拟器差距清单.md §2.3 两行)。 - 接线:菜单树 6 叶绑 #page=,绑定叶 22→28、UI_PAGE_MAP_COUNT=28; 字库 185→218 字 / 280→313 字形;双端注册 uvprojx +30、CMakeLists +6。 - 回归:内容页黄金 53→60(含 runreport_p2 真拍第 2 页),文本断言 425 正向 / 36 反向;存量 53 张逐字节零漂移;记录 5 页各用独立空目录 (模拟器把操作留痕写进 cwd 的 sim_oper.log,共用 tmp 会让空态拍不到)。 二、数据可信销项 + 「数据可信」任务卡号整族清退 - 删除已闭环的"已知电压注入验证"行与闭环结论块(证据在 docs/硬件平台/ADC通道映射说明.md §8.1 与 docs/测试说明/ADC前端注入验证流程.md §5~§7),§5 标识列改语义短语。 - 卡 0/0b/1~12 共 14 个号全仓 288 站点 / 56 文件清退成语义短语, 代码注释、测试、门禁配置与文档一并改;换算表入 docs/编号规范.md §9.3。 这一族是中文前缀,不进 check_ids 语料,刻意不入 id_aliases.json。 验证(实测) - @build 0 错 0 警:Code=236456 RO=28124 RW=1876 ZI=137084; RAM +464 B(RW +304 / ZI +160),RW_IRAM1 62.2% / RW_CCM 87.9%, 体积同码凭证与 RAM 台账已回填。 - check_menu_rows 8 状态 rc=0(池 5368/5448 常量未动); check_content_pages 60 页 rc=0;check_font_charset / registration / coding-standard / bom-drift / mojibake / doc-links --all / layer-deps / nested-comment 全 rc=0;check_ids --strict 无错误;主机单测 ui_data 295 / boot_stats 21 / sys_log_mcu 141 / soe 32 / soe_store 27。 未做(不是遗漏) - 六页真机判据(真记录翻页、掉电保持、清除后归零、boot_count 自增、 拔 RTC 电池落 RESTARTED、IWDG 复位落 rst_cause、主站连一次 conn_up+1) 已登记 docs/任务规划/登记簿.md §7;模拟器结构上照不到。 - docs/产品需求/产品需求设计文档.md 属另一条在飞改动,本笔不含; 其中我已同步 6 处现状数字(28 叶 / 60 页 / 218 字),随该笔入库。 |
||
|
|
a275539c17 |
docs(全仓): 内容页黄金 47 → 53 的当前态同步——含钩子显示名,登记簿口径另笔
task-9 让内容页黄金从 47 涨到 53(通讯设置页六态),本笔只做**当前态陈述**的同步;真值全部本轮实测: `simulator/tests/ref/*.bin` = **53**;`git diff 31ca4a1f..HEAD` 对该目录 = ** 6 个新增、零修改** ⇒ 存量 47 张 逐字节未变(这条是关键:新增页不该让旧页重生成,否则回归基线就失去意义)。 **四类命中各走各路,这是本笔的全部难点**(分类比替换重要得多): · 当前态 → 改,25 处页数,落在 16 只文件(AGENTS、钩子显示名、导航器 docstring、`lvgl-simulator` s kill、 门禁索引与 `ui-golden` 详解、UI 自动化流程、内容页架构、页面补齐规划与扩充方案、数据供给、自动化测 试流程、覆盖率口径、测试开发标准流程、模拟器两份 README); · 变更记录历史行 → **一条不动**,另在其下一行插入当日「47 → 53」新行 6 条(含判据与零漂移证据); · **同数不同义 → 不动**:覆盖率口径里的「47 个 .c」「47 = 13+5+29」、ram_report 的表行号 47、`#47-D` 诊断号、LSI 47kHz、47,360 栈字数、像素哈希 3476/3474 —— 一并替换就会毁掉六个不相干的事实; · 带日期戳 / 自declare 冻结的快照 → 不臆改(数据供给篇那句上一笔已加「当时」,页面补齐规划里那张 「2026-10-25 复核…剩 21 叶」的表自带「完成即冻结不回改」声明)。 复扫残留当前态 47 = **0**。⚠ 顺手纠一条**会教错方法**的陈旧:naive 计数 `grep -c '#page='` 上一笔写的是 22,现在真值是 **23**(文件头示例行也算一次)——旧文档里那个 22 **恰好等于新的正确 bound 数**,不改的话, 下一个人用错算法还能碰巧对上,比错更糟。 ⚠ `AGENTS.md` 里本次有两件事同行相邻(本笔的内容页 53 页 + 批次三那句 `TC-*` 停发),**分两句说明**: 前者是计数同步,后者是昨天那条「批次三不坐」裁定的入口文案,二者不是同一件事。 钩子显示名改在 `.pre-commit-config.yaml`(47 → 53)。**这条必须与代码同批入库**:配置脏而未 stage 时 pre-commit 会直接报错挡住全仓任何提交(今天已实测两次),所以本笔先落它,把后面几笔的路清开。 自查:`check_doc_links --all` rc=0(203 篇)、`check_ids --strict` rc=0 且不可判定绝对 数 **979 未变**(新增 文本里的提交号与 `#page=comm` 没推高它)、`gen_task_table --check` 0、`check_bom_drift` 0、18 只文件 noBOM / LF / U+FFFD=0;行数变动 +49/−47 全部可归属(插 6、删 2、并 1),其余为 1:1 等行替换。登记簿销项与叶数 (`UI补齐.md` + `docs/任务规划/README.md`)**不在本笔** —— 那是口径变更不是计数同步。 |
||
|
|
31ca4a1faa |
docs(计数): 内容页黄金参考 40 → 47 页全仓同步,登记簿剩余项与顺序一并更新
上一笔 UI 落地时我把「文档计数与真值同源」这件事留在了别人身上(setpage 的移交项写得很明白:内容页 40→47 的全仓计数改写由 Lead 派)。这一笔把它做完,**只改当前态陈述,历史不动**: · **改成 47 的判据**:simulator/tests/ref/ 实测 47 张 .bin(7 张新增、存量 40 张逐字节零漂移),共 1 6 处当前态 陈述跨 11 只文件同步——含 AGENTS 的 ui-golden 条目、**钩子显示名**(.pre-commit-config.yaml) 、门禁索引与 ui-golden 详解的标题与正文、UI 自动化流程的页数与 PASS 文案与快检张数、内容页架构回归段、覆盖率口径的 「不做 UI 单测」理由、启动自动化流程回归段、导航器脚本 docstring。 · **不改的两处,理由写明**:5 只文件里的历史变更记录行「27 → 40 页」是当天事实,原样保留,只在各文件 变更记录顶部加一行 2026-10-27 的 40 → 47;UI_界面制作流程 的「< 40KB」是绘制缓冲红线,与本数无关, 属扫描误报,不动。 · **一处加限定语而不是改数**:UI_自动化测试流程 的「文本断言 310 条、覆盖 40 页中的…」带 2026-10-26 实跑 日期戳,那是那天的测量口径 ⇒ 改成「覆盖**当时** 40 页」,310 条不臆改,等下一次实跑回填真值。 顺带同步 docs/任务规划/README.md(登记簿销项,用户授权):开篇「第一项是实现 param_request 写路径、然后做 整定 4 页」早已完成 ⇒ 改为剩余三项「通讯设置 → 记录 5 页 → 运行报告」;§2 顺序表「按 §1 的 1→5 推进」改为 「剩余 3 项按 1→3 推进(原第 1、2 两项已落地)」,与上一笔 UI补齐.md 的销项结果对齐。 三数同源自查:AGENTS 钩子数 51(YAML 解析口径)、内容页 47 页、门禁索引 27 篇均与配置一致; check_doc_links.py --all exit 0、check_ids --strict exit 0 且不可判定绝对数 **1,156 未变**(本笔没吃余量、 没新增编号引用);13 只文件全部 noBOM / LF / U+FFFD=0 / 结尾换行齐。本笔零固件源,不改构建产物。 |
||
|
|
b24b2b5f31 |
docs(计数): 内容页黄金参考 27 → 40 全仓同源改写,含钩子显示名这最后一处不同码
UI 三页落地后机器真值变了,文档却还写着 27 页——这不是笔误,是会骗人的现在时。 作者(batch-auditor)先自己从机器面取数再动笔:git ls-files 里 ref/*.bin = 40、 PAGES 40 项、TEXTS 37 页 310 条、ABSENT 16 页 29 条、池 5448 B 与常驻堆 5368 B 未变、 字库 183 字未扩。**它还纠了我一个数**:我报 ABSENT +19,实测 11 → 29 = +18;作者按 实测写文档,并且不在文档里写 ABSENT 总数,免得把我这个差 1 的数固化成第二真源。 改写面(现在时陈述,共 12 + 3 处):AGENTS.md 的 ui-golden 半句、门禁说明 ui-golden.md (标题与「拦不住什么」段)与索引行、simulator/tests/README.md、simulator/README.md 两处、 UI_自动化测试流程.md(310 条 / 40 页 / PASS 文案 / 覆盖面)、内容页架构.md、页面补齐规划.md、 主机单测覆盖率口径.md、自动化测试流程.md、测试开发标准流程.md,外加 .pre-commit-config.yaml 里 ui-golden 的显示名——那是「声明 / 文档 / 实跑」三数同源的最后 一处不同码:AGENTS 与门禁说明都已 40,配置里还写着 27。 两条纪律作者都自证了,不是口头: · **历史数字一律保留**:8 处变更记录行里的 27 / 242 / 24 / 11 原样未改;两处带日期的 验证记录只加限定语(「该下限此后已扩至 40 页,双次逐字节一致需按同口径重跑回填」), 没有把 27 改成 40 去造连续性。 · **同名不同义的数字不许全局替换**:页面补齐规划.md:12 与 :157、页面扩充详细方案.md:649 里的「27」是**空叶子口径**,与黄金参考数无关——三处故意没动。这条比改对更值钱。 · 余量自证(回退名下 12 文件复跑同一扫描器):refs +0 / dec +0 / und +0 / 比值 +0.00pp / 错误 +0,本笔零消耗,也未新增任何编号引用;simulator/tests/ref/ 下 40 个 .bin 零改动。 闸线现状照实挂着:id-check 不可判定 1,162 / 上限 1,166,**余量只剩 4**(吃进者是今天 UI 那笔的注释引用,账在 Lead)。清账之前任何人新增编号都可能直接撞闸。 |
||
|
|
7d9db33a25 |
chore(门禁+文档): 生成器幂等钩子落地,文档真值与 UI 清单销项同步
新增 generated-fresh 钩子(scripts/check_generated_fresh.py,只读):把 gen_menu.py、
中文字库链、tools/arch_diagram/gen_*.py 拷进系统临时目录重跑,与工作区逐字节必须
一致;钩子本身不写仓库、不 stash 别人在飞的改动。实证起点是上一笔
|
||
|
|
7099c56c34 |
chore(ledger): pages 批 1~4 合入后回填凭证/覆盖率并同步黄金参考计数
task/pages-ui 合入(merge
|
||
|
|
db9f256fd0 |
docs(收口): 批 1 台架记录与门禁/产物分家口径落档(道C 文案兑现)
- test/board/ch395f_test.h:f3 轮台架记录追加——VERDICT PASS, task stall 468→14(net 类 431→0,掩码复活实锤),余 13 条为 netTask 自挂起前喂一次 vs 精确匹配判据的语义打架 + 1 条 uiTask 瞬态 + 1 条设计内 P2 留痕;lead 裁决不放宽超集判定 - 主机单测覆盖率口径.md:中间产物路径 L80 与坑 12 重写为 %TEMP%/dtu_host/<工作树哈希>/ 分家 + runs 独占(adabb6a 落地, 旧固定共享名作废;判据正文指向 harness 顶部) - 并行任务工作树方案.md §5 表:单测产物一行——不需要锁但必须 按树分家(对照 build/bench 真独占) - 门禁说明/cubemx-survival.md §6:新增第 7 条「基线漏项=静默不检」 (net-msgq-adopt 实证,--write 后抽查新增条目) - 门禁说明/id-check.md §5:拦下过真事追加——P1-10 注释裸编号被 校验 6 拦(紧邻写法在 C 注释同样生效) |
||
|
|
15aa08552c |
docs(问题记录): 删除项目审查与优化建议报告,结论并入模块文档与编号规范
用户裁定:该报告大部分内容已闭环,可删除;仍有价值的迁到合适文档。删前逐条核对, 确认绝大多数结论在仓内早已有权威副本(LV_DRAW_SW_COMPLEX / osFlagsNoClear / SPI 分频 8 / 分层反向依赖存量基线等),只迁真正独有的四类内容: 1. 暂缓理由与重开条件(三条量化门槛)→ RAM 使用分析与优化方案 §4,并明确「本节是唯一 权威」,其它文档只留指针;同时把 10 处指向报告 §5.3 的重开条件引用改指该节。 2. 2026-10-22「常驻 vs 动态」三项诊断(静态前五块 43,462 B = 58%;动态面只有 heap_4, 故栈余量=CCM 余量;栈深主因是日志/配置写链)→ 同文档 §2.5。 3. `_fp_digits` 1,018 B 删不掉的机理与 F-6 根因修法(Q10 宏改单精度,−332 B,4.49e6 例 逐位等价)→ §2.6.4。 4. 「UI 侧缓冲进 CCM」要按 DMA 可达性二分(LVGL 池可进,帧缓冲/推流缓冲不可)→ 暂缓清单。 编号侧(报告曾是 6 个族的属主登记处): - 原报告 §9 的 26 行「号 → 现在详述于」对照表迁到 docs/编号规范.md 新增 §4.5,6 族 owner_docs 改指该节。规范正文含大量写法示例,直接当定义集会误伤,故 check_ids.py 新增 `_def_owners`:snapshot_only 族剔除规范正文,只恢复可判定、不参与悬空/撞号/裸形 /紧邻判定(实测若不分,裸形 S1 误报 238 条、S-3 与登记簿 S-3 误判重复定义)。 - 11 个已死号(N-03b/N-10~N-13/N-20/N-21/N-24/N-26/S-1/S-2)在 id_aliases.json 登记 status=void 并写明去处——它们被约 110 处代码注释与文档引用,原先只靠「报告里顺带提过 一句」才算有定义,报告一删就集体悬空。N-21/S-1/S-2 一号两义,注记已分别标明。 - 顺带修 id_aliases.json 的 note 被脚本累积重复 26 次;修 registry 里 3 处仍指向改名前的 任务卡文件名(W1-采集保护收尾.md / W8-数据可信.md),以及 iec104_master.py 两处同类注释。 门禁:check_ids --strict 通过(可判定 8747/10410 = 84.02%,基线由 82.6% 抬到 84.0%); check_doc_links --all --sections-strict 通过(177 文档);pre-commit 相关钩子全过。 |
||
|
|
92b8bf9bcf |
docs(任务规划): 销项 S1-c,纠偏并行分道与覆盖率套件数
cov / cov2 两批主机单测已合入 v2-on-ui,规划侧随之过期,本次一并纠偏。 销项 S1-c(sys_log / time_sync 主机侧单测): - test/host/sys_log_mcu/ 106 例、test/host/time_sync/ 4 例均已落地,且被 test-coverage 总闸(^App/|^Drivers/BSP/|^test/host/)全量跑到,比专属钩子 覆盖面更宽,故按"完成项删除"规则从 测试与架构专项.md 删表行与 §2.2 整节。 - 后续小节顺移:A-4 §2.3→§2.2、A-5 §2.4→§2.3,同步改 暂缓-RS485.md 的引用。 - 头部补证据指针到 主机单测覆盖率口径.md §9「P1 落地」/「P2 落地」。 - 任务规划/README.md:测试/基础设施 6→5,W5 行与顺序图去掉 S1-c。 并行分道方案: - 删「批 0 回收 cov」整节(冲突矩阵上移为 §6.1、done 动作变 §6.2), §8 执行清单去掉批 0 段。 - 道C 去掉 S1-c,四分支缩为三分支;冲突矩阵里道C 不再占 .pre-commit-config.yaml 与 test/host/**。 - C1 行去掉已闭环的 E-25(net_select 两处死路径已由主机单测照出并修掉)。 - 修正原 §8 两处会直接跑挂的写法:new 必须两个参数(<ID> <slug>),单参 rc=3; ID 全局唯一(台账 .state/<ID>.json),同簇多任务须靠 ID 区分。 - 补 abort 陷阱:失效 .git 时停在 "is not a working tree",不会替你 rmtree, 需手工删目录 + 删台账 + git worktree prune,删前先查联接。 覆盖率口径: - 套件数 16→33,未挂钩子 12→17,全量耗时 25 s→67 s(2026-10-25 实测, 33 套 5609/6279 行 = 89.3%、函数 92.3%,rc=0 无回退)。 门禁:check_ids --strict 增量拦截通过(校验8 84.62% ≥ 基线 82.6%); check_doc_links --all --sections-strict 178 个文档通过。 |
||
|
|
ea7d0170e2 |
docs(test): 补第 3 轮变更记录(台架必验项 + 投递即拷贝)
合回主干后补上批次变更记录表尾这一行(上一步提交时故意跳过:当时在分支上, 变更记录属单写者文档、按纪律留到主干统一处理)。 行内容覆盖:台架 7 条必验项的实测结论(3 条硬通过、第 5 条给出非破坏性正/负 双向证明 12/12 vs 5/12、第 6 条经用户裁定暂停、需物理拔接 TX 的子项因读端未 接线而不可见)、第一次全量超时的真因(Public 配置文件 + 入站 Allow 只绑被放行 解释器,两条均已登记在 Trap 记录里)、以及由该实验驱动的投递即拷贝改动与它的 RAM 代价。 另记两条自我教训进正文:快投递测到的是设计内的投递侧丢弃(测错对象);钩子 return 导致 testTask 退出被看门狗复位、污染自己的计数。 验证:pre-commit(暂存范围)相关钩子全绿;文档字节口径无 BOM / LF / 无 U+FFFD。 |
||
|
|
4a09d4c63f |
fix(log): syslog_post 投递即拷贝(台架实证驱动的 B 方案整改)
待发条目 syslog_entry_t 原为 {id, const char *desc},只存调用方指针,
隐含「desc 必须活到落盘」的契约,但契约既未写进 sys_log.h、也无机械
检查。台架一次可控实验照出后果:testTask 临时钩子复用同一块缓冲连投
12 条带序号记录、并与 8 MB FTP STOR(占住卷锁)重叠,落盘的 12 条变
成「1~5 + 第 12 条七份」——滞留条目全部别名到该缓冲最后一次写入。
叠加卷忙整批回投(条目可滞留到卷空闲),投栈缓冲即成悬垂读。
改法(B 方案):条目内嵌 char desc[SYSLOG_DESC_MAX + 1u],syslog_post
投递时按 SYSLOG_DESC_MAX 截断拷贝并补终止符;sys_log.h 参数说明同步
写明「投递即拷贝、不要求生命周期覆盖到落盘」。
R1 台架正/负双向实证(非破坏性,钩子与临时改动已全量反向还原):
- 修复在位:12/12 全落、序号严格递增、无重无缺(768 B = 12x64)
- 临时把 -2 支路改成 rc == -2 && 0u:只落 5/12、6~12 全丢,两轮独立
复现一致,且该轮无 queue full 留痕 => 丢的路径确为「卷忙整批丢」
RAM/Flash 前后(scripts/ram_report.py 同口径,同一 .map 来源):
- ZI 131,500 -> 131,852 B(+352 B;s_preboot/s_batch_src 各 8 条
由 8 B 变 32 B);RW_IRAM1 75,280/57.4% -> 75,632/57.7%
- Code 213,720 -> 213,780 B(+60 B);RW 1,204 B 不变
- RW_CCM 静态不变(57,376 B / 87.9%);队列存储多要 192 B 取自
FreeRTOS 堆池,烧后串口水位 [STACK] heap free=15624 min_ever=8464
余量充足;总量仍远低于 mem-budget 92% 红线
测试:sys_log_mcu 套件 104 -> 106 条全绿,新增 TC-SYSLOG-108(投出
后改写调用方缓冲,落盘必须仍是投递时原文)与 TC-SYSLOG-108b(超长
在条目内钳位)。假消息队列护栏 MQ_MAXMSG 由 24 抬到 40:条目变大后
它拒收一切 Put、从既有用例起整片失败,属「尺寸变了就响」的正向记录。
主机侧此前照不出该类问题,是因为用例传字面量、静态存储期无限。
门禁 pre-commit run --all-files:39 项中 37 通过,含 test-coverage
(已覆盖行只增不减)、id-check(0 条台账外新发现、游标 TC-SYSLOG-108)、
doc-links --sections-strict、coding-standard、cppcheck/test-cppcheck、
bsp-err、layer-deps、no-mojibake、ui-golden、font-charset。仅
mem-numbers 与 mem-budget 失败:docs/内存管理/ 受管台账仍记改前
131,500/213,720。按分支约定不在分支上 --write 受管台账,留待合回
主干时统一回填,故 --no-verify 提交。
真机:产品构建 0 错 0 警告,重烧 + 回读校验 OK(suite=NONE dstr=off)、
启动标记齐全、无 [ERR]、VERDICT PASS。
|
||
|
|
924ac3d428 |
test(host): 断言字面量统一纯 ASCII,30 文件 1535 行
用户裁定「统一」。口径就是 AGENTS 铁律:字符串字面量必须纯 ASCII (无 BOM 文件里奇数个汉字会吞掉结束引号,偶数才侥幸通过),中文只写注释。 四名队友按目录分四组并行、写范围互斥:net_socket 418 行(6 文件)、 console_log/sys_log_mcu/soe_store/cfg_file_store_mcu 268 行(4 文件)、 11 个存量套件 404 行 + adc_input 89 行、iec104 与 lftpd 与 NET 四套等 356 行 (9 文件)。runner 的 .py 文档串不在口径内(Python 源不经 ARMCC 代码页)。 两套口径必须分清:粗口径(按行切引号)普查 = 34 文件 / 1,552 行;严格口径 (状态机剥掉块/行注释与字符常量后再取字符串字面量)= 30 文件 / 1,535 行。 差值那 4 个文件的命中全在注释里(ns_peers.c、net_socket/stub/ns_rtos.h、 common/ch395f_fake.h、common/fatfs_fake.c)——按口径一处都不该改,改了等于 把中文从注释里搬走、正好违背口径。粗口径绝不能当交付判据,否则要么白改 注释、要么留下「这行为什么没改」的悬案。 Lead 独立终检(不采信自报)五条硬判据,结果 30 文件漂移 0:① 每文件 TC 号 集合与 HEAD 逐字相同 ② 断言宏调用点数不变 ③ 行数与 HEAD 完全相等(专抓 整文件回写把尾部含 main() 吃掉的截断,第 1 轮真发生过两次)④ 剥注释后代码区 非 ASCII 字符 = 0 ⑤ 字节卫生(BOM 与 HEAD 一致、无 CR、无 U+FFFD、单尾换行)。 另用「逐行把字面量整体替换成空串后与 HEAD 必须逐行相等」机械证明只改了字面量; 各套 --module 覆盖率与断言数逐字不变(net_socket 887/889·53/53、console_log 199/200·16/16、sys_log_mcu 324/326·13/13、adc_input 103/103、sys_clock 100/100) → 生产码零改动,基线不需重登。 闸拦下一处真实回归(我的验收器没抓到):ns_s6_edges.c 翻译时把一个上一轮 已整改成平实描述的编号又写回了 CHECK 标签。原因很清楚:「只改字面量」并不 等于「只改无害内容」,标签里完全可以带进未登记族的编号形态;而 Lead 的自动 验收器只比对了 TC 形 id 集合,未覆盖 L/E/N/S/TRAP 等族。教训:批量改标签的 守卫必须加一条「新串不得出现任何已登记前缀族之外的编号形态」,且结构不变式 只覆盖自己想到的维度时不能替代真闸。 Lead 自查另清掉 126 处写坏的反斜杠(JS→Python 转义层数翻倍,把 markdown 反引号写成反斜杠+反引号),恰好发生在记录这条教训的那一节里。 文档:docs/测试说明/主机单测覆盖率口径.md §9 新增「第 2 轮」小节(两套口径对照 表、五条终检判据、六条手法含「转换说明符序列必须逐位相同」取代「禁止裸 %」、 三条协作纪律含先改板再派工与临时文件带组前缀、后续项 3 条)+ 变更记录一行。 后续项(本轮不做):把剥注释词法扫描器收进 scripts 当门禁(需用户点头并先定 test/ 是否纳入 coding-standard);iec104 的简写编号归族(牵 id_registry, 不能顺手做);替换器加「编号形态白名单」校验。 gate: pre-commit run --all-files -> 39 hooks, 37 Passed; failed = mem-numbers + mem-budget(受管快照与本树 .map 的漂移,属第 1 轮生产码改动的遗留,非本批 增量)。本批只碰 test/host/**,不进 Keil 编译面也不进模拟器 CMake,**RAM/Flash 增量 = 0**;实测水位 RW_IRAM1 57.4%、RW_CCM 87.9%,均低于 92% 红线。 id-check 0 台账外新账(可判定 84.63% > 基线 82.6%);test-coverage 33/33 全绿; cppcheck / coding-standard / bsp-err / layer-deps / doc-links 全 Passed。 按仓内分支约定 --no-verify 提交,台账 --write 回填留待主工作树合并后。 |
||
|
|
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 回填留待主工作树合并后。
|
||
|
|
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,台账回填留待主树合并后。
|
||
|
|
2d7c4fd40b |
ci(gates): test-coverage 的触发面补上门禁自身,否则改坏 files: 就没人跑这根闸
上一提交落地后立刻暴露这个口子:那次提交里新钩子显示 Skipped (no files to check), 因为它自己的 files: 不含 .pre-commit-config.yaml —— 也就是说,任何人改坏这一行 正则(哪怕只是打错一个字符),这根闸就永久静默停用,而提交照样全绿。这正是 本仓反对的"会静默失效的门禁",且已有先例处置:lv-conf-sync 与 bsp-err 都把 "门禁自身"写进触发面。 验证方式(不是只测绿的一半): - 改前:pre-commit run test-coverage --files .pre-commit-config.yaml → Skipped - 改后:同一命令 → 实跑并 Passed(约 45 s,28 套) 代价:以后改门禁配置要多等 45 秒——这种提交本来就是低频操作,值。 顺带修两处上一批留下的表述: - 口径 §8 的 YAML 块补同一个 files: 模式(保持与落库那份一致)。 - 该节抬头那句我上次写坏了:JS 里把续行写成独立表达式,导致"…README.md 里只挂 一行索引指向本文"这半句被丢掉,句子断在文档名上。已补全。 - 门禁说明 host-unit-tests.md 的触发面描述同步为"含门禁自身",并写明理由。 自查:doc-links --all --sections-strict 通过;改动文件 U+FFFD = 0、无 CRLF; 零代码改动故未跑 @build。未 push。 |
||
|
|
744a1cb086 |
ci(gates): 接线 test-coverage 覆盖率总闸(FAIL 语义),并修三处已成假话的门禁文档
用户裁定 2026-10-24:缺 mscoverage 组件时**失败**,不做 skip。于是 P1 之后那个 "钩子只挂 16/28 套"的缺口关闭了。 为什么挂这一根而不是补 12 根 per-module 钩子:本闸跑**全部 28 套**,并且守 "已覆盖行只增不减"的基线台账。这是 per-module 钩子结构性做不到的——删掉一套 测试,它那根钩子只会因为"没有文件要检查"而静默跳过(Skipped 不等于失败)。 实测接线代价:45 s(28 套,含 28 次 MSVC 编译与插桩运行)。 FAIL 语义已按本仓"合成自证"的纪律验证(不是只测绿的那一半):把 DTU_MS_COVERAGE 指到一个不存在的文件 → 钩子 Failed、脚本退出码 2、并打印可 执行的 [ENV] 提示(说明该 exe 装在哪、怎么覆盖)。脚本对显式环境变量刻意 "不做自动探测回退",所以指错会被点名,而不是悄悄换一份工具跑出另一本账。 顺手修掉三条**现在已经不成立**的文档说法(门禁文档最贵的就是骗人): - 口径 §7 第 8 条:原写"只有 BuildTools 且没勾代码覆盖率工具的机器会退出码 2"。 实测本机 Community 18.10 与 BuildTools 18.9 **两份安装都自带**该 exe(mtime 2026-09-15 02:30 / 2026-09-05 16:17,都是各自安装当日,不是后来补勾)→ VS 18 的 BuildTools 默认就带它。真实风险面只剩:裸 MSVC 无 VS 的机器、环境变量指错、 以及靠 PATH 兜底的环境。 - 门禁说明 host-unit-tests.md 盲区第 4 条:原写"没有对应钩子的模块 = 零回归"—— 如今 12 套新模块由总闸兜住,这句话已经不成立,改成两种口径并写清触发面。 - 同篇第 6 条:原写"无覆盖率统计"。行/函数覆盖**已有**度量(本闸出 Cobertura 并 守基线),仍无的只是分支覆盖——那是用户裁定 2026-10-23 记下的**已接受缺口**, 不是待办,改口时把这个区分写清楚。 钩子计数同步(新增 1 根 → 声明 40 根 / pre-commit 阶段 39 根 + commit-msg 的 gitlint,已用脚本核对,不是手数):AGENTS.md(总数、枚举、索引表)、 pre-commit使用说明.md(66 / 91 / 234 三处,含清点日期)、门禁说明 README (正文与索引标题,另加一行索引指向口径文档——23 篇不变,同一事实不在两处维护)、 门禁说明 gitlint.md(--no-verify 跳过的"其它 38 个"→39)、并行任务工作树方案。 自查:end-of-file-fixer 与 trailing-whitespace 复跑 Passed(首轮 --all-files 只 因末行空行红过一次,已按钩子自身规范化);doc-links --all --sections-strict 通过 (178 个文档);id-check --strict 零台账外新账;8 个改动文件 U+FFFD = 0、无 CRLF。 零代码改动,故未跑 @build。未 push。 |
||
|
|
2c3939494d |
docs(测试): 更正覆盖率口径 §8 的接线理由——现在缺口换了方向
P1 合入后本仓主机单测从 16 套涨到 28 套,而 pre-commit 只挂 16 个 -unit-test 钩子。§8 理由 1 写的是"再加一份全量插桩等于同一件事做两遍"——这句话在只有 16 套时成立,现在**反了**:P1 新增的 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)目前只被 ci_local 的 L0.5(自动发现)与 check_test_coverage.py 跑到,**不在提交钩子里**。 仍然不擅自接线,理由只剩 §8 第 2 条那一个,而且它是要用户裁定的:挂之前必须 先定"缺 VS 可选组件(mscoverage)时 skip 还是 FAIL"——选 skip 就是造一根会 静默失效的门禁(与 AGENTS 里"pre-commit 4.x 移除了 always: true、必须靠 files: 覆盖"是同一类坑);选 FAIL 则任何没装该组件的机器都提交不了。与其悄悄挂一根 可能假绿的钩子,不如把缺口写在文档里。 同时更新 §8 下一步第 1 条的过期数字:P1 已吃完(files_tested 19 → 32), 下一步是 P2 的 5 个文件 / 2,226 行;"把已有 19 个文件从 88% 推到 95%"改成按 当前实测口径表述。核对:该文档 U+FFFD=0、无 CRLF,doc-links 钩子 Passed。 |
||
|
|
ec2dbc9e01 |
chore(merge): 回填内存台账与钩子规范化,收口 task/cov-host-line-coverage
done cov 已把 15 个提交合入主干(合并提交 c8de948),但它的门禁按已知顺序跑在 @build 之前(skill 实测陷阱那条),比对用的是合并前的旧 .map,台账必然失配。 这里按 skill 的官方处置在主工作树回填,使主干自身全绿。 内存台账(@build 已在 done 里跑过:0 错 0 警,Code=213652 RO=18860 RW=1192 ZI=131504;主工作树的 .map 是权威值): - scripts/ram_baseline.py --update(趋势行 2026-10-24:RW_IRAM1 75,272 B 57.4% / RW_CCM 57,376 B 87.9%)+ --write(受管区);check_mem_numbers.py --write。 - RAM使用分析与优化方案.md 两处正文数字:「当前状态」那句刷成合并后的真值 (75,264 → 75,272,日期改 2026-10-24);2026-10-23 那条变更行里的裸 B 值 232,536 改为 KB + 快照文件指针——**历史结论一字未改**,只是不再在正文里留 会过期的裸字节值(--scan 的判据正是"曾是区域值、现已不是",无内联豁免)。 - 补一行本次合并的变更:RAM +8 B(net_cfg.c 为 WAIT 契约修复新增 s_save_busy)、 Flash -24 B(lftpd_string 改名删死循环 + net_select 去冗余守卫)、CCM 零增量。 覆盖率口径文档:修我自己留下的占位符泄漏——§9 有一段写的时候用 @@ 当反引号哨兵 (run_code 里反引号与模板串冲突),漏做还原,共 52 处。后果不只是难看: - `docs/网络/网络自愈架构设计.md` §4.10 因失去反引号而不再被识别为"紧邻写法", 被 doc-links(钩子带 --sections-strict)按**自引用**判成失效章节——这条真红了。 - "test/host/common/ch395f_fake.h + .c" 被整体当路径引用检查,报文件不存在。 - 另有一处未配对的 @@ 与一个 ``` 残留,以及过期计数("套件 21+"、"族数 16"), 改为落地实测值:TC-HOST 族 16 → 22、套件 16 → 28,并指向下方落地进度表。 核对:该文档 @@ 残留 0、U+FFFD 0、无 CRLF。 12 个 run_unit_test.py:end-of-file-fixer 去掉末尾多余空行。分支期我用 --no-verify 提交(只为跳过 .map 台账那两条已知误报),把这两个改写型钩子的规范化机会一起 跳掉了——本批由钩子自己改正,**内容零改动**,也正是"提交门禁不该长期绕过"的账单。 自查:pre-commit run --all-files 在本工作树 **38 Passed / 0 Failed**(含 16 个主机 单测钩子实跑);doc-links --all --sections-strict 0 问题;mem-numbers --scan 绿。 未 push(推送需用户明确指示)。 |
||
|
|
957ec64645 |
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 不入库)三条失败,与本分支改动无关。 |
||
|
|
a0c261b5ee |
test(NET/lftpd): lftpd 控制通道模块(35 条),FTP 一套四块全部进分母
新增 test/host/lftpd/,lftpd.c 覆盖 304/473 = 64.3%。做法是把生产源整个 #include 进 TU,于是命令表 commands[]、17 个 static cmd_* 与 handle_control_channel 都能直接驱动: 假 inet 层给"脚本化命令行队列 + 响应记录",假 io 层给"极简内存视图 + 可注入失败", 认证与配置当可注入黑盒(认证本身有 test/host/ftp_auth 的 10 条专测,不重复验)。 钉住的契约(都是真机上贵、却难按需复现的那几类): - STOR 传到一半写失败必须 **unlink 半成品**:否则 Flash 上永久留个坏文件,现场表现 是"升级包传坏了再也传不上去"。真机要卡准"传一半掐线"的时机,这里注一个短写就行。 - 数据通道是单连接 socket:LIST/RETR/STOR 用完必须关监听并把 data_socket 复位成 -1; accept 失败要报 425 不留残口,会话退出(QUIT 或对端断线)时 cleanup 也要关掉还没 用的被动监听。漏一次就是下一条命令拿着上一个号继续 accept,偶发连不上。 - 命令名缓冲只有 command_tmp[4+1],前置判断 if (index >= 5) 就是防 memcpy 越界; 恰好 5 个字符必须被拒(改一行这条就响)。 - 认证前置白名单:未登录只放行 USER/PASS/QUIT/FEAT/SYST/NOOP,其余一律 530;口令错 必须清 auth_user_ok 强制重走 USER;被锁与口令错都只回 530,不给攻击者区分提示; 配置读不到时 USER 无法匹配、PASS 直接 530(fail-closed)。 - 写开关([ftp] write_en=0)必须在**碰文件之前**挡住 STOR/DELE,且不去 accept 数据 通道;DELE 不许删目录。 - 227/229 编码:IP 经 net_htonl 后按高字节到低字节排(就是点分序),端口高/低两字节; 拿不到本机 IP 时要把刚建的监听关掉。FEAT 多行:首行 211-、四条能力无状态码、末行 211 结束(共 6 条响应)。 - 220 欢迎没送达(对端已走)就直接收工,不空转读命令;该留痕按真实分级算 INFO 而不是 ERROR(真实 private/lftpd_log.h 把 lftpd_log_peer 归到 INFO:任何人连 21 端口就走 就能无限制造固件级 ERROR,还会撞掉 @test 的 Forbidden 标记 [ERR])。 - 传输循环每轮喂狗(lftpd_task_wd_beat 计数可断言)。 主机侧两个必要的兼容处理(都写在桩注释里,不是随手改): 1. lftpd.c 会 include ff.h,而这份 FatFs 在 Windows 支路上把 winsock.h 拉进来,它带 "#define s_addr S_un.S_addr" 的 BSD 兼容宏,会把真实 net_types.h 里的字段声明 uint32_t s_addr; 展开成 uint32_t S_un.S_addr; 直接炸(实测 C2143 在 "." 前缺 ";")。 桩里只 #undef 这个污染宏,让固件那份结构体定义原样参与编译,被测代码读的还是真字段。 2. 引号 include 先从 lftpd.c 自己目录找 private/lftpd_log.h,桩目录赢不过它——于是 顺着用真实那份(它只是把 lftpd_log_* 映射成 DBG_*),只桩最底下的 dbg_log.h。这样 真实日志分级也一起被编译验证,比我自己重写一套更对。 自查(35 条里前两轮红了 4 条,全是**我的期望/前置态写错**,实现均正确):未登录时 FOO 被白名单以 530 拦掉测不到 502 分派;227 的 IP 顺序我把 htonl 前后搞反;FEAT 是 6 条响应我按 5 条索引;PASV 之后走完整会话会被 cleanup 关掉监听(那正是 006 要验的 行为),观察 PASV 留下的状态必须直调 cmd_pasv。 覆盖率与台账:套件 26 → 27 全绿;files_tested 30 → 31;未触及 .c 36 → 35;分母 4,072 → 4,545 行。**总体 line_rate 88.4% → 85.9% 是摊薄不是回退**(64.3% 的文件进 了分母),按 §5 口径要盯 files_tested/files_untested——它从 19 一路涨到 31。 恒等式 35 = 1(P1 剩 iec104_serv.c) + 5(P2) + 29(P3)。编号补建 TC-HOST-LFTPD (游标 034),族数 20 → 21。 自查门禁:test-cppcheck Passed;check_ids --strict 通过;doc-links 35 处(= 存量); 9 个文件 BOM/CR/U+FFFD/行尾逐项净。零生产代码改动,未跑 @build。--all-files 仍只有 mem-numbers / mem-budget(.map 台账漂移,等 Lead 合并后统一回填)与 ui-golden (ref/*.bin 不入库)三条失败,与本分支改动无关。 |
||
|
|
8aaf9bbb0e |
test(NET/lftpd): lftpd_inet 模块(28 条,127/127 = 100%)
FTP 的 socket 适配层吃满——全仓第一个 **100% 行覆盖**的文件(127/127)。 新增 test/host/lftpd_inet/,28 条断言全部 MSVC 编译通过并 PASS。 钉住的契约: - 端口字节序:bind 前必须过 net_htons(sin_port 的注释就写着"网络字节序"),问名字 回来要 ntohs 还原;问不到名字报 -1,不拿脏端口当结果。 - 失败路径一律关 socket:建口失败不 bind;bind 失败关口不 listen;listen 失败关口 并各留一条 ERROR(漏关就是白占一个号,CH395F 只有 8 个)。 - accept 每轮失败都让出 CPU(同一段里也喂狗),直到成功;**网络代次一变立刻撤掉 accept**——否则改了 IP 之后监听还挂在旧代次上,端口在新地址上根本不通。这是现场 最常见的自愈路径,台架上要凑"对面正连着我顺手改 IP"极难。 - 带超时的 accept:监听门撤防时**立即放弃**,不对着死监听耗满窗口;对端一直不开 数据通道就按 100ms 步进等满再 INFO 收场。 - read_line:CRLF 就地截成结束符(FTP 命令行的边界);跨包分片要能拼出整行;读到 0 (对端关闭)立刻失败;-2 表示这一片超时继续等;其它负值**原样返回**(错误码不许被 吞成 -1);等待按 200ms 分片、30 s 总预算到点退出(分片写错就饿死看门狗);缓冲 填满仍无行尾要失败且**不越界写**(最后一格留给结束符)。 - 短写续传:net_send 可以只写一部分,续传必须从正确偏移接着写(少半条响应就是协议 错乱);写 0 字节按失败处理;二进制写成功时回请求长度。 桩的口径:private/lftpd_inet.h 与 net_types.h 用真实头;net_socket.h 是桩但函数原型 与真实头逐字一致,并**照做真实头对 FreeRTOS.h / task.h 的 include**(lftpd_inet.c 的 TickType_t / vTaskDelay / pdMS_TO_TICKS 就是从那条链上拿的,桩少这一行就是 C2065)。 lftpd_task_wd_beat 是 __weak 空实现、主机侧替换不掉,因此用同段代码里的"睡 100ms" 次数当喂狗轮数的代理(写进桩注释,不假装能直接数到喂狗)。accept 循环带 100 轮跑飞 保险(net_select 批次被挂死教训换来的)。 覆盖率与台账:套件 25 → 26 全绿;files_tested 29 → 30;未触及 .c 37 → 36;分母 3,945 → 4,072 行;line=3599/4072(**88.4%**,连涨三批)、func=272/299(91.0%)。 恒等式 36 = 2(P1 剩:lftpd.c 与 iec104_serv.c) + 5(P2) + 29(P3) 仍咬合。编号补建 TC-HOST-LFTINET(游标 070),族数 19 → 20。 自查:test-cppcheck Passed;check_ids --strict 通过;doc-links 35 处(= 存量); 9 个新增/改动文件 BOM/CR/U+FFFD/行尾逐项净。零生产代码改动,未跑 @build。 --all-files 仍只有 mem-numbers / mem-budget(.map 台账漂移,等 Lead 合并后统一回填) 与 ui-golden(ref/*.bin 不入库,新 worktree 天生没有黄金参考)三条失败,与本分支无关。 |
||
|
|
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。 |
||
|
|
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 不入库)三条失败,与本分支改动无关。 |
||
|
|
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 天生没有黄金参考)三条失败,与本分支无关。
|
||
|
|
fa4cde36f6 |
docs(覆盖率): §9 新增"假 CH395F 芯片"实施规格,并更正一项被我高估的前置成本
立项假 CH395F:NET 域剩下三块(net_pending 287 / net_events 263 / net_listener 191 = 741 行)都把芯片当真理读,这些语义真机上只在总线上一闪而过、台架不可复现;做完它 连 P2 的 net_socket.c(1,251 行,全仓最大逻辑块)一起解锁。 **一处更正**:我上一轮(提交 8bf4273 的正文与对话汇报)说这三块"还调 net_socket.c 的 内部函数(net_drain_chip_rx / net_relisten_done / net_relisten_locked),要一并给假件"。 实测这三个名字全是 **net_listener.c 文件内的 static**(net_listener.c:86 / :114 / :273), 不存在跨 TU 引用 → 该项前置成本**不成立**,工作量比我说的小。三块之间只走公开 API (net_listener_request/settle、net_pending_*、net_get_sock/net_sock_raw 等)。 规格内容(全部从真实头逐条核出,不凭记忆): - 15 个芯片函数的真实原型,假件必须一字不改照抄;特别标出三种形态: ch395f_get_socket_status / ch395f_get_remot_ipp 是**出参**、 ch395f_sock_state_cached 返回 **ch395f_sock_state_t 枚举**(不是 uint8_t,虽然底层 整型兼容,照抄才会在原型漂移时编译期报错)。 - 假芯片必须复刻的两条芯片怪癖:① get_sock_int_status **整体读即清**、芯片无按位 清除手段 —— 假件要按"返回该 socket 挂起位图并同步清空、可逐 socket 预置"实现, 否则照不出 AGENTS 模块约定里 B3 的实证坑("顺手全量清一遍"把别的在用 Socket 的 门焊死);② sock_state_cached / link_up_cached 是**缓存值**而非现场值,要把"缓存 在第几次调用后更新"做成可脚本化,才能测对账循环读到陈旧状态的路径。 - 脚本化模型沿用本仓既有假件风格(nand_port 的真头+假 diskio、lftpd_io 的可注入假 堆):读函数一条按调用序号取的返回队列、写函数一条调用记录、外加每函数调用计数 以断言"这一轮不该碰芯片"。 - 桩放置点需要拍板,已给推荐:test/host/common/ch395f_fake.h/.c 三模块共享,理由是 三份各自漂移会让"假件与被测代码同时错"这类问题再也报不出来;代价是不进 MCU 构建、 要靠 test-cppcheck 覆盖。若沿用每模块自带桩的惯例,则三份必须 include 同一份头锁签名。 - 实施顺序 net_listener → net_pending → net_events,逐个写明各自要桩什么、判据抄哪份 文档(网络自愈架构设计 §4.10 的读清点与三条准入判据),以及每步的验收动作 (@build 0/0、套件全绿、--update-baseline、先补 TC-HOST-* 族再往文档里引号)。 本轮**只动这一篇文档**(零代码改动,故不跑 @build);check_doc_links --all = 35 处 (= 存量基线,本分支贡献 0);文件 BOM/CR/行尾与 HEAD 一致。 另:本会话上下文见底,故把这块以规格形式交付而不是留下编译不过的半成品 —— 规格里的 原型、static 归属、怪癖与顺序都已核实,下一会话可直接从"写 test/host/common/ ch395f_fake.h"开始。 |
||
|
|
5224f21622 |
fix(NET,lftpd): 处置覆盖率照出的三处死路径(改名 + 修 timeout=0 + 激活异常集)
三处都由本轮 P1 新增的主机单测照出,用户逐条裁定后实施。共同性质:都不是改动引入
的回归,而是**从没被执行到的分支**——生产路径上要么没有调用者、要么没走边界,所以
编译器(0 错 0 警)、cppcheck、真机日志对它都无话可说。
1) 缺陷一(选 a:改名,行为零变化)
lftpd_string_trim 的尾部循环起点落在 strlen(p) 那一格 NUL,isspace(0) 为假 →
循环体一次都不执行,"尾部空白从未被裁",而名字承诺两头都去。改名
lftpd_string_ltrim + 删掉那条死循环 + 在 .h/.c 写明"只去前导"。不去尾才是对的:
RFC 959 以命令终止符 CRLF 界定命令,参数(PASS 口令、含空格的文件名)的尾随
空格属于参数本体,去掉等于静默改数据;CRLF 已由 lftpd_inet_read_line 截断。
调用点 lftpd.c 同步改名。全库无遗留真引用(余 6 处旧名均为解释改名历史的散文)。
2) 缺陷二(按推荐修)
net_select 的 timeout = {0,0} 换算出 timeout_ms = 0,而退出条件写着
if ((timeout_ms > 0) && (已用时 >= timeout_ms)) → 超时永不生效,退化成死等;
POSIX 语义是"只查一轮立即返回"。去掉 timeout_ms > 0 这个错误前置即可。就绪
检查在本判断之前,故有就绪时仍立刻带计数返回;同文件 net_poll_events 的
timeout >= 0 本来就对,现在两条对齐。风险面:拿它当"非阻塞查一遍"会让 netTask
回不到主循环,而 netTask 在 IWDG required_mask 里(串行 socket 栈唯一执行者)。
3) 缺陷三(选 ①:按 POSIX 置位)
exceptfds 是恒为空的死参数:循环开头 p_sock == NULL → continue 先跳过,而
check_socket_error() 只有"表项为空 / fd 越界"才返回 1,越界的 i 又不在本循环
范围内 → 调用方按 POSIX 直觉用它收异常,永远什么都收不到,且返回码不报错。
把异常判定提到 continue 之前:表项不存在的 fd 计入 exceptfds 与返回计数,并更新
函数注释。选 ① 会改变返回计数语义,故补一条"未请求异常集时不计数"的用例,
确保只查可读的老调用方不被误伤(TC-NETSEL-012b)。
测试同步翻转:原 TC-NETSEL-008 / TC-NETSEL-012 是"现状桩、改了会响",现在改成正向
契约,并新增 TC-NETSEL-012c(异常位只落到被请求的那个集合);TC-LFTSTR-005 从
"尾部不裁"的现状登记改为正向契约(尾随空格必须保留)。net_select 22/22、
lftpd_io 33/33 全绿。
@build:0 错误 0 警告。实测体积 ER_IROM1 232,512 B(快照 232,536 → **−24 B**)、
Flash 233,704 B(−20 B)、RW_IRAM1 75,272 B(+8 B 是上一提交 net_cfg 那个字节,
本批零新增)、**RW_CCM 57,376 B 不变**;四档百分比 22.2/57.4/87.9/22.3 全不变。
Code 减少 36 B 即删掉的死循环。
覆盖率台账:门禁如实报了一次 lftpd_string.c 已覆盖行 8 → 7(**代码少了一行**,不是
覆盖退化),已 --update-baseline 重登;现 22/22 套件、line=3003/3446(87.1%)、
func=236/259(91.1%)、tested_files=26、untested_files=118。
文档:主机单测覆盖率口径 §9 新增"三处死路径的处置"小节(裁定、改法、影响、钉住
它的用例一一对应),并把两处"等裁定"的登记改为已处置。
自查:coding-standard / test-cppcheck / id-check --strict(无台账外新账)/ doc-links
(35 处 = 存量)全过;八个改动文件与 HEAD 逐项比对 BOM/CR/U+FFFD/行尾无变化
(这几个文件本就不带 BOM,未被动过)。pre-commit --all-files 仍只有三条失败且与
本分支无关:mem-numbers / mem-budget(.map 与 docs/内存管理 受管快照漂移,增量见
上,按并行分支约定不在分支里 --write,等 Lead 合并后统一回填)与 ui-golden
(simulator/tests/ref/*.bin 在 .gitignore 内,新建 worktree 天生没有黄金参考)。
|
||
|
|
658bc40ccc |
test(NET): net_select 模块(21 条)与 NET 域首份 ch395f 桩,登记两处死路径
P1 的 NET 四块开了第一块:新增 test/host/net_select/,21 条断言全部 MSVC 编译
通过并 PASS。net_select.c 一个芯片函数都不调,所以它是四块里唯一不需要假芯片的,
先把它拿下,顺带把 NET 域的桩骨架立起来。
钉住的契约:
- 就绪位回写:入参集合里未就绪的 fd 必须被清掉,只留就绪的(POSIX 语义);未请求
的集合不被写回、可以传 NULL。
- 状态准入:LISTENING 即使 readable 也不算可读(accept 语义不在这层);ESTABLISHED
+ send_ready 才可写;CONNECTING 时 send_ready 不作数。
- 边界:nfds 为 0 / 负 / 超过 NET_MAX_SOCKETS 都返回 -1(不许扫出 socket 表外)。
- 超时算术:tv_usec 是**整除截断**(1999us 当 1ms 而不是 2ms);timeout=NULL 时
无限等并由对端事件唤醒;poll 的 timeout=0 首轮即退、每轮先清 revents。
用可控 tick(每问一次 +1)+ 可控 osDelay(计数、可指定轮数后造一个就绪 socket)
把这些变成确定性轮数,台架上根本构造不出"没人来连时该怎么退"。
照出两处**死路径**(只登记,未改语义;全库唯一调用点 test/board/net_test_task.c
传的是 NULL 与非零 tv,今天没有受害者):
1. net_select 的 timeout = {0,0} 换算出 timeout_ms = 0,而退出条件写的是
if ((timeout_ms > 0) && (已用时 >= timeout_ms)) → **超时 0 永不触发退出**,退化成
无限等;POSIX 在这里必须立刻返回 0。同文件 net_poll_events 写的 timeout >= 0
是对的,两条并排放着正好是对照。风险面:谁拿它当"非阻塞查一遍",netTask 就
回不到主循环,而 netTask 在 IWDG required_mask 里。
2. exceptfds 是恒为空的死参数:循环体开头 p_sock == NULL → continue 先跳过,
check_socket_error 的 NULL 分支从 select 里到不了。按 POSIX 直觉用它收异常的人
会永远什么都收不到。
两处都用"现状桩"钉成改了会响(用例名里写明不是期望行为)。另修掉我自己写错的一条
用例:给 timeout=NULL 又没有就绪 fd,直接死循环——已在假 osDelay 里加了 500 轮
保险(跑飞就 exit(2)),因为挂死的测试会把 exe 文件名占住、让后续构建报 LNK1104。
桩的边界写死在文件头:net_select.h / net_types.h / net_config.h 用真实头(net_sock_t
字段名与 NET_SOCK_STATE_* 一改即编译期报错);main.h / cmsis_os.h / FreeRTOS.h /
task.h 是本目录桩;新建的 ch395f.h 是**空壳**,只服务不调芯片函数的文件——
net_pending / net_events / net_listener 直接调十余处 ch395f_*,要的是可脚本化返回的
假芯片,不要把这个空壳当成"NET 域桩已就绪"。
覆盖率:套件 21 → 22 全绿;files_tested 25 → 26;未触及 .c 41 → 40;分母 3,332 →
3,446 行;net_select.c 97/114(85.1%)。恒等式更新为 40 = 6(P1 剩) + 5(P2) + 29(P3)。
编号按 B 层口径补建 TC-HOST-NETSEL(游标 019),TC-HOST 族现共 16 族;注册表仍只
做增量插入。
自查: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 天生没有黄金参考)三条失败,与本分支无关。
|
||
|
|
6bdf063f50 |
test(NET/lftpd): P1 增补 lftpd_io 模块(33 条),登记 FTP 域一处潜伏缺陷
新增 test/host/lftpd_io/:一个 TU 同时纳入生产源 lftpd_io.c 与 lftpd_string.c (两者静态符号不冲突,省一套桩),33 条断言全部 MSVC 编译通过并 PASS。 钉住的契约(都是真机上偶发、日志看不出根因的那类): - 路径规范化:**".." 上跳过头仍被钳在根内**("/a" + "../../x" = "/x")——FTP 客户端 可发任意路径,这是安全属性;绝对名忽略 base、"." 丢弃、段间多余斜杠折叠、 NULL 入参容忍、全空结果归 "/"。 - 分配失败:这个函数返回 pvPortMalloc 出来的指针(历史上正是它落在 C 库 malloc 上, RAM 方案 §4.2.1)。用**可注入失败的假堆**跑两条分支:首块失败 → NULL;结果缓冲 失败 → NULL **且第一块已 vPortFree**(不泄漏堆)。模拟器宿主 CRT 永不失败,这条 只有假堆能构造。 - 卷锁时机:open 成功**故意不释放**(整个传输期独占卷,由 close 释放),失败必须 释放并留 ERROR;单文件槽守卫 s_file_open 命中时**不碰卷锁也不碰 FatFs**。 - 等卷锁循环每轮喂看门狗(lftpd_task_wd_beat):defaultTask 与 FTP 争卷时这里可能 空转上百毫秒,漏喂就是整机复位。脚本化 vol_lock_wait 的返回序列直接构造 2 轮空等。 - lftpd_string_trim:行首空白由**返回指针**跳过、原缓冲不搬移(调用点 lftpd.c 用的 正是返回值);全空白串返回空串。 **本批照出一处,未改语义、只登记**:lftpd_string_trim 的尾部循环写成 for (i = strlen(p); i >= 0 && isspace(p[i]); i--),i 起步那格是 NUL、isspace(0) 为假 → 循环一次都不执行,**尾部空白与 CRLF 从未被裁掉**。今天不发作是因为 CRLF 在 lftpd_inet_read_line 里已用 strstr 截断、且唯一调用点只关心行首;风险在于"以后有人 拿它去尾部空白"不会生效。修法只要一行(i 从 strlen(p)-1 起),但那会改变 FTP 参数 (含空格的文件名/口令)的可见行为,需要 FTP 域一起过,**留给用户裁定**;当前用 TC-LFTSTR-005 把现状钉成"改了会响"(明确标注是现状桩、不是期望行为)。 未覆盖也写在文件头:lftpd_io_read/write/close/stat/unlink/opendir/readdir/closedir 要先把假 FIL/FILINFO 的字段语义(fptr、obj.objsize、fname 等)做实,否则断言会落在 假件自己写的规则上;这 8 个 f_* 只给了可链接的 FR_INT_ERR 实现,走进即失败不静默。 覆盖率:套件 20 → 21 全绿;files_tested 23 → 25;未触及 .c 43 → 41;分母 3,163 → 3,332 行;lftpd_io.c 85/159(53.5%)、lftpd_string.c 8/10。总体 line_rate 88.9% → 87.2% —— **这是摊薄不是回退**(新进来的文件只测了一半,分母涨得比分子快),已按 §5 口径写进口径文档 §9 的落地进度表;恒等式更新为 41 = 7(P1 剩) + 5(P2) + 29(P3)。 编号:按 B 层口径补建 2 族 TC-HOST-LFTPIO(游标 037)与 TC-HOST-LFTSTR(005), TC-HOST 族现共 **15** 族;id_registry.json 仍只做增量插入(无重排)。 自查:test-cppcheck Passed;coding-standard Passed;check_ids --strict 通过(无台账外 新账);check_doc_links --all = 35 处(= 存量,本分支贡献 0);新增/改动文件 BOM/CR/ U+FFFD/行尾卫生逐项净。**本增量零生产代码改动**(只加测试、注册表与文档),故未跑 @build。pre-commit --all-files 仍只有三条失败且与本分支无关:mem-numbers / mem-budget (本 worktree 上次 @build 生成的 .map 与受管快照漂移,增量 ER_IROM1 +12B、RW_IRAM1 +8B、CCM 0,按并行分支约定不在分支里 --write,等 Lead 合并后统一回填)与 ui-golden (simulator/tests/ref/*.bin 在 .gitignore 内,新 worktree 天生没有黄金参考)。 |
||
|
|
b1291f1308 |
test(NETCFG,CFG): P1①四个主机单测模块,并修 net_cfg 的 WAIT 契约与定长错记
按 docs/测试说明/主机单测覆盖率口径.md §9 的清单吃 P1 第①批「配置与工具」四块, 新增 4 个主机单测模块共 104 条断言,全部 MSVC 编译通过并 PASS: - test/host/net_cfg/(45 条):出厂默认可用、注册幂等与内容、load 报 -1 仍返回 0 (RAM 恒有效契约)、跨任务保存交接状态机 IDLE/WAIT/OK/FAIL、MAC 三态优先级、 IPv4 定长格式化。 - test/host/ftp_cfg/(16 条):[ftp] 段注册(**p_cb 必须为 NULL**,口令下次登录才 生效)、默认口令判定大小写不敏感、NULL 一律判不等(fail-closed)。 - test/host/prot_set_store/(16 条):blob 返回码分流 1 无档 / 0 成功 / **2 换代 (只打 INFO)** / -1 损坏;写盘失败必须回置脏标志(否则定值静默丢失)。 - test/host/task_prof/(27 条):无效样本闸门(防 DWT 复位把 max 抬到 2^32)、 未 enter 就 exit 不记账、首次 5s + 每 2s 节流、环形缓冲与裸发两通道、 周期→微秒换算随 SystemCoreClock 变。 覆盖率收益(已 --update-baseline 登记):套件 16 → 20 全绿;files_tested 19 → 23; 未触及 125 → 121 个文件(.c 47 → 43、9,657 → 9,228 行);分母 2,893 → 3,163 插桩 行;line=2811/3163(88.9%)、func=225/240(93.8%)。§9 的恒等式同步更新且仍咬合: 43 = 9(P1 剩) + 5(P2) + 29(P3)、9,228 = 2,188 + 2,226 + 4,814。 新单测照出并修掉两处真问题(都在 net_cfg,不是测试写错): 1. 实现违反自己的文档契约:net_cfg.h 明写"属主任务写盘中显示 WAIT",但 net_cfg_save_run 为了不丢新请求会先清 pending 再写盘 → 写盘那几百毫秒里 UI 保存行退回上一轮的 OK/IDLE(第一次保存按 ENTER 后屏幕上还是 ENTER),用户只 会再按一次。补 s_save_busy 标志,TC-NETCFG-028/026/030 钉死(含失败路径必须 清 busy,否则 UI 永久卡 WAIT)。 2. "定长 11 字符"是错的:%03u.%03u.%03u.%03u = 4 段×3 位 + 3 个点 = **15**。这个数 错了 4 处(net_cfg.h、net_cfg.c、网络配置说明.md 两处)——是我照抄它才撞上 FAIL,错在文档不是代码。已全部更正,并确认 len ≥ 16 的要求与 UI_BUF_STRLEN=32 本来就没问题(无截断风险)。 桩的口径(沿用既有模块,不另起一套):真实头优先——cfg_file.h / cfg_file_store.h / console_log.h / prot_set.h 全用真头(接口一漂移就编译期报错),只有 main.h 必须桩 (真实 main.h 拉整棵 HAL 头树,PC 上编不动;桩里字段名与位掩码宏名与 CMSIS 一致)。 prot_set_store 连真实 prot_set.c 一起编:blob 的编解码与脏标志在它里面,只有拿真 serialize 产出的 blob 才能造出"换代"与"损坏"两种真实形态,手搓等于测自己的桩。 配置引擎本身(解析/写盘/沉降)归 test/host/cfg_file 的 103 条,本批不重复测。 编号门禁:按 2026-10-21 批 3 的 B 层口径补建 4 族 TC-HOST-*(NETCFG 游标 056 / FTPCFG 014 / PROTSTOR 016 / TASKPROF 020,代码即属主登记处)。这一步不是走形式: 建族之前文档里引用 TC-NETCFG-028 被判「台账之外新账」直接卡提交,建族后才通过; 编号规范 §8.4 变更记录加一行说明族数 9 → 13。id_registry.json 只做增量插入(+88 行, 无重排)。 自查(提交时 pre-commit run --all-files,**仅 3 条失败且都与本分支改动无关**): - mem-numbers / mem-budget:本 worktree 因需验证代码改动而 @build 过,新生成的 .map 与 docs/内存管理 受管快照漂移。实测增量 ER_IROM1 +12 B、RW_IRAM1 +8 B、Flash +16 B、**CCM 0**,水位百分比全不变(改动 = 1 个静态字节 + 几条指令)。按并行分支 约定**不在分支里 --write**,台账由 Lead 合并后统一回填。 - ui-golden:simulator/tests/ref/*.bin 写在 .gitignore 里(黄金参考是本地生成物), 新建的 worktree 天生没有,报的是"缺黄金参考"而非像素差异;本分支零 UI 文件改动。 - 其余全部 Passed,含 @build 0 错误 0 警告、test-cppcheck(它抓到过我一条 p!=NULL 后又解引用的写法,已改为断言+显式守卫)、coding-standard、doc-links(35 处=存量)、 id-check --strict、mojibake 与 BOM/CR/U+FFFD/行尾逐项卫生。 - 自审修掉自己写的两条恒真断言(形如 x==0?1:1 的假绿)与一个重复编号标签。 - 4 个新模块**尚未挂 pre-commit 钩子**:.pre-commit-config.yaml 是单写文件,任务工作 树内不改;覆盖率脚本按 test/host/**/run_unit_test.py 自动发现,已实跑 20/20。 - 未跑 @test:改动仅 1 个静态字节 + UI 保存行的显示时序,无硬件语义变化。 |
||
|
|
e70b861215 |
docs(覆盖率): 新增 §9 未触及面建桩优先级清单(P1/P2/P3 逐个画像)
回答"那 125 个到底怎么吃":对 47 个未触及 .c(9,657 有效代码行)逐个量化后再排序, 不靠拍脑袋。排序口径写进文档、可复核可反驳:价值 = 有效代码行 × 逻辑度 × 缺陷历史 (该文件的 fix/refactor 提交数),成本 = 要桩的外部头数 − 能复用的现有桩;逻辑度 = 1 − 硬件符号密度(HAL_*() / FatFs f_*() / CH395F_* / SPI-I2C / OSAL 调用数 ÷ 行数)。 三档(数字互相咬合,覆盖率"不该 100%"在这里是算出来的): - P1 先吃 13 个 .c = 2,617 行:配置与工具 429(真实头即可编译,建议第一个落地,先用它 跑通"新增文件进台账 + NOTE 不误报回退"这条链)→ FTP 一套 1,083(20 次提交里 6 次 fix,最近两条都是静默型)→ NET 拆出的四块 903(须新建 stub/ch395f.h)→ 104 服务层 202。 - P2 值得但工程量大 5 个 = 2,226 行:net_socket.c 1,251(51 函数,**必须分片**,先测状态 迁移与语义判据);假 FatFs 一次吃 sys_log_mcu/cfg_file_store_mcu/soe_store 共 710; console_log 265(单写者契约 K1/K2 已写成表)。 - P3 明确不做 29 个 = 4,814 行,并把理由写死免得下次又列回待办:芯片驱动 1,990(假到底 ≈ 重写驱动,测的是自己的桩)、UI 侧 1,942(与模拟器同一份源码,已由 ui-golden 覆盖)、 任务编排 747(4~5 个函数的装配层,写成单测就是"断言我调了几次")、app_main 135 (被测对象本身就是内核启动前的时序约束)。 - 恒等式:47 = 13 + 5 + 29、9,657 = 2,617 + 2,226 + 4,814;做完 P1+P2 约 4.8k 行进分母 (现分母 2,893),files_tested 19 → 37。 配套口径修正: - "125 个未触及"易被读成 125 个待办,三处写精确 = 47 个 .c(9,657 行)+ 78 个 .h (2,182 行,多为声明与宏);待办池是那 47 个 .c。 - §8 下一步①改为指向 §9;终点判据从百分比改成"untested_sources 里剩下的每一条都能在 P3 表里找到归属",并提醒盯 files_tested/files_untested 而不是 88.2%(新增大块未测代码 会把它摊薄,那不是回退)。 自查:check_doc_links.py --all = 35 处(与主干同数,本分支贡献 0;期间修掉一处 §4.5 被 误算成本文自引用);check_ids.py --strict 通过;check_mojibake + BOM/CR/行尾卫生净。 本轮纯文档,零 .c/.h 改动,未跑 @build、未占 build/bench 锁。 |
||
|
|
6505cef9df |
feat(门禁): coding-standard 加 CODE-RULE-20 禁 C 库堆分配(销 §4.8 空白 ②)
规则:在**掩码后**的文本上匹配 malloc/calloc/realloc/strdup/strndup/ free/aligned_alloc 调用。三条判据都是踩过坑之后定的—— - 大小写敏感:pvPortMalloc / vPortFree(唯一允许的配对)不会误报; - 词边界 \b:dhara_nand_is_free(、buf_alloc( 这类同形后缀不误报; - 用掩码:注释与字符串字面量里写 malloc 不报(规范示例就踩过这种)。 覆盖名比 AGENTS 列的五枚多带上 strndup / aligned_alloc——同族同类, 漏一个就等于留后门。 出处不在规范 V1.0,在平台约束:MicroLIB 的 C 堆由 startup_stm32f407xx.s 的 Heap_Size EQU 0x200 只有 512 B,KB 级分配必返 NULL,而模拟器(宿主 CRT 堆)完全正常、**照不出来**——配置引擎 2026-09-18 那次堆分配纠偏与 lftpd 的 malloc 隐患都是**先上线才发现**的那类,靠评审等于没管。 落地前按 门禁说明/coding-standard.md §8 的纪律先全库普查:钩子范围内 C/H 共 151 个文件,命中**只有** simulator/sim/cfg_file_store_sim.c 两行, 即 cfg_store_alloc/cfg_store_free = **平台分配后端的宿主侧实现**(设计依据 写在 docs/子系统设计/配置文件设计说明.md 的"MCU 端 = pvPortMalloc、 模拟器端 = 宿主 CRT")。所以零基线、直接硬拦截;豁免用脚本里的 HEAP_ALLOC_ALLOWLIST(**文件路径 + 函数名**双条件),刻意不做目录级豁免: 下次谁要碰 C 堆就得往这张表加一行,评审看得见。 自证:临时样例文件里 malloc/calloc/free 各报一处,pvPortMalloc/vPortFree/ buf_alloc(、注释、字符串五种陷阱形态全部不误报; pre-commit run coding-standard --all-files → **Passed**(= 全库零存量)。 顺带修三处口径不一致: - 规范 V1.0 §6.1 的示例自己写着 malloc/free(通用 C 模板遗留)→ 已在示例 下加"本仓例外,别照抄"批注,事实源指向 AGENTS.md 平台约束; - docs/门禁说明/mem-budget.md 里"这条纪律只靠 AGENTS 铁律与评审"已过期 → 更正为由 CODE-RULE-20 机器拦截; - AGENTS.md 平台约束条加"已机器拦截 + 唯一豁免"一句;scripts/README.md 补登 check_test_coverage.py(上一个提交漏登记)。 另:本次一并修好上一处编辑事故——报告 §4.8 曾被我的脚本调用截成半句 (只落第一行、丢掉 malloc 那半句),已重写为"两处空白均已闭合"。 自查:pre-commit run --files(9 个改动文件)适用项全 Passed; check_doc_links.py --all = 35 处,与主干同数(本分支贡献 0); check_ids.py --strict 通过(裸编号 P0-1 改平实说法,避免记成新账); 9 个文件 BOM/CR/U+FFFD/行尾与 HEAD 逐项一致。本轮零 .c/.h 改动,未跑 @build、未占 build/bench 锁;分支已 rebase 到 v2-on-ui,覆盖率脚本全量 回归仍 16/16、line=2552/2893 不变。 |
||
|
|
2bfa09ed15 |
feat(门禁): 主机单测覆盖率脚本与口径落地(销报告 §4.8 空白 ①)
落地物: - scripts/check_test_coverage.py:发现规则与 L0.5 同一条(test/host/**/ run_unit_test.py),直接 import 各运行器复用其 TestBuild 与 unit_test_harness._BASE_FLAGS —— 配置零复制,不会漂出第二套口径; 只算自研生产源码(App/**、Drivers/BSP/**)的行/函数、多套取并集、 生成物(文件头写"自动生成/请勿手改")不进分母、分母一律来自插桩 报告不猜行数;中间物全在 %TEMP%,不新增 .gitignore 规则。 - scripts/test_coverage_baseline.json:增量拦截台账(只拦回退不拦进步, 与 layer-deps / bsp-err 同姿态);ci_local.py 加 opt-in --coverage。 - docs/测试说明/主机单测覆盖率口径.md:口径 + 工具链实测 + 九条盲区。 - 报告 §0.1 / §0.4 / §4.8 / §5.5 / §7 改写 + 变更记录 + 去处表。 工具链是实测出来的(换机必读):Windows 原生侧无任何 gcc/gcov(全盘扫 Program Files*、msys64、scoop、choco、.platformio、LocalAppData = 0 处); MSVC 14.51 已砍 /coverage,且只报 D9002 后照常编译成功 = 静默假数据; CodeCoverage.exe 17.0 三个子命令只打印用法。最终用 mscoverage 18.x (Microsoft.CodeCoverage.Console.exe)collect -f cobertura 做加载期动态 插桩,并实测它透传子进程退出码 → "插桩运行下仍必须 PASS"可当硬条件。 首登数字:16/16 套插桩下全绿,并集 line=2552/2893(88.2%)、 func=193/207(93.2%)、19 个文件在测、**未触及 125 个生产源文件 / 约 11,839 行**——最后一栏才是这套口径真正照出来的东西。 门禁自证:人为抬高基线后 行/函数/总计 三条回退路径都报 REGRESS 且 退出码 1;--module 子集跑不再假报总计回退(首版会,已修:并集口径下 子集必然变小,全部回退判定只在全量跑时生效)。 GCC 追问的复测结论(口径文档 §2.1 决策记录):本机 WSL2 Ubuntu 确实有 gcc/gcov 15.2.0,16 套单测在 gcc 下全部编过、用例数与 MSVC 逐个一致, gcov -b -c 也确实给出分支覆盖(nand_ftl.c 分支至少走过一次 41.40%); 但换编译器=换分母(sha256.c gcc 105 行 vs MSVC 114 行;adc_input.c 101 vs 120)。**用户裁定:本仓只留 MSVC 一条覆盖率通道**,不建第二条—— 多一套工具链就多一套分母、多一本台账、多一个"这台机器到底有没有 WSL/ gcov"的未决项,换来的只有分支这一维。分支覆盖因此**记为已接受缺口** (不是待办):绿灯只证明行与函数被测到,补偿判据 = TC 双向追溯 + 用例显式列边界值;§8 写明重开该裁定的前提是先答"两份分母怎么对齐"。 刻意未挂 pre-commit:16 个单测钩子已经跑过同一批测试;依赖 VS 可选 组件(缺组件时 skip 会静默失效、FAIL 会拦换机);待一个真实回退事件。 YAML 片段与理由写在口径文档 §8。 自查:pre-commit run --files(6 个改动文件)全 Passed;check_doc_links --all 回到存量 35 处(本分支贡献 0);check_ids.py --strict PASS; check_mojibake + BOM/CR/U+FFFD/行尾卫生逐项自查全净。本轮零 .c/.h 改动 (纯 .py/.json/.md),故未跑 @build、未占 build/bench 锁。 |