@@ -174,27 +174,41 @@ def testAppendChild(self):
174174 self .assertEqual (dom .documentElement .childNodes [- 1 ].data , "Hello" )
175175 dom .unlink ()
176176
177- @support .requires_resource ('cpu' )
178177 def testAppendChildNoQuadraticComplexity (self ):
178+ # Don't use wall-clock timing (too flaky). Instead count a proxy for the
179+ # old quadratic behavior: repeated attribute access, such as of
180+ # parentNode/nodeType during document-membership checks.
179181 impl = getDOMImplementation ()
180182
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 )
183+ def work (n ):
184+ doc = impl .createDocument (None , "some_tag" , None )
185+ element = doc .documentElement
186+ total_calls = 0
187+
188+ def getattribute_counter (self , attr ):
189+ nonlocal total_calls
190+ total_calls += 1
191+ return object .__getattribute__ (self , attr )
192+
193+ with support .swap_attr (Element , "__getattribute__" , getattribute_counter ):
194+ for _ in range (n ):
195+ child = doc .createElement ("child" )
196+ element .appendChild (child )
197+ element = child
198+ return total_calls
199+
200+ # Doubling N should not ~quadruple the work.
201+ w1 = work (1024 )
202+ w2 = work (2048 )
203+ w3 = work (4096 )
204+
205+ self .assertGreater (w1 , 0 )
206+ r1 = w2 / w1
207+ r2 = w3 / w2
208+ self .assertLess (
209+ max (r1 , r2 ), 3.2 ,
210+ msg = f"Possible quadratic behavior: work={ w1 ,w2 ,w3 } ratios={ r1 ,r2 } "
211+ )
198212
199213 def testSetAttributeNodeWithoutOwnerDocument (self ):
200214 # regression test for gh-142754
0 commit comments