Home | History | Annotate | Download | only in test
      1 "Test InteractiveConsole and InteractiveInterpreter from code module"
      2 import sys
      3 import unittest
      4 from textwrap import dedent
      5 from contextlib import ExitStack
      6 from unittest import mock
      7 from test import support
      8 
      9 code = support.import_module('code')
     10 
     11 
     12 class TestInteractiveConsole(unittest.TestCase):
     13 
     14     def setUp(self):
     15         self.console = code.InteractiveConsole()
     16         self.mock_sys()
     17 
     18     def mock_sys(self):
     19         "Mock system environment for InteractiveConsole"
     20         # use exit stack to match patch context managers to addCleanup
     21         stack = ExitStack()
     22         self.addCleanup(stack.close)
     23         self.infunc = stack.enter_context(mock.patch('code.input',
     24                                           create=True))
     25         self.stdout = stack.enter_context(mock.patch('code.sys.stdout'))
     26         self.stderr = stack.enter_context(mock.patch('code.sys.stderr'))
     27         prepatch = mock.patch('code.sys', wraps=code.sys, spec=code.sys)
     28         self.sysmod = stack.enter_context(prepatch)
     29         if sys.excepthook is sys.__excepthook__:
     30             self.sysmod.excepthook = self.sysmod.__excepthook__
     31 
     32     def test_ps1(self):
     33         self.infunc.side_effect = EOFError('Finished')
     34         self.console.interact()
     35         self.assertEqual(self.sysmod.ps1, '>>> ')
     36 
     37     def test_ps2(self):
     38         self.infunc.side_effect = EOFError('Finished')
     39         self.console.interact()
     40         self.assertEqual(self.sysmod.ps2, '... ')
     41 
     42     def test_console_stderr(self):
     43         self.infunc.side_effect = ["'antioch'", "", EOFError('Finished')]
     44         self.console.interact()
     45         for call in list(self.stdout.method_calls):
     46             if 'antioch' in ''.join(call[1]):
     47                 break
     48         else:
     49             raise AssertionError("no console stdout")
     50 
     51     def test_syntax_error(self):
     52         self.infunc.side_effect = ["undefined", EOFError('Finished')]
     53         self.console.interact()
     54         for call in self.stderr.method_calls:
     55             if 'NameError' in ''.join(call[1]):
     56                 break
     57         else:
     58             raise AssertionError("No syntax error from console")
     59 
     60     def test_sysexcepthook(self):
     61         self.infunc.side_effect = ["raise ValueError('')",
     62                                     EOFError('Finished')]
     63         hook = mock.Mock()
     64         self.sysmod.excepthook = hook
     65         self.console.interact()
     66         self.assertTrue(hook.called)
     67 
     68     def test_banner(self):
     69         # with banner
     70         self.infunc.side_effect = EOFError('Finished')
     71         self.console.interact(banner='Foo')
     72         self.assertEqual(len(self.stderr.method_calls), 3)
     73         banner_call = self.stderr.method_calls[0]
     74         self.assertEqual(banner_call, ['write', ('Foo\n',), {}])
     75 
     76         # no banner
     77         self.stderr.reset_mock()
     78         self.infunc.side_effect = EOFError('Finished')
     79         self.console.interact(banner='')
     80         self.assertEqual(len(self.stderr.method_calls), 2)
     81 
     82     def test_exit_msg(self):
     83         # default exit message
     84         self.infunc.side_effect = EOFError('Finished')
     85         self.console.interact(banner='')
     86         self.assertEqual(len(self.stderr.method_calls), 2)
     87         err_msg = self.stderr.method_calls[1]
     88         expected = 'now exiting InteractiveConsole...\n'
     89         self.assertEqual(err_msg, ['write', (expected,), {}])
     90 
     91         # no exit message
     92         self.stderr.reset_mock()
     93         self.infunc.side_effect = EOFError('Finished')
     94         self.console.interact(banner='', exitmsg='')
     95         self.assertEqual(len(self.stderr.method_calls), 1)
     96 
     97         # custom exit message
     98         self.stderr.reset_mock()
     99         message = (
    100             'bye! \N{GREEK SMALL LETTER ZETA}\N{CYRILLIC SMALL LETTER ZHE}'
    101             )
    102         self.infunc.side_effect = EOFError('Finished')
    103         self.console.interact(banner='', exitmsg=message)
    104         self.assertEqual(len(self.stderr.method_calls), 2)
    105         err_msg = self.stderr.method_calls[1]
    106         expected = message + '\n'
    107         self.assertEqual(err_msg, ['write', (expected,), {}])
    108 
    109 
    110     def test_cause_tb(self):
    111         self.infunc.side_effect = ["raise ValueError('') from AttributeError",
    112                                     EOFError('Finished')]
    113         self.console.interact()
    114         output = ''.join(''.join(call[1]) for call in self.stderr.method_calls)
    115         expected = dedent("""
    116         AttributeError
    117 
    118         The above exception was the direct cause of the following exception:
    119 
    120         Traceback (most recent call last):
    121           File "<console>", line 1, in <module>
    122         ValueError
    123         """)
    124         self.assertIn(expected, output)
    125 
    126     def test_context_tb(self):
    127         self.infunc.side_effect = ["try: ham\nexcept: eggs\n",
    128                                     EOFError('Finished')]
    129         self.console.interact()
    130         output = ''.join(''.join(call[1]) for call in self.stderr.method_calls)
    131         expected = dedent("""
    132         Traceback (most recent call last):
    133           File "<console>", line 1, in <module>
    134         NameError: name 'ham' is not defined
    135 
    136         During handling of the above exception, another exception occurred:
    137 
    138         Traceback (most recent call last):
    139           File "<console>", line 2, in <module>
    140         NameError: name 'eggs' is not defined
    141         """)
    142         self.assertIn(expected, output)
    143 
    144 
    145 if __name__ == "__main__":
    146     unittest.main()
    147