Merge branch 'faster-stage-tests' into 'v3'
Speed up `tests/test_stage.py` significantly See merge request openflexure/openflexure-microscope-server!353
This commit is contained in:
commit
c9cb0c65e1
2 changed files with 46 additions and 12 deletions
19
tests/mock_things/mock_cancel.py
Normal file
19
tests/mock_things/mock_cancel.py
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
"""Create a fake CancelHook used instead of a dependency, it cannot be used to cancel!
|
||||
|
||||
Certain actions require a CancelHook to be supplied if called directly. The only method
|
||||
we use from the LabThings CancelHook is ``sleep()``. The CanceHook ``sleep()`` works
|
||||
exactly like ``time.sleep()`` except with raise an ``InvocationCancelledError`` if the
|
||||
server receives a cancellation request.
|
||||
|
||||
This is just an object with a sleep method.
|
||||
"""
|
||||
|
||||
import time
|
||||
|
||||
|
||||
class MockCancel:
|
||||
"""A class to use when directly calling an action with CancelHook dependency."""
|
||||
|
||||
def sleep(self, time_in_seconds: float) -> None:
|
||||
"""Sleep for the input number of seconds."""
|
||||
time.sleep(time_in_seconds)
|
||||
|
|
@ -18,6 +18,9 @@ from openflexure_microscope_server.things.stage import (
|
|||
)
|
||||
from openflexure_microscope_server.things.stage.dummy import DummyStage
|
||||
|
||||
|
||||
from .mock_things.mock_cancel import MockCancel
|
||||
|
||||
# Keep the size and number of moves fairly small or the tests can take forever
|
||||
point3d = st.tuples(
|
||||
st.integers(min_value=-100, max_value=100),
|
||||
|
|
@ -191,23 +194,26 @@ def test_direction_inversion(stage_client, dummy_stage):
|
|||
assert excinfo.value.response.status_code == 422
|
||||
|
||||
|
||||
def _test_move_relative(stage_client, dummy_stage, axis_inverted, path):
|
||||
def _test_move_relative(dummy_stage, axis_inverted, path):
|
||||
"""Test moving relative, ensuring position and hardware position behave as expected.
|
||||
|
||||
:param axis_inverted: Is used to set the inversion.
|
||||
:param path: The 3d path to move over, generated by hypothesis.
|
||||
"""
|
||||
cancel = MockCancel()
|
||||
_set_axis_direction(dummy_stage, axis_inverted)
|
||||
# Explicitly do axes calculation here to check logic in main code.
|
||||
x_dir = -1 if axis_inverted["x"] else 1
|
||||
y_dir = -1 if axis_inverted["y"] else 1
|
||||
z_dir = -1 if axis_inverted["z"] else 1
|
||||
|
||||
position = list(stage_client.position.values())
|
||||
position = list(dummy_stage.position.values())
|
||||
for movement in path:
|
||||
stage_client.move_relative(x=movement[0], y=movement[1], z=movement[2])
|
||||
dummy_stage.move_relative(
|
||||
cancel=cancel, x=movement[0], y=movement[1], z=movement[2]
|
||||
)
|
||||
position = [pos + move for pos, move in zip(position, movement)]
|
||||
stage_pos = stage_client.get_xyz_position()
|
||||
stage_pos = dummy_stage.get_xyz_position()
|
||||
hw_pos = dummy_stage._hardware_position
|
||||
assert position[0] == stage_pos[0] == hw_pos["x"] * x_dir
|
||||
assert position[1] == stage_pos[1] == hw_pos["y"] * y_dir
|
||||
|
|
@ -226,6 +232,11 @@ def test_move_relative(stage_client, dummy_stage, path):
|
|||
3 paths are tried for each case of axis inversion. This checks both that the
|
||||
reported position changes as is input in path, and that the hardware position is
|
||||
inverted when appropriate.
|
||||
|
||||
NOTE: it is essential that `stage_client` is imported even if any time it is used
|
||||
dummy stage could be used. This is because the fixture that creates stage client
|
||||
handles adding the dummy_stage to a server. It needs to have been added to a
|
||||
server for it not to throw errors about not being connected to a server.
|
||||
"""
|
||||
# Note that the fixture is not reset. This is fine, because the stage_should work
|
||||
# no matter the starting position.
|
||||
|
|
@ -239,30 +250,31 @@ def test_move_relative(stage_client, dummy_stage, path):
|
|||
print(path)
|
||||
for axis_inverted in inversion_combinations:
|
||||
_test_move_relative(
|
||||
stage_client=stage_client,
|
||||
dummy_stage=dummy_stage,
|
||||
axis_inverted=axis_inverted,
|
||||
path=path,
|
||||
)
|
||||
|
||||
|
||||
def _test_move_absolute(stage_client, dummy_stage, axis_inverted, path):
|
||||
def _test_move_absolute(dummy_stage, axis_inverted, path):
|
||||
"""Test moving relative, ensuring position and hardware position behave as expected.
|
||||
|
||||
:param axis_inverted: Is used to set the inversion.
|
||||
:param path: The 3d path to move over, generated by hypothesis.
|
||||
"""
|
||||
cancel = MockCancel()
|
||||
_set_axis_direction(dummy_stage, axis_inverted)
|
||||
# Explicitly do axes calculation here to check logic in main code.
|
||||
x_dir = -1 if axis_inverted["x"] else 1
|
||||
y_dir = -1 if axis_inverted["y"] else 1
|
||||
z_dir = -1 if axis_inverted["z"] else 1
|
||||
|
||||
position = list(stage_client.position.values())
|
||||
position = list(dummy_stage.position.values())
|
||||
for move_to in path:
|
||||
stage_client.move_absolute(x=move_to[0], y=move_to[1], z=move_to[2])
|
||||
dummy_stage.move_absolute(
|
||||
cancel=cancel, x=move_to[0], y=move_to[1], z=move_to[2]
|
||||
)
|
||||
position = move_to
|
||||
stage_pos = stage_client.get_xyz_position()
|
||||
stage_pos = dummy_stage.get_xyz_position()
|
||||
hw_pos = dummy_stage._hardware_position
|
||||
assert position[0] == stage_pos[0] == hw_pos["x"] * x_dir
|
||||
assert position[1] == stage_pos[1] == hw_pos["y"] * y_dir
|
||||
|
|
@ -281,6 +293,11 @@ def test_move_absolute(stage_client, dummy_stage, path):
|
|||
3 paths are tried for each case of axis inversion. This checks both that the
|
||||
reported position changes as is input in path, and that the hardware position is
|
||||
inverted when appropriate.
|
||||
|
||||
NOTE: it is essential that `stage_client` is imported even if any time it is used
|
||||
dummy stage could be used. This is because the fixture that creates stage client
|
||||
handles adding the dummy_stage to a server. It needs to have been added to a
|
||||
server for it not to throw errors about not being connected to a server.
|
||||
"""
|
||||
# Note that the fixture is not reset. This is fine, because the stage_should work
|
||||
# no matter the starting position.
|
||||
|
|
@ -291,10 +308,8 @@ def test_move_absolute(stage_client, dummy_stage, path):
|
|||
dict(zip(axis_names, inverted))
|
||||
for inverted in itertools.product([True, False], repeat=len(axis_names))
|
||||
]
|
||||
print(path)
|
||||
for axis_inverted in inversion_combinations:
|
||||
_test_move_absolute(
|
||||
stage_client=stage_client,
|
||||
dummy_stage=dummy_stage,
|
||||
axis_inverted=axis_inverted,
|
||||
path=path,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue