Skip to content

Commit ae00bce

Browse files
miss-islingtonserhiy-storchakaclaude
authored
[3.14] gh-69134: Harden NotebookTest.test_traversal (GH-153403) (GH-153409)
identify(5, 5) could run before the notebook reached its requested size, so the pixel fell outside the first tab and returned ''. Guard it with a new opt-in wait_until_mapped(full_size=True). (cherry picked from commit 53b0498) Co-authored-by: Serhiy Storchaka <storchaka@gmail.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 1248ac9 commit ae00bce

2 files changed

Lines changed: 20 additions & 7 deletions

File tree

Lib/test/test_tkinter/support.py

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ def destroy_default_root():
9393
tkinter._default_root.destroy()
9494
tkinter._default_root = None
9595

96-
def wait_until_mapped(widget, timeout=None):
96+
def wait_until_mapped(widget, timeout=None, *, full_size=False):
9797
"""Wait until *widget* is actually mapped and laid out by the window
9898
manager, so that realized-geometry queries (winfo_width(), identify(),
9999
coords(), ...) return meaningful values.
@@ -103,17 +103,26 @@ def wait_until_mapped(widget, timeout=None):
103103
``support.LOOPBACK_TIMEOUT``). Unlike Misc.wait_visibility(), this
104104
never blocks indefinitely, so it is safe under a window manager that
105105
never maps the window (see gh-69134, gh-74941, bpo-40722).
106+
107+
If *full_size* is true, also wait until the realized size reaches the
108+
requested size, so that per-pixel queries near an edge (e.g. identify())
109+
are reliable even under load.
106110
"""
107111
if timeout is None:
108112
timeout = support.LOOPBACK_TIMEOUT
109113
deadline = time.monotonic() + timeout
110114
widget.update_idletasks()
111115
while True:
112116
widget.update() # drain pending Map/Configure events
113-
if (widget.winfo_ismapped()
114-
and widget.winfo_width() > 1
115-
and widget.winfo_height() > 1):
116-
return True
117+
if widget.winfo_ismapped():
118+
if full_size:
119+
w_ok = widget.winfo_width() >= widget.winfo_reqwidth() > 1
120+
h_ok = widget.winfo_height() >= widget.winfo_reqheight() > 1
121+
else:
122+
w_ok = widget.winfo_width() > 1
123+
h_ok = widget.winfo_height() > 1
124+
if w_ok and h_ok:
125+
return True
117126
if time.monotonic() >= deadline:
118127
return False
119128
time.sleep(0.01)

Lib/test/test_ttk/test_widgets.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1224,7 +1224,10 @@ def test_traversal(self):
12241224
focus_identify_as = 'focus'
12251225
else:
12261226
focus_identify_as = 'focus' if tk_version < (8, 7) else 'padding'
1227-
self.assertEqual(self.nb.identify(5, 5), focus_identify_as)
1227+
# identify() at (5, 5) needs the tab realized there; under focus
1228+
# contention the mapped size can lag, so wait for the full size.
1229+
if wait_until_mapped(self.nb, full_size=True):
1230+
self.assertEqual(self.nb.identify(5, 5), focus_identify_as)
12281231
simulate_mouse_click(self.nb, 5, 5)
12291232
self.nb.focus_force()
12301233
self.nb.event_generate('<Control-Tab>')
@@ -1240,7 +1243,8 @@ def test_traversal(self):
12401243
self.nb.tab(self.child2, text='e', underline=0)
12411244
self.nb.enable_traversal()
12421245
self.nb.focus_force()
1243-
self.assertEqual(self.nb.identify(5, 5), focus_identify_as)
1246+
if wait_until_mapped(self.nb, full_size=True):
1247+
self.assertEqual(self.nb.identify(5, 5), focus_identify_as)
12441248
simulate_mouse_click(self.nb, 5, 5)
12451249
# on macOS Emacs-style keyboard shortcuts are region-dependent;
12461250
# let's use the regular arrow keys instead

0 commit comments

Comments
 (0)