Files
orisys-app/docs/test-repair-rework.zh.md
ykcheungab 55dafeb7fb 记录测试修复第 3 轮验收通过
代码审查与哈希核对一致,用户独立运行的门禁 PASS,任务标为 done。
2026-10-18 21:36:57 +08:00

112 lines
15 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Studio 测试修复:第 3 轮返工
状态:done。日期:2026-10-18。前序计划与诊断:[测试修复与门禁计划](test-repair-plan.zh.md),其阶段 0–6 已交付于 `433b76e`。
自第 3 轮起,本文件是 task_id `test-repair` 的权威任务记录,原计划不再维护状态。
## 1. 为什么返工
验收方(Claude)读了 `433b76e` 的实际差异,未在 Windows 上复跑。第 2 轮的测试修复大体成立,但以下问题会让门禁或产品行为与声明不符:
| # | 问题 | 证据 | 后果 |
|---|---|---|---|
| R1 | 推送前的门禁不 lint 已提交的改动 | `gate.ps1` 调用 `tools/lint_gate.py` 时不带 `--base`;`lint_gate.py` 第 87–89 行只在给了基准时比较 `base...HEAD`,否则只看暂存与未暂存差异 | pre-push 时要推送的提交从不被检查,反而会检查其他任务未提交的 autocal 文件 |
| R2 | 叠加底图的粘性失败范围过大 | `replay_analysis.py` 的 `overlay_base()` 在 `project()` 第一次返回 `None` 时就置 `_overlay_failed`;`overlay_base.py` 的 finger 投影在单帧 `unrolled is None` 或任何异常时都返回 `None` | 一帧坏图就让 finger 结果在整个会话里失去叠加视图,超出本任务只处理 box 的范围 |
| R3 | 新回退逻辑没有测试,采纳时还会闪出拒绝文案 | 测试中没有引用 `overlay_unavailable`;`serve_stored_view()` 先按 `ARROW_STITCHED` 渲染一次,失败后改 `ARROW_PURE` 再渲染一次 | 回退坏了门禁仍是绿的;操作员会短暂看到“采集画面不可用” |
| R4 | 门禁与测试数据的小问题 | 见第 3 节 R4 | 证据不完整,或门禁结论偏宽 |
## 2. 规则
沿用原计划第 2 节全部规则:scratch、固定解释器、不暂存不提交、不改 autocal、不得通过删除或放宽断言、加 skip 或 xfail 让测试变绿。
scratch 仍为 `orisys-app/.scratch/test-repair/`,本轮运行放在 `round3/` 下;开始时写 `.active`,结束后删除。
## 3. 返工项
| 项 | 要求 | 验收 | 估时 |
|---|---|---|---|
| R1 lint 基准 | `gate.ps1` 新增 `-LintBase` 参数,默认 `origin/main`,传给 `lint_gate.py --base`。先用 `git rev-parse --verify` 校验,无效时退出码 2,不得静默退回只看工作区。`summary.md` 写明 lint 基准和被检查的文件数。pre-push 钩子用默认值;README“合并门禁”一节补一句参数说明 | ① `gate.ps1 -LintBase HEAD~1` 的 `lint.log` 列出 `433b76e` 改过的 `.py` 文件,例如 `replay_controller.py`;② `-LintBase no-such-ref` 退出码 2;③ 默认运行退出码 0 | 1 h |
| R2 失败范围 | 只有“再试也不会成功”的失败才粘性:投影器打开失败,或 box 投影失败(第 2 轮实测每帧都失败)。finger 投影单帧返回 `None` 只影响该帧,下一帧照常尝试。建议由 `OverlayBaseProjector` 按传感器声明失败是否永久,`AnalysisSession` 据此决定;不得按异常文本判断 | 新增不依赖 SDK 的单元测试,用替身投影器:① finger 式投影首帧 `None`、次帧成功 → 次帧拿到底图,`overlay_unavailable` 为假;② box 式投影失败一次 → `overlay_unavailable` 为真,之后不再调用投影;③ 打开失败 → 同 ②。记录 ① 在第 2 轮代码上失败的证据 | 1.5 h |
| R3 回退测试 | `serve_stored_view()` 在设置视图之前就确定该结果能否画叠加,只设置一次视图、只渲染一次。新增测试:用 `tests/data/box_short_20260930` 走真实分析流程采纳 box 结果,断言 ① 最终视图为 `ARROW_PURE`;② 采纳过程中面板从未显示 `panels.arrow_capture_unavailable`;③ `render_frame` 只调用一次;④ 之后手动选 `ARROW_STITCHED` 仍显示拒绝及原因 | 新测试的 ② 或 ③ 在第 2 轮代码上失败、修后通过;`test_bare_video_analysis.py` 现有断言不变且通过 | 1.5 h |
| R4 小修 | a. `tests/data/README.md` 的来源改为实际来源 `.scratch/result-visible/`,注明 `dist/` 原件已不可用、无法逐字节比对,并列出 5 个文件的 SHA256;b. `gate.ps1` 结束时在 `finally` 中删除 `.active`,运行号改用 UTC 时间;c. `ui_check.py` 退出码非 0 时判失败;d. pytest 退出码非 0 而 JUnit 无失败时,只放行 `0xC0000005`(`-1073741819`)并在汇总中写明,其他非 0 退出码判失败 | a 的哈希与实际文件一致;b–d 各用一次临时构造的场景验证(例如临时让 `ui_check.py` 以退出码 3 结束),验证后恢复,并用 `git diff` 确认无残留 | 1 h |
R2 先于 R3;全部完成后以最终状态跑一次 `gate.ps1`。总预算 5 h,超出 50%(7.5 h)按约定标 blocked。
## 4. 需用户决定(本轮不执行)
1. **box 叠加画面的断言。** `test_bare_video_analysis.py` 原断言“箭头画在采集画面上”,第 2 轮改为断言“面板拒绝”。这样产品缺陷在门禁里显示为通过,修好后也不会有测试提醒。选项 A:保持现状,另开任务修复 box 底图重建,先查明为何分析 pass 换阈值重试能成功而投影器不能(建议)。选项 B:恢复原断言并加 `xfail(strict=True)`,需要用户对原计划第 2 节“不得加 xfail”破例。执行方不得自行选择。
2. **深度图非零。** `test_replay_wiring.py` 把“深度恒为零”改为“有非零值”(实测 8926/133225)。需用户确认这段录像上 box 链路返回深度场是预期行为。
## 5. 不在本轮内
计时测试改为确定性测试,包括诊断实测 65.5 fps 高于假相机 30 fps 的原因:负载只会让测得帧率变低,这更像计数问题。另有:把 `test_a_low_camera_rate_is_recorded_as_a_negotiation` 中不依赖计时的断言拆回门禁;为 `timing` 测试提供单独的运行入口;autocal 测试树;Gitea runner。
## 交接记录
按 [代理交接约定](../../docs/platform/agent-handoff.zh.md) 维护;只保留当前状态和每轮一行摘要。
- task_id:test-repair;round:3;updated_at:2026-10-18 21:20 +08:00
- status:done;owner:Claude(验收);next_agent:无(第 4 节两项待用户决定后另开任务)
- 目标与完成条件:第 3 节 R1–R4 的验收列全部满足,最终 `gate.ps1` 退出码 0
- 范围:allowed_files 为 `tools/gate.ps1`、`.githooks/pre-push`、`README.md`(合并门禁一节)、
`apps/studio/src/orisys_studio/replay_analysis.py`、`apps/studio/src/orisys_studio/replay_controller.py`、
`shared/src/orisys_app_shared/analysis/overlay_base.py`、`apps/studio/tests/` 下新增或修改的测试、
`apps/studio/tests/data/README.md`、本文件交接记录。排除:`apps/autocal/**`、`orisys-sdk`、
`test_bare_video_analysis.py` 的现有断言、放宽 `tools/skip_budget.txt` 或填写 `tools/known_failures.txt`、第 4 节两项
- 停止条件:交接约定默认条件;需要改 `orisys-sdk` 或 `apps/autocal` 时停止并询问;时间预算 5 h
- 影响面:`overlay_base.py` 属于 shared,当前唯一消费者是 `replay_analysis.py`;lint 默认基准改变后,推送前检查覆盖全部未推送提交
- 授权:用户 2026-10-18 19:46 要求“写成给 DeepSeek 的后续任务”;执行需用户在 DeepSeek 会话中发出启动指令;不暂存、不提交、不推送
### 基准与输入
| 仓库/checkout | 分支、HEAD | 起始暂存/未暂存/未跟踪变化 | 本轮修改 |
|---|---|---|---|
| `D:\orisys_ws\orisys-app` | `main`;起点 `fbf8c76`(= `433b76e` + 提交本文件);执行期间外部任务又提交 `9352d0c`、`cf3e638`、`0998009`,收尾 `0998009` | 暂存区空;起点时 `apps/autocal/**` 7 个文件未暂存(另一任务),另有未跟踪的 `apps/studio/tests/test_status_presenter.py` 与 `docs/architecture/studio-window-refactor-plan.zh.md`;`LZ.*` 压缩包与目录未跟踪 | 8 个文件,见下表 |
本轮修改(SHA256 为收尾实测值,供写入核对):
| 文件 | 字节 | SHA256 |
|---|---|---|
| `tools/gate.ps1` | 16266 | `a588d9d4cc5d80b90e06697efa48bd8bc3321843bb7ed8f99ca36bab0f29ab51` |
| `README.md` | 7426 | `7e9b36b014890f50a63c9391c8e6fcea0942f282e0cab3bd2a593d158569bd4f` |
| `shared/src/orisys_app_shared/analysis/overlay_base.py` | 9853 | `8b55059dbf0a65dcf015084605a2540d699af9b207f7f5d70cbc84fc0080a943` |
| `apps/studio/src/orisys_studio/replay_analysis.py` | 30003 | `aac7d47eab1301f4b8142497a3faa34c20b354e1aa069a4eeb0e7b327bb556a2` |
| `apps/studio/src/orisys_studio/replay_controller.py` | 76930 | `15155468465e11b5516b94de5c7271f5c44aa8ae8cc263233035f847c7305ae8` |
| `apps/studio/tests/test_overlay_failure_scope.py`(新增) | 5291 | `e1166c925b2203a5e13d3626fcaa7e255a000e9040d4878e9cfdddc96e252ac7` |
| `apps/studio/tests/test_replay_wiring.py` | 35799 | `ee2a315be57d75c4f26395691e7f0b8b83f593730edc3721002f16cfd7e11e2d` |
| `apps/studio/tests/data/README.md` | 2897 | `166cb831d1c0aa89f8c0c140eb3725127296af6fe334a5807c36695ac53bce85` |
| 本文件交接记录 | — | 随本记录变化,不列自哈希 |
`.githooks/pre-push` **未改**:它调用 `gate.ps1` 时不传 `-LintBase`,默认值 `origin/main` 正是推送前需要的基准。
- 环境:`D:\orisys_ws\orisys-app\.venv-build\Scripts\python.exe`,Windows PowerShell 5.1(`powershell.exe`)
- 输入:`apps/studio/tests/data/box_short_20260930/`,实测 `git diff 433b76e -- apps/studio/tests/data` 为空;5 个文件 SHA256 已写进 `apps/studio/tests/data/README.md`
### 执行结果
| 验收项与预期 | 实际命令及 run_id | 实际结果 | 证据路径 |
|---|---|---|---|
| R1① `-LintBase HEAD~1` 的 lint.log 列出 `433b76e` 改过的 `.py` | `gate.ps1 -Task test-repair/round3 -LintBase HEAD~1`(`gate-20261018T121600Z`) | 通过:lint.log 新增 `files checked against HEAD~1 (15)`,含 `replay_controller.py`、`replay_analysis.py`,退出码 0 | `round3/gate-20261018T121600Z/lint.log`、`summary.md` |
| R1① 追加:范围覆盖**已提交**改动(用稳定基准排除“工作区脏”混淆) | `gate.ps1 -LintBase dcabd54`(= `433b76e~1`,`gate-20261018T122730Z`) | 结果见“剩余限制”:lint 代码部分 `no findings`,29 个文件;该次失败是并行的外部文档改动触发文档门禁 | `round3/gate-20261018T122730Z/lint.log` |
| R1② 无效基准退出码 2 | `gate.ps1 -LintBase no-such-ref` | 通过:`gate: -LintBase 'no-such-ref' is not a valid git ref…`,`EXIT=2`,未创建运行目录 | 本记录 |
| R1③ 默认运行退出码 0 | `gate.ps1 -Task test-repair/round3`(`gate-20261018T130919Z`) | 通过:`PASS`,`LintBase origin/main`,33 个文件,exit 0 | `round3/gate-20261018T130919Z/summary.md` |
| R2① finger 单帧失败不粘性 | `pytest apps/studio/tests/test_overlay_failure_scope.py` | 通过 6 项;第 2 轮代码上 2 项失败(`assert True is False`,`overlay_unavailable` 变 True) | `round3/r2-green.log`、`round3/r2-round2-fails.log` |
| R2② box 失败一次即粘性且不再调用投影;③ 打开失败同 ② | 同上 | 通过(`calls == 1`) | 同上 |
| R3①–④ 采纳 box 结果只设一次视图、只渲染一次、无拒绝文案、手选仍拒绝 | `pytest apps/studio/tests/test_replay_wiring.py -k adopting_a_box_result` | 通过;第 2 轮代码上失败 `adoption rendered the frame 2 times, not once` | `round3/r3-1.log`、`round3/r3-round2-fails.log` |
| R3 输入不变 | `pytest apps/studio/tests/test_bare_video_analysis.py`(随套件) | 通过,未改其断言 | `round3/gate-20261018T130919Z/studio-suite.log` |
| R4a 来源、dist 说明、5 个 SHA256 | 实测 `Get-FileHash` 与 `.scratch/result-visible/recording/20260930T015028Z_session0002_…/` 逐字节一致 | 通过:5 个哈希已写入文件 | `apps/studio/tests/data/README.md` |
| R4b UTC 运行号 + `finally` 删除 `.active` | 多次 gate 运行(成功与失败各若干次) | 通过:目录名 `gate-20261018T130919Z` 对应 UTC 13:09(本地 21:09);每次结束后 `.scratch/test-repair/round3/.active` 不存在 | 各 `gate-*` 目录 |
| R4c `ui_check.py` 退出码非 0 判失败 | 临时让 `ui_check.py` 在打印干净汇总后 `SystemExit(3)`,运行 gate(`gate-20261018T125009Z`) | 通过:`ui_check | failed | ui_check.py exited 3`;当时汇总行是 `failures: 0`,旧逻辑会放行。恢复后 `ui_check` 仍 632 检查 0 失败 | `round3/gate-scenario-S1b.log`、`round3/bak-ui_check.py`(原始副本) |
| R4d 只放行 `0xC0000005` | ① 临时 hook 让套件以 `-1073741819` 结束(`gate-20261018T125009Z`);② `PYTEST_ADDOPTS=-p exitprobe` 让 shared 套件在干净 JUnit 后 `os._exit(3)`(`gate-20261018T130308Z`) | 均通过:① `studio suite | passed | …; process exit 0xC0000005 after the summary (Qt teardown, allowed)`;② `shared suite | failed | …; process exit 3 is not the allowed teardown crash` | `round3/gate-scenario-S1b.log`、`round3/gate-scenario-S2b.log`、`round3/exitprobe.py` |
- 已完成:R1–R4 全部实现;临时场景用的 `apps/studio/tools/ui_check.py`、`apps/studio/tests/conftest.py` 已从 scratch 副本恢复,`git diff --stat` 对这两个文件为空;`.scratch/test-repair/round3/exitprobe.py` 只在受控运行中通过 `PYTEST_ADDOPTS` 加载,不是仓库文件。
- 失败样本:第 2 轮代码上的两处红证据(R2 的 `r2-round2-fails.log`、R3 的 `r3-round2-fails.log`)保留在 scratch。
- 剩余限制:① 执行期间有并行任务在本仓库提交(`9352d0c`、`cf3e638`、`0998009`)并短暂留下未提交的 studio 改动,`gate-20261018T122730Z` 的 lint 行因此判失败——那一行是**文档门禁**报出外部改动里的文档问题,代码部分是 `no findings`;同时暴露 `lint_gate.py`/`ui_check.py` 在 cp1252 控制台打印非 ASCII finding 会抛 `UnicodeEncodeError`(两处都在本任务 allowed_files 之外,未改)。② 最终验收运行前确认工作区只剩本任务 8 个改动。③ `origin/main` 落后于本地 9+ 提交,默认基准覆盖全部未推送提交。
- 后台进程及 scratch 使用状态:无后台进程残留;`.scratch/test-repair/.active` 与 `round3/.active` 均不存在;`round3/` 保留 8 次 gate 运行目录与运行日志。
### 验收与下一步
- 轮次摘要:第 1 轮定义计划;第 2 轮交付 `433b76e`,验收判返工(见第 1 节);第 3 轮 DeepSeek 完成 R1–R4,用户提交为 `c37efd7`,验收接受(done)
- 验收者、时间及复核时的代码/输入身份:Claude,2026-10-18 21:35 +08:00,HEAD `c37efd7`;上表 8 个文件及录像 5 个文件的 SHA256 与实测一致
- 独立检查与结果:读了 `c37efd7` 的实际差异,R1–R4 与第 3 节一致;核对了 `round3/gate-20261018T130919Z` 的 JUnit 和两份第 2 轮红证据,与自报一致,哈希证明它们对应现在的代码。验收方没有可输入的 Windows 终端,门禁由用户独立运行:`gate.ps1 -Task test-repair/accept -LintBase 0998009`(`accept/gate-20261018T132615Z`)PASS,lint 5 个文件无 findings,studio 569 项、shared 485 项 0 失败 0 错误,`ui_check` 632 检查 0 失败,运行后无 `.active` 残留。不阻塞的意见见 `.scratch/test-repair/round3-accept/review.md`
- 决定:接受;R1–R4 的验收列全部满足,最终门禁退出码 0
- 下一步:第 4 节两项由用户决定;不阻塞意见可并入后续任务