bec-ipython-client 3.64.5__py3-none-any.whl → 3.89.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of bec-ipython-client might be problematic. Click here for more details.

@@ -1,20 +1,23 @@
1
1
  import collections
2
+ import time
2
3
  from unittest import mock
3
4
 
4
5
  import pytest
5
6
 
6
7
  from bec_ipython_client.callbacks.move_device import (
7
8
  LiveUpdatesReadbackProgressbar,
8
- ReadbackDataMixin,
9
+ ReadbackDataHandler,
9
10
  )
10
11
  from bec_lib import messages
11
12
  from bec_lib.endpoints import MessageEndpoints
12
13
 
13
14
 
14
15
  @pytest.fixture
15
- def readback_data_mixin(bec_client_mock):
16
- with mock.patch.object(bec_client_mock.device_manager, "connector"):
17
- yield ReadbackDataMixin(bec_client_mock.device_manager, ["samx", "samy"])
16
+ def readback_data_handler(bec_client_mock, connected_connector):
17
+ with mock.patch.object(bec_client_mock.device_manager, "connector", connected_connector):
18
+ yield ReadbackDataHandler(
19
+ bec_client_mock.device_manager, ["samx", "samy"], request_id="something"
20
+ )
18
21
 
19
22
 
20
23
  def test_move_callback(bec_client_mock):
@@ -33,30 +36,25 @@ def test_move_callback(bec_client_mock):
33
36
  return readback[0]
34
37
 
35
38
  req_done = collections.deque()
36
- msg_acc = messages.DeviceReqStatusMessage(
37
- device="samx", success=True, metadata={"RID": "something"}
38
- )
39
- req_done.extend([[None], [None], [None], [msg_acc]])
39
+ req_done.extend([{"samx": (False, False)}, {"samx": (False, False)}, {"samx": (True, True)}])
40
40
 
41
41
  def mock_req_msg(*args):
42
42
  if len(req_done) > 1:
43
43
  return req_done.popleft()
44
44
  return req_done[0]
45
45
 
46
- with mock.patch("bec_ipython_client.callbacks.move_device.check_alarms") as check_alarms_mock:
47
- with mock.patch.object(ReadbackDataMixin, "wait_for_RID"):
48
- with mock.patch.object(LiveUpdatesReadbackProgressbar, "wait_for_request_acceptance"):
46
+ with mock.patch("bec_ipython_client.callbacks.move_device.check_alarms"):
47
+ with mock.patch.object(LiveUpdatesReadbackProgressbar, "wait_for_request_acceptance"):
48
+ with mock.patch.object(
49
+ LiveUpdatesReadbackProgressbar, "_print_client_msgs_asap"
50
+ ) as mock_client_msgs:
49
51
  with mock.patch.object(
50
- LiveUpdatesReadbackProgressbar, "_print_client_msgs_asap"
51
- ) as mock_client_msgs:
52
- with mock.patch.object(
53
- LiveUpdatesReadbackProgressbar, "_print_client_msgs_all"
54
- ) as mock_client_msgs_all:
55
- with mock.patch.object(
56
- ReadbackDataMixin, "get_device_values", mock_readback
57
- ):
52
+ LiveUpdatesReadbackProgressbar, "_print_client_msgs_all"
53
+ ) as mock_client_msgs_all:
54
+ with mock.patch.object(ReadbackDataHandler, "get_device_values", mock_readback):
55
+ with mock.patch.object(ReadbackDataHandler, "device_states", mock_req_msg):
58
56
  with mock.patch.object(
59
- ReadbackDataMixin, "get_request_done_msgs", mock_req_msg
57
+ ReadbackDataHandler, "done", side_effect=[False, False, True]
60
58
  ):
61
59
  LiveUpdatesReadbackProgressbar(bec=client, request=request).run()
62
60
  assert mock_client_msgs.called is True
@@ -82,28 +80,21 @@ def test_move_callback_with_report_instruction(bec_client_mock):
82
80
  return readback[0]
83
81
 
84
82
  req_done = collections.deque()
85
- msg_acc = messages.DeviceReqStatusMessage(
86
- device="samx", success=True, metadata={"RID": "something"}
87
- )
88
- req_done.extend([[None], [None], [None], [msg_acc]])
83
+ req_done.extend([{"samx": (False, False)}, {"samx": (False, False)}, {"samx": (True, True)}])
89
84
 
90
85
  def mock_req_msg(*args):
91
86
  if len(req_done) > 1:
92
87
  return req_done.popleft()
93
88
  return req_done[0]
94
89
 
95
- with mock.patch("bec_ipython_client.callbacks.move_device.check_alarms") as check_alarms_mock:
96
- with mock.patch.object(ReadbackDataMixin, "wait_for_RID"):
97
- with mock.patch.object(LiveUpdatesReadbackProgressbar, "wait_for_request_acceptance"):
98
- with mock.patch.object(LiveUpdatesReadbackProgressbar, "_print_client_msgs_asap"):
99
- with mock.patch.object(
100
- LiveUpdatesReadbackProgressbar, "_print_client_msgs_all"
101
- ):
102
- with mock.patch.object(
103
- ReadbackDataMixin, "get_device_values", mock_readback
104
- ):
90
+ with mock.patch("bec_ipython_client.callbacks.move_device.check_alarms"):
91
+ with mock.patch.object(LiveUpdatesReadbackProgressbar, "wait_for_request_acceptance"):
92
+ with mock.patch.object(LiveUpdatesReadbackProgressbar, "_print_client_msgs_asap"):
93
+ with mock.patch.object(LiveUpdatesReadbackProgressbar, "_print_client_msgs_all"):
94
+ with mock.patch.object(ReadbackDataHandler, "get_device_values", mock_readback):
95
+ with mock.patch.object(ReadbackDataHandler, "device_states", mock_req_msg):
105
96
  with mock.patch.object(
106
- ReadbackDataMixin, "get_request_done_msgs", mock_req_msg
97
+ ReadbackDataHandler, "done", side_effect=[False, False, False, True]
107
98
  ):
108
99
  LiveUpdatesReadbackProgressbar(
109
100
  bec=client,
@@ -112,70 +103,121 @@ def test_move_callback_with_report_instruction(bec_client_mock):
112
103
  ).run()
113
104
 
114
105
 
115
- def test_readback_data_mixin(readback_data_mixin):
116
- readback_data_mixin.device_manager.connector.get.side_effect = [
117
- messages.DeviceMessage(
106
+ def test_readback_data_handler(readback_data_handler):
107
+ readback_data_handler.data = {
108
+ "samx": messages.DeviceMessage(
118
109
  signals={"samx": {"value": 10}, "samx_setpoint": {"value": 20}},
119
110
  metadata={"device": "samx"},
120
111
  ),
121
- messages.DeviceMessage(
112
+ "samy": messages.DeviceMessage(
122
113
  signals={"samy": {"value": 10}, "samy_setpoint": {"value": 20}},
123
114
  metadata={"device": "samy"},
124
115
  ),
125
- ]
126
- res = readback_data_mixin.get_device_values()
116
+ }
117
+
118
+ res = readback_data_handler.get_device_values()
127
119
  assert res == [10, 10]
128
120
 
129
121
 
130
- def test_readback_data_mixin_multiple_hints(readback_data_mixin):
131
- readback_data_mixin.device_manager.devices.samx._info["hints"]["fields"] = [
122
+ def test_readback_data_handler_multiple_hints(readback_data_handler):
123
+ readback_data_handler.device_manager.devices.samx._info["hints"]["fields"] = [
132
124
  "samx_setpoint",
133
125
  "samx",
134
126
  ]
135
- readback_data_mixin.device_manager.connector.get.side_effect = [
136
- messages.DeviceMessage(
127
+ readback_data_handler.data = {
128
+ "samx": messages.DeviceMessage(
137
129
  signals={"samx": {"value": 10}, "samx_setpoint": {"value": 20}},
138
130
  metadata={"device": "samx"},
139
131
  ),
140
- messages.DeviceMessage(
132
+ "samy": messages.DeviceMessage(
141
133
  signals={"samy": {"value": 10}, "samy_setpoint": {"value": 20}},
142
134
  metadata={"device": "samy"},
143
135
  ),
144
- ]
145
- res = readback_data_mixin.get_device_values()
136
+ }
137
+ res = readback_data_handler.get_device_values()
146
138
  assert res == [20, 10]
147
139
 
148
140
 
149
- def test_readback_data_mixin_multiple_no_hints(readback_data_mixin):
150
- readback_data_mixin.device_manager.devices.samx._info["hints"]["fields"] = []
151
- readback_data_mixin.device_manager.connector.get.side_effect = [
152
- messages.DeviceMessage(
141
+ def test_readback_data_handler_multiple_no_hints(readback_data_handler):
142
+ readback_data_handler.device_manager.devices.samx._info["hints"]["fields"] = []
143
+ readback_data_handler.data = {
144
+ "samx": messages.DeviceMessage(
153
145
  signals={"samx": {"value": 10}, "samx_setpoint": {"value": 20}},
154
146
  metadata={"device": "samx"},
155
147
  ),
156
- messages.DeviceMessage(
148
+ "samy": messages.DeviceMessage(
157
149
  signals={"samy": {"value": 10}, "samy_setpoint": {"value": 20}},
158
150
  metadata={"device": "samy"},
159
151
  ),
160
- ]
161
- res = readback_data_mixin.get_device_values()
152
+ }
153
+ res = readback_data_handler.get_device_values()
162
154
  assert res == [10, 10]
163
155
 
164
156
 
165
- def test_get_request_done_msgs(readback_data_mixin):
166
- res = readback_data_mixin.get_request_done_msgs()
167
- readback_data_mixin.device_manager.connector.pipeline.assert_called_once()
168
- assert (
169
- mock.call(
170
- MessageEndpoints.device_req_status("samx"),
171
- readback_data_mixin.device_manager.connector.pipeline.return_value,
172
- )
173
- in readback_data_mixin.device_manager.connector.get.call_args_list
157
+ def test_readback_data_handler_init(readback_data_handler):
158
+ """
159
+ Test that the ReadbackDataHandler is initialized correctly.
160
+ """
161
+
162
+ # Initial state
163
+ assert readback_data_handler._devices_done_state == {
164
+ "samx": (False, False),
165
+ "samy": (False, False),
166
+ }
167
+ assert readback_data_handler._devices_received == {"samx": False, "samy": False}
168
+ assert readback_data_handler.data == {}
169
+
170
+
171
+ def test_readback_data_handler_readback_callbacks(readback_data_handler):
172
+ """
173
+ Test that the readback callback properly updates the readback data.
174
+ """
175
+
176
+ # Submit readback for samx
177
+ msg = messages.DeviceMessage(
178
+ signals={"samx": {"value": 15}}, metadata={"device": "samx", "RID": "something"}
174
179
  )
175
- assert (
176
- mock.call(
177
- MessageEndpoints.device_req_status("samy"),
178
- readback_data_mixin.device_manager.connector.pipeline.return_value,
179
- )
180
- in readback_data_mixin.device_manager.connector.get.call_args_list
180
+ readback_data_handler.connector.set_and_publish(MessageEndpoints.device_readback("samx"), msg)
181
+
182
+ msg_old = messages.DeviceMessage(
183
+ signals={"samy": {"value": 10}}, metadata={"device": "samx", "RID": "something_else"}
184
+ )
185
+ readback_data_handler.connector.set_and_publish(
186
+ MessageEndpoints.device_readback("samy"), msg_old
187
+ )
188
+ while (
189
+ not readback_data_handler._devices_received["samx"]
190
+ or "samx" not in readback_data_handler.data
191
+ ):
192
+ time.sleep(0.01)
193
+ assert readback_data_handler.data["samx"].signals["samx"]["value"] == 15
194
+ dev_data = readback_data_handler.get_device_values()
195
+ assert dev_data[0] == 15
196
+ assert dev_data[1] == 10 # samy remains unchanged
197
+
198
+
199
+ def test_readback_data_handler_request_done_callbacks(readback_data_handler):
200
+ """
201
+ Test that the request done callback properly updates the device done state.
202
+ """
203
+
204
+ # Submit request done for samx
205
+ msg = messages.DeviceReqStatusMessage(device="samx", success=True, request_id="something")
206
+ readback_data_handler.connector.xadd(
207
+ MessageEndpoints.device_req_status("something"), {"data": msg}
208
+ )
209
+ while not readback_data_handler._devices_done_state["samx"][0]:
210
+ time.sleep(0.01)
211
+ assert readback_data_handler._devices_done_state["samx"] == (True, True)
212
+
213
+ assert readback_data_handler.done() is False
214
+
215
+ # Submit request done for samy
216
+ msg = messages.DeviceReqStatusMessage(device="samy", success=False, request_id="something")
217
+ readback_data_handler.connector.xadd(
218
+ MessageEndpoints.device_req_status("something"), {"data": msg}
181
219
  )
220
+ while not readback_data_handler._devices_done_state["samy"][0]:
221
+ time.sleep(0.01)
222
+ assert readback_data_handler._devices_done_state["samy"] == (True, False)
223
+ assert readback_data_handler.done() is True
@@ -1,21 +1,21 @@
1
1
  from time import sleep
2
2
 
3
- from bec_server.scan_server.procedures.constants import PROCEDURE, ProcedureWorkerError
4
- from bec_server.scan_server.procedures.container_utils import PodmanCliUtils
3
+ from bec_server.procedures.constants import PROCEDURE, ProcedureWorkerError
4
+ from bec_server.procedures.container_utils import PodmanCliUtils
5
5
 
6
6
  image_name = (
7
7
  f"ghcr.io/bec-project/{PROCEDURE.CONTAINER.REQUIREMENTS_IMAGE_NAME}:v{PROCEDURE.BEC_VERSION}"
8
8
  )
9
9
  podman = PodmanCliUtils()
10
10
 
11
- for i in range(1, 4):
11
+ for i in range(1, 6):
12
12
  try:
13
13
  output = podman._run_and_capture_error("podman", "pull", image_name)
14
14
  print("successfully pulled requirements image for current version")
15
15
  exit(0)
16
16
  except ProcedureWorkerError as e:
17
17
  print(e)
18
- print("retrying in 2 minutes...")
19
- sleep(120)
18
+ print("retrying in 5 minutes...")
19
+ sleep(5 * 60)
20
20
  print(f"No more retries. Check if {image_name} actually exists!")
21
21
  exit(1)
@@ -1,6 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import time
4
+ from dataclasses import dataclass
4
5
  from importlib.metadata import version
5
6
  from typing import TYPE_CHECKING, Callable, Generator
6
7
  from unittest.mock import MagicMock, patch
@@ -11,10 +12,10 @@ from bec_ipython_client.main import BECIPythonClient
11
12
  from bec_lib import messages
12
13
  from bec_lib.endpoints import MessageEndpoints
13
14
  from bec_lib.logger import bec_logger
14
- from bec_server.scan_server.procedures.constants import PROCEDURE
15
- from bec_server.scan_server.procedures.container_utils import get_backend
16
- from bec_server.scan_server.procedures.container_worker import ContainerProcedureWorker
17
- from bec_server.scan_server.procedures.manager import ProcedureManager
15
+ from bec_server.procedures.constants import _CONTAINER, _WORKER
16
+ from bec_server.procedures.container_utils import get_backend
17
+ from bec_server.procedures.container_worker import ContainerProcedureWorker
18
+ from bec_server.procedures.manager import ProcedureManager
18
19
 
19
20
  if TYPE_CHECKING:
20
21
  from pytest_bec_e2e.plugin import LogTestTool
@@ -28,14 +29,23 @@ logger = bec_logger.logger
28
29
  pytestmark = pytest.mark.random_order(disabled=True)
29
30
 
30
31
 
32
+ @dataclass(frozen=True)
33
+ class PATCHED_CONSTANTS:
34
+ WORKER = _WORKER()
35
+ CONTAINER = _CONTAINER()
36
+ MANAGER_SHUTDOWN_TIMEOUT_S = 2
37
+ BEC_VERSION = version("bec_lib")
38
+ REDIS_HOST = "localhost"
39
+
40
+
31
41
  @pytest.fixture
32
42
  def client_logtool_and_manager(
33
43
  bec_ipython_client_fixture_with_logtool: tuple[BECIPythonClient, "LogTestTool"],
34
44
  ) -> Generator[tuple[BECIPythonClient, "LogTestTool", ProcedureManager], None, None]:
35
45
  client, logtool = bec_ipython_client_fixture_with_logtool
36
- server = MagicMock()
37
- server.bootstrap_server = f"{client.connector.host}:{client.connector.port}"
38
- manager = ProcedureManager(server, ContainerProcedureWorker)
46
+ manager = ProcedureManager(
47
+ f"{client.connector.host}:{client.connector.port}", ContainerProcedureWorker
48
+ )
39
49
  yield client, logtool, manager
40
50
  manager.shutdown()
41
51
 
@@ -52,20 +62,21 @@ def _wait_while(cond: Callable[[], bool], timeout_s):
52
62
  def test_building_worker_image():
53
63
  podman_utils = get_backend()
54
64
  build = podman_utils.build_worker_image()
55
- assert len(build._command_output.splitlines()[-1]) == 64
65
+ assert len(build._command_output.splitlines()[-1]) == 64 # type: ignore
56
66
  assert podman_utils.image_exists(f"bec_procedure_worker:v{version('bec_lib')}")
57
67
 
58
68
 
59
69
  @pytest.mark.timeout(100)
60
- @patch("bec_server.scan_server.procedures.manager.procedure_registry.is_registered", lambda _: True)
70
+ @patch("bec_server.procedures.manager.procedure_registry.is_registered", lambda _: True)
71
+ @patch("bec_server.procedures.container_worker.PROCEDURE", PATCHED_CONSTANTS())
61
72
  def test_procedure_runner_spawns_worker(
62
73
  client_logtool_and_manager: tuple[BECIPythonClient, "LogTestTool", ProcedureManager],
63
74
  ):
64
75
  client, _, manager = client_logtool_and_manager
65
- assert manager.active_workers == {}
76
+ assert manager._active_workers == {}
66
77
  endpoint = MessageEndpoints.procedure_request()
67
78
  msg = messages.ProcedureRequestMessage(
68
- identifier="sleep", args_kwargs=((), {"time_s": 2}), queue="test"
79
+ identifier="sleep", args_kwargs=((), {"time_s": 0.1}), queue="test"
69
80
  )
70
81
 
71
82
  logs = []
@@ -77,21 +88,22 @@ def test_procedure_runner_spawns_worker(
77
88
  manager.add_callback("test", cb)
78
89
  client.connector.xadd(topic=endpoint, msg_dict=msg.model_dump())
79
90
 
80
- _wait_while(lambda: manager.active_workers == {}, 5)
81
- _wait_while(lambda: manager.active_workers != {}, 20)
91
+ _wait_while(lambda: manager._active_workers == {}, 5)
92
+ _wait_while(lambda: manager._active_workers != {}, 90)
82
93
 
83
94
  assert logs != []
84
95
 
85
96
 
86
97
  @pytest.mark.timeout(100)
87
- @patch("bec_server.scan_server.procedures.manager.procedure_registry.is_registered", lambda _: True)
98
+ @patch("bec_server.procedures.manager.procedure_registry.is_registered", lambda _: True)
99
+ @patch("bec_server.procedures.container_worker.PROCEDURE", PATCHED_CONSTANTS())
88
100
  def test_happy_path_container_procedure_runner(
89
101
  client_logtool_and_manager: tuple[BECIPythonClient, "LogTestTool", ProcedureManager],
90
102
  ):
91
103
  test_args = (1, 2, 3)
92
104
  test_kwargs = {"a": "b", "c": "d"}
93
105
  client, logtool, manager = client_logtool_and_manager
94
- assert manager.active_workers == {}
106
+ assert manager._active_workers == {}
95
107
  conn = client.connector
96
108
  endpoint = MessageEndpoints.procedure_request()
97
109
  msg = messages.ProcedureRequestMessage(
@@ -99,12 +111,15 @@ def test_happy_path_container_procedure_runner(
99
111
  )
100
112
  conn.xadd(topic=endpoint, msg_dict=msg.model_dump())
101
113
 
102
- _wait_while(lambda: manager.active_workers == {}, 5)
103
- _wait_while(lambda: manager.active_workers != {}, 20)
114
+ _wait_while(lambda: manager._active_workers == {}, 5)
115
+ _wait_while(lambda: manager._active_workers != {}, 90)
104
116
 
105
117
  logtool.fetch()
106
118
  assert logtool.is_present_in_any_message("procedure accepted: True, message:")
107
- assert logtool.is_present_in_any_message("ContainerWorker started container for queue primary")
119
+ assert logtool.is_present_in_any_message(
120
+ "ContainerWorker started container for queue primary"
121
+ ), f"Log content relating to procedures: {manager._logs}"
122
+
108
123
  res, msg = logtool.are_present_in_order(
109
124
  [
110
125
  "Container worker 'primary' status update: IDLE",
@@ -114,12 +129,7 @@ def test_happy_path_container_procedure_runner(
114
129
  ]
115
130
  )
116
131
  assert res, f"failed on {msg}"
117
- res, msg = logtool.are_present_in_order(
118
- [
119
- "Container worker 'primary' status update: IDLE",
120
- f"Builtin procedure log_message_args_kwargs called with args: {test_args} and kwargs: {test_kwargs}",
121
- "Container worker 'primary' status update: IDLE",
122
- "Container worker 'primary' status update: FINISHED",
123
- ]
132
+
133
+ assert logtool.is_present_in_any_message(
134
+ f"Builtin procedure log_message_args_kwargs called with args: {test_args} and kwargs: {test_kwargs}"
124
135
  )
125
- assert res, f"failed on {msg}"
@@ -1,9 +1,12 @@
1
+ from __future__ import annotations
2
+
1
3
  import _thread
2
4
  import io
3
5
  import os
4
6
  import threading
5
7
  import time
6
8
  from contextlib import redirect_stdout
9
+ from typing import TYPE_CHECKING
7
10
  from unittest.mock import PropertyMock
8
11
 
9
12
  import h5py
@@ -19,6 +22,9 @@ from bec_lib.logger import bec_logger
19
22
 
20
23
  logger = bec_logger.logger
21
24
 
25
+ if TYPE_CHECKING: # pragma: no cover
26
+ from bec_ipython_client.main import BECIPythonClient
27
+
22
28
  # pylint: disable=protected-access
23
29
 
24
30
 
@@ -201,13 +207,13 @@ def test_mv_scan_mv(bec_ipython_client_fixture):
201
207
 
202
208
 
203
209
  @pytest.mark.timeout(100)
204
- def test_scan_abort(bec_ipython_client_fixture):
210
+ def test_scan_abort(bec_ipython_client_fixture: BECIPythonClient):
205
211
  def send_abort(bec):
206
212
  while True:
207
213
  current_scan_info = bec.queue.scan_storage.current_scan_info
208
214
  if not current_scan_info:
209
215
  continue
210
- status = current_scan_info.get("status").lower()
216
+ status = current_scan_info.status.lower()
211
217
  if status not in ["running", "deferred_pause"]:
212
218
  continue
213
219
  if bec.queue.scan_storage.current_scan is None:
@@ -217,7 +223,7 @@ def test_scan_abort(bec_ipython_client_fixture):
217
223
  break
218
224
  while True:
219
225
  queue = bec.queue.queue_storage.current_scan_queue
220
- if queue["primary"]["info"][0]["status"] == "DEFERRED_PAUSE":
226
+ if queue["primary"].info[0].status == "DEFERRED_PAUSE":
221
227
  break
222
228
  time.sleep(0.5)
223
229
  _thread.interrupt_main()
@@ -241,7 +247,7 @@ def test_scan_abort(bec_ipython_client_fixture):
241
247
  time.sleep(0.5)
242
248
 
243
249
  current_queue = bec.queue.queue_storage.current_scan_queue["primary"]
244
- while current_queue["info"] or current_queue["status"] != "RUNNING":
250
+ while current_queue.info or current_queue.status != "RUNNING":
245
251
  time.sleep(0.5)
246
252
  current_queue = bec.queue.queue_storage.current_scan_queue["primary"]
247
253
 
@@ -307,7 +313,7 @@ def test_queued_scan(bec_ipython_client_fixture):
307
313
  while len(scan2.scan.live_data) != 50:
308
314
  time.sleep(0.5)
309
315
  current_queue = bec.queue.queue_storage.current_scan_queue["primary"]
310
- while current_queue["info"] or current_queue["status"] != "RUNNING":
316
+ while current_queue.info or current_queue.status != "RUNNING":
311
317
  time.sleep(0.5)
312
318
  current_queue = bec.queue.queue_storage.current_scan_queue["primary"]
313
319
  scan_number_end = bec.queue.next_scan_number
@@ -356,7 +362,7 @@ def test_scan_restart(bec_ipython_client_fixture):
356
362
  scan2.wait()
357
363
 
358
364
  current_queue = bec.queue.queue_storage.current_scan_queue["primary"]
359
- while current_queue["info"] or current_queue["status"] != "RUNNING":
365
+ while current_queue.info or current_queue.status != "RUNNING":
360
366
  time.sleep(0.5)
361
367
  current_queue = bec.queue.queue_storage.current_scan_queue["primary"]
362
368
  scan_number_end = bec.queue.next_scan_number
@@ -364,7 +370,7 @@ def test_scan_restart(bec_ipython_client_fixture):
364
370
 
365
371
 
366
372
  @pytest.mark.timeout(100)
367
- def test_scan_observer_repeat_queued(bec_ipython_client_fixture):
373
+ def test_scan_observer_repeat_queued(bec_ipython_client_fixture: BECIPythonClient):
368
374
  bec = bec_ipython_client_fixture
369
375
  bec.metadata.update({"unit_test": "test_scan_observer_repeat_queued"})
370
376
  scans = bec.scans
@@ -396,7 +402,7 @@ def test_scan_observer_repeat_queued(bec_ipython_client_fixture):
396
402
  scan2.wait()
397
403
 
398
404
  current_queue = bec.queue.queue_storage.current_scan_queue["primary"]
399
- while current_queue["info"] or current_queue["status"] != "RUNNING":
405
+ while current_queue.info or current_queue.status != "RUNNING":
400
406
  time.sleep(0.5)
401
407
  current_queue = bec.queue.queue_storage.current_scan_queue["primary"]
402
408
  scan_number_end = bec.queue.next_scan_number
@@ -433,7 +439,7 @@ def test_scan_observer_repeat(bec_ipython_client_fixture):
433
439
  scan1.wait()
434
440
 
435
441
  current_queue = bec.queue.queue_storage.current_scan_queue["primary"]
436
- while current_queue["info"] or current_queue["status"] != "RUNNING":
442
+ while current_queue.info or current_queue.status != "RUNNING":
437
443
  time.sleep(0.5)
438
444
  current_queue = bec.queue.queue_storage.current_scan_queue["primary"]
439
445
  while True:
@@ -737,12 +743,12 @@ def test_update_config(bec_ipython_client_fixture):
737
743
  bec = bec_ipython_client_fixture
738
744
  bec.metadata.update({"unit_test": "test_update_config"})
739
745
  demo_config_path = os.path.join(os.path.dirname(configs.__file__), "demo_config.yaml")
740
- config = bec.config._load_config_from_file(demo_config_path)
746
+ config = bec.device_manager.config_helper._load_config_from_file(demo_config_path)
741
747
  config.pop("samx")
742
- bec.config.send_config_request(action="set", config=config)
748
+ bec.device_manager.config_helper.send_config_request(action="set", config=config)
743
749
  assert "samx" not in bec.device_manager.devices
744
- config = bec.config._load_config_from_file(demo_config_path)
745
- bec.config.send_config_request(action="set", config=config)
750
+ config = bec.device_manager.config_helper._load_config_from_file(demo_config_path)
751
+ bec.device_manager.config_helper.send_config_request(action="set", config=config)
746
752
 
747
753
 
748
754
  @pytest.mark.timeout(100)
@@ -819,3 +825,46 @@ def test_client_info_message(bec_ipython_client_fixture):
819
825
  s1 = scans.line_scan(dev.samx, 0, 1, steps=10, exp_time=0.5, relative=False)
820
826
  output = buffer.getvalue()
821
827
  assert "test_client_info_message" in output
828
+
829
+
830
+ @pytest.mark.timeout(100)
831
+ def test_device_progress_grid_scan(bec_ipython_client_fixture, capsys):
832
+ bec = bec_ipython_client_fixture
833
+ scans = bec.scans
834
+ bec.metadata.update({"unit_test": "test_device_progress_grid_scan"})
835
+ dev = bec.device_manager.devices
836
+ scans.device_progress_grid_scan(
837
+ dev.samx, -5, 5, 10, dev.samy, -5, 5, 10, relative=True, exp_time=0.01
838
+ )
839
+ captured = capsys.readouterr()
840
+ assert "bpm4i" not in captured.out
841
+ assert "samx" not in captured.out
842
+ assert "Scan" in captured.out
843
+ assert "100 %" in captured.out
844
+
845
+
846
+ @pytest.mark.timeout(100)
847
+ def test_grid_scan_secondary_queue(capsys, bec_ipython_client_fixture):
848
+ bec = bec_ipython_client_fixture
849
+ scans = bec.scans
850
+ bec.metadata.update({"unit_test": "test_grid_scan_secondary_queue"})
851
+ dev = bec.device_manager.devices
852
+ status = scans.grid_scan(
853
+ dev.samx,
854
+ -5,
855
+ 5,
856
+ 10,
857
+ dev.samy,
858
+ -5,
859
+ 5,
860
+ 10,
861
+ exp_time=0.01,
862
+ relative=False,
863
+ scan_queue="secondary",
864
+ )
865
+ assert len(status.scan.live_data) == 100
866
+ assert status.scan.num_points == 100
867
+ captured = capsys.readouterr()
868
+ assert "finished. Scan ID" in captured.out
869
+
870
+ assert "secondary" in bec.queue.queue_storage.current_scan_queue