mirror of
https://github.com/python/cpython.git
synced 2024-12-12 03:04:15 +08:00
16dbbae298
Also fixes a resource warning that occurred when the fallback is taken. Patch by Serhiy Storchaka. (We couldn't figure out how to write tests for this.)
156 lines
6.0 KiB
Python
156 lines
6.0 KiB
Python
import getpass
|
|
import os
|
|
import unittest
|
|
from io import BytesIO, StringIO
|
|
from unittest import mock
|
|
from test import support
|
|
|
|
try:
|
|
import termios
|
|
except ImportError:
|
|
termios = None
|
|
try:
|
|
import pwd
|
|
except ImportError:
|
|
pwd = None
|
|
|
|
@mock.patch('os.environ')
|
|
class GetpassGetuserTest(unittest.TestCase):
|
|
|
|
def test_username_takes_username_from_env(self, environ):
|
|
expected_name = 'some_name'
|
|
environ.get.return_value = expected_name
|
|
self.assertEqual(expected_name, getpass.getuser())
|
|
|
|
def test_username_priorities_of_env_values(self, environ):
|
|
environ.get.return_value = None
|
|
try:
|
|
getpass.getuser()
|
|
except ImportError: # in case there's no pwd module
|
|
pass
|
|
self.assertEqual(
|
|
environ.get.call_args_list,
|
|
[mock.call(x) for x in ('LOGNAME', 'USER', 'LNAME', 'USERNAME')])
|
|
|
|
def test_username_falls_back_to_pwd(self, environ):
|
|
expected_name = 'some_name'
|
|
environ.get.return_value = None
|
|
if pwd:
|
|
with mock.patch('os.getuid') as uid, \
|
|
mock.patch('pwd.getpwuid') as getpw:
|
|
uid.return_value = 42
|
|
getpw.return_value = [expected_name]
|
|
self.assertEqual(expected_name,
|
|
getpass.getuser())
|
|
getpw.assert_called_once_with(42)
|
|
else:
|
|
self.assertRaises(ImportError, getpass.getuser)
|
|
|
|
|
|
class GetpassRawinputTest(unittest.TestCase):
|
|
|
|
def test_flushes_stream_after_prompt(self):
|
|
# see issue 1703
|
|
stream = mock.Mock(spec=StringIO)
|
|
input = StringIO('input_string')
|
|
getpass._raw_input('some_prompt', stream, input=input)
|
|
stream.flush.assert_called_once_with()
|
|
|
|
def test_uses_stderr_as_default(self):
|
|
input = StringIO('input_string')
|
|
prompt = 'some_prompt'
|
|
with mock.patch('sys.stderr') as stderr:
|
|
getpass._raw_input(prompt, input=input)
|
|
stderr.write.assert_called_once_with(prompt)
|
|
|
|
@mock.patch('sys.stdin')
|
|
def test_uses_stdin_as_default_input(self, mock_input):
|
|
mock_input.readline.return_value = 'input_string'
|
|
getpass._raw_input(stream=StringIO())
|
|
mock_input.readline.assert_called_once_with()
|
|
|
|
def test_raises_on_empty_input(self):
|
|
input = StringIO('')
|
|
self.assertRaises(EOFError, getpass._raw_input, input=input)
|
|
|
|
def test_trims_trailing_newline(self):
|
|
input = StringIO('test\n')
|
|
self.assertEqual('test', getpass._raw_input(input=input))
|
|
|
|
|
|
# Some of these tests are a bit white-box. The functional requirement is that
|
|
# the password input be taken directly from the tty, and that it not be echoed
|
|
# on the screen, unless we are falling back to stderr/stdin.
|
|
|
|
# Some of these might run on platforms without termios, but play it safe.
|
|
@unittest.skipUnless(termios, 'tests require system with termios')
|
|
class UnixGetpassTest(unittest.TestCase):
|
|
|
|
def test_uses_tty_directly(self):
|
|
with mock.patch('os.open') as open, \
|
|
mock.patch('io.FileIO') as fileio, \
|
|
mock.patch('io.TextIOWrapper') as textio:
|
|
# By setting open's return value to None the implementation will
|
|
# skip code we don't care about in this test. We can mock this out
|
|
# fully if an alternate implementation works differently.
|
|
open.return_value = None
|
|
getpass.unix_getpass()
|
|
open.assert_called_once_with('/dev/tty',
|
|
os.O_RDWR | os.O_NOCTTY)
|
|
fileio.assert_called_once_with(open.return_value, 'w+')
|
|
textio.assert_called_once_with(fileio.return_value)
|
|
|
|
def test_resets_termios(self):
|
|
with mock.patch('os.open') as open, \
|
|
mock.patch('io.FileIO'), \
|
|
mock.patch('io.TextIOWrapper'), \
|
|
mock.patch('termios.tcgetattr') as tcgetattr, \
|
|
mock.patch('termios.tcsetattr') as tcsetattr:
|
|
open.return_value = 3
|
|
fake_attrs = [255, 255, 255, 255, 255]
|
|
tcgetattr.return_value = list(fake_attrs)
|
|
getpass.unix_getpass()
|
|
tcsetattr.assert_called_with(3, mock.ANY, fake_attrs)
|
|
|
|
def test_falls_back_to_fallback_if_termios_raises(self):
|
|
with mock.patch('os.open') as open, \
|
|
mock.patch('io.FileIO') as fileio, \
|
|
mock.patch('io.TextIOWrapper') as textio, \
|
|
mock.patch('termios.tcgetattr'), \
|
|
mock.patch('termios.tcsetattr') as tcsetattr, \
|
|
mock.patch('getpass.fallback_getpass') as fallback:
|
|
open.return_value = 3
|
|
fileio.return_value = BytesIO()
|
|
tcsetattr.side_effect = termios.error
|
|
getpass.unix_getpass()
|
|
fallback.assert_called_once_with('Password: ',
|
|
textio.return_value)
|
|
|
|
def test_flushes_stream_after_input(self):
|
|
# issue 7208
|
|
with mock.patch('os.open') as open, \
|
|
mock.patch('io.FileIO'), \
|
|
mock.patch('io.TextIOWrapper'), \
|
|
mock.patch('termios.tcgetattr'), \
|
|
mock.patch('termios.tcsetattr'):
|
|
open.return_value = 3
|
|
mock_stream = mock.Mock(spec=StringIO)
|
|
getpass.unix_getpass(stream=mock_stream)
|
|
mock_stream.flush.assert_called_with()
|
|
|
|
def test_falls_back_to_stdin(self):
|
|
with mock.patch('os.open') as os_open, \
|
|
mock.patch('sys.stdin', spec=StringIO) as stdin:
|
|
os_open.side_effect = IOError
|
|
stdin.fileno.side_effect = AttributeError
|
|
with support.captured_stderr() as stderr:
|
|
with self.assertWarns(getpass.GetPassWarning):
|
|
getpass.unix_getpass()
|
|
stdin.readline.assert_called_once_with()
|
|
self.assertIn('Warning', stderr.getvalue())
|
|
self.assertIn('Password:', stderr.getvalue())
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|