Coverage for tests/test_metrics.py: 32%

187 statements  

« prev     ^ index     » next       coverage.py v7.2.1, created at 2023-03-12 10:45 +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/>. 

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 TotalUnassociatedDiaObjectsMetricTask 

43 

44 

45def _makeAssociationMetadata(numUpdated=27, numNew=4, numUnassociated=15): 

46 metadata = PropertySet() 

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

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

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

50 return metadata 

51 

52 

53class TestNewDiaObjects(MetadataMetricTestCase): 

54 

55 @classmethod 

56 def makeTask(cls): 

57 return NumberNewDiaObjectsMetricTask() 

58 

59 def testValid(self): 

60 metadata = _makeAssociationMetadata() 

61 result = self.task.run(metadata) 

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

63 meas = result.measurement 

64 

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

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

67 

68 def testNoNew(self): 

69 metadata = _makeAssociationMetadata(numNew=0) 

70 result = self.task.run(metadata) 

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

72 meas = result.measurement 

73 

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

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

76 

77 def testMissingData(self): 

78 result = self.task.run(None) 

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

80 meas = result.measurement 

81 self.assertIsNone(meas) 

82 

83 def testAssociationFailed(self): 

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

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

86 meas = result.measurement 

87 self.assertIsNone(meas) 

88 

89 def testBadlyTypedKeys(self): 

90 metadata = _makeAssociationMetadata() 

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

92 

93 with self.assertRaises(MetricComputationError): 

94 self.task.run(metadata) 

95 

96 

97class TestUnassociatedDiaObjects(MetadataMetricTestCase): 

98 

99 @classmethod 

100 def makeTask(cls): 

101 return NumberUnassociatedDiaObjectsMetricTask() 

102 

103 def testValid(self): 

104 metadata = _makeAssociationMetadata() 

105 result = self.task.run(metadata) 

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

107 meas = result.measurement 

108 

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

110 self.assertEqual(meas.quantity, 

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

112 

113 def testAllUpdated(self): 

114 metadata = _makeAssociationMetadata(numUnassociated=0) 

115 result = self.task.run(metadata) 

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

117 meas = result.measurement 

118 

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

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

121 

122 def testMissingData(self): 

123 result = self.task.run(None) 

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

125 meas = result.measurement 

126 self.assertIsNone(meas) 

127 

128 def testAssociationFailed(self): 

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

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

131 meas = result.measurement 

132 self.assertIsNone(meas) 

133 

134 def testBadlyTypedKeys(self): 

135 metadata = _makeAssociationMetadata() 

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

137 

138 with self.assertRaises(MetricComputationError): 

139 self.task.run(metadata) 

140 

141 

142class TestFracUpdatedDiaObjects(MetadataMetricTestCase): 

143 

144 @classmethod 

145 def makeTask(cls): 

146 return FractionUpdatedDiaObjectsMetricTask() 

147 

148 def testValid(self): 

149 metadata = _makeAssociationMetadata() 

150 result = self.task.run(metadata) 

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

152 meas = result.measurement 

153 

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

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

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

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

158 

159 def testNoUpdated(self): 

160 metadata = _makeAssociationMetadata(numUpdated=0) 

161 result = self.task.run(metadata) 

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

163 meas = result.measurement 

164 

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

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

167 

168 def testAllUpdated(self): 

169 metadata = _makeAssociationMetadata(numUnassociated=0) 

170 result = self.task.run(metadata) 

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

172 meas = result.measurement 

173 

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

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

176 

177 def testNoObjects(self): 

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

179 result = self.task.run(metadata) 

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

181 meas = result.measurement 

182 

183 self.assertIsNone(meas) 

184 

185 def testMissingData(self): 

186 result = self.task.run(None) 

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

188 meas = result.measurement 

189 self.assertIsNone(meas) 

190 

191 def testAssociationFailed(self): 

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

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

194 meas = result.measurement 

195 self.assertIsNone(meas) 

196 

197 def testBadlyTypedKeys(self): 

198 metadata = _makeAssociationMetadata() 

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

200 

201 with self.assertRaises(MetricComputationError): 

202 self.task.run(metadata) 

203 

204 

205class TestTotalUnassociatedObjects(ApdbMetricTestCase): 

206 

207 @staticmethod 

208 def _makeApdb(dummy_dbInfo): 

209 """Create a dummy apdb. 

210 

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

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

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

214 instantiated. 

215 """ 

216 apdb = unittest.mock.Mock(Apdb) 

217 test_value = dummy_dbInfo["test_value"] 

218 apdb.countUnassociatedObjects = unittest.mock.MagicMock( 

219 return_value=test_value) 

220 return apdb 

221 

222 @classmethod 

223 def makeTask(cls): 

224 class SimpleDbLoader(Task): 

225 ConfigClass = Config 

226 

227 def run(self, dummy): 

228 if dummy is not None: 

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

230 else: 

231 return Struct(apdb=None) 

232 

233 config = TotalUnassociatedDiaObjectsMetricTask.ConfigClass() 

234 config.dbLoader.retarget(SimpleDbLoader) 

235 return TotalUnassociatedDiaObjectsMetricTask(config=config) 

236 

237 @classmethod 

238 def makeDbInfo(cls): 

239 return {"test_value": "whatever"} 

240 

241 def setUp(self): 

242 super().setUp() 

243 

244 def testValid(self): 

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

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

247 meas = result.measurement 

248 

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

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

251 

252 def testAllAssociated(self): 

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

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

255 meas = result.measurement 

256 

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

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

259 

260 def testMissingData(self): 

261 result = self.task.run(None) 

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

263 meas = result.measurement 

264 self.assertIsNone(meas) 

265 

266 def testFineGrainedMetric(self): 

267 with self.assertRaises(ValueError): 

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

269 

270 

271# Hack around unittest's hacky test setup system 

272del MetricTaskTestCase 

273del MetadataMetricTestCase 

274del ApdbMetricTestCase 

275 

276 

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

278 pass 

279 

280 

281def setup_module(module): 

282 lsst.utils.tests.init() 

283 

284 

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

286 lsst.utils.tests.init() 

287 unittest.main()