[llvm] r276199 - [utils] Add script to check for code coverage regressions

Vedant Kumar via llvm-commits llvm-commits at lists.llvm.org
Wed Jul 20 15:44:16 PDT 2016


Author: vedantk
Date: Wed Jul 20 17:44:16 2016
New Revision: 276199

URL: http://llvm.org/viewvc/llvm-project?rev=276199&view=rev
Log:
[utils] Add script to check for code coverage regressions

Differential Revision: https://reviews.llvm.org/D22544

Added:
    llvm/trunk/utils/check-coverage-regressions.py   (with props)

Added: llvm/trunk/utils/check-coverage-regressions.py
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/utils/check-coverage-regressions.py?rev=276199&view=auto
==============================================================================
--- llvm/trunk/utils/check-coverage-regressions.py (added)
+++ llvm/trunk/utils/check-coverage-regressions.py Wed Jul 20 17:44:16 2016
@@ -0,0 +1,135 @@
+#!/usr/bin/env python
+
+'''Compare two coverage summaries for regressions.
+
+You can create a coverage summary by using the `llvm-cov report` command.
+Alternatively, you can use `utils/prepare-code-coverage-artifact.py` which
+creates summaries as well as file-based html reports.
+'''
+
+from __future__ import print_function
+
+import argparse
+import collections
+import re
+import sys
+
+# This threshold must be in [0, 1]. The lower the threshold, the less likely
+# it is that a regression will be flagged and vice versa.
+kThresh = 1.0
+
+FileCoverage = collections.namedtuple('FileCoverage',
+        ['Regions', 'Missed', 'Coverage', 'Functions', 'Executed'])
+
+CoverageEntry = re.compile('^(.*) +(\d+) +(\d+) +([\d.]+)% +(\d+) +([\d.]+)%$')
+
+def parse_file_coverage_line(line):
+    '''Parse @line as a summary of a file's coverage information.
+
+    >>> parse_file_coverage_line('foo.cpp 10 5 50.0% 10 0.0%')
+    ('foo.cpp', FileCoverage(Regions=10, Missed=5, Coverage=50.0, Functions=10, Executed=0.0))
+    '''
+
+    m = re.match(CoverageEntry, line)
+    if not m:
+        print("Could not read coverage summary:", line)
+        exit(1)
+
+    groups = m.groups()
+    filename = groups[0].strip()
+    regions = int(groups[1])
+    missed = int(groups[2])
+    coverage = float(groups[3])
+    functions = int(groups[4])
+    executed = float(groups[5])
+    return (filename,
+            FileCoverage(regions, missed, coverage, functions, executed))
+
+def parse_summary(path):
+    '''Parse the summary at @path. Return a dictionary mapping filenames to
+       FileCoverage instances.'''
+
+    with open(path, 'r') as f:
+        lines = f.readlines()
+
+    # Drop the header and the cell dividers. Include "TOTAL" in this list.
+    file_coverages = lines[2:-2] + [lines[-1]]
+
+    summary = {}
+    for line in file_coverages:
+        filename, fc = parse_file_coverage_line(line)
+        summary[filename] = fc
+    return summary
+
+def find_coverage_regressions(old_coverage, new_coverage):
+    '''Given two coverage summaries, generate coverage regressions of the form:
+       (filename, old FileCoverage, new FileCoverage).'''
+
+    for filename in old_coverage.keys():
+        if filename not in new_coverage:
+            continue
+
+        old_fc = old_coverage[filename]
+        new_fc = new_coverage[filename]
+        if new_fc.Coverage < kThresh * old_fc.Coverage or \
+                new_fc.Executed < kThresh * old_fc.Executed:
+            yield (filename, old_fc, new_fc)
+
+def print_regression(filename, old_fc, new_fc):
+    '''Pretty-print a coverage regression in @filename. @old_fc is the old
+       FileCoverage and @new_fc is the new one.
+
+    >>> print_regression('foo.cpp', FileCoverage(10, 5, 50.0, 10, 0), \
+                         FileCoverage(10, 7, 30.0, 10, 0))
+    Code coverage regression:
+      File: foo.cpp
+      Change in region coverage: -20.00%
+      Change in function coverage: 0.00%
+      No functions were added or removed.
+      No regions were added or removed.
+
+    >>> print_regression('foo.cpp', FileCoverage(10, 5, 50.0, 10, 0), \
+                         FileCoverage(5, 4, 20.0, 5, 0))
+    Code coverage regression:
+      File: foo.cpp
+      Change in region coverage: -30.00%
+      Change in function coverage: 0.00%
+      Change in the number of functions: -5
+      Change in the number of regions: -5
+    '''
+
+    region_coverage_delta = new_fc.Coverage - old_fc.Coverage
+    func_coverage_delta = new_fc.Executed - old_fc.Executed
+    num_functions_delta = new_fc.Functions - old_fc.Functions
+    num_regions_delta = new_fc.Regions - old_fc.Regions
+    print("Code coverage regression:")
+    print("  File:", filename)
+    print("  Change in region coverage: {0:.2f}%".format(region_coverage_delta))
+    print("  Change in function coverage: {0:.2f}%".format(func_coverage_delta))
+    if num_functions_delta:
+        print("  Change in the number of functions:", num_functions_delta)
+    else:
+        print("  No functions were added or removed.")
+    if num_regions_delta:
+        print("  Change in the number of regions:", num_regions_delta)
+    else:
+        print("  No regions were added or removed.")
+
+if __name__ == '__main__':
+    parser = argparse.ArgumentParser(description=__doc__)
+    parser.add_argument('old_summary', help='Path to the old coverage summary')
+    parser.add_argument('new_summary', help='Path to the new coverage summary')
+    args = parser.parse_args()
+
+    old_coverage = parse_summary(args.old_summary)
+    new_coverage = parse_summary(args.new_summary)
+
+    num_regressions = 0
+    for filename, old_fc, new_fc in \
+            find_coverage_regressions(old_coverage, new_coverage):
+        print_regression(filename, old_fc, new_fc)
+        num_regressions += 1
+
+    if num_regressions > 0:
+        exit(1)
+    exit(0)

Propchange: llvm/trunk/utils/check-coverage-regressions.py
------------------------------------------------------------------------------
    svn:executable = *




More information about the llvm-commits mailing list