Files
orisys-app/apps/studio/tests/test_status_presenter.py
T
ykcheungab 0998009ec3 refactor: 拆出 Studio 状态显示并记录分步迁移计划
让状态显示独立于主窗口,保留录制快照注入入口。验证结果和完整门禁限制见 docs/architecture/studio-window-refactor-plan.zh.md。
2026-10-18 20:32:32 +08:00

173 lines
5.4 KiB
Python

"""Status presentation without a main window or running acquisition."""
from dataclasses import replace
from types import SimpleNamespace
import pytest
from orisys_app_shared.i18n import t
from orisys_app_shared.record.experiment import RecordStats
from orisys_studio.status_presenter import RailState, StatusPresenter
from orisys_studio.ui.toolbar import ToolbarWidget
@pytest.fixture
def surface(qt_app):
toolbar = ToolbarWidget(theme="dark")
yield toolbar, StatusPresenter(toolbar)
toolbar.close()
toolbar.deleteLater()
def rail(**changes):
return replace(
RailState(
sensor_type="finger",
config_name="finger/r01/sensor.json",
view_preset="live",
video_path="D:/clips/box.mp4",
has_video=True,
replay_warnings="video warning",
calibration_summary="calibration",
),
**changes,
)
def test_live_summary_keeps_video_details_out_and_reports_capture_cost(surface):
toolbar, presenter = surface
state = rail(
show_fps=True,
fps=16.67,
process_ms=58.4,
capture_fps=30.0,
capture_dropped=7,
)
presenter.refresh_rail(state)
text = toolbar.source_summary.text()
assert "box.mp4" not in text
assert "video warning" not in toolbar.source_summary.toolTip()
assert "16.7 FPS" in text and "58 ms" in text
assert "30.0" in text and "(-7)" in text
assert state.view_preset == "live"
@pytest.mark.parametrize(
"loaded,key",
[
(True, "status.rail_video_name"),
(False, "status.rail_recent_video"),
],
)
def test_replay_distinguishes_open_video_from_remembered_path(surface, loaded, key):
toolbar, presenter = surface
presenter.refresh_rail(rail(view_preset="replay", has_video=loaded))
assert toolbar.source_summary.text() == t(key, name="box.mp4")
assert ("video warning" in toolbar.source_summary.toolTip()) is loaded
@pytest.mark.parametrize(
"progress,key",
[
("reused", "status.rail_result_reused"),
("complete", "status.rail_result_complete"),
("running", "status.rail_result_running"),
("", "status.rail_result_ready"),
],
)
def test_result_identity_belongs_to_the_result_not_the_picker(surface, progress, key):
toolbar, presenter = surface
presenter.refresh_rail(
rail(
view_preset="replay",
result_sensor_type="box",
result_progress=progress,
)
)
text = toolbar.source_summary.text()
assert text.startswith(t("status.rail_result_profile", type="box"))
assert text.endswith(t(key))
assert "FINGER" in toolbar.btn_sensor.text()
@pytest.mark.parametrize(
"version,fallback,axes,key,labels",
[
("", None, None, "status.uncalibrated", ""),
("confirmed", None, None, "status.calibrated", ""),
("confirmed", True, (True, True, True), "status.uncalibrated", ""),
("confirmed", False, None, "status.uncalibrated", ""),
("confirmed", False, (False, False, False), "status.uncalibrated", ""),
("confirmed", False, (True, True, True), "status.calibrated_all", ""),
("confirmed", False, (True, False, True), "status.calibrated_axes", "FN+FY"),
],
)
def test_calibration_summary_preserves_unknown_raw_and_partial_axes(
version,
fallback,
axes,
key,
labels,
):
assert StatusPresenter.calibration_summary(version, fallback, axes) == t(
key, axes=labels
)
def test_phase_repaints_after_a_theme_change_without_changing_the_phase(surface):
toolbar, presenter = surface
presenter.refresh_phase("connecting", theme="dark")
assert toolbar.status_chip.text() == f"● {t('status.phase_connecting')}"
presenter.refresh_phase("connecting", theme="light")
assert toolbar.status_chip.text() == f"● {t('status.phase_connecting')}"
def test_record_close_and_all_gap_counts_remain_visible(surface):
toolbar, presenter = surface
stats = RecordStats(
frames=12,
duration_s=2.5,
dropped_data=1,
dropped_video=2,
dropped_gaps=3,
unrecorded_gaps=4,
write_errors=1,
last_error="disk full",
)
recording = SimpleNamespace(
recorder=SimpleNamespace(directory="D:/recordings/run", stats=lambda: stats),
is_recording=False,
report_pending=True,
last_report=SimpleNamespace(stats=RecordStats()),
)
snapshot = presenter.record_stats(recording)
assert snapshot == ("closing", stats, "run")
presenter.refresh_record(*snapshot)
displayed = toolbar.record_chip.stats()
assert displayed["state"] == "closing"
assert displayed["dropped"] == 10
assert displayed["dropped_gaps"] == 7
assert displayed["errors"] == 1
@pytest.mark.parametrize(
"failed,errors,expected",
[
(False, 0, "done"),
(True, 0, "error"),
(False, 1, "error"),
],
)
def test_finished_recording_uses_the_final_report(surface, failed, errors, expected):
toolbar, presenter = surface
stats = RecordStats(failed=failed, write_errors=errors)
recording = SimpleNamespace(
recorder=SimpleNamespace(directory=None),
is_recording=False,
report_pending=False,
last_report=SimpleNamespace(stats=stats),
)
assert presenter.record_stats(recording) == (expected, stats, "")
presenter.refresh_record(expected, stats, "")
assert toolbar.record_chip.state() == expected