Skip to content

Commit b710edb

Browse files
colesburymiss-islington
authored andcommitted
gh-142145: Avoid timing measurements in quadratic behavior test (gh-143105)
Count the number of Element attribute accesses as a proxy for work done. With double the amount of work, a ratio of 2.0 indicates linear scaling and 4.0 quadratic scaling. Use 3.2 as an intermediate threshold. (cherry picked from commit 57937a8) Co-authored-by: Sam Gross <colesbury@gmail.com>
1 parent 64461f1 commit b710edb

File tree

1 file changed

+30
-18
lines changed

1 file changed

+30
-18
lines changed

Lib/test/test_minidom.py

Lines changed: 30 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import copy
44
import pickle
5-
import time
65
import io
76
from test import support
87
import unittest
@@ -178,23 +177,36 @@ def testAppendChild(self):
178177
def testAppendChildNoQuadraticComplexity(self):
179178
impl = getDOMImplementation()
180179

181-
newdoc = impl.createDocument(None, "some_tag", None)
182-
top_element = newdoc.documentElement
183-
children = [newdoc.createElement(f"child-{i}") for i in range(1, 2 ** 15 + 1)]
184-
element = top_element
185-
186-
start = time.monotonic()
187-
for child in children:
188-
element.appendChild(child)
189-
element = child
190-
end = time.monotonic()
191-
192-
# This example used to take at least 30 seconds.
193-
# Conservative assertion due to the wide variety of systems and
194-
# build configs timing based tests wind up run under.
195-
# A --with-address-sanitizer --with-pydebug build on a rpi5 still
196-
# completes this loop in <0.5 seconds.
197-
self.assertLess(end - start, 4)
180+
def work(n):
181+
doc = impl.createDocument(None, "some_tag", None)
182+
element = doc.documentElement
183+
total_calls = 0
184+
185+
# Count attribute accesses as a proxy for work done
186+
def getattribute_counter(self, attr):
187+
nonlocal total_calls
188+
total_calls += 1
189+
return object.__getattribute__(self, attr)
190+
191+
with support.swap_attr(Element, "__getattribute__", getattribute_counter):
192+
for _ in range(n):
193+
child = doc.createElement("child")
194+
element.appendChild(child)
195+
element = child
196+
return total_calls
197+
198+
# Doubling N should not ~quadruple the work.
199+
w1 = work(1024)
200+
w2 = work(2048)
201+
w3 = work(4096)
202+
203+
self.assertGreater(w1, 0)
204+
r1 = w2 / w1
205+
r2 = w3 / w2
206+
self.assertLess(
207+
max(r1, r2), 3.2,
208+
msg=f"Possible quadratic behavior: work={w1,w2,w3} ratios={r1,r2}"
209+
)
198210

199211
def testSetAttributeNodeWithoutOwnerDocument(self):
200212
# regression test for gh-142754

0 commit comments

Comments
 (0)