修复 Studio 失败测试并新增合并门禁

把丢失的 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。
This commit is contained in:
ykcheungab
2026-10-18 19:24:31 +08:00
parent dcabd547fa
commit 433b76e43c
38 changed files with 1036 additions and 171 deletions
+45 -19
View File
@@ -13,12 +13,13 @@ What is being defended, in the order the plan asked for it:
5. **frames are addressed by id.** An id the recording does not hold is refused, never
coerced into an index.
The fixtures use real footage — the 33-frame box recording under ``dist/`` and the
shipped finger configuration — driven through the **finger** chain, which is what gives
the result a validity mask and therefore a static candidate grid. The pass is O(5 s)
and is shared per module. Where the SDK or the footage is absent the tests skip rather
than fake a result: a synthetic result would not defend the id span (20…52), the
keyframe identity or the sample round trip.
The fixtures use real footage — the 33-frame box recording tracked beside this test
(``data/box_short_20260930``) and the shipped finger configuration — driven through the
**finger** chain, which is what gives the result a validity mask and therefore a static
candidate grid. The pass is O(5 s) and is shared per module. Where the SDK is absent the
tests skip rather than fake a result: a synthetic result would not defend the id span
(20…52), the keyframe identity or the sample round trip. Where the *footage* is absent they
fail, naming the file: a missing input is not an environment that should go green.
"""
from __future__ import annotations
@@ -73,16 +74,13 @@ from orisys_studio.replay_analysis import (
ResultPolicyError,
)
REPO = Path(__file__).resolve().parents[3]
STUDIO = REPO / "dist" / "OrisysStudio"
RECORDING = (
STUDIO
/ "outputs"
/ "recordings"
/ "20260930T015028Z_session0002_40cb8e7441434feab48bb646afdba011"
)
FINGER_CONFIG = STUDIO / "config" / "sensors" / "finger" / "sensor.json"
FINGER_EXPORT = STUDIO / "config" / "sensors" / "finger" / "assets" / "30fps-0703"
#: The tracked test input (``apps/studio/tests/data/README.md`` says where it came from).
RECORDING = Path(__file__).resolve().parent / "data" / "box_short_20260930"
#: The repository's own sensor configuration. These used to be read from the packaged copy
#: under ``dist/``, which a build can delete — the same reason the footage moved here.
CONFIG = Path(__file__).resolve().parents[1] / "config"
FINGER_CONFIG = CONFIG / "sensors" / "finger" / "sensor.json"
FINGER_EXPORT = CONFIG / "sensors" / "finger" / "assets" / "30fps-0703"
#: The evidence recording's own frame ids. Deliberately not 0…32: an implementation
#: that used an index would pass every test written against a 0-based recording.
@@ -91,13 +89,22 @@ LAST_FRAME_ID = 52
def _require_inputs() -> None:
"""The footage is committed, so its absence is a failure rather than a skip."""
missing = [
str(path)
for path in (RECORDING / "frames.csv", RECORDING / "raw_0001.mp4", FINGER_CONFIG)
for path in (
RECORDING / "frames.csv",
RECORDING / "raw_0001.mp4",
FINGER_CONFIG,
)
if not path.exists()
]
if missing:
pytest.skip("analysis inputs are not present: " + ", ".join(missing))
assert not missing, (
"analysis inputs are missing: "
+ ", ".join(missing)
+ ". The recording is committed (apps/studio/tests/data/README.md); restore it "
"rather than skipping."
)
pytest.importorskip("orisys", reason="the SDK is needed to run the analysis pass")
@@ -479,6 +486,11 @@ class _Host:
if paint_2d:
from orisys_app_shared.acquisition.image_backend import ImageFrame
# The fields the window's own sink puts on the frame it draws
# (``app_window.py:2835-2851``). ``depth_valid``/``flow_valid`` are what
# ``plan_image`` reads to decide whether the frame can be drawn as that
# view, so a stub that leaves them out cannot answer "the depth view is
# real" — which is exactly what the depth test below asks.
self.frames.append(
ImageFrame(
getattr(packet, "processed_frame", None),
@@ -487,6 +499,10 @@ class _Host:
valid_mask=getattr(packet, "valid_mask", None),
source=image_source or "replay",
frame_id=int(getattr(packet, "frame_id", 0) or 0),
flow_valid=getattr(packet, "flow_valid", None),
flow_frame_id=getattr(packet, "flow_frame_id", None),
depth_valid=getattr(packet, "depth_valid", None),
depth_frame_id=getattr(packet, "depth_frame_id", None),
arrow_policy=arrow_policy,
result_available=True,
)
@@ -1652,6 +1668,8 @@ def test_a_stitch_grid_is_not_painted_onto_the_camera_frame():
"""
from types import SimpleNamespace
from orisys_app_shared.analysis.model import Provenance
class _Reader:
complete = True
key = "stitch"
@@ -1659,6 +1677,14 @@ def test_a_stitch_grid_is_not_painted_onto_the_camera_frame():
frame_ids = np.array([1], dtype=np.int64)
times = np.array([0.0])
keyframes: dict = {}
#: A real reader always carries one (``AnalysisReader.__init__`` builds it from
#: the result's metadata) and ``AnalysisSession`` reads it: *where* the samples
#: are travels from ``provenance.policy``, and the overlay base opens from the
#: rest. ``829d2c1`` added those reads; this stub predated them and the default
#: ``Provenance()`` is the honest stand-in — an empty policy, so the session
#: falls back to the display policy exactly as it does for a result that stored
#: no grid.
provenance = Provenance()
def mask(self):
return None