Coverage for tests/test_metrics.py: 32%

255 statements  

« prev     ^ index     » next       coverage.py v6.4.1, created at 2022-07-06 01:45 -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.gen2tasks.testUtils import MetricTaskTestCase 

35from lsst.verify.tasks import MetricComputationError 

36from lsst.verify.tasks.testUtils import MetadataMetricTestCase, ApdbMetricTestCase 

37 

38from lsst.ap.association.metrics import \ 

39 NumberNewDiaObjectsMetricTask, \ 

40 NumberUnassociatedDiaObjectsMetricTask, \ 

41 FractionUpdatedDiaObjectsMetricTask, \ 

42 NumberSolarSystemObjectsMetricTask, \ 

43 NumberAssociatedSolarSystemObjectsMetricTask, \ 

44 TotalUnassociatedDiaObjectsMetricTask 

45 

46 

47def _makeAssociationMetadata(numUpdated=27, numNew=4, numUnassociated=15, numSso=5, numAssocSso=1): 

48 metadata = PropertySet() 

49 metadata.add("association.numUpdatedDiaObjects", numUpdated) 

50 metadata.add("association.numNewDiaObjects", numNew) 

51 metadata.add("association.numTotalSolarSystemObjects", numSso) 

52 metadata.add("association.numAssociatedSsObjects", numAssocSso) 

53 metadata.add("association.numUnassociatedDiaObjects", numUnassociated) 

54 return metadata 

55 

56 

57class TestNewDiaObjects(MetadataMetricTestCase): 

58 

59 @classmethod 

60 def makeTask(cls): 

61 return NumberNewDiaObjectsMetricTask() 

62 

63 def testValid(self): 

64 metadata = _makeAssociationMetadata() 

65 result = self.task.run(metadata) 

66 lsst.pipe.base.testUtils.assertValidOutput(self.task, result) 

67 meas = result.measurement 

68 

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

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

71 

72 def testNoNew(self): 

73 metadata = _makeAssociationMetadata(numNew=0) 

74 result = self.task.run(metadata) 

75 lsst.pipe.base.testUtils.assertValidOutput(self.task, result) 

76 meas = result.measurement 

77 

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

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

80 

81 def testMissingData(self): 

82 result = self.task.run(None) 

83 lsst.pipe.base.testUtils.assertValidOutput(self.task, result) 

84 meas = result.measurement 

85 self.assertIsNone(meas) 

86 

87 def testAssociationFailed(self): 

88 result = self.task.run(PropertySet()) 

89 lsst.pipe.base.testUtils.assertValidOutput(self.task, result) 

90 meas = result.measurement 

91 self.assertIsNone(meas) 

92 

93 def testBadlyTypedKeys(self): 

94 metadata = _makeAssociationMetadata() 

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

96 

97 with self.assertRaises(MetricComputationError): 

98 self.task.run(metadata) 

99 

100 

101class TestUnassociatedDiaObjects(MetadataMetricTestCase): 

102 

103 @classmethod 

104 def makeTask(cls): 

105 return NumberUnassociatedDiaObjectsMetricTask() 

106 

107 def testValid(self): 

108 metadata = _makeAssociationMetadata() 

109 result = self.task.run(metadata) 

110 lsst.pipe.base.testUtils.assertValidOutput(self.task, result) 

111 meas = result.measurement 

112 

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

114 self.assertEqual(meas.quantity, 

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

116 

117 def testAllUpdated(self): 

118 metadata = _makeAssociationMetadata(numUnassociated=0) 

119 result = self.task.run(metadata) 

120 lsst.pipe.base.testUtils.assertValidOutput(self.task, result) 

121 meas = result.measurement 

122 

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

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

125 

126 def testMissingData(self): 

127 result = self.task.run(None) 

128 lsst.pipe.base.testUtils.assertValidOutput(self.task, result) 

129 meas = result.measurement 

130 self.assertIsNone(meas) 

131 

132 def testAssociationFailed(self): 

133 result = self.task.run(PropertySet()) 

134 lsst.pipe.base.testUtils.assertValidOutput(self.task, result) 

135 meas = result.measurement 

136 self.assertIsNone(meas) 

137 

138 def testBadlyTypedKeys(self): 

139 metadata = _makeAssociationMetadata() 

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

141 

142 with self.assertRaises(MetricComputationError): 

143 self.task.run(metadata) 

144 

145 

146class TestFracUpdatedDiaObjects(MetadataMetricTestCase): 

147 

148 @classmethod 

149 def makeTask(cls): 

150 return FractionUpdatedDiaObjectsMetricTask() 

151 

152 def testValid(self): 

153 metadata = _makeAssociationMetadata() 

154 result = self.task.run(metadata) 

155 lsst.pipe.base.testUtils.assertValidOutput(self.task, result) 

156 meas = result.measurement 

157 

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

159 nUpdated = metadata.getAsDouble("association.numUpdatedDiaObjects") 

160 nTotal = metadata.getAsDouble("association.numUnassociatedDiaObjects") + nUpdated 

161 self.assertEqual(meas.quantity, nUpdated / nTotal * u.dimensionless_unscaled) 

162 

163 def testNoUpdated(self): 

164 metadata = _makeAssociationMetadata(numUpdated=0) 

165 result = self.task.run(metadata) 

166 lsst.pipe.base.testUtils.assertValidOutput(self.task, result) 

167 meas = result.measurement 

168 

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

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

171 

172 def testAllUpdated(self): 

173 metadata = _makeAssociationMetadata(numUnassociated=0) 

174 result = self.task.run(metadata) 

175 lsst.pipe.base.testUtils.assertValidOutput(self.task, result) 

176 meas = result.measurement 

177 

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

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

180 

181 def testNoObjects(self): 

182 metadata = _makeAssociationMetadata(numUpdated=0, numUnassociated=0) 

183 result = self.task.run(metadata) 

184 lsst.pipe.base.testUtils.assertValidOutput(self.task, result) 

185 meas = result.measurement 

186 

187 self.assertIsNone(meas) 

188 

189 def testMissingData(self): 

190 result = self.task.run(None) 

191 lsst.pipe.base.testUtils.assertValidOutput(self.task, result) 

192 meas = result.measurement 

193 self.assertIsNone(meas) 

194 

195 def testAssociationFailed(self): 

196 result = self.task.run(PropertySet()) 

197 lsst.pipe.base.testUtils.assertValidOutput(self.task, result) 

198 meas = result.measurement 

199 self.assertIsNone(meas) 

200 

201 def testBadlyTypedKeys(self): 

202 metadata = _makeAssociationMetadata() 

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

204 

205 with self.assertRaises(MetricComputationError): 

206 self.task.run(metadata) 

207 

208 

209class TestNumberSolarSystemObjects(MetadataMetricTestCase): 

210 

211 @classmethod 

212 def makeTask(cls): 

213 return NumberSolarSystemObjectsMetricTask() 

214 

215 def testValid(self): 

216 metadata = _makeAssociationMetadata() 

217 result = self.task.run(metadata) 

218 lsst.pipe.base.testUtils.assertValidOutput(self.task, result) 

219 meas = result.measurement 

220 

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

222 self.assertEqual(meas.quantity, 

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

224 

225 def testAllUpdated(self): 

226 metadata = _makeAssociationMetadata(numSso=0) 

227 result = self.task.run(metadata) 

228 lsst.pipe.base.testUtils.assertValidOutput(self.task, result) 

229 meas = result.measurement 

230 

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

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

233 

234 def testMissingData(self): 

235 result = self.task.run(None) 

236 lsst.pipe.base.testUtils.assertValidOutput(self.task, result) 

237 meas = result.measurement 

238 self.assertIsNone(meas) 

239 

240 def testAssociationFailed(self): 

241 result = self.task.run(PropertySet()) 

242 lsst.pipe.base.testUtils.assertValidOutput(self.task, result) 

243 meas = result.measurement 

244 self.assertIsNone(meas) 

245 

246 def testBadlyTypedKeys(self): 

247 metadata = _makeAssociationMetadata() 

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

249 

250 with self.assertRaises(MetricComputationError): 

251 self.task.run(metadata) 

252 

253 

254class TestNumberAssociatedSolarSystemObjects(MetadataMetricTestCase): 

255 

256 @classmethod 

257 def makeTask(cls): 

258 return NumberAssociatedSolarSystemObjectsMetricTask() 

259 

260 def testValid(self): 

261 metadata = _makeAssociationMetadata() 

262 result = self.task.run(metadata) 

263 lsst.pipe.base.testUtils.assertValidOutput(self.task, result) 

264 meas = result.measurement 

265 

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

267 self.assertEqual(meas.quantity, 

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

269 

270 def testAllUpdated(self): 

271 metadata = _makeAssociationMetadata(numAssocSso=0) 

272 result = self.task.run(metadata) 

273 lsst.pipe.base.testUtils.assertValidOutput(self.task, result) 

274 meas = result.measurement 

275 

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

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

278 

279 def testMissingData(self): 

280 result = self.task.run(None) 

281 lsst.pipe.base.testUtils.assertValidOutput(self.task, result) 

282 meas = result.measurement 

283 self.assertIsNone(meas) 

284 

285 def testAssociationFailed(self): 

286 result = self.task.run(PropertySet()) 

287 lsst.pipe.base.testUtils.assertValidOutput(self.task, result) 

288 meas = result.measurement 

289 self.assertIsNone(meas) 

290 

291 def testBadlyTypedKeys(self): 

292 metadata = _makeAssociationMetadata() 

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

294 

295 with self.assertRaises(MetricComputationError): 

296 self.task.run(metadata) 

297 

298 

299class TestTotalUnassociatedObjects(ApdbMetricTestCase): 

300 

301 @staticmethod 

302 def _makeApdb(dummy_dbInfo): 

303 """Create a dummy apdb. 

304 

305 We don't have access to the apdb in the task directly so mocking 

306 return values is difficult. We thus make use of the dummy dbInfo 

307 that is passed to the init task to pass values to the apdb object 

308 instantiated. 

309 """ 

310 apdb = unittest.mock.Mock(Apdb) 

311 test_value = dummy_dbInfo["test_value"] 

312 apdb.countUnassociatedObjects = unittest.mock.MagicMock( 

313 return_value=test_value) 

314 return apdb 

315 

316 @classmethod 

317 def makeTask(cls): 

318 class SimpleDbLoader(Task): 

319 ConfigClass = Config 

320 

321 def run(self, dummy): 

322 if dummy is not None: 

323 return Struct(apdb=cls._makeApdb(dummy)) 

324 else: 

325 return Struct(apdb=None) 

326 

327 config = TotalUnassociatedDiaObjectsMetricTask.ConfigClass() 

328 config.dbLoader.retarget(SimpleDbLoader) 

329 return TotalUnassociatedDiaObjectsMetricTask(config=config) 

330 

331 @classmethod 

332 def makeDbInfo(cls): 

333 return {"test_value": "whatever"} 

334 

335 def setUp(self): 

336 super().setUp() 

337 

338 def testValid(self): 

339 result = self.task.run([{"test_value": 42}]) 

340 lsst.pipe.base.testUtils.assertValidOutput(self.task, result) 

341 meas = result.measurement 

342 

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

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

345 

346 def testAllAssociated(self): 

347 result = self.task.run([{"test_value": 0}]) 

348 lsst.pipe.base.testUtils.assertValidOutput(self.task, result) 

349 meas = result.measurement 

350 

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

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

353 

354 def testMissingData(self): 

355 result = self.task.run(None) 

356 lsst.pipe.base.testUtils.assertValidOutput(self.task, result) 

357 meas = result.measurement 

358 self.assertIsNone(meas) 

359 

360 def testFineGrainedMetric(self): 

361 with self.assertRaises(ValueError): 

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

363 

364 

365# Hack around unittest's hacky test setup system 

366del MetricTaskTestCase 

367del MetadataMetricTestCase 

368del ApdbMetricTestCase 

369 

370 

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

372 pass 

373 

374 

375def setup_module(module): 

376 lsst.utils.tests.init() 

377 

378 

379if __name__ == "__main__": 379 ↛ 380line 379 didn't jump to line 380, because the condition on line 379 was never true

380 lsst.utils.tests.init() 

381 unittest.main()