Coverage for tests/test_headers.py : 24%

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):
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):
39 raise RuntimeError("Failure to work something out from header")
42class HeadersTestCase(unittest.TestCase):
44 def setUp(self):
45 # Define reference headers
46 self.h1 = dict(
47 ORIGIN="LSST",
48 KEY0=0,
49 KEY1=1,
50 KEY2=3,
51 KEY3=3.1415,
52 KEY4="a",
53 )
55 self.h2 = dict(
56 ORIGIN="LSST",
57 KEY0="0",
58 KEY2=4,
59 KEY5=42
60 )
61 self.h3 = dict(
62 ORIGIN="AUXTEL",
63 KEY3=3.1415,
64 KEY2=50,
65 KEY5=42,
66 )
67 self.h4 = dict(
68 KEY6="New",
69 KEY1="Exists",
70 )
72 # Add keys for sorting by time
73 # Sorted order: h2, h1, h4, h3
74 self.h1["MJD-OBS"] = 50000.0
75 self.h2["MJD-OBS"] = 49000.0
76 self.h3["MJD-OBS"] = 53000.0
77 self.h4["MJD-OBS"] = 52000.0
79 def test_fail(self):
80 with self.assertRaises(ValueError):
81 merge_headers([self.h1, self.h2], mode="wrong")
83 with self.assertRaises(ValueError):
84 merge_headers([])
86 def test_one(self):
87 merged = merge_headers([self.h1], mode="drop")
88 self.assertEqual(merged, self.h1)
90 def test_merging_overwrite(self):
91 merged = merge_headers([self.h1, self.h2], mode="overwrite")
92 # The merged header should be the same type as the first header
93 self.assertIsInstance(merged, type(self.h1))
95 expected = {
96 "MJD-OBS": self.h2["MJD-OBS"],
97 "ORIGIN": self.h2["ORIGIN"],
98 "KEY0": self.h2["KEY0"],
99 "KEY1": self.h1["KEY1"],
100 "KEY2": self.h2["KEY2"],
101 "KEY3": self.h1["KEY3"],
102 "KEY4": self.h1["KEY4"],
103 "KEY5": self.h2["KEY5"],
104 }
105 self.assertEqual(merged, expected)
107 merged = merge_headers([self.h1, self.h2, self.h3, self.h4],
108 mode="overwrite")
110 expected = {
111 "MJD-OBS": self.h4["MJD-OBS"],
112 "ORIGIN": self.h3["ORIGIN"],
113 "KEY0": self.h2["KEY0"],
114 "KEY1": self.h4["KEY1"],
115 "KEY2": self.h3["KEY2"],
116 "KEY3": self.h3["KEY3"],
117 "KEY4": self.h1["KEY4"],
118 "KEY5": self.h3["KEY5"],
119 "KEY6": self.h4["KEY6"],
120 }
122 self.assertEqual(merged, expected)
124 def test_merging_first(self):
125 merged = merge_headers([self.h1, self.h2, self.h3, self.h4],
126 mode="first")
128 expected = {
129 "MJD-OBS": self.h1["MJD-OBS"],
130 "ORIGIN": self.h1["ORIGIN"],
131 "KEY0": self.h1["KEY0"],
132 "KEY1": self.h1["KEY1"],
133 "KEY2": self.h1["KEY2"],
134 "KEY3": self.h1["KEY3"],
135 "KEY4": self.h1["KEY4"],
136 "KEY5": self.h2["KEY5"],
137 "KEY6": self.h4["KEY6"],
138 }
140 self.assertEqual(merged, expected)
142 def test_merging_drop(self):
143 merged = merge_headers([self.h1, self.h2, self.h3, self.h4],
144 mode="drop")
146 expected = {
147 "KEY3": self.h1["KEY3"],
148 "KEY4": self.h1["KEY4"],
149 "KEY5": self.h2["KEY5"],
150 "KEY6": self.h4["KEY6"],
151 }
153 self.assertEqual(merged, expected)
155 # Sorting the headers should make no difference to drop mode
156 merged = merge_headers([self.h1, self.h2, self.h3, self.h4],
157 mode="drop", sort=True)
158 self.assertEqual(merged, expected)
160 # Now retain some headers
161 merged = merge_headers([self.h1, self.h2, self.h3, self.h4],
162 mode="drop", sort=False, first=["ORIGIN"], last=["KEY2", "KEY1"])
164 expected = {
165 "KEY2": self.h3["KEY2"],
166 "ORIGIN": self.h1["ORIGIN"],
167 "KEY1": self.h4["KEY1"],
168 "KEY3": self.h1["KEY3"],
169 "KEY4": self.h1["KEY4"],
170 "KEY5": self.h2["KEY5"],
171 "KEY6": self.h4["KEY6"],
172 }
173 self.assertEqual(merged, expected)
175 # Now retain some headers with sorting
176 merged = merge_headers([self.h1, self.h2, self.h3, self.h4],
177 mode="drop", sort=True, first=["ORIGIN"], last=["KEY2", "KEY1"])
179 expected = {
180 "KEY2": self.h3["KEY2"],
181 "ORIGIN": self.h2["ORIGIN"],
182 "KEY1": self.h4["KEY1"],
183 "KEY3": self.h1["KEY3"],
184 "KEY4": self.h1["KEY4"],
185 "KEY5": self.h2["KEY5"],
186 "KEY6": self.h4["KEY6"],
187 }
188 self.assertEqual(merged, expected)
190 def test_merging_append(self):
191 # Try with two headers first
192 merged = merge_headers([self.h1, self.h2], mode="append")
194 expected = {
195 "MJD-OBS": [self.h1["MJD-OBS"], self.h2["MJD-OBS"]],
196 "ORIGIN": self.h1["ORIGIN"],
197 "KEY0": [self.h1["KEY0"], self.h2["KEY0"]],
198 "KEY1": self.h1["KEY1"],
199 "KEY2": [self.h1["KEY2"], self.h2["KEY2"]],
200 "KEY3": self.h1["KEY3"],
201 "KEY4": self.h1["KEY4"],
202 "KEY5": self.h2["KEY5"],
203 }
205 self.assertEqual(merged, expected)
207 merged = merge_headers([self.h1, self.h2, self.h3, self.h4],
208 mode="append")
210 expected = {
211 "MJD-OBS": [self.h1["MJD-OBS"], self.h2["MJD-OBS"], self.h3["MJD-OBS"], self.h4["MJD-OBS"]],
212 "ORIGIN": [self.h1["ORIGIN"], self.h2["ORIGIN"], self.h3["ORIGIN"], None],
213 "KEY0": [self.h1["KEY0"], self.h2["KEY0"], None, None],
214 "KEY1": [self.h1["KEY1"], None, None, self.h4["KEY1"]],
215 "KEY2": [self.h1["KEY2"], self.h2["KEY2"], self.h3["KEY2"], None],
216 "KEY3": self.h3["KEY3"],
217 "KEY4": self.h1["KEY4"],
218 "KEY5": self.h3["KEY5"],
219 "KEY6": self.h4["KEY6"],
220 }
222 self.assertEqual(merged, expected)
224 def test_merging_overwrite_sort(self):
225 merged = merge_headers([self.h1, self.h2], mode="overwrite", sort=True)
227 expected = {
228 "MJD-OBS": self.h1["MJD-OBS"],
229 "ORIGIN": self.h1["ORIGIN"],
230 "KEY0": self.h1["KEY0"],
231 "KEY1": self.h1["KEY1"],
232 "KEY2": self.h1["KEY2"],
233 "KEY3": self.h1["KEY3"],
234 "KEY4": self.h1["KEY4"],
235 "KEY5": self.h2["KEY5"],
236 }
237 self.assertEqual(merged, expected)
239 merged = merge_headers([self.h1, self.h2, self.h3, self.h4],
240 mode="overwrite", sort=True)
242 expected = {
243 "MJD-OBS": self.h3["MJD-OBS"],
244 "ORIGIN": self.h3["ORIGIN"],
245 "KEY0": self.h1["KEY0"],
246 "KEY1": self.h4["KEY1"],
247 "KEY2": self.h3["KEY2"],
248 "KEY3": self.h3["KEY3"],
249 "KEY4": self.h1["KEY4"],
250 "KEY5": self.h3["KEY5"],
251 "KEY6": self.h4["KEY6"],
252 }
254 self.assertEqual(merged, expected)
256 # Changing the order should not change the result
257 merged = merge_headers([self.h4, self.h1, self.h3, self.h2],
258 mode="overwrite", sort=True)
260 self.assertEqual(merged, expected)
262 def test_merging_first_sort(self):
263 merged = merge_headers([self.h1, self.h2, self.h3, self.h4],
264 mode="first", sort=True)
266 expected = {
267 "MJD-OBS": self.h2["MJD-OBS"],
268 "ORIGIN": self.h2["ORIGIN"],
269 "KEY0": self.h2["KEY0"],
270 "KEY1": self.h1["KEY1"],
271 "KEY2": self.h2["KEY2"],
272 "KEY3": self.h1["KEY3"],
273 "KEY4": self.h1["KEY4"],
274 "KEY5": self.h2["KEY5"],
275 "KEY6": self.h4["KEY6"],
276 }
278 self.assertEqual(merged, expected)
280 def test_merging_append_sort(self):
281 # Try with two headers first
282 merged = merge_headers([self.h1, self.h2], mode="append", sort=True)
284 expected = {
285 "MJD-OBS": [self.h2["MJD-OBS"], self.h1["MJD-OBS"]],
286 "ORIGIN": self.h1["ORIGIN"],
287 "KEY0": [self.h2["KEY0"], self.h1["KEY0"]],
288 "KEY1": self.h1["KEY1"],
289 "KEY2": [self.h2["KEY2"], self.h1["KEY2"]],
290 "KEY3": self.h1["KEY3"],
291 "KEY4": self.h1["KEY4"],
292 "KEY5": self.h2["KEY5"],
293 }
295 self.assertEqual(merged, expected)
297 merged = merge_headers([self.h1, self.h2, self.h3, self.h4],
298 mode="append", sort=True)
300 expected = {
301 "MJD-OBS": [self.h2["MJD-OBS"], self.h1["MJD-OBS"], self.h4["MJD-OBS"], self.h3["MJD-OBS"]],
302 "ORIGIN": [self.h2["ORIGIN"], self.h1["ORIGIN"], None, self.h3["ORIGIN"]],
303 "KEY0": [self.h2["KEY0"], self.h1["KEY0"], None, None],
304 "KEY1": [None, self.h1["KEY1"], self.h4["KEY1"], None],
305 "KEY2": [self.h2["KEY2"], self.h1["KEY2"], None, self.h3["KEY2"]],
306 "KEY3": self.h3["KEY3"],
307 "KEY4": self.h1["KEY4"],
308 "KEY5": self.h3["KEY5"],
309 "KEY6": self.h4["KEY6"],
310 }
312 self.assertEqual(merged, expected)
314 # Order should not matter
315 merged = merge_headers([self.h4, self.h3, self.h2, self.h1],
316 mode="append", sort=True)
317 self.assertEqual(merged, expected)
320class FixHeadersTestCase(unittest.TestCase):
322 def test_basic_fix_header(self):
323 """Test that a header can be fixed if we specify a local path.
324 """
326 header = read_test_file("fitsheader-decam-0160496.yaml", dir=os.path.join(TESTDIR, "data"))
327 self.assertEqual(header["DETECTOR"], "S3-111_107419-8-3")
329 # First fix header but using no search path (should work as no-op)
330 fixed = fix_header(header)
331 self.assertFalse(fixed)
333 # Now using the test corrections directory
334 fixed = fix_header(header, search_path=os.path.join(TESTDIR, "data", "corrections"))
335 self.assertTrue(fixed)
336 self.assertEqual(header["DETECTOR"], "NEW-ID")
338 # Test that fix_header of unknown header is allowed
339 header = {"SOMETHING": "UNKNOWN"}
340 fixed = fix_header(header)
341 self.assertFalse(fixed)
343 def test_hsc_fix_header(self):
344 """Check that one of the known HSC corrections is being applied
345 properly."""
346 header = {"EXP-ID": "HSCA00120800",
347 "INSTRUME": "HSC",
348 "DATA-TYP": "FLAT"}
350 fixed = fix_header(header, translator_class=HscTranslator)
351 self.assertTrue(fixed)
352 self.assertEqual(header["DATA-TYP"], "OBJECT")
354 # And that this header won't be corrected
355 header = {"EXP-ID": "HSCA00120800X",
356 "INSTRUME": "HSC",
357 "DATA-TYP": "FLAT"}
359 fixed = fix_header(header, translator_class=HscTranslator)
360 self.assertFalse(fixed)
361 self.assertEqual(header["DATA-TYP"], "FLAT")
363 def test_translator_fix_header(self):
364 """Check that translator classes can fix headers."""
366 # Read in a known header
367 header = read_test_file("fitsheader-decam-0160496.yaml", dir=os.path.join(TESTDIR, "data"))
368 self.assertEqual(header["DTSITE"], "ct")
369 fixed = fix_header(header, translator_class=NotDecamTranslator)
370 self.assertTrue(fixed)
371 self.assertEqual(header["DTSITE"], "hi")
373 header["DTSITE"] = "reset"
374 with self.assertLogs("astro_metadata_translator", level="FATAL"):
375 fixed = fix_header(header, translator_class=AlsoNotDecamTranslator)
376 self.assertFalse(fixed)
377 self.assertEqual(header["DTSITE"], "reset")
380if __name__ == "__main__": 380 ↛ 381line 380 didn't jump to line 381, because the condition on line 380 was never true
381 unittest.main()