Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1# 

2# LSST Data Management System 

3# 

4# This product includes software developed by the 

5# LSST Project (http://www.lsst.org/). 

6# 

7# See COPYRIGHT file at the top of the source tree. 

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 LSST License Statement and 

20# the GNU General Public License along with this program. If not, 

21# see <https://www.lsstcorp.org/LegalNotices/>. 

22# 

23 

24import unittest 

25import yaml 

26 

27import astropy.units as u 

28from astropy.tests.helper import quantity_allclose 

29 

30from lsst.utils.tests import TestCase 

31from lsst.verify import Measurement, Metric, Name, Blob, BlobSet, Datum 

32 

33 

34class MeasurementTestCase(TestCase): 

35 """Test lsst.verify.measurment.Measurement class.""" 

36 

37 def setUp(self): 

38 self.pa1 = Metric( 

39 'validate_drp.PA1', 

40 "The maximum rms of the unresolved source magnitude distribution " 

41 "around the mean value (repeatability).", 

42 'mmag', 

43 tags=['photometric', 'LPM-17'], 

44 reference_doc='LPM-17', 

45 reference_url='http://ls.st/lpm-17', 

46 reference_page=21) 

47 

48 self.blob1 = Blob('Blob1') 

49 self.blob1['datum1'] = Datum(5 * u.arcsec, 'Datum 1') 

50 self.blob1['datum2'] = Datum(28. * u.mag, 'Datum 2') 

51 

52 self.blob2 = Blob('Blob2') 

53 self.blob2['datumN'] = Datum(11 * u.dimensionless_unscaled, 'Count') 

54 

55 def test_PA1_measurement_with_metric(self): 

56 """Standard metric with a given Metric instance.""" 

57 measurement = Measurement(self.pa1, 0.002 * u.mag, blobs=[self.blob1], 

58 notes={'filter_name': 'r'}) 

59 measurement.link_blob(self.blob2) 

60 

61 measurement2 = Measurement(self.pa1, 0.002 * u.mag) 

62 

63 self.assertTrue(quantity_allclose(measurement.quantity, 0.002 * u.mag)) 

64 self.assertIsInstance(measurement.metric_name, Name) 

65 self.assertEqual(measurement.metric_name, Name('validate_drp.PA1')) 

66 self.assertEqual(measurement.metric, self.pa1) 

67 self.assertNotEqual(measurement.identifier, measurement2.identifier) 

68 

69 # Test blob access 

70 self.assertIn('Blob1', measurement.blobs) 

71 self.assertIn('Blob2', measurement.blobs) 

72 

73 # Test Datum representation 

74 datum = measurement.datum 

75 self.assertTrue(quantity_allclose(datum.quantity, 0.002 * u.mag)) 

76 self.assertEqual(datum.label, str(self.pa1.name)) 

77 self.assertEqual(datum.description, str(self.pa1.description)) 

78 

79 # Test notes (MeasurementNotes) 

80 self.assertEqual(measurement.notes['filter_name'], 'r') 

81 # Add a note 

82 measurement.notes['camera'] = 'MegaCam' 

83 self.assertEqual(measurement.notes['camera'], 'MegaCam') 

84 self.assertEqual(len(measurement.notes), 2) 

85 self.assertIn('camera', measurement.notes) 

86 self.assertIn('filter_name', measurement.notes) 

87 # Prefixed keys 

88 self.assertIn('validate_drp.PA1.camera', measurement.notes) 

89 # test iteration 

90 iterkeys = set([key for key in measurement.notes]) 

91 self.assertEqual(len(iterkeys), 2) 

92 self.assertEqual(set(iterkeys), set(measurement.notes.keys())) 

93 itemkeys = set() 

94 for key, value in measurement.notes.items(): 

95 self.assertEqual(measurement.notes[key], value) 

96 itemkeys.add(key) 

97 self.assertEqual(itemkeys, iterkeys) 

98 # Test update 

99 measurement.notes.update({'photometric': True, 'facility': 'CFHT'}) 

100 self.assertIn('photometric', measurement.notes) 

101 # Test delete 

102 del measurement.notes['photometric'] 

103 self.assertNotIn('photometric', measurement.notes) 

104 

105 # Test serialization 

106 json_doc = measurement.json 

107 # Units should be cast to those of the metric 

108 self.assertEqual(json_doc['unit'], 'mmag') 

109 self.assertFloatsAlmostEqual(json_doc['value'], 2.0) 

110 self.assertEqual(json_doc['identifier'], measurement.identifier) 

111 self.assertIsInstance(json_doc['blob_refs'], list) 

112 self.assertIn(self.blob1.identifier, json_doc['blob_refs']) 

113 self.assertIn(self.blob2.identifier, json_doc['blob_refs']) 

114 # No extras, so should not be serialized 

115 self.assertNotIn(measurement.extras.identifier, json_doc['blob_refs']) 

116 

117 # Test deserialization 

118 new_measurement = Measurement.deserialize( 

119 blobs=BlobSet([self.blob1, self.blob2]), 

120 **json_doc) 

121 # shim in original notes; normally these are deserialized via the 

122 # Job object. 

123 new_measurement.notes.update(measurement.notes) 

124 self.assertEqual(measurement, new_measurement) 

125 self.assertEqual(measurement.identifier, new_measurement.identifier) 

126 self.assertIn('Blob1', measurement.blobs) 

127 self.assertIn('Blob2', measurement.blobs) 

128 

129 def test_PA1_measurement_without_metric(self): 

130 """Test a measurement without a Metric instance.""" 

131 measurement = Measurement('validate_drp.PA1', 0.002 * u.mag) 

132 

133 self.assertIsInstance(measurement.metric_name, Name) 

134 self.assertEqual(measurement.metric_name, Name('validate_drp.PA1')) 

135 self.assertIsNone(measurement.metric) 

136 

137 json_doc = measurement.json 

138 # Units are not converted 

139 self.assertEqual(json_doc['unit'], 'mag') 

140 self.assertFloatsAlmostEqual(json_doc['value'], 0.002) 

141 

142 new_measurement = Measurement.deserialize(**json_doc) 

143 self.assertEqual(measurement, new_measurement) 

144 self.assertEqual(measurement.identifier, new_measurement.identifier) 

145 

146 def test_PA1_deferred_metric(self): 

147 """Test a measurement when the Metric instance is added later.""" 

148 measurement = Measurement('PA1', 0.002 * u.mag) 

149 

150 self.assertIsNone(measurement.metric) 

151 self.assertEqual(measurement.metric_name, Name(metric='PA1')) 

152 

153 # Try adding in a metric with the wrong units to existing quantity 

154 other_metric = Metric('testing.other', 'Incompatible units', 'arcsec') 

155 with self.assertRaises(TypeError): 

156 measurement.metric = other_metric 

157 

158 # Add metric in; the name should also update 

159 measurement.metric = self.pa1 

160 self.assertEqual(measurement.metric, self.pa1) 

161 self.assertEqual(measurement.metric_name, self.pa1.name) 

162 

163 def test_PA1_deferred_quantity(self): 

164 """Test a measurement where the quantity is added later.""" 

165 measurement = Measurement(self.pa1) 

166 json_doc = measurement.json 

167 self.assertIsNone(json_doc['unit']) 

168 self.assertIsNone(json_doc['value']) 

169 

170 with self.assertRaises(TypeError): 

171 # wrong units 

172 measurement.quantity = 5 * u.arcsec 

173 

174 measurement.quantity = 5 * u.mmag 

175 quantity_allclose(measurement.quantity, 5 * u.mmag) 

176 

177 def test_creation_with_extras(self): 

178 """Test creating a measurement with an extra.""" 

179 measurement = Measurement( 

180 self.pa1, 5. * u.mmag, 

181 extras={'extra1': Datum(10. * u.arcmin, 'Extra 1')}) 

182 

183 self.assertIn(str(self.pa1.name), measurement.blobs) 

184 self.assertIn('extra1', measurement.extras) 

185 

186 json_doc = measurement.json 

187 self.assertIn(measurement.extras.identifier, json_doc['blob_refs']) 

188 

189 blobs = BlobSet([b for k, b in measurement.blobs.items()]) 

190 new_measurement = Measurement.deserialize(blobs=blobs, **json_doc) 

191 self.assertIn('extra1', new_measurement.extras) 

192 self.assertEqual(measurement, new_measurement) 

193 self.assertEqual(measurement.identifier, new_measurement.identifier) 

194 

195 def test_deferred_extras(self): 

196 """Test adding extras to an existing measurement.""" 

197 measurement = Measurement(self.pa1, 5. * u.mmag) 

198 

199 self.assertIn(str(self.pa1.name), measurement.blobs) 

200 

201 measurement.extras['extra1'] = Datum(10. * u.arcmin, 'Extra 1') 

202 self.assertIn('extra1', measurement.extras) 

203 

204 def test_quantity_coercion(self): 

205 # strings can't be changed into a Quantity 

206 with self.assertRaises(TypeError): 

207 Measurement('test_metric', quantity='hello') 

208 # objects can't be a Quantity 

209 with self.assertRaises(TypeError): 

210 Measurement('test_metric', quantity=int) 

211 m = Measurement('test_metric', quantity=5) 

212 self.assertEqual(m.quantity, 5) 

213 m = Measurement('test_metric', quantity=5.1) 

214 self.assertEqual(m.quantity, 5.1) 

215 

216 def test_str(self): 

217 metric = 'test.cmodel_mag' 

218 value = 1235 * u.mag 

219 m = Measurement(metric, value) 

220 self.assertEqual(str(m), "test.cmodel_mag: 1235.0 mag") 

221 

222 def test_repr(self): 

223 metric = 'test.cmodel_mag' 

224 self.assertEqual( 

225 repr(Measurement(metric)), 

226 "Measurement('test.cmodel_mag', None)") 

227 value = 1235 * u.mag 

228 self.assertEqual( 

229 repr(Measurement(metric, value)), 

230 "Measurement('test.cmodel_mag', <Quantity 1235. mag>)") 

231 

232 self.assertEqual( 

233 repr(Measurement(metric, value, [self.blob1])), 

234 "Measurement('test.cmodel_mag', <Quantity 1235. mag>, " 

235 f"blobs=[{self.blob1!r}])" 

236 ) 

237 

238 notes = {metric + '.filter_name': 'r'} 

239 extras = {'extra1': Datum(10. * u.arcmin, 'Extra 1')} 

240 self.assertEqual( 

241 repr(Measurement(metric, value, notes=notes, blobs=[self.blob1], 

242 extras=extras)), 

243 "Measurement('test.cmodel_mag', <Quantity 1235. mag>, " 

244 f"blobs=[{self.blob1!r}], extras={extras!r}, notes={notes!r})" 

245 ) 

246 

247 def _check_yaml_round_trip(self, old_measurement): 

248 persisted = yaml.dump(old_measurement) 

249 new_measurement = yaml.safe_load(persisted) 

250 

251 self.assertEqual(old_measurement, new_measurement) 

252 # These fields don't participate in Measurement equality 

253 self.assertEqual(old_measurement.identifier, 

254 new_measurement.identifier) 

255 self.assertEqual(old_measurement.blobs, 

256 new_measurement.blobs) 

257 self.assertEqual(old_measurement.extras, 

258 new_measurement.extras) 

259 

260 def test_yamlpersist_basic(self): 

261 measurement = Measurement('validate_drp.PA1', 0.002 * u.mag) 

262 self._check_yaml_round_trip(measurement) 

263 

264 def test_yamlpersist_complex(self): 

265 measurement = Measurement( 

266 self.pa1, 

267 5. * u.mmag, 

268 notes={'filter_name': 'r'}, 

269 blobs=[self.blob1], 

270 extras={'extra1': Datum(10. * u.arcmin, 'Extra 1')} 

271 ) 

272 self._check_yaml_round_trip(measurement) 

273 

274 

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

276 unittest.main()