1package wlturbo
2
3import (
4 "bytes"
5 "encoding/binary"
6 "testing"
7)
8
9// Unit tests that don't require a compositor
10
11func TestFixed(t *testing.T) {
12 // Test Fixed type conversion
13 tests := []struct {
14 input float64
15 expected float64
16 }{
17 {1.0, 1.0},
18 {0.5, 0.5},
19 {123.456, 123.456},
20 {-1.5, -1.5},
21 {0.0, 0.0},
22 {256.0, 256.0},
23 }
24
25 for _, test := range tests {
26 fixed := NewFixed(test.input)
27 result := fixed.Float64()
28
29 // Allow small precision differences
30 diff := result - test.expected
31 if diff < 0 {
32 diff = -diff
33 }
34 if diff > 0.01 {
35 t.Errorf("Fixed conversion: input=%f, expected=%f, got=%f",
36 test.input, test.expected, result)
37 }
38 }
39}
40
41func TestEventPool(t *testing.T) {
42 // Test that the global event pool works without requiring a compositor
43 // Get an event
44 event := eventPool.Get().(*Event)
45 if event == nil {
46 t.Fatal("Expected event from pool, got nil")
47 }
48
49 // Set some data
50 event.ProxyId = 123
51 event.Opcode = 456
52
53 // Return to pool
54 eventPool.Put(event)
55
56 // Get another event - should be reused
57 event2 := eventPool.Get().(*Event)
58 if event2 == nil {
59 t.Fatal("Expected reused event from pool, got nil")
60 }
61
62 // The pool might return a clean event or the same one, both are valid
63 t.Logf("Event2: ProxyId=%d, Opcode=%d", event2.ProxyId, event2.Opcode)
64
65 // Clean up
66 eventPool.Put(event2)
67}
68
69func TestEventDispatcher(t *testing.T) {
70 dispatcher := NewEventDispatcher()
71
72 called := false
73 handler := func(event *Event) {
74 called = true
75 if event.ProxyId != 123 || event.Opcode != 1 {
76 t.Errorf("Expected ProxyId=123, Opcode=1, got ProxyId=%d, Opcode=%d", event.ProxyId, event.Opcode)
77 }
78 }
79
80 // Register handler
81 dispatcher.RegisterHandler(123, 1, handler)
82
83 // Dispatch event
84 dispatcher.Dispatch(123, 1, []byte{})
85
86 if !called {
87 t.Error("Handler should have been called")
88 }
89}
90
91func TestEventDispatcherMultipleHandlers(t *testing.T) {
92 dispatcher := NewEventDispatcher()
93
94 called1 := false
95 called2 := false
96
97 handler1 := func(event *Event) {
98 called1 = true
99 }
100
101 handler2 := func(event *Event) {
102 called2 = true
103 }
104
105 // Register handlers for different opcodes
106 dispatcher.RegisterHandler(123, 1, handler1)
107 dispatcher.RegisterHandler(123, 2, handler2)
108
109 // Dispatch different events
110 dispatcher.Dispatch(123, 1, []byte{})
111 dispatcher.Dispatch(123, 2, []byte{})
112
113 if !called1 {
114 t.Error("Handler1 should have been called")
115 }
116 if !called2 {
117 t.Error("Handler2 should have been called")
118 }
119}
120
121func TestMessageMarshalingBasic(t *testing.T) {
122 // Create a display to test marshaling (without connecting)
123 d := &Display{
124 nextID: 2,
125 }
126
127 buf := &bytes.Buffer{}
128
129 // Test marshaling different argument types
130 tests := []struct {
131 name string
132 arg interface{}
133 want []byte
134 }{
135 {
136 name: "uint32",
137 arg: uint32(0x12345678),
138 want: []byte{0x78, 0x56, 0x34, 0x12}, // little endian
139 },
140 {
141 name: "int32",
142 arg: int32(-1),
143 want: []byte{0xFF, 0xFF, 0xFF, 0xFF},
144 },
145 {
146 name: "Fixed",
147 arg: NewFixed(1.0),
148 want: []byte{0x00, 0x01, 0x00, 0x00}, // 256 in little endian
149 },
150 {
151 name: "string",
152 arg: "test",
153 want: []byte{0x05, 0x00, 0x00, 0x00, 't', 'e', 's', 't', 0x00, 0x00, 0x00, 0x00}, // length + string + null + padding
154 },
155 {
156 name: "nil object",
157 arg: nil,
158 want: []byte{0x00, 0x00, 0x00, 0x00},
159 },
160 }
161
162 for _, test := range tests {
163 t.Run(test.name, func(t *testing.T) {
164 buf.Reset()
165 err := d.marshalArg(buf, test.arg)
166 if err != nil {
167 t.Fatalf("marshalArg failed: %v", err)
168 }
169
170 got := buf.Bytes()
171 if !bytes.Equal(got, test.want) {
172 t.Errorf("marshalArg(%v) = %v, want %v", test.arg, got, test.want)
173 }
174 })
175 }
176}
177
178func TestMessageHeaderParsing(t *testing.T) {
179 // Test parsing message headers
180 tests := []struct {
181 name string
182 header []byte
183 wantID uint32
184 wantSize uint32
185 wantOp uint16
186 }{
187 {
188 name: "basic header",
189 header: []byte{0x05, 0x00, 0x00, 0x00, 0x02, 0x00, 0x0C, 0x00}, // ID=5, opcode=2, size=12 (size is upper 16 bits)
190 wantID: 5,
191 wantSize: 12,
192 wantOp: 2,
193 },
194 {
195 name: "large values",
196 header: []byte{0xFF, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x10}, // ID=65535, opcode=255, size=4096
197 wantID: 65535,
198 wantSize: 4096,
199 wantOp: 255,
200 },
201 }
202
203 for _, test := range tests {
204 t.Run(test.name, func(t *testing.T) {
205 if len(test.header) != 8 {
206 t.Fatalf("header must be 8 bytes, got %d", len(test.header))
207 }
208
209 objectID := binary.LittleEndian.Uint32(test.header[0:4])
210 sizeOpcode := binary.LittleEndian.Uint32(test.header[4:8])
211 size := sizeOpcode >> 16
212 opcode := sizeOpcode & 0xffff
213
214 if objectID != test.wantID {
215 t.Errorf("object ID = %d, want %d", objectID, test.wantID)
216 }
217 if size != test.wantSize {
218 t.Errorf("size = %d, want %d", size, test.wantSize)
219 }
220 if uint16(opcode) != test.wantOp {
221 t.Errorf("opcode = %d, want %d", opcode, test.wantOp)
222 }
223 })
224 }
225}
226
227func TestAllocateID(t *testing.T) {
228 d := &Display{
229 nextID: 2, // Start at 2 (1 is reserved for display)
230 }
231
232 // Test sequential ID allocation
233 id1 := d.allocateID()
234 id2 := d.allocateID()
235 id3 := d.allocateID()
236
237 if id1 != 2 {
238 t.Errorf("First ID = %d, want 2", id1)
239 }
240 if id2 != 3 {
241 t.Errorf("Second ID = %d, want 3", id2)
242 }
243 if id3 != 4 {
244 t.Errorf("Third ID = %d, want 4", id3)
245 }
246}
247
248func TestRegistryGlobalStorage(t *testing.T) {
249 registry := &Registry{
250 id: 2,
251 globals: make(map[uint32]Global),
252 }
253
254 // Test storing and retrieving globals
255 global1 := Global{
256 Name: 1,
257 Interface: "wl_compositor",
258 Version: 4,
259 }
260
261 global2 := Global{
262 Name: 2,
263 Interface: "wl_seat",
264 Version: 7,
265 }
266
267 registry.globals[global1.Name] = global1
268 registry.globals[global2.Name] = global2
269
270 // Test GetGlobals
271 globals := registry.GetGlobals()
272 if len(globals) != 2 {
273 t.Errorf("GetGlobals() returned %d globals, want 2", len(globals))
274 }
275
276 // Test FindGlobal
277 found, exists := registry.FindGlobal("wl_compositor")
278 if !exists {
279 t.Error("wl_compositor should be found")
280 }
281 if found.Name != global1.Name || found.Version != global1.Version {
282 t.Errorf("Found global = %+v, want %+v", found, global1)
283 }
284
285 // Test non-existent global
286 _, exists = registry.FindGlobal("non_existent")
287 if exists {
288 t.Error("non_existent should not be found")
289 }
290}
291
292func BenchmarkEventDispatch(b *testing.B) {
293 dispatcher := NewEventDispatcher()
294
295 handler := func(event *Event) {
296 // Minimal handler for benchmarking
297 }
298
299 // Register handler
300 dispatcher.RegisterHandler(123, 1, handler)
301
302 data := []byte{0x01, 0x02, 0x03, 0x04}
303
304 b.ResetTimer()
305 for i := 0; i < b.N; i++ {
306 dispatcher.Dispatch(123, 1, data)
307 }
308}
309
310func BenchmarkFixedConversion(b *testing.B) {
311 values := []float64{1.0, 0.5, 123.456, -1.5, 256.789}
312
313 b.ResetTimer()
314 for i := 0; i < b.N; i++ {
315 for _, v := range values {
316 fixed := NewFixed(v)
317 _ = fixed.Float64()
318 }
319 }
320}