24
24
MOVE_DOWN ,
25
25
ERASE_IN_LINE ,
26
26
)
27
+ import _pyrepl .windows_console as wc
27
28
except ImportError :
28
29
pass
29
30
@@ -350,8 +351,226 @@ def test_multiline_ctrl_z(self):
350
351
Event (evt = "key" , data = '\x1a ' , raw = bytearray (b'\x1a ' )),
351
352
],
352
353
)
353
- reader , _ = self .handle_events_narrow (events )
354
+ reader , con = self .handle_events_narrow (events )
354
355
self .assertEqual (reader .cxy , (2 , 3 ))
356
+ con .restore ()
357
+
358
+
359
+ class WindowsConsoleGetEventTests (TestCase ):
360
+ # Virtual-Key Codes: https://learn.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes
361
+ VK_BACK = 0x08
362
+ VK_RETURN = 0x0D
363
+ VK_LEFT = 0x25
364
+ VK_7 = 0x37
365
+ VK_M = 0x4D
366
+ # Used for miscellaneous characters; it can vary by keyboard.
367
+ # For the US standard keyboard, the '" key.
368
+ # For the German keyboard, the Ä key.
369
+ VK_OEM_7 = 0xDE
370
+
371
+ # State of control keys: https://learn.microsoft.com/en-us/windows/console/key-event-record-str
372
+ RIGHT_ALT_PRESSED = 0x0001
373
+ RIGHT_CTRL_PRESSED = 0x0004
374
+ LEFT_ALT_PRESSED = 0x0002
375
+ LEFT_CTRL_PRESSED = 0x0008
376
+ ENHANCED_KEY = 0x0100
377
+ SHIFT_PRESSED = 0x0010
378
+
379
+
380
+ def get_event (self , input_records , ** kwargs ) -> Console :
381
+ self .console = WindowsConsole (encoding = 'utf-8' )
382
+ self .mock = MagicMock (side_effect = input_records )
383
+ self .console ._read_input = self .mock
384
+ self .console ._WindowsConsole__vt_support = kwargs .get ("vt_support" ,
385
+ False )
386
+ event = self .console .get_event (block = False )
387
+ return event
388
+
389
+ def get_input_record (self , unicode_char , vcode = 0 , control = 0 ):
390
+ return wc .INPUT_RECORD (
391
+ wc .KEY_EVENT ,
392
+ wc .ConsoleEvent (KeyEvent =
393
+ wc .KeyEvent (
394
+ bKeyDown = True ,
395
+ wRepeatCount = 1 ,
396
+ wVirtualKeyCode = vcode ,
397
+ wVirtualScanCode = 0 , # not used
398
+ uChar = wc .Char (unicode_char ),
399
+ dwControlKeyState = control
400
+ )))
401
+
402
+ def test_EmptyBuffer (self ):
403
+ self .assertEqual (self .get_event ([None ]), None )
404
+ self .assertEqual (self .mock .call_count , 1 )
405
+
406
+ def test_WINDOW_BUFFER_SIZE_EVENT (self ):
407
+ ir = wc .INPUT_RECORD (
408
+ wc .WINDOW_BUFFER_SIZE_EVENT ,
409
+ wc .ConsoleEvent (WindowsBufferSizeEvent =
410
+ wc .WindowsBufferSizeEvent (
411
+ wc ._COORD (0 , 0 ))))
412
+ self .assertEqual (self .get_event ([ir ]), Event ("resize" , "" ))
413
+ self .assertEqual (self .mock .call_count , 1 )
414
+
415
+ def test_KEY_EVENT_up_ignored (self ):
416
+ ir = wc .INPUT_RECORD (
417
+ wc .KEY_EVENT ,
418
+ wc .ConsoleEvent (KeyEvent =
419
+ wc .KeyEvent (bKeyDown = False )))
420
+ self .assertEqual (self .get_event ([ir ]), None )
421
+ self .assertEqual (self .mock .call_count , 1 )
422
+
423
+ def test_unhandled_events (self ):
424
+ for event in (wc .FOCUS_EVENT , wc .MENU_EVENT , wc .MOUSE_EVENT ):
425
+ ir = wc .INPUT_RECORD (
426
+ event ,
427
+ # fake data, nothing is read except bKeyDown
428
+ wc .ConsoleEvent (KeyEvent =
429
+ wc .KeyEvent (bKeyDown = False )))
430
+ self .assertEqual (self .get_event ([ir ]), None )
431
+ self .assertEqual (self .mock .call_count , 1 )
432
+
433
+ def test_enter (self ):
434
+ ir = self .get_input_record ("\r " , self .VK_RETURN )
435
+ self .assertEqual (self .get_event ([ir ]), Event ("key" , "\n " ))
436
+ self .assertEqual (self .mock .call_count , 1 )
437
+
438
+ def test_backspace (self ):
439
+ ir = self .get_input_record ("\x08 " , self .VK_BACK )
440
+ self .assertEqual (
441
+ self .get_event ([ir ]), Event ("key" , "backspace" ))
442
+ self .assertEqual (self .mock .call_count , 1 )
443
+
444
+ def test_m (self ):
445
+ ir = self .get_input_record ("m" , self .VK_M )
446
+ self .assertEqual (self .get_event ([ir ]), Event ("key" , "m" ))
447
+ self .assertEqual (self .mock .call_count , 1 )
448
+
449
+ def test_M (self ):
450
+ ir = self .get_input_record ("M" , self .VK_M , self .SHIFT_PRESSED )
451
+ self .assertEqual (self .get_event ([ir ]), Event ("key" , "M" ))
452
+ self .assertEqual (self .mock .call_count , 1 )
453
+
454
+ def test_left (self ):
455
+ # VK_LEFT is sent as ENHANCED_KEY
456
+ ir = self .get_input_record ("\x00 " , self .VK_LEFT , self .ENHANCED_KEY )
457
+ self .assertEqual (self .get_event ([ir ]), Event ("key" , "left" ))
458
+ self .assertEqual (self .mock .call_count , 1 )
459
+
460
+ def test_left_RIGHT_CTRL_PRESSED (self ):
461
+ ir = self .get_input_record (
462
+ "\x00 " , self .VK_LEFT , self .RIGHT_CTRL_PRESSED | self .ENHANCED_KEY )
463
+ self .assertEqual (
464
+ self .get_event ([ir ]), Event ("key" , "ctrl left" ))
465
+ self .assertEqual (self .mock .call_count , 1 )
466
+
467
+ def test_left_LEFT_CTRL_PRESSED (self ):
468
+ ir = self .get_input_record (
469
+ "\x00 " , self .VK_LEFT , self .LEFT_CTRL_PRESSED | self .ENHANCED_KEY )
470
+ self .assertEqual (
471
+ self .get_event ([ir ]), Event ("key" , "ctrl left" ))
472
+ self .assertEqual (self .mock .call_count , 1 )
473
+
474
+ def test_left_RIGHT_ALT_PRESSED (self ):
475
+ ir = self .get_input_record (
476
+ "\x00 " , self .VK_LEFT , self .RIGHT_ALT_PRESSED | self .ENHANCED_KEY )
477
+ self .assertEqual (self .get_event ([ir ]), Event (evt = "key" , data = "\033 " ))
478
+ self .assertEqual (
479
+ self .console .get_event (), Event ("key" , "left" ))
480
+ # self.mock is not called again, since the second time we read from the
481
+ # command queue
482
+ self .assertEqual (self .mock .call_count , 1 )
483
+
484
+ def test_left_LEFT_ALT_PRESSED (self ):
485
+ ir = self .get_input_record (
486
+ "\x00 " , self .VK_LEFT , self .LEFT_ALT_PRESSED | self .ENHANCED_KEY )
487
+ self .assertEqual (self .get_event ([ir ]), Event (evt = "key" , data = "\033 " ))
488
+ self .assertEqual (
489
+ self .console .get_event (), Event ("key" , "left" ))
490
+ self .assertEqual (self .mock .call_count , 1 )
491
+
492
+ def test_m_LEFT_ALT_PRESSED_and_LEFT_CTRL_PRESSED (self ):
493
+ # For the shift keys, Windows does not send anything when
494
+ # ALT and CTRL are both pressed, so let's test with VK_M.
495
+ # get_event() receives this input, but does not
496
+ # generate an event.
497
+ # This is for e.g. an English keyboard layout, for a
498
+ # German layout this returns `µ`, see test_AltGr_m.
499
+ ir = self .get_input_record (
500
+ "\x00 " , self .VK_M , self .LEFT_ALT_PRESSED | self .LEFT_CTRL_PRESSED )
501
+ self .assertEqual (self .get_event ([ir ]), None )
502
+ self .assertEqual (self .mock .call_count , 1 )
503
+
504
+ def test_m_LEFT_ALT_PRESSED (self ):
505
+ ir = self .get_input_record (
506
+ "m" , vcode = self .VK_M , control = self .LEFT_ALT_PRESSED )
507
+ self .assertEqual (self .get_event ([ir ]), Event (evt = "key" , data = "\033 " ))
508
+ self .assertEqual (self .console .get_event (), Event ("key" , "m" ))
509
+ self .assertEqual (self .mock .call_count , 1 )
510
+
511
+ def test_m_RIGHT_ALT_PRESSED (self ):
512
+ ir = self .get_input_record (
513
+ "m" , vcode = self .VK_M , control = self .RIGHT_ALT_PRESSED )
514
+ self .assertEqual (self .get_event ([ir ]), Event (evt = "key" , data = "\033 " ))
515
+ self .assertEqual (self .console .get_event (), Event ("key" , "m" ))
516
+ self .assertEqual (self .mock .call_count , 1 )
517
+
518
+ def test_AltGr_7 (self ):
519
+ # E.g. on a German keyboard layout, '{' is entered via
520
+ # AltGr + 7, where AltGr is the right Alt key on the keyboard.
521
+ # In this case, Windows automatically sets
522
+ # RIGHT_ALT_PRESSED = 0x0001 + LEFT_CTRL_PRESSED = 0x0008
523
+ # This can also be entered like
524
+ # LeftAlt + LeftCtrl + 7 or
525
+ # LeftAlt + RightCtrl + 7
526
+ # See https://learn.microsoft.com/en-us/windows/console/key-event-record-str
527
+ # https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-vkkeyscanw
528
+ ir = self .get_input_record (
529
+ "{" , vcode = self .VK_7 ,
530
+ control = self .RIGHT_ALT_PRESSED | self .LEFT_CTRL_PRESSED )
531
+ self .assertEqual (self .get_event ([ir ]), Event ("key" , "{" ))
532
+ self .assertEqual (self .mock .call_count , 1 )
533
+
534
+ def test_AltGr_m (self ):
535
+ # E.g. on a German keyboard layout, this yields 'µ'
536
+ # Let's use LEFT_ALT_PRESSED and RIGHT_CTRL_PRESSED this
537
+ # time, to cover that, too. See above in test_AltGr_7.
538
+ ir = self .get_input_record (
539
+ "µ" , vcode = self .VK_M , control = self .LEFT_ALT_PRESSED | self .RIGHT_CTRL_PRESSED )
540
+ self .assertEqual (self .get_event ([ir ]), Event ("key" , "µ" ))
541
+ self .assertEqual (self .mock .call_count , 1 )
542
+
543
+ def test_umlaut_a_german (self ):
544
+ ir = self .get_input_record ("ä" , self .VK_OEM_7 )
545
+ self .assertEqual (self .get_event ([ir ]), Event ("key" , "ä" ))
546
+ self .assertEqual (self .mock .call_count , 1 )
547
+
548
+ # virtual terminal tests
549
+ # Note: wVirtualKeyCode, wVirtualScanCode and dwControlKeyState
550
+ # are always zero in this case.
551
+ # "\r" and backspace are handled specially, everything else
552
+ # is handled in "elif self.__vt_support:" in WindowsConsole.get_event().
553
+ # Hence, only one regular key ("m") and a terminal sequence
554
+ # are sufficient to test here, the real tests happen in test_eventqueue
555
+ # and test_keymap.
556
+
557
+ def test_enter_vt (self ):
558
+ ir = self .get_input_record ("\r " )
559
+ self .assertEqual (self .get_event ([ir ], vt_support = True ),
560
+ Event ("key" , "\n " ))
561
+ self .assertEqual (self .mock .call_count , 1 )
562
+
563
+ def test_backspace_vt (self ):
564
+ ir = self .get_input_record ("\x7f " )
565
+ self .assertEqual (self .get_event ([ir ], vt_support = True ),
566
+ Event ("key" , "backspace" , b"\x7f " ))
567
+ self .assertEqual (self .mock .call_count , 1 )
568
+
569
+ def test_up_vt (self ):
570
+ irs = [self .get_input_record (x ) for x in "\x1b [A" ]
571
+ self .assertEqual (self .get_event (irs , vt_support = True ),
572
+ Event (evt = 'key' , data = 'up' , raw = bytearray (b'\x1b [A' )))
573
+ self .assertEqual (self .mock .call_count , 3 )
355
574
356
575
357
576
if __name__ == "__main__" :
0 commit comments