from unittest import mock import pytest from tomcat_bec.devices.gigafrost.gigafrost_base import GigaFrostBase from tomcat_bec.devices.gigafrost.gigafrostcamera import GigaFrostCamera, default_config from tomcat_bec.devices.std_daq.std_daq_client import StdDaqClient from tomcat_bec.devices.std_daq.std_daq_preview import StdDaqPreview @pytest.fixture() def gfcam_base(): gfcam = GigaFrostCamera( "X02DA-CAM-GF2:", name="gfcam", std_daq_rest="http://example.com/rest", std_daq_ws="ws://example.com/ws", ) for component in gfcam.component_names: type.__setattr__(GigaFrostCamera, component, mock.MagicMock()) yield gfcam def test_gfcam_init_raises_without_rest_ws(): with pytest.raises(ValueError) as excinfo: GigaFrostCamera("X02DA-CAM-GF2:", name="gfcam") excinfo.match("std_daq_rest and std_daq_ws must be provided") def test_gfcam_init(): gfcam = GigaFrostCamera( "X02DA-CAM-GF2:", name="gfcam", std_daq_rest="http://example.com/rest", std_daq_ws="ws://example.com/ws", ) assert gfcam.name == "gfcam" assert isinstance(gfcam.backend, StdDaqClient) assert gfcam.live_preview is None def test_gfcam_init_with_live_preview(): gfcam = GigaFrostCamera( "X02DA-CAM-GF2:", name="gfcam", std_daq_rest="http://example.com/rest", std_daq_ws="ws://example.com/ws", std_daq_live="http://example.com/live_preview", ) assert gfcam.live_preview is not None assert isinstance(gfcam.live_preview, StdDaqPreview) def test_gfcam_configure(gfcam_base): with mock.patch.object(gfcam_base, "stop_camera") as stop_camera: with mock.patch.object(gfcam_base.backend, "set_config") as set_config: with mock.patch.object(GigaFrostBase, "configure") as base_configure: gfcam_base.configure({}) stop_camera.assert_called_once() stop_camera().wait.assert_called_once() set_config.assert_not_called() config = default_config() base_configure.assert_called_once_with(config) def test_gfcam_default_config_copies(): assert isinstance(default_config(), dict) assert id(default_config()) != id(default_config()) def test_gfcam_configure_sets_exp_time_in_ms(gfcam_base): with mock.patch.object(gfcam_base, "stop_camera") as stop_camera: with mock.patch.object(gfcam_base.backend, "set_config") as set_config: with mock.patch.object(GigaFrostBase, "configure") as base_configure: gfcam_base.configure({"exp_time": 0.1}) stop_camera.assert_called_once() stop_camera().wait.assert_called_once() set_config.assert_not_called() config = default_config() config.update({"exposure": 100}) # in ms base_configure.assert_called_once_with(config) def test_gfcam_set_acquisition_mode_invalid(gfcam_base): """Test setting invalid acquisition mode""" with pytest.raises(RuntimeError) as excinfo: gfcam_base.set_acquisition_mode("invalid_mode") excinfo.match("Unsupported acquisition mode: invalid_mode") @pytest.mark.parametrize( "mode_soft, mode_external, mode_always, expected_result", [ (0, 0, 0, None), # No enable mode set (1, 0, 0, "soft"), # Only soft mode enabled (0, 1, 0, "external"), # Only external mode enabled (1, 1, 0, "soft+ext"), # Both soft and external enabled (0, 0, 1, "always"), # Always mode enabled (1, 0, 1, "always"), # Always overrides soft (0, 1, 1, "always"), # Always overrides external (1, 1, 1, "always"), # Always overrides both soft and external ], ) def test_gfcam_enable_mode_property( gfcam_base, mode_soft, mode_external, mode_always, expected_result ): """Test that the enable_mode property returns the correct mode based on signal values""" # Configure the mock return values for the mode signals gfcam_base.mode_endbl_soft.get.return_value = mode_soft gfcam_base.mode_enbl_ext.get.return_value = mode_external gfcam_base.mode_enbl_auto.get.return_value = mode_always # Check that the property returns the expected result assert gfcam_base.enable_mode == expected_result @pytest.mark.parametrize( "mode,expected_settings", [ ("soft", {"mode_enbl_ext": 0, "mode_endbl_soft": 1, "mode_enbl_auto": 0}), ("external", {"mode_enbl_ext": 1, "mode_endbl_soft": 0, "mode_enbl_auto": 0}), ("soft+ext", {"mode_enbl_ext": 1, "mode_endbl_soft": 1, "mode_enbl_auto": 0}), ("always", {"mode_enbl_ext": 0, "mode_endbl_soft": 0, "mode_enbl_auto": 1}), ], ) def test_gfcam_enable_mode_setter(gfcam_base, mode, expected_settings): """Test setting the enable mode of the GigaFRoST camera""" # Mock the const.gf_valid_enable_modes to avoid importing the constants with mock.patch( "tomcat_bec.devices.gigafrost.gigafrostcamera.const.gf_valid_enable_modes", ["soft", "external", "soft+ext", "always"], ): # Set the enable mode gfcam_base.enable_mode = mode # Verify the correct signals were set gfcam_base.mode_enbl_ext.set.assert_called_once_with(expected_settings["mode_enbl_ext"]) gfcam_base.mode_endbl_soft.set.assert_called_once_with(expected_settings["mode_endbl_soft"]) gfcam_base.mode_enbl_auto.set.assert_called_once_with(expected_settings["mode_enbl_auto"]) # Verify wait was called on each set operation gfcam_base.mode_enbl_ext.set().wait.assert_called_once() gfcam_base.mode_endbl_soft.set().wait.assert_called_once() gfcam_base.mode_enbl_auto.set().wait.assert_called_once() # Verify parameters were committed gfcam_base.set_param.set.assert_called_once_with(1) gfcam_base.set_param.set().wait.assert_called_once() def test_gfcam_enable_mode_setter_invalid(gfcam_base): """Test setting an invalid enable mode raises an error""" # Mock the const.gf_valid_enable_modes to avoid importing the constants with mock.patch( "tomcat_bec.devices.gigafrost.gigafrostcamera.const.gf_valid_enable_modes", ["soft", "external", "soft+ext", "always"], ): with pytest.raises(ValueError) as excinfo: gfcam_base.enable_mode = "invalid_mode" assert "Invalid enable mode invalid_mode!" in str(excinfo.value) assert "Valid modes are:" in str(excinfo.value) # Verify no signals were set gfcam_base.mode_enbl_ext.set.assert_not_called() gfcam_base.mode_endbl_soft.set.assert_not_called() gfcam_base.mode_enbl_auto.set.assert_not_called() gfcam_base.set_param.set.assert_not_called() @pytest.mark.parametrize( "mode_auto, mode_soft, mode_timer, mode_external, expected_result", [ (0, 0, 0, 0, None), # No trigger mode set (1, 0, 0, 0, "auto"), # Only auto mode enabled (0, 1, 0, 0, "soft"), # Only soft mode enabled (0, 0, 1, 0, "timer"), # Only timer mode enabled (0, 0, 0, 1, "external"), # Only external mode enabled (1, 1, 0, 0, "auto"), # Auto takes precedence over soft (1, 0, 1, 0, "auto"), # Auto takes precedence over timer (1, 0, 0, 1, "auto"), # Auto takes precedence over external (0, 1, 1, 0, "soft"), # Soft takes precedence over timer (0, 1, 0, 1, "soft"), # Soft takes precedence over external (0, 0, 1, 1, "timer"), # Timer takes precedence over external (1, 1, 1, 1, "auto"), # Auto takes precedence over all ], ) def test_gfcam_trigger_mode_property( gfcam_base, mode_auto, mode_soft, mode_timer, mode_external, expected_result ): """Test that the trigger_mode property returns the correct mode based on signal values""" # Configure the mock return values for the mode signals gfcam_base.mode_trig_auto.get.return_value = mode_auto gfcam_base.mode_trig_soft.get.return_value = mode_soft gfcam_base.mode_trig_timer.get.return_value = mode_timer gfcam_base.mode_trig_ext.get.return_value = mode_external # Check that the property returns the expected result assert gfcam_base.trigger_mode == expected_result @pytest.mark.parametrize( "mode,expected_settings", [ ( "auto", {"mode_trig_auto": 1, "mode_trig_soft": 0, "mode_trig_timer": 0, "mode_trig_ext": 0}, ), ( "soft", {"mode_trig_auto": 0, "mode_trig_soft": 1, "mode_trig_timer": 0, "mode_trig_ext": 0}, ), ( "timer", {"mode_trig_auto": 0, "mode_trig_soft": 0, "mode_trig_timer": 1, "mode_trig_ext": 0}, ), ( "external", {"mode_trig_auto": 0, "mode_trig_soft": 0, "mode_trig_timer": 0, "mode_trig_ext": 1}, ), ], ) def test_gfcam_trigger_mode_setter(gfcam_base, mode, expected_settings): """Test setting the trigger mode of the GigaFRoST camera""" # Set the trigger mode gfcam_base.trigger_mode = mode # Verify the correct signals were set gfcam_base.mode_trig_auto.set.assert_called_with(expected_settings["mode_trig_auto"]) gfcam_base.mode_trig_soft.set.assert_called_with(expected_settings["mode_trig_soft"]) gfcam_base.mode_trig_timer.set.assert_called_with(expected_settings["mode_trig_timer"]) gfcam_base.mode_trig_ext.set.assert_called_with(expected_settings["mode_trig_ext"]) # Verify wait was called on each set operation gfcam_base.mode_trig_auto.set().wait.assert_called_once() gfcam_base.mode_trig_soft.set().wait.assert_called_once() gfcam_base.mode_trig_timer.set().wait.assert_called_once() gfcam_base.mode_trig_ext.set().wait.assert_called_once() # Verify parameters were committed gfcam_base.set_param.set.assert_called_once_with(1) gfcam_base.set_param.set().wait.assert_called_once() def test_gfcam_trigger_mode_setter_invalid(gfcam_base): """Test setting an invalid trigger mode raises an error""" with pytest.raises(ValueError) as excinfo: gfcam_base.trigger_mode = "invalid_mode" assert "Invalid trigger mode!" in str(excinfo.value) assert "Valid modes are: ['auto', 'external', 'timer', 'soft']" in str(excinfo.value) # Verify no signals were set gfcam_base.mode_trig_auto.set.assert_not_called() gfcam_base.mode_trig_soft.set.assert_not_called() gfcam_base.mode_trig_timer.set.assert_not_called() gfcam_base.mode_trig_ext.set.assert_not_called() gfcam_base.set_param.set.assert_not_called() @pytest.mark.parametrize( "start_bit, end_bit, expected_result", [ (0, 0, "off"), # Both bits off (1, 0, "start"), # Only start bit on (0, 1, "end"), # Only end bit on (1, 1, "start+end"), # Both bits on ], ) def test_gfcam_fix_nframes_mode_property(gfcam_base, start_bit, end_bit, expected_result): """Test that the fix_nframes_mode property returns the correct mode based on bit values""" # Configure the mock return values for the bits gfcam_base.cnt_startbit.get.return_value = start_bit # Note: The original code has a bug here - it calls cnt_startbit.get() twice instead of cnt_endbit.get() # For testing purposes, we'll mock both appropriately gfcam_base.cnt_endbit.get.return_value = end_bit # Check that the property returns the expected result assert gfcam_base.fix_nframes_mode == expected_result @pytest.mark.parametrize( "mode, expected_settings", [ ("off", {"cnt_startbit": 0, "cnt_endbit": 0}), ("start", {"cnt_startbit": 1, "cnt_endbit": 0}), ("end", {"cnt_startbit": 0, "cnt_endbit": 1}), ("start+end", {"cnt_startbit": 1, "cnt_endbit": 1}), ], ) def test_gfcam_fix_nframes_mode_setter(gfcam_base, mode, expected_settings): """Test setting the fixed number of frames mode of the GigaFRoST camera""" # Mock the const.gf_valid_fix_nframe_modes to avoid importing the constants with mock.patch( "tomcat_bec.devices.gigafrost.gigafrostcamera.const.gf_valid_fix_nframe_modes", ["off", "start", "end", "start+end"], ): # Set the mode gfcam_base.fix_nframes_mode = mode # Verify the class attribute was set assert gfcam_base._fix_nframes_mode == mode # Verify the correct signals were set gfcam_base.cnt_startbit.set.assert_called_once_with(expected_settings["cnt_startbit"]) gfcam_base.cnt_endbit.set.assert_called_once_with(expected_settings["cnt_endbit"]) # Verify wait was called on each set operation gfcam_base.cnt_startbit.set().wait.assert_called_once() gfcam_base.cnt_endbit.set().wait.assert_called_once() # Verify parameters were committed gfcam_base.set_param.set.assert_called_once_with(1) gfcam_base.set_param.set().wait.assert_called_once() def test_gfcam_fix_nframes_mode_setter_invalid(gfcam_base): """Test setting an invalid fixed number of frames mode raises an error""" # Mock the const.gf_valid_fix_nframe_modes to avoid importing the constants with mock.patch( "tomcat_bec.devices.gigafrost.gigafrostcamera.const.gf_valid_fix_nframe_modes", ["off", "start", "end", "start+end"], ): with pytest.raises(ValueError) as excinfo: gfcam_base.fix_nframes_mode = "invalid_mode" assert "Invalid fixed frame number mode!" in str(excinfo.value) assert "Valid modes are:" in str(excinfo.value) # Verify no signals were set gfcam_base.cnt_startbit.set.assert_not_called() gfcam_base.cnt_endbit.set.assert_not_called() gfcam_base.set_param.set.assert_not_called()