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