91 lines
3.1 KiB
Go
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)
|
|
}
|
|
}
|