| # Common tests for test_tkinter/test_widgets.py and test_ttk/test_widgets.py | |
| import re | |
| import tkinter | |
| from test.test_tkinter.support import (AbstractTkTest, requires_tk, tk_version, | |
| pixels_conv, tcl_obj_eq) | |
| import test.support | |
| _sentinel = object() | |
| # Abbreviated option names accepted by Tk in addition to the full names. | |
| _OPTION_ALIASES = { | |
| 'bd': 'borderwidth', | |
| 'bg': 'background', | |
| 'bgimg': 'backgroundimage', | |
| 'fg': 'foreground', | |
| 'invcmd': 'invalidcommand', | |
| 'vcmd': 'validatecommand', | |
| } | |
| # Options which accept all values allowed by Tk_GetPixels | |
| # borderwidth = bd | |
| class AbstractWidgetTest(AbstractTkTest): | |
| _default_pixels = '' if tk_version >= (9, 0) else -1 # Value for unset pixel options. | |
| _rounds_pixels = (tk_version < (9, 0)) # True if some pixel options are rounded. | |
| _no_round = set() # Pixel options which are not rounded nonetheless | |
| _stringify = False # Whether to convert tuples to strings | |
| _allow_empty_justify = False | |
| _clipped_to_default = set() | |
| @property | |
| def scaling(self): | |
| try: | |
| return self._scaling | |
| except AttributeError: | |
| self._scaling = self.root.tk_scaling() | |
| return self._scaling | |
| def _str(self, value): | |
| if not self._stringify and self.wantobjects and tk_version >= (8, 6): | |
| return value | |
| if isinstance(value, tuple): | |
| return ' '.join(map(self._str, value)) | |
| return str(value) | |
| def assertEqual2(self, actual, expected, msg=None, eq=object.__eq__): | |
| if eq(actual, expected): | |
| return | |
| self.assertEqual(actual, expected, msg) | |
| def checkParam(self, widget, name, value, *, expected=_sentinel, | |
| conv=False, eq=None): | |
| widget[name] = value | |
| if expected is _sentinel: | |
| expected = value | |
| if name in self._clipped: | |
| if not isinstance(expected, str) and expected < 0: | |
| if tk_version >= (8, 7) and name in self._clipped_to_default: | |
| expected = self._default_pixels | |
| else: | |
| expected = 0 | |
| if conv: | |
| expected = conv(expected) | |
| if self._stringify or not self.wantobjects: | |
| if isinstance(expected, tuple): | |
| expected = tkinter._join(expected) | |
| else: | |
| expected = str(expected) | |
| elif type(expected) is int: | |
| self.assertIsInstance(widget[name], int) | |
| elif type(expected) is float: | |
| # An integral value may be returned as an int. | |
| self.assertIsInstance(widget[name], (int, float)) | |
| if eq is None: | |
| eq = tcl_obj_eq | |
| self.assertEqual2(widget[name], expected, eq=eq) | |
| self.assertEqual2(widget.cget(name), expected, eq=eq) | |
| t = widget.configure(name) | |
| self.assertEqual(len(t), 5) | |
| self.assertEqual2(t[4], expected, eq=eq) | |
| def checkInvalidParam(self, widget, name, value, errmsg=None): | |
| orig = widget[name] | |
| if errmsg is not None: | |
| errmsg = errmsg.format(re.escape(str(value))) | |
| errmsg = fr'\A{errmsg}\z' | |
| with self.assertRaisesRegex(tkinter.TclError, errmsg or ''): | |
| widget[name] = value | |
| self.assertEqual(widget[name], orig) | |
| with self.assertRaisesRegex(tkinter.TclError, errmsg or ''): | |
| widget.configure({name: value}) | |
| self.assertEqual(widget[name], orig) | |
| def checkParams(self, widget, name, *values, **kwargs): | |
| for value in values: | |
| self.checkParam(widget, name, value, **kwargs) | |
| def checkIntegerParam(self, widget, name, *values, **kwargs): | |
| self.checkParams(widget, name, *values, **kwargs) | |
| errmsg = 'expected integer but got "{}"' | |
| self.checkInvalidParam(widget, name, '', errmsg=errmsg) | |
| self.checkInvalidParam(widget, name, '10p', errmsg=errmsg) | |
| self.checkInvalidParam(widget, name, 3.2, errmsg=errmsg) | |
| def checkFloatParam(self, widget, name, *values, conv=float, **kwargs): | |
| for value in values: | |
| self.checkParam(widget, name, value, conv=conv, **kwargs) | |
| errmsg = 'expected floating-point number but got "{}"' | |
| self.checkInvalidParam(widget, name, '', errmsg=errmsg) | |
| self.checkInvalidParam(widget, name, 'spam', errmsg=errmsg) | |
| def checkBooleanParam(self, widget, name): | |
| for value in (False, 0, 'false', 'no', 'off'): | |
| self.checkParam(widget, name, value, expected=0) | |
| for value in (True, 1, 'true', 'yes', 'on'): | |
| self.checkParam(widget, name, value, expected=1) | |
| errmsg = 'expected boolean value but got "{}"' | |
| self.checkInvalidParam(widget, name, '', errmsg=errmsg) | |
| self.checkInvalidParam(widget, name, 'spam', errmsg=errmsg) | |
| def checkColorParam(self, widget, name, *, allow_empty=None, **kwargs): | |
| self.checkParams(widget, name, | |
| '#ff0000', '#00ff00', '#0000ff', '#123456', | |
| 'red', 'green', 'blue', 'white', 'black', 'grey', | |
| **kwargs) | |
| self.checkInvalidParam(widget, name, 'spam', | |
| errmsg='unknown color name "spam"') | |
| def checkCursorParam(self, widget, name, **kwargs): | |
| self.checkParams(widget, name, 'arrow', 'watch', 'cross', '',**kwargs) | |
| self.checkParam(widget, name, 'none') | |
| self.checkInvalidParam(widget, name, 'spam', | |
| errmsg='bad cursor spec "spam"') | |
| def checkCommandParam(self, widget, name): | |
| def command(*args): | |
| pass | |
| widget[name] = command | |
| self.assertTrue(widget[name]) | |
| self.checkParams(widget, name, '') | |
| def checkEnumParam(self, widget, name, *values, | |
| errmsg=None, allow_empty=False, fullname=None, | |
| sort=False, **kwargs): | |
| self.checkParams(widget, name, *values, **kwargs) | |
| if errmsg is None: | |
| if sort: | |
| if values[-1]: | |
| values = tuple(sorted(values)) | |
| else: | |
| values = tuple(sorted(values[:-1])) + ('',) | |
| errmsg2 = ' %s "{}": must be %s%s or %s' % ( | |
| fullname or name, | |
| ', '.join(values[:-1]), | |
| ',' if len(values) > 2 else '', | |
| values[-1] or '""') | |
| if '' not in values and not allow_empty: | |
| self.checkInvalidParam(widget, name, '', | |
| errmsg='ambiguous' + errmsg2) | |
| errmsg = 'bad' + errmsg2 | |
| self.checkInvalidParam(widget, name, 'spam', errmsg=errmsg) | |
| def checkPixelsParam(self, widget, name, *values, conv=None, **kwargs): | |
| if conv is None: | |
| if self._rounds_pixels and name not in self._no_round: | |
| conv = round | |
| alow_neg = tk_version < (9, 1) | |
| for value in values: | |
| expected = _sentinel | |
| conv1 = conv | |
| if isinstance(value, str): | |
| if not getattr(self, '_converts_pixels', True): | |
| conv1 = str | |
| if conv1 and conv1 is not str: | |
| expected = pixels_conv(value) * self.scaling | |
| conv1 = round | |
| elif not alow_neg and isinstance(value, (int, float)) and value < 0: | |
| self.checkInvalidParam(widget, name, value) | |
| continue | |
| self.checkParam(widget, name, value, expected=expected, | |
| conv=conv1, **kwargs) | |
| errmsg = '(bad|expected) screen distance ((or "" )?but got )?"{}"' | |
| self.checkInvalidParam(widget, name, '6x', errmsg=errmsg) | |
| self.checkInvalidParam(widget, name, 'spam', errmsg=errmsg) | |
| def checkReliefParam(self, widget, name, *, allow_empty=False): | |
| values = ('flat', 'groove', 'raised', 'ridge', 'solid', 'sunken') | |
| if allow_empty: | |
| values += ('',) | |
| self.checkParams(widget, name, *values) | |
| errmsg = 'bad relief "{}": must be %s, or %s' % ( | |
| ', '.join(values[:-1]), | |
| values[-1] or '""') | |
| if tk_version < (8, 6): | |
| errmsg = None | |
| self.checkInvalidParam(widget, name, 'spam', errmsg=errmsg) | |
| def checkImageParam(self, widget, name): | |
| image = tkinter.PhotoImage(master=self.root, name='image1') | |
| self.checkParam(widget, name, image, conv=str) | |
| if tk_version < (8, 7): | |
| errmsg = 'image "spam" doesn\'t exist' | |
| else: | |
| errmsg = 'image "spam" does not exist' | |
| self.checkInvalidParam(widget, name, 'spam', | |
| errmsg=errmsg) | |
| widget[name] = '' | |
| def checkVariableParam(self, widget, name, var): | |
| self.checkParam(widget, name, var, conv=str) | |
| def assertIsBoundingBox(self, bbox): | |
| self.assertIsNotNone(bbox) | |
| self.assertIsInstance(bbox, tuple) | |
| if len(bbox) != 4: | |
| self.fail('Invalid bounding box: %r' % (bbox,)) | |
| for item in bbox: | |
| if not isinstance(item, int): | |
| self.fail('Invalid bounding box: %r' % (bbox,)) | |
| break | |
| def test_keys(self): | |
| widget = self.create() | |
| keys = widget.keys() | |
| self.assertEqual(sorted(keys), sorted(widget.configure())) | |
| for k in keys: | |
| widget[k] | |
| # Test if OPTIONS contains all keys | |
| if test.support.verbose: | |
| keys = set(keys) | |
| expected = set(self.OPTIONS) | |
| for k in sorted(keys - expected): | |
| if not (k in _OPTION_ALIASES and | |
| _OPTION_ALIASES[k] in keys and | |
| _OPTION_ALIASES[k] in expected): | |
| print('%s.OPTIONS doesn\'t contain "%s"' % | |
| (self.__class__.__name__, k)) | |
| def test_options_in_docstring(self): | |
| # Every option in OPTIONS must be listed in the docstring of the | |
| # __init__ method (see gh-78335). Options reported by keys() but | |
| # missing from the docstring are only printed in verbose mode, as | |
| # some of them depend on the Tk version. | |
| widget = self.create() | |
| doc = type(widget).__init__.__doc__ | |
| if doc is None: | |
| self.skipTest('docstrings are not available (run with -OO)') | |
| # Look at the option list only, not the leading description. | |
| start = doc.find('Valid option names') | |
| if start < 0: | |
| start = doc.find('OPTIONS') | |
| if start < 0: | |
| self.skipTest('the __init__ docstring does not list options') | |
| documented = set(re.findall(r'[a-z][a-z0-9]+', doc[start:])) | |
| def is_documented(option): | |
| return (option in documented or | |
| _OPTION_ALIASES.get(option) in documented) | |
| missing = sorted(o for o in self.OPTIONS if not is_documented(o)) | |
| self.assertEqual(missing, [], | |
| '%s options missing from the __init__ docstring: %s' | |
| % (type(widget).__name__, missing)) | |
| if test.support.verbose: | |
| for key in sorted(set(widget.keys())): | |
| if not is_documented(key): | |
| print('%s.__init__ docstring doesn\'t contain "%s"' | |
| % (type(widget).__name__, key)) | |
| class PixelOptionsTests: | |
| """Standard options that accept all formats acceptable to Tk_GetPixels. | |
| In addition to numbers, these options can be set with distances | |
| specified as a string consisting of a number followed by a single | |
| character giving the unit of distance. The allowed units are: | |
| millimeters ('m'), centimeters ('c'), inches ('i') or points ('p'). | |
| In Tk 9 a cget call for one of these options returns a Tcl_Obj of | |
| type "pixels", whose string representation is the distance string | |
| passed to configure. | |
| """ | |
| PIXEL_OPTIONS = ('activeborderwidth', 'borderwidth', 'highlightthickness', | |
| 'insertborderwidth', 'insertwidth', 'padx', 'pady', 'selectborderwidth') | |
| def test_configure_activeborderwidth(self): | |
| widget = self.create() | |
| self.checkPixelsParam(widget, 'activeborderwidth', | |
| 0, 1.3, 2.9, 6, -2, '10p') | |
| def test_configure_borderwidth(self): | |
| widget = self.create() | |
| self.checkPixelsParam(widget, 'borderwidth', | |
| 0, 1.3, 2.6, 6, -2, '10p') | |
| if 'bd' in self.OPTIONS: | |
| self.checkPixelsParam(widget, 'bd', 0, 1.3, 2.6, 6, '10p') | |
| self.checkParam(widget, 'bd', -2, expected=expected) | |
| def test_configure_highlightthickness(self): | |
| widget = self.create() | |
| self.checkPixelsParam(widget, 'highlightthickness', | |
| 0, 1.3, 2.6, 6, -2, '10p') | |
| def test_configure_insertborderwidth(self): | |
| widget = self.create() | |
| self.checkPixelsParam(widget, 'insertborderwidth', 0, 1.3, 2.6, 6, -2, '10p') | |
| def test_configure_insertwidth(self): | |
| widget = self.create() | |
| self.checkPixelsParam(widget, 'insertwidth', 1.3, 2.6, -2, '10p') | |
| def test_configure_padx(self): | |
| widget = self.create() | |
| self.checkPixelsParam(widget, 'padx', 3, 4.4, 5.6, -2, '12m') | |
| def test_configure_pady(self): | |
| widget = self.create() | |
| self.checkPixelsParam(widget, 'pady', 3, 4.4, 5.6, -2, '12m') | |
| def test_configure_selectborderwidth(self): | |
| widget = self.create() | |
| self.checkPixelsParam(widget, 'selectborderwidth', 1.3, 2.6, -2, '10p') | |
| class StandardOptionsTests(PixelOptionsTests): | |
| STANDARD_OPTIONS = ( 'activebackground', 'activeforeground', | |
| 'anchor', 'background', 'bitmap', 'compound', 'cursor', | |
| 'disabledforeground', 'exportselection', 'font', 'foreground', | |
| 'highlightbackground', 'highlightcolor', 'image', | |
| 'insertbackground', 'insertofftime', 'insertontime', 'jump', | |
| 'justify', 'orient', 'relief', 'repeatdelay', 'repeatinterval', | |
| 'selectbackground', 'selectforeground', 'setgrid', 'takefocus', | |
| 'text', 'textvariable', 'troughcolor', 'underline', 'wraplength', | |
| 'xscrollcommand', 'yscrollcommand', ) + PixelOptionsTests.PIXEL_OPTIONS | |
| def test_configure_activebackground(self): | |
| widget = self.create() | |
| self.checkColorParam(widget, 'activebackground') | |
| def test_configure_activeforeground(self): | |
| widget = self.create() | |
| self.checkColorParam(widget, 'activeforeground') | |
| def test_configure_activerelief(self): | |
| widget = self.create() | |
| self.checkReliefParam(widget, 'activerelief') | |
| def test_configure_anchor(self): | |
| widget = self.create() | |
| self.checkEnumParam(widget, 'anchor', | |
| 'n', 'ne', 'e', 'se', 's', 'sw', 'w', 'nw', 'center') | |
| def test_configure_background(self): | |
| widget = self.create() | |
| self.checkColorParam(widget, 'background') | |
| if 'bg' in self.OPTIONS: | |
| self.checkColorParam(widget, 'bg') | |
| @requires_tk(8, 7) | |
| def test_configure_backgroundimage(self): | |
| widget = self.create() | |
| self.checkImageParam(widget, 'backgroundimage') | |
| def test_configure_bitmap(self): | |
| widget = self.create() | |
| self.checkParam(widget, 'bitmap', 'questhead') | |
| self.checkParam(widget, 'bitmap', 'gray50') | |
| filename = test.support.findfile('python.xbm', subdir='tkinterdata') | |
| self.checkParam(widget, 'bitmap', '@' + filename) | |
| # Cocoa Tk widgets don't detect invalid -bitmap values | |
| # See https://core.tcl.tk/tk/info/31cd33dbf0 | |
| if not ('aqua' in self.root.tk.call('tk', 'windowingsystem') and | |
| 'AppKit' in self.root.winfo_server()): | |
| self.checkInvalidParam(widget, 'bitmap', 'spam', | |
| errmsg='bitmap "spam" not defined') | |
| def test_configure_compound(self): | |
| widget = self.create() | |
| self.checkEnumParam(widget, 'compound', | |
| 'bottom', 'center', 'left', 'none', 'right', 'top') | |
| def test_configure_cursor(self): | |
| widget = self.create() | |
| self.checkCursorParam(widget, 'cursor') | |
| def test_configure_disabledforeground(self): | |
| widget = self.create() | |
| self.checkColorParam(widget, 'disabledforeground') | |
| def test_configure_exportselection(self): | |
| widget = self.create() | |
| self.checkBooleanParam(widget, 'exportselection') | |
| def test_configure_font(self): | |
| widget = self.create() | |
| self.checkParam(widget, 'font', | |
| '-Adobe-Helvetica-Medium-R-Normal--*-120-*-*-*-*-*-*') | |
| is_ttk = widget.__class__.__module__ == 'tkinter.ttk' | |
| if not is_ttk: | |
| errmsg = 'font "" does ?n[o\']t exist' | |
| self.checkInvalidParam(widget, 'font', '', errmsg=errmsg) | |
| def test_configure_foreground(self): | |
| widget = self.create() | |
| self.checkColorParam(widget, 'foreground') | |
| if 'fg' in self.OPTIONS: | |
| self.checkColorParam(widget, 'fg') | |
| def test_configure_highlightbackground(self): | |
| widget = self.create() | |
| self.checkColorParam(widget, 'highlightbackground') | |
| def test_configure_highlightcolor(self): | |
| widget = self.create() | |
| self.checkColorParam(widget, 'highlightcolor') | |
| def test_configure_image(self): | |
| widget = self.create() | |
| self.checkImageParam(widget, 'image') | |
| def test_configure_insertbackground(self): | |
| widget = self.create() | |
| self.checkColorParam(widget, 'insertbackground') | |
| def test_configure_insertofftime(self): | |
| widget = self.create() | |
| self.checkIntegerParam(widget, 'insertofftime', 100) | |
| def test_configure_insertontime(self): | |
| widget = self.create() | |
| self.checkIntegerParam(widget, 'insertontime', 100) | |
| def test_configure_jump(self): | |
| widget = self.create() | |
| self.checkBooleanParam(widget, 'jump') | |
| def test_configure_justify(self): | |
| widget = self.create() | |
| values = ('left', 'right', 'center') | |
| if self._allow_empty_justify: | |
| values += ('',) | |
| self.checkEnumParam(widget, 'justify', *values, | |
| fullname='justification') | |
| def test_configure_orient(self): | |
| widget = self.create() | |
| self.assertEqual(widget['orient'], self.default_orient) | |
| self.checkEnumParam(widget, 'orient', 'horizontal', 'vertical') | |
| @requires_tk(8, 7) | |
| def test_configure_placeholder(self): | |
| widget = self.create() | |
| self.checkParam(widget, 'placeholder', 'xxx') | |
| @requires_tk(8, 7) | |
| def test_configure_placeholderforeground(self): | |
| widget = self.create() | |
| self.checkColorParam(widget, 'placeholderforeground') | |
| def test_configure_relief(self): | |
| widget = self.create() | |
| self.checkReliefParam(widget, 'relief') | |
| def test_configure_repeatdelay(self): | |
| widget = self.create() | |
| self.checkIntegerParam(widget, 'repeatdelay', -500, 500) | |
| def test_configure_repeatinterval(self): | |
| widget = self.create() | |
| self.checkIntegerParam(widget, 'repeatinterval', -500, 500) | |
| def test_configure_selectbackground(self): | |
| widget = self.create() | |
| self.checkColorParam(widget, 'selectbackground') | |
| def test_configure_selectforeground(self): | |
| widget = self.create() | |
| self.checkColorParam(widget, 'selectforeground') | |
| def test_configure_setgrid(self): | |
| widget = self.create() | |
| self.checkBooleanParam(widget, 'setgrid') | |
| def test_configure_state(self): | |
| widget = self.create() | |
| self.checkEnumParam(widget, 'state', 'active', 'disabled', 'normal') | |
| def test_configure_takefocus(self): | |
| widget = self.create() | |
| self.checkParams(widget, 'takefocus', '0', '1', '') | |
| def test_configure_text(self): | |
| widget = self.create() | |
| self.checkParams(widget, 'text', '', 'any string') | |
| def test_configure_textvariable(self): | |
| widget = self.create() | |
| var = tkinter.StringVar(self.root) | |
| self.checkVariableParam(widget, 'textvariable', var) | |
| @requires_tk(8, 7) | |
| def test_configure_tile(self): | |
| widget = self.create() | |
| self.checkBooleanParam(widget, 'tile') | |
| def test_configure_troughcolor(self): | |
| widget = self.create() | |
| self.checkColorParam(widget, 'troughcolor') | |
| def test_configure_underline(self): | |
| widget = self.create() | |
| self.checkParams(widget, 'underline', 0, 1, 10) | |
| if tk_version >= (8, 7): | |
| is_ttk = widget.__class__.__module__ == 'tkinter.ttk' | |
| self.checkParam(widget, 'underline', '', | |
| expected='' if is_ttk else self._default_pixels) | |
| self.checkParam(widget, 'underline', '5+2', | |
| expected='5+2' if is_ttk else 7) | |
| self.checkParam(widget, 'underline', '5-2', | |
| expected='5-2' if is_ttk else 3) | |
| self.checkParam(widget, 'underline', 'end', expected='end') | |
| self.checkParam(widget, 'underline', 'end-2', expected='end-2') | |
| errmsg = (r'bad index "{}": must be integer\?\[\+-\]integer\?, ' | |
| r'end\?\[\+-\]integer\?, or ""') | |
| else: | |
| errmsg = 'expected integer but got "{}"' | |
| self.checkInvalidParam(widget, 'underline', '', errmsg=errmsg) | |
| self.checkInvalidParam(widget, 'underline', '10p', errmsg=errmsg) | |
| self.checkInvalidParam(widget, 'underline', 3.2, errmsg=errmsg) | |
| def test_configure_wraplength(self): | |
| widget = self.create() | |
| self.checkPixelsParam(widget, 'wraplength', 100) | |
| def test_configure_xscrollcommand(self): | |
| widget = self.create() | |
| self.checkCommandParam(widget, 'xscrollcommand') | |
| def test_configure_yscrollcommand(self): | |
| widget = self.create() | |
| self.checkCommandParam(widget, 'yscrollcommand') | |
| # non-standard but common options | |
| def test_configure_command(self): | |
| widget = self.create() | |
| self.checkCommandParam(widget, 'command') | |
| def test_configure_indicatoron(self): | |
| widget = self.create() | |
| self.checkBooleanParam(widget, 'indicatoron') | |
| def test_configure_offrelief(self): | |
| widget = self.create() | |
| self.checkReliefParam(widget, 'offrelief') | |
| def test_configure_overrelief(self): | |
| widget = self.create() | |
| self.checkReliefParam(widget, 'overrelief', | |
| allow_empty=(tk_version >= (8, 7))) | |
| def test_configure_selectcolor(self): | |
| widget = self.create() | |
| self.checkColorParam(widget, 'selectcolor') | |
| def test_configure_selectimage(self): | |
| widget = self.create() | |
| self.checkImageParam(widget, 'selectimage') | |
| def test_configure_tristateimage(self): | |
| widget = self.create() | |
| self.checkImageParam(widget, 'tristateimage') | |
| def test_configure_tristatevalue(self): | |
| widget = self.create() | |
| self.checkParam(widget, 'tristatevalue', 'unknowable') | |
| def test_configure_variable(self): | |
| widget = self.create() | |
| var = tkinter.DoubleVar(self.root) | |
| self.checkVariableParam(widget, 'variable', var) | |
| class IntegerSizeTests: | |
| """ Tests widgets which only accept integral width and height.""" | |
| def test_configure_height(self): | |
| widget = self.create() | |
| self.checkIntegerParam(widget, 'height', 100, -100, 0) | |
| def test_configure_width(self): | |
| widget = self.create() | |
| self.checkIntegerParam(widget, 'width', 402, -402, 0) | |
| class PixelSizeTests: | |
| """ Tests widgets which accept screen distances for width and height.""" | |
| def test_configure_height(self): | |
| widget = self.create() | |
| self.checkPixelsParam(widget, 'height', 100, 101.2, 102.6, -100, 0, '3c') | |
| def test_configure_width(self): | |
| widget = self.create() | |
| self.checkPixelsParam(widget, 'width', 402, 403.4, 404.6, -402, 0, '5i') | |
| def add_configure_tests(*source_classes): | |
| # This decorator adds test_configure_xxx methods from source classes for | |
| # every xxx option in the OPTIONS class attribute if they are not defined | |
| # explicitly. | |
| def decorator(cls): | |
| for option in cls.OPTIONS: | |
| methodname = 'test_configure_' + option | |
| if not hasattr(cls, methodname): | |
| for source_class in source_classes: | |
| if hasattr(source_class, methodname): | |
| setattr(cls, methodname, | |
| getattr(source_class, methodname)) | |
| break | |
| else: | |
| def test(self, option=option): | |
| widget = self.create() | |
| widget[option] | |
| raise AssertionError('Option "%s" is not tested in %s' % | |
| (option, cls.__name__)) | |
| test.__name__ = methodname | |
| setattr(cls, methodname, test) | |
| return cls | |
| return decorator | |
| def setUpModule(): | |
| if test.support.verbose: | |
| tcl = tkinter.Tcl() | |
| print('patchlevel =', tcl.call('info', 'patchlevel'), flush=True) |