okami.mom/ultraviolet

git clone https://git.nkp.pet/okami.mom/ultraviolet

Ayman Bagabasfix: lint issuesf678006

upstream
18.2 KiB747 linesraw
1package uv
2
3import (
4	"image/color"
5	"testing"
6
7	"github.com/charmbracelet/x/ansi"
8	xwindows "github.com/charmbracelet/x/windows"
9	"github.com/lucasb-eyer/go-colorful"
10)
11
12// TestLegacyKeyEncodingMethods tests all the LegacyKeyEncoding flag methods
13func TestLegacyKeyEncodingMethods(t *testing.T) {
14	tests := []struct {
15		name   string
16		method func(LegacyKeyEncoding) LegacyKeyEncoding
17		flag   uint32
18	}{
19		{
20			name: "CtrlAt true",
21			method: func(l LegacyKeyEncoding) LegacyKeyEncoding {
22				return l.CtrlAt(true)
23			},
24			flag: flagCtrlAt,
25		},
26		{
27			name: "CtrlAt false",
28			method: func(l LegacyKeyEncoding) LegacyKeyEncoding {
29				return l.CtrlAt(false)
30			},
31			flag: 0,
32		},
33		{
34			name: "CtrlI true",
35			method: func(l LegacyKeyEncoding) LegacyKeyEncoding {
36				return l.CtrlI(true)
37			},
38			flag: flagCtrlI,
39		},
40		{
41			name: "CtrlI false",
42			method: func(l LegacyKeyEncoding) LegacyKeyEncoding {
43				return l.CtrlI(false)
44			},
45			flag: 0,
46		},
47		{
48			name: "CtrlM true",
49			method: func(l LegacyKeyEncoding) LegacyKeyEncoding {
50				return l.CtrlM(true)
51			},
52			flag: flagCtrlM,
53		},
54		{
55			name: "CtrlM false",
56			method: func(l LegacyKeyEncoding) LegacyKeyEncoding {
57				return l.CtrlM(false)
58			},
59			flag: 0,
60		},
61		{
62			name: "CtrlOpenBracket true",
63			method: func(l LegacyKeyEncoding) LegacyKeyEncoding {
64				return l.CtrlOpenBracket(true)
65			},
66			flag: flagCtrlOpenBracket,
67		},
68		{
69			name: "CtrlOpenBracket false",
70			method: func(l LegacyKeyEncoding) LegacyKeyEncoding {
71				return l.CtrlOpenBracket(false)
72			},
73			flag: 0,
74		},
75		{
76			name: "Backspace true",
77			method: func(l LegacyKeyEncoding) LegacyKeyEncoding {
78				return l.Backspace(true)
79			},
80			flag: flagBackspace,
81		},
82		{
83			name: "Backspace false",
84			method: func(l LegacyKeyEncoding) LegacyKeyEncoding {
85				return l.Backspace(false)
86			},
87			flag: 0,
88		},
89		{
90			name: "Find true",
91			method: func(l LegacyKeyEncoding) LegacyKeyEncoding {
92				return l.Find(true)
93			},
94			flag: flagFind,
95		},
96		{
97			name: "Find false",
98			method: func(l LegacyKeyEncoding) LegacyKeyEncoding {
99				return l.Find(false)
100			},
101			flag: 0,
102		},
103		{
104			name: "Select true",
105			method: func(l LegacyKeyEncoding) LegacyKeyEncoding {
106				return l.Select(true)
107			},
108			flag: flagSelect,
109		},
110		{
111			name: "Select false",
112			method: func(l LegacyKeyEncoding) LegacyKeyEncoding {
113				return l.Select(false)
114			},
115			flag: 0,
116		},
117		{
118			name: "FKeys true",
119			method: func(l LegacyKeyEncoding) LegacyKeyEncoding {
120				return l.FKeys(true)
121			},
122			flag: flagFKeys,
123		},
124		{
125			name: "FKeys false",
126			method: func(l LegacyKeyEncoding) LegacyKeyEncoding {
127				return l.FKeys(false)
128			},
129			flag: 0,
130		},
131	}
132
133	for _, tt := range tests {
134		t.Run(tt.name, func(t *testing.T) {
135			// Start with all flags set if testing false, or no flags if testing true
136			var initial LegacyKeyEncoding
137			if tt.flag == 0 {
138				initial = LegacyKeyEncoding(0xFFFFFFFF)
139			}
140
141			result := tt.method(initial)
142
143			if tt.flag != 0 {
144				// Testing true - flag should be set
145				if uint32(result)&tt.flag == 0 {
146					t.Errorf("expected flag %x to be set, got %x", tt.flag, uint32(result))
147				}
148			} // else: Testing false - specific flag should be cleared. We can't check the exact value since we don't know which flag is being cleared. Just verify the method runs without panic.
149		})
150	}
151}
152
153// TestDeviceAttributesParsing tests device attributes parsing functions
154func TestDeviceAttributesParsing(t *testing.T) {
155	// Test parsePrimaryDevAttrs
156	t.Run("parsePrimaryDevAttrs", func(t *testing.T) {
157		params := ansi.Params{62, 1, 2, 6, 9}
158		event := parsePrimaryDevAttrs(params)
159		if pda, ok := event.(PrimaryDeviceAttributesEvent); ok {
160			expected := []int{62, 1, 2, 6, 9}
161			if len(pda) != len(expected) {
162				t.Errorf("expected %d attrs, got %d", len(expected), len(pda))
163			}
164			for i, v := range expected {
165				if i < len(pda) && pda[i] != v {
166					t.Errorf("attr[%d]: expected %d, got %d", i, v, pda[i])
167				}
168			}
169		} else {
170			t.Errorf("expected PrimaryDeviceAttributesEvent, got %T", event)
171		}
172	})
173
174	// Test parseSecondaryDevAttrs
175	t.Run("parseSecondaryDevAttrs", func(t *testing.T) {
176		params := ansi.Params{1, 2, 3}
177		event := parseSecondaryDevAttrs(params)
178		if sda, ok := event.(SecondaryDeviceAttributesEvent); ok {
179			expected := []int{1, 2, 3}
180			if len(sda) != len(expected) {
181				t.Errorf("expected %d attrs, got %d", len(expected), len(sda))
182			}
183			for i, v := range expected {
184				if i < len(sda) && sda[i] != v {
185					t.Errorf("attr[%d]: expected %d, got %d", i, v, sda[i])
186				}
187			}
188		} else {
189			t.Errorf("expected SecondaryDeviceAttributesEvent, got %T", event)
190		}
191	})
192
193	// Test parseTertiaryDevAttrs
194	t.Run("parseTertiaryDevAttrs", func(t *testing.T) {
195		data := []byte("4368726d")
196		event := parseTertiaryDevAttrs(data)
197		if tda, ok := event.(TertiaryDeviceAttributesEvent); ok {
198			if expected := "Chrm"; string(tda) != expected {
199				t.Errorf("expected '%s', got '%s'", expected, string(tda))
200			}
201		} else {
202			t.Errorf("expected TertiaryDeviceAttributesEvent, got %T", event)
203		}
204	})
205}
206
207// TestHelperFunctions tests various helper functions
208func TestHelperFunctions(t *testing.T) {
209	// Test shift
210	t.Run("shift", func(t *testing.T) {
211		if shift(uint(0)) != 0 {
212			t.Errorf("shift(0) = %d, want 0", shift(uint(0)))
213		}
214		if shift(uint(1)) != 1 {
215			t.Errorf("shift(1) = %d, want 1", shift(uint(1)))
216		}
217		if shift(uint(0x100)) != 1 {
218			t.Errorf("shift(0x100) = %d, want 1", shift(uint(0x100)))
219		}
220		if shift(uint32(0x1000)) != 0x10 {
221			t.Errorf("shift(0x1000) = %d, want 0x10", shift(uint32(0x1000)))
222		}
223	})
224
225	// Test colorToHex
226	t.Run("colorToHex", func(t *testing.T) {
227		tests := []struct {
228			color color.Color
229			want  string
230		}{
231			{color.RGBA{R: 255, G: 128, B: 64, A: 255}, "#ff8040"},
232			{color.RGBA{R: 0, G: 0, B: 0, A: 255}, "#000000"},
233			{color.RGBA{R: 255, G: 255, B: 255, A: 255}, "#ffffff"},
234			{nil, ""},
235		}
236		for _, tt := range tests {
237			got := colorToHex(tt.color)
238			if got != tt.want {
239				t.Errorf("colorToHex(%v) = %s, want %s", tt.color, got, tt.want)
240			}
241		}
242	})
243
244	// Test getMaxMin
245	t.Run("getMaxMin", func(t *testing.T) {
246		tests := []struct {
247			r, g, b  float64
248			max, min float64
249		}{
250			{0.5, 0.3, 0.8, 0.8, 0.3},
251			{1.0, 1.0, 1.0, 1.0, 1.0},
252			{0.0, 0.0, 0.0, 0.0, 0.0},
253			{0.2, 0.5, 0.3, 0.5, 0.2},
254		}
255		for _, tt := range tests {
256			max, min := getMaxMin(tt.r, tt.g, tt.b)
257			if max != tt.max || min != tt.min {
258				t.Errorf("getMaxMin(%f, %f, %f) = (%f, %f), want (%f, %f)",
259					tt.r, tt.g, tt.b, max, min, tt.max, tt.min)
260			}
261		}
262	})
263
264	// Test rgbToHSL
265	t.Run("rgbToHSL", func(t *testing.T) {
266		tests := []struct {
267			r, g, b uint8
268			h, s, l float64
269		}{
270			{255, 0, 0, 0, 1.0, 0.5},     // Red
271			{0, 255, 0, 120, 1.0, 0.5},   // Green
272			{0, 0, 255, 240, 1.0, 0.5},   // Blue
273			{128, 128, 128, 0, 0.0, 0.5}, // Gray
274			{255, 255, 255, 0, 0.0, 1.0}, // White
275			{0, 0, 0, 0, 0.0, 0.0},       // Black
276		}
277		for _, tt := range tests {
278			h, s, l := rgbToHSL(tt.r, tt.g, tt.b)
279			// Allow small floating point differences
280			const epsilon = 0.01
281			if (h-tt.h) > epsilon || (s-tt.s) > epsilon || (l-tt.l) > epsilon {
282				if !(tt.s == 0 && s == 0) { // When saturation is 0, hue is undefined
283					t.Errorf("rgbToHSL(%d, %d, %d) = (%f, %f, %f), want (%f, %f, %f)",
284						tt.r, tt.g, tt.b, h, s, l, tt.h, tt.s, tt.l)
285				}
286			}
287		}
288	})
289
290	// Test isDarkColor
291	t.Run("isDarkColor", func(t *testing.T) {
292		tests := []struct {
293			hex  string
294			dark bool
295		}{
296			{"#ffffff", false}, // White
297			{"#000000", true},  // Black
298			{"#808080", false}, // Medium gray
299			{"#404040", true},  // Dark gray
300			{"#c0c0c0", false}, // Light gray
301			{"#ff0000", false}, // Red
302			{"#800000", true},  // Dark red
303		}
304		for _, tt := range tests {
305			c, _ := colorful.Hex(tt.hex)
306			result := isDarkColor(c)
307			if result != tt.dark {
308				t.Errorf("isDarkColor(%s) = %v, want %v", tt.hex, result, tt.dark)
309			}
310		}
311	})
312}
313
314// TestParseTermcap tests termcap parsing
315func TestParseTermcap(t *testing.T) {
316	tests := []struct {
317		name        string
318		input       []byte
319		expectEvent CapabilityEvent
320	}{
321		{
322			name:        "RGB capability",
323			input:       []byte("524742"),
324			expectEvent: CapabilityEvent{"RGB"},
325		},
326		{
327			name:        "Co capability",
328			input:       []byte("436F=323536"),
329			expectEvent: CapabilityEvent{"Co=256"},
330		},
331		{
332			name:        "Empty input",
333			input:       []byte(""),
334			expectEvent: CapabilityEvent{""},
335		},
336		{
337			name:        "Invalid hex",
338			input:       []byte("GGGG"),
339			expectEvent: CapabilityEvent{""},
340		},
341		{
342			name:        "Odd length hex",
343			input:       []byte("52474"),
344			expectEvent: CapabilityEvent{""},
345		},
346	}
347
348	for _, tt := range tests {
349		t.Run(tt.name, func(t *testing.T) {
350			event := parseTermcap(tt.input)
351			if event != tt.expectEvent {
352				t.Errorf("parseTermcap(%q) = %q, want %q",
353					tt.input, event, tt.expectEvent)
354			}
355		})
356	}
357}
358
359// TestParseUtf8 tests UTF-8 parsing
360func TestParseUtf8(t *testing.T) {
361	var p EventDecoder
362	tests := []struct {
363		name      string
364		input     []byte
365		wantN     int
366		wantEvent Event
367	}{
368		{
369			name:      "empty input",
370			input:     []byte{},
371			wantN:     0,
372			wantEvent: nil,
373		},
374		{
375			name:      "control character",
376			input:     []byte{0x01}, // SOH
377			wantN:     1,
378			wantEvent: KeyPressEvent{Code: 'a', Mod: ModCtrl},
379		},
380		{
381			name:      "ASCII printable",
382			input:     []byte{'a'},
383			wantN:     1,
384			wantEvent: KeyPressEvent{Code: 'a', Text: "a"},
385		},
386		{
387			name:      "uppercase letter",
388			input:     []byte{'A'},
389			wantN:     1,
390			wantEvent: KeyPressEvent{Code: 'a', ShiftedCode: 'A', Text: "A", Mod: ModShift},
391		},
392		{
393			name:      "DEL character",
394			input:     []byte{0x7F}, // DEL
395			wantN:     1,
396			wantEvent: KeyPressEvent{Code: KeyBackspace},
397		},
398		{
399			name:      "UTF-8 multi-byte",
400			input:     []byte("€"), // Euro sign
401			wantN:     3,
402			wantEvent: KeyPressEvent{Code: '€', Text: "€"},
403		},
404		{
405			name:      "invalid UTF-8",
406			input:     []byte{0xFF},
407			wantN:     1,
408			wantEvent: UnknownEvent("\u00ff"),
409		},
410	}
411
412	for _, tt := range tests {
413		t.Run(tt.name, func(t *testing.T) {
414			n, event := p.parseUtf8(tt.input)
415			if n != tt.wantN {
416				t.Errorf("parseUtf8() n = %d, want %d", n, tt.wantN)
417			}
418			if tt.wantEvent == nil {
419				if event != nil {
420					t.Errorf("parseUtf8() event = %v, want nil", event)
421				}
422			} else {
423				switch want := tt.wantEvent.(type) {
424				case KeyPressEvent:
425					if got, ok := event.(KeyPressEvent); ok {
426						if got.Code != want.Code || got.Text != want.Text || got.Mod != want.Mod || got.ShiftedCode != want.ShiftedCode {
427							t.Errorf("parseUtf8() = %+v, want %+v", got, want)
428						}
429					} else {
430						t.Errorf("parseUtf8() = %T, want KeyPressEvent", event)
431					}
432				case UnknownEvent:
433					if got, ok := event.(UnknownEvent); ok {
434						if got != want {
435							t.Errorf("parseUtf8() = %v, want %v", got, want)
436						}
437					} else {
438						t.Errorf("parseUtf8() = %T, want UnknownEvent", event)
439					}
440				}
441			}
442		})
443	}
444}
445
446// TestParseControl tests control character parsing
447func TestParseControl(t *testing.T) {
448	var p EventDecoder
449	tests := []struct {
450		name      string
451		input     byte
452		legacy    LegacyKeyEncoding
453		wantEvent Event
454	}{
455		{
456			name:      "NUL with CtrlAt flag",
457			input:     ansi.NUL,
458			legacy:    LegacyKeyEncoding(flagCtrlAt),
459			wantEvent: KeyPressEvent{Code: '@', Mod: ModCtrl},
460		},
461		{
462			name:      "NUL without CtrlAt flag",
463			input:     ansi.NUL,
464			legacy:    0,
465			wantEvent: KeyPressEvent{Code: KeySpace, Mod: ModCtrl},
466		},
467		{
468			name:      "BS (backspace)",
469			input:     ansi.BS,
470			legacy:    0,
471			wantEvent: KeyPressEvent{Code: 'h', Mod: ModCtrl},
472		},
473		{
474			name:      "HT with CtrlI flag",
475			input:     ansi.HT,
476			legacy:    LegacyKeyEncoding(flagCtrlI),
477			wantEvent: KeyPressEvent{Code: 'i', Mod: ModCtrl},
478		},
479		{
480			name:      "HT without CtrlI flag",
481			input:     ansi.HT,
482			legacy:    0,
483			wantEvent: KeyPressEvent{Code: KeyTab},
484		},
485		{
486			name:      "CR with CtrlM flag",
487			input:     ansi.CR,
488			legacy:    LegacyKeyEncoding(flagCtrlM),
489			wantEvent: KeyPressEvent{Code: 'm', Mod: ModCtrl},
490		},
491		{
492			name:      "CR without CtrlM flag",
493			input:     ansi.CR,
494			legacy:    0,
495			wantEvent: KeyPressEvent{Code: KeyEnter},
496		},
497		{
498			name:      "ESC with CtrlOpenBracket flag",
499			input:     ansi.ESC,
500			legacy:    LegacyKeyEncoding(flagCtrlOpenBracket),
501			wantEvent: KeyPressEvent{Code: '[', Mod: ModCtrl},
502		},
503		{
504			name:      "ESC without CtrlOpenBracket flag",
505			input:     ansi.ESC,
506			legacy:    0,
507			wantEvent: KeyPressEvent{Code: KeyEscape},
508		},
509		{
510			name:      "DEL with Backspace flag",
511			input:     ansi.DEL,
512			legacy:    LegacyKeyEncoding(flagBackspace),
513			wantEvent: KeyPressEvent{Code: KeyDelete},
514		},
515		{
516			name:      "DEL without Backspace flag",
517			input:     ansi.DEL,
518			legacy:    0,
519			wantEvent: KeyPressEvent{Code: KeyBackspace},
520		},
521		{
522			name:      "Space",
523			input:     ansi.SP,
524			legacy:    0,
525			wantEvent: KeyPressEvent{Code: KeySpace, Text: " "},
526		},
527		{
528			name:      "Control-A (SOH)",
529			input:     ansi.SOH,
530			legacy:    0,
531			wantEvent: KeyPressEvent{Code: 'a', Mod: ModCtrl},
532		},
533		{
534			name:      "Control-Z (SUB)",
535			input:     ansi.SUB,
536			legacy:    0,
537			wantEvent: KeyPressEvent{Code: 'z', Mod: ModCtrl},
538		},
539		{
540			name:      "FS (File Separator)",
541			input:     ansi.FS,
542			legacy:    0,
543			wantEvent: KeyPressEvent{Code: '\\', Mod: ModCtrl},
544		},
545		{
546			name:      "US (Unit Separator)",
547			input:     ansi.US,
548			legacy:    0,
549			wantEvent: KeyPressEvent{Code: '_', Mod: ModCtrl},
550		},
551		{
552			name:      "Unknown control",
553			input:     0x80,
554			legacy:    0,
555			wantEvent: UnknownEvent("\u0080"),
556		},
557	}
558
559	for _, tt := range tests {
560		t.Run(tt.name, func(t *testing.T) {
561			p.Legacy = tt.legacy
562			event := p.parseControl(tt.input)
563			switch want := tt.wantEvent.(type) {
564			case KeyPressEvent:
565				if got, ok := event.(KeyPressEvent); ok {
566					if got.Code != want.Code || got.Text != want.Text || got.Mod != want.Mod {
567						t.Errorf("parseControl() = %+v, want %+v", got, want)
568					}
569				} else {
570					t.Errorf("parseControl() = %T, want KeyPressEvent", event)
571				}
572			case UnknownEvent:
573				if got, ok := event.(UnknownEvent); ok {
574					if got != want {
575						t.Errorf("parseControl() = %v, want %v", got, want)
576					}
577				} else {
578					t.Errorf("parseControl() = %T, want UnknownEvent", event)
579				}
580			}
581		})
582	}
583}
584
585// TestWin32Functions tests Win32-related functions
586func TestWin32Functions(t *testing.T) {
587	// Test ensureKeyCase
588	t.Run("ensureKeyCase", func(t *testing.T) {
589		tests := []struct {
590			name        string
591			key         Key
592			flags       uint32
593			wantCode    rune
594			wantShifted rune
595			wantText    string
596		}{
597			{
598				name:        "uppercase with shift",
599				key:         Key{Code: 'a', Text: "A", Mod: ModShift},
600				flags:       xwindows.SHIFT_PRESSED,
601				wantCode:    'a',
602				wantShifted: 'A',
603				wantText:    "A",
604			},
605			{
606				name:     "lowercase without shift",
607				key:      Key{Code: 'a', Text: "a", Mod: 0},
608				flags:    0,
609				wantCode: 'a',
610				wantText: "a",
611			},
612			{
613				name:        "uppercase without shift",
614				key:         Key{Code: 'A', Text: "A", Mod: 0},
615				flags:       0,
616				wantCode:    'A',
617				wantShifted: 'a',
618				wantText:    "a",
619			},
620			{
621				name:     "non-letter",
622				key:      Key{Code: '1', Text: "1", Mod: 0},
623				flags:    0,
624				wantCode: '1',
625				wantText: "1",
626			},
627		}
628		for _, tt := range tests {
629			t.Run(tt.name, func(t *testing.T) {
630				result := ensureKeyCase(tt.key, tt.flags)
631				if result.Text != tt.wantText {
632					t.Errorf("ensureKeyCase(%+v, %d).Text = %q, want %q",
633						tt.key, tt.flags, result.Text, tt.wantText)
634				}
635				if result.Code != tt.wantCode {
636					t.Errorf("ensureKeyCase(%+v, %d).Code = %c, want %c",
637						tt.key, tt.flags, result.Code, tt.wantCode)
638				}
639				if result.ShiftedCode != tt.wantShifted {
640					t.Errorf("ensureKeyCase(%+v, %d).ShiftedCode = %c, want %c",
641						tt.key, tt.flags, result.ShiftedCode, tt.wantShifted)
642				}
643			})
644		}
645	})
646
647	// Test translateControlKeyState
648	t.Run("translateControlKeyState", func(t *testing.T) {
649		tests := []struct {
650			state uint32
651			want  KeyMod
652		}{
653			{xwindows.RIGHT_ALT_PRESSED, ModAlt},    // RIGHT_ALT_PRESSED
654			{xwindows.LEFT_ALT_PRESSED, ModAlt},     // LEFT_ALT_PRESSED
655			{xwindows.RIGHT_CTRL_PRESSED, ModCtrl},  // RIGHT_CTRL_PRESSED
656			{xwindows.LEFT_CTRL_PRESSED, ModCtrl},   // LEFT_CTRL_PRESSED
657			{xwindows.SHIFT_PRESSED, ModShift},      // SHIFT_PRESSED
658			{xwindows.NUMLOCK_ON, ModNumLock},       // NUMLOCK_ON
659			{xwindows.SCROLLLOCK_ON, ModScrollLock}, // SCROLLLOCK_ON
660			{xwindows.CAPSLOCK_ON, ModCapsLock},     // CAPSLOCK_ON
661			{xwindows.ENHANCED_KEY, 0},              // ENHANCED_KEY
662			{xwindows.RIGHT_ALT_PRESSED | xwindows.RIGHT_CTRL_PRESSED | xwindows.SHIFT_PRESSED, ModAlt | ModCtrl | ModShift}, // Combination
663		}
664		for _, tt := range tests {
665			got := translateControlKeyState(tt.state)
666			if got != tt.want {
667				t.Errorf("translateControlKeyState(0x%04x) = %d, want %d",
668					tt.state, got, tt.want)
669			}
670		}
671	})
672
673	// Test parseWin32InputKeyEvent
674	t.Run("parseWin32InputKeyEvent", func(t *testing.T) {
675		var p EventDecoder
676		tests := []struct {
677			name string
678			vk   uint16
679			sc   uint16
680			uc   rune
681			kd   bool
682			cs   uint32
683			rc   uint16
684			want Event
685		}{
686			{
687				name: "simple key press",
688				vk:   0x41, // VK_A
689				sc:   0,
690				uc:   'a',
691				kd:   true,
692				cs:   0,
693				rc:   1,
694				want: KeyPressEvent{Code: 'a', Text: "a"},
695			},
696			{
697				name: "key release",
698				vk:   0x41, // VK_A
699				sc:   0,
700				uc:   'a',
701				kd:   false,
702				cs:   0,
703				rc:   1,
704				want: KeyReleaseEvent{Code: 'a', Text: "a"},
705			},
706			{
707				name: "function key",
708				vk:   0x70, // VK_F1
709				sc:   0,
710				uc:   0,
711				kd:   true,
712				cs:   0,
713				rc:   1,
714				want: KeyPressEvent{Code: KeyF1},
715			},
716			{
717				name: "enter key",
718				vk:   0x0D, // VK_RETURN
719				sc:   0,
720				uc:   '\r',
721				kd:   true,
722				cs:   0,
723				rc:   1,
724				want: KeyPressEvent{Code: KeyEnter},
725			},
726		}
727		for _, tt := range tests {
728			t.Run(tt.name, func(t *testing.T) {
729				got := p.parseWin32InputKeyEvent(tt.vk, tt.sc, tt.uc, tt.kd, tt.cs, tt.rc)
730				if got == nil {
731					t.Fatal("parseWin32InputKeyEvent returned nil")
732				}
733				// Basic type check
734				switch tt.want.(type) {
735				case KeyPressEvent:
736					if _, ok := got.(KeyPressEvent); !ok {
737						t.Errorf("expected KeyPressEvent, got %T", got)
738					}
739				case KeyReleaseEvent:
740					if _, ok := got.(KeyReleaseEvent); !ok {
741						t.Errorf("expected KeyReleaseEvent, got %T", got)
742					}
743				}
744			})
745		}
746	})
747}