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