Skip to content

Commit 60cf040

Browse files
authored
Merge pull request #402 from Limmen/experiment
add test_experiment_util
2 parents 6440523 + 6904cca commit 60cf040

File tree

1 file changed

+334
-0
lines changed

1 file changed

+334
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,334 @@
1+
from unittest.mock import patch, MagicMock
2+
from csle_common.util.experiment_util import ExperimentUtil
3+
import csle_common.constants.constants as constants
4+
import tempfile
5+
import os
6+
import shutil
7+
import sys
8+
import numpy as np
9+
import random
10+
import torch
11+
12+
13+
class TestExperimentUtilSuite:
14+
"""
15+
Test suite for experiment util
16+
"""
17+
18+
def test_get_subdir(self) -> None:
19+
"""
20+
Test the function that constructs the subdir string from a given results dir, subdir, and random seed
21+
22+
:return: None
23+
"""
24+
output_dir = "output"
25+
results_dir = "results"
26+
subdir = "logs"
27+
seed = 10
28+
29+
expected = "output/results/logs/10/"
30+
result = ExperimentUtil.get_subdir(output_dir=output_dir, results_dir=results_dir, subdir=subdir, seed=seed)
31+
32+
assert expected == result
33+
34+
def test_create_artifact_dirs(self) -> None:
35+
"""
36+
Test the method that creates artifact directories if they do not already exist
37+
38+
:return: None
39+
"""
40+
temp_dir = tempfile.mkdtemp()
41+
try:
42+
random_seed = 10
43+
ExperimentUtil.create_artifact_dirs(output_dir=temp_dir, random_seed=random_seed)
44+
expected_dirs = [
45+
constants.EXPERIMENT.LOG_DIR,
46+
constants.EXPERIMENT.PLOTS_DIR,
47+
constants.EXPERIMENT.DATA_DIR,
48+
constants.EXPERIMENT.HYPERPARAMETERS_DIR,
49+
constants.EXPERIMENT.GIFS_DIR,
50+
constants.EXPERIMENT.TENSORBOARD_DIR,
51+
constants.EXPERIMENT.ENV_DATA_DIR,
52+
]
53+
for subdir in expected_dirs:
54+
subdir_path = os.path.join(temp_dir, constants.EXPERIMENT.RESULTS_DIR, subdir, str(random_seed))
55+
assert os.path.exists(subdir_path), f"Directory {subdir_path} does not exist"
56+
57+
finally:
58+
shutil.rmtree(temp_dir)
59+
60+
def test_setup_experiment_logger(self, tmpdir) -> None:
61+
"""
62+
Test the function that configures the logger for writing log-data of training
63+
64+
:param tmpdir: temporary directory
65+
66+
:return: None
67+
"""
68+
logger_name = "test_logger"
69+
logdir = str(tmpdir.mkdir("logs"))
70+
time_str = "20240101-1234"
71+
72+
logger = ExperimentUtil.setup_experiment_logger(name=logger_name, logdir=logdir, time_str=time_str)
73+
assert logger.name == logger_name
74+
75+
@patch("io.open")
76+
def test_write_emulation_config_file(self, mock_open) -> None:
77+
"""
78+
Test the function that writes a config object to a config file
79+
80+
:param mock_open: mock_open
81+
82+
:return: None
83+
"""
84+
emulation_env_config = MagicMock()
85+
emulation_env_config.to_dict.return_value = {"key1": "value1", "key2": "value2"}
86+
ExperimentUtil.write_emulation_config_file(emulation_env_config=emulation_env_config, path="path")
87+
mock_open.assert_called()
88+
89+
@patch("io.open")
90+
def test_write_simulation_config_file(self, mock_open) -> None:
91+
"""
92+
Test the function that writes a config object to a config file
93+
94+
:param mock_open: mock_open
95+
96+
:return: None
97+
"""
98+
simulation_env_config = MagicMock()
99+
simulation_env_config.to_dict.return_value = {"key1": "value1", "key2": "value2"}
100+
ExperimentUtil.write_simulation_config_file(simulation_env_config=simulation_env_config, path="path")
101+
mock_open.assert_called()
102+
103+
@patch("builtins.open")
104+
def test_read_env_picture(self, mock_open) -> None:
105+
"""
106+
Mock the method that reads the environment topology picture from a file
107+
108+
:param mock_open: mock_open
109+
110+
:return: None
111+
"""
112+
mock_open.return_value.__enter__.return_value.read.return_value = b"fake_image_data"
113+
image_data = ExperimentUtil.read_env_picture("fake_path")
114+
assert image_data == b"fake_image_data"
115+
116+
@patch.object(sys, "argv", ["file_name"])
117+
def test_parse_args(self) -> None:
118+
"""
119+
Test the method that parses the commandline arguments with argparse
120+
121+
:return: None
122+
"""
123+
default_config_path = "default/path/to/config.json"
124+
args = ExperimentUtil.parse_args(default_config_path)
125+
assert args.configpath == default_config_path
126+
127+
@patch.object(sys, "argv", ["/fake/path/to/script.py"])
128+
def test_get_script_path(self) -> None:
129+
"""
130+
Test the method that returns the script path
131+
132+
:return: None
133+
"""
134+
expected_path = "/fake/path/to"
135+
script_path = ExperimentUtil.get_script_path()
136+
assert script_path == expected_path
137+
138+
@patch("csle_common.util.experiment_util.ExperimentUtil.get_script_path")
139+
def test_default_output_dir(self, mock_get_script_path) -> None:
140+
"""
141+
Test the method that returns the default output dir
142+
143+
:param mock_get_script_path: mock_get_script_path
144+
145+
:return: None
146+
"""
147+
mock_get_script_path.return_value = "path"
148+
expected_path = "path"
149+
output_dir = ExperimentUtil.default_output_dir()
150+
assert output_dir == expected_path
151+
152+
@patch("csle_common.util.experiment_util.ExperimentUtil.default_output_dir")
153+
def test_default_emulation_config_path(self, mock_default_output_dir) -> None:
154+
"""
155+
Test the method that returns the default path to emulation config file
156+
157+
:param mock_default_output_dir: mock_default_output_dir
158+
159+
:return: None
160+
"""
161+
mock_default_output_dir.return_value = "/fake/default/output/dir"
162+
constants.COMMANDS.DOT_DELIM = "."
163+
constants.DOCKER.EMULATION_ENV_CFG_PATH = "fake_emulation_config.json"
164+
expected_path = "/fake/default/output/dir/.fake_emulation_config.json"
165+
config_path = ExperimentUtil.default_emulation_config_path()
166+
assert config_path == expected_path
167+
168+
@patch("csle_common.util.experiment_util.ExperimentUtil.default_output_dir")
169+
def test_default_simulation_config_path(self, mock_default_output_dir) -> None:
170+
"""
171+
Test the method that returns the default path to simulation config file
172+
173+
:param mock_default_output_dir: mock_default_output_dir
174+
175+
:return: None
176+
"""
177+
mock_default_output_dir.return_value = "/fake/default/output/dir"
178+
constants.COMMANDS.DOT_DELIM = "."
179+
constants.SIMULATION.SIMULATION_ENV_CFG_PATH = "fake_simulation_config.json"
180+
expected_path = "/fake/default/output/dir/.fake_simulation_config.json"
181+
config_path = ExperimentUtil.default_simulation_config_path()
182+
assert config_path == expected_path
183+
184+
@patch("csle_common.util.experiment_util.ExperimentUtil.default_output_dir")
185+
def test_default_emulation_picture_path(self, mock_default_output_dir) -> None:
186+
"""
187+
Test the method that returns the default path to emulation picture file
188+
189+
:param mock_default_output_dir: mock_default_output_dir
190+
191+
:return: None
192+
"""
193+
mock_default_output_dir.return_value = "/fake/default/output/dir"
194+
constants.COMMANDS.DOT_DELIM = "."
195+
constants.DOCKER.EMULATION_ENV_IMAGE = "fake_emulation_picture.img"
196+
expected_path = "/fake/default/output/dir/.fake_emulation_picture.img"
197+
picture_path = ExperimentUtil.default_emulation_picture_path()
198+
assert picture_path == expected_path
199+
200+
@patch("csle_common.util.experiment_util.ExperimentUtil.default_output_dir")
201+
def test_default_simulation_picture_path(self, mock_default_output_dir) -> None:
202+
"""
203+
Test the method that returns the default path to simulation picture file
204+
205+
:param mock_default_output_dir: mock_default_output_dir
206+
207+
:return: None
208+
"""
209+
mock_default_output_dir.return_value = "/fake/default/output/dir"
210+
constants.COMMANDS.DOT_DELIM = "."
211+
constants.DOCKER.SIMULATION_ENV_IMAGE = "fake_simulation_picture.img"
212+
expected_path = "/fake/default/output/dir/.fake_simulation_picture.img"
213+
picture_path = ExperimentUtil.default_simulation_picture_path()
214+
assert picture_path == expected_path
215+
216+
@patch("csle_common.util.experiment_util.ExperimentUtil.default_output_dir")
217+
def test_default_containers_folders_path(self, mock_default_output_dir) -> None:
218+
"""
219+
Test the method that returns the default path to container folders
220+
221+
:param mock_default_output_dir: mock_default_output_dir
222+
223+
:return: None
224+
"""
225+
mock_default_output_dir.return_value = "/fake/default/output/dir"
226+
constants.COMMANDS.DOT_DELIM = "."
227+
constants.COMMANDS.SLASH_DELIM = "/"
228+
constants.DOCKER.CONTAINERS_DIR = "container"
229+
expected_path = "/fake/default/output/dir/./container"
230+
container_path = ExperimentUtil.default_containers_folders_path()
231+
assert container_path == expected_path
232+
233+
@patch("csle_common.util.experiment_util.ExperimentUtil.default_output_dir")
234+
def default_makefile_template_path(self, mock_default_output_dir) -> None:
235+
"""
236+
Test the method that returns the default path to makefile tempalte
237+
238+
:param mock_default_output_dir: mock_default_output_dir
239+
240+
:return: None
241+
"""
242+
mock_default_output_dir.return_value = "/fake/default/output/dir"
243+
constants.COMMANDS.DOT_DELIM = "."
244+
constants.COMMANDS.SLASH_DELIM = "/"
245+
constants.DOCKER.MAKEFILE_TEMPLATE = "makefile"
246+
expected_path = "/fake/default/output/dir/./makefile"
247+
config_path = ExperimentUtil.default_makefile_template_path()
248+
assert config_path == expected_path
249+
250+
@patch("csle_common.util.experiment_util.ExperimentUtil.default_output_dir")
251+
def default_makefile_path(self, mock_default_output_dir) -> None:
252+
"""
253+
Test the method that returns the default path to makefile tempalte
254+
255+
:param mock_default_output_dir: mock_default_output_dir
256+
257+
:return: None
258+
"""
259+
mock_default_output_dir.return_value = "/fake/default/output/dir"
260+
constants.COMMANDS.DOT_DELIM = "."
261+
constants.COMMANDS.SLASH_DELIM = "/"
262+
constants.DOCKER.MAKEFILE = "makefile"
263+
expected_path = "/fake/default/output/dir/./makefile"
264+
config_path = ExperimentUtil.default_makefile_path()
265+
assert config_path == expected_path
266+
267+
def test_running_average_basic(self) -> None:
268+
"""
269+
Test the method that used to compute the running average of the last N elements of a vector x
270+
271+
:return: None
272+
"""
273+
x = np.array([1, 2])
274+
N = 3
275+
result = ExperimentUtil.running_average(x, N)
276+
expected_result = 1
277+
assert result == expected_result
278+
279+
def test_running_average_list(self) -> None:
280+
"""
281+
Test the method that used to compute the running average of the last N elements of a vector x
282+
283+
:return: None
284+
"""
285+
x = [1, 2, 3, 4, 5]
286+
N = 3
287+
result = ExperimentUtil.running_average_list(x, N)
288+
expected_result = [1, 2, 2, 3, 3]
289+
assert np.allclose(result, expected_result)
290+
291+
def test_mean_confidence_interval(self) -> None:
292+
"""
293+
Test the method that compute confidence intervals
294+
295+
:return: None
296+
"""
297+
data = [1, 2, 3, 4, 5]
298+
m, _ = ExperimentUtil.mean_confidence_interval(data, 0.95)
299+
assert np.isclose(m, 3.0)
300+
301+
def test_set_seed(self) -> None:
302+
"""
303+
Test the method that sets the random seed
304+
305+
:return: None
306+
"""
307+
seed = 42
308+
309+
ExperimentUtil.set_seed(seed)
310+
random_numbers1 = [random.random() for _ in range(5)]
311+
np_random_numbers1 = np.random.rand(5)
312+
torch_random_numbers1 = torch.rand(5)
313+
314+
ExperimentUtil.set_seed(seed)
315+
random_numbers2 = [random.random() for _ in range(5)]
316+
np_random_numbers2 = np.random.rand(5)
317+
torch_random_numbers2 = torch.rand(5)
318+
319+
assert random_numbers1 == random_numbers2
320+
assert np.allclose(np_random_numbers1, np_random_numbers2)
321+
assert torch.equal(torch_random_numbers1, torch_random_numbers2)
322+
323+
def test_regress_lists(self) -> None:
324+
"""
325+
Test the method that regress sublists.
326+
327+
:return: None
328+
"""
329+
lists = [[1.0, 2.0, 3.0], [4.0, 5.0, 6.0], [7.0, 8.0, 9.0]]
330+
result = ExperimentUtil.regress_lists(lists)
331+
expected_result = [[1.0, 2.0, 3.0], [4.0, 5.0, 6.0], [7.0, 8.0, 9.0]]
332+
assert np.allclose(result[0], expected_result[0])
333+
assert np.allclose(result[1], expected_result[1])
334+
assert np.allclose(result[2], expected_result[2])

0 commit comments

Comments
 (0)