Files
sanderling/internal/tracecorpus/corpus_test.go
T
pj 7823f53453 feat(tracecorpus): load recorded runs for offline measures
reads a run directory's meta and every step, and refuses a step whose trace_version is not the current one: an older step stores no element depths, so its hierarchy decodes with a nil root and a structural hash over it is the empty string for every screen. Discover walks a tree for the directories holding both meta.json and trace.jsonl.
2026-08-16 17:44:52 +05:30

123 lines
3.1 KiB
Go

package tracecorpus
import (
"encoding/json"
"os"
"path/filepath"
"strings"
"testing"
"github.com/priyanshujain/sanderling/internal/hierarchy"
"github.com/priyanshujain/sanderling/internal/trace"
)
const oneScreen = `{"attributes": {"text": "Hi", "bounds": "[0,0,10,10]"}, "children": [
{"attributes": {"text": "child"}, "children": []}
]}`
func TestLoadRebuildsTheTreeAStepStored(t *testing.T) {
directory := writeRun(t, oneScreen)
run, err := Load(directory)
if err != nil {
t.Fatalf("load: %v", err)
}
if len(run.Steps) != 1 {
t.Fatalf("steps = %d, want 1", len(run.Steps))
}
root := run.Steps[0].Hierarchy.Root
if root == nil {
t.Fatal("stored hierarchy came back with no root, so no selector or hash reads it")
}
if len(root.Children) != 1 || root.Children[0].Text != "child" {
t.Fatalf("rebuilt tree lost its child: %+v", root)
}
}
func TestLoadRefusesAVersionZeroStepByVersion(t *testing.T) {
directory := writeRun(t, oneScreen)
downgrade(t, filepath.Join(directory, "trace.jsonl"))
_, err := Load(directory)
if err == nil {
t.Fatal("a version 0 step must be refused, not measured")
}
if !strings.Contains(err.Error(), "trace_version 0") {
t.Fatalf("refusal must name the version, got %q", err)
}
}
// TestLoadRefusesElementsWithNoStoredShape covers the failure that would be
// silent: a tree that decodes with elements and no root hashes to the empty
// string, which reads as one state shared by every screen.
func TestLoadRefusesElementsWithNoStoredShape(t *testing.T) {
directory := writeRun(t, oneScreen)
path := filepath.Join(directory, "trace.jsonl")
body, err := os.ReadFile(path)
if err != nil {
t.Fatal(err)
}
var step map[string]any
if err := json.Unmarshal(body, &step); err != nil {
t.Fatal(err)
}
hierarchyField := step["hierarchy"].(map[string]any)
delete(hierarchyField, "depths")
rewrite(t, path, step)
if _, err := Load(directory); err == nil ||
!strings.Contains(err.Error(), "cannot be rebuilt") {
t.Fatalf("a shapeless tree must be refused, got %v", err)
}
}
func writeRun(t *testing.T, dumps ...string) string {
t.Helper()
directory := t.TempDir()
writer, err := trace.NewWriter(directory)
if err != nil {
t.Fatal(err)
}
if err := writer.WriteMeta(trace.Meta{Seed: 7, Platform: "web"}); err != nil {
t.Fatal(err)
}
for index, dump := range dumps {
tree, err := hierarchy.Parse(dump)
if err != nil {
t.Fatal(err)
}
if err := writer.WriteStep(trace.Step{Index: index + 1, Hierarchy: tree}); err != nil {
t.Fatal(err)
}
}
if err := writer.Close(); err != nil {
t.Fatal(err)
}
return directory
}
func downgrade(t *testing.T, path string) {
t.Helper()
body, err := os.ReadFile(path)
if err != nil {
t.Fatal(err)
}
var step map[string]any
if err := json.Unmarshal(body, &step); err != nil {
t.Fatal(err)
}
delete(step, "trace_version")
rewrite(t, path, step)
}
func rewrite(t *testing.T, path string, step map[string]any) {
t.Helper()
body, err := json.Marshal(step)
if err != nil {
t.Fatal(err)
}
if err := os.WriteFile(path, append(body, '\n'), 0o644); err != nil {
t.Fatal(err)
}
}