Coverage for tests/test_report.py: 21%

118 statements  

« prev     ^ index     » next       coverage.py v7.3.0, created at 2023-08-23 10:45 +0000

1# This file is part of ctrl_bps. 

2# 

3# Developed for the LSST Data Management System. 

4# This product includes software developed by the LSST Project 

5# (https://www.lsst.org). 

6# See the COPYRIGHT file at the top-level directory of this distribution 

7# for details of code ownership. 

8# 

9# This program is free software: you can redistribute it and/or modify 

10# it under the terms of the GNU General Public License as published by 

11# the Free Software Foundation, either version 3 of the License, or 

12# (at your option) any later version. 

13# 

14# This program is distributed in the hope that it will be useful, 

15# but WITHOUT ANY WARRANTY; without even the implied warranty of 

16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

17# GNU General Public License for more details. 

18# 

19# You should have received a copy of the GNU General Public License 

20# along with this program. If not, see <https://www.gnu.org/licenses/>. 

21 

22"""Tests for reporting mechanism.""" 

23 

24import io 

25import unittest 

26 

27from astropy.table import Table 

28from lsst.ctrl.bps import ( 

29 BaseRunReport, 

30 DetailedRunReport, 

31 SummaryRunReport, 

32 WmsJobReport, 

33 WmsRunReport, 

34 WmsStates, 

35) 

36 

37 

38class FakeRunReport(BaseRunReport): 

39 """A fake run report""" 

40 

41 def add(self, run_report, use_global_id=False): 

42 id_ = run_report.global_wms_id if use_global_id else run_report.wms_id 

43 self._table.add_row([id_, run_report.state.name]) 

44 

45 

46class FakeRunReportTestCase(unittest.TestCase): 

47 """Test shared methods.""" 

48 

49 def setUp(self): 

50 self.fields = [("ID", "S"), ("STATE", "S")] 

51 

52 self.report = FakeRunReport(self.fields) 

53 self.report.add(WmsRunReport(wms_id="2.0", state=WmsStates.RUNNING)) 

54 self.report.add(WmsRunReport(wms_id="1.0", state=WmsStates.SUCCEEDED)) 

55 

56 def testEquality(self): 

57 """Test if two reports are identical.""" 

58 other = FakeRunReport(self.fields) 

59 other.add(WmsRunReport(wms_id="2.0", state=WmsStates.RUNNING)) 

60 other.add(WmsRunReport(wms_id="1.0", state=WmsStates.SUCCEEDED)) 

61 self.assertEqual(self.report, other) 

62 

63 def testInequality(self): 

64 """Test if two reports are not identical.""" 

65 other = FakeRunReport(self.fields) 

66 other.add(WmsRunReport(wms_id="1.0", state=WmsStates.FAILED)) 

67 self.assertNotEqual(self.report, other) 

68 

69 def testLength(self): 

70 self.assertEqual(len(self.report), 2) 

71 

72 def testClear(self): 

73 """Test clearing the report.""" 

74 self.report.clear() 

75 self.assertEqual(len(self.report), 0) 

76 

77 def testSortWithKnownKey(self): 

78 """Test sorting the report using known column.""" 

79 expected_output = io.StringIO() 

80 expected = Table(dtype=self.fields) 

81 expected.add_row(["1.0", WmsStates.SUCCEEDED.name]) 

82 expected.add_row(["2.0", WmsStates.RUNNING.name]) 

83 print(expected, file=expected_output) 

84 

85 actual_output = io.StringIO() 

86 self.report.sort("ID") 

87 print(self.report, file=actual_output) 

88 

89 self.assertEqual(actual_output.getvalue(), expected_output.getvalue()) 

90 

91 expected_output.close() 

92 actual_output.close() 

93 

94 def testSortWithUnknownKey(self): 

95 """Test sorting the report using unknown column.""" 

96 with self.assertRaises(AttributeError): 

97 self.report.sort("foo") 

98 

99 

100class SummaryRunReportTestCase(unittest.TestCase): 

101 """Test a summary run report.""" 

102 

103 def setUp(self): 

104 self.fields = [ 

105 ("X", "S"), 

106 ("STATE", "S"), 

107 ("%S", "S"), 

108 ("ID", "S"), 

109 ("OPERATOR", "S"), 

110 ("PROJECT", "S"), 

111 ("CAMPAIGN", "S"), 

112 ("PAYLOAD", "S"), 

113 ("RUN", "S"), 

114 ] 

115 self.run = WmsRunReport( 

116 wms_id="1.0", 

117 global_wms_id="foo#1.0", 

118 path="/path/to/run", 

119 label="label", 

120 run="run", 

121 project="dev", 

122 campaign="testing", 

123 payload="test", 

124 operator="tester", 

125 run_summary="foo:1;bar:1", 

126 state=WmsStates.RUNNING, 

127 jobs=None, 

128 total_number_jobs=2, 

129 job_state_counts={ 

130 state: 1 if state in {WmsStates.SUCCEEDED, WmsStates.RUNNING} else 0 for state in WmsStates 

131 }, 

132 job_summary=None, 

133 ) 

134 self.report = SummaryRunReport(self.fields) 

135 

136 self.expected = Table(dtype=self.fields) 

137 self.expected.add_row(["", "RUNNING", "50", "1.0", "tester", "dev", "testing", "test", "run"]) 

138 

139 self.expected_output = io.StringIO() 

140 self.actual_output = io.StringIO() 

141 

142 def tearDown(self): 

143 self.expected_output.close() 

144 self.actual_output.close() 

145 

146 def testAddWithNoFlag(self): 

147 """Test adding a report for a run with no issues.""" 

148 print("\n".join(self.expected.pformat_all()), file=self.expected_output) 

149 

150 self.report.add(self.run) 

151 print(self.report, file=self.actual_output) 

152 

153 self.assertEqual(self.actual_output.getvalue(), self.expected_output.getvalue()) 

154 

155 def testAddWithFailedFlag(self): 

156 """Test adding a run with a failed job.""" 

157 self.expected["X"][0] = "F" 

158 print("\n".join(self.expected.pformat_all()), file=self.expected_output) 

159 

160 # Alter the run report to include a failed job. 

161 self.run.job_state_counts = { 

162 state: 1 if state in {WmsStates.FAILED, WmsStates.SUCCEEDED} else 0 for state in WmsStates 

163 } 

164 self.report.add(self.run) 

165 print(self.report, file=self.actual_output) 

166 

167 self.assertEqual(self.actual_output.getvalue(), self.expected_output.getvalue()) 

168 

169 def testAddWithHeldFlag(self): 

170 """Test adding a run with a held job.""" 

171 self.expected["X"][0] = "H" 

172 print("\n".join(self.expected.pformat_all()), file=self.expected_output) 

173 

174 # Alter the run report to include a held job. 

175 self.run.job_state_counts = { 

176 state: 1 if state in {WmsStates.SUCCEEDED, WmsStates.HELD} else 0 for state in WmsStates 

177 } 

178 self.report.add(self.run) 

179 print(self.report, file=self.actual_output) 

180 

181 self.assertEqual(self.actual_output.getvalue(), self.expected_output.getvalue()) 

182 

183 def testAddWithDeletedFlag(self): 

184 """Test adding a run with a deleted job.""" 

185 self.expected["X"][0] = "D" 

186 print("\n".join(self.expected.pformat_all()), file=self.expected_output) 

187 

188 # Alter the run report to include a deleted job. 

189 self.run.job_state_counts = { 

190 state: 1 if state in {WmsStates.SUCCEEDED, WmsStates.DELETED} else 0 for state in WmsStates 

191 } 

192 self.report.add(self.run) 

193 print(self.report, file=self.actual_output) 

194 

195 self.assertEqual(self.actual_output.getvalue(), self.expected_output.getvalue()) 

196 

197 

198class DetailedRunReportTestCase(unittest.TestCase): 

199 """Test a detailed run report.""" 

200 

201 def setUp(self): 

202 self.fields = [("", "S")] + [(state.name, "I") for state in WmsStates] + [("EXPECTED", "i")] 

203 

204 table = Table(dtype=self.fields) 

205 table.add_row( 

206 ["TOTAL"] 

207 + [1 if state in {WmsStates.RUNNING, WmsStates.SUCCEEDED} else 0 for state in WmsStates] 

208 + [2] 

209 ) 

210 table.add_row(["foo"] + [1 if state == WmsStates.SUCCEEDED else 0 for state in WmsStates] + [1]) 

211 table.add_row(["bar"] + [1 if state == WmsStates.RUNNING else 0 for state in WmsStates] + [1]) 

212 self.expected = DetailedRunReport.from_table(table) 

213 

214 self.run = WmsRunReport( 

215 wms_id="1.0", 

216 global_wms_id="foo#1.0", 

217 path="/path/to/run", 

218 label="label", 

219 run="run", 

220 project="dev", 

221 campaign="testing", 

222 payload="test", 

223 operator="tester", 

224 run_summary="foo:1;bar:1", 

225 state=WmsStates.RUNNING, 

226 jobs=[ 

227 WmsJobReport(wms_id="1.0", name="", label="foo", state=WmsStates.SUCCEEDED), 

228 WmsJobReport(wms_id="2.0", name="", label="bar", state=WmsStates.RUNNING), 

229 ], 

230 total_number_jobs=2, 

231 job_state_counts={ 

232 state: 1 if state in {WmsStates.SUCCEEDED, WmsStates.RUNNING} else 0 for state in WmsStates 

233 }, 

234 job_summary={ 

235 "foo": {state: 1 if state == WmsStates.SUCCEEDED else 0 for state in WmsStates}, 

236 "bar": {state: 1 if state == WmsStates.RUNNING else 0 for state in WmsStates}, 

237 }, 

238 ) 

239 

240 self.actual = DetailedRunReport(self.fields) 

241 

242 def testAddWithJobSummary(self): 

243 """Test adding a run with a job summary.""" 

244 self.run.jobs = None 

245 self.actual.add(self.run) 

246 

247 self.assertEqual(self.actual, self.expected) 

248 

249 def testAddWithJobs(self): 

250 """Test adding a run with a job info, but not job summary.""" 

251 self.run.job_summary = None 

252 self.actual.add(self.run) 

253 

254 self.assertEqual(self.actual, self.expected) 

255 

256 def testAddWithoutJobInfo(self): 

257 """Test adding a run without either a job summary or job info.""" 

258 self.run.jobs = None 

259 self.run.job_summary = None 

260 self.actual.add(self.run) 

261 

262 self.assertEqual(len(self.actual), 1) 

263 self.assertRegex(self.actual.message, r"^WARNING.*incomplete") 

264 

265 def testAddWithoutRunSummary(self): 

266 """Test adding a run without a run summary.""" 

267 table = Table(dtype=self.fields) 

268 table.add_row( 

269 ["TOTAL"] 

270 + [1 if state in {WmsStates.RUNNING, WmsStates.SUCCEEDED} else 0 for state in WmsStates] 

271 + [2] 

272 ) 

273 table.add_row(["bar"] + [1 if state == WmsStates.RUNNING else 0 for state in WmsStates] + [-1]) 

274 table.add_row(["foo"] + [1 if state == WmsStates.SUCCEEDED else 0 for state in WmsStates] + [-1]) 

275 expected = DetailedRunReport.from_table(table) 

276 

277 self.run.run_summary = None 

278 self.actual.add(self.run) 

279 

280 self.assertRegex(self.actual.message, r"^WARNING.*sorted alphabetically") 

281 self.assertEqual(self.actual, expected) 

282 

283 

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

285 unittest.main()