# 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