[3.13] gh-142145: Avoid timing measurements in quadratic behavior test (gh-143105) (#143140)

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 57937a8e5e)

Co-authored-by: Sam Gross <colesbury@gmail.com>
This commit is contained in:
Miss Islington (bot) 2025-12-24 14:26:07 +01:00 committed by GitHub
parent e20863f223
commit e91c11449c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -2,7 +2,6 @@
import copy
import pickle
import time
import io
from test import support
import unittest
@ -178,23 +177,36 @@ def testAppendChild(self):
def testAppendChildNoQuadraticComplexity(self):
impl = getDOMImplementation()
newdoc = impl.createDocument(None, "some_tag", None)
top_element = newdoc.documentElement
children = [newdoc.createElement(f"child-{i}") for i in range(1, 2 ** 15 + 1)]
element = top_element
def work(n):
doc = impl.createDocument(None, "some_tag", None)
element = doc.documentElement
total_calls = 0
start = time.monotonic()
for child in children:
element.appendChild(child)
element = child
end = time.monotonic()
# Count attribute accesses as a proxy for work done
def getattribute_counter(self, attr):
nonlocal total_calls
total_calls += 1
return object.__getattribute__(self, attr)
# This example used to take at least 30 seconds.
# Conservative assertion due to the wide variety of systems and
# build configs timing based tests wind up run under.
# A --with-address-sanitizer --with-pydebug build on a rpi5 still
# completes this loop in <0.5 seconds.
self.assertLess(end - start, 4)
with support.swap_attr(Element, "__getattribute__", getattribute_counter):
for _ in range(n):
child = doc.createElement("child")
element.appendChild(child)
element = child
return total_calls
# Doubling N should not ~quadruple the work.
w1 = work(1024)
w2 = work(2048)
w3 = work(4096)
self.assertGreater(w1, 0)
r1 = w2 / w1
r2 = w3 / w2
self.assertLess(
max(r1, r2), 3.2,
msg=f"Possible quadratic behavior: work={w1,w2,w3} ratios={r1,r2}"
)
def testSetAttributeNodeWithoutOwnerDocument(self):
# regression test for gh-142754