From de268cb56934afe9103760f9e7fb714bf32b4e8f Mon Sep 17 00:00:00 2001 From: wakonig_k Date: Sat, 24 Oct 2026 12:49:18 +0200 Subject: [PATCH] fix(undulator_mode): rely on new base class to resolve done --- tests/tests_devices/test_undulator.py | 21 +++++++++++++++++---- xtreme_bec/devices/undulator.py | 9 ++++----- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/tests/tests_devices/test_undulator.py b/tests/tests_devices/test_undulator.py index 3595e8d..93b3b4f 100644 --- a/tests/tests_devices/test_undulator.py +++ b/tests/tests_devices/test_undulator.py @@ -61,6 +61,7 @@ def test_set_accepts_every_mode(mode, selected, input_type): index = list(UndulatorModeEnum).index(selected) target = {"label": selected.value, "enum": selected, "index": index}[input_type] + mode.setpoint._read_pv.mock_data = (index + 1) % len(UndulatorModeEnum) status = mode.set(target) assert mode.setpoint.get() == index @@ -68,10 +69,8 @@ def test_set_accepts_every_mode(mode, selected, input_type): finish_move(mode, status) -@pytest.mark.parametrize("busy", [False, True]) -def test_same_mode_still_writes_and_waits_for_done(mode, busy): - if busy: - mode.done._read_pv.mock_data = 0 +def test_same_mode_while_busy_still_writes_and_waits_for_done(mode): + mode.done._read_pv.mock_data = 0 with patch.object(mode.setpoint, "put", wraps=mode.setpoint.put) as put: status = mode.set("Off") @@ -80,6 +79,20 @@ def test_same_mode_still_writes_and_waits_for_done(mode, busy): finish_move(mode, status) +@pytest.mark.parametrize("target", ["Circular+", UndulatorModeEnum.CIRCULAR_PLUS, 4]) +@pytest.mark.parametrize("method", ["set", "move"]) +def test_idle_same_mode_completes_without_write(mode, target, method): + mode.setpoint._read_pv.mock_data = 4 + with patch.object(mode.setpoint, "put") as put: + status = getattr(mode, method)(target, timeout=1) + status.wait(timeout=1) + + put.assert_not_called() + assert status.done + assert status.success + assert status.target == 4 + + def test_move_normalizes_labels_and_preserves_callback_and_timeout(mode): moved_cb = Mock() status = mode.move("Circular+", wait=False, timeout=1, moved_cb=moved_cb) diff --git a/xtreme_bec/devices/undulator.py b/xtreme_bec/devices/undulator.py index e4c2a38..bca2f99 100644 --- a/xtreme_bec/devices/undulator.py +++ b/xtreme_bec/devices/undulator.py @@ -65,9 +65,9 @@ class UndulatorMode(_CMUOnlineMixin, UndulatorBase): """X-Treme undulator polarization positioner, completed by the IOC's DONE signal. The CMU must be On-line and control must belong to the beamline. - Use ``set("Circular+")`` or ``set(4)`` to select a mode. As with harmonic - and alpha control, every request writes the target and waits for DONE, - including requests for the currently selected mode. + Use ``set("Circular+")`` or ``set(4)`` to select a mode. Requests for the + currently selected mode complete without writing when the undulator is idle. + All other requests write the target and wait for DONE. """ control_validations = UndulatorBase.control_validations + _CMU_ONLINE_VALIDATIONS @@ -103,7 +103,7 @@ class UndulatorMode(_CMUOnlineMixin, UndulatorBase): ) -> StatusBase: """Select a mode by label or index and wait for undulator completion. - ``set()`` delegates here with ``wait=False`` and returns a pending status. + ``set()`` delegates here with ``wait=False`` and returns a status. Invalid labels and indices raise ValueError before issuing a command. """ modes = list(UndulatorModeEnum) @@ -124,5 +124,4 @@ class UndulatorMode(_CMUOnlineMixin, UndulatorBase): else: raise TypeError("Mode must be either a string or an integer index.") - self.validate_control() return super().move(mode, wait=wait, timeout=timeout, moved_cb=moved_cb)