Coverage for tests/test_translation.py: 12%

114 statements  

« prev     ^ index     » next       coverage.py v6.5.0, created at 2023-04-13 02:37 -0700

1# This file is part of astro_metadata_translator. 

2# 

3# Developed for the LSST Data Management System. 

4# This product includes software developed by the LSST Project 

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

6# See the LICENSE file at the top-level directory of this distribution 

7# for details of code ownership. 

8# 

9# Use of this source code is governed by a 3-clause BSD-style 

10# license that can be found in the LICENSE file. 

11 

12import os.path 

13import unittest 

14 

15from astropy.time import Time 

16 

17from astro_metadata_translator import FitsTranslator, ObservationInfo, StubTranslator 

18 

19TESTDIR = os.path.abspath(os.path.dirname(__file__)) 

20 

21 

22class InstrumentTestTranslator(FitsTranslator, StubTranslator): 

23 """Simple FITS-like translator to test the infrastructure""" 

24 

25 # Needs a name to be registered 

26 name = "TestTranslator" 

27 

28 # Indicate the instrument this class understands 

29 supported_instrument = "SCUBA_test" 

30 

31 # Some new mappings, including an override 

32 _trivial_map = { 

33 "telescope": "TELCODE", 

34 "exposure_id": "EXPID", 

35 "relative_humidity": "HUMIDITY", 

36 "detector_name": "DETNAME", 

37 "observation_id": "OBSID", 

38 } 

39 

40 # Add translator method to test joining 

41 def to_physical_filter(self): 

42 return self._join_keyword_values(["DETNAME", "HUMIDITY"], delim="_") 

43 

44 

45class MissingMethodsTranslator(FitsTranslator): 

46 """Translator class that does not implement all the methods.""" 

47 

48 pass 

49 

50 

51class TranslatorTestCase(unittest.TestCase): 

52 def setUp(self): 

53 # Known simple header 

54 self.header = { 

55 "TELESCOP": "JCMT", 

56 "TELCODE": "LSST", 

57 "INSTRUME": "SCUBA_test", 

58 "DATE-OBS": "2000-01-01T01:00:01.500", 

59 "DATE-END": "2000-01-01T02:00:01.500", 

60 "OBSGEO-X": "-5464588.84421314", 

61 "OBSGEO-Y": "-2493000.19137644", 

62 "OBSGEO-Z": "2150653.35350771", 

63 "OBSID": "20000101_00002", 

64 "EXPID": "22", # Should cast to a number 

65 "DETNAME": 76, # Should cast to a string 

66 "HUMIDITY": "55", # Should cast to a float 

67 "BAZ": "bar", 

68 } 

69 

70 def test_manual_translation(self): 

71 header = self.header 

72 translator = FitsTranslator(header) 

73 

74 # Treat the header as standard FITS 

75 self.assertFalse(FitsTranslator.can_translate(header)) 

76 self.assertEqual(translator.to_telescope(), "JCMT") 

77 self.assertEqual(translator.to_instrument(), "SCUBA_test") 

78 self.assertEqual(translator.to_datetime_begin(), Time(header["DATE-OBS"], format="isot")) 

79 

80 # This class will issue warnings 

81 with self.assertLogs("astro_metadata_translator") as cm: 

82 

83 class InstrumentTestTranslatorExtras(InstrumentTestTranslator): 

84 """Version of InstrumentTestTranslator with unexpected 

85 fields.""" 

86 

87 name = "InstrumentTestTranslatorExtras" 

88 _trivial_map = {"foobar": "BAZ"} 

89 _const_map = {"format": "HDF5"} 

90 

91 self.assertIn("Unexpected trivial", cm.output[0]) 

92 self.assertIn("Unexpected constant", cm.output[1]) 

93 

94 # Use the special test translator instead 

95 translator = InstrumentTestTranslatorExtras(header) 

96 self.assertTrue(InstrumentTestTranslator.can_translate(header)) 

97 self.assertEqual(translator.to_telescope(), "LSST") 

98 self.assertEqual(translator.to_instrument(), "SCUBA_test") 

99 self.assertEqual(translator.to_format(), "HDF5") 

100 self.assertEqual(translator.to_foobar(), "bar") 

101 

102 def test_translator(self): 

103 header = self.header 

104 

105 # Specify a translation class 

106 with self.assertWarns(UserWarning): 

107 # Since the translator is incomplete it should issue warnings 

108 v1 = ObservationInfo(header, translator_class=InstrumentTestTranslator) 

109 self.assertEqual(v1.instrument, "SCUBA_test") 

110 self.assertEqual(v1.telescope, "LSST") 

111 self.assertEqual(v1.exposure_id, 22) 

112 self.assertIsInstance(v1.exposure_id, int) 

113 self.assertEqual(v1.detector_name, "76") 

114 self.assertEqual(v1.relative_humidity, 55.0) 

115 self.assertIsInstance(v1.relative_humidity, float) 

116 self.assertEqual(v1.physical_filter, "76_55") 

117 

118 # Now automated class 

119 with self.assertWarns(UserWarning): 

120 # Since the translator is incomplete it should issue warnings 

121 v1 = ObservationInfo(header) 

122 self.assertEqual(v1.instrument, "SCUBA_test") 

123 self.assertEqual(v1.telescope, "LSST") 

124 

125 location = v1.location.to_geodetic() 

126 self.assertAlmostEqual(location.height.to("m").to_value(), 4123.0, places=1) 

127 

128 # Check that headers have been removed 

129 new_hdr = v1.stripped_header() 

130 self.assertNotIn("INSTRUME", new_hdr) 

131 self.assertNotIn("OBSGEO-X", new_hdr) 

132 self.assertIn("TELESCOP", new_hdr) 

133 

134 # Check the list of cards that were used 

135 used = v1.cards_used 

136 self.assertIn("INSTRUME", used) 

137 self.assertIn("OBSGEO-Y", used) 

138 self.assertNotIn("TELESCOP", used) 

139 

140 # Stringification 

141 summary = str(v1) 

142 self.assertIn("datetime_begin", summary) 

143 

144 # Create with a subset of properties 

145 v2 = ObservationInfo( 

146 header, 

147 translator_class=InstrumentTestTranslator, 

148 subset={"telescope", "datetime_begin", "exposure_group"}, 

149 ) 

150 

151 self.assertEqual(v2.telescope, v1.telescope) 

152 self.assertEqual(v2.datetime_begin, v2.datetime_begin) 

153 self.assertIsNone(v2.datetime_end) 

154 self.assertIsNone(v2.location) 

155 self.assertIsNone(v2.observation_id) 

156 

157 def test_corrections(self): 

158 """Apply corrections before translation.""" 

159 header = self.header 

160 

161 # Specify a translation class 

162 with self.assertWarns(UserWarning): 

163 # Since the translator is incomplete it should issue warnings 

164 v1 = ObservationInfo( 

165 header, 

166 translator_class=InstrumentTestTranslator, 

167 search_path=[os.path.join(TESTDIR, "data", "corrections")], 

168 ) 

169 

170 # These values should match the expected translation 

171 self.assertEqual(v1.instrument, "SCUBA_test") 

172 self.assertEqual(v1.detector_name, "76") 

173 self.assertEqual(v1.relative_humidity, 55.0) 

174 self.assertIsInstance(v1.relative_humidity, float) 

175 self.assertEqual(v1.physical_filter, "76_55") 

176 

177 # These two should be the "corrected" values 

178 self.assertEqual(v1.telescope, "AuxTel") 

179 self.assertEqual(v1.exposure_id, 42) 

180 

181 def test_failures(self): 

182 header = {} 

183 

184 with self.assertRaises(TypeError): 

185 ObservationInfo(header, translator_class=ObservationInfo) 

186 

187 with self.assertRaises(ValueError): 

188 ObservationInfo( 

189 header, translator_class=InstrumentTestTranslator, subset={"definitely_not_known"} 

190 ) 

191 

192 with self.assertRaises(ValueError): 

193 ObservationInfo( 

194 header, translator_class=InstrumentTestTranslator, required={"definitely_not_known"} 

195 ) 

196 

197 with self.assertLogs("astro_metadata_translator"): 

198 with self.assertWarns(UserWarning): 

199 ObservationInfo(header, translator_class=InstrumentTestTranslator, pedantic=False) 

200 

201 with self.assertRaises(KeyError): 

202 with self.assertWarns(UserWarning): 

203 ObservationInfo(header, translator_class=InstrumentTestTranslator, pedantic=True) 

204 

205 # Pass in header where the key does exist but the value is bad. 

206 bad_header = { 

207 "OBSGEO-X": "not-float", 

208 "OBSGEO-Y": "not-float", 

209 "OBSGEO-Z": "not-float", 

210 "TELESCOP": "JCMT", 

211 "TELCODE": "LSST", 

212 "INSTRUME": "SCUBA_test", 

213 } 

214 

215 with self.assertLogs("astro_metadata_translator"): 

216 with self.assertWarns(UserWarning): 

217 ObservationInfo(bad_header, translator_class=InstrumentTestTranslator, pedantic=False) 

218 

219 with self.assertLogs("astro_metadata_translator"): 

220 with self.assertWarns(UserWarning): 

221 ObservationInfo( 

222 header, translator_class=InstrumentTestTranslator, pedantic=False, filename="testfile1" 

223 ) 

224 

225 with self.assertRaises(KeyError): 

226 with self.assertWarns(UserWarning): 

227 ObservationInfo( 

228 header, translator_class=InstrumentTestTranslator, pedantic=True, filename="testfile2" 

229 ) 

230 

231 with self.assertRaises(NotImplementedError): 

232 with self.assertLogs("astro_metadata_translator", level="WARN"): 

233 ObservationInfo(header, translator_class=MissingMethodsTranslator) 

234 

235 with self.assertRaises(KeyError): 

236 with self.assertWarns(UserWarning): 

237 with self.assertLogs("astro_metadata_translator", level="WARN"): 

238 ObservationInfo( 

239 header, 

240 translator_class=InstrumentTestTranslator, 

241 pedantic=False, 

242 required={"boresight_airmass"}, 

243 ) 

244 

245 

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

247 unittest.main()