Coverage for tests/test_metrics.py: 31%

254 statements  

« prev     ^ index     » next       coverage.py v6.4.4, created at 2022-08-26 03:22 -0700

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/>. 

21 

22import unittest 

23import unittest.mock 

24 

25import astropy.units as u 

26 

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 

36 

37from lsst.ap.association.metrics import \ 

38 NumberNewDiaObjectsMetricTask, \ 

39 NumberUnassociatedDiaObjectsMetricTask, \ 

40 FractionUpdatedDiaObjectsMetricTask, \ 

41 NumberSolarSystemObjectsMetricTask, \ 

42 NumberAssociatedSolarSystemObjectsMetricTask, \ 

43 TotalUnassociatedDiaObjectsMetricTask 

44 

45 

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 

54 

55 

56class TestNewDiaObjects(MetadataMetricTestCase): 

57 

58 @classmethod 

59 def makeTask(cls): 

60 return NumberNewDiaObjectsMetricTask() 

61 

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 

67 

68 self.assertEqual(meas.metric_name, Name(metric="ap_association.numNewDiaObjects")) 

69 self.assertEqual(meas.quantity, metadata.getAsDouble("association.numNewDiaObjects") * u.count) 

70 

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 

76 

77 self.assertEqual(meas.metric_name, Name(metric="ap_association.numNewDiaObjects")) 

78 self.assertEqual(meas.quantity, 0.0 * u.count) 

79 

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) 

85 

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) 

91 

92 def testBadlyTypedKeys(self): 

93 metadata = _makeAssociationMetadata() 

94 metadata.set("association.numNewDiaObjects", "Ultimate Answer") 

95 

96 with self.assertRaises(MetricComputationError): 

97 self.task.run(metadata) 

98 

99 

100class TestUnassociatedDiaObjects(MetadataMetricTestCase): 

101 

102 @classmethod 

103 def makeTask(cls): 

104 return NumberUnassociatedDiaObjectsMetricTask() 

105 

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 

111 

112 self.assertEqual(meas.metric_name, Name(metric="ap_association.numUnassociatedDiaObjects")) 

113 self.assertEqual(meas.quantity, 

114 metadata.getAsDouble("association.numUnassociatedDiaObjects") * u.count) 

115 

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 

121 

122 self.assertEqual(meas.metric_name, Name(metric="ap_association.numUnassociatedDiaObjects")) 

123 self.assertEqual(meas.quantity, 0.0 * u.count) 

124 

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) 

130 

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) 

136 

137 def testBadlyTypedKeys(self): 

138 metadata = _makeAssociationMetadata() 

139 metadata.set("association.numUnassociatedDiaObjects", "Ultimate Answer") 

140 

141 with self.assertRaises(MetricComputationError): 

142 self.task.run(metadata) 

143 

144 

145class TestFracUpdatedDiaObjects(MetadataMetricTestCase): 

146 

147 @classmethod 

148 def makeTask(cls): 

149 return FractionUpdatedDiaObjectsMetricTask() 

150 

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 

156 

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) 

161 

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 

167 

168 self.assertEqual(meas.metric_name, Name(metric="ap_association.fracUpdatedDiaObjects")) 

169 self.assertEqual(meas.quantity, 0.0 * u.dimensionless_unscaled) 

170 

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 

176 

177 self.assertEqual(meas.metric_name, Name(metric="ap_association.fracUpdatedDiaObjects")) 

178 self.assertEqual(meas.quantity, 1.0 * u.dimensionless_unscaled) 

179 

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 

185 

186 self.assertIsNone(meas) 

187 

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) 

193 

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) 

199 

200 def testBadlyTypedKeys(self): 

201 metadata = _makeAssociationMetadata() 

202 metadata.set("association.numUnassociatedDiaObjects", "Ultimate Answer") 

203 

204 with self.assertRaises(MetricComputationError): 

205 self.task.run(metadata) 

206 

207 

208class TestNumberSolarSystemObjects(MetadataMetricTestCase): 

209 

210 @classmethod 

211 def makeTask(cls): 

212 return NumberSolarSystemObjectsMetricTask() 

213 

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 

219 

220 self.assertEqual(meas.metric_name, Name(metric="ap_association.numTotalSolarSystemObjects")) 

221 self.assertEqual(meas.quantity, 

222 metadata.getAsDouble("association.numTotalSolarSystemObjects") * u.count) 

223 

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 

229 

230 self.assertEqual(meas.metric_name, Name(metric="ap_association.numTotalSolarSystemObjects")) 

231 self.assertEqual(meas.quantity, 0.0 * u.count) 

232 

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) 

238 

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) 

244 

245 def testBadlyTypedKeys(self): 

246 metadata = _makeAssociationMetadata() 

247 metadata.set("association.numTotalSolarSystemObjects", "Ultimate Answer") 

248 

249 with self.assertRaises(MetricComputationError): 

250 self.task.run(metadata) 

251 

252 

253class TestNumberAssociatedSolarSystemObjects(MetadataMetricTestCase): 

254 

255 @classmethod 

256 def makeTask(cls): 

257 return NumberAssociatedSolarSystemObjectsMetricTask() 

258 

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 

264 

265 self.assertEqual(meas.metric_name, Name(metric="ap_association.numAssociatedSsObjects")) 

266 self.assertEqual(meas.quantity, 

267 metadata.getAsDouble("association.numAssociatedSsObjects") * u.count) 

268 

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 

274 

275 self.assertEqual(meas.metric_name, Name(metric="ap_association.numAssociatedSsObjects")) 

276 self.assertEqual(meas.quantity, 0.0 * u.count) 

277 

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) 

283 

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) 

289 

290 def testBadlyTypedKeys(self): 

291 metadata = _makeAssociationMetadata() 

292 metadata.set("association.numAssociatedSsObjects", "Ultimate Answer") 

293 

294 with self.assertRaises(MetricComputationError): 

295 self.task.run(metadata) 

296 

297 

298class TestTotalUnassociatedObjects(ApdbMetricTestCase): 

299 

300 @staticmethod 

301 def _makeApdb(dummy_dbInfo): 

302 """Create a dummy apdb. 

303 

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 

314 

315 @classmethod 

316 def makeTask(cls): 

317 class SimpleDbLoader(Task): 

318 ConfigClass = Config 

319 

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) 

325 

326 config = TotalUnassociatedDiaObjectsMetricTask.ConfigClass() 

327 config.dbLoader.retarget(SimpleDbLoader) 

328 return TotalUnassociatedDiaObjectsMetricTask(config=config) 

329 

330 @classmethod 

331 def makeDbInfo(cls): 

332 return {"test_value": "whatever"} 

333 

334 def setUp(self): 

335 super().setUp() 

336 

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 

341 

342 self.assertEqual(meas.metric_name, Name(metric="ap_association.totalUnassociatedDiaObjects")) 

343 self.assertEqual(meas.quantity, 42 * u.count) 

344 

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 

349 

350 self.assertEqual(meas.metric_name, Name(metric="ap_association.totalUnassociatedDiaObjects")) 

351 self.assertEqual(meas.quantity, 0.0 * u.count) 

352 

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) 

358 

359 def testFineGrainedMetric(self): 

360 with self.assertRaises(ValueError): 

361 self.task.run([self.makeDbInfo()], outputDataId={"visit": 42}) 

362 

363 

364# Hack around unittest's hacky test setup system 

365del MetricTaskTestCase 

366del MetadataMetricTestCase 

367del ApdbMetricTestCase 

368 

369 

370class MemoryTester(lsst.utils.tests.MemoryTestCase): 

371 pass 

372 

373 

374def setup_module(module): 

375 lsst.utils.tests.init() 

376 

377 

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()