@@ -406,17 +406,43 @@ def _get_parallel_tests_skip_list(repo_path):
406406 return list (sorted (skip_list_tests ))
407407
408408 @staticmethod
409- def _get_broken_tests_list (repo_path : str ) -> dict :
410- skip_list_file_path = f"{ repo_path } /tests/broken_tests.json "
409+ def _get_broken_tests_rules (repo_path : str ) -> dict :
410+ broken_tests_file_path = f"{ repo_path } /tests/broken_tests.yaml "
411411 if (
412- not os .path .isfile (skip_list_file_path )
413- or os .path .getsize (skip_list_file_path ) == 0
412+ not os .path .isfile (broken_tests_file_path )
413+ or os .path .getsize (broken_tests_file_path ) == 0
414414 ):
415- return {}
415+ raise ValueError (
416+ "There is something wrong with getting broken tests rules: "
417+ f"file '{ broken_tests_file_path } ' is empty or does not exist."
418+ )
416419
417- with open (skip_list_file_path , "r" , encoding = "utf-8" ) as skip_list_file :
418- skip_list_tests = json .load (skip_list_file )
419- return skip_list_tests
420+ with open (broken_tests_file_path , "r" , encoding = "utf-8" ) as broken_tests_file :
421+ broken_tests = yaml .safe_load (broken_tests_file )
422+
423+ compiled_rules = {"exact" : {}, "pattern" : {}}
424+
425+ for test in broken_tests :
426+ regex = test .get ("regex" ) is True
427+ rule = {
428+ "reason" : test ["reason" ],
429+ "message" : re .compile (test ["message" ]) if regex else test ["message" ],
430+ }
431+
432+ if test .get ("not_message" ):
433+ rule ["not_message" ] = (
434+ re .compile (test ["not_message" ]) if regex else test ["not_message" ]
435+ )
436+ if test .get ("check_types" ):
437+ rule ["check_types" ] = test ["check_types" ]
438+
439+ if regex :
440+ rule ["regex" ] = True
441+ compiled_rules ["pattern" ][re .compile (test ["name" ])] = rule
442+ else :
443+ compiled_rules ["exact" ][test ["name" ]] = rule
444+
445+ return compiled_rules
420446
421447 @staticmethod
422448 def group_test_by_file (tests ):
@@ -469,6 +495,43 @@ def get_log_paths(test_name):
469495
470496 broken_tests_log = os .path .join (self .result_path , "broken_tests_handler.log" )
471497
498+ def test_is_known_fail (test_name , test_logs ):
499+ matching_rules = []
500+
501+ exact_rule = known_broken_tests ["exact" ].get (test_name )
502+ if exact_rule :
503+ matching_rules .append (exact_rule )
504+
505+ for name_re , data in known_broken_tests ["pattern" ].items ():
506+ if name_re .fullmatch (test_name ):
507+ matching_rules .append (data )
508+
509+ if not matching_rules :
510+ return False
511+
512+ def matches_field (field , log , is_regex ):
513+ if field is None :
514+ return True
515+ if is_regex :
516+ return bool (field .search (log ))
517+ return field in log
518+
519+ for rule_data in matching_rules :
520+ if rule_data .get ("check_types" ) and not any (
521+ ct in context_name for ct in rule_data ["check_types" ]
522+ ):
523+ continue # check_types didn't match → skip rule
524+
525+ is_regex = rule_data .get ("regex" , False )
526+ if matches_field (rule_data .get ("not_message" ), test_logs , is_regex ):
527+ continue # not_message matched → skip rule
528+ if not matches_field (rule_data .get ("message" ), test_logs , is_regex ):
529+ continue # message didn't match → skip rule
530+
531+ return rule_data ["reason" ]
532+
533+ return False
534+
472535 with open (broken_tests_log , "a" ) as log_file :
473536 log_file .write (f"{ len (known_broken_tests )} Known broken tests\n " )
474537 for status , tests in counters .items ():
@@ -479,45 +542,21 @@ def get_log_paths(test_name):
479542 log_file .write (
480543 f"Checking test { failed_test } (status: { fail_status } )\n "
481544 )
482- if failed_test not in known_broken_tests .keys ():
483- log_file .write (
484- f"Test { failed_test } is not in known broken tests\n "
485- )
486- continue
487-
488- check_types = known_broken_tests [failed_test ].get ("check_types" )
489- fail_message = known_broken_tests [failed_test ].get ("message" )
490545
491- if check_types and not any (
492- check_type in context_name for check_type in check_types
493- ):
546+ # Should only care about the most recent log file
547+ log_path = get_log_paths (failed_test )[0 ]
548+ test_log = extract_fail_logs (log_path ).get (
549+ failed_test .split ("::" )[- 1 ]
550+ )
551+ known_fail_reason = test_is_known_fail (failed_test , test_log )
552+ if known_fail_reason is not False :
494553 log_file .write (
495- f"Test { context_name } { failed_test } is only known to be broken for check types { check_types } \n "
554+ f"Test { failed_test } is known to fail: { known_fail_reason } \n "
496555 )
497- mark_as_broken = False
498- elif not fail_message :
499- log_file .write ("No fail message specified, marking as broken\n " )
500- mark_as_broken = True
501- else :
502- log_file .write (f"Looking for fail message: { fail_message } \n " )
503- mark_as_broken = False
504- for log_path in get_log_paths (failed_test ):
505- if log_path .endswith (".log" ):
506- log_file .write (f"Checking log file: { log_path } \n " )
507- fail_logs = extract_fail_logs (log_path ).get (
508- failed_test .split ("::" )[- 1 ]
509- )
510- if fail_logs and fail_message in fail_logs :
511- log_file .write ("Found fail message in logs\n " )
512- mark_as_broken = True
513- break
514-
515- if mark_as_broken :
516- log_file .write (f"Moving test to BROKEN state\n " )
517556 counters [fail_status ].remove (failed_test )
518557 counters ["BROKEN" ].append (failed_test )
519558 else :
520- log_file .write ("Test not marked as broken \n " )
559+ log_file .write (f "Test { failed_test } is not known to fail \n " )
521560
522561 for status , tests in counters .items ():
523562 log_file .write (f"Total tests in { status } state: { len (tests )} \n " )
@@ -796,7 +835,7 @@ def run_flaky_check(self, should_fail=False):
796835 } # type: Dict
797836 tests_times = defaultdict (float ) # type: Dict
798837 tests_log_paths = defaultdict (list )
799- known_broken_tests = self ._get_broken_tests_list (self .repo_path )
838+ known_broken_tests = self ._get_broken_tests_rules (self .repo_path )
800839 id_counter = 0
801840 for test_to_run in tests_to_run :
802841 tries_num = 1 if should_fail else FLAKY_TRIES_COUNT
@@ -1234,7 +1273,7 @@ def run_normal_check(self):
12341273 tests_times = defaultdict (float )
12351274 tests_log_paths = defaultdict (list )
12361275 items_to_run = list (grouped_tests .items ())
1237- known_broken_tests = self ._get_broken_tests_list (self .repo_path )
1276+ known_broken_tests = self ._get_broken_tests_rules (self .repo_path )
12381277 logging .info ("Total test groups %s" , len (items_to_run ))
12391278 if self .shuffle_test_groups ():
12401279 logging .info ("Shuffling test groups" )
0 commit comments