diff --git a/Lib/idlelib/idle_test/test_colorizer.py b/Lib/idlelib/idle_test/test_colorizer.py index 498480a74e3..b0b120e75a1 100644 --- a/Lib/idlelib/idle_test/test_colorizer.py +++ b/Lib/idlelib/idle_test/test_colorizer.py @@ -551,7 +551,7 @@ class ColorDelegatorTest(unittest.TestCase): ''') self._assert_highlighting(source, {'STRING': [('1.0', '5.4')]}) - @run_in_tk_mainloop + @run_in_tk_mainloop(delay=50) def test_incremental_editing(self): text = self.text eq = self.assertEqual diff --git a/Lib/idlelib/idle_test/test_sidebar.py b/Lib/idlelib/idle_test/test_sidebar.py index 43e8137d707..53ac3eb2733 100644 --- a/Lib/idlelib/idle_test/test_sidebar.py +++ b/Lib/idlelib/idle_test/test_sidebar.py @@ -510,19 +510,19 @@ class ShellSidebarTest(unittest.TestCase): ) self.assert_sidebar_lines_synced() - @run_in_tk_mainloop + @run_in_tk_mainloop() def test_single_empty_input(self): self.do_input('\n') yield self.assert_sidebar_lines_end_with(['>>>', '>>>']) - @run_in_tk_mainloop + @run_in_tk_mainloop() def test_single_line_statement(self): self.do_input('1\n') yield self.assert_sidebar_lines_end_with(['>>>', None, '>>>']) - @run_in_tk_mainloop + @run_in_tk_mainloop() def test_multi_line_statement(self): # Block statements are not indented because IDLE auto-indents. self.do_input(dedent('''\ @@ -540,14 +540,14 @@ class ShellSidebarTest(unittest.TestCase): '>>>', ]) - @run_in_tk_mainloop + @run_in_tk_mainloop() def test_single_long_line_wraps(self): self.do_input('1' * 200 + '\n') yield self.assert_sidebar_lines_end_with(['>>>', None, '>>>']) self.assert_sidebar_lines_synced() - @run_in_tk_mainloop + @run_in_tk_mainloop() def test_squeeze_multi_line_output(self): shell = self.shell text = shell.text @@ -567,7 +567,7 @@ class ShellSidebarTest(unittest.TestCase): self.assert_sidebar_lines_end_with(['>>>', None, None, None, '>>>']) self.assert_sidebar_lines_synced() - @run_in_tk_mainloop + @run_in_tk_mainloop() def test_interrupt_recall_undo_redo(self): text = self.shell.text # Block statements are not indented because IDLE auto-indents. @@ -613,7 +613,7 @@ class ShellSidebarTest(unittest.TestCase): ['>>>', '...', '...', '...', None, '>>>'] ) - @run_in_tk_mainloop + @run_in_tk_mainloop() def test_very_long_wrapped_line(self): with swap_attr(self.shell, 'squeezer', None): self.do_input('x = ' + '1'*10_000 + '\n') @@ -678,7 +678,7 @@ class ShellSidebarTest(unittest.TestCase): sidebar.update_colors() self.assertEqual(get_sidebar_colors(), test_colors) - @run_in_tk_mainloop + @run_in_tk_mainloop() def test_mousewheel(self): sidebar = self.shell.shell_sidebar text = self.shell.text @@ -703,7 +703,7 @@ class ShellSidebarTest(unittest.TestCase): yield self.assertIsNotNone(text.dlineinfo(text.index(f'{last_lineno}.0'))) - @run_in_tk_mainloop + @run_in_tk_mainloop() def test_copy(self): sidebar = self.shell.shell_sidebar text = self.shell.text @@ -728,7 +728,7 @@ class ShellSidebarTest(unittest.TestCase): copied_text = text.clipboard_get() self.assertEqual(copied_text, selected_text) - @run_in_tk_mainloop + @run_in_tk_mainloop() def test_copy_with_prompts(self): sidebar = self.shell.shell_sidebar text = self.shell.text diff --git a/Lib/idlelib/idle_test/tkinter_testing_utils.py b/Lib/idlelib/idle_test/tkinter_testing_utils.py index a9f8386e2cd..a89839bbe38 100644 --- a/Lib/idlelib/idle_test/tkinter_testing_utils.py +++ b/Lib/idlelib/idle_test/tkinter_testing_utils.py @@ -2,7 +2,7 @@ import functools -def run_in_tk_mainloop(test_method): +def run_in_tk_mainloop(delay=1): """Decorator for running a test method with a real Tk mainloop. This starts a Tk mainloop before running the test, and stops it @@ -13,44 +13,50 @@ def run_in_tk_mainloop(test_method): using "yield" to allow the mainloop to process events and "after" callbacks, and then continue the test from that point. + The delay argument is passed into root.after(...) calls as the number + of ms to wait before passing execution back to the generator function. + This also assumes that the test class has a .root attribute, which is a tkinter.Tk object. For example (from test_sidebar.py): - @run_test_with_tk_mainloop + @run_test_with_tk_mainloop() def test_single_empty_input(self): self.do_input('\n') yield self.assert_sidebar_lines_end_with(['>>>', '>>>']) """ - @functools.wraps(test_method) - def new_test_method(self): - test_generator = test_method(self) - root = self.root - # Exceptions raised by self.assert...() need to be raised - # outside of the after() callback in order for the test - # harness to capture them. - exception = None - def after_callback(): - nonlocal exception - try: - next(test_generator) - except StopIteration: - root.quit() - except Exception as exc: - exception = exc - root.quit() - else: - # Schedule the Tk mainloop to call this function again, - # using a robust method of ensuring that it gets a - # chance to process queued events before doing so. - # See: https://stackoverflow.com/q/18499082#comment65004099_38817470 - root.after(1, root.after_idle, after_callback) - root.after(0, root.after_idle, after_callback) - root.mainloop() + def decorator(test_method): + @functools.wraps(test_method) + def new_test_method(self): + test_generator = test_method(self) + root = self.root + # Exceptions raised by self.assert...() need to be raised + # outside of the after() callback in order for the test + # harness to capture them. + exception = None + def after_callback(): + nonlocal exception + try: + next(test_generator) + except StopIteration: + root.quit() + except Exception as exc: + exception = exc + root.quit() + else: + # Schedule the Tk mainloop to call this function again, + # using a robust method of ensuring that it gets a + # chance to process queued events before doing so. + # See: https://stackoverflow.com/q/18499082#comment65004099_38817470 + root.after(delay, root.after_idle, after_callback) + root.after(0, root.after_idle, after_callback) + root.mainloop() - if exception: - raise exception + if exception: + raise exception - return new_test_method + return new_test_method + + return decorator