Commit 113d735e authored by Louie Lu's avatar Louie Lu Committed by Terry Jan Reedy

bpo-30348: IDLE: Add test_autocomplete unittest (GH-2209)

parent 13c1f72c
...@@ -104,9 +104,14 @@ class AutoComplete: ...@@ -104,9 +104,14 @@ class AutoComplete:
def open_completions(self, evalfuncs, complete, userWantsWin, mode=None): def open_completions(self, evalfuncs, complete, userWantsWin, mode=None):
"""Find the completions and create the AutoCompleteWindow. """Find the completions and create the AutoCompleteWindow.
Return True if successful (no syntax error or so found). Return True if successful (no syntax error or so found).
if complete is True, then if there's nothing to complete and no If complete is True, then if there's nothing to complete and no
start of completion, won't open completions and return False. start of completion, won't open completions and return False.
If mode is given, will open a completion list only in this mode. If mode is given, will open a completion list only in this mode.
Action Function Eval Complete WantWin Mode
^space force_open_completions True, False, True no
. or / try_open_completions False, False, False yes
tab autocomplete False, True, True no
""" """
# Cancel another delayed call, if it exists. # Cancel another delayed call, if it exists.
if self._delayed_completion_id is not None: if self._delayed_completion_id is not None:
...@@ -117,11 +122,11 @@ class AutoComplete: ...@@ -117,11 +122,11 @@ class AutoComplete:
curline = self.text.get("insert linestart", "insert") curline = self.text.get("insert linestart", "insert")
i = j = len(curline) i = j = len(curline)
if hp.is_in_string() and (not mode or mode==COMPLETE_FILES): if hp.is_in_string() and (not mode or mode==COMPLETE_FILES):
# Find the beginning of the string # Find the beginning of the string.
# fetch_completions will look at the file system to determine whether the # fetch_completions will look at the file system to determine
# string value constitutes an actual file name # whether the string value constitutes an actual file name
# XXX could consider raw strings here and unescape the string value if it's # XXX could consider raw strings here and unescape the string
# not raw. # value if it's not raw.
self._remove_autocomplete_window() self._remove_autocomplete_window()
mode = COMPLETE_FILES mode = COMPLETE_FILES
# Find last separator or string start # Find last separator or string start
......
"Test autocomplete, coverage 57%." "Test autocomplete, coverage 87%."
import unittest import unittest
from unittest.mock import Mock, patch
from test.support import requires from test.support import requires
from tkinter import Tk, Text from tkinter import Tk, Text
import os
import __main__ import __main__
import idlelib.autocomplete as ac import idlelib.autocomplete as ac
...@@ -26,12 +28,14 @@ class AutoCompleteTest(unittest.TestCase): ...@@ -26,12 +28,14 @@ class AutoCompleteTest(unittest.TestCase):
def setUpClass(cls): def setUpClass(cls):
requires('gui') requires('gui')
cls.root = Tk() cls.root = Tk()
cls.root.withdraw()
cls.text = Text(cls.root) cls.text = Text(cls.root)
cls.editor = DummyEditwin(cls.root, cls.text) cls.editor = DummyEditwin(cls.root, cls.text)
@classmethod @classmethod
def tearDownClass(cls): def tearDownClass(cls):
del cls.editor, cls.text del cls.editor, cls.text
cls.root.update_idletasks()
cls.root.destroy() cls.root.destroy()
del cls.root del cls.root
...@@ -53,7 +57,7 @@ class AutoCompleteTest(unittest.TestCase): ...@@ -53,7 +57,7 @@ class AutoCompleteTest(unittest.TestCase):
self.assertIsNone(self.autocomplete.autocompletewindow) self.assertIsNone(self.autocomplete.autocompletewindow)
def test_force_open_completions_event(self): def test_force_open_completions_event(self):
# Test that force_open_completions_event calls _open_completions # Test that force_open_completions_event calls _open_completions.
o_cs = Func() o_cs = Func()
self.autocomplete.open_completions = o_cs self.autocomplete.open_completions = o_cs
self.autocomplete.force_open_completions_event('event') self.autocomplete.force_open_completions_event('event')
...@@ -66,16 +70,16 @@ class AutoCompleteTest(unittest.TestCase): ...@@ -66,16 +70,16 @@ class AutoCompleteTest(unittest.TestCase):
o_c_l = Func() o_c_l = Func()
autocomplete._open_completions_later = o_c_l autocomplete._open_completions_later = o_c_l
# _open_completions_later should not be called with no text in editor # _open_completions_later should not be called with no text in editor.
trycompletions('event') trycompletions('event')
Equal(o_c_l.args, None) Equal(o_c_l.args, None)
# _open_completions_later should be called with COMPLETE_ATTRIBUTES (1) # _open_completions_later should be called with COMPLETE_ATTRIBUTES (1).
self.text.insert('1.0', 're.') self.text.insert('1.0', 're.')
trycompletions('event') trycompletions('event')
Equal(o_c_l.args, (False, False, False, 1)) Equal(o_c_l.args, (False, False, False, 1))
# _open_completions_later should be called with COMPLETE_FILES (2) # _open_completions_later should be called with COMPLETE_FILES (2).
self.text.delete('1.0', 'end') self.text.delete('1.0', 'end')
self.text.insert('1.0', '"./Lib/') self.text.insert('1.0', '"./Lib/')
trycompletions('event') trycompletions('event')
...@@ -86,7 +90,7 @@ class AutoCompleteTest(unittest.TestCase): ...@@ -86,7 +90,7 @@ class AutoCompleteTest(unittest.TestCase):
autocomplete = self.autocomplete autocomplete = self.autocomplete
# Test that the autocomplete event is ignored if user is pressing a # Test that the autocomplete event is ignored if user is pressing a
# modifier key in addition to the tab key # modifier key in addition to the tab key.
ev = Event(mc_state=True) ev = Event(mc_state=True)
self.assertIsNone(autocomplete.autocomplete_event(ev)) self.assertIsNone(autocomplete.autocomplete_event(ev))
del ev.mc_state del ev.mc_state
...@@ -96,15 +100,15 @@ class AutoCompleteTest(unittest.TestCase): ...@@ -96,15 +100,15 @@ class AutoCompleteTest(unittest.TestCase):
self.assertIsNone(autocomplete.autocomplete_event(ev)) self.assertIsNone(autocomplete.autocomplete_event(ev))
self.text.delete('1.0', 'end') self.text.delete('1.0', 'end')
# If autocomplete window is open, complete() method is called # If autocomplete window is open, complete() method is called.
self.text.insert('1.0', 're.') self.text.insert('1.0', 're.')
# This must call autocomplete._make_autocomplete_window() # This must call autocomplete._make_autocomplete_window().
Equal(self.autocomplete.autocomplete_event(ev), 'break') Equal(self.autocomplete.autocomplete_event(ev), 'break')
# If autocomplete window is not active or does not exist, # If autocomplete window is not active or does not exist,
# open_completions is called. Return depends on its return. # open_completions is called. Return depends on its return.
autocomplete._remove_autocomplete_window() autocomplete._remove_autocomplete_window()
o_cs = Func() # .result = None o_cs = Func() # .result = None.
autocomplete.open_completions = o_cs autocomplete.open_completions = o_cs
Equal(self.autocomplete.autocomplete_event(ev), None) Equal(self.autocomplete.autocomplete_event(ev), None)
Equal(o_cs.args, (False, True, True)) Equal(o_cs.args, (False, True, True))
...@@ -113,36 +117,130 @@ class AutoCompleteTest(unittest.TestCase): ...@@ -113,36 +117,130 @@ class AutoCompleteTest(unittest.TestCase):
Equal(o_cs.args, (False, True, True)) Equal(o_cs.args, (False, True, True))
def test_open_completions_later(self): def test_open_completions_later(self):
# Test that autocomplete._delayed_completion_id is set # Test that autocomplete._delayed_completion_id is set.
pass acp = self.autocomplete
acp._delayed_completion_id = None
acp._open_completions_later(False, False, False, ac.COMPLETE_ATTRIBUTES)
cb1 = acp._delayed_completion_id
self.assertTrue(cb1.startswith('after'))
# Test that cb1 is cancelled and cb2 is new.
acp._open_completions_later(False, False, False, ac.COMPLETE_FILES)
self.assertNotIn(cb1, self.root.tk.call('after', 'info'))
cb2 = acp._delayed_completion_id
self.assertTrue(cb2.startswith('after') and cb2 != cb1)
self.text.after_cancel(cb2)
def test_delayed_open_completions(self): def test_delayed_open_completions(self):
# Test that autocomplete._delayed_completion_id set to None and that # Test that autocomplete._delayed_completion_id set to None
# open_completions only called if insertion index is the same as # and that open_completions is not called if the index is not
# _delayed_completion_index # equal to _delayed_completion_index.
pass acp = self.autocomplete
acp.open_completions = Func()
acp._delayed_completion_id = 'after'
acp._delayed_completion_index = self.text.index('insert+1c')
acp._delayed_open_completions(1, 2, 3)
self.assertIsNone(acp._delayed_completion_id)
self.assertEqual(acp.open_completions.called, 0)
# Test that open_completions is called if indexes match.
acp._delayed_completion_index = self.text.index('insert')
acp._delayed_open_completions(1, 2, 3, ac.COMPLETE_FILES)
self.assertEqual(acp.open_completions.args, (1, 2, 3, 2))
def test_open_completions(self): def test_open_completions(self):
# Test completions of files and attributes as well as non-completion # Test completions of files and attributes as well as non-completion
# of errors # of errors.
pass self.text.insert('1.0', 'pr')
self.assertTrue(self.autocomplete.open_completions(False, True, True))
self.text.delete('1.0', 'end')
# Test files.
self.text.insert('1.0', '"t')
#self.assertTrue(self.autocomplete.open_completions(False, True, True))
self.text.delete('1.0', 'end')
# Test with blank will fail.
self.assertFalse(self.autocomplete.open_completions(False, True, True))
# Test with only string quote will fail.
self.text.insert('1.0', '"')
self.assertFalse(self.autocomplete.open_completions(False, True, True))
self.text.delete('1.0', 'end')
def test_fetch_completions(self): def test_fetch_completions(self):
# Test that fetch_completions returns 2 lists: # Test that fetch_completions returns 2 lists:
# For attribute completion, a large list containing all variables, and # For attribute completion, a large list containing all variables, and
# a small list containing non-private variables. # a small list containing non-private variables.
# For file completion, a large list containing all files in the path, # For file completion, a large list containing all files in the path,
# and a small list containing files that do not start with '.' # and a small list containing files that do not start with '.'.
autocomplete = self.autocomplete
small, large = self.autocomplete.fetch_completions( small, large = self.autocomplete.fetch_completions(
'', ac.COMPLETE_ATTRIBUTES) '', ac.COMPLETE_ATTRIBUTES)
self.assertLess(len(small), len(large))
if __main__.__file__ != ac.__file__: if __main__.__file__ != ac.__file__:
self.assertNotIn('AutoComplete', small) # See issue 36405. self.assertNotIn('AutoComplete', small) # See issue 36405.
# Test attributes
s, b = autocomplete.fetch_completions('', ac.COMPLETE_ATTRIBUTES)
self.assertLess(len(small), len(large))
self.assertTrue(all(filter(lambda x: x.startswith('_'), s)))
self.assertTrue(any(filter(lambda x: x.startswith('_'), b)))
# Test smalll should respect to __all__.
with patch.dict('__main__.__dict__', {'__all__': ['a', 'b']}):
s, b = autocomplete.fetch_completions('', ac.COMPLETE_ATTRIBUTES)
self.assertEqual(s, ['a', 'b'])
self.assertIn('__name__', b) # From __main__.__dict__
self.assertIn('sum', b) # From __main__.__builtins__.__dict__
# Test attributes with name entity.
mock = Mock()
mock._private = Mock()
with patch.dict('__main__.__dict__', {'foo': mock}):
s, b = autocomplete.fetch_completions('foo', ac.COMPLETE_ATTRIBUTES)
self.assertNotIn('_private', s)
self.assertIn('_private', b)
self.assertEqual(s, [i for i in sorted(dir(mock)) if i[:1] != '_'])
self.assertEqual(b, sorted(dir(mock)))
# Test files
def _listdir(path):
# This will be patch and used in fetch_completions.
if path == '.':
return ['foo', 'bar', '.hidden']
return ['monty', 'python', '.hidden']
with patch.object(os, 'listdir', _listdir):
s, b = autocomplete.fetch_completions('', ac.COMPLETE_FILES)
self.assertEqual(s, ['bar', 'foo'])
self.assertEqual(b, ['.hidden', 'bar', 'foo'])
s, b = autocomplete.fetch_completions('~', ac.COMPLETE_FILES)
self.assertEqual(s, ['monty', 'python'])
self.assertEqual(b, ['.hidden', 'monty', 'python'])
def test_get_entity(self): def test_get_entity(self):
# Test that a name is in the namespace of sys.modules and # Test that a name is in the namespace of sys.modules and
# __main__.__dict__ # __main__.__dict__.
self.assertEqual(self.autocomplete.get_entity('int'), int) autocomplete = self.autocomplete
Equal = self.assertEqual
Equal(self.autocomplete.get_entity('int'), int)
# Test name from sys.modules.
mock = Mock()
with patch.dict('sys.modules', {'tempfile': mock}):
Equal(autocomplete.get_entity('tempfile'), mock)
# Test name from __main__.__dict__.
di = {'foo': 10, 'bar': 20}
with patch.dict('__main__.__dict__', {'d': di}):
Equal(autocomplete.get_entity('d'), di)
# Test name not in namespace.
with patch.dict('__main__.__dict__', {}):
with self.assertRaises(NameError):
autocomplete.get_entity('not_exist')
if __name__ == '__main__': if __name__ == '__main__':
......
Increase test coverage of idlelib.autocomplete by 30%.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment