81 lines
2.4 KiB
Go
81 lines
2.4 KiB
Go
// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package io
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"sourcedock.dev/petrbalvin/tensor/internal/core"
|
|
)
|
|
|
|
// The binary-table reader resolves header keywords through a map whose
|
|
// documented rule is first-occurrence-wins, the behaviour the previous
|
|
// linear scan answered. A hostile header may repeat a structural card
|
|
// with a different value: this pin fixes that the reader follows the
|
|
// first value of each repeated card.
|
|
func TestLoadFITSTableRepeatedKeywordsFirstWins(t *testing.T) {
|
|
body := make([]byte, 16)
|
|
for r := range 2 {
|
|
binary.BigEndian.PutUint64(body[r*8:], uint64(1000+r))
|
|
}
|
|
cards := []string{
|
|
fitsStringCardRaw("XTENSION", "BINTABLE"),
|
|
fitsIntCard("BITPIX", 8),
|
|
fitsIntCard("NAXIS", 2),
|
|
fitsIntCard("NAXIS1", 8),
|
|
fitsIntCard("NAXIS2", 2),
|
|
fitsIntCard("NAXIS2", 99),
|
|
fitsIntCard("PCOUNT", 0),
|
|
fitsIntCard("GCOUNT", 1),
|
|
fitsIntCard("TFIELDS", 1),
|
|
fitsIntCard("TFIELDS", 5),
|
|
fitsStringCardRaw("TTYPE1", "COL1"),
|
|
fitsStringCardRaw("TFORM1", "K"),
|
|
fitsStringCardRaw("TFORM1", "D"),
|
|
fitsEndCard(),
|
|
}
|
|
out := fitsAppendCards(nil, []string{
|
|
fitsBoolCard("SIMPLE", true),
|
|
fitsIntCard("BITPIX", 8),
|
|
fitsIntCard("NAXIS", 0),
|
|
fitsBoolCard("EXTEND", true),
|
|
fitsEndCard(),
|
|
})
|
|
out = fitsAppendCards(out, cards)
|
|
out = append(out, body...)
|
|
out = fitsAppendZeroPad(out)
|
|
path := filepath.Join(t.TempDir(), "repeated.fits")
|
|
if err := os.WriteFile(path, out, 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
table, err := LoadFITSTable(path)
|
|
if err != nil {
|
|
t.Fatalf("LoadFITSTable: %v", err)
|
|
}
|
|
if table.Rows != 2 {
|
|
t.Fatalf("rows = %d, want 2 from the first NAXIS2", table.Rows)
|
|
}
|
|
if len(table.Columns) != 1 {
|
|
t.Fatalf("columns = %d, want 1 from the first TFIELDS", len(table.Columns))
|
|
}
|
|
col := table.Columns[0]
|
|
if col == nil {
|
|
t.Fatal("the first column came back nil; the repeated TFORM1 must still decode the first form")
|
|
}
|
|
if len(table.Text) > 0 && table.Text[0] != nil {
|
|
t.Fatalf("column text = %v, want nil for a K column decoded from the first TFORM1", table.Text[0])
|
|
}
|
|
if col.Dtype() != core.Int {
|
|
t.Fatalf("column dtype = %s, want Int from the first TFORM1 (K)", col.Dtype())
|
|
}
|
|
for i, want := range []int64{1000, 1001} {
|
|
if got := col.RawInts()[i]; got != want {
|
|
t.Fatalf("value %d = %d, want %d", i, got, want)
|
|
}
|
|
}
|
|
}
|