From 498ef1bce311cf6c640039a759ae17c6040761e6 Mon Sep 17 00:00:00 2001 From: Tal Einat Date: Sun, 1 Sep 2019 10:14:36 +0300 Subject: [PATCH 1/6] refactor common check into a method --- Lib/idlelib/idle_test/test_tooltip.py | 29 +++++++++++++++------------ 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/Lib/idlelib/idle_test/test_tooltip.py b/Lib/idlelib/idle_test/test_tooltip.py index 44ea1110e155dcb..d1a31a9e2a2d868 100644 --- a/Lib/idlelib/idle_test/test_tooltip.py +++ b/Lib/idlelib/idle_test/test_tooltip.py @@ -65,22 +65,25 @@ class HovertipTest(unittest.TestCase): def setUp(self): self.top, self.button = _make_top_and_button(self) + def is_tipwindow_shown(self, tooltip): + return tooltip.tipwindow and tooltip.tipwindow.winfo_viewable() + def test_showtip(self): tooltip = Hovertip(self.button, 'ToolTip text') self.addCleanup(tooltip.hidetip) - self.assertFalse(tooltip.tipwindow and tooltip.tipwindow.winfo_viewable()) + self.assertFalse(self.is_tipwindow_shown(tooltip)) tooltip.showtip() - self.assertTrue(tooltip.tipwindow and tooltip.tipwindow.winfo_viewable()) + self.assertTrue(self.is_tipwindow_shown(tooltip)) def test_showtip_twice(self): tooltip = Hovertip(self.button, 'ToolTip text') self.addCleanup(tooltip.hidetip) - self.assertFalse(tooltip.tipwindow and tooltip.tipwindow.winfo_viewable()) + self.assertFalse(self.is_tipwindow_shown(tooltip)) tooltip.showtip() - self.assertTrue(tooltip.tipwindow and tooltip.tipwindow.winfo_viewable()) + self.assertTrue(self.is_tipwindow_shown(tooltip)) orig_tipwindow = tooltip.tipwindow tooltip.showtip() - self.assertTrue(tooltip.tipwindow and tooltip.tipwindow.winfo_viewable()) + self.assertTrue(self.is_tipwindow_shown(tooltip)) self.assertIs(tooltip.tipwindow, orig_tipwindow) def test_hidetip(self): @@ -88,17 +91,17 @@ def test_hidetip(self): self.addCleanup(tooltip.hidetip) tooltip.showtip() tooltip.hidetip() - self.assertFalse(tooltip.tipwindow and tooltip.tipwindow.winfo_viewable()) + self.assertFalse(self.is_tipwindow_shown(tooltip)) def test_showtip_on_mouse_enter_no_delay(self): tooltip = Hovertip(self.button, 'ToolTip text', hover_delay=None) self.addCleanup(tooltip.hidetip) tooltip.showtip = add_call_counting(tooltip.showtip) root_update() - self.assertFalse(tooltip.tipwindow and tooltip.tipwindow.winfo_viewable()) + self.assertFalse(self.is_tipwindow_shown(tooltip)) self.button.event_generate('', x=0, y=0) root_update() - self.assertTrue(tooltip.tipwindow and tooltip.tipwindow.winfo_viewable()) + self.assertTrue(self.is_tipwindow_shown(tooltip)) self.assertGreater(len(tooltip.showtip.call_args_list), 0) def test_showtip_on_mouse_enter_hover_delay(self): @@ -106,13 +109,13 @@ def test_showtip_on_mouse_enter_hover_delay(self): self.addCleanup(tooltip.hidetip) tooltip.showtip = add_call_counting(tooltip.showtip) root_update() - self.assertFalse(tooltip.tipwindow and tooltip.tipwindow.winfo_viewable()) + self.assertFalse(self.is_tipwindow_shown(tooltip)) self.button.event_generate('', x=0, y=0) root_update() - self.assertFalse(tooltip.tipwindow and tooltip.tipwindow.winfo_viewable()) + self.assertFalse(self.is_tipwindow_shown(tooltip)) time.sleep(0.1) root_update() - self.assertTrue(tooltip.tipwindow and tooltip.tipwindow.winfo_viewable()) + self.assertTrue(self.is_tipwindow_shown(tooltip)) self.assertGreater(len(tooltip.showtip.call_args_list), 0) def test_hidetip_on_mouse_leave(self): @@ -124,7 +127,7 @@ def test_hidetip_on_mouse_leave(self): root_update() self.button.event_generate('', x=0, y=0) root_update() - self.assertFalse(tooltip.tipwindow and tooltip.tipwindow.winfo_viewable()) + self.assertFalse(self.is_tipwindow_shown(tooltip)) self.assertGreater(len(tooltip.showtip.call_args_list), 0) def test_dont_show_on_mouse_leave_before_delay(self): @@ -138,7 +141,7 @@ def test_dont_show_on_mouse_leave_before_delay(self): root_update() time.sleep(0.1) root_update() - self.assertFalse(tooltip.tipwindow and tooltip.tipwindow.winfo_viewable()) + self.assertFalse(self.is_tipwindow_shown(tooltip)) self.assertEqual(tooltip.showtip.call_args_list, []) From 5fa03237721466c072f0bdd0de3ec0307e5b2c12 Mon Sep 17 00:00:00 2001 From: Tal Einat Date: Sun, 1 Sep 2019 10:17:12 +0300 Subject: [PATCH 2/6] refactor hover tests with a delay into a single test This is done to avoid waiting for a delay to expire multiple times. --- Lib/idlelib/idle_test/test_tooltip.py | 52 +++++++++++++++------------ 1 file changed, 30 insertions(+), 22 deletions(-) diff --git a/Lib/idlelib/idle_test/test_tooltip.py b/Lib/idlelib/idle_test/test_tooltip.py index d1a31a9e2a2d868..f23c9163624c17a 100644 --- a/Lib/idlelib/idle_test/test_tooltip.py +++ b/Lib/idlelib/idle_test/test_tooltip.py @@ -104,34 +104,44 @@ def test_showtip_on_mouse_enter_no_delay(self): self.assertTrue(self.is_tipwindow_shown(tooltip)) self.assertGreater(len(tooltip.showtip.call_args_list), 0) - def test_showtip_on_mouse_enter_hover_delay(self): - tooltip = Hovertip(self.button, 'ToolTip text', hover_delay=50) - self.addCleanup(tooltip.hidetip) - tooltip.showtip = add_call_counting(tooltip.showtip) + def test_hover_with_delay(self): + # Run multiple tests requiring an actual delay simultaneously. + + # Test #1: A hover tip with a non-zero delay appears after the delay. + tooltip1 = Hovertip(self.button, 'ToolTip text', hover_delay=50) + self.addCleanup(tooltip1.hidetip) + tooltip1.showtip = add_call_counting(tooltip1.showtip) root_update() - self.assertFalse(self.is_tipwindow_shown(tooltip)) + self.assertFalse(self.is_tipwindow_shown(tooltip1)) self.button.event_generate('', x=0, y=0) root_update() - self.assertFalse(self.is_tipwindow_shown(tooltip)) - time.sleep(0.1) - root_update() - self.assertTrue(self.is_tipwindow_shown(tooltip)) - self.assertGreater(len(tooltip.showtip.call_args_list), 0) - - def test_hidetip_on_mouse_leave(self): - tooltip = Hovertip(self.button, 'ToolTip text', hover_delay=None) - self.addCleanup(tooltip.hidetip) - tooltip.showtip = add_call_counting(tooltip.showtip) + self.assertFalse(self.is_tipwindow_shown(tooltip1)) + + # Test #2: A hover tip with a non-zero delay doesn't appear when + # the mouse stops hovering over the base widget before the delay + # expires. + tooltip2 = Hovertip(self.button, 'ToolTip text', hover_delay=50) + self.addCleanup(tooltip2.hidetip) + tooltip2.showtip = add_call_counting(tooltip2.showtip) root_update() self.button.event_generate('', x=0, y=0) root_update() self.button.event_generate('', x=0, y=0) root_update() - self.assertFalse(self.is_tipwindow_shown(tooltip)) - self.assertGreater(len(tooltip.showtip.call_args_list), 0) - def test_dont_show_on_mouse_leave_before_delay(self): - tooltip = Hovertip(self.button, 'ToolTip text', hover_delay=50) + time.sleep(0.1) + root_update() + + # Test #1 assertions. + self.assertTrue(self.is_tipwindow_shown(tooltip1)) + self.assertGreater(len(tooltip1.showtip.call_args_list), 0) + + # Test #2 assertions. + self.assertFalse(self.is_tipwindow_shown(tooltip2)) + self.assertEqual(tooltip2.showtip.call_args_list, []) + + def test_hidetip_on_mouse_leave(self): + tooltip = Hovertip(self.button, 'ToolTip text', hover_delay=None) self.addCleanup(tooltip.hidetip) tooltip.showtip = add_call_counting(tooltip.showtip) root_update() @@ -139,10 +149,8 @@ def test_dont_show_on_mouse_leave_before_delay(self): root_update() self.button.event_generate('', x=0, y=0) root_update() - time.sleep(0.1) - root_update() self.assertFalse(self.is_tipwindow_shown(tooltip)) - self.assertEqual(tooltip.showtip.call_args_list, []) + self.assertGreater(len(tooltip.showtip.call_args_list), 0) if __name__ == '__main__': From bea1fd0968dab6ef3ec451e9e5ce6c0d0425c861 Mon Sep 17 00:00:00 2001 From: Tal Einat Date: Sun, 1 Sep 2019 10:17:42 +0300 Subject: [PATCH 3/6] increase hover delay and sleep duration for robustness --- Lib/idlelib/idle_test/test_tooltip.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Lib/idlelib/idle_test/test_tooltip.py b/Lib/idlelib/idle_test/test_tooltip.py index f23c9163624c17a..707a783eb275a8e 100644 --- a/Lib/idlelib/idle_test/test_tooltip.py +++ b/Lib/idlelib/idle_test/test_tooltip.py @@ -108,7 +108,7 @@ def test_hover_with_delay(self): # Run multiple tests requiring an actual delay simultaneously. # Test #1: A hover tip with a non-zero delay appears after the delay. - tooltip1 = Hovertip(self.button, 'ToolTip text', hover_delay=50) + tooltip1 = Hovertip(self.button, 'ToolTip text', hover_delay=100) self.addCleanup(tooltip1.hidetip) tooltip1.showtip = add_call_counting(tooltip1.showtip) root_update() @@ -120,7 +120,7 @@ def test_hover_with_delay(self): # Test #2: A hover tip with a non-zero delay doesn't appear when # the mouse stops hovering over the base widget before the delay # expires. - tooltip2 = Hovertip(self.button, 'ToolTip text', hover_delay=50) + tooltip2 = Hovertip(self.button, 'ToolTip text', hover_delay=100) self.addCleanup(tooltip2.hidetip) tooltip2.showtip = add_call_counting(tooltip2.showtip) root_update() @@ -129,7 +129,7 @@ def test_hover_with_delay(self): self.button.event_generate('', x=0, y=0) root_update() - time.sleep(0.1) + time.sleep(0.15) root_update() # Test #1 assertions. From fd15e0587281e0f636860f9650e12252cf79dbcb Mon Sep 17 00:00:00 2001 From: Tal Einat Date: Sun, 1 Sep 2019 10:20:41 +0300 Subject: [PATCH 4/6] minor: whitespace and import ordering --- Lib/idlelib/idle_test/test_tooltip.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Lib/idlelib/idle_test/test_tooltip.py b/Lib/idlelib/idle_test/test_tooltip.py index 707a783eb275a8e..2af09a80ba2c285 100644 --- a/Lib/idlelib/idle_test/test_tooltip.py +++ b/Lib/idlelib/idle_test/test_tooltip.py @@ -1,27 +1,31 @@ +from functools import wraps from idlelib.tooltip import TooltipBase, Hovertip from test.support import requires -requires('gui') - -from functools import wraps import time from tkinter import Button, Tk, Toplevel import unittest +requires('gui') + + def setUpModule(): global root root = Tk() + def root_update(): global root root.update() + def tearDownModule(): global root root.update_idletasks() root.destroy() del root + def add_call_counting(func): @wraps(func) def wrapped_func(*args, **kwargs): From 79c800fdee39e0e24dcbb9564e39b4051df49637 Mon Sep 17 00:00:00 2001 From: Tal Einat Date: Sun, 1 Sep 2019 10:23:07 +0300 Subject: [PATCH 5/6] add a NEWS entry --- Misc/NEWS.d/next/IDLE/2019-09-01-10-22-55.bpo-35771.tdbmbP.rst | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 Misc/NEWS.d/next/IDLE/2019-09-01-10-22-55.bpo-35771.tdbmbP.rst diff --git a/Misc/NEWS.d/next/IDLE/2019-09-01-10-22-55.bpo-35771.tdbmbP.rst b/Misc/NEWS.d/next/IDLE/2019-09-01-10-22-55.bpo-35771.tdbmbP.rst new file mode 100644 index 000000000000000..a6936e9ab19d528 --- /dev/null +++ b/Misc/NEWS.d/next/IDLE/2019-09-01-10-22-55.bpo-35771.tdbmbP.rst @@ -0,0 +1,2 @@ +Increase the ``hover_delay`` in IDLE's test_tooltip, and run both tests +waiting for a delay to expire simultaneously, to reduce total run time. From 39dae8ce642e9f283b426b83efb890a2ce9af539 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Tue, 3 Sep 2019 00:50:23 -0400 Subject: [PATCH 6/6] Minor revision for style and test coverage. --- Lib/idlelib/NEWS.txt | 3 ++ Lib/idlelib/idle_test/test_tooltip.py | 42 +++++++++---------- Lib/idlelib/tooltip.py | 8 ++-- .../2019-09-01-10-22-55.bpo-35771.tdbmbP.rst | 4 +- 4 files changed, 30 insertions(+), 27 deletions(-) diff --git a/Lib/idlelib/NEWS.txt b/Lib/idlelib/NEWS.txt index 949b30bd99ca0c0..47c2291d2377271 100644 --- a/Lib/idlelib/NEWS.txt +++ b/Lib/idlelib/NEWS.txt @@ -3,6 +3,9 @@ Released on 2019-10-20? ====================================== +bpo-35771: To avoid occasional spurious test_idle failures on slower +machines, increase the ``hover_delay`` in test_tooltip. + bpo-37824: Properly handle user input warnings in IDLE shell. Cease turning SyntaxWarnings into SyntaxErrors. diff --git a/Lib/idlelib/idle_test/test_tooltip.py b/Lib/idlelib/idle_test/test_tooltip.py index 2af09a80ba2c285..c616d4fde3b6d30 100644 --- a/Lib/idlelib/idle_test/test_tooltip.py +++ b/Lib/idlelib/idle_test/test_tooltip.py @@ -1,24 +1,24 @@ -from functools import wraps +"""Test tooltip, coverage 100%. + +Coverage is 100% after excluding 6 lines with "# pragma: no cover". +They involve TclErrors that either should or should not happen in a +particular situation, and which are 'pass'ed if they do. +""" + from idlelib.tooltip import TooltipBase, Hovertip from test.support import requires +requires('gui') + +from functools import wraps import time from tkinter import Button, Tk, Toplevel import unittest -requires('gui') - - def setUpModule(): global root root = Tk() - -def root_update(): - global root - root.update() - - def tearDownModule(): global root root.update_idletasks() @@ -101,10 +101,10 @@ def test_showtip_on_mouse_enter_no_delay(self): tooltip = Hovertip(self.button, 'ToolTip text', hover_delay=None) self.addCleanup(tooltip.hidetip) tooltip.showtip = add_call_counting(tooltip.showtip) - root_update() + root.update() self.assertFalse(self.is_tipwindow_shown(tooltip)) self.button.event_generate('', x=0, y=0) - root_update() + root.update() self.assertTrue(self.is_tipwindow_shown(tooltip)) self.assertGreater(len(tooltip.showtip.call_args_list), 0) @@ -115,10 +115,10 @@ def test_hover_with_delay(self): tooltip1 = Hovertip(self.button, 'ToolTip text', hover_delay=100) self.addCleanup(tooltip1.hidetip) tooltip1.showtip = add_call_counting(tooltip1.showtip) - root_update() + root.update() self.assertFalse(self.is_tipwindow_shown(tooltip1)) self.button.event_generate('', x=0, y=0) - root_update() + root.update() self.assertFalse(self.is_tipwindow_shown(tooltip1)) # Test #2: A hover tip with a non-zero delay doesn't appear when @@ -127,14 +127,14 @@ def test_hover_with_delay(self): tooltip2 = Hovertip(self.button, 'ToolTip text', hover_delay=100) self.addCleanup(tooltip2.hidetip) tooltip2.showtip = add_call_counting(tooltip2.showtip) - root_update() + root.update() self.button.event_generate('', x=0, y=0) - root_update() + root.update() self.button.event_generate('', x=0, y=0) - root_update() + root.update() time.sleep(0.15) - root_update() + root.update() # Test #1 assertions. self.assertTrue(self.is_tipwindow_shown(tooltip1)) @@ -148,11 +148,11 @@ def test_hidetip_on_mouse_leave(self): tooltip = Hovertip(self.button, 'ToolTip text', hover_delay=None) self.addCleanup(tooltip.hidetip) tooltip.showtip = add_call_counting(tooltip.showtip) - root_update() + root.update() self.button.event_generate('', x=0, y=0) - root_update() + root.update() self.button.event_generate('', x=0, y=0) - root_update() + root.update() self.assertFalse(self.is_tipwindow_shown(tooltip)) self.assertGreater(len(tooltip.showtip.call_args_list), 0) diff --git a/Lib/idlelib/tooltip.py b/Lib/idlelib/tooltip.py index f54ea36f059d6fa..69658264dbd4a48 100644 --- a/Lib/idlelib/tooltip.py +++ b/Lib/idlelib/tooltip.py @@ -75,7 +75,7 @@ def hidetip(self): if tw: try: tw.destroy() - except TclError: + except TclError: # pragma: no cover pass @@ -103,8 +103,8 @@ def __init__(self, anchor_widget, hover_delay=1000): def __del__(self): try: self.anchor_widget.unbind("", self._id1) - self.anchor_widget.unbind("", self._id2) - self.anchor_widget.unbind("