Coverage for tests / test_testRepo.py: 23%
119 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-18 08:43 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-18 08:43 +0000
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 software is dual licensed under the GNU General Public License and also
10# under a 3-clause BSD license. Recipients may choose which of these licenses
11# to use; please see the files gpl-3.0.txt and/or bsd_license.txt,
12# respectively. If you choose the GPL option then the following text applies
13# (but note that there is still no warranty even if you opt for BSD instead):
14#
15# This program is free software: you can redistribute it and/or modify
16# it under the terms of the GNU General Public License as published by
17# the Free Software Foundation, either version 3 of the License, or
18# (at your option) any later version.
19#
20# This program is distributed in the hope that it will be useful,
21# but WITHOUT ANY WARRANTY; without even the implied warranty of
22# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23# GNU General Public License for more details.
24#
25# You should have received a copy of the GNU General Public License
26# along with this program. If not, see <http://www.gnu.org/licenses/>.
28"""Unit tests for `lsst.daf.butler.tests.testRepo`, a module for creating
29test repositories or butlers.
30"""
32import os
33import shutil
34import unittest
36import lsst.daf.butler
37from lsst.daf.butler.tests import (
38 MetricsExample,
39 addDataIdValue,
40 addDatasetType,
41 expandUniqueId,
42 makeTestCollection,
43 makeTestRepo,
44 registerMetricsExample,
45)
46from lsst.daf.butler.tests.utils import makeTestTempDir, removeTestTempDir, safeTestTempDir
48TESTDIR = os.path.abspath(os.path.dirname(__file__))
51class ButlerTestRepoTestCase(unittest.TestCase):
52 """Simpler test than below without setUpClass getting in the way."""
54 def setUp(self):
55 self.root = makeTestTempDir(TESTDIR)
57 def tearDown(self):
58 removeTestTempDir(self.root)
60 def testMakeTestRepo(self):
61 dataIds = {
62 "instrument": ["DummyCam"],
63 "physical_filter": ["d-r"],
64 "exposure": [42, 43, 44],
65 "visit": [42, 43, 44],
66 }
68 butler = makeTestRepo(self.root, dataIds)
69 self.enterContext(butler)
71 records = list(butler.registry.queryDimensionRecords("visit"))
72 self.assertEqual(len(records), 3)
75class ButlerUtilsTestSuite(unittest.TestCase):
76 """Test the butler test utilities."""
78 @classmethod
79 def setUpClass(cls):
80 # Repository should be re-created for each test case, but
81 # this has a prohibitive run-time cost at present
82 cls.root = makeTestTempDir(TESTDIR)
84 cls.creatorButler = makeTestRepo(cls.root)
85 cls.enterClassContext(cls.creatorButler)
86 addDataIdValue(cls.creatorButler, "instrument", "notACam")
87 addDataIdValue(cls.creatorButler, "instrument", "dummyCam")
88 addDataIdValue(cls.creatorButler, "physical_filter", "k2020", band="k", instrument="notACam")
89 addDataIdValue(cls.creatorButler, "physical_filter", "l2019", instrument="dummyCam")
90 addDataIdValue(cls.creatorButler, "visit", 101, instrument="notACam", physical_filter="k2020")
91 addDataIdValue(cls.creatorButler, "visit", 102, instrument="notACam", physical_filter="k2020")
92 addDataIdValue(cls.creatorButler, "detector", 5)
93 # Leave skymap/patch/tract undefined so that tests can assume
94 # they're missing.
96 registerMetricsExample(cls.creatorButler)
97 addDatasetType(cls.creatorButler, "DataType1", {"instrument"}, "StructuredDataNoComponents")
98 addDatasetType(cls.creatorButler, "DataType2", {"instrument", "visit"}, "StructuredData")
100 @classmethod
101 def tearDownClass(cls):
102 # TODO: use addClassCleanup rather than tearDownClass in Python 3.8
103 # to keep the addition and removal together and make it more robust
104 removeTestTempDir(cls.root)
106 def setUp(self):
107 # TestCase.id() is unique for each test method
108 self.butler = makeTestCollection(self.creatorButler, uniqueId=self.id())
110 def testButlerValid(self):
111 self.butler.validateConfiguration()
113 def testButlerKwargs(self):
114 # outfile has the most obvious effects of any Butler.makeRepo keyword
115 with safeTestTempDir(TESTDIR) as temp:
116 path = os.path.join(temp, "oddConfig.json")
117 butler = makeTestRepo(temp, {}, outfile=path)
118 self.enterContext(butler)
119 self.assertTrue(os.path.isfile(path))
121 def _checkButlerDimension(self, dimensions, query, expected):
122 result = list(self.butler.registry.queryDataIds(dimensions, where=query, check=False))
123 self.assertEqual(len(result), 1)
124 self.assertIn(result[0].required, expected)
126 def testButlerDimensions(self):
127 self._checkButlerDimension(
128 {"instrument"}, "instrument='notACam'", [{"instrument": "notACam"}, {"instrument": "dummyCam"}]
129 )
130 self._checkButlerDimension(
131 {"visit", "instrument"},
132 "visit=101",
133 [{"instrument": "notACam", "visit": 101}, {"instrument": "dummyCam", "visit": 101}],
134 )
135 self._checkButlerDimension(
136 {"visit", "instrument"},
137 "visit=102",
138 [{"instrument": "notACam", "visit": 102}, {"instrument": "dummyCam", "visit": 102}],
139 )
140 self._checkButlerDimension(
141 {"detector", "instrument"},
142 "detector=5",
143 [{"instrument": "notACam", "detector": 5}, {"instrument": "dummyCam", "detector": 5}],
144 )
146 def testAddDataIdValue(self):
147 addDataIdValue(self.butler, "visit", 1, instrument="notACam", physical_filter="k2020")
148 self._checkButlerDimension(
149 {"visit", "instrument"}, "visit=1", [{"instrument": "notACam", "visit": 1}]
150 )
151 addDataIdValue(self.butler, "visit", 2, instrument="dummyCam", physical_filter="l2019")
152 self._checkButlerDimension(
153 {"visit", "instrument"}, "visit=2", [{"instrument": "dummyCam", "visit": 2}]
154 )
156 with self.assertRaises(ValueError):
157 addDataIdValue(self.butler, "NotADimension", 42)
158 with self.assertRaises(ValueError):
159 addDataIdValue(self.butler, "detector", "nonNumeric")
160 with self.assertRaises(ValueError):
161 addDataIdValue(self.butler, "detector", 101, nonsenseField="string")
163 # Keywords imply different instruments
164 with self.assertRaises(RuntimeError):
165 addDataIdValue(self.butler, "exposure", 101, instrument="dummyCam", physical_filter="k2020")
167 # No skymap defined
168 with self.assertRaises(RuntimeError):
169 addDataIdValue(self.butler, "tract", 42)
170 # Didn't create skymap "map" first.
171 with self.assertRaises(RuntimeError):
172 addDataIdValue(self.butler, "tract", 43, skymap="map")
174 def testAddDatasetType(self):
175 # 1 for StructuredDataNoComponents, 1 for StructuredData (components
176 # not included).
177 self.assertEqual(len(list(self.butler.registry.queryDatasetTypes())), 2)
179 # Testing the DatasetType objects is not practical, because all tests
180 # need a DimensionUniverse. So just check that we have the dataset
181 # types we expect.
182 self.butler.get_dataset_type("DataType1")
183 self.butler.get_dataset_type("DataType2")
185 with self.assertRaises(ValueError):
186 addDatasetType(self.butler, "DataType3", {"4thDimension"}, "NumpyArray")
187 with self.assertRaises(ValueError):
188 addDatasetType(self.butler, "DataType3", {"instrument"}, "UnstorableType")
190 def testRegisterMetricsExample(self):
191 id1 = {"instrument": "notACam"}
192 id2 = expandUniqueId(self.butler, {"visit": 101})
193 data = MetricsExample(summary={"answer": 42, "question": "unknown"})
195 self.butler.put(data, "DataType1", id1)
196 self.assertEqual(self.butler.get("DataType1", id1), data)
198 self.butler.put(data, "DataType2", id2)
199 self.assertEqual(self.butler.get("DataType2", id2), data)
200 self.assertEqual(self.butler.get("DataType2.summary", id2), data.summary)
202 def testRegisterMetricsExampleChained(self):
203 """Regression test for registerMetricsExample having no effect
204 on ChainedDatastore.
205 """
206 temp = makeTestTempDir(TESTDIR)
207 try:
208 config = lsst.daf.butler.Config()
209 config["datastore", "cls"] = "lsst.daf.butler.datastores.chainedDatastore.ChainedDatastore"
210 config["datastore", "datastores"] = [
211 {
212 "cls": "lsst.daf.butler.datastores.fileDatastore.FileDatastore",
213 }
214 ]
216 repo = lsst.daf.butler.Butler.makeRepo(temp, config=config)
217 butler = lsst.daf.butler.Butler.from_config(repo, run="chainedExample")
218 self.enterContext(butler)
219 registerMetricsExample(butler)
220 addDatasetType(butler, "DummyType", {}, "StructuredDataNoComponents")
222 data = MetricsExample(summary={})
223 # Should not raise
224 butler.put(data, "DummyType")
225 finally:
226 shutil.rmtree(temp, ignore_errors=True)
228 def testUniqueButler(self):
229 dataId = {"instrument": "notACam"}
230 ref = self.butler.put(MetricsExample({"answer": 42, "question": "unknown"}), "DataType1", dataId)
231 self.assertTrue(self.butler.exists("DataType1", dataId))
232 self.assertTrue(self.butler.exists(ref))
234 newButler = makeTestCollection(self.creatorButler)
236 # Can not be found in the new default collection.
237 self.assertFalse(newButler.exists("DataType1", dataId))
239 # The ref does exist in the new butler though.
240 self.assertTrue(newButler.exists(ref))
242 def testExpandUniqueId(self):
243 self.assertEqual(
244 expandUniqueId(self.butler, {"instrument": "notACam"}).required, {"instrument": "notACam"}
245 )
246 self.assertIn(
247 expandUniqueId(self.butler, {"visit": 101}).required,
248 [{"instrument": "notACam", "visit": 101}, {"instrument": "dummyCam", "visit": 101}],
249 )
250 self.assertIn(
251 expandUniqueId(self.butler, {"detector": 5}).required,
252 [{"instrument": "notACam", "detector": 5}, {"instrument": "dummyCam", "detector": 5}],
253 )
254 self.assertIn(
255 expandUniqueId(self.butler, {"physical_filter": "k2020"}).required,
256 [
257 {"instrument": "notACam", "physical_filter": "k2020"},
258 {"instrument": "notACam", "physical_filter": "k2020"},
259 ],
260 )
261 with self.assertRaises(ValueError):
262 expandUniqueId(self.butler, {"tract": 42})
265if __name__ == "__main__":
266 unittest.main()