Lint the commits being pushed and scope overlay failures per sensor
The pre-push gate linted only the dirty tree, so the commits about to be pushed went unchecked; and every failed overlay projection was sticky, which cost a finger result its overlay over one bad frame while box results were retried nowhere. gate.ps1 now takes -LintBase (default origin/main, exit 2 on an unknown ref), the projector declares whether its own failures are permanent, and serve_stored_view decides before it sets the view. Verified with gate.ps1 (PASS: 569 studio + 485 shared tests, 632 ui_check checks) and the two round-2 red samples kept in scratch.
This commit is contained in:
@@ -0,0 +1,147 @@
|
||||
"""How long an overlay-base failure lasts: one frame, or the whole session.
|
||||
|
||||
R2 of ``docs/test-repair-rework.zh.md``. The second round made every failed projection
|
||||
sticky, so one bad finger frame cost that result its overlay view for the session — a
|
||||
behaviour change beyond the box case it was written for. The scope is now the
|
||||
projector's own declaration (``overlay_base.OverlayBaseProjector``), never a guess made
|
||||
from a ``None`` or from an exception's text:
|
||||
|
||||
* a **finger** unroll that loses a frame is transient — the next frame is tried and the
|
||||
session is *not* marked unavailable;
|
||||
* a **box** stitch that cannot rebuild the lenslet grid on this footage is permanent —
|
||||
the session records it and stops asking, so scrubbing does not rebuild the same
|
||||
failure on every cursor move;
|
||||
* a projector that cannot be **opened** at all is permanent, because no frame can work.
|
||||
|
||||
No SDK and no recording are involved: the projector is given a stand-in ``project``, and
|
||||
the session is handed that projector directly, so these run on any machine.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
from types import SimpleNamespace
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
from orisys_app_shared.analysis.model import Provenance
|
||||
from orisys_app_shared.analysis.overlay_base import (
|
||||
OverlayBaseProjector,
|
||||
projection_failures_are_permanent,
|
||||
)
|
||||
from orisys_studio.replay_analysis import AnalysisSession
|
||||
|
||||
FRAME = np.zeros((6, 6, 3), dtype=np.uint8)
|
||||
BASE = np.full((6, 6, 3), 7, dtype=np.uint8)
|
||||
|
||||
|
||||
class _Reader:
|
||||
"""The smallest reader ``AnalysisSession`` accepts, with no result behind it."""
|
||||
|
||||
complete = True
|
||||
key = "overlay-scope"
|
||||
directory = Path(".")
|
||||
frame_ids = np.array([1, 2], dtype=np.int64)
|
||||
times = np.array([0.0, 0.1])
|
||||
keyframes: dict = {}
|
||||
provenance = Provenance()
|
||||
|
||||
def mask(self):
|
||||
return None
|
||||
|
||||
def frame(self, frame_id):
|
||||
return SimpleNamespace(depth=np.zeros((6, 6), dtype=np.float32))
|
||||
|
||||
def grid(self, grid_id):
|
||||
raise AssertionError("no grid in this stub")
|
||||
|
||||
|
||||
def _session(projector) -> AnalysisSession:
|
||||
"""A session whose projector is supplied directly (its provenance is not used)."""
|
||||
session = AnalysisSession(_Reader())
|
||||
session._overlay = projector
|
||||
return session
|
||||
|
||||
|
||||
def test_one_bad_finger_frame_does_not_cost_the_session_its_overlay():
|
||||
"""A finger unroll that fails on one frame is retried on the next."""
|
||||
answers = [None, BASE]
|
||||
projector = OverlayBaseProjector(
|
||||
sensor_type="finger",
|
||||
project=lambda image: answers.pop(0),
|
||||
projection_failures_are_permanent=False,
|
||||
)
|
||||
session = _session(projector)
|
||||
|
||||
assert session.overlay_base(FRAME, frame_id=1) is None
|
||||
assert session.overlay_unavailable is False, "one frame made the whole result unavailable"
|
||||
|
||||
base = session.overlay_base(FRAME, frame_id=2)
|
||||
assert base is not None, "the second frame was never projected"
|
||||
assert np.array_equal(base, BASE)
|
||||
assert session.overlay_unavailable is False
|
||||
|
||||
|
||||
def test_a_finger_projection_that_raises_is_also_only_that_frame():
|
||||
"""The exception text is not consulted: a raising finger projection is transient."""
|
||||
answers = [RuntimeError("unroll miss"), BASE]
|
||||
|
||||
def project(image):
|
||||
answer = answers.pop(0)
|
||||
if isinstance(answer, Exception):
|
||||
raise answer
|
||||
return answer
|
||||
|
||||
session = _session(
|
||||
OverlayBaseProjector(
|
||||
sensor_type="finger",
|
||||
project=project,
|
||||
projection_failures_are_permanent=False,
|
||||
)
|
||||
)
|
||||
assert session.overlay_base(FRAME, frame_id=1) is None
|
||||
assert session.overlay_unavailable is False
|
||||
assert session.overlay_base(FRAME, frame_id=2) is not None
|
||||
|
||||
|
||||
def test_a_box_projection_that_fails_is_permanent_and_is_not_retried():
|
||||
"""A box stitch that fails will fail again; the session stops asking."""
|
||||
calls: list[int] = []
|
||||
|
||||
def project(image):
|
||||
calls.append(1)
|
||||
return None
|
||||
|
||||
session = _session(OverlayBaseProjector(sensor_type="box", project=project))
|
||||
|
||||
assert session.overlay_base(FRAME, frame_id=1) is None
|
||||
assert session.overlay_unavailable is True
|
||||
assert session.overlay_base(FRAME, frame_id=2) is None
|
||||
assert len(calls) == 1, "a permanent failure was retried on the next frame"
|
||||
|
||||
|
||||
def test_a_projector_that_cannot_open_is_permanent(monkeypatch):
|
||||
"""No projector at all is the other permanent case: no frame could ever work."""
|
||||
calls: list[int] = []
|
||||
|
||||
@classmethod
|
||||
def _cannot_open(cls, provenance):
|
||||
calls.append(1)
|
||||
return None
|
||||
|
||||
monkeypatch.setattr(OverlayBaseProjector, "open_from_provenance", _cannot_open)
|
||||
session = AnalysisSession(_Reader())
|
||||
|
||||
assert session.overlay_base(FRAME, frame_id=1) is None
|
||||
assert session.overlay_unavailable is True
|
||||
assert session.overlay_base(FRAME, frame_id=2) is None
|
||||
assert len(calls) == 1, "opening was retried after it had already failed"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("sensor_type", "permanent"),
|
||||
(("finger", False), ("box", True)),
|
||||
)
|
||||
def test_the_sensor_decides_which_kind_of_failure_it_has(sensor_type, permanent):
|
||||
"""The policy lives with the sensor's opener, not in each caller's judgement."""
|
||||
assert projection_failures_are_permanent(sensor_type) is permanent
|
||||
Reference in New Issue
Block a user