Files
navigate/test/config/test_experiment.py
2025-12-04 16:07:30 +08:00

204 lines
7.0 KiB
Python

# Copyright (c) 2021-2025 The University of Texas Southwestern Medical Center.
# All rights reserved.
# Redistribution and use in source and binary forms, with or without
# modification, are permitted for academic and research use only
# (subject to the limitations in the disclaimer below)
# provided that the following conditions are met:
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of the copyright holders nor the names of its
# contributors may be used to endorse or promote products derived from this
# software without specific prior written permission.
# NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY
# THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
# CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
# BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
# IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
# Standard Imports
import unittest
import yaml
import os
import datetime
# Third Party Imports
# Local Imports
class TextExperimentFile(unittest.TestCase):
"""Test the experiment configuration file."""
def setUp(self):
current_path = os.path.abspath(os.path.dirname(__file__))
root_path = os.path.dirname(os.path.dirname(current_path))
yaml_path = os.path.join(
root_path, "src", "navigate", "config", "experiment.yml"
)
with open(yaml_path) as file:
self.data = yaml.safe_load(file)
def tearDown(self):
pass
def parse_entries(self, section, expected_values):
"""Parse the entries in the configuration file.
Parameters
----------
section : str
The section of the configuration file to parse
expected_values : dict
A dictionary of expected values for the section
Raises
------
AssertionError
If the key is not in the section or if the value is not the expected type
"""
keys = self.data[section].keys()
for key in keys:
self.assertIn(key, expected_values)
assert isinstance(
self.data[section][key], expected_values[key]
), f"{key} is not of type {expected_values[key]}"
def test_user(self):
expected_values = {"name": str}
self.parse_entries(section="User", expected_values=expected_values)
def test_saving(self):
expected_values = {
"root_directory": str,
"save_directory": str,
"user": str,
"tissue": str,
"celltype": str,
"label": str,
"file_type": str,
"date": datetime.date,
"solvent": str,
}
self.parse_entries(section="Saving", expected_values=expected_values)
def test_camera_parameters(self):
expected_values = {
"x_pixels": int,
"y_pixels": int,
"sensor_mode": str,
"readout_direction": str,
"number_of_pixels": int,
"binning": str,
"pixel_size": float,
"frames_to_average": float,
"databuffer_size": int,
}
self.parse_entries(section="CameraParameters", expected_values=expected_values)
def test_autofocus_parameters(self):
expected_values = {
"coarse_range": int,
"coarse_step_size": int,
"coarse_selected": int,
"fine_range": int,
"fine_step_size": int,
"fine_selected": bool,
"robust_fit": bool,
"spline_fit": bool,
"test_significance": bool,
}
self.parse_entries(
section="AutoFocusParameters", expected_values=expected_values
)
def test_stage_parameters(self):
expected_values = {
"xy_step": float,
"z_step": float,
"theta_step": float,
"f_step": float,
"x": float,
"y": float,
"z": float,
"theta": float,
"f": float,
"limits": bool,
}
self.parse_entries(section="StageParameters", expected_values=expected_values)
def test_microscope_state(self):
expected_values = {
"microscope_name": str,
"image_mode": str,
"zoom": str,
"stack_cycling_mode": str,
"start_position": float,
"end_position": float,
"step_size": float,
"number_z_steps": float,
"timepoints": int,
"stack_pause": float,
"is_save": bool,
"stack_acq_time": float,
"timepoint_interval": int,
"experiment_duration": float,
"is_multiposition": bool,
"channels": dict,
"stack_z_origin": float,
"stack_focus_origin": float,
"start_focus": float,
"end_focus": float,
"abs_z_start": float,
"abs_z_end": float,
"waveform_template": str,
}
self.parse_entries(section="MicroscopeState", expected_values=expected_values)
# Check that the channels dictionary has the correct keys
channel_keys = self.data["MicroscopeState"]["channels"].keys()
for key in channel_keys:
# Number of channels can vary depending upon the experiment.
assert "channel_" in key
expected_values = {
"is_selected": bool,
"laser": str,
"laser_index": int,
"filter": str,
"filter_position": int,
"camera_exposure_time": float,
"laser_power": str,
"interval_time": str,
"defocus": float,
}
key_keys = self.data["MicroscopeState"]["channels"][key].keys()
for key_key in key_keys:
self.assertIn(key_key, expected_values)
assert isinstance(
self.data["MicroscopeState"]["channels"][key][key_key],
expected_values[key_key],
), f"{key_key} is not of type {expected_values[key_key]}"