Coverage for tests/test_diagnostics.py: 21%

90 statements  

« prev     ^ index     » next       coverage.py v7.3.1, created at 2023-09-27 10:10 +0000

1# This file is part of daf_relation. 

2# 

3# Developed for the LSST Data Management System. 

4# This product includes software developed by the LSST Project 

5# (http://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 <http://www.gnu.org/licenses/>. 

21 

22from __future__ import annotations 

23 

24import unittest 

25from collections.abc import Set 

26from typing import Any 

27 

28from lsst.daf.relation import ( 

29 ColumnTag, 

30 Diagnostics, 

31 GenericConcreteEngine, 

32 LeafRelation, 

33 Predicate, 

34 Relation, 

35 tests, 

36) 

37 

38 

39class EmptyLookupEngine(GenericConcreteEngine[str]): 

40 """A toy Engine for testing Diagnostics.""" 

41 

42 def __init__(self) -> None: 

43 self.empty: set[Relation] = set() 

44 

45 def get_join_identity_payload(self) -> str: 

46 return "I" 

47 

48 def get_doomed_payload(self, columns: Set[ColumnTag]) -> str: 

49 return "0" 

50 

51 def make_leaf(self, name: str, *columns: ColumnTag, **kwargs: Any) -> Relation: 

52 return LeafRelation(self, frozenset(columns), name=name, payload=name, **kwargs) 

53 

54 def is_relation_nonempty(self, relation: Relation) -> bool: 

55 return relation not in self.empty and relation.max_rows != 0 

56 

57 

58class DiagnosticsTestCase(tests.RelationTestCase): 

59 """Tests for the Diagnostics class.""" 

60 

61 def setUp(self) -> None: 

62 self.maxDiff = None 

63 

64 def test_static_leaf(self) -> None: 

65 """Test Diagnostics on LeafRelations with max_rows=0 and 

66 empty-invariant operations acting on them. 

67 """ 

68 engine = EmptyLookupEngine() 

69 leaf = engine.make_leaf("leaf", max_rows=0) 

70 self.assertEqual( 

71 Diagnostics.run(leaf), 

72 Diagnostics( 

73 is_doomed=True, 

74 messages=["Relation 'leaf' has no rows (static)."], 

75 ), 

76 ) 

77 self.assertEqual( 

78 Diagnostics.run(leaf, engine.is_relation_nonempty), 

79 Diagnostics( 

80 is_doomed=True, 

81 messages=["Relation 'leaf' has no rows (static)."], 

82 ), 

83 ) 

84 self.assertEqual( 

85 Diagnostics.run(leaf.without_duplicates(), engine.is_relation_nonempty), 

86 Diagnostics( 

87 is_doomed=True, 

88 messages=["Relation 'leaf' has no rows (static)."], 

89 ), 

90 ) 

91 

92 def test_executed_leaf(self) -> None: 

93 """Test Diagnostics on LeafRelations with max_rows != 0 and 

94 empty-invariant operations acting on them. 

95 """ 

96 engine = EmptyLookupEngine() 

97 leaf = engine.make_leaf("leaf") 

98 self.assertEqual( 

99 Diagnostics.run(leaf), 

100 Diagnostics( 

101 is_doomed=False, 

102 messages=[], 

103 ), 

104 ) 

105 self.assertEqual( 

106 Diagnostics.run(leaf.without_duplicates()), 

107 Diagnostics( 

108 is_doomed=False, 

109 messages=[], 

110 ), 

111 ) 

112 engine.empty.add(leaf) 

113 self.assertEqual( 

114 Diagnostics.run(leaf, engine.is_relation_nonempty), 

115 Diagnostics( 

116 is_doomed=True, 

117 messages=["Relation 'leaf' has no rows (executed)."], 

118 ), 

119 ) 

120 self.assertEqual( 

121 Diagnostics.run(leaf.without_duplicates(), engine.is_relation_nonempty), 

122 Diagnostics( 

123 is_doomed=True, 

124 messages=["Relation 'leaf' has no rows (executed)."], 

125 ), 

126 ) 

127 

128 def test_slice(self) -> None: 

129 """Test Diagnostics on Slice operations.""" 

130 engine = EmptyLookupEngine() 

131 leaf = engine.make_leaf("leaf") 

132 self.assertEqual( 

133 Diagnostics.run(leaf[2:2]), 

134 Diagnostics( 

135 is_doomed=True, 

136 messages=["Slice with limit=0 applied to 'leaf'"], 

137 ), 

138 ) 

139 sliced = leaf[1:3] 

140 self.assertEqual( 

141 Diagnostics.run(sliced), 

142 Diagnostics( 

143 is_doomed=False, 

144 messages=[], 

145 ), 

146 ) 

147 engine.empty.add(sliced) 

148 self.assertEqual( 

149 Diagnostics.run(sliced, engine.is_relation_nonempty), 

150 Diagnostics( 

151 is_doomed=True, 

152 messages=["Operation slice[1:3] yields no results when applied to 'leaf'"], 

153 ), 

154 ) 

155 

156 def test_selection(self) -> None: 

157 """Test Diagnostics on Selection operations.""" 

158 engine = EmptyLookupEngine() 

159 a = tests.ColumnTag("a") 

160 leaf = engine.make_leaf("leaf", a) 

161 self.assertEqual( 

162 Diagnostics.run(leaf.with_rows_satisfying(Predicate.literal(False))), 

163 Diagnostics( 

164 is_doomed=True, 

165 messages=["Predicate 'False' is trivially false (applied to 'leaf')"], 

166 ), 

167 ) 

168 selected = leaf.with_rows_satisfying(Predicate.reference(a)) 

169 self.assertEqual( 

170 Diagnostics.run(selected), 

171 Diagnostics( 

172 is_doomed=False, 

173 messages=[], 

174 ), 

175 ) 

176 engine.empty.add(selected) 

177 self.assertEqual( 

178 Diagnostics.run(selected, engine.is_relation_nonempty), 

179 Diagnostics( 

180 is_doomed=True, 

181 messages=["Operation σ[a] yields no results when applied to 'leaf'"], 

182 ), 

183 ) 

184 

185 def test_chain(self) -> None: 

186 """Test Diagnostics on Chain operations.""" 

187 engine = EmptyLookupEngine() 

188 a = tests.ColumnTag("a") 

189 leaf1 = engine.make_leaf("leaf1", a) 

190 leaf2 = engine.make_leaf("leaf2", a) 

191 leaf3 = engine.make_leaf("leaf3", a, max_rows=0) 

192 leaf4 = engine.make_leaf("leaf4", a, max_rows=0) 

193 self.assertEqual( 

194 Diagnostics.run(leaf1.chain(leaf2)), 

195 Diagnostics( 

196 is_doomed=False, 

197 messages=[], 

198 ), 

199 ) 

200 self.assertEqual( 

201 Diagnostics.run(leaf1.chain(leaf3)), 

202 Diagnostics( 

203 is_doomed=False, 

204 messages=["Relation 'leaf3' has no rows (static)."], 

205 ), 

206 ) 

207 self.assertEqual( 

208 Diagnostics.run(leaf3.chain(leaf1)), 

209 Diagnostics( 

210 is_doomed=False, 

211 messages=["Relation 'leaf3' has no rows (static)."], 

212 ), 

213 ) 

214 self.assertEqual( 

215 Diagnostics.run(leaf3.chain(leaf4)), 

216 Diagnostics( 

217 is_doomed=True, 

218 messages=[ 

219 "Relation 'leaf3' has no rows (static).", 

220 "Relation 'leaf4' has no rows (static).", 

221 ], 

222 ), 

223 ) 

224 engine.empty.add(leaf1) 

225 self.assertEqual( 

226 Diagnostics.run(leaf1.chain(leaf2), engine.is_relation_nonempty), 

227 Diagnostics( 

228 is_doomed=False, 

229 messages=["Relation 'leaf1' has no rows (executed)."], 

230 ), 

231 ) 

232 self.assertEqual( 

233 Diagnostics.run(leaf1.chain(leaf3), engine.is_relation_nonempty), 

234 Diagnostics( 

235 is_doomed=True, 

236 messages=[ 

237 "Relation 'leaf1' has no rows (executed).", 

238 "Relation 'leaf3' has no rows (static).", 

239 ], 

240 ), 

241 ) 

242 self.assertEqual( 

243 Diagnostics.run(leaf3.chain(leaf1), engine.is_relation_nonempty), 

244 Diagnostics( 

245 is_doomed=True, 

246 messages=[ 

247 "Relation 'leaf3' has no rows (static).", 

248 "Relation 'leaf1' has no rows (executed).", 

249 ], 

250 ), 

251 ) 

252 engine.empty.add(leaf2) 

253 self.assertEqual( 

254 Diagnostics.run(leaf1.chain(leaf2), engine.is_relation_nonempty), 

255 Diagnostics( 

256 is_doomed=True, 

257 messages=[ 

258 "Relation 'leaf1' has no rows (executed).", 

259 "Relation 'leaf2' has no rows (executed).", 

260 ], 

261 ), 

262 ) 

263 

264 def test_join(self) -> None: 

265 """Test Diagnostics on Join operations.""" 

266 engine = EmptyLookupEngine() 

267 a = tests.ColumnTag("a") 

268 b = tests.ColumnTag("b") 

269 c = tests.ColumnTag("c") 

270 leaf1 = engine.make_leaf("leaf1", a, b) 

271 leaf2 = engine.make_leaf("leaf2", a, c) 

272 leaf3 = engine.make_leaf("leaf3", a, b, max_rows=0) 

273 leaf4 = engine.make_leaf("leaf4", a, c, max_rows=0) 

274 self.assertEqual( 

275 Diagnostics.run(leaf1.join(leaf2)), 

276 Diagnostics( 

277 is_doomed=False, 

278 messages=[], 

279 ), 

280 ) 

281 self.assertEqual( 

282 Diagnostics.run(leaf1.join(leaf4)), 

283 Diagnostics( 

284 is_doomed=True, 

285 messages=["Relation 'leaf4' has no rows (static)."], 

286 ), 

287 ) 

288 self.assertEqual( 

289 Diagnostics.run(leaf3.join(leaf2)), 

290 Diagnostics( 

291 is_doomed=True, 

292 messages=["Relation 'leaf3' has no rows (static)."], 

293 ), 

294 ) 

295 self.assertEqual( 

296 Diagnostics.run(leaf3.join(leaf4)), 

297 Diagnostics( 

298 is_doomed=True, 

299 messages=[ 

300 "Relation 'leaf3' has no rows (static).", 

301 "Relation 'leaf4' has no rows (static).", 

302 ], 

303 ), 

304 ) 

305 

306 engine.empty.add(leaf1) 

307 self.assertEqual( 

308 Diagnostics.run(leaf1.join(leaf2), engine.is_relation_nonempty), 

309 Diagnostics( 

310 is_doomed=True, 

311 messages=["Relation 'leaf1' has no rows (executed)."], 

312 ), 

313 ) 

314 self.assertEqual( 

315 Diagnostics.run(leaf1.join(leaf4), engine.is_relation_nonempty), 

316 Diagnostics( 

317 is_doomed=True, 

318 messages=[ 

319 "Relation 'leaf1' has no rows (executed).", 

320 "Relation 'leaf4' has no rows (static).", 

321 ], 

322 ), 

323 ) 

324 engine.empty.add(leaf2) 

325 self.assertEqual( 

326 Diagnostics.run(leaf1.join(leaf2), engine.is_relation_nonempty), 

327 Diagnostics( 

328 is_doomed=True, 

329 messages=[ 

330 "Relation 'leaf1' has no rows (executed).", 

331 "Relation 'leaf2' has no rows (executed).", 

332 ], 

333 ), 

334 ) 

335 engine.empty.clear() 

336 self.assertEqual( 

337 Diagnostics.run(leaf1.join(leaf2, Predicate.literal(False))), 

338 Diagnostics( 

339 is_doomed=True, 

340 messages=["Join predicate 'False' is trivially false in 'leaf1 ⋈ leaf2'."], 

341 ), 

342 ) 

343 joined = leaf1.join(leaf2) 

344 engine.empty.add(joined) 

345 self.assertEqual( 

346 Diagnostics.run(joined, engine.is_relation_nonempty), 

347 Diagnostics( 

348 is_doomed=True, 

349 messages=["Operation ⋈ yields no results when executed: 'leaf1 ⋈ leaf2'"], 

350 ), 

351 ) 

352 

353 

354if __name__ == "__main__": 

355 unittest.main()