⋯ 26 hidden lines@@ -27,3 +27,137 @@ func TestWorkspaceStepFollowsWorkspaceAxis(t *testing.T) { 27 27 }) 28 28 } 29 29 } 30 + 31 + // oldValidSpans is the reference implementation validSpans must match. 32 + func 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 + 55 + func 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 + 91 + func 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. 119 + func 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 + }