1import unittest
2import tkinter
3from tkinter import TclError
4import os
5from test.support import requires
6
7from tkinter.test.support import (tcl_version, requires_tcl,
8                                  get_tk_patchlevel, widget_eq,
9                                  AbstractDefaultRootTest)
10from tkinter.test.widget_tests import (
11    add_standard_options, noconv, pixels_round,
12    AbstractWidgetTest, StandardOptionsTests, IntegerSizeTests, PixelSizeTests,
13    setUpModule)
14
15requires('gui')
16
17
18def float_round(x):
19    return float(round(x))
20
21
22class AbstractToplevelTest(AbstractWidgetTest, PixelSizeTests):
23    _conv_pad_pixels = noconv
24
25    def test_configure_class(self):
26        widget = self.create()
27        self.assertEqual(widget['class'],
28                         widget.__class__.__name__.title())
29        self.checkInvalidParam(widget, 'class', 'Foo',
30                errmsg="can't modify -class option after widget is created")
31        widget2 = self.create(class_='Foo')
32        self.assertEqual(widget2['class'], 'Foo')
33
34    def test_configure_colormap(self):
35        widget = self.create()
36        self.assertEqual(widget['colormap'], '')
37        self.checkInvalidParam(widget, 'colormap', 'new',
38                errmsg="can't modify -colormap option after widget is created")
39        widget2 = self.create(colormap='new')
40        self.assertEqual(widget2['colormap'], 'new')
41
42    def test_configure_container(self):
43        widget = self.create()
44        self.assertEqual(widget['container'], 0 if self.wantobjects else '0')
45        self.checkInvalidParam(widget, 'container', 1,
46                errmsg="can't modify -container option after widget is created")
47        widget2 = self.create(container=True)
48        self.assertEqual(widget2['container'], 1 if self.wantobjects else '1')
49
50    def test_configure_visual(self):
51        widget = self.create()
52        self.assertEqual(widget['visual'], '')
53        self.checkInvalidParam(widget, 'visual', 'default',
54                errmsg="can't modify -visual option after widget is created")
55        widget2 = self.create(visual='default')
56        self.assertEqual(widget2['visual'], 'default')
57
58
59@add_standard_options(StandardOptionsTests)
60class ToplevelTest(AbstractToplevelTest, unittest.TestCase):
61    OPTIONS = (
62        'background', 'borderwidth',
63        'class', 'colormap', 'container', 'cursor', 'height',
64        'highlightbackground', 'highlightcolor', 'highlightthickness',
65        'menu', 'padx', 'pady', 'relief', 'screen',
66        'takefocus', 'use', 'visual', 'width',
67    )
68
69    def create(self, **kwargs):
70        return tkinter.Toplevel(self.root, **kwargs)
71
72    def test_configure_menu(self):
73        widget = self.create()
74        menu = tkinter.Menu(self.root)
75        self.checkParam(widget, 'menu', menu, eq=widget_eq)
76        self.checkParam(widget, 'menu', '')
77
78    def test_configure_screen(self):
79        widget = self.create()
80        self.assertEqual(widget['screen'], '')
81        try:
82            display = os.environ['DISPLAY']
83        except KeyError:
84            self.skipTest('No $DISPLAY set.')
85        self.checkInvalidParam(widget, 'screen', display,
86                errmsg="can't modify -screen option after widget is created")
87        widget2 = self.create(screen=display)
88        self.assertEqual(widget2['screen'], display)
89
90    def test_configure_use(self):
91        widget = self.create()
92        self.assertEqual(widget['use'], '')
93        parent = self.create(container=True)
94        wid = hex(parent.winfo_id())
95        with self.subTest(wid=wid):
96            widget2 = self.create(use=wid)
97            self.assertEqual(widget2['use'], wid)
98
99
100@add_standard_options(StandardOptionsTests)
101class FrameTest(AbstractToplevelTest, unittest.TestCase):
102    OPTIONS = (
103        'background', 'borderwidth',
104        'class', 'colormap', 'container', 'cursor', 'height',
105        'highlightbackground', 'highlightcolor', 'highlightthickness',
106        'padx', 'pady', 'relief', 'takefocus', 'visual', 'width',
107    )
108
109    def create(self, **kwargs):
110        return tkinter.Frame(self.root, **kwargs)
111
112
113@add_standard_options(StandardOptionsTests)
114class LabelFrameTest(AbstractToplevelTest, unittest.TestCase):
115    OPTIONS = (
116        'background', 'borderwidth',
117        'class', 'colormap', 'container', 'cursor',
118        'font', 'foreground', 'height',
119        'highlightbackground', 'highlightcolor', 'highlightthickness',
120        'labelanchor', 'labelwidget', 'padx', 'pady', 'relief',
121        'takefocus', 'text', 'visual', 'width',
122    )
123
124    def create(self, **kwargs):
125        return tkinter.LabelFrame(self.root, **kwargs)
126
127    def test_configure_labelanchor(self):
128        widget = self.create()
129        self.checkEnumParam(widget, 'labelanchor',
130                            'e', 'en', 'es', 'n', 'ne', 'nw',
131                            's', 'se', 'sw', 'w', 'wn', 'ws')
132        self.checkInvalidParam(widget, 'labelanchor', 'center')
133
134    def test_configure_labelwidget(self):
135        widget = self.create()
136        label = tkinter.Label(self.root, text='Mupp', name='foo')
137        self.checkParam(widget, 'labelwidget', label, expected='.foo')
138        label.destroy()
139
140
141class AbstractLabelTest(AbstractWidgetTest, IntegerSizeTests):
142    _conv_pixels = noconv
143
144    def test_configure_highlightthickness(self):
145        widget = self.create()
146        self.checkPixelsParam(widget, 'highlightthickness',
147                              0, 1.3, 2.6, 6, -2, '10p')
148
149
150@add_standard_options(StandardOptionsTests)
151class LabelTest(AbstractLabelTest, unittest.TestCase):
152    OPTIONS = (
153        'activebackground', 'activeforeground', 'anchor',
154        'background', 'bitmap', 'borderwidth', 'compound', 'cursor',
155        'disabledforeground', 'font', 'foreground', 'height',
156        'highlightbackground', 'highlightcolor', 'highlightthickness',
157        'image', 'justify', 'padx', 'pady', 'relief', 'state',
158        'takefocus', 'text', 'textvariable',
159        'underline', 'width', 'wraplength',
160    )
161
162    def create(self, **kwargs):
163        return tkinter.Label(self.root, **kwargs)
164
165
166@add_standard_options(StandardOptionsTests)
167class ButtonTest(AbstractLabelTest, unittest.TestCase):
168    OPTIONS = (
169        'activebackground', 'activeforeground', 'anchor',
170        'background', 'bitmap', 'borderwidth',
171        'command', 'compound', 'cursor', 'default',
172        'disabledforeground', 'font', 'foreground', 'height',
173        'highlightbackground', 'highlightcolor', 'highlightthickness',
174        'image', 'justify', 'overrelief', 'padx', 'pady', 'relief',
175        'repeatdelay', 'repeatinterval',
176        'state', 'takefocus', 'text', 'textvariable',
177        'underline', 'width', 'wraplength')
178
179    def create(self, **kwargs):
180        return tkinter.Button(self.root, **kwargs)
181
182    def test_configure_default(self):
183        widget = self.create()
184        self.checkEnumParam(widget, 'default', 'active', 'disabled', 'normal')
185
186
187@add_standard_options(StandardOptionsTests)
188class CheckbuttonTest(AbstractLabelTest, unittest.TestCase):
189    OPTIONS = (
190        'activebackground', 'activeforeground', 'anchor',
191        'background', 'bitmap', 'borderwidth',
192        'command', 'compound', 'cursor',
193        'disabledforeground', 'font', 'foreground', 'height',
194        'highlightbackground', 'highlightcolor', 'highlightthickness',
195        'image', 'indicatoron', 'justify',
196        'offrelief', 'offvalue', 'onvalue', 'overrelief',
197        'padx', 'pady', 'relief', 'selectcolor', 'selectimage', 'state',
198        'takefocus', 'text', 'textvariable',
199        'tristateimage', 'tristatevalue',
200        'underline', 'variable', 'width', 'wraplength',
201    )
202
203    def create(self, **kwargs):
204        return tkinter.Checkbutton(self.root, **kwargs)
205
206
207    def test_configure_offvalue(self):
208        widget = self.create()
209        self.checkParams(widget, 'offvalue', 1, 2.3, '', 'any string')
210
211    def test_configure_onvalue(self):
212        widget = self.create()
213        self.checkParams(widget, 'onvalue', 1, 2.3, '', 'any string')
214
215
216@add_standard_options(StandardOptionsTests)
217class RadiobuttonTest(AbstractLabelTest, unittest.TestCase):
218    OPTIONS = (
219        'activebackground', 'activeforeground', 'anchor',
220        'background', 'bitmap', 'borderwidth',
221        'command', 'compound', 'cursor',
222        'disabledforeground', 'font', 'foreground', 'height',
223        'highlightbackground', 'highlightcolor', 'highlightthickness',
224        'image', 'indicatoron', 'justify', 'offrelief', 'overrelief',
225        'padx', 'pady', 'relief', 'selectcolor', 'selectimage', 'state',
226        'takefocus', 'text', 'textvariable',
227        'tristateimage', 'tristatevalue',
228        'underline', 'value', 'variable', 'width', 'wraplength',
229    )
230
231    def create(self, **kwargs):
232        return tkinter.Radiobutton(self.root, **kwargs)
233
234    def test_configure_value(self):
235        widget = self.create()
236        self.checkParams(widget, 'value', 1, 2.3, '', 'any string')
237
238
239@add_standard_options(StandardOptionsTests)
240class MenubuttonTest(AbstractLabelTest, unittest.TestCase):
241    OPTIONS = (
242        'activebackground', 'activeforeground', 'anchor',
243        'background', 'bitmap', 'borderwidth',
244        'compound', 'cursor', 'direction',
245        'disabledforeground', 'font', 'foreground', 'height',
246        'highlightbackground', 'highlightcolor', 'highlightthickness',
247        'image', 'indicatoron', 'justify', 'menu',
248        'padx', 'pady', 'relief', 'state',
249        'takefocus', 'text', 'textvariable',
250        'underline', 'width', 'wraplength',
251    )
252    _conv_pixels = staticmethod(pixels_round)
253
254    def create(self, **kwargs):
255        return tkinter.Menubutton(self.root, **kwargs)
256
257    def test_configure_direction(self):
258        widget = self.create()
259        self.checkEnumParam(widget, 'direction',
260                'above', 'below', 'flush', 'left', 'right')
261
262    def test_configure_height(self):
263        widget = self.create()
264        self.checkIntegerParam(widget, 'height', 100, -100, 0, conv=str)
265
266    test_configure_highlightthickness = \
267        StandardOptionsTests.test_configure_highlightthickness
268
269    def test_configure_image(self):
270        widget = self.create()
271        image = tkinter.PhotoImage(master=self.root, name='image1')
272        self.checkParam(widget, 'image', image, conv=str)
273        errmsg = 'image "spam" doesn\'t exist'
274        with self.assertRaises(tkinter.TclError) as cm:
275            widget['image'] = 'spam'
276        if errmsg is not None:
277            self.assertEqual(str(cm.exception), errmsg)
278        with self.assertRaises(tkinter.TclError) as cm:
279            widget.configure({'image': 'spam'})
280        if errmsg is not None:
281            self.assertEqual(str(cm.exception), errmsg)
282
283    def test_configure_menu(self):
284        widget = self.create()
285        menu = tkinter.Menu(widget, name='menu')
286        self.checkParam(widget, 'menu', menu, eq=widget_eq)
287        menu.destroy()
288
289    def test_configure_padx(self):
290        widget = self.create()
291        self.checkPixelsParam(widget, 'padx', 3, 4.4, 5.6, '12m')
292        self.checkParam(widget, 'padx', -2, expected=0)
293
294    def test_configure_pady(self):
295        widget = self.create()
296        self.checkPixelsParam(widget, 'pady', 3, 4.4, 5.6, '12m')
297        self.checkParam(widget, 'pady', -2, expected=0)
298
299    def test_configure_width(self):
300        widget = self.create()
301        self.checkIntegerParam(widget, 'width', 402, -402, 0, conv=str)
302
303
304class OptionMenuTest(MenubuttonTest, unittest.TestCase):
305
306    def create(self, default='b', values=('a', 'b', 'c'), **kwargs):
307        return tkinter.OptionMenu(self.root, None, default, *values, **kwargs)
308
309    def test_bad_kwarg(self):
310        with self.assertRaisesRegex(TclError, r"^unknown option -image$"):
311            tkinter.OptionMenu(self.root, None, 'b', image='')
312
313
314@add_standard_options(IntegerSizeTests, StandardOptionsTests)
315class EntryTest(AbstractWidgetTest, unittest.TestCase):
316    OPTIONS = (
317        'background', 'borderwidth', 'cursor',
318        'disabledbackground', 'disabledforeground',
319        'exportselection', 'font', 'foreground',
320        'highlightbackground', 'highlightcolor', 'highlightthickness',
321        'insertbackground', 'insertborderwidth',
322        'insertofftime', 'insertontime', 'insertwidth',
323        'invalidcommand', 'justify', 'readonlybackground', 'relief',
324        'selectbackground', 'selectborderwidth', 'selectforeground',
325        'show', 'state', 'takefocus', 'textvariable',
326        'validate', 'validatecommand', 'width', 'xscrollcommand',
327    )
328
329    def create(self, **kwargs):
330        return tkinter.Entry(self.root, **kwargs)
331
332    def test_configure_disabledbackground(self):
333        widget = self.create()
334        self.checkColorParam(widget, 'disabledbackground')
335
336    def test_configure_insertborderwidth(self):
337        widget = self.create(insertwidth=100)
338        self.checkPixelsParam(widget, 'insertborderwidth',
339                              0, 1.3, 2.6, 6, -2, '10p')
340        # insertborderwidth is bounded above by a half of insertwidth.
341        self.checkParam(widget, 'insertborderwidth', 60, expected=100//2)
342
343    def test_configure_insertwidth(self):
344        widget = self.create()
345        self.checkPixelsParam(widget, 'insertwidth', 1.3, 3.6, '10p')
346        self.checkParam(widget, 'insertwidth', 0.1, expected=2)
347        self.checkParam(widget, 'insertwidth', -2, expected=2)
348        if pixels_round(0.9) <= 0:
349            self.checkParam(widget, 'insertwidth', 0.9, expected=2)
350        else:
351            self.checkParam(widget, 'insertwidth', 0.9, expected=1)
352
353    def test_configure_invalidcommand(self):
354        widget = self.create()
355        self.checkCommandParam(widget, 'invalidcommand')
356        self.checkCommandParam(widget, 'invcmd')
357
358    def test_configure_readonlybackground(self):
359        widget = self.create()
360        self.checkColorParam(widget, 'readonlybackground')
361
362    def test_configure_show(self):
363        widget = self.create()
364        self.checkParam(widget, 'show', '*')
365        self.checkParam(widget, 'show', '')
366        self.checkParam(widget, 'show', ' ')
367
368    def test_configure_state(self):
369        widget = self.create()
370        self.checkEnumParam(widget, 'state',
371                            'disabled', 'normal', 'readonly')
372
373    def test_configure_validate(self):
374        widget = self.create()
375        self.checkEnumParam(widget, 'validate',
376                'all', 'key', 'focus', 'focusin', 'focusout', 'none')
377
378    def test_configure_validatecommand(self):
379        widget = self.create()
380        self.checkCommandParam(widget, 'validatecommand')
381        self.checkCommandParam(widget, 'vcmd')
382
383    def test_selection_methods(self):
384        widget = self.create()
385        widget.insert(0, '12345')
386        self.assertFalse(widget.selection_present())
387        widget.selection_range(0, 'end')
388        self.assertEqual(widget.selection_get(), '12345')
389        self.assertTrue(widget.selection_present())
390        widget.selection_from(1)
391        widget.selection_to(2)
392        self.assertEqual(widget.selection_get(), '2')
393        widget.selection_range(3, 4)
394        self.assertEqual(widget.selection_get(), '4')
395        widget.selection_clear()
396        self.assertFalse(widget.selection_present())
397        widget.selection_range(0, 'end')
398        widget.selection_adjust(4)
399        self.assertEqual(widget.selection_get(), '1234')
400        widget.selection_adjust(1)
401        self.assertEqual(widget.selection_get(), '234')
402        widget.selection_adjust(5)
403        self.assertEqual(widget.selection_get(), '2345')
404        widget.selection_adjust(0)
405        self.assertEqual(widget.selection_get(), '12345')
406        widget.selection_adjust(0)
407
408
409@add_standard_options(StandardOptionsTests)
410class SpinboxTest(EntryTest, unittest.TestCase):
411    OPTIONS = (
412        'activebackground', 'background', 'borderwidth',
413        'buttonbackground', 'buttoncursor', 'buttondownrelief', 'buttonuprelief',
414        'command', 'cursor', 'disabledbackground', 'disabledforeground',
415        'exportselection', 'font', 'foreground', 'format', 'from',
416        'highlightbackground', 'highlightcolor', 'highlightthickness',
417        'increment',
418        'insertbackground', 'insertborderwidth',
419        'insertofftime', 'insertontime', 'insertwidth',
420        'invalidcommand', 'justify', 'relief', 'readonlybackground',
421        'repeatdelay', 'repeatinterval',
422        'selectbackground', 'selectborderwidth', 'selectforeground',
423        'state', 'takefocus', 'textvariable', 'to',
424        'validate', 'validatecommand', 'values',
425        'width', 'wrap', 'xscrollcommand',
426    )
427
428    def create(self, **kwargs):
429        return tkinter.Spinbox(self.root, **kwargs)
430
431    test_configure_show = None
432
433    def test_configure_buttonbackground(self):
434        widget = self.create()
435        self.checkColorParam(widget, 'buttonbackground')
436
437    def test_configure_buttoncursor(self):
438        widget = self.create()
439        self.checkCursorParam(widget, 'buttoncursor')
440
441    def test_configure_buttondownrelief(self):
442        widget = self.create()
443        self.checkReliefParam(widget, 'buttondownrelief')
444
445    def test_configure_buttonuprelief(self):
446        widget = self.create()
447        self.checkReliefParam(widget, 'buttonuprelief')
448
449    def test_configure_format(self):
450        widget = self.create()
451        self.checkParam(widget, 'format', '%2f')
452        self.checkParam(widget, 'format', '%2.2f')
453        self.checkParam(widget, 'format', '%.2f')
454        self.checkParam(widget, 'format', '%2.f')
455        self.checkInvalidParam(widget, 'format', '%2e-1f')
456        self.checkInvalidParam(widget, 'format', '2.2')
457        self.checkInvalidParam(widget, 'format', '%2.-2f')
458        self.checkParam(widget, 'format', '%-2.02f')
459        self.checkParam(widget, 'format', '% 2.02f')
460        self.checkParam(widget, 'format', '% -2.200f')
461        self.checkParam(widget, 'format', '%09.200f')
462        self.checkInvalidParam(widget, 'format', '%d')
463
464    def test_configure_from(self):
465        widget = self.create()
466        self.checkParam(widget, 'to', 100.0)
467        self.checkFloatParam(widget, 'from', -10, 10.2, 11.7)
468        self.checkInvalidParam(widget, 'from', 200,
469                errmsg='-to value must be greater than -from value')
470
471    def test_configure_increment(self):
472        widget = self.create()
473        self.checkFloatParam(widget, 'increment', -1, 1, 10.2, 12.8, 0)
474
475    def test_configure_to(self):
476        widget = self.create()
477        self.checkParam(widget, 'from', -100.0)
478        self.checkFloatParam(widget, 'to', -10, 10.2, 11.7)
479        self.checkInvalidParam(widget, 'to', -200,
480                errmsg='-to value must be greater than -from value')
481
482    def test_configure_values(self):
483        # XXX
484        widget = self.create()
485        self.assertEqual(widget['values'], '')
486        self.checkParam(widget, 'values', 'mon tue wed thur')
487        self.checkParam(widget, 'values', ('mon', 'tue', 'wed', 'thur'),
488                        expected='mon tue wed thur')
489        self.checkParam(widget, 'values', (42, 3.14, '', 'any string'),
490                        expected='42 3.14 {} {any string}')
491        self.checkParam(widget, 'values', '')
492
493    def test_configure_wrap(self):
494        widget = self.create()
495        self.checkBooleanParam(widget, 'wrap')
496
497    def test_bbox(self):
498        widget = self.create()
499        self.assertIsBoundingBox(widget.bbox(0))
500        self.assertRaises(tkinter.TclError, widget.bbox, 'noindex')
501        self.assertRaises(tkinter.TclError, widget.bbox, None)
502        self.assertRaises(TypeError, widget.bbox)
503        self.assertRaises(TypeError, widget.bbox, 0, 1)
504
505    def test_selection_methods(self):
506        widget = self.create()
507        widget.insert(0, '12345')
508        self.assertFalse(widget.selection_present())
509        widget.selection_range(0, 'end')
510        self.assertEqual(widget.selection_get(), '12345')
511        self.assertTrue(widget.selection_present())
512        widget.selection_from(1)
513        widget.selection_to(2)
514        self.assertEqual(widget.selection_get(), '2')
515        widget.selection_range(3, 4)
516        self.assertEqual(widget.selection_get(), '4')
517        widget.selection_clear()
518        self.assertFalse(widget.selection_present())
519        widget.selection_range(0, 'end')
520        widget.selection_adjust(4)
521        self.assertEqual(widget.selection_get(), '1234')
522        widget.selection_adjust(1)
523        self.assertEqual(widget.selection_get(), '234')
524        widget.selection_adjust(5)
525        self.assertEqual(widget.selection_get(), '2345')
526        widget.selection_adjust(0)
527        self.assertEqual(widget.selection_get(), '12345')
528
529    def test_selection_element(self):
530        widget = self.create()
531        self.assertEqual(widget.selection_element(), "none")
532        widget.selection_element("buttonup")
533        self.assertEqual(widget.selection_element(), "buttonup")
534        widget.selection_element("buttondown")
535        self.assertEqual(widget.selection_element(), "buttondown")
536
537
538@add_standard_options(StandardOptionsTests)
539class TextTest(AbstractWidgetTest, unittest.TestCase):
540    OPTIONS = (
541        'autoseparators', 'background', 'blockcursor', 'borderwidth',
542        'cursor', 'endline', 'exportselection',
543        'font', 'foreground', 'height',
544        'highlightbackground', 'highlightcolor', 'highlightthickness',
545        'inactiveselectbackground', 'insertbackground', 'insertborderwidth',
546        'insertofftime', 'insertontime', 'insertunfocussed', 'insertwidth',
547        'maxundo', 'padx', 'pady', 'relief',
548        'selectbackground', 'selectborderwidth', 'selectforeground',
549        'setgrid', 'spacing1', 'spacing2', 'spacing3', 'startline', 'state',
550        'tabs', 'tabstyle', 'takefocus', 'undo', 'width', 'wrap',
551        'xscrollcommand', 'yscrollcommand',
552    )
553    if tcl_version < (8, 5):
554        _stringify = True
555
556    def create(self, **kwargs):
557        return tkinter.Text(self.root, **kwargs)
558
559    def test_configure_autoseparators(self):
560        widget = self.create()
561        self.checkBooleanParam(widget, 'autoseparators')
562
563    @requires_tcl(8, 5)
564    def test_configure_blockcursor(self):
565        widget = self.create()
566        self.checkBooleanParam(widget, 'blockcursor')
567
568    @requires_tcl(8, 5)
569    def test_configure_endline(self):
570        widget = self.create()
571        text = '\n'.join('Line %d' for i in range(100))
572        widget.insert('end', text)
573        self.checkParam(widget, 'endline', 200, expected='')
574        self.checkParam(widget, 'endline', -10, expected='')
575        self.checkInvalidParam(widget, 'endline', 'spam',
576                errmsg='expected integer but got "spam"')
577        self.checkParam(widget, 'endline', 50)
578        self.checkParam(widget, 'startline', 15)
579        self.checkInvalidParam(widget, 'endline', 10,
580                errmsg='-startline must be less than or equal to -endline')
581
582    def test_configure_height(self):
583        widget = self.create()
584        self.checkPixelsParam(widget, 'height', 100, 101.2, 102.6, '3c')
585        self.checkParam(widget, 'height', -100, expected=1)
586        self.checkParam(widget, 'height', 0, expected=1)
587
588    def test_configure_maxundo(self):
589        widget = self.create()
590        self.checkIntegerParam(widget, 'maxundo', 0, 5, -1)
591
592    @requires_tcl(8, 5)
593    def test_configure_inactiveselectbackground(self):
594        widget = self.create()
595        self.checkColorParam(widget, 'inactiveselectbackground')
596
597    @requires_tcl(8, 6)
598    def test_configure_insertunfocussed(self):
599        widget = self.create()
600        self.checkEnumParam(widget, 'insertunfocussed',
601                            'hollow', 'none', 'solid')
602
603    def test_configure_selectborderwidth(self):
604        widget = self.create()
605        self.checkPixelsParam(widget, 'selectborderwidth',
606                              1.3, 2.6, -2, '10p', conv=noconv,
607                              keep_orig=tcl_version >= (8, 5))
608
609    def test_configure_spacing1(self):
610        widget = self.create()
611        self.checkPixelsParam(widget, 'spacing1', 20, 21.4, 22.6, '0.5c')
612        self.checkParam(widget, 'spacing1', -5, expected=0)
613
614    def test_configure_spacing2(self):
615        widget = self.create()
616        self.checkPixelsParam(widget, 'spacing2', 5, 6.4, 7.6, '0.1c')
617        self.checkParam(widget, 'spacing2', -1, expected=0)
618
619    def test_configure_spacing3(self):
620        widget = self.create()
621        self.checkPixelsParam(widget, 'spacing3', 20, 21.4, 22.6, '0.5c')
622        self.checkParam(widget, 'spacing3', -10, expected=0)
623
624    @requires_tcl(8, 5)
625    def test_configure_startline(self):
626        widget = self.create()
627        text = '\n'.join('Line %d' for i in range(100))
628        widget.insert('end', text)
629        self.checkParam(widget, 'startline', 200, expected='')
630        self.checkParam(widget, 'startline', -10, expected='')
631        self.checkInvalidParam(widget, 'startline', 'spam',
632                errmsg='expected integer but got "spam"')
633        self.checkParam(widget, 'startline', 10)
634        self.checkParam(widget, 'endline', 50)
635        self.checkInvalidParam(widget, 'startline', 70,
636                errmsg='-startline must be less than or equal to -endline')
637
638    def test_configure_state(self):
639        widget = self.create()
640        if tcl_version < (8, 5):
641            self.checkParams(widget, 'state', 'disabled', 'normal')
642        else:
643            self.checkEnumParam(widget, 'state', 'disabled', 'normal')
644
645    def test_configure_tabs(self):
646        widget = self.create()
647        if get_tk_patchlevel() < (8, 5, 11):
648            self.checkParam(widget, 'tabs', (10.2, 20.7, '1i', '2i'),
649                            expected=('10.2', '20.7', '1i', '2i'))
650        else:
651            self.checkParam(widget, 'tabs', (10.2, 20.7, '1i', '2i'))
652        self.checkParam(widget, 'tabs', '10.2 20.7 1i 2i',
653                        expected=('10.2', '20.7', '1i', '2i'))
654        self.checkParam(widget, 'tabs', '2c left 4c 6c center',
655                        expected=('2c', 'left', '4c', '6c', 'center'))
656        self.checkInvalidParam(widget, 'tabs', 'spam',
657                               errmsg='bad screen distance "spam"',
658                               keep_orig=tcl_version >= (8, 5))
659
660    @requires_tcl(8, 5)
661    def test_configure_tabstyle(self):
662        widget = self.create()
663        self.checkEnumParam(widget, 'tabstyle', 'tabular', 'wordprocessor')
664
665    def test_configure_undo(self):
666        widget = self.create()
667        self.checkBooleanParam(widget, 'undo')
668
669    def test_configure_width(self):
670        widget = self.create()
671        self.checkIntegerParam(widget, 'width', 402)
672        self.checkParam(widget, 'width', -402, expected=1)
673        self.checkParam(widget, 'width', 0, expected=1)
674
675    def test_configure_wrap(self):
676        widget = self.create()
677        if tcl_version < (8, 5):
678            self.checkParams(widget, 'wrap', 'char', 'none', 'word')
679        else:
680            self.checkEnumParam(widget, 'wrap', 'char', 'none', 'word')
681
682    def test_bbox(self):
683        widget = self.create()
684        self.assertIsBoundingBox(widget.bbox('1.1'))
685        self.assertIsNone(widget.bbox('end'))
686        self.assertRaises(tkinter.TclError, widget.bbox, 'noindex')
687        self.assertRaises(tkinter.TclError, widget.bbox, None)
688        self.assertRaises(TypeError, widget.bbox)
689        self.assertRaises(TypeError, widget.bbox, '1.1', 'end')
690
691
692@add_standard_options(PixelSizeTests, StandardOptionsTests)
693class CanvasTest(AbstractWidgetTest, unittest.TestCase):
694    OPTIONS = (
695        'background', 'borderwidth',
696        'closeenough', 'confine', 'cursor', 'height',
697        'highlightbackground', 'highlightcolor', 'highlightthickness',
698        'insertbackground', 'insertborderwidth',
699        'insertofftime', 'insertontime', 'insertwidth',
700        'offset', 'relief', 'scrollregion',
701        'selectbackground', 'selectborderwidth', 'selectforeground',
702        'state', 'takefocus',
703        'xscrollcommand', 'xscrollincrement',
704        'yscrollcommand', 'yscrollincrement', 'width',
705    )
706
707    _conv_pixels = round
708    _stringify = True
709
710    def create(self, **kwargs):
711        return tkinter.Canvas(self.root, **kwargs)
712
713    def test_configure_closeenough(self):
714        widget = self.create()
715        self.checkFloatParam(widget, 'closeenough', 24, 2.4, 3.6, -3,
716                             conv=float)
717
718    def test_configure_confine(self):
719        widget = self.create()
720        self.checkBooleanParam(widget, 'confine')
721
722    def test_configure_offset(self):
723        widget = self.create()
724        self.assertEqual(widget['offset'], '0,0')
725        self.checkParams(widget, 'offset',
726                'n', 'ne', 'e', 'se', 's', 'sw', 'w', 'nw', 'center')
727        self.checkParam(widget, 'offset', '10,20')
728        self.checkParam(widget, 'offset', '#5,6')
729        self.checkInvalidParam(widget, 'offset', 'spam')
730
731    def test_configure_scrollregion(self):
732        widget = self.create()
733        self.checkParam(widget, 'scrollregion', '0 0 200 150')
734        self.checkParam(widget, 'scrollregion', (0, 0, 200, 150),
735                        expected='0 0 200 150')
736        self.checkParam(widget, 'scrollregion', '')
737        self.checkInvalidParam(widget, 'scrollregion', 'spam',
738                               errmsg='bad scrollRegion "spam"')
739        self.checkInvalidParam(widget, 'scrollregion', (0, 0, 200, 'spam'))
740        self.checkInvalidParam(widget, 'scrollregion', (0, 0, 200))
741        self.checkInvalidParam(widget, 'scrollregion', (0, 0, 200, 150, 0))
742
743    def test_configure_state(self):
744        widget = self.create()
745        self.checkEnumParam(widget, 'state', 'disabled', 'normal',
746                errmsg='bad state value "{}": must be normal or disabled')
747
748    def test_configure_xscrollincrement(self):
749        widget = self.create()
750        self.checkPixelsParam(widget, 'xscrollincrement',
751                              40, 0, 41.2, 43.6, -40, '0.5i')
752
753    def test_configure_yscrollincrement(self):
754        widget = self.create()
755        self.checkPixelsParam(widget, 'yscrollincrement',
756                              10, 0, 11.2, 13.6, -10, '0.1i')
757
758    @requires_tcl(8, 6)
759    def test_moveto(self):
760        widget = self.create()
761        i1 = widget.create_rectangle(1, 1, 20, 20, tags='group')
762        i2 = widget.create_rectangle(30, 30, 50, 70, tags='group')
763        x1, y1, _, _ = widget.bbox(i1)
764        x2, y2, _, _ = widget.bbox(i2)
765        widget.moveto('group', 200, 100)
766        x1_2, y1_2, _, _ = widget.bbox(i1)
767        x2_2, y2_2, _, _ = widget.bbox(i2)
768        self.assertEqual(x1_2, 200)
769        self.assertEqual(y1_2, 100)
770        self.assertEqual(x2 - x1, x2_2 - x1_2)
771        self.assertEqual(y2 - y1, y2_2 - y1_2)
772        widget.tag_lower(i2, i1)
773        widget.moveto('group', y=50)
774        x1_3, y1_3, _, _ = widget.bbox(i1)
775        x2_3, y2_3, _, _ = widget.bbox(i2)
776        self.assertEqual(y2_3, 50)
777        self.assertEqual(x2_3, x2_2)
778        self.assertEqual(x2_2 - x1_2, x2_3 - x1_3)
779        self.assertEqual(y2_2 - y1_2, y2_3 - y1_3)
780
781
782@add_standard_options(IntegerSizeTests, StandardOptionsTests)
783class ListboxTest(AbstractWidgetTest, unittest.TestCase):
784    OPTIONS = (
785        'activestyle', 'background', 'borderwidth', 'cursor',
786        'disabledforeground', 'exportselection',
787        'font', 'foreground', 'height',
788        'highlightbackground', 'highlightcolor', 'highlightthickness',
789        'justify', 'listvariable', 'relief',
790        'selectbackground', 'selectborderwidth', 'selectforeground',
791        'selectmode', 'setgrid', 'state',
792        'takefocus', 'width', 'xscrollcommand', 'yscrollcommand',
793    )
794
795    def create(self, **kwargs):
796        return tkinter.Listbox(self.root, **kwargs)
797
798    def test_configure_activestyle(self):
799        widget = self.create()
800        self.checkEnumParam(widget, 'activestyle',
801                            'dotbox', 'none', 'underline')
802
803    test_justify = requires_tcl(8, 6, 5)(StandardOptionsTests.test_configure_justify)
804
805    def test_configure_listvariable(self):
806        widget = self.create()
807        var = tkinter.DoubleVar(self.root)
808        self.checkVariableParam(widget, 'listvariable', var)
809
810    def test_configure_selectmode(self):
811        widget = self.create()
812        self.checkParam(widget, 'selectmode', 'single')
813        self.checkParam(widget, 'selectmode', 'browse')
814        self.checkParam(widget, 'selectmode', 'multiple')
815        self.checkParam(widget, 'selectmode', 'extended')
816
817    def test_configure_state(self):
818        widget = self.create()
819        self.checkEnumParam(widget, 'state', 'disabled', 'normal')
820
821    def test_itemconfigure(self):
822        widget = self.create()
823        with self.assertRaisesRegex(TclError, 'item number "0" out of range'):
824            widget.itemconfigure(0)
825        colors = 'red orange yellow green blue white violet'.split()
826        widget.insert('end', *colors)
827        for i, color in enumerate(colors):
828            widget.itemconfigure(i, background=color)
829        with self.assertRaises(TypeError):
830            widget.itemconfigure()
831        with self.assertRaisesRegex(TclError, 'bad listbox index "red"'):
832            widget.itemconfigure('red')
833        self.assertEqual(widget.itemconfigure(0, 'background'),
834                         ('background', 'background', 'Background', '', 'red'))
835        self.assertEqual(widget.itemconfigure('end', 'background'),
836                         ('background', 'background', 'Background', '', 'violet'))
837        self.assertEqual(widget.itemconfigure('@0,0', 'background'),
838                         ('background', 'background', 'Background', '', 'red'))
839
840        d = widget.itemconfigure(0)
841        self.assertIsInstance(d, dict)
842        for k, v in d.items():
843            self.assertIn(len(v), (2, 5))
844            if len(v) == 5:
845                self.assertEqual(v, widget.itemconfigure(0, k))
846                self.assertEqual(v[4], widget.itemcget(0, k))
847
848    def check_itemconfigure(self, name, value):
849        widget = self.create()
850        widget.insert('end', 'a', 'b', 'c', 'd')
851        widget.itemconfigure(0, **{name: value})
852        self.assertEqual(widget.itemconfigure(0, name)[4], value)
853        self.assertEqual(widget.itemcget(0, name), value)
854        with self.assertRaisesRegex(TclError, 'unknown color name "spam"'):
855            widget.itemconfigure(0, **{name: 'spam'})
856
857    def test_itemconfigure_background(self):
858        self.check_itemconfigure('background', '#ff0000')
859
860    def test_itemconfigure_bg(self):
861        self.check_itemconfigure('bg', '#ff0000')
862
863    def test_itemconfigure_fg(self):
864        self.check_itemconfigure('fg', '#110022')
865
866    def test_itemconfigure_foreground(self):
867        self.check_itemconfigure('foreground', '#110022')
868
869    def test_itemconfigure_selectbackground(self):
870        self.check_itemconfigure('selectbackground', '#110022')
871
872    def test_itemconfigure_selectforeground(self):
873        self.check_itemconfigure('selectforeground', '#654321')
874
875    def test_box(self):
876        lb = self.create()
877        lb.insert(0, *('el%d' % i for i in range(8)))
878        lb.pack()
879        self.assertIsBoundingBox(lb.bbox(0))
880        self.assertIsNone(lb.bbox(-1))
881        self.assertIsNone(lb.bbox(10))
882        self.assertRaises(TclError, lb.bbox, 'noindex')
883        self.assertRaises(TclError, lb.bbox, None)
884        self.assertRaises(TypeError, lb.bbox)
885        self.assertRaises(TypeError, lb.bbox, 0, 1)
886
887    def test_curselection(self):
888        lb = self.create()
889        lb.insert(0, *('el%d' % i for i in range(8)))
890        lb.selection_clear(0, tkinter.END)
891        lb.selection_set(2, 4)
892        lb.selection_set(6)
893        self.assertEqual(lb.curselection(), (2, 3, 4, 6))
894        self.assertRaises(TypeError, lb.curselection, 0)
895
896    def test_get(self):
897        lb = self.create()
898        lb.insert(0, *('el%d' % i for i in range(8)))
899        self.assertEqual(lb.get(0), 'el0')
900        self.assertEqual(lb.get(3), 'el3')
901        self.assertEqual(lb.get('end'), 'el7')
902        self.assertEqual(lb.get(8), '')
903        self.assertEqual(lb.get(-1), '')
904        self.assertEqual(lb.get(3, 5), ('el3', 'el4', 'el5'))
905        self.assertEqual(lb.get(5, 'end'), ('el5', 'el6', 'el7'))
906        self.assertEqual(lb.get(5, 0), ())
907        self.assertEqual(lb.get(0, 0), ('el0',))
908        self.assertRaises(TclError, lb.get, 'noindex')
909        self.assertRaises(TclError, lb.get, None)
910        self.assertRaises(TypeError, lb.get)
911        self.assertRaises(TclError, lb.get, 'end', 'noindex')
912        self.assertRaises(TypeError, lb.get, 1, 2, 3)
913        self.assertRaises(TclError, lb.get, 2.4)
914
915
916@add_standard_options(PixelSizeTests, StandardOptionsTests)
917class ScaleTest(AbstractWidgetTest, unittest.TestCase):
918    OPTIONS = (
919        'activebackground', 'background', 'bigincrement', 'borderwidth',
920        'command', 'cursor', 'digits', 'font', 'foreground', 'from',
921        'highlightbackground', 'highlightcolor', 'highlightthickness',
922        'label', 'length', 'orient', 'relief',
923        'repeatdelay', 'repeatinterval',
924        'resolution', 'showvalue', 'sliderlength', 'sliderrelief', 'state',
925        'takefocus', 'tickinterval', 'to', 'troughcolor', 'variable', 'width',
926    )
927    default_orient = 'vertical'
928
929    def create(self, **kwargs):
930        return tkinter.Scale(self.root, **kwargs)
931
932    def test_configure_bigincrement(self):
933        widget = self.create()
934        self.checkFloatParam(widget, 'bigincrement', 12.4, 23.6, -5)
935
936    def test_configure_digits(self):
937        widget = self.create()
938        self.checkIntegerParam(widget, 'digits', 5, 0)
939
940    def test_configure_from(self):
941        widget = self.create()
942        conv = False if get_tk_patchlevel() >= (8, 6, 10) else float_round
943        self.checkFloatParam(widget, 'from', 100, 14.9, 15.1, conv=conv)
944
945    def test_configure_label(self):
946        widget = self.create()
947        self.checkParam(widget, 'label', 'any string')
948        self.checkParam(widget, 'label', '')
949
950    def test_configure_length(self):
951        widget = self.create()
952        self.checkPixelsParam(widget, 'length', 130, 131.2, 135.6, '5i')
953
954    def test_configure_resolution(self):
955        widget = self.create()
956        self.checkFloatParam(widget, 'resolution', 4.2, 0, 6.7, -2)
957
958    def test_configure_showvalue(self):
959        widget = self.create()
960        self.checkBooleanParam(widget, 'showvalue')
961
962    def test_configure_sliderlength(self):
963        widget = self.create()
964        self.checkPixelsParam(widget, 'sliderlength',
965                              10, 11.2, 15.6, -3, '3m')
966
967    def test_configure_sliderrelief(self):
968        widget = self.create()
969        self.checkReliefParam(widget, 'sliderrelief')
970
971    def test_configure_tickinterval(self):
972        widget = self.create()
973        self.checkFloatParam(widget, 'tickinterval', 1, 4.3, 7.6, 0,
974                             conv=float_round)
975        self.checkParam(widget, 'tickinterval', -2, expected=2,
976                        conv=float_round)
977
978    def test_configure_to(self):
979        widget = self.create()
980        self.checkFloatParam(widget, 'to', 300, 14.9, 15.1, -10,
981                             conv=float_round)
982
983
984@add_standard_options(PixelSizeTests, StandardOptionsTests)
985class ScrollbarTest(AbstractWidgetTest, unittest.TestCase):
986    OPTIONS = (
987        'activebackground', 'activerelief',
988        'background', 'borderwidth',
989        'command', 'cursor', 'elementborderwidth',
990        'highlightbackground', 'highlightcolor', 'highlightthickness',
991        'jump', 'orient', 'relief',
992        'repeatdelay', 'repeatinterval',
993        'takefocus', 'troughcolor', 'width',
994    )
995    _conv_pixels = round
996    _stringify = True
997    default_orient = 'vertical'
998
999    def create(self, **kwargs):
1000        return tkinter.Scrollbar(self.root, **kwargs)
1001
1002    def test_configure_activerelief(self):
1003        widget = self.create()
1004        self.checkReliefParam(widget, 'activerelief')
1005
1006    def test_configure_elementborderwidth(self):
1007        widget = self.create()
1008        self.checkPixelsParam(widget, 'elementborderwidth', 4.3, 5.6, -2, '1m')
1009
1010    def test_configure_orient(self):
1011        widget = self.create()
1012        self.checkEnumParam(widget, 'orient', 'vertical', 'horizontal',
1013                errmsg='bad orientation "{}": must be vertical or horizontal')
1014
1015    def test_activate(self):
1016        sb = self.create()
1017        for e in ('arrow1', 'slider', 'arrow2'):
1018            sb.activate(e)
1019            self.assertEqual(sb.activate(), e)
1020        sb.activate('')
1021        self.assertIsNone(sb.activate())
1022        self.assertRaises(TypeError, sb.activate, 'arrow1', 'arrow2')
1023
1024    def test_set(self):
1025        sb = self.create()
1026        sb.set(0.2, 0.4)
1027        self.assertEqual(sb.get(), (0.2, 0.4))
1028        self.assertRaises(TclError, sb.set, 'abc', 'def')
1029        self.assertRaises(TclError, sb.set, 0.6, 'def')
1030        self.assertRaises(TclError, sb.set, 0.6, None)
1031        self.assertRaises(TypeError, sb.set, 0.6)
1032        self.assertRaises(TypeError, sb.set, 0.6, 0.7, 0.8)
1033
1034
1035@add_standard_options(StandardOptionsTests)
1036class PanedWindowTest(AbstractWidgetTest, unittest.TestCase):
1037    OPTIONS = (
1038        'background', 'borderwidth', 'cursor',
1039        'handlepad', 'handlesize', 'height',
1040        'opaqueresize', 'orient',
1041        'proxybackground', 'proxyborderwidth', 'proxyrelief',
1042        'relief',
1043        'sashcursor', 'sashpad', 'sashrelief', 'sashwidth',
1044        'showhandle', 'width',
1045    )
1046    default_orient = 'horizontal'
1047
1048    def create(self, **kwargs):
1049        return tkinter.PanedWindow(self.root, **kwargs)
1050
1051    def test_configure_handlepad(self):
1052        widget = self.create()
1053        self.checkPixelsParam(widget, 'handlepad', 5, 6.4, 7.6, -3, '1m')
1054
1055    def test_configure_handlesize(self):
1056        widget = self.create()
1057        self.checkPixelsParam(widget, 'handlesize', 8, 9.4, 10.6, -3, '2m',
1058                              conv=noconv)
1059
1060    def test_configure_height(self):
1061        widget = self.create()
1062        self.checkPixelsParam(widget, 'height', 100, 101.2, 102.6, -100, 0, '1i',
1063                              conv=noconv)
1064
1065    def test_configure_opaqueresize(self):
1066        widget = self.create()
1067        self.checkBooleanParam(widget, 'opaqueresize')
1068
1069    @requires_tcl(8, 6, 5)
1070    def test_configure_proxybackground(self):
1071        widget = self.create()
1072        self.checkColorParam(widget, 'proxybackground')
1073
1074    @requires_tcl(8, 6, 5)
1075    def test_configure_proxyborderwidth(self):
1076        widget = self.create()
1077        self.checkPixelsParam(widget, 'proxyborderwidth',
1078                              0, 1.3, 2.9, 6, -2, '10p',
1079                              conv=noconv)
1080
1081    @requires_tcl(8, 6, 5)
1082    def test_configure_proxyrelief(self):
1083        widget = self.create()
1084        self.checkReliefParam(widget, 'proxyrelief')
1085
1086    def test_configure_sashcursor(self):
1087        widget = self.create()
1088        self.checkCursorParam(widget, 'sashcursor')
1089
1090    def test_configure_sashpad(self):
1091        widget = self.create()
1092        self.checkPixelsParam(widget, 'sashpad', 8, 1.3, 2.6, -2, '2m')
1093
1094    def test_configure_sashrelief(self):
1095        widget = self.create()
1096        self.checkReliefParam(widget, 'sashrelief')
1097
1098    def test_configure_sashwidth(self):
1099        widget = self.create()
1100        self.checkPixelsParam(widget, 'sashwidth', 10, 11.1, 15.6, -3, '1m',
1101                              conv=noconv)
1102
1103    def test_configure_showhandle(self):
1104        widget = self.create()
1105        self.checkBooleanParam(widget, 'showhandle')
1106
1107    def test_configure_width(self):
1108        widget = self.create()
1109        self.checkPixelsParam(widget, 'width', 402, 403.4, 404.6, -402, 0, '5i',
1110                              conv=noconv)
1111
1112    def create2(self):
1113        p = self.create()
1114        b = tkinter.Button(p)
1115        c = tkinter.Button(p)
1116        p.add(b)
1117        p.add(c)
1118        return p, b, c
1119
1120    def test_paneconfigure(self):
1121        p, b, c = self.create2()
1122        self.assertRaises(TypeError, p.paneconfigure)
1123        d = p.paneconfigure(b)
1124        self.assertIsInstance(d, dict)
1125        for k, v in d.items():
1126            self.assertEqual(len(v), 5)
1127            self.assertEqual(v, p.paneconfigure(b, k))
1128            self.assertEqual(v[4], p.panecget(b, k))
1129
1130    def check_paneconfigure(self, p, b, name, value, expected, stringify=False):
1131        conv = lambda x: x
1132        if not self.wantobjects or stringify:
1133            expected = str(expected)
1134        if self.wantobjects and stringify:
1135            conv = str
1136        p.paneconfigure(b, **{name: value})
1137        self.assertEqual(conv(p.paneconfigure(b, name)[4]), expected)
1138        self.assertEqual(conv(p.panecget(b, name)), expected)
1139
1140    def check_paneconfigure_bad(self, p, b, name, msg):
1141        with self.assertRaisesRegex(TclError, msg):
1142            p.paneconfigure(b, **{name: 'badValue'})
1143
1144    def test_paneconfigure_after(self):
1145        p, b, c = self.create2()
1146        self.check_paneconfigure(p, b, 'after', c, str(c))
1147        self.check_paneconfigure_bad(p, b, 'after',
1148                                     'bad window path name "badValue"')
1149
1150    def test_paneconfigure_before(self):
1151        p, b, c = self.create2()
1152        self.check_paneconfigure(p, b, 'before', c, str(c))
1153        self.check_paneconfigure_bad(p, b, 'before',
1154                                     'bad window path name "badValue"')
1155
1156    def test_paneconfigure_height(self):
1157        p, b, c = self.create2()
1158        self.check_paneconfigure(p, b, 'height', 10, 10,
1159                                 stringify=get_tk_patchlevel() < (8, 5, 11))
1160        self.check_paneconfigure_bad(p, b, 'height',
1161                                     'bad screen distance "badValue"')
1162
1163    @requires_tcl(8, 5)
1164    def test_paneconfigure_hide(self):
1165        p, b, c = self.create2()
1166        self.check_paneconfigure(p, b, 'hide', False, 0)
1167        self.check_paneconfigure_bad(p, b, 'hide',
1168                                     'expected boolean value but got "badValue"')
1169
1170    def test_paneconfigure_minsize(self):
1171        p, b, c = self.create2()
1172        self.check_paneconfigure(p, b, 'minsize', 10, 10)
1173        self.check_paneconfigure_bad(p, b, 'minsize',
1174                                     'bad screen distance "badValue"')
1175
1176    def test_paneconfigure_padx(self):
1177        p, b, c = self.create2()
1178        self.check_paneconfigure(p, b, 'padx', 1.3, 1)
1179        self.check_paneconfigure_bad(p, b, 'padx',
1180                                     'bad screen distance "badValue"')
1181
1182    def test_paneconfigure_pady(self):
1183        p, b, c = self.create2()
1184        self.check_paneconfigure(p, b, 'pady', 1.3, 1)
1185        self.check_paneconfigure_bad(p, b, 'pady',
1186                                     'bad screen distance "badValue"')
1187
1188    def test_paneconfigure_sticky(self):
1189        p, b, c = self.create2()
1190        self.check_paneconfigure(p, b, 'sticky', 'nsew', 'nesw')
1191        self.check_paneconfigure_bad(p, b, 'sticky',
1192                                     'bad stickyness value "badValue": must '
1193                                     'be a string containing zero or more of '
1194                                     'n, e, s, and w')
1195
1196    @requires_tcl(8, 5)
1197    def test_paneconfigure_stretch(self):
1198        p, b, c = self.create2()
1199        self.check_paneconfigure(p, b, 'stretch', 'alw', 'always')
1200        self.check_paneconfigure_bad(p, b, 'stretch',
1201                                     'bad stretch "badValue": must be '
1202                                     'always, first, last, middle, or never')
1203
1204    def test_paneconfigure_width(self):
1205        p, b, c = self.create2()
1206        self.check_paneconfigure(p, b, 'width', 10, 10,
1207                                 stringify=get_tk_patchlevel() < (8, 5, 11))
1208        self.check_paneconfigure_bad(p, b, 'width',
1209                                     'bad screen distance "badValue"')
1210
1211
1212@add_standard_options(StandardOptionsTests)
1213class MenuTest(AbstractWidgetTest, unittest.TestCase):
1214    OPTIONS = (
1215        'activebackground', 'activeborderwidth', 'activeforeground',
1216        'background', 'borderwidth', 'cursor',
1217        'disabledforeground', 'font', 'foreground',
1218        'postcommand', 'relief', 'selectcolor', 'takefocus',
1219        'tearoff', 'tearoffcommand', 'title', 'type',
1220    )
1221    _conv_pixels = noconv
1222
1223    def create(self, **kwargs):
1224        return tkinter.Menu(self.root, **kwargs)
1225
1226    def test_configure_postcommand(self):
1227        widget = self.create()
1228        self.checkCommandParam(widget, 'postcommand')
1229
1230    def test_configure_tearoff(self):
1231        widget = self.create()
1232        self.checkBooleanParam(widget, 'tearoff')
1233
1234    def test_configure_tearoffcommand(self):
1235        widget = self.create()
1236        self.checkCommandParam(widget, 'tearoffcommand')
1237
1238    def test_configure_title(self):
1239        widget = self.create()
1240        self.checkParam(widget, 'title', 'any string')
1241
1242    def test_configure_type(self):
1243        widget = self.create()
1244        self.checkEnumParam(
1245            widget, 'type',
1246            'normal', 'tearoff', 'menubar',
1247            errmsg='bad type "{}": must be normal, tearoff, or menubar',
1248            )
1249
1250    def test_entryconfigure(self):
1251        m1 = self.create()
1252        m1.add_command(label='test')
1253        self.assertRaises(TypeError, m1.entryconfigure)
1254        with self.assertRaisesRegex(TclError, 'bad menu entry index "foo"'):
1255            m1.entryconfigure('foo')
1256        d = m1.entryconfigure(1)
1257        self.assertIsInstance(d, dict)
1258        for k, v in d.items():
1259            self.assertIsInstance(k, str)
1260            self.assertIsInstance(v, tuple)
1261            self.assertEqual(len(v), 5)
1262            self.assertEqual(v[0], k)
1263            self.assertEqual(m1.entrycget(1, k), v[4])
1264        m1.destroy()
1265
1266    def test_entryconfigure_label(self):
1267        m1 = self.create()
1268        m1.add_command(label='test')
1269        self.assertEqual(m1.entrycget(1, 'label'), 'test')
1270        m1.entryconfigure(1, label='changed')
1271        self.assertEqual(m1.entrycget(1, 'label'), 'changed')
1272
1273    def test_entryconfigure_variable(self):
1274        m1 = self.create()
1275        v1 = tkinter.BooleanVar(self.root)
1276        v2 = tkinter.BooleanVar(self.root)
1277        m1.add_checkbutton(variable=v1, onvalue=True, offvalue=False,
1278                           label='Nonsense')
1279        self.assertEqual(str(m1.entrycget(1, 'variable')), str(v1))
1280        m1.entryconfigure(1, variable=v2)
1281        self.assertEqual(str(m1.entrycget(1, 'variable')), str(v2))
1282
1283
1284@add_standard_options(PixelSizeTests, StandardOptionsTests)
1285class MessageTest(AbstractWidgetTest, unittest.TestCase):
1286    OPTIONS = (
1287        'anchor', 'aspect', 'background', 'borderwidth',
1288        'cursor', 'font', 'foreground',
1289        'highlightbackground', 'highlightcolor', 'highlightthickness',
1290        'justify', 'padx', 'pady', 'relief',
1291        'takefocus', 'text', 'textvariable', 'width',
1292    )
1293    _conv_pad_pixels = noconv
1294
1295    def create(self, **kwargs):
1296        return tkinter.Message(self.root, **kwargs)
1297
1298    def test_configure_aspect(self):
1299        widget = self.create()
1300        self.checkIntegerParam(widget, 'aspect', 250, 0, -300)
1301
1302
1303class DefaultRootTest(AbstractDefaultRootTest, unittest.TestCase):
1304
1305    def test_frame(self):
1306        self._test_widget(tkinter.Frame)
1307
1308    def test_label(self):
1309        self._test_widget(tkinter.Label)
1310
1311
1312tests_gui = (
1313        ButtonTest, CanvasTest, CheckbuttonTest, EntryTest,
1314        FrameTest, LabelFrameTest,LabelTest, ListboxTest,
1315        MenubuttonTest, MenuTest, MessageTest, OptionMenuTest,
1316        PanedWindowTest, RadiobuttonTest, ScaleTest, ScrollbarTest,
1317        SpinboxTest, TextTest, ToplevelTest, DefaultRootTest,
1318)
1319
1320if __name__ == '__main__':
1321    unittest.main()
1322