v0.1.0 4a0dffe

client_test.go RawHistoryFolder

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}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 1

Tags 8

↑↓ moveenter openesc close