Skip to content

Commit 716fa93

Browse files
committed
Added unit test for'get_machine_config' which tests 'machine_config_from_file' by proxy as well
1 parent 64636c5 commit 716fa93

File tree

1 file changed

+348
-0
lines changed

1 file changed

+348
-0
lines changed

tests/util/test_config.py

Lines changed: 348 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,348 @@
1+
from pathlib import Path
2+
from typing import Any
3+
4+
import pytest
5+
import yaml
6+
from pytest_mock import MockerFixture
7+
8+
from murfey.util.config import Settings, get_machine_config
9+
10+
11+
@pytest.fixture
12+
def mock_general_config():
13+
# Most extra keys go in this category
14+
return {
15+
"pkg_2": {
16+
"url": "https://some-url.some.org",
17+
"token": "pneumonoultrasmicroscopicsilicovolcanoconiosis",
18+
}
19+
}
20+
21+
22+
@pytest.fixture
23+
def mock_tem_shared_config():
24+
return {
25+
# Hardware and software
26+
"acquisition_software": ["epu", "tomo", "serialem"],
27+
"software_versions": {"tomo": "5.12"},
28+
"data_required_substrings": {
29+
"epu": {
30+
".mrc": ["fractions", "Fractions"],
31+
".tiff": ["fractions", "Fractions"],
32+
".eer": ["EER"],
33+
},
34+
"tomo": {
35+
".mrc": ["fractions", "Fractions"],
36+
".tiff": ["fractions", "Fractions"],
37+
".eer": ["EER"],
38+
},
39+
},
40+
# Client directory setup
41+
"analyse_created_directories": ["atlas"],
42+
"gain_reference_directory": "C:/ProgramData/Gatan/Reference Images/",
43+
# Data transfer keys
44+
"data_transfer_enabled": True,
45+
"substrings_blacklist": {
46+
"directories": ["some_str"],
47+
"files": ["some_str"],
48+
},
49+
"rsync_module": "rsync",
50+
"allow_removal": True,
51+
"upstream_data_directories": {
52+
"upstream_instrument": "/path/to/upstream_instrument",
53+
},
54+
"upstream_data_download_directory": "/path/to/download/directory",
55+
"upstream_data_search_strings": {
56+
"upstream_instrument": ["some_string"],
57+
},
58+
# Data processing keys
59+
"processing_enabled": True,
60+
"gain_directory_name": "some_directory",
61+
"processed_directory_name": "some_directory",
62+
"processed_extra_directory": "some_directory",
63+
"recipes": {
64+
"recipe_1": "recipe_1",
65+
"recipe_2": "recipe_2",
66+
},
67+
"default_model": "some_file",
68+
"external_executables": {
69+
"app_1": "/path/to/app_1",
70+
"app_2": "/path/to/app_2",
71+
"app_3": "/path/to/app_3",
72+
},
73+
"external_executables_eer": {
74+
"app_1": "/path/to/app_1",
75+
"app_2": "/path/to/app_2",
76+
"app_3": "/path/to/app_3",
77+
},
78+
"external_environment": {
79+
"ENV_1": "/path/to/env_1",
80+
"ENV_2": "/path/to/env_2",
81+
},
82+
"plugin_packages": {
83+
"pkg_1": "/path/to/pkg_1",
84+
"pkg_2": "/path/to/pkg_2",
85+
},
86+
# Extra keys
87+
"pkg_1": {
88+
"file_path": "",
89+
"command": [
90+
"/path/to/executable",
91+
"--some_arg",
92+
"-a",
93+
"./path/to/file",
94+
],
95+
"step_size": 100,
96+
},
97+
}
98+
99+
100+
@pytest.fixture
101+
def mock_instrument_config():
102+
return {
103+
# Extra key to point to hierarchical dictionary to use
104+
"instrument_type": "tem",
105+
# General information
106+
"display_name": "Some TEM",
107+
"image_path": "/path/to/tem.jpg",
108+
# Hardware and software
109+
"camera": "Some camera",
110+
"superres": True,
111+
"calibrations": {
112+
"magnification": {
113+
100: 0.1,
114+
200: 0.05,
115+
400: 0.025,
116+
},
117+
},
118+
# Client directory setup
119+
"data_directories": ["C:"],
120+
# Data transfer keys
121+
"rsync_basepath": "/path/to/data",
122+
"rsync_url": "http://123.45.678.90:8000",
123+
# Server and network keys
124+
"security_configuration_path": "/path/to/security-config.yaml",
125+
"murfey_url": "https://www.murfey.com",
126+
"instrument_server_url": "http://10.123.4.5:8000",
127+
"node_creator_queue": "node_creator",
128+
# Extra keys
129+
"pkg_1": {
130+
"file_path": "/path/to/pkg_1/file.txt",
131+
},
132+
}
133+
134+
135+
@pytest.fixture
136+
def mock_hierarchical_machine_config_yaml(
137+
mock_general_config: dict[str, Any],
138+
mock_tem_shared_config: dict[str, Any],
139+
mock_instrument_config: dict[str, Any],
140+
tmp_path: Path,
141+
):
142+
# Create machine config (with all currently supported keys) for the instrument
143+
hierarchical_config = {
144+
"general": mock_general_config,
145+
"tem": mock_tem_shared_config,
146+
"m01": mock_instrument_config,
147+
"m02": mock_instrument_config,
148+
}
149+
config_file = tmp_path / "config" / "murfey-machine-config-hierarchical.yaml"
150+
config_file.parent.mkdir(parents=True, exist_ok=True)
151+
with open(config_file, "w") as file:
152+
yaml.safe_dump(hierarchical_config, file, indent=2)
153+
return config_file
154+
155+
156+
@pytest.fixture
157+
def mock_standard_machine_config_yaml(
158+
mock_general_config: dict[str, Any],
159+
mock_tem_shared_config: dict[str, Any],
160+
mock_instrument_config: dict[str, Any],
161+
tmp_path: Path,
162+
):
163+
# Compile the different dictionaries into one dictionary for the instrument
164+
machine_config = {
165+
key: value
166+
for config in (
167+
mock_general_config,
168+
mock_tem_shared_config,
169+
mock_instrument_config,
170+
)
171+
for key, value in config.items()
172+
}
173+
174+
# Correct for nested dicts that would have been partially overwritten
175+
machine_config["pkg_1"] = (
176+
{
177+
"file_path": "/path/to/pkg_1/file.txt",
178+
"command": [
179+
"/path/to/executable",
180+
"--some_arg",
181+
"-a",
182+
"./path/to/file",
183+
],
184+
"step_size": 100,
185+
},
186+
)
187+
188+
master_config = {
189+
"m01": machine_config,
190+
"m02": machine_config,
191+
}
192+
config_file = tmp_path / "config" / "murfey-machine-config-standard.yaml"
193+
config_file.parent.mkdir(parents=True, exist_ok=True)
194+
with open(config_file, "w") as file:
195+
yaml.safe_dump(master_config, file, indent=2)
196+
return config_file
197+
198+
199+
get_machine_config_test_matrix: tuple[tuple[str, list[str]], ...] = (
200+
# Config to test | Instrument names to pass to function
201+
("hierarchical", ["", "m01", "m02"]),
202+
("standard", ["", "m01", "m02"]),
203+
)
204+
205+
206+
@pytest.mark.parametrize("test_params", get_machine_config_test_matrix)
207+
def test_get_machine_config(
208+
mocker: MockerFixture,
209+
mock_general_config: dict[str, Any],
210+
mock_tem_shared_config: dict[str, Any],
211+
mock_instrument_config: dict[str, Any],
212+
mock_hierarchical_machine_config_yaml: Path,
213+
mock_standard_machine_config_yaml: Path,
214+
test_params: tuple[str, list[str]],
215+
):
216+
# Unpack test params
217+
config_to_test, instrument_names = test_params
218+
219+
# Set up mocks
220+
mock_settings = mocker.patch("murfey.util.config.settings", spec=Settings)
221+
222+
# Run 'get_machine_config' using different instrument name parameters
223+
for i in instrument_names:
224+
# Patch the 'settings' environment variable with the YAML file to test
225+
mock_settings.murfey_machine_configuration = (
226+
str(mock_hierarchical_machine_config_yaml)
227+
if config_to_test == "hierarchical"
228+
else str(mock_standard_machine_config_yaml)
229+
)
230+
# Run the function
231+
config = get_machine_config(i)
232+
233+
# Validate that the config was loaded correctly
234+
assert config
235+
236+
# Multiple configs should be returned if instrument name was ""
237+
assert len(config) == 2 if i == "" else len(config) == 1
238+
239+
# When getting the config for individual microscopes, validate key-by-key
240+
if i != "":
241+
# General info
242+
assert config[i].display_name == mock_instrument_config["display_name"]
243+
assert config[i].image_path == Path(mock_instrument_config["image_path"])
244+
# Hardware & software
245+
assert config[i].camera == mock_instrument_config["camera"]
246+
assert config[i].superres == mock_instrument_config["superres"]
247+
assert config[i].calibrations == mock_instrument_config["calibrations"]
248+
assert (
249+
config[i].acquisition_software
250+
== mock_tem_shared_config["acquisition_software"]
251+
)
252+
assert (
253+
config[i].software_versions
254+
== mock_tem_shared_config["software_versions"]
255+
)
256+
assert (
257+
config[i].data_required_substrings
258+
== mock_tem_shared_config["data_required_substrings"]
259+
)
260+
# Client directory setup
261+
assert config[i].data_directories == [
262+
Path(p) for p in mock_instrument_config["data_directories"]
263+
]
264+
assert (
265+
config[i].analyse_created_directories
266+
== mock_tem_shared_config["analyse_created_directories"]
267+
)
268+
assert config[i].gain_reference_directory == Path(
269+
mock_tem_shared_config["gain_reference_directory"]
270+
)
271+
# Data transfer setup
272+
assert (
273+
config[i].data_transfer_enabled
274+
== mock_tem_shared_config["data_transfer_enabled"]
275+
)
276+
assert (
277+
config[i].substrings_blacklist
278+
== mock_tem_shared_config["substrings_blacklist"]
279+
)
280+
assert config[i].rsync_url == mock_instrument_config["rsync_url"]
281+
assert config[i].rsync_basepath == Path(
282+
mock_instrument_config["rsync_basepath"]
283+
)
284+
assert config[i].rsync_module == mock_tem_shared_config["rsync_module"]
285+
assert config[i].allow_removal == mock_tem_shared_config["allow_removal"]
286+
assert config[i].upstream_data_directories == {
287+
key: Path(value)
288+
for key, value in mock_tem_shared_config[
289+
"upstream_data_directories"
290+
].items()
291+
}
292+
assert config[i].upstream_data_download_directory == Path(
293+
mock_tem_shared_config["upstream_data_download_directory"]
294+
)
295+
assert (
296+
config[i].upstream_data_search_strings
297+
== mock_tem_shared_config["upstream_data_search_strings"]
298+
)
299+
# Data processing setup
300+
assert (
301+
config[i].processing_enabled
302+
== mock_tem_shared_config["processing_enabled"]
303+
)
304+
assert (
305+
config[i].gain_directory_name
306+
== mock_tem_shared_config["gain_directory_name"]
307+
)
308+
assert (
309+
config[i].processed_directory_name
310+
== mock_tem_shared_config["processed_directory_name"]
311+
)
312+
assert (
313+
config[i].processed_extra_directory
314+
== mock_tem_shared_config["processed_extra_directory"]
315+
)
316+
assert config[i].recipes == mock_tem_shared_config["recipes"]
317+
assert config[i].default_model == Path(
318+
mock_tem_shared_config["default_model"]
319+
)
320+
assert (
321+
config[i].external_executables
322+
== mock_tem_shared_config["external_executables"]
323+
)
324+
assert (
325+
config[i].external_executables_eer
326+
== mock_tem_shared_config["external_executables_eer"]
327+
)
328+
assert (
329+
config[i].external_environment
330+
== mock_tem_shared_config["external_environment"]
331+
)
332+
assert config[i].plugin_packages == {
333+
key: Path(value)
334+
for key, value in mock_tem_shared_config["plugin_packages"].items()
335+
}
336+
# Server and network setup
337+
assert config[i].security_configuration_path == Path(
338+
mock_instrument_config["security_configuration_path"]
339+
)
340+
assert config[i].murfey_url == mock_instrument_config["murfey_url"]
341+
assert (
342+
config[i].instrument_server_url
343+
== mock_instrument_config["instrument_server_url"]
344+
)
345+
assert (
346+
config[i].node_creator_queue
347+
== mock_instrument_config["node_creator_queue"]
348+
)

0 commit comments

Comments
 (0)