test(device-manager): use mocked client for tests

This commit is contained in:
2026-01-08 11:32:02 +01:00
committed by wyzula-jan
parent 89d5c5abdb
commit 836fedd50e
8 changed files with 218 additions and 119 deletions
@@ -57,6 +57,8 @@ from bec_widgets.widgets.control.device_manager.components.ophyd_validation.vali
)
from bec_widgets.widgets.utility.toggle.toggle import ToggleSwitch
from .client_mocks import mocked_client
class TestConstants:
"""Test class for constants and configuration values."""
@@ -296,9 +298,9 @@ class TestDeviceTable:
"""Test class for DeviceTable component."""
@pytest.fixture
def device_table(self, qtbot) -> Generator[DeviceTable, None, None]:
def device_table(self, qtbot, mocked_client) -> Generator[DeviceTable, None, None]:
"""Fixture to create a DeviceTable instance."""
table = DeviceTable()
table = DeviceTable(client=mocked_client)
qtbot.addWidget(table)
qtbot.waitExposed(table)
yield table
@@ -997,7 +999,7 @@ class TestOphydValidation:
assert label.text() == "Connect Legend:"
@pytest.fixture
def ophyd_test(self, qtbot):
def ophyd_test(self, qtbot, mocked_client):
"""Fixture to create an OphydValidation instance. We patch the method that starts the polling loop to avoid side effects."""
with (
mock.patch(
@@ -1009,7 +1011,7 @@ class TestOphydValidation:
return_value=False,
),
):
widget = OphydValidation()
widget = OphydValidation(client=mocked_client)
qtbot.addWidget(widget)
qtbot.waitExposed(widget)
yield widget
@@ -1034,6 +1036,47 @@ class TestOphydValidation:
qtbot.mouseClick(ophyd_test._stop_validation_button, QtCore.Qt.LeftButton)
assert click_event.is_set()
def test_ophyd_test_keep_visible_after_validation(self, ophyd_test: OphydValidation, qtbot):
"""Test the keep visible after validation logic."""
# Initially false
assert len(ophyd_test._keep_visible_after_validation) == 0
# Add device to keep visible
ophyd_test.add_device_to_keep_visible_after_validation("device_1")
assert "device_1" in ophyd_test._keep_visible_after_validation
# Add second device
ophyd_test.add_device_to_keep_visible_after_validation("device_2")
assert "device_2" in ophyd_test._keep_visible_after_validation
assert len(ophyd_test._keep_visible_after_validation) == 2
# Remove device
ophyd_test.remove_device_to_keep_visible_after_validation("device_1")
assert "device_1" not in ophyd_test._keep_visible_after_validation
assert "device_2" in ophyd_test._keep_visible_after_validation
# Change config with skip validation and device in keep visible list
with (
mock.patch.object(
ophyd_test, "_is_device_in_redis_session", return_value=True
) as mock_is_device_in_redis_session,
mock.patch.object(ophyd_test, "_add_device_config") as mock_add_device_config,
mock.patch.object(
ophyd_test, "_on_device_test_completed"
) as mock_on_device_test_completed,
):
ophyd_test.change_device_configs(
[{"name": "device_2", "deviceClass": "TestClass"}],
added=True,
skip_validation=False,
)
mock_add_device_config.assert_called_once()
mock_on_device_test_completed.assert_called_once_with(
{"name": "device_2", "deviceClass": "TestClass"},
ConfigStatus.VALID.value,
ConnectionStatus.CONNECTED.value,
"Device already in session.",
)
def test_ophyd_test_adding_devices(self, ophyd_test: OphydValidation, qtbot):
"""Test adding devices to OphydValidation widget."""
sample_devices = [
+87 -34
View File
@@ -41,6 +41,8 @@ from bec_widgets.widgets.control.device_manager.components.ophyd_validation.ophy
OphydValidation,
)
from .client_mocks import mocked_client
@pytest.fixture
def device_config() -> dict:
@@ -164,6 +166,67 @@ class TestDeviceManagerViewDialogs:
connection_settings_layout.count() == fields_in_config * 2
) # Each field has a label and a widget
def test_device_form_dialog_help_methods(
self, device_form_dialog: DeviceFormDialog, device_config, qtbot
):
"""Test help methods in DeviceFormDialog."""
# Test handle devices already in session results
dialog = device_form_dialog
# Test _handle_devices_already_in_session_results
with mock.patch.object(dialog, "_handle_validation_result") as mock_handle_validation:
dialog._handle_devices_already_in_session_results([(device_config, 0, 0, "")])
mock_handle_validation.assert_called_once_with(device_config, 0, 0, "")
mock_handle_validation.reset_mock()
dialog._handle_devices_already_in_session_results([])
mock_handle_validation.assert_not_called()
mock_handle_validation.reset_mock()
dialog._handle_devices_already_in_session_results(
[(device_config, 1, 0, ""), (device_config, 0, 0, "")]
)
mock_handle_validation.assert_called_once_with(
device_config, 1, 0, ""
) # Should be called with first
# Test _handle_validation_result
# I. No wait dialog present
dialog._handle_validation_result(device_config, 1, 3, "All good")
assert dialog._validation_result == (device_config, 1, 3, "All good")
# II. No previous validation, but wait dialog present
with mock.patch.object(dialog, "_wait_dialog") as mock_wait_dialog:
dialog._handle_validation_result(device_config, 1, 3, "All good")
assert dialog.config_validation_result == (device_config, 1, 3, "All good")
mock_wait_dialog.accept.assert_called_once()
mock_wait_dialog.close.assert_called_once()
mock_wait_dialog.deleteLater.assert_called_once()
mock_wait_dialog.reset_mock()
assert dialog._wait_dialog is None
# III. Previous validation present and the same config, wait dialog present
with mock.patch.object(dialog, "_wait_dialog") as mock_wait_dialog:
dialog._validation_result = (device_config, 1, 1, "Previous bad")
dialog._handle_validation_result(device_config, 1, 3, "All good")
assert dialog.config_validation_result == (device_config, 1, 1, "Previous bad")
mock_wait_dialog.accept.assert_called_once()
mock_wait_dialog.close.assert_called_once()
mock_wait_dialog.deleteLater.assert_called_once()
mock_wait_dialog.reset_mock()
assert dialog._wait_dialog is None
# IV. Previous validation present but different config, wait dialog present
with mock.patch.object(dialog, "_wait_dialog") as mock_wait_dialog:
different_config = device_config.copy()
different_config["deviceClass"] = "DifferentClass"
dialog._validation_result = (different_config, 1, 1, "Previous bad")
dialog._handle_validation_result(device_config, 1, 3, "All good")
assert dialog.config_validation_result == (device_config, 1, 3, "All good")
mock_wait_dialog.accept.assert_called_once()
mock_wait_dialog.close.assert_called_once()
mock_wait_dialog.deleteLater.assert_called_once()
def test_set_device_config(self, device_form_dialog: DeviceFormDialog, qtbot):
"""Test setting device configuration in DeviceFormDialog."""
dialog = device_form_dialog
@@ -330,9 +393,7 @@ class TestDeviceManagerViewDialogs:
@pytest.fixture
def upload_redis_dialog(self, qtbot):
"""Fixture for UploadRedisDialog."""
dialog = UploadRedisDialog(
parent=None, ophyd_test_widget=mock.MagicMock(spec=OphydValidation), device_configs={}
)
dialog = UploadRedisDialog(parent=None, device_configs={})
try:
qtbot.addWidget(dialog)
qtbot.waitExposed(dialog)
@@ -460,37 +521,16 @@ class TestDeviceManagerViewDialogs:
assert dialog.config_section.summary_label.text() == expected_text
def test_upload_redis_validate_connections(self, device_configs_invalid, qtbot):
"""Test the validate connections method in UploadRedisDialog."""
configs = device_configs_invalid
ophyd_test_mock = mock.MagicMock(spec=OphydValidation)
try:
dialog = UploadRedisDialog(
parent=None, ophyd_test_widget=ophyd_test_mock, device_configs=configs
)
qtbot.addWidget(dialog)
qtbot.waitExposed(dialog)
with mock.patch.object(
dialog.ophyd_test_widget, "change_device_configs"
) as mock_change:
dialog._validate_connections()
mock_change.assert_called_once_with(
[cfg for k, (cfg, _, _) in configs.items() if k in ["Device_0", "Device_3"]],
added=True,
connect=True,
)
finally:
dialog.close()
class TestDeviceManagerView:
"""Test class for DeviceManagerView functionality."""
@pytest.fixture
def dm_view(self, qtbot):
def dm_view(self, qtbot, mocked_client):
"""Fixture for DeviceManagerView."""
widget = DeviceManagerView()
# Assign the mocked client
widget.device_manager_widget.client = mocked_client
qtbot.addWidget(widget)
qtbot.waitExposed(widget)
yield widget
@@ -513,7 +553,6 @@ class TestDeviceManagerView:
# Reset for test loading current config
dm_widget._initialized = False
dm_widget.stacked_layout.setCurrentWidget(dm_widget._overlay_widget)
dm_widget.client.device_manager = mock.MagicMock()
with mock.patch.object(
dm_widget.client.device_manager, "_get_redis_device_config"
@@ -532,12 +571,26 @@ class TestDeviceManagerView:
mock_set.assert_called_once_with([])
@pytest.fixture
def device_manager_display_widget(self, qtbot):
"""Fixture for DeviceManagerDisplayWidget within DeviceManagerView."""
widget = DeviceManagerDisplayWidget()
qtbot.addWidget(widget)
qtbot.waitExposed(widget)
yield widget
def device_manager_display_widget(self, qtbot, mocked_client):
"""Fixture for DeviceManagerDisplayWidget within DeviceManagerView.
We will patch the OphydValidation _thread_pool_poll_loop to avoid starting threads during tests,
and the _is_device_in_redis_session method to avoid Redis dependencies
"""
with (
mock.patch(
"bec_widgets.widgets.control.device_manager.components.ophyd_validation.ophyd_validation.OphydValidation._thread_pool_poll_loop",
return_value=None,
),
mock.patch(
"bec_widgets.widgets.control.device_manager.components.ophyd_validation.ophyd_validation.OphydValidation._is_device_in_redis_session",
return_value=False,
),
):
widget = DeviceManagerDisplayWidget(client=mocked_client)
qtbot.addWidget(widget)
qtbot.waitExposed(widget)
yield widget
@pytest.fixture
def device_configs(self, device_config: dict):