"""Test the functionality in the scan_directories module.""" from datetime import datetime, timedelta from copy import copy import json from math import floor from openflexure_microscope_server.scan_directories import ScanData MOCK_START_TIME = datetime( year=2024, month=12, day=25, hour=11, minute=0, second=0, microsecond=1234, ) MOCK_END_TIME = datetime( year=2024, month=12, day=25, hour=12, minute=29, second=3, microsecond=5678, ) def _fake_scan_data(**kwargs) -> ScanData: """Make fake scan data, the start time is now. Final properties are not added. :param **kwargs: Key word arguments can be used to override other values. """ data_dict = { "scan_name": "fake_scan_0001", "overlap": 0.1, "max_dist": 100000, "dx": 100, "dy": 100, "autofocus_dz": 1000, "autofocus_on": True, "start_time": copy(MOCK_START_TIME), "skip_background": True, "stitch_automatically": True, "stitch_resize": 0.25, "save_resolution": (1000, 1000), } for key, value in kwargs.items(): data_dict[key] = value return ScanData(**data_dict) def test_set_final_data(): """Check that adding final data to a ScanData object works as expected.""" scan_data = _fake_scan_data() assert scan_data.final_image_count is None assert scan_data.duration is None assert scan_data.scan_result is None # Should set duration based of finishing at datetime.now() scan_data.set_final_data(result="Success", final_image_count=123) expected_duration = datetime.now() - MOCK_START_TIME expected_duration_s = expected_duration.total_seconds() scan_duration_s = scan_data.duration.total_seconds() assert scan_data.final_image_count == 123 assert expected_duration_s - 1 < scan_duration_s < expected_duration_s + 1 assert scan_data.scan_result == "Success" def test_custom_serialisation(): """Check that the custom serialisation in ScanData works as expected.""" scan_data = _fake_scan_data() # Serialise to string then load directly as json scan_data_dict = json.loads(scan_data.model_dump_json()) assert scan_data_dict["start_time"] == "11_00_00-25_12_2024" assert scan_data_dict["final_image_count"] == "Unknown" assert scan_data_dict["duration"] == "Unknown" assert scan_data_dict["scan_result"] == "Unknown" scan_data.set_final_data(result="Success", final_image_count=123) # Can't mock datetime.now as datetime is immutable. So just replace duraction scan_data.duration = MOCK_END_TIME - scan_data.start_time scan_data_dict = json.loads(scan_data.model_dump_json()) assert scan_data_dict["final_image_count"] == 123 assert scan_data_dict["duration"] == "1:29:03" assert scan_data_dict["scan_result"] == "Success" def test_round_trip_not_finalised(): """Check that ScanData without final data can be serialised and deserialised.""" scan_data = _fake_scan_data() scan_data_dict = json.loads(scan_data.model_dump_json()) scan_data_reloaded = ScanData(**scan_data_dict) # For the round trip to be equal we must remove microseconds from the start # time as they are not saved scan_data.start_time = scan_data.start_time.replace(microsecond=0) assert scan_data == scan_data_reloaded def test_round_trip_finalised(): """Check that finalised ScanData can be serialised and deserialised.""" scan_data = _fake_scan_data() # Finalise the data. scan_data.set_final_data(result="Success", final_image_count=123) scan_data_dict = json.loads(scan_data.model_dump_json()) scan_data_reloaded = ScanData(**scan_data_dict) # For the round trip to be equal we must remove microseconds from the start # time and duration as they are not saved scan_data.model_copy(update={}) scan_data.start_time = scan_data.start_time.replace(microsecond=0) scan_data.duration = timedelta(seconds=floor(scan_data.duration.total_seconds())) assert scan_data == scan_data_reloaded