From 1a431654d3e58feb50a226a26dc936287a8498ee Mon Sep 17 00:00:00 2001 From: Joachim Jablon Date: Sun, 26 May 2024 09:19:47 +0200 Subject: [PATCH 1/9] Branch coverage WIP --- CONTRIBUTING.md | 13 +++++++++ coverage_comment/coverage.py | 55 ++++++++++++++++++++++++++++++------ 2 files changed, 60 insertions(+), 8 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index bbd6c822..8f817690 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -64,3 +64,16 @@ To run the end-to-end tests, you'll need: - Please be aware that the tests will launch `gh auth setup-git` which might be surprising if you use `https` remotes (sadly, setting `GIT_CONFIG_GLOBAL` seems not to be enough to isolate tests.) + +## Coverage labs + +### Computing the coverage rate + +The coverage rate is `covered_lines / total_lines` (as one would expect). +In case "branch coverage" is enabled, the coverage rate is +`(covered_lines + covered_branches) / (total_lines + total_branches)`. +In order to display coverage rates, we need to round the values. Depending on +the situation, we either round to 0 or 2 decimal places. Rounding rules are: +- We always round down (truncate) the value. +- We don't display the trailing zeros in the decimal part (nor the decimal point + if the decimal part is 0). diff --git a/coverage_comment/coverage.py b/coverage_comment/coverage.py index 564649a5..f3479985 100644 --- a/coverage_comment/coverage.py +++ b/coverage_comment/coverage.py @@ -6,6 +6,7 @@ import json import pathlib from collections.abc import Sequence +from typing import cast from coverage_comment import log, subprocess @@ -40,6 +41,8 @@ class FileCoverage: executed_lines: list[int] missing_lines: list[int] excluded_lines: list[int] + executed_branches: list[list[int]] | None + missing_branches: list[list[int]] | None info: CoverageInfo @@ -82,10 +85,26 @@ class DiffCoverage: files: dict[pathlib.Path, FileDiffCoverage] -def compute_coverage(num_covered: int, num_total: int) -> decimal.Decimal: - if num_total == 0: +def compute_coverage( + num_covered: int, + num_total: int, + use_branch_coverage: bool, + num_branches_covered: int | None, + num_branches_total: int | None, +) -> decimal.Decimal: + """Compute the coverage percentage, with or without branch coverage.""" + if use_branch_coverage: + num_branches_covered = cast(int, num_branches_covered) + num_branches_total = cast(int, num_branches_total) + numerator = decimal.Decimal(num_covered + num_branches_covered) + denominator = decimal.Decimal(num_total + num_branches_total) + else: + numerator = decimal.Decimal(num_covered) + denominator = decimal.Decimal(num_total) + + if denominator == 0: return decimal.Decimal("1") - return decimal.Decimal(num_covered) / decimal.Decimal(num_total) + return numerator / denominator def get_coverage_info( @@ -191,12 +210,19 @@ def extract_info(data: dict, coverage_path: pathlib.Path) -> Coverage: excluded_lines=file_data["excluded_lines"], executed_lines=file_data["executed_lines"], missing_lines=file_data["missing_lines"], + executed_branches=file_data.get("executed_branches"), + missing_branches=file_data.get("missing_branches"), info=CoverageInfo( covered_lines=file_data["summary"]["covered_lines"], num_statements=file_data["summary"]["num_statements"], percent_covered=compute_coverage( - file_data["summary"]["covered_lines"], - file_data["summary"]["num_statements"], + num_covered=file_data["summary"]["covered_lines"], + num_total=file_data["summary"]["num_statements"], + use_branch_coverage=data["meta"]["branch_coverage"], + num_branches_covered=file_data["summary"].get( + "covered_branches" + ), + num_branches_total=file_data["summary"].get("num_branches"), ), missing_lines=file_data["summary"]["missing_lines"], excluded_lines=file_data["summary"]["excluded_lines"], @@ -214,8 +240,11 @@ def extract_info(data: dict, coverage_path: pathlib.Path) -> Coverage: covered_lines=data["totals"]["covered_lines"], num_statements=data["totals"]["num_statements"], percent_covered=compute_coverage( - data["totals"]["covered_lines"], - data["totals"]["num_statements"], + num_covered=data["totals"]["covered_lines"], + num_total=data["totals"]["num_statements"], + use_branch_coverage=data["meta"]["branch_coverage"], + num_branches_covered=data["totals"].get("covered_branches"), + num_branches_total=data["totals"].get("num_branches"), ), missing_lines=data["totals"]["missing_lines"], excluded_lines=data["totals"]["excluded_lines"], @@ -255,8 +284,18 @@ def get_diff_coverage_info( total_num_lines += count_total total_num_violations += count_missing + num_branches_covered = None + num_branches_total = None + if coverage.meta.branch_coverage: + num_branches_covered = 12 + num_branches_total = 12 + percent_covered = compute_coverage( - num_covered=count_executed, num_total=count_total + num_covered=count_executed, + num_total=count_total, + use_branch_coverage=coverage.meta.branch_coverage, + num_branches_covered=num_branches_covered, + num_branches_total=num_branches_total, ) files[path] = FileDiffCoverage( From 10428b44784e5aacc18b52dadb7ad9d9747a6988 Mon Sep 17 00:00:00 2001 From: "FeRD (Frank Dana)" Date: Fri, 30 Aug 2024 20:02:37 -0400 Subject: [PATCH 2/9] Branch coverage --- coverage_comment/coverage.py | 99 +++++++++++++----------------------- 1 file changed, 34 insertions(+), 65 deletions(-) diff --git a/coverage_comment/coverage.py b/coverage_comment/coverage.py index f3479985..d2dd0bd5 100644 --- a/coverage_comment/coverage.py +++ b/coverage_comment/coverage.py @@ -29,10 +29,10 @@ class CoverageInfo: percent_covered: decimal.Decimal missing_lines: int excluded_lines: int - num_branches: int | None - num_partial_branches: int | None - covered_branches: int | None - missing_branches: int | None + num_branches: int = 0 + num_partial_branches: int = 0 + covered_branches: int = 0 + missing_branches: int = 0 @dataclasses.dataclass @@ -41,9 +41,9 @@ class FileCoverage: executed_lines: list[int] missing_lines: list[int] excluded_lines: list[int] - executed_branches: list[list[int]] | None - missing_branches: list[list[int]] | None info: CoverageInfo + executed_branches: list[list[int]] | None = None + missing_branches: list[list[int]] | None = None @dataclasses.dataclass @@ -88,20 +88,14 @@ class DiffCoverage: def compute_coverage( num_covered: int, num_total: int, - use_branch_coverage: bool, - num_branches_covered: int | None, - num_branches_total: int | None, + num_branches_covered: int = 0, + num_branches_total: int = 0, ) -> decimal.Decimal: """Compute the coverage percentage, with or without branch coverage.""" - if use_branch_coverage: - num_branches_covered = cast(int, num_branches_covered) - num_branches_total = cast(int, num_branches_total) - numerator = decimal.Decimal(num_covered + num_branches_covered) - denominator = decimal.Decimal(num_total + num_branches_total) - else: - numerator = decimal.Decimal(num_covered) - denominator = decimal.Decimal(num_total) - + num_branches_covered = cast(int, num_branches_covered) + num_branches_total = cast(int, num_branches_total) + numerator = decimal.Decimal(num_covered + num_branches_covered) + denominator = decimal.Decimal(num_total + num_branches_total) if denominator == 0: return decimal.Decimal("1") return numerator / denominator @@ -157,6 +151,26 @@ def generate_coverage_markdown(coverage_path: pathlib.Path) -> str: ) +def _make_coverage_info(data: dict) -> CoverageInfo: + """Build a CoverageInfo object from a "summary" or "totals" key.""" + return CoverageInfo( + covered_lines=data["covered_lines"], + num_statements=data["num_statements"], + percent_covered=compute_coverage( + num_covered=data["covered_lines"], + num_total=data["num_statements"], + num_branches_covered=data.get("covered_branches", 0), + num_branches_total=data.get("num_branches", 0), + ), + missing_lines=data["missing_lines"], + excluded_lines=data["excluded_lines"], + num_branches=data.get("num_branches"), + num_partial_branches=data.get("num_partial_branches"), + covered_branches=data.get("covered_branches", 0), + missing_branches=data.get("missing_branches", 0), + ) + + def extract_info(data: dict, coverage_path: pathlib.Path) -> Coverage: """ { @@ -212,47 +226,11 @@ def extract_info(data: dict, coverage_path: pathlib.Path) -> Coverage: missing_lines=file_data["missing_lines"], executed_branches=file_data.get("executed_branches"), missing_branches=file_data.get("missing_branches"), - info=CoverageInfo( - covered_lines=file_data["summary"]["covered_lines"], - num_statements=file_data["summary"]["num_statements"], - percent_covered=compute_coverage( - num_covered=file_data["summary"]["covered_lines"], - num_total=file_data["summary"]["num_statements"], - use_branch_coverage=data["meta"]["branch_coverage"], - num_branches_covered=file_data["summary"].get( - "covered_branches" - ), - num_branches_total=file_data["summary"].get("num_branches"), - ), - missing_lines=file_data["summary"]["missing_lines"], - excluded_lines=file_data["summary"]["excluded_lines"], - num_branches=file_data["summary"].get("num_branches"), - num_partial_branches=file_data["summary"].get( - "num_partial_branches" - ), - covered_branches=file_data["summary"].get("covered_branches"), - missing_branches=file_data["summary"].get("missing_branches"), - ), + info=_make_coverage_info(file_data["summary"]), ) for path, file_data in data["files"].items() }, - info=CoverageInfo( - covered_lines=data["totals"]["covered_lines"], - num_statements=data["totals"]["num_statements"], - percent_covered=compute_coverage( - num_covered=data["totals"]["covered_lines"], - num_total=data["totals"]["num_statements"], - use_branch_coverage=data["meta"]["branch_coverage"], - num_branches_covered=data["totals"].get("covered_branches"), - num_branches_total=data["totals"].get("num_branches"), - ), - missing_lines=data["totals"]["missing_lines"], - excluded_lines=data["totals"]["excluded_lines"], - num_branches=data["totals"].get("num_branches"), - num_partial_branches=data["totals"].get("num_partial_branches"), - covered_branches=data["totals"].get("covered_branches"), - missing_branches=data["totals"].get("missing_branches"), - ), + info=_make_coverage_info(data["totals"]), ) @@ -284,18 +262,9 @@ def get_diff_coverage_info( total_num_lines += count_total total_num_violations += count_missing - num_branches_covered = None - num_branches_total = None - if coverage.meta.branch_coverage: - num_branches_covered = 12 - num_branches_total = 12 - percent_covered = compute_coverage( num_covered=count_executed, num_total=count_total, - use_branch_coverage=coverage.meta.branch_coverage, - num_branches_covered=num_branches_covered, - num_branches_total=num_branches_total, ) files[path] = FileDiffCoverage( From f22d2c6cac59f30af00a596b50e84ac98a1f30b0 Mon Sep 17 00:00:00 2001 From: "FeRD (Frank Dana)" Date: Sat, 31 Aug 2024 11:31:16 -0400 Subject: [PATCH 3/9] Add new tests for compute_coverage with branches --- tests/unit/test_coverage.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tests/unit/test_coverage.py b/tests/unit/test_coverage.py index 6ea249a2..67a03d1c 100644 --- a/tests/unit/test_coverage.py +++ b/tests/unit/test_coverage.py @@ -39,6 +39,24 @@ def test_compute_coverage(num_covered, num_total, expected_coverage): ) +@pytest.mark.parametrize( + "num_covered, num_total, branch_covered, branch_total, expected_coverage", + [ + (0, 10, 0, 15, "0"), + (0, 0, 0, 0, "1"), + (5, 0, 5, 0, "1"), + (5, 10, 5, 10, "0.5"), + (1, 50, 1, 50, "0.02"), + ], +) +def test_compute_coverage_with_branches( + num_covered, num_total, branch_covered, branch_total, expected_coverage +): + assert coverage.compute_coverage( + num_covered, num_total, branch_covered, branch_total + ) == decimal.Decimal(expected_coverage) + + def test_get_coverage_info(mocker, coverage_json, coverage_obj): run = mocker.patch( "coverage_comment.subprocess.run", return_value=json.dumps(coverage_json) From 9c04cbb9a6fe15e8def41a77eb39c8575daa4c24 Mon Sep 17 00:00:00 2001 From: "FeRD (Frank Dana)" Date: Sat, 31 Aug 2024 11:32:04 -0400 Subject: [PATCH 4/9] Adjust tests for branch coverage support --- tests/conftest.py | 28 ++++++++++++++-------------- tests/unit/test_template.py | 8 ++++---- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 7aead5fe..5fea3739 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -282,10 +282,10 @@ def _(code: str, has_branches: bool = True) -> coverage_module.Coverage: percent_covered=decimal.Decimal("1.0"), missing_lines=0, excluded_lines=0, - num_branches=0 if has_branches else None, - num_partial_branches=0 if has_branches else None, - covered_branches=0 if has_branches else None, - missing_branches=0 if has_branches else None, + num_branches=0, + num_partial_branches=0, + covered_branches=0, + missing_branches=0, ), files={}, ) @@ -313,10 +313,10 @@ def _(code: str, has_branches: bool = True) -> coverage_module.Coverage: percent_covered=decimal.Decimal("1.0"), missing_lines=0, excluded_lines=0, - num_branches=0 if has_branches else None, - num_partial_branches=0 if has_branches else None, - covered_branches=0 if has_branches else None, - missing_branches=0 if has_branches else None, + num_branches=0, + num_partial_branches=0, + covered_branches=0, + missing_branches=0, ), ) if set(line.split()) & { @@ -340,7 +340,6 @@ def _(code: str, has_branches: bool = True) -> coverage_module.Coverage: coverage_obj.files[current_file].excluded_lines.append(line_number) coverage_obj.files[current_file].info.excluded_lines += 1 coverage_obj.info.excluded_lines += 1 - if has_branches and "branch" in line: coverage_obj.files[current_file].info.num_branches += 1 coverage_obj.info.num_branches += 1 @@ -353,21 +352,22 @@ def _(code: str, has_branches: bool = True) -> coverage_module.Coverage: elif "branch missing" in line: coverage_obj.files[current_file].info.missing_branches += 1 coverage_obj.info.missing_branches += 1 - info = coverage_obj.files[current_file].info coverage_obj.files[ current_file ].info.percent_covered = coverage_module.compute_coverage( num_covered=info.covered_lines, num_total=info.num_statements, + num_branches_covered=info.covered_branches, + num_branches_total=info.num_branches, ) - info = coverage_obj.info coverage_obj.info.percent_covered = coverage_module.compute_coverage( num_covered=info.covered_lines, num_total=info.num_statements, + num_branches_covered=info.covered_branches, + num_branches_total=info.num_branches, ) - return coverage_obj return _ @@ -446,7 +446,7 @@ def coverage_json(): "summary": { "covered_lines": 6, "num_statements": 10, - "percent_covered": 60.0, + "percent_covered": 53.84615384615384615384615385, "missing_lines": 4, "excluded_lines": 0, "num_branches": 3, @@ -461,7 +461,7 @@ def coverage_json(): "totals": { "covered_lines": 6, "num_statements": 10, - "percent_covered": 60.0, + "percent_covered": 53.84615384615384615384615385, "missing_lines": 4, "excluded_lines": 0, "num_branches": 3, diff --git a/tests/unit/test_template.py b/tests/unit/test_template.py index 1e60c18c..a04b1aea 100644 --- a/tests/unit/test_template.py +++ b/tests/unit/test_template.py @@ -44,7 +44,7 @@ def test_get_comment_markdown(coverage_obj, diff_coverage_obj): .split(maxsplit=4) ) - expected = ["92%", "60%", "50%", "bar", ""] + expected = ["92%", "53.84%", "50%", "bar", ""] assert result == expected @@ -79,17 +79,17 @@ def test_template(coverage_obj, diff_coverage_obj): expected = """## Coverage report (foo) -
Click to see where and how coverage changed +
Click to see where and how coverage changed
- + - +
FileStatementsMissingCoverageCoverage
(new stmts)
Lines missing
  codebase
  code.py6-8
6-8
Project Total  
From ca41318ad0da56ad3892de49adde5171284431fb Mon Sep 17 00:00:00 2001 From: "FeRD (Frank Dana)" Date: Sat, 31 Aug 2024 11:32:04 -0400 Subject: [PATCH 5/9] Adjust tests for branch coverage support --- tests/conftest.py | 88 ++++++++++++++++++++++++------------- tests/unit/test_template.py | 14 +++--- 2 files changed, 64 insertions(+), 38 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 7aead5fe..84ac63cf 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -282,10 +282,10 @@ def _(code: str, has_branches: bool = True) -> coverage_module.Coverage: percent_covered=decimal.Decimal("1.0"), missing_lines=0, excluded_lines=0, - num_branches=0 if has_branches else None, - num_partial_branches=0 if has_branches else None, - covered_branches=0 if has_branches else None, - missing_branches=0 if has_branches else None, + num_branches=0, + num_partial_branches=0, + covered_branches=0, + missing_branches=0, ), files={}, ) @@ -313,10 +313,10 @@ def _(code: str, has_branches: bool = True) -> coverage_module.Coverage: percent_covered=decimal.Decimal("1.0"), missing_lines=0, excluded_lines=0, - num_branches=0 if has_branches else None, - num_partial_branches=0 if has_branches else None, - covered_branches=0 if has_branches else None, - missing_branches=0 if has_branches else None, + num_branches=0, + num_partial_branches=0, + covered_branches=0, + missing_branches=0, ), ) if set(line.split()) & { @@ -340,7 +340,6 @@ def _(code: str, has_branches: bool = True) -> coverage_module.Coverage: coverage_obj.files[current_file].excluded_lines.append(line_number) coverage_obj.files[current_file].info.excluded_lines += 1 coverage_obj.info.excluded_lines += 1 - if has_branches and "branch" in line: coverage_obj.files[current_file].info.num_branches += 1 coverage_obj.info.num_branches += 1 @@ -353,21 +352,22 @@ def _(code: str, has_branches: bool = True) -> coverage_module.Coverage: elif "branch missing" in line: coverage_obj.files[current_file].info.missing_branches += 1 coverage_obj.info.missing_branches += 1 - info = coverage_obj.files[current_file].info coverage_obj.files[ current_file ].info.percent_covered = coverage_module.compute_coverage( num_covered=info.covered_lines, num_total=info.num_statements, + num_branches_covered=info.covered_branches, + num_branches_total=info.num_branches, ) - info = coverage_obj.info coverage_obj.info.percent_covered = coverage_module.compute_coverage( num_covered=info.covered_lines, num_total=info.num_statements, + num_branches_covered=info.covered_branches, + num_branches_total=info.num_branches, ) - return coverage_obj return _ @@ -425,9 +425,19 @@ def coverage_code(): 9 10 branch missing 11 missing - 12 + 12 covered 13 branch covered 14 covered + 15 branch partial + 16 branch covered + 17 branch missing + 18 covered + 19 covered + 20 branch partial + 21 branch missing + 22 branch covered + 23 branch covered + 24 branch covered """ @@ -442,32 +452,48 @@ def coverage_json(): }, "files": { "codebase/code.py": { - "executed_lines": [1, 2, 3, 5, 13, 14], + "executed_lines": [ + 1, + 2, + 3, + 5, + 12, + 13, + 14, + 15, + 16, + 18, + 19, + 20, + 22, + 23, + 24, + ], "summary": { - "covered_lines": 6, - "num_statements": 10, - "percent_covered": 60.0, - "missing_lines": 4, + "covered_lines": 15, + "num_statements": 21, + "percent_covered": 0.625, + "missing_lines": 6, "excluded_lines": 0, - "num_branches": 3, - "num_partial_branches": 1, - "covered_branches": 1, - "missing_branches": 1, + "num_branches": 11, + "num_partial_branches": 3, + "covered_branches": 5, + "missing_branches": 3, }, - "missing_lines": [6, 8, 10, 11], + "missing_lines": [6, 8, 10, 11, 17, 21], "excluded_lines": [], } }, "totals": { - "covered_lines": 6, - "num_statements": 10, - "percent_covered": 60.0, - "missing_lines": 4, + "covered_lines": 15, + "num_statements": 21, + "percent_covered": 0.625, + "missing_lines": 6, "excluded_lines": 0, - "num_branches": 3, - "num_partial_branches": 1, - "covered_branches": 1, - "missing_branches": 1, + "num_branches": 11, + "num_partial_branches": 3, + "covered_branches": 5, + "missing_branches": 3, }, } diff --git a/tests/unit/test_template.py b/tests/unit/test_template.py index 1e60c18c..41d2b54c 100644 --- a/tests/unit/test_template.py +++ b/tests/unit/test_template.py @@ -44,7 +44,7 @@ def test_get_comment_markdown(coverage_obj, diff_coverage_obj): .split(maxsplit=4) ) - expected = ["92%", "60%", "50%", "bar", ""] + expected = ["92%", "62.5%", "60%", "bar", ""] assert result == expected @@ -79,17 +79,17 @@ def test_template(coverage_obj, diff_coverage_obj): expected = """## Coverage report (foo) -
Click to see where and how coverage changed +
Click to see where and how coverage changed
- + - +
FileStatementsMissingCoverageCoverage
(new stmts)
Lines missing
  codebase
  code.py6-8
6-8
Project Total  
@@ -264,17 +264,17 @@ def test_template__no_previous(coverage_obj_no_branch, diff_coverage_obj): expected = """## Coverage report -
Click to see where and how coverage changed +
Click to see where and how coverage changed
- + - +
FileStatementsMissingCoverageCoverage
(new stmts)
Lines missing
  codebase
  code.py6-8
6-8
Project Total  
From 008b1b7b35725ef75d695f9daa1b92fa73076874 Mon Sep 17 00:00:00 2001 From: "FeRD (Frank Dana)" Date: Fri, 13 Sep 2024 15:12:02 -0400 Subject: [PATCH 6/9] Make Diff dataclasses keyword-only --- coverage_comment/coverage.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/coverage_comment/coverage.py b/coverage_comment/coverage.py index d2dd0bd5..4058a5ad 100644 --- a/coverage_comment/coverage.py +++ b/coverage_comment/coverage.py @@ -14,7 +14,7 @@ # The dataclasses in this module are accessible in the template, which is overridable by the user. # As a coutesy, we should do our best to keep the existing fields for backward compatibility, # and if we really can't and can't add properties, at least bump the major version. -@dataclasses.dataclass +@dataclasses.dataclass(kw_only=True) class CoverageMetadata: version: str timestamp: datetime.datetime @@ -22,7 +22,7 @@ class CoverageMetadata: show_contexts: bool -@dataclasses.dataclass +@dataclasses.dataclass(kw_only=True) class CoverageInfo: covered_lines: int num_statements: int @@ -35,7 +35,7 @@ class CoverageInfo: missing_branches: int = 0 -@dataclasses.dataclass +@dataclasses.dataclass(kw_only=True) class FileCoverage: path: pathlib.Path executed_lines: list[int] @@ -59,7 +59,7 @@ class Coverage: # Maybe in v4, we can change it to a simpler format. -@dataclasses.dataclass +@dataclasses.dataclass(kw_only=True) class FileDiffCoverage: path: pathlib.Path percent_covered: decimal.Decimal @@ -76,7 +76,7 @@ def violation_lines(self) -> list[int]: return self.missing_statements -@dataclasses.dataclass +@dataclasses.dataclass(kw_only=True) class DiffCoverage: total_num_lines: int total_num_violations: int From 8279be27b949d400cf66477a4600c9ab2a211c5e Mon Sep 17 00:00:00 2001 From: "FeRD (Frank Dana)" Date: Fri, 13 Sep 2024 15:12:48 -0400 Subject: [PATCH 7/9] Eliminate type casting in compute_coverage --- coverage_comment/coverage.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/coverage_comment/coverage.py b/coverage_comment/coverage.py index 4058a5ad..7775f677 100644 --- a/coverage_comment/coverage.py +++ b/coverage_comment/coverage.py @@ -6,7 +6,6 @@ import json import pathlib from collections.abc import Sequence -from typing import cast from coverage_comment import log, subprocess @@ -92,8 +91,6 @@ def compute_coverage( num_branches_total: int = 0, ) -> decimal.Decimal: """Compute the coverage percentage, with or without branch coverage.""" - num_branches_covered = cast(int, num_branches_covered) - num_branches_total = cast(int, num_branches_total) numerator = decimal.Decimal(num_covered + num_branches_covered) denominator = decimal.Decimal(num_total + num_branches_total) if denominator == 0: From ffd1aa12992c2272b950cc6c882674e0c07bc8e8 Mon Sep 17 00:00:00 2001 From: "FeRD (Frank Dana)" Date: Fri, 13 Sep 2024 15:13:27 -0400 Subject: [PATCH 8/9] Properly default branch counts to 0 --- coverage_comment/coverage.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/coverage_comment/coverage.py b/coverage_comment/coverage.py index 7775f677..591e9de9 100644 --- a/coverage_comment/coverage.py +++ b/coverage_comment/coverage.py @@ -161,8 +161,8 @@ def _make_coverage_info(data: dict) -> CoverageInfo: ), missing_lines=data["missing_lines"], excluded_lines=data["excluded_lines"], - num_branches=data.get("num_branches"), - num_partial_branches=data.get("num_partial_branches"), + num_branches=data.get("num_branches", 0), + num_partial_branches=data.get("num_partial_branches", 0), covered_branches=data.get("covered_branches", 0), missing_branches=data.get("missing_branches", 0), ) From a4968586f42d43b70ff7dd4ee114e2592c5f041d Mon Sep 17 00:00:00 2001 From: "FeRD (Frank Dana)" Date: Fri, 13 Sep 2024 15:42:10 -0400 Subject: [PATCH 9/9] Add tests for _make_coverage_info --- tests/unit/test_coverage.py | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/tests/unit/test_coverage.py b/tests/unit/test_coverage.py index 67a03d1c..621219da 100644 --- a/tests/unit/test_coverage.py +++ b/tests/unit/test_coverage.py @@ -150,6 +150,42 @@ def test_generate_coverage_markdown(mocker): assert result == "foo" +def test__make_coverage_info(): + result = coverage._make_coverage_info( + { + "covered_lines": 14, + "num_statements": 20, + "missing_lines": 6, + "excluded_lines": 0, + } + ) + assert isinstance(result, coverage.CoverageInfo) + assert result.percent_covered == decimal.Decimal(14) / decimal.Decimal(20) + assert result.num_branches == 0 + assert result.num_partial_branches == 0 + assert result.covered_branches == 0 + assert result.missing_branches == 0 + + +def test__make_coverage_info__with_branches(): + result = coverage._make_coverage_info( + { + "covered_lines": 4, + "num_statements": 10, + "missing_lines": 1, + "excluded_lines": 0, + "covered_branches": 4, + "num_branches": 6, + "num_partial_branches": 2, + } + ) + assert isinstance(result, coverage.CoverageInfo) + assert result.percent_covered == decimal.Decimal(4 + 4) / decimal.Decimal(10 + 6) + assert result.covered_branches == 4 + assert result.missing_branches == 0 + assert result.excluded_lines == 0 + + @pytest.mark.parametrize( "added_lines, update_obj, expected", [