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