1package core
2
3import (
4 "reflect"
5 "testing"
6)
7
8// cols builds a workspace with the given columns, focus on the last window
9// of column focus.
10func cols(overflow Overflow, max, focus int, columns ...[]WindowID) *Workspace {
11 w := &Workspace{Overflow: overflow, MaxColumns: max}
12 w.SetOutput(300, 100)
13 for _, c := range columns {
14 w.Columns = append(w.Columns, Column{Windows: c, Focus: len(c) - 1})
15 }
16 w.Focus = focus
17 return w
18}
19
20func ids(w *Workspace) [][]WindowID {
21 var out [][]WindowID
22 for _, c := range w.Columns {
23 out = append(out, c.Windows)
24 }
25 return out
26}
27
28func TestConsumeOrExpel(t *testing.T) {
29 for _, tc := range []struct {
30 name string
31 w *Workspace
32 dir int
33 want [][]WindowID
34 stays bool
35 focusCol int
36 }{
37 {"consume right", cols(OverflowScroll, 2, 0, []WindowID{1}, []WindowID{2}), 1, [][]WindowID{{2, 1}}, true, 0},
38 {"consume left", cols(OverflowScroll, 2, 1, []WindowID{1}, []WindowID{2}), -1, [][]WindowID{{1, 2}}, true, 0},
39 {"consume at the edge does nothing", cols(OverflowFixed, 3, 1, []WindowID{1}, []WindowID{2}), 1, [][]WindowID{{1}, {2}}, true, 1},
40 {"expel right in scroll", cols(OverflowScroll, 1, 0, []WindowID{1, 2}, []WindowID{3}), 1, [][]WindowID{{1}, {2}, {3}}, true, 1},
41 {"expel left in scroll", cols(OverflowScroll, 1, 1, []WindowID{1}, []WindowID{2, 3}), -1, [][]WindowID{{1}, {3}, {2}}, true, 1},
42 {"expel in fixed under the limit", cols(OverflowFixed, 3, 0, []WindowID{1, 2}, []WindowID{3}), 1, [][]WindowID{{1}, {2}, {3}}, true, 1},
43 {"full fixed stacks into the next column", cols(OverflowFixed, 3, 0, []WindowID{1, 2}, []WindowID{3}, []WindowID{4}), 1, [][]WindowID{{1}, {3, 2}, {4}}, true, 1},
44 {"full fixed at the edge leaves", cols(OverflowFixed, 3, 2, []WindowID{1}, []WindowID{2}, []WindowID{3, 4}), 1, [][]WindowID{{1}, {2}, {3, 4}}, false, 2},
45 {"past the limit counts as full", cols(OverflowFixed, 2, 0, []WindowID{1, 2}, []WindowID{3}, []WindowID{4}), 1, [][]WindowID{{1}, {3, 2}, {4}}, true, 1},
46 } {
47 t.Run(tc.name, func(t *testing.T) {
48 stays := tc.w.ConsumeOrExpel(tc.dir)
49 if got := ids(tc.w); !reflect.DeepEqual(got, tc.want) || stays != tc.stays || tc.w.Focus != tc.focusCol {
50 t.Fatal(got, stays, tc.w.Focus)
51 }
52 })
53 }
54}
55
56func TestConsumeOrExpelFocusFollows(t *testing.T) {
57 w := cols(OverflowFixed, 3, 0, []WindowID{1, 2}, []WindowID{3}, []WindowID{4})
58 w.ConsumeOrExpel(1)
59 if id, _ := w.Focused(); id != 2 {
60 t.Fatal(id)
61 }
62}
63
64func TestConsumeOrExpelSkips(t *testing.T) {
65 t.Run("floating focus", func(t *testing.T) {
66 w := cols(OverflowScroll, 2, 0, []WindowID{1, 2})
67 w.AddFloating(9, 10, 10)
68 w.FocusID(9)
69 w.ConsumeOrExpel(1)
70 if got := ids(w); !reflect.DeepEqual(got, [][]WindowID{{1, 2}}) {
71 t.Fatal(got)
72 }
73 })
74 t.Run("fullscreen", func(t *testing.T) {
75 w := cols(OverflowFixed, 3, 0, []WindowID{1, 2})
76 w.fullscreen = 2
77 w.ConsumeOrExpel(1)
78 if got := ids(w); !reflect.DeepEqual(got, [][]WindowID{{1, 2}}) {
79 t.Fatal(got)
80 }
81 })
82}
83
84func TestConsumeIntoFullscreenColumnShows(t *testing.T) {
85 w := cols(OverflowScroll, 2, 0, []WindowID{1}, []WindowID{2})
86 w.fullscreen = 2
87 w.ConsumeOrExpel(1)
88 if id, _ := w.Focused(); !reflect.DeepEqual(ids(w), [][]WindowID{{2, 1}}) || id != 1 || w.fullscreen != 0 {
89 t.Fatal(ids(w), id, w.fullscreen)
90 }
91}
92
93func TestConsumeOrExpelSlots(t *testing.T) {
94 slots := func(w *Workspace) []int {
95 var out []int
96 for _, c := range w.Columns {
97 out = append(out, c.Slot)
98 }
99 return out
100 }
101 t.Run("the slot follows its window", func(t *testing.T) {
102 w := cols(OverflowScroll, 1, 0, []WindowID{1, 2})
103 w.Columns[0].Slot, w.Columns[0].Width = 1, Width{Num: 2, Den: 3}
104 w.Columns[0].Focus = 0
105 w.ConsumeOrExpel(1)
106 if got := ids(w); !reflect.DeepEqual(got, [][]WindowID{{2}, {1}}) || !reflect.DeepEqual(slots(w), []int{0, 1}) || w.Columns[1].Width != (Width{Num: 2, Den: 3}) || w.Columns[0].Width != (Width{}) {
107 t.Fatal(got, slots(w), w.Columns)
108 }
109 })
110 t.Run("another window leaves the slot", func(t *testing.T) {
111 w := cols(OverflowScroll, 1, 0, []WindowID{1, 2})
112 w.Columns[0].Slot = 1
113 w.ConsumeOrExpel(1)
114 if !reflect.DeepEqual(slots(w), []int{1, 0}) {
115 t.Fatal(slots(w))
116 }
117 })
118 t.Run("stacking the slot window releases it", func(t *testing.T) {
119 w := cols(OverflowFixed, 2, 0, []WindowID{1, 2}, []WindowID{3})
120 w.Columns[0].Slot, w.Columns[0].Focus = 1, 0
121 w.ConsumeOrExpel(1)
122 if got := ids(w); !reflect.DeepEqual(got, [][]WindowID{{2}, {3, 1}}) || !reflect.DeepEqual(slots(w), []int{0, 0}) {
123 t.Fatal(got, slots(w))
124 }
125 })
126}
127
128func TestExpelTo(t *testing.T) {
129 t.Run("new edge column", func(t *testing.T) {
130 w := cols(OverflowFixed, 3, 0, []WindowID{1}, []WindowID{2})
131 w.expelTo(9, 1)
132 if got := ids(w); !reflect.DeepEqual(got, [][]WindowID{{9}, {1}, {2}}) || w.Focus != 0 {
133 t.Fatal(got)
134 }
135 })
136 t.Run("full stacks into the first column", func(t *testing.T) {
137 w := cols(OverflowFixed, 2, 1, []WindowID{1}, []WindowID{2})
138 w.expelTo(9, 1)
139 if id, _ := w.Focused(); !reflect.DeepEqual(ids(w), [][]WindowID{{1, 9}, {2}}) || id != 9 {
140 t.Fatal(ids(w), id)
141 }
142 })
143 t.Run("full from the right stacks into the last column", func(t *testing.T) {
144 w := cols(OverflowFixed, 2, 0, []WindowID{1}, []WindowID{2}, []WindowID{3})
145 w.expelTo(9, -1)
146 if id, _ := w.Focused(); !reflect.DeepEqual(ids(w), [][]WindowID{{1}, {2}, {3, 9}}) || id != 9 {
147 t.Fatal(ids(w), id)
148 }
149 })
150 t.Run("empty workspace", func(t *testing.T) {
151 w := cols(OverflowFixed, 2, 0)
152 w.expelTo(9, -1)
153 if !reflect.DeepEqual(ids(w), [][]WindowID{{9}}) {
154 t.Fatal(ids(w))
155 }
156 })
157}