From 2d4c5e84348883f5dc337fc38b9e01331ed5e117 Mon Sep 17 00:00:00 2001 From: appel_c Date: Sun, 24 Dec 2023 16:42:42 +0100 Subject: [PATCH] tests: add tests for grashopper --- .../epics/devices/grashopper_tomcat.py | 1 - tests/test_grashopper_tomcat.py | 226 ++++++++++++++++++ 2 files changed, 226 insertions(+), 1 deletion(-) create mode 100644 tests/test_grashopper_tomcat.py diff --git a/ophyd_devices/epics/devices/grashopper_tomcat.py b/ophyd_devices/epics/devices/grashopper_tomcat.py index 10f0acd..d48a4c3 100644 --- a/ophyd_devices/epics/devices/grashopper_tomcat.py +++ b/ophyd_devices/epics/devices/grashopper_tomcat.py @@ -202,7 +202,6 @@ class GrashopperTOMCATSetup(CustomDetectorMixin): self.parent.cam.image_mode.put(ImageMode.MULTIPLE) self.parent.cam.acquire_time_auto.put(AutoMode.CONTINUOUS) self.set_exposure_time(self.parent.scaninfo.exp_time) - self.parent.cam.image_mode.put(ImageMode.MULTIPLE) self.parent.set_trigger(TriggerSource.SOFTWARE) self.parent.cam.set_image_counter.put(0) self.set_acquisition_params() diff --git a/tests/test_grashopper_tomcat.py b/tests/test_grashopper_tomcat.py new file mode 100644 index 0000000..4a0d516 --- /dev/null +++ b/tests/test_grashopper_tomcat.py @@ -0,0 +1,226 @@ +# pylint: skip-file +import pytest +from unittest import mock + +from ophyd_devices.epics.devices.grashopper_tomcat import ( + GrashopperTOMCATSetup, + AutoMode, + ImageMode, + DetectorState, + ImageBinning, + VideoMode, + PixelFormat, + COLORMODE, + TriggerSource, + MemoryPolling, + GrashopperError, + GrashopperTimeoutError, +) + + +def patch_dual_pvs(device): + for walk in device.walk_signals(): + if not hasattr(walk.item, "_read_pv"): + continue + if not hasattr(walk.item, "_write_pv"): + continue + if walk.item._read_pv.pvname.endswith("_RBV"): + walk.item._read_pv = walk.item._write_pv + + +@pytest.fixture(scope="function") +def mock_GrashopperSetup(): + mock_Grashopper = mock.MagicMock() + yield GrashopperTOMCATSetup(parent=mock_Grashopper) + + +# Fixture for scaninfo +@pytest.fixture( + params=[ + { + "scanID": "1234", + "scan_type": "step", + "num_points": 500, + "frames_per_trigger": 1, + "exp_time": 0.1, + "readout_time": 0.1, + }, + { + "scanID": "1234", + "scan_type": "step", + "num_points": 500, + "frames_per_trigger": 5, + "exp_time": 0.01, + "readout_time": 0, + }, + { + "scanID": "1234", + "scan_type": "fly", + "num_points": 500, + "frames_per_trigger": 1, + "exp_time": 1, + "readout_time": 0.2, + }, + { + "scanID": "1234", + "scan_type": "fly", + "num_points": 500, + "frames_per_trigger": 5, + "exp_time": 0.1, + "readout_time": 0.4, + }, + ] +) +def scaninfo(request): + return request.param + + +def test_initialize_detector(mock_GrashopperSetup): + """Test the initialize_detector method.""" + # Call the function you want to test + mock_GrashopperSetup.initialize_detector() + + # Assert the correct methods are called with the expected arguments + mock_GrashopperSetup.parent.cam.acquire.put.assert_called_once_with(0) + mock_GrashopperSetup.parent.cam.acquire_time_auto.put.assert_called_once_with( + AutoMode.CONTINUOUS + ) + mock_GrashopperSetup.parent.cam.gain_auto.put.assert_called_once_with(AutoMode.CONTINUOUS) + mock_GrashopperSetup.parent.cam.image_mode.put.assert_called_once_with(ImageMode.MULTIPLE) + mock_GrashopperSetup.parent.cam.image_binning.put.assert_called_once_with(ImageBinning.X1) + mock_GrashopperSetup.parent.cam.video_mode.put.assert_called_once_with(VideoMode.MODE0) + mock_GrashopperSetup.parent.cam.pixel_format.put.assert_called_once_with(PixelFormat.MONO16) + mock_GrashopperSetup.parent.cam.trigger_source.put.assert_called_once_with( + TriggerSource.SOFTWARE + ) + mock_GrashopperSetup.parent.cam.memory_polling.put.assert_called_once_with( + MemoryPolling.SECONDS1 + ) + mock_GrashopperSetup.parent.cam.set_image_counter.put.assert_called_once_with(0) + + +def test_initialize_detector_backend(mock_GrashopperSetup): + """Test the initialize_detector_backend method.""" + # Call the function you want to test + mock_GrashopperSetup.initialize_detector_backend() + + # Assert the correct methods are called with the expected arguments + mock_GrashopperSetup.parent.image.queue_size.put.assert_called_once_with(5) + mock_GrashopperSetup.parent.image.array_port.put.assert_called_once_with( + mock_GrashopperSetup.parent.cam.port_name.get() + ) + mock_GrashopperSetup.parent.image.enable_cb.put.assert_called_once_with(1) + mock_GrashopperSetup.parent.image.set_array_counter.put.assert_called_once_with(0) + + +@pytest.mark.parametrize("exposure_time", [1, 0.1, 0.01, 0.001, 0.0001]) +def test_set_exposure_time(mock_GrashopperSetup, exposure_time): + """Test the set_exposure_time method.""" + # Call the function you want to test + frame_rate = 1 / exposure_time + if frame_rate > mock_GrashopperSetup.low_frame_rate: + with pytest.raises(GrashopperError): + mock_GrashopperSetup.set_exposure_time(exposure_time) + else: + mock_GrashopperSetup.set_exposure_time(exposure_time) + mock_GrashopperSetup.parent.cam.frame_rate.put.assert_called_once_with(frame_rate) + + +def test_prepare_detector(mock_GrashopperSetup, scaninfo): + """Test the prepare_detector method.""" + # setup scaninof + for k, v in scaninfo.items(): + setattr(mock_GrashopperSetup.parent.scaninfo, k, v) + + # Call the function you want to test + with mock.patch.object( + mock_GrashopperSetup, "set_acquisition_params" + ) as mock_set_acquisition_params, mock.patch.object( + mock_GrashopperSetup, "set_exposure_time" + ) as mock_set_exposure_time: + mock_GrashopperSetup.prepare_detector() + + # Assert the correct methods are called with the expected arguments + mock_GrashopperSetup.parent.cam.image_mode.put.assert_called_once_with(ImageMode.MULTIPLE) + mock_GrashopperSetup.parent.cam.acquire_time_auto.put.assert_called_once_with( + AutoMode.CONTINUOUS + ) + mock_GrashopperSetup.parent.set_trigger.assert_called_once_with(TriggerSource.SOFTWARE) + mock_GrashopperSetup.parent.cam.set_image_counter.put.assert_called_once_with(0) + mock_set_acquisition_params.assert_called_once() + mock_set_exposure_time.assert_called_once_with(scaninfo["exp_time"]) + + +def test_prepare_detector_backend(mock_GrashopperSetup): + """Test the prepare_detector_backend method.""" + # Call the function you want to test + with mock.patch.object(mock_GrashopperSetup, "run_monitor") as mock_run_monitor: + mock_GrashopperSetup.prepare_detector_backend() + + # Assert the correct methods are called with the expected arguments + mock_GrashopperSetup.parent.image.set_array_counter.put.assert_called_once_with(0) + assert mock_GrashopperSetup.monitor_thread is None + assert not mock_GrashopperSetup.stop_monitor + mock_run_monitor.assert_called_once() + + +@pytest.mark.parametrize("detector_state", [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) +def test_arm_acquisition(mock_GrashopperSetup, detector_state): + """Test the arm_acquisition method.""" + # Call the function you want to test + mock_GrashopperSetup.parent.cam.detector_state.get.return_value = detector_state + mock_GrashopperSetup.parent.timeout = 0.1 + + if detector_state != DetectorState.WAITING: + with pytest.raises(GrashopperTimeoutError): + mock_GrashopperSetup.arm_acquisition() + assert mock_GrashopperSetup.parent.cam.acquire.put.call_count == 1 + assert mock_GrashopperSetup.parent.cam.acquire.put.call_args == mock.call(1) + else: + mock_GrashopperSetup.arm_acquisition() + assert mock_GrashopperSetup.parent.cam.acquire.put.call_count == 1 + assert mock_GrashopperSetup.parent.cam.acquire.put.call_args == mock.call(1) + + +@pytest.mark.parametrize("trigger_source", [0, 1, 2, 3]) +def test_on_trigger(mock_GrashopperSetup, trigger_source): + """Test the on_trigger method.""" + # Call the function you want to test + mock_GrashopperSetup.parent.cam.trigger_source.get.return_value = trigger_source + mock_GrashopperSetup.on_trigger() + + # Assert the correct methods are called with the expected arguments + if trigger_source == TriggerSource.SOFTWARE: + mock_GrashopperSetup.parent.cam.software_trigger.put.assert_called_once_with(1) + + +def test_set_acquisition_params(mock_GrashopperSetup, scaninfo): + """Test the set_acquisition_params method.""" + # Setup scaninfo + mock_GrashopperSetup.parent.scaninfo.num_points = scaninfo["num_points"] + mock_GrashopperSetup.parent.scaninfo.frames_per_trigger = scaninfo["frames_per_trigger"] + + # Call the function you want to test + mock_GrashopperSetup.set_acquisition_params() + + # Assert the correct methods are called with the expected arguments + expected_num_images = scaninfo["num_points"] * scaninfo["frames_per_trigger"] + mock_GrashopperSetup.parent.cam.num_images.put.assert_called_once_with(expected_num_images) + + +@pytest.mark.parametrize("detector_state", [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) +def test_stop_detector(mock_GrashopperSetup, detector_state): + """Test the arm_acquisition method.""" + # Call the function you want to test + mock_GrashopperSetup.parent.cam.detector_state.get.return_value = detector_state + mock_GrashopperSetup.parent.timeout = 0.1 + + if detector_state != DetectorState.IDLE: + with pytest.raises(GrashopperTimeoutError): + mock_GrashopperSetup.stop_detector() + assert mock_GrashopperSetup.parent.cam.acquire.put.call_count == 2 + assert mock_GrashopperSetup.parent.cam.acquire.put.call_args == mock.call(0) + else: + mock_GrashopperSetup.stop_detector() + assert mock_GrashopperSetup.parent.cam.acquire.put.call_count == 1 + assert mock_GrashopperSetup.parent.cam.acquire.put.call_args == mock.call(0)