workspace_manager_recursion_test.go RawHistoryFolder
1package browser
2
3import (
4 "testing"
5 "time"
6
7 "github.com/bnema/dumber/pkg/webkit"
8)
9
10func TestInfiniteRecursionProtection(t *testing.T) {
11 t.Run("BoundaryFallback with deep tree", func(t *testing.T) {
12 wm := newTestWorkspaceManagerWithMocksForTree(t)
13
14 // Create a pathologically deep tree
15 current := wm.root
16 for i := 0; i < 20; i++ {
17 newNode, err := wm.splitNode(current, "right")
18 if err != nil {
19 t.Fatalf("Split %d failed: %v", i, err)
20 }
21 current = newNode
22 }
23
24 // Test that boundaryFallback doesn't hang even with deep trees
25 done := make(chan bool, 1)
26 go func() {
27 // This should complete quickly without infinite recursion
28 result := wm.boundaryFallback(wm.root, "right")
29 _ = result // We just care that it returns
30 done <- true
31 }()
32
33 select {
34 case <-done:
35 // Good - function completed
36 case <-time.After(1 * time.Second):
37 t.Fatal("boundaryFallback took too long - likely infinite recursion")
38 }
39 })
40
41 t.Run("CollectLeaves with complex tree", func(t *testing.T) {
42 wm := newTestWorkspaceManagerWithMocksForTree(t)
43
44 // Build a complex tree that could potentially cause issues
45 leaves := []*paneNode{wm.root}
46 for i := 0; i < 15; i++ {
47 if len(leaves) > 0 {
48 target := leaves[i%len(leaves)]
49 newNode, err := wm.splitNode(target, "right")
50 if err != nil {
51 continue
52 }
53 leaves = append(leaves, newNode)
54 }
55 }
56
57 // Test that collectLeaves doesn't hang
58 done := make(chan []*paneNode, 1)
59 go func() {
60 result := wm.collectLeaves()
61 done <- result
62 }()
63
64 select {
65 case result := <-done:
66 // Verify we got a reasonable result
67 if len(result) == 0 {
68 t.Error("Expected some leaves to be collected")
69 }
70 t.Logf("Collected %d leaves from complex tree", len(result))
71 case <-time.After(1 * time.Second):
72 t.Fatal("collectLeaves took too long - likely infinite recursion")
73 }
74 })
75
76 t.Run("Focus calculation performance", func(t *testing.T) {
77 wm := newTestWorkspaceManagerWithMocksForTree(t)
78
79 // Create a tree with geometry
80 right, err := wm.splitNode(wm.root, "right")
81 if err != nil {
82 t.Fatalf("Failed to split right: %v", err)
83 }
84 bottom, err := wm.splitNode(right, "down")
85 if err != nil {
86 t.Fatalf("Failed to split down: %v", err)
87 }
88
89 // Set widget bounds
90 webkit.SetWidgetBoundsForTesting(wm.root.container, webkit.WidgetBounds{X: 0, Y: 0, Width: 100, Height: 100})
91 if right != nil {
92 webkit.SetWidgetBoundsForTesting(right.container, webkit.WidgetBounds{X: 120, Y: 0, Width: 100, Height: 100})
93 }
94 if bottom != nil {
95 webkit.SetWidgetBoundsForTesting(bottom.container, webkit.WidgetBounds{X: 0, Y: 120, Width: 100, Height: 100})
96 }
97
98 // Test that focus neighbor calculations complete quickly
99 start := time.Now()
100
101 // Perform multiple focus operations
102 for i := 0; i < 10; i++ {
103 wm.FocusNeighbor("right")
104 wm.FocusNeighbor("left")
105 wm.FocusNeighbor("down")
106 wm.FocusNeighbor("up")
107 }
108
109 elapsed := time.Since(start)
110 if elapsed > 100*time.Millisecond {
111 t.Errorf("Focus operations took too long: %v", elapsed)
112 }
113 t.Logf("40 focus operations completed in %v", elapsed)
114 })
115
116 t.Run("StructuralNeighbor depth protection", func(t *testing.T) {
117 wm := newTestWorkspaceManagerWithMocksForTree(t)
118
119 // Create nested structure
120 node1, _ := wm.splitNode(wm.root, "right")
121 node2, _ := wm.splitNode(node1, "down")
122 node3, _ := wm.splitNode(node2, "right")
123
124 // Set some bounds for the calculation
125 webkit.SetWidgetBoundsForTesting(node3.container, webkit.WidgetBounds{X: 0, Y: 0, Width: 100, Height: 100})
126
127 // Test structural neighbor calculation doesn't hang
128 done := make(chan *paneNode, 1)
129 go func() {
130 result := wm.structuralNeighbor(node3, "left")
131 done <- result
132 }()
133
134 select {
135 case result := <-done:
136 t.Logf("Structural neighbor calculation completed, result: %v", result != nil)
137 case <-time.After(500 * time.Millisecond):
138 t.Fatal("structuralNeighbor took too long")
139 }
140 })
141}
142
143func TestRecursionLimits(t *testing.T) {
144 t.Run("MaxDepth protection", func(t *testing.T) {
145 wm := newTestWorkspaceManagerWithMocksForTree(t)
146
147 // Create an artificially corrupted node for testing
148 // (This would never happen in normal operation)
149 corruptedNode := &paneNode{
150 isLeaf: false,
151 left: nil, // This creates a problematic state
152 right: nil, // No children but not a leaf
153 }
154
155 // Test that boundaryFallback handles this gracefully
156 result := wm.boundaryFallback(corruptedNode, "right")
157 if result != nil {
158 t.Error("Expected nil result for corrupted node")
159 }
160
161 // The function should return quickly, not hang
162 start := time.Now()
163 for i := 0; i < 10; i++ {
164 wm.boundaryFallback(corruptedNode, "left")
165 }
166 elapsed := time.Since(start)
167
168 if elapsed > 10*time.Millisecond {
169 t.Errorf("Corrupted node handling took too long: %v", elapsed)
170 }
171 })
172}