Coverage for tests/test_diagnostics.py: 21%
90 statements
« prev ^ index » next coverage.py v7.3.0, created at 2023-08-16 09:55 +0000
« prev ^ index » next coverage.py v7.3.0, created at 2023-08-16 09:55 +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/>.
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 """
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 )
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 )
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 )
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 )
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 )
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 )
354if __name__ == "__main__":
355 unittest.main()