diff options
Diffstat (limited to 'utils/analyzer/CmpRuns.py')
-rwxr-xr-x | utils/analyzer/CmpRuns.py | 97 |
1 files changed, 44 insertions, 53 deletions
diff --git a/utils/analyzer/CmpRuns.py b/utils/analyzer/CmpRuns.py index 2d1f44f6880c..2c0ed6aae3a2 100755 --- a/utils/analyzer/CmpRuns.py +++ b/utils/analyzer/CmpRuns.py @@ -6,8 +6,8 @@ which reports have been added, removed, or changed. This is designed to support automated testing using the static analyzer, from two perspectives: - 1. To monitor changes in the static analyzer's reports on real code bases, for - regression testing. + 1. To monitor changes in the static analyzer's reports on real code bases, + for regression testing. 2. For use by end users who want to integrate regular static analyzer testing into a buildbot like environment. @@ -28,7 +28,7 @@ Usage: import os import plistlib -import CmpRuns + # Information about analysis run: # path - the analysis output directory @@ -40,6 +40,7 @@ class SingleRunInfo: self.root = root.rstrip("/\\") self.verboseLog = verboseLog + class AnalysisDiagnostic: def __init__(self, data, report, htmlReport): self._data = data @@ -51,7 +52,7 @@ class AnalysisDiagnostic: root = self._report.run.root fileName = self._report.files[self._loc['file']] if fileName.startswith(root) and len(root) > 0: - return fileName[len(root)+1:] + return fileName[len(root) + 1:] return fileName def getLine(self): @@ -66,12 +67,12 @@ class AnalysisDiagnostic: def getDescription(self): return self._data['description'] - def getIssueIdentifier(self) : + def getIssueIdentifier(self): id = self.getFileName() + "+" - if 'issue_context' in self._data : - id += self._data['issue_context'] + "+" - if 'issue_hash_content_of_line_in_context' in self._data : - id += str(self._data['issue_hash_content_of_line_in_context']) + if 'issue_context' in self._data: + id += self._data['issue_context'] + "+" + if 'issue_hash_content_of_line_in_context' in self._data: + id += str(self._data['issue_hash_content_of_line_in_context']) return id def getReport(self): @@ -89,29 +90,6 @@ class AnalysisDiagnostic: def getRawData(self): return self._data -class multidict: - def __init__(self, elts=()): - self.data = {} - for key,value in elts: - self[key] = value - - def __getitem__(self, item): - return self.data[item] - def __setitem__(self, key, value): - if key in self.data: - self.data[key].append(value) - else: - self.data[key] = [value] - def items(self): - return self.data.items() - def values(self): - return self.data.values() - def keys(self): - return self.data.keys() - def __len__(self): - return len(self.data) - def get(self, key, default=None): - return self.data.get(key, default) class CmpOptions: def __init__(self, verboseLog=None, rootA="", rootB=""): @@ -119,12 +97,14 @@ class CmpOptions: self.rootB = rootB self.verboseLog = verboseLog + class AnalysisReport: def __init__(self, run, files): self.run = run self.files = files self.diagnostics = [] + class AnalysisRun: def __init__(self, info): self.path = info.path @@ -145,14 +125,14 @@ class AnalysisRun: # reports. Assume that all reports were created using the same # clang version (this is always true and is more efficient). if 'clang_version' in data: - if self.clang_version == None: + if self.clang_version is None: self.clang_version = data.pop('clang_version') else: data.pop('clang_version') # Ignore/delete empty reports. if not data['files']: - if deleteEmpty == True: + if deleteEmpty: os.remove(p) return @@ -169,8 +149,7 @@ class AnalysisRun: report = AnalysisReport(self, data.pop('files')) diagnostics = [AnalysisDiagnostic(d, report, h) - for d,h in zip(data.pop('diagnostics'), - htmlFiles)] + for d, h in zip(data.pop('diagnostics'), htmlFiles)] assert not data @@ -179,15 +158,21 @@ class AnalysisRun: self.diagnostics.extend(diagnostics) -# Backward compatibility API. -def loadResults(path, opts, root = "", deleteEmpty=True): +def loadResults(path, opts, root="", deleteEmpty=True): + """ + Backwards compatibility API. + """ return loadResultsFromSingleRun(SingleRunInfo(path, root, opts.verboseLog), deleteEmpty) -# Load results of the analyzes from a given output folder. -# - info is the SingleRunInfo object -# - deleteEmpty specifies if the empty plist files should be deleted + def loadResultsFromSingleRun(info, deleteEmpty=True): + """ + # Load results of the analyzes from a given output folder. + # - info is the SingleRunInfo object + # - deleteEmpty specifies if the empty plist files should be deleted + + """ path = info.path run = AnalysisRun(info) @@ -203,9 +188,11 @@ def loadResultsFromSingleRun(info, deleteEmpty=True): return run -def cmpAnalysisDiagnostic(d) : + +def cmpAnalysisDiagnostic(d): return d.getIssueIdentifier() + def compareResults(A, B): """ compareResults - Generate a relation from diagnostics in run A to @@ -224,12 +211,12 @@ def compareResults(A, B): neqB = [] eltsA = list(A.diagnostics) eltsB = list(B.diagnostics) - eltsA.sort(key = cmpAnalysisDiagnostic) - eltsB.sort(key = cmpAnalysisDiagnostic) + eltsA.sort(key=cmpAnalysisDiagnostic) + eltsB.sort(key=cmpAnalysisDiagnostic) while eltsA and eltsB: a = eltsA.pop() b = eltsB.pop() - if (a.getIssueIdentifier() == b.getIssueIdentifier()) : + if (a.getIssueIdentifier() == b.getIssueIdentifier()): res.append((a, b, 0)) elif a.getIssueIdentifier() > b.getIssueIdentifier(): eltsB.append(b) @@ -240,11 +227,11 @@ def compareResults(A, B): neqA.extend(eltsA) neqB.extend(eltsB) - # FIXME: Add fuzzy matching. One simple and possible effective idea would be - # to bin the diagnostics, print them in a normalized form (based solely on - # the structure of the diagnostic), compute the diff, then use that as the - # basis for matching. This has the nice property that we don't depend in any - # way on the diagnostic format. + # FIXME: Add fuzzy matching. One simple and possible effective idea would + # be to bin the diagnostics, print them in a normalized form (based solely + # on the structure of the diagnostic), compute the diff, then use that as + # the basis for matching. This has the nice property that we don't depend + # in any way on the diagnostic format. for a in neqA: res.append((a, None, None)) @@ -253,6 +240,7 @@ def compareResults(A, B): return res + def dumpScanBuildResultsDiff(dirA, dirB, opts, deleteEmpty=True): # Load the run results. resultsA = loadResults(dirA, opts, opts.rootA, deleteEmpty) @@ -267,7 +255,7 @@ def dumpScanBuildResultsDiff(dirA, dirB, opts, deleteEmpty=True): diff = compareResults(resultsA, resultsB) foundDiffs = 0 for res in diff: - a,b,confidence = res + a, b, confidence = res if a is None: print "ADDED: %r" % b.getReadableName() foundDiffs += 1 @@ -302,6 +290,7 @@ def dumpScanBuildResultsDiff(dirA, dirB, opts, deleteEmpty=True): return foundDiffs, len(resultsA.diagnostics), len(resultsB.diagnostics) + def main(): from optparse import OptionParser parser = OptionParser("usage: %prog [options] [dir A] [dir B]") @@ -312,7 +301,8 @@ def main(): help="Prefix to ignore on source files for directory B", action="store", type=str, default="") parser.add_option("", "--verbose-log", dest="verboseLog", - help="Write additional information to LOG [default=None]", + help="Write additional information to LOG \ + [default=None]", action="store", type=str, default=None, metavar="LOG") (opts, args) = parser.parse_args() @@ -320,9 +310,10 @@ def main(): if len(args) != 2: parser.error("invalid number of arguments") - dirA,dirB = args + dirA, dirB = args dumpScanBuildResultsDiff(dirA, dirB, opts) + if __name__ == '__main__': main() |