|
2 | 2 |
|
3 | 3 | import copy |
4 | 4 | import pickle |
5 | | -import time |
6 | 5 | import io |
7 | 6 | from test import support |
8 | 7 | import unittest |
@@ -178,23 +177,36 @@ def testAppendChild(self): |
178 | 177 | def testAppendChildNoQuadraticComplexity(self): |
179 | 178 | impl = getDOMImplementation() |
180 | 179 |
|
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 | + ) |
198 | 210 |
|
199 | 211 | def testSetAttributeNodeWithoutOwnerDocument(self): |
200 | 212 | # regression test for gh-142754 |
|
0 commit comments