package render import ( "testing" "github.com/bnema/nefergui/internal/css" "github.com/bnema/nefergui/internal/layout" ) func rectCommands(n int) []layout.Command { cmds := make([]layout.Command, n) for i := range cmds { cmds[i] = layout.Command{Op: "rect", Rect: layout.Rect{X: 1, Y: float64(i), W: 10, H: 1}, Color: css.Color{A: 1}, Opacity: 1} } return cmds } func TestQuadCapacityIsSufficientAndExactWithoutCulling(t *testing.T) { glyphs := glyphCommand(t, "office") n := 0 for _, r := range glyphs.Runs { n += len(r.Glyphs) } cmds := []layout.Command{{Op: "clip-push", Rect: layout.Rect{W: 200, H: 100}}, rectCommands(1)[0], glyphs, {Op: "clip-pop"}} if got := quadCapacity(cmds); got != n+1 { t.Fatalf("capacity %d, want %d", got, n+1) } p, err := NewPreparer(256, 2) if err != nil { t.Fatal(err) } f, err := p.Prepare(cmds, 1, 200, 100) if err != nil { t.Fatal(err) } if len(f.Quads) == 0 || len(f.Quads) > n+1 || cap(f.Quads) != n+1 { t.Fatalf("len=%d cap=%d, want cap %d", len(f.Quads), cap(f.Quads), n+1) } } // Frames are detached: a later Prepare must not alias or overwrite an earlier // frame that is still queued, blocked or superseded. func TestSuccessiveFramesAreDetached(t *testing.T) { p, err := NewPreparer(256, 2) if err != nil { t.Fatal(err) } a, err := p.Prepare(rectCommands(4), 1, 100, 100) if err != nil { t.Fatal(err) } want := append([]Quad(nil), a.Quads...) b, err := p.Prepare(rectCommands(3), 2, 100, 100) if err != nil { t.Fatal(err) } if len(a.Quads) != 4 || len(b.Quads) != 3 || &a.Quads[0] == &b.Quads[0] { t.Fatalf("frames alias: %d %d", len(a.Quads), len(b.Quads)) } for i := range want { if a.Quads[i] != want[i] { t.Fatalf("earlier frame quad %d overwritten", i) } } // Appending to a returned frame must not write into another frame. b.Quads = append(b.Quads[:len(b.Quads):len(b.Quads)], Quad{Op: "x"}) if a.Quads[0] != want[0] { t.Fatal("append leaked into earlier frame") } } func TestFailedPrepareReturnsNoPartialFrame(t *testing.T) { p, err := NewPreparer(256, 2) if err != nil { t.Fatal(err) } cmds := append(rectCommands(8), layout.Command{Op: "bogus"}) f, err := p.Prepare(cmds, 1, 100, 100) if err == nil || f.Quads != nil || f.Uploads != nil { t.Fatalf("partial frame on failure: %v %+v", err, f) } if f, err = p.Prepare(nil, 1, 100, 100); err != nil || f.Quads != nil { t.Fatalf("empty frame: %v %+v", err, f) } } // Preallocation must not disturb owed-upload accounting across frames whose // quads were sized differently. func TestPreallocatedFramesKeepUploadDebt(t *testing.T) { cmd := glyphCommand(t, "office") p, err := NewPreparer(256, 2) if err != nil { t.Fatal(err) } first, err := p.Prepare([]layout.Command{cmd}, 1, 200, 100) if err != nil || len(first.Uploads) == 0 { t.Fatalf("first: %v %d", err, len(first.Uploads)) } // Superseding frame with no glyphs still resends the owed pages. second, err := p.Prepare(rectCommands(2), 1, 200, 100) if err != nil || len(second.Uploads) == 0 { t.Fatalf("debt lost: %v %d", err, len(second.Uploads)) } p.Submitted(first.Uploads) if f, err := p.Prepare(rectCommands(2), 1, 200, 100); err != nil || len(f.Uploads) == 0 { t.Fatalf("stale ack cleared debt: %v %d", err, len(f.Uploads)) } } // Box-only frames must cost one Quads allocation regardless of command count. func TestPrepareQuadAllocationsDoNotScaleWithCommands(t *testing.T) { p, err := NewPreparer(256, 2) if err != nil { t.Fatal(err) } cmds := rectCommands(512) allocs := testing.AllocsPerRun(20, func() { if _, err := p.Prepare(cmds, 1, 100, 1000); err != nil { t.Fatal(err) } }) if allocs > 2 { t.Fatalf("%v allocs for 512 rects, want a small constant", allocs) } } func BenchmarkPrepareBoxes(b *testing.B) { p, err := NewPreparer(256, 2) if err != nil { b.Fatal(err) } cmds := rectCommands(512) b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { if f, err := p.Prepare(cmds, 1, 100, 1000); err != nil || len(f.Quads) != 512 { b.Fatal(err) } } }