Skip to content

Commit 816ab20

Browse files
committed
test: add more tests for bl state actor
1 parent d5b7828 commit 816ab20

3 files changed

Lines changed: 87 additions & 2 deletions

File tree

bec_server/bec_server/actors/actor.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ def run(self):
9999
self.push_status(ProcedureWorkerStatus.IDLE)
100100

101101
def stop(self, *_):
102+
"""Stop the actor and cleanup subscriptions."""
102103
self._stopped = True
103104
for endpoint in self._endpoints:
104105
for cb in self.default_monitor_callbacks():
@@ -108,6 +109,7 @@ def stop(self, *_):
108109
logger.error(
109110
f"{self.__class__} {self.__qualname__} failed to unregister {cb} from {endpoint}: {e}"
110111
)
112+
self.stop_event.set()
111113

112114

113115
class BlStateActor(SubscriptionActor):
@@ -163,7 +165,7 @@ def some_mismatch_action(self, client: BECClient):
163165
pass
164166

165167
def run(self):
166-
while not self.client.beamline_states.ready:
168+
while not self.client.beamline_states.ready and not self.stop_event.set():
167169
logger.warning(f"{self.__class__.__name__} waiting for beamline states to become ready")
168170
time.sleep(0.1)
169171
self._update_cache()

bec_server/tests/tests_actors/test_actors.py

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from bec_lib.endpoints import MessageEndpoints
1010
from bec_lib.messages import ActorStartRequestMessage, ProcedureWorkerStatus, RawMessage
1111
from bec_lib.redis_connector import MessageObject, RedisConnector
12-
from bec_server.actors.actor import ActorBase
12+
from bec_server.actors.actor import ActorBase, BlStateActor
1313
from bec_server.actors.manager import ActorManager
1414
from bec_server.actors.worker import actor_procedure
1515
from bec_server.procedures.constants import BecClientType
@@ -173,3 +173,42 @@ def test_actor_procedure_logs_error_not_actor():
173173
with patch("bec_server.actors.worker.logger") as logger:
174174
actor_procedure("bec_server.test.actor_test_utils", "EndpointInfo", "test", MagicMock())
175175
assert "is not a valid Actor!" in logger.error.call_args.args[0]
176+
177+
178+
class BlStateTestActor(BlStateActor):
179+
state_table = {"test_state": "valid", "test_state_2": "valid"}
180+
181+
182+
def test_blstateactor_init_table_and_cache():
183+
mock_client = MagicMock()
184+
185+
def get_status_by_name(name: str):
186+
if name == "test_state":
187+
return "valid"
188+
189+
mock_client.beamline_states.get_status_by_name.side_effect = get_status_by_name
190+
actor = BlStateTestActor(mock_client, "Test", "Test")
191+
actor.stop_event.set()
192+
actor.run()
193+
194+
assert actor.state_table == {"test_state": "valid"}
195+
assert actor.state_cache == {"test_state": "valid"}
196+
197+
198+
def test_bl_state_actor_waits_for_states():
199+
mock_client = MagicMock()
200+
201+
mock_client.beamline_states.ready = False
202+
actor = BlStateTestActor(mock_client, "Test", "Test")
203+
actor.evaluate = MagicMock()
204+
with patch("bec_server.actors.actor.logger") as mock_logger:
205+
t = Thread(target=actor.run)
206+
t.start()
207+
sleep(0.1)
208+
mock_logger.warning.assert_called()
209+
actor.evaluate.assert_not_called()
210+
mock_client.beamline_states.ready = True
211+
sleep(0.2)
212+
actor.stop_event.set()
213+
t.join()
214+
actor.evaluate.assert_called()

bec_server/tests/tests_scan_server/test_builtin_actor_manager.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@
33

44
import pytest
55

6+
from bec_lib.messages import (
7+
AvailableBeamlineStatesMessage,
8+
ScanInterlockModifyStateTableMessage,
9+
ScanInterlockStateTableContent,
10+
)
611
from bec_server.actors.builtin_actor_manager import BuiltinActorManager
712

813

@@ -148,3 +153,42 @@ def test_shutdown_stops_all_and_shuts_down_client(mocked_manager):
148153
assert mock_stop.call_count == 2
149154

150155
mock_client.shutdown.assert_called_once()
156+
157+
158+
def _req_msg(action, state_name, status):
159+
return {
160+
"data": ScanInterlockModifyStateTableMessage(
161+
action=action, state_name=state_name, status=status
162+
)
163+
}
164+
165+
166+
INITIAL_STATES = {"initial_state_1": "valid", "initial_state_2": "valid"}
167+
168+
169+
@pytest.mark.parametrize(
170+
["modification_request", "new_watched_states"],
171+
[
172+
(_req_msg("add", "test_state", "valid"), {**INITIAL_STATES, "test_state": "valid"}),
173+
(_req_msg("remove", "initial_state_1", None), {"initial_state_2": "valid"}),
174+
(_req_msg("remove_all", None, None), {}),
175+
(_req_msg("remove", "missing_state", None), INITIAL_STATES),
176+
],
177+
)
178+
def test_modify_interlock_table(mocked_manager, modification_request, new_watched_states):
179+
manager, mock_client = mocked_manager
180+
mock_client.connector.get.return_value = ScanInterlockStateTableContent(
181+
states_watched=INITIAL_STATES
182+
)
183+
manager._modify_interlock_table(modification_request)
184+
assert mock_client.connector.set.call_args.args[1].states_watched == new_watched_states
185+
186+
187+
def test_handle_state_update(mocked_manager):
188+
manager, _ = mocked_manager
189+
manager._current_watched_states = lambda: {"test_state": "valid"}
190+
manager._modify_interlock_table = MagicMock()
191+
manager._handle_state_update({"data": AvailableBeamlineStatesMessage(states=[])})
192+
manager._modify_interlock_table.assert_called_with(
193+
{"data": ScanInterlockModifyStateTableMessage(action="remove", state_name="test_state")}
194+
)

0 commit comments

Comments
 (0)