1package core
2
3import "testing"
4
5func TestWorkspaceStepFollowsWorkspaceAxis(t *testing.T) {
6 for _, tc := range []struct {
7 name string
8 overflow Overflow
9 vertical int
10 horizontal int
11 }{
12 {"scroll", OverflowScroll, 1, 0},
13 {"fixed", OverflowFixed, 1, 0},
14 {"cascade", OverflowCascade, 0, 1},
15 } {
16 t.Run(tc.name, func(t *testing.T) {
17 w := &Workspace{Overflow: tc.overflow}
18 p := w.policy()
19 for _, dir := range []int{-1, 1} {
20 if got := p.workspaceStep(verticalAxis, dir); got != dir*tc.vertical {
21 t.Errorf("vertical step %d = %d, want %d", dir, got, dir*tc.vertical)
22 }
23 if got := p.workspaceStep(horizontalAxis, dir); got != dir*tc.horizontal {
24 t.Errorf("horizontal step %d = %d, want %d", dir, got, dir*tc.horizontal)
25 }
26 }
27 })
28 }
29}
30
31// oldValidSpans is the reference implementation validSpans must match.
32func oldValidSpans(w *Workspace, first, last int) bool {
33 if first > last || w.Overflow == OverflowFixed && !w.laneRow() {
34 return false
35 }
36 stamp := w.Columns[first].spanStamp
37 if stamp == 0 {
38 return false
39 }
40 total, count := 0, 0
41 for i := first; i <= last; i++ {
42 if w.Columns[i].Span <= 0 || w.Columns[i].spanStamp != stamp {
43 return false
44 }
45 total += w.Columns[i].Span
46 }
47 for _, c := range w.Columns {
48 if c.spanStamp == stamp {
49 count++
50 }
51 }
52 return count == last-first+1 && (!w.policy().equalCells || total == 10000)
53}
54
55func TestScrollWidthsMatchPerColumnValidation(t *testing.T) {
56 for _, n := range []int{1, 2, 5, 9} {
57 for _, state := range []string{"unstamped", "stamped", "stale", "foreign", "zero"} {
58 w := spanWorkspace(OverflowScroll, n)
59 switch state {
60 case "stamped":
61 stampSpans(w, 0, n-1)
62 case "stale":
63 stampSpans(w, 0, n-1)
64 w.Columns[n-1].spanStamp = 99
65 case "foreign":
66 stampSpans(w, 0, n-1)
67 w.Columns = append(w.Columns, Column{Windows: []WindowID{100}, Span: 5, spanStamp: w.Columns[0].spanStamp})
68 case "zero":
69 stampSpans(w, 0, n-1)
70 w.Columns[0].Span = 0
71 }
72 last := len(w.Columns) - 1
73 if got, want := w.validSpans(0, last), oldValidSpans(w, 0, last); got != want {
74 t.Fatal(n, state, got, want)
75 }
76 valid := w.scrollSpansValid()
77 xs, widths := w.columnEdges()
78 rs := w.columnRects()
79 for i := range w.Columns {
80 if widths[i] != w.scrollWidth(i, oldValidSpans(w, 0, last)) || valid != oldValidSpans(w, 0, last) {
81 t.Fatal(n, state, i, "width")
82 }
83 if xs[i] != w.columnX(i) || widths[i] != w.columnWidth(i) || rs[i].W != widths[i] {
84 t.Fatal(n, state, i, "edges")
85 }
86 }
87 }
88 }
89}
90
91func TestDropStaleSpansKeepsValidLanes(t *testing.T) {
92 w := spanWorkspace(OverflowScroll, 4)
93 // An unstamped workspace returns before any lane is measured.
94 if n := testing.AllocsPerRun(10, w.dropStaleSpans); n != 0 {
95 t.Fatalf("unstamped workspace allocates %v", n)
96 }
97 if w.Columns[0].Span != 0 || w.Columns[0].spanStamp != 0 {
98 t.Fatal("unstamped workspace changed")
99 }
100 stampSpans(w, 0, 3)
101 spans := []int{w.Columns[0].Span, w.Columns[3].Span}
102 if n := testing.AllocsPerRun(10, w.dropStaleSpans); n != 0 {
103 t.Fatalf("valid lane allocates %v", n)
104 }
105 if w.Columns[0].Span != spans[0] || w.Columns[3].Span != spans[1] {
106 t.Fatal("valid lane dropped")
107 }
108 w.Columns[2].spanStamp = 77
109 w.dropStaleSpans()
110 for i, c := range w.Columns {
111 if c.Span != 0 || c.spanStamp != 0 {
112 t.Fatal("stale lane kept", i)
113 }
114 }
115}
116
117// TestColumnEdgesMatchColumnRects checks columnEdges against columnRects, an
118// independent path that builds the same row with the view shift applied.
119func TestColumnEdgesMatchColumnRects(t *testing.T) {
120 for _, tc := range []struct {
121 name string
122 mode Overflow
123 setup func(w *Workspace)
124 // xs is compared where the rects keep the row's x (not cascade bands,
125 // nor the focused maximized column of fixed overflow).
126 xs bool
127 }{
128 {"scroll plain", OverflowScroll, func(w *Workspace) {}, true},
129 {"scroll stamped", OverflowScroll, func(w *Workspace) { stampSpans(w, 0, len(w.Columns)-1) }, true},
130 {"scroll full width", OverflowScroll, func(w *Workspace) { w.Columns[1].FullWidth = true }, true},
131 {"scroll fullscreen", OverflowScroll, func(w *Workspace) { w.SetFullscreen(w.Columns[2].Windows[0], true) }, true},
132 {"scroll viewed", OverflowScroll, func(w *Workspace) { w.View = 137 }, true},
133 {"fixed", OverflowFixed, func(w *Workspace) {}, true},
134 {"fixed full width", OverflowFixed, func(w *Workspace) { w.Columns[1].FullWidth = true; w.Focus = 0 }, true},
135 {"fixed focused full width", OverflowFixed, func(w *Workspace) { w.Columns[1].FullWidth = true; w.Focus = 1 }, false},
136 {"fixed fullscreen", OverflowFixed, func(w *Workspace) { w.SetFullscreen(w.Columns[2].Windows[0], true) }, true},
137 {"fixed stamped", OverflowFixed, func(w *Workspace) { stampSpans(w, 0, len(w.Columns)-1) }, true},
138 {"cascade", OverflowCascade, func(w *Workspace) {}, false},
139 {"cascade full width", OverflowCascade, func(w *Workspace) { w.Columns[1].FullWidth = true }, false},
140 {"cascade stamped", OverflowCascade, func(w *Workspace) { stampSpans(w, 0, len(w.Columns)-1) }, false},
141 } {
142 t.Run(tc.name, func(t *testing.T) {
143 w := spanWorkspace(tc.mode, 3)
144 tc.setup(w)
145 if tc.mode != OverflowScroll && w.scrollSpansValid() {
146 t.Fatal("equal-cell workspace must not take the scroll span path")
147 }
148 xs, ws := w.columnEdges()
149 rs := w.columnRectsAt(nil, false, 0)
150 if len(xs) != len(rs) || len(ws) != len(rs) {
151 t.Fatal("length", len(xs), len(ws), len(rs))
152 }
153 for i, r := range rs {
154 if ws[i] != r.W {
155 t.Errorf("column %d width %d, rect %d", i, ws[i], r.W)
156 }
157 if tc.xs && xs[i] != r.X+w.View {
158 t.Errorf("column %d x %d, rect %d + view %d", i, xs[i], r.X, w.View)
159 }
160 }
161 })
162 }
163}