def testPickling(self): # Pickling and copying should always yield the exact same object within # a single process (cross-process is impossible to test here). universe1 = DimensionUniverse() universe2 = pickle.loads(pickle.dumps(universe1)) universe3 = copy.copy(universe1) universe4 = copy.deepcopy(universe1) self.assertIs(universe1, universe2) self.assertIs(universe1, universe3) self.assertIs(universe1, universe4) for element1 in universe1.getStaticElements(): element2 = pickle.loads(pickle.dumps(element1)) self.assertIs(element1, element2) graph1 = element1.graph graph2 = pickle.loads(pickle.dumps(graph1)) self.assertIs(graph1, graph2)
class DimensionTestCase(unittest.TestCase): """Tests for dimensions. All tests here rely on the content of ``config/dimensions.yaml``, either to test that the definitions there are read in properly or just as generic data for testing various operations. """ def setUp(self): self.universe = DimensionUniverse() def checkGraphInvariants(self, graph): elements = list(graph.elements) for n, element in enumerate(elements): # Ordered comparisons on graphs behave like sets. self.assertLessEqual(element.graph, graph) # Ordered comparisons on elements correspond to the ordering within # a DimensionUniverse (topological, with deterministic # tiebreakers). for other in elements[:n]: self.assertLess(other, element) self.assertLessEqual(other, element) for other in elements[n + 1:]: self.assertGreater(other, element) self.assertGreaterEqual(other, element) if isinstance(element, Dimension): self.assertEqual(element.graph.required, element.required) self.assertEqual(DimensionGraph(self.universe, graph.required), graph) self.assertCountEqual(graph.required, [dimension for dimension in graph.dimensions if not any(dimension in other.graph.implied for other in graph.elements)]) self.assertCountEqual(graph.implied, graph.dimensions - graph.required) self.assertCountEqual(graph.dimensions, [element for element in graph.elements if isinstance(element, Dimension)]) self.assertCountEqual(graph.dimensions, itertools.chain(graph.required, graph.implied)) # Check primary key traversal order: each element should follow any it # requires, and element that is implied by any other in the graph # follow at least one of those. seen = NamedValueSet() for element in graph.primaryKeyTraversalOrder: with self.subTest(required=graph.required, implied=graph.implied, element=element): seen.add(element) self.assertLessEqual(element.graph.required, seen) if element in graph.implied: self.assertTrue(any(element in s.implied for s in seen)) self.assertCountEqual(seen, graph.elements) def testConfigPresent(self): config = self.universe.dimensionConfig self.assertIsInstance(config, DimensionConfig) def testConfigRead(self): self.assertEqual(self.universe.getStaticDimensions().names, {"instrument", "visit", "visit_system", "exposure", "detector", "physical_filter", "band", "subfilter", "skymap", "tract", "patch"} | {f"htm{level}" for level in range(25)}) def testGraphs(self): self.checkGraphInvariants(self.universe.empty) for element in self.universe.getStaticElements(): self.checkGraphInvariants(element.graph) def testInstrumentDimensions(self): graph = DimensionGraph(self.universe, names=("exposure", "detector", "visit")) self.assertCountEqual(graph.dimensions.names, ("instrument", "exposure", "detector", "visit", "physical_filter", "band", "visit_system")) self.assertCountEqual(graph.required.names, ("instrument", "exposure", "detector", "visit")) self.assertCountEqual(graph.implied.names, ("physical_filter", "band", "visit_system")) self.assertCountEqual(graph.elements.names - graph.dimensions.names, ("visit_detector_region", "visit_definition")) self.assertCountEqual(graph.governors.names, {"instrument"}) def testCalibrationDimensions(self): graph = DimensionGraph(self.universe, names=("physical_filter", "detector")) self.assertCountEqual(graph.dimensions.names, ("instrument", "detector", "physical_filter", "band")) self.assertCountEqual(graph.required.names, ("instrument", "detector", "physical_filter")) self.assertCountEqual(graph.implied.names, ("band",)) self.assertCountEqual(graph.elements.names, graph.dimensions.names) self.assertCountEqual(graph.governors.names, {"instrument"}) def testObservationDimensions(self): graph = DimensionGraph(self.universe, names=("exposure", "detector", "visit")) self.assertCountEqual(graph.dimensions.names, ("instrument", "detector", "visit", "exposure", "physical_filter", "band", "visit_system")) self.assertCountEqual(graph.required.names, ("instrument", "detector", "exposure", "visit")) self.assertCountEqual(graph.implied.names, ("physical_filter", "band", "visit_system")) self.assertCountEqual(graph.elements.names - graph.dimensions.names, ("visit_detector_region", "visit_definition")) self.assertCountEqual(graph.spatial.names, ("observation_regions",)) self.assertCountEqual(graph.temporal.names, ("observation_timespans",)) self.assertCountEqual(graph.governors.names, {"instrument"}) self.assertEqual(graph.spatial.names, {"observation_regions"}) self.assertEqual(graph.temporal.names, {"observation_timespans"}) self.assertEqual(next(iter(graph.spatial)).governor, self.universe["instrument"]) self.assertEqual(next(iter(graph.temporal)).governor, self.universe["instrument"]) def testSkyMapDimensions(self): graph = DimensionGraph(self.universe, names=("patch",)) self.assertCountEqual(graph.dimensions.names, ("skymap", "tract", "patch")) self.assertCountEqual(graph.required.names, ("skymap", "tract", "patch")) self.assertCountEqual(graph.implied.names, ()) self.assertCountEqual(graph.elements.names, graph.dimensions.names) self.assertCountEqual(graph.spatial.names, ("skymap_regions",)) self.assertCountEqual(graph.governors.names, {"skymap"}) self.assertEqual(graph.spatial.names, {"skymap_regions"}) self.assertEqual(next(iter(graph.spatial)).governor, self.universe["skymap"]) def testSubsetCalculation(self): """Test that independent spatial and temporal options are computed correctly. """ graph = DimensionGraph(self.universe, names=("visit", "detector", "tract", "patch", "htm7", "exposure")) self.assertCountEqual(graph.spatial.names, ("observation_regions", "skymap_regions", "htm")) self.assertCountEqual(graph.temporal.names, ("observation_timespans",)) def testSchemaGeneration(self): tableSpecs = NamedKeyDict({}) for element in self.universe.getStaticElements(): if element.hasTable and element.viewOf is None: tableSpecs[element] = element.RecordClass.fields.makeTableSpec( RegionReprClass=SpatialRegionDatabaseRepresentation, TimespanReprClass=TimespanDatabaseRepresentation.Compound ) for element, tableSpec in tableSpecs.items(): for dep in element.required: with self.subTest(element=element.name, dep=dep.name): if dep != element: self.assertIn(dep.name, tableSpec.fields) self.assertEqual(tableSpec.fields[dep.name].dtype, dep.primaryKey.dtype) self.assertEqual(tableSpec.fields[dep.name].length, dep.primaryKey.length) self.assertEqual(tableSpec.fields[dep.name].nbytes, dep.primaryKey.nbytes) self.assertFalse(tableSpec.fields[dep.name].nullable) self.assertTrue(tableSpec.fields[dep.name].primaryKey) else: self.assertIn(element.primaryKey.name, tableSpec.fields) self.assertEqual(tableSpec.fields[element.primaryKey.name].dtype, dep.primaryKey.dtype) self.assertEqual(tableSpec.fields[element.primaryKey.name].length, dep.primaryKey.length) self.assertEqual(tableSpec.fields[element.primaryKey.name].nbytes, dep.primaryKey.nbytes) self.assertFalse(tableSpec.fields[element.primaryKey.name].nullable) self.assertTrue(tableSpec.fields[element.primaryKey.name].primaryKey) for dep in element.implied: with self.subTest(element=element.name, dep=dep.name): self.assertIn(dep.name, tableSpec.fields) self.assertEqual(tableSpec.fields[dep.name].dtype, dep.primaryKey.dtype) self.assertFalse(tableSpec.fields[dep.name].primaryKey) for foreignKey in tableSpec.foreignKeys: self.assertIn(foreignKey.table, tableSpecs) self.assertIn(foreignKey.table, element.graph.dimensions.names) self.assertEqual(len(foreignKey.source), len(foreignKey.target)) for source, target in zip(foreignKey.source, foreignKey.target): self.assertIn(source, tableSpec.fields.names) self.assertIn(target, tableSpecs[foreignKey.table].fields.names) self.assertEqual(tableSpec.fields[source].dtype, tableSpecs[foreignKey.table].fields[target].dtype) self.assertEqual(tableSpec.fields[source].length, tableSpecs[foreignKey.table].fields[target].length) self.assertEqual(tableSpec.fields[source].nbytes, tableSpecs[foreignKey.table].fields[target].nbytes) def testPickling(self): # Pickling and copying should always yield the exact same object within # a single process (cross-process is impossible to test here). universe1 = DimensionUniverse() universe2 = pickle.loads(pickle.dumps(universe1)) universe3 = copy.copy(universe1) universe4 = copy.deepcopy(universe1) self.assertIs(universe1, universe2) self.assertIs(universe1, universe3) self.assertIs(universe1, universe4) for element1 in universe1.getStaticElements(): element2 = pickle.loads(pickle.dumps(element1)) self.assertIs(element1, element2) graph1 = element1.graph graph2 = pickle.loads(pickle.dumps(graph1)) self.assertIs(graph1, graph2)