Coverage for tests/test_metrics.py: 31%
254 statements
« prev ^ index » next coverage.py v6.4.4, created at 2022-09-02 10:32 +0000
« prev ^ index » next coverage.py v6.4.4, created at 2022-09-02 10:32 +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
31from lsst.pipe.base import Task, Struct
32import lsst.pipe.base.testUtils
33from lsst.verify import Name
34from lsst.verify.tasks import MetricComputationError
35from lsst.verify.tasks.testUtils import MetricTaskTestCase, MetadataMetricTestCase, ApdbMetricTestCase
37from lsst.ap.association.metrics import \
38 NumberNewDiaObjectsMetricTask, \
39 NumberUnassociatedDiaObjectsMetricTask, \
40 FractionUpdatedDiaObjectsMetricTask, \
41 NumberSolarSystemObjectsMetricTask, \
42 NumberAssociatedSolarSystemObjectsMetricTask, \
43 TotalUnassociatedDiaObjectsMetricTask
46def _makeAssociationMetadata(numUpdated=27, numNew=4, numUnassociated=15, numSso=5, numAssocSso=1):
47 metadata = PropertySet()
48 metadata.add("association.numUpdatedDiaObjects", numUpdated)
49 metadata.add("association.numNewDiaObjects", numNew)
50 metadata.add("association.numTotalSolarSystemObjects", numSso)
51 metadata.add("association.numAssociatedSsObjects", numAssocSso)
52 metadata.add("association.numUnassociatedDiaObjects", numUnassociated)
53 return metadata
56class TestNewDiaObjects(MetadataMetricTestCase):
58 @classmethod
59 def makeTask(cls):
60 return NumberNewDiaObjectsMetricTask()
62 def testValid(self):
63 metadata = _makeAssociationMetadata()
64 result = self.task.run(metadata)
65 lsst.pipe.base.testUtils.assertValidOutput(self.task, result)
66 meas = result.measurement
68 self.assertEqual(meas.metric_name, Name(metric="ap_association.numNewDiaObjects"))
69 self.assertEqual(meas.quantity, metadata.getAsDouble("association.numNewDiaObjects") * u.count)
71 def testNoNew(self):
72 metadata = _makeAssociationMetadata(numNew=0)
73 result = self.task.run(metadata)
74 lsst.pipe.base.testUtils.assertValidOutput(self.task, result)
75 meas = result.measurement
77 self.assertEqual(meas.metric_name, Name(metric="ap_association.numNewDiaObjects"))
78 self.assertEqual(meas.quantity, 0.0 * u.count)
80 def testMissingData(self):
81 result = self.task.run(None)
82 lsst.pipe.base.testUtils.assertValidOutput(self.task, result)
83 meas = result.measurement
84 self.assertIsNone(meas)
86 def testAssociationFailed(self):
87 result = self.task.run(PropertySet())
88 lsst.pipe.base.testUtils.assertValidOutput(self.task, result)
89 meas = result.measurement
90 self.assertIsNone(meas)
92 def testBadlyTypedKeys(self):
93 metadata = _makeAssociationMetadata()
94 metadata.set("association.numNewDiaObjects", "Ultimate Answer")
96 with self.assertRaises(MetricComputationError):
97 self.task.run(metadata)
100class TestUnassociatedDiaObjects(MetadataMetricTestCase):
102 @classmethod
103 def makeTask(cls):
104 return NumberUnassociatedDiaObjectsMetricTask()
106 def testValid(self):
107 metadata = _makeAssociationMetadata()
108 result = self.task.run(metadata)
109 lsst.pipe.base.testUtils.assertValidOutput(self.task, result)
110 meas = result.measurement
112 self.assertEqual(meas.metric_name, Name(metric="ap_association.numUnassociatedDiaObjects"))
113 self.assertEqual(meas.quantity,
114 metadata.getAsDouble("association.numUnassociatedDiaObjects") * u.count)
116 def testAllUpdated(self):
117 metadata = _makeAssociationMetadata(numUnassociated=0)
118 result = self.task.run(metadata)
119 lsst.pipe.base.testUtils.assertValidOutput(self.task, result)
120 meas = result.measurement
122 self.assertEqual(meas.metric_name, Name(metric="ap_association.numUnassociatedDiaObjects"))
123 self.assertEqual(meas.quantity, 0.0 * u.count)
125 def testMissingData(self):
126 result = self.task.run(None)
127 lsst.pipe.base.testUtils.assertValidOutput(self.task, result)
128 meas = result.measurement
129 self.assertIsNone(meas)
131 def testAssociationFailed(self):
132 result = self.task.run(PropertySet())
133 lsst.pipe.base.testUtils.assertValidOutput(self.task, result)
134 meas = result.measurement
135 self.assertIsNone(meas)
137 def testBadlyTypedKeys(self):
138 metadata = _makeAssociationMetadata()
139 metadata.set("association.numUnassociatedDiaObjects", "Ultimate Answer")
141 with self.assertRaises(MetricComputationError):
142 self.task.run(metadata)
145class TestFracUpdatedDiaObjects(MetadataMetricTestCase):
147 @classmethod
148 def makeTask(cls):
149 return FractionUpdatedDiaObjectsMetricTask()
151 def testValid(self):
152 metadata = _makeAssociationMetadata()
153 result = self.task.run(metadata)
154 lsst.pipe.base.testUtils.assertValidOutput(self.task, result)
155 meas = result.measurement
157 self.assertEqual(meas.metric_name, Name(metric="ap_association.fracUpdatedDiaObjects"))
158 nUpdated = metadata.getAsDouble("association.numUpdatedDiaObjects")
159 nTotal = metadata.getAsDouble("association.numUnassociatedDiaObjects") + nUpdated
160 self.assertEqual(meas.quantity, nUpdated / nTotal * u.dimensionless_unscaled)
162 def testNoUpdated(self):
163 metadata = _makeAssociationMetadata(numUpdated=0)
164 result = self.task.run(metadata)
165 lsst.pipe.base.testUtils.assertValidOutput(self.task, result)
166 meas = result.measurement
168 self.assertEqual(meas.metric_name, Name(metric="ap_association.fracUpdatedDiaObjects"))
169 self.assertEqual(meas.quantity, 0.0 * u.dimensionless_unscaled)
171 def testAllUpdated(self):
172 metadata = _makeAssociationMetadata(numUnassociated=0)
173 result = self.task.run(metadata)
174 lsst.pipe.base.testUtils.assertValidOutput(self.task, result)
175 meas = result.measurement
177 self.assertEqual(meas.metric_name, Name(metric="ap_association.fracUpdatedDiaObjects"))
178 self.assertEqual(meas.quantity, 1.0 * u.dimensionless_unscaled)
180 def testNoObjects(self):
181 metadata = _makeAssociationMetadata(numUpdated=0, numUnassociated=0)
182 result = self.task.run(metadata)
183 lsst.pipe.base.testUtils.assertValidOutput(self.task, result)
184 meas = result.measurement
186 self.assertIsNone(meas)
188 def testMissingData(self):
189 result = self.task.run(None)
190 lsst.pipe.base.testUtils.assertValidOutput(self.task, result)
191 meas = result.measurement
192 self.assertIsNone(meas)
194 def testAssociationFailed(self):
195 result = self.task.run(PropertySet())
196 lsst.pipe.base.testUtils.assertValidOutput(self.task, result)
197 meas = result.measurement
198 self.assertIsNone(meas)
200 def testBadlyTypedKeys(self):
201 metadata = _makeAssociationMetadata()
202 metadata.set("association.numUnassociatedDiaObjects", "Ultimate Answer")
204 with self.assertRaises(MetricComputationError):
205 self.task.run(metadata)
208class TestNumberSolarSystemObjects(MetadataMetricTestCase):
210 @classmethod
211 def makeTask(cls):
212 return NumberSolarSystemObjectsMetricTask()
214 def testValid(self):
215 metadata = _makeAssociationMetadata()
216 result = self.task.run(metadata)
217 lsst.pipe.base.testUtils.assertValidOutput(self.task, result)
218 meas = result.measurement
220 self.assertEqual(meas.metric_name, Name(metric="ap_association.numTotalSolarSystemObjects"))
221 self.assertEqual(meas.quantity,
222 metadata.getAsDouble("association.numTotalSolarSystemObjects") * u.count)
224 def testAllUpdated(self):
225 metadata = _makeAssociationMetadata(numSso=0)
226 result = self.task.run(metadata)
227 lsst.pipe.base.testUtils.assertValidOutput(self.task, result)
228 meas = result.measurement
230 self.assertEqual(meas.metric_name, Name(metric="ap_association.numTotalSolarSystemObjects"))
231 self.assertEqual(meas.quantity, 0.0 * u.count)
233 def testMissingData(self):
234 result = self.task.run(None)
235 lsst.pipe.base.testUtils.assertValidOutput(self.task, result)
236 meas = result.measurement
237 self.assertIsNone(meas)
239 def testAssociationFailed(self):
240 result = self.task.run(PropertySet())
241 lsst.pipe.base.testUtils.assertValidOutput(self.task, result)
242 meas = result.measurement
243 self.assertIsNone(meas)
245 def testBadlyTypedKeys(self):
246 metadata = _makeAssociationMetadata()
247 metadata.set("association.numTotalSolarSystemObjects", "Ultimate Answer")
249 with self.assertRaises(MetricComputationError):
250 self.task.run(metadata)
253class TestNumberAssociatedSolarSystemObjects(MetadataMetricTestCase):
255 @classmethod
256 def makeTask(cls):
257 return NumberAssociatedSolarSystemObjectsMetricTask()
259 def testValid(self):
260 metadata = _makeAssociationMetadata()
261 result = self.task.run(metadata)
262 lsst.pipe.base.testUtils.assertValidOutput(self.task, result)
263 meas = result.measurement
265 self.assertEqual(meas.metric_name, Name(metric="ap_association.numAssociatedSsObjects"))
266 self.assertEqual(meas.quantity,
267 metadata.getAsDouble("association.numAssociatedSsObjects") * u.count)
269 def testAllUpdated(self):
270 metadata = _makeAssociationMetadata(numAssocSso=0)
271 result = self.task.run(metadata)
272 lsst.pipe.base.testUtils.assertValidOutput(self.task, result)
273 meas = result.measurement
275 self.assertEqual(meas.metric_name, Name(metric="ap_association.numAssociatedSsObjects"))
276 self.assertEqual(meas.quantity, 0.0 * u.count)
278 def testMissingData(self):
279 result = self.task.run(None)
280 lsst.pipe.base.testUtils.assertValidOutput(self.task, result)
281 meas = result.measurement
282 self.assertIsNone(meas)
284 def testAssociationFailed(self):
285 result = self.task.run(PropertySet())
286 lsst.pipe.base.testUtils.assertValidOutput(self.task, result)
287 meas = result.measurement
288 self.assertIsNone(meas)
290 def testBadlyTypedKeys(self):
291 metadata = _makeAssociationMetadata()
292 metadata.set("association.numAssociatedSsObjects", "Ultimate Answer")
294 with self.assertRaises(MetricComputationError):
295 self.task.run(metadata)
298class TestTotalUnassociatedObjects(ApdbMetricTestCase):
300 @staticmethod
301 def _makeApdb(dummy_dbInfo):
302 """Create a dummy apdb.
304 We don't have access to the apdb in the task directly so mocking
305 return values is difficult. We thus make use of the dummy dbInfo
306 that is passed to the init task to pass values to the apdb object
307 instantiated.
308 """
309 apdb = unittest.mock.Mock(Apdb)
310 test_value = dummy_dbInfo["test_value"]
311 apdb.countUnassociatedObjects = unittest.mock.MagicMock(
312 return_value=test_value)
313 return apdb
315 @classmethod
316 def makeTask(cls):
317 class SimpleDbLoader(Task):
318 ConfigClass = Config
320 def run(self, dummy):
321 if dummy is not None:
322 return Struct(apdb=cls._makeApdb(dummy))
323 else:
324 return Struct(apdb=None)
326 config = TotalUnassociatedDiaObjectsMetricTask.ConfigClass()
327 config.dbLoader.retarget(SimpleDbLoader)
328 return TotalUnassociatedDiaObjectsMetricTask(config=config)
330 @classmethod
331 def makeDbInfo(cls):
332 return {"test_value": "whatever"}
334 def setUp(self):
335 super().setUp()
337 def testValid(self):
338 result = self.task.run([{"test_value": 42}])
339 lsst.pipe.base.testUtils.assertValidOutput(self.task, result)
340 meas = result.measurement
342 self.assertEqual(meas.metric_name, Name(metric="ap_association.totalUnassociatedDiaObjects"))
343 self.assertEqual(meas.quantity, 42 * u.count)
345 def testAllAssociated(self):
346 result = self.task.run([{"test_value": 0}])
347 lsst.pipe.base.testUtils.assertValidOutput(self.task, result)
348 meas = result.measurement
350 self.assertEqual(meas.metric_name, Name(metric="ap_association.totalUnassociatedDiaObjects"))
351 self.assertEqual(meas.quantity, 0.0 * u.count)
353 def testMissingData(self):
354 result = self.task.run(None)
355 lsst.pipe.base.testUtils.assertValidOutput(self.task, result)
356 meas = result.measurement
357 self.assertIsNone(meas)
359 def testFineGrainedMetric(self):
360 with self.assertRaises(ValueError):
361 self.task.run([self.makeDbInfo()], outputDataId={"visit": 42})
364# Hack around unittest's hacky test setup system
365del MetricTaskTestCase
366del MetadataMetricTestCase
367del ApdbMetricTestCase
370class MemoryTester(lsst.utils.tests.MemoryTestCase):
371 pass
374def setup_module(module):
375 lsst.utils.tests.init()
378if __name__ == "__main__": 378 ↛ 379line 378 didn't jump to line 379, because the condition on line 378 was never true
379 lsst.utils.tests.init()
380 unittest.main()