Coverage for idle_test/test_tooltip.py: 2%

114 statements  

« prev     ^ index     » next       coverage.py v7.2.5, created at 2023-05-11 13:22 -0700

1"""Test tooltip, coverage 100%. 

2 

3Coverage is 100% after excluding 6 lines with "# pragma: no cover". 

4They involve TclErrors that either should or should not happen in a 

5particular situation, and which are 'pass'ed if they do. 

6""" 

7 

8from idlelib.tooltip import TooltipBase, Hovertip 

9from test.support import requires 

10requires('gui') 

11 

12from functools import wraps 

13import time 

14from tkinter import Button, Tk, Toplevel 

15import unittest 

16 

17 

18def setUpModule(): 

19 global root 

20 root = Tk() 

21 

22def tearDownModule(): 

23 global root 

24 root.update_idletasks() 

25 root.destroy() 

26 del root 

27 

28 

29def add_call_counting(func): 

30 @wraps(func) 

31 def wrapped_func(*args, **kwargs): 

32 wrapped_func.call_args_list.append((args, kwargs)) 

33 return func(*args, **kwargs) 

34 wrapped_func.call_args_list = [] 

35 return wrapped_func 

36 

37 

38def _make_top_and_button(testobj): 

39 global root 

40 top = Toplevel(root) 

41 testobj.addCleanup(top.destroy) 

42 top.title("Test tooltip") 

43 button = Button(top, text='ToolTip test button') 

44 button.pack() 

45 testobj.addCleanup(button.destroy) 

46 top.lift() 

47 return top, button 

48 

49 

50class ToolTipBaseTest(unittest.TestCase): 

51 def setUp(self): 

52 self.top, self.button = _make_top_and_button(self) 

53 

54 def test_base_class_is_unusable(self): 

55 global root 

56 top = Toplevel(root) 

57 self.addCleanup(top.destroy) 

58 

59 button = Button(top, text='ToolTip test button') 

60 button.pack() 

61 self.addCleanup(button.destroy) 

62 

63 with self.assertRaises(NotImplementedError): 

64 tooltip = TooltipBase(button) 

65 tooltip.showtip() 

66 

67 

68class HovertipTest(unittest.TestCase): 

69 def setUp(self): 

70 self.top, self.button = _make_top_and_button(self) 

71 

72 def is_tipwindow_shown(self, tooltip): 

73 return tooltip.tipwindow and tooltip.tipwindow.winfo_viewable() 

74 

75 def test_showtip(self): 

76 tooltip = Hovertip(self.button, 'ToolTip text') 

77 self.addCleanup(tooltip.hidetip) 

78 self.assertFalse(self.is_tipwindow_shown(tooltip)) 

79 tooltip.showtip() 

80 self.assertTrue(self.is_tipwindow_shown(tooltip)) 

81 

82 def test_showtip_twice(self): 

83 tooltip = Hovertip(self.button, 'ToolTip text') 

84 self.addCleanup(tooltip.hidetip) 

85 self.assertFalse(self.is_tipwindow_shown(tooltip)) 

86 tooltip.showtip() 

87 self.assertTrue(self.is_tipwindow_shown(tooltip)) 

88 orig_tipwindow = tooltip.tipwindow 

89 tooltip.showtip() 

90 self.assertTrue(self.is_tipwindow_shown(tooltip)) 

91 self.assertIs(tooltip.tipwindow, orig_tipwindow) 

92 

93 def test_hidetip(self): 

94 tooltip = Hovertip(self.button, 'ToolTip text') 

95 self.addCleanup(tooltip.hidetip) 

96 tooltip.showtip() 

97 tooltip.hidetip() 

98 self.assertFalse(self.is_tipwindow_shown(tooltip)) 

99 

100 def test_showtip_on_mouse_enter_no_delay(self): 

101 tooltip = Hovertip(self.button, 'ToolTip text', hover_delay=None) 

102 self.addCleanup(tooltip.hidetip) 

103 tooltip.showtip = add_call_counting(tooltip.showtip) 

104 root.update() 

105 self.assertFalse(self.is_tipwindow_shown(tooltip)) 

106 self.button.event_generate('<Enter>', x=0, y=0) 

107 root.update() 

108 self.assertTrue(self.is_tipwindow_shown(tooltip)) 

109 self.assertGreater(len(tooltip.showtip.call_args_list), 0) 

110 

111 def test_hover_with_delay(self): 

112 # Run multiple tests requiring an actual delay simultaneously. 

113 

114 # Test #1: A hover tip with a non-zero delay appears after the delay. 

115 tooltip1 = Hovertip(self.button, 'ToolTip text', hover_delay=100) 

116 self.addCleanup(tooltip1.hidetip) 

117 tooltip1.showtip = add_call_counting(tooltip1.showtip) 

118 root.update() 

119 self.assertFalse(self.is_tipwindow_shown(tooltip1)) 

120 self.button.event_generate('<Enter>', x=0, y=0) 

121 root.update() 

122 self.assertFalse(self.is_tipwindow_shown(tooltip1)) 

123 

124 # Test #2: A hover tip with a non-zero delay doesn't appear when 

125 # the mouse stops hovering over the base widget before the delay 

126 # expires. 

127 tooltip2 = Hovertip(self.button, 'ToolTip text', hover_delay=100) 

128 self.addCleanup(tooltip2.hidetip) 

129 tooltip2.showtip = add_call_counting(tooltip2.showtip) 

130 root.update() 

131 self.button.event_generate('<Enter>', x=0, y=0) 

132 root.update() 

133 self.button.event_generate('<Leave>', x=0, y=0) 

134 root.update() 

135 

136 time.sleep(0.15) 

137 root.update() 

138 

139 # Test #1 assertions. 

140 self.assertTrue(self.is_tipwindow_shown(tooltip1)) 

141 self.assertGreater(len(tooltip1.showtip.call_args_list), 0) 

142 

143 # Test #2 assertions. 

144 self.assertFalse(self.is_tipwindow_shown(tooltip2)) 

145 self.assertEqual(tooltip2.showtip.call_args_list, []) 

146 

147 def test_hidetip_on_mouse_leave(self): 

148 tooltip = Hovertip(self.button, 'ToolTip text', hover_delay=None) 

149 self.addCleanup(tooltip.hidetip) 

150 tooltip.showtip = add_call_counting(tooltip.showtip) 

151 root.update() 

152 self.button.event_generate('<Enter>', x=0, y=0) 

153 root.update() 

154 self.button.event_generate('<Leave>', x=0, y=0) 

155 root.update() 

156 self.assertFalse(self.is_tipwindow_shown(tooltip)) 

157 self.assertGreater(len(tooltip.showtip.call_args_list), 0) 

158 

159 

160if __name__ == '__main__': 

161 unittest.main(verbosity=2)