diff options
Diffstat (limited to 'Lib/idlelib/idle_test')
-rw-r--r-- | Lib/idlelib/idle_test/test_squeezer.py | 311 |
1 files changed, 134 insertions, 177 deletions
diff --git a/Lib/idlelib/idle_test/test_squeezer.py b/Lib/idlelib/idle_test/test_squeezer.py index da2c2dd..7c28a10 100644 --- a/Lib/idlelib/idle_test/test_squeezer.py +++ b/Lib/idlelib/idle_test/test_squeezer.py @@ -1,3 +1,5 @@ +"Test squeezer, coverage 95%" + from collections import namedtuple from textwrap import dedent from tkinter import Text, Tk @@ -33,10 +35,10 @@ def get_test_tk_root(test_instance): class CountLinesTest(unittest.TestCase): """Tests for the count_lines_with_wrapping function.""" - def check(self, expected, text, linewidth, tabwidth): + def check(self, expected, text, linewidth): return self.assertEqual( expected, - count_lines_with_wrapping(text, linewidth, tabwidth), + count_lines_with_wrapping(text, linewidth), ) def test_count_empty(self): @@ -55,37 +57,14 @@ class CountLinesTest(unittest.TestCase): """Test with several lines of text.""" self.assertEqual(count_lines_with_wrapping("1\n2\n3\n"), 3) - def test_tab_width(self): - """Test with various tab widths and line widths.""" - self.check(expected=1, text='\t' * 1, linewidth=8, tabwidth=4) - self.check(expected=1, text='\t' * 2, linewidth=8, tabwidth=4) - self.check(expected=2, text='\t' * 3, linewidth=8, tabwidth=4) - self.check(expected=2, text='\t' * 4, linewidth=8, tabwidth=4) - self.check(expected=3, text='\t' * 5, linewidth=8, tabwidth=4) - - # test longer lines and various tab widths - self.check(expected=4, text='\t' * 10, linewidth=12, tabwidth=4) - self.check(expected=10, text='\t' * 10, linewidth=12, tabwidth=8) - self.check(expected=2, text='\t' * 4, linewidth=10, tabwidth=3) - - # test tabwidth=1 - self.check(expected=2, text='\t' * 9, linewidth=5, tabwidth=1) - self.check(expected=2, text='\t' * 10, linewidth=5, tabwidth=1) - self.check(expected=3, text='\t' * 11, linewidth=5, tabwidth=1) - - # test for off-by-one errors - self.check(expected=2, text='\t' * 6, linewidth=12, tabwidth=4) - self.check(expected=3, text='\t' * 6, linewidth=11, tabwidth=4) - self.check(expected=2, text='\t' * 6, linewidth=13, tabwidth=4) - def test_empty_lines(self): - self.check(expected=1, text='\n', linewidth=80, tabwidth=8) - self.check(expected=2, text='\n\n', linewidth=80, tabwidth=8) - self.check(expected=10, text='\n' * 10, linewidth=80, tabwidth=8) + self.check(expected=1, text='\n', linewidth=80) + self.check(expected=2, text='\n\n', linewidth=80) + self.check(expected=10, text='\n' * 10, linewidth=80) def test_long_line(self): - self.check(expected=3, text='a' * 200, linewidth=80, tabwidth=8) - self.check(expected=3, text='a' * 200 + '\n', linewidth=80, tabwidth=8) + self.check(expected=3, text='a' * 200, linewidth=80) + self.check(expected=3, text='a' * 200 + '\n', linewidth=80) def test_several_lines_different_lengths(self): text = dedent("""\ @@ -94,82 +73,78 @@ class CountLinesTest(unittest.TestCase): 7 chars 13 characters""") - self.check(expected=5, text=text, linewidth=80, tabwidth=8) - self.check(expected=5, text=text + '\n', linewidth=80, tabwidth=8) - self.check(expected=6, text=text, linewidth=40, tabwidth=8) - self.check(expected=7, text=text, linewidth=20, tabwidth=8) - self.check(expected=11, text=text, linewidth=10, tabwidth=8) + self.check(expected=5, text=text, linewidth=80) + self.check(expected=5, text=text + '\n', linewidth=80) + self.check(expected=6, text=text, linewidth=40) + self.check(expected=7, text=text, linewidth=20) + self.check(expected=11, text=text, linewidth=10) class SqueezerTest(unittest.TestCase): """Tests for the Squeezer class.""" - def make_mock_editor_window(self): + def tearDown(self): + # Clean up the Squeezer class's reference to its instance, + # to avoid side-effects from one test case upon another. + if Squeezer._instance_weakref is not None: + Squeezer._instance_weakref = None + + def make_mock_editor_window(self, with_text_widget=False): """Create a mock EditorWindow instance.""" editwin = NonCallableMagicMock() # isinstance(editwin, PyShell) must be true for Squeezer to enable - # auto-squeezing; in practice this will always be true + # auto-squeezing; in practice this will always be true. editwin.__class__ = PyShell + + if with_text_widget: + editwin.root = get_test_tk_root(self) + text_widget = self.make_text_widget(root=editwin.root) + editwin.text = editwin.per.bottom = text_widget + return editwin def make_squeezer_instance(self, editor_window=None): """Create an actual Squeezer instance with a mock EditorWindow.""" if editor_window is None: editor_window = self.make_mock_editor_window() - return Squeezer(editor_window) + squeezer = Squeezer(editor_window) + squeezer.get_line_width = Mock(return_value=80) + return squeezer + + def make_text_widget(self, root=None): + if root is None: + root = get_test_tk_root(self) + text_widget = Text(root) + text_widget["font"] = ('Courier', 10) + text_widget.mark_set("iomark", "1.0") + return text_widget + + def set_idleconf_option_with_cleanup(self, configType, section, option, value): + prev_val = idleConf.GetOption(configType, section, option) + idleConf.SetOption(configType, section, option, value) + self.addCleanup(idleConf.SetOption, + configType, section, option, prev_val) def test_count_lines(self): - """Test Squeezer.count_lines() with various inputs. - - This checks that Squeezer.count_lines() calls the - count_lines_with_wrapping() function with the appropriate parameters. - """ - for tabwidth, linewidth in [(4, 80), (1, 79), (8, 80), (3, 120)]: - self._test_count_lines_helper(linewidth=linewidth, - tabwidth=tabwidth) - - def _prepare_mock_editwin_for_count_lines(self, editwin, - linewidth, tabwidth): - """Prepare a mock EditorWindow object for Squeezer.count_lines.""" - CHAR_WIDTH = 10 - BORDER_WIDTH = 2 - PADDING_WIDTH = 1 - - # Prepare all the required functionality on the mock EditorWindow object - # so that the calculations in Squeezer.count_lines() can run. - editwin.get_tk_tabwidth.return_value = tabwidth - editwin.text.winfo_width.return_value = \ - linewidth * CHAR_WIDTH + 2 * (BORDER_WIDTH + PADDING_WIDTH) - text_opts = { - 'border': BORDER_WIDTH, - 'padx': PADDING_WIDTH, - 'font': None, - } - editwin.text.cget = lambda opt: text_opts[opt] - - # monkey-path tkinter.font.Font with a mock object, so that - # Font.measure('0') returns CHAR_WIDTH - mock_font = Mock() - def measure(char): - if char == '0': - return CHAR_WIDTH - raise ValueError("measure should only be called on '0'!") - mock_font.return_value.measure = measure - patcher = patch('idlelib.squeezer.Font', mock_font) - patcher.start() - self.addCleanup(patcher.stop) - - def _test_count_lines_helper(self, linewidth, tabwidth): - """Helper for test_count_lines.""" + """Test Squeezer.count_lines() with various inputs.""" editwin = self.make_mock_editor_window() - self._prepare_mock_editwin_for_count_lines(editwin, linewidth, tabwidth) squeezer = self.make_squeezer_instance(editwin) - mock_count_lines = Mock(return_value=SENTINEL_VALUE) - text = 'TEXT' - with patch('idlelib.squeezer.count_lines_with_wrapping', - mock_count_lines): - self.assertIs(squeezer.count_lines(text), SENTINEL_VALUE) - mock_count_lines.assert_called_with(text, linewidth, tabwidth) + for text_code, line_width, expected in [ + (r"'\n'", 80, 1), + (r"'\n' * 3", 80, 3), + (r"'a' * 40 + '\n'", 80, 1), + (r"'a' * 80 + '\n'", 80, 1), + (r"'a' * 200 + '\n'", 80, 3), + (r"'aa\t' * 20", 80, 2), + (r"'aa\t' * 21", 80, 3), + (r"'aa\t' * 20", 40, 4), + ]: + with self.subTest(text_code=text_code, + line_width=line_width, + expected=expected): + text = eval(text_code) + squeezer.get_line_width.return_value = line_width + self.assertEqual(squeezer.count_lines(text), expected) def test_init(self): """Test the creation of Squeezer instances.""" @@ -207,8 +182,6 @@ class SqueezerTest(unittest.TestCase): def test_write_stdout(self): """Test Squeezer's overriding of the EditorWindow's write() method.""" editwin = self.make_mock_editor_window() - self._prepare_mock_editwin_for_count_lines(editwin, - linewidth=80, tabwidth=8) for text in ['', 'TEXT']: editwin.write = orig_write = Mock(return_value=SENTINEL_VALUE) @@ -232,12 +205,8 @@ class SqueezerTest(unittest.TestCase): def test_auto_squeeze(self): """Test that the auto-squeezing creates an ExpandingButton properly.""" - root = get_test_tk_root(self) - text_widget = Text(root) - text_widget.mark_set("iomark", "1.0") - - editwin = self.make_mock_editor_window() - editwin.text = text_widget + editwin = self.make_mock_editor_window(with_text_widget=True) + text_widget = editwin.text squeezer = self.make_squeezer_instance(editwin) squeezer.auto_squeeze_min_lines = 5 squeezer.count_lines = Mock(return_value=6) @@ -248,58 +217,48 @@ class SqueezerTest(unittest.TestCase): def test_squeeze_current_text_event(self): """Test the squeeze_current_text event.""" - root = get_test_tk_root(self) - - # squeezing text should work for both stdout and stderr + # Squeezing text should work for both stdout and stderr. for tag_name in ["stdout", "stderr"]: - text_widget = Text(root) - text_widget.mark_set("iomark", "1.0") - - editwin = self.make_mock_editor_window() - editwin.text = editwin.per.bottom = text_widget + editwin = self.make_mock_editor_window(with_text_widget=True) + text_widget = editwin.text squeezer = self.make_squeezer_instance(editwin) squeezer.count_lines = Mock(return_value=6) - # prepare some text in the Text widget + # Prepare some text in the Text widget. text_widget.insert("1.0", "SOME\nTEXT\n", tag_name) text_widget.mark_set("insert", "1.0") self.assertEqual(text_widget.get('1.0', 'end'), 'SOME\nTEXT\n\n') self.assertEqual(len(squeezer.expandingbuttons), 0) - # test squeezing the current text + # Test squeezing the current text. retval = squeezer.squeeze_current_text_event(event=Mock()) self.assertEqual(retval, "break") self.assertEqual(text_widget.get('1.0', 'end'), '\n\n') self.assertEqual(len(squeezer.expandingbuttons), 1) self.assertEqual(squeezer.expandingbuttons[0].s, 'SOME\nTEXT') - # test that expanding the squeezed text works and afterwards the - # Text widget contains the original text + # Test that expanding the squeezed text works and afterwards + # the Text widget contains the original text. squeezer.expandingbuttons[0].expand(event=Mock()) self.assertEqual(text_widget.get('1.0', 'end'), 'SOME\nTEXT\n\n') self.assertEqual(len(squeezer.expandingbuttons), 0) def test_squeeze_current_text_event_no_allowed_tags(self): """Test that the event doesn't squeeze text without a relevant tag.""" - root = get_test_tk_root(self) - - text_widget = Text(root) - text_widget.mark_set("iomark", "1.0") - - editwin = self.make_mock_editor_window() - editwin.text = editwin.per.bottom = text_widget + editwin = self.make_mock_editor_window(with_text_widget=True) + text_widget = editwin.text squeezer = self.make_squeezer_instance(editwin) squeezer.count_lines = Mock(return_value=6) - # prepare some text in the Text widget + # Prepare some text in the Text widget. text_widget.insert("1.0", "SOME\nTEXT\n", "TAG") text_widget.mark_set("insert", "1.0") self.assertEqual(text_widget.get('1.0', 'end'), 'SOME\nTEXT\n\n') self.assertEqual(len(squeezer.expandingbuttons), 0) - # test squeezing the current text + # Test squeezing the current text. retval = squeezer.squeeze_current_text_event(event=Mock()) self.assertEqual(retval, "break") self.assertEqual(text_widget.get('1.0', 'end'), 'SOME\nTEXT\n\n') @@ -307,23 +266,18 @@ class SqueezerTest(unittest.TestCase): def test_squeeze_text_before_existing_squeezed_text(self): """Test squeezing text before existing squeezed text.""" - root = get_test_tk_root(self) - - text_widget = Text(root) - text_widget.mark_set("iomark", "1.0") - - editwin = self.make_mock_editor_window() - editwin.text = editwin.per.bottom = text_widget + editwin = self.make_mock_editor_window(with_text_widget=True) + text_widget = editwin.text squeezer = self.make_squeezer_instance(editwin) squeezer.count_lines = Mock(return_value=6) - # prepare some text in the Text widget and squeeze it + # Prepare some text in the Text widget and squeeze it. text_widget.insert("1.0", "SOME\nTEXT\n", "stdout") text_widget.mark_set("insert", "1.0") squeezer.squeeze_current_text_event(event=Mock()) self.assertEqual(len(squeezer.expandingbuttons), 1) - # test squeezing the current text + # Test squeezing the current text. text_widget.insert("1.0", "MORE\nSTUFF\n", "stdout") text_widget.mark_set("insert", "1.0") retval = squeezer.squeeze_current_text_event(event=Mock()) @@ -336,27 +290,30 @@ class SqueezerTest(unittest.TestCase): squeezer.expandingbuttons[1], )) - GetOptionSignature = namedtuple('GetOptionSignature', - 'configType section option default type warn_on_default raw') - @classmethod - def _make_sig(cls, configType, section, option, default=sentinel.NOT_GIVEN, - type=sentinel.NOT_GIVEN, - warn_on_default=sentinel.NOT_GIVEN, - raw=sentinel.NOT_GIVEN): - return cls.GetOptionSignature(configType, section, option, default, - type, warn_on_default, raw) - - @classmethod - def get_GetOption_signature(cls, mock_call_obj): - args, kwargs = mock_call_obj[-2:] - return cls._make_sig(*args, **kwargs) - def test_reload(self): """Test the reload() class-method.""" - self.assertIsInstance(Squeezer.auto_squeeze_min_lines, int) - idleConf.SetOption('main', 'PyShell', 'auto-squeeze-min-lines', '42') + editwin = self.make_mock_editor_window(with_text_widget=True) + text_widget = editwin.text + squeezer = self.make_squeezer_instance(editwin) + + orig_zero_char_width = squeezer.zero_char_width + orig_auto_squeeze_min_lines = squeezer.auto_squeeze_min_lines + + # Increase both font size and auto-squeeze-min-lines. + text_widget["font"] = ('Courier', 20) + new_auto_squeeze_min_lines = orig_auto_squeeze_min_lines + 10 + self.set_idleconf_option_with_cleanup( + 'main', 'PyShell', 'auto-squeeze-min-lines', + str(new_auto_squeeze_min_lines)) + + Squeezer.reload() + self.assertGreater(squeezer.zero_char_width, orig_zero_char_width) + self.assertEqual(squeezer.auto_squeeze_min_lines, + new_auto_squeeze_min_lines) + + def test_reload_no_squeezer_instances(self): + """Test that Squeezer.reload() runs without any instances existing.""" Squeezer.reload() - self.assertEqual(Squeezer.auto_squeeze_min_lines, 42) class ExpandingButtonTest(unittest.TestCase): @@ -369,7 +326,7 @@ class ExpandingButtonTest(unittest.TestCase): squeezer = Mock() squeezer.editwin.text = Text(root) - # Set default values for the configuration settings + # Set default values for the configuration settings. squeezer.auto_squeeze_min_lines = 50 return squeezer @@ -382,23 +339,23 @@ class ExpandingButtonTest(unittest.TestCase): expandingbutton = ExpandingButton('TEXT', 'TAGS', 50, squeezer) self.assertEqual(expandingbutton.s, 'TEXT') - # check that the underlying tkinter.Button is properly configured + # Check that the underlying tkinter.Button is properly configured. self.assertEqual(expandingbutton.master, text_widget) self.assertTrue('50 lines' in expandingbutton.cget('text')) - # check that the text widget still contains no text + # Check that the text widget still contains no text. self.assertEqual(text_widget.get('1.0', 'end'), '\n') - # check that the mouse events are bound + # Check that the mouse events are bound. self.assertIn('<Double-Button-1>', expandingbutton.bind()) right_button_code = '<Button-%s>' % ('2' if macosx.isAquaTk() else '3') self.assertIn(right_button_code, expandingbutton.bind()) - # check that ToolTip was called once, with appropriate values + # Check that ToolTip was called once, with appropriate values. self.assertEqual(MockHovertip.call_count, 1) MockHovertip.assert_called_with(expandingbutton, ANY, hover_delay=ANY) - # check that 'right-click' appears in the tooltip text + # Check that 'right-click' appears in the tooltip text. tooltip_text = MockHovertip.call_args[0][1] self.assertIn('right-click', tooltip_text.lower()) @@ -407,29 +364,30 @@ class ExpandingButtonTest(unittest.TestCase): squeezer = self.make_mock_squeezer() expandingbutton = ExpandingButton('TEXT', 'TAGS', 50, squeezer) - # insert the button into the text widget - # (this is normally done by the Squeezer class) + # Insert the button into the text widget + # (this is normally done by the Squeezer class). text_widget = expandingbutton.text text_widget.window_create("1.0", window=expandingbutton) - # set base_text to the text widget, so that changes are actually made - # to it (by ExpandingButton) and we can inspect these changes afterwards + # Set base_text to the text widget, so that changes are actually + # made to it (by ExpandingButton) and we can inspect these + # changes afterwards. expandingbutton.base_text = expandingbutton.text # trigger the expand event retval = expandingbutton.expand(event=Mock()) self.assertEqual(retval, None) - # check that the text was inserted into the text widget + # Check that the text was inserted into the text widget. self.assertEqual(text_widget.get('1.0', 'end'), 'TEXT\n') - # check that the 'TAGS' tag was set on the inserted text + # Check that the 'TAGS' tag was set on the inserted text. text_end_index = text_widget.index('end-1c') self.assertEqual(text_widget.get('1.0', text_end_index), 'TEXT') self.assertEqual(text_widget.tag_nextrange('TAGS', '1.0'), ('1.0', text_end_index)) - # check that the button removed itself from squeezer.expandingbuttons + # Check that the button removed itself from squeezer.expandingbuttons. self.assertEqual(squeezer.expandingbuttons.remove.call_count, 1) squeezer.expandingbuttons.remove.assert_called_with(expandingbutton) @@ -441,55 +399,54 @@ class ExpandingButtonTest(unittest.TestCase): expandingbutton.set_is_dangerous() self.assertTrue(expandingbutton.is_dangerous) - # insert the button into the text widget - # (this is normally done by the Squeezer class) + # Insert the button into the text widget + # (this is normally done by the Squeezer class). text_widget = expandingbutton.text text_widget.window_create("1.0", window=expandingbutton) - # set base_text to the text widget, so that changes are actually made - # to it (by ExpandingButton) and we can inspect these changes afterwards + # Set base_text to the text widget, so that changes are actually + # made to it (by ExpandingButton) and we can inspect these + # changes afterwards. expandingbutton.base_text = expandingbutton.text - # patch the message box module to always return False + # Patch the message box module to always return False. with patch('idlelib.squeezer.tkMessageBox') as mock_msgbox: mock_msgbox.askokcancel.return_value = False mock_msgbox.askyesno.return_value = False - - # trigger the expand event + # Trigger the expand event. retval = expandingbutton.expand(event=Mock()) - # check that the event chain was broken and no text was inserted + # Check that the event chain was broken and no text was inserted. self.assertEqual(retval, 'break') self.assertEqual(expandingbutton.text.get('1.0', 'end-1c'), '') - # patch the message box module to always return True + # Patch the message box module to always return True. with patch('idlelib.squeezer.tkMessageBox') as mock_msgbox: mock_msgbox.askokcancel.return_value = True mock_msgbox.askyesno.return_value = True - - # trigger the expand event + # Trigger the expand event. retval = expandingbutton.expand(event=Mock()) - # check that the event chain wasn't broken and the text was inserted + # Check that the event chain wasn't broken and the text was inserted. self.assertEqual(retval, None) self.assertEqual(expandingbutton.text.get('1.0', 'end-1c'), text) def test_copy(self): """Test the copy event.""" - # testing with the actual clipboard proved problematic, so this test - # replaces the clipboard manipulation functions with mocks and checks - # that they are called appropriately + # Testing with the actual clipboard proved problematic, so this + # test replaces the clipboard manipulation functions with mocks + # and checks that they are called appropriately. squeezer = self.make_mock_squeezer() expandingbutton = ExpandingButton('TEXT', 'TAGS', 50, squeezer) expandingbutton.clipboard_clear = Mock() expandingbutton.clipboard_append = Mock() - # trigger the copy event + # Trigger the copy event. retval = expandingbutton.copy(event=Mock()) self.assertEqual(retval, None) - # check that the expanding button called clipboard_clear() and - # clipboard_append('TEXT') once each + # Vheck that the expanding button called clipboard_clear() and + # clipboard_append('TEXT') once each. self.assertEqual(expandingbutton.clipboard_clear.call_count, 1) self.assertEqual(expandingbutton.clipboard_append.call_count, 1) expandingbutton.clipboard_append.assert_called_with('TEXT') @@ -502,13 +459,13 @@ class ExpandingButtonTest(unittest.TestCase): with patch('idlelib.squeezer.view_text', autospec=view_text)\ as mock_view_text: - # trigger the view event + # Trigger the view event. expandingbutton.view(event=Mock()) - # check that the expanding button called view_text + # Check that the expanding button called view_text. self.assertEqual(mock_view_text.call_count, 1) - # check that the proper text was passed + # Check that the proper text was passed. self.assertEqual(mock_view_text.call_args[0][2], 'TEXT') def test_rmenu(self): |