feat: initial release
Assisted-by: GLM 5.3 Flash
This commit is contained in:
@@ -0,0 +1,80 @@
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user