Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
RWTH-EBC
GitHub Repository: RWTH-EBC/ebcpy
Path: blob/master/tests/test_utils.py
505 views
1
"""Test-module for all classes inside
2
ebcpy.utils."""
3
import shutil
4
import sys
5
import unittest
6
import os
7
from pathlib import Path
8
import zipfile
9
import numpy as np
10
import pandas as pd
11
import scipy.io as spio
12
from ebcpy import load_time_series_data
13
from ebcpy.utils import setup_logger, conversion, statistics_analyzer, reproduction, get_names
14
15
16
class TestConversion(unittest.TestCase):
17
"""Test-class for preprocessing."""
18
19
def setUp(self):
20
"""Called before every test.
21
Used to setup relevant paths and APIs etc."""
22
self.example_dir = Path(__file__).parent.joinpath("data")
23
self.example_data_hdf_path = self.example_dir.joinpath("example_data.csv")
24
self.tsd = load_time_series_data(self.example_data_hdf_path, sep=";")
25
self.columns = ["sine.freqHz / Hz"]
26
27
def test_conversion_tsd_to_mat(self):
28
"""Test function conversion.convert_tsd_to_modelica_mat().
29
For an example, see the doctest in the function."""
30
# First convert the file
31
save_path = self.example_dir.joinpath("example_data_converted.mat")
32
# Test both conversion with specification of columns and without passing the names.
33
for col in [self.columns, None]:
34
filepath_mat = conversion.convert_tsd_to_modelica_mat(
35
tsd=self.tsd,
36
save_path_file=save_path,
37
columns=col)
38
# Check if converted file exists
39
self.assertTrue(os.path.isfile(filepath_mat))
40
# Check if converted filepath is provided filepath
41
self.assertEqual(filepath_mat, save_path)
42
# Now check if the created mat-file can be used.
43
self.assertIsInstance(spio.loadmat(save_path), dict)
44
# Remove converted file again
45
os.remove(save_path)
46
47
with self.assertRaises(ValueError):
48
conversion.convert_tsd_to_modelica_mat(
49
tsd=self.tsd,
50
save_path_file="not_a_mat_file.txt",
51
columns=col)
52
53
def test_conversion_tsd_to_modelica_txt(self):
54
"""Test function conversion.convert_tsd_to_modelica_txt().
55
For an example, see the doctest in the function."""
56
for col, with_tag in zip([self.columns, None], [True, False]):
57
# Check if successfully converted
58
filepath_txt = conversion.convert_tsd_to_modelica_txt(
59
tsd=self.tsd,
60
save_path_file=Path("some_text_data.txt"),
61
table_name="dummy",
62
columns=col,
63
with_tag=with_tag
64
)
65
# Check if converted file exists
66
self.assertTrue(os.path.isfile(filepath_txt))
67
# Check if converted filepath is provided filepath
68
self.assertTrue(filepath_txt.suffix == ".txt")
69
# Remove converted file again
70
os.remove(filepath_txt)
71
for with_tag in [True, False]:
72
# Test case for df with no tags
73
filepath_txt = conversion.convert_tsd_to_modelica_txt(
74
tsd=self.tsd,
75
save_path_file=Path("some_text_data.txt"),
76
table_name="dummy",
77
columns=self.columns[0],
78
with_tag=with_tag
79
)
80
# Check if converted file exists
81
self.assertTrue(os.path.isfile(filepath_txt))
82
# Check if converted filepath is provided filepath
83
self.assertTrue(filepath_txt.suffix == ".txt")
84
# Check if header matches:
85
with open(filepath_txt, "r") as file:
86
header_line = file.readlines()[2]
87
self.assertEqual(header_line, "#time_in_s\tsine.freqHz / Hz\n")
88
89
# Remove converted file again
90
os.remove(filepath_txt)
91
92
with self.assertRaises(ValueError):
93
conversion.convert_tsd_to_modelica_txt(
94
tsd=self.tsd,
95
save_path_file="not_a_txt.mat",
96
table_name="dummy",
97
columns=self.columns[0])
98
99
def test_conversion_tsd_to_clustering_txt(self):
100
"""Test function conversion.convert_tsd_to_clustering_txt().
101
For an example, see the doctest in the function."""
102
# First convert the file
103
save_path = self.example_dir.joinpath("example_data_converted.txt")
104
# Test both conversion with specification of columns and without passing the names.
105
for col in [self.columns, None]:
106
filepath_txt = conversion.convert_tsd_to_clustering_txt(
107
tsd=self.tsd,
108
save_path_file=save_path,
109
columns=col)
110
# Check if converted file exists
111
self.assertTrue(os.path.isfile(filepath_txt))
112
# Check if converted filepath is provided filepath
113
self.assertEqual(filepath_txt, save_path)
114
# Remove converted file again
115
os.remove(save_path)
116
117
def test_convert_subset(self):
118
"""Test _convert_to_df_subset function"""
119
df = pd.DataFrame({"val": np.random.rand(100)})
120
for columns in [[], "val"]:
121
df_out, headers = conversion._convert_to_subset(
122
df=df,
123
columns=[],
124
offset=0)
125
self.assertIsInstance(df_out, pd.DataFrame)
126
self.assertEqual(len(headers), 2)
127
# Try with NaN
128
df = pd.DataFrame({"val": np.random.rand(100)})
129
df.loc[2, "val"] = np.nan
130
with self.assertRaises(ValueError):
131
conversion._convert_to_subset(
132
df=df,
133
columns=[],
134
offset=0)
135
with self.assertRaises(IndexError):
136
df = pd.DataFrame({"val": 5}, index=["string"])
137
conversion._convert_to_subset(
138
df=df,
139
columns=[], offset=0
140
)
141
142
143
class TestStatisticsAnalyzer(unittest.TestCase):
144
"""Test-class for the StatisticsAnalyzer-Class"""
145
146
def setUp(self):
147
"""Called before every test.
148
Used to setup relevant paths and APIs etc."""
149
self.example_dir = Path(__file__).parent.joinpath("data")
150
self.meas_ex = np.random.rand(1000)
151
self.sim_ex = np.random.rand(1000) * 10
152
153
def test_calc(self):
154
"""Test class StatisticsAnalyzer"""
155
sup_methods = ["mae", "r2", "mse", "rmse", "cvrmse", "nrmse"]
156
for method in sup_methods:
157
stat_analyzer = statistics_analyzer.StatisticsAnalyzer(method)
158
self.assertIsInstance(stat_analyzer.calc(self.meas_ex, self.sim_ex),
159
float)
160
with self.assertRaises(ValueError):
161
stat_analyzer = statistics_analyzer.StatisticsAnalyzer("not_supported_method")
162
# Test factor:
163
stat_analyzer_min = statistics_analyzer.StatisticsAnalyzer("R2")
164
stat_analyzer_max = statistics_analyzer.StatisticsAnalyzer("R2", for_minimization=False)
165
self.assertEqual(-1 * stat_analyzer_min.calc(self.meas_ex, self.sim_ex),
166
stat_analyzer_max.calc(self.meas_ex, self.sim_ex))
167
168
def test_calc_rmse(self):
169
"""Test static function calc_rmse"""
170
stat_analyzer = statistics_analyzer.StatisticsAnalyzer
171
self.assertIsInstance(stat_analyzer.calc_rmse(self.meas_ex, self.sim_ex),
172
float)
173
174
def test_calc_mse(self):
175
"""Test static function calc_mse"""
176
stat_analyzer = statistics_analyzer.StatisticsAnalyzer
177
self.assertIsInstance(stat_analyzer.calc_mse(self.meas_ex, self.sim_ex),
178
float)
179
180
def test_calc_mae(self):
181
"""Test static function calc_mae"""
182
stat_analyzer = statistics_analyzer.StatisticsAnalyzer
183
self.assertIsInstance(stat_analyzer.calc_mae(self.meas_ex, self.sim_ex),
184
float)
185
186
def test_calc_nrmse(self):
187
"""Test static function calc_nrmse"""
188
stat_analyzer = statistics_analyzer.StatisticsAnalyzer
189
self.assertIsInstance(stat_analyzer.calc_nrmse(self.meas_ex, self.sim_ex),
190
float)
191
with self.assertRaises(ValueError):
192
custom_meas = self.meas_ex / self.meas_ex
193
stat_analyzer.calc_nrmse(custom_meas, self.sim_ex)
194
195
def test_calc_cvrmse(self):
196
"""Test static function calc_cvrmse"""
197
stat_analyzer = statistics_analyzer.StatisticsAnalyzer
198
self.assertIsInstance(stat_analyzer.calc_cvrmse(self.meas_ex, self.sim_ex),
199
float)
200
with self.assertRaises(ValueError):
201
custom_meas = self.meas_ex - self.meas_ex
202
stat_analyzer.calc_cvrmse(custom_meas, self.sim_ex)
203
204
def test_calc_r2(self):
205
"""Test static function calc_r2"""
206
stat_analyzer = statistics_analyzer.StatisticsAnalyzer
207
self.assertIsInstance(stat_analyzer.calc_r2(self.meas_ex, self.sim_ex),
208
float)
209
210
def test_custom_func(self):
211
"""Test custom function calc_r2"""
212
213
def my_func(x, y):
214
return x - y
215
216
stat_meas = statistics_analyzer.StatisticsAnalyzer(method=my_func)
217
self.assertEqual(stat_meas.calc(10, 10), 0)
218
self.assertEqual(stat_meas.calc(20, 10), 10)
219
with self.assertRaises(TypeError):
220
stat_meas.calc(1, 2, 3)
221
222
223
class TestLogger(unittest.TestCase):
224
"""Test-class for the logger function."""
225
226
def setUp(self):
227
"""Called before every test.
228
Used to setup relevant paths and APIs etc."""
229
self.example_dir = Path(__file__).parent.joinpath("test_logger")
230
self.logger = setup_logger(working_directory=self.example_dir,
231
name="test_logger")
232
233
def test_logging(self):
234
"""Test if logging works."""
235
example_str = "This is a test"
236
self.logger.info(example_str)
237
with open(self.logger.handlers[1].baseFilename, "r") as logfile:
238
logfile.seek(0)
239
self.assertTrue(example_str in logfile.read())
240
241
def tearDown(self):
242
"""Remove created files."""
243
self.logger.handlers[0].close()
244
try:
245
os.remove(self.logger.handlers[1].baseFilename)
246
except PermissionError:
247
pass
248
249
250
class TestReproduction(unittest.TestCase):
251
"""Test-class for the reproduction functions"""
252
253
def setUp(self):
254
"""Called before every test.
255
Used to setup relevant paths and APIs etc."""
256
self.data_dir = Path(__file__).parent.joinpath("data")
257
self.save_dir = self.data_dir.joinpath('testzone', 'reproduction_tests')
258
self.working_directory_files = []
259
260
def test_save_reproduction_archive(self):
261
os.getlogin = lambda: "test_login"
262
# test no input
263
reproduction.input = lambda _: ""
264
zip_file = reproduction.save_reproduction_archive()
265
# for tearDown of the files which will be saved in working_directory when no path is given
266
self.working_directory_files.append(zip_file)
267
file = Path(sys.modules['__main__'].__file__).absolute().name.replace(".py", "")
268
logger_name = f"Study_log_{file}.txt"
269
self.working_directory_files.append(logger_name)
270
self.assertTrue(zipfile.is_zipfile(zip_file))
271
# create a file to remove with CopyFile but leave it open for executing except block
272
f = open(self.data_dir.joinpath('remove.txt'), 'w')
273
f.write('test file to remove')
274
f.close()
275
# test different correct inputs
276
test_files = [
277
'not a file string',
278
str(self.data_dir.joinpath('example_data.csv')),
279
reproduction.CopyFile(
280
filename='example_data_copy_file.csv',
281
sourcepath=self.data_dir.joinpath('example_data.csv'),
282
remove=False),
283
reproduction.CopyFile(
284
filename="test_remove_file.txt",
285
sourcepath=self.data_dir.joinpath('remove.txt'),
286
remove=True
287
)
288
]
289
zip_file = reproduction.save_reproduction_archive(
290
title="Test_correct_inputs",
291
path=self.save_dir,
292
log_message="Test study correct inputs",
293
files=test_files
294
)
295
self.assertTrue(zipfile.is_zipfile(zip_file))
296
# test false inputs
297
false_test_files = [1]
298
with self.assertRaises(TypeError):
299
reproduction.save_reproduction_archive(
300
title="Test_false_inputs",
301
path=self.save_dir,
302
log_message="Test study false inputs",
303
files=false_test_files
304
)
305
306
def test_creat_copy_files_from_dir(self):
307
files = reproduction.creat_copy_files_from_dir(
308
foldername='test_dir',
309
sourcepath=self.data_dir,
310
remove=False
311
)
312
for copy_file in files:
313
self.assertIsInstance(copy_file, reproduction.CopyFile)
314
315
def tearDown(self) -> None:
316
"""Delete saved files"""
317
try:
318
shutil.rmtree(self.save_dir, ignore_errors=True)
319
for file in self.working_directory_files:
320
os.remove(file)
321
except Exception:
322
pass
323
324
325
class TestGetNames(unittest.TestCase):
326
def test_matches(self):
327
"""
328
Test various literal and wildcard patterns, including brackets and multiple '*' usage.
329
"""
330
test_cases = [
331
# literal single match
332
(['alpha', 'beta', 'gamma'], 'beta', ['beta']),
333
# literal list of matches
334
(['alpha', 'beta', 'gamma', 'delta'], ['alpha', 'delta'], ['alpha', 'delta']),
335
# single '*' wildcard
336
(['wall1.T', 'wall2.T', 'floor.T'], 'wall*.T', ['wall1.T', 'wall2.T']),
337
# wildcard inside brackets
338
(['wall[1].T', 'wall[2].T', 'wallX.T'], 'wall[*].T', ['wall[1].T', 'wall[2].T']),
339
# two '*' wildcards
340
(['a1b2', 'axby', 'ab'], 'a*b*', ['a1b2', 'axby', 'ab']),
341
# mix of wildcard and literal in list
342
(['a1', 'a2', 'b1', 'b2'], ['a*', 'b1'], ['a1', 'a2', 'b1']),
343
# order preservation test
344
(['first', 'second', 'third'], ['third', 'first'], ['first', 'third']),
345
]
346
for all_names, patterns, expected in test_cases:
347
with self.subTest(patterns=patterns):
348
result = get_names(all_names, patterns)
349
self.assertEqual(result, expected)
350
351
def test_errors(self):
352
"""
353
Patterns or literals that match nothing should raise a warning.
354
"""
355
with self.assertWarns(UserWarning):
356
get_names(['alpha', 'beta'], 'unknown')
357
with self.assertWarns(UserWarning):
358
get_names(['x1', 'x2'], 'y*')
359
360
361
if __name__ == "__main__":
362
unittest.main()
363
364