python (3.11.7)
1 # Common tests for test_tkinter/test_widgets.py and test_ttk/test_widgets.py
2
3 import unittest
4 import tkinter
5 from tkinter.test.support import (AbstractTkTest, tk_version,
6 pixels_conv, tcl_obj_eq)
7 import test.support
8
9
10 _sentinel = object()
11
12 class ESC[4;38;5;81mAbstractWidgetTest(ESC[4;38;5;149mAbstractTkTest):
13 _conv_pixels = round
14 _conv_pad_pixels = None
15 _stringify = False
16
17 @property
18 def scaling(self):
19 try:
20 return self._scaling
21 except AttributeError:
22 self._scaling = float(self.root.call('tk', 'scaling'))
23 return self._scaling
24
25 def _str(self, value):
26 if not self._stringify and self.wantobjects and tk_version >= (8, 6):
27 return value
28 if isinstance(value, tuple):
29 return ' '.join(map(self._str, value))
30 return str(value)
31
32 def assertEqual2(self, actual, expected, msg=None, eq=object.__eq__):
33 if eq(actual, expected):
34 return
35 self.assertEqual(actual, expected, msg)
36
37 def checkParam(self, widget, name, value, *, expected=_sentinel,
38 conv=False, eq=None):
39 widget[name] = value
40 if expected is _sentinel:
41 expected = value
42 if conv:
43 expected = conv(expected)
44 if self._stringify or not self.wantobjects:
45 if isinstance(expected, tuple):
46 expected = tkinter._join(expected)
47 else:
48 expected = str(expected)
49 if eq is None:
50 eq = tcl_obj_eq
51 self.assertEqual2(widget[name], expected, eq=eq)
52 self.assertEqual2(widget.cget(name), expected, eq=eq)
53 t = widget.configure(name)
54 self.assertEqual(len(t), 5)
55 self.assertEqual2(t[4], expected, eq=eq)
56
57 def checkInvalidParam(self, widget, name, value, errmsg=None):
58 orig = widget[name]
59 if errmsg is not None:
60 errmsg = errmsg.format(value)
61 with self.assertRaises(tkinter.TclError) as cm:
62 widget[name] = value
63 if errmsg is not None:
64 self.assertEqual(str(cm.exception), errmsg)
65 self.assertEqual(widget[name], orig)
66 with self.assertRaises(tkinter.TclError) as cm:
67 widget.configure({name: value})
68 if errmsg is not None:
69 self.assertEqual(str(cm.exception), errmsg)
70 self.assertEqual(widget[name], orig)
71
72 def checkParams(self, widget, name, *values, **kwargs):
73 for value in values:
74 self.checkParam(widget, name, value, **kwargs)
75
76 def checkIntegerParam(self, widget, name, *values, **kwargs):
77 self.checkParams(widget, name, *values, **kwargs)
78 self.checkInvalidParam(widget, name, '',
79 errmsg='expected integer but got ""')
80 self.checkInvalidParam(widget, name, '10p',
81 errmsg='expected integer but got "10p"')
82 self.checkInvalidParam(widget, name, 3.2,
83 errmsg='expected integer but got "3.2"')
84
85 def checkFloatParam(self, widget, name, *values, conv=float, **kwargs):
86 for value in values:
87 self.checkParam(widget, name, value, conv=conv, **kwargs)
88 self.checkInvalidParam(widget, name, '',
89 errmsg='expected floating-point number but got ""')
90 self.checkInvalidParam(widget, name, 'spam',
91 errmsg='expected floating-point number but got "spam"')
92
93 def checkBooleanParam(self, widget, name):
94 for value in (False, 0, 'false', 'no', 'off'):
95 self.checkParam(widget, name, value, expected=0)
96 for value in (True, 1, 'true', 'yes', 'on'):
97 self.checkParam(widget, name, value, expected=1)
98 self.checkInvalidParam(widget, name, '',
99 errmsg='expected boolean value but got ""')
100 self.checkInvalidParam(widget, name, 'spam',
101 errmsg='expected boolean value but got "spam"')
102
103 def checkColorParam(self, widget, name, *, allow_empty=None, **kwargs):
104 self.checkParams(widget, name,
105 '#ff0000', '#00ff00', '#0000ff', '#123456',
106 'red', 'green', 'blue', 'white', 'black', 'grey',
107 **kwargs)
108 self.checkInvalidParam(widget, name, 'spam',
109 errmsg='unknown color name "spam"')
110
111 def checkCursorParam(self, widget, name, **kwargs):
112 self.checkParams(widget, name, 'arrow', 'watch', 'cross', '',**kwargs)
113 self.checkParam(widget, name, 'none')
114 self.checkInvalidParam(widget, name, 'spam',
115 errmsg='bad cursor spec "spam"')
116
117 def checkCommandParam(self, widget, name):
118 def command(*args):
119 pass
120 widget[name] = command
121 self.assertTrue(widget[name])
122 self.checkParams(widget, name, '')
123
124 def checkEnumParam(self, widget, name, *values, errmsg=None, **kwargs):
125 self.checkParams(widget, name, *values, **kwargs)
126 if errmsg is None:
127 errmsg2 = ' %s "{}": must be %s%s or %s' % (
128 name,
129 ', '.join(values[:-1]),
130 ',' if len(values) > 2 else '',
131 values[-1])
132 self.checkInvalidParam(widget, name, '',
133 errmsg='ambiguous' + errmsg2)
134 errmsg = 'bad' + errmsg2
135 self.checkInvalidParam(widget, name, 'spam', errmsg=errmsg)
136
137 def checkPixelsParam(self, widget, name, *values,
138 conv=None, **kwargs):
139 if conv is None:
140 conv = self._conv_pixels
141 for value in values:
142 expected = _sentinel
143 conv1 = conv
144 if isinstance(value, str):
145 if conv1 and conv1 is not str:
146 expected = pixels_conv(value) * self.scaling
147 conv1 = round
148 self.checkParam(widget, name, value, expected=expected,
149 conv=conv1, **kwargs)
150 self.checkInvalidParam(widget, name, '6x',
151 errmsg='bad screen distance "6x"')
152 self.checkInvalidParam(widget, name, 'spam',
153 errmsg='bad screen distance "spam"')
154
155 def checkReliefParam(self, widget, name):
156 self.checkParams(widget, name,
157 'flat', 'groove', 'raised', 'ridge', 'solid', 'sunken')
158 errmsg='bad relief "spam": must be '\
159 'flat, groove, raised, ridge, solid, or sunken'
160 if tk_version < (8, 6):
161 errmsg = None
162 self.checkInvalidParam(widget, name, 'spam',
163 errmsg=errmsg)
164
165 def checkImageParam(self, widget, name):
166 image = tkinter.PhotoImage(master=self.root, name='image1')
167 self.checkParam(widget, name, image, conv=str)
168 self.checkInvalidParam(widget, name, 'spam',
169 errmsg='image "spam" doesn\'t exist')
170 widget[name] = ''
171
172 def checkVariableParam(self, widget, name, var):
173 self.checkParam(widget, name, var, conv=str)
174
175 def assertIsBoundingBox(self, bbox):
176 self.assertIsNotNone(bbox)
177 self.assertIsInstance(bbox, tuple)
178 if len(bbox) != 4:
179 self.fail('Invalid bounding box: %r' % (bbox,))
180 for item in bbox:
181 if not isinstance(item, int):
182 self.fail('Invalid bounding box: %r' % (bbox,))
183 break
184
185
186 def test_keys(self):
187 widget = self.create()
188 keys = widget.keys()
189 self.assertEqual(sorted(keys), sorted(widget.configure()))
190 for k in keys:
191 widget[k]
192 # Test if OPTIONS contains all keys
193 if test.support.verbose:
194 aliases = {
195 'bd': 'borderwidth',
196 'bg': 'background',
197 'fg': 'foreground',
198 'invcmd': 'invalidcommand',
199 'vcmd': 'validatecommand',
200 }
201 keys = set(keys)
202 expected = set(self.OPTIONS)
203 for k in sorted(keys - expected):
204 if not (k in aliases and
205 aliases[k] in keys and
206 aliases[k] in expected):
207 print('%s.OPTIONS doesn\'t contain "%s"' %
208 (self.__class__.__name__, k))
209
210
211 class ESC[4;38;5;81mStandardOptionsTests:
212 STANDARD_OPTIONS = (
213 'activebackground', 'activeborderwidth', 'activeforeground', 'anchor',
214 'background', 'bitmap', 'borderwidth', 'compound', 'cursor',
215 'disabledforeground', 'exportselection', 'font', 'foreground',
216 'highlightbackground', 'highlightcolor', 'highlightthickness',
217 'image', 'insertbackground', 'insertborderwidth',
218 'insertofftime', 'insertontime', 'insertwidth',
219 'jump', 'justify', 'orient', 'padx', 'pady', 'relief',
220 'repeatdelay', 'repeatinterval',
221 'selectbackground', 'selectborderwidth', 'selectforeground',
222 'setgrid', 'takefocus', 'text', 'textvariable', 'troughcolor',
223 'underline', 'wraplength', 'xscrollcommand', 'yscrollcommand',
224 )
225
226 def test_configure_activebackground(self):
227 widget = self.create()
228 self.checkColorParam(widget, 'activebackground')
229
230 def test_configure_activeborderwidth(self):
231 widget = self.create()
232 self.checkPixelsParam(widget, 'activeborderwidth',
233 0, 1.3, 2.9, 6, -2, '10p')
234
235 def test_configure_activeforeground(self):
236 widget = self.create()
237 self.checkColorParam(widget, 'activeforeground')
238
239 def test_configure_anchor(self):
240 widget = self.create()
241 self.checkEnumParam(widget, 'anchor',
242 'n', 'ne', 'e', 'se', 's', 'sw', 'w', 'nw', 'center')
243
244 def test_configure_background(self):
245 widget = self.create()
246 self.checkColorParam(widget, 'background')
247 if 'bg' in self.OPTIONS:
248 self.checkColorParam(widget, 'bg')
249
250 def test_configure_bitmap(self):
251 widget = self.create()
252 self.checkParam(widget, 'bitmap', 'questhead')
253 self.checkParam(widget, 'bitmap', 'gray50')
254 filename = test.support.findfile('python.xbm', subdir='imghdrdata')
255 self.checkParam(widget, 'bitmap', '@' + filename)
256 # Cocoa Tk widgets don't detect invalid -bitmap values
257 # See https://core.tcl.tk/tk/info/31cd33dbf0
258 if not ('aqua' in self.root.tk.call('tk', 'windowingsystem') and
259 'AppKit' in self.root.winfo_server()):
260 self.checkInvalidParam(widget, 'bitmap', 'spam',
261 errmsg='bitmap "spam" not defined')
262
263 def test_configure_borderwidth(self):
264 widget = self.create()
265 self.checkPixelsParam(widget, 'borderwidth',
266 0, 1.3, 2.6, 6, -2, '10p')
267 if 'bd' in self.OPTIONS:
268 self.checkPixelsParam(widget, 'bd', 0, 1.3, 2.6, 6, -2, '10p')
269
270 def test_configure_compound(self):
271 widget = self.create()
272 self.checkEnumParam(widget, 'compound',
273 'bottom', 'center', 'left', 'none', 'right', 'top')
274
275 def test_configure_cursor(self):
276 widget = self.create()
277 self.checkCursorParam(widget, 'cursor')
278
279 def test_configure_disabledforeground(self):
280 widget = self.create()
281 self.checkColorParam(widget, 'disabledforeground')
282
283 def test_configure_exportselection(self):
284 widget = self.create()
285 self.checkBooleanParam(widget, 'exportselection')
286
287 def test_configure_font(self):
288 widget = self.create()
289 self.checkParam(widget, 'font',
290 '-Adobe-Helvetica-Medium-R-Normal--*-120-*-*-*-*-*-*')
291 self.checkInvalidParam(widget, 'font', '',
292 errmsg='font "" doesn\'t exist')
293
294 def test_configure_foreground(self):
295 widget = self.create()
296 self.checkColorParam(widget, 'foreground')
297 if 'fg' in self.OPTIONS:
298 self.checkColorParam(widget, 'fg')
299
300 def test_configure_highlightbackground(self):
301 widget = self.create()
302 self.checkColorParam(widget, 'highlightbackground')
303
304 def test_configure_highlightcolor(self):
305 widget = self.create()
306 self.checkColorParam(widget, 'highlightcolor')
307
308 def test_configure_highlightthickness(self):
309 widget = self.create()
310 self.checkPixelsParam(widget, 'highlightthickness',
311 0, 1.3, 2.6, 6, '10p')
312 self.checkParam(widget, 'highlightthickness', -2, expected=0,
313 conv=self._conv_pixels)
314
315 def test_configure_image(self):
316 widget = self.create()
317 self.checkImageParam(widget, 'image')
318
319 def test_configure_insertbackground(self):
320 widget = self.create()
321 self.checkColorParam(widget, 'insertbackground')
322
323 def test_configure_insertborderwidth(self):
324 widget = self.create()
325 self.checkPixelsParam(widget, 'insertborderwidth',
326 0, 1.3, 2.6, 6, -2, '10p')
327
328 def test_configure_insertofftime(self):
329 widget = self.create()
330 self.checkIntegerParam(widget, 'insertofftime', 100)
331
332 def test_configure_insertontime(self):
333 widget = self.create()
334 self.checkIntegerParam(widget, 'insertontime', 100)
335
336 def test_configure_insertwidth(self):
337 widget = self.create()
338 self.checkPixelsParam(widget, 'insertwidth', 1.3, 2.6, -2, '10p')
339
340 def test_configure_jump(self):
341 widget = self.create()
342 self.checkBooleanParam(widget, 'jump')
343
344 def test_configure_justify(self):
345 widget = self.create()
346 self.checkEnumParam(widget, 'justify', 'left', 'right', 'center',
347 errmsg='bad justification "{}": must be '
348 'left, right, or center')
349 self.checkInvalidParam(widget, 'justify', '',
350 errmsg='ambiguous justification "": must be '
351 'left, right, or center')
352
353 def test_configure_orient(self):
354 widget = self.create()
355 self.assertEqual(str(widget['orient']), self.default_orient)
356 self.checkEnumParam(widget, 'orient', 'horizontal', 'vertical')
357
358 def test_configure_padx(self):
359 widget = self.create()
360 self.checkPixelsParam(widget, 'padx', 3, 4.4, 5.6, -2, '12m',
361 conv=self._conv_pad_pixels)
362
363 def test_configure_pady(self):
364 widget = self.create()
365 self.checkPixelsParam(widget, 'pady', 3, 4.4, 5.6, -2, '12m',
366 conv=self._conv_pad_pixels)
367
368 def test_configure_relief(self):
369 widget = self.create()
370 self.checkReliefParam(widget, 'relief')
371
372 def test_configure_repeatdelay(self):
373 widget = self.create()
374 self.checkIntegerParam(widget, 'repeatdelay', -500, 500)
375
376 def test_configure_repeatinterval(self):
377 widget = self.create()
378 self.checkIntegerParam(widget, 'repeatinterval', -500, 500)
379
380 def test_configure_selectbackground(self):
381 widget = self.create()
382 self.checkColorParam(widget, 'selectbackground')
383
384 def test_configure_selectborderwidth(self):
385 widget = self.create()
386 self.checkPixelsParam(widget, 'selectborderwidth', 1.3, 2.6, -2, '10p')
387
388 def test_configure_selectforeground(self):
389 widget = self.create()
390 self.checkColorParam(widget, 'selectforeground')
391
392 def test_configure_setgrid(self):
393 widget = self.create()
394 self.checkBooleanParam(widget, 'setgrid')
395
396 def test_configure_state(self):
397 widget = self.create()
398 self.checkEnumParam(widget, 'state', 'active', 'disabled', 'normal')
399
400 def test_configure_takefocus(self):
401 widget = self.create()
402 self.checkParams(widget, 'takefocus', '0', '1', '')
403
404 def test_configure_text(self):
405 widget = self.create()
406 self.checkParams(widget, 'text', '', 'any string')
407
408 def test_configure_textvariable(self):
409 widget = self.create()
410 var = tkinter.StringVar(self.root)
411 self.checkVariableParam(widget, 'textvariable', var)
412
413 def test_configure_troughcolor(self):
414 widget = self.create()
415 self.checkColorParam(widget, 'troughcolor')
416
417 def test_configure_underline(self):
418 widget = self.create()
419 self.checkIntegerParam(widget, 'underline', 0, 1, 10)
420
421 def test_configure_wraplength(self):
422 widget = self.create()
423 self.checkPixelsParam(widget, 'wraplength', 100)
424
425 def test_configure_xscrollcommand(self):
426 widget = self.create()
427 self.checkCommandParam(widget, 'xscrollcommand')
428
429 def test_configure_yscrollcommand(self):
430 widget = self.create()
431 self.checkCommandParam(widget, 'yscrollcommand')
432
433 # non-standard but common options
434
435 def test_configure_command(self):
436 widget = self.create()
437 self.checkCommandParam(widget, 'command')
438
439 def test_configure_indicatoron(self):
440 widget = self.create()
441 self.checkBooleanParam(widget, 'indicatoron')
442
443 def test_configure_offrelief(self):
444 widget = self.create()
445 self.checkReliefParam(widget, 'offrelief')
446
447 def test_configure_overrelief(self):
448 widget = self.create()
449 self.checkReliefParam(widget, 'overrelief')
450
451 def test_configure_selectcolor(self):
452 widget = self.create()
453 self.checkColorParam(widget, 'selectcolor')
454
455 def test_configure_selectimage(self):
456 widget = self.create()
457 self.checkImageParam(widget, 'selectimage')
458
459 def test_configure_tristateimage(self):
460 widget = self.create()
461 self.checkImageParam(widget, 'tristateimage')
462
463 def test_configure_tristatevalue(self):
464 widget = self.create()
465 self.checkParam(widget, 'tristatevalue', 'unknowable')
466
467 def test_configure_variable(self):
468 widget = self.create()
469 var = tkinter.DoubleVar(self.root)
470 self.checkVariableParam(widget, 'variable', var)
471
472
473 class ESC[4;38;5;81mIntegerSizeTests:
474 def test_configure_height(self):
475 widget = self.create()
476 self.checkIntegerParam(widget, 'height', 100, -100, 0)
477
478 def test_configure_width(self):
479 widget = self.create()
480 self.checkIntegerParam(widget, 'width', 402, -402, 0)
481
482
483 class ESC[4;38;5;81mPixelSizeTests:
484 def test_configure_height(self):
485 widget = self.create()
486 self.checkPixelsParam(widget, 'height', 100, 101.2, 102.6, -100, 0, '3c')
487
488 def test_configure_width(self):
489 widget = self.create()
490 self.checkPixelsParam(widget, 'width', 402, 403.4, 404.6, -402, 0, '5i')
491
492
493 def add_standard_options(*source_classes):
494 # This decorator adds test_configure_xxx methods from source classes for
495 # every xxx option in the OPTIONS class attribute if they are not defined
496 # explicitly.
497 def decorator(cls):
498 for option in cls.OPTIONS:
499 methodname = 'test_configure_' + option
500 if not hasattr(cls, methodname):
501 for source_class in source_classes:
502 if hasattr(source_class, methodname):
503 setattr(cls, methodname,
504 getattr(source_class, methodname))
505 break
506 else:
507 def test(self, option=option):
508 widget = self.create()
509 widget[option]
510 raise AssertionError('Option "%s" is not tested in %s' %
511 (option, cls.__name__))
512 test.__name__ = methodname
513 setattr(cls, methodname, test)
514 return cls
515 return decorator
516
517 def setUpModule():
518 if test.support.verbose:
519 tcl = tkinter.Tcl()
520 print('patchlevel =', tcl.call('info', 'patchlevel'), flush=True)