Make the rail reset zero the sensor in live and the display in replay
The replay view disabled a control whose display reset is exactly what an operator wants there, and the bare "Reset" hid that the live one zeroes the sensor tracker; the label, tooltip and status line now name the target, and Ctrl+R follows the view. Verified with the studio suite, ui_check (628 checks) and real-GL rail snapshots of both views.
This commit is contained in:
@@ -7,12 +7,15 @@ Written against the requirement rather than the implementation:
|
||||
* the primary button is a play/pause toggle in **both** views, and pressing it while a
|
||||
session runs pauses without stopping — the device stays connected;
|
||||
* stop ends the session (live) or leaves the replay (playback);
|
||||
* reset is disabled in the replay view, where there is no tracker to zero.
|
||||
* reset is one control with one meaning *per view*: the sensor tracker's in the live
|
||||
view (which owns a device session), the display's in the replay view (which owns
|
||||
none).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import ui_check
|
||||
from orisys_app_shared.i18n import t
|
||||
from tests.conftest import finish_run, running_window, show_window
|
||||
|
||||
|
||||
@@ -77,28 +80,81 @@ def test_stop_ends_the_session(make_window):
|
||||
stuck.release()
|
||||
|
||||
|
||||
def test_reset_is_for_the_device_and_is_disabled_in_the_replay_view(make_window):
|
||||
"""Reset stays a *device* control, and it is the thing the replay view cannot offer.
|
||||
def test_a_paused_session_still_offers_both_stop_and_reset(make_window):
|
||||
"""Pause holds the session; the device is still open, so both controls stay offered.
|
||||
|
||||
This is the state the two rules are easiest to get wrong in: the transport is
|
||||
unchecked, which is not "stopped", so Stop must still end the session and the reset
|
||||
must still reach the device it is holding.
|
||||
"""
|
||||
window = show_window(make_window(sensor_type="box", source_mode="camera"), (1440, 880))
|
||||
stuck = ui_check._StuckWorker()
|
||||
window._ensure_workers = lambda: None
|
||||
window._box_worker = stuck
|
||||
window._finger_worker = stuck
|
||||
window._start_run()
|
||||
window._run_timer.stop()
|
||||
try:
|
||||
# A first frame is the application's own path to ``running``, and the moment
|
||||
# the rail recomputes what the session can do (the reset keys off it), so the
|
||||
# session is reached through that path rather than stated directly.
|
||||
stuck.emit(ui_check._fake_packet(1))
|
||||
window._on_run_tick()
|
||||
assert window._session_phase == "running"
|
||||
assert window.toolbar.btn_reset.isEnabled(), (
|
||||
"a running session must offer the tracker reset before it is paused"
|
||||
)
|
||||
window.toolbar.btn_run.setChecked(False)
|
||||
assert window._display_paused is True and window._running
|
||||
|
||||
assert window.toolbar.btn_stop.isEnabled(), "no way out of the paused session"
|
||||
assert window.toolbar.btn_reset.isEnabled(), (
|
||||
"pausing took the device's reset away: the device is still open"
|
||||
)
|
||||
assert window.toolbar.reset_target() == "sensor"
|
||||
assert window.toolbar.btn_reset.text() == t("toolbar.reset_sensor")
|
||||
assert window.toolbar.btn_reset.toolTip() == t("toolbar.reset_sensor_tip")
|
||||
|
||||
window.toolbar.btn_reset.click()
|
||||
assert stuck.reset_requests == 1, (
|
||||
"the paused session's reset did not reach the device"
|
||||
)
|
||||
|
||||
window.toolbar.btn_stop.click()
|
||||
assert not window._running, "Stop did not end the paused session"
|
||||
finally:
|
||||
stuck.release()
|
||||
|
||||
|
||||
def test_reset_is_the_sensor_in_live_and_the_display_in_the_replay_view(make_window):
|
||||
"""Reset stays one control, and each view gets the meaning it can act on.
|
||||
|
||||
The enabled state of reset once frames are flowing is covered by
|
||||
``test_ui_smoke.py::test_a_reset_reaches_the_device_and_is_recorded`` (which drives
|
||||
``_on_reset``); this test pins what the unification is about: reset and stop are two
|
||||
different controls, and the replay view disables the one that needs a device.
|
||||
``test_ui_smoke.py::test_a_zeroing_is_recorded_only_after_the_worker_confirms_it``
|
||||
(which drives ``_on_reset``); this test pins what the unification is about: reset and
|
||||
stop are two different controls, and the view decides which of the two things the
|
||||
reset acts on — the sensor tracker where a session owns a device, the display where
|
||||
there is nothing of the device to act on.
|
||||
"""
|
||||
window, stuck = running_window(make_window)
|
||||
try:
|
||||
assert window.toolbar.btn_reset.text() != window.toolbar.btn_stop.text(), (
|
||||
"reset and stop must be distinguishable controls"
|
||||
)
|
||||
assert window.toolbar.reset_target() == "sensor"
|
||||
assert window.toolbar.btn_stop.isEnabled(), "stop is not reachable during a session"
|
||||
finally:
|
||||
finish_run(window, stuck)
|
||||
|
||||
replay = show_window(make_window(sensor_type="box", source_mode="camera"), (1440, 880))
|
||||
replay.brand_bar.btn_playback.click()
|
||||
assert not replay.toolbar.btn_reset.isEnabled(), (
|
||||
"reset is offered in the replay view, where there is no tracker to zero"
|
||||
assert replay.toolbar.reset_target() == "display", (
|
||||
"the replay view owns no device: its reset is the display's"
|
||||
)
|
||||
assert replay.toolbar.btn_reset.isEnabled(), (
|
||||
"the display reset is unavailable in the replay view"
|
||||
)
|
||||
assert replay.toolbar.btn_reset.text() == t("toolbar.reset_display")
|
||||
assert hasattr(replay.toolbar, "btn_stop"), "the replay view has no stop control"
|
||||
assert replay.toolbar.btn_run.text().startswith("▶"), (
|
||||
f"the replay transport idle reads {replay.toolbar.btn_run.text()!r}, not play"
|
||||
|
||||
Reference in New Issue
Block a user