# pylint: disable=missing-function-docstring, missing-module-docstring, unused-import import pytest from bec_lib.endpoints import MessageEndpoints from pydantic import ValidationError from bec_widgets.utils import Colors from bec_widgets.widgets.progress.ring_progress_bar import RingProgressBar from bec_widgets.widgets.progress.ring_progress_bar.ring import ProgressbarConnections, RingConfig from bec_widgets.widgets.progress.ring_progress_bar.ring_progress_bar import RingProgressBarConfig from .client_mocks import mocked_client @pytest.fixture def ring_progress_bar(qtbot, mocked_client): widget = RingProgressBar(client=mocked_client) qtbot.addWidget(widget) qtbot.waitExposed(widget) yield widget def test_bar_init(ring_progress_bar): assert ring_progress_bar is not None assert ring_progress_bar.client is not None assert isinstance(ring_progress_bar, RingProgressBar) assert ring_progress_bar.config.widget_class == "RingProgressBar" assert ring_progress_bar.config.gui_id is not None assert ring_progress_bar.gui_id == ring_progress_bar.config.gui_id def test_config_validation_num_of_bars(): config = RingProgressBarConfig(num_bars=100, min_num_bars=1, max_num_bars=10) assert config.num_bars == 10 def test_config_validation_num_of_ring_error(): ring_config_0 = RingConfig(index=0) ring_config_1 = RingConfig(index=1) with pytest.raises(ValidationError) as excinfo: RingProgressBarConfig(rings=[ring_config_0, ring_config_1], num_bars=1) errors = excinfo.value.errors() assert len(errors) == 1 assert errors[0]["type"] == "different number of configs" assert "Length of rings configuration (2) does not match the number of bars (1)." in str( excinfo.value ) def test_config_validation_ring_indices_wrong_order(): ring_config_0 = RingConfig(index=2) ring_config_1 = RingConfig(index=5) with pytest.raises(ValidationError) as excinfo: RingProgressBarConfig(rings=[ring_config_0, ring_config_1], num_bars=2) errors = excinfo.value.errors() assert len(errors) == 1 assert errors[0]["type"] == "wrong indices" assert ( "Indices of ring configurations must be unique and in order from 0 to num_bars 2." in str(excinfo.value) ) def test_config_validation_ring_same_indices(): ring_config_0 = RingConfig(index=0) ring_config_1 = RingConfig(index=0) with pytest.raises(ValidationError) as excinfo: RingProgressBarConfig(rings=[ring_config_0, ring_config_1], num_bars=2) errors = excinfo.value.errors() assert len(errors) == 1 assert errors[0]["type"] == "wrong indices" assert ( "Indices of ring configurations must be unique and in order from 0 to num_bars 2." in str(excinfo.value) ) def test_config_validation_invalid_colormap(): with pytest.raises(ValueError) as excinfo: RingProgressBarConfig(color_map="crazy_colors") errors = excinfo.value.errors() assert len(errors) == 1 assert errors[0]["type"] == "unsupported colormap" assert "Colormap 'crazy_colors' not found in the current installation of pyqtgraph" in str( excinfo.value ) def test_ring_connection_endpoint_validation(): with pytest.raises(ValueError) as excinfo: ProgressbarConnections(slot="on_scan_progress", endpoint="non_existing") errors = excinfo.value.errors() assert len(errors) == 1 assert errors[0]["type"] == "unsupported endpoint" assert ( "For slot 'on_scan_progress', endpoint must be MessageEndpoint.scan_progress or 'scans/scan_progress'." in str(excinfo.value) ) with pytest.raises(ValueError) as excinfo: ProgressbarConnections(slot="on_device_readback", endpoint="non_existing") errors = excinfo.value.errors() assert len(errors) == 1 assert errors[0]["type"] == "unsupported endpoint" assert ( "For slot 'on_device_readback', endpoint must be MessageEndpoint.device_readback(device) or 'internal/devices/readback/{device}'." in str(excinfo.value) ) def test_bar_add_number_of_bars(ring_progress_bar): assert ring_progress_bar.config.num_bars == 1 ring_progress_bar.set_number_of_bars(5) assert ring_progress_bar.config.num_bars == 5 ring_progress_bar.set_number_of_bars(2) assert ring_progress_bar.config.num_bars == 2 def test_add_remove_bars_individually(ring_progress_bar): ring_progress_bar.add_ring() ring_progress_bar.add_ring() assert ring_progress_bar.config.num_bars == 3 assert len(ring_progress_bar.config.rings) == 3 ring_progress_bar.remove_ring(1) assert ring_progress_bar.config.num_bars == 2 assert len(ring_progress_bar.config.rings) == 2 assert ring_progress_bar.rings[0].config.index == 0 assert ring_progress_bar.rings[1].config.index == 1 def test_bar_set_value(ring_progress_bar): ring_progress_bar.set_number_of_bars(5) assert ring_progress_bar.config.num_bars == 5 assert len(ring_progress_bar.config.rings) == 5 assert len(ring_progress_bar.rings) == 5 ring_progress_bar.set_value([10, 20, 30, 40, 50]) ring_values = [ring.config.value for ring in ring_progress_bar.rings] assert ring_values == [10, 20, 30, 40, 50] # update just one bar ring_progress_bar.set_value(90, 1) ring_values = [ring.config.value for ring in ring_progress_bar.rings] assert ring_values == [10, 90, 30, 40, 50] def test_bar_set_precision(ring_progress_bar): ring_progress_bar.set_number_of_bars(3) assert ring_progress_bar.config.num_bars == 3 assert len(ring_progress_bar.config.rings) == 3 assert len(ring_progress_bar.rings) == 3 ring_progress_bar.set_precision(2) ring_precision = [ring.config.precision for ring in ring_progress_bar.rings] assert ring_precision == [2, 2, 2] ring_progress_bar.set_value([10.1234, 20.1234, 30.1234]) ring_values = [ring.config.value for ring in ring_progress_bar.rings] assert ring_values == [10.12, 20.12, 30.12] ring_progress_bar.set_precision(4, 1) ring_precision = [ring.config.precision for ring in ring_progress_bar.rings] assert ring_precision == [2, 4, 2] ring_progress_bar.set_value([10.1234, 20.1234, 30.1234]) ring_values = [ring.config.value for ring in ring_progress_bar.rings] assert ring_values == [10.12, 20.1234, 30.12] def test_set_min_max_value(ring_progress_bar): ring_progress_bar.set_number_of_bars(2) ring_progress_bar.set_min_max_values(0, 10) ring_min_values = [ring.config.min_value for ring in ring_progress_bar.rings] ring_max_values = [ring.config.max_value for ring in ring_progress_bar.rings] assert ring_min_values == [0, 0] assert ring_max_values == [10, 10] ring_progress_bar.set_value([5, 15]) ring_values = [ring.config.value for ring in ring_progress_bar.rings] assert ring_values == [5, 10] def test_setup_colors_from_colormap(ring_progress_bar): ring_progress_bar.set_number_of_bars(5) ring_progress_bar.set_colors_from_map("viridis", "RGB") expected_colors = Colors.golden_angle_color("viridis", 5, "RGB") converted_colors = [ring.color.getRgb() for ring in ring_progress_bar.rings] ring_config_colors = [ring.config.color for ring in ring_progress_bar.rings] assert expected_colors == converted_colors assert ring_config_colors == expected_colors def get_colors_from_rings(rings): converted_colors = [ring.color.getRgb() for ring in rings] ring_config_colors = [ring.config.color for ring in rings] return converted_colors, ring_config_colors def test_set_colors_from_colormap_and_change_num_of_bars(ring_progress_bar): ring_progress_bar.set_number_of_bars(2) ring_progress_bar.set_colors_from_map("viridis", "RGB") expected_colors = Colors.golden_angle_color("viridis", 2, "RGB") converted_colors, ring_config_colors = get_colors_from_rings(ring_progress_bar.rings) assert expected_colors == converted_colors assert ring_config_colors == expected_colors # increase the number of bars to 6 ring_progress_bar.set_number_of_bars(6) expected_colors = Colors.golden_angle_color("viridis", 6, "RGB") converted_colors, ring_config_colors = get_colors_from_rings(ring_progress_bar.rings) assert expected_colors == converted_colors assert ring_config_colors == expected_colors # decrease the number of bars to 3 ring_progress_bar.set_number_of_bars(3) expected_colors = Colors.golden_angle_color("viridis", 3, "RGB") converted_colors, ring_config_colors = get_colors_from_rings(ring_progress_bar.rings) assert expected_colors == converted_colors assert ring_config_colors == expected_colors def test_set_colors_directly(ring_progress_bar): ring_progress_bar.set_number_of_bars(3) # setting as a list of rgb tuples colors = [(255, 0, 0, 255), (0, 255, 0, 255), (0, 0, 255, 255)] ring_progress_bar.set_colors_directly(colors) converted_colors = get_colors_from_rings(ring_progress_bar.rings)[0] assert colors == converted_colors ring_progress_bar.set_colors_directly((255, 0, 0, 255), 1) converted_colors = get_colors_from_rings(ring_progress_bar.rings)[0] assert converted_colors == [(255, 0, 0, 255), (255, 0, 0, 255), (0, 0, 255, 255)] def test_set_line_width(ring_progress_bar): ring_progress_bar.set_number_of_bars(3) ring_progress_bar.set_line_widths(5) line_widths = [ring.config.line_width for ring in ring_progress_bar.rings] assert line_widths == [5, 5, 5] ring_progress_bar.set_line_widths([10, 20, 30]) line_widths = [ring.config.line_width for ring in ring_progress_bar.rings] assert line_widths == [10, 20, 30] ring_progress_bar.set_line_widths(15, 1) line_widths = [ring.config.line_width for ring in ring_progress_bar.rings] assert line_widths == [10, 15, 30] def test_set_gap(ring_progress_bar): ring_progress_bar.set_number_of_bars(3) ring_progress_bar.set_gap(20) assert ring_progress_bar.config.gap == 20 def test_auto_update(ring_progress_bar): ring_progress_bar.enable_auto_updates(True) scan_queue_status_scan_progress = { "queue": { "primary": { "info": [{"active_request_block": {"report_instructions": [{"scan_progress": 10}]}}] } } } meta = {} ring_progress_bar.on_scan_queue_status(scan_queue_status_scan_progress, meta) assert ring_progress_bar._auto_updates is True assert len(ring_progress_bar._rings) == 1 assert ring_progress_bar._rings[0].config.connections == ProgressbarConnections( slot="on_scan_progress", endpoint=MessageEndpoints.scan_progress() ) scan_queue_status_device_readback = { "queue": { "primary": { "info": [ { "active_request_block": { "report_instructions": [ { "readback": { "devices": ["samx", "samy"], "start": [1, 2], "end": [10, 20], } } ] } } ] } } } ring_progress_bar.on_scan_queue_status(scan_queue_status_device_readback, meta) assert ring_progress_bar._auto_updates is True assert len(ring_progress_bar._rings) == 2 assert ring_progress_bar._rings[0].config.connections == ProgressbarConnections( slot="on_device_readback", endpoint=MessageEndpoints.device_readback("samx") ) assert ring_progress_bar._rings[1].config.connections == ProgressbarConnections( slot="on_device_readback", endpoint=MessageEndpoints.device_readback("samy") ) assert ring_progress_bar._rings[0].config.min_value == 1 assert ring_progress_bar._rings[0].config.max_value == 10 assert ring_progress_bar._rings[1].config.min_value == 2 assert ring_progress_bar._rings[1].config.max_value == 20