Update ROM tests

This commit is contained in:
Julian Stirling 2025-12-16 17:11:51 +00:00
parent 7f8fb623ef
commit 5a391bff1c

View file

@ -1,6 +1,5 @@
"""File contains unit tests for stage_measure.""" """File contains unit tests for stage_measure."""
import dataclasses
import logging import logging
from copy import copy from copy import copy
@ -143,23 +142,21 @@ def test_parasitic_detect(par_fraction, too_high):
def test_error_if_no_stream_res_set_when_requesting_img_coords(): def test_error_if_no_stream_res_set_when_requesting_img_coords():
"""Check a RuntimeError thrown when requesting image coordinates if resolution unset.""" """Check a RuntimeError thrown when requesting image coordinates if resolution unset."""
rom_thing = create_thing_without_server(stage_measure.RangeofMotionThing) rom_thing = create_thing_without_server(
stage_measure.RangeofMotionThing, mock_all_slots=True
)
with pytest.raises(RuntimeError, match="Stream resolution must be set"): with pytest.raises(RuntimeError, match="Stream resolution must be set"):
rom_thing._img_percentage_to_img_coords(20, "x") rom_thing._img_percentage_to_img_coords(20, "x")
@pytest.fixture @pytest.fixture
def rom_thing(example_rom_data) -> stage_measure.RangeofMotionThing: def rom_thing(example_rom_data, csm_matrix) -> stage_measure.RangeofMotionThing:
"""Yield a RangeofMotionThing already populated with some example rom_data.""" """Yield a RangeofMotionThing already populated with some example rom_data."""
rom_thing = create_thing_without_server(stage_measure.RangeofMotionThing) rom_thing = create_thing_without_server(
stage_measure.RangeofMotionThing, mock_all_slots=True
)
rom_thing._stream_resolution = [800, 600] rom_thing._stream_resolution = [800, 600]
rom_thing._rom_data = example_rom_data rom_thing._rom_data = example_rom_data
return rom_thing
@pytest.fixture
def mock_rom_deps(csm_matrix, mocker) -> stage_measure.RomDeps:
"""Return a RomDeps object full of mocks, except the logger which is LOGGER."""
def apply_csm(x: float, y: float, **_kwargs: float) -> dict[str, int]: def apply_csm(x: float, y: float, **_kwargs: float) -> dict[str, int]:
"""Convert image coordinates to stage coordinates.""" """Convert image coordinates to stage coordinates."""
@ -169,22 +166,13 @@ def mock_rom_deps(csm_matrix, mocker) -> stage_measure.RomDeps:
"""Convert stage coordinates to image coordinates.""" """Convert stage coordinates to image coordinates."""
return csm_stage_to_img(csm_matrix, x=x, y=y) return csm_stage_to_img(csm_matrix, x=x, y=y)
mock_cam = mocker.Mock() rom_thing._cam.image_is_sample.return_value = (True, "Mocked not measured.")
mock_cam.image_is_sample.return_value = (True, "Mocked not measured.")
mock_csm = mocker.Mock()
# Set up mock csm to return a CSM matrix # Set up mock csm to return a CSM matrix
mock_csm.image_to_stage_displacement_matrix = csm_matrix rom_thing._csm.image_to_stage_displacement_matrix = csm_matrix
mock_csm.convert_image_to_stage_coordinates.side_effect = apply_csm rom_thing._csm.convert_image_to_stage_coordinates.side_effect = apply_csm
mock_csm.convert_stage_to_image_coordinates.side_effect = un_apply_csm rom_thing._csm.convert_stage_to_image_coordinates.side_effect = un_apply_csm
return rom_thing
return stage_measure.RomDeps(
autofocus=mocker.Mock(),
stage=mocker.Mock(),
cam=mock_cam,
csm=mock_csm,
logger=LOGGER,
)
@pytest.mark.parametrize( @pytest.mark.parametrize(
@ -197,22 +185,18 @@ def mock_rom_deps(csm_matrix, mocker) -> stage_measure.RomDeps:
({"x": 0, "y": 5000, "z": 0}, {"x": 0, "y": 0, "z": 0}, "y", 1), ({"x": 0, "y": 5000, "z": 0}, {"x": 0, "y": 0, "z": 0}, "y", 1),
], ],
) )
def test_img_dir_from_stage_coords( def test_img_dir_from_stage_coords(pos, target_pos, axis, expected_dir, rom_thing):
pos, target_pos, axis, expected_dir, rom_thing, mock_rom_deps
):
"""Check image direction is correctly generated.""" """Check image direction is correctly generated."""
mock_rom_deps.stage.position = pos rom_thing._stage.position = pos
direction = rom_thing._img_dir_from_stage_coords(target_pos, axis, mock_rom_deps) direction = rom_thing._img_dir_from_stage_coords(target_pos, axis)
assert direction == expected_dir assert direction == expected_dir
def test_distance_in_img_percentage_err(rom_thing, mock_rom_deps): def test_distance_in_img_percentage_err(rom_thing):
"""Check that _distance_in_img_percentage throws error if stream res is not set.""" """Check that _distance_in_img_percentage throws error if stream res is not set."""
rom_thing._stream_resolution = None rom_thing._stream_resolution = None
with pytest.raises(RuntimeError): with pytest.raises(RuntimeError):
rom_thing._distance_in_img_percentage( rom_thing._distance_in_img_percentage({"x": 0, "y": 0, "z": 0}, "x")
{"x": 0, "y": 0, "z": 0}, "x", mock_rom_deps
)
@pytest.mark.parametrize( @pytest.mark.parametrize(
@ -225,16 +209,14 @@ def test_distance_in_img_percentage_err(rom_thing, mock_rom_deps):
({"x": 0, "y": 5000, "z": 0}, {"x": 0, "y": 0, "z": 0}, "y", 462.131), ({"x": 0, "y": 5000, "z": 0}, {"x": 0, "y": 0, "z": 0}, "y", 462.131),
], ],
) )
def test_distance_in_img_percentage( def test_distance_in_img_percentage(pos, target_pos, axis, expected_perc, rom_thing):
pos, target_pos, axis, expected_perc, rom_thing, mock_rom_deps
):
"""Check _distance_in_img_percentage calculates expected values based on mock CSM.""" """Check _distance_in_img_percentage calculates expected values based on mock CSM."""
mock_rom_deps.stage.position = pos rom_thing._stage.position = pos
img_perc = rom_thing._distance_in_img_percentage(target_pos, axis, mock_rom_deps) img_perc = rom_thing._distance_in_img_percentage(target_pos, axis)
assert round(img_perc, 3) == expected_perc assert round(img_perc, 3) == expected_perc
def test_offset_from(rom_thing, mock_rom_deps, mocker): def test_offset_from(rom_thing, mocker):
"""Check the calls and returns for RangeofMotionThing._offset_from.""" """Check the calls and returns for RangeofMotionThing._offset_from."""
# Set up mock for the FFT displacement # Set up mock for the FFT displacement
disp_between_route = ( disp_between_route = (
@ -247,14 +229,14 @@ def test_offset_from(rom_thing, mock_rom_deps, mocker):
) )
# Run it # Run it
offset = rom_thing._offset_from(before_img="MOCK_IMAGE", rom_deps=mock_rom_deps) offset = rom_thing._offset_from(before_img="MOCK_IMAGE")
# Check the offset is a dictionary with the correct values for the axes # Check the offset is a dictionary with the correct values for the axes
assert offset["x"] == 456 assert offset["x"] == 456
assert offset["y"] == 123 assert offset["y"] == 123
# Check 1 image was taken # Check 1 image was taken
assert mock_rom_deps.cam.grab_as_array.call_count == 1 assert rom_thing._cam.grab_as_array.call_count == 1
mock_after_image = mock_rom_deps.cam.grab_as_array.return_value mock_after_image = rom_thing._cam.grab_as_array.return_value
# Check the FFT displacement was called once # Check the FFT displacement was called once
assert mock_disp_between.call_count == 1 assert mock_disp_between.call_count == 1
displacement_kwargs = mock_disp_between.call_args.kwargs displacement_kwargs = mock_disp_between.call_args.kwargs
@ -264,7 +246,7 @@ def test_offset_from(rom_thing, mock_rom_deps, mocker):
@pytest.mark.parametrize("perform_autofocus", [True, False]) @pytest.mark.parametrize("perform_autofocus", [True, False])
def test_move_and_measure(perform_autofocus, rom_thing, mock_rom_deps, mocker): def test_move_and_measure(perform_autofocus, rom_thing, mocker):
"""Test _move_and_measure with and without initial autofocus checking call counts. """Test _move_and_measure with and without initial autofocus checking call counts.
This doesn't test with the repeate autofocus if motion isn't detected This doesn't test with the repeate autofocus if motion isn't detected
@ -273,16 +255,16 @@ def test_move_and_measure(perform_autofocus, rom_thing, mock_rom_deps, mocker):
mocker.patch.object(rom_thing, "_offset_from", return_value=mock_offset_value) mocker.patch.object(rom_thing, "_offset_from", return_value=mock_offset_value)
movement = {"x": 100, "y": 0} movement = {"x": 100, "y": 0}
offset = rom_thing._move_and_measure( offset = rom_thing._move_and_measure(
movement=movement, rom_deps=mock_rom_deps, perform_autofocus=perform_autofocus movement=movement, perform_autofocus=perform_autofocus
) )
# Check exactly 1 move # Check exactly 1 move
assert mock_rom_deps.csm.move_in_image_coordinates.call_count == 1 assert rom_thing._csm.move_in_image_coordinates.call_count == 1
# The kwargs of the call should movement dict # The kwargs of the call should movement dict
assert mock_rom_deps.csm.move_in_image_coordinates.call_args.kwargs == movement assert rom_thing._csm.move_in_image_coordinates.call_args.kwargs == movement
# Check autofocus call count # Check autofocus call count
expected_af_count = 1 if perform_autofocus else 0 expected_af_count = 1 if perform_autofocus else 0
assert mock_rom_deps.autofocus.looping_autofocus.call_count == expected_af_count assert rom_thing._autofocus.looping_autofocus.call_count == expected_af_count
# And check final return # And check final return
assert offset == mock_offset_value assert offset == mock_offset_value
@ -300,7 +282,7 @@ def test_move_and_measure(perform_autofocus, rom_thing, mock_rom_deps, mocker):
], ],
) )
def test_move_and_measure_with_refocus( def test_move_and_measure_with_refocus(
x_offsets, n_offset_measures, expected_return, rom_thing, mock_rom_deps, mocker x_offsets, n_offset_measures, expected_return, rom_thing, mocker
): ):
"""Test _move_and_measure with final refocus if offset is too small.""" """Test _move_and_measure with final refocus if offset is too small."""
return_dicts = tuple({"x": x, "y": 0} for x in x_offsets) return_dicts = tuple({"x": x, "y": 0} for x in x_offsets)
@ -310,21 +292,20 @@ def test_move_and_measure_with_refocus(
movement = {"x": 10, "y": 0} movement = {"x": 10, "y": 0}
offset = rom_thing._move_and_measure( offset = rom_thing._move_and_measure(
movement=movement, movement=movement,
rom_deps=mock_rom_deps,
perform_autofocus=False, perform_autofocus=False,
max_autofocus_repeats=3, max_autofocus_repeats=3,
abs_min_offset=5, abs_min_offset=5,
) )
# Check exactly 1 move # Check exactly 1 move
assert mock_rom_deps.csm.move_in_image_coordinates.call_count == 1 assert rom_thing._csm.move_in_image_coordinates.call_count == 1
# Check expected _offset_from calls # Check expected _offset_from calls
assert offset_from_mock.call_count == n_offset_measures assert offset_from_mock.call_count == n_offset_measures
# The kwargs of the call should movement dict # The kwargs of the call should movement dict
assert mock_rom_deps.csm.move_in_image_coordinates.call_args.kwargs == movement assert rom_thing._csm.move_in_image_coordinates.call_args.kwargs == movement
# Check autofocus call count is 1 less than number of offset measures as no autofocus # Check autofocus call count is 1 less than number of offset measures as no autofocus
# is performed before the first one # is performed before the first one
expected_af_count = n_offset_measures - 1 expected_af_count = n_offset_measures - 1
assert mock_rom_deps.autofocus.looping_autofocus.call_count == expected_af_count assert rom_thing._autofocus.looping_autofocus.call_count == expected_af_count
# And check final return # And check final return
assert offset == expected_return assert offset == expected_return
@ -339,7 +320,6 @@ def test_move_and_measure_with_bad_refocus_args(rom_thing, mocker):
with pytest.raises(ValueError, match="abs_min_offset must be positive"): with pytest.raises(ValueError, match="abs_min_offset must be positive"):
rom_thing._move_and_measure( rom_thing._move_and_measure(
movement={"x": 10, "y": 0}, movement={"x": 10, "y": 0},
rom_deps=mock_rom_deps,
perform_autofocus=False, perform_autofocus=False,
max_autofocus_repeats=3, max_autofocus_repeats=3,
) )
@ -348,7 +328,6 @@ def test_move_and_measure_with_bad_refocus_args(rom_thing, mocker):
with pytest.raises(ValueError, match="abs_min_offset must be positive"): with pytest.raises(ValueError, match="abs_min_offset must be positive"):
rom_thing._move_and_measure( rom_thing._move_and_measure(
movement={"x": 10, "y": 0}, movement={"x": 10, "y": 0},
rom_deps=mock_rom_deps,
perform_autofocus=False, perform_autofocus=False,
max_autofocus_repeats=3, max_autofocus_repeats=3,
abs_min_offset=-123.456, abs_min_offset=-123.456,
@ -357,7 +336,7 @@ def test_move_and_measure_with_bad_refocus_args(rom_thing, mocker):
assert offset_from_mock.call_count == 0 assert offset_from_mock.call_count == 0
def test_move_back_until_motion_detected(rom_thing, mock_rom_deps, mocker): def test_move_back_until_motion_detected(rom_thing, mocker):
"""Check that _move_back_until_motion_detected is making increasing negative moves. """Check that _move_back_until_motion_detected is making increasing negative moves.
The moves for this method should be in opposite direction to the direction The moves for this method should be in opposite direction to the direction
@ -368,7 +347,7 @@ def test_move_back_until_motion_detected(rom_thing, mock_rom_deps, mocker):
) )
with pytest.raises(RuntimeError, match="Cannot detect motion again"): with pytest.raises(RuntimeError, match="Cannot detect motion again"):
rom_thing._move_back_until_motion_detected("y", -1, rom_deps=mock_rom_deps) rom_thing._move_back_until_motion_detected("y", -1)
max_tries = int(1.5 * stage_measure.BIG_STEP / stage_measure.SMALL_STEP) max_tries = int(1.5 * stage_measure.BIG_STEP / stage_measure.SMALL_STEP)
expected_step = 800 * stage_measure.SMALL_STEP / 100 expected_step = 800 * stage_measure.SMALL_STEP / 100
@ -386,7 +365,7 @@ def test_move_back_until_motion_detected(rom_thing, mock_rom_deps, mocker):
) )
# Other axis and direction this time # Other axis and direction this time
rom_thing._move_back_until_motion_detected("x", 1, rom_deps=mock_rom_deps) rom_thing._move_back_until_motion_detected("x", 1)
# Should only be called 3 times # Should only be called 3 times
assert mock_move_n_meas.call_count == 3 assert mock_move_n_meas.call_count == 3
@ -417,7 +396,6 @@ def test_stage_still_moves(
expected_to_detect_motion, expected_to_detect_motion,
offset_calls, offset_calls,
rom_thing, rom_thing,
mock_rom_deps,
mocker, mocker,
): ):
"""Test _stage_still_moves correctly detects stage movement.""" """Test _stage_still_moves correctly detects stage movement."""
@ -435,38 +413,34 @@ def test_stage_still_moves(
rom_thing, "_offset_from", side_effect=gen_offsets() rom_thing, "_offset_from", side_effect=gen_offsets()
) )
still_moves = rom_thing._stage_still_moves( still_moves = rom_thing._stage_still_moves(axis="x", direction=1)
axis="x",
direction=1,
rom_deps=mock_rom_deps,
)
assert still_moves is expected_to_detect_motion assert still_moves is expected_to_detect_motion
assert mock_offset_from.call_count == offset_calls assert mock_offset_from.call_count == offset_calls
def test_big_z_corrected_movement(rom_thing, mock_rom_deps): def test_big_z_corrected_movement(rom_thing):
"""Check big z corrected move moves in x/y and z the expected distances.""" """Check big z corrected move moves in x/y and z the expected distances."""
mock_rom_deps.stage.position = {"x": 5000, "y": 30, "z": 500} rom_thing._stage.position = {"x": 5000, "y": 30, "z": 500}
rom_thing._big_z_corrected_movement("x", direction=1, rom_deps=mock_rom_deps) rom_thing._big_z_corrected_movement("x", direction=1)
expected_movement = {"x": 800 * stage_measure.BIG_STEP / 100, "y": 0} expected_movement = {"x": 800 * stage_measure.BIG_STEP / 100, "y": 0}
# Check there is one z move in steps # Check there is one z move in steps
assert mock_rom_deps.stage.move_relative.call_count == 1 assert rom_thing._stage.move_relative.call_count == 1
move_kwargs = mock_rom_deps.stage.move_relative.call_args.kwargs move_kwargs = rom_thing._stage.move_relative.call_args.kwargs
assert "x" not in move_kwargs assert "x" not in move_kwargs
assert "y" not in move_kwargs assert "y" not in move_kwargs
assert "z" in move_kwargs assert "z" in move_kwargs
assert move_kwargs["z"] == 160 assert move_kwargs["z"] == 160
# And one move in image coordinates # And one move in image coordinates
assert mock_rom_deps.csm.move_in_image_coordinates.call_count == 1 assert rom_thing._csm.move_in_image_coordinates.call_count == 1
lat_mov_kwargs = mock_rom_deps.csm.move_in_image_coordinates.call_args.kwargs lat_mov_kwargs = rom_thing._csm.move_in_image_coordinates.call_args.kwargs
assert lat_mov_kwargs == expected_movement assert lat_mov_kwargs == expected_movement
def test_moves_for_z_prediction(rom_thing, mock_rom_deps, mocker): def test_moves_for_z_prediction(rom_thing, mocker):
"""Check the initial moves are of the correct size and are recorded.""" """Check the initial moves are of the correct size and are recorded."""
# Mock the _offset_from and stage.position to return generated dictionaries that # Mock the _offset_from and stage.position to return generated dictionaries that
# increment each time they are called. (All values 0 the first time, all values 1 # increment each time they are called. (All values 0 the first time, all values 1
@ -474,7 +448,7 @@ def test_moves_for_z_prediction(rom_thing, mock_rom_deps, mocker):
mocker.patch.object( mocker.patch.object(
rom_thing, "_offset_from", side_effect=increasing_xy_dict_generator() rom_thing, "_offset_from", side_effect=increasing_xy_dict_generator()
) )
type(mock_rom_deps.stage).position = mocker.PropertyMock( type(rom_thing._stage).position = mocker.PropertyMock(
side_effect=increasing_xyz_dict_generator() side_effect=increasing_xyz_dict_generator()
) )
@ -484,7 +458,7 @@ def test_moves_for_z_prediction(rom_thing, mock_rom_deps, mocker):
rom_thing._rom_data = stage_measure.RomDataTracker() rom_thing._rom_data = stage_measure.RomDataTracker()
# Run it! # Run it!
rom_thing._moves_for_z_prediction("x", direction=-1, rom_deps=mock_rom_deps) rom_thing._moves_for_z_prediction("x", direction=-1)
# Check that _rom_data now contains the 5 mocked returns in order. # Check that _rom_data now contains the 5 mocked returns in order.
assert rom_thing._rom_data.offsets == [{"x": i, "y": i} for i in range(5)] assert rom_thing._rom_data.offsets == [{"x": i, "y": i} for i in range(5)]
@ -493,16 +467,16 @@ def test_moves_for_z_prediction(rom_thing, mock_rom_deps, mocker):
] ]
# Check that the csm movement function is called 5 times # Check that the csm movement function is called 5 times
assert mock_rom_deps.csm.move_in_image_coordinates.call_count == 5 assert rom_thing._csm.move_in_image_coordinates.call_count == 5
# Each time with the expected movement # Each time with the expected movement
for arg_list in mock_rom_deps.csm.move_in_image_coordinates.call_args_list: for arg_list in rom_thing._csm.move_in_image_coordinates.call_args_list:
assert arg_list.kwargs == expected_movement assert arg_list.kwargs == expected_movement
def test_move_until_edge_error(rom_thing, mock_rom_deps, mocker): def test_move_until_edge_error(rom_thing, mocker):
"""Check that if there is an error while moving to the edge the stage returns to start.""" """Check that if there is an error while moving to the edge the stage returns to start."""
mock_position_dict = {"x": 123, "y": 456, "z": 789} mock_position_dict = {"x": 123, "y": 456, "z": 789}
type(mock_rom_deps.stage).position = mocker.PropertyMock( type(rom_thing._stage).position = mocker.PropertyMock(
return_value=mock_position_dict return_value=mock_position_dict
) )
mocker.patch.object( mocker.patch.object(
@ -514,20 +488,20 @@ def test_move_until_edge_error(rom_thing, mock_rom_deps, mocker):
# Error should be raised even though it is in the Try: # Error should be raised even though it is in the Try:
with pytest.raises(RuntimeError, match="Mock"): with pytest.raises(RuntimeError, match="Mock"):
rom_thing._move_until_edge("y", direction=-1, rom_deps=mock_rom_deps) rom_thing._move_until_edge("y", direction=-1)
# However the "finally" should have executed returning to the starting position # However the "finally" should have executed returning to the starting position
assert mock_rom_deps.stage.move_absolute.call_count == 1 assert rom_thing._stage.move_absolute.call_count == 1
abs_move_kwargs = mock_rom_deps.stage.move_absolute.call_args.kwargs abs_move_kwargs = rom_thing._stage.move_absolute.call_args.kwargs
expected_abs_move_kwargs = dict(**mock_position_dict, block_cancellation=True) expected_abs_move_kwargs = dict(**mock_position_dict, block_cancellation=True)
assert abs_move_kwargs == expected_abs_move_kwargs assert abs_move_kwargs == expected_abs_move_kwargs
def test_move_until_edge(rom_thing, mock_rom_deps, mocker): def test_move_until_edge(rom_thing, mocker):
"""Check move until edge runs the correct movement sequence.""" """Check move until edge runs the correct movement sequence."""
# Remove the mock RomData before starting # Remove the mock RomData before starting
rom_thing._rom_data = stage_measure.RomDataTracker() rom_thing._rom_data = stage_measure.RomDataTracker()
mock_rom_deps.stage.position = {"x": "mock", "y": "starting", "z": "pos"} rom_thing._stage.position = {"x": "mock", "y": "starting", "z": "pos"}
def add_fake_initial_positions(*_args, **_kwargs): def add_fake_initial_positions(*_args, **_kwargs):
"""Rather than run initial moves just add some fake data.""" """Rather than run initial moves just add some fake data."""
@ -538,13 +512,13 @@ def test_move_until_edge(rom_thing, mock_rom_deps, mocker):
"""With big moves update the stage position.""" """With big moves update the stage position."""
big_move_count = 1 big_move_count = 1
while True: while True:
mock_rom_deps.stage.position = f"mocked-big-move-pos{big_move_count}" rom_thing._stage.position = f"mocked-big-move-pos{big_move_count}"
yield yield
big_move_count += 1 big_move_count += 1
def set_final_pos(*_args, **_kwargs): def set_final_pos(*_args, **_kwargs):
"""When move_back_until_motion_detected is run set a final stage position.""" """When move_back_until_motion_detected is run set a final stage position."""
mock_rom_deps.stage.position = "mock-final-pos" rom_thing._stage.position = "mock-final-pos"
# Mock the main movement functions # Mock the main movement functions
mock_init_moves = mocker.patch.object( mock_init_moves = mocker.patch.object(
@ -568,7 +542,7 @@ def test_move_until_edge(rom_thing, mock_rom_deps, mocker):
rom_thing._rom_data = stage_measure.RomDataTracker() rom_thing._rom_data = stage_measure.RomDataTracker()
# Run function # Run function
rom_thing._move_until_edge("y", direction=-1, rom_deps=mock_rom_deps) rom_thing._move_until_edge("y", direction=-1)
# Check the call counts are as expected # Check the call counts are as expected
# One call of initial moves # One call of initial moves
@ -600,19 +574,19 @@ def test_move_until_edge(rom_thing, mock_rom_deps, mocker):
] ]
def test_perform_rom_actions_locked(rom_thing, mock_rom_deps): def test_perform_rom_actions_locked(rom_thing):
"""Check the error if running one of the calibration actions while locked.""" """Check the error if running one of the calibration actions while locked."""
# Not an RLock so no need to thread. # Not an RLock so no need to thread.
rom_thing._lock.acquire() rom_thing._lock.acquire()
err_msg = "Trying to run ROM test when a test is already running." err_msg = "Trying to run ROM test when a test is already running."
with pytest.raises(RuntimeError, match=err_msg): with pytest.raises(RuntimeError, match=err_msg):
rom_thing.perform_rom_test(**dataclasses.asdict(mock_rom_deps)) rom_thing.perform_rom_test()
err_msg = "Trying to run recentre when a test is already running." err_msg = "Trying to run recentre when a test is already running."
with pytest.raises(RuntimeError, match=err_msg): with pytest.raises(RuntimeError, match=err_msg):
rom_thing.perform_recentre(**dataclasses.asdict(mock_rom_deps)) rom_thing.perform_recentre()
def test_perform_rom_test_(rom_thing, mock_rom_deps, mocker): def test_perform_rom_test_(rom_thing, mocker):
"""Check that perform Rom Test runs the expected high level algorithm.""" """Check that perform Rom Test runs the expected high level algorithm."""
def check_and_modify_rom_data(axis: str, direction: int, **_kwargs): def check_and_modify_rom_data(axis: str, direction: int, **_kwargs):
@ -629,9 +603,9 @@ def test_perform_rom_test_(rom_thing, mock_rom_deps, mocker):
) )
mocker.patch.object( mocker.patch.object(
mock_rom_deps.cam, "grab_as_array", return_value=np.zeros([123, 456, 3]) rom_thing._cam, "grab_as_array", return_value=np.zeros([123, 456, 3])
) )
final_dict = rom_thing.perform_rom_test(**dataclasses.asdict(mock_rom_deps)) final_dict = rom_thing.perform_rom_test()
# This should take less than 1 sec # This should take less than 1 sec
assert final_dict["Time"] <= 1 assert final_dict["Time"] <= 1
@ -659,7 +633,7 @@ def test_perform_rom_test_(rom_thing, mock_rom_deps, mocker):
assert mock_move_until_edge.call_args_list[3].kwargs["direction"] == -1 assert mock_move_until_edge.call_args_list[3].kwargs["direction"] == -1
def test_perform_recenter(rom_thing, mock_rom_deps, mocker, caplog): def test_perform_recenter(rom_thing, mocker, caplog):
"""Check that performing the recentre runs through expected operations.""" """Check that performing the recentre runs through expected operations."""
def check_lock(*_args, **_kwargs): def check_lock(*_args, **_kwargs):
@ -673,14 +647,10 @@ def test_perform_recenter(rom_thing, mock_rom_deps, mocker, caplog):
# Set a mock stage position, as we patch _recentre_axis this should be logged # Set a mock stage position, as we patch _recentre_axis this should be logged
# as the centre. # as the centre.
mock_rom_deps.stage.position = {"x": 4321, "y": 1234, "z": 0} rom_thing._stage.position = {"x": 4321, "y": 1234, "z": 0}
# Unpack the dict and save because it the dictionary is a copy so we can't check
# mocks from `mock_rom_deps`
mock_deps_dict = dataclasses.asdict(mock_rom_deps)
with caplog.at_level(logging.INFO): with caplog.at_level(logging.INFO):
rom_thing.perform_recentre(**mock_deps_dict) rom_thing.perform_recentre()
assert len(caplog.messages) == 3 assert len(caplog.messages) == 3
assert caplog.messages[0] == "Recentring the stage." assert caplog.messages[0] == "Recentring the stage."
@ -699,17 +669,17 @@ def test_perform_recenter(rom_thing, mock_rom_deps, mocker, caplog):
assert mock_recentre_axis.call_args_list[1].args[0] == "y" assert mock_recentre_axis.call_args_list[1].args[0] == "y"
# check that the position set to zero once # check that the position set to zero once
assert mock_deps_dict["stage"].set_zero_position.call_count == 1 assert rom_thing._stage.set_zero_position.call_count == 1
@pytest.mark.parametrize("true_on", [1, 4, 10, 11]) @pytest.mark.parametrize("true_on", [1, 4, 10, 11])
def test_recentre_axis(true_on, rom_thing, mock_rom_deps, mocker): def test_recentre_axis(true_on, rom_thing, mocker):
"""Test the high level algorithm of recentring an axis. """Test the high level algorithm of recentring an axis.
This doesn't include deciding if we are centred or choosing the direction to move. This doesn't include deciding if we are centred or choosing the direction to move.
""" """
## Start such that movement starts negative ## Start such that movement starts negative
mock_rom_deps.stage.position = {"x": 10000, "y": 10000, "z": 0} rom_thing._stage.position = {"x": 10000, "y": 10000, "z": 0}
mock_moves = mocker.patch.object(rom_thing, "_moves_for_z_prediction") mock_moves = mocker.patch.object(rom_thing, "_moves_for_z_prediction")
# Mock _recentre_decision so it returns (False, 1) a number of times then finally # Mock _recentre_decision so it returns (False, 1) a number of times then finally
@ -721,9 +691,9 @@ def test_recentre_axis(true_on, rom_thing, mock_rom_deps, mocker):
if true_on > 10: if true_on > 10:
with pytest.raises(RuntimeError, match="Couldn't find centre"): with pytest.raises(RuntimeError, match="Couldn't find centre"):
rom_thing._recentre_axis("x", mock_rom_deps) rom_thing._recentre_axis("x")
else: else:
rom_thing._recentre_axis("x", mock_rom_deps) rom_thing._recentre_axis("x")
# _recentre_decision is called until True or exits after 10 attempts # _recentre_decision is called until True or exits after 10 attempts
assert mock_recentre_decision.call_count == min(true_on, 10) assert mock_recentre_decision.call_count == min(true_on, 10)
@ -748,9 +718,7 @@ def test_recentre_axis(true_on, rom_thing, mock_rom_deps, mocker):
(-199, -1, (True, 1)), # Always return 1 when c (-199, -1, (True, 1)), # Always return 1 when c
], ],
) )
def test_recentre_decision( def test_recentre_decision(dist, direction, expected_decision, rom_thing, mocker):
dist, direction, expected_decision, rom_thing, mock_rom_deps, mocker
):
"""What the algorithm decides to do in different situations.""" """What the algorithm decides to do in different situations."""
# Mock find turning point just because there is no _rom_data # Mock find turning point just because there is no _rom_data
mocker.patch.object(rom_thing._rom_data, "find_turning_point") mocker.patch.object(rom_thing._rom_data, "find_turning_point")
@ -760,8 +728,8 @@ def test_recentre_decision(
mocker.patch.object(rom_thing, "_distance_in_img_percentage", return_value=dist) mocker.patch.object(rom_thing, "_distance_in_img_percentage", return_value=dist)
mocker.patch.object(rom_thing, "_img_dir_from_stage_coords", return_value=direction) mocker.patch.object(rom_thing, "_img_dir_from_stage_coords", return_value=direction)
assert rom_thing._recentre_decision("x", mock_rom_deps) == expected_decision assert rom_thing._recentre_decision("x") == expected_decision
# Check that we make an absolute move (to the centre) before returning centred. # Check that we make an absolute move (to the centre) before returning centred.
centred = expected_decision[0] centred = expected_decision[0]
mock_rom_deps.stage.move_absolute.call_count == 1 if centred else 0 rom_thing._stage.move_absolute.call_count == 1 if centred else 0