Skip to content

Instantly share code, notes, and snippets.

@bofm
Last active September 11, 2023 07:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bofm/d75938f842648678d9b4cf22ff00133a to your computer and use it in GitHub Desktop.
Save bofm/d75938f842648678d9b4cf22ff00133a to your computer and use it in GitHub Desktop.
Calculate test coverage for the new code only. Usable in pull/merge requests.
"""
Calculates test coverage for the new code only, that is for the added and
changed lines of code.
Usage:
pytest --cov --cov-report=xml
git diff master..HEAD |python calculate_new_coverage.py --coverage-report coverage.xml
"""
import sys
import xml.etree.ElementTree as ET
import argparse
def parse_diff(diff_lines):
changes = {}
current_file = ""
line_num = 0
for line in diff_lines:
if line.startswith('+++ b/'):
current_file = line[6:].strip()
if not current_file.endswith('.py'):
current_file = None
elif line.startswith('@@'):
line_num = int(line.split(" ")[2].split(",")[0][1:]) + 1
elif line.startswith('+') and current_file:
changes.setdefault(current_file, []).append(line_num)
line_num += 1
elif line.startswith(' ') and current_file:
line_num += 1
return changes
def calculate_new_coverage(diff_changes, coverage_report_path):
tree = ET.parse(coverage_report_path)
root = tree.getroot()
total_lines, covered_lines = 0, 0
for file_path in diff_changes.keys():
for file in root.findall(".//class[@filename='%s']" % file_path):
for line in file.findall('.//line'):
line_num = int(line.get('number'))
if line_num in diff_changes[file_path]:
total_lines += 1
if int(line.get('hits')) > 0:
covered_lines += 1
if total_lines == 0:
return 100
return (covered_lines / total_lines) * 100
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument(
'--coverage-report', required=True, help="Path to pytest coverage XML report"
)
args = parser.parse_args()
diff_changes = parse_diff(sys.stdin)
new_coverage = calculate_new_coverage(diff_changes, args.coverage_report)
print(f"{int(new_coverage)}%")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment