@@ -96,10 +96,11 @@ def get_module(name, path):
9696
9797class ProgressIndicator (object ):
9898
99- def __init__ (self , cases , flaky_tests_mode ):
99+ def __init__ (self , cases , flaky_tests_mode , measure_flakiness ):
100100 self .cases = cases
101101 self .serial_id = 0
102102 self .flaky_tests_mode = flaky_tests_mode
103+ self .measure_flakiness = measure_flakiness
103104 self .parallel_queue = Queue (len (cases ))
104105 self .sequential_queue = Queue (len (cases ))
105106 for case in cases :
@@ -211,10 +212,20 @@ def RunSingle(self, parallel, thread_id):
211212 if output .UnexpectedOutput ():
212213 if FLAKY in output .test .outcomes and self .flaky_tests_mode == DONTCARE :
213214 self .flaky_failed .append (output )
215+ elif FLAKY in output .test .outcomes and self .flaky_tests_mode == KEEP_RETRYING :
216+ for _ in range (99 ):
217+ if not case .Run ().UnexpectedOutput ():
218+ self .flaky_failed .append (output )
219+ break
220+ # If after 100 tries, the test is not passing, it's not flaky.
221+ self .failed .append (output )
214222 else :
215223 self .failed .append (output )
216224 if output .HasCrashed ():
217225 self .crashed += 1
226+ if self .measure_flakiness :
227+ outputs = [case .Run () for _ in range (self .measure_flakiness )]
228+ print (f" failed { len ([i for i in outputs if i .UnexpectedOutput ()])} out of { self .measure_flakiness } " )
218229 else :
219230 self .succeeded += 1
220231 self .remaining -= 1
@@ -436,8 +447,8 @@ def Done(self):
436447
437448class CompactProgressIndicator (ProgressIndicator ):
438449
439- def __init__ (self , cases , flaky_tests_mode , templates ):
440- super (CompactProgressIndicator , self ).__init__ (cases , flaky_tests_mode )
450+ def __init__ (self , cases , flaky_tests_mode , measure_flakiness , templates ):
451+ super (CompactProgressIndicator , self ).__init__ (cases , flaky_tests_mode , measure_flakiness )
441452 self .templates = templates
442453 self .last_status_length = 0
443454 self .start_time = time .time ()
@@ -492,29 +503,29 @@ def PrintProgress(self, name):
492503
493504class ColorProgressIndicator (CompactProgressIndicator ):
494505
495- def __init__ (self , cases , flaky_tests_mode ):
506+ def __init__ (self , cases , flaky_tests_mode , measure_flakiness ):
496507 templates = {
497508 'status_line' : "[%(mins)02i:%(secs)02i|\033 [34m%%%(remaining) 4d\033 [0m|\033 [32m+%(passed) 4d\033 [0m|\033 [31m-%(failed) 4d\033 [0m]: %(test)s" ,
498509 'stdout' : "\033 [1m%s\033 [0m" ,
499510 'stderr' : "\033 [31m%s\033 [0m" ,
500511 }
501- super (ColorProgressIndicator , self ).__init__ (cases , flaky_tests_mode , templates )
512+ super (ColorProgressIndicator , self ).__init__ (cases , flaky_tests_mode , measure_flakiness , templates )
502513
503514 def ClearLine (self , last_line_length ):
504515 print ("\033 [1K\r " , end = '' )
505516
506517
507518class MonochromeProgressIndicator (CompactProgressIndicator ):
508519
509- def __init__ (self , cases , flaky_tests_mode ):
520+ def __init__ (self , cases , flaky_tests_mode , measure_flakiness ):
510521 templates = {
511522 'status_line' : "[%(mins)02i:%(secs)02i|%%%(remaining) 4d|+%(passed) 4d|-%(failed) 4d]: %(test)s" ,
512523 'stdout' : '%s' ,
513524 'stderr' : '%s' ,
514525 'clear' : lambda last_line_length : ("\r " + (" " * last_line_length ) + "\r " ),
515526 'max_length' : 78
516527 }
517- super (MonochromeProgressIndicator , self ).__init__ (cases , flaky_tests_mode , templates )
528+ super (MonochromeProgressIndicator , self ).__init__ (cases , flaky_tests_mode , measure_flakiness , templates )
518529
519530 def ClearLine (self , last_line_length ):
520531 print (("\r " + (" " * last_line_length ) + "\r " ), end = '' )
@@ -948,8 +959,8 @@ def GetTimeout(self, mode, section=''):
948959 timeout = timeout * 6
949960 return timeout
950961
951- def RunTestCases (cases_to_run , progress , tasks , flaky_tests_mode ):
952- progress = PROGRESS_INDICATORS [progress ](cases_to_run , flaky_tests_mode )
962+ def RunTestCases (cases_to_run , progress , tasks , flaky_tests_mode , measure_flakiness ):
963+ progress = PROGRESS_INDICATORS [progress ](cases_to_run , flaky_tests_mode , measure_flakiness )
953964 return progress .Run (tasks )
954965
955966# -------------------------------------------
@@ -967,6 +978,7 @@ def RunTestCases(cases_to_run, progress, tasks, flaky_tests_mode):
967978SLOW = 'slow'
968979FLAKY = 'flaky'
969980DONTCARE = 'dontcare'
981+ KEEP_RETRYING = 'keep_retrying'
970982
971983class Expression (object ):
972984 pass
@@ -1357,6 +1369,9 @@ def BuildOptions():
13571369 result .add_option ("--flaky-tests" ,
13581370 help = "Regard tests marked as flaky (run|skip|dontcare)" ,
13591371 default = "run" )
1372+ result .add_option ("--measure-flakiness" ,
1373+ help = "When a test fails, re-run it x number of times" ,
1374+ default = 0 , type = "int" )
13601375 result .add_option ("--skip-tests" ,
13611376 help = "Tests that should not be executed (comma-separated)" ,
13621377 default = "" )
@@ -1733,7 +1748,7 @@ def should_keep(case):
17331748 else :
17341749 try :
17351750 start = time .time ()
1736- if RunTestCases (cases_to_run , options .progress , options .j , options .flaky_tests ):
1751+ if RunTestCases (cases_to_run , options .progress , options .j , options .flaky_tests , options . measure_flakiness ):
17371752 result = 0
17381753 else :
17391754 result = 1
0 commit comments