1package text
2
3import (
4 "fmt"
5 "testing"
6)
7
8func TestMeasureCacheHitsKeysAndEviction(t *testing.T) {
9 e := NewEngine(fixture(t))
10 r := Request{Families: []string{"Noto Sans"}, Size: 16}
11 first, err := e.Measure("Bonjour Ada", r, 0)
12 if err != nil {
13 t.Fatal(err)
14 }
15 allocs := testing.AllocsPerRun(5, func() {
16 if _, err := e.Measure("Bonjour Ada", r, 0); err != nil {
17 panic(err)
18 }
19 })
20 if allocs > 1 {
21 t.Fatalf("cached Measure allocates %.0f times", allocs)
22 }
23 // Any request field, the text or the width must miss.
24 for name, other := range map[string]func() (Layout, error){
25 "size": func() (Layout, error) { return e.Measure("Bonjour Ada", Request{Families: r.Families, Size: 20}, 0) },
26 "family": func() (Layout, error) {
27 return e.Measure("Bonjour Ada", Request{Families: []string{"serif"}, Size: 16}, 0)
28 },
29 "weight": func() (Layout, error) {
30 return e.Measure("Bonjour Ada", Request{Families: r.Families, Size: 16, Weight: 700}, 0)
31 },
32 "width": func() (Layout, error) { return e.Measure("Bonjour Ada", r, 40) },
33 "text": func() (Layout, error) { return e.Measure("Bonjour", r, 0) },
34 } {
35 l, err := other()
36 if err != nil {
37 t.Fatal(name, err)
38 }
39 if l.Width == first.Width && len(l.Lines) == len(first.Lines) && name != "family" && name != "weight" {
40 t.Errorf("%s: expected a different measurement", name)
41 }
42 }
43 if n := len(e.cache.entries); n != 6 {
44 t.Fatalf("entries=%d, want 6", n)
45 }
46 // Entries unused for two frames are evicted; used ones stay.
47 e.EndFrame()
48 if _, err := e.Measure("Bonjour Ada", r, 0); err != nil {
49 t.Fatal(err)
50 }
51 e.EndFrame()
52 e.EndFrame()
53 if n := len(e.cache.entries); n != 1 {
54 t.Fatalf("after eviction entries=%d, want 1", n)
55 }
56}
57
58func TestMeasureCacheBoundAndCloneIsolation(t *testing.T) {
59 e := NewEngine(fixture(t))
60 r := Request{Families: []string{"Noto Sans"}, Size: 16}
61 for i := 0; i < maxMeasureEntries+10; i++ {
62 if _, err := e.Measure(fmt.Sprint(i), r, 0); err != nil {
63 t.Fatal(err)
64 }
65 }
66 if n := len(e.cache.entries); n != maxMeasureEntries {
67 t.Fatalf("entries=%d, want bound %d", n, maxMeasureEntries)
68 }
69 cached, err := e.Measure("0", r, 0)
70 if err != nil || len(cached.Lines) == 0 || len(cached.Lines[0].Runs) == 0 || len(cached.Lines[0].Runs[0].Glyphs) == 0 {
71 t.Fatalf("measure: %+v %v", cached, err)
72 }
73 y := cached.Lines[0].Runs[0].Glyphs[0].Y
74 c := cached.Clone()
75 c.Lines[0].Baseline += 10
76 c.Lines[0].Runs[0].Y += 10
77 c.Lines[0].Runs[0].Glyphs[0].Y += 10
78 again, _ := e.Measure("0", r, 0)
79 if again.Lines[0].Runs[0].Glyphs[0].Y != y || again.Lines[0].Baseline != cached.Lines[0].Baseline {
80 t.Fatal("Clone shares memory with the cache")
81 }
82}