diff options
author | Maxim Kuvyrkov <maxim.kuvyrkov@linaro.org> | 2023-06-01 12:27:51 +0000 |
---|---|---|
committer | Maxim Kuvyrkov <maxim.kuvyrkov@linaro.org> | 2023-06-14 14:29:46 +0000 |
commit | c855862799232bc571cb7266d1889cef28727f22 (patch) | |
tree | fe619c5fef1f706838aa870cae6093fdb102497f /contrib | |
parent | 4ec6b627cb008e31ea3d1ee93a209297f56c6a3e (diff) | |
download | gcc-c855862799232bc571cb7266d1889cef28727f22.zip gcc-c855862799232bc571cb7266d1889cef28727f22.tar.gz gcc-c855862799232bc571cb7266d1889cef28727f22.tar.bz2 |
[contrib] validate_failures.py: Avoid testsuite aliasing
This patch adds tracking of current testsuite "tool" and "exp"
to the processing of .sum files. This avoids aliasing between
tests from different testsuites with same name+description.
E.g., this is necessary for testsuite/c-c++-common, which is ran
for both gcc and g++ "tools".
This patch changes manifest format from ...
<cut>
FAIL: gcc_test
FAIL: g++_test
</cut>
... to ...
<cut>
=== gcc tests ===
Running gcc/foo.exp ...
FAIL: gcc_test
=== gcc Summary ==
=== g++ tests ===
Running g++/bar.exp ...
FAIL: g++_test
=== g++ Summary ==
</cut>.
The new format uses same formatting as DejaGnu's .sum files
to specify which "tool" and "exp" the test belongs to.
contrib/ChangeLog:
* testsuite-management/validate_failures.py: Avoid testsuite
aliasing.
Diffstat (limited to 'contrib')
-rwxr-xr-x | contrib/testsuite-management/validate_failures.py | 137 |
1 files changed, 115 insertions, 22 deletions
diff --git a/contrib/testsuite-management/validate_failures.py b/contrib/testsuite-management/validate_failures.py index 43d9d50..94ba2e5 100755 --- a/contrib/testsuite-management/validate_failures.py +++ b/contrib/testsuite-management/validate_failures.py @@ -64,6 +64,16 @@ import sys _VALID_TEST_RESULTS = [ 'FAIL', 'UNRESOLVED', 'XPASS', 'ERROR' ] _VALID_TEST_RESULTS_REX = re.compile("%s" % "|".join(_VALID_TEST_RESULTS)) +# Formats of .sum file sections +_TOOL_LINE_FORMAT = '\t\t=== %s tests ===\n' +_EXP_LINE_FORMAT = '\nRunning %s ...\n' +_SUMMARY_LINE_FORMAT = '\n\t\t=== %s Summary ===\n' + +# ... and their compiled regexs. +_TOOL_LINE_REX = re.compile('^\t\t=== (.*) tests ===\n') +_EXP_LINE_REX = re.compile('^Running (.*\.exp) \.\.\.\n') +_SUMMARY_LINE_REX = re.compile('^\t\t=== (.*) Summary ===\n') + # Subdirectory of srcdir in which to find the manifest file. _MANIFEST_SUBDIR = 'contrib/testsuite-management' @@ -111,9 +121,11 @@ class TestResult(object): ordinal: Monotonically increasing integer. It is used to keep results for one .exp file sorted by the order the tests were run. + tool: Top-level testsuite name (aka "tool" in DejaGnu parlance) of the test. + exp: Name of .exp testsuite file. """ - def __init__(self, summary_line, ordinal=-1): + def __init__(self, summary_line, ordinal, tool, exp): try: (self.attrs, summary_line) = SplitAttributesFromSummaryLine(summary_line) try: @@ -125,6 +137,12 @@ class TestResult(object): print('Failed to parse summary line: "%s"' % summary_line) raise self.ordinal = ordinal + if tool == None or exp == None: + # .sum file seem to be broken. There was no "tool" and/or "exp" + # lines preceding this result. + raise + self.tool = tool + self.exp = exp except ValueError: Error('Cannot parse summary line "%s"' % summary_line) @@ -133,14 +151,27 @@ class TestResult(object): self.state, summary_line, self)) def __lt__(self, other): - return (self.name < other.name or - (self.name == other.name and self.ordinal < other.ordinal)) + if (self.tool != other.tool): + return self.tool < other.tool + if (self.exp != other.exp): + return self.exp < other.exp + if (self.name != other.name): + return self.name < other.name + return self.ordinal < other.ordinal def __hash__(self): - return hash(self.state) ^ hash(self.name) ^ hash(self.description) - + return (hash(self.state) ^ hash(self.tool) ^ hash(self.exp) + ^ hash(self.name) ^ hash(self.description)) + + # Note that we don't include "attrs" in this comparison. This means that + # result entries "FAIL: test" and "flaky | FAIL: test" are considered + # the same. Therefore the ResultSet will preserve only the first occurence. + # In practice this means that flaky entries should preceed expected fails + # entries. def __eq__(self, other): return (self.state == other.state and + self.tool == other.tool and + self.exp == other.exp and self.name == other.name and self.description == other.description) @@ -174,6 +205,43 @@ class TestResult(object): return now > expiration_date +class ResultSet(set): + """Describes a set of DejaGNU test results. + This set can be read in from .sum files or emitted as a manifest. + + Attributes: + current_tool: Name of the current top-level DejaGnu testsuite. + current_exp: Name of the current .exp testsuite file. + """ + + def __init__(self): + super().__init__() + self.ResetToolExp() + + def ResetToolExp(self): + self.current_tool = None + self.current_exp = None + + def MakeTestResult(self, summary_line, ordinal=-1): + return TestResult(summary_line, ordinal, + self.current_tool, self.current_exp) + + def Print(self, outfile=sys.stdout): + current_tool = None + current_exp = None + + for result in sorted(self): + if current_tool != result.tool: + current_tool = result.tool + outfile.write(_TOOL_LINE_FORMAT % current_tool) + if current_exp != result.exp: + current_exp = result.exp + outfile.write(_EXP_LINE_FORMAT % current_exp) + outfile.write('%s\n' % result) + + outfile.write(_SUMMARY_LINE_FORMAT % 'Results') + + def GetMakefileValue(makefile_name, value_name): if os.path.exists(makefile_name): makefile = open(makefile_name, encoding='latin-1', mode='r') @@ -216,6 +284,21 @@ def IsInterestingResult(line): return bool(_VALID_TEST_RESULTS_REX.match(line)) +def IsToolLine(line): + """Return True if line mentions the tool (in DejaGnu terms) for the following tests.""" + return bool(_TOOL_LINE_REX.match(line)) + + +def IsExpLine(line): + """Return True if line mentions the .exp file for the following tests.""" + return bool(_EXP_LINE_REX.match(line)) + + +def IsSummaryLine(line): + """Return True if line starts .sum footer.""" + return bool(_SUMMARY_LINE_REX.match(line)) + + def IsInclude(line): """Return True if line is an include of another file.""" return line.startswith("@include ") @@ -244,18 +327,24 @@ def ParseManifestWorker(result_set, manifest_path): if _OPTIONS.verbosity >= 1: print('Parsing manifest file %s.' % manifest_path) manifest_file = open(manifest_path, encoding='latin-1', mode='r') - for line in manifest_file: - line = line.strip() + for orig_line in manifest_file: + line = orig_line.strip() if line == "": pass elif IsComment(line): pass elif IsNegativeResult(line): - result_set.remove(TestResult(GetNegativeResult(line))) + result_set.remove(result_set.MakeTestResult(GetNegativeResult(line))) elif IsInclude(line): ParseManifestWorker(result_set, GetIncludeFile(line, manifest_path)) elif IsInterestingResult(line): - result_set.add(TestResult(line)) + result_set.add(result_set.MakeTestResult(line)) + elif IsExpLine(orig_line): + result_set.current_exp = _EXP_LINE_REX.match(orig_line).groups()[0] + elif IsToolLine(orig_line): + result_set.current_tool = _TOOL_LINE_REX.match(orig_line).groups()[0] + elif IsSummaryLine(orig_line): + result_set.ResetToolExp() else: Error('Unrecognized line in manifest file: %s' % line) manifest_file.close() @@ -263,21 +352,21 @@ def ParseManifestWorker(result_set, manifest_path): def ParseManifest(manifest_path): """Create a set of TestResult instances from the given manifest file.""" - result_set = set() + result_set = ResultSet() ParseManifestWorker(result_set, manifest_path) return result_set def ParseSummary(sum_fname): """Create a set of TestResult instances from the given summary file.""" - result_set = set() + result_set = ResultSet() # ordinal is used when sorting the results so that tests within each # .exp file are kept sorted. ordinal=0 sum_file = open(sum_fname, encoding='latin-1', mode='r') for line in sum_file: if IsInterestingResult(line): - result = TestResult(line, ordinal) + result = result_set.MakeTestResult(line, ordinal) ordinal += 1 if result.HasExpired(): # Tests that have expired are not added to the set of expected @@ -286,6 +375,13 @@ def ParseSummary(sum_fname): print('WARNING: Expected failure "%s" has expired.' % line.strip()) continue result_set.add(result) + elif IsExpLine(line): + result_set.current_exp = _EXP_LINE_REX.match(line).groups()[0] + elif IsToolLine(line): + result_set.current_tool = _TOOL_LINE_REX.match(line).groups()[0] + result_set.current_exp = None + elif IsSummaryLine(line): + result_set.ResetToolExp() sum_file.close() return result_set @@ -301,7 +397,7 @@ def GetManifest(manifest_path): if os.path.exists(manifest_path): return ParseManifest(manifest_path) else: - return set() + return ResultSet() def CollectSumFiles(builddir): @@ -318,7 +414,7 @@ def CollectSumFiles(builddir): def GetResults(sum_files): """Collect all the test results from the given .sum files.""" - build_results = set() + build_results = ResultSet() for sum_fname in sum_files: print('\t%s' % sum_fname) build_results |= ParseSummary(sum_fname) @@ -332,7 +428,7 @@ def CompareResults(manifest, actual): """ # Collect all the actual results not present in the manifest. # Results in this set will be reported as errors. - actual_vs_manifest = set() + actual_vs_manifest = ResultSet() for actual_result in actual: if actual_result not in manifest: actual_vs_manifest.add(actual_result) @@ -341,7 +437,7 @@ def CompareResults(manifest, actual): # in the actual results. # Results in this set will be reported as warnings (since # they are expected failures that are not failing anymore). - manifest_vs_actual = set() + manifest_vs_actual = ResultSet() for expected_result in manifest: # Ignore tests marked flaky. if 'flaky' in expected_result.attrs: @@ -390,9 +486,7 @@ def GetBuildData(): def PrintSummary(msg, summary): print('\n\n%s' % msg) - for result in sorted(summary): - print(result) - + summary.Print() def GetSumFiles(results, build_dir): if not results: @@ -452,9 +546,8 @@ def ProduceManifest(): sum_files = GetSumFiles(_OPTIONS.results, _OPTIONS.build_dir) actual = GetResults(sum_files) manifest_file = open(manifest_path, encoding='latin-1', mode='w') - for result in sorted(actual): - print(result) - manifest_file.write('%s\n' % result) + actual.Print(manifest_file) + actual.Print() manifest_file.close() return True |