Starting to test more smart scan functionality using mocked Thing Dependencies
This commit is contained in:
parent
3aa9b9100a
commit
7193ec34f5
7 changed files with 184 additions and 1 deletions
|
|
@ -138,7 +138,9 @@ class SmartScanThing(Thing):
|
|||
self._capture_thread = None
|
||||
self._scan_images_taken = 0
|
||||
|
||||
# Don't set self._scan_data dictionary. This is done at the start of _run_scan
|
||||
# Set _scan_data to None. It will for error checking. It will be set to a value
|
||||
# by _run_scan
|
||||
self._scan_data = None
|
||||
|
||||
try:
|
||||
self._check_background_and_csm_set()
|
||||
|
|
|
|||
0
tests/mock_things/__init__.py
Normal file
0
tests/mock_things/__init__.py
Normal file
17
tests/mock_things/mock_autofocus.py
Normal file
17
tests/mock_things/mock_autofocus.py
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
"""This testing submodule contains mock autofocus things.
|
||||
|
||||
These mocks are designed to be inserted as dependencies to give specific
|
||||
functionality and returns.
|
||||
|
||||
The mocks do not subclass, and instead return very specific defined answers
|
||||
to functions
|
||||
"""
|
||||
|
||||
|
||||
class MockAutoFocusThing:
|
||||
# Counter for checking functions were called
|
||||
mock_call_count = {"looping_autofocus": 0}
|
||||
|
||||
def looping_autofocus(self, dz=2000, start="centre"): # noqa: ARG002
|
||||
"""This function mocks autofocus with no return"""
|
||||
self.mock_call_count["looping_autofocus"] += 1
|
||||
20
tests/mock_things/mock_background_detect.py
Normal file
20
tests/mock_things/mock_background_detect.py
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
"""This testing submodule contains mock autofocus things.
|
||||
|
||||
These mocks are designed to be inserted as dependencies to give specific
|
||||
functionality and returns.
|
||||
|
||||
The mocks do not subclass, and instead return very specific defined answers
|
||||
to functions
|
||||
"""
|
||||
|
||||
from openflexure_microscope_server.things.background_detect import ChannelDistributions
|
||||
|
||||
|
||||
class MockBackgoundDetectThing:
|
||||
# Counter for checking functions were called
|
||||
|
||||
background_distributions = ChannelDistributions(
|
||||
means=[128.0, 128.0, 128.0],
|
||||
standard_deviations=[3.0, 3.0, 3.0],
|
||||
colorspace="LUV",
|
||||
)
|
||||
12
tests/mock_things/mock_csm.py
Normal file
12
tests/mock_things/mock_csm.py
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
"""This testing submodule contains mock camera stage mapping things.
|
||||
|
||||
These mocks are designed to be inserted as dependencies to give specific
|
||||
functionality and returns.
|
||||
|
||||
The mocks do not subclass, and instead return very specific defined answers
|
||||
to functions
|
||||
"""
|
||||
|
||||
|
||||
class MockCSMThing:
|
||||
image_resolution = (123, 456)
|
||||
12
tests/mock_things/mock_stage.py
Normal file
12
tests/mock_things/mock_stage.py
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
"""This testing submodule contains stage things.
|
||||
|
||||
These mocks are designed to be inserted as dependencies to give specific
|
||||
functionality and returns.
|
||||
|
||||
The mocks do not subclass, and instead return very specific defined answers
|
||||
to functions
|
||||
"""
|
||||
|
||||
|
||||
class MockStageThing:
|
||||
position = (111, 222, 333)
|
||||
|
|
@ -14,6 +14,7 @@ For these tests to reliably represent real behaviour the mock Things will need t
|
|||
be tested for matching signatures with dynamically generated clients.
|
||||
"""
|
||||
|
||||
from typing import Callable, Optional
|
||||
import tempfile
|
||||
import os
|
||||
import shutil
|
||||
|
|
@ -27,6 +28,11 @@ from openflexure_microscope_server.things.smart_scan import (
|
|||
ScanNotRunningError,
|
||||
)
|
||||
|
||||
from .mock_things.mock_csm import MockCSMThing
|
||||
from .mock_things.mock_autofocus import MockAutoFocusThing
|
||||
from .mock_things.mock_stage import MockStageThing
|
||||
from .mock_things.mock_background_detect import MockBackgoundDetectThing
|
||||
|
||||
# A global logger to pass in as an Invocation Logger
|
||||
LOGGER = logging.getLogger("mock-invocation_logger")
|
||||
|
||||
|
|
@ -135,3 +141,117 @@ def test_delete_all_scans(smart_scan_thing, caplog):
|
|||
assert not os.path.exists(fake_scan_path)
|
||||
# No logs generated
|
||||
assert len(caplog.records) == 0
|
||||
|
||||
|
||||
def _run_only_outer_scan(adjust_inital_state: Optional[Callable] = None):
|
||||
"""
|
||||
Create a subclass of SmartScanThing to mock _run_scan and run sample_scan
|
||||
|
||||
This should do all the set up for a scan, move into the mocked
|
||||
_run_scan method where this can be tested. Once this is done
|
||||
the final scan behaviour can be tested too.
|
||||
|
||||
adjust_initial_scan is a callable which accepts the mocked smart scan thing
|
||||
as the only variable. It can be used to adjust the initial state of the test
|
||||
|
||||
|
||||
This seems hard to do with a fixture so it is being done with a private
|
||||
function
|
||||
"""
|
||||
|
||||
# cancel handle shouldn't be used. Set to arbitrary value for checking
|
||||
cancel_mock = 1 # not called
|
||||
af_mock = MockAutoFocusThing()
|
||||
stage_mock = MockStageThing()
|
||||
cam_mock = 4 # not called
|
||||
meta_mock = 5 # not called
|
||||
csm_mock = MockCSMThing()
|
||||
bkgrnd_det_mock = MockBackgoundDetectThing()
|
||||
|
||||
class MockedSmartScanThing(SmartScanThing):
|
||||
"""
|
||||
This is a subclass of SmartScanThing with a mocked method and
|
||||
mocked thing_settings.
|
||||
"""
|
||||
|
||||
# Counter for checking functions were called
|
||||
mock_call_count = {"_run_scan": 0}
|
||||
|
||||
# Mock thing settngs as a dictionary
|
||||
thing_settings = {"skip_background": True}
|
||||
|
||||
def _run_scan(self):
|
||||
self.mock_call_count["_run_scan"] += 1
|
||||
|
||||
"""Check scan vars are set up as expected"""
|
||||
assert not self._scan_lock.acquire(timeout=0.1)
|
||||
assert self._cancel is cancel_mock
|
||||
assert self._scan_logger is LOGGER
|
||||
assert self._autofocus is af_mock
|
||||
assert self._stage is stage_mock
|
||||
assert self._cam is cam_mock
|
||||
assert self._metadata_getter is meta_mock
|
||||
assert self._csm is csm_mock
|
||||
assert self._background_detect is bkgrnd_det_mock
|
||||
assert self._capture_thread is None
|
||||
assert self._scan_images_taken == 0
|
||||
|
||||
# mock smart scan thing
|
||||
mock_ss_thing = MockedSmartScanThing(SCAN_DIR)
|
||||
|
||||
if adjust_inital_state is not None:
|
||||
adjust_inital_state(mock_ss_thing)
|
||||
|
||||
exec_info = None
|
||||
try:
|
||||
mock_ss_thing.sample_scan(
|
||||
cancel=cancel_mock, # Shouldn't be used, can be checked
|
||||
logger=LOGGER,
|
||||
autofocus=af_mock,
|
||||
stage=stage_mock,
|
||||
cam=cam_mock,
|
||||
metadata_getter=meta_mock,
|
||||
csm=csm_mock,
|
||||
background_detect=bkgrnd_det_mock,
|
||||
scan_name="FooBar",
|
||||
)
|
||||
except Exception as e:
|
||||
exec_info = e
|
||||
|
||||
assert mock_ss_thing._scan_lock.acquire(timeout=0.1)
|
||||
mock_ss_thing._scan_lock.release()
|
||||
assert mock_ss_thing._cancel is None
|
||||
assert mock_ss_thing._scan_logger is None
|
||||
assert mock_ss_thing._autofocus is None
|
||||
assert mock_ss_thing._stage is None
|
||||
assert mock_ss_thing._cam is None
|
||||
assert mock_ss_thing._metadata_getter is None
|
||||
assert mock_ss_thing._csm is None
|
||||
assert mock_ss_thing._background_detect is None
|
||||
assert mock_ss_thing._capture_thread is None
|
||||
assert mock_ss_thing._scan_images_taken is None
|
||||
|
||||
# Return the mock thing for further state testing, and the
|
||||
# exec_info of any uncaught exeptions that were raised
|
||||
return mock_ss_thing, exec_info
|
||||
|
||||
|
||||
def test_outer_scan():
|
||||
"""Test setup and teardown of the scan."""
|
||||
mock_ss_thing, exec_info = _run_only_outer_scan()
|
||||
assert exec_info is None
|
||||
# Checked the mocked _run_scan was run exactly once
|
||||
assert mock_ss_thing.mock_call_count["_run_scan"] == 1
|
||||
|
||||
|
||||
def test_outer_scan_wo_scample_skip():
|
||||
"""Test setup and teardown of the scan."""
|
||||
|
||||
def _set_skip_background(mock_ss_thing):
|
||||
mock_ss_thing.thing_settings["skip_background"] = False
|
||||
|
||||
mock_ss_thing, exec_info = _run_only_outer_scan(_set_skip_background)
|
||||
|
||||
assert exec_info is None
|
||||
# Checked the mocked _run_scan was run exactly once
|
||||
assert mock_ss_thing.mock_call_count["_run_scan"] == 1
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue