Coverage for test/test_io.py: 99%
299 statements
« prev ^ index » next coverage.py v7.10.6, created at 2025-09-15 20:42 -0400
« prev ^ index » next coverage.py v7.10.6, created at 2025-09-15 20:42 -0400
1import unittest
2from unittest.mock import patch
3from pathlib import Path
4import json
5import textwrap
7import pytest
8import numpy as np
9import nmrglue as ng
10import pandas as pd
12from peakipy.io import (
13 Pseudo3D,
14 Peaklist,
15 LoadData,
16 PeaklistFormat,
17 OutFmt,
18 StrucEl,
19 UnknownFormat,
20 ClustersResult,
21 get_vclist,
22)
23from peakipy.fitting import PeakLimits
24from peakipy.utils import load_config, write_config, update_config_file
27@pytest.fixture
28def test_directory():
29 return Path(__file__).parent
31@pytest.fixture
32def test_directory_protein_L(test_directory):
33 return test_directory / "test_protein_L"
35# test for read, edit, fit, check and spec scripts
36# need to actually write proper tests
37class TestBokehScript(unittest.TestCase):
38 @patch("peakipy.cli.edit.BokehScript")
39 def test_BokehScript(self, MockBokehScript):
40 args = {"<peaklist>": "hello", "<data>": "data"}
41 bokeh_plots = MockBokehScript(args)
42 self.assertIsNotNone(bokeh_plots)
45class TestCheckScript(unittest.TestCase):
46 @patch("peakipy.cli.main.check")
47 def test_main(self, MockCheck):
48 args = {"<peaklist>": "hello", "<data>": "data"}
49 check = MockCheck(args)
50 self.assertIsNotNone(check)
53class TestFitScript(unittest.TestCase):
54 @patch("peakipy.cli.main.fit")
55 def test_main(self, MockFit):
56 args = {"<peaklist>": "hello", "<data>": "data"}
57 fit = MockFit(args)
58 self.assertIsNotNone(fit)
62class TestReadScript(unittest.TestCase):
63 test_directory = "./test/"
65 @patch("peakipy.cli.main.read")
66 def test_main(self, MockRead):
67 args = {"<peaklist>": "hello", "<data>": "data"}
68 read = MockRead(args)
69 self.assertIsNotNone(read)
71 def test_read_pipe_peaklist(self):
72 args = {
73 "path": f"{self.test_directory}/test_pipe.tab",
74 "data_path": f"{self.test_directory}/test_pipe.ft2",
75 "dims": [0, 1, 2],
76 "fmt": PeaklistFormat.pipe,
77 }
78 peaklist = Peaklist(**args)
79 self.assertIsNotNone(peaklist)
80 self.assertIs(len(peaklist.df), 3)
81 # self.assertIs(peaklist.df.X_AXISf.iloc[0], 323.019)
82 self.assertIs(peaklist.fmt.value, "pipe")
83 # self.assertEqual(peaklist.df.ASS.iloc[0], "None")
84 # self.assertEqual(peaklist.df.ASS.iloc[1], "None_dummy_1")
86def test_read_custom_csv(test_directory_protein_L, capsys):
87 custom_csv_data = textwrap.dedent("""\
88 ASS,X_PPM,Y_PPM
89 test1,8.763,117.821
90 test2,8.973,122.359
91 test3,9.005,122.436
92 """)
93 custom_csv_data_path = test_directory_protein_L / "custom.csv"
94 custom_csv_data_path.write_text(custom_csv_data)
95 args = {
96 "path": custom_csv_data_path,
97 "data_path": f"{test_directory_protein_L}/test1.ft2",
98 "dims": [0, 1, 2],
99 "fmt": PeaklistFormat.csv,
100 }
101 peaklist = Peaklist(**args)
102 assert peaklist != None
103 assert peaklist.df.shape[0] == 3
104 assert peaklist.fmt.value == "csv"
107def test_read_pipe_peaklist_check_radius_too_small(test_directory, capsys):
108 args = {
109 "path": f"{test_directory}/test_pipe.tab",
110 "data_path": f"{test_directory}/test_pipe.ft2",
111 "dims": [0, 1, 2],
112 "fmt": PeaklistFormat.pipe,
113 "radii": [0.0001,0.0001],
114 }
115 peaklist = Peaklist(**args)
116 assert peaklist != None
117 assert peaklist.df.shape[0] == 3
118 assert peaklist.fmt.value == "pipe"
119 # assert peaklist.f1_radius == 0.0001
120 peaklist.f2_radius
121 assert "Warning: --x-radius-ppm" in capsys.readouterr().out
122 peaklist.f1_radius
123 assert "Warning: --y-radius-ppm" in capsys.readouterr().out
124 # check that number of points are set to 2 if invalid input radius
125 assert (peaklist.f1_radius * peaklist.pt_per_ppm_f1) == 2
126 assert (peaklist.f2_radius * peaklist.pt_per_ppm_f2) == 2
128def test_read_pipe_peaklist_check_radius_valid(test_directory, capsys):
129 args = {
130 "path": f"{test_directory}/test_pipe.tab",
131 "data_path": f"{test_directory}/test_pipe.ft2",
132 "dims": [0, 1, 2],
133 "fmt": PeaklistFormat.pipe,
134 "radii": [0.05,0.5],
135 }
136 peaklist = Peaklist(**args)
137 assert peaklist != None
138 assert peaklist.df.shape[0] == 3
139 assert peaklist.fmt.value == "pipe"
140 assert peaklist.f1_radius == 0.5
141 assert peaklist.f2_radius == 0.05
142 peaklist.f2_radius
143 assert capsys.readouterr().out == ""
144 peaklist.f1_radius
145 assert capsys.readouterr().out == ""
147def test_load_config_existing():
148 config_path = Path("test_config.json")
149 # Create a dummy existing config file
150 with open(config_path, "w") as f:
151 json.dump({"key1": "value1"}, f)
153 loaded_config = load_config(config_path)
155 assert loaded_config == {"key1": "value1"}
157 # Clean up
158 config_path.unlink()
161def test_load_config_nonexistent():
162 config_path = Path("test_config.json")
164 loaded_config = load_config(config_path)
166 assert loaded_config == {}
169def test_write_config():
170 config_path = Path("test_config.json")
171 config_kvs = {"key1": "value1", "key2": "value2"}
173 write_config(config_path, config_kvs)
175 # Check if the config file is created correctly
176 assert config_path.exists()
178 # Check if the config file content is correct
179 with open(config_path) as f:
180 created_config = json.load(f)
181 assert created_config == {"key1": "value1", "key2": "value2"}
183 # Clean up
184 config_path.unlink()
187def test_update_config_file_existing():
188 config_path = Path("test_config.json")
189 # Create a dummy existing config file
190 with open(config_path, "w") as f:
191 json.dump({"key1": "value1"}, f)
193 config_kvs = {"key2": "value2", "key3": "value3"}
194 updated_config = update_config_file(config_path, config_kvs)
196 assert updated_config == {"key1": "value1", "key2": "value2", "key3": "value3"}
198 # Clean up
199 config_path.unlink()
202def test_update_config_file_nonexistent():
203 config_path = Path("test_config.json")
204 config_kvs = {"key1": "value1", "key2": "value2"}
205 updated_config = update_config_file(config_path, config_kvs)
207 assert updated_config == {"key1": "value1", "key2": "value2"}
209 # Clean up
210 config_path.unlink()
213@pytest.fixture
214def sample_data():
215 return np.zeros((10, 10))
218@pytest.fixture
219def sample_peak():
220 peak_data = {"X_AXIS": [5], "Y_AXIS": [5], "XW": [2], "YW": [2]}
221 return pd.DataFrame(peak_data).iloc[0]
224def test_peak_limits_max_min(sample_peak, sample_data):
225 limits = PeakLimits(sample_peak, sample_data)
227 assert limits.max_x == 8
228 assert limits.max_y == 8
229 assert limits.min_x == 3
230 assert limits.min_y == 3
233def test_peak_limits_boundary(sample_data):
234 peak_data = {"X_AXIS": [8], "Y_AXIS": [8], "XW": [2], "YW": [2]}
235 peak = pd.DataFrame(peak_data).iloc[0]
236 limits = PeakLimits(peak, sample_data)
238 assert limits.max_x == 10
239 assert limits.max_y == 10
240 assert limits.min_x == 6
241 assert limits.min_y == 6
244def test_peak_limits_at_boundary(sample_data):
245 peak_data = {"X_AXIS": [0], "Y_AXIS": [0], "XW": [2], "YW": [2]}
246 peak = pd.DataFrame(peak_data).iloc[0]
247 limits = PeakLimits(peak, sample_data)
249 assert limits.max_x == 3
250 assert limits.max_y == 3
251 assert limits.min_x == 0
252 assert limits.min_y == 0
255def test_peak_limits_outside_boundary(sample_data):
256 peak_data = {"X_AXIS": [15], "Y_AXIS": [15], "XW": [2], "YW": [2]}
257 peak = pd.DataFrame(peak_data).iloc[0]
258 with pytest.raises(AssertionError):
259 limits = PeakLimits(peak, sample_data)
262def test_peak_limits_1d_data():
263 data = np.zeros(10)
264 peak_data = {"X_AXIS": [5], "Y_AXIS": [0], "XW": [2], "YW": [0]}
265 peak = pd.DataFrame(peak_data).iloc[0]
266 with pytest.raises(IndexError):
267 limits = PeakLimits(peak, data)
270def test_StrucEl():
271 assert StrucEl.square.value == "square"
272 assert StrucEl.disk.value == "disk"
273 assert StrucEl.rectangle.value == "rectangle"
274 assert StrucEl.mask_method.value == "mask_method"
277def test_PeaklistFormat():
278 assert PeaklistFormat.a2.value == "a2"
279 assert PeaklistFormat.a3.value == "a3"
280 assert PeaklistFormat.sparky.value == "sparky"
281 assert PeaklistFormat.pipe.value == "pipe"
282 assert PeaklistFormat.peakipy.value == "peakipy"
285def test_OutFmt():
286 assert OutFmt.csv.value == "csv"
287 assert OutFmt.pkl.value == "pkl"
290@pytest.fixture
291def test_data_path():
292 return Path("./test/test_protein_L")
295@pytest.fixture
296def pseudo3d_args(test_data_path):
297 dic, data = ng.pipe.read(test_data_path / "test1.ft2")
298 dims = [0, 1, 2]
299 return dic, data, dims
302@pytest.fixture
303def peaklist(test_data_path):
304 dims = [0, 1, 2]
305 path = test_data_path / "test.tab"
306 data_path = test_data_path / "test1.ft2"
307 fmt = PeaklistFormat.pipe
308 radii = [0.04, 0.4]
309 return Peaklist(path, data_path, fmt, dims, radii)
312def test_Pseudo3D_properties(pseudo3d_args):
313 dic, data, dims = pseudo3d_args
314 pseudo3d = Pseudo3D(dic, data, dims)
315 assert pseudo3d.dic == dic
316 assert np.array_equal(pseudo3d._data, data.reshape((4, 256, 546)))
317 assert pseudo3d.dims == dims
320def test_Peaklist_initialization(test_data_path, peaklist):
322 assert peaklist.peaklist_path == test_data_path / "test.tab"
323 assert peaklist.data_path == test_data_path / "test1.ft2"
324 assert peaklist.fmt == PeaklistFormat.pipe
325 assert peaklist.radii == [0.04, 0.4]
328def test_Peaklist_a2(test_data_path):
329 dims = [0, 1, 2]
330 path = test_data_path / "peaks.a2"
331 data_path = test_data_path / "test1.ft2"
332 fmt = PeaklistFormat.a2
333 radii = [0.04, 0.4]
334 peaklist = Peaklist(path, data_path, fmt, dims, radii)
335 peaklist.update_df()
338def test_Peaklist_a3(test_data_path):
339 dims = [0, 1, 2]
340 path = test_data_path / "ccpnTable.tsv"
341 data_path = test_data_path / "test1.ft2"
342 fmt = PeaklistFormat.a3
343 radii = [0.04, 0.4]
344 peaklist = Peaklist(path, data_path, fmt, dims, radii)
345 peaklist.update_df()
348def test_Peaklist_sparky(test_data_path):
349 dims = [0, 1, 2]
350 path = test_data_path / "peaks.sparky"
351 data_path = test_data_path / "test1.ft2"
352 fmt = PeaklistFormat.sparky
353 radii = [0.04, 0.4]
354 Peaklist(path, data_path, fmt, dims, radii)
357@pytest.fixture
358def loaddata(test_data_path):
359 dims = [0, 1, 2]
360 path = test_data_path / "test.csv"
361 data_path = test_data_path / "test1.ft2"
362 fmt = PeaklistFormat.peakipy
363 radii = [0.04, 0.4]
364 return LoadData(path, data_path, fmt, dims, radii)
367def test_LoadData_initialization(test_data_path, loaddata):
368 assert loaddata.peaklist_path == test_data_path / "test.csv"
369 assert loaddata.data_path == test_data_path / "test1.ft2"
370 assert loaddata.fmt == PeaklistFormat.peakipy
371 assert loaddata.radii == [0.04, 0.4]
372 loaddata.check_data_frame()
373 loaddata.check_assignments()
374 loaddata.check_peak_bounds()
375 loaddata.update_df()
378def test_LoadData_with_Edited_column(loaddata):
379 loaddata.df["Edited"] = "yes"
380 loaddata.check_data_frame()
383def test_LoadData_without_include_column(loaddata):
384 loaddata.df.drop(columns=["include"], inplace=True)
385 loaddata.check_data_frame()
386 assert "include" in loaddata.df.columns
387 assert np.all(loaddata.df.include == "yes")
390def test_LoadData_with_X_DIAMETER_PPM_column(loaddata):
391 loaddata.df["X_DIAMETER_PPM"] = 0.04
392 loaddata.check_data_frame()
393 assert "X_DIAMETER_PPM" in loaddata.df.columns
396def test_UnknownFormat():
397 with pytest.raises(UnknownFormat):
398 raise UnknownFormat("This is an unknown format")
401def test_update_df(peaklist):
402 peaklist.update_df()
404 df = peaklist.df
406 # Check that X_AXIS and Y_AXIS columns are created and values are set correctly
407 assert "X_AXIS" in df.columns
408 assert "Y_AXIS" in df.columns
410 # Check that X_AXISf and Y_AXISf columns are created and values are set correctly
411 assert "X_AXISf" in df.columns
412 assert "Y_AXISf" in df.columns
414 # Check that XW_HZ and YW_HZ columns are converted to float correctly
415 assert df["XW_HZ"].dtype == float
416 assert df["YW_HZ"].dtype == float
418 # Check that XW and YW columns are created
419 assert "XW" in df.columns
420 assert "YW" in df.columns
422 # Check the assignment column
423 assert "ASS" in df.columns
425 # Check radii columns
426 assert "X_RADIUS_PPM" in df.columns
427 assert "Y_RADIUS_PPM" in df.columns
428 assert "X_RADIUS" in df.columns
429 assert "Y_RADIUS" in df.columns
431 # Check 'include' column is created and set to 'yes'
432 assert "include" in df.columns
433 assert all(df["include"] == "yes")
435 # Check that the peaks are within bounds
436 assert all(
437 (df["X_PPM"] < peaklist.f2_ppm_max) & (df["X_PPM"] > peaklist.f2_ppm_min)
438 )
439 assert all(
440 (df["Y_PPM"] < peaklist.f1_ppm_max) & (df["Y_PPM"] > peaklist.f1_ppm_min)
441 )
444def test_update_df_with_excluded_peaks(peaklist):
445 peaklist._df.loc[1, "X_PPM"] = 100.0 # This peak should be out of bounds
446 peaklist.update_df()
448 df = peaklist.df
450 # Check that out of bounds peak is excluded
451 assert len(df) == 62
452 assert not ((df["X_PPM"] == 100.0).any())
455def test_clusters_result_initialization():
456 labeled_array = np.array([[1, 2], [3, 4]])
457 num_features = 5
458 closed_data = np.array([[5, 6], [7, 8]])
459 peaks = [(1, 2), (3, 4)]
461 clusters_result = ClustersResult(labeled_array, num_features, closed_data, peaks)
463 assert np.array_equal(clusters_result.labeled_array, labeled_array)
464 assert clusters_result.num_features == num_features
465 assert np.array_equal(clusters_result.closed_data, closed_data)
466 assert clusters_result.peaks == peaks
469def test_get_vclist_None():
470 assert get_vclist(None, {})["vclist"] == False
473def test_get_vclist_exists(test_data_path):
474 vclist = test_data_path / "vclist"
475 assert get_vclist(vclist, {})["vclist"] == True
478def test_get_vclist_not_exists(test_data_path):
479 vclist = test_data_path / "vclistbla"
480 with pytest.raises(Exception):
481 get_vclist(vclist, {})["vclist"] == True
484if __name__ == "__main__":
485 unittest.main(verbosity=2)