Files

81 lines
2.4 KiB
Go
Raw Permalink Normal View History

2026-09-03 10:00:00 +02:00
// 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)
}
}
}