@@ -162,5 +162,64 @@ def test_falls_back_to_stdin(self):
162162 self .assertIn ('Password:' , stderr .getvalue ())
163163
164164
165+ try :
166+ import msvcrt
167+ msvcrt = True
168+ except ImportError :
169+ msvcrt = False
170+
171+ @unittest .skipUnless (msvcrt , 'tests require system with msvcrt (Windows)' )
172+ class WinGetpassTest (unittest .TestCase ):
173+
174+ def test_uses_msvcrt_directly (self ):
175+ # Mock the msvcrt.getch function to return a sequence ending with Enter key
176+ with mock .patch ('msvcrt.getch' ) as getch :
177+ getch .side_effect = [b'a' , b'b' , b'c' , b'\r ' ]
178+ mock_stream = mock .Mock (spec = StringIO )
179+ result = getpass .win_getpass (stream = mock_stream )
180+ self .assertEqual (getch .call_count , 4 )
181+ self .assertEqual (result , 'abc' )
182+ # Check that the prompt was written to the stream
183+ mock_stream .write .assert_any_call ('Password: ' )
184+ # Check that the stream was flushed
185+ mock_stream .flush .assert_called ()
186+
187+ def test_handles_backspace (self ):
188+ with mock .patch ('msvcrt.getch' ) as getch , \
189+ mock .patch ('msvcrt.putch' ) as putch :
190+ getch .side_effect = [b'a' , b'b' , b'\b ' , b'c' , b'\r ' ]
191+ result = getpass .win_getpass ()
192+ self .assertEqual (result , 'ac' )
193+ # Verify putch was called to handle the backspace (erase character)
194+ # The exact sequence depends on the implementation, but should include
195+ # calls to handle the backspace character
196+ putch .assert_any_call (b'\b ' )
197+
198+ def test_handles_ctrl_c (self ):
199+ with mock .patch ('msvcrt.getch' ) as getch :
200+ # Simulate typing 'a' then Ctrl+C (ASCII value 3)
201+ getch .side_effect = [b'a' , b'\x03 ' ]
202+ # Verify that KeyboardInterrupt is raised
203+ with self .assertRaises (KeyboardInterrupt ):
204+ getpass .win_getpass ()
205+
206+ def test_flushes_stream_after_input (self ):
207+ with mock .patch ('msvcrt.getch' ) as getch :
208+ # Simulate typing 'a' then Enter
209+ getch .side_effect = [b'a' , b'\r ' ]
210+ mock_stream = mock .Mock (spec = StringIO )
211+ getpass .win_getpass (stream = mock_stream )
212+ mock_stream .flush .assert_called ()
213+
214+ def test_falls_back_to_fallback_if_msvcrt_raises (self ):
215+ with mock .patch ('msvcrt.getch' ) as getch , \
216+ mock .patch ('getpass.fallback_getpass' ) as fallback :
217+ # Make getch raise an exception to trigger the fallback
218+ getch .side_effect = RuntimeError ("Simulated msvcrt failure" )
219+ mock_stream = mock .Mock (spec = StringIO )
220+ getpass .win_getpass (stream = mock_stream )
221+ fallback .assert_called_once_with ('Password: ' , mock_stream )
222+
223+
165224if __name__ == "__main__" :
166225 unittest .main ()
0 commit comments