1package layout
2
3import (
4 "testing"
5
6 "github.com/bnema/vev/internal/domain"
7 "github.com/stretchr/testify/require"
8)
9
10func rectsOverlap(a, b domain.Rect) bool {
11 if a.Width <= 0 || a.Height <= 0 || b.Width <= 0 || b.Height <= 0 {
12 return false
13 }
14 return a.X < b.X+b.Width && b.X < a.X+a.Width && a.Y < b.Y+b.Height && b.Y < a.Y+a.Height
15}
16
17func TestDividersMatchSolvedGaps(t *testing.T) {
18 t.Parallel()
19
20 tests := []struct {
21 name string
22 root *Node
23 area domain.Rect
24 expected []Divider
25 }{
26 {
27 name: "equal weights",
28 root: &Node{Kind: Split, Dir: Horizontal, Children: []*Node{
29 {Kind: Leaf, Leaf: "a", Weight: 1},
30 {Kind: Leaf, Leaf: "b", Weight: 1},
31 }},
32 area: domain.Rect{Width: 61, Height: 10},
33 expected: []Divider{{Rect: domain.Rect{X: 30, Width: 1, Height: 10}, Dir: Horizontal}},
34 },
35 {
36 name: "unequal weights 2:1",
37 root: &Node{Kind: Split, Dir: Horizontal, Children: []*Node{
38 {Kind: Leaf, Leaf: "a", Weight: 2},
39 {Kind: Leaf, Leaf: "b", Weight: 1},
40 }},
41 area: domain.Rect{Width: 61, Height: 10},
42 expected: []Divider{{Rect: domain.Rect{X: 40, Width: 1, Height: 10}, Dir: Horizontal}},
43 },
44 {
45 name: "unequal weights 1:3",
46 root: &Node{Kind: Split, Dir: Horizontal, Children: []*Node{
47 {Kind: Leaf, Leaf: "a", Weight: 1},
48 {Kind: Leaf, Leaf: "b", Weight: 3},
49 }},
50 area: domain.Rect{Width: 81, Height: 10},
51 expected: []Divider{{Rect: domain.Rect{X: 20, Width: 1, Height: 10}, Dir: Horizontal}},
52 },
53 {
54 name: "nested split",
55 root: &Node{Kind: Split, Dir: Horizontal, Children: []*Node{
56 {Kind: Leaf, Leaf: "a", Weight: 1},
57 {Kind: Split, Dir: Vertical, Weight: 1, Children: []*Node{
58 {Kind: Leaf, Leaf: "b", Weight: 1},
59 {Kind: Leaf, Leaf: "c", Weight: 1},
60 }},
61 }},
62 area: domain.Rect{Width: 61, Height: 20},
63 expected: []Divider{
64 {Rect: domain.Rect{X: 30, Width: 1, Height: 20}, Dir: Horizontal},
65 {Rect: domain.Rect{X: 31, Y: 10, Width: 30, Height: 1}, Dir: Vertical},
66 },
67 },
68 {
69 name: "three or more children",
70 root: &Node{Kind: Split, Dir: Horizontal, Children: []*Node{
71 {Kind: Leaf, Leaf: "a", Weight: 1},
72 {Kind: Leaf, Leaf: "b", Weight: 1},
73 {Kind: Leaf, Leaf: "c", Weight: 1},
74 }},
75 area: domain.Rect{Width: 62, Height: 10},
76 expected: []Divider{
77 {Rect: domain.Rect{X: 20, Width: 1, Height: 10}, Dir: Horizontal},
78 {Rect: domain.Rect{X: 41, Width: 1, Height: 10}, Dir: Horizontal},
79 },
80 },
81 {
82 name: "minimum pins a child",
83 root: &Node{Kind: Split, Dir: Horizontal, Children: []*Node{
84 {Kind: Leaf, Leaf: "a", Weight: 1},
85 {Kind: Leaf, Leaf: "b", Weight: 100},
86 }},
87 area: domain.Rect{Width: 61, Height: 10},
88 expected: []Divider{{Rect: domain.Rect{X: 20, Width: 1, Height: 10}, Dir: Horizontal}},
89 },
90 {
91 name: "vertical split",
92 root: &Node{Kind: Split, Dir: Vertical, Children: []*Node{
93 {Kind: Leaf, Leaf: "a", Weight: 1},
94 {Kind: Leaf, Leaf: "b", Weight: 2},
95 }},
96 area: domain.Rect{Width: 41, Height: 10},
97 expected: []Divider{{Rect: domain.Rect{Y: 3, Width: 41, Height: 1}, Dir: Vertical}},
98 },
99 }
100
101 for _, tt := range tests {
102 t.Run(tt.name, func(t *testing.T) {
103 t.Parallel()
104
105 solved, solveOK := Solve(tt.root, tt.area)
106 placements, dividers, dividersOK := SolveWithDividers(tt.root, tt.area)
107 require.Equal(t, solveOK, dividersOK, "SolveWithDividers must succeed/fail exactly when Solve does")
108 require.True(t, solveOK)
109 require.Equal(t, solved, placements, "collecting dividers must not change the solved placements")
110
111 require.Equal(t, tt.expected, dividers, "divider geometry and direction must match the solved gaps exactly")
112
113 for _, d := range dividers {
114 require.True(t, d.Rect.Width == 1 || d.Rect.Height == 1, "divider rect must be a single row or column")
115 for _, p := range placements {
116 require.False(t, rectsOverlap(d.Rect, p.Content), "divider %+v overlaps pane %s content %+v", d.Rect, p.ID, p.Content)
117 }
118 }
119 })
120 }
121}
122
123func TestDividersFailsLikeSolve(t *testing.T) {
124 t.Parallel()
125
126 root := &Node{Kind: Split, Dir: Horizontal, Children: []*Node{
127 {Kind: Leaf, Leaf: "a", Weight: 1},
128 {Kind: Leaf, Leaf: "b", Weight: 1},
129 }}
130 area := domain.Rect{Width: MinPaneCols, Height: MinPaneRows}
131
132 _, solveOK := Solve(root, area)
133 require.False(t, solveOK)
134
135 _, _, dividersOK := SolveWithDividers(root, area)
136 require.False(t, dividersOK)
137}
138
139func TestDividersNilRoot(t *testing.T) {
140 t.Parallel()
141
142 placements, dividers, ok := SolveWithDividers(nil, domain.Rect{Width: 40, Height: 10})
143 require.False(t, ok)
144 require.Nil(t, placements)
145 require.Nil(t, dividers)
146}