Coverage for tests/test_dimensions.py : 13%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# This file is part of daf_butler.
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/>.
22import unittest
23import copy
24import pickle
25import itertools
27from lsst.daf.butler.core.utils import NamedKeyDict, NamedValueSet
28from lsst.daf.butler.core.dimensions import DimensionUniverse, DimensionGraph, Dimension
29from lsst.daf.butler.core.dimensions.schema import makeElementTableSpec
32class DimensionTestCase(unittest.TestCase):
33 """Tests for dimensions.
35 All tests here rely on the content of ``config/dimensions.yaml``, either
36 to test that the definitions there are read in properly or just as generic
37 data for testing various operations.
38 """
40 def setUp(self):
41 self.universe = DimensionUniverse()
43 def checkGraphInvariants(self, graph):
44 elements = list(graph.elements)
45 for n, element in enumerate(elements):
46 # Ordered comparisons on graphs behave like sets.
47 self.assertLessEqual(element.graph, graph)
48 # Ordered comparisons on elements correspond to the ordering within
49 # a DimensionUniverse (topological, with deterministic
50 # tiebreakers).
51 for other in elements[:n]:
52 self.assertLess(other, element)
53 self.assertLessEqual(other, element)
54 for other in elements[n + 1:]:
55 self.assertGreater(other, element)
56 self.assertGreaterEqual(other, element)
57 if isinstance(element, Dimension):
58 self.assertEqual(element.graph.required, element.required)
59 self.assertEqual(DimensionGraph(self.universe, graph.required), graph)
60 self.assertCountEqual(graph.required,
61 [dimension for dimension in graph.dimensions
62 if not any(dimension in other.graph.implied for other in graph.elements)])
63 self.assertCountEqual(graph.implied, graph.dimensions - graph.required)
64 self.assertCountEqual(graph.dimensions,
65 [element for element in graph.elements
66 if isinstance(element, Dimension)])
67 self.assertCountEqual(graph.dimensions, itertools.chain(graph.required, graph.implied))
68 # Check primary key traversal order: each element should follow any it
69 # requires, and element that is implied by any other in the graph
70 # follow at least one of those.
71 seen = NamedValueSet()
72 for element in graph.primaryKeyTraversalOrder:
73 with self.subTest(required=graph.required, implied=graph.implied, element=element):
74 seen.add(element)
75 self.assertLessEqual(element.graph.required, seen)
76 if element in graph.implied:
77 self.assertTrue(any(element in s.implied for s in seen))
78 self.assertCountEqual(seen, graph.elements)
80 def testConfigRead(self):
81 self.assertEqual(self.universe.dimensions.names,
82 {"instrument", "visit", "visit_system", "exposure", "detector",
83 "physical_filter", "abstract_filter", "subfilter", "calibration_label",
84 "skymap", "tract", "patch", "htm7", "htm9"})
86 def testGraphs(self):
87 self.checkGraphInvariants(self.universe.empty)
88 self.checkGraphInvariants(self.universe)
89 for element in self.universe.elements:
90 self.checkGraphInvariants(element.graph)
92 def testInstrumentDimensions(self):
93 graph = DimensionGraph(self.universe, names=("exposure", "detector", "visit", "calibration_label"))
94 self.assertCountEqual(graph.dimensions.names,
95 ("instrument", "exposure", "detector", "calibration_label",
96 "visit", "physical_filter", "abstract_filter", "visit_system"))
97 self.assertCountEqual(graph.required.names, ("instrument", "exposure", "detector",
98 "calibration_label", "visit"))
99 self.assertCountEqual(graph.implied.names, ("physical_filter", "abstract_filter", "visit_system"))
100 self.assertCountEqual(graph.elements.names - graph.dimensions.names,
101 ("visit_detector_region", "visit_definition"))
103 def testCalibrationDimensions(self):
104 graph = DimensionGraph(self.universe, names=("calibration_label", "physical_filter", "detector"))
105 self.assertCountEqual(graph.dimensions.names,
106 ("instrument", "detector", "calibration_label",
107 "physical_filter", "abstract_filter"))
108 self.assertCountEqual(graph.required.names, ("instrument", "detector", "calibration_label",
109 "physical_filter"))
110 self.assertCountEqual(graph.implied.names, ("abstract_filter",))
111 self.assertCountEqual(graph.elements.names, graph.dimensions.names)
113 def testObservationDimensions(self):
114 graph = DimensionGraph(self.universe, names=("exposure", "detector", "visit"))
115 self.assertCountEqual(graph.dimensions.names, ("instrument", "detector", "visit", "exposure",
116 "physical_filter", "abstract_filter", "visit_system"))
117 self.assertCountEqual(graph.required.names, ("instrument", "detector", "exposure", "visit"))
118 self.assertCountEqual(graph.implied.names, ("physical_filter", "abstract_filter", "visit_system"))
119 self.assertCountEqual(graph.elements.names - graph.dimensions.names,
120 ("visit_detector_region", "visit_definition"))
121 self.assertCountEqual(graph.spatial.names, ("visit_detector_region",))
122 self.assertCountEqual(graph.temporal.names, ("exposure",))
124 def testSkyMapDimensions(self):
125 graph = DimensionGraph(self.universe, names=("patch",))
126 self.assertCountEqual(graph.dimensions.names, ("skymap", "tract", "patch"))
127 self.assertCountEqual(graph.required.names, ("skymap", "tract", "patch"))
128 self.assertCountEqual(graph.implied.names, ())
129 self.assertCountEqual(graph.elements.names, graph.dimensions.names)
130 self.assertCountEqual(graph.spatial.names, ("patch",))
132 def testSubsetCalculation(self):
133 """Test that independent spatial and temporal options are computed
134 correctly.
135 """
136 graph = DimensionGraph(self.universe, names=("visit", "detector", "tract", "patch", "htm7",
137 "exposure", "calibration_label"))
138 self.assertCountEqual(graph.spatial.names,
139 ("visit_detector_region", "patch", "htm7"))
140 self.assertCountEqual(graph.temporal.names,
141 ("exposure", "calibration_label"))
143 def testSchemaGeneration(self):
144 tableSpecs = NamedKeyDict({})
145 for element in self.universe.elements:
146 if element.hasTable and element.viewOf is None:
147 tableSpecs[element] = makeElementTableSpec(element)
148 for element, tableSpec in tableSpecs.items():
149 for dep in element.required:
150 with self.subTest(element=element.name, dep=dep.name):
151 if dep != element:
152 self.assertIn(dep.name, tableSpec.fields)
153 self.assertEqual(tableSpec.fields[dep.name].dtype, dep.primaryKey.dtype)
154 self.assertEqual(tableSpec.fields[dep.name].length, dep.primaryKey.length)
155 self.assertEqual(tableSpec.fields[dep.name].nbytes, dep.primaryKey.nbytes)
156 self.assertFalse(tableSpec.fields[dep.name].nullable)
157 self.assertTrue(tableSpec.fields[dep.name].primaryKey)
158 else:
159 self.assertIn(element.primaryKey.name, tableSpec.fields)
160 self.assertEqual(tableSpec.fields[element.primaryKey.name].dtype,
161 dep.primaryKey.dtype)
162 self.assertEqual(tableSpec.fields[element.primaryKey.name].length,
163 dep.primaryKey.length)
164 self.assertEqual(tableSpec.fields[element.primaryKey.name].nbytes,
165 dep.primaryKey.nbytes)
166 self.assertFalse(tableSpec.fields[element.primaryKey.name].nullable)
167 self.assertTrue(tableSpec.fields[element.primaryKey.name].primaryKey)
168 for dep in element.implied:
169 with self.subTest(element=element.name, dep=dep.name):
170 self.assertIn(dep.name, tableSpec.fields)
171 self.assertEqual(tableSpec.fields[dep.name].dtype, dep.primaryKey.dtype)
172 self.assertFalse(tableSpec.fields[dep.name].primaryKey)
173 for foreignKey in tableSpec.foreignKeys:
174 self.assertIn(foreignKey.table, tableSpecs)
175 self.assertIn(foreignKey.table, element.graph.dimensions.names)
176 self.assertEqual(len(foreignKey.source), len(foreignKey.target))
177 for source, target in zip(foreignKey.source, foreignKey.target):
178 self.assertIn(source, tableSpec.fields.names)
179 self.assertIn(target, tableSpecs[foreignKey.table].fields.names)
180 self.assertEqual(tableSpec.fields[source].dtype,
181 tableSpecs[foreignKey.table].fields[target].dtype)
182 self.assertEqual(tableSpec.fields[source].length,
183 tableSpecs[foreignKey.table].fields[target].length)
184 self.assertEqual(tableSpec.fields[source].nbytes,
185 tableSpecs[foreignKey.table].fields[target].nbytes)
186 self.assertEqual(tuple(tableSpec.fields.names), element.RecordClass.__slots__)
188 def testPickling(self):
189 # Pickling and copying should always yield the exact same object within
190 # a single process (cross-process is impossible to test here).
191 universe1 = DimensionUniverse()
192 universe2 = pickle.loads(pickle.dumps(universe1))
193 universe3 = copy.copy(universe1)
194 universe4 = copy.deepcopy(universe1)
195 self.assertIs(universe1, universe2)
196 self.assertIs(universe1, universe3)
197 self.assertIs(universe1, universe4)
198 for element1 in universe1.elements:
199 element2 = pickle.loads(pickle.dumps(element1))
200 self.assertIs(element1, element2)
201 graph1 = element1.graph
202 graph2 = pickle.loads(pickle.dumps(graph1))
203 self.assertIs(graph1, graph2)
206if __name__ == "__main__": 206 ↛ 207line 206 didn't jump to line 207, because the condition on line 206 was never true
207 unittest.main()