Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions 3 Lib/idlelib/NEWS.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
103 changes: 59 additions & 44 deletions 103 Lib/idlelib/idle_test/test_tooltip.py
Original file line number Diff line number Diff line change
@@ -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 test.support import requires
requires('gui')
Expand All @@ -12,16 +19,13 @@ 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):
Expand Down Expand Up @@ -65,82 +69,93 @@ 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):
tooltip = Hovertip(self.button, 'ToolTip text')
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())
root.update()
self.assertFalse(self.is_tipwindow_shown(tooltip))
self.button.event_generate('<Enter>', x=0, y=0)
root_update()
self.assertTrue(tooltip.tipwindow and tooltip.tipwindow.winfo_viewable())
root.update()
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)
root_update()
self.assertFalse(tooltip.tipwindow and tooltip.tipwindow.winfo_viewable())
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=100)
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)
root_update()
self.assertFalse(tooltip.tipwindow and tooltip.tipwindow.winfo_viewable())
time.sleep(0.1)
root_update()
self.assertTrue(tooltip.tipwindow and tooltip.tipwindow.winfo_viewable())
self.assertGreater(len(tooltip.showtip.call_args_list), 0)
root.update()
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=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):
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('<Enter>', x=0, y=0)
root_update()
root.update()
self.button.event_generate('<Leave>', x=0, y=0)
root_update()
self.assertFalse(tooltip.tipwindow and tooltip.tipwindow.winfo_viewable())
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)
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__':
unittest.main(verbosity=2)
8 changes: 4 additions & 4 deletions 8 Lib/idlelib/tooltip.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ def hidetip(self):
if tw:
try:
tw.destroy()
except TclError:
except TclError: # pragma: no cover
pass


Expand Down Expand Up @@ -103,8 +103,8 @@ def __init__(self, anchor_widget, hover_delay=1000):
def __del__(self):
try:
self.anchor_widget.unbind("<Enter>", self._id1)
self.anchor_widget.unbind("<Leave>", self._id2)
self.anchor_widget.unbind("<Button>", self._id3)
self.anchor_widget.unbind("<Leave>", self._id2) # pragma: no cover
self.anchor_widget.unbind("<Button>", self._id3) # pragma: no cover
except TclError:
pass
super(OnHoverTooltipBase, self).__del__()
Expand Down Expand Up @@ -137,7 +137,7 @@ def hidetip(self):
"""hide the tooltip"""
try:
self.unschedule()
except TclError:
except TclError: # pragma: no cover
pass
super(OnHoverTooltipBase, self).hidetip()

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
To avoid occasional spurious test_idle failures on slower machines,
increase the ``hover_delay`` in test_tooltip.
Morty Proxy This is a proxified and sanitized view of the page, visit original site.