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 列(名称 / 载体 / 抓住什么)。
This commit is contained in:
+4
-4
@@ -8,7 +8,7 @@
|
||||
> 日常改代码**不需要读本目录**:提交前的等价自查是本地门禁,runner 在不在线都照样跑——
|
||||
>
|
||||
> ```powershell
|
||||
> & '.venv\Scripts\python.exe' scripts/ci_local.py # L0 静态 + L0b 提交信息 + L0.5 主机单测
|
||||
> & '.venv\Scripts\python.exe' scripts/ci_local.py # 静态门禁静态 + 提交信息门禁 提交信息 + 主机侧纯逻辑单测主机单测
|
||||
> ```
|
||||
>
|
||||
> 只有在这三种情况下才需要进来:**① 换机/重装后要重新部署 runner;② CI 不出任务或莫名
|
||||
@@ -71,7 +71,7 @@
|
||||
|
||||
## 相关文档
|
||||
|
||||
- 测试分层与门禁决策(L0~L3 各层由谁把关):[`../测试说明/测试开发标准流程.md`](../测试说明/测试开发标准流程.md)
|
||||
- 测试分层与门禁决策(静态门禁~人工目检各层由谁把关):[`../测试说明/测试开发标准流程.md`](../测试说明/测试开发标准流程.md)
|
||||
- workflow 本体:[`../../.gitea/workflows/ci.yml`](../../.gitea/workflows/ci.yml)
|
||||
- 本地等价脚本:[`../../scripts/ci_local.py`](../../scripts/ci_local.py)
|
||||
- runner 机器的工具链环境要求:[`../环境与部署/项目部署环境要求.md`](../环境与部署/项目部署环境要求.md)
|
||||
@@ -81,9 +81,9 @@
|
||||
|
||||
| 日期 | 变更 |
|
||||
|---|---|
|
||||
| 2026-10-12 | 🐛 **首次真跑出 run**:`L0.5` **成功**(`10 runner(s), 647/647, fail=0`,与本地逐字一致);`L0` **假失败**——gitlint 因 runner 无 git 身份而 `exit 254`(`Missing git configuration: please set user.name`),旧文案把它说成"提交信息违规",且该步 `exit 1` 连带让 26 个钩子全部没跑。已在 `ci_gitlint_range.ps1` 用 `GIT_CONFIG_*` 注入一次性身份(不写文件)并区分 254/1 退出码。另记:**新建分支的首次 push 不触发 run**,需 `workflow_dispatch` 或再推一个非文档提交。详见指南 §7 |
|
||||
| 2026-10-12 | 🐛 **首次真跑出 run**:`主机侧纯逻辑单测` **成功**(`10 runner(s), 647/647, fail=0`,与本地逐字一致);`静态门禁` **假失败**——gitlint 因 runner 无 git 身份而 `exit 254`(`Missing git configuration: please set user.name`),旧文案把它说成"提交信息违规",且该步 `exit 1` 连带让 26 个钩子全部没跑。已在 `ci_gitlint_range.ps1` 用 `GIT_CONFIG_*` 注入一次性身份(不写文件)并区分 254/1 退出码。另记:**新建分支的首次 push 不触发 run**,需 `workflow_dispatch` 或再推一个非文档提交。详见指南 §7 |
|
||||
| 2026-10-12 | 🔀 本轮 CI 修复随 `master` **合入 `v2-on-ui`**(该分支另有 21 个功能提交;冲突仅 `AGENTS.md` 与 `docs/README.md` 两处,解法是"保留其新版结构 + 把 CI 指针搬进对应位置")。因该分支门禁更多(**26 钩子 / 10 单测套件** vs master 的 18 / 3),指南里写死的数字已改为**规则 + 分分支快照**,见指南 §6 / §7 / §10 |
|
||||
| 2026-10-12 | ✅ 双看门狗缺陷**实测收敛**(管理员 `-StartNow` 清掉 3 个残留 → 单实例、链路恢复、runner online)。另修两处:daemon 日志走 stderr 被 PS 5.1 `2>&1` 包成 ErrorRecord(2 行真日志写成 8 行,每 4 行 3 行噪声)→ 启动器改 `ForEach-Object { "$_" }`;`-Status` 诚实化(非管理员读不到 SYSTEM 进程命令行 → 明说需提权而非谎报"无看门狗";重启循环判据改看进程存活时长,不再靠不轮转日志的尾部字符串报假警)。详见指南 §7.3 |
|
||||
| 2026-10-12 | 🔴 **修复"双看门狗抢 `.runner.lock`"缺陷**:`Unregister-ScheduledTask` 不停旧实例进程 → 孤儿看门狗养着旧 daemon 占锁,新看门狗每 10 秒失败一次,而 runner 网页照样 online、`-Status` 也说"在跑"。现 `-StartNow` 先清残留进程再启动、`-Status` 报重复实例与锁冲突。同日清理机器侧:`config.yaml` 头部错误指引("改用 sc.exe / New-Service")改为看门狗口径,删除已废弃的 `deploy_runner_task.ps1` 与 `run_runner.cmd`,`check_env.ps1` 提示同步改指 bootstrap |
|
||||
| 2026-10-12 | **单一口径清理**:合并稿初版对 4 处分歧"两说并存",现统一为本机实测现状——注册口径(`gitea-runner v4.0.1` / `D:\Docker\act_runner` / `127.0.0.1:3001`,旧 `act_runner v0.2.11` + `C:\act_runner` 段删去可复制命令只留警示)、`config.yaml`(`fetch_timeout: 1m0s`、`timeout: 30m0s`)、常驻方式(只认看门狗,`deploy_runner_task.ps1` 标废弃)、验收数字(L0.5 `288/288`,历史数字标注为快照)。并补"机器侧遗留脚本勿误用"警示 |
|
||||
| 2026-10-12 | **单一口径清理**:合并稿初版对 4 处分歧"两说并存",现统一为本机实测现状——注册口径(`gitea-runner v4.0.1` / `D:\Docker\act_runner` / `127.0.0.1:3001`,旧 `act_runner v0.2.11` + `C:\act_runner` 段删去可复制命令只留警示)、`config.yaml`(`fetch_timeout: 1m0s`、`timeout: 30m0s`)、常驻方式(只认看门狗,`deploy_runner_task.ps1` 标废弃)、验收数字(主机侧纯逻辑单测 `288/288`,历史数字标注为快照)。并补"机器侧遗留脚本勿误用"警示 |
|
||||
| 2026-10-12 | **新建本目录**:远程 CI 内容原先散在 `docs/环境与部署/`(两篇互相重叠的指南)、`docs/测试说明/测试开发标准流程.md` §9、`AGENTS.md` 提交门禁节共 4 处,约 2000 行;现合并为单一指南并把 `AGENTS.md` 压成一行指针。动因:远程 CI 只涉及本机一台 runner,与日常开发规范混在一起每次会话都被读,造成困惑 |
|
||||
|
||||
+33
-33
@@ -6,7 +6,7 @@
|
||||
> 日常改代码的等价自查是本地跑 [`scripts/ci_local.py`](../../scripts/ci_local.py)
|
||||
> (与 CI 同一套判定),**不依赖远程 CI 是否在线**——runner 掉线不影响本地门禁。
|
||||
|
||||
> **适用**:为 STM32F4-Base 部署 / 排障 CI(`.gitea/workflows/ci.yml`,L0 + L0.5)。
|
||||
> **适用**:为 STM32F4-Base 部署 / 排障 CI(`.gitea/workflows/ci.yml`,静态门禁 + 主机侧纯逻辑单测)。
|
||||
> **前置阅读**:[docs/测试说明/测试开发标准流程.md §9](../测试说明/测试开发标准流程.md)(CI 覆盖边界与判定细节)、
|
||||
> [docs/环境与部署/项目部署环境要求.md §2.11](../环境与部署/项目部署环境要求.md)(runner 环境要求)。
|
||||
> 本文只讲**怎么配**,不重复"为什么这么设计"。
|
||||
@@ -43,9 +43,9 @@
|
||||
> `@build` → 该钩子在远端只会被跳过(现以 `SKIP` 名单诚实显示 Skipped,不再伪装 Passed)。
|
||||
> 补上的强制路径 = **同码凭证**:本地 `@build` 0/0 后 `ram_baseline.py --update` 顺手盖
|
||||
> `scripts/size_attestation.json`(三道前置:`.map` 可解析 / `.map` 不早于指纹内源码 mtime /
|
||||
> 受管区 ≤92%);CI 无需 Keil,L0 作业末尾的「体积门禁」步用 `scripts/check_size_gate.py --ci`
|
||||
> 受管区 ≤92%);CI 无需 Keil,静态门禁作业末尾的「体积门禁」步用 `scripts/check_size_gate.py --ci`
|
||||
> 复核两件事——**工作树指纹 == 凭证指纹**(改了代码从没测过 = 拦)+ **按凭证数字复跑 92% 红线**。
|
||||
> 主干 push 拦红,其余事件 `--warn-only`;本地 `ci_local.py` 的 L0c 步无条件 fail-closed。
|
||||
> 主干 push 拦红,其余事件 `--warn-only`;本地 `ci_local.py` 的 静态门禁c 步无条件 fail-closed。
|
||||
>
|
||||
> 仍守不住的三项(正文唯一口径 = `scripts/check_size_gate.py` docstring):CI 不重算绝对数字
|
||||
> (只复核同码性与红线复算);编译器选项(`.uvprojx`)不在指纹内;凭证由本地真实构建产生,
|
||||
@@ -64,7 +64,7 @@
|
||||
│ │ │
|
||||
└────────────┬───────────┴──────────────────────────────┘
|
||||
▼
|
||||
push / PR 触发 → runner 拉起容器/壳 → 跑 L0 + L0.5 → 结果回显到提交
|
||||
push / PR 触发 → runner 拉起容器/壳 → 跑 静态门禁 + 主机侧纯逻辑单测 → 结果回显到提交
|
||||
```
|
||||
|
||||
**三者缺一,CI 就是空转的**(workflow 存在但永不执行 / 执行但拉不到 runner)。
|
||||
@@ -113,7 +113,7 @@
|
||||
| Windows runner | 已注册且 **labels 含 `windows`**;装有 Python 3.x、cppcheck、pre-commit、MSVC(VS2019/2022 C++ 工具链 + Windows SDK) |
|
||||
| **runner 必须有看门狗** | `gitea-runner` v4 的 `daemon` **无服务支持、且首次 Declare 失败就直接退出进程** → Gitea 重启一次,CI 就静默停摆(2026-10-11 实测:run 16~21 共 12 个 job 零日志)。用 **`scripts/ci_runner_bootstrap.ps1`** 装"等 Gitea 就绪 + 崩溃自拉起"的看门狗 + 计划任务,见 §2.4 |
|
||||
| 机器级工具变量 | runner 以 **SYSTEM** 运行,看不见交互用户的 PATH/用户级 site。用 `ci_runner_bootstrap.ps1 -SetEnv` 写入 **MACHINE** 级 `DTU_PRECOMMIT_PYTHON` / `DTU_CPPCHECK` / `DTU_MSVC_CL`,见 §4.1.3 |
|
||||
| 缺 MSVC 时 | L0.5 降级为 WARNING 跳过(不硬失败),但**等于该层裸奔**,须尽快补装 |
|
||||
| 缺 MSVC 时 | 主机侧纯逻辑单测降级为 WARNING 跳过(不硬失败),但**等于该层裸奔**,须尽快补装 |
|
||||
|
||||
---
|
||||
|
||||
@@ -363,7 +363,7 @@ log:
|
||||
runner:
|
||||
file: .runner # 注册凭据(由 register 生成,勿手改)
|
||||
capacity: 1 # 并行 job 数;单机建议 1(cppcheck 吃 CPU)
|
||||
timeout: 30m0s # 单 job 超时;L0+L0.5 约 1~3 分钟
|
||||
timeout: 30m0s # 单 job 超时;静态门禁+主机侧纯逻辑单测约 1~3 分钟
|
||||
fetch_timeout: 1m0s # 🔴 不得超过 RPC timeout(1m0s):原写 5m0s 会被自动截断并刷告警,2026-10-02 已改
|
||||
fetch_interval: 2s
|
||||
# 🔴 标签:windows 必须与 workflow 的 runs-on: windows 逐字一致
|
||||
@@ -656,9 +656,9 @@ runner 是一台干净的 Windows,**下面这些必须装**,否则 job 会
|
||||
| **Python 3.x** | 🔴 必需 | `l0-gates` 环境自检失败 → **job 硬失败** | `winget install Python.Python.3.12` |
|
||||
| **cppcheck** | 🔴 必需 | 同上(`cppcheck --version` 探测不过) | `winget install Cppcheck.Cppcheck` |
|
||||
| **pre-commit** | 🔴 必需 | 同上(`python -m pre_commit --version` 探测) | `pip install pre-commit` 或 `uv tool install pre-commit` |
|
||||
| **MSVC(VS + Windows SDK)** | 🟡 建议 | 缺则 `l05-host-unit-tests` **告警降级跳过**(不硬失败,但 L0.5 裸奔) | `winget install Microsoft.VisualStudio.2022.BuildTools`,勾"使用 C++ 的桌面开发" |
|
||||
| **MSVC(VS + Windows SDK)** | 🟡 建议 | 缺则 `l05-host-unit-tests` **告警降级跳过**(不硬失败,但 主机侧纯逻辑单测裸奔) | `winget install Microsoft.VisualStudio.2022.BuildTools`,勾"使用 C++ 的桌面开发" |
|
||||
| Keil / ST-LINK | ⛔ 不需要 | CI 刻意不跑 `@build` | — |
|
||||
| 开发板 / 串口 | ⛔ 不需要 | L1/L2/L3 不上 CI | — |
|
||||
| 开发板 / 串口 | ⛔ 不需要 | 板端测试套件/主机侧回归/人工目检不上 CI | — |
|
||||
|
||||
Git 另算:`git --version` 任意版本即可(你已在用)。
|
||||
|
||||
@@ -679,10 +679,10 @@ runner 以 **SYSTEM 服务/计划任务**运行,它看到的 PATH 与 site-pac
|
||||
|
||||
| 你以为 | SYSTEM 实际看到 | 后果 |
|
||||
|---|---|---|
|
||||
| `python` 在 PATH 里 | PATH 上是 `C:\msys64\ucrt64\bin\python.exe`,**没有 pre-commit** | L0 环境自检失败 |
|
||||
| `python` 在 PATH 里 | PATH 上是 `C:\msys64\ucrt64\bin\python.exe`,**没有 pre-commit** | 静态门禁环境自检失败 |
|
||||
| `pip install --user pre-commit` 装好了 | **用户级 site 对 SYSTEM 完全不可见**(换任何 python 路径都没用) | 同上 |
|
||||
| 仓库里有 `.venv` | `.venv` 已 `.gitignore`,runner 全新工作区**根本没有** | 同上 |
|
||||
| `cppcheck` 在 PATH 里 | 机器 PATH 无 `C:\Program Files\Cppcheck` | L0 环境自检失败 |
|
||||
| `cppcheck` 在 PATH 里 | 机器 PATH 无 `C:\Program Files\Cppcheck` | 静态门禁环境自检失败 |
|
||||
|
||||
> 机制补充(2026-10-02 实测):计划任务以 **`SYSTEM`** 账户运行时,继承的是**系统级 PATH**
|
||||
> (注册表 `HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Environment` 的 `Path`),
|
||||
@@ -719,7 +719,7 @@ python -m pre_commit --version
|
||||
|
||||
#### 4.1.1 ⚠️⚠️ 关键坑:`pre-commit` 必须在「全局 site-packages」,装到用户态等于没装
|
||||
|
||||
这是 2026-10-02 排查 **L0 job 反复失败**(`::error::no Python with a SYSTEM-visible pre-commit`)的真正根因,
|
||||
这是 2026-10-02 排查 **静态门禁 job 反复失败**(`::error::no Python with a SYSTEM-visible pre-commit`)的真正根因,
|
||||
也是本环境最隐蔽的一个坑。
|
||||
|
||||
**现象**:`pre-commit` 在你自己的终端里好好的(`pre-commit --version` 有输出),
|
||||
@@ -836,9 +836,9 @@ workflow 已改为统一消费 `DTU_*` 接口(见 [本地工具路径环境变
|
||||
|
||||
| 变量 | 作用 | 未设置时的回退 |
|
||||
|---|---|---|
|
||||
| `DTU_PRECOMMIT_PYTHON` | 指向装有 pre-commit 的 python.exe(L0/L0.5 用) | PATH 的 `python` → 工作区 `.venv`(干净克隆里通常没有) |
|
||||
| `DTU_CPPCHECK` | 指向 cppcheck.exe(L0 钩子用) | SYSTEM 的 PATH(用户级 PATH 不可见) |
|
||||
| `DTU_MSVC_CL` | 指向 x64 `cl.exe`(L0.5 用) | `%ProgramFiles%`/`%ProgramFiles(x86)%` 推导的 VS 标准根自动探测 |
|
||||
| `DTU_PRECOMMIT_PYTHON` | 指向装有 pre-commit 的 python.exe(静态门禁/主机侧纯逻辑单测用) | PATH 的 `python` → 工作区 `.venv`(干净克隆里通常没有) |
|
||||
| `DTU_CPPCHECK` | 指向 cppcheck.exe(静态门禁钩子用) | SYSTEM 的 PATH(用户级 PATH 不可见) |
|
||||
| `DTU_MSVC_CL` | 指向 x64 `cl.exe`(主机侧纯逻辑单测用) | `%ProgramFiles%`/`%ProgramFiles(x86)%` 推导的 VS 标准根自动探测 |
|
||||
|
||||
设置(**必须管理员**;scope 用 `Machine` 才能被 SYSTEM 任务看到):
|
||||
|
||||
@@ -870,7 +870,7 @@ Start-ScheduledTask -TaskName GiteaActionsRunner
|
||||
**本机实测命中第 1 条(2026-10-02)**:
|
||||
`C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.29.30133\bin\Hostx64\x64\cl.exe` ✅
|
||||
|
||||
> ⚠️ **装在完全非标准位置(如 D 盘自定义目录)会探测不到** → 被当成"没装 MSVC"跳过 L0.5(仅告警)。
|
||||
> ⚠️ **装在完全非标准位置(如 D 盘自定义目录)会探测不到** → 被当成"没装 MSVC"跳过 主机侧纯逻辑单测(仅告警)。
|
||||
> 处置(二选一):设机器级 `DTU_MSVC_CL` 指向 `cl.exe`(推荐;值无效会硬报错),
|
||||
> 或把 VS 装回标准位置(探测按 `%ProgramFiles*` 推导,换盘符的标准布局仍命中)。
|
||||
>
|
||||
@@ -971,8 +971,8 @@ github.com 会在 checkout 步骤卡住/失败。三种处置:
|
||||
|
||||
| 层 | 汇总行 |
|
||||
|---|---|
|
||||
| L0 | `L0: pre-commit hooks -- passed=N failed=M skipped=K (log: ci-artifacts\l0-pre-commit.log)` |
|
||||
| L0.5 | `L0.5: <K> runner(s), <A>/<B> assertions passed, fail=<F>`(逐模块另有 `::notice::<模块>: N/M PASSED (failed=0)`) |
|
||||
| 静态门禁 | `静态门禁: pre-commit hooks -- passed=N failed=M skipped=K (log: ci-artifacts\l0-pre-commit.log)` |
|
||||
| 主机侧纯逻辑单测 | `主机侧纯逻辑单测: <K> runner(s), <A>/<B> assertions passed, fail=<F>`(逐模块另有 `::notice::<模块>: N/M PASSED (failed=0)`) |
|
||||
|
||||
> ⚠️ **`ci-artifacts/` 已加入 `.gitignore`**:它是 runner 工作区的运行时产物,不要提交。
|
||||
>
|
||||
@@ -988,11 +988,11 @@ github.com 会在 checkout 步骤卡住/失败。三种处置:
|
||||
|
||||
| job | `runs-on` | 步骤要点 |
|
||||
|---|---|---|
|
||||
| `l0-gates` | `windows` | checkout(`fetch-depth: 0`,gitlint/cubemx 需完整历史)→ 环境自检(**Python 探测走 `scripts/ci_find_python.ps1`**、cppcheck 按 `DTU_CPPCHECK` → PATH → 标准安装目录,缺则 `::error::` + exit 1)→ `pre_commit install --install-hooks` → **提交信息门禁 `scripts/ci_gitlint_range.ps1`**(见 §7 排障表"钩子条数对不上"行)→ **决定跳过哪些钩子**(探测 `.map`,写入 `GITHUB_OUTPUT`;无 `.map` 时 `::warning::` 声明体积门禁未跑)→ `pre-commit run --all-files --show-diff-on-failure`(`env: SKIP`,输出 `Tee` 到 `ci-artifacts/l0-pre-commit.log`)→ 打印 `::notice::L0: passed/failed/skipped` 汇总行 → 归档日志说明 |
|
||||
| `l05-host-unit-tests` | `windows` | checkout → **探测 MSVC**(按 VS_ROOTS 找 `Hostx64\x64\cl.exe`)→ 遍历 `test/host/**/run_unit_test.py` 逐个跑(输出 `Tee` 到 `ci-artifacts/l05-<模块>.log`;解析各模块 `N/M PASSED` 汇总行 → 聚合 `::notice::L0.5: K runner(s), A/B assertions passed`;失败 `::error::` 并置退出码);**未探测到 MSVC 时打印醒目 WARNING 并降级跳过**(不硬失败,避免"没装 VS 的 runner 全红") |
|
||||
| `l0-gates` | `windows` | checkout(`fetch-depth: 0`,gitlint/cubemx 需完整历史)→ 环境自检(**Python 探测走 `scripts/ci_find_python.ps1`**、cppcheck 按 `DTU_CPPCHECK` → PATH → 标准安装目录,缺则 `::error::` + exit 1)→ `pre_commit install --install-hooks` → **提交信息门禁 `scripts/ci_gitlint_range.ps1`**(见 §7 排障表"钩子条数对不上"行)→ **决定跳过哪些钩子**(探测 `.map`,写入 `GITHUB_OUTPUT`;无 `.map` 时 `::warning::` 声明体积门禁未跑)→ `pre-commit run --all-files --show-diff-on-failure`(`env: SKIP`,输出 `Tee` 到 `ci-artifacts/l0-pre-commit.log`)→ 打印 `::notice::静态门禁: passed/failed/skipped` 汇总行 → 归档日志说明 |
|
||||
| `l05-host-unit-tests` | `windows` | checkout → **探测 MSVC**(按 VS_ROOTS 找 `Hostx64\x64\cl.exe`)→ 遍历 `test/host/**/run_unit_test.py` 逐个跑(输出 `Tee` 到 `ci-artifacts/l05-<模块>.log`;解析各模块 `N/M PASSED` 汇总行 → 聚合 `::notice::主机侧纯逻辑单测: K runner(s), A/B assertions passed`;失败 `::error::` 并置退出码);**未探测到 MSVC 时打印醒目 WARNING 并降级跳过**(不硬失败,避免"没装 VS 的 runner 全红") |
|
||||
|
||||
> 🔴 **两条 Python 探测链必须同序同步**:本地 `scripts/ci_local.py` 与 CI 侧
|
||||
> `scripts/ci_find_python.ps1` 的候选顺序要一致。2026-10-11 那次 L0 全灭,就是 workflow
|
||||
> `scripts/ci_find_python.ps1` 的候选顺序要一致。2026-10-11 那次 静态门禁全灭,就是 workflow
|
||||
> 单方面把候选收窄成"PATH 上的 python + 仓库 `.venv`"(而 runner 上这两条**都是死路**:
|
||||
> PATH python 没装 pre-commit、`.venv` 被 gitignore 根本不存在)导致的。改一边必须改另一边。
|
||||
|
||||
@@ -1009,10 +1009,10 @@ github.com 会在 checkout 步骤卡住/失败。三种处置:
|
||||
| 3 | `scripts\ci_runner_bootstrap.ps1 -Status` | 计划任务已注册 / daemon 在跑 / Gitea 可达 / 三个 `DTU_*` 已设——**四项全绿** |
|
||||
| 4 | runner 上 `python --version` / `cppcheck --version` / `python -m pre_commit --version` | 三条全通(且对 SYSTEM 成立,见 §4.1) |
|
||||
| 5 | `git push` 一次(或页面 Run workflow) | Actions 页出现运行中任务 |
|
||||
| 6 | `l0-gates` 日志 | `using python (…)` + `runner environment check passed`;`::notice::L0: pre-commit hooks -- passed=.. failed=0 skipped=..`;无 `.map` 时见 `::warning::` 体积门禁未跑 |
|
||||
| 6 | `l0-gates` 日志 | `using python (…)` + `runner environment check passed`;`::notice::静态门禁: pre-commit hooks -- passed=.. failed=0 skipped=..`;无 `.map` 时见 `::warning::` 体积门禁未跑 |
|
||||
| 7 | `l0-gates` 的 **提交信息门禁** 步骤 | `::notice::gitlint: N commit message(s) checked, 0 failed`(**这一行以前根本不存在**,见 §7) |
|
||||
| 8 | `l05-host-unit-tests` 日志 | `::notice::L0.5: <N> runner(s), A/A assertions passed, fail=0`。**`N` = `test/host/` 下 `run_unit_test.py` 的个数**(2026-10-12 实测:`master` 3 个 / 288 断言,`v2-on-ui` 10 个);断言总数随单测增减而变,判定只看 **`fail=0` 且 A==B**,不要拿历史数字当基准 |
|
||||
| 9 | job 运行页的 **Summary** 区块 | 出现 "L0 static gates" / "Commit message gate (gitlint)" / "L0.5 host-side unit tests" 小标题与汇总行 |
|
||||
| 8 | `l05-host-unit-tests` 日志 | `::notice::主机侧纯逻辑单测: <N> runner(s), A/A assertions passed, fail=0`。**`N` = `test/host/` 下 `run_unit_test.py` 的个数**(2026-10-12 实测:`master` 3 个 / 288 断言,`v2-on-ui` 10 个);断言总数随单测增减而变,判定只看 **`fail=0` 且 A==B**,不要拿历史数字当基准 |
|
||||
| 9 | job 运行页的 **Summary** 区块 | 出现 "静态门禁 static gates" / "Commit message gate (gitlint)" / "主机侧纯逻辑单测 host-side unit tests" 小标题与汇总行 |
|
||||
| 10 | 提交记录旁出现 ✅ | CI 全绿 |
|
||||
|
||||
**本地先自查**(与 CI 同一套判定,避免推上去才发现问题):
|
||||
@@ -1067,8 +1067,8 @@ date +%s # 对比 last_online
|
||||
| `checkout` 步骤卡住/超时 | 拉不到 `actions/checkout@v4`(内网) | 见 §5.3 |
|
||||
| `l05` 日志出现 `::warning::runner 未安装 MSVC` | VS 装在非默认路径或未装 | 见 §4.2(默认路径 / 同步改两处根目录) |
|
||||
| `mem-numbers` 显示 `Skipped` | 正常(CI 无 Keil,无 `.map`) | 无需处理;但注意 **CI 未守体积门禁**(见 §0.0),改代码后本地 `@build` + `check_mem_numbers.py --scan` 兜底 |
|
||||
| L0.5 显示 `2 runner(s), X/Y ...` 但 X<Y | 有单测用例未通过 | 看该模块的 `::error::unit test failed` 行与其 `ci-artifacts/l05-<模块>.log` |
|
||||
| L0.5 报 `produced no '=== ... N/M PASSED ===' summary line` | 该模块运行器未输出汇总行(改了 harness 或运行器崩溃) | 核对 `test/host/common/unit_test_harness.py` 的 `summary_marker` 与该模块 `TestBuild` 是否一致 |
|
||||
| 主机侧纯逻辑单测显示 `2 runner(s), X/Y ...` 但 X<Y | 有单测用例未通过 | 看该模块的 `::error::unit test failed` 行与其 `ci-artifacts/l05-<模块>.log` |
|
||||
| 主机侧纯逻辑单测报 `produced no '=== ... N/M PASSED ===' summary line` | 该模块运行器未输出汇总行(改了 harness 或运行器崩溃) | 核对 `test/host/common/unit_test_harness.py` 的 `summary_marker` 与该模块 `TestBuild` 是否一致 |
|
||||
| `Tee-Object` 步骤报 `TerminatorExpectedAtEndOfString` / 字符串缺少终止符 | `run:` 块混入了非 ASCII(GBK 解码错位) | 见文件头 🔴🔴;`run:` 内必须纯 ASCII |
|
||||
| `l05` 报 `fatal error C1083: 无法打开包括文件 "stdio.h"` | Windows SDK 缺失 | 装 VS 时勾"使用 C++ 的桌面开发"+ SDK |
|
||||
| 单测退出码 2 | 环境错误(多为无 MSVC) | 同上 |
|
||||
@@ -1208,8 +1208,8 @@ runner 的日志走 **stderr**,于是上面两个日志文件都栽在这两
|
||||
- **钩子集变化**(`.pre-commit-config.yaml` 增删钩子):CI 自动跟随,无需改 workflow;
|
||||
但**必须核对条数**——`pre-commit run --all-files` 只跑 `pre-commit` stage,
|
||||
`commit-msg` stage 的钩子(gitlint)不在其中,靠 `ci_gitlint_range.ps1` 单独守(见 §7);
|
||||
- **`@build` / L1 / L2**:永远留在本地,不要往 CI 加(需 Keil 授权与真机);
|
||||
- **runner 长时间不可用**时:CI 只是"多一道网",**提交前的本地 `ci_local.py` 仍是 L0 的唯一保障**,
|
||||
- **`@build` / 板端测试套件 / 主机侧回归**:永远留在本地,不要往 CI 加(需 Keil 授权与真机);
|
||||
- **runner 长时间不可用**时:CI 只是"多一道网",**提交前的本地 `ci_local.py` 仍是 静态门禁的唯一保障**,
|
||||
不能因"已经配了 CI"放宽(见 [docs/任务规划/登记簿.md](../任务规划/登记簿.md) 的条目「CI 远端闭环未验证」)。
|
||||
|
||||
---
|
||||
@@ -1352,18 +1352,18 @@ powershell -ExecutionPolicy Bypass -File scripts\ci_runner_bootstrap.ps1 -DryRun
|
||||
|
||||
| 日期 | 变更 |
|
||||
|---|---|
|
||||
| 2026-10-25 | 🔒 **A-5 残余销项联动(§0.0 重写)**:体积门禁由"CI 不守"改为**同码凭证强制**——本地 `@build` 0/0 后 `ram_baseline.py --update` 盖 `scripts/size_attestation.json`,L0 作业末尾 `check_size_gate.py --ci` 复核同码性 + 红线复算(runner 无需 Keil);主干 push 拦红、其余事件 warn-only;`mem-budget` 补进 SKIP 名单诚实显示。指纹取 git 索引 blob(跨 checkout 稳定,修 7c4628b);三项仍守不住的口径唯一正文 = 脚本 docstring |
|
||||
| 2026-10-12 | 🐛 **第一次真跑出 run,当场暴露 gitlint 门禁空转**:把本轮修复推到新分支 `v2-on-ui-ci` 后拿到 task 31/32——**L0.5 成功**(`10 runner(s), 647/647 assertions passed, fail=0`,与本地逐字一致),**L0 失败**却与提交信息毫无关系:`exit code: 254` + `Missing git configuration: please set user.name`。根因是 `actions/checkout` 把 HOME 指向每次任务的临时目录、SYSTEM 账号没有 git 身份,gitlint 在读到规则之前就配置错误退出;旧脚本还把失败原因**写死**成"标题超 100 字符",加上该步 `exit 1` 让后面 26 个钩子**一个都没跑**。修法:脚本先探测身份,缺失就用 `GIT_CONFIG_COUNT/KEY/VALUE` 注入一次性身份(不写文件、不碰开发者机器),并区分 254(门禁没跑起来 → `exit 2`)与真实违规(→ `exit 1`)。另记一条新坑:**新建分支的首次 push 不产生 run**(§7 排障表 ④) |
|
||||
| 2026-10-12 | 🔀 **随 `master` 合入 `v2-on-ui`:数字口径改为分支中立**。本指南原先写死 master 的数字(18 钩子 / 17 报告、3 runner / 288 断言),而 `v2-on-ui` 是 **26 钩子 / 10 套件**——同一篇文档在两个分支上必然有一个是错的。§6 验收第 8 行、§7 排障表、§10 验收标准三处改为讲规则(**报告数 = 定义数 − 1**;**runner 数 = `test/host/` 下 `run_unit_test.py` 个数**)+ 分分支实测快照。本次合并只带文档与脚本,**零 C 代码改动**;合并后本分支 `@build` 0 错 0 警、`ci_local.py` 全绿(L0 25 钩子 + L0.5 10 runner / 647 断言) |
|
||||
| 2026-10-25 | 🔒 **A-5 残余销项联动(§0.0 重写)**:体积门禁由"CI 不守"改为**同码凭证强制**——本地 `@build` 0/0 后 `ram_baseline.py --update` 盖 `scripts/size_attestation.json`,静态门禁作业末尾 `check_size_gate.py --ci` 复核同码性 + 红线复算(runner 无需 Keil);主干 push 拦红、其余事件 warn-only;`mem-budget` 补进 SKIP 名单诚实显示。指纹取 git 索引 blob(跨 checkout 稳定,修 7c4628b);三项仍守不住的口径唯一正文 = 脚本 docstring |
|
||||
| 2026-10-12 | 🐛 **第一次真跑出 run,当场暴露 gitlint 门禁空转**:把本轮修复推到新分支 `v2-on-ui-ci` 后拿到 task 31/32——**主机侧纯逻辑单测成功**(`10 runner(s), 647/647 assertions passed, fail=0`,与本地逐字一致),**静态门禁失败**却与提交信息毫无关系:`exit code: 254` + `Missing git configuration: please set user.name`。根因是 `actions/checkout` 把 HOME 指向每次任务的临时目录、SYSTEM 账号没有 git 身份,gitlint 在读到规则之前就配置错误退出;旧脚本还把失败原因**写死**成"标题超 100 字符",加上该步 `exit 1` 让后面 26 个钩子**一个都没跑**。修法:脚本先探测身份,缺失就用 `GIT_CONFIG_COUNT/KEY/VALUE` 注入一次性身份(不写文件、不碰开发者机器),并区分 254(门禁没跑起来 → `exit 2`)与真实违规(→ `exit 1`)。另记一条新坑:**新建分支的首次 push 不产生 run**(§7 排障表 ④) |
|
||||
| 2026-10-12 | 🔀 **随 `master` 合入 `v2-on-ui`:数字口径改为分支中立**。本指南原先写死 master 的数字(18 钩子 / 17 报告、3 runner / 288 断言),而 `v2-on-ui` 是 **26 钩子 / 10 套件**——同一篇文档在两个分支上必然有一个是错的。§6 验收第 8 行、§7 排障表、§10 验收标准三处改为讲规则(**报告数 = 定义数 − 1**;**runner 数 = `test/host/` 下 `run_unit_test.py` 个数**)+ 分分支实测快照。本次合并只带文档与脚本,**零 C 代码改动**;合并后本分支 `@build` 0 错 0 警、`ci_local.py` 全绿(静态门禁 25 钩子 + 主机侧纯逻辑单测 10 runner / 647 断言) |
|
||||
| 2026-10-12 | ✅ **双看门狗缺陷实测收敛** + 修掉日志里的 PS 5.1 stderr 包装噪声与 `-Status` 假警。① 管理员 `-StartNow` 杀掉 3 个残留(孤儿看门狗 26900/19716 + 占锁 daemon 53848)→ `daemon up: pid 40360 (single instance)`,监管链 `svchost → 看门狗 → daemon`,看门狗日志 25 秒零增长、runner 回 online。② 新坑:daemon 日志写 **stderr**,PS 5.1 的 `2>&1` 把每行包成 ErrorRecord → `gitea-runner.exe : ` 前缀 + 4 行 `CategoryInfo` 块,实测 2 行真日志写成 8 行(**每 4 行有 3 行是噪声**,且就在文档让大家第一个打开的文件里)。启动器改为 `2>&1 \| ForEach-Object { "$_" } \| Out-File -Encoding utf8`(实测与 cmd 包装等价但免引号剥离坑,`$LASTEXITCODE` 不受影响);§7.2 方式 B 的示例原本正是 `> log 2>&1`(UTF-16LE + 包装双坑),已改;新增 §7.3 汇总这两条。③ `-Status` 两处诚实化:非管理员**读不到 SYSTEM 进程命令行**(`CommandLine` 为 null → 过滤器静默匹配 0 个),改为明说"需提权"而不是谎报"没有看门狗";重启循环判据不再靠"日志尾部有没有错误串"(文件不轮转,修好后旧失败仍在尾部 → 对健康 runner 报假警),改看 **daemon 进程存活时长**(CIM `CreationDate` 非管理员可读,`Process.StartTime` 对 SYSTEM 进程不可读) |
|
||||
| 2026-10-12 | 🔴 **实测揪出"双看门狗抢锁"缺陷并修复** + 机器侧清理。① 现象:runner 网页 online、`-Status` 说 daemon 在跑,但 `runner-watchdog.log` 每 10 秒刷 `starting daemon → daemon exited, exit code 1`,daemon 日志 `runner file is already locked by another process`。② 根因:`Unregister-ScheduledTask` **不停旧实例启动的进程**——13:22 重跑 bootstrap 时旧看门狗(12:53 起)成孤儿并继续养着旧 daemon、占住 `.runner.lock`,新看门狗永远抢不到锁,任务实际谁也没管,日志以约 20 MB/天在刷。③ 修复:`ci_runner_bootstrap.ps1` 的 `-StartNow` 现在**先停孤儿看门狗与残留 daemon 再启动**并回显单实例确认;`-Status` 新增重复实例计数与锁冲突检测。④ 机器侧清理:`config.yaml` 头部那句**错误指引**("常驻改用 Windows sc.exe / New-Service",正是 §3.0 证伪的路)改为看门狗口径并指向本文;删除已废弃的 `deploy_runner_task.ps1` 与 `run_runner.cmd`(§3.2),`check_env.ps1` 指向它们的提示同步改指 bootstrap。⑤ 核实:`config.yaml` 改后 YAML 可解析且**所有配置值与改前逐项一致**(只动注释);`check_env.ps1` 带 BOM 且语法 0 错;删除前确认计划任务动作目标是 `run_runner_watchdog.ps1`,删除不影响在跑 runner |
|
||||
| 2026-10-12 | 🧹 **单一口径清理(以本机实测现状为准)**:合并稿初版对 4 处分歧采取"两说并存 + 衔接注",本次统一。① 注册口径——`act_runner v0.2.11` + `C:\act_runner` + 公网域名 instance 一段**删除可复制命令**,只留"勿照抄"警示(核实 `C:\act_runner` 至今不存在,本机是 `gitea-runner v4.0.1` + `D:\Docker\act_runner` + `127.0.0.1:3001`);② `config.yaml` 示例 `fetch_timeout` 由 `5m0s` 改为磁盘实际值 **`1m0s`**、`timeout` 统一 `30m0s`,并补"不得超过 RPC timeout(1m0s)";③ 常驻口径统一为看门狗——§3.2 `deploy_runner_task.ps1` 标为**已废弃**并说明失效机理(单次 daemon = 正常退出,失败重启永不触发),§3.3 手动等价命令**重写为看门狗版任务对象**(旧版会教人手搓出坏配置),§10 TL;DR 第 4 步改指 `ci_runner_bootstrap.ps1`;④ 数字口径——L0.5 期望值更新为 **288/288**(run#8 的 39/39 与 278/278 标注为历史快照,判定只看 `fail=0` 与 A==B),钩子数按现行 18 定义 / 17 报告。另同步:健康三查与排障表改用现行日志名(`runner-daemon.log` / `runner-watchdog.log`,`runner-stdout.log` 标为遗留且说明其混编码)、BOM 示例对象换成 `check_env.ps1` 并注明看门狗是纯 ASCII 无需 BOM、配套脚本表补 `run_runner_watchdog.ps1` 并标出磁盘上仍存的废弃脚本建议删除、补 `Next Run Time` 为空属正常的说明 |
|
||||
| 2026-10-12 | 🧹 **单一口径清理(以本机实测现状为准)**:合并稿初版对 4 处分歧采取"两说并存 + 衔接注",本次统一。① 注册口径——`act_runner v0.2.11` + `C:\act_runner` + 公网域名 instance 一段**删除可复制命令**,只留"勿照抄"警示(核实 `C:\act_runner` 至今不存在,本机是 `gitea-runner v4.0.1` + `D:\Docker\act_runner` + `127.0.0.1:3001`);② `config.yaml` 示例 `fetch_timeout` 由 `5m0s` 改为磁盘实际值 **`1m0s`**、`timeout` 统一 `30m0s`,并补"不得超过 RPC timeout(1m0s)";③ 常驻口径统一为看门狗——§3.2 `deploy_runner_task.ps1` 标为**已废弃**并说明失效机理(单次 daemon = 正常退出,失败重启永不触发),§3.3 手动等价命令**重写为看门狗版任务对象**(旧版会教人手搓出坏配置),§10 TL;DR 第 4 步改指 `ci_runner_bootstrap.ps1`;④ 数字口径——主机侧纯逻辑单测期望值更新为 **288/288**(run#8 的 39/39 与 278/278 标注为历史快照,判定只看 `fail=0` 与 A==B),钩子数按现行 18 定义 / 17 报告。另同步:健康三查与排障表改用现行日志名(`runner-daemon.log` / `runner-watchdog.log`,`runner-stdout.log` 标为遗留且说明其混编码)、BOM 示例对象换成 `check_env.ps1` 并注明看门狗是纯 ASCII 无需 BOM、配套脚本表补 `run_runner_watchdog.ps1` 并标出磁盘上仍存的废弃脚本建议删除、补 `Next Run Time` 为空属正常的说明 |
|
||||
| 2026-10-12 | **文档合并**:本次把 `CI配置指南.md` 与 `Gitea_Runner部署指南.md` 合并为本篇(`docs/CI/远程CI完整指南.md`)并迁入 `docs/CI/`,去重(runner 注册 / config.yaml / 装软件 / 排障表两篇均有)、补入 runner 看门狗与机器级变量实测教训;相对链接按新位置改写,原两篇互引改为本文内部引用 |
|
||||
| 2026-10-11 | **bootstrap 自身修复**:`StartWhenAvailable` 误设到**触发器**上(该属性属**任务设置**,触发器没有)→ 脚本在"已注销旧任务、尚未注册新任务"之间抛 `SetValueInvocationException`,把 runner 任务删没了。现改为①先构建 action/trigger/settings、**后**注销旧任务,②`Register-ScheduledTask` 失败时显式告警"旧任务已删、CI 裸奔",③新增 **`-DryRun`**(免管理员即可全量校验任务对象),④`-Status` 在非管理员下额外提示"看不见任务 ≠ 任务不存在"。同时更正根因:旧任务其实存在,但跑的是**单次** `daemon`——daemon 属"正常退出"而非"失败",计划任务的失败重启**救不到**它,必须用看门狗循环重拉 |
|
||||
| 2026-10-11 | **修"很多任务不执行"(runner 静默停摆)+ 补 gitlint 门禁**:① §2.4 重写——`gitea-runner` v4 的 `daemon` **无服务支持、首次 Declare 失败即退出进程**(日志实证),Gitea/机器重启一次 CI 就停摆,**run 16~21 共 12 个 job 零日志**(`cancelled`/`queued`,叠加 `cancel-in-progress` 后表现为"任务凭空消失"而非失败);新增 **`scripts/ci_runner_bootstrap.ps1`**(等 Gitea 就绪 + 崩溃自拉起的看门狗 + 开机计划任务 + `-SetEnv` 写机器级 `DTU_*` + `-Status` 只读体检)。② §3.1 重写——SYSTEM 可见性三重坑(PATH python 是 msys64 无 pre-commit / 用户级 site 对 SYSTEM 不可见 / `.venv` 已 gitignore)是 run 14~15 L0 全灭根因(`33a4aa4` 把候选收窄成这两个、又删了 cppcheck 目录兜底,而机器级 `DTU_*` 从未设置);探测收进 **`scripts/ci_find_python.ps1`**(两 job 共用、可离线自测、失败列出全部候选)并恢复机器级安装兜底。③ 新增 **gitlint 提交信息门禁**:钩子定义数比 CI 报告条数多 **1**(当时 17/16,master 并入 `test-cppcheck` 后为 18/17),缺的正是 gitlint——其 `stages: [commit-msg]` 声明在**上游** `.pre-commit-hooks.yaml`(`--all-files` 跑不到,且表现为全绿 → 长期无人发现)→ **`scripts/ci_gitlint_range.ps1`** 逐条校验本次 push/PR 范围(消息文件须写在仓库内 + UTF-8 无 BOM)。④ `GITHUB_ENV/PATH/OUTPUT` 写入统一显式 `-Encoding ascii`(PS 5.1 `Out-File -Append` 默认 **UTF-16LE**,实测落盘 `42 00 42 00 42 00 3D 00 31 00`)。§5 清单 8→10 条、§6 排障表 +5 行、§7 维护项重写 |
|
||||
| 2026-10-11 | **修"很多任务不执行"(runner 静默停摆)+ 补 gitlint 门禁**:① §2.4 重写——`gitea-runner` v4 的 `daemon` **无服务支持、首次 Declare 失败即退出进程**(日志实证),Gitea/机器重启一次 CI 就停摆,**run 16~21 共 12 个 job 零日志**(`cancelled`/`queued`,叠加 `cancel-in-progress` 后表现为"任务凭空消失"而非失败);新增 **`scripts/ci_runner_bootstrap.ps1`**(等 Gitea 就绪 + 崩溃自拉起的看门狗 + 开机计划任务 + `-SetEnv` 写机器级 `DTU_*` + `-Status` 只读体检)。② §3.1 重写——SYSTEM 可见性三重坑(PATH python 是 msys64 无 pre-commit / 用户级 site 对 SYSTEM 不可见 / `.venv` 已 gitignore)是 run 14~15 静态门禁全灭根因(`33a4aa4` 把候选收窄成这两个、又删了 cppcheck 目录兜底,而机器级 `DTU_*` 从未设置);探测收进 **`scripts/ci_find_python.ps1`**(两 job 共用、可离线自测、失败列出全部候选)并恢复机器级安装兜底。③ 新增 **gitlint 提交信息门禁**:钩子定义数比 CI 报告条数多 **1**(当时 17/16,master 并入 `test-cppcheck` 后为 18/17),缺的正是 gitlint——其 `stages: [commit-msg]` 声明在**上游** `.pre-commit-hooks.yaml`(`--all-files` 跑不到,且表现为全绿 → 长期无人发现)→ **`scripts/ci_gitlint_range.ps1`** 逐条校验本次 push/PR 范围(消息文件须写在仓库内 + UTF-8 无 BOM)。④ `GITHUB_ENV/PATH/OUTPUT` 写入统一显式 `-Encoding ascii`(PS 5.1 `Out-File -Append` 默认 **UTF-16LE**,实测落盘 `42 00 42 00 42 00 3D 00 31 00`)。§5 清单 8→10 条、§6 排障表 +5 行、§7 维护项重写 |
|
||||
| 2026-10-02 | **S3 + S14**:新增 §0.0「CI 不覆盖的体积门禁」醒目声明;新增 §4.4「产物存档与结果汇总」(零依赖 `Tee-Object` + `$GITHUB_STEP_SUMMARY` + `::notice::` 汇总行,不用 `actions/upload-artifact`;记录 `$ErrorActionPreference='Stop'` 与 `2>&1 \| Tee-Object` 的陷阱及处置)。§5 验证清单(7→8 条)与 §6 排障表同步更新 |
|
||||
| 2026-10-02 | **workflow 接入 `DTU_*` 机器级变量**(`本地工具路径环境变量方案.md` 实施):`l0-gates` 的 Python 探测改为 `DTU_PRECOMMIT_PYTHON`(显式,无效即硬失败)→ PATH → `.venv`(删除 `C:\ProgramData\anaconda3` 固定候选);cppcheck 改为 `DTU_CPPCHECK` 优先 → PATH(**移除 `C:\Program Files\Cppcheck` 固定回退**);`l05` 的 MSVC 探测改为 `DTU_MSVC_CL` 优先 → `%ProgramFiles*` 推导的标准根(与 `unit_test_harness.py` 同步,不再写死盘符);新增 §5.1.3 机器级变量设置方法(`Machine` scope + 重启任务)、§5.2 与 check_env 样例、排障表 cppcheck 行、TL;DR 5b 步骤。§5.1.1 的报错原文标注为历史记录 |
|
||||
| 2026-10-02 | ✅ **首次 CI 全绿(run#8:L0 + L0.5 双 job Success)**。定位并修复最后两个拦路虎:① **§5.1.1 用户态 site-packages 陷阱**——`pre-commit` 只装在 `%APPDATA%\Roaming\Python\...`(SYSTEM 不可见);且提权 `pip install --no-user` 会因 pip 扫**整个 `sys.path`**、见 user-site 已满足而**静默跳过**全局安装(9 依赖中唯 `cfgv` 因版本相同被跳过→`-s` 下 `ModuleNotFoundError`)。修法:提权 + `--force-reinstall --no-deps` 强制落全局,并以 `-s` 复验。② **§5.1.2 doc-links 假阳性**——文档引用了 gitignore 的运行时文件 `.dsh/skills/serial-assistant/scripts/state/meta.json`,本地存在、干净克隆无 → 按《文档规范》§3.3 加同状态词「运行时未创建」。排障表补 6 条(pre-commit 不可见 / GBK 中文 / pwsh / cppcheck / doc-links / MSVC 降级)。新增配套脚本 `install_precommit_global.ps1`、`install_precommit_global2.ps1`、`fix_cfgv_global.ps1` |
|
||||
| 2026-10-02 | ✅ **首次 CI 全绿(run#8:静态门禁 + 主机侧纯逻辑单测双 job Success)**。定位并修复最后两个拦路虎:① **§5.1.1 用户态 site-packages 陷阱**——`pre-commit` 只装在 `%APPDATA%\Roaming\Python\...`(SYSTEM 不可见);且提权 `pip install --no-user` 会因 pip 扫**整个 `sys.path`**、见 user-site 已满足而**静默跳过**全局安装(9 依赖中唯 `cfgv` 因版本相同被跳过→`-s` 下 `ModuleNotFoundError`)。修法:提权 + `--force-reinstall --no-deps` 强制落全局,并以 `-s` 复验。② **§5.1.2 doc-links 假阳性**——文档引用了 gitignore 的运行时文件 `.dsh/skills/serial-assistant/scripts/state/meta.json`,本地存在、干净克隆无 → 按《文档规范》§3.3 加同状态词「运行时未创建」。排障表补 6 条(pre-commit 不可见 / GBK 中文 / pwsh / cppcheck / doc-links / MSVC 降级)。新增配套脚本 `install_precommit_global.ps1`、`install_precommit_global2.ps1`、`fix_cfgv_global.ps1` |
|
||||
| 2026-10-02 | 🔴 **常驻方式重大修正(实测推翻)**:`Start-Service gitea-runner` 必然失败。经二进制逆向确认 `gitea-runner v4.0.1` **无任何服务管理代码**(命令集仅 `bug-report/cache-server/config/daemon/exec/register`,无 `service.go`;`OpenSCManagerW` 等符号来自 Docker SDK,非服务安装代码),`daemon` 是纯控制台程序、不向 SCM 回报 `SERVICE_RUNNING`。**§4 全节重写为「任务计划程序」方案**(新增 §4.0 根因分析、§4.2 `deploy_runner_task.ps1`、§4.3 手动命令 + 72h 超时警示、§4.5 清理遗留服务、§4.6 提权);删除失效的 `deploy_runner.ps1`/`install_service.ps1`;排障表补「错误 1053」「LastTaskResult 非 0」「72 小时被杀」3 条;拓扑图/§5.1/§7 日志/§10 TL;DR 同步更新。另修 `config.yaml` 的 `fetch_timeout` 5m→1m(超出 RPC 1m 会被截断并告警) |
|
||||
| 2026-10-02 | **修 `.ps1` 编码坑(实测)**:无 BOM 的 UTF-8 脚本被 WinPS 5.1 按 GBK 解读 → 中文乱码(`鐒跺悗鎵ц`)并撞坏引号配对,报 `源文本中包含无法识别的标记`/`字符串缺少终止符`。**新增 §7.1「给含中文的 `.ps1` 加 UTF-8 BOM」**(两种修法 + 解析器校验命令);排障表补该条目;配套脚本已统一加 BOM |
|
||||
| 2026-10-02 | **按实测修正为 v4.0.1**:act_runner 已更名 **`gitea-runner`**,发布页迁至 `gitea.com/gitea/runner`。**v4 移除 `daemon install/start/stop/status`**;**v4 `log` 段无 `file` 键**(§3 改用 `log.job.dir`);补 §1.3 命令集对照、§6.1 MySQL 直查核验、排障表(`accepts at most 0 arg(s)` / `unknown config key log.file` 等) |
|
||||
|
||||
+4
-4
@@ -46,7 +46,7 @@ docs/
|
||||
| 3 | [`项目启动顺序管理.md`](架构设计/项目启动顺序管理.md) | Event Flags 就绪屏障(取代魔法延时)的任务启动模型 |
|
||||
| 4 | [`项目部署环境要求.md`](环境与部署/项目部署环境要求.md) | 开发机/目标板/现场部署的完整环境核对清单 |
|
||||
| 5 | [`项目部署环境要求.md`](环境与部署/项目部署环境要求.md) → 构建 | 跑通 `@build` / `@test`(见根 `AGENTS.md`) |
|
||||
| 6 | [`测试开发标准流程.md`](测试说明/测试开发标准流程.md) | 测试分层 L0~保护判据与门禁决策表 |
|
||||
| 6 | [`测试开发标准流程.md`](测试说明/测试开发标准流程.md) | 测试分层 静态门禁~保护判据与门禁决策表 |
|
||||
| 7 | [`嵌入式C语言代码规范_V1.0.md`](代码规范/嵌入式C语言代码规范_V1.0.md) | 命名/格式/注释规范(提交门禁强制) |
|
||||
| 8 | [`项目架构总览.md`](架构设计/项目架构总览.md) → 按需 | 进入具体子系统设计文档 |
|
||||
|
||||
@@ -91,7 +91,7 @@ docs/
|
||||
| [`运行与自检记录设计说明.md`](子系统设计/运行与自检记录设计说明.md) | `/log/run.log`·`selftest.log` 定长记录与待机显示 |
|
||||
| [`看门狗设计说明.md`](子系统设计/看门狗设计说明.md) | IWDG(CubeMX 配置)+ 任务心跳位图:选型与超时推导、掩码与埋点、取证机制、验证记录;已台架闭环(V2/V4/V5)。**§11 = 任务级饥饿的诊断方法与 `[PROF]` 实测基线**(诊断沉淀:判读口径、量化归因、三条脆弱点、明确不做的四条) |
|
||||
| [`ADC采样节拍设计说明.md`](子系统设计/ADC采样节拍设计说明.md) | **ADC 采集链路采样节拍(现状说明)**:TIM3 1 kHz 更新事件同时触发 TPAFE CONVST 与(经 TRGO)STM32 ADC1 + DMA 乒乓缓冲;节拍源为什么是 TIM3、DMA 硬约束、事件驱动消费与就绪门、实现落点、观测计数、性能特征与设计边界 |
|
||||
| [`IEC104上行设计说明.md`](子系统设计/IEC104上行设计说明.md) | **B4 上行规约**:IEC 60870-5-104 被控站(TCP 2404)范围/点表(V0.1 有效子集)/链路流程(k/w/t1~t3、总召、COV、遥控两段式、定值)/netTask 接入决策/预算与测试方案;协议核心纯逻辑 + L0.5 全栈单测 |
|
||||
| [`IEC104上行设计说明.md`](子系统设计/IEC104上行设计说明.md) | **B4 上行规约**:IEC 60870-5-104 被控站(TCP 2404)范围/点表(V0.1 有效子集)/链路流程(k/w/t1~t3、总召、COV、遥控两段式、定值)/netTask 接入决策/预算与测试方案;协议核心纯逻辑 + 主机侧纯逻辑单测全栈单测 |
|
||||
| [`网络/BSD_Socket_API_使用指南.md`](网络/BSD_Socket_API_使用指南.md) | **上层视图**:BSD Socket API 公开语义、示例、常见问题 |
|
||||
| [`网络/BSD_Socket_API_函数调用关系.md`](网络/BSD_Socket_API_函数调用关系.md) | **内部视图**:netTask / 消息队列 / 保护判据挂起 / 重试对账的调用关系与设计意图 |
|
||||
| [`网络/Net层阻塞模型架构设计.md`](网络/Net层阻塞模型架构设计.md) | **等待模型**:recv/send 挂起请求架构、数据结构与挂起槽状态机、recv 五路 + send 四路完成语义、模块接口契约(14 函数)、实现落点(20 集成点)、调用方契约、设计边界与性能特征 |
|
||||
@@ -124,7 +124,7 @@ docs/
|
||||
| [`实机测试实施指南.md`](测试说明/实机测试实施指南.md) | **上板(真机)测试执行流程**(按顺序:环境预检 → `@test`+自检判定 → IWDG 注入/长跑 → 堆告警 → GD5F 错误通道 → 网络基线 → 工程卫生;每步含判据/命令/状态);已完成事项证据去向见 §10 |
|
||||
| [`ADC前端注入验证流程.md`](测试说明/ADC前端注入验证流程.md) | **ADC 前端与两片转换器的台架注入验证**(已知电压注入验证的执行侧,实测证据 → `docs/硬件平台/ADC通道映射说明.md` §8.1):注入点清单 / 期望码值表 / 零点·线性·映射·极性·节奏五步(每步自带现场记录表,边做边填);**不需示波器与信号源**,一块万用表即可 |
|
||||
|
||||
**主机侧单元测试(L0.5)**
|
||||
**主机侧单元测试(主机侧纯逻辑单测)**
|
||||
|
||||
| 文档 | 内容 |
|
||||
|---|---|
|
||||
@@ -136,7 +136,7 @@ docs/
|
||||
| [`相量层单元测试说明.md`](测试说明/相量层单元测试说明.md) | phasor 全周傅氏系数表 / 幅值·相位 / 谐波抑制与提取 / 频偏 / 采样环 / 双区交换,57 用例 |
|
||||
| [`卷锁单元测试说明.md`](测试说明/卷锁单元测试说明.md) | vol_lock 创建 / 获取 / 释放契约(**fail-open 不变量**:创建失败仍返回 1 且零阻塞)/ 幂等 / 超时透传,33 断言点 |
|
||||
| [`时间同步单元测试说明.md`](测试说明/时间同步单元测试说明.md) | time_sync 分片喂狗延时 / 监听退避 / 时间包重组 / 任务主循环(探针窗口心跳缺口的全部逻辑贡献者),80 断言点 |
|
||||
| [`主机单测覆盖率口径.md`](测试说明/主机单测覆盖率口径.md) | **跨全部 16 套 L0.5 的覆盖率**:`scripts/check_test_coverage.py` 用法 / 统计面与生成物例外 / 基线增量门禁(只拦回退)/ **为什么本机拿不到分支覆盖** / 九条限制与盲区 / 未挂 pre-commit 的三条理由与待粘 YAML |
|
||||
| [`主机单测覆盖率口径.md`](测试说明/主机单测覆盖率口径.md) | **跨全部 16 套 主机侧纯逻辑单测的覆盖率**:`scripts/check_test_coverage.py` 用法 / 统计面与生成物例外 / 基线增量门禁(只拦回退)/ **为什么本机拿不到分支覆盖** / 九条限制与盲区 / 未挂 pre-commit 的三条理由与待粘 YAML |
|
||||
|
||||
**模块测试指南**(动手写测试前先读)
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ git commit
|
||||
├─ mem-numbers 内存/Flash 数字一致性(体积档)
|
||||
├─ lv-conf-sync 两份 lv_conf.h 特性子集一致性(比对生效值)
|
||||
├─ cubemx-survival CubeMX 重生成存活审计(易失定制清单 + 基线漂移)
|
||||
├─ watchdog-unit-test 看门狗判定状态机主机侧单测(L0.5)
|
||||
├─ watchdog-unit-test 看门狗判定状态机主机侧单测(主机侧纯逻辑单测)
|
||||
├─ test-suite-switch 测试套件开关=产品构建(防测试固件静默提交)
|
||||
├─ registration-check 双端注册反向校验(uvprojx + CMakeLists,登记簿「rs485_task 白名单缺口」;含 UI 页面描述符表接线,「UI 字符集与像素回归门禁」)
|
||||
├─ mem-budget RAM 预算 92% 阈值门禁(登记簿条目「缩堆迁 CCM 后半(暂缓)」,2026-10-12)
|
||||
@@ -102,7 +102,7 @@ git commit
|
||||
|
||||
### 2.3 快速自查入口
|
||||
|
||||
- 提交前等价自查(L0 + L0b + L0.5):`scripts/ci_local.py`(用 `.venv` 解释器跑)。
|
||||
- 提交前等价自查(静态门禁 + L0b + 主机侧纯逻辑单测):`scripts/ci_local.py`(用 `.venv` 解释器跑)。
|
||||
- 单独跑某一条:`-m pre_commit run <hook-id> --files <改动的文件>`;全库:`--all-files`。
|
||||
- 临时跳过只在紧急情况,且必须在提交说明里写明跳过了什么、为什么、随后怎么补——**跳之前先跑 §2.4 自查表**(`--no-verify` 一次跳过 49 条,连 `gitlint` 一起跳,见 §2.2「两个阶段」行的 49/50 口径)。
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
> **`S` 裸形 / `T-x` / `A-x` 三族**是 2026-10-02 版复审的**条目编号**——编号**不随条目删除而回收**(现存有效条目 = 「采集链路测试可验证性约束」「体积门禁 CI 强制路径」与「CI 远端闭环未验证」);
|
||||
> 其他文档若引用这些编号,其证据见对应模块文档与 `git log`。已完成事项的证据沉淀:
|
||||
> `../测试说明/系统时钟单元测试说明.md`(S1-a)、`../测试说明/配置文件单元测试说明.md`(S1-b)、
|
||||
> `../测试说明/测试开发标准流程.md` §4.2.1(L0.5 适格台账)、`../测试说明/模拟器差距清单.md`(S13)、
|
||||
> `../测试说明/测试开发标准流程.md` §4.2.1(主机侧纯逻辑单测适格台账)、`../测试说明/模拟器差距清单.md`(S13)、
|
||||
> `../测试说明/实机测试实施指南.md`(S4/S5/S7/S9/S11)、`../问题记录/GD5F2GQ5UE_Trap_Records.md`(S9)、
|
||||
> `../测试说明/主机单测覆盖率口径.md` §9「P1 落地」与「P2 落地」(S1-c:`sys_log_mcu` 106 例、
|
||||
> `time_sync` 既有 4 例,两套均已被 `test-coverage` 总闸覆盖)。
|
||||
@@ -43,13 +43,13 @@ CH395F 套件 5 档构建全部链接通过,最坏态 `RW_IRAM1` 91,088 B / 69
|
||||
**约束**(建议纳入 B2 设计):
|
||||
|
||||
1. **协议解析必须与硬件 I/O 分离**——`App/protocol/` 的解析函数应接受
|
||||
`(const uint8_t *buf, size_t len)` 并返回解析结果,使其成为 L0.5 的**天然适格对象**
|
||||
`(const uint8_t *buf, size_t len)` 并返回解析结果,使其成为 主机侧纯逻辑单测的**天然适格对象**
|
||||
(参考 `cfg_file` 的"核心 + 后端"模式);
|
||||
2. **采集缓冲必须双缓冲 + 序号**——否则消费者读到撕裂数据,
|
||||
且**撕裂在单测里测不出来**(时序相关),必须在设计上消除;
|
||||
3. **DE 收尾必须有超时路径覆盖**(RS485 半双工特有陷阱)。
|
||||
|
||||
**完成判据**:B2 设计文档体现上述三条;协议解析函数有对应 L0.5 单测。(B1 侧:ADC 无协议解析面,其"同一时刻性"由原子快照保证、已闭环。)
|
||||
**完成判据**:B2 设计文档体现上述三条;协议解析函数有对应 主机侧纯逻辑单测单测。(B1 侧:ADC 无协议解析面,其"同一时刻性"由原子快照保证、已闭环。)
|
||||
|
||||
### 2.3 「体积门禁 CI 强制路径」残余:CI 体积门禁盲区(✅ 2026-10-25 销项)
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
> **脚本**:[`scripts/check_test_coverage.py`](../../scripts/check_test_coverage.py)(纯标准库)
|
||||
> **基线台账**:[`scripts/test_coverage_baseline.json`](../../scripts/test_coverage_baseline.json)
|
||||
> **关联文档**:
|
||||
> [`测试开发标准流程.md`](测试开发标准流程.md)(L0~L3 分层与门禁决策表)、
|
||||
> [`测试开发标准流程.md`](测试开发标准流程.md)(静态门禁~人工目检分层与门禁决策表)、
|
||||
> [`../代码规范/pre-commit使用说明.md`](../代码规范/pre-commit使用说明.md)(钩子通用机制)
|
||||
|
||||
---
|
||||
@@ -68,7 +68,7 @@
|
||||
& '.venv\Scripts\python.exe' scripts/check_test_coverage.py --md $env:TEMP/cov.md --json $env:TEMP/cov.json
|
||||
```
|
||||
|
||||
**与 L0.5 同一套发现规则**(`test/host/**/run_unit_test.py`,与 `scripts/ci_local.py` 一致),
|
||||
**与 主机侧纯逻辑单测同一套发现规则**(`test/host/**/run_unit_test.py`,与 `scripts/ci_local.py` 一致),
|
||||
并且**不复制任何编译配置**:脚本 import 各模块运行器、直接复用其声明式 `BUILD`(`TestBuild`),
|
||||
编译开关取 `unit_test_harness._BASE_FLAGS`,只追加 `/Zi /Od` 与 `/link /INCREMENTAL:NO`。
|
||||
所以"include 目录 / 桩顺序 / 测试单元路径"只有一处定义,覆盖率脚本不会漂成第二套口径。
|
||||
@@ -178,7 +178,7 @@
|
||||
|
||||
⚠ **这条理由的事实基础已变(2026-10-24,P1 落地后)**:本仓现有
|
||||
`test/host/**/run_unit_test.py` **33 套**,而钩子只挂 **16 套**——另外 17 套(P1/P2 新增的
|
||||
全部模块)目前只被 `ci_local.py`(L0.5 自动发现)与 `check_test_coverage.py` 跑到,
|
||||
全部模块)目前只被 `ci_local.py`(主机侧纯逻辑单测自动发现)与 `check_test_coverage.py` 跑到,
|
||||
**不在提交钩子里**。所以现状不是"重复两遍",而是**有一遍没做**。
|
||||
|
||||
当时唯一没解的是第 2 条:必须先定"缺 VS 组件时 skip 还是 FAIL"——那是要用户裁定的事(选 skip
|
||||
@@ -680,7 +680,7 @@ uint16_t ch395f_link_change_count(void);
|
||||
并行期**不再需要**"每人只跑 `--module` 自己模块",同树并发跑同名套件也不再撞 obj/exe;
|
||||
旧固定共享名 `%TEMP%/dtu_test_cov/` 与固定 `<suite>_obj`/`<suite>.exe` 一并作废,`--clean`
|
||||
改为只清本工作树根。**判据正文** = `test/host/common/unit_test_harness.py` 顶部「产物目录归属」。
|
||||
⚠ 加锁**不是**解法:这里没有真独占资源,只是名字撞——加锁等于把 L0.5 全局串行化。
|
||||
⚠ 加锁**不是**解法:这里没有真独占资源,只是名字撞——加锁等于把 主机侧纯逻辑单测全局串行化。
|
||||
|
||||
**两处口径问答(免得下次重新推导)**:① 子集数不能和并集基线直接比 —— `soe_store` 单套对
|
||||
`App/log/soe_rec.c` 只到 78/87(89.7%),而台账是全部 33 套的**并集** 81/87(93.1%);本批
|
||||
|
||||
@@ -92,7 +92,7 @@
|
||||
| `App/task/default_task.c` | `filesystem fault`(×2) / `storage fault`(×2) |
|
||||
| `App/task/net_task.c` | `comms fault` / `net init fail` / `net reinit fail` |
|
||||
|
||||
**联动**:`syslog_post` 的两级自愈(L1 文件级重试 / L2 错误去重 `s_fault_reported[]`)
|
||||
**联动**:`syslog_post` 的两级自愈(板端测试套件文件级重试 / 主机侧回归错误去重 `s_fault_reported[]`)
|
||||
决定**持续故障只留第一条**——故 S7 判定"有记录即 FAIL"是**充分**的(不会被去重掩盖)。
|
||||
|
||||
---
|
||||
@@ -301,13 +301,13 @@ $cli = if ($env:DTU_STLINK_CLI) { $env:DTU_STLINK_CLI } else { (Get-Command ST-L
|
||||
|
||||
### 7.3 L5 队列往返基线待重验 · ⬜ 待 PC peer
|
||||
|
||||
**现状**:L5 基线(1223/988µs)属**分片时代**;L3 对该路径逐位不变 → 机制上无影响,
|
||||
**现状**:L5 基线(1223/988µs)属**分片时代**;人工目检对该路径逐位不变 → 机制上无影响,
|
||||
但严格重锁需跑一轮 Phase 13(**需 PC peer**)。
|
||||
|
||||
**判据**:Phase 13 窗口跑一轮,基线数值与阈值余量(>8×)对照,重锁数值。
|
||||
|
||||
**2026-10-03 台架**:本窗口**无 PC peer**,Phase 13 无法执行 → 保留待办(低风险:
|
||||
阈值余量 >8×,机制上 L3 对该路径逐位不变)。
|
||||
阈值余量 >8×,机制上 人工目检对该路径逐位不变)。
|
||||
|
||||
---
|
||||
|
||||
@@ -390,10 +390,10 @@ CH395F / STORAGE / GD5F,未含 RTC;怀疑 RTC 构建下 `testTask` 不被创
|
||||
## 9. 执行注意:实测陷阱(2026-10-02 台架窗口,照做可少绕一天)
|
||||
|
||||
1. **写入"复位后消失"的两层原因**(窗口最大坑,根因均已修复,详见模块文档):
|
||||
- **L1 dhara checkpoint 回滚**(`架构设计/STM32F4-Base存储架构说明.md` §5.4.1):环回绕后
|
||||
- **板端测试套件 dhara checkpoint 回滚**(`架构设计/STM32F4-Base存储架构说明.md` §5.4.1):环回绕后
|
||||
`dhara_journal_resume()` 恢复旧 checkpoint,**之后写的一切整段丢弃**
|
||||
(已修:epoch 锚点改比较两候选的新旧)。
|
||||
- **L2 FTL 单页写回缓存**:一次 `disk_write` 里不满整页(2048 B)的写,在**写到另一个 LPN /
|
||||
- **主机侧回归 FTL 单页写回缓存**:一次 `disk_write` 里不满整页(2048 B)的写,在**写到另一个 LPN /
|
||||
整页写 / `CTRL_SYNC`** 之前只存在于 RAM(`nand_ftl.c` 的 `s_cache_buf/s_cache_dirty`)。
|
||||
但正常 `f_close` 就会走到 `CTRL_SYNC`(FatFs `sync_fs` → `disk_ioctl` → `ftl_flush_cache`),
|
||||
所以**"写完即关"的路径(日志、配置保存)关文件即落盘,不必再"写另一个文件逼刷"**;会丢的
|
||||
@@ -401,8 +401,8 @@ CH395F / STORAGE / GD5F,未含 RTC;怀疑 RTC 构建下 `testTask` 不被创
|
||||
FTP STOR 传输途中掉电就落在这一档(已登记观察;「写回缓存空闲 flush」于 2026-10-22 裁定暂不做,
|
||||
缺的是"空闲/掉电前周期 flush",且本机无掉电检测硬件,掉电时点无法补救)。
|
||||
→ 排查写入丢失时**先看开机日志的 head/tail/epoch**(`Map head=… tail=… epoch=…`,
|
||||
纯内存字段、零 I/O):head 复位后倒退 = L1(resume 错);head 正常 = L2(缓存未刷)。
|
||||
(旧口径"清台账/写证据后写另一个文件"的 flush 诱导属 L1 时代的产物:现码 `f_close` 自带
|
||||
纯内存字段、零 I/O):head 复位后倒退 = 板端测试套件(resume 错);head 正常 = 主机侧回归(缓存未刷)。
|
||||
(旧口径"清台账/写证据后写另一个文件"的 flush 诱导属 板端测试套件时代的产物:现码 `f_close` 自带
|
||||
`CTRL_SYNC`,正常写完即关就不需要它;这条诱导只在 `f_close` 失败时才还有兜底意义。)
|
||||
2. **FTP 路径**:lftpd 相对路径基准是**根目录**(`SIZE selftest.log` → 550),一律用
|
||||
绝对路径 `/log/xxx`;`LIST /log` 实测列的是**根目录**内容(lftpd 怪癖)——查状态只用
|
||||
@@ -502,7 +502,7 @@ CH395F / STORAGE / GD5F,未含 RTC;怀疑 RTC 构建下 `testTask` 不被创
|
||||
| 2026-10-03 | **重构为纯执行流程**:确立单一组织逻辑——本文 = **按顺序执行**的上板测试流程(步骤 1~7 + 状态标记),参考内容(§9 执行注意 / §10 已完成事项去向 / §11 引用闭环)后置。已完成事项(S7、A1~A4)的实施细节与真机证据**移出本文**,§10 只留「事项 → 去向」指针;§3 只留复跑命令与判据;§1「事项总览」改「执行流程总览」(步骤/状态列) |
|
||||
| 2026-10-03 | **§3.2 去"待上板"框架**:A1~A4 均已落地并(A1 全项 / A2~A4 正常路径)经真机验证,删除"待上板事项(代码状态 2026-10-02 核对)"表述,改写为**客观事实记录**(列名改为「实施内容(已落地)」+「验证判据」);§1 总览 P0-B 状态由「待上板」改「✅ 已闭环」;抬头与 `docs/README.md` 索引同步为「真机验证状态」。残留事实(A2/A3/A4 故障分支 + A4 推流压测需专项窗口、N-16 拔线负向测试)仍如实保留 |
|
||||
| 2026-10-02 | **更名**:`实机测试规划.md` → `实机测试实施指南.md`(与文档性质对齐——常设执行判据,非规划;引用面改造后仅剩 `docs/README.md` 索引 1 处,同步更新) |
|
||||
| 2026-10-03 | **A2/A3/A4 实施 + 真机验证**:A2 = `net_task.c` 初始组网失败/重建失败 → 自检留痕 + 30s 周期重试(分片喂心跳,不自驻留);A3 = `ch395f.c` 3 处字节级 SPI 调用 `HAL_MAX_DELAY` → 100ms 有限超时 + 返回值检查 + 故障锁存 `ch395f_spi_byte_fault()`(首条记日志);A4 = `ui_net_sink.c` `remo_send` 校验 `net_send` 返回值,短写告警、连续 3 次失败阻断推送、新连接/网络重建解除(单连接模型不 `net_close` 数据 fd)。`@build` 0/0 + L0/L0.5 全绿 + 模拟器构建通过;上板 `@test` PASS 7/7:A1 判定 PASS(含 IP 改→复位→持久→可达闭环,原档已恢复)、A2/A3/A4 正常路径零回归;自检清单 18→20 处(`net init fail` / `net reinit fail`);上板新数据:min_ever 下限 3,712 B(S11 阈值建议改 3,072 B)、开机良性 `MISS start` 行新增陷阱 §9.8;拔线负向/短写压测记 `待处理内容.md` N-16 |
|
||||
| 2026-10-03 | **A2/A3/A4 实施 + 真机验证**:A2 = `net_task.c` 初始组网失败/重建失败 → 自检留痕 + 30s 周期重试(分片喂心跳,不自驻留);A3 = `ch395f.c` 3 处字节级 SPI 调用 `HAL_MAX_DELAY` → 100ms 有限超时 + 返回值检查 + 故障锁存 `ch395f_spi_byte_fault()`(首条记日志);A4 = `ui_net_sink.c` `remo_send` 校验 `net_send` 返回值,短写告警、连续 3 次失败阻断推送、新连接/网络重建解除(单连接模型不 `net_close` 数据 fd)。`@build` 0/0 + 静态门禁/主机侧纯逻辑单测全绿 + 模拟器构建通过;上板 `@test` PASS 7/7:A1 判定 PASS(含 IP 改→复位→持久→可达闭环,原档已恢复)、A2/A3/A4 正常路径零回归;自检清单 18→20 处(`net init fail` / `net reinit fail`);上板新数据:min_ever 下限 3,712 B(S11 阈值建议改 3,072 B)、开机良性 `MISS start` 行新增陷阱 §9.8;拔线负向/短写压测记 `待处理内容.md` N-16 |
|
||||
| 2026-10-02 | 初版:无 MCU 窗口建立。汇总 P0-A 环境预检 / P0-B `@test`+S7 / P1-A IWDG V2/V4/V5 / P1-B S11 / P2-A S9 / P2-B P1-8+N-2+N-3 / P3 S5+S4+待核实项,共 **7 批**。每项含现状证据 / 目标 / 判据(量化)/ 执行步骤(精确命令)/ 风险回滚。18 处自检调用点清单 + 模拟器差距清单 §3 决策规则交叉引用 |
|
||||
| 2026-10-02 | **台架窗口执行交接**:新增 **§0.5**(P0-A 通过;S7 已落地并提交 `4a4cd29`;P0-B 唯一阻断 = 0 字节 STOR → IWDG 误复位(round 3~6 后仍 5/5 复现);round 1~6 修复集摘要;**round 7 纯取证埋点设计 + 判据表**;P0-B 出口闭环(含 FTL flush 诱导式清台账);设备/台账/本地证据快照;**7 条实测陷阱**);§2.1 标注 S7 已落地、命令形态已确认 |
|
||||
| 2026-10-02 | **文档改造**(同 `本地工具路径环境变量方案.md` 思路):删除一次性台架窗口状态(原 §0.5——round 1~9 取证全过程、快照、未提交警告:结论已沉淀 `看门狗设计说明.md`/`STM32F4-Base存储架构说明.md` §5.4.1/`控制台单写者日志设计说明.md` §12,修复随 `930215f` 提交)与规划框架(预估耗时/阶段措辞),重组为执行判据说明——§1 事项总览 / §2 环境预检 / §3 `@test` 闭环+自检判定+A1~A4 项 / §4 IWDG 注入长跑 / §5 堆告警 / §6 GD5F 错误通道 / §7 网络基线 / §8 工程卫生 / §9 实测陷阱 / §10 引用闭环;A1~A4 代码状态按当日代码核对改写(A1 已落地 / A2 部分 / A3、A4 未补码);A4 基线更新 264,284 B、A7 端口更正 COM6;原 §0.5.x 的 8 处代码注释、`待处理内容.md` 3 行、`看门狗设计说明.md` 变更记录、`docs/README.md` 同步重指向模块文档 |
|
||||
|
||||
+50
-50
@@ -23,21 +23,21 @@
|
||||
|
||||
测试体系分四层,门禁由便宜到昂贵依次布防,改动须过对应门禁方可提交:
|
||||
|
||||
| 层 | 名称 | 载体 | 抓住什么 |
|
||||
|---|---|---|---|
|
||||
| L0 | 静态门禁 | pre-commit 钩子(cppcheck / 代码规范 / 乱码 / 空白 / gitlint)+ `@build` | 编码规范、静态缺陷、任何编译警告 |
|
||||
| **L0.5** | **主机侧纯逻辑单测** | `test/host/<模块>/run_unit_test.py` + MSVC 离线编译真实源码(`#include` 纳入)+ 平台桩 | **纯逻辑模块的边界穷举**(节流窗口、计数阈值、整数回绕)——板端无法覆盖、无需硬件 |
|
||||
| L1 | 板端测试套件 | testTask + `test/board/test_config.h` 套件 | 驱动 / 协议功能(串口 `[PASS]/[FAIL]` + `=== ... PASSED` 汇总) |
|
||||
| L2 | 主机侧回归 | `@test` 串口日志标记 + LVGL 模拟器像素 / 池水位回归 | 启动序列、UI 渲染、LVGL 内存池水位 |
|
||||
| L3 | 人工目检 | 上机屏幕、PC 端交互工具 | 视觉内容、自动化无法覆盖的场景 |
|
||||
| 名称 | 载体 | 抓住什么 |
|
||||
|---|---|---|
|
||||
| 静态门禁 | pre-commit 钩子(cppcheck / 代码规范 / 乱码 / 空白 / gitlint)+ `@build` | 编码规范、静态缺陷、任何编译警告 |
|
||||
| **主机侧纯逻辑单测** | `test/host/<模块>/run_unit_test.py` + MSVC 离线编译真实源码(`#include` 纳入)+ 平台桩 | **纯逻辑模块的边界穷举**(节流窗口、计数阈值、整数回绕)——板端无法覆盖、无需硬件 |
|
||||
| 板端测试套件 | testTask + `test/board/test_config.h` 套件 | 驱动 / 协议功能(串口 `[PASS]/[FAIL]` + `=== ... PASSED` 汇总) |
|
||||
| 主机侧回归 | `@test` 串口日志标记 + LVGL 模拟器像素 / 池水位回归 | 启动序列、UI 渲染、LVGL 内存池水位 |
|
||||
| 人工目检 | 上机屏幕、PC 端交互工具 | 视觉内容、自动化无法覆盖的场景 |
|
||||
|
||||
> **L0.5 与 L2 的区别**:L2 的模拟器回归依赖 SDL / LVGL 与图形输出;L0.5 是**纯计算**单测
|
||||
> **主机侧纯逻辑单测与 主机侧回归的区别**:主机侧回归的模拟器回归依赖 SDL / LVGL 与图形输出;主机侧纯逻辑单测是**纯计算**单测
|
||||
> (无窗口、无硬件、秒级),专测"判定逻辑"。目前仅看门狗模块使用,见
|
||||
> [`看门狗单元测试说明.md`](看门狗单元测试说明.md)。适格对象:算法 / 状态机 / 协议编解码 /
|
||||
> 参数校验等**无副作用**的模块。
|
||||
>
|
||||
> **CI**:**L0 + L0.5 已上 CI**(Gitea Actions,`.gitea/workflows/ci.yml`,2026-10-01);
|
||||
> L1/L2/L3 需硬件,只在本地。本地等价命令 `scripts/ci_local.py`。详见 §9。
|
||||
> **CI**:**静态门禁 + 主机侧纯逻辑单测已上 CI**(Gitea Actions,`.gitea/workflows/ci.yml`,2026-10-01);
|
||||
> 板端测试套件/主机侧回归/人工目检需硬件,只在本地。本地等价命令 `scripts/ci_local.py`。详见 §9。
|
||||
|
||||
### 1.1 门禁决策表(改动类型 → 必跑门禁)
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
|---|---|
|
||||
| 任何源码提交 | G0 pre-commit(提交时自动触发)+ G1 `@build` |
|
||||
| App / Drivers 功能代码(板端行为可观测) | G0 + G1 + G2 `@test`(模块标记) |
|
||||
| **有 L0.5 单测的模块(如看门狗)** | G0(含该模块单测钩子,自动)+ G1 `@build`(+ 台架项另按需 G2) |
|
||||
| **有 主机侧纯逻辑单测单测的模块(如看门狗)** | G0(含该模块单测钩子,自动)+ G1 `@build`(+ 台架项另按需 G2) |
|
||||
| UI(菜单 / 内容页 / 字体 / 样式 / 几何) | G0 + G1 + G2 + G3 ui-verify 五步 |
|
||||
| UI 新增中文文案 | 另需 font-regen(字体重生成 + 回归),**先于提交** |
|
||||
| `test/` 套件或 PC 端工具代码 | G1 + G2(套件标题标记 / 工具自测) |
|
||||
@@ -74,7 +74,7 @@ G0~G3 命令详见 §6 / §7;ui-verify 完整命令见 `UI_自动化测试流
|
||||
### 2.3 三条铁律
|
||||
|
||||
1. **先写判定标准,再写测试代码** —— 文档里先写"什么算过 / 什么算失败"(精确到返回值 / 日志字符串 / 像素参考),再实现,避免"测了但不知道算不算过"。
|
||||
2. **判定标准必须可机器判定** —— 必须能落到:串口日志标记(→ `@test -Required`)、返回值 / 寄存器值(→ `TEST_CHECK`)、像素参考(→ 模拟器回归)、池水位(→ 基线常量)之一。无法机器判定的标准只能放 L3 人工层。
|
||||
2. **判定标准必须可机器判定** —— 必须能落到:串口日志标记(→ `@test -Required`)、返回值 / 寄存器值(→ `TEST_CHECK`)、像素参考(→ 模拟器回归)、池水位(→ 基线常量)之一。无法机器判定的标准只能放 人工目检人工层。
|
||||
3. **每条设计原则必须有对应用例** —— 覆盖表中每个 DP-xx 至少一个 TC;新功能设计时先定 DP,再设计 TC。
|
||||
|
||||
---
|
||||
@@ -269,17 +269,17 @@ static void phaseN_run(void) {
|
||||
| `.dsh/skills/serial-boot-test/scripts/run_boot_test.py` | `test/host/` | 一键闭环:采集 → 烧录 → 提取日志 → 校验 |
|
||||
| **`test/run_all_suites.py`** | `test/` | **多套件回归编排** + **`--check-build` 只编译不烧录**(纯离机,验证 4 套件可编译性);见 §3.6 |
|
||||
| `.dsh/skills/serial-boot-test/scripts/verify_log.py` | `test/host/` | 日志标记校验(Required / Forbidden → PASS / FAIL)+ 可选套件汇总行校验 |
|
||||
| **`test/host/common/unit_test_harness.py`** | `test/host/common/` | **L0.5 通用单测 harness**:工具链探测 / `INCLUDE`-`LIB` 拼接 / 编译运行 / 退出码契约。各模块运行器只做声明式配置 |
|
||||
| **`test/host/watchdog/run_unit_test.py`** | `test/host/watchdog/` | **L0.5 主机侧单测**:声明 `TestBuild` 后调 harness,穷举看门狗判定状态机(零硬件、秒级)。**需本机装有 VS(`cl.exe`)**,见 §4.2 环境要求 |
|
||||
| **`test/host/sys_clock/run_unit_test.py`** | `test/host/sys_clock/` | **L0.5 主机侧单测**:穷举 `sys_clock` 时间换算 / 星期 / 惰性再基准化 / RTC 故障降级(100 断言,首批主机侧单测) |
|
||||
| **`test/host/cfg_file/run_unit_test.py`** | `test/host/cfg_file/` | **L0.5 主机侧单测**:INI 解析 / 值编解码 / 段注册 / load-save-poll 契约,内存虚拟 FS 驱动(139 断言,首批主机侧单测) |
|
||||
| **`test/host/nand_port/run_unit_test.py`** | `test/host/nand_port/` | **L0.5 主机侧单测**:离线编译真实 `Drivers/BSP/GD5F2GQ5UE/nand_ftl.c` + 真实 dhara,以 **RAM 假芯片**驱动,149 断言钉死 is_free 整页扫描与六个入口的越界守卫 |
|
||||
| **`scripts/ci_local.py`** | `scripts/` | **本地跑与 CI 等价的门禁**(L0 + L0.5):自动选用装了 pre-commit 的解释器;`.map` 自适应跳过 `mem-numbers`;退出码 0/1/2 与 CI 对齐。见 §9.3 |
|
||||
| **`test/host/common/unit_test_harness.py`** | `test/host/common/` | **主机侧纯逻辑单测通用单测 harness**:工具链探测 / `INCLUDE`-`LIB` 拼接 / 编译运行 / 退出码契约。各模块运行器只做声明式配置 |
|
||||
| **`test/host/watchdog/run_unit_test.py`** | `test/host/watchdog/` | **主机侧纯逻辑单测主机侧单测**:声明 `TestBuild` 后调 harness,穷举看门狗判定状态机(零硬件、秒级)。**需本机装有 VS(`cl.exe`)**,见 §4.2 环境要求 |
|
||||
| **`test/host/sys_clock/run_unit_test.py`** | `test/host/sys_clock/` | **主机侧纯逻辑单测主机侧单测**:穷举 `sys_clock` 时间换算 / 星期 / 惰性再基准化 / RTC 故障降级(100 断言,首批主机侧单测) |
|
||||
| **`test/host/cfg_file/run_unit_test.py`** | `test/host/cfg_file/` | **主机侧纯逻辑单测主机侧单测**:INI 解析 / 值编解码 / 段注册 / load-save-poll 契约,内存虚拟 FS 驱动(139 断言,首批主机侧单测) |
|
||||
| **`test/host/nand_port/run_unit_test.py`** | `test/host/nand_port/` | **主机侧纯逻辑单测主机侧单测**:离线编译真实 `Drivers/BSP/GD5F2GQ5UE/nand_ftl.c` + 真实 dhara,以 **RAM 假芯片**驱动,149 断言钉死 is_free 整页扫描与六个入口的越界守卫 |
|
||||
| **`scripts/ci_local.py`** | `scripts/` | **本地跑与 CI 等价的门禁**(静态门禁 + 主机侧纯逻辑单测):自动选用装了 pre-commit 的解释器;`.map` 自适应跳过 `mem-numbers`;退出码 0/1/2 与 CI 对齐。见 §9.3 |
|
||||
| `.serialtest/config.json` | 仓库 | **串口端口单一配置源**(当前 COM6 / 115200);换端口只改这个文件 |
|
||||
| `test/host/ftp_*.py`(4 个)/ `ch395f_socket_test.py` / `time_sync_server.py` | `test/host/` | PC 端交互测试工具(FTP 客户端 / socket 回显 / 对时推送) |
|
||||
| `simulator/tests/*.py`(4 个) | `simulator/tests/` | LVGL 模拟器回归脚本(见 §5) |
|
||||
|
||||
### 4.2 L0.5 主机侧单元测试(纯逻辑,无硬件)
|
||||
### 4.2 主机侧纯逻辑单测主机侧单元测试(纯逻辑,无硬件)
|
||||
|
||||
**适用对象**:**无副作用**的纯逻辑模块——算法、状态机、协议编解码、参数校验等
|
||||
(例:看门狗的位图判定 / 掩码 / 计数阈值 / 整数回绕)。
|
||||
@@ -312,13 +312,13 @@ static void phaseN_run(void) {
|
||||
|
||||
- 平台层的抽取必须**零行为变化、零额外开销**(MCU 端默认实现即原逻辑;`@build` 须 0/0);
|
||||
- 测试专用钩子(如静态态复位)用 `#ifdef <模块>_UNIT_TEST` 守卫,MCU 构建不编入(零符号);
|
||||
- 单测**不能替代**板端验证:真机寄存器 / 复位原因 / 时序仍须 L1/L2 台架闭环。
|
||||
- 单测**不能替代**板端验证:真机寄存器 / 复位原因 / 时序仍须 板端测试套件/主机侧回归台架闭环。
|
||||
|
||||
#### 4.2.1 L0.5 适格模块台账(2026-10-02 加)
|
||||
#### 4.2.1 主机侧纯逻辑单测适格模块台账(2026-10-02 加)
|
||||
|
||||
**问题**:L0.5 起步阶段只有"看门狗"一个先例,**下一个该测谁**靠临时判断,易遗漏高价值模块。
|
||||
**问题**:主机侧纯逻辑单测起步阶段只有"看门狗"一个先例,**下一个该测谁**靠临时判断,易遗漏高价值模块。
|
||||
|
||||
**判据**(是否适格 L0.5):模块的核心逻辑是否**无副作用**且**与硬件解耦**——
|
||||
**判据**(是否适格 主机侧纯逻辑单测):模块的核心逻辑是否**无副作用**且**与硬件解耦**——
|
||||
满足"纯逻辑 + 已抽象平台层(或依赖全是外部符号/宏)"即可主机侧穷举。
|
||||
|
||||
| 模块 | 规模 | 状态 | 平台层形态 | 备注 |
|
||||
@@ -336,12 +336,12 @@ static void phaseN_run(void) {
|
||||
| 其余驱动(CH395F/GD5F2GQ5UE 芯片层/SD2506) | — | ❌ 不适格 | — | 需硬件时序 / SPI / I2C 总线。⚠ **例外**:GD5F 之上的 `nand_ftl.c` 移植层**适格**(见上一行)——"驱动不适格"不等于"驱动之上的胶水层不适格" |
|
||||
| `App/util/crc.c` · `ringbuf.c` | 120/154 行 | ⛔ 已删除(2026-10-02) | — | 孤儿文件,零引用,已 `git rm` |
|
||||
|
||||
**使用方式**:新增/改动上述模块前,按表判断是否需补 L0.5;`⬜ 候选` 表示"适格但未做",
|
||||
**使用方式**:新增/改动上述模块前,按表判断是否需补 主机侧纯逻辑单测;`⬜ 候选` 表示"适格但未做",
|
||||
在性价比允许时应排入(判据:边界密集、零硬件、单次真机回归 30s~2min vs 单测 2~5s)。
|
||||
|
||||
> ⭐ **两条实证**:`sys_clock` 与 `cfg_file` **首次落地即各抓出一处
|
||||
> 生产缺陷**(Zeller 星期公式错误 / 外科手术保存死码)。这印证了本台账的性价比判断——
|
||||
> 高价值纯逻辑模块的 L0.5 覆盖,是**投入最小、产出最大**的测试改进方向。
|
||||
> 高价值纯逻辑模块的 主机侧纯逻辑单测覆盖,是**投入最小、产出最大**的测试改进方向。
|
||||
|
||||
### 4.3 新工具约定
|
||||
|
||||
@@ -396,7 +396,7 @@ flowchart TD
|
||||
B --> C["run_boot_test.py:烧录前武装采集(daemon 全量日志兜底)"]
|
||||
C --> D["@download 烧录(Reset & Run 触发重启)"]
|
||||
D --> E[提取日志 + verify_log 标记校验]
|
||||
E -->|exit 0| F["PASS → G3(UI 改动)→ L3 上机目检 → 提交"]
|
||||
E -->|exit 0| F["PASS → G3(UI 改动)→ 人工目检上机目检 → 提交"]
|
||||
E -->|exit 1 FAIL| G["[MISS]/[HIT] + 日志尾部定位 → 修代码"]
|
||||
G --> B
|
||||
E -->|exit 2/3 环境错误| H["按 自动化测试流程.md 失败模式表处理 → 重试"]
|
||||
@@ -491,7 +491,7 @@ flowchart LR
|
||||
B --> C["G1 @build<br/>0 错 0 警"]
|
||||
C --> D["G2 @test<br/>采集→烧录→校验"]
|
||||
D -->|PASS| E["G3 ui-verify<br/>(UI 改动时)"]
|
||||
E --> F["L3 上机目检"]
|
||||
E --> F["人工目检上机目检"]
|
||||
F --> G["提交<br/>G0 门禁 + 逻辑提交"]
|
||||
G --> H["沉淀:陷阱记录 /<br/>文档同步 / 变更记录"]
|
||||
D -->|FAIL| I["[MISS]/[HIT] 定位"]
|
||||
@@ -511,15 +511,15 @@ flowchart LR
|
||||
|
||||
| 层 | 上 CI? | 载体 | 理由 |
|
||||
|---|---|---|---|
|
||||
| **L0** 静态门禁 | ✅ | `pre-commit run --all-files`(全部钩子) | 纯 Python + cppcheck,零硬件 |
|
||||
| **L0.5** 主机侧纯逻辑单测 | ✅ | `test/host/**/run_unit_test.py`(MSVC 离线编译) | 纯标准库 + `cl.exe`,零硬件 |
|
||||
| L1 板端套件 | ⛔ | `test/board/` | 需真机 + 串口 + ST-LINK |
|
||||
| L2 `@test` 闭环 | ⛔ | 烧录 + 串口采集 | 同上 |
|
||||
| L3 人工目检 | ⛔ | 上机屏幕 | 人的判断 |
|
||||
| **静态门禁** 静态门禁 | ✅ | `pre-commit run --all-files`(全部钩子) | 纯 Python + cppcheck,零硬件 |
|
||||
| **主机侧纯逻辑单测** 主机侧纯逻辑单测 | ✅ | `test/host/**/run_unit_test.py`(MSVC 离线编译) | 纯标准库 + `cl.exe`,零硬件 |
|
||||
| 板端测试套件板端套件 | ⛔ | `test/board/` | 需真机 + 串口 + ST-LINK |
|
||||
| 主机侧回归 `@test` 闭环 | ⛔ | 烧录 + 串口采集 | 同上 |
|
||||
| 人工目检 | ⛔ | 上机屏幕 | 人的判断 |
|
||||
|
||||
**明确不做的两件事(都是刻意为之)**:
|
||||
|
||||
1. **不跑 `@build`(Keil UV4)**——需本机 Keil 授权,runner 上不具备。L0 的 cppcheck + 代码规范
|
||||
1. **不跑 `@build`(Keil UV4)**——需本机 Keil 授权,runner 上不具备。静态门禁的 cppcheck + 代码规范
|
||||
已覆盖"编译前的静态问题",真编译仍是开发者的本地红线(`AGENTS.md`)。
|
||||
2. **`mem-numbers` 钩子按 `.map` 有无自适应跳过**——它的事实源是 `MDK-ARM/**/*.map`
|
||||
(Keil 构建产物,已 `.gitignore`),无 `@build` 即无 `.map`。故 workflow 在**检测不到
|
||||
@@ -536,7 +536,7 @@ flowchart LR
|
||||
> `.pre-commit-config.yaml` 定义 **18** 个钩子,而 CI 日志只报 **17** 个(`passed=16 skipped=1`)——
|
||||
> 缺的正是 gitlint。即"单行中文标题 ≤100 字符"这条门禁**本地有、CI 无**,且表现为**全绿**
|
||||
> 而非失败,所以长期无人发现。现由 CI 侧 **`scripts/ci_gitlint_range.ps1`**(PR 用 `base..head`、
|
||||
> push 用 `event.before..sha`、跳过 merge 提交)与本地 `ci_local.py` 的 **L0b** 段分别逐条校验。
|
||||
> push 用 `event.before..sha`、跳过 merge 提交)与本地 `ci_local.py` 的 **提交信息门禁** 段分别逐条校验。
|
||||
> ⚠️ 机制根因(`stages: [commit-msg]` 写在 **gitlint 上游** 的 `.pre-commit-hooks.yaml` 里,
|
||||
> 项目配置中 grep 不到)与完整排障口径见
|
||||
> [`../CI/远程CI完整指南.md`](../CI/远程CI完整指南.md) §7"钩子条数对不上"行。
|
||||
@@ -545,8 +545,8 @@ flowchart LR
|
||||
|
||||
| job | 覆盖层 | 一句话职责 |
|
||||
|---|---|---|
|
||||
| `l0-gates` | **L0** | `pre-commit run --all-files`(cppcheck / 编码规范 / 文档一致性 / 主机侧单测钩子)+ **提交信息门禁**(gitlint,见上方 🔴);无 `.map` 时自适应跳过 `mem-numbers` |
|
||||
| `l05-host-unit-tests` | **L0.5** | 探测 MSVC 后遍历 `test/host/**/run_unit_test.py` 逐个跑并聚合断言数;**未装 MSVC 时降级为 WARNING 跳过**(不硬失败,但等于该层裸奔) |
|
||||
| `l0-gates` | **静态门禁** | `pre-commit run --all-files`(cppcheck / 编码规范 / 文档一致性 / 主机侧单测钩子)+ **提交信息门禁**(gitlint,见上方 🔴);无 `.map` 时自适应跳过 `mem-numbers` |
|
||||
| `l05-host-unit-tests` | **主机侧纯逻辑单测** | 探测 MSVC 后遍历 `test/host/**/run_unit_test.py` 逐个跑并聚合断言数;**未装 MSVC 时降级为 WARNING 跳过**(不硬失败,但等于该层裸奔) |
|
||||
|
||||
触发:`push`(`paths-ignore: docs/** / **/*.md / .workbuddy-ai/**` → **纯文档改动不会触发 CI**)
|
||||
与 `pull_request`,另留 `workflow_dispatch` 手动触发;同分支 `cancel-in-progress: true`。
|
||||
@@ -560,21 +560,21 @@ flowchart LR
|
||||
CI 只在 push / PR 时跑,且需 runner 就绪。**提交前自查、或 runner 还没配好时**,用同一套判定在本地跑:
|
||||
|
||||
```powershell
|
||||
& '.venv\Scripts\python.exe' scripts/ci_local.py # L0 + L0b + L0.5(与 CI 等价)
|
||||
& '.venv\Scripts\python.exe' scripts/ci_local.py --l0-only # 只跑 L0(含 L0b 提交信息门禁)
|
||||
& '.venv\Scripts\python.exe' scripts/ci_local.py --l05-only # 只跑 L0.5
|
||||
& '.venv\Scripts\python.exe' scripts/ci_local.py # 静态门禁 + 提交信息门禁 + 主机侧纯逻辑单测(与 CI 等价)
|
||||
& '.venv\Scripts\python.exe' scripts/ci_local.py --l0-only # 只跑 静态门禁(含 提交信息门禁 提交信息门禁)
|
||||
& '.venv\Scripts\python.exe' scripts/ci_local.py --l05-only # 只跑 主机侧纯逻辑单测
|
||||
& '.venv\Scripts\python.exe' scripts/ci_local.py --commits 20 # 提交信息校验最近 20 条
|
||||
```
|
||||
|
||||
- **自动选用装了 pre-commit 的解释器**(优先级:`DTU_PRECOMMIT_PYTHON` 显式指定 → 当前解释器(重建 `.venv` 已自带 pre-commit)→ 仓库 `.venv` → PATH 的 `python` → **常见机器级安装位置**;显式值无效 → 硬报错并指明变量名,不静默回退);
|
||||
仍不可用时退出码 2 并列出**全部已尝试路径**。
|
||||
⚠️ 该候选表与 CI 侧 **`scripts/ci_find_python.ps1`** 必须**同序同步**——2026-10-11 的 L0 全灭就是
|
||||
⚠️ 该候选表与 CI 侧 **`scripts/ci_find_python.ps1`** 必须**同序同步**——2026-10-11 的 静态门禁全灭就是
|
||||
workflow 单方面把候选收窄成"PATH python + 仓库 `.venv`"(runner 上两者都是死路)导致的。
|
||||
- **L0b 提交信息门禁**:与 CI 的 `ci_gitlint_range.ps1` 同语义(`--all-files` 跑不到 commit-msg stage)。
|
||||
- **提交信息门禁 提交信息门禁**:与 CI 的 `ci_gitlint_range.ps1` 同语义(`--all-files` 跑不到 commit-msg stage)。
|
||||
- **`.map` 自适应与 CI 完全一致**:本地有 `.map` 就照常跑 `mem-numbers`,没有就跳过。
|
||||
- 退出码:`0` 通过 / `1` 门禁或单测失败 / `2` 环境错误。
|
||||
|
||||
> ⚠️ **CI 不能替代本地门禁**:`.map` 类体积门禁、`@build`、L1/L2 台架项都只存在于本地。
|
||||
> ⚠️ **CI 不能替代本地门禁**:`.map` 类体积门禁、`@build`、板端测试套件/主机侧回归台架项都只存在于本地。
|
||||
> 提交前跑一遍 `ci_local.py`(或 `pre-commit run --all-files`)仍是**强制**动作,见 §7.1。
|
||||
|
||||
### 9.4 部署前置(需人工确认)
|
||||
@@ -585,7 +585,7 @@ CI 能不能跑起来,取决于 runner 机器上四件事是否就位:**服
|
||||
注册细节:`--instance` 用回环、labels 必须精确匹配 `runs-on`;看门狗缺了它 Gitea 一重启
|
||||
CI 就静默停摆(2026-10-11 实测 run 16~21 共 12 个 job 零日志)。
|
||||
|
||||
> ⚠️ 对测试流程只需记住一条:**runner 不在线时,`ci_local.py` 就是 L0 / L0.5 的唯一保障**。
|
||||
> ⚠️ 对测试流程只需记住一条:**runner 不在线时,`ci_local.py` 就是 静态门禁 / 主机侧纯逻辑单测的唯一保障**。
|
||||
> "CI 显示绿"与"本地门禁通过"是两回事,见 §9.3 与 §7.1。
|
||||
|
||||
---
|
||||
@@ -617,27 +617,27 @@ CI 就静默停摆(2026-10-11 实测 run 16~21 共 12 个 job 零日志)。
|
||||
| `docs/测试说明/串口助手设计说明.md` | 串口调试工具(Python 串口助手) |
|
||||
| `docs/测试说明/自动化测试流程.md` | @test 闭环细节 |
|
||||
| `docs/测试说明/测试文档模板规范.md` | 测试文档模板与命名 |
|
||||
| `docs/测试说明/看门狗单元测试说明.md` | 看门狗判定状态机(L0.5 主机侧) |
|
||||
| `docs/测试说明/系统时钟单元测试说明.md` | 系统时钟换算(L0.5 主机侧) |
|
||||
| `docs/测试说明/配置文件单元测试说明.md` | 配置引擎解析/契约(L0.5 主机侧) |
|
||||
| `docs/测试说明/看门狗单元测试说明.md` | 看门狗判定状态机(主机侧纯逻辑单测主机侧) |
|
||||
| `docs/测试说明/系统时钟单元测试说明.md` | 系统时钟换算(主机侧纯逻辑单测主机侧) |
|
||||
| `docs/测试说明/配置文件单元测试说明.md` | 配置引擎解析/契约(主机侧纯逻辑单测主机侧) |
|
||||
|
||||
## 变更记录
|
||||
|
||||
| 日期 | 变更 |
|
||||
|---|---|
|
||||
| 2026-10-15 | **U4 残余入门禁(钩子总数 34→35)**:① 新增 `font-charset` 钩子(`scripts/check_font_charset.py`)——UI 源码在用字符 vs `ui_cn_chars.txt` vs `ui_font_cn_12.c` cmap 两段差集,白框方向 FAIL、冗余字 WARN;提取与 cmap 解析**复用** `tools/font_gen/` 两个生成器同一套规则(importlib 直载),牙齿实测=临时副本注入缺字三例全 FAIL 点名到字。② `registration-check` 补第 ④ 类缺口——`ui_page_*.c` 定义的 `ui_page_<name>_desc` 未接入 `ui_content_page.c` 的 `s_defs[]` 描述符表即 FAIL(实测原 ①②③ 对"进了两端构建但菜单打不开"的页面全绿)。§5.3 font-regen 铁律补"已入门禁"指针;详解见 `../代码规范/pre-commit使用说明.md` §2.18/§2.24 |
|
||||
| 2026-10-15 | **新增 `test/host/nand_port/`(dhara NAND 移植层 L0.5,N-21 / 报告 N-12+N-13)**:`#include` 真实 `nand_ftl.c` + 真实 `Lib/dhara/{journal,map}.c`,芯片侧换成 **RAM 假芯片**(32 块 × 64 页 × 2048 B,两段式读缓存 + NOR 叠加编程 + 双编程违规计数 + 访问次数记账),149 断言覆盖 is_free 整页扫描、六入口越界守卫(`-1` + `DHARA_E_CORRUPT_MAP` + 零芯片访问 / 零标坏块)、故障注入与 dhara map"写入 → 重新 resume"场景;**内联一份"只看页首 64 B"的旧算法做对照**,证明断言能咬人。挂 pre-commit 钩子 `nand-port-unit-test`(第 16 个主机侧单测,钩子总数 33→34)。§4.1 登记运行器、§4.2.1 台账新增该模块并给"其余驱动不适格"补例外说明(驱动不适格 ≠ 驱动之上的胶水层不适格) |
|
||||
| 2026-10-15 | **新增 `test/host/nand_port/`(dhara NAND 移植层 主机侧纯逻辑单测,N-21 / 报告 N-12+N-13)**:`#include` 真实 `nand_ftl.c` + 真实 `Lib/dhara/{journal,map}.c`,芯片侧换成 **RAM 假芯片**(32 块 × 64 页 × 2048 B,两段式读缓存 + NOR 叠加编程 + 双编程违规计数 + 访问次数记账),149 断言覆盖 is_free 整页扫描、六入口越界守卫(`-1` + `DHARA_E_CORRUPT_MAP` + 零芯片访问 / 零标坏块)、故障注入与 dhara map"写入 → 重新 resume"场景;**内联一份"只看页首 64 B"的旧算法做对照**,证明断言能咬人。挂 pre-commit 钩子 `nand-port-unit-test`(第 16 个主机侧单测,钩子总数 33→34)。§4.1 登记运行器、§4.2.1 台账新增该模块并给"其余驱动不适格"补例外说明(驱动不适格 ≠ 驱动之上的胶水层不适格) |
|
||||
| 2026-10-12 | **§9 瘦身:机器侧内容迁出**(远程 CI 文档独立成 `docs/CI/`)。§9.2 两个 job 的**步骤级细节**并入 `docs/CI/远程CI完整指南.md` §5.5、§9.4 部署前置表并入其 §0.2;本文 §9 只保留**测试分层视角**的内容(CI 覆盖哪些层、两个 job 各守哪层、触发与 paths-ignore 语义、本地等价 `ci_local.py`、"CI 不替代本地门禁")。§9.1 两个 🔴 块压缩为结论 + 指针(机制根因与排障口径归 `docs/CI/`)。相关文档表两行 CI 条目合并为一行指向 `docs/CI/README.md` |
|
||||
| 2026-10-10 | §4.2.1 台账:`Drivers/BSP/NETCFG/net_cfg_legacy.c` 由 `⬜ 候选` 改为 **`⛔ 已删除`**——旧档 NCFG 迁移整体移除(`net_cfg_legacy.{c,h}` 删除、`cfg_store_legacy_path()` 撤销;旧档 `/cfg/netcfg.bin` 不读、不删、不迁移,配置回落工厂默认),该测试候选随之撤销 |
|
||||
| 2026-10-02 | **S6**:`test/run_all_suites.py` 新增 **`--check-build`**(只编译不烧录,纯离机,不需要 MCU/串口/ST-LINK);新增 **§3.6.1** 说明其解决的问题(测试构建无独立门禁,2026-08~09 CH395F 套件实例)、判定语义与用法。**实测 4 套件全部 `BUILD_OK: 0 errors, 0 warnings`**(即 T-4 提到的"CH395F 不可编译"已修复) |
|
||||
| 2026-10-02 | **S1-b + S2**:新增 **§4.2.1「L0.5 适格模块台账」**(判据 + 模块状态表 + "两条实证":sys_clock / cfg_file 首测即各抓一处生产缺陷);§4.1 登记 `test/host/cfg_file/` 新单测。同步新增 [`配置文件单元测试说明.md`](配置文件单元测试说明.md)(含抓出的 `s_encode` 返回值误判 P0 缺陷记录) |
|
||||
| 2026-10-02 | **S1-b + S2**:新增 **§4.2.1「主机侧纯逻辑单测适格模块台账」**(判据 + 模块状态表 + "两条实证":sys_clock / cfg_file 首测即各抓一处生产缺陷);§4.1 登记 `test/host/cfg_file/` 新单测。同步新增 [`配置文件单元测试说明.md`](配置文件单元测试说明.md)(含抓出的 `s_encode` 返回值误判 P0 缺陷记录) |
|
||||
| 2026-10-02 | **S3 + S14**:§9.1 第 2 条改正(原文"强制跑 `mem-numbers`"自相矛盾 → 改为"按 `.map` 有无自适应跳过")并加 🔴 门禁盲区声明;§9.2 补 S3 结果可见性(`Tee-Object` 落盘 + `$GITHUB_STEP_SUMMARY` + `::notice::` 汇总行,不用 `actions/upload-artifact`)。另:§4.1 登记 `test/host/sys_clock/` 新单测(S1-a) |
|
||||
| 2026-10-01 | 新增 **§9 CI 持续集成(Gitea Actions,T-6)**:`.gitea/workflows/ci.yml`(`l0-gates` + `l05-host-unit-tests`,Windows runner);**L0 + L0.5 上 CI、L1/L2/L3 留本地**;刻意不跑 `@build`、`.map` 缺失时自适应跳过 `mem-numbers`;新增 `scripts/ci_local.py` 本地等价命令(§4.1 登记);§1 补 CI 说明;`docs/任务规划/README.md` T-6 销项 |
|
||||
| 2026-10-01 | 新增 **§9 CI 持续集成(Gitea Actions,T-6)**:`.gitea/workflows/ci.yml`(`l0-gates` + `l05-host-unit-tests`,Windows runner);**静态门禁 + 主机侧纯逻辑单测上 CI、板端测试套件/主机侧回归/人工目检留本地**;刻意不跑 `@build`、`.map` 缺失时自适应跳过 `mem-numbers`;新增 `scripts/ci_local.py` 本地等价命令(§4.1 登记);§1 补 CI 说明;`docs/任务规划/README.md` T-6 销项 |
|
||||
| 2026-10-01 | §3.6 新增 **`test/run_all_suites.py`** 多套件回归编排(`try/finally` 无条件还原套件开关;T-5);`test/README.md` 同步 |
|
||||
| 2026-10-01 | §3.2 断言宏改为**集中内核**:新增 `test/board/test_common.h`(`test_stats_t` + `TEST_CHECK_IMPL`/`TEST_REPORT_IMPL`),4 套件各自用一行宏绑定自身统计变量(T-4;零行为变化、零 RAM 增量、`@build` 0/0,四套件均 0/0 验证)。附 `test_common.h` 不 include `dbg_log.h` 的原因(`DBG_TAG` 冻结 → `#47-D`) |
|
||||
| 2026-10-01 | 新增 §7.1 钩子 **`test-suite-switch`**(测试套件开关=产品构建,防测试固件静默提交)+ §3.1 铁律(跑完套件回归务必改回产品构建);依据 `docs/任务规划/README.md` T-1(测试架构评审 P0)。同步更新 `../代码规范/pre-commit使用说明.md` §2.13 |
|
||||
| 2026-10-01 | §4.1/§4.2 更新:主机侧单测运行器统一走通用 harness `test/host/common/unit_test_harness.py`(T-3),模块运行器只做声明式配置;§6.2.1 新增套件汇总行校验(T-2) |
|
||||
| 2026-10-01 | §4.2 补 **L0.5 环境要求表**(MSVC `cl.exe` = 装了 VS 即有、与模拟器共用;Windows SDK;纯标准库 Python;缺失时退出码 2 且 pre-commit 钩子拦截)+ "不调 `vcvars64.bat`(`reg.exe` 受限)"原因说明;§4.1 工具清单标注该运行器需 VS。同步更新 `../环境与部署/项目部署环境要求.md`(§2.3 / §2.7 / 第 3、10 步 / 清单 / 下载表 / 速查表) |
|
||||
| 2026-10-01 | §4.2 补 **主机侧纯逻辑单测环境要求表**(MSVC `cl.exe` = 装了 VS 即有、与模拟器共用;Windows SDK;纯标准库 Python;缺失时退出码 2 且 pre-commit 钩子拦截)+ "不调 `vcvars64.bat`(`reg.exe` 受限)"原因说明;§4.1 工具清单标注该运行器需 VS。同步更新 `../环境与部署/项目部署环境要求.md`(§2.3 / §2.7 / 第 3、10 步 / 清单 / 下载表 / 速查表) |
|
||||
| 2026-09-15 | §3.4 更新:P1-6 修复落地(基础线程策略)——USER CODE 区冗余 netTask 创建删除 + `net_task_func` 入口 `ENABLE_HW_LAYER_TESTS` 自挂起守卫;"代码事实/设计约束"按修复后现状重写(硬件层模式中断断言恢复安全、NET 层/产品仍按"netTask 恒在轮询"设计);彻底方案(`.ioc` 移除)经评估未采用。同日构建验证发现两处**存量**测试固件缺陷(与 P1-6 无关,A/B 对照确证):① `net_test_task.c` `s_rx_buf` 使用先于声明 → NET 层配置自 2026-08(88160a5)起不可编译;② 128KB 静态 RAM 预算溢出(产品镜像 123.6KB,HW 全阶段固件超 8.7KB、NET 固件超 ~28KB)——已立 P1-9/P1-10 跟踪 |
|
||||
| 2026-09-13 | 重写:对齐现测试架构——统一 `test_config.h` 套件(旧版仅描述 CH395F 单套件与 `ch395fTestTask` 旧入口);新增 @test 自动化闭环、pre-commit 门禁、UI 模拟器五步门禁、主机侧工具约定;新增"测试构建下 netTask"已知问题(旧版"硬件层模式 netTask 不创建"表述与代码不符,改为设计约束并给出重构路径);修正附录路径(旧版 `docs/测试说明/CH395F_Test_Guide.md` 等已移入 `docs/测试说明/`);新增双端注册、内存预算、陷阱沉淀要求 |
|
||||
| 2026-10-22 | §5.3 黄金参考纪律更新:`ref/*.bin` 入库(新克隆即可跑 UI 像素回归)、`.png` 仍不入库;补充"刷参考会在 `git diff` 显形,须逐页过目并在提交正文说明" |
|
||||
|
||||
+3
-3
@@ -15,12 +15,12 @@
|
||||
用只认行首缩进的模式直接数是 52(含那一条注释掉的候选,故与 YAML 口径差 1)。
|
||||
⚠ 反过来的坑也真踩过(2026-10-27):在配置**注释里复述那个 grep 字面量**,会让文本 grep 再虚高 1——
|
||||
数数只认 YAML 解析,注释与文档里都别写出那个字面量(`docs/门禁说明/nested-comment.md` §8 记了现场)。
|
||||
实跑侧口径:`scripts/ci_local.py` 的 `[SUMMARY] L0: pre-commit hooks -- passed=A failed=B skipped=C`,
|
||||
实跑侧口径:`scripts/ci_local.py` 的 `[SUMMARY] 静态门禁: pre-commit hooks -- passed=A failed=B skipped=C`,
|
||||
`A+B+C` = **commit 阶段**的钩子数(50),再加 `gitlint` 1 条(它的 commit-msg 阶段声明写在**上游**仓库自带的
|
||||
钩子清单里,本配置文件里查不到,见 `scripts/ci_local.py` 文件头)→ 50 + 1 = 51 = 声明数,三数同源。
|
||||
- 通用机制(两阶段、stash 陷阱、`always` 已废、增量拦截三段式、旧 §2.x 查号表):
|
||||
`docs/代码规范/pre-commit使用说明.md` §2。
|
||||
- 快速自查(不等提交):`& '.venv\Scripts\python.exe' scripts/ci_local.py`(L0 + L0b + L0.5)。
|
||||
- 快速自查(不等提交):`& '.venv\Scripts\python.exe' scripts/ci_local.py`(静态门禁 + 提交信息门禁 + 主机侧纯逻辑单测)。
|
||||
|
||||
## 1. 三种拦截姿态(先分清这个,报错时才知道该干什么)
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
| [`nested-comment.md`](nested-comment.md) | `nested-comment` | 块注释体内再出现 /*(ARMCC 9-D 嵌套注释;诱因是注释里写路径通配) | 全仓 `.c/.h`(第三方与生成物除外) | 已写 |
|
||||
| [`generated-fresh.md`](generated-fresh.md) | `generated-fresh` | 生成器幂等性:把生成器重跑到**系统临时目录**,与工作区逐字节必须一致(不一致 = 生成物比生成器旧) | `tools/**`、UI 源码、`ui_menu_tree.txt`、`ui_cn_chars.txt`、`docs/images/*.svg`、两份注册表 | 已写 |
|
||||
| [`lv-conf-sync.md`](lv-conf-sync.md) | `lv-conf-sync` | 两份 `lv_conf.h`(MCU / 模拟器)的特性子集是否一致 | 两份 lv_conf 或脚本 | 已写 |
|
||||
| [`cubemx-survival.md`](cubemx-survival.md) | `cubemx-survival` | CubeMX 重生成会不会把定制静默抹掉(L3 审计兜底) | `.ioc`、`Src/`、`Inc/` | 已写 |
|
||||
| [`cubemx-survival.md`](cubemx-survival.md) | `cubemx-survival` | CubeMX 重生成会不会把定制静默抹掉(人工目检审计兜底) | `.ioc`、`Src/`、`Inc/` | 已写 |
|
||||
| [`test-suite-switch.md`](test-suite-switch.md) | `test-suite-switch` | 板级套件开关必须互斥;全注释 = 产品构建 | `test/board/test_config.h` | 已写 |
|
||||
| [`font-charset.md`](font-charset.md) | `font-charset` | UI 用到的汉字是否都在子集字体里(缺字 = 白框) | `Drivers/BSP/UI/**`、字体脚本 | 已写 |
|
||||
| [`ui-golden.md`](ui-golden.md) | `ui-golden` | UI 像素回归:菜单 8 状态 + 内容页 60 页黄金参考图比对 | UI 源码、模拟器 | 已写 |
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
> 它是"回归最快的两层之一"(静态层 + 逻辑层),但**不等于真机正确**。
|
||||
|
||||
- 22 个钩子各有一个 `files:` 白名单(改动被测模块或测试目录才跑)、`pass_filenames: false`;
|
||||
远端 CI 只跑 L0 静态门禁 + L0.5 主机侧单测(见 `docs/CI/README.md`)。
|
||||
远端 CI 只跑 静态门禁 + 主机侧纯逻辑单测主机侧单测(见 `docs/CI/README.md`)。
|
||||
|
||||
## 1. 它在查什么(22 个实例)
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
| `soe`(B9) | 32 断言 | 64 B 定长记录编解码往返 / 截断与坏校验和拒绝 / IV 位 / seq 连续性 / 类型枚举。记录格式是**磁盘上的字节级契约**,错了要 FTP 下载再解码才看得见 |
|
||||
| `nand-port`(dhara 移植层两条硬约束) | 149 断言 | 整页扫描的空闲判定(页首 64 B 全 0xFF 而第 1000 字节有数据等形态)+ **与旧算法对照**(旧实现只看页首,同一页答"空闲")。这类缺陷真机极难复现,发作后只表现为"某次复位后丢数据" |
|
||||
|
||||
- 运行器统一用 MSVC `cl.exe`(远端 CI 的 L0.5 层);公共骨架是
|
||||
- 运行器统一用 MSVC `cl.exe`(远端 CI 的 主机侧纯逻辑单测层);公共骨架是
|
||||
`test/host/common/unit_test_harness.py` —— **改动这个骨架会同时触发 22 个钩子**
|
||||
(每个钩子的 `files:` 都把它写进触发面,这是有意的)。
|
||||
|
||||
@@ -97,7 +97,7 @@
|
||||
`net_listener`、`net_pending`、`net_select`、`net_socket`、`prot_set_store`、`soe_store`、
|
||||
`sys_log_mcu`、`task_prof`、`ui_sink`;
|
||||
- 这 18 套每次提交都被 `test-coverage` **总闸**真跑:发现规则是
|
||||
`HOST_TEST_DIR.rglob("run_unit_test.py")`(与 `scripts/ci_local.py` 的 L0.5 同一口径),
|
||||
`HOST_TEST_DIR.rglob("run_unit_test.py")`(与 `scripts/ci_local.py` 的 主机侧纯逻辑单测同一口径),
|
||||
退出码语义写死「0 = 全部单测通过且覆盖率相对基线没回退 / 1 = 有单测失败或覆盖率回退」⇒
|
||||
**失败照样拦提交**。差的只是归因粒度(要在总闸输出里认出是哪套)与耗时(40 套全量比单根慢得多);
|
||||
- 要不要给某套补第 23 根,判据现成:pre-commit 4.x 没有 `always: true`,**该模块有没有自己的
|
||||
|
||||
Reference in New Issue
Block a user