Start updating ROM tests for new structure

This commit is contained in:
Julian Stirling 2025-10-16 22:28:32 +01:00
parent 8a1acbea28
commit 50d6298c06
2 changed files with 97 additions and 45 deletions

View file

@ -57,21 +57,21 @@ class RomDataTracker:
def __init__(self) -> None:
"""Define useful data tracked throughout test."""
self.stage_coords: list[dict[str, int]] = []
self.displacements: list[dict[str, int]] = []
self.offsets: list[dict[str, float]] = []
def record_movement(
self, current_pos: dict[str, int], offset: dict[str, int]
) -> None:
"""Record the current position and the measured offset of the last move."""
self.stage_coords.append(current_pos)
self.displacements.append(offset)
self.offsets.append(offset)
@property
def final_position(self) -> dict[str, int]:
"""The last stage coordinate recorded."""
return self.stage_coords[-1].copy()
def _predict_z_displacament(
def predict_z_displacament(
self,
movement: dict[str, int],
stage_position: dict[str, int],
@ -356,7 +356,7 @@ class RangeofMotionThing(lt.Thing):
big_movement = self._movement_in_img_coords(
fov_perc=BIG_STEP, axis=axis, direction=direction
)
z_disp = self._rom_data._predict_z_displacament(
z_disp = self._rom_data.predict_z_displacament(
movement=big_movement[axis],
stage_position=rom_deps.stage.position,
csm_matrix=rom_deps.csm.image_to_stage_displacement_matrix,
@ -541,6 +541,10 @@ def _axis_from_movement_dict(movement: dict[str, float], return_other: bool = Fa
axis and the second being the other axis. Default is False
:return: The movement axis (and optionally the other axis) as strings.
"""
# Not exclusive or, this errors if both axes are zero or neither axes are zero.
if not ((movement["x"] == 0) ^ (movement["y"] == 0)):
raise ValueError("Either x or y movement should be zero, but not both.")
if return_other:
return ("y", "x") if movement["x"] == 0 else ("x", "y")
return "y" if movement["x"] == 0 else "x"

View file

@ -1,24 +1,10 @@
"""File contains unit tests for stage_measure."""
import logging
import pytest
from openflexure_microscope_server.things import stage_measure as sm
from .mock_things.mock_csm import MockCSMThing
from .mock_things.mock_stage import MockStageThing
from openflexure_microscope_server.things import stage_measure
stage_mock = MockStageThing()
csm_mock = MockCSMThing()
def test_generate_move_dicts():
"""Check that the dictionary generated for moves is correct."""
mock_perc = 50
mock_res = [820, 616]
mock_dir = 1
mock_dict = sm._generate_move_dicts(
fov_perc=mock_perc, stream_resolution=mock_res, direction=mock_dir
)
expected_dict = {"x": 410, "y": 308}
assert mock_dict == expected_dict
LOGGER = logging.getLogger("mock-invocation_logger")
def test_predict_z():
@ -31,38 +17,100 @@ def test_predict_z():
{"x": 2908, "y": 8, "z": 509},
{"x": 3635, "y": 10, "z": 617},
]
mock_axis = "x"
mock_move = 2908
mock_z_diff = sm._predict_z(
positions=mock_positions,
axis=mock_axis,
relative_move=mock_move,
stage=stage_mock,
csm=csm_mock,
rom_data = stage_measure.RomDataTracker()
for position in mock_positions:
offset = {"x": 54.4, "y": 0}
rom_data.record_movement(position, offset)
csm_matrix = [
[0.03061156624485296, 1.8031242270940833],
[1.773236372778601, 0.006660431608601435],
]
mock_z_diff = rom_data.predict_z_displacament(
movement={"x": 2908, "y": 0},
stage_position={"x": 3635, "y": 10, "z": 617},
csm_matrix=csm_matrix,
)
expected_z_diff = 1343.1625053206606
assert mock_z_diff == expected_z_diff
@pytest.mark.parametrize(
("movement", "axis", "other_axis"),
[
({"x": 2908, "y": 0}, "x", "y"),
({"x": -29, "y": 0}, "x", "y"),
({"x": 0, "y": 123}, "y", "x"),
({"x": 0, "y": -456}, "y", "x"),
],
)
def test_axis_from_movement_dict(movement, axis, other_axis):
"""Test that _axis_from_movement_dict identifies the correct axes."""
assert stage_measure._axis_from_movement_dict(movement) == axis
ret_axes = stage_measure._axis_from_movement_dict(movement, return_other=True)
assert ret_axes == (axis, other_axis)
@pytest.mark.parametrize("movement", [{"x": 0, "y": 0}, {"x": 10, "y": 10}])
def test_error_on_axis_from_movement_dict(movement):
"""Check _axis_from_movement_dict errors if both axes are zero, or both are non-zero."""
with pytest.raises(ValueError, match="Either x or y movement should be zero"):
assert stage_measure._axis_from_movement_dict(movement)
def test_parasitic_detect():
"""Check that the parasitic error is raised correctly."""
mock_delta = 100
mock_max = 50
with pytest.raises(sm.ParasiticMotionError):
sm._parasitic_detect(delta=mock_delta, max_allowed_delta=mock_max)
with pytest.raises(stage_measure.ParasiticMotionError):
stage_measure._detect_parasitic_motion(
movement={"x": 2908, "y": 0}, offset={"x": 2908, "y": 300}
)
def test_collate_data():
"""Check that the data is being collected and calculated correctly."""
mock_data = {
"positive x": {"final_position": {"x": 100}},
"negative x": {"final_position": {"x": 50}},
"positive y": {"final_position": {"y": 100}},
"negative y": {"final_position": {"y": 50}},
}
@pytest.fixture
def rom_thing() -> stage_measure.RangeofMotionThing:
"""Return a RangeofMotionThing."""
return stage_measure.RangeofMotionThing()
mock_step_range = [50, 50]
mock_time = 123
mock_dict = sm._collate_data(data=mock_data, time=mock_time, csm=MockCSMThing)
assert mock_dict["Step Range"] == mock_step_range
@pytest.fixture
def mock_rom_deps(mocker) -> stage_measure.RomDeps:
"""Return a RomDeps object full of mocks, except the logger which is LOGGER."""
return stage_measure.RomDeps(
autofocus=mocker.Mock(),
stage=mocker.Mock(),
cam=mocker.Mock(),
csm=mocker.Mock(),
logger=LOGGER,
)
def test_offset_from(rom_thing, mock_rom_deps, mocker):
"""Check the calls and returns for RangeofMotionThing._offset_from."""
# Set up mock for the FFT displacement
disp_between_route = (
"openflexure_microscope_server.things.stage_measure."
"fft_image_tracking.displacement_between_images"
)
mock_disp_between = mocker.patch(
disp_between_route,
side_effect=([123, 456],),
)
# Run it
offset = rom_thing._offset_from(before_img="MOCK_IMAGE", rom_deps=mock_rom_deps)
# Check the offset is a dictionary with the correct values for the axes
assert offset["x"] == 456
assert offset["y"] == 123
# Check 1 image was taken
assert mock_rom_deps.cam.grab_as_array.call_count == 1
mock_after_image = mock_rom_deps.cam.grab_as_array.return_value
# Check the FFT displacement was called once
assert mock_disp_between.call_count == 1
displacement_kwargs = mock_disp_between.call_args.kwargs
# Check the image inputs
assert displacement_kwargs["image_0"] == "MOCK_IMAGE"
assert displacement_kwargs["image_1"] == mock_after_image