cpython/Lib/test/test_code_module.py
Russell Keith-Magee 391659b3da
gh-114099: Add test exclusions to support running the test suite on iOS (#114889)
Add test annotations required to run the test suite on iOS (PEP 730).

The majority of the change involve annotating tests that use subprocess,
but are skipped on Emscripten/WASI for other reasons, and including
iOS/tvOS/watchOS under the same umbrella as macOS/darwin checks.

`is_apple` and `is_apple_mobile` test helpers have been added to
identify *any* Apple platform, and "any Apple platform except macOS",
respectively.
2024-02-05 01:04:57 +01:00

176 lines
6.2 KiB
Python

"Test InteractiveConsole and InteractiveInterpreter from code module"
import sys
import unittest
from textwrap import dedent
from contextlib import ExitStack
from unittest import mock
from test.support import import_helper
code = import_helper.import_module('code')
class MockSys:
def mock_sys(self):
"Mock system environment for InteractiveConsole"
# use exit stack to match patch context managers to addCleanup
stack = ExitStack()
self.addCleanup(stack.close)
self.infunc = stack.enter_context(mock.patch('code.input',
create=True))
self.stdout = stack.enter_context(mock.patch('code.sys.stdout'))
self.stderr = stack.enter_context(mock.patch('code.sys.stderr'))
prepatch = mock.patch('code.sys', wraps=code.sys, spec=code.sys)
self.sysmod = stack.enter_context(prepatch)
if sys.excepthook is sys.__excepthook__:
self.sysmod.excepthook = self.sysmod.__excepthook__
del self.sysmod.ps1
del self.sysmod.ps2
class TestInteractiveConsole(unittest.TestCase, MockSys):
def setUp(self):
self.console = code.InteractiveConsole()
self.mock_sys()
def test_ps1(self):
self.infunc.side_effect = EOFError('Finished')
self.console.interact()
self.assertEqual(self.sysmod.ps1, '>>> ')
self.sysmod.ps1 = 'custom1> '
self.console.interact()
self.assertEqual(self.sysmod.ps1, 'custom1> ')
def test_ps2(self):
self.infunc.side_effect = EOFError('Finished')
self.console.interact()
self.assertEqual(self.sysmod.ps2, '... ')
self.sysmod.ps1 = 'custom2> '
self.console.interact()
self.assertEqual(self.sysmod.ps1, 'custom2> ')
def test_console_stderr(self):
self.infunc.side_effect = ["'antioch'", "", EOFError('Finished')]
self.console.interact()
for call in list(self.stdout.method_calls):
if 'antioch' in ''.join(call[1]):
break
else:
raise AssertionError("no console stdout")
def test_syntax_error(self):
self.infunc.side_effect = ["undefined", EOFError('Finished')]
self.console.interact()
for call in self.stderr.method_calls:
if 'NameError' in ''.join(call[1]):
break
else:
raise AssertionError("No syntax error from console")
def test_sysexcepthook(self):
self.infunc.side_effect = ["raise ValueError('')",
EOFError('Finished')]
hook = mock.Mock()
self.sysmod.excepthook = hook
self.console.interact()
self.assertTrue(hook.called)
def test_banner(self):
# with banner
self.infunc.side_effect = EOFError('Finished')
self.console.interact(banner='Foo')
self.assertEqual(len(self.stderr.method_calls), 3)
banner_call = self.stderr.method_calls[0]
self.assertEqual(banner_call, ['write', ('Foo\n',), {}])
# no banner
self.stderr.reset_mock()
self.infunc.side_effect = EOFError('Finished')
self.console.interact(banner='')
self.assertEqual(len(self.stderr.method_calls), 2)
def test_exit_msg(self):
# default exit message
self.infunc.side_effect = EOFError('Finished')
self.console.interact(banner='')
self.assertEqual(len(self.stderr.method_calls), 2)
err_msg = self.stderr.method_calls[1]
expected = 'now exiting InteractiveConsole...\n'
self.assertEqual(err_msg, ['write', (expected,), {}])
# no exit message
self.stderr.reset_mock()
self.infunc.side_effect = EOFError('Finished')
self.console.interact(banner='', exitmsg='')
self.assertEqual(len(self.stderr.method_calls), 1)
# custom exit message
self.stderr.reset_mock()
message = (
'bye! \N{GREEK SMALL LETTER ZETA}\N{CYRILLIC SMALL LETTER ZHE}'
)
self.infunc.side_effect = EOFError('Finished')
self.console.interact(banner='', exitmsg=message)
self.assertEqual(len(self.stderr.method_calls), 2)
err_msg = self.stderr.method_calls[1]
expected = message + '\n'
self.assertEqual(err_msg, ['write', (expected,), {}])
def test_cause_tb(self):
self.infunc.side_effect = ["raise ValueError('') from AttributeError",
EOFError('Finished')]
self.console.interact()
output = ''.join(''.join(call[1]) for call in self.stderr.method_calls)
expected = dedent("""
AttributeError
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "<console>", line 1, in <module>
ValueError
""")
self.assertIn(expected, output)
def test_context_tb(self):
self.infunc.side_effect = ["try: ham\nexcept: eggs\n",
EOFError('Finished')]
self.console.interact()
output = ''.join(''.join(call[1]) for call in self.stderr.method_calls)
expected = dedent("""
Traceback (most recent call last):
File "<console>", line 1, in <module>
NameError: name 'ham' is not defined
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "<console>", line 2, in <module>
NameError: name 'eggs' is not defined
""")
self.assertIn(expected, output)
class TestInteractiveConsoleLocalExit(unittest.TestCase, MockSys):
def setUp(self):
self.console = code.InteractiveConsole(local_exit=True)
self.mock_sys()
@unittest.skipIf(sys.flags.no_site, "exit() isn't defined unless there's a site module")
def test_exit(self):
# default exit message
self.infunc.side_effect = ["exit()"]
self.console.interact(banner='')
self.assertEqual(len(self.stderr.method_calls), 2)
err_msg = self.stderr.method_calls[1]
expected = 'now exiting InteractiveConsole...\n'
self.assertEqual(err_msg, ['write', (expected,), {}])
if __name__ == "__main__":
unittest.main()