Coverage for tests/test_diagnostics.py: 22%
92 statements
« prev ^ index » next coverage.py v6.5.0, created at 2023-02-14 01:59 -0800
« prev ^ index » next coverage.py v6.5.0, created at 2023-02-14 01:59 -0800
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/>.
22from __future__ import annotations
24import unittest
25from collections.abc import Set
26from typing import Any
28from lsst.daf.relation import (
29 ColumnTag,
30 Diagnostics,
31 GenericConcreteEngine,
32 LeafRelation,
33 Predicate,
34 Relation,
35 tests,
36)
39class EmptyLookupEngine(GenericConcreteEngine[str]):
40 """A toy Engine for testing Diagnostics."""
42 def __init__(self) -> None:
43 self.empty: set[Relation] = set()
45 def get_join_identity_payload(self) -> str:
46 return "I"
48 def get_doomed_payload(self, columns: Set[ColumnTag]) -> str:
49 return "0"
51 def make_leaf(self, name: str, *columns: ColumnTag, **kwargs: Any) -> Relation:
52 return LeafRelation(self, frozenset(columns), name=name, payload=name, **kwargs)
54 def is_relation_nonempty(self, relation: Relation) -> bool:
55 return relation not in self.empty and relation.max_rows != 0
58class DiagnosticsTestCase(tests.RelationTestCase):
59 """Tests for the Diagnostics class."""
61 def setUp(self) -> None:
62 self.maxDiff = None
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 )
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 engine = EmptyLookupEngine()
96 leaf = engine.make_leaf("leaf")
97 self.assertEqual(
98 Diagnostics.run(leaf),
99 Diagnostics(
100 is_doomed=False,
101 messages=[],
102 ),
103 )
104 self.assertEqual(
105 Diagnostics.run(leaf.without_duplicates()),
106 Diagnostics(
107 is_doomed=False,
108 messages=[],
109 ),
110 )
111 engine.empty.add(leaf)
112 self.assertEqual(
113 Diagnostics.run(leaf, engine.is_relation_nonempty),
114 Diagnostics(
115 is_doomed=True,
116 messages=["Relation 'leaf' has no rows (executed)."],
117 ),
118 )
119 self.assertEqual(
120 Diagnostics.run(leaf.without_duplicates(), engine.is_relation_nonempty),
121 Diagnostics(
122 is_doomed=True,
123 messages=["Relation 'leaf' has no rows (executed)."],
124 ),
125 )
127 def test_slice(self) -> None:
128 """Test Diagnostics on Slice operations."""
129 engine = EmptyLookupEngine()
130 leaf = engine.make_leaf("leaf")
131 self.assertEqual(
132 Diagnostics.run(leaf[2:2]),
133 Diagnostics(
134 is_doomed=True,
135 messages=["Slice with limit=0 applied to 'leaf'"],
136 ),
137 )
138 sliced = leaf[1:3]
139 self.assertEqual(
140 Diagnostics.run(sliced),
141 Diagnostics(
142 is_doomed=False,
143 messages=[],
144 ),
145 )
146 engine.empty.add(sliced)
147 self.assertEqual(
148 Diagnostics.run(sliced, engine.is_relation_nonempty),
149 Diagnostics(
150 is_doomed=True,
151 messages=["Operation slice[1:3] yields no results when applied to 'leaf'"],
152 ),
153 )
155 def test_selection(self) -> None:
156 """Test Diagnostics on Selection operations."""
157 engine = EmptyLookupEngine()
158 a = tests.ColumnTag("a")
159 leaf = engine.make_leaf("leaf", a)
160 self.assertEqual(
161 Diagnostics.run(leaf.with_rows_satisfying(Predicate.literal(False))),
162 Diagnostics(
163 is_doomed=True,
164 messages=["Predicate 'False' is trivially false (applied to 'leaf')"],
165 ),
166 )
167 selected = leaf.with_rows_satisfying(Predicate.reference(a))
168 self.assertEqual(
169 Diagnostics.run(selected),
170 Diagnostics(
171 is_doomed=False,
172 messages=[],
173 ),
174 )
175 engine.empty.add(selected)
176 self.assertEqual(
177 Diagnostics.run(selected, engine.is_relation_nonempty),
178 Diagnostics(
179 is_doomed=True,
180 messages=["Operation σ[a] yields no results when applied to 'leaf'"],
181 ),
182 )
184 def test_chain(self) -> None:
185 """Test Diagnostics on Chain operations."""
186 engine = EmptyLookupEngine()
187 a = tests.ColumnTag("a")
188 leaf1 = engine.make_leaf("leaf1", a)
189 leaf2 = engine.make_leaf("leaf2", a)
190 leaf3 = engine.make_leaf("leaf3", a, max_rows=0)
191 leaf4 = engine.make_leaf("leaf4", a, max_rows=0)
192 self.assertEqual(
193 Diagnostics.run(leaf1.chain(leaf2)),
194 Diagnostics(
195 is_doomed=False,
196 messages=[],
197 ),
198 )
199 self.assertEqual(
200 Diagnostics.run(leaf1.chain(leaf3)),
201 Diagnostics(
202 is_doomed=False,
203 messages=["Relation 'leaf3' has no rows (static)."],
204 ),
205 )
206 self.assertEqual(
207 Diagnostics.run(leaf3.chain(leaf1)),
208 Diagnostics(
209 is_doomed=False,
210 messages=["Relation 'leaf3' has no rows (static)."],
211 ),
212 )
213 self.assertEqual(
214 Diagnostics.run(leaf3.chain(leaf4)),
215 Diagnostics(
216 is_doomed=True,
217 messages=[
218 "Relation 'leaf3' has no rows (static).",
219 "Relation 'leaf4' has no rows (static).",
220 ],
221 ),
222 )
223 engine.empty.add(leaf1)
224 self.assertEqual(
225 Diagnostics.run(leaf1.chain(leaf2), engine.is_relation_nonempty),
226 Diagnostics(
227 is_doomed=False,
228 messages=["Relation 'leaf1' has no rows (executed)."],
229 ),
230 )
231 self.assertEqual(
232 Diagnostics.run(leaf1.chain(leaf3), engine.is_relation_nonempty),
233 Diagnostics(
234 is_doomed=True,
235 messages=[
236 "Relation 'leaf1' has no rows (executed).",
237 "Relation 'leaf3' has no rows (static).",
238 ],
239 ),
240 )
241 self.assertEqual(
242 Diagnostics.run(leaf3.chain(leaf1), engine.is_relation_nonempty),
243 Diagnostics(
244 is_doomed=True,
245 messages=[
246 "Relation 'leaf3' has no rows (static).",
247 "Relation 'leaf1' has no rows (executed).",
248 ],
249 ),
250 )
251 engine.empty.add(leaf2)
252 self.assertEqual(
253 Diagnostics.run(leaf1.chain(leaf2), engine.is_relation_nonempty),
254 Diagnostics(
255 is_doomed=True,
256 messages=[
257 "Relation 'leaf1' has no rows (executed).",
258 "Relation 'leaf2' has no rows (executed).",
259 ],
260 ),
261 )
263 def test_join(self) -> None:
264 """Test Diagnostics on Join operations."""
265 engine = EmptyLookupEngine()
266 a = tests.ColumnTag("a")
267 b = tests.ColumnTag("b")
268 c = tests.ColumnTag("c")
269 leaf1 = engine.make_leaf("leaf1", a, b)
270 leaf2 = engine.make_leaf("leaf2", a, c)
271 leaf3 = engine.make_leaf("leaf3", a, b, max_rows=0)
272 leaf4 = engine.make_leaf("leaf4", a, c, max_rows=0)
273 self.assertEqual(
274 Diagnostics.run(leaf1.join(leaf2)),
275 Diagnostics(
276 is_doomed=False,
277 messages=[],
278 ),
279 )
280 self.assertEqual(
281 Diagnostics.run(leaf1.join(leaf4)),
282 Diagnostics(
283 is_doomed=True,
284 messages=["Relation 'leaf4' has no rows (static)."],
285 ),
286 )
287 self.assertEqual(
288 Diagnostics.run(leaf3.join(leaf2)),
289 Diagnostics(
290 is_doomed=True,
291 messages=["Relation 'leaf3' has no rows (static)."],
292 ),
293 )
294 self.assertEqual(
295 Diagnostics.run(leaf3.join(leaf4)),
296 Diagnostics(
297 is_doomed=True,
298 messages=[
299 "Relation 'leaf3' has no rows (static).",
300 "Relation 'leaf4' has no rows (static).",
301 ],
302 ),
303 )
305 engine.empty.add(leaf1)
306 self.assertEqual(
307 Diagnostics.run(leaf1.join(leaf2), engine.is_relation_nonempty),
308 Diagnostics(
309 is_doomed=True,
310 messages=["Relation 'leaf1' has no rows (executed)."],
311 ),
312 )
313 self.assertEqual(
314 Diagnostics.run(leaf1.join(leaf4), engine.is_relation_nonempty),
315 Diagnostics(
316 is_doomed=True,
317 messages=[
318 "Relation 'leaf1' has no rows (executed).",
319 "Relation 'leaf4' has no rows (static).",
320 ],
321 ),
322 )
323 engine.empty.add(leaf2)
324 self.assertEqual(
325 Diagnostics.run(leaf1.join(leaf2), engine.is_relation_nonempty),
326 Diagnostics(
327 is_doomed=True,
328 messages=[
329 "Relation 'leaf1' has no rows (executed).",
330 "Relation 'leaf2' has no rows (executed).",
331 ],
332 ),
333 )
334 engine.empty.clear()
335 self.assertEqual(
336 Diagnostics.run(leaf1.join(leaf2, Predicate.literal(False))),
337 Diagnostics(
338 is_doomed=True,
339 messages=["Join predicate 'False' is trivially false in 'leaf1 ⋈ leaf2'."],
340 ),
341 )
342 joined = leaf1.join(leaf2)
343 engine.empty.add(joined)
344 self.assertEqual(
345 Diagnostics.run(joined, engine.is_relation_nonempty),
346 Diagnostics(
347 is_doomed=True,
348 messages=["Operation ⋈ yields no results when executed: 'leaf1 ⋈ leaf2'"],
349 ),
350 )
353if __name__ == "__main__": 353 ↛ 354line 353 didn't jump to line 354, because the condition on line 353 was never true
354 unittest.main()