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         del self.sysmod.ps1
     32         del self.sysmod.ps2
     33 
     34     def test_ps1(self):
     35         self.infunc.side_effect = EOFError('Finished')
     36         self.console.interact()
     37         self.assertEqual(self.sysmod.ps1, '>>> ')
     38         self.sysmod.ps1 = 'custom1> '
     39         self.console.interact()
     40         self.assertEqual(self.sysmod.ps1, 'custom1> ')
     41 
     42     def test_ps2(self):
     43         self.infunc.side_effect = EOFError('Finished')
     44         self.console.interact()
     45         self.assertEqual(self.sysmod.ps2, '... ')
     46         self.sysmod.ps1 = 'custom2> '
     47         self.console.interact()
     48         self.assertEqual(self.sysmod.ps1, 'custom2> ')
     49 
     50     def test_console_stderr(self):
     51         self.infunc.side_effect = ["'antioch'", "", EOFError('Finished')]
     52         self.console.interact()
     53         for call in list(self.stdout.method_calls):
     54             if 'antioch' in ''.join(call[1]):
     55                 break
     56         else:
     57             raise AssertionError("no console stdout")
     58 
     59     def test_syntax_error(self):
     60         self.infunc.side_effect = ["undefined", EOFError('Finished')]
     61         self.console.interact()
     62         for call in self.stderr.method_calls:
     63             if 'NameError' in ''.join(call[1]):
     64                 break
     65         else:
     66             raise AssertionError("No syntax error from console")
     67 
     68     def test_sysexcepthook(self):
     69         self.infunc.side_effect = ["raise ValueError('')",
     70                                     EOFError('Finished')]
     71         hook = mock.Mock()
     72         self.sysmod.excepthook = hook
     73         self.console.interact()
     74         self.assertTrue(hook.called)
     75 
     76     def test_banner(self):
     77         # with banner
     78         self.infunc.side_effect = EOFError('Finished')
     79         self.console.interact(banner='Foo')
     80         self.assertEqual(len(self.stderr.method_calls), 3)
     81         banner_call = self.stderr.method_calls[0]
     82         self.assertEqual(banner_call, ['write', ('Foo\n',), {}])
     83 
     84         # no banner
     85         self.stderr.reset_mock()
     86         self.infunc.side_effect = EOFError('Finished')
     87         self.console.interact(banner='')
     88         self.assertEqual(len(self.stderr.method_calls), 2)
     89 
     90     def test_exit_msg(self):
     91         # default exit message
     92         self.infunc.side_effect = EOFError('Finished')
     93         self.console.interact(banner='')
     94         self.assertEqual(len(self.stderr.method_calls), 2)
     95         err_msg = self.stderr.method_calls[1]
     96         expected = 'now exiting InteractiveConsole...\n'
     97         self.assertEqual(err_msg, ['write', (expected,), {}])
     98 
     99         # no exit message
    100         self.stderr.reset_mock()
    101         self.infunc.side_effect = EOFError('Finished')
    102         self.console.interact(banner='', exitmsg='')
    103         self.assertEqual(len(self.stderr.method_calls), 1)
    104 
    105         # custom exit message
    106         self.stderr.reset_mock()
    107         message = (
    108             'bye! \N{GREEK SMALL LETTER ZETA}\N{CYRILLIC SMALL LETTER ZHE}'
    109             )
    110         self.infunc.side_effect = EOFError('Finished')
    111         self.console.interact(banner='', exitmsg=message)
    112         self.assertEqual(len(self.stderr.method_calls), 2)
    113         err_msg = self.stderr.method_calls[1]
    114         expected = message + '\n'
    115         self.assertEqual(err_msg, ['write', (expected,), {}])
    116 
    117 
    118     def test_cause_tb(self):
    119         self.infunc.side_effect = ["raise ValueError('') from AttributeError",
    120                                     EOFError('Finished')]
    121         self.console.interact()
    122         output = ''.join(''.join(call[1]) for call in self.stderr.method_calls)
    123         expected = dedent("""
    124         AttributeError
    125 
    126         The above exception was the direct cause of the following exception:
    127 
    128         Traceback (most recent call last):
    129           File "<console>", line 1, in <module>
    130         ValueError
    131         """)
    132         self.assertIn(expected, output)
    133 
    134     def test_context_tb(self):
    135         self.infunc.side_effect = ["try: ham\nexcept: eggs\n",
    136                                     EOFError('Finished')]
    137         self.console.interact()
    138         output = ''.join(''.join(call[1]) for call in self.stderr.method_calls)
    139         expected = dedent("""
    140         Traceback (most recent call last):
    141           File "<console>", line 1, in <module>
    142         NameError: name 'ham' is not defined
    143 
    144         During handling of the above exception, another exception occurred:
    145 
    146         Traceback (most recent call last):
    147           File "<console>", line 2, in <module>
    148         NameError: name 'eggs' is not defined
    149         """)
    150         self.assertIn(expected, output)
    151 
    152 
    153 if __name__ == "__main__":
    154     unittest.main()
    155