basedpyright requires _admin_tip_anchor to exist after __init__, so the anchor is created eagerly instead of lazily. Cover the red-tip branch and the status-bar menu gate; the menu test swaps in a QMenu subclass with a no-op exec because PySide method lookup ignores class-attr monkeypatches. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
159 lines
6.4 KiB
Python
159 lines
6.4 KiB
Python
from aarecommon.models.models import BeamlineStateEnum
|
|
from PySide6.QtCore import Qt
|
|
|
|
from aare.gui.panels.beamline_state_panel import BeamlineStatePanel
|
|
from aare.gui.styles import STATE_AVAILABLE, STATE_MSG_ERROR, STATE_MSG_INFO, STATE_UNAVAILABLE
|
|
|
|
|
|
def _panel(qtbot, staff=True):
|
|
panel = BeamlineStatePanel(staff=staff)
|
|
qtbot.addWidget(panel)
|
|
return panel
|
|
|
|
|
|
def test_availability_from_maintenance(qtbot):
|
|
panel = _panel(qtbot)
|
|
panel.set_current_state(BeamlineStateEnum.Maintenance)
|
|
assert panel._available_targets() == frozenset({BeamlineStateEnum.SampleExchange})
|
|
|
|
|
|
def test_availability_is_union_of_routes_and_menu_shortcuts(qtbot):
|
|
panel = _panel(qtbot)
|
|
panel.set_current_state(BeamlineStateEnum.SampleAlignment)
|
|
targets = panel._available_targets()
|
|
assert BeamlineStateEnum.FluxMeasurement in targets # one-hop route
|
|
assert BeamlineStateEnum.SampleExchange in targets # status-bar shortcut
|
|
assert BeamlineStateEnum.XtalSnapshot not in targets # two hops away
|
|
|
|
|
|
def test_non_staff_never_reach_admin_only_states(qtbot):
|
|
panel = _panel(qtbot, staff=False)
|
|
panel.set_current_state(BeamlineStateEnum.SampleAlignment)
|
|
targets = panel._available_targets()
|
|
assert BeamlineStateEnum.BeamLocation not in targets
|
|
assert BeamlineStateEnum.BeamstopAlignment not in targets
|
|
assert BeamlineStateEnum.FluxMeasurement not in targets
|
|
assert BeamlineStateEnum.DataCollection in targets # non-admin route stays
|
|
# Greyed like unreachable states, and the warning tip carries the red
|
|
# QToolTip wash while a normal-hint button does not.
|
|
gated = panel._buttons[BeamlineStateEnum.BeamLocation]
|
|
assert gated.cursor().shape() == Qt.CursorShape.ForbiddenCursor
|
|
assert "QToolTip" in gated.styleSheet()
|
|
assert "QToolTip" not in panel._buttons[BeamlineStateEnum.XtalSnapshot].styleSheet()
|
|
with qtbot.assertNotEmitted(panel.beam_location):
|
|
panel._emit_for_state(BeamlineStateEnum.BeamLocation)
|
|
panel._on_left_click(BeamlineStateEnum.BeamLocation) # shows the admin tip
|
|
|
|
|
|
def test_no_targets_while_moving_or_unknown(qtbot):
|
|
panel = _panel(qtbot)
|
|
panel.set_current_state(None)
|
|
assert panel._available_targets() == frozenset()
|
|
panel.set_current_state(BeamlineStateEnum.Moving)
|
|
assert panel._available_targets() == frozenset()
|
|
|
|
|
|
def test_emit_gated_by_availability(qtbot):
|
|
panel = _panel(qtbot)
|
|
panel.set_current_state(BeamlineStateEnum.Maintenance)
|
|
with qtbot.waitSignal(panel.sample_exchange, timeout=1000):
|
|
panel._emit_for_state(BeamlineStateEnum.SampleExchange)
|
|
with qtbot.assertNotEmitted(panel.flux_measurement):
|
|
panel._emit_for_state(BeamlineStateEnum.FluxMeasurement)
|
|
|
|
|
|
def test_active_maintenance_is_red_and_bold(qtbot):
|
|
panel = _panel(qtbot)
|
|
panel.set_current_state(BeamlineStateEnum.Maintenance)
|
|
maintenance = panel._buttons[BeamlineStateEnum.Maintenance]
|
|
assert maintenance.font().bold()
|
|
assert STATE_MSG_ERROR in maintenance.styleSheet()
|
|
|
|
|
|
def test_availability_palette_and_cursors(qtbot):
|
|
panel = _panel(qtbot)
|
|
panel.set_current_state(BeamlineStateEnum.Maintenance)
|
|
available = panel._buttons[BeamlineStateEnum.SampleExchange]
|
|
grey = panel._buttons[BeamlineStateEnum.FluxMeasurement]
|
|
assert STATE_AVAILABLE in available.styleSheet()
|
|
assert available.cursor().shape() == Qt.CursorShape.PointingHandCursor
|
|
assert not available.font().bold()
|
|
assert STATE_UNAVAILABLE in grey.styleSheet()
|
|
assert grey.cursor().shape() == Qt.CursorShape.ForbiddenCursor
|
|
assert grey.toolTip() == ""
|
|
assert available.toolTip() != ""
|
|
|
|
|
|
def test_active_non_maintenance_is_blue(qtbot):
|
|
panel = _panel(qtbot)
|
|
panel.set_current_state(BeamlineStateEnum.SampleAlignment)
|
|
active = panel._buttons[BeamlineStateEnum.SampleAlignment]
|
|
assert active.font().bold()
|
|
assert STATE_MSG_INFO in active.styleSheet()
|
|
|
|
|
|
def test_labels_wrap_when_narrow_and_unwrap_when_wide(qtbot):
|
|
panel = _panel(qtbot)
|
|
sample_exchange = panel._buttons[BeamlineStateEnum.SampleExchange]
|
|
|
|
panel.resize(600, 60)
|
|
panel._update_label_mode()
|
|
assert not panel._single_line
|
|
assert sample_exchange.text() == "Manual sample\nexchange"
|
|
|
|
panel.resize(4000, 60)
|
|
panel._update_label_mode()
|
|
assert panel._single_line
|
|
assert sample_exchange.text() == "Manual sample exchange"
|
|
|
|
|
|
def test_left_click_never_transitions_but_hints(qtbot):
|
|
panel = _panel(qtbot)
|
|
panel.set_current_state(BeamlineStateEnum.Maintenance)
|
|
with qtbot.assertNotEmitted(panel.sample_exchange):
|
|
panel._on_left_click(BeamlineStateEnum.SampleExchange) # reminder tip
|
|
panel._on_left_click(BeamlineStateEnum.FluxMeasurement) # reachability hint
|
|
panel._on_left_click(BeamlineStateEnum.Maintenance) # current: no-op
|
|
|
|
|
|
def test_hover_hint_timer_lifecycle(qtbot):
|
|
panel = _panel(qtbot)
|
|
panel.set_current_state(BeamlineStateEnum.Maintenance)
|
|
panel._set_hovered_state(BeamlineStateEnum.FluxMeasurement)
|
|
assert panel._hover_hint_timer.isActive()
|
|
panel._show_hover_hint() # runs the unavailable-hint path
|
|
panel._clear_hovered_state()
|
|
assert not panel._hover_hint_timer.isActive()
|
|
assert panel._hovered_state is None
|
|
|
|
|
|
def test_update_daq_status_sets_state(qtbot, daq_status_factory):
|
|
panel = _panel(qtbot)
|
|
panel.update_daq_status(daq_status_factory(state=BeamlineStateEnum.SampleAlignment))
|
|
assert panel._current_state == BeamlineStateEnum.SampleAlignment
|
|
|
|
|
|
def test_pending_target_cleared_on_arrival(qtbot):
|
|
panel = _panel(qtbot)
|
|
panel.set_current_state(BeamlineStateEnum.Maintenance)
|
|
panel._emit_for_state(BeamlineStateEnum.SampleExchange)
|
|
assert panel._pending_target_state == BeamlineStateEnum.SampleExchange
|
|
panel.set_current_state(BeamlineStateEnum.SampleExchange)
|
|
assert panel._pending_target_state is None
|
|
|
|
|
|
def test_pending_target_breathes_only_while_moving(qtbot):
|
|
panel = _panel(qtbot)
|
|
panel.set_current_state(BeamlineStateEnum.Maintenance)
|
|
panel._emit_for_state(BeamlineStateEnum.SampleExchange)
|
|
target = panel._buttons[BeamlineStateEnum.SampleExchange]
|
|
assert target.graphicsEffect() is None # not Moving yet
|
|
|
|
panel.set_current_state(BeamlineStateEnum.Moving)
|
|
assert panel._breathing_button is target
|
|
assert target.graphicsEffect() is not None
|
|
|
|
panel.set_current_state(BeamlineStateEnum.SampleExchange)
|
|
assert panel._breathing_button is None
|
|
assert target.graphicsEffect() is None
|