123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119 |
- #!/usr/bin/env python3
- #
- # Compare output of two gcovr JSON reports and report differences. To
- # generate the required output first:
- # - create two build dirs with --enable-gcov
- # - run set of tests in each
- # - run make coverage-html in each
- # - run gcovr --json --exclude-unreachable-branches \
- # --print-summary -o coverage.json --root ../../ . *.p
- #
- # Author: Alex Bennée <alex.bennee@linaro.org>
- #
- # SPDX-License-Identifier: GPL-2.0-or-later
- #
- import argparse
- import json
- import sys
- from pathlib import Path
- def create_parser():
- parser = argparse.ArgumentParser(
- prog='compare_gcov_json',
- description='analyse the differences in coverage between two runs')
- parser.add_argument('-a', type=Path, default=None,
- help=('First file to check'))
- parser.add_argument('-b', type=Path, default=None,
- help=('Second file to check'))
- parser.add_argument('--verbose', action='store_true', default=False,
- help=('A minimal verbosity level that prints the '
- 'overall result of the check/wait'))
- return parser
- # See https://gcovr.com/en/stable/output/json.html#json-format-reference
- def load_json(json_file_path: Path, verbose = False) -> dict[str, set[int]]:
- with open(json_file_path) as f:
- data = json.load(f)
- root_dir = json_file_path.absolute().parent
- covered_lines = dict()
- for filecov in data["files"]:
- file_path = Path(filecov["file"])
- # account for generated files - map into src tree
- resolved_path = Path(file_path).absolute()
- if resolved_path.is_relative_to(root_dir):
- file_path = resolved_path.relative_to(root_dir)
- # print(f"remapped {resolved_path} to {file_path}")
- lines = filecov["lines"]
- executed_lines = set(
- linecov["line_number"]
- for linecov in filecov["lines"]
- if linecov["count"] != 0 and not linecov["gcovr/noncode"]
- )
- # if this file has any coverage add it to the system
- if len(executed_lines) > 0:
- if verbose:
- print(f"file {file_path} {len(executed_lines)}/{len(lines)}")
- covered_lines[str(file_path)] = executed_lines
- return covered_lines
- def find_missing_files(first, second):
- """
- Return a list of files not covered in the second set
- """
- missing_files = []
- for f in sorted(first):
- file_a = first[f]
- try:
- file_b = second[f]
- except KeyError:
- missing_files.append(f)
- return missing_files
- def main():
- """
- Script entry point
- """
- parser = create_parser()
- args = parser.parse_args()
- if not args.a or not args.b:
- print("We need two files to compare")
- sys.exit(1)
- first_coverage = load_json(args.a, args.verbose)
- second_coverage = load_json(args.b, args.verbose)
- first_missing = find_missing_files(first_coverage,
- second_coverage)
- second_missing = find_missing_files(second_coverage,
- first_coverage)
- a_name = args.a.parent.name
- b_name = args.b.parent.name
- print(f"{b_name} missing coverage in {len(first_missing)} files")
- for f in first_missing:
- print(f" {f}")
- print(f"{a_name} missing coverage in {len(second_missing)} files")
- for f in second_missing:
- print(f" {f}")
- if __name__ == '__main__':
- main()
|