1import functools 2import unittest 3import tkinter 4import enum 5from test import support 6from tkinter.test.support import AbstractTkTest, AbstractDefaultRootTest 7 8support.requires('gui') 9 10class MiscTest(AbstractTkTest, unittest.TestCase): 11 12 def test_all(self): 13 self.assertIn("Widget", tkinter.__all__) 14 # Check that variables from tkinter.constants are also in tkinter.__all__ 15 self.assertIn("CASCADE", tkinter.__all__) 16 self.assertIsNotNone(tkinter.CASCADE) 17 # Check that sys, re, and constants are not in tkinter.__all__ 18 self.assertNotIn("re", tkinter.__all__) 19 self.assertNotIn("sys", tkinter.__all__) 20 self.assertNotIn("constants", tkinter.__all__) 21 # Check that an underscored functions is not in tkinter.__all__ 22 self.assertNotIn("_tkerror", tkinter.__all__) 23 # Check that wantobjects is not in tkinter.__all__ 24 self.assertNotIn("wantobjects", tkinter.__all__) 25 26 def test_repr(self): 27 t = tkinter.Toplevel(self.root, name='top') 28 f = tkinter.Frame(t, name='child') 29 self.assertEqual(repr(f), '<tkinter.Frame object .top.child>') 30 31 def test_generated_names(self): 32 t = tkinter.Toplevel(self.root) 33 f = tkinter.Frame(t) 34 f2 = tkinter.Frame(t) 35 b = tkinter.Button(f2) 36 for name in str(b).split('.'): 37 self.assertFalse(name.isidentifier(), msg=repr(name)) 38 39 def test_tk_setPalette(self): 40 root = self.root 41 root.tk_setPalette('black') 42 self.assertEqual(root['background'], 'black') 43 root.tk_setPalette('white') 44 self.assertEqual(root['background'], 'white') 45 self.assertRaisesRegex(tkinter.TclError, 46 '^unknown color name "spam"$', 47 root.tk_setPalette, 'spam') 48 49 root.tk_setPalette(background='black') 50 self.assertEqual(root['background'], 'black') 51 root.tk_setPalette(background='blue', highlightColor='yellow') 52 self.assertEqual(root['background'], 'blue') 53 self.assertEqual(root['highlightcolor'], 'yellow') 54 root.tk_setPalette(background='yellow', highlightColor='blue') 55 self.assertEqual(root['background'], 'yellow') 56 self.assertEqual(root['highlightcolor'], 'blue') 57 self.assertRaisesRegex(tkinter.TclError, 58 '^unknown color name "spam"$', 59 root.tk_setPalette, background='spam') 60 self.assertRaisesRegex(tkinter.TclError, 61 '^must specify a background color$', 62 root.tk_setPalette, spam='white') 63 self.assertRaisesRegex(tkinter.TclError, 64 '^must specify a background color$', 65 root.tk_setPalette, highlightColor='blue') 66 67 def test_after(self): 68 root = self.root 69 70 def callback(start=0, step=1): 71 nonlocal count 72 count = start + step 73 74 # Without function, sleeps for ms. 75 self.assertIsNone(root.after(1)) 76 77 # Set up with callback with no args. 78 count = 0 79 timer1 = root.after(0, callback) 80 self.assertIn(timer1, root.tk.call('after', 'info')) 81 (script, _) = root.tk.splitlist(root.tk.call('after', 'info', timer1)) 82 root.update() # Process all pending events. 83 self.assertEqual(count, 1) 84 with self.assertRaises(tkinter.TclError): 85 root.tk.call(script) 86 87 # Set up with callback with args. 88 count = 0 89 timer1 = root.after(0, callback, 42, 11) 90 root.update() # Process all pending events. 91 self.assertEqual(count, 53) 92 93 # Cancel before called. 94 timer1 = root.after(1000, callback) 95 self.assertIn(timer1, root.tk.call('after', 'info')) 96 (script, _) = root.tk.splitlist(root.tk.call('after', 'info', timer1)) 97 root.after_cancel(timer1) # Cancel this event. 98 self.assertEqual(count, 53) 99 with self.assertRaises(tkinter.TclError): 100 root.tk.call(script) 101 102 # Call with a callable class 103 count = 0 104 timer1 = root.after(0, functools.partial(callback, 42, 11)) 105 root.update() # Process all pending events. 106 self.assertEqual(count, 53) 107 108 def test_after_idle(self): 109 root = self.root 110 111 def callback(start=0, step=1): 112 nonlocal count 113 count = start + step 114 115 # Set up with callback with no args. 116 count = 0 117 idle1 = root.after_idle(callback) 118 self.assertIn(idle1, root.tk.call('after', 'info')) 119 (script, _) = root.tk.splitlist(root.tk.call('after', 'info', idle1)) 120 root.update_idletasks() # Process all pending events. 121 self.assertEqual(count, 1) 122 with self.assertRaises(tkinter.TclError): 123 root.tk.call(script) 124 125 # Set up with callback with args. 126 count = 0 127 idle1 = root.after_idle(callback, 42, 11) 128 root.update_idletasks() # Process all pending events. 129 self.assertEqual(count, 53) 130 131 # Cancel before called. 132 idle1 = root.after_idle(callback) 133 self.assertIn(idle1, root.tk.call('after', 'info')) 134 (script, _) = root.tk.splitlist(root.tk.call('after', 'info', idle1)) 135 root.after_cancel(idle1) # Cancel this event. 136 self.assertEqual(count, 53) 137 with self.assertRaises(tkinter.TclError): 138 root.tk.call(script) 139 140 def test_after_cancel(self): 141 root = self.root 142 143 def callback(): 144 nonlocal count 145 count += 1 146 147 timer1 = root.after(5000, callback) 148 idle1 = root.after_idle(callback) 149 150 # No value for id raises a ValueError. 151 with self.assertRaises(ValueError): 152 root.after_cancel(None) 153 154 # Cancel timer event. 155 count = 0 156 (script, _) = root.tk.splitlist(root.tk.call('after', 'info', timer1)) 157 root.tk.call(script) 158 self.assertEqual(count, 1) 159 root.after_cancel(timer1) 160 with self.assertRaises(tkinter.TclError): 161 root.tk.call(script) 162 self.assertEqual(count, 1) 163 with self.assertRaises(tkinter.TclError): 164 root.tk.call('after', 'info', timer1) 165 166 # Cancel same event - nothing happens. 167 root.after_cancel(timer1) 168 169 # Cancel idle event. 170 count = 0 171 (script, _) = root.tk.splitlist(root.tk.call('after', 'info', idle1)) 172 root.tk.call(script) 173 self.assertEqual(count, 1) 174 root.after_cancel(idle1) 175 with self.assertRaises(tkinter.TclError): 176 root.tk.call(script) 177 self.assertEqual(count, 1) 178 with self.assertRaises(tkinter.TclError): 179 root.tk.call('after', 'info', idle1) 180 181 def test_clipboard(self): 182 root = self.root 183 root.clipboard_clear() 184 root.clipboard_append('Ùñî') 185 self.assertEqual(root.clipboard_get(), 'Ùñî') 186 root.clipboard_append('çōđě') 187 self.assertEqual(root.clipboard_get(), 'Ùñîçōđě') 188 root.clipboard_clear() 189 with self.assertRaises(tkinter.TclError): 190 root.clipboard_get() 191 192 def test_clipboard_astral(self): 193 root = self.root 194 root.clipboard_clear() 195 root.clipboard_append('') 196 self.assertEqual(root.clipboard_get(), '') 197 root.clipboard_append('') 198 self.assertEqual(root.clipboard_get(), '') 199 root.clipboard_clear() 200 with self.assertRaises(tkinter.TclError): 201 root.clipboard_get() 202 203 def test_winfo_rgb(self): 204 205 def assertApprox(col1, col2): 206 # A small amount of flexibility is required (bpo-45496) 207 # 33 is ~0.05% of 65535, which is a reasonable margin 208 for col1_channel, col2_channel in zip(col1, col2): 209 self.assertAlmostEqual(col1_channel, col2_channel, delta=33) 210 211 root = self.root 212 rgb = root.winfo_rgb 213 214 # Color name. 215 self.assertEqual(rgb('red'), (65535, 0, 0)) 216 self.assertEqual(rgb('dark slate blue'), (18504, 15677, 35723)) 217 # #RGB - extends each 4-bit hex value to be 16-bit. 218 self.assertEqual(rgb('#F0F'), (0xFFFF, 0x0000, 0xFFFF)) 219 # #RRGGBB - extends each 8-bit hex value to be 16-bit. 220 assertApprox(rgb('#4a3c8c'), (0x4a4a, 0x3c3c, 0x8c8c)) 221 # #RRRRGGGGBBBB 222 assertApprox(rgb('#dede14143939'), (0xdede, 0x1414, 0x3939)) 223 # Invalid string. 224 with self.assertRaises(tkinter.TclError): 225 rgb('#123456789a') 226 # RGB triplet is invalid input. 227 with self.assertRaises(tkinter.TclError): 228 rgb((111, 78, 55)) 229 230 def test_event_repr_defaults(self): 231 e = tkinter.Event() 232 e.serial = 12345 233 e.num = '??' 234 e.height = '??' 235 e.keycode = '??' 236 e.state = 0 237 e.time = 123456789 238 e.width = '??' 239 e.x = '??' 240 e.y = '??' 241 e.char = '' 242 e.keysym = '??' 243 e.keysym_num = '??' 244 e.type = '100' 245 e.widget = '??' 246 e.x_root = '??' 247 e.y_root = '??' 248 e.delta = 0 249 self.assertEqual(repr(e), '<100 event>') 250 251 def test_event_repr(self): 252 e = tkinter.Event() 253 e.serial = 12345 254 e.num = 3 255 e.focus = True 256 e.height = 200 257 e.keycode = 65 258 e.state = 0x30405 259 e.time = 123456789 260 e.width = 300 261 e.x = 10 262 e.y = 20 263 e.char = 'A' 264 e.send_event = True 265 e.keysym = 'Key-A' 266 e.keysym_num = ord('A') 267 e.type = tkinter.EventType.Configure 268 e.widget = '.text' 269 e.x_root = 1010 270 e.y_root = 1020 271 e.delta = -1 272 self.assertEqual(repr(e), 273 "<Configure event send_event=True" 274 " state=Shift|Control|Button3|0x30000" 275 " keysym=Key-A keycode=65 char='A'" 276 " num=3 delta=-1 focus=True" 277 " x=10 y=20 width=300 height=200>") 278 279 def test_eventtype_enum(self): 280 class CheckedEventType(enum.StrEnum): 281 KeyPress = '2' 282 Key = KeyPress 283 KeyRelease = '3' 284 ButtonPress = '4' 285 Button = ButtonPress 286 ButtonRelease = '5' 287 Motion = '6' 288 Enter = '7' 289 Leave = '8' 290 FocusIn = '9' 291 FocusOut = '10' 292 Keymap = '11' # undocumented 293 Expose = '12' 294 GraphicsExpose = '13' # undocumented 295 NoExpose = '14' # undocumented 296 Visibility = '15' 297 Create = '16' 298 Destroy = '17' 299 Unmap = '18' 300 Map = '19' 301 MapRequest = '20' 302 Reparent = '21' 303 Configure = '22' 304 ConfigureRequest = '23' 305 Gravity = '24' 306 ResizeRequest = '25' 307 Circulate = '26' 308 CirculateRequest = '27' 309 Property = '28' 310 SelectionClear = '29' # undocumented 311 SelectionRequest = '30' # undocumented 312 Selection = '31' # undocumented 313 Colormap = '32' 314 ClientMessage = '33' # undocumented 315 Mapping = '34' # undocumented 316 VirtualEvent = '35' # undocumented 317 Activate = '36' 318 Deactivate = '37' 319 MouseWheel = '38' 320 enum._test_simple_enum(CheckedEventType, tkinter.EventType) 321 322 def test_getboolean(self): 323 for v in 'true', 'yes', 'on', '1', 't', 'y', 1, True: 324 self.assertIs(self.root.getboolean(v), True) 325 for v in 'false', 'no', 'off', '0', 'f', 'n', 0, False: 326 self.assertIs(self.root.getboolean(v), False) 327 self.assertRaises(ValueError, self.root.getboolean, 'yea') 328 self.assertRaises(ValueError, self.root.getboolean, '') 329 self.assertRaises(TypeError, self.root.getboolean, None) 330 self.assertRaises(TypeError, self.root.getboolean, ()) 331 332 def test_mainloop(self): 333 log = [] 334 def callback(): 335 log.append(1) 336 self.root.after(100, self.root.quit) 337 self.root.after(100, callback) 338 self.root.mainloop(1) 339 self.assertEqual(log, []) 340 self.root.mainloop(0) 341 self.assertEqual(log, [1]) 342 self.assertTrue(self.root.winfo_exists()) 343 344 def test_info_patchlevel(self): 345 vi = self.root.info_patchlevel() 346 f = tkinter.Frame(self.root) 347 self.assertEqual(f.info_patchlevel(), vi) 348 # The following is almost a copy of tests for sys.version_info. 349 self.assertIsInstance(vi[:], tuple) 350 self.assertEqual(len(vi), 5) 351 self.assertIsInstance(vi[0], int) 352 self.assertIsInstance(vi[1], int) 353 self.assertIsInstance(vi[2], int) 354 self.assertIn(vi[3], ("alpha", "beta", "candidate", "final")) 355 self.assertIsInstance(vi[4], int) 356 self.assertIsInstance(vi.major, int) 357 self.assertIsInstance(vi.minor, int) 358 self.assertIsInstance(vi.micro, int) 359 self.assertIn(vi.releaselevel, ("alpha", "beta", "final")) 360 self.assertIsInstance(vi.serial, int) 361 self.assertEqual(vi[0], vi.major) 362 self.assertEqual(vi[1], vi.minor) 363 self.assertEqual(vi[2], vi.micro) 364 self.assertEqual(vi[3], vi.releaselevel) 365 self.assertEqual(vi[4], vi.serial) 366 self.assertTrue(vi > (1,0,0)) 367 if vi.releaselevel == 'final': 368 self.assertEqual(vi.serial, 0) 369 else: 370 self.assertEqual(vi.micro, 0) 371 self.assertTrue(str(vi).startswith(f'{vi.major}.{vi.minor}')) 372 373 374class DefaultRootTest(AbstractDefaultRootTest, unittest.TestCase): 375 376 def test_default_root(self): 377 self.assertIs(tkinter._support_default_root, True) 378 self.assertIsNone(tkinter._default_root) 379 root = tkinter.Tk() 380 root2 = tkinter.Tk() 381 root3 = tkinter.Tk() 382 self.assertIs(tkinter._default_root, root) 383 root2.destroy() 384 self.assertIs(tkinter._default_root, root) 385 root.destroy() 386 self.assertIsNone(tkinter._default_root) 387 root3.destroy() 388 self.assertIsNone(tkinter._default_root) 389 390 def test_no_default_root(self): 391 self.assertIs(tkinter._support_default_root, True) 392 self.assertIsNone(tkinter._default_root) 393 root = tkinter.Tk() 394 self.assertIs(tkinter._default_root, root) 395 tkinter.NoDefaultRoot() 396 self.assertIs(tkinter._support_default_root, False) 397 self.assertFalse(hasattr(tkinter, '_default_root')) 398 # repeated call is no-op 399 tkinter.NoDefaultRoot() 400 self.assertIs(tkinter._support_default_root, False) 401 self.assertFalse(hasattr(tkinter, '_default_root')) 402 root.destroy() 403 self.assertIs(tkinter._support_default_root, False) 404 self.assertFalse(hasattr(tkinter, '_default_root')) 405 root = tkinter.Tk() 406 self.assertIs(tkinter._support_default_root, False) 407 self.assertFalse(hasattr(tkinter, '_default_root')) 408 root.destroy() 409 410 def test_getboolean(self): 411 self.assertRaises(RuntimeError, tkinter.getboolean, '1') 412 root = tkinter.Tk() 413 self.assertIs(tkinter.getboolean('1'), True) 414 self.assertRaises(ValueError, tkinter.getboolean, 'yea') 415 root.destroy() 416 tkinter.NoDefaultRoot() 417 self.assertRaises(RuntimeError, tkinter.getboolean, '1') 418 419 def test_mainloop(self): 420 self.assertRaises(RuntimeError, tkinter.mainloop) 421 root = tkinter.Tk() 422 root.after_idle(root.quit) 423 tkinter.mainloop() 424 root.destroy() 425 tkinter.NoDefaultRoot() 426 self.assertRaises(RuntimeError, tkinter.mainloop) 427 428 429if __name__ == "__main__": 430 unittest.main() 431