diff options
author | Miss Islington (bot) <31488909+miss-islington@users.noreply.github.com> | 2019-08-05 00:08:16 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-08-05 00:08:16 (GMT) |
commit | 5349f8cd784220fc6599830c56d3f0614de2b8cb (patch) | |
tree | a888f6adac70a7140969dcca28137f7e392b239a /Lib/idlelib/idle_test | |
parent | 9c95fc752c1465202df67fa894ef326c8ebb8cac (diff) | |
download | cpython-5349f8cd784220fc6599830c56d3f0614de2b8cb.zip cpython-5349f8cd784220fc6599830c56d3f0614de2b8cb.tar.gz cpython-5349f8cd784220fc6599830c56d3f0614de2b8cb.tar.bz2 |
bpo-36419: IDLE - Refactor autocompete and improve testing. (GH-15121)
(cherry picked from commit 1213123005d9f94bb5027c0a5256ea4d3e97b61d)
Co-authored-by: Terry Jan Reedy <tjreedy@udel.edu>
Diffstat (limited to 'Lib/idlelib/idle_test')
-rw-r--r-- | Lib/idlelib/idle_test/test_autocomplete.py | 248 |
1 files changed, 149 insertions, 99 deletions
diff --git a/Lib/idlelib/idle_test/test_autocomplete.py b/Lib/idlelib/idle_test/test_autocomplete.py index 6181b29..2c478cd 100644 --- a/Lib/idlelib/idle_test/test_autocomplete.py +++ b/Lib/idlelib/idle_test/test_autocomplete.py @@ -1,4 +1,4 @@ -"Test autocomplete, coverage 87%." +"Test autocomplete, coverage 93%." import unittest from unittest.mock import Mock, patch @@ -45,127 +45,177 @@ class AutoCompleteTest(unittest.TestCase): def test_init(self): self.assertEqual(self.autocomplete.editwin, self.editor) + self.assertEqual(self.autocomplete.text, self.text) def test_make_autocomplete_window(self): testwin = self.autocomplete._make_autocomplete_window() self.assertIsInstance(testwin, acw.AutoCompleteWindow) def test_remove_autocomplete_window(self): - self.autocomplete.autocompletewindow = ( - self.autocomplete._make_autocomplete_window()) - self.autocomplete._remove_autocomplete_window() - self.assertIsNone(self.autocomplete.autocompletewindow) + acp = self.autocomplete + acp.autocompletewindow = m = Mock() + acp._remove_autocomplete_window() + m.hide_window.assert_called_once() + self.assertIsNone(acp.autocompletewindow) def test_force_open_completions_event(self): - # Test that force_open_completions_event calls _open_completions. - o_cs = Func() - self.autocomplete.open_completions = o_cs - self.autocomplete.force_open_completions_event('event') - self.assertEqual(o_cs.args, (True, False, True)) - - def test_try_open_completions_event(self): - Equal = self.assertEqual - autocomplete = self.autocomplete - trycompletions = self.autocomplete.try_open_completions_event - o_c_l = Func() - autocomplete._open_completions_later = o_c_l - - # _open_completions_later should not be called with no text in editor. - trycompletions('event') - Equal(o_c_l.args, None) - - # _open_completions_later should be called with COMPLETE_ATTRIBUTES (1). - self.text.insert('1.0', 're.') - trycompletions('event') - Equal(o_c_l.args, (False, False, False, 1)) - - # _open_completions_later should be called with COMPLETE_FILES (2). - self.text.delete('1.0', 'end') - self.text.insert('1.0', '"./Lib/') - trycompletions('event') - Equal(o_c_l.args, (False, False, False, 2)) + # Call _open_completions and break. + acp = self.autocomplete + open_c = Func() + acp.open_completions = open_c + self.assertEqual(acp.force_open_completions_event('event'), 'break') + self.assertEqual(open_c.args[0], ac.FORCE) def test_autocomplete_event(self): Equal = self.assertEqual - autocomplete = self.autocomplete + acp = self.autocomplete - # Test that the autocomplete event is ignored if user is pressing a - # modifier key in addition to the tab key. + # Result of autocomplete event: If modified tab, None. ev = Event(mc_state=True) - self.assertIsNone(autocomplete.autocomplete_event(ev)) + self.assertIsNone(acp.autocomplete_event(ev)) del ev.mc_state - # Test that tab after whitespace is ignored. + # If tab after whitespace, None. self.text.insert('1.0', ' """Docstring.\n ') - self.assertIsNone(autocomplete.autocomplete_event(ev)) + self.assertIsNone(acp.autocomplete_event(ev)) self.text.delete('1.0', 'end') - # If autocomplete window is open, complete() method is called. + # If active autocomplete window, complete() and 'break'. self.text.insert('1.0', 're.') - # This must call autocomplete._make_autocomplete_window(). - Equal(self.autocomplete.autocomplete_event(ev), 'break') - - # If autocomplete window is not active or does not exist, - # open_completions is called. Return depends on its return. - autocomplete._remove_autocomplete_window() - o_cs = Func() # .result = None. - autocomplete.open_completions = o_cs - Equal(self.autocomplete.autocomplete_event(ev), None) - Equal(o_cs.args, (False, True, True)) - o_cs.result = True - Equal(self.autocomplete.autocomplete_event(ev), 'break') - Equal(o_cs.args, (False, True, True)) - - def test_open_completions_later(self): - # Test that autocomplete._delayed_completion_id is set. + acp.autocompletewindow = mock = Mock() + mock.is_active = Mock(return_value=True) + Equal(acp.autocomplete_event(ev), 'break') + mock.complete.assert_called_once() + acp.autocompletewindow = None + + # If no active autocomplete window, open_completions(), None/break. + open_c = Func(result=False) + acp.open_completions = open_c + Equal(acp.autocomplete_event(ev), None) + Equal(open_c.args[0], ac.TAB) + open_c.result = True + Equal(acp.autocomplete_event(ev), 'break') + Equal(open_c.args[0], ac.TAB) + + def test_try_open_completions_event(self): + Equal = self.assertEqual + text = self.text acp = self.autocomplete + trycompletions = acp.try_open_completions_event + after = Func(result='after1') + acp.text.after = after + + # If no text or trigger, after not called. + trycompletions() + Equal(after.called, 0) + text.insert('1.0', 're') + trycompletions() + Equal(after.called, 0) + + # Attribute needed, no existing callback. + text.insert('insert', ' re.') acp._delayed_completion_id = None - acp._open_completions_later(False, False, False, ac.COMPLETE_ATTRIBUTES) + trycompletions() + Equal(acp._delayed_completion_index, text.index('insert')) + Equal(after.args, + (acp.popupwait, acp._delayed_open_completions, ac.TRY_A)) 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) + Equal(cb1, 'after1') + + # File needed, existing callback cancelled. + text.insert('insert', ' "./Lib/') + after.result = 'after2' + cancel = Func() + acp.text.after_cancel = cancel + trycompletions() + Equal(acp._delayed_completion_index, text.index('insert')) + Equal(cancel.args, (cb1,)) + Equal(after.args, + (acp.popupwait, acp._delayed_open_completions, ac.TRY_F)) + Equal(acp._delayed_completion_id, 'after2') def test_delayed_open_completions(self): - # Test that autocomplete._delayed_completion_id set to None - # and that open_completions is not called if the index is not - # equal to _delayed_completion_index. + Equal = self.assertEqual acp = self.autocomplete - acp.open_completions = Func() + open_c = Func() + acp.open_completions = open_c + self.text.insert('1.0', '"dict.') + + # Set autocomplete._delayed_completion_id to None. + # Text index changed, don't call open_completions. acp._delayed_completion_id = 'after' acp._delayed_completion_index = self.text.index('insert+1c') - acp._delayed_open_completions(1, 2, 3) + acp._delayed_open_completions('dummy') self.assertIsNone(acp._delayed_completion_id) - self.assertEqual(acp.open_completions.called, 0) + Equal(open_c.called, 0) - # Test that open_completions is called if indexes match. + # Text index unchanged, call open_completions. 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)) + acp._delayed_open_completions((1, 2, 3, ac.FILES)) + self.assertEqual(open_c.args[0], (1, 2, 3, ac.FILES)) + + def test_oc_cancel_comment(self): + none = self.assertIsNone + acp = self.autocomplete + + # Comment is in neither code or string. + acp._delayed_completion_id = 'after' + after = Func(result='after') + acp.text.after_cancel = after + self.text.insert(1.0, '# comment') + none(acp.open_completions(ac.TAB)) # From 'else' after 'elif'. + none(acp._delayed_completion_id) + + def test_oc_no_list(self): + acp = self.autocomplete + fetch = Func(result=([],[])) + acp.fetch_completions = fetch + self.text.insert('1.0', 'object') + self.assertIsNone(acp.open_completions(ac.TAB)) + self.text.insert('insert', '.') + self.assertIsNone(acp.open_completions(ac.TAB)) + self.assertEqual(fetch.called, 2) + + + def test_open_completions_none(self): + # Test other two None returns. + none = self.assertIsNone + acp = self.autocomplete + + # No object for attributes or need call not allowed. + self.text.insert(1.0, '.') + none(acp.open_completions(ac.TAB)) + self.text.insert('insert', ' int().') + none(acp.open_completions(ac.TAB)) + + # Blank or quote trigger 'if complete ...'. + self.text.delete(1.0, 'end') + self.assertFalse(acp.open_completions(ac.TAB)) + self.text.insert('1.0', '"') + self.assertFalse(acp.open_completions(ac.TAB)) + self.text.delete('1.0', 'end') + + class dummy_acw(): + __init__ = Func() + show_window = Func(result=False) + hide_window = Func() def test_open_completions(self): - # Test completions of files and attributes as well as non-completion - # of errors. - self.text.insert('1.0', 'pr') - self.assertTrue(self.autocomplete.open_completions(False, True, True)) + # Test completions of files and attributes. + acp = self.autocomplete + fetch = Func(result=(['tem'],['tem', '_tem'])) + acp.fetch_completions = fetch + def make_acw(): return self.dummy_acw() + acp._make_autocomplete_window = make_acw + + self.text.insert('1.0', 'int.') + acp.open_completions(ac.TAB) + self.assertIsInstance(acp.autocompletewindow, self.dummy_acw) 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.assertTrue(acp.open_completions(ac.TAB)) self.text.delete('1.0', 'end') def test_fetch_completions(self): @@ -174,21 +224,21 @@ class AutoCompleteTest(unittest.TestCase): # a small list containing non-private variables. # For file completion, a large list containing all files in the path, # and a small list containing files that do not start with '.'. - autocomplete = self.autocomplete - small, large = self.autocomplete.fetch_completions( - '', ac.COMPLETE_ATTRIBUTES) + acp = self.autocomplete + small, large = acp.fetch_completions( + '', ac.ATTRS) if __main__.__file__ != ac.__file__: self.assertNotIn('AutoComplete', small) # See issue 36405. # Test attributes - s, b = autocomplete.fetch_completions('', ac.COMPLETE_ATTRIBUTES) + s, b = acp.fetch_completions('', ac.ATTRS) 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) + s, b = acp.fetch_completions('', ac.ATTRS) self.assertEqual(s, ['a', 'b']) self.assertIn('__name__', b) # From __main__.__dict__ self.assertIn('sum', b) # From __main__.__builtins__.__dict__ @@ -197,7 +247,7 @@ class AutoCompleteTest(unittest.TestCase): mock = Mock() mock._private = Mock() with patch.dict('__main__.__dict__', {'foo': mock}): - s, b = autocomplete.fetch_completions('foo', ac.COMPLETE_ATTRIBUTES) + s, b = acp.fetch_completions('foo', ac.ATTRS) self.assertNotIn('_private', s) self.assertIn('_private', b) self.assertEqual(s, [i for i in sorted(dir(mock)) if i[:1] != '_']) @@ -211,36 +261,36 @@ class AutoCompleteTest(unittest.TestCase): return ['monty', 'python', '.hidden'] with patch.object(os, 'listdir', _listdir): - s, b = autocomplete.fetch_completions('', ac.COMPLETE_FILES) + s, b = acp.fetch_completions('', ac.FILES) self.assertEqual(s, ['bar', 'foo']) self.assertEqual(b, ['.hidden', 'bar', 'foo']) - s, b = autocomplete.fetch_completions('~', ac.COMPLETE_FILES) + s, b = acp.fetch_completions('~', ac.FILES) self.assertEqual(s, ['monty', 'python']) self.assertEqual(b, ['.hidden', 'monty', 'python']) def test_get_entity(self): # Test that a name is in the namespace of sys.modules and # __main__.__dict__. - autocomplete = self.autocomplete + acp = self.autocomplete Equal = self.assertEqual - Equal(self.autocomplete.get_entity('int'), int) + Equal(acp.get_entity('int'), int) # Test name from sys.modules. mock = Mock() with patch.dict('sys.modules', {'tempfile': mock}): - Equal(autocomplete.get_entity('tempfile'), mock) + Equal(acp.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) + Equal(acp.get_entity('d'), di) # Test name not in namespace. with patch.dict('__main__.__dict__', {}): with self.assertRaises(NameError): - autocomplete.get_entity('not_exist') + acp.get_entity('not_exist') if __name__ == '__main__': |