mirror of
https://github.com/bec-project/bec_widgets.git
synced 2025-12-27 17:41:17 +01:00
225 lines
9.1 KiB
Python
225 lines
9.1 KiB
Python
"""Unit tests for the device manager view"""
|
|
|
|
# pylint: disable=protected-access,redefined-outer-name
|
|
|
|
from unittest import mock
|
|
|
|
import pytest
|
|
from qtpy import QtCore
|
|
from qtpy.QtWidgets import QFileDialog, QMessageBox
|
|
|
|
from bec_widgets.applications.views.device_manager_view.device_manager_view import (
|
|
ConfigChoiceDialog,
|
|
DeviceManagerView,
|
|
)
|
|
from bec_widgets.utils.help_inspector.help_inspector import HelpInspector
|
|
from bec_widgets.widgets.control.device_manager.components import (
|
|
DeviceTableView,
|
|
DMConfigView,
|
|
DMOphydTest,
|
|
DocstringView,
|
|
)
|
|
|
|
|
|
@pytest.fixture
|
|
def dm_view(qtbot):
|
|
"""Fixture for DeviceManagerView."""
|
|
widget = DeviceManagerView()
|
|
qtbot.addWidget(widget)
|
|
qtbot.waitExposed(widget)
|
|
yield widget
|
|
|
|
|
|
@pytest.fixture
|
|
def config_choice_dialog(qtbot, dm_view):
|
|
"""Fixture for ConfigChoiceDialog."""
|
|
dialog = ConfigChoiceDialog(dm_view)
|
|
qtbot.addWidget(dialog)
|
|
qtbot.waitExposed(dialog)
|
|
yield dialog
|
|
|
|
|
|
def test_device_manager_view_config_choice_dialog(qtbot, dm_view, config_choice_dialog):
|
|
"""Test the configuration choice dialog."""
|
|
assert config_choice_dialog is not None
|
|
assert config_choice_dialog.parent() == dm_view
|
|
|
|
# Test dialog components
|
|
with (
|
|
mock.patch.object(config_choice_dialog, "accept") as mock_accept,
|
|
mock.patch.object(config_choice_dialog, "reject") as mock_reject,
|
|
):
|
|
|
|
# Replace
|
|
qtbot.mouseClick(config_choice_dialog.replace_btn, QtCore.Qt.LeftButton)
|
|
mock_accept.assert_called_once()
|
|
mock_reject.assert_not_called()
|
|
mock_accept.reset_mock()
|
|
assert config_choice_dialog.result() == config_choice_dialog.REPLACE
|
|
# Add
|
|
qtbot.mouseClick(config_choice_dialog.add_btn, QtCore.Qt.LeftButton)
|
|
mock_accept.assert_called_once()
|
|
mock_reject.assert_not_called()
|
|
mock_accept.reset_mock()
|
|
assert config_choice_dialog.result() == config_choice_dialog.ADD
|
|
# Cancel
|
|
qtbot.mouseClick(config_choice_dialog.cancel_btn, QtCore.Qt.LeftButton)
|
|
mock_accept.assert_not_called()
|
|
mock_reject.assert_called_once()
|
|
assert config_choice_dialog.result() == config_choice_dialog.CANCEL
|
|
|
|
|
|
class TestDeviceManagerViewInitialization:
|
|
"""Test class for DeviceManagerView initialization and basic components."""
|
|
|
|
def test_dock_manager_initialization(self, dm_view):
|
|
"""Test that the QtAds DockManager is properly initialized."""
|
|
assert dm_view.dock_manager is not None
|
|
assert dm_view.dock_manager.centralWidget() is not None
|
|
|
|
def test_central_widget_is_device_table_view(self, dm_view):
|
|
"""Test that the central widget is DeviceTableView."""
|
|
central_widget = dm_view.dock_manager.centralWidget().widget()
|
|
assert isinstance(central_widget, DeviceTableView)
|
|
assert central_widget is dm_view.device_table_view
|
|
|
|
def test_dock_widgets_exist(self, dm_view):
|
|
"""Test that all required dock widgets are created."""
|
|
dock_widgets = dm_view.dock_manager.dockWidgets()
|
|
|
|
# Check that we have the expected number of dock widgets
|
|
assert len(dock_widgets) >= 4
|
|
|
|
# Check for specific widget types
|
|
widget_types = [dock.widget().__class__ for dock in dock_widgets]
|
|
|
|
assert DMConfigView in widget_types
|
|
assert DMOphydTest in widget_types
|
|
assert DocstringView in widget_types
|
|
|
|
def test_toolbar_initialization(self, dm_view):
|
|
"""Test that the toolbar is properly initialized with expected bundles."""
|
|
assert dm_view.toolbar is not None
|
|
assert "IO" in dm_view.toolbar.bundles
|
|
assert "Table" in dm_view.toolbar.bundles
|
|
|
|
def test_toolbar_components_exist(self, dm_view):
|
|
"""Test that all expected toolbar components exist."""
|
|
expected_components = [
|
|
"load",
|
|
"save_to_disk",
|
|
"load_redis",
|
|
"update_config_redis",
|
|
"reset_composed",
|
|
"add_device",
|
|
"remove_device",
|
|
"rerun_validation",
|
|
]
|
|
|
|
for component in expected_components:
|
|
assert dm_view.toolbar.components.exists(component)
|
|
|
|
def test_signal_connections(self, dm_view):
|
|
"""Test that signals are properly connected between components."""
|
|
# Test that device_table_view signals are connected
|
|
assert dm_view.device_table_view.selected_devices is not None
|
|
assert dm_view.device_table_view.device_configs_changed is not None
|
|
|
|
# Test that ophyd_test_view signals are connected
|
|
assert dm_view.ophyd_test_view.device_validated is not None
|
|
|
|
|
|
class TestDeviceManagerViewIOBundle:
|
|
"""Test class for DeviceManagerView IO bundle actions."""
|
|
|
|
def test_io_bundle_exists(self, dm_view):
|
|
"""Test that IO bundle exists and contains expected actions."""
|
|
assert "IO" in dm_view.toolbar.bundles
|
|
io_actions = ["load", "save_to_disk", "load_redis", "update_config_redis"]
|
|
for action in io_actions:
|
|
assert dm_view.toolbar.components.exists(action)
|
|
|
|
def test_load_file_action_triggered(self, tmp_path, dm_view):
|
|
"""Test load file action trigger mechanism."""
|
|
|
|
with (
|
|
mock.patch.object(dm_view, "_get_file_path", return_value=tmp_path),
|
|
mock.patch(
|
|
"bec_widgets.applications.views.device_manager_view.device_manager_view.yaml_load"
|
|
) as mock_yaml_load,
|
|
mock.patch.object(dm_view, "_open_config_choice_dialog") as mock_open_dialog,
|
|
):
|
|
mock_yaml_data = {"device1": {"param1": "value1"}}
|
|
mock_yaml_load.return_value = mock_yaml_data
|
|
|
|
# Setup dialog mock
|
|
dm_view.toolbar.components._components["load"].action.action.triggered.emit()
|
|
mock_yaml_load.assert_called_once_with(tmp_path)
|
|
mock_open_dialog.assert_called_once_with([{"name": "device1", "param1": "value1"}])
|
|
|
|
def test_save_config_to_file(self, tmp_path, dm_view):
|
|
"""Test saving config to file."""
|
|
yaml_path = tmp_path / "test_save.yaml"
|
|
mock_config = [{"name": "device1", "param1": "value1"}]
|
|
with (
|
|
mock.patch.object(dm_view, "_get_file_path", return_value=tmp_path),
|
|
mock.patch.object(dm_view, "_get_recovery_config_path", return_value=tmp_path),
|
|
mock.patch.object(dm_view, "_get_file_path", return_value=yaml_path),
|
|
mock.patch.object(
|
|
dm_view.device_table_view, "get_device_config", return_value=mock_config
|
|
),
|
|
):
|
|
dm_view.toolbar.components._components["save_to_disk"].action.action.triggered.emit()
|
|
assert yaml_path.exists()
|
|
|
|
|
|
class TestDeviceManagerViewTableBundle:
|
|
"""Test class for DeviceManagerView Table bundle actions."""
|
|
|
|
def test_table_bundle_exists(self, dm_view):
|
|
"""Test that Table bundle exists and contains expected actions."""
|
|
assert "Table" in dm_view.toolbar.bundles
|
|
table_actions = ["reset_composed", "add_device", "remove_device", "rerun_validation"]
|
|
for action in table_actions:
|
|
assert dm_view.toolbar.components.exists(action)
|
|
|
|
@mock.patch(
|
|
"bec_widgets.applications.views.device_manager_view.device_manager_view._yes_no_question"
|
|
)
|
|
def test_reset_composed_view(self, mock_question, dm_view):
|
|
"""Test reset composed view when user confirms."""
|
|
with mock.patch.object(dm_view.device_table_view, "clear_device_configs") as mock_clear:
|
|
mock_question.return_value = QMessageBox.StandardButton.Yes
|
|
dm_view.toolbar.components._components["reset_composed"].action.action.triggered.emit()
|
|
mock_clear.assert_called_once()
|
|
mock_clear.reset_mock()
|
|
mock_question.return_value = QMessageBox.StandardButton.No
|
|
dm_view.toolbar.components._components["reset_composed"].action.action.triggered.emit()
|
|
mock_clear.assert_not_called()
|
|
|
|
def test_add_device_action_connected(self, dm_view):
|
|
"""Test add device action opens dialog correctly."""
|
|
with mock.patch.object(dm_view, "_add_device_action") as mock_add:
|
|
dm_view.toolbar.components._components["add_device"].action.action.triggered.emit()
|
|
mock_add.assert_called_once()
|
|
|
|
def test_remove_device_action(self, dm_view):
|
|
"""Test remove device action."""
|
|
with mock.patch.object(dm_view.device_table_view, "remove_selected_rows") as mock_remove:
|
|
dm_view.toolbar.components._components["remove_device"].action.action.triggered.emit()
|
|
mock_remove.assert_called_once()
|
|
|
|
def test_rerun_device_validation(self, dm_view):
|
|
"""Test rerun device validation action."""
|
|
cfgs = [{"name": "device1", "param1": "value1"}]
|
|
with (
|
|
mock.patch.object(dm_view.ophyd_test_view, "change_device_configs") as mock_change,
|
|
mock.patch.object(
|
|
dm_view.device_table_view.table, "selected_configs", return_value=cfgs
|
|
),
|
|
):
|
|
dm_view.toolbar.components._components[
|
|
"rerun_validation"
|
|
].action.action.triggered.emit()
|
|
mock_change.assert_called_once_with(cfgs, True, True)
|