Files
tensor/io/hdf5_bool_fixture_test.go
T
petrbalvin af4ee19703
Release / gates (push) Successful in 4m38s
Test / test (push) Successful in 5m16s
Release / release (push) Successful in 35s
feat: initial release
Assisted-by: GLM 5.3 Flash
2026-09-03 10:00:00 +02:00

91 lines
3.1 KiB
Go

// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
// SPDX-License-Identifier: MIT
package io
import (
"path/filepath"
"slices"
"testing"
"sourcedock.dev/petrbalvin/tensor/internal/core"
)
// The boolean fixture bool_enum.h5 under testdata/h5 was written by
// hand against the HDF5 1.8 specification, in the shape the reference
// library writes for an old-style group with a symbol table, and it
// validates against the reference library's own tools: h5dump reads
// the dataset back as an H5T_ENUM over H5T_STD_U8LE with the members
// FALSE = 0 and TRUE = 1 and the values TRUE, FALSE, TRUE, TRUE,
// FALSE. Its layout deliberately differs from what this package's
// writer produces: the messages of the dataset's object header sit in
// the order datatype, fill value, dataspace, layout, the dataspace
// carries no maximum dimensions and the fill value message declares a
// defined zero fill, so the pins below prove the reader's tolerance of
// a foreign layout rather than a round trip of its own bytes.
// TestLoadHDF5ForeignBoolFixture pins the read side: the hand-crafted
// boolean enumeration of a foreign layout lands the dataset "flags"
// with the core Bool dtype and the values the reference library reads
// from the same bytes.
func TestLoadHDF5ForeignBoolFixture(t *testing.T) {
sets, err := LoadHDF5(h5Fixture(t, "bool_enum.h5"))
if err != nil {
t.Fatalf("LoadHDF5: %v", err)
}
if len(sets) != 1 {
t.Fatalf("datasets = %d, want 1", len(sets))
}
d := sets[0]
if d.Path != "/flags" {
t.Fatalf("path = %q, want /flags", d.Path)
}
if s := d.Shape; len(s) != 1 || s[0] != 5 {
t.Fatalf("shape = %v, want [5]", s)
}
if dt := d.Values.Dtype(); dt != core.Bool {
t.Fatalf("dtype = %s, want bool", dt)
}
want := []bool{true, false, true, true, false}
if got := d.Values.RawBools()[:5]; !slices.Equal(got, want) {
t.Fatalf("values = %v, want %v", got, want)
}
}
// TestSaveHDF5BoolConvention pins the write side against the same
// logical values: SaveHDF5 stores them through the boolean enumeration
// convention and LoadHDF5 reads them back identically. The pin holds
// the two-sided agreement on the convention, not a byte match with the
// foreign fixture, whose layout the writer need not reproduce.
func TestSaveHDF5BoolConvention(t *testing.T) {
want := []bool{true, false, true, true, false}
values, err := core.FromBools(want, 5)
if err != nil {
t.Fatalf("FromBools: %v", err)
}
path := filepath.Join(t.TempDir(), "bool_convention.h5")
if err := SaveHDF5(path, []HDF5Dataset{{Path: "/flags", Shape: []int{5}, Values: values}}, nil); err != nil {
t.Fatalf("SaveHDF5: %v", err)
}
sets, err := LoadHDF5(path)
if err != nil {
t.Fatalf("LoadHDF5: %v", err)
}
if len(sets) != 1 {
t.Fatalf("datasets = %d, want 1", len(sets))
}
d := sets[0]
if d.Path != "/flags" {
t.Fatalf("path = %q, want /flags", d.Path)
}
if s := d.Shape; len(s) != 1 || s[0] != 5 {
t.Fatalf("shape = %v, want [5]", s)
}
if dt := d.Values.Dtype(); dt != core.Bool {
t.Fatalf("dtype = %s, want bool", dt)
}
if got := d.Values.RawBools()[:5]; !slices.Equal(got, want) {
t.Fatalf("values = %v, want %v", got, want)
}
}