| """This module calculates the incremental coverage of a git commit using |
| information from `git diff` and LCOV's ".info" file. |
| """ |
| from typing import Tuple, Text, List, Dict |
| import changed_lines |
| import lcov_parser |
| import os |
| import re |
| import sys |
| |
| Lines = Dict[int, bool] |
| |
| |
| class CurrDirectoryError(Exception): |
| """CurrDirectoryError is raised when the current working directory |
| cannot be found at the beginning of the absolute paths of the files |
| listed in the ".info" file. |
| """ |
| pass |
| |
| |
| def coverage(updated_lines: Dict[Text, List[int]], |
| covered_lines: Dict[Text, Lines], |
| curr_dir: Text) -> float: |
| """Computes the the proportion of lines covered in updated_lines, ignoring |
| those that are not instrumented (e.g. like comments or macros). |
| |
| Args: |
| updated_lines: a dict mapping files to a list of its updated lines |
| covered_lines: a dict mapping files to another dict mapping lines to |
| whether they've been covered or not |
| curr_dir: name of the current directory, to be removed from beginning of |
| absolute file paths in covered_lines |
| |
| Raises: |
| CurrDirectoryError: if there is an issue removing the current directory from the |
| beginning of the absolute paths of the files listed in the ".info" file. |
| """ |
| num_covered_lines = 0 |
| total_lines = 0 |
| |
| for (file_name, covered) in covered_lines.items(): |
| |
| # remove the prefix to the absolute path in the files |
| if not file_name.startswith(curr_dir): |
| raise CurrDirectoryError('Error removing "' + curr_dir + \ |
| '" from beginning of "' + file_name + '".') |
| |
| trimmed_filename = file_name[len(curr_dir) + 1:] |
| |
| if trimmed_filename in updated_lines: |
| |
| for updated_line in updated_lines[trimmed_filename]: |
| if updated_line in covered: |
| total_lines += 1 |
| |
| if covered[updated_line]: |
| num_covered_lines += 1 |
| |
| # some files may not have any instrumented code, e.g. header files with |
| # only macros |
| if total_lines == 0: |
| return None |
| |
| return float(num_covered_lines) / total_lines |
| |
| |
| def main(lcov_file_name: Text): |
| """Takes in the name of an LCOV ".info" file and calculates the incremental |
| coverage results of the current git commit. |
| |
| Raises: |
| GitDiffSyntaxError if there are formatting issues parsing file name in `file_diff`. |
| """ |
| _, covered_lines = lcov_parser.parse(open(lcov_file_name, 'r')) |
| |
| git_diff = os.popen('git diff HEAD~').read() |
| updated_lines = changed_lines.from_diff(git_diff) |
| |
| perc = coverage(updated_lines, covered_lines, os.getcwd()) |
| |
| if perc == None: |
| print("None") |
| return |
| |
| # prints the coverage as a percentage |
| print(round(perc*100, 2)) |
| |
| |
| if __name__ == '__main__': |
| main(sys.argv[1]) |