"""The units page (locked by default) and the axis page's alarm and event log. Interface tests: they drive the widgets an operator uses. The controller is never reached; snapshots are built by hand, and ``QMessageBox.question`` is answered by the test, so nothing here opens a dialog. """ from __future__ import annotations import pytest from orisys_app_shared.i18n import t from orisys_autocal.motion import AxisState, Snapshot from orisys_autocal.motion.models import AxisStatusBits from orisys_autocal.motion.service import DEFAULT_AXIS_ROLES from orisys_autocal.motion.unit_report import build_unit_rows from PySide6.QtWidgets import QMessageBox ROLES = dict(DEFAULT_AXIS_ROLES) def _snapshot(*, connected: bool = True, alarm_axis: int | None = None, units: float = 10.0): axes = tuple( AxisState( axis=axis, enabled=True, units=units, status=AxisStatusBits(512 if axis == alarm_axis else 0), read_ok=True, ) for axis in range(5) ) return Snapshot(connected=connected, axes=axes) def _rows(connected: bool = True, expected: float = 10.0): return build_unit_rows(ROLES, lambda axis: expected, _snapshot(connected=connected)) def _answer(monkeypatch, button) -> list[str]: """Answer the confirmation with ``button`` and record what it asked.""" asked: list[str] = [] def question(parent, title, body, *args, **kwargs): asked.append(body) return button monkeypatch.setattr(QMessageBox, "question", question) return asked def test_the_page_is_locked_until_the_operator_presses_modify(make_window): window = make_window() page = window.device_setup.units_page page.set_rows(_rows(connected=False), False) assert page.locked assert all(edit.isReadOnly() for edit in page._edits.values()) # Nothing to change on a machine that is not connected. assert not page.modify_button.isEnabled() page.set_rows(_rows(), True) assert page.modify_button.isEnabled() page.modify_button.click() assert not page.locked assert not any(edit.isReadOnly() for edit in page._edits.values()) assert page.apply_button.isEnabled() window.close() def test_a_confirmed_change_is_emitted_and_the_page_locks_again(make_window, monkeypatch): window = make_window() page = window.device_setup.units_page page.set_rows(_rows(), True) emitted: list[dict] = [] page.unitsApplied.connect(emitted.append) asked = _answer(monkeypatch, QMessageBox.StandardButton.Yes) page.modify_button.click() page._edits["x"].setText("5") page.apply_button.click() assert emitted == [{0: 5.0}] assert page.locked assert "5" in asked[0] and "10" in asked[0] window.close() def test_declining_the_confirmation_changes_nothing(make_window, monkeypatch): window = make_window() page = window.device_setup.units_page page.set_rows(_rows(), True) emitted: list[dict] = [] page.unitsApplied.connect(emitted.append) _answer(monkeypatch, QMessageBox.StandardButton.No) page.modify_button.click() page._edits["y"].setText("20") page.apply_button.click() assert emitted == [] assert not page.locked # still the operator's to finish or cancel page.cancel_button.click() assert page.locked assert page._edits["y"].text() == "10" window.close() def test_a_rotary_value_off_the_calibration_is_warned_about(make_window, monkeypatch): window = make_window() page = window.device_setup.units_page page.set_rows(_rows(), True) asked = _answer(monkeypatch, QMessageBox.StandardButton.No) page.modify_button.click() page._edits["yaw"].setText("5") page.apply_button.click() assert t("units.confirm_rotary", roles=t("setup.role_yaw"), cal="10") in asked[0] window.close() @pytest.mark.parametrize("bad", ["", "abc", "0", "-3", "nan", "inf"]) def test_an_unusable_value_is_refused_before_any_question(make_window, monkeypatch, bad): window = make_window() page = window.device_setup.units_page page.set_rows(_rows(), True) emitted: list[dict] = [] page.unitsApplied.connect(emitted.append) asked = _answer(monkeypatch, QMessageBox.StandardButton.Yes) page.modify_button.click() page._edits["z"].setText(bad) page.apply_button.click() assert emitted == [] and asked == [] assert t("units.invalid", role=t("setup.role_z")) == page.message_label.text() window.close() def test_applying_without_a_change_just_locks(make_window, monkeypatch): window = make_window() page = window.device_setup.units_page page.set_rows(_rows(), True) emitted: list[dict] = [] page.unitsApplied.connect(emitted.append) asked = _answer(monkeypatch, QMessageBox.StandardButton.Yes) page.modify_button.click() page.apply_button.click() assert emitted == [] and asked == [] assert page.locked window.close() def test_a_changed_axis_mapping_drops_an_open_edit(make_window): window = make_window() page = window.device_setup.units_page page.set_rows(_rows(), True) page.modify_button.click() page._edits["x"].setText("5") swapped = dict(ROLES, x=1, y=0) page.set_rows(build_unit_rows(swapped, lambda axis: 10.0, _snapshot()), True) assert page.locked assert page._edits["x"].text() == "10" window.close() def test_the_units_tab_and_the_axis_page_are_wired(make_window, monkeypatch): window = make_window() monkeypatch.setattr(window.poller, "submit", lambda *args, **kwargs: 0) panel = window.axis_panel # The always-live form no longer takes a pulse equivalent. assert panel.units_edit.isReadOnly() # The button on the axis page opens the units tab. panel.unitsRequested.emit() assert window.device_setup.current_tab() == "units" # A confirmed change goes out as an ordinary axis-parameter apply. applied: list[tuple] = [] panel.paramsApplied.connect(lambda axis, params: applied.append((axis, params.units))) window.device_setup.units_page.unitsApplied.emit({1: 5.0}) assert applied == [(1, 5.0)] assert panel.expected_units(1) == 5.0 window.close() def test_the_axis_page_logs_alarms_and_can_clear_the_log(make_window): window = make_window() panel = window.axis_panel panel.update_snapshot(_snapshot()) assert panel.event_log.count() == 0 assert not panel.events_empty.isHidden() panel.update_snapshot(_snapshot(alarm_axis=2)) assert panel.event_log.count() == 1 assert t("axes.ev_alarm_on", axis=2) in panel.event_log.item(0).text() assert panel.events_empty.isHidden() panel.clear_events_button.click() assert panel.event_log.count() == 0 window.close() def test_a_unit_mismatch_reaches_the_axis_page_summary(make_window): window = make_window() window.axis_panel.update_snapshot(_snapshot(units=5.0)) window._refresh_unit_report() assert t("units.summary_bad", bad=5, total=5) == window.axis_panel.units_chip.text() window.axis_panel.update_snapshot(_snapshot(units=10.0)) window._refresh_unit_report() assert t("units.summary_ok", total=5) == window.axis_panel.units_chip.text() window.close()