Coverage for tests/test_apdbMetricTask.py: 33%
95 statements
« prev ^ index » next coverage.py v6.4.2, created at 2022-08-01 01:31 -0700
« prev ^ index » next coverage.py v6.4.2, created at 2022-08-01 01:31 -0700
1# This file is part of verify.
2#
3# Developed for the LSST Data Management System.
4# This product includes software developed by the LSST Project
5# (https://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 <https://www.gnu.org/licenses/>.
22import shutil
23import tempfile
24import unittest.mock
26import astropy.units as u
28import lsst.utils.tests
29from lsst.pex.config import Config
30import lsst.daf.butler.tests as butlerTests
31from lsst.pipe.base import Task, Struct, testUtils
33from lsst.verify import Measurement
34from lsst.verify.tasks import ApdbMetricTask, MetricComputationError
35from lsst.verify.tasks.testUtils import ApdbMetricTestCase
38class DummyTask(ApdbMetricTask):
39 _DefaultName = "NotARealTask"
41 def makeMeasurement(self, _dbHandle, outputDataId):
42 if outputDataId:
43 nChars = len(outputDataId["instrument"])
44 return Measurement(self.config.metricName,
45 nChars * u.dimensionless_unscaled)
46 else:
47 return Measurement(self.config.metricName,
48 0 * u.dimensionless_unscaled)
51class Gen3ApdbTestSuite(ApdbMetricTestCase):
52 @classmethod
53 def makeTask(cls):
54 class MockDbLoader(Task):
55 ConfigClass = Config
57 def run(self, _):
58 return Struct(apdb=unittest.mock.Mock())
60 config = DummyTask.ConfigClass()
61 config.dbLoader.retarget(MockDbLoader)
62 config.connections.package = "verify"
63 config.connections.metric = "DummyApdb"
64 config.validate()
65 return DummyTask(config=config)
67 @classmethod
68 def setUpClass(cls):
69 super().setUpClass()
71 cls.CAMERA_ID = "NotACam"
72 cls.VISIT_ID = 42
73 cls.CHIP_ID = 5
75 # makeTestRepo called in setUpClass because it's *very* slow
76 cls.root = tempfile.mkdtemp()
77 cls.repo = butlerTests.makeTestRepo(cls.root, {
78 "instrument": [cls.CAMERA_ID],
79 "visit": [cls.VISIT_ID],
80 "detector": [cls.CHIP_ID],
81 })
83 # self.task not visible at class level
84 task = cls.makeTask()
85 connections = task.config.ConnectionsClass(config=task.config)
87 butlerTests.addDatasetType(
88 cls.repo,
89 connections.measurement.name,
90 connections.measurement.dimensions,
91 connections.measurement.storageClass)
92 butlerTests.addDatasetType(
93 cls.repo,
94 connections.dbInfo.name,
95 connections.dbInfo.dimensions,
96 connections.dbInfo.storageClass)
98 @classmethod
99 def tearDownClass(cls):
100 shutil.rmtree(cls.root, ignore_errors=True)
101 super().tearDownClass()
103 def setUp(self):
104 super().setUp()
106 self.connections = self.task.config.ConnectionsClass(
107 config=self.task.config)
109 def _prepareQuantum(self, task):
110 globalId = {
111 "instrument": self.CAMERA_ID,
112 }
113 detectorId = {
114 "instrument": self.CAMERA_ID,
115 "visit": self.VISIT_ID,
116 "detector": self.CHIP_ID,
117 }
119 butler = butlerTests.makeTestCollection(self.repo)
120 # task.config not persistable if it refers to a local class
121 # We don't actually use the persisted config, so just make a new one
122 info = task.ConfigClass()
123 butler.put(info, "apdb_marker", detectorId)
125 quantum = testUtils.makeQuantum(
126 task, butler, globalId,
127 {"dbInfo": [detectorId], "measurement": globalId})
129 return (butler, quantum, info)
131 def testRunQuantum(self):
132 butler, quantum, input = self._prepareQuantum(self.task)
134 run = testUtils.runTestQuantum(self.task, butler, quantum)
136 # Did output data ID get passed to DummyTask.run?
137 expectedId = lsst.daf.butler.DataCoordinate.standardize(
138 {"instrument": self.CAMERA_ID},
139 universe=butler.registry.dimensions)
140 run.assert_called_once_with(
141 dbInfo=[input],
142 outputDataId=expectedId)
144 def testRunQuantumNone(self):
145 class NoneTask(DummyTask):
146 def run(self, *args, **kwargs):
147 return Struct(measurement=None)
149 config = NoneTask.ConfigClass()
150 config.connections.package = "verify"
151 config.connections.metric = "DummyApdb"
152 task = NoneTask(config=config)
153 butler, quantum, input = self._prepareQuantum(task)
155 with unittest.mock.patch.object(
156 lsst.pipe.base.ButlerQuantumContext, "put") as put:
157 testUtils.runTestQuantum(task, butler, quantum, mockRun=False)
158 # Should not attempt to write nonexistent data
159 put.assert_not_called()
161 def testRunQuantumException(self):
162 class ExceptionalTask(DummyTask):
163 def run(self, *args, **kwargs):
164 raise MetricComputationError()
166 config = ExceptionalTask.ConfigClass()
167 config.connections.package = "verify"
168 config.connections.metric = "DummyApdb"
169 task = ExceptionalTask(config=config)
170 butler, quantum, input = self._prepareQuantum(task)
172 with unittest.mock.patch.object(
173 lsst.pipe.base.ButlerQuantumContext, "put") as put:
174 testUtils.runTestQuantum(task, butler, quantum, mockRun=False)
175 # Should not propagate MetricComputationError
176 # Should not attempt to write data that was never returned
177 put.assert_not_called()
180# Hack around unittest's hacky test setup system
181del ApdbMetricTestCase
184class MemoryTester(lsst.utils.tests.MemoryTestCase):
185 pass
188def setup_module(module):
189 lsst.utils.tests.init()
192if __name__ == "__main__": 192 ↛ 193line 192 didn't jump to line 193, because the condition on line 192 was never true
193 lsst.utils.tests.init()
194 unittest.main()