bpo-35771: IDLE: Fix flaky tool-tip hover delay tests (GH-15634)
Extending the hover delay in test_tooltip should avoid spurious test_idle failures. One longer delay instead of two shorter delays results in a net speedup.
This commit is contained in:
parent
efa3b51fd0
commit
132acaba5a
|
@ -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.
|
bpo-37824: Properly handle user input warnings in IDLE shell.
|
||||||
Cease turning SyntaxWarnings into SyntaxErrors.
|
Cease turning SyntaxWarnings into SyntaxErrors.
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,10 @@
|
||||||
|
"""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 idlelib.tooltip import TooltipBase, Hovertip
|
||||||
from test.support import requires
|
from test.support import requires
|
||||||
requires('gui')
|
requires('gui')
|
||||||
|
@ -12,16 +19,13 @@ def setUpModule():
|
||||||
global root
|
global root
|
||||||
root = Tk()
|
root = Tk()
|
||||||
|
|
||||||
def root_update():
|
|
||||||
global root
|
|
||||||
root.update()
|
|
||||||
|
|
||||||
def tearDownModule():
|
def tearDownModule():
|
||||||
global root
|
global root
|
||||||
root.update_idletasks()
|
root.update_idletasks()
|
||||||
root.destroy()
|
root.destroy()
|
||||||
del root
|
del root
|
||||||
|
|
||||||
|
|
||||||
def add_call_counting(func):
|
def add_call_counting(func):
|
||||||
@wraps(func)
|
@wraps(func)
|
||||||
def wrapped_func(*args, **kwargs):
|
def wrapped_func(*args, **kwargs):
|
||||||
|
@ -65,22 +69,25 @@ class HovertipTest(unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.top, self.button = _make_top_and_button(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):
|
def test_showtip(self):
|
||||||
tooltip = Hovertip(self.button, 'ToolTip text')
|
tooltip = Hovertip(self.button, 'ToolTip text')
|
||||||
self.addCleanup(tooltip.hidetip)
|
self.addCleanup(tooltip.hidetip)
|
||||||
self.assertFalse(tooltip.tipwindow and tooltip.tipwindow.winfo_viewable())
|
self.assertFalse(self.is_tipwindow_shown(tooltip))
|
||||||
tooltip.showtip()
|
tooltip.showtip()
|
||||||
self.assertTrue(tooltip.tipwindow and tooltip.tipwindow.winfo_viewable())
|
self.assertTrue(self.is_tipwindow_shown(tooltip))
|
||||||
|
|
||||||
def test_showtip_twice(self):
|
def test_showtip_twice(self):
|
||||||
tooltip = Hovertip(self.button, 'ToolTip text')
|
tooltip = Hovertip(self.button, 'ToolTip text')
|
||||||
self.addCleanup(tooltip.hidetip)
|
self.addCleanup(tooltip.hidetip)
|
||||||
self.assertFalse(tooltip.tipwindow and tooltip.tipwindow.winfo_viewable())
|
self.assertFalse(self.is_tipwindow_shown(tooltip))
|
||||||
tooltip.showtip()
|
tooltip.showtip()
|
||||||
self.assertTrue(tooltip.tipwindow and tooltip.tipwindow.winfo_viewable())
|
self.assertTrue(self.is_tipwindow_shown(tooltip))
|
||||||
orig_tipwindow = tooltip.tipwindow
|
orig_tipwindow = tooltip.tipwindow
|
||||||
tooltip.showtip()
|
tooltip.showtip()
|
||||||
self.assertTrue(tooltip.tipwindow and tooltip.tipwindow.winfo_viewable())
|
self.assertTrue(self.is_tipwindow_shown(tooltip))
|
||||||
self.assertIs(tooltip.tipwindow, orig_tipwindow)
|
self.assertIs(tooltip.tipwindow, orig_tipwindow)
|
||||||
|
|
||||||
def test_hidetip(self):
|
def test_hidetip(self):
|
||||||
|
@ -88,59 +95,67 @@ class HovertipTest(unittest.TestCase):
|
||||||
self.addCleanup(tooltip.hidetip)
|
self.addCleanup(tooltip.hidetip)
|
||||||
tooltip.showtip()
|
tooltip.showtip()
|
||||||
tooltip.hidetip()
|
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):
|
def test_showtip_on_mouse_enter_no_delay(self):
|
||||||
tooltip = Hovertip(self.button, 'ToolTip text', hover_delay=None)
|
tooltip = Hovertip(self.button, 'ToolTip text', hover_delay=None)
|
||||||
self.addCleanup(tooltip.hidetip)
|
self.addCleanup(tooltip.hidetip)
|
||||||
tooltip.showtip = add_call_counting(tooltip.showtip)
|
tooltip.showtip = add_call_counting(tooltip.showtip)
|
||||||
root_update()
|
root.update()
|
||||||
self.assertFalse(tooltip.tipwindow and tooltip.tipwindow.winfo_viewable())
|
self.assertFalse(self.is_tipwindow_shown(tooltip))
|
||||||
self.button.event_generate('<Enter>', x=0, y=0)
|
self.button.event_generate('<Enter>', x=0, y=0)
|
||||||
root_update()
|
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)
|
self.assertGreater(len(tooltip.showtip.call_args_list), 0)
|
||||||
|
|
||||||
def test_showtip_on_mouse_enter_hover_delay(self):
|
def test_hover_with_delay(self):
|
||||||
tooltip = Hovertip(self.button, 'ToolTip text', hover_delay=50)
|
# Run multiple tests requiring an actual delay simultaneously.
|
||||||
self.addCleanup(tooltip.hidetip)
|
|
||||||
tooltip.showtip = add_call_counting(tooltip.showtip)
|
# Test #1: A hover tip with a non-zero delay appears after the delay.
|
||||||
root_update()
|
tooltip1 = Hovertip(self.button, 'ToolTip text', hover_delay=100)
|
||||||
self.assertFalse(tooltip.tipwindow and tooltip.tipwindow.winfo_viewable())
|
self.addCleanup(tooltip1.hidetip)
|
||||||
|
tooltip1.showtip = add_call_counting(tooltip1.showtip)
|
||||||
|
root.update()
|
||||||
|
self.assertFalse(self.is_tipwindow_shown(tooltip1))
|
||||||
self.button.event_generate('<Enter>', x=0, y=0)
|
self.button.event_generate('<Enter>', x=0, y=0)
|
||||||
root_update()
|
root.update()
|
||||||
self.assertFalse(tooltip.tipwindow and tooltip.tipwindow.winfo_viewable())
|
self.assertFalse(self.is_tipwindow_shown(tooltip1))
|
||||||
time.sleep(0.1)
|
|
||||||
root_update()
|
# Test #2: A hover tip with a non-zero delay doesn't appear when
|
||||||
self.assertTrue(tooltip.tipwindow and tooltip.tipwindow.winfo_viewable())
|
# the mouse stops hovering over the base widget before the delay
|
||||||
self.assertGreater(len(tooltip.showtip.call_args_list), 0)
|
# expires.
|
||||||
|
tooltip2 = Hovertip(self.button, 'ToolTip text', hover_delay=100)
|
||||||
|
self.addCleanup(tooltip2.hidetip)
|
||||||
|
tooltip2.showtip = add_call_counting(tooltip2.showtip)
|
||||||
|
root.update()
|
||||||
|
self.button.event_generate('<Enter>', x=0, y=0)
|
||||||
|
root.update()
|
||||||
|
self.button.event_generate('<Leave>', x=0, y=0)
|
||||||
|
root.update()
|
||||||
|
|
||||||
|
time.sleep(0.15)
|
||||||
|
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):
|
def test_hidetip_on_mouse_leave(self):
|
||||||
tooltip = Hovertip(self.button, 'ToolTip text', hover_delay=None)
|
tooltip = Hovertip(self.button, 'ToolTip text', hover_delay=None)
|
||||||
self.addCleanup(tooltip.hidetip)
|
self.addCleanup(tooltip.hidetip)
|
||||||
tooltip.showtip = add_call_counting(tooltip.showtip)
|
tooltip.showtip = add_call_counting(tooltip.showtip)
|
||||||
root_update()
|
root.update()
|
||||||
self.button.event_generate('<Enter>', x=0, y=0)
|
self.button.event_generate('<Enter>', x=0, y=0)
|
||||||
root_update()
|
root.update()
|
||||||
self.button.event_generate('<Leave>', x=0, y=0)
|
self.button.event_generate('<Leave>', x=0, y=0)
|
||||||
root_update()
|
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)
|
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)
|
|
||||||
self.addCleanup(tooltip.hidetip)
|
|
||||||
tooltip.showtip = add_call_counting(tooltip.showtip)
|
|
||||||
root_update()
|
|
||||||
self.button.event_generate('<Enter>', x=0, y=0)
|
|
||||||
root_update()
|
|
||||||
self.button.event_generate('<Leave>', x=0, y=0)
|
|
||||||
root_update()
|
|
||||||
time.sleep(0.1)
|
|
||||||
root_update()
|
|
||||||
self.assertFalse(tooltip.tipwindow and tooltip.tipwindow.winfo_viewable())
|
|
||||||
self.assertEqual(tooltip.showtip.call_args_list, [])
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main(verbosity=2)
|
unittest.main(verbosity=2)
|
||||||
|
|
|
@ -75,7 +75,7 @@ class TooltipBase(object):
|
||||||
if tw:
|
if tw:
|
||||||
try:
|
try:
|
||||||
tw.destroy()
|
tw.destroy()
|
||||||
except TclError:
|
except TclError: # pragma: no cover
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@ -103,8 +103,8 @@ class OnHoverTooltipBase(TooltipBase):
|
||||||
def __del__(self):
|
def __del__(self):
|
||||||
try:
|
try:
|
||||||
self.anchor_widget.unbind("<Enter>", self._id1)
|
self.anchor_widget.unbind("<Enter>", self._id1)
|
||||||
self.anchor_widget.unbind("<Leave>", self._id2)
|
self.anchor_widget.unbind("<Leave>", self._id2) # pragma: no cover
|
||||||
self.anchor_widget.unbind("<Button>", self._id3)
|
self.anchor_widget.unbind("<Button>", self._id3) # pragma: no cover
|
||||||
except TclError:
|
except TclError:
|
||||||
pass
|
pass
|
||||||
super(OnHoverTooltipBase, self).__del__()
|
super(OnHoverTooltipBase, self).__del__()
|
||||||
|
@ -137,7 +137,7 @@ class OnHoverTooltipBase(TooltipBase):
|
||||||
"""hide the tooltip"""
|
"""hide the tooltip"""
|
||||||
try:
|
try:
|
||||||
self.unschedule()
|
self.unschedule()
|
||||||
except TclError:
|
except TclError: # pragma: no cover
|
||||||
pass
|
pass
|
||||||
super(OnHoverTooltipBase, self).hidetip()
|
super(OnHoverTooltipBase, self).hidetip()
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
To avoid occasional spurious test_idle failures on slower machines,
|
||||||
|
increase the ``hover_delay`` in test_tooltip.
|
Loading…
Reference in New Issue