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

1import unittest 

2from unittest.mock import patch 

3from pathlib import Path 

4import json 

5import textwrap 

6 

7import pytest 

8import numpy as np 

9import nmrglue as ng 

10import pandas as pd 

11 

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 

25 

26 

27@pytest.fixture 

28def test_directory(): 

29 return Path(__file__).parent 

30 

31@pytest.fixture 

32def test_directory_protein_L(test_directory): 

33 return test_directory / "test_protein_L" 

34 

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) 

43 

44 

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) 

51 

52 

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) 

59 

60 

61 

62class TestReadScript(unittest.TestCase): 

63 test_directory = "./test/" 

64 

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) 

70 

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") 

85 

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" 

105 

106 

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 

127 

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 == "" 

146 

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) 

152 

153 loaded_config = load_config(config_path) 

154 

155 assert loaded_config == {"key1": "value1"} 

156 

157 # Clean up 

158 config_path.unlink() 

159 

160 

161def test_load_config_nonexistent(): 

162 config_path = Path("test_config.json") 

163 

164 loaded_config = load_config(config_path) 

165 

166 assert loaded_config == {} 

167 

168 

169def test_write_config(): 

170 config_path = Path("test_config.json") 

171 config_kvs = {"key1": "value1", "key2": "value2"} 

172 

173 write_config(config_path, config_kvs) 

174 

175 # Check if the config file is created correctly 

176 assert config_path.exists() 

177 

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"} 

182 

183 # Clean up 

184 config_path.unlink() 

185 

186 

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) 

192 

193 config_kvs = {"key2": "value2", "key3": "value3"} 

194 updated_config = update_config_file(config_path, config_kvs) 

195 

196 assert updated_config == {"key1": "value1", "key2": "value2", "key3": "value3"} 

197 

198 # Clean up 

199 config_path.unlink() 

200 

201 

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) 

206 

207 assert updated_config == {"key1": "value1", "key2": "value2"} 

208 

209 # Clean up 

210 config_path.unlink() 

211 

212 

213@pytest.fixture 

214def sample_data(): 

215 return np.zeros((10, 10)) 

216 

217 

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] 

222 

223 

224def test_peak_limits_max_min(sample_peak, sample_data): 

225 limits = PeakLimits(sample_peak, sample_data) 

226 

227 assert limits.max_x == 8 

228 assert limits.max_y == 8 

229 assert limits.min_x == 3 

230 assert limits.min_y == 3 

231 

232 

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) 

237 

238 assert limits.max_x == 10 

239 assert limits.max_y == 10 

240 assert limits.min_x == 6 

241 assert limits.min_y == 6 

242 

243 

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) 

248 

249 assert limits.max_x == 3 

250 assert limits.max_y == 3 

251 assert limits.min_x == 0 

252 assert limits.min_y == 0 

253 

254 

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) 

260 

261 

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) 

268 

269 

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" 

275 

276 

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" 

283 

284 

285def test_OutFmt(): 

286 assert OutFmt.csv.value == "csv" 

287 assert OutFmt.pkl.value == "pkl" 

288 

289 

290@pytest.fixture 

291def test_data_path(): 

292 return Path("./test/test_protein_L") 

293 

294 

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 

300 

301 

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) 

310 

311 

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 

318 

319 

320def test_Peaklist_initialization(test_data_path, peaklist): 

321 

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] 

326 

327 

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() 

336 

337 

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() 

346 

347 

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) 

355 

356 

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) 

365 

366 

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() 

376 

377 

378def test_LoadData_with_Edited_column(loaddata): 

379 loaddata.df["Edited"] = "yes" 

380 loaddata.check_data_frame() 

381 

382 

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") 

388 

389 

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 

394 

395 

396def test_UnknownFormat(): 

397 with pytest.raises(UnknownFormat): 

398 raise UnknownFormat("This is an unknown format") 

399 

400 

401def test_update_df(peaklist): 

402 peaklist.update_df() 

403 

404 df = peaklist.df 

405 

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 

409 

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 

413 

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 

417 

418 # Check that XW and YW columns are created 

419 assert "XW" in df.columns 

420 assert "YW" in df.columns 

421 

422 # Check the assignment column 

423 assert "ASS" in df.columns 

424 

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 

430 

431 # Check 'include' column is created and set to 'yes' 

432 assert "include" in df.columns 

433 assert all(df["include"] == "yes") 

434 

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 ) 

442 

443 

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() 

447 

448 df = peaklist.df 

449 

450 # Check that out of bounds peak is excluded 

451 assert len(df) == 62 

452 assert not ((df["X_PPM"] == 100.0).any()) 

453 

454 

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)] 

460 

461 clusters_result = ClustersResult(labeled_array, num_features, closed_data, peaks) 

462 

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 

467 

468 

469def test_get_vclist_None(): 

470 assert get_vclist(None, {})["vclist"] == False 

471 

472 

473def test_get_vclist_exists(test_data_path): 

474 vclist = test_data_path / "vclist" 

475 assert get_vclist(vclist, {})["vclist"] == True 

476 

477 

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 

482 

483 

484if __name__ == "__main__": 

485 unittest.main(verbosity=2)