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

1import unittest 

2 

3import sys 

4from io import StringIO 

5from contextlib import redirect_stdout, redirect_stderr 

6 

7# import subprocess 

8import tempfile 

9from pathlib import Path 

10import yaml 

11import hashlib 

12import MPP.run as run_module 

13 

14 

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" 

26 

27 

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 

41 

42 

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) 

48 

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) 

53 

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) 

75 

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

81 

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 ) 

94 

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 

108 

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 ) 

126 

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) 

132 

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) 

138 

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) 

144 

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) 

150 

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) 

156 

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) 

162 

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) 

168 

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) 

174 

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 ) 

182 

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) 

188 

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) 

194 

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 ) 

202 

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 ) 

210 

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) 

216 

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) 

222 

223 

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

231 

232 

233if __name__ == "__main__": 233 ↛ 234line 233 didn't jump to line 234 because the condition on line 233 was never true

234 unittest.main()