github.com/astrogo/fitsio@v0.3.0/header_test.go (about) 1 // Copyright 2015 The astrogo Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package fitsio 6 7 import ( 8 "fmt" 9 "io/ioutil" 10 "math" 11 "math/big" 12 "os" 13 "reflect" 14 "testing" 15 ) 16 17 func newBigInt(t *testing.T) big.Int { 18 var i big.Int 19 _, err := fmt.Sscanf("40002100000000422948", "%v", &i) 20 if err != nil { 21 t.Fatalf("error creating a big.Int: %v\n", err) 22 } 23 return i 24 } 25 26 func TestHeaderRW(t *testing.T) { 27 curdir, err := os.Getwd() 28 if err != nil { 29 t.Fatalf(err.Error()) 30 } 31 defer os.Chdir(curdir) 32 33 workdir, err := ioutil.TempDir("", "go-fitsio-test-") 34 if err != nil { 35 t.Fatalf(err.Error()) 36 } 37 defer os.RemoveAll(workdir) 38 39 err = os.Chdir(workdir) 40 if err != nil { 41 t.Fatalf(err.Error()) 42 } 43 44 table := struct { 45 name string 46 version int 47 cards []Card 48 bitpix int 49 axes []int 50 image interface{} 51 }{ 52 name: "new.fits", 53 version: 2, 54 cards: []Card{ 55 { 56 "EXTNAME", 57 "primary hdu", 58 "the primary HDU", 59 }, 60 { 61 "EXTVER", 62 2, 63 "the primary hdu version", 64 }, 65 { 66 "card_uint8", 67 byte(42), 68 "an uint8", 69 }, 70 { 71 "card_uint16", 72 uint16(42), 73 "an uint16", 74 }, 75 { 76 "card_uint32", 77 uint32(42), 78 "an uint32", 79 }, 80 { 81 "card_uint64", 82 uint64(42), 83 "an uint64", 84 }, 85 { 86 "card_int8", 87 int8(42), 88 "an int8", 89 }, 90 { 91 "card_int16", 92 int16(42), 93 "an int16", 94 }, 95 { 96 "card_int32", 97 int32(42), 98 "an int32", 99 }, 100 { 101 "card_int64", 102 int64(42), 103 "an int64", 104 }, 105 { 106 "card_int3264", 107 int(42), 108 "an int", 109 }, 110 { 111 "card_uintxx", 112 uint(42), 113 "an uint", 114 }, 115 { 116 "card_float32", 117 float32(666), 118 "a float32", 119 }, 120 { 121 "card_float64", 122 float64(666), 123 "a float64", 124 }, 125 { 126 "card_complex64", 127 complex(float32(42), float32(66)), 128 "a complex64", 129 }, 130 { 131 "card_complex128", 132 complex(float64(42), float64(66)), 133 "a complex128", 134 }, 135 { 136 "card_bigint", 137 newBigInt(t), 138 "a big int", 139 }, 140 }, 141 bitpix: 8, 142 axes: []int{}, 143 } 144 fname := "new.fits" 145 for _, fct := range []func(){ 146 // create 147 func() { 148 w, err := os.Create(fname) 149 if err != nil { 150 t.Fatalf("error creating new file [%v]: %v", fname, err) 151 } 152 defer w.Close() 153 154 f, err := Create(w) 155 if err != nil { 156 t.Fatalf("error creating new file [%v]: %v", fname, err) 157 } 158 defer f.Close() 159 160 phdr := NewHeader( 161 table.cards, 162 IMAGE_HDU, 163 table.bitpix, 164 table.axes, 165 ) 166 phdu, err := NewPrimaryHDU(phdr) 167 if err != nil { 168 t.Fatalf("error creating PHDU: %v", err) 169 } 170 defer phdu.Close() 171 172 hdr := phdu.Header() 173 if hdr.bitpix != table.bitpix { 174 t.Fatalf("expected BITPIX=%v. got %v", table.bitpix, hdr.bitpix) 175 } 176 177 name := phdu.Name() 178 if name != "primary hdu" { 179 t.Fatalf("expected EXTNAME==%q. got %q", "primary hdu", name) 180 } 181 182 vers := phdu.Version() 183 if vers != table.version { 184 t.Fatalf("expected EXTVER==%v. got %v", table.version, vers) 185 } 186 187 card := hdr.Get("EXTNAME") 188 if card == nil { 189 t.Fatalf("error retrieving card [EXTNAME]") 190 } 191 if card.Comment != "the primary HDU" { 192 t.Fatalf("expected EXTNAME.Comment==%q. got %q", "the primary HDU", card.Comment) 193 } 194 195 card = hdr.Get("EXTVER") 196 if card == nil { 197 t.Fatalf("error retrieving card [EXTVER]") 198 } 199 if card.Comment != "the primary hdu version" { 200 t.Fatalf("expected EXTVER.Comment==%q. got %q", "the primary hdu version", card.Comment) 201 202 } 203 204 for _, ref := range table.cards { 205 card := hdr.Get(ref.Name) 206 if card == nil { 207 t.Fatalf("error retrieving card [%v]", ref.Name) 208 } 209 rv := reflect.ValueOf(ref.Value) 210 var val interface{} 211 switch rv.Type().Kind() { 212 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 213 val = int(rv.Int()) 214 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 215 val = int(rv.Uint()) 216 case reflect.Float32, reflect.Float64: 217 val = rv.Float() 218 case reflect.Complex64, reflect.Complex128: 219 val = rv.Complex() 220 case reflect.String: 221 val = ref.Value.(string) 222 case reflect.Bool: 223 val = ref.Value.(bool) 224 case reflect.Struct: 225 switch ref.Value.(type) { 226 case big.Int: 227 val = ref.Value.(big.Int) 228 } 229 } 230 if !reflect.DeepEqual(card.Value, val) { 231 t.Fatalf( 232 "card %q. expected [%v](%T). got [%v](%T)", 233 ref.Name, 234 val, val, 235 card.Value, card.Value, 236 ) 237 } 238 if card.Comment != ref.Comment { 239 t.Fatalf("card %q. comment differ. expected %q. got %q", ref.Name, ref.Comment, card.Comment) 240 } 241 } 242 243 card = hdr.Get("NOT THERE") 244 if card != nil { 245 t.Fatalf("expected no card. got [%v]", card) 246 } 247 248 err = f.Write(phdu) 249 if err != nil { 250 t.Fatalf("error writing hdu to file: %v", err) 251 } 252 }, 253 // read-back 254 func() { 255 r, err := os.Open(fname) 256 if err != nil { 257 t.Fatalf("error opening file [%v]: %v", fname, err) 258 } 259 defer r.Close() 260 f, err := Open(r) 261 if err != nil { 262 buf, _ := ioutil.ReadFile(fname) 263 t.Fatalf("error opening file [%v]: %v\nbuf=%s\n", fname, err, string(buf)) 264 } 265 defer f.Close() 266 267 hdu := f.HDU(0) 268 hdr := hdu.Header() 269 if hdr.bitpix != table.bitpix { 270 t.Fatalf("expected BITPIX=%v. got %v", 8, hdr.bitpix) 271 } 272 273 name := hdu.Name() 274 if name != "primary hdu" { 275 t.Fatalf("expected EXTNAME==%q. got %q", "primary hdu", name) 276 } 277 278 vers := hdu.Version() 279 if vers != table.version { 280 t.Fatalf("expected EXTVER==%v. got %v", 2, vers) 281 } 282 283 card := hdr.Get("EXTNAME") 284 if card == nil { 285 t.Fatalf("error retrieving card [EXTNAME]") 286 } 287 if card.Comment != "the primary HDU" { 288 t.Fatalf("expected EXTNAME.Comment==%q. got %q", "the primary HDU", card.Comment) 289 } 290 291 card = hdr.Get("EXTVER") 292 if card == nil { 293 t.Fatalf("error retrieving card [EXTVER]") 294 } 295 if card.Comment != "the primary hdu version" { 296 t.Fatalf("expected EXTVER.Comment==%q. got %q", "the primary hdu version", card.Comment) 297 298 } 299 300 for _, ref := range table.cards { 301 card := hdr.Get(ref.Name) 302 if card == nil { 303 t.Fatalf("error retrieving card [%v]", ref.Name) 304 } 305 306 rv := reflect.ValueOf(ref.Value) 307 var val interface{} 308 switch rv.Type().Kind() { 309 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 310 val = int(rv.Int()) 311 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 312 val = int(rv.Uint()) 313 case reflect.Float32, reflect.Float64: 314 val = rv.Float() 315 case reflect.Complex64, reflect.Complex128: 316 val = rv.Complex() 317 case reflect.String: 318 val = ref.Value.(string) 319 case reflect.Bool: 320 val = ref.Value.(bool) 321 case reflect.Struct: 322 switch ref.Value.(type) { 323 case big.Int: 324 val = ref.Value.(big.Int) 325 } 326 } 327 if !reflect.DeepEqual(card.Value, val) { 328 t.Fatalf( 329 "card %q. expected [%v](%T). got [%v](%T)", 330 ref.Name, 331 val, val, 332 card.Value, card.Value, 333 ) 334 } 335 336 if card.Comment != ref.Comment { 337 t.Fatalf("card %q. comment differ. expected %q. got %q", ref.Name, ref.Comment, card.Comment) 338 } 339 } 340 341 card = hdr.Get("NOT THERE") 342 if card != nil { 343 t.Fatalf("expected no card. got [%v]", card) 344 } 345 }, 346 } { 347 fct() 348 } 349 } 350 351 func TestRWHeaderLine(t *testing.T) { 352 for _, table := range []struct { 353 line []byte 354 card *Card 355 err error 356 }{ 357 { 358 line: []byte("SIMPLE = T / file does conform to FITS standard "), 359 card: &Card{ 360 Name: "SIMPLE", 361 Value: true, 362 Comment: "file does conform to FITS standard", 363 }, 364 err: nil, 365 }, 366 { 367 line: []byte("BITPIX = 16 / number of bits per data pixel "), 368 card: &Card{ 369 Name: "BITPIX", 370 Value: 16, 371 Comment: "number of bits per data pixel", 372 }, 373 err: nil, 374 }, 375 { 376 line: []byte("EXTNAME = 'primary hdu' / the primary HDU "), 377 card: &Card{ 378 Name: "EXTNAME", 379 Value: "primary hdu", 380 Comment: "the primary HDU", 381 }, 382 err: nil, 383 }, 384 { 385 line: []byte("STRING = 'a / ' / comment "), 386 card: &Card{ 387 Name: "STRING", 388 Value: "a /", 389 Comment: "comment", 390 }, 391 err: nil, 392 }, 393 { 394 line: []byte("STRING = ' a / ' / comment "), 395 card: &Card{ 396 Name: "STRING", 397 Value: " a /", 398 Comment: "comment", 399 }, 400 err: nil, 401 }, 402 { 403 line: []byte("STRING = ' a / / comment |'"), 404 card: &Card{ 405 Name: "STRING", 406 Value: " a / / comment |", 407 Comment: "", 408 }, 409 err: nil, 410 }, 411 { 412 line: []byte("STRING = ' a / / comment '"), 413 card: &Card{ 414 Name: "STRING", 415 Value: " a / / comment", 416 Comment: "", 417 }, 418 err: nil, 419 }, 420 { 421 line: []byte("STRING = 'a / ''' / comment "), 422 card: &Card{ 423 Name: "STRING", 424 Value: "a / '", 425 Comment: "comment", 426 }, 427 err: nil, 428 }, 429 { 430 line: []byte("COMPLEX = (42.0, 66.0) / comment "), 431 card: &Card{ 432 Name: "COMPLEX", 433 Value: complex(42, 66), 434 Comment: "comment", 435 }, 436 err: nil, 437 }, 438 { 439 line: []byte("COMPLEX = (42.0,66.0) / comment "), 440 card: &Card{ 441 Name: "COMPLEX", 442 Value: complex(42, 66), 443 Comment: "comment", 444 }, 445 err: nil, 446 }, 447 { 448 line: []byte("COMPLEX = (42,66) / comment "), 449 card: &Card{ 450 Name: "COMPLEX", 451 Value: complex(42, 66), 452 Comment: "comment", 453 }, 454 err: nil, 455 }, 456 { 457 line: []byte("COMPLEX = (42.0,66) / comment "), 458 card: &Card{ 459 Name: "COMPLEX", 460 Value: complex(42, 66), 461 Comment: "comment", 462 }, 463 err: nil, 464 }, 465 { 466 line: []byte("COMPLEX = (42,66.0) / comment "), 467 card: &Card{ 468 Name: "COMPLEX", 469 Value: complex(42, 66), 470 Comment: "comment", 471 }, 472 err: nil, 473 }, 474 { 475 line: []byte("COMPLEX = (42.000,66.0000) / comment "), 476 card: &Card{ 477 Name: "COMPLEX", 478 Value: complex(42, 66), 479 Comment: "comment", 480 }, 481 err: nil, 482 }, 483 } { 484 card, err := parseHeaderLine(table.line) 485 if !reflect.DeepEqual(err, table.err) { 486 t.Fatalf("expected error [%v]. got: %v", table.err, err) 487 } 488 if !reflect.DeepEqual(card, table.card) { 489 t.Fatalf("cards differ.\nexp= %#v\ngot= %#v", table.card, card) 490 } 491 492 line, err := makeHeaderLine(card) 493 if err != nil { 494 t.Fatalf("error making header-line: %v (%s)", err, string(line)) 495 } 496 } 497 498 for _, table := range []struct { 499 line []byte 500 err error 501 }{ 502 { 503 line: []byte("SIMPLE= T / FITS file"), 504 err: fmt.Errorf("fitsio: invalid header line length"), 505 }, 506 { 507 line: []byte("STRING = 'foo / comment "), 508 err: fmt.Errorf(`fitsio: string ends prematurely ("'foo / comment ")`), 509 }, 510 { 511 line: []byte("STRING = 'foo '' / comment "), 512 err: fmt.Errorf(`fitsio: string ends prematurely ("'foo '' / comment ")`), 513 }, 514 } { 515 card, err := parseHeaderLine(table.line) 516 if !reflect.DeepEqual(err, table.err) { 517 t.Fatalf("expected error [%v]. got: %v\ncard=%#v", table.err, err, card) 518 } 519 if card != nil { 520 t.Fatalf("expected a nil card. got= %#v", card) 521 } 522 } 523 } 524 525 func TestMakeHeaderLine(t *testing.T) { 526 for _, table := range []struct { 527 card *Card 528 line []byte 529 err error 530 }{ 531 { 532 card: &Card{ 533 Name: "SIMPLE", 534 Value: true, 535 Comment: "file does conform to FITS standard", 536 }, 537 line: []byte("SIMPLE = T / file does conform to FITS standard "), 538 err: nil, 539 }, 540 { 541 line: []byte("STRING = ' a / / no-comment 1&'CONTINUE '2| ' COMMENT my-comment "), 542 card: &Card{ 543 Name: "STRING", 544 Value: " a / / no-comment 12|", 545 Comment: "my-comment", 546 }, 547 err: nil, 548 }, 549 { 550 line: []byte("STRING = ' a / / no-comment 1&'CONTINUE '2| ' "), 551 card: &Card{ 552 Name: "STRING", 553 Value: " a / / no-comment 12|", 554 Comment: "", 555 }, 556 err: nil, 557 }, 558 { 559 line: []byte("STRING = ' a / / no-comment |&'CONTINUE '0123456789012345678901234567890123456789012345678901234567890123456&'CONTINUE '7890123456789|' "), 560 card: &Card{ 561 Name: "STRING", 562 Value: " a / / no-comment |01234567890123456789012345678901234567890123456789012345678901234567890123456789|", 563 Comment: "", 564 }, 565 err: nil, 566 }, 567 { 568 line: []byte("STRING = ' a / / no-comment |&'CONTINUE '0123456789012345678901234567890123456789012345678901234567890123456&'CONTINUE '7890123456789|' COMMENT my-comment "), 569 card: &Card{ 570 Name: "STRING", 571 Value: " a / / no-comment |01234567890123456789012345678901234567890123456789012345678901234567890123456789|", 572 Comment: "my-comment", 573 }, 574 err: nil, 575 }, 576 { 577 line: []byte("COMMENT * "), 578 card: &Card{ 579 Name: "COMMENT", 580 Value: "", 581 Comment: "*", 582 }, 583 err: nil, 584 }, 585 { 586 line: []byte("FLOAT64 = 1.1234567891234568 / small float value "), 587 card: &Card{ 588 Name: "FLOAT64", 589 Value: float64(1.123456789123456789123456789), 590 Comment: "small float value", 591 }, 592 err: nil, 593 }, 594 { 595 line: []byte("FLOAT64 = 9.223372036854776E+18 / large float value "), 596 card: &Card{ 597 Name: "FLOAT64", 598 Value: float64(9223372036854775807.123456789123456789123456789), 599 Comment: "large float value", 600 }, 601 err: nil, 602 }, 603 { 604 line: []byte("HIERARCH DIVFLOAT64= 0.6666666666666666 / infinit "), 605 card: &Card{ 606 Name: "DIVFLOAT64", 607 Value: float64(2.0 / 3.0), 608 Comment: "infinit", 609 }, 610 err: nil, 611 }, 612 { 613 line: []byte("HIERARCH MAXFLOAT64= 1.7976931348623157E+308 / MaxFloat64 "), 614 card: &Card{ 615 Name: "MAXFLOAT64", 616 Value: math.MaxFloat64, 617 Comment: "MaxFloat64", 618 }, 619 err: nil, 620 }, 621 { 622 line: []byte("HIERARCH SMALLESTNONZEROFLOAT= 5.00000E-324 / SmallestNonzeroFloat64 "), 623 card: &Card{ 624 Name: "SMALLESTNONZEROFLOAT", 625 Value: math.SmallestNonzeroFloat64, 626 Comment: "SmallestNonzeroFloat64", 627 }, 628 err: nil, 629 }, 630 } { 631 line, err := makeHeaderLine(table.card) 632 if !reflect.DeepEqual(err, table.err) { 633 t.Fatalf("expected error [%v]. got: %v\nline=%q", table.err, err, string(line)) 634 } 635 if !reflect.DeepEqual(line, table.line) { 636 t.Fatalf("bline differ.\nexp=%q\ngot=%q", string(table.line), string(line)) 637 } 638 } 639 } 640 641 func TestDuplicateCardKeys(t *testing.T) { 642 r, err := os.Open("testdata/issue-38.fits") 643 if err != nil { 644 t.Fatal(err) 645 } 646 defer r.Close() 647 648 f, err := Open(r) 649 if err != nil { 650 t.Fatal(err) 651 } 652 defer f.Close() 653 654 hdu := f.HDU(0) 655 hdr := hdu.Header() 656 c := hdr.Get("DUP") 657 want := 1 658 if !reflect.DeepEqual(c.Value, want) { 659 t.Fatalf("got %v for duplicate key. want %v (the first one)", c.Value, want) 660 } 661 }