Skip to content

Commit f2d783e

Browse files
gh-153741: Harden IDLE GUI tests for newer Tk and display scaling
Use <MouseWheel> instead of the X11 <Button-4>/<Button-5> events, which Tk 8.7 dropped, in test_sidebar's test_mousewheel. In test_configdialog, assert the 'disabled' state flag alone instead of the exact ttk state tuple (which transient pointer states break), and restore the tests' method masks with addCleanup(). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 423ae0f commit f2d783e

2 files changed

Lines changed: 38 additions & 35 deletions

File tree

Lib/idlelib/idle_test/test_configdialog.py

Lines changed: 31 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,7 @@ def test_font_set(self):
244244

245245
def test_set_samples(self):
246246
d = self.page
247+
self.addCleanup(setattr, d, 'set_samples', Func()) # Re-mask for other tests.
247248
del d.set_samples # Unmask method for test
248249
orig_samples = d.font_sample, d.highlight_sample
249250
d.font_sample, d.highlight_sample = {}, {}
@@ -257,7 +258,6 @@ def test_set_samples(self):
257258
self.assertTrue(d.font_sample == d.highlight_sample == expected)
258259

259260
d.font_sample, d.highlight_sample = orig_samples
260-
d.set_samples = Func() # Re-mask for other tests.
261261

262262

263263
class HighPageTest(unittest.TestCase):
@@ -307,7 +307,7 @@ def test_load_theme_cfg(self):
307307
# builtinlist sets variable builtin_name to the CurrentTheme default.
308308
eq(d.builtin_name.get(), 'IDLE Classic')
309309
eq(d.custom_name.get(), '- no custom themes -')
310-
eq(d.custom_theme_on.state(), ('disabled',))
310+
self.assertIn('disabled', d.custom_theme_on.state())
311311
eq(d.set_theme_type.called, 1)
312312
eq(d.paint_theme_sample.called, 1)
313313
eq(d.set_highlight_target.called, 1)
@@ -495,23 +495,23 @@ def test_highlight_sample_b1_motion(self):
495495
def test_set_theme_type(self):
496496
eq = self.assertEqual
497497
d = self.page
498+
self.addCleanup(setattr, d, 'set_theme_type', Func()) # Re-mask method.
498499
del d.set_theme_type
499500

500501
# Builtin theme selected.
501502
d.theme_source.set(True)
502503
d.set_theme_type()
503504
eq(d.builtinlist['state'], NORMAL)
504505
eq(d.customlist['state'], DISABLED)
505-
eq(d.button_delete_custom.state(), ('disabled',))
506+
self.assertIn('disabled', d.button_delete_custom.state())
506507

507508
# Custom theme selected.
508509
d.theme_source.set(False)
509510
d.set_theme_type()
510511
eq(d.builtinlist['state'], DISABLED)
511-
eq(d.custom_theme_on.state(), ('selected',))
512+
self.assertNotIn('disabled', d.custom_theme_on.state())
512513
eq(d.customlist['state'], NORMAL)
513-
eq(d.button_delete_custom.state(), ())
514-
d.set_theme_type = Func()
514+
self.assertNotIn('disabled', d.button_delete_custom.state())
515515

516516
def test_get_color(self):
517517
eq = self.assertEqual
@@ -637,24 +637,23 @@ def test_create_new_and_save_new(self):
637637
def test_set_highlight_target(self):
638638
eq = self.assertEqual
639639
d = self.page
640+
self.addCleanup(setattr, d, 'set_highlight_target', Func()) # Re-mask method.
640641
del d.set_highlight_target
641642

642643
# Target is cursor.
643644
d.highlight_target.set('Cursor')
644-
eq(d.fg_on.state(), ('disabled', 'selected'))
645-
eq(d.bg_on.state(), ('disabled',))
645+
self.assertIn('disabled', d.fg_on.state())
646+
self.assertIn('disabled', d.bg_on.state())
646647
self.assertTrue(d.fg_bg_toggle)
647648
eq(d.set_color_sample.called, 1)
648649

649650
# Target is not cursor.
650651
d.highlight_target.set('Comment')
651-
eq(d.fg_on.state(), ('selected',))
652-
eq(d.bg_on.state(), ())
652+
self.assertNotIn('disabled', d.fg_on.state())
653+
self.assertNotIn('disabled', d.bg_on.state())
653654
self.assertTrue(d.fg_bg_toggle)
654655
eq(d.set_color_sample.called, 2)
655656

656-
d.set_highlight_target = Func()
657-
658657
def test_set_color_sample_binding(self):
659658
d = self.page
660659
scs = d.set_color_sample
@@ -667,14 +666,14 @@ def test_set_color_sample_binding(self):
667666

668667
def test_set_color_sample(self):
669668
d = self.page
669+
self.addCleanup(setattr, d, 'set_color_sample', Func()) # Re-mask method.
670670
del d.set_color_sample
671671
d.highlight_target.set('Selected Text')
672672
d.fg_bg_toggle.set(True)
673673
d.set_color_sample()
674674
self.assertEqual(
675675
d.style.lookup(d.frame_color_set['style'], 'background'),
676676
d.highlight_sample.tag_cget('hilite', 'foreground'))
677-
d.set_color_sample = Func()
678677

679678
def test_paint_theme_sample(self):
680679
eq = self.assertEqual
@@ -745,7 +744,7 @@ def test_delete_custom(self):
745744
eq(yesno.called, 2)
746745
self.assertNotIn(theme_name, highpage)
747746
eq(idleConf.GetSectionList('user', 'highlight'), [theme_name2])
748-
eq(d.custom_theme_on.state(), ())
747+
self.assertNotIn('disabled', d.custom_theme_on.state())
749748
eq(d.custom_name.get(), theme_name2)
750749
eq(dialog.deactivate_current_config.called, 1)
751750
eq(dialog.activate_config_changes.called, 1)
@@ -758,7 +757,7 @@ def test_delete_custom(self):
758757
eq(yesno.called, 3)
759758
self.assertNotIn(theme_name, highpage)
760759
eq(idleConf.GetSectionList('user', 'highlight'), [])
761-
eq(d.custom_theme_on.state(), ('disabled',))
760+
self.assertIn('disabled', d.custom_theme_on.state())
762761
eq(d.custom_name.get(), '- no custom themes -')
763762
eq(dialog.deactivate_current_config.called, 2)
764763
eq(dialog.activate_config_changes.called, 2)
@@ -809,7 +808,7 @@ def test_load_key_cfg(self):
809808
# builtinlist sets variable builtin_name to the CurrentKeys default.
810809
eq(d.builtin_name.get(), 'IDLE Classic OSX')
811810
eq(d.custom_name.get(), '- no custom keys -')
812-
eq(d.custom_keyset_on.state(), ('disabled',))
811+
self.assertIn('disabled', d.custom_keyset_on.state())
813812
eq(d.set_keys_type.called, 1)
814813
eq(d.load_keys_list.called, 1)
815814
eq(d.load_keys_list.args, ('IDLE Classic OSX', ))
@@ -930,23 +929,23 @@ def test_keybinding(self):
930929
def test_set_keys_type(self):
931930
eq = self.assertEqual
932931
d = self.page
932+
self.addCleanup(setattr, d, 'set_keys_type', Func()) # Re-mask method.
933933
del d.set_keys_type
934934

935935
# Builtin keyset selected.
936936
d.keyset_source.set(True)
937937
d.set_keys_type()
938938
eq(d.builtinlist['state'], NORMAL)
939939
eq(d.customlist['state'], DISABLED)
940-
eq(d.button_delete_custom_keys.state(), ('disabled',))
940+
self.assertIn('disabled', d.button_delete_custom_keys.state())
941941

942942
# Custom keyset selected.
943943
d.keyset_source.set(False)
944944
d.set_keys_type()
945945
eq(d.builtinlist['state'], DISABLED)
946-
eq(d.custom_keyset_on.state(), ('selected',))
946+
self.assertNotIn('disabled', d.custom_keyset_on.state())
947947
eq(d.customlist['state'], NORMAL)
948-
eq(d.button_delete_custom_keys.state(), ())
949-
d.set_keys_type = Func()
948+
self.assertNotIn('disabled', d.button_delete_custom_keys.state())
950949

951950
def test_get_new_keys(self):
952951
eq = self.assertEqual
@@ -1043,7 +1042,7 @@ def test_on_bindingslist_select(self):
10431042
b.event_generate('<Button-1>', x=x, y=y)
10441043
b.event_generate('<ButtonRelease-1>', x=x, y=y)
10451044
self.assertEqual(b.get('anchor'), 'find')
1046-
self.assertEqual(d.button_new_keys.state(), ())
1045+
self.assertNotIn('disabled', d.button_new_keys.state())
10471046

10481047
def test_create_new_key_set_and_save_new_key_set(self):
10491048
eq = self.assertEqual
@@ -1080,6 +1079,7 @@ def test_load_keys_list(self):
10801079
eq = self.assertEqual
10811080
d = self.page
10821081
gks = idleConf.GetKeySet = Func()
1082+
self.addCleanup(setattr, d, 'load_keys_list', Func()) # Re-mask method.
10831083
del d.load_keys_list
10841084
b = d.bindingslist
10851085

@@ -1115,7 +1115,6 @@ def test_load_keys_list(self):
11151115
eq(b.get(0, 'end'), expected)
11161116
eq(b.get('anchor'), 'spam - <Shift-Key-a>')
11171117
eq(b.curselection(), (2, ))
1118-
d.load_keys_list = Func()
11191118

11201119
del idleConf.GetKeySet
11211120

@@ -1156,7 +1155,7 @@ def test_delete_custom_keys(self):
11561155
eq(yesno.called, 2)
11571156
self.assertNotIn(keyset_name, keyspage)
11581157
eq(idleConf.GetSectionList('user', 'keys'), [keyset_name2])
1159-
eq(d.custom_keyset_on.state(), ())
1158+
self.assertNotIn('disabled', d.custom_keyset_on.state())
11601159
eq(d.custom_name.get(), keyset_name2)
11611160
eq(dialog.deactivate_current_config.called, 1)
11621161
eq(dialog.activate_config_changes.called, 1)
@@ -1169,7 +1168,7 @@ def test_delete_custom_keys(self):
11691168
eq(yesno.called, 3)
11701169
self.assertNotIn(keyset_name, keyspage)
11711170
eq(idleConf.GetSectionList('user', 'keys'), [])
1172-
eq(d.custom_keyset_on.state(), ('disabled',))
1171+
self.assertIn('disabled', d.custom_keyset_on.state())
11731172
eq(d.custom_name.get(), '- no custom keys -')
11741173
eq(dialog.deactivate_current_config.called, 2)
11751174
eq(dialog.activate_config_changes.called, 2)
@@ -1365,25 +1364,25 @@ def test_set_add_delete_state(self):
13651364
# Call with 0 items, 1 unselected item, 1 selected item.
13661365
eq = self.assertEqual
13671366
fr = self.frame
1367+
self.addCleanup(setattr, fr, 'set_add_delete_state', Func()) # Re-mask method.
13681368
del fr.set_add_delete_state # Unmask method.
13691369
sad = fr.set_add_delete_state
13701370
h = fr.helplist
13711371

13721372
h.delete(0, 'end')
13731373
sad()
1374-
eq(fr.button_helplist_edit.state(), ('disabled',))
1375-
eq(fr.button_helplist_remove.state(), ('disabled',))
1374+
self.assertIn('disabled', fr.button_helplist_edit.state())
1375+
self.assertIn('disabled', fr.button_helplist_remove.state())
13761376

13771377
h.insert(0, 'source')
13781378
sad()
1379-
eq(fr.button_helplist_edit.state(), ('disabled',))
1380-
eq(fr.button_helplist_remove.state(), ('disabled',))
1379+
self.assertIn('disabled', fr.button_helplist_edit.state())
1380+
self.assertIn('disabled', fr.button_helplist_remove.state())
13811381

13821382
h.selection_set(0)
13831383
sad()
1384-
eq(fr.button_helplist_edit.state(), ())
1385-
eq(fr.button_helplist_remove.state(), ())
1386-
fr.set_add_delete_state = Func() # Mask method.
1384+
self.assertNotIn('disabled', fr.button_helplist_edit.state())
1385+
self.assertNotIn('disabled', fr.button_helplist_remove.state())
13871386

13881387
def test_helplist_item_add(self):
13891388
# Call without and twice with HelpSource result.
@@ -1461,6 +1460,7 @@ def test_helplist_item_remove(self):
14611460

14621461
def test_update_help_changes(self):
14631462
fr = self.frame
1463+
self.addCleanup(setattr, fr, 'update_help_changes', Func()) # Re-mask method.
14641464
del fr.update_help_changes
14651465
fr.user_helplist.clear()
14661466
fr.user_helplist.append(('name1', 'file1'))
@@ -1469,7 +1469,6 @@ def test_update_help_changes(self):
14691469
fr.update_help_changes()
14701470
self.assertEqual(mainpage['HelpFiles'],
14711471
{'1': 'name1;file1', '2': 'name2;file2'})
1472-
fr.update_help_changes = Func()
14731472

14741473

14751474
class VarTraceTest(unittest.TestCase):

Lib/idlelib/idle_test/test_sidebar.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -689,19 +689,23 @@ def test_mousewheel(self):
689689
last_lineno = get_end_linenumber(text)
690690
self.assertIsNotNone(text.dlineinfo(text.index(f'{last_lineno}.0')))
691691

692-
# Delta for <MouseWheel>, whose meaning is platform-dependent.
692+
# Simulate a mouse wheel notch. Tk 8.7 replaced the X11
693+
# <Button-4>/<Button-5> wheel events with <MouseWheel> (whose delta is
694+
# platform-dependent); older Tk on X11 still uses the button events.
695+
x11_buttons = (sidebar.canvas._windowingsystem == 'x11'
696+
and tk.TkVersion < 8.7)
693697
delta = 1 if sidebar.canvas._windowingsystem == 'aqua' else 120
694698

695699
# Scroll up.
696-
if sidebar.canvas._windowingsystem == 'x11':
700+
if x11_buttons:
697701
sidebar.canvas.event_generate('<Button-4>', x=0, y=0)
698702
else:
699703
sidebar.canvas.event_generate('<MouseWheel>', x=0, y=0, delta=delta)
700704
yield
701705
self.assertIsNone(text.dlineinfo(text.index(f'{last_lineno}.0')))
702706

703707
# Scroll back down.
704-
if sidebar.canvas._windowingsystem == 'x11':
708+
if x11_buttons:
705709
sidebar.canvas.event_generate('<Button-5>', x=0, y=0)
706710
else:
707711
sidebar.canvas.event_generate('<MouseWheel>', x=0, y=0, delta=-delta)

0 commit comments

Comments
 (0)