Coverage for tests/test_cliCmdQueryDataIds.py: 30%
69 statements
« prev ^ index » next coverage.py v7.11.0, created at 2025-11-13 01:31 -0800
« prev ^ index » next coverage.py v7.11.0, created at 2025-11-13 01:31 -0800
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 daf_butler CLI query-collections command."""
30import os
31import unittest
33from astropy.table import Table as AstropyTable
34from numpy import array
36from lsst.daf.butler import Butler, DatasetType, script
37from lsst.daf.butler.direct_butler import DirectButler
38from lsst.daf.butler.tests.utils import ButlerTestHelper, makeTestTempDir, removeTestTempDir
40TESTDIR = os.path.abspath(os.path.dirname(__file__))
43class QueryDataIdsTest(unittest.TestCase, ButlerTestHelper):
44 """Test the query-data-ids command-line."""
46 mockFuncName = "lsst.daf.butler.cli.cmd.commands.script.queryDataIds"
48 @staticmethod
49 def _queryDataIds(repo, dimensions=(), collections=(), datasets=None, where=""):
50 """Call script.queryDataIds, allowing for default values."""
51 return script.queryDataIds(
52 repo=repo,
53 dimensions=dimensions,
54 collections=collections,
55 datasets=datasets,
56 where=where,
57 order_by=None,
58 limit=0,
59 offset=0,
60 )
62 def setUp(self):
63 self.root = makeTestTempDir(TESTDIR)
64 self.repo = Butler.makeRepo(self.root)
66 def tearDown(self):
67 removeTestTempDir(self.root)
69 def loadData(self, *filenames: str) -> Butler:
70 """Load registry test data from ``TESTDIR/data/registry/<filename>``,
71 which should be a YAML import/export file.
72 """
73 butler = Butler.from_config(self.repo, writeable=True)
74 assert isinstance(butler, DirectButler), "Test expects DirectButler"
75 for filename in filenames:
76 butler.import_(
77 filename=f"resource://lsst.daf.butler/tests/registry_data/{filename}",
78 without_datastore=True,
79 )
80 return butler
82 def testDimensions(self):
83 """Test getting a dimension."""
84 self.loadData("base.yaml")
85 res, msg = self._queryDataIds(self.root, dimensions=("detector",))
86 expected = AstropyTable(
87 array((("Cam1", 1), ("Cam1", 2), ("Cam1", 3), ("Cam1", 4))), names=("instrument", "detector")
88 )
89 self.assertFalse(msg)
90 self.assertAstropyTablesEqual(res, expected)
92 def testNoDimensions(self):
93 """Test asking for no dimensions."""
94 res, msg = self._queryDataIds(self.root)
95 self.assertIsNone(res, msg)
96 self.assertEqual(
97 msg, "Result has one logical row but no columns because no dimensions were requested."
98 )
100 def testNoResultsEasy(self):
101 """Test getting no results in a way that's detectable without having
102 to execute the full query.
103 """
104 self.loadData("base.yaml", "spatial.yaml")
105 res, msg = self._queryDataIds(
106 self.root,
107 dimensions=("visit", "tract"),
108 where="instrument='Cam1' AND skymap='SkyMap1' AND visit=1 AND tract=1",
109 )
110 self.assertIsNone(res, msg)
111 self.assertEqual(msg, "")
113 def testNoResultsHard(self):
114 """Test getting no results in a way that can't be detected unless we
115 run the whole query.
116 """
117 self.loadData("base.yaml", "spatial.yaml")
118 res, msg = self._queryDataIds(
119 self.root,
120 dimensions=("visit", "tract"),
121 where="instrument='Cam1' AND skymap='SkyMap1' AND visit=1 AND tract=0 AND patch=5",
122 )
123 self.assertIsNone(res, msg)
124 self.assertIn("Post-query region filtering removed all rows", msg)
126 def testWhere(self):
127 """Test with a WHERE constraint."""
128 self.loadData("base.yaml")
129 res, msg = self._queryDataIds(
130 self.root, dimensions=("detector",), where="instrument='Cam1' AND detector=2"
131 )
132 expected = AstropyTable(
133 array((("Cam1", 2),)),
134 names=(
135 "instrument",
136 "detector",
137 ),
138 )
139 self.assertAstropyTablesEqual(res, expected)
140 self.assertIsNone(msg)
142 def testDatasetsAndCollections(self):
143 """Test constraining via datasets and collections."""
144 butler = self.loadData("base.yaml", "datasets.yaml")
145 # See that the data IDs returned are constrained by that collection's
146 # contents.
147 res, msg = self._queryDataIds(
148 repo=self.root, dimensions=("detector",), collections=("imported_g",), datasets="bias"
149 )
150 expected = AstropyTable(
151 array((("Cam1", 1), ("Cam1", 2), ("Cam1", 3))),
152 names=(
153 "instrument",
154 "detector",
155 ),
156 )
157 self.assertAstropyTablesEqual(res, expected)
158 self.assertIsNone(msg)
160 # Check that the dimensions are inferred when not provided.
161 with self.assertLogs("lsst.daf.butler.script.queryDataIds", "INFO") as cm:
162 res, msg = self._queryDataIds(repo=self.root, collections=("imported_g",), datasets="bias")
163 self.assertIn("Determined dimensions", "\n".join(cm.output))
164 self.assertAstropyTablesEqual(res, expected)
165 self.assertIsNone(msg)
167 # Check that we get a reason if no dimensions can be inferred.
168 new_dataset_type = DatasetType(
169 "test_metric_dimensionless",
170 (),
171 "StructuredDataDict",
172 universe=butler.dimensions,
173 )
174 butler.registry.registerDatasetType(new_dataset_type)
175 res, msg = self._queryDataIds(repo=self.root, collections=("imported_g",), datasets=...)
176 self.assertIsNone(res)
177 self.assertIn("No dimensions in common", msg)
179 # Check that we get a reason returned if no dataset is found in
180 # collection.
181 res, msg = self._queryDataIds(
182 repo=self.root,
183 dimensions=("detector",),
184 collections=("imported_g",),
185 datasets="test_metric_dimensionless",
186 )
187 self.assertIsNone(res)
188 self.assertIn("No datasets of type 'test_metric_dimensionless'", msg)
191if __name__ == "__main__":
192 unittest.main()