@@ -3479,27 +3479,35 @@ def test_find_xpath(self):
34793479 self .assertRaisesRegex (SyntaxError , 'XPath' , e .find , './tag[last()+1]' )
34803480
34813481 def test_find_xpath_index_no_quadratic_complexity (self ):
3482- root = ET .Element ("root" )
3483- first_a = ET .SubElement (root , "a" )
3484- first_a .set ("pos" , "first" )
3485- n = 2 ** 15
3486- for i in range (n ):
3487- ET .SubElement (root , "a" )
3488- last_a = ET .SubElement (root , "a" )
3489- last_a .set ("pos" , "last" )
3482+ class CountingElement (ET .Element ):
3483+ findall_calls = 0
3484+ def findall (self , * args , ** kwargs ):
3485+ type (self ).findall_calls += 1
3486+ return super ().findall (* args , ** kwargs )
3487+
3488+ def work (n , pattern ):
3489+ root = CountingElement ("root" )
3490+ for _ in range (n ):
3491+ ET .SubElement (root , "a" )
3492+ CountingElement .findall_calls = 0
3493+ root .findall (pattern )
3494+ return CountingElement .findall_calls
34903495
34913496 for pattern in [".//a[1]" , ".//a[last()]" ]:
3492- start = time .time ()
3493- result = root .findall (pattern )
3494- end = time .time ()
3495-
3496- # Before the fix these took 30+ seconds.
3497- self .assertLess (end - start , 1 )
3498-
3499- self .assertIs (root .find (".//a[1]" ), first_a )
3500- self .assertEqual (root .find (".//a[1]" ).get ("pos" ), "first" )
3501- self .assertIs (root .find (".//a[last()]" ), last_a )
3502- self .assertEqual (root .find (".//a[last()]" ).get ("pos" ), "last" )
3497+ w1 = work (1024 , pattern )
3498+ w2 = work (2048 , pattern )
3499+ w3 = work (4096 , pattern )
3500+
3501+ self .assertGreater (w1 , 0 )
3502+ r1 = w2 / w1
3503+ r2 = w3 / w2
3504+ # Doubling N must not ~double the parent.findall calls.
3505+ # Linear-in-N call counts indicate the cache is missing.
3506+ self .assertLess (
3507+ max (r1 , r2 ), 1.5 ,
3508+ msg = f"Possible quadratic behavior on { pattern !r} : "
3509+ f"calls={ w1 , w2 , w3 } ratios={ r1 , r2 } " ,
3510+ )
35033511
35043512 def test_findall (self ):
35053513 e = ET .XML (SAMPLE_XML )
0 commit comments