@@ -513,3 +513,44 @@ def send(self, data: bytes) -> None:
513513 shell_module ._windows_input_loop (session ) # type: ignore[arg-type]
514514
515515 assert session .sent == [b"\x00 b" ]
516+
517+ def test_posix_input_loop_forwards_stdin_and_restores_terminal (self ) -> None :
518+ chunks = [b"ls\r " , b"" ] # empty read (EOF) ends the loop
519+
520+ class FakeSession :
521+ def __init__ (self ) -> None :
522+ self .sent : list [bytes ] = []
523+
524+ @property
525+ def connected (self ) -> bool :
526+ return True
527+
528+ def send (self , data : bytes ) -> None :
529+ self .sent .append (data )
530+
531+ fake_termios = Mock ()
532+ fake_termios .tcgetattr .return_value = "old-settings"
533+ fake_tty = Mock ()
534+ fake_stdin = Mock ()
535+ fake_stdin .fileno .return_value = 0
536+
537+ session = FakeSession ()
538+ with (
539+ patch .dict (sys .modules , {"termios" : fake_termios , "tty" : fake_tty }),
540+ patch .object (shell_module .sys , "stdin" , fake_stdin ),
541+ patch .object (
542+ shell_module .select ,
543+ "select" ,
544+ return_value = ([fake_stdin ], [], []),
545+ ),
546+ patch .object (
547+ shell_module .os , "read" , side_effect = lambda fd , n : chunks .pop (0 )
548+ ),
549+ ):
550+ shell_module ._posix_input_loop (session ) # type: ignore[arg-type]
551+
552+ assert session .sent == [b"\x00 ls\r " ]
553+ fake_tty .setraw .assert_called_once_with (0 )
554+ fake_termios .tcsetattr .assert_called_once_with (
555+ 0 , fake_termios .TCSADRAIN , "old-settings"
556+ )
0 commit comments