433b76e43c
把丢失的 box 录像放回 apps/studio/tests/data/,修掉第 3 节 3–10 号与两处过时测试桩, 注册 timing 标记并让根目录收集零错误;新增 tools/gate.ps1 与可选的 pre-push 钩子。 验证:studio 524 通过 19 跳过、shared 467 通过 18 跳过、ui_check 632 项 0 失败、gate.ps1 退出码 0。 细节见 docs/test-repair-plan.zh.md。
14 KiB
14 KiB
Studio / shared 测试修复与门禁计划
状态:待实施。日期:2026-10-18。基线:orisys-app main 的 85d0b39。执行者:一个代理(DeepSeek),按顺序完成,
每个阶段结束时向用户汇报。本计划补充 分层测试执行计划,不替代它。
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. 需要用户先决定的事
- 测试录像放在哪里。 建议放进 git(
apps/studio/tests/data/,控制在 5 MB 以内);如果不想让仓库变大,就改用环境变量ORISYS_TEST_DATA指向 NAS 上的目录,但缺失时测试必须失败,不能跳过。还需要用户用 box 传感器录一段约 1 秒的短录像。 - 第 3 节 9 号:暂停提示是否恢复"录制会留空档"。
- 第 3 节 10 号:回放与实时布局不一致,属于有意改动还是回归(执行者 bisect 之后再问)。
- 以后是否搭 Gitea runner(Windows 自托管)自动跑
gate.ps1。本计划只做到本地钩子;runner 另开任务。
6. 不在本计划内
计时测试改为确定性测试;GL 真机冒烟测试;autocal 测试树的问题;测试目录重排。这些都在门禁稳定之后另行安排。
交接记录
按 代理交接约定 维护;只保留当前状态和每轮一行摘要。
- task_id:test-repair;round:2;updated_at:2026-10-18 18:45 +08:00
- status:ready(待验收);owner:Claude(定义);executed_by:DeepSeek
- 目标与完成条件:用户 18:36 把范围扩到全计划,阶段 0–6 全部执行,验收以第 4 节各行与本记录为准
- 范围:
pyproject.toml、apps/studio/**(tests、tools、locales、docs)、shared/tests/**、新增tools/gate.ps1等门禁文件;apps/autocal/**一律未改 - 停止条件:交接约定的默认条件;收集问题必须改 autocal 文件时停止(未触发)
- 时间预算:按第 4 节估时逐阶段累计;等待用户决定与数据的时间不计
- 影响面:根
pyproject.toml的--strict-markers同时作用于 autocal 测试树;收集已核对(0 未知标记、 0 错误) - 授权:用户 18:36 授权执行全计划并按阶段汇报;本轮未提交、未推送、未安装依赖
基准与输入
| 仓库/checkout | 分支、HEAD | 起始暂存/未暂存/未跟踪变化 | 本轮修改 |
|---|---|---|---|
D:\orisys_ws\orisys-app |
main、e268a9d(执行期间他人提交到 9a3886b,均属 autocal 任务) |
autocal 有其他任务的改动(已由其自行提交),不属本任务 | 23 项修好、门禁落地、录像入库;见下 |
- 环境:
D:\orisys_ws\orisys-app\.venv-build\Scripts\python.exe(版本见 phase0 汇总) - 输入:录像从
.scratch/result-visible/找回(帧号 20…52,与测试期望一致),复制进apps/studio/tests/data/box_short_20260930/(3.79 MB,不含 preroll);用户无需补录
执行结果
| 验收项与预期 | 实际命令及 run_id | 实际结果 | 证据路径 |
|---|---|---|---|
| 阶段 0:失败清单与第 3 节对上 | 两次全量 pytest + ui_check.py |
23 项逐项对上;另有 ui_check 重试竞态 | .scratch/test-repair/phase0/ |
阶段 1:根目录收集 0 错误;timing 排除 |
python -m pytest --collect-only |
112 文件 1754 项、0 错误;排除 3 项 | phase1/ |
| 阶段 2:数据用例可运行 | 三个数据用例文件 | 12 / 11 / 30 项全绿 | phase2/ |
| 阶段 3:3–10 号 | 定向用例 | 全绿;9、10 号按用户决定 | phase3/items*.xml |
| 阶段 4:门禁 | tools/gate.ps1 |
当前状态退出码 0;改坏断言、删数据均非 0 | .scratch/gate/gate-* |
| 阶段 5:钩子与文档 | git config core.hooksPath .githooks(未安装) |
README 与两份计划已改;文档门禁 clean | scripts/doc_lint.py |
验收与下一步
- 轮次摘要:阶段 0–6 完成;偏差(
timing三项、box 叠加画布、四处计划外过时断言)见.scratch/test-repair/final-report.md - 验收者:用户(或评审代理);决定:待验收
- 下一步:box 画布重建与计时确定性各开任务;等用户授权后再提交