Skip to content

Commit 8d1b79a

Browse files
committed
fix: take branch coverage into account in the diff coverage
1 parent f43d950 commit 8d1b79a

6 files changed

Lines changed: 129 additions & 110 deletions

File tree

codecov/coverage/base.py

Lines changed: 41 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -73,10 +73,26 @@ class DiffCoverage:
7373

7474

7575
class BaseCoverage(ABC):
76-
def compute_coverage(self, num_covered: int, num_total: int) -> decimal.Decimal:
77-
if num_total == 0:
76+
def convert_to_decimal(self, value: float | decimal.Decimal, precision: int = 2) -> decimal.Decimal:
77+
if not isinstance(value, decimal.Decimal):
78+
value = decimal.Decimal(str(float(value) / 100))
79+
return value.quantize(
80+
exp=decimal.Decimal(10) ** -precision,
81+
rounding=decimal.ROUND_DOWN,
82+
)
83+
84+
def compute_coverage(
85+
self,
86+
num_covered: int,
87+
num_total: int,
88+
num_branches_covered: int = 0,
89+
num_branches_total: int = 0,
90+
) -> decimal.Decimal:
91+
numerator = decimal.Decimal(num_covered + num_branches_covered)
92+
denominator = decimal.Decimal(num_total + num_branches_total)
93+
if denominator == 0:
7894
return decimal.Decimal('1')
79-
return decimal.Decimal(num_covered) / decimal.Decimal(num_total)
95+
return numerator / denominator
8096

8197
def get_coverage_info(self, coverage_path: pathlib.Path) -> Coverage:
8298
try:
@@ -99,6 +115,7 @@ def get_diff_coverage_info( # pylint: disable=too-many-locals
99115
self,
100116
added_lines: dict[pathlib.Path, list[int]],
101117
coverage: Coverage,
118+
branch_coverage: bool = False,
102119
) -> DiffCoverage:
103120
files = {}
104121
total_num_lines = 0
@@ -126,7 +143,15 @@ def get_diff_coverage_info( # pylint: disable=too-many-locals
126143
total_num_lines += count_total
127144
total_num_violations += count_missing
128145

129-
percent_covered = self.compute_coverage(num_covered=count_executed, num_total=count_total)
146+
if branch_coverage:
147+
percent_covered = self.compute_coverage(
148+
num_covered=count_executed,
149+
num_total=count_total,
150+
num_branches_covered=file.info.covered_branches or 0,
151+
num_branches_total=file.info.num_branches or 0,
152+
)
153+
else:
154+
percent_covered = self.compute_coverage(num_covered=count_executed, num_total=count_total)
130155

131156
files[path] = FileDiffCoverage(
132157
path=path,
@@ -136,10 +161,18 @@ def get_diff_coverage_info( # pylint: disable=too-many-locals
136161
added_statements=sorted(added),
137162
added_lines=added_lines_for_file,
138163
)
139-
final_percentage = self.compute_coverage(
140-
num_covered=total_num_lines - total_num_violations,
141-
num_total=total_num_lines,
142-
)
164+
if branch_coverage:
165+
final_percentage = self.compute_coverage(
166+
num_covered=total_num_lines - total_num_violations,
167+
num_total=total_num_lines,
168+
num_branches_covered=coverage.info.covered_branches or 0,
169+
num_branches_total=coverage.info.num_branches or 0,
170+
)
171+
else:
172+
final_percentage = self.compute_coverage(
173+
num_covered=total_num_lines - total_num_violations,
174+
num_total=total_num_lines,
175+
)
143176

144177
return DiffCoverage(
145178
total_num_lines=total_num_lines,

codecov/coverage/pytest.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,12 @@
11
from __future__ import annotations
22

33
import datetime
4-
import decimal
54
import pathlib
65

76
from codecov.coverage.base import BaseCoverage, Coverage, CoverageInfo, CoverageMetadata, FileCoverage
87

98

109
class PytestCoverage(BaseCoverage):
11-
def _convert_to_decimal(self, value: float, precision: int = 2) -> decimal.Decimal:
12-
return decimal.Decimal(str(float(value) / 100)).quantize(decimal.Decimal(10) ** -precision)
13-
1410
def extract_info(self, data: dict) -> Coverage:
1511
"""
1612
{
@@ -73,7 +69,7 @@ def extract_info(self, data: dict) -> Coverage:
7369
info=CoverageInfo(
7470
covered_lines=file_data['summary']['covered_lines'],
7571
num_statements=file_data['summary']['num_statements'],
76-
percent_covered=self._convert_to_decimal(file_data['summary']['percent_covered']),
72+
percent_covered=self.convert_to_decimal(file_data['summary']['percent_covered']),
7773
percent_covered_display=file_data['summary']['percent_covered_display'],
7874
missing_lines=file_data['summary']['missing_lines'],
7975
excluded_lines=file_data['summary']['excluded_lines'],
@@ -88,7 +84,7 @@ def extract_info(self, data: dict) -> Coverage:
8884
info=CoverageInfo(
8985
covered_lines=data['totals']['covered_lines'],
9086
num_statements=data['totals']['num_statements'],
91-
percent_covered=self._convert_to_decimal(data['totals']['percent_covered']),
87+
percent_covered=self.convert_to_decimal(data['totals']['percent_covered']),
9288
percent_covered_display=data['totals']['percent_covered_display'],
9389
missing_lines=data['totals']['missing_lines'],
9490
excluded_lines=data['totals']['excluded_lines'],

codecov/main.py

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,11 @@ def _process_coverage(self):
6262
if self.config.BRANCH_COVERAGE:
6363
coverage = diff_grouper.fill_branch_missing_groups(coverage=coverage)
6464
added_lines = GithubDiffParser(diff=self.github.pr_diff).parse()
65-
diff_coverage = self.coverage_module.get_diff_coverage_info(added_lines=added_lines, coverage=coverage)
65+
diff_coverage = self.coverage_module.get_diff_coverage_info(
66+
added_lines=added_lines,
67+
coverage=coverage,
68+
branch_coverage=self.config.BRANCH_COVERAGE,
69+
)
6670
self.coverage = coverage
6771
self.diff_coverage = diff_coverage
6872

@@ -86,24 +90,24 @@ def _process_pr(self):
8690
)
8791
try:
8892
comment = template.get_comment_markdown(
89-
coverage=self.coverage,
90-
diff_coverage=self.diff_coverage,
91-
files=files_info,
92-
count_files=count_files,
93-
coverage_files=coverage_files_info,
94-
count_coverage_files=count_coverage_files,
95-
max_files=self.config.MAX_FILES_IN_COMMENT,
96-
minimum_green=self.config.MINIMUM_GREEN,
97-
minimum_orange=self.config.MINIMUM_ORANGE,
98-
repo_name=self.config.GITHUB_REPOSITORY,
99-
pr_number=self.github.pr_number,
100-
base_ref=self.github.base_ref,
101-
base_template=template.read_template_file('comment.md.j2'),
102-
marker=marker,
93+
template.read_template_file('comment.md.j2'),
94+
self.coverage,
95+
self.diff_coverage,
96+
self.config.MINIMUM_GREEN,
97+
self.config.MINIMUM_ORANGE,
98+
self.config.GITHUB_REPOSITORY,
99+
self.github.pr_number,
100+
self.github.base_ref,
101+
marker,
103102
subproject_id=self.config.SUBPROJECT_ID,
104103
branch_coverage=self.config.BRANCH_COVERAGE,
105104
complete_project_report=self.config.COMPLETE_PROJECT_REPORT,
106105
coverage_report_url=self.config.COVERAGE_REPORT_URL,
106+
max_files=self.config.MAX_FILES_IN_COMMENT,
107+
files=files_info,
108+
count_files=count_files,
109+
coverage_files=coverage_files_info,
110+
count_coverage_files=count_coverage_files,
107111
)
108112
except MissingMarker as e:
109113
log.error(

codecov/template.py

Lines changed: 5 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import itertools
88
import pathlib
99
from importlib import resources
10+
from typing import Any
1011

1112
import jinja2
1213
from jinja2.sandbox import SandboxedEnvironment
@@ -61,24 +62,17 @@ class FileInfo:
6162

6263

6364
def get_comment_markdown( # pylint: disable=too-many-arguments,too-many-locals,too-many-positional-arguments
65+
base_template: str,
6466
coverage: Coverage,
6567
diff_coverage: DiffCoverage,
66-
files: list[FileInfo],
67-
count_files: int,
68-
coverage_files: list[FileInfo],
69-
count_coverage_files: int,
70-
max_files: int | None,
7168
minimum_green: decimal.Decimal,
7269
minimum_orange: decimal.Decimal,
7370
repo_name: str,
7471
pr_number: int,
7572
base_ref: str,
76-
base_template: str,
7773
marker: str,
78-
subproject_id: str | None = None,
79-
branch_coverage: bool = False,
80-
complete_project_report: bool = False,
81-
coverage_report_url: str | None = None,
74+
/,
75+
**kwargs: Any,
8276
):
8377
env = SandboxedEnvironment(loader=jinja2.FileSystemLoader('codecov/template_files/'))
8478
env.filters['pct'] = pct
@@ -116,18 +110,10 @@ def get_comment_markdown( # pylint: disable=too-many-arguments,too-many-locals,
116110
comment = env.from_string(base_template).render(
117111
coverage=coverage,
118112
diff_coverage=diff_coverage,
119-
max_files=max_files,
120-
files=files,
121-
count_files=count_files,
122-
coverage_files=coverage_files,
123-
count_coverage_files=count_coverage_files,
124113
missing_diff_lines=missing_diff_lines,
125114
missing_lines_for_whole_project=missing_lines_for_whole_project,
126-
subproject_id=subproject_id,
127115
marker=marker,
128-
branch_coverage=branch_coverage,
129-
complete_project_report=complete_project_report,
130-
coverage_report_url=coverage_report_url,
116+
**kwargs,
131117
)
132118
except jinja2.exceptions.TemplateError as exc:
133119
log.error('Template rendering error: %s', str(exc))

tests/coverage/test_pytest.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ def test_extract_info(self, coverage_json):
2424
info=CoverageInfo(
2525
covered_lines=6,
2626
num_statements=10,
27-
percent_covered=PytestCoverage()._convert_to_decimal(60.0),
27+
percent_covered=PytestCoverage().convert_to_decimal(60.0),
2828
percent_covered_display='60%',
2929
missing_lines=4,
3030
excluded_lines=0,
@@ -40,7 +40,7 @@ def test_extract_info(self, coverage_json):
4040
info=CoverageInfo(
4141
covered_lines=6,
4242
num_statements=10,
43-
percent_covered=PytestCoverage()._convert_to_decimal(60.0),
43+
percent_covered=PytestCoverage().convert_to_decimal(60.0),
4444
percent_covered_display='60%',
4545
missing_lines=4,
4646
excluded_lines=0,

0 commit comments

Comments
 (0)