test_autocomplete.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  1. "Test autocomplete, coverage 93%."
  2. import unittest
  3. from unittest.mock import Mock, patch
  4. from test.support import requires
  5. from tkinter import Tk, Text
  6. import os
  7. import __main__
  8. import idlelib.autocomplete as ac
  9. import idlelib.autocomplete_w as acw
  10. from idlelib.idle_test.mock_idle import Func
  11. from idlelib.idle_test.mock_tk import Event
  12. class DummyEditwin:
  13. def __init__(self, root, text):
  14. self.root = root
  15. self.text = text
  16. self.indentwidth = 8
  17. self.tabwidth = 8
  18. self.prompt_last_line = '>>>' # Currently not used by autocomplete.
  19. class AutoCompleteTest(unittest.TestCase):
  20. @classmethod
  21. def setUpClass(cls):
  22. requires('gui')
  23. cls.root = Tk()
  24. cls.root.withdraw()
  25. cls.text = Text(cls.root)
  26. cls.editor = DummyEditwin(cls.root, cls.text)
  27. @classmethod
  28. def tearDownClass(cls):
  29. del cls.editor, cls.text
  30. cls.root.update_idletasks()
  31. cls.root.destroy()
  32. del cls.root
  33. def setUp(self):
  34. self.text.delete('1.0', 'end')
  35. self.autocomplete = ac.AutoComplete(self.editor)
  36. def test_init(self):
  37. self.assertEqual(self.autocomplete.editwin, self.editor)
  38. self.assertEqual(self.autocomplete.text, self.text)
  39. def test_make_autocomplete_window(self):
  40. testwin = self.autocomplete._make_autocomplete_window()
  41. self.assertIsInstance(testwin, acw.AutoCompleteWindow)
  42. def test_remove_autocomplete_window(self):
  43. acp = self.autocomplete
  44. acp.autocompletewindow = m = Mock()
  45. acp._remove_autocomplete_window()
  46. m.hide_window.assert_called_once()
  47. self.assertIsNone(acp.autocompletewindow)
  48. def test_force_open_completions_event(self):
  49. # Call _open_completions and break.
  50. acp = self.autocomplete
  51. open_c = Func()
  52. acp.open_completions = open_c
  53. self.assertEqual(acp.force_open_completions_event('event'), 'break')
  54. self.assertEqual(open_c.args[0], ac.FORCE)
  55. def test_autocomplete_event(self):
  56. Equal = self.assertEqual
  57. acp = self.autocomplete
  58. # Result of autocomplete event: If modified tab, None.
  59. ev = Event(mc_state=True)
  60. self.assertIsNone(acp.autocomplete_event(ev))
  61. del ev.mc_state
  62. # If tab after whitespace, None.
  63. self.text.insert('1.0', ' """Docstring.\n ')
  64. self.assertIsNone(acp.autocomplete_event(ev))
  65. self.text.delete('1.0', 'end')
  66. # If active autocomplete window, complete() and 'break'.
  67. self.text.insert('1.0', 're.')
  68. acp.autocompletewindow = mock = Mock()
  69. mock.is_active = Mock(return_value=True)
  70. Equal(acp.autocomplete_event(ev), 'break')
  71. mock.complete.assert_called_once()
  72. acp.autocompletewindow = None
  73. # If no active autocomplete window, open_completions(), None/break.
  74. open_c = Func(result=False)
  75. acp.open_completions = open_c
  76. Equal(acp.autocomplete_event(ev), None)
  77. Equal(open_c.args[0], ac.TAB)
  78. open_c.result = True
  79. Equal(acp.autocomplete_event(ev), 'break')
  80. Equal(open_c.args[0], ac.TAB)
  81. def test_try_open_completions_event(self):
  82. Equal = self.assertEqual
  83. text = self.text
  84. acp = self.autocomplete
  85. trycompletions = acp.try_open_completions_event
  86. after = Func(result='after1')
  87. acp.text.after = after
  88. # If no text or trigger, after not called.
  89. trycompletions()
  90. Equal(after.called, 0)
  91. text.insert('1.0', 're')
  92. trycompletions()
  93. Equal(after.called, 0)
  94. # Attribute needed, no existing callback.
  95. text.insert('insert', ' re.')
  96. acp._delayed_completion_id = None
  97. trycompletions()
  98. Equal(acp._delayed_completion_index, text.index('insert'))
  99. Equal(after.args,
  100. (acp.popupwait, acp._delayed_open_completions, ac.TRY_A))
  101. cb1 = acp._delayed_completion_id
  102. Equal(cb1, 'after1')
  103. # File needed, existing callback cancelled.
  104. text.insert('insert', ' "./Lib/')
  105. after.result = 'after2'
  106. cancel = Func()
  107. acp.text.after_cancel = cancel
  108. trycompletions()
  109. Equal(acp._delayed_completion_index, text.index('insert'))
  110. Equal(cancel.args, (cb1,))
  111. Equal(after.args,
  112. (acp.popupwait, acp._delayed_open_completions, ac.TRY_F))
  113. Equal(acp._delayed_completion_id, 'after2')
  114. def test_delayed_open_completions(self):
  115. Equal = self.assertEqual
  116. acp = self.autocomplete
  117. open_c = Func()
  118. acp.open_completions = open_c
  119. self.text.insert('1.0', '"dict.')
  120. # Set autocomplete._delayed_completion_id to None.
  121. # Text index changed, don't call open_completions.
  122. acp._delayed_completion_id = 'after'
  123. acp._delayed_completion_index = self.text.index('insert+1c')
  124. acp._delayed_open_completions('dummy')
  125. self.assertIsNone(acp._delayed_completion_id)
  126. Equal(open_c.called, 0)
  127. # Text index unchanged, call open_completions.
  128. acp._delayed_completion_index = self.text.index('insert')
  129. acp._delayed_open_completions((1, 2, 3, ac.FILES))
  130. self.assertEqual(open_c.args[0], (1, 2, 3, ac.FILES))
  131. def test_oc_cancel_comment(self):
  132. none = self.assertIsNone
  133. acp = self.autocomplete
  134. # Comment is in neither code or string.
  135. acp._delayed_completion_id = 'after'
  136. after = Func(result='after')
  137. acp.text.after_cancel = after
  138. self.text.insert(1.0, '# comment')
  139. none(acp.open_completions(ac.TAB)) # From 'else' after 'elif'.
  140. none(acp._delayed_completion_id)
  141. def test_oc_no_list(self):
  142. acp = self.autocomplete
  143. fetch = Func(result=([],[]))
  144. acp.fetch_completions = fetch
  145. self.text.insert('1.0', 'object')
  146. self.assertIsNone(acp.open_completions(ac.TAB))
  147. self.text.insert('insert', '.')
  148. self.assertIsNone(acp.open_completions(ac.TAB))
  149. self.assertEqual(fetch.called, 2)
  150. def test_open_completions_none(self):
  151. # Test other two None returns.
  152. none = self.assertIsNone
  153. acp = self.autocomplete
  154. # No object for attributes or need call not allowed.
  155. self.text.insert(1.0, '.')
  156. none(acp.open_completions(ac.TAB))
  157. self.text.insert('insert', ' int().')
  158. none(acp.open_completions(ac.TAB))
  159. # Blank or quote trigger 'if complete ...'.
  160. self.text.delete(1.0, 'end')
  161. self.assertFalse(acp.open_completions(ac.TAB))
  162. self.text.insert('1.0', '"')
  163. self.assertFalse(acp.open_completions(ac.TAB))
  164. self.text.delete('1.0', 'end')
  165. class dummy_acw:
  166. __init__ = Func()
  167. show_window = Func(result=False)
  168. hide_window = Func()
  169. def test_open_completions(self):
  170. # Test completions of files and attributes.
  171. acp = self.autocomplete
  172. fetch = Func(result=(['tem'],['tem', '_tem']))
  173. acp.fetch_completions = fetch
  174. def make_acw(): return self.dummy_acw()
  175. acp._make_autocomplete_window = make_acw
  176. self.text.insert('1.0', 'int.')
  177. acp.open_completions(ac.TAB)
  178. self.assertIsInstance(acp.autocompletewindow, self.dummy_acw)
  179. self.text.delete('1.0', 'end')
  180. # Test files.
  181. self.text.insert('1.0', '"t')
  182. self.assertTrue(acp.open_completions(ac.TAB))
  183. self.text.delete('1.0', 'end')
  184. def test_completion_kwds(self):
  185. self.assertIn('and', ac.completion_kwds)
  186. self.assertIn('case', ac.completion_kwds)
  187. self.assertNotIn('None', ac.completion_kwds)
  188. def test_fetch_completions(self):
  189. # Test that fetch_completions returns 2 lists:
  190. # For attribute completion, a large list containing all variables, and
  191. # a small list containing non-private variables.
  192. # For file completion, a large list containing all files in the path,
  193. # and a small list containing files that do not start with '.'.
  194. acp = self.autocomplete
  195. small, large = acp.fetch_completions(
  196. '', ac.ATTRS)
  197. if hasattr(__main__, '__file__') and __main__.__file__ != ac.__file__:
  198. self.assertNotIn('AutoComplete', small) # See issue 36405.
  199. # Test attributes
  200. s, b = acp.fetch_completions('', ac.ATTRS)
  201. self.assertLess(len(small), len(large))
  202. self.assertTrue(all(filter(lambda x: x.startswith('_'), s)))
  203. self.assertTrue(any(filter(lambda x: x.startswith('_'), b)))
  204. # Test smalll should respect to __all__.
  205. with patch.dict('__main__.__dict__', {'__all__': ['a', 'b']}):
  206. s, b = acp.fetch_completions('', ac.ATTRS)
  207. self.assertEqual(s, ['a', 'b'])
  208. self.assertIn('__name__', b) # From __main__.__dict__.
  209. self.assertIn('sum', b) # From __main__.__builtins__.__dict__.
  210. self.assertIn('nonlocal', b) # From keyword.kwlist.
  211. pos = b.index('False') # Test False not included twice.
  212. self.assertNotEqual(b[pos+1], 'False')
  213. # Test attributes with name entity.
  214. mock = Mock()
  215. mock._private = Mock()
  216. with patch.dict('__main__.__dict__', {'foo': mock}):
  217. s, b = acp.fetch_completions('foo', ac.ATTRS)
  218. self.assertNotIn('_private', s)
  219. self.assertIn('_private', b)
  220. self.assertEqual(s, [i for i in sorted(dir(mock)) if i[:1] != '_'])
  221. self.assertEqual(b, sorted(dir(mock)))
  222. # Test files
  223. def _listdir(path):
  224. # This will be patch and used in fetch_completions.
  225. if path == '.':
  226. return ['foo', 'bar', '.hidden']
  227. return ['monty', 'python', '.hidden']
  228. with patch.object(os, 'listdir', _listdir):
  229. s, b = acp.fetch_completions('', ac.FILES)
  230. self.assertEqual(s, ['bar', 'foo'])
  231. self.assertEqual(b, ['.hidden', 'bar', 'foo'])
  232. s, b = acp.fetch_completions('~', ac.FILES)
  233. self.assertEqual(s, ['monty', 'python'])
  234. self.assertEqual(b, ['.hidden', 'monty', 'python'])
  235. def test_get_entity(self):
  236. # Test that a name is in the namespace of sys.modules and
  237. # __main__.__dict__.
  238. acp = self.autocomplete
  239. Equal = self.assertEqual
  240. Equal(acp.get_entity('int'), int)
  241. # Test name from sys.modules.
  242. mock = Mock()
  243. with patch.dict('sys.modules', {'tempfile': mock}):
  244. Equal(acp.get_entity('tempfile'), mock)
  245. # Test name from __main__.__dict__.
  246. di = {'foo': 10, 'bar': 20}
  247. with patch.dict('__main__.__dict__', {'d': di}):
  248. Equal(acp.get_entity('d'), di)
  249. # Test name not in namespace.
  250. with patch.dict('__main__.__dict__', {}):
  251. with self.assertRaises(NameError):
  252. acp.get_entity('not_exist')
  253. if __name__ == '__main__':
  254. unittest.main(verbosity=2)