Coverage for tests/test_plots.py: 87%
137 statements
« prev ^ index » next coverage.py v7.9.1, created at 2025-07-11 14:46 +0200
« prev ^ index » next coverage.py v7.9.1, created at 2025-07-11 14:46 +0200
1import unittest
3import sys
4from io import StringIO
5from contextlib import redirect_stdout, redirect_stderr
7# import subprocess
8import tempfile
9from pathlib import Path
10import yaml
11import hashlib
12import MPP.run as run_module
15DATASETS = ["HP35", "PDZ3", "aSyn"]
16PLOT_KINDS = [
17 "dendrogram",
18 "timescales",
19 "sankey",
20 "contacts",
21 "macrotraj",
22 "ck_test",
23 "state_network",
24]
25MAPPING_FILE = Path(__file__).parent / "data" / "lumpings.yaml"
28def _run_main_with_args(args_list):
29 """Helper to run run.main() with patched sys.argv and capture output."""
30 saved_argv = sys.argv
31 sys.argv = ["run.py"] + args_list
32 stdout, stderr = StringIO(), StringIO()
33 try:
34 with redirect_stdout(stdout), redirect_stderr(stderr):
35 run_module.main()
36 return 0, stdout.getvalue(), stderr.getvalue()
37 except SystemExit as e:
38 return e.code, stdout.getvalue(), stderr.getvalue()
39 finally:
40 sys.argv = saved_argv
43class TestPlotting(unittest.TestCase):
44 # @classmethod
45 # def setUpClass(cls):
46 # with open(MAPPING_FILE, "r") as f:
47 # cls.param_map = yaml.safe_load(f)
49 def setUp(self):
50 self.data_root = Path(__file__).parent / "data"
51 with open(MAPPING_FILE, "r") as f:
52 self.param_map = yaml.safe_load(f)
54 def _run_plot(self, config, d, g, kind, output_file, stochastic=False):
55 key = self._get_key(d, g)
56 args = [
57 str(config),
58 d,
59 g,
60 "-p",
61 kind,
62 "-o",
63 str(output_file),
64 "-Z",
65 str(
66 Path(__file__).parent
67 / "data"
68 / config.parent.name
69 / "expected_output"
70 / key
71 / f"Z{'_stochastic' if stochastic else ''}.npy"
72 ),
73 ]
74 return _run_main_with_args(args)
76 def _get_key(self, d, g):
77 for key, val in self.param_map.items(): 77 ↛ 80line 77 didn't jump to line 80 because the loop on line 77 didn't complete
78 if val["kernel similarity"] == d and val["feature kernel"] == g:
79 return key
80 raise ValueError(f"No mapping found for d={d}, g={g}")
82 def run_single_plot_test(
83 self, dataset, kind, d, g, manual_inspection=False, stochastic=False
84 ):
85 config = (
86 self.data_root
87 / dataset
88 / f"config{'_stochastic' if stochastic else ''}.yaml"
89 )
90 key = self._get_key(d, g)
91 expected_file = (
92 self.data_root / dataset / "expected_output" / key / f"{kind}.pdf"
93 )
95 with tempfile.TemporaryDirectory() as tmpdir:
96 tmpdir = Path(tmpdir)
97 if manual_inspection: 97 ↛ 107line 97 didn't jump to line 107 because the condition on line 97 was always true
98 plot_path = (
99 self.data_root
100 / dataset
101 / f"output{'_stochastic' if stochastic else ''}"
102 / key
103 / expected_file.name
104 )
105 plot_path.unlink(missing_ok=True)
106 else:
107 plot_path = tmpdir / expected_file.name
109 exit_code, stdout, stderr = self._run_plot(
110 config, d, g, kind, plot_path, stochastic=stochastic
111 )
112 self.assertEqual(exit_code, 0, f"Plot command failed: {stderr}")
113 if manual_inspection: 113 ↛ 119line 113 didn't jump to line 119 because the condition on line 113 was always true
114 self.assertTrue(
115 plot_path.exists(), f"Plot file not created: {plot_path}"
116 )
117 else:
118 # Hashed comparison
119 expected_hash = file_hash(expected_file)
120 generated_hash = file_hash(plot_path)
121 self.assertEqual(
122 expected_hash,
123 generated_hash,
124 f"Hash mismatch for {dataset} {d}-{g} {kind}",
125 )
127 def test_manual_dendrogram(self):
128 dataset = "HP35"
129 d, g = "T", "none"
130 kind = "dendrogram"
131 self.run_single_plot_test(dataset, kind, d, g, manual_inspection=True)
133 def test_manual_timescales(self):
134 dataset = "HP35"
135 d, g = "T", "none"
136 kind = "timescales"
137 self.run_single_plot_test(dataset, kind, d, g, manual_inspection=True)
139 def test_manual_sankey(self):
140 dataset = "HP35"
141 d, g = "KL", "none"
142 kind = "sankey"
143 self.run_single_plot_test(dataset, kind, d, g, manual_inspection=True)
145 def test_manual_contacts(self):
146 dataset = "HP35"
147 d, g = "T", "none"
148 kind = "contacts"
149 self.run_single_plot_test(dataset, kind, d, g, manual_inspection=True)
151 def test_manual_macrotraj_ref(self):
152 dataset = "HP35"
153 d, g = "T", "none"
154 kind = "macrotraj"
155 self.run_single_plot_test(dataset, kind, d, g, manual_inspection=True)
157 def test_manual_macrotraj_PDZ3(self):
158 dataset = "PDZ3"
159 d, g = "T", "none"
160 kind = "macrotraj"
161 self.run_single_plot_test(dataset, kind, d, g, manual_inspection=True)
163 def test_manual_ck_test(self):
164 dataset = "HP35"
165 d, g = "T", "none"
166 kind = "ck_test"
167 self.run_single_plot_test(dataset, kind, d, g, manual_inspection=True)
169 def test_manual_state_network(self):
170 dataset = "HP35"
171 d, g = "T", "none"
172 kind = "state_network"
173 self.run_single_plot_test(dataset, kind, d, g, manual_inspection=True)
175 def test_manual_macro_feature(self):
176 dataset = "HP35"
177 d, g = "T", "none"
178 kind = "macro_feature"
179 self.run_single_plot_test(
180 dataset, kind, d, g, manual_inspection=True, stochastic=True
181 )
183 def test_manual_rmsd(self):
184 dataset = "PDZ3"
185 d, g = "KL", "none"
186 kind = "rmsd"
187 self.run_single_plot_test(dataset, kind, d, g, manual_inspection=True)
189 def test_manual_delta_rmsd(self):
190 dataset = "PDZ3"
191 d, g = "KL", "none"
192 kind = "delta_rmsd"
193 self.run_single_plot_test(dataset, kind, d, g, manual_inspection=True)
195 def test_manual_stochastic_state_similarity(self):
196 dataset = "HP35"
197 d, g = "T", "none"
198 kind = "stochastic_state_similarity"
199 self.run_single_plot_test(
200 dataset, kind, d, g, manual_inspection=True, stochastic=True
201 )
203 def test_manual_relative_implied_timescales(self):
204 dataset = "HP35"
205 d, g = "T", "none"
206 kind = "relative_implied_timescales"
207 self.run_single_plot_test(
208 dataset, kind, d, g, manual_inspection=True, stochastic=True
209 )
211 def test_manual_transition_matrix(self):
212 dataset = "HP35"
213 d, g = "T", "none"
214 kind = "transition_matrix"
215 self.run_single_plot_test(dataset, kind, d, g, manual_inspection=True)
217 def test_manual_transition_time(self):
218 dataset = "HP35"
219 d, g = "T", "none"
220 kind = "transition_time"
221 self.run_single_plot_test(dataset, kind, d, g, manual_inspection=True)
224def file_hash(path, algo="sha256"):
225 """Returns the hash digest of a file."""
226 h = hashlib.new(algo)
227 with open(path, "rb") as f:
228 while chunk := f.read(8192):
229 h.update(chunk)
230 return h.hexdigest()
233if __name__ == "__main__": 233 ↛ 234line 233 didn't jump to line 234 because the condition on line 233 was never true
234 unittest.main()