Coverage for tests/test_metrics.py: 30%
231 statements
« prev ^ index » next coverage.py v7.5.1, created at 2024-05-10 10:38 +0000
« prev ^ index » next coverage.py v7.5.1, created at 2024-05-10 10:38 +0000
1# This file is part of ap_association.
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 unittest
23import unittest.mock
25import astropy.units as u
27import lsst.utils.tests
28from lsst.pex.config import Config
29from lsst.daf.base import PropertySet
30from lsst.dax.apdb import Apdb
31import lsst.pipe.base.testUtils
32from lsst.verify import Name
33from lsst.verify.tasks import MetricComputationError
34from lsst.verify.tasks.testUtils import MetricTaskTestCase, MetadataMetricTestCase, ApdbMetricTestCase
36from lsst.ap.association.metrics import \
37 NumberNewDiaObjectsMetricTask, \
38 NumberUnassociatedDiaObjectsMetricTask, \
39 FractionUpdatedDiaObjectsMetricTask, \
40 NumberSolarSystemObjectsMetricTask, \
41 NumberAssociatedSolarSystemObjectsMetricTask, \
42 TotalUnassociatedDiaObjectsMetricTask
45def _makeAssociationMetadata(numUpdated=27, numNew=4, numUnassociated=15, numSso=5, numAssocSso=1):
46 metadata = PropertySet()
47 metadata.add("association.numUpdatedDiaObjects", numUpdated)
48 metadata.add("association.numNewDiaObjects", numNew)
49 metadata.add("association.numTotalSolarSystemObjects", numSso)
50 metadata.add("association.numAssociatedSsObjects", numAssocSso)
51 metadata.add("association.numUnassociatedDiaObjects", numUnassociated)
52 return metadata
55class TestNewDiaObjects(MetadataMetricTestCase):
57 @classmethod
58 def makeTask(cls):
59 return NumberNewDiaObjectsMetricTask()
61 def testValid(self):
62 metadata = _makeAssociationMetadata()
63 result = self.task.run(metadata)
64 lsst.pipe.base.testUtils.assertValidOutput(self.task, result)
65 meas = result.measurement
67 self.assertEqual(meas.metric_name, Name(metric="ap_association.numNewDiaObjects"))
68 self.assertEqual(meas.quantity, metadata.getAsDouble("association.numNewDiaObjects") * u.count)
70 def testNoNew(self):
71 metadata = _makeAssociationMetadata(numNew=0)
72 result = self.task.run(metadata)
73 lsst.pipe.base.testUtils.assertValidOutput(self.task, result)
74 meas = result.measurement
76 self.assertEqual(meas.metric_name, Name(metric="ap_association.numNewDiaObjects"))
77 self.assertEqual(meas.quantity, 0.0 * u.count)
79 def testAssociationFailed(self):
80 try:
81 result = self.task.run(PropertySet())
82 except lsst.pipe.base.NoWorkFound:
83 # Correct behavior
84 pass
85 else:
86 # Alternative correct behavior
87 lsst.pipe.base.testUtils.assertValidOutput(self.task, result)
88 meas = result.measurement
89 self.assertIsNone(meas)
91 def testBadlyTypedKeys(self):
92 metadata = _makeAssociationMetadata()
93 metadata.set("association.numNewDiaObjects", "Ultimate Answer")
95 with self.assertRaises(MetricComputationError):
96 self.task.run(metadata)
99class TestUnassociatedDiaObjects(MetadataMetricTestCase):
101 @classmethod
102 def makeTask(cls):
103 return NumberUnassociatedDiaObjectsMetricTask()
105 def testValid(self):
106 metadata = _makeAssociationMetadata()
107 result = self.task.run(metadata)
108 lsst.pipe.base.testUtils.assertValidOutput(self.task, result)
109 meas = result.measurement
111 self.assertEqual(meas.metric_name, Name(metric="ap_association.numUnassociatedDiaObjects"))
112 self.assertEqual(meas.quantity,
113 metadata.getAsDouble("association.numUnassociatedDiaObjects") * u.count)
115 def testAllUpdated(self):
116 metadata = _makeAssociationMetadata(numUnassociated=0)
117 result = self.task.run(metadata)
118 lsst.pipe.base.testUtils.assertValidOutput(self.task, result)
119 meas = result.measurement
121 self.assertEqual(meas.metric_name, Name(metric="ap_association.numUnassociatedDiaObjects"))
122 self.assertEqual(meas.quantity, 0.0 * u.count)
124 def testAssociationFailed(self):
125 try:
126 result = self.task.run(PropertySet())
127 except lsst.pipe.base.NoWorkFound:
128 # Correct behavior
129 pass
130 else:
131 # Alternative correct behavior
132 lsst.pipe.base.testUtils.assertValidOutput(self.task, result)
133 meas = result.measurement
134 self.assertIsNone(meas)
136 def testBadlyTypedKeys(self):
137 metadata = _makeAssociationMetadata()
138 metadata.set("association.numUnassociatedDiaObjects", "Ultimate Answer")
140 with self.assertRaises(MetricComputationError):
141 self.task.run(metadata)
144class TestFracUpdatedDiaObjects(MetadataMetricTestCase):
146 @classmethod
147 def makeTask(cls):
148 return FractionUpdatedDiaObjectsMetricTask()
150 def testValid(self):
151 metadata = _makeAssociationMetadata()
152 result = self.task.run(metadata)
153 lsst.pipe.base.testUtils.assertValidOutput(self.task, result)
154 meas = result.measurement
156 self.assertEqual(meas.metric_name, Name(metric="ap_association.fracUpdatedDiaObjects"))
157 nUpdated = metadata.getAsDouble("association.numUpdatedDiaObjects")
158 nTotal = metadata.getAsDouble("association.numUnassociatedDiaObjects") + nUpdated
159 self.assertEqual(meas.quantity, nUpdated / nTotal * u.dimensionless_unscaled)
161 def testNoUpdated(self):
162 metadata = _makeAssociationMetadata(numUpdated=0)
163 result = self.task.run(metadata)
164 lsst.pipe.base.testUtils.assertValidOutput(self.task, result)
165 meas = result.measurement
167 self.assertEqual(meas.metric_name, Name(metric="ap_association.fracUpdatedDiaObjects"))
168 self.assertEqual(meas.quantity, 0.0 * u.dimensionless_unscaled)
170 def testAllUpdated(self):
171 metadata = _makeAssociationMetadata(numUnassociated=0)
172 result = self.task.run(metadata)
173 lsst.pipe.base.testUtils.assertValidOutput(self.task, result)
174 meas = result.measurement
176 self.assertEqual(meas.metric_name, Name(metric="ap_association.fracUpdatedDiaObjects"))
177 self.assertEqual(meas.quantity, 1.0 * u.dimensionless_unscaled)
179 def testNoObjects(self):
180 metadata = _makeAssociationMetadata(numUpdated=0, numUnassociated=0)
181 try:
182 result = self.task.run(metadata)
183 except lsst.pipe.base.NoWorkFound:
184 # Correct behavior
185 pass
186 else:
187 # Alternative correct behavior
188 lsst.pipe.base.testUtils.assertValidOutput(self.task, result)
189 meas = result.measurement
190 self.assertIsNone(meas)
192 def testAssociationFailed(self):
193 try:
194 result = self.task.run(PropertySet())
195 except lsst.pipe.base.NoWorkFound:
196 # Correct behavior
197 pass
198 else:
199 # Alternative correct behavior
200 lsst.pipe.base.testUtils.assertValidOutput(self.task, result)
201 meas = result.measurement
202 self.assertIsNone(meas)
204 def testBadlyTypedKeys(self):
205 metadata = _makeAssociationMetadata()
206 metadata.set("association.numUnassociatedDiaObjects", "Ultimate Answer")
208 with self.assertRaises(MetricComputationError):
209 self.task.run(metadata)
212class TestNumberSolarSystemObjects(MetadataMetricTestCase):
214 @classmethod
215 def makeTask(cls):
216 return NumberSolarSystemObjectsMetricTask()
218 def testValid(self):
219 metadata = _makeAssociationMetadata()
220 result = self.task.run(metadata)
221 lsst.pipe.base.testUtils.assertValidOutput(self.task, result)
222 meas = result.measurement
224 self.assertEqual(meas.metric_name, Name(metric="ap_association.numTotalSolarSystemObjects"))
225 self.assertEqual(meas.quantity,
226 metadata.getAsDouble("association.numTotalSolarSystemObjects") * u.count)
228 def testAllUpdated(self):
229 metadata = _makeAssociationMetadata(numSso=0)
230 result = self.task.run(metadata)
231 lsst.pipe.base.testUtils.assertValidOutput(self.task, result)
232 meas = result.measurement
234 self.assertEqual(meas.metric_name, Name(metric="ap_association.numTotalSolarSystemObjects"))
235 self.assertEqual(meas.quantity, 0.0 * u.count)
237 def testAssociationFailed(self):
238 try:
239 result = self.task.run(PropertySet())
240 except lsst.pipe.base.NoWorkFound:
241 # Correct behavior
242 pass
243 else:
244 # Alternative correct behavior
245 lsst.pipe.base.testUtils.assertValidOutput(self.task, result)
246 meas = result.measurement
247 self.assertIsNone(meas)
249 def testBadlyTypedKeys(self):
250 metadata = _makeAssociationMetadata()
251 metadata.set("association.numTotalSolarSystemObjects", "Ultimate Answer")
253 with self.assertRaises(MetricComputationError):
254 self.task.run(metadata)
257class TestNumberAssociatedSolarSystemObjects(MetadataMetricTestCase):
259 @classmethod
260 def makeTask(cls):
261 return NumberAssociatedSolarSystemObjectsMetricTask()
263 def testValid(self):
264 metadata = _makeAssociationMetadata()
265 result = self.task.run(metadata)
266 lsst.pipe.base.testUtils.assertValidOutput(self.task, result)
267 meas = result.measurement
269 self.assertEqual(meas.metric_name, Name(metric="ap_association.numAssociatedSsObjects"))
270 self.assertEqual(meas.quantity,
271 metadata.getAsDouble("association.numAssociatedSsObjects") * u.count)
273 def testAllUpdated(self):
274 metadata = _makeAssociationMetadata(numAssocSso=0)
275 result = self.task.run(metadata)
276 lsst.pipe.base.testUtils.assertValidOutput(self.task, result)
277 meas = result.measurement
279 self.assertEqual(meas.metric_name, Name(metric="ap_association.numAssociatedSsObjects"))
280 self.assertEqual(meas.quantity, 0.0 * u.count)
282 def testAssociationFailed(self):
283 try:
284 result = self.task.run(PropertySet())
285 except lsst.pipe.base.NoWorkFound:
286 # Correct behavior
287 pass
288 else:
289 # Alternative correct behavior
290 lsst.pipe.base.testUtils.assertValidOutput(self.task, result)
291 meas = result.measurement
292 self.assertIsNone(meas)
294 def testBadlyTypedKeys(self):
295 metadata = _makeAssociationMetadata()
296 metadata.set("association.numAssociatedSsObjects", "Ultimate Answer")
298 with self.assertRaises(MetricComputationError):
299 self.task.run(metadata)
302class TestTotalUnassociatedObjects(ApdbMetricTestCase):
304 @classmethod
305 def makeTask(cls):
306 config = TotalUnassociatedDiaObjectsMetricTask.ConfigClass()
307 config.doReadMarker = False
308 config.apdb_config_url = "dummy/path.yaml"
309 return TotalUnassociatedDiaObjectsMetricTask(config=config)
311 def setUp(self):
312 super().setUp()
314 # Default patch that applies to ApdbMetricTestCase's tests
315 loadPatcher = unittest.mock.patch(
316 "lsst.dax.apdb.Apdb.from_uri",
317 return_value=unittest.mock.Mock(Apdb, **{"countUnassociatedObjects.return_value": 42})
318 )
319 loadPatcher.start()
320 self.addCleanup(loadPatcher.stop)
322 def testValid(self):
323 result = self.task.run([Config()])
324 lsst.pipe.base.testUtils.assertValidOutput(self.task, result)
325 meas = result.measurement
327 self.assertEqual(meas.metric_name, Name(metric="ap_association.totalUnassociatedDiaObjects"))
328 self.assertEqual(meas.quantity, 42 * u.count)
330 def testAllAssociated(self):
331 with unittest.mock.patch(
332 "lsst.dax.apdb.Apdb.from_uri",
333 return_value=unittest.mock.Mock(Apdb, **{"countUnassociatedObjects.return_value": 0})
334 ):
335 result = self.task.run([Config()])
336 lsst.pipe.base.testUtils.assertValidOutput(self.task, result)
337 meas = result.measurement
339 self.assertEqual(meas.metric_name, Name(metric="ap_association.totalUnassociatedDiaObjects"))
340 self.assertEqual(meas.quantity, 0.0 * u.count)
342 def testFineGrainedMetric(self):
343 with self.assertRaises(ValueError):
344 self.task.run([Config()], outputDataId={"visit": 42})
347# Hack around unittest's hacky test setup system
348del MetricTaskTestCase
349del MetadataMetricTestCase
350del ApdbMetricTestCase
353class MemoryTester(lsst.utils.tests.MemoryTestCase):
354 pass
357def setup_module(module):
358 lsst.utils.tests.init()
361if __name__ == "__main__": 361 ↛ 362line 361 didn't jump to line 362, because the condition on line 361 was never true
362 lsst.utils.tests.init()
363 unittest.main()