Files
orisys-app/docs/test-repair-plan.zh.md
T
ykcheungab e268a9dde3 新增测试修复与门禁计划
记录 85d0b39 上各项失败的原因与处理方式、门禁脚本与已知失败清零的验收,并在分层执行计划中链接为先决条件。
2026-10-18 17:31:16 +08:00

75 lines
11 KiB
Markdown
Raw 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 / shared 测试修复与门禁计划
状态:待实施。日期:2026-10-18。基线:`orisys-app` `main` 的 `85d0b39`。执行者:一个代理(DeepSeek),按顺序完成,
每个阶段结束时向用户汇报。本计划补充 [分层测试执行计划](test-execution-plan.zh.md),不替代它。
## 1. 为什么要做
在 `85d0b39` 上,studio 测试在 Windows 上有 23 项失败或错误(Linux 上 24 项),shared 有 2 项,没有人察觉。原因不是单个缺陷,而是流程本身:
| 问题 | 证据 | 后果 |
|---|---|---|
| 没有强制执行的门禁 | 仓库没有 CI 或 pre-commit;规则只写在 AGENTS 里 | 10-05 至 10-08 至少 6 个提交改了行为但没改测试(第 3 节逐项列出) |
| 只跑定向测试,从不定期全量 | 分层计划写明"定向测试通过后结束验证" | 其他分组里的测试悄悄变红,没有人发现 |
| 文档里的门禁命令无效 | `-m "not timing"` 是文档写的门禁命令,但 `timing` 标记从未注册,也没有测试使用 | 已知不稳定的计时测试每次都失败,大家习惯无视红色 |
| 测试依赖未跟踪的数据 | 回放和裸视频测试读取 `dist/OrisysStudio/outputs/recordings/…`;10-14 重新打包后这份录像消失 | 换机器或重新打包,11 项测试报错,4 项失败 |
| 从仓库根目录无法运行 studio 测试 | `from tests.conftest import …` 在根目录找不到 `tests`;studio 与 autocal 的 `test_binding_purity.py` 同名冲突 | 文档写的"根目录 pytest"跑不起来,每个人自己找运行方式 |
| `ui_check.py` 不在任何门禁里 | 它的模拟 worker 从 10-08 起与 `start()` 签名不符,脚本中途崩溃,并连带约 15 项冒烟测试失败(已在 `85d0b39` 修复) | 3700 行的检查静默失效 |
| 跳过的测试无人计数 | studio 有 35 项跳过,原因没有汇总 | 环境坏了也可能显示为通过 |
结论:分层选择测试的思路是对的,但缺少两样东西——**一个任何人都绕不过去的全量门禁**,以及**一份必须为空的已知失败清单**。
## 2. 规则(执行者必须遵守)
- 遵守 `D:\orisys_ws\AGENTS.md`:scratch 放在 `orisys-app/.scratch/test-repair/`,带 `.active` 标记;pytest 使用 `--basetemp=<scratch>/pytest-<run>` 和 `-p no:cacheprovider`;改动不暂存、不提交;向用户汇报用中文。
- 解释器固定为 `D:\orisys_ws\orisys-app\.venv-build\Scripts\python.exe`,用 Windows PowerShell 5.1 语法。
- **不得**通过删除断言、放宽断言、加 skip 或 xfail 让测试变绿。改测试只有一个理由:测试与已确认的产品行为不一致,并且要写明是哪个提交确认了该行为。
- 每修一项,记录它在修复前失败、修复后通过的证据(命令和输出摘要)。
- `apps/autocal/` 目前有另一个任务在改。不要编辑 autocal 的源码或测试;如果第 4 节的收集问题必须改 autocal 文件,停下来问用户。
- 遇到第 3 节标为"需决定"的项,先准备好证据和建议,再停下来问用户,不要自己决定。
## 3. 逐项诊断(执行者先复核,再动手)
以下诊断来自 2026-10-18 在 Linux 离屏环境与 Windows 上的两次运行;"引入提交"由 `git log -S` 查得。执行者须用 `git show <提交>` 确认诊断后再修。
| # | 测试 | 现象 | 判断 | 处理 |
|---|---|---|---|---|
| 1 | `test_bare_video_analysis.py` 全部 11 项 | 找不到 `dist/…/20260930T015028Z_session0002_…/raw_0001.mp4` | 测试数据缺失 | 见第 4 节阶段 2 |
| 2 | `test_replay_wiring.py` 4 项(`_open` 报 the recording did not open;declined prompt 报 FileNotFoundError) | 同一份录像缺失;`_copy_recording` 静默跳过缺失文件,而模块级的 skip 只在部分路径生效 | 测试数据缺失,加上 fixture 行为不一致 | 阶段 2;同时让缺数据时一律明确失败 |
| 3 | `test_replay_analysis.py::test_a_stitch_grid_is_not_painted_onto_the_camera_frame` | 测试桩 `_Reader` 没有 `provenance` 属性 | 测试过时(`replay_analysis.py:245,401` 读取 `reader.provenance`,引入于 `829d2c1`,10-06) | 给测试桩补上 `provenance`,取值仿照真实 reader,不得改产品代码 |
| 4 | `test_ui_smoke.py::test_old_result_depth_is_not_sent_to_the_new_sensor_scene` | 调用已删除的 `_show_analysis_frame` | 测试过时(`cf49b08`、`28e955a` 改为 `show_analysis_packet`) | 改用公共入口,**保留**原断言意图:旧结果的深度不得送进新传感器的场景 |
| 5 | `test_scene3d_view.py::test_the_replay_view_shows_the_pane_and_says_where_the_field_comes_from` | 断言文案含"重算",现在是"分析结果:非录制时的测量值" | 测试过时(`c0a10e6` 改了文案) | 改为对比 locale 键 `source_recompute`,不再硬编码字面词 |
| 6 | `test_ui_elements.py::test_every_declared_condition_is_used_and_reaches_a_window` | `liveCalibrationCombo`、`sensorProfileScroll` 未登记 | 登记表过时(`3d2fb0c` 新增控件) | 用 `apps/studio/tools/ui_elements_dump.py` 生成条目,补进 `apps/studio/docs/ui-elements.json`,逐条写明用途 |
| 7 | `test_ui_smoke.py::test_run_tooltip_explains_disabled_reason` | 期望选了视频文件后「运行」可用 | 测试过时:实时视图不再用视频运行(`test_live_run_refuses_a_video_source` 断言的正是相反行为) | 改为断言:视频源时按钮禁用、提示文字和顶栏 `run_hint` 都是 `toolbar.run_video_in_replay` |
| 8 | `test_ui_smoke.py::test_the_sensor_summary_opens_the_dialog` | 没有导出系数时「应用到设备」仍可用 | 很可能是测试过时(`3d2fb0c` 让该按钮应用对话框里选中的实时标定) | 读 `sensor_dialog.py:324` 一带确认行为后改断言;若该按钮在什么都没选时也可用并会出错,按产品缺陷处理 |
| 9 | `test_pause_controls.py::test_the_transport_says_which_way_it_goes` | 暂停提示不再提到录制会留空档 | **需决定**:`f2bbab7`(10-08)为缩短文案删掉了这句,但修复计划 A 要求提示写明空档 | 建议恢复一句短的"录制会留空档",测试保持不变;须用户确认 |
| 10 | `test_ui_smoke.py::test_the_replay_view_is_the_live_view_with_a_video_source`,以及 `ui_check` 的同名检查 | 回放与实时的分栏宽度不同(589/825 对 643/771) | **需决定**:可能是 `321e52f`(10-06,改善回放布局)有意改变,也可能是回归 | 用 `git bisect` 在测试上定位引入提交,向用户说明那次改动的意图,再决定改测试还是改代码 |
| 11 | `test_ui_smoke.py::test_the_rail_puts_result_identity_before_a_long_video_name` | 仅 Linux 失败(字体宽度不同),Windows 通过 | 环境差异 | 不改;在门禁说明里写明门禁平台是 Windows |
| 12 | `shared/tests/test_worker_frame_rate.py` 2 项 | "30 fps 设备只消费到 21 fps",取决于机器负载 | 计时测试,本身不稳定 | 注册并标记为 `timing`,移出合入门禁;另开任务把它改成确定性测试 |
| 13 | `shared/tests/test_scene3d_protect.py::test_the_release_gate_fails_an_unsigned_package` | 仅 Linux 失败 | 环境差异 | 不改;记录在案 |
## 4. 实施阶段
| 阶段 | 内容 | 验收 | 估时 |
|---|---|---|---|
| 0 基线 | 记录 HEAD、解释器、PySide6 与 pyqtgraph 版本;分别在 `apps/studio`、`shared` 下跑全量(`-m "not timing"`),并运行 `ui_check.py`,产出 JUnit XML | scratch 下有失败清单,与第 3 节逐项对上;对不上的项先报告用户 | 0.5 h |
| 1 让门禁能跑 | 在根 `pyproject.toml` 注册 `timing` 标记,并加 `--strict-markers`;标记第 3 节 12 号的两项;解决根目录收集问题(建议:把 studio 测试从 `tests.conftest` 导入的辅助函数移到 `apps/studio/tests/_studio_helpers.py`,并在 studio 的 conftest 里把该目录加入 `sys.path`;同名测试文件用 `--import-mode=importlib` 或改名解决,**不改 autocal 文件**) | 在仓库根目录执行 `python -m pytest --collect-only -q` 时收集错误为 0;`-m "not timing"` 确实排除了 2 项 | 2–3 h |
| 2 测试数据 | 第 3 节 1、2 号:新建 `apps/studio/tests/data/`,放一段真实的 box 短录像(`raw_0001.mp4`、`frames.csv`、`forces.csv`、`events.csv`、`session.json`),约 30 帧;所有测试改为从这里读取;只需要回放接线的测试,优先用 `ExperimentRecorder`(`shared/.../record/experiment.py`)在 fixture 里生成合成录像,写法参考 `ui_snapshot.py --scene replay`;数据缺失时一律明确失败,不跳过 | 两个文件的全部用例在干净的检出上可运行;`dist/` 不再出现在任何测试路径中(`grep -r "dist" apps/studio/tests` 只剩注释) | 2–3 h,另需用户录一段 |
| 3 逐项修复 | 第 3 节 3–8 号直接修;9、10 号先问用户再修 | 每项记录修前失败、修后通过;断言数量不减少(列出每个改动测试的断言前后对比) | 3–4 h |
| 4 门禁脚本 | 新增 `tools/gate.ps1`:依次运行 `tools/lint_gate.py`(改动文件)、studio 全量、shared 全量、`ui_check.py`;全部输出写进 scratch 并汇总为一页;读取 `tools/known_failures.txt`,**该文件必须为空,有内容就让门禁失败**;记录跳过数,超过 `tools/skip_budget.txt` 中的上限就失败;收集数低于下限也失败(防止整个模块被静默跳过) | 在当前状态运行 `gate.ps1` 退出码为 0;故意改坏一个断言后退出码非 0;故意删掉测试数据后退出码非 0 | 3–4 h |
| 5 接入流程 | 在 `orisys-app` 提供可选的 `pre-push` 钩子(调用 `gate.ps1`,安装方式写进 README,不自动安装);修订 `test-execution-plan.zh.md`:定向测试用于开发中,**提交前跑一次 `gate.ps1`**;第 4 节表格里加"门禁平台:Windows" | 文档门禁通过;README 写有安装钩子的一条命令 | 1 h |
| 6 收尾 | 以最终状态跑一次 `gate.ps1`;写 `commit-message.txt`;用中文汇报 | 汇报区分:已修、需用户决定、移出门禁的(含理由)、仍未解决的 | 0.5 h |
阶段 1 必须先于其他阶段完成;阶段 2 和 3 可以交错进行;阶段 4 依赖前三个阶段全部变绿。总计约 12–16 小时,外加用户录制测试数据的时间。
## 5. 需要用户先决定的事
1. **测试录像放在哪里。** 建议放进 git(`apps/studio/tests/data/`,控制在 5 MB 以内);如果不想让仓库变大,就改用环境变量 `ORISYS_TEST_DATA` 指向 NAS 上的目录,但缺失时测试必须失败,不能跳过。还需要用户用 box 传感器录一段约 1 秒的短录像。
2. 第 3 节 9 号:暂停提示是否恢复"录制会留空档"。
3. 第 3 节 10 号:回放与实时布局不一致,属于有意改动还是回归(执行者 bisect 之后再问)。
4. 以后是否搭 Gitea runner(Windows 自托管)自动跑 `gate.ps1`。本计划只做到本地钩子;runner 另开任务。
## 6. 不在本计划内
计时测试改为确定性测试;GL 真机冒烟测试;autocal 测试树的问题;测试目录重排。这些都在门禁稳定之后另行安排。