856 lines
32 KiB
Python
856 lines
32 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 pathlib
|
|
import unittest
|
|
from unittest.mock import patch, MagicMock
|
|
from multiprocessing import Manager
|
|
from multiprocessing.managers import ListProxy, DictProxy
|
|
import os
|
|
import time
|
|
import random
|
|
import yaml
|
|
import sys
|
|
|
|
# Third Party Imports
|
|
|
|
# Local Imports
|
|
import navigate.config.config as config
|
|
from navigate.tools.file_functions import save_yaml_file, delete_folder, load_yaml_file
|
|
|
|
|
|
def test_config_methods():
|
|
methods = dir(config)
|
|
desired_methods = [
|
|
"DictProxy",
|
|
"ListProxy",
|
|
"Path",
|
|
"__builtins__",
|
|
"__cached__",
|
|
"__doc__",
|
|
"__file__",
|
|
"__loader__",
|
|
"__name__",
|
|
"__package__",
|
|
"__spec__",
|
|
"build_nested_dict",
|
|
"build_ref_name",
|
|
"load_param_from_module",
|
|
"save_yaml_file",
|
|
"get_configuration_paths",
|
|
"get_navigate_path",
|
|
"isfile",
|
|
"load_configs",
|
|
"os",
|
|
"platform",
|
|
"shutil",
|
|
"sys",
|
|
"time",
|
|
"update_config_dict",
|
|
"verify_experiment_config",
|
|
"verify_waveform_constants",
|
|
"verify_positions_config",
|
|
"verify_configuration",
|
|
"support_deceased_configuration",
|
|
"yaml",
|
|
"logging",
|
|
"logger",
|
|
"p",
|
|
"Union",
|
|
"multiprocessing",
|
|
]
|
|
for method in methods:
|
|
assert method in desired_methods
|
|
|
|
|
|
def test_get_navigate_path():
|
|
"""Test that the Navigate path is a string."""
|
|
assert isinstance(config.get_navigate_path(), str)
|
|
path_string = config.get_navigate_path()
|
|
assert ".navigate" in path_string
|
|
|
|
|
|
def test_get_navigate_path_windows(monkeypatch):
|
|
"""Test that the Navigate path is a string."""
|
|
monkeypatch.setattr(config.platform, "system", lambda: "Windows")
|
|
monkeypatch.setattr(config.os, "getenv", lambda x: "LOCALAPPDATA")
|
|
monkeypatch.setattr(config.os.path, "exists", lambda x: True)
|
|
assert isinstance(config.get_navigate_path(), str)
|
|
path_string = config.get_navigate_path()
|
|
assert path_string.startswith("LOCALAPPDATA")
|
|
assert path_string.endswith(".navigate")
|
|
|
|
|
|
def test_get_navigate_path_mac(monkeypatch):
|
|
"""Test that the Navigate path is a string."""
|
|
monkeypatch.setattr(config.platform, "system", lambda: "Darwin")
|
|
monkeypatch.setattr(config.os, "getenv", lambda x: "HOME")
|
|
monkeypatch.setattr(config.os.path, "exists", lambda x: True)
|
|
assert isinstance(config.get_navigate_path(), str)
|
|
path_string = config.get_navigate_path()
|
|
assert path_string.startswith("HOME")
|
|
assert path_string.endswith(".navigate")
|
|
|
|
|
|
# Write a test for config.get_configuration_paths()
|
|
def test_get_configuration_paths():
|
|
"""Test that the configuration paths are a list."""
|
|
paths = config.get_configuration_paths()
|
|
for path in paths:
|
|
assert isinstance(path, pathlib.Path)
|
|
assert len(paths) == 7
|
|
|
|
|
|
def test_get_configuration_paths_create_dir(monkeypatch):
|
|
"""Test that the configuration path is created,
|
|
and that they are a list."""
|
|
monkeypatch.setattr(config, "get_navigate_path", lambda: "TESTPATH")
|
|
paths = config.get_configuration_paths()
|
|
for path in paths:
|
|
assert isinstance(path, pathlib.Path)
|
|
assert os.path.exists(path), "Each configuration yaml file is copied"
|
|
assert path.suffix.lower() in [".yml", ".yaml"]
|
|
# delete generated folder
|
|
delete_folder("TESTPATH")
|
|
|
|
|
|
# test that the system is exited if no file is provided to load_yaml_config
|
|
def test_load_yaml_config_no_file():
|
|
"""Test that the system exits if no file is provided."""
|
|
from unittest import mock
|
|
|
|
with mock.patch("sys.exit") as mock_sys_exit:
|
|
config.load_configs(manager=Manager(), **{})
|
|
mock_sys_exit.assert_called_once()
|
|
|
|
|
|
class TestLoadConfigsWithYAMLError(unittest.TestCase):
|
|
"""Test the load_configs function.
|
|
|
|
Target is the yaml.YAMLError exception clause.
|
|
"""
|
|
|
|
@patch("yaml.load")
|
|
@patch("builtins.open")
|
|
@patch("pathlib.Path.exists")
|
|
def test_yaml_error(self, mock_exists, mock_open, mock_yaml_load):
|
|
# Set up the mocks
|
|
mock_exists.return_value = True
|
|
mock_open.return_value = MagicMock()
|
|
mock_yaml_load.side_effect = yaml.YAMLError("Test YAMLError")
|
|
|
|
# Mocking sys.exit to prevent the test runner from exiting
|
|
with patch.object(sys, "exit") as mock_exit:
|
|
manager = MagicMock()
|
|
config.load_configs(manager, config1="path/to/config1.yaml")
|
|
|
|
# Check if sys.exit was called with the expected argument
|
|
mock_exit.assert_called_with(1)
|
|
|
|
# Check if the YAMLError was triggered
|
|
mock_yaml_load.assert_called_once()
|
|
|
|
|
|
class TestBuildNestedDict(unittest.TestCase):
|
|
def setUp(self):
|
|
self.manager = Manager()
|
|
self.parent_dict = {}
|
|
self.key_name = "nested_dict"
|
|
|
|
def tearDown(self):
|
|
self.manager.shutdown()
|
|
|
|
def test_build_nested_dict_with_string_data(self):
|
|
string_data = "string"
|
|
expected_result = {"nested_dict": "string"}
|
|
|
|
config.build_nested_dict(
|
|
self.manager, self.parent_dict, self.key_name, string_data
|
|
)
|
|
|
|
self.assertEqual(self.parent_dict, expected_result)
|
|
self.assertEqual(self.parent_dict[self.key_name], "string")
|
|
assert isinstance(self.parent_dict, dict)
|
|
|
|
def test_build_nested_dict_with_list_data(self):
|
|
list_data = ["string1", "string2"]
|
|
|
|
config.build_nested_dict(
|
|
self.manager, self.parent_dict, self.key_name, list_data
|
|
)
|
|
|
|
assert isinstance(self.parent_dict, dict)
|
|
assert isinstance(self.parent_dict[self.key_name], ListProxy)
|
|
for i in range(2):
|
|
assert self.parent_dict[self.key_name][i] == list_data[i]
|
|
|
|
def test_build_nested_dict_with_dict_data(self):
|
|
dict_data = {"key1": "string1", "key2": "string2"}
|
|
|
|
config.build_nested_dict(
|
|
self.manager, self.parent_dict, self.key_name, dict_data
|
|
)
|
|
|
|
assert isinstance(self.parent_dict, dict)
|
|
assert isinstance(self.parent_dict[self.key_name], DictProxy)
|
|
for key in dict_data.keys():
|
|
assert self.parent_dict[self.key_name][key] == dict_data[key]
|
|
|
|
def test_update_config_dict_with_bad_file_name(self):
|
|
test_entry = "string"
|
|
dict_data = {"key1": "string1", "key2": "string2"}
|
|
# Build the nested config
|
|
config.build_nested_dict(
|
|
self.manager, self.parent_dict, self.key_name, dict_data
|
|
)
|
|
|
|
# Update the nested config
|
|
result = config.update_config_dict(
|
|
self.manager, self.parent_dict, self.key_name, test_entry
|
|
)
|
|
assert result is False
|
|
|
|
def test_update_config_dict_with_file_name(self):
|
|
test_entry = "test.yml"
|
|
# create an yaml file
|
|
test_yaml_data = {"test_key1": "test_string1", "test_key2": "test_string2"}
|
|
save_yaml_file("", test_yaml_data, test_entry)
|
|
|
|
dict_data = {"key1": "string1", "key2": "string2"}
|
|
# Build the nested config
|
|
config.build_nested_dict(
|
|
self.manager, self.parent_dict, self.key_name, dict_data
|
|
)
|
|
|
|
# Update the nested config
|
|
result = config.update_config_dict(
|
|
self.manager, self.parent_dict, self.key_name, test_entry
|
|
)
|
|
|
|
assert result is True
|
|
assert isinstance(self.parent_dict[self.key_name], DictProxy)
|
|
for k in self.parent_dict[self.key_name].keys():
|
|
assert self.parent_dict[self.key_name][k] == test_yaml_data[k]
|
|
|
|
# delete test yaml file
|
|
os.remove(test_entry)
|
|
|
|
|
|
class TestVerifyExperimentConfig(unittest.TestCase):
|
|
def setUp(self):
|
|
self.manager = Manager()
|
|
current_path = os.path.abspath(os.path.dirname(__file__))
|
|
root_path = os.path.dirname(os.path.dirname(current_path))
|
|
self.config_path = os.path.join(root_path, "src", "navigate", "config")
|
|
self.test_root = "test_dir"
|
|
os.mkdir(self.test_root)
|
|
|
|
configuration = config.load_configs(
|
|
self.manager,
|
|
configuration=os.path.join(self.config_path, "configuration.yaml"),
|
|
)
|
|
config.verify_configuration(self.manager, configuration)
|
|
saving_dict_sample = {
|
|
"root_directory": config.get_navigate_path(),
|
|
"save_directory": config.get_navigate_path(),
|
|
"user": "Kevin",
|
|
"tissue": "Lung",
|
|
"celltype": "MV3",
|
|
"label": "GFP",
|
|
"file_type": "TIFF",
|
|
"date": time.strftime("%Y-%m-%d"),
|
|
"solvent": "BABB",
|
|
}
|
|
|
|
camera_parameters_dict_sample = {
|
|
"x_pixels": 2048,
|
|
"y_pixels": 2048,
|
|
"img_x_pixels": 2048,
|
|
"img_y_pixels": 2048,
|
|
"sensor_mode": "Normal",
|
|
"readout_direction": "Top-to-Bottom",
|
|
"number_of_pixels": 10,
|
|
"binning": "1x1",
|
|
"frames_to_average": 1,
|
|
"databuffer_size": 100,
|
|
}
|
|
|
|
# Autofocus
|
|
# autofocus_sample = {
|
|
# "coarse_range": 500,
|
|
# "coarse_step_size": 50,
|
|
# "coarse_selected": True,
|
|
# "fine_range": 50,
|
|
# "fine_step_size": 5,
|
|
# "fine_selected": True,
|
|
# "robust_fit": False,
|
|
# }
|
|
|
|
stage_parameters_dict_sample = {
|
|
"limits": True,
|
|
}
|
|
for microscope_name in configuration["configuration"]["microscopes"].keys():
|
|
stage_parameters_dict_sample[microscope_name] = {}
|
|
for k in ["theta_step", "f_step", "z_step"]:
|
|
stage_parameters_dict_sample[microscope_name][k] = configuration[
|
|
"configuration"
|
|
]["microscopes"][microscope_name]["stage"].get(k, 30)
|
|
stage_parameters_dict_sample[microscope_name]["xy_step"] = min(
|
|
configuration["configuration"]["microscopes"][microscope_name][
|
|
"stage"
|
|
].get("x_step", 500),
|
|
configuration["configuration"]["microscopes"][microscope_name][
|
|
"stage"
|
|
].get("y_step", 500),
|
|
)
|
|
|
|
microscope_name = configuration["configuration"]["microscopes"].keys()[0]
|
|
zoom = configuration["configuration"]["microscopes"][microscope_name]["zoom"][
|
|
"position"
|
|
].keys()[0]
|
|
microscope_parameters_dict_sample = {
|
|
"microscope_name": microscope_name,
|
|
"image_mode": "live",
|
|
"zoom": zoom,
|
|
"stack_cycling_mode": "per_stack",
|
|
"start_position": 0.0,
|
|
"end_position": 100.0,
|
|
"step_size": 20.0,
|
|
"number_z_steps": 5,
|
|
"timepoints": 1,
|
|
"stack_pause": 0.0,
|
|
"is_save": False,
|
|
"stack_acq_time": 1.0,
|
|
"timepoint_interval": 0,
|
|
"experiment_duration": 1.03,
|
|
"is_multiposition": False,
|
|
"stack_z_origin": 0,
|
|
"stack_focus_origin": 0,
|
|
"start_focus": 0.0,
|
|
"end_focus": 0.0,
|
|
"abs_z_start": 0.0,
|
|
"abs_z_end": 100.0,
|
|
"waveform_template": "Default",
|
|
}
|
|
|
|
# multipositions_sample = [[10.0, 10.0, 10.0, 10.0, 10.0]]
|
|
|
|
self.experiment_sample = {
|
|
"Saving": saving_dict_sample,
|
|
"CameraParameters": camera_parameters_dict_sample,
|
|
"StageParameters": stage_parameters_dict_sample,
|
|
"MicroscopeState": microscope_parameters_dict_sample,
|
|
}
|
|
|
|
def tearDown(self):
|
|
delete_folder(self.test_root)
|
|
self.manager.shutdown()
|
|
|
|
def assert_equal_dict(self, dict1, dict2):
|
|
# dict1 and dict2 are not nested dict
|
|
for k in dict1.keys():
|
|
assert dict1[k] == dict2[k], f"{k}: {dict1[k]} -- {dict2[k]}"
|
|
|
|
def test_load_empty_experiment_file(self):
|
|
experiment_file_path = os.path.join(self.test_root, "experiment.yml")
|
|
with open(experiment_file_path, "w") as f:
|
|
f.write("")
|
|
configuration = config.load_configs(
|
|
self.manager,
|
|
configuration=os.path.join(self.config_path, "configuration.yaml"),
|
|
experiment=experiment_file_path,
|
|
)
|
|
config.verify_configuration(self.manager, configuration)
|
|
config.verify_experiment_config(self.manager, configuration)
|
|
|
|
experiement_config = configuration["experiment"]
|
|
assert type(experiement_config) == DictProxy
|
|
|
|
# Saving parameters
|
|
self.assert_equal_dict(
|
|
self.experiment_sample["Saving"], experiement_config["Saving"]
|
|
)
|
|
|
|
# Camera parameters
|
|
self.assert_equal_dict(
|
|
self.experiment_sample["CameraParameters"],
|
|
experiement_config["CameraParameters"],
|
|
)
|
|
|
|
# AutoFocusParameters
|
|
|
|
# Stage parameters
|
|
for k, value in self.experiment_sample["StageParameters"].items():
|
|
if type(value) == dict:
|
|
assert k in experiement_config["StageParameters"].keys()
|
|
self.assert_equal_dict(value, experiement_config["StageParameters"][k])
|
|
else:
|
|
assert value == experiement_config["StageParameters"][k]
|
|
|
|
# MicroscopeState parameters
|
|
self.assert_equal_dict(
|
|
self.experiment_sample["MicroscopeState"],
|
|
experiement_config["MicroscopeState"],
|
|
)
|
|
|
|
# # MultiPositions
|
|
# for i, position in enumerate(self.experiment_sample["MultiPositions"]):
|
|
# assert position == experiement_config["MultiPositions"][i]
|
|
|
|
def test_load_experiment_file_with_missing_parameters(self):
|
|
experiment = load_yaml_file(os.path.join(self.config_path, "experiment.yml"))
|
|
# Saving prameters
|
|
saving_parameters = list(self.experiment_sample["Saving"].keys())
|
|
saving_parameters_deleted = self.delete_random_entries_from_dict(
|
|
saving_parameters, experiment["Saving"]
|
|
)
|
|
|
|
# Camera parameters
|
|
camera_parameters = list(self.experiment_sample["CameraParameters"].keys())
|
|
camera_parameters.append("img_x_pixels")
|
|
camera_parameters.append("img_y_pixels")
|
|
camera_parameters_deleted = self.delete_random_entries_from_dict(
|
|
camera_parameters, experiment["CameraParameters"]
|
|
)
|
|
|
|
# StageParameters
|
|
configuration = load_yaml_file(
|
|
os.path.join(self.config_path, "configuration.yaml")
|
|
)
|
|
# delete limits
|
|
if "limits" in experiment["StageParameters"].keys():
|
|
del experiment["StageParameters"]["limits"]
|
|
# delete part of stage parameters of one microscope
|
|
microscope_names = list(configuration["microscopes"].keys())
|
|
if microscope_names[0] not in experiment["StageParameters"]:
|
|
experiment["StageParameters"][microscope_names[0]] = {
|
|
"z_step": 100.0,
|
|
"theta_step": 10.0,
|
|
}
|
|
# delete all stage parameter of another microscope
|
|
if microscope_names[1] in experiment["StageParameters"].keys():
|
|
del experiment["StageParameters"][microscope_names[1]]
|
|
|
|
# MicroscopeState
|
|
micrscope_parameters = list(self.experiment_sample["MicroscopeState"].keys())
|
|
micrscope_parameters.append("channels")
|
|
micrscope_parameters_deleted = self.delete_random_entries_from_dict(
|
|
micrscope_parameters, experiment["MicroscopeState"]
|
|
)
|
|
|
|
save_yaml_file(self.test_root, experiment, "experiment_missing_parameters.yml")
|
|
configuration = config.load_configs(
|
|
self.manager,
|
|
configuration=os.path.join(self.config_path, "configuration.yaml"),
|
|
experiment=os.path.join(
|
|
self.test_root, "experiment_missing_parameters.yml"
|
|
),
|
|
)
|
|
config.verify_configuration(self.manager, configuration)
|
|
config.verify_experiment_config(self.manager, configuration)
|
|
# verify Saving parameters are added
|
|
for k in saving_parameters_deleted:
|
|
assert (
|
|
k in configuration["experiment"]["Saving"].keys()
|
|
), f"parameter {k} should be added to Saving parameters"
|
|
|
|
# verify CameraParameters are added
|
|
for k in camera_parameters_deleted:
|
|
assert (
|
|
k in configuration["experiment"]["CameraParameters"].keys()
|
|
), f"parameter {k} should be added into CameraParameters"
|
|
|
|
# verify MicroscopeState parameters are added
|
|
for k in micrscope_parameters_deleted:
|
|
assert (
|
|
k in configuration["experiment"]["MicroscopeState"].keys()
|
|
), f"parameter {k} should be added to MicroscopeState"
|
|
|
|
# verify Stage parameters are added
|
|
assert (
|
|
"limits" in configuration["experiment"]["StageParameters"].keys()
|
|
), "limits should be added to Stage parameters"
|
|
for microscope_name in microscope_names:
|
|
for k in ["xy_step", "z_step", "f_step", "theta_step"]:
|
|
assert (
|
|
k in configuration["experiment"]["StageParameters"][microscope_name]
|
|
)
|
|
|
|
def test_load_experiment_file_with_wrong_parameter_values(self):
|
|
configuration = config.load_configs(
|
|
self.manager,
|
|
configuration=os.path.join(self.config_path, "configuration.yaml"),
|
|
experiment=os.path.join(self.config_path, "experiment.yml"),
|
|
)
|
|
config.verify_configuration(self.manager, configuration)
|
|
experiment = configuration["experiment"]
|
|
# Saving parameters
|
|
# check if root_directory and save_directory exist
|
|
experiment["Saving"]["root_directory"] = self.config_path
|
|
experiment["Saving"]["save_directory"] = os.path.join(
|
|
self.test_root, "not_exist", "not_exist"
|
|
)
|
|
config.verify_experiment_config(self.manager, configuration)
|
|
assert experiment["Saving"]["root_directory"] == self.config_path
|
|
assert os.path.exists(experiment["Saving"]["save_directory"])
|
|
assert experiment["Saving"]["save_directory"] != os.path.join(
|
|
self.test_root, "not_exist", "not_exist"
|
|
)
|
|
|
|
# CameraParameters
|
|
# x_pixels, y_pixels
|
|
experiment["CameraParameters"]["x_pixels"] = -10
|
|
experiment["CameraParameters"]["y_pixels"] = "abcd"
|
|
config.verify_experiment_config(self.manager, configuration)
|
|
assert (
|
|
experiment["CameraParameters"]["x_pixels"]
|
|
== self.experiment_sample["CameraParameters"]["x_pixels"]
|
|
)
|
|
assert (
|
|
experiment["CameraParameters"]["y_pixels"]
|
|
== self.experiment_sample["CameraParameters"]["y_pixels"]
|
|
)
|
|
binning = int(experiment["CameraParameters"]["binning"][0])
|
|
assert (
|
|
experiment["CameraParameters"]["img_x_pixels"]
|
|
== experiment["CameraParameters"]["x_pixels"] // binning
|
|
)
|
|
assert (
|
|
experiment["CameraParameters"]["img_y_pixels"]
|
|
== experiment["CameraParameters"]["y_pixels"] // binning
|
|
)
|
|
|
|
# binning
|
|
for v in ["abcd", "3x3", "12.4"]:
|
|
experiment["CameraParameters"]["binning"] = v
|
|
config.verify_experiment_config(self.manager, configuration)
|
|
assert experiment["CameraParameters"]["binning"] == "1x1"
|
|
assert (
|
|
experiment["CameraParameters"]["img_x_pixels"]
|
|
== experiment["CameraParameters"]["x_pixels"]
|
|
)
|
|
assert (
|
|
experiment["CameraParameters"]["img_y_pixels"]
|
|
== experiment["CameraParameters"]["y_pixels"]
|
|
)
|
|
|
|
# sensor_mode
|
|
experiment["CameraParameters"]["sensor_mode"] = "None"
|
|
config.verify_experiment_config(self.manager, configuration)
|
|
assert experiment["CameraParameters"]["sensor_mode"] == "Normal"
|
|
experiment["CameraParameters"]["sensor_mode"] = "Lightsheet"
|
|
config.verify_experiment_config(self.manager, configuration)
|
|
assert experiment["CameraParameters"]["sensor_mode"] == "Normal"
|
|
experiment["CameraParameters"]["sensor_mode"] = "Light-Sheet"
|
|
config.verify_experiment_config(self.manager, configuration)
|
|
assert experiment["CameraParameters"]["sensor_mode"] == "Light-Sheet"
|
|
|
|
# readout_direction
|
|
for v in ["abcd", 123, None]:
|
|
experiment["CameraParameters"]["readout_direction"] = v
|
|
config.verify_experiment_config(self.manager, configuration)
|
|
assert (
|
|
experiment["CameraParameters"]["readout_direction"] == "Top-to-Bottom"
|
|
)
|
|
|
|
experiment["CameraParameters"]["readout_direction"] = "Bottom-to-Top"
|
|
config.verify_experiment_config(self.manager, configuration)
|
|
assert experiment["CameraParameters"]["readout_direction"] == "Bottom-to-Top"
|
|
|
|
# other parameters should be int
|
|
for k in ["number_of_pixels", "databuffer_size", "frames_to_average"]:
|
|
for v in ["abc", -10, 0]:
|
|
experiment["CameraParameters"][k] = v
|
|
config.verify_experiment_config(self.manager, configuration)
|
|
assert (
|
|
experiment["CameraParameters"][k]
|
|
== self.experiment_sample["CameraParameters"][k]
|
|
)
|
|
|
|
# StageParameters
|
|
experiment["StageParameters"]["limits"] = "abc"
|
|
config.verify_experiment_config(self.manager, configuration)
|
|
assert experiment["StageParameters"]["limits"] is True
|
|
|
|
microscope_names = list(configuration["configuration"]["microscopes"].keys())
|
|
for microscope_name in microscope_names:
|
|
for k in ["xy_step", "z_step", "f_step", "theta_step"]:
|
|
experiment["StageParameters"][microscope_name][k] = "abc"
|
|
config.verify_experiment_config(self.manager, configuration)
|
|
assert isinstance(
|
|
experiment["StageParameters"][microscope_name][k], int
|
|
)
|
|
|
|
# MicroscopeState
|
|
experiment["MicroscopeState"]["microscope_name"] = "nonexist_microscope"
|
|
config.verify_experiment_config(self.manager, configuration)
|
|
assert experiment["MicroscopeState"]["microscope_name"] == microscope_names[0]
|
|
|
|
experiment["MicroscopeState"]["zoom"] = "abc"
|
|
config.verify_experiment_config(self.manager, configuration)
|
|
assert (
|
|
experiment["MicroscopeState"]["zoom"]
|
|
== list(
|
|
configuration["configuration"]["microscopes"][microscope_names[0]][
|
|
"zoom"
|
|
]["position"].keys()
|
|
)[0]
|
|
)
|
|
|
|
for k in [
|
|
"start_position",
|
|
"end_position",
|
|
"step_size",
|
|
"number_z_steps",
|
|
"timepoints",
|
|
"stack_acq_time",
|
|
"timepoint_interval",
|
|
"experiment_duration",
|
|
"stack_z_origin",
|
|
"stack_focus_origin",
|
|
"start_focus",
|
|
"end_focus",
|
|
"abs_z_start",
|
|
"abs_z_end",
|
|
]:
|
|
experiment["MicroscopeState"][k] = "nonsense_value"
|
|
config.verify_experiment_config(self.manager, configuration)
|
|
assert isinstance(experiment["MicroscopeState"][k], int) or isinstance(
|
|
experiment["MicroscopeState"][k], float
|
|
)
|
|
|
|
# channels
|
|
experiment["MicroscopeState"]["channels"] = [
|
|
{
|
|
"is_selected": True,
|
|
"laser": "488nm",
|
|
"laser_index": 0,
|
|
"filter": "Empty-Alignment",
|
|
"filter_position": 0,
|
|
"camera_exposure_time": 200.0,
|
|
"laser_power": 20.0,
|
|
"interval_time": 5.0,
|
|
"defocus": 0.0,
|
|
}
|
|
]
|
|
# number_of_filter_wheels =
|
|
|
|
config.verify_experiment_config(self.manager, configuration)
|
|
assert type(experiment["MicroscopeState"]["channels"]) is DictProxy
|
|
assert len(list(experiment["MicroscopeState"]["channels"].keys())) == 0
|
|
|
|
experiment["MicroscopeState"]["channels"] = {
|
|
"channel_0": {
|
|
"is_selected": True,
|
|
"laser": "488nm",
|
|
"laser_index": 0,
|
|
"filter": "Empty-Alignment",
|
|
"filter_position": 0,
|
|
"camera_exposure_time": 200.0,
|
|
"laser_power": 20.0,
|
|
"interval_time": 5.0,
|
|
"defocus": 0.0,
|
|
}
|
|
}
|
|
config.verify_experiment_config(self.manager, configuration)
|
|
assert type(experiment["MicroscopeState"]["channels"]) is DictProxy
|
|
assert len(list(experiment["MicroscopeState"]["channels"].keys())) == 0
|
|
|
|
experiment["MicroscopeState"]["channels"] = {
|
|
"channel_100": {
|
|
"is_selected": True,
|
|
"laser": "488nm",
|
|
"laser_index": 0,
|
|
"filter": "Empty-Alignment",
|
|
"filter_position": 0,
|
|
"camera_exposure_time": 200.0,
|
|
"laser_power": 20.0,
|
|
"interval_time": 5.0,
|
|
"defocus": 0.0,
|
|
}
|
|
}
|
|
config.verify_experiment_config(self.manager, configuration)
|
|
assert type(experiment["MicroscopeState"]["channels"]) is DictProxy
|
|
assert len(list(experiment["MicroscopeState"]["channels"].keys())) == 0
|
|
|
|
microscope_name = experiment["MicroscopeState"]["microscope_name"]
|
|
lasers = [
|
|
f"{laser['wavelength']}nm"
|
|
for laser in configuration["configuration"]["microscopes"][microscope_name][
|
|
"laser"
|
|
]
|
|
]
|
|
filterwheels = list(
|
|
configuration["configuration"]["microscopes"][microscope_name][
|
|
"filter_wheel"
|
|
][0]["available_filters"].keys()
|
|
)
|
|
config.update_config_dict(
|
|
self.manager,
|
|
experiment["MicroscopeState"]["channels"],
|
|
"channel_2",
|
|
{
|
|
"is_selected": 1,
|
|
"laser": "48nm",
|
|
"laser_index": -1,
|
|
"filter_wheel_0": "nonexsit_filter_***",
|
|
"filter_position_0": 1,
|
|
"camera_exposure_time": -200.0,
|
|
"laser_power": "a",
|
|
"interval_time": -3,
|
|
"defocus": "a",
|
|
},
|
|
)
|
|
expected_value = {
|
|
"is_selected": False,
|
|
"laser": lasers[0],
|
|
"laser_index": 0,
|
|
"filter_wheel_0": filterwheels[0],
|
|
"filter_position_0": 0,
|
|
"camera_exposure_time": 200.0,
|
|
"laser_power": 20.0,
|
|
"interval_time": 0.0,
|
|
"defocus": 0.0,
|
|
}
|
|
config.verify_experiment_config(self.manager, configuration)
|
|
assert type(experiment["MicroscopeState"]["channels"]) is DictProxy
|
|
assert "channel_2" in experiment["MicroscopeState"]["channels"].keys()
|
|
for k in expected_value:
|
|
assert (
|
|
experiment["MicroscopeState"]["channels"]["channel_2"][k]
|
|
== expected_value[k]
|
|
)
|
|
|
|
config.update_config_dict(
|
|
self.manager,
|
|
experiment["MicroscopeState"]["channels"],
|
|
"channel_2",
|
|
{
|
|
"is_selected": 1,
|
|
"laser": lasers[1],
|
|
"laser_index": 3,
|
|
"filter_wheel_0": filterwheels[2],
|
|
"filter_position_0": 1,
|
|
"camera_exposure_time": -200.0,
|
|
"laser_power": "a",
|
|
"interval_time": -3,
|
|
"defocus": "a",
|
|
},
|
|
)
|
|
expected_value = {
|
|
"is_selected": False,
|
|
"laser": lasers[1],
|
|
"laser_index": 1,
|
|
"filter_wheel_0": filterwheels[2],
|
|
"filter_position_0": 2,
|
|
"camera_exposure_time": 200.0,
|
|
"laser_power": 20.0,
|
|
"interval_time": 0.0,
|
|
"defocus": 0.0,
|
|
}
|
|
config.verify_experiment_config(self.manager, configuration)
|
|
assert type(experiment["MicroscopeState"]["channels"]) is DictProxy
|
|
assert "channel_2" in experiment["MicroscopeState"]["channels"].keys()
|
|
for k in expected_value:
|
|
assert (
|
|
experiment["MicroscopeState"]["channels"]["channel_2"][k]
|
|
== expected_value[k]
|
|
)
|
|
|
|
def select_random_entries_from_list(self, parameter_list):
|
|
n = random.randint(1, len(parameter_list))
|
|
return random.choices(parameter_list, k=n)
|
|
|
|
def delete_random_entries_from_dict(self, parameter_list, parameter_dict):
|
|
n = random.randint(1, len(parameter_list))
|
|
deleted_parameters = random.choices(parameter_list, k=n)
|
|
for k in deleted_parameters:
|
|
if k in parameter_dict.keys():
|
|
del parameter_dict[k]
|
|
return deleted_parameters
|
|
|
|
def test_load_empty_multi_positions(self):
|
|
positions_file_path = os.path.join(self.test_root, "multi_positions.yml")
|
|
with open(positions_file_path, "w") as f:
|
|
f.write("")
|
|
positions = load_yaml_file(positions_file_path)
|
|
new_positions = config.verify_positions_config(positions)
|
|
assert isinstance(new_positions, list)
|
|
assert len(new_positions) == 0
|
|
|
|
def test_load_multi_positions_with_corrupted_values(self):
|
|
positions = [
|
|
[1, 2, 3],
|
|
["a", "b", "c", 1, 2],
|
|
[
|
|
10,
|
|
"a",
|
|
30,
|
|
40,
|
|
],
|
|
]
|
|
new_positions = config.verify_positions_config(positions)
|
|
assert isinstance(new_positions, list)
|
|
assert len(new_positions) == 1
|
|
|
|
positions = [
|
|
[1, 2, 3],
|
|
["a", "b", "c", 1, 2],
|
|
[1, 2, 3, 4, 5],
|
|
[
|
|
10,
|
|
"a",
|
|
30,
|
|
40,
|
|
],
|
|
]
|
|
new_positions = config.verify_positions_config(positions)
|
|
assert isinstance(new_positions, list)
|
|
assert len(new_positions) == 2
|
|
|
|
positions = [
|
|
[1, 2, 3],
|
|
["a", "b", "c", 1, 2],
|
|
[1, 2, 3, 4, 5],
|
|
[
|
|
10,
|
|
"a",
|
|
30,
|
|
40,
|
|
],
|
|
[1, 2, 3, 4, 5, 6],
|
|
]
|
|
new_positions = config.verify_positions_config(positions)
|
|
assert isinstance(new_positions, list)
|
|
assert len(new_positions) == 3
|