diff options
author | Miss Islington (bot) <31488909+miss-islington@users.noreply.github.com> | 2024-06-22 07:17:55 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-06-22 07:17:55 (GMT) |
commit | 4fabbf97735ea6d91b32011fb7413d3909b082a0 (patch) | |
tree | 453a438cff1a273c1bfff47c54bdd483160627e3 | |
parent | 4dc27bc0b76aa5985ccef2901f7a4f5d36b97995 (diff) | |
download | cpython-4fabbf97735ea6d91b32011fb7413d3909b082a0.zip cpython-4fabbf97735ea6d91b32011fb7413d3909b082a0.tar.gz cpython-4fabbf97735ea6d91b32011fb7413d3909b082a0.tar.bz2 |
[3.13] gh-104855: Update Tkinter tests for Tcl/Tk 8.7 and 9.0 (GH-120824) (GH-120864)
The tests are now passed with the current version of Tcl/Tk under
development (8.7b1+ and 9.0b3+).
The following changes were also made to make the tests more flexible:
* Helper methods like checkParam() now interpret the expected error message
as a regular expression instead of a literal.
* Add support of new arguments in checkEnumParam():
- allow_empty=True skips testing with empty string;
- fullname= specifies the name for error message if it differs from the
option name;
- sort=True sorts values for error message.
* Add support of the allow_empty argument in checkReliefParam():
allow_empty=True adds an empty string to the list of accepted values.
* Attributes _clip_highlightthickness, _clip_pad and _clip_borderwidth
specify how negative values of options -highlightthickness, -padx, -pady
and -borderwidth are handled.
* Use global variables for some common error messages.
(cherry picked from commit 6ad26de6e8ab61b035e7ecfff9791c2b349c3ad0)
Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
Co-authored-by: Terry Jan Reedy <tjreedy@udel.edu>
-rw-r--r-- | Lib/test/test_tcl.py | 10 | ||||
-rw-r--r-- | Lib/test/test_tkinter/test_geometry_managers.py | 47 | ||||
-rw-r--r-- | Lib/test/test_tkinter/test_variables.py | 4 | ||||
-rw-r--r-- | Lib/test/test_tkinter/test_widgets.py | 121 | ||||
-rw-r--r-- | Lib/test/test_tkinter/widget_tests.py | 131 | ||||
-rw-r--r-- | Lib/test/test_ttk/test_widgets.py | 74 |
6 files changed, 250 insertions, 137 deletions
diff --git a/Lib/test/test_tcl.py b/Lib/test/test_tcl.py index 553d543..e6cf2c7 100644 --- a/Lib/test/test_tcl.py +++ b/Lib/test/test_tcl.py @@ -219,10 +219,18 @@ class TclTest(unittest.TestCase): with open(filename, 'wb') as f: f.write(b""" set a "<\xed\xa0\xbd\xed\xb2\xbb>" + """) + if tcl_version >= (9, 0): + self.assertRaises(TclError, tcl.evalfile, filename) + else: + tcl.evalfile(filename) + self.assertEqual(tcl.eval('set a'), '<\U0001f4bb>') + + with open(filename, 'wb') as f: + f.write(b""" set b "<\\ud83d\\udcbb>" """) tcl.evalfile(filename) - self.assertEqual(tcl.eval('set a'), '<\U0001f4bb>') self.assertEqual(tcl.eval('set b'), '<\U0001f4bb>') def testEvalFileException(self): diff --git a/Lib/test/test_tkinter/test_geometry_managers.py b/Lib/test/test_tkinter/test_geometry_managers.py index f8f1c89..d71a634 100644 --- a/Lib/test/test_tkinter/test_geometry_managers.py +++ b/Lib/test/test_tkinter/test_geometry_managers.py @@ -10,6 +10,11 @@ from test.test_tkinter.widget_tests import AbstractWidgetTest requires('gui') +EXPECTED_FLOAT_ERRMSG = 'expected floating-point number but got "{}"' +EXPECTED_FLOAT_OR_EMPTY_ERRMSG = 'expected floating-point number (or "" )?but got "{}"' +EXPECTED_SCREEN_DISTANCE_ERRMSG = '(bad|expected) screen distance (but got )?"{}"' +EXPECTED_SCREEN_DISTANCE_OR_EMPTY_ERRMSG = '(bad|expected) screen distance (or "" but got )?"{}"' + class PackTest(AbstractWidgetTest, unittest.TestCase): test_keys = None @@ -317,7 +322,8 @@ class PlaceTest(AbstractWidgetTest, unittest.TestCase): self.assertEqual(f2.place_info()['x'], '-10') self.root.update() self.assertEqual(f2.winfo_x(), 190) - with self.assertRaisesRegex(TclError, 'bad screen distance "spam"'): + with self.assertRaisesRegex(TclError, + EXPECTED_SCREEN_DISTANCE_ERRMSG.format('spam')): f2.place_configure(in_=f, x='spam') def test_place_configure_y(self): @@ -334,7 +340,8 @@ class PlaceTest(AbstractWidgetTest, unittest.TestCase): self.assertEqual(f2.place_info()['y'], '-10') self.root.update() self.assertEqual(f2.winfo_y(), 110) - with self.assertRaisesRegex(TclError, 'bad screen distance "spam"'): + with self.assertRaisesRegex(TclError, + EXPECTED_SCREEN_DISTANCE_ERRMSG.format('spam')): f2.place_configure(in_=f, y='spam') def test_place_configure_relx(self): @@ -351,8 +358,7 @@ class PlaceTest(AbstractWidgetTest, unittest.TestCase): self.assertEqual(f2.place_info()['relx'], '1') self.root.update() self.assertEqual(f2.winfo_x(), 200) - with self.assertRaisesRegex(TclError, 'expected floating-point number ' - 'but got "spam"'): + with self.assertRaisesRegex(TclError, EXPECTED_FLOAT_ERRMSG.format('spam')): f2.place_configure(in_=f, relx='spam') def test_place_configure_rely(self): @@ -369,8 +375,7 @@ class PlaceTest(AbstractWidgetTest, unittest.TestCase): self.assertEqual(f2.place_info()['rely'], '1') self.root.update() self.assertEqual(f2.winfo_y(), 120) - with self.assertRaisesRegex(TclError, 'expected floating-point number ' - 'but got "spam"'): + with self.assertRaisesRegex(TclError, EXPECTED_FLOAT_ERRMSG.format('spam')): f2.place_configure(in_=f, rely='spam') def test_place_configure_anchor(self): @@ -391,7 +396,8 @@ class PlaceTest(AbstractWidgetTest, unittest.TestCase): f2.place_configure(width='') self.root.update() self.assertEqual(f2.winfo_width(), 30) - with self.assertRaisesRegex(TclError, 'bad screen distance "abcd"'): + with self.assertRaisesRegex(TclError, + EXPECTED_SCREEN_DISTANCE_OR_EMPTY_ERRMSG.format('abcd')): f2.place_configure(width='abcd') def test_place_configure_height(self): @@ -402,7 +408,8 @@ class PlaceTest(AbstractWidgetTest, unittest.TestCase): f2.place_configure(height='') self.root.update() self.assertEqual(f2.winfo_height(), 60) - with self.assertRaisesRegex(TclError, 'bad screen distance "abcd"'): + with self.assertRaisesRegex(TclError, + EXPECTED_SCREEN_DISTANCE_OR_EMPTY_ERRMSG.format('abcd')): f2.place_configure(height='abcd') def test_place_configure_relwidth(self): @@ -413,8 +420,7 @@ class PlaceTest(AbstractWidgetTest, unittest.TestCase): f2.place_configure(relwidth='') self.root.update() self.assertEqual(f2.winfo_width(), 30) - with self.assertRaisesRegex(TclError, 'expected floating-point number ' - 'but got "abcd"'): + with self.assertRaisesRegex(TclError, EXPECTED_FLOAT_OR_EMPTY_ERRMSG.format('abcd')): f2.place_configure(relwidth='abcd') def test_place_configure_relheight(self): @@ -425,8 +431,7 @@ class PlaceTest(AbstractWidgetTest, unittest.TestCase): f2.place_configure(relheight='') self.root.update() self.assertEqual(f2.winfo_height(), 60) - with self.assertRaisesRegex(TclError, 'expected floating-point number ' - 'but got "abcd"'): + with self.assertRaisesRegex(TclError, EXPECTED_FLOAT_OR_EMPTY_ERRMSG.format('abcd')): f2.place_configure(relheight='abcd') def test_place_configure_bordermode(self): @@ -629,7 +634,8 @@ class GridTest(AbstractWidgetTest, unittest.TestCase): self.assertEqual(self.root.grid_columnconfigure(0, 'weight'), 4) def test_grid_columnconfigure_minsize(self): - with self.assertRaisesRegex(TclError, 'bad screen distance "foo"'): + with self.assertRaisesRegex(TclError, + EXPECTED_SCREEN_DISTANCE_ERRMSG.format('foo')): self.root.grid_columnconfigure(0, minsize='foo') self.root.grid_columnconfigure(0, minsize=10) self.assertEqual(self.root.grid_columnconfigure(0, 'minsize'), 10) @@ -646,7 +652,8 @@ class GridTest(AbstractWidgetTest, unittest.TestCase): self.assertEqual(self.root.grid_columnconfigure(0)['weight'], 3) def test_grid_columnconfigure_pad(self): - with self.assertRaisesRegex(TclError, 'bad screen distance "foo"'): + with self.assertRaisesRegex(TclError, + EXPECTED_SCREEN_DISTANCE_ERRMSG.format('foo')): self.root.grid_columnconfigure(0, pad='foo') with self.assertRaisesRegex(TclError, 'invalid arg "-pad": ' 'should be non-negative'): @@ -683,7 +690,8 @@ class GridTest(AbstractWidgetTest, unittest.TestCase): self.assertEqual(self.root.grid_rowconfigure(0, 'weight'), 4) def test_grid_rowconfigure_minsize(self): - with self.assertRaisesRegex(TclError, 'bad screen distance "foo"'): + with self.assertRaisesRegex(TclError, + EXPECTED_SCREEN_DISTANCE_ERRMSG.format('foo')): self.root.grid_rowconfigure(0, minsize='foo') self.root.grid_rowconfigure(0, minsize=10) self.assertEqual(self.root.grid_rowconfigure(0, 'minsize'), 10) @@ -700,7 +708,8 @@ class GridTest(AbstractWidgetTest, unittest.TestCase): self.assertEqual(self.root.grid_rowconfigure(0)['weight'], 3) def test_grid_rowconfigure_pad(self): - with self.assertRaisesRegex(TclError, 'bad screen distance "foo"'): + with self.assertRaisesRegex(TclError, + EXPECTED_SCREEN_DISTANCE_ERRMSG.format('foo')): self.root.grid_rowconfigure(0, pad='foo') with self.assertRaisesRegex(TclError, 'invalid arg "-pad": ' 'should be non-negative'): @@ -818,9 +827,11 @@ class GridTest(AbstractWidgetTest, unittest.TestCase): self.root.grid_location(0) with self.assertRaises(TypeError): self.root.grid_location(0, 0, 0) - with self.assertRaisesRegex(TclError, 'bad screen distance "x"'): + with self.assertRaisesRegex(TclError, + EXPECTED_SCREEN_DISTANCE_ERRMSG.format('x')): self.root.grid_location('x', 'y') - with self.assertRaisesRegex(TclError, 'bad screen distance "y"'): + with self.assertRaisesRegex(TclError, + EXPECTED_SCREEN_DISTANCE_ERRMSG.format('y')): self.root.grid_location('1c', 'y') t = self.root # de-maximize diff --git a/Lib/test/test_tkinter/test_variables.py b/Lib/test/test_tkinter/test_variables.py index c1d232e..def7aec 100644 --- a/Lib/test/test_tkinter/test_variables.py +++ b/Lib/test/test_tkinter/test_variables.py @@ -6,7 +6,7 @@ import tkinter from tkinter import (Variable, StringVar, IntVar, DoubleVar, BooleanVar, Tcl, TclError) from test.support import ALWAYS_EQ -from test.test_tkinter.support import AbstractDefaultRootTest +from test.test_tkinter.support import AbstractDefaultRootTest, tcl_version class Var(Variable): @@ -112,6 +112,8 @@ class TestVariable(TestBase): self.assertTrue(v.side_effect) def test_trace_old(self): + if tcl_version >= (9, 0): + self.skipTest('requires Tcl version < 9.0') # Old interface v = Variable(self.root) vname = str(v) diff --git a/Lib/test/test_tkinter/test_widgets.py b/Lib/test/test_tkinter/test_widgets.py index f5f2fd2..64ea87e 100644 --- a/Lib/test/test_tkinter/test_widgets.py +++ b/Lib/test/test_tkinter/test_widgets.py @@ -4,7 +4,7 @@ from tkinter import TclError import os from test.support import requires -from test.test_tkinter.support import (requires_tk, +from test.test_tkinter.support import (requires_tk, tk_version, get_tk_patchlevel, widget_eq, AbstractDefaultRootTest) from test.test_tkinter.widget_tests import ( @@ -14,6 +14,9 @@ from test.test_tkinter.widget_tests import ( requires('gui') +EXPECTED_SCREEN_DISTANCE_ERRMSG = '(bad|expected) screen distance (but got )?"{}"' +EXPECTED_SCREEN_DISTANCE_OR_EMPTY_ERRMSG = '(bad|expected) screen distance (or "" but got )?"{}"' + def float_round(x): return float(round(x)) @@ -141,11 +144,9 @@ class LabelFrameTest(AbstractToplevelTest, unittest.TestCase): class AbstractLabelTest(AbstractWidgetTest, IntegerSizeTests): _conv_pixels = False - - def test_configure_highlightthickness(self): - widget = self.create() - self.checkPixelsParam(widget, 'highlightthickness', - 0, 1.3, 2.6, 6, -2, '10p') + _clip_highlightthickness = tk_version >= (8, 7) + _clip_pad = tk_version >= (8, 7) + _clip_borderwidth = tk_version >= (8, 7) @add_standard_options(StandardOptionsTests) @@ -277,6 +278,9 @@ class MenubuttonTest(AbstractLabelTest, unittest.TestCase): 'underline', 'width', 'wraplength', ) _conv_pixels = round + _clip_highlightthickness = True + _clip_pad = True + _clip_borderwidth = False def create(self, **kwargs): return tkinter.Menubutton(self.root, **kwargs) @@ -290,9 +294,6 @@ class MenubuttonTest(AbstractLabelTest, unittest.TestCase): widget = self.create() self.checkIntegerParam(widget, 'height', 100, -100, 0, conv=str) - test_configure_highlightthickness = \ - StandardOptionsTests.test_configure_highlightthickness - def test_configure_image(self): widget = self.create() image = tkinter.PhotoImage(master=self.root, name='image1') @@ -313,16 +314,6 @@ class MenubuttonTest(AbstractLabelTest, unittest.TestCase): self.checkParam(widget, 'menu', menu, eq=widget_eq) menu.destroy() - def test_configure_padx(self): - widget = self.create() - self.checkPixelsParam(widget, 'padx', 3, 4.4, 5.6, '12m') - self.checkParam(widget, 'padx', -2, expected=0) - - def test_configure_pady(self): - widget = self.create() - self.checkPixelsParam(widget, 'pady', 3, 4.4, 5.6, '12m') - self.checkParam(widget, 'pady', -2, expected=0) - def test_configure_width(self): widget = self.create() self.checkIntegerParam(widget, 'width', 402, -402, 0, conv=str) @@ -489,8 +480,12 @@ class SpinboxTest(EntryTest, unittest.TestCase): widget = self.create() self.checkParam(widget, 'to', 100.0) self.checkFloatParam(widget, 'from', -10, 10.2, 11.7) - self.checkInvalidParam(widget, 'from', 200, - errmsg='-to value must be greater than -from value') + if tk_version >= (8, 7): + self.checkFloatParam(widget, 'from', 200, expected=100) + else: + self.checkInvalidParam( + widget, 'from', 200, + errmsg='-to value must be greater than -from value') def test_configure_increment(self): widget = self.create() @@ -500,8 +495,12 @@ class SpinboxTest(EntryTest, unittest.TestCase): widget = self.create() self.checkParam(widget, 'from', -100.0) self.checkFloatParam(widget, 'to', -10, 10.2, 11.7) - self.checkInvalidParam(widget, 'to', -200, - errmsg='-to value must be greater than -from value') + if tk_version >= (8, 7): + self.checkFloatParam(widget, 'to', -200, expected=-100) + else: + self.checkInvalidParam( + widget, 'to', -200, + errmsg='-to value must be greater than -from value') def test_configure_values(self): # XXX @@ -666,7 +665,7 @@ class TextTest(AbstractWidgetTest, unittest.TestCase): self.checkParam(widget, 'tabs', '2c left 4c 6c center', expected=('2c', 'left', '4c', '6c', 'center')) self.checkInvalidParam(widget, 'tabs', 'spam', - errmsg='bad screen distance "spam"') + errmsg=EXPECTED_SCREEN_DISTANCE_ERRMSG.format('spam')) def test_configure_tabstyle(self): widget = self.create() @@ -860,24 +859,27 @@ class CanvasTest(AbstractWidgetTest, unittest.TestCase): def test_create_polygon(self): c = self.create() - i1 = c.create_polygon(20, 30, 40, 50, 60, 10) + tk87 = tk_version >= (8, 7) + # In Tk < 8.7 polygons are filled, but has no outline by default. + # This affects its size, so always explicitly specify outline. + i1 = c.create_polygon(20, 30, 40, 50, 60, 10, outline='red') self.assertEqual(c.coords(i1), [20.0, 30.0, 40.0, 50.0, 60.0, 10.0]) - self.assertEqual(c.bbox(i1), (19, 9, 61, 51)) + self.assertEqual(c.bbox(i1), (18, 8, 62, 52)) self.assertEqual(c.itemcget(i1, 'joinstyle'), 'round') self.assertEqual(c.itemcget(i1, 'smooth'), '0') self.assertEqual(c.itemcget(i1, 'splinestep'), '12') - i2 = c.create_polygon([21, 31, 41, 51, 61, 11]) + i2 = c.create_polygon([21, 31, 41, 51, 61, 11], outline='red') self.assertEqual(c.coords(i2), [21.0, 31.0, 41.0, 51.0, 61.0, 11.0]) - self.assertEqual(c.bbox(i2), (20, 10, 62, 52)) + self.assertEqual(c.bbox(i2), (19, 9, 63, 53)) - i3 = c.create_polygon((22, 32), (42, 52), (62, 12)) + i3 = c.create_polygon((22, 32), (42, 52), (62, 12), outline='red') self.assertEqual(c.coords(i3), [22.0, 32.0, 42.0, 52.0, 62.0, 12.0]) - self.assertEqual(c.bbox(i3), (21, 11, 63, 53)) + self.assertEqual(c.bbox(i3), (20, 10, 64, 54)) - i4 = c.create_polygon([(23, 33), (43, 53), (63, 13)]) + i4 = c.create_polygon([(23, 33), (43, 53), (63, 13)], outline='red') self.assertEqual(c.coords(i4), [23.0, 33.0, 43.0, 53.0, 63.0, 13.0]) - self.assertEqual(c.bbox(i4), (22, 12, 64, 54)) + self.assertEqual(c.bbox(i4), (21, 11, 65, 55)) self.assertRaises(TclError, c.create_polygon, 20, 30, 60) self.assertRaises(TclError, c.create_polygon, [20, 30, 60]) @@ -1180,12 +1182,14 @@ class ScrollbarTest(AbstractWidgetTest, unittest.TestCase): def test_configure_elementborderwidth(self): widget = self.create() - self.checkPixelsParam(widget, 'elementborderwidth', 4.3, 5.6, -2, '1m') + self.checkPixelsParam(widget, 'elementborderwidth', 4.3, 5.6, '1m') + expected = self._default_pixels if tk_version >= (8, 7) else -2 + self.checkParam(widget, 'elementborderwidth', -2, expected=expected) def test_configure_orient(self): widget = self.create() self.checkEnumParam(widget, 'orient', 'vertical', 'horizontal', - errmsg='bad orientation "{}": must be vertical or horizontal') + fullname='orientation', allow_empty=True) def test_activate(self): sb = self.create() @@ -1256,7 +1260,8 @@ class PanedWindowTest(AbstractWidgetTest, unittest.TestCase): @requires_tk(8, 6, 5) def test_configure_proxyrelief(self): widget = self.create() - self.checkReliefParam(widget, 'proxyrelief') + self.checkReliefParam(widget, 'proxyrelief', + allow_empty=(tk_version >= (8, 7))) def test_configure_sashcursor(self): widget = self.create() @@ -1329,7 +1334,7 @@ class PanedWindowTest(AbstractWidgetTest, unittest.TestCase): p, b, c = self.create2() self.check_paneconfigure(p, b, 'height', 10, 10) self.check_paneconfigure_bad(p, b, 'height', - 'bad screen distance "badValue"') + EXPECTED_SCREEN_DISTANCE_OR_EMPTY_ERRMSG.format('badValue')) def test_paneconfigure_hide(self): p, b, c = self.create2() @@ -1341,19 +1346,19 @@ class PanedWindowTest(AbstractWidgetTest, unittest.TestCase): p, b, c = self.create2() self.check_paneconfigure(p, b, 'minsize', 10, 10) self.check_paneconfigure_bad(p, b, 'minsize', - 'bad screen distance "badValue"') + EXPECTED_SCREEN_DISTANCE_ERRMSG.format('badValue')) def test_paneconfigure_padx(self): p, b, c = self.create2() self.check_paneconfigure(p, b, 'padx', 1.3, 1) self.check_paneconfigure_bad(p, b, 'padx', - 'bad screen distance "badValue"') + EXPECTED_SCREEN_DISTANCE_ERRMSG.format('badValue')) def test_paneconfigure_pady(self): p, b, c = self.create2() self.check_paneconfigure(p, b, 'pady', 1.3, 1) self.check_paneconfigure_bad(p, b, 'pady', - 'bad screen distance "badValue"') + EXPECTED_SCREEN_DISTANCE_ERRMSG.format('badValue')) def test_paneconfigure_sticky(self): p, b, c = self.create2() @@ -1374,7 +1379,7 @@ class PanedWindowTest(AbstractWidgetTest, unittest.TestCase): p, b, c = self.create2() self.check_paneconfigure(p, b, 'width', 10, 10) self.check_paneconfigure_bad(p, b, 'width', - 'bad screen distance "badValue"') + EXPECTED_SCREEN_DISTANCE_OR_EMPTY_ERRMSG.format('badValue')) @add_standard_options(StandardOptionsTests) @@ -1414,14 +1419,10 @@ class MenuTest(AbstractWidgetTest, unittest.TestCase): def test_configure_type(self): widget = self.create() - opts = ('normal, tearoff, or menubar' - if widget.info_patchlevel() < (8, 7) else - 'menubar, normal, or tearoff') - self.checkEnumParam( - widget, 'type', - 'normal', 'tearoff', 'menubar', - errmsg='bad type "{}": must be ' + opts, - ) + values = ('normal', 'tearoff', 'menubar') + self.checkEnumParam(widget, 'type', *values, + allow_empty=tk_version < (8, 7), + sort=tk_version >= (8, 7)) def test_entryconfigure(self): m1 = self.create() @@ -1467,6 +1468,10 @@ class MessageTest(AbstractWidgetTest, unittest.TestCase): 'takefocus', 'text', 'textvariable', 'width', ) _conv_pad_pixels = False + if tk_version >= (8, 7): + _conv_pixels = False + _clip_pad = tk_version >= (8, 7) + _clip_borderwidth = tk_version >= (8, 7) def create(self, **kwargs): return tkinter.Message(self.root, **kwargs) @@ -1475,6 +1480,26 @@ class MessageTest(AbstractWidgetTest, unittest.TestCase): widget = self.create() self.checkIntegerParam(widget, 'aspect', 250, 0, -300) + def test_configure_padx(self): + widget = self.create() + self.checkPixelsParam(widget, 'padx', 3, 4.4, 5.6, '12m', + conv=self._conv_pad_pixels) + expected = self._default_pixels if self._clip_pad else -2 + self.checkParam(widget, 'padx', -2, expected=expected) + + def test_configure_pady(self): + widget = self.create() + self.checkPixelsParam(widget, 'pady', 3, 4.4, 5.6, '12m', + conv=self._conv_pad_pixels) + expected = self._default_pixels if self._clip_pad else -2 + self.checkParam(widget, 'pady', -2, expected=expected) + + def test_configure_width(self): + widget = self.create() + self.checkPixelsParam(widget, 'width', 402, 403.4, 404.6, 0, '5i') + expected = 0 if tk_version >= (8, 7) else -402 + self.checkParam(widget, 'width', -402, expected=expected) + class DefaultRootTest(AbstractDefaultRootTest, unittest.TestCase): diff --git a/Lib/test/test_tkinter/widget_tests.py b/Lib/test/test_tkinter/widget_tests.py index 31f82f4..eef2efb 100644 --- a/Lib/test/test_tkinter/widget_tests.py +++ b/Lib/test/test_tkinter/widget_tests.py @@ -1,5 +1,6 @@ # 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, tk_version, pixels_conv, tcl_obj_eq) @@ -9,9 +10,13 @@ import test.support _sentinel = object() class AbstractWidgetTest(AbstractTkTest): + _default_pixels = '' if tk_version >= (9, 0) else -1 if tk_version >= (8, 7) else '' _conv_pixels = round _conv_pad_pixels = None _stringify = False + _clip_highlightthickness = True + _clip_pad = False + _clip_borderwidth = False @property def scaling(self): @@ -56,16 +61,13 @@ class AbstractWidgetTest(AbstractTkTest): def checkInvalidParam(self, widget, name, value, errmsg=None): orig = widget[name] if errmsg is not None: - errmsg = errmsg.format(value) - with self.assertRaises(tkinter.TclError) as cm: + errmsg = errmsg.format(re.escape(str(value))) + errmsg = fr'\A{errmsg}\Z' + with self.assertRaisesRegex(tkinter.TclError, errmsg or ''): widget[name] = value - if errmsg is not None: - self.assertEqual(str(cm.exception), errmsg) self.assertEqual(widget[name], orig) - with self.assertRaises(tkinter.TclError) as cm: + with self.assertRaisesRegex(tkinter.TclError, errmsg or ''): widget.configure({name: value}) - if errmsg is not None: - self.assertEqual(str(cm.exception), errmsg) self.assertEqual(widget[name], orig) def checkParams(self, widget, name, *values, **kwargs): @@ -74,30 +76,26 @@ class AbstractWidgetTest(AbstractTkTest): def checkIntegerParam(self, widget, name, *values, **kwargs): self.checkParams(widget, name, *values, **kwargs) - self.checkInvalidParam(widget, name, '', - errmsg='expected integer but got ""') - self.checkInvalidParam(widget, name, '10p', - errmsg='expected integer but got "10p"') - self.checkInvalidParam(widget, name, 3.2, - errmsg='expected integer but got "3.2"') + 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) - self.checkInvalidParam(widget, name, '', - errmsg='expected floating-point number but got ""') - self.checkInvalidParam(widget, name, 'spam', - errmsg='expected floating-point number but got "spam"') + 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) - self.checkInvalidParam(widget, name, '', - errmsg='expected boolean value but got ""') - self.checkInvalidParam(widget, name, 'spam', - errmsg='expected boolean value but got "spam"') + 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, @@ -120,16 +118,24 @@ class AbstractWidgetTest(AbstractTkTest): self.assertTrue(widget[name]) self.checkParams(widget, name, '') - def checkEnumParam(self, widget, name, *values, errmsg=None, **kwargs): + 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' % ( - name, + fullname or name, ', '.join(values[:-1]), ',' if len(values) > 2 else '', - values[-1]) - self.checkInvalidParam(widget, name, '', - errmsg='ambiguous' + errmsg2) + 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) @@ -146,20 +152,21 @@ class AbstractWidgetTest(AbstractTkTest): conv1 = round self.checkParam(widget, name, value, expected=expected, conv=conv1, **kwargs) - self.checkInvalidParam(widget, name, '6x', - errmsg='bad screen distance "6x"') - self.checkInvalidParam(widget, name, 'spam', - errmsg='bad screen distance "spam"') + 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): - self.checkParams(widget, name, - 'flat', 'groove', 'raised', 'ridge', 'solid', 'sunken') - errmsg='bad relief "spam": must be '\ - 'flat, groove, raised, ridge, solid, or sunken' + 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) + self.checkInvalidParam(widget, name, 'spam', errmsg=errmsg) def checkImageParam(self, widget, name): image = tkinter.PhotoImage(master=self.root, name='image1') @@ -262,9 +269,14 @@ class StandardOptionsTests: def test_configure_borderwidth(self): widget = self.create() self.checkPixelsParam(widget, 'borderwidth', - 0, 1.3, 2.6, 6, -2, '10p') + 0, 1.3, 2.6, 6, '10p') + expected = 0 if self._clip_borderwidth else -2 + self.checkParam(widget, 'borderwidth', -2, expected=expected, + conv=self._conv_pixels) if 'bd' in self.OPTIONS: - self.checkPixelsParam(widget, 'bd', 0, 1.3, 2.6, 6, -2, '10p') + self.checkPixelsParam(widget, 'bd', 0, 1.3, 2.6, 6, '10p') + self.checkParam(widget, 'bd', -2, expected=expected, + conv=self._conv_pixels) def test_configure_compound(self): widget = self.create() @@ -308,7 +320,8 @@ class StandardOptionsTests: widget = self.create() self.checkPixelsParam(widget, 'highlightthickness', 0, 1.3, 2.6, 6, '10p') - self.checkParam(widget, 'highlightthickness', -2, expected=0, + expected = 0 if self._clip_highlightthickness else -2 + self.checkParam(widget, 'highlightthickness', -2, expected=expected, conv=self._conv_pixels) def test_configure_image(self): @@ -343,11 +356,7 @@ class StandardOptionsTests: def test_configure_justify(self): widget = self.create() self.checkEnumParam(widget, 'justify', 'left', 'right', 'center', - errmsg='bad justification "{}": must be ' - 'left, right, or center') - self.checkInvalidParam(widget, 'justify', '', - errmsg='ambiguous justification "": must be ' - 'left, right, or center') + fullname='justification') def test_configure_orient(self): widget = self.create() @@ -356,13 +365,19 @@ class StandardOptionsTests: def test_configure_padx(self): widget = self.create() - self.checkPixelsParam(widget, 'padx', 3, 4.4, 5.6, -2, '12m', + self.checkPixelsParam(widget, 'padx', 3, 4.4, 5.6, '12m', conv=self._conv_pad_pixels) + expected = 0 if self._clip_pad else -2 + self.checkParam(widget, 'padx', -2, expected=expected, + conv=self._conv_pad_pixels) def test_configure_pady(self): widget = self.create() - self.checkPixelsParam(widget, 'pady', 3, 4.4, 5.6, -2, '12m', + self.checkPixelsParam(widget, 'pady', 3, 4.4, 5.6, '12m', conv=self._conv_pad_pixels) + expected = 0 if self._clip_pad else -2 + self.checkParam(widget, 'pady', -2, expected=expected, + conv=self._conv_pad_pixels) def test_configure_relief(self): widget = self.create() @@ -415,7 +430,24 @@ class StandardOptionsTests: def test_configure_underline(self): widget = self.create() - self.checkIntegerParam(widget, 'underline', 0, 1, 10) + 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() @@ -445,7 +477,8 @@ class StandardOptionsTests: def test_configure_overrelief(self): widget = self.create() - self.checkReliefParam(widget, 'overrelief') + self.checkReliefParam(widget, 'overrelief', + allow_empty=(tk_version >= (8, 7))) def test_configure_selectcolor(self): widget = self.create() diff --git a/Lib/test/test_ttk/test_widgets.py b/Lib/test/test_ttk/test_widgets.py index 2e0702d..2eb2c44 100644 --- a/Lib/test/test_ttk/test_widgets.py +++ b/Lib/test/test_ttk/test_widgets.py @@ -57,6 +57,11 @@ class StandardTtkOptionsTests(StandardOptionsTests): self.assertEqual(widget2['class'], 'Foo') # XXX + def test_configure_relief(self): + widget = self.create() + self.checkReliefParam(widget, 'relief', + allow_empty=(tk_version >= (8, 7))) + class WidgetTest(AbstractTkTest, unittest.TestCase): """Tests methods available in every ttk widget.""" @@ -172,13 +177,11 @@ class AbstractLabelTest(AbstractWidgetTest): errmsg='image "spam" doesn\'t exist') def test_configure_compound(self): - options = 'none text image center top bottom left right'.split() - errmsg = ( - 'bad compound "{}": must be' - f' {", ".join(options[:-1])}, or {options[-1]}' - ) + values = ('none', 'text', 'image', 'center', 'top', 'bottom', 'left', 'right') + if tk_version >= (8, 7): + values += ('',) widget = self.create() - self.checkEnumParam(widget, 'compound', *options, errmsg=errmsg) + self.checkEnumParam(widget, 'compound', *values, allow_empty=True) def test_configure_state(self): widget = self.create() @@ -208,6 +211,13 @@ class LabelTest(AbstractLabelTest, unittest.TestCase): self.checkParam(widget, 'font', '-Adobe-Helvetica-Medium-R-Normal--*-120-*-*-*-*-*-*') + def test_configure_justify(self): + widget = self.create() + values = ('left', 'right', 'center') + if tk_version >= (8, 7): + values += ('',) + self.checkEnumParam(widget, 'justify', *values, + fullname='justification') @add_standard_options(StandardTtkOptionsTests) class ButtonTest(AbstractLabelTest, unittest.TestCase): @@ -223,7 +233,9 @@ class ButtonTest(AbstractLabelTest, unittest.TestCase): def test_configure_default(self): widget = self.create() - self.checkEnumParam(widget, 'default', 'normal', 'active', 'disabled') + values = ('normal', 'active', 'disabled') + self.checkEnumParam(widget, 'default', *values, + sort=tk_version >= (8, 7)) def test_invoke(self): success = [] @@ -275,7 +287,10 @@ class CheckbuttonTest(AbstractLabelTest, unittest.TestCase): cbtn['command'] = '' res = cbtn.invoke() - self.assertFalse(str(res)) + if tk_version >= (8, 7) and self.wantobjects: + self.assertEqual(res, ()) + else: + self.assertEqual(str(res), '') self.assertLessEqual(len(success), 1) self.assertEqual(cbtn['offvalue'], cbtn.tk.globalgetvar(cbtn['variable'])) @@ -513,7 +528,7 @@ class ComboboxTest(EntryTest, unittest.TestCase): self.assertEqual(self.combo.get(), getval) self.assertEqual(self.combo.current(), currval) - self.assertEqual(self.combo['values'], '') + self.assertIn(self.combo['values'], ((), '')) check_get_current('', -1) self.checkParam(self.combo, 'values', 'mon tue wed thur', @@ -638,8 +653,14 @@ class PanedWindowTest(AbstractWidgetTest, unittest.TestCase): child2 = ttk.Label(self.root) child3 = ttk.Label(self.root) - self.assertRaises(tkinter.TclError, self.paned.insert, 0, child) + if tk_version >= (8, 7): + self.paned.insert(0, child) + self.assertEqual(self.paned.panes(), (str(child),)) + self.paned.forget(0) + else: + self.assertRaises(tkinter.TclError, self.paned.insert, 0, child) + self.assertEqual(self.paned.panes(), ()) self.paned.insert('end', child2) self.paned.insert(0, child) self.assertEqual(self.paned.panes(), (str(child), str(child2))) @@ -742,7 +763,10 @@ class RadiobuttonTest(AbstractLabelTest, unittest.TestCase): cbtn2['command'] = '' res = cbtn2.invoke() - self.assertEqual(str(res), '') + if tk_version >= (8, 7) and self.wantobjects: + self.assertEqual(res, ()) + else: + self.assertEqual(str(res), '') self.assertLessEqual(len(success), 1) self.assertEqual(conv(cbtn2['value']), myvar.get()) self.assertEqual(myvar.get(), @@ -762,10 +786,11 @@ class MenubuttonTest(AbstractLabelTest, unittest.TestCase): def create(self, **kwargs): return ttk.Menubutton(self.root, **kwargs) - def test_direction(self): + def test_configure_direction(self): widget = self.create() - self.checkEnumParam(widget, 'direction', - 'above', 'below', 'left', 'right', 'flush') + values = ('above', 'below', 'left', 'right', 'flush') + self.checkEnumParam(widget, 'direction', *values, + sort=tk_version >= (8, 7)) def test_configure_menu(self): widget = self.create() @@ -928,11 +953,14 @@ class ScrollbarTest(AbstractWidgetTest, unittest.TestCase): return ttk.Scrollbar(self.root, **kwargs) -@add_standard_options(IntegerSizeTests, StandardTtkOptionsTests) +@add_standard_options(PixelSizeTests if tk_version >= (8, 7) else IntegerSizeTests, + StandardTtkOptionsTests) class NotebookTest(AbstractWidgetTest, unittest.TestCase): OPTIONS = ( 'class', 'cursor', 'height', 'padding', 'style', 'takefocus', 'width', ) + if tk_version >= (8, 7): + _conv_pixels = False def setUp(self): super().setUp() @@ -1051,7 +1079,11 @@ class NotebookTest(AbstractWidgetTest, unittest.TestCase): self.nb.insert(self.child1, child3) self.assertEqual(self.nb.tabs(), (str(child3), ) + tabs) self.nb.forget(child3) - self.assertRaises(tkinter.TclError, self.nb.insert, 2, child3) + if tk_version >= (8, 7): + self.nb.insert(2, child3) + self.assertEqual(self.nb.tabs(), (*tabs, str(child3))) + else: + self.assertRaises(tkinter.TclError, self.nb.insert, 2, child3) self.assertRaises(tkinter.TclError, self.nb.insert, -1, child3) # bad inserts @@ -1333,7 +1365,8 @@ class TreeviewTest(AbstractWidgetTest, unittest.TestCase): self.checkParam(widget, 'columns', 'a b c', expected=('a', 'b', 'c')) self.checkParam(widget, 'columns', ('a', 'b', 'c')) - self.checkParam(widget, 'columns', '') + self.checkParam(widget, 'columns', '', + expected=() if tk_version >= (8, 7) else '') def test_configure_displaycolumns(self): widget = self.create() @@ -1345,11 +1378,12 @@ class TreeviewTest(AbstractWidgetTest, unittest.TestCase): expected=('#all',)) self.checkParam(widget, 'displaycolumns', (2, 1, 0)) self.checkInvalidParam(widget, 'displaycolumns', ('a', 'b', 'd'), - errmsg='Invalid column index d') + errmsg='Invalid column index "?d"?') + errmsg = 'Column index "?{}"? out of bounds' self.checkInvalidParam(widget, 'displaycolumns', (1, 2, 3), - errmsg='Column index 3 out of bounds') + errmsg=errmsg.format(3)) self.checkInvalidParam(widget, 'displaycolumns', (1, -2), - errmsg='Column index -2 out of bounds') + errmsg=errmsg.format(-2)) def test_configure_height(self): widget = self.create() |