test_run.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430
  1. "Test run, coverage 54%."
  2. from idlelib import run
  3. import io
  4. import sys
  5. from test.support import captured_output, captured_stderr
  6. import unittest
  7. from unittest import mock
  8. import idlelib
  9. from idlelib.idle_test.mock_idle import Func
  10. idlelib.testing = True # Use {} for executing test user code.
  11. class ExceptionTest(unittest.TestCase):
  12. def test_print_exception_unhashable(self):
  13. class UnhashableException(Exception):
  14. def __eq__(self, other):
  15. return True
  16. ex1 = UnhashableException('ex1')
  17. ex2 = UnhashableException('ex2')
  18. try:
  19. raise ex2 from ex1
  20. except UnhashableException:
  21. try:
  22. raise ex1
  23. except UnhashableException:
  24. with captured_stderr() as output:
  25. with mock.patch.object(run, 'cleanup_traceback') as ct:
  26. ct.side_effect = lambda t, e: t
  27. run.print_exception()
  28. tb = output.getvalue().strip().splitlines()
  29. self.assertEqual(11, len(tb))
  30. self.assertIn('UnhashableException: ex2', tb[3])
  31. self.assertIn('UnhashableException: ex1', tb[10])
  32. data = (('1/0', ZeroDivisionError, "division by zero\n"),
  33. ('abc', NameError, "name 'abc' is not defined. "
  34. "Did you mean: 'abs'? "
  35. "Or did you forget to import 'abc'?\n"),
  36. ('int.reel', AttributeError,
  37. "type object 'int' has no attribute 'reel'. "
  38. "Did you mean: 'real'?\n"),
  39. )
  40. def test_get_message(self):
  41. for code, exc, msg in self.data:
  42. with self.subTest(code=code):
  43. try:
  44. eval(compile(code, '', 'eval'))
  45. except exc:
  46. typ, val, tb = sys.exc_info()
  47. actual = run.get_message_lines(typ, val, tb)[0]
  48. expect = f'{exc.__name__}: {msg}'
  49. self.assertEqual(actual, expect)
  50. @mock.patch.object(run, 'cleanup_traceback',
  51. new_callable=lambda: (lambda t, e: None))
  52. def test_get_multiple_message(self, mock):
  53. d = self.data
  54. data2 = ((d[0], d[1]), (d[1], d[2]), (d[2], d[0]))
  55. subtests = 0
  56. for (code1, exc1, msg1), (code2, exc2, msg2) in data2:
  57. with self.subTest(codes=(code1,code2)):
  58. try:
  59. eval(compile(code1, '', 'eval'))
  60. except exc1:
  61. try:
  62. eval(compile(code2, '', 'eval'))
  63. except exc2:
  64. with captured_stderr() as output:
  65. run.print_exception()
  66. actual = output.getvalue()
  67. self.assertIn(msg1, actual)
  68. self.assertIn(msg2, actual)
  69. subtests += 1
  70. self.assertEqual(subtests, len(data2)) # All subtests ran?
  71. # StdioFile tests.
  72. class S(str):
  73. def __str__(self):
  74. return '%s:str' % type(self).__name__
  75. def __unicode__(self):
  76. return '%s:unicode' % type(self).__name__
  77. def __len__(self):
  78. return 3
  79. def __iter__(self):
  80. return iter('abc')
  81. def __getitem__(self, *args):
  82. return '%s:item' % type(self).__name__
  83. def __getslice__(self, *args):
  84. return '%s:slice' % type(self).__name__
  85. class MockShell:
  86. def __init__(self):
  87. self.reset()
  88. def write(self, *args):
  89. self.written.append(args)
  90. def readline(self):
  91. return self.lines.pop()
  92. def close(self):
  93. pass
  94. def reset(self):
  95. self.written = []
  96. def push(self, lines):
  97. self.lines = list(lines)[::-1]
  98. class StdInputFilesTest(unittest.TestCase):
  99. def test_misc(self):
  100. shell = MockShell()
  101. f = run.StdInputFile(shell, 'stdin')
  102. self.assertIsInstance(f, io.TextIOBase)
  103. self.assertEqual(f.encoding, 'utf-8')
  104. self.assertEqual(f.errors, 'strict')
  105. self.assertIsNone(f.newlines)
  106. self.assertEqual(f.name, '<stdin>')
  107. self.assertFalse(f.closed)
  108. self.assertTrue(f.isatty())
  109. self.assertTrue(f.readable())
  110. self.assertFalse(f.writable())
  111. self.assertFalse(f.seekable())
  112. def test_unsupported(self):
  113. shell = MockShell()
  114. f = run.StdInputFile(shell, 'stdin')
  115. self.assertRaises(OSError, f.fileno)
  116. self.assertRaises(OSError, f.tell)
  117. self.assertRaises(OSError, f.seek, 0)
  118. self.assertRaises(OSError, f.write, 'x')
  119. self.assertRaises(OSError, f.writelines, ['x'])
  120. def test_read(self):
  121. shell = MockShell()
  122. f = run.StdInputFile(shell, 'stdin')
  123. shell.push(['one\n', 'two\n', ''])
  124. self.assertEqual(f.read(), 'one\ntwo\n')
  125. shell.push(['one\n', 'two\n', ''])
  126. self.assertEqual(f.read(-1), 'one\ntwo\n')
  127. shell.push(['one\n', 'two\n', ''])
  128. self.assertEqual(f.read(None), 'one\ntwo\n')
  129. shell.push(['one\n', 'two\n', 'three\n', ''])
  130. self.assertEqual(f.read(2), 'on')
  131. self.assertEqual(f.read(3), 'e\nt')
  132. self.assertEqual(f.read(10), 'wo\nthree\n')
  133. shell.push(['one\n', 'two\n'])
  134. self.assertEqual(f.read(0), '')
  135. self.assertRaises(TypeError, f.read, 1.5)
  136. self.assertRaises(TypeError, f.read, '1')
  137. self.assertRaises(TypeError, f.read, 1, 1)
  138. def test_readline(self):
  139. shell = MockShell()
  140. f = run.StdInputFile(shell, 'stdin')
  141. shell.push(['one\n', 'two\n', 'three\n', 'four\n'])
  142. self.assertEqual(f.readline(), 'one\n')
  143. self.assertEqual(f.readline(-1), 'two\n')
  144. self.assertEqual(f.readline(None), 'three\n')
  145. shell.push(['one\ntwo\n'])
  146. self.assertEqual(f.readline(), 'one\n')
  147. self.assertEqual(f.readline(), 'two\n')
  148. shell.push(['one', 'two', 'three'])
  149. self.assertEqual(f.readline(), 'one')
  150. self.assertEqual(f.readline(), 'two')
  151. shell.push(['one\n', 'two\n', 'three\n'])
  152. self.assertEqual(f.readline(2), 'on')
  153. self.assertEqual(f.readline(1), 'e')
  154. self.assertEqual(f.readline(1), '\n')
  155. self.assertEqual(f.readline(10), 'two\n')
  156. shell.push(['one\n', 'two\n'])
  157. self.assertEqual(f.readline(0), '')
  158. self.assertRaises(TypeError, f.readlines, 1.5)
  159. self.assertRaises(TypeError, f.readlines, '1')
  160. self.assertRaises(TypeError, f.readlines, 1, 1)
  161. def test_readlines(self):
  162. shell = MockShell()
  163. f = run.StdInputFile(shell, 'stdin')
  164. shell.push(['one\n', 'two\n', ''])
  165. self.assertEqual(f.readlines(), ['one\n', 'two\n'])
  166. shell.push(['one\n', 'two\n', ''])
  167. self.assertEqual(f.readlines(-1), ['one\n', 'two\n'])
  168. shell.push(['one\n', 'two\n', ''])
  169. self.assertEqual(f.readlines(None), ['one\n', 'two\n'])
  170. shell.push(['one\n', 'two\n', ''])
  171. self.assertEqual(f.readlines(0), ['one\n', 'two\n'])
  172. shell.push(['one\n', 'two\n', ''])
  173. self.assertEqual(f.readlines(3), ['one\n'])
  174. shell.push(['one\n', 'two\n', ''])
  175. self.assertEqual(f.readlines(4), ['one\n', 'two\n'])
  176. shell.push(['one\n', 'two\n', ''])
  177. self.assertRaises(TypeError, f.readlines, 1.5)
  178. self.assertRaises(TypeError, f.readlines, '1')
  179. self.assertRaises(TypeError, f.readlines, 1, 1)
  180. def test_close(self):
  181. shell = MockShell()
  182. f = run.StdInputFile(shell, 'stdin')
  183. shell.push(['one\n', 'two\n', ''])
  184. self.assertFalse(f.closed)
  185. self.assertEqual(f.readline(), 'one\n')
  186. f.close()
  187. self.assertFalse(f.closed)
  188. self.assertEqual(f.readline(), 'two\n')
  189. self.assertRaises(TypeError, f.close, 1)
  190. class StdOutputFilesTest(unittest.TestCase):
  191. def test_misc(self):
  192. shell = MockShell()
  193. f = run.StdOutputFile(shell, 'stdout')
  194. self.assertIsInstance(f, io.TextIOBase)
  195. self.assertEqual(f.encoding, 'utf-8')
  196. self.assertEqual(f.errors, 'strict')
  197. self.assertIsNone(f.newlines)
  198. self.assertEqual(f.name, '<stdout>')
  199. self.assertFalse(f.closed)
  200. self.assertTrue(f.isatty())
  201. self.assertFalse(f.readable())
  202. self.assertTrue(f.writable())
  203. self.assertFalse(f.seekable())
  204. def test_unsupported(self):
  205. shell = MockShell()
  206. f = run.StdOutputFile(shell, 'stdout')
  207. self.assertRaises(OSError, f.fileno)
  208. self.assertRaises(OSError, f.tell)
  209. self.assertRaises(OSError, f.seek, 0)
  210. self.assertRaises(OSError, f.read, 0)
  211. self.assertRaises(OSError, f.readline, 0)
  212. def test_write(self):
  213. shell = MockShell()
  214. f = run.StdOutputFile(shell, 'stdout')
  215. f.write('test')
  216. self.assertEqual(shell.written, [('test', 'stdout')])
  217. shell.reset()
  218. f.write('t\xe8\u015b\U0001d599')
  219. self.assertEqual(shell.written, [('t\xe8\u015b\U0001d599', 'stdout')])
  220. shell.reset()
  221. f.write(S('t\xe8\u015b\U0001d599'))
  222. self.assertEqual(shell.written, [('t\xe8\u015b\U0001d599', 'stdout')])
  223. self.assertEqual(type(shell.written[0][0]), str)
  224. shell.reset()
  225. self.assertRaises(TypeError, f.write)
  226. self.assertEqual(shell.written, [])
  227. self.assertRaises(TypeError, f.write, b'test')
  228. self.assertRaises(TypeError, f.write, 123)
  229. self.assertEqual(shell.written, [])
  230. self.assertRaises(TypeError, f.write, 'test', 'spam')
  231. self.assertEqual(shell.written, [])
  232. def test_write_stderr_nonencodable(self):
  233. shell = MockShell()
  234. f = run.StdOutputFile(shell, 'stderr', 'iso-8859-15', 'backslashreplace')
  235. f.write('t\xe8\u015b\U0001d599\xa4')
  236. self.assertEqual(shell.written, [('t\xe8\\u015b\\U0001d599\\xa4', 'stderr')])
  237. shell.reset()
  238. f.write(S('t\xe8\u015b\U0001d599\xa4'))
  239. self.assertEqual(shell.written, [('t\xe8\\u015b\\U0001d599\\xa4', 'stderr')])
  240. self.assertEqual(type(shell.written[0][0]), str)
  241. shell.reset()
  242. self.assertRaises(TypeError, f.write)
  243. self.assertEqual(shell.written, [])
  244. self.assertRaises(TypeError, f.write, b'test')
  245. self.assertRaises(TypeError, f.write, 123)
  246. self.assertEqual(shell.written, [])
  247. self.assertRaises(TypeError, f.write, 'test', 'spam')
  248. self.assertEqual(shell.written, [])
  249. def test_writelines(self):
  250. shell = MockShell()
  251. f = run.StdOutputFile(shell, 'stdout')
  252. f.writelines([])
  253. self.assertEqual(shell.written, [])
  254. shell.reset()
  255. f.writelines(['one\n', 'two'])
  256. self.assertEqual(shell.written,
  257. [('one\n', 'stdout'), ('two', 'stdout')])
  258. shell.reset()
  259. f.writelines(['on\xe8\n', 'tw\xf2'])
  260. self.assertEqual(shell.written,
  261. [('on\xe8\n', 'stdout'), ('tw\xf2', 'stdout')])
  262. shell.reset()
  263. f.writelines([S('t\xe8st')])
  264. self.assertEqual(shell.written, [('t\xe8st', 'stdout')])
  265. self.assertEqual(type(shell.written[0][0]), str)
  266. shell.reset()
  267. self.assertRaises(TypeError, f.writelines)
  268. self.assertEqual(shell.written, [])
  269. self.assertRaises(TypeError, f.writelines, 123)
  270. self.assertEqual(shell.written, [])
  271. self.assertRaises(TypeError, f.writelines, [b'test'])
  272. self.assertRaises(TypeError, f.writelines, [123])
  273. self.assertEqual(shell.written, [])
  274. self.assertRaises(TypeError, f.writelines, [], [])
  275. self.assertEqual(shell.written, [])
  276. def test_close(self):
  277. shell = MockShell()
  278. f = run.StdOutputFile(shell, 'stdout')
  279. self.assertFalse(f.closed)
  280. f.write('test')
  281. f.close()
  282. self.assertTrue(f.closed)
  283. self.assertRaises(ValueError, f.write, 'x')
  284. self.assertEqual(shell.written, [('test', 'stdout')])
  285. f.close()
  286. self.assertRaises(TypeError, f.close, 1)
  287. class RecursionLimitTest(unittest.TestCase):
  288. # Test (un)install_recursionlimit_wrappers and fixdoc.
  289. def test_bad_setrecursionlimit_calls(self):
  290. run.install_recursionlimit_wrappers()
  291. self.addCleanup(run.uninstall_recursionlimit_wrappers)
  292. f = sys.setrecursionlimit
  293. self.assertRaises(TypeError, f, limit=100)
  294. self.assertRaises(TypeError, f, 100, 1000)
  295. self.assertRaises(ValueError, f, 0)
  296. def test_roundtrip(self):
  297. run.install_recursionlimit_wrappers()
  298. self.addCleanup(run.uninstall_recursionlimit_wrappers)
  299. # Check that setting the recursion limit works.
  300. orig_reclimit = sys.getrecursionlimit()
  301. self.addCleanup(sys.setrecursionlimit, orig_reclimit)
  302. sys.setrecursionlimit(orig_reclimit + 3)
  303. # Check that the new limit is returned by sys.getrecursionlimit().
  304. new_reclimit = sys.getrecursionlimit()
  305. self.assertEqual(new_reclimit, orig_reclimit + 3)
  306. def test_default_recursion_limit_preserved(self):
  307. orig_reclimit = sys.getrecursionlimit()
  308. run.install_recursionlimit_wrappers()
  309. self.addCleanup(run.uninstall_recursionlimit_wrappers)
  310. new_reclimit = sys.getrecursionlimit()
  311. self.assertEqual(new_reclimit, orig_reclimit)
  312. def test_fixdoc(self):
  313. # Put here until better place for miscellaneous test.
  314. def func(): "docstring"
  315. run.fixdoc(func, "more")
  316. self.assertEqual(func.__doc__, "docstring\n\nmore")
  317. func.__doc__ = None
  318. run.fixdoc(func, "more")
  319. self.assertEqual(func.__doc__, "more")
  320. class HandleErrorTest(unittest.TestCase):
  321. # Method of MyRPCServer
  322. def test_fatal_error(self):
  323. eq = self.assertEqual
  324. with captured_output('__stderr__') as err,\
  325. mock.patch('idlelib.run.thread.interrupt_main',
  326. new_callable=Func) as func:
  327. try:
  328. raise EOFError
  329. except EOFError:
  330. run.MyRPCServer.handle_error(None, 'abc', '123')
  331. eq(run.exit_now, True)
  332. run.exit_now = False
  333. eq(err.getvalue(), '')
  334. try:
  335. raise IndexError
  336. except IndexError:
  337. run.MyRPCServer.handle_error(None, 'abc', '123')
  338. eq(run.quitting, True)
  339. run.quitting = False
  340. msg = err.getvalue()
  341. self.assertIn('abc', msg)
  342. self.assertIn('123', msg)
  343. self.assertIn('IndexError', msg)
  344. eq(func.called, 2)
  345. class ExecRuncodeTest(unittest.TestCase):
  346. @classmethod
  347. def setUpClass(cls):
  348. cls.addClassCleanup(setattr,run,'print_exception',run.print_exception)
  349. cls.prt = Func() # Need reference.
  350. run.print_exception = cls.prt
  351. mockrpc = mock.Mock()
  352. mockrpc.console.getvar = Func(result=False)
  353. cls.ex = run.Executive(mockrpc)
  354. @classmethod
  355. def tearDownClass(cls):
  356. assert sys.excepthook == sys.__excepthook__
  357. def test_exceptions(self):
  358. ex = self.ex
  359. ex.runcode('1/0')
  360. self.assertIs(ex.user_exc_info[0], ZeroDivisionError)
  361. self.addCleanup(setattr, sys, 'excepthook', sys.__excepthook__)
  362. sys.excepthook = lambda t, e, tb: run.print_exception(t)
  363. ex.runcode('1/0')
  364. self.assertIs(self.prt.args[0], ZeroDivisionError)
  365. sys.excepthook = lambda: None
  366. ex.runcode('1/0')
  367. t, e, tb = ex.user_exc_info
  368. self.assertIs(t, TypeError)
  369. self.assertTrue(isinstance(e.__context__, ZeroDivisionError))
  370. if __name__ == '__main__':
  371. unittest.main(verbosity=2)