Coverage for tests/test_headers.py : 26%

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# 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.
12import unittest
13import os.path
15from astro_metadata_translator import merge_headers, fix_header, HscTranslator
16from astro_metadata_translator.tests import read_test_file
17from astro_metadata_translator import DecamTranslator
19TESTDIR = os.path.abspath(os.path.dirname(__file__))
22class NotDecamTranslator(DecamTranslator):
23 """This is a DECam translator with override list of header corrections."""
24 name = None
26 @classmethod
27 def fix_header(cls, header, instrument, obsid, filename=None):
28 header["DTSITE"] = "hi"
29 return True
32class AlsoNotDecamTranslator(DecamTranslator):
33 """This is a DECam translator with override list of header corrections
34 that fails."""
35 name = None
37 @classmethod
38 def fix_header(cls, header, instrument, obsid, filename=None):
39 raise RuntimeError("Failure to work something out from header")
42class NullDecamTranslator(DecamTranslator):
43 """This is a DECam translator that doesn't do any fixes."""
44 name = None
46 @classmethod
47 def fix_header(cls, header, instrument, obsid, filename=None):
48 return False
51class HeadersTestCase(unittest.TestCase):
53 def setUp(self):
54 # Define reference headers
55 self.h1 = dict(
56 ORIGIN="LSST",
57 KEY0=0,
58 KEY1=1,
59 KEY2=3,
60 KEY3=3.1415,
61 KEY4="a",
62 )
64 self.h2 = dict(
65 ORIGIN="LSST",
66 KEY0="0",
67 KEY2=4,
68 KEY5=42
69 )
70 self.h3 = dict(
71 ORIGIN="AUXTEL",
72 KEY3=3.1415,
73 KEY2=50,
74 KEY5=42,
75 )
76 self.h4 = dict(
77 KEY6="New",
78 KEY1="Exists",
79 )
81 # Add keys for sorting by time
82 # Sorted order: h2, h1, h4, h3
83 self.h1["MJD-OBS"] = 50000.0
84 self.h2["MJD-OBS"] = 49000.0
85 self.h3["MJD-OBS"] = 53000.0
86 self.h4["MJD-OBS"] = 52000.0
88 def test_fail(self):
89 with self.assertRaises(ValueError):
90 merge_headers([self.h1, self.h2], mode="wrong")
92 with self.assertRaises(ValueError):
93 merge_headers([])
95 def test_one(self):
96 merged = merge_headers([self.h1], mode="drop")
97 self.assertEqual(merged, self.h1)
99 def test_merging_overwrite(self):
100 merged = merge_headers([self.h1, self.h2], mode="overwrite")
101 # The merged header should be the same type as the first header
102 self.assertIsInstance(merged, type(self.h1))
104 expected = {
105 "MJD-OBS": self.h2["MJD-OBS"],
106 "ORIGIN": self.h2["ORIGIN"],
107 "KEY0": self.h2["KEY0"],
108 "KEY1": self.h1["KEY1"],
109 "KEY2": self.h2["KEY2"],
110 "KEY3": self.h1["KEY3"],
111 "KEY4": self.h1["KEY4"],
112 "KEY5": self.h2["KEY5"],
113 }
114 self.assertEqual(merged, expected)
116 merged = merge_headers([self.h1, self.h2, self.h3, self.h4],
117 mode="overwrite")
119 expected = {
120 "MJD-OBS": self.h4["MJD-OBS"],
121 "ORIGIN": self.h3["ORIGIN"],
122 "KEY0": self.h2["KEY0"],
123 "KEY1": self.h4["KEY1"],
124 "KEY2": self.h3["KEY2"],
125 "KEY3": self.h3["KEY3"],
126 "KEY4": self.h1["KEY4"],
127 "KEY5": self.h3["KEY5"],
128 "KEY6": self.h4["KEY6"],
129 }
131 self.assertEqual(merged, expected)
133 def test_merging_first(self):
134 merged = merge_headers([self.h1, self.h2, self.h3, self.h4],
135 mode="first")
137 expected = {
138 "MJD-OBS": self.h1["MJD-OBS"],
139 "ORIGIN": self.h1["ORIGIN"],
140 "KEY0": self.h1["KEY0"],
141 "KEY1": self.h1["KEY1"],
142 "KEY2": self.h1["KEY2"],
143 "KEY3": self.h1["KEY3"],
144 "KEY4": self.h1["KEY4"],
145 "KEY5": self.h2["KEY5"],
146 "KEY6": self.h4["KEY6"],
147 }
149 self.assertEqual(merged, expected)
151 def test_merging_drop(self):
152 merged = merge_headers([self.h1, self.h2, self.h3, self.h4],
153 mode="drop")
155 expected = {
156 "KEY3": self.h1["KEY3"],
157 "KEY4": self.h1["KEY4"],
158 "KEY5": self.h2["KEY5"],
159 "KEY6": self.h4["KEY6"],
160 }
162 self.assertEqual(merged, expected)
164 # Sorting the headers should make no difference to drop mode
165 merged = merge_headers([self.h1, self.h2, self.h3, self.h4],
166 mode="drop", sort=True)
167 self.assertEqual(merged, expected)
169 # Now retain some headers
170 merged = merge_headers([self.h1, self.h2, self.h3, self.h4],
171 mode="drop", sort=False, first=["ORIGIN"], last=["KEY2", "KEY1"])
173 expected = {
174 "KEY2": self.h3["KEY2"],
175 "ORIGIN": self.h1["ORIGIN"],
176 "KEY1": self.h4["KEY1"],
177 "KEY3": self.h1["KEY3"],
178 "KEY4": self.h1["KEY4"],
179 "KEY5": self.h2["KEY5"],
180 "KEY6": self.h4["KEY6"],
181 }
182 self.assertEqual(merged, expected)
184 # Now retain some headers with sorting
185 merged = merge_headers([self.h1, self.h2, self.h3, self.h4],
186 mode="drop", sort=True, first=["ORIGIN"], last=["KEY2", "KEY1"])
188 expected = {
189 "KEY2": self.h3["KEY2"],
190 "ORIGIN": self.h2["ORIGIN"],
191 "KEY1": self.h4["KEY1"],
192 "KEY3": self.h1["KEY3"],
193 "KEY4": self.h1["KEY4"],
194 "KEY5": self.h2["KEY5"],
195 "KEY6": self.h4["KEY6"],
196 }
197 self.assertEqual(merged, expected)
199 def test_merging_append(self):
200 # Try with two headers first
201 merged = merge_headers([self.h1, self.h2], mode="append")
203 expected = {
204 "MJD-OBS": [self.h1["MJD-OBS"], self.h2["MJD-OBS"]],
205 "ORIGIN": self.h1["ORIGIN"],
206 "KEY0": [self.h1["KEY0"], self.h2["KEY0"]],
207 "KEY1": self.h1["KEY1"],
208 "KEY2": [self.h1["KEY2"], self.h2["KEY2"]],
209 "KEY3": self.h1["KEY3"],
210 "KEY4": self.h1["KEY4"],
211 "KEY5": self.h2["KEY5"],
212 }
214 self.assertEqual(merged, expected)
216 merged = merge_headers([self.h1, self.h2, self.h3, self.h4],
217 mode="append")
219 expected = {
220 "MJD-OBS": [self.h1["MJD-OBS"], self.h2["MJD-OBS"], self.h3["MJD-OBS"], self.h4["MJD-OBS"]],
221 "ORIGIN": [self.h1["ORIGIN"], self.h2["ORIGIN"], self.h3["ORIGIN"], None],
222 "KEY0": [self.h1["KEY0"], self.h2["KEY0"], None, None],
223 "KEY1": [self.h1["KEY1"], None, None, self.h4["KEY1"]],
224 "KEY2": [self.h1["KEY2"], self.h2["KEY2"], self.h3["KEY2"], None],
225 "KEY3": self.h3["KEY3"],
226 "KEY4": self.h1["KEY4"],
227 "KEY5": self.h3["KEY5"],
228 "KEY6": self.h4["KEY6"],
229 }
231 self.assertEqual(merged, expected)
233 def test_merging_overwrite_sort(self):
234 merged = merge_headers([self.h1, self.h2], mode="overwrite", sort=True)
236 expected = {
237 "MJD-OBS": self.h1["MJD-OBS"],
238 "ORIGIN": self.h1["ORIGIN"],
239 "KEY0": self.h1["KEY0"],
240 "KEY1": self.h1["KEY1"],
241 "KEY2": self.h1["KEY2"],
242 "KEY3": self.h1["KEY3"],
243 "KEY4": self.h1["KEY4"],
244 "KEY5": self.h2["KEY5"],
245 }
246 self.assertEqual(merged, expected)
248 merged = merge_headers([self.h1, self.h2, self.h3, self.h4],
249 mode="overwrite", sort=True)
251 expected = {
252 "MJD-OBS": self.h3["MJD-OBS"],
253 "ORIGIN": self.h3["ORIGIN"],
254 "KEY0": self.h1["KEY0"],
255 "KEY1": self.h4["KEY1"],
256 "KEY2": self.h3["KEY2"],
257 "KEY3": self.h3["KEY3"],
258 "KEY4": self.h1["KEY4"],
259 "KEY5": self.h3["KEY5"],
260 "KEY6": self.h4["KEY6"],
261 }
263 self.assertEqual(merged, expected)
265 # Changing the order should not change the result
266 merged = merge_headers([self.h4, self.h1, self.h3, self.h2],
267 mode="overwrite", sort=True)
269 self.assertEqual(merged, expected)
271 def test_merging_first_sort(self):
272 merged = merge_headers([self.h1, self.h2, self.h3, self.h4],
273 mode="first", sort=True)
275 expected = {
276 "MJD-OBS": self.h2["MJD-OBS"],
277 "ORIGIN": self.h2["ORIGIN"],
278 "KEY0": self.h2["KEY0"],
279 "KEY1": self.h1["KEY1"],
280 "KEY2": self.h2["KEY2"],
281 "KEY3": self.h1["KEY3"],
282 "KEY4": self.h1["KEY4"],
283 "KEY5": self.h2["KEY5"],
284 "KEY6": self.h4["KEY6"],
285 }
287 self.assertEqual(merged, expected)
289 def test_merging_append_sort(self):
290 # Try with two headers first
291 merged = merge_headers([self.h1, self.h2], mode="append", sort=True)
293 expected = {
294 "MJD-OBS": [self.h2["MJD-OBS"], self.h1["MJD-OBS"]],
295 "ORIGIN": self.h1["ORIGIN"],
296 "KEY0": [self.h2["KEY0"], self.h1["KEY0"]],
297 "KEY1": self.h1["KEY1"],
298 "KEY2": [self.h2["KEY2"], self.h1["KEY2"]],
299 "KEY3": self.h1["KEY3"],
300 "KEY4": self.h1["KEY4"],
301 "KEY5": self.h2["KEY5"],
302 }
304 self.assertEqual(merged, expected)
306 merged = merge_headers([self.h1, self.h2, self.h3, self.h4],
307 mode="append", sort=True)
309 expected = {
310 "MJD-OBS": [self.h2["MJD-OBS"], self.h1["MJD-OBS"], self.h4["MJD-OBS"], self.h3["MJD-OBS"]],
311 "ORIGIN": [self.h2["ORIGIN"], self.h1["ORIGIN"], None, self.h3["ORIGIN"]],
312 "KEY0": [self.h2["KEY0"], self.h1["KEY0"], None, None],
313 "KEY1": [None, self.h1["KEY1"], self.h4["KEY1"], None],
314 "KEY2": [self.h2["KEY2"], self.h1["KEY2"], None, self.h3["KEY2"]],
315 "KEY3": self.h3["KEY3"],
316 "KEY4": self.h1["KEY4"],
317 "KEY5": self.h3["KEY5"],
318 "KEY6": self.h4["KEY6"],
319 }
321 self.assertEqual(merged, expected)
323 # Order should not matter
324 merged = merge_headers([self.h4, self.h3, self.h2, self.h1],
325 mode="append", sort=True)
326 self.assertEqual(merged, expected)
329class FixHeadersTestCase(unittest.TestCase):
331 def test_basic_fix_header(self):
332 """Test that a header can be fixed if we specify a local path.
333 """
335 header = read_test_file("fitsheader-decam-0160496.yaml", dir=os.path.join(TESTDIR, "data"))
336 self.assertEqual(header["DETECTOR"], "S3-111_107419-8-3")
338 # First fix header but using no search path (should work as no-op)
339 fixed = fix_header(header, translator_class=NullDecamTranslator)
340 self.assertFalse(fixed)
342 # Now using the test corrections directory
343 fixed = fix_header(header, search_path=os.path.join(TESTDIR, "data", "corrections"),
344 translator_class=NullDecamTranslator)
345 self.assertTrue(fixed)
346 self.assertEqual(header["DETECTOR"], "NEW-ID")
348 # Now with a corrections directory that has bad YAML in it
349 with self.assertLogs(level="WARN"):
350 fixed = fix_header(header, search_path=os.path.join(TESTDIR, "data", "bad_corrections"),
351 translator_class=NullDecamTranslator)
352 self.assertFalse(fixed)
354 # Test that fix_header of unknown header is allowed
355 header = {"SOMETHING": "UNKNOWN"}
356 fixed = fix_header(header, translator_class=NullDecamTranslator)
357 self.assertFalse(fixed)
359 def test_hsc_fix_header(self):
360 """Check that one of the known HSC corrections is being applied
361 properly."""
362 header = {"EXP-ID": "HSCA00120800",
363 "INSTRUME": "HSC",
364 "DATA-TYP": "FLAT"}
366 fixed = fix_header(header, translator_class=HscTranslator)
367 self.assertTrue(fixed)
368 self.assertEqual(header["DATA-TYP"], "OBJECT")
370 # And that this header won't be corrected
371 header = {"EXP-ID": "HSCA00120800X",
372 "INSTRUME": "HSC",
373 "DATA-TYP": "FLAT"}
375 fixed = fix_header(header, translator_class=HscTranslator)
376 self.assertFalse(fixed)
377 self.assertEqual(header["DATA-TYP"], "FLAT")
379 def test_decam_fix_header(self):
380 """Check that one of the known DECam corrections is being applied
381 properly."""
383 # This header is a bias (zero) with an erroneous Y filter
384 header = read_test_file("fitsheader-decam-0160496.yaml", dir=os.path.join(TESTDIR, "data"))
385 fixed = fix_header(header, translator_class=DecamTranslator)
386 self.assertTrue(fixed)
387 self.assertEqual(header["FILTER"], "solid plate 0.0 0.0")
389 def test_translator_fix_header(self):
390 """Check that translator classes can fix headers."""
392 # Read in a known header
393 header = read_test_file("fitsheader-decam-0160496.yaml", dir=os.path.join(TESTDIR, "data"))
394 self.assertEqual(header["DTSITE"], "ct")
395 fixed = fix_header(header, translator_class=NotDecamTranslator)
396 self.assertTrue(fixed)
397 self.assertEqual(header["DTSITE"], "hi")
399 header["DTSITE"] = "reset"
400 with self.assertLogs("astro_metadata_translator", level="FATAL"):
401 fixed = fix_header(header, translator_class=AlsoNotDecamTranslator)
402 self.assertFalse(fixed)
403 self.assertEqual(header["DTSITE"], "reset")
406if __name__ == "__main__": 406 ↛ 407line 406 didn't jump to line 407, because the condition on line 406 was never true
407 unittest.main()