diff --git a/Lib/test/test_getpass.py b/Lib/test/test_getpass.py index 80dda2caaa3331..f3962c9854c72b 100644 --- a/Lib/test/test_getpass.py +++ b/Lib/test/test_getpass.py @@ -13,6 +13,11 @@ import pwd except ImportError: pwd = None +try: + import msvcrt +except ImportError: + msvcrt = None + @mock.patch('os.environ') class GetpassGetuserTest(unittest.TestCase): @@ -162,5 +167,53 @@ def test_falls_back_to_stdin(self): self.assertIn('Password:', stderr.getvalue()) +@unittest.skipUnless(msvcrt, 'tests require system with msvcrt') +class WinGetpassTest(unittest.TestCase): + + def test_uses_msvcrt_directly(self): + # Mock the msvcrt.getch function to return a sequence ending with Enter key + with mock.patch('msvcrt.getch') as getch: + getch.side_effect = [b'a', b'b', b'c', b'\r'] + mock_stream = mock.Mock(spec=StringIO) + result = getpass.win_getpass(stream=mock_stream) + self.assertEqual(getch.call_count, 4) + self.assertEqual(result, 'abc') + # Check that the prompt was written to the stream + mock_stream.write.assert_any_call('Password: ') + # Check that the stream was flushed + mock_stream.flush.assert_called() + + def test_handles_backspace(self): + with mock.patch('msvcrt.getch') as getch, \ + mock.patch('msvcrt.putch') as putch: + getch.side_effect = [b'a', b'b', b'\b', b'c', b'\r'] + result = getpass.win_getpass() + self.assertEqual(result, 'ac') + putch.assert_any_call(b'\b') + + def test_handles_ctrl_c(self): + with mock.patch('msvcrt.getch') as getch: + # Simulate typing 'a' then Ctrl+C (ASCII value 3) + getch.side_effect = [b'a', b'\x03'] + # Verify that KeyboardInterrupt is raised + self.assertRaises(KeyboardInterrupt, getpass.win_getpass) + + def test_flushes_stream_after_input(self): + with mock.patch('msvcrt.getch') as getch: + # Simulate typing 'a' then Enter + getch.side_effect = [b'a', b'\r'] + mock_stream = mock.Mock(spec=StringIO) + getpass.win_getpass(stream=mock_stream) + mock_stream.flush.assert_called() + + def test_falls_back_to_fallback_if_msvcrt_raises(self): + with mock.patch('msvcrt.getch') as getch, \ + mock.patch('getpass.fallback_getpass') as fallback: + getch.side_effect = RuntimeError("Simulated msvcrt failure") + mock_stream = mock.Mock(spec=StringIO) + getpass.win_getpass(stream=mock_stream) + fallback.assert_called_once_with('Password: ', mock_stream) + + if __name__ == "__main__": unittest.main()