diff --git a/README.md b/README.md index 65248ec..c5a61a8 100644 --- a/README.md +++ b/README.md @@ -40,8 +40,6 @@ Note: Either `GITHUB_PR_NUMBER` or `GITHUB_REF` is required. `GITHUB_PR_NUMBER` - `MINIMUM_GREEN`: The minimum coverage percentage for green status. Default is 100. - `MINIMUM_ORANGE`: The minimum coverage percentage for orange status. Default is 70. - `BRANCH_COVERAGE`: Show branch coverage in the report. Default is False. -- `ANNOTATE_MISSING_LINES`: Whether to annotate missing lines in the coverage report. Default is False. -- `ANNOTATION_TYPE`: The type of annotation to use for missing lines. 'notice' or 'warning' or 'error'. Default is 'warning'. - `MAX_FILES_IN_COMMENT`: The maximum number of files to include in the coverage report comment. Default is 25. - `SKIP_COVERED_FILES_IN_REPORT`: Skip the files with coverage 100% from the report. Default is True. - `COMPLETE_PROJECT_REPORT`: Whether to include the complete project coverage report in the comment. Default is False. diff --git a/codecov/config.py b/codecov/config.py index edc1729..4011c11 100644 --- a/codecov/config.py +++ b/codecov/config.py @@ -27,12 +27,6 @@ def str_to_bool(value: str) -> bool: return value.lower() in ('1', 'true', 'yes') -class AnnotationType(Enum): - NOTICE = 'notice' - WARNING = 'warning' - ERROR = 'error' - - class TestFramework(Enum): PYTEST = 'pytest' JEST = 'jest' @@ -53,8 +47,6 @@ class Config: TEST_FRAMEWORK: TestFramework = TestFramework.PYTEST # TODO: Remove branch coverage and just use the report BRANCH_COVERAGE: bool = False - ANNOTATE_MISSING_LINES: bool = False - ANNOTATION_TYPE: AnnotationType = AnnotationType.WARNING MAX_FILES_IN_COMMENT: int = 25 SKIP_COVERED_FILES_IN_REPORT: bool = True COMPLETE_PROJECT_REPORT: bool = False @@ -74,10 +66,6 @@ def clean_minimum_green(cls, value: str) -> decimal.Decimal: def clean_minimum_orange(cls, value: str) -> decimal.Decimal: return decimal.Decimal(value) - @classmethod - def clean_annotate_missing_lines(cls, value: str) -> bool: - return str_to_bool(value) - @classmethod def clean_branch_coverage(cls, value: str) -> bool: return str_to_bool(value) @@ -94,10 +82,6 @@ def clean_skip_covered_files_in_report(cls, value: str) -> bool: def clean_debug(cls, value: str) -> bool: return str_to_bool(value) - @classmethod - def clean_annotation_type(cls, value: str) -> AnnotationType: - return AnnotationType(value) - @classmethod def clean_github_pr_number(cls, value: str) -> int: return int(value) diff --git a/codecov/diff_grouper.py b/codecov/diff_grouper.py index 2c7e53f..d0dc6cd 100644 --- a/codecov/diff_grouper.py +++ b/codecov/diff_grouper.py @@ -8,7 +8,7 @@ from codecov.coverage.jest import JestCoverage from codecov.coverage.pytest import PytestCoverage -MAX_ANNOTATION_GAP = 3 +MAX_GROUP_GAP = 3 def _flatten_branches(branches: list[list[int]] | None) -> list[int]: @@ -45,7 +45,7 @@ def get_missing_groups( values=coverage_file.missing_lines, separators=separators, joiners=joiners, - max_gap=MAX_ANNOTATION_GAP, + max_gap=MAX_GROUP_GAP, ): yield groups.Group( file=path, @@ -70,7 +70,7 @@ def get_diff_missing_groups( values=diff_file.missing_statements, separators=separators, joiners=joiners, - max_gap=MAX_ANNOTATION_GAP, + max_gap=MAX_GROUP_GAP, ): yield groups.Group( file=path, @@ -93,21 +93,7 @@ def fill_branch_missing_groups(coverage: 'PytestCoverage') -> 'PytestCoverage': values=_flatten_branches(branches=file_coverage.missing_branches), separators=separators, joiners=joiners, - max_gap=MAX_ANNOTATION_GAP, + max_gap=MAX_GROUP_GAP, ) ] return coverage - - -def get_diff_branch_missing_groups( - coverage: 'PytestCoverage', - diff_coverage: 'DiffCoverage', -) -> Iterable[groups.Group]: - for path, _ in diff_coverage.files.items(): - coverage_file = coverage.files[path] - for start, end in coverage_file.missing_branches or []: - yield groups.Group( - file=path, - line_start=start, - line_end=end, - ) diff --git a/codecov/groups.py b/codecov/groups.py index 52d7984..23b88b5 100644 --- a/codecov/groups.py +++ b/codecov/groups.py @@ -2,7 +2,6 @@ import functools import itertools import pathlib -from collections.abc import Iterable @dataclasses.dataclass(frozen=True) @@ -12,64 +11,6 @@ class Group: line_end: int -@dataclasses.dataclass -class Annotation: - file: pathlib.Path - line_start: int - line_end: int - title: str - message_type: str - message: str - - def __str__(self) -> str: - return f'{self.message_type.upper()} {self.message} in {self.file}:{self.line_start}-{self.line_end}' - - def __repr__(self) -> str: - return f'{self.message_type.upper()} {self.message} in {self.file}:{self.line_start}-{self.line_end}' - - def to_dict(self): - return { - 'file': str(self.file), - 'line_start': self.line_start, - 'line_end': self.line_end, - 'title': self.title, - 'message_type': self.message_type, - 'message': self.message, - } - - -def create_missing_coverage_annotations( - annotation_type: str, - annotations: Iterable[Group], - branch: bool = False, -) -> list[Annotation]: - """ - Create annotations for lines with missing coverage. - - annotation_type: The type of annotation to create. Can be either "error" or "warning" or "notice". - annotations: A list of tuples of the form (file, line_start, line_end) - branch: Whether to create branch coverage annotations or not - """ - formatted_annotations: list[Annotation] = [] - for group in annotations: - if group.line_start == group.line_end: - message = f'Missing {"branch " if branch else ""}coverage on line {group.line_start}' - else: - message = f'Missing {"branch " if branch else ""}coverage on lines {group.line_start}-{group.line_end}' - - formatted_annotations.append( - Annotation( - file=group.file, - line_start=group.line_start, - line_end=group.line_end, - title=f'Missing {"branch " if branch else ""}coverage', - message_type=annotation_type, - message=message, - ) - ) - return formatted_annotations - - def compute_contiguous_groups( values: list[int], separators: set[int], diff --git a/codecov/main.py b/codecov/main.py index aa49ee4..0f7a1b2 100644 --- a/codecov/main.py +++ b/codecov/main.py @@ -1,7 +1,6 @@ import os -from typing import cast -from codecov import diff_grouper, groups, template +from codecov import template from codecov.config import Config from codecov.coverage.base import BaseCoverageHandler, DiffCoverage from codecov.coverage.jest import JestCoverage @@ -49,7 +48,6 @@ def _init_coverage_module(self): def run(self): self._process_coverage() self._create_comment() - self._generate_annotations() def _process_coverage(self): log.info('Processing coverage data') @@ -123,40 +121,3 @@ def _create_comment(self) -> None: self.github.post_comment(contents=comment, marker=self.marker) log.info('Comment created on PR.') - - def _generate_annotations(self): - if not self.config.ANNOTATE_MISSING_LINES: - log.info('Skipping annotations generation.') - return - - log.info('Generating annotations for missing lines.') - annotations = diff_grouper.get_diff_missing_groups(coverage=self.coverage, diff_coverage=self.diff_coverage) - formatted_annotations = groups.create_missing_coverage_annotations( - annotation_type=self.config.ANNOTATION_TYPE.value, - annotations=annotations, - ) - - if self.config.BRANCH_COVERAGE: - branch_annotations = diff_grouper.get_diff_branch_missing_groups( - coverage=cast(PytestCoverage, self.coverage), - diff_coverage=self.diff_coverage, - ) - formatted_annotations.extend( - groups.create_missing_coverage_annotations( - annotation_type=self.config.ANNOTATION_TYPE.value, - annotations=branch_annotations, - branch=True, - ) - ) - - if not formatted_annotations: - log.info('No annotations to generate. Exiting.') - return - - # Print to console - log.info('Annotations:') - yellow = '\033[93m' - reset = '\033[0m' - print(yellow, end='') - print(*formatted_annotations, sep='\n') - print(reset, end='') diff --git a/codecov/template_files/comment.md.j2 b/codecov/template_files/comment.md.j2 index 728e2c9..8d528ab 100644 --- a/codecov/template_files/comment.md.j2 +++ b/codecov/template_files/comment.md.j2 @@ -10,7 +10,7 @@ {%- endif -%} {%- endblock coverage_evolution_badge -%} - +   {#- PR coverage badge -#} {%- block diff_coverage_badge -%} {%- set text = (diff_coverage.total_percent_covered | pct) ~ " of the statements added in this PR are covered." -%} diff --git a/run.py b/run.py index 66544e0..a8d82dc 100644 --- a/run.py +++ b/run.py @@ -1,6 +1,7 @@ import sys from codecov.exceptions import CoreBaseException +from codecov.log import log from codecov.main import Main @@ -8,7 +9,8 @@ def main_call(name): if name == '__main__': try: Main().run() - except CoreBaseException: + except CoreBaseException as e: + log.error(f'Error: {str(e)}') sys.exit(1) diff --git a/tests/conftest.py b/tests/conftest.py index 3a9e728..4f80141 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -425,5 +425,4 @@ def gh(gh_client, test_config: Config): github_mock.user.email = 'baz@foobar.com' github_mock.user.login = 'foo' github_mock.post_comment = MagicMock(return_value=None) - github_mock.write_annotations_to_branch = MagicMock(return_value=None) return github_mock diff --git a/tests/test_config.py b/tests/test_config.py index 5af7db5..625765d 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -51,8 +51,6 @@ def test_config_from_environ_sample(): 'GITHUB_REF': 'main', 'MINIMUM_GREEN': '90', 'MINIMUM_ORANGE': '70', - 'ANNOTATE_MISSING_LINES': 'True', - 'ANNOTATION_TYPE': 'notice', 'MAX_FILES_IN_COMMENT': 25, 'COMPLETE_PROJECT_REPORT': 'True', 'COVERAGE_REPORT_URL': 'https://your_coverage_report_url', @@ -66,8 +64,6 @@ def test_config_from_environ_sample(): GITHUB_REF='main', MINIMUM_GREEN=decimal.Decimal('90'), MINIMUM_ORANGE=decimal.Decimal('70'), - ANNOTATE_MISSING_LINES=True, - ANNOTATION_TYPE=config.AnnotationType.NOTICE, MAX_FILES_IN_COMMENT=25, COMPLETE_PROJECT_REPORT=True, COVERAGE_REPORT_URL='https://your_coverage_report_url', @@ -87,11 +83,6 @@ def test_config_required_pr_or_ref(): ) -def test_config_invalid_annotation_type(): - with pytest.raises(ValueError): - config.Config.from_environ({'ANNOTATION_TYPE': 'foo'}) - - @pytest.mark.parametrize( 'input_data, output_data', [ @@ -120,11 +111,6 @@ def test_config_clean_minimum_orange(): assert value == decimal.Decimal('70') -def test_config_clean_annotate_missing_lines(): - value = config.Config.clean_annotate_missing_lines('True') - assert value is True - - def test_config_clean_branch_coverage(): value = config.Config.clean_branch_coverage('False') assert value is False @@ -145,16 +131,6 @@ def test_config_clean_debug(): assert value is False -def test_config_clean_annotation_type(): - value = config.Config.clean_annotation_type('warning') - assert value == config.AnnotationType.WARNING - - -def test_config_clean_annotation_type_invalid(): - with pytest.raises(ValueError): - config.Config.clean_annotation_type('foo') - - def test_config_clean_github_pr_number(): value = config.Config.clean_github_pr_number('123') assert value == 123 diff --git a/tests/test_diff_grouper.py b/tests/test_diff_grouper.py index c6777a4..5a31c19 100644 --- a/tests/test_diff_grouper.py +++ b/tests/test_diff_grouper.py @@ -58,30 +58,6 @@ def test_flatten_branches(): assert flattened_branches == [1, 2, 3, 4, 5] -def test_get_branch_missing_groups(coverage_obj, diff_coverage_obj): - result = diff_grouper.get_diff_branch_missing_groups(coverage=coverage_obj, diff_coverage=diff_coverage_obj) - - assert list(result) == [ - groups.Group(file=pathlib.Path('codebase/code.py'), line_start=5, line_end=6), - groups.Group(file=pathlib.Path('codebase/code.py'), line_start=10, line_end=11), - ] - - -def test_get_branch_missing_groups_more_files(coverage_obj_more_files, diff_coverage_obj_more_files): - result = diff_grouper.get_diff_branch_missing_groups( - coverage=coverage_obj_more_files, - diff_coverage=diff_coverage_obj_more_files, - ) - - assert list(result) == [ - groups.Group(file=pathlib.Path('codebase/code.py'), line_start=5, line_end=6), - groups.Group(file=pathlib.Path('codebase/code.py'), line_start=10, line_end=11), - groups.Group(file=pathlib.Path('codebase/other.py'), line_start=3, line_end=4), - groups.Group(file=pathlib.Path('codebase/other.py'), line_start=5, line_end=6), - groups.Group(file=pathlib.Path('codebase/other.py'), line_start=10, line_end=11), - ] - - def test_fill_branch_missing_groups(coverage_obj): result = diff_grouper.fill_branch_missing_groups(coverage=coverage_obj) diff --git a/tests/test_groups.py b/tests/test_groups.py index 43b19e8..3b3a75f 100644 --- a/tests/test_groups.py +++ b/tests/test_groups.py @@ -1,105 +1,6 @@ -import pathlib - import pytest -from codecov.groups import Annotation, Group, compute_contiguous_groups, create_missing_coverage_annotations - - -def test_annotation_str(): - file = pathlib.Path('/path/to/file.py') - annotation = Annotation( - file=file, line_start=10, line_end=15, title='Error', message_type='ERROR', message='Something went wrong' - ) - expected_str = 'ERROR Something went wrong in /path/to/file.py:10-15' - assert str(annotation) == expected_str - - -def test_annotation_repr(): - file = pathlib.Path('/path/to/file.py') - annotation = Annotation( - file=file, line_start=10, line_end=15, title='Error', message_type='ERROR', message='Something went wrong' - ) - expected_repr = 'ERROR Something went wrong in /path/to/file.py:10-15' - assert repr(annotation) == expected_repr - - -def test_annotation_to_dict(): - file = pathlib.Path('/path/to/file.py') - annotation = Annotation( - file=file, line_start=10, line_end=15, title='Error', message_type='ERROR', message='Something went wrong' - ) - expected_dict = { - 'file': '/path/to/file.py', - 'line_start': 10, - 'line_end': 15, - 'title': 'Error', - 'message_type': 'ERROR', - 'message': 'Something went wrong', - } - assert annotation.to_dict() == expected_dict - - -@pytest.mark.parametrize( - 'annotation_type, annotations, expected_annotations', - [ - ('error', [], []), - ( - 'error', - [Group(file=pathlib.Path('file.py'), line_start=10, line_end=10)], - [ - Annotation( - file=pathlib.Path('file.py'), - line_start=10, - line_end=10, - title='Missing coverage', - message_type='error', - message='Missing coverage on line 10', - ) - ], - ), - ( - 'warning', - [Group(file=pathlib.Path('file.py'), line_start=5, line_end=10)], - [ - Annotation( - file=pathlib.Path('file.py'), - line_start=5, - line_end=10, - title='Missing coverage', - message_type='warning', - message='Missing coverage on lines 5-10', - ) - ], - ), - ( - 'notice', - [ - Group(file=pathlib.Path('file1.py'), line_start=5, line_end=5), - Group(file=pathlib.Path('file2.py'), line_start=10, line_end=15), - ], - [ - Annotation( - file=pathlib.Path('file1.py'), - line_start=5, - line_end=5, - title='Missing coverage', - message_type='notice', - message='Missing coverage on line 5', - ), - Annotation( - file=pathlib.Path('file2.py'), - line_start=10, - line_end=15, - title='Missing coverage', - message_type='notice', - message='Missing coverage on lines 10-15', - ), - ], - ), - ], -) -def test_create_missing_coverage_annotations(annotation_type, annotations, expected_annotations): - assert create_missing_coverage_annotations(annotation_type, annotations) == expected_annotations +from codecov.groups import compute_contiguous_groups @pytest.mark.parametrize( diff --git a/tests/test_main.py b/tests/test_main.py index a0dac9c..f386ace 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -121,67 +121,14 @@ def test_create_comment( marker=template.MARKER, ) - @patch('codecov.main.groups.create_missing_coverage_annotations') - def test_generate_annotations_empty( - self, - create_missing_coverage_annotations_mock: MagicMock, - test_config, - gh, - coverage_obj, - diff_coverage_obj, - ): - with patch.object(Main, '_init_config', return_value=test_config): - with patch.object(Main, '_init_github', return_value=gh): - main = Main() - main.config.ANNOTATE_MISSING_LINES = False - assert main._generate_annotations() is None - - create_missing_coverage_annotations_mock.return_value = [] - with patch.object(Main, '_init_config', return_value=test_config): - with patch.object(Main, '_init_github', return_value=gh): - main = Main() - main.config.ANNOTATE_MISSING_LINES = True - main.coverage = coverage_obj - main.diff_coverage = diff_coverage_obj - assert main._generate_annotations() is None - - with patch.object(Main, '_init_config', return_value=test_config): - with patch.object(Main, '_init_github', return_value=gh): - main = Main() - main.config.ANNOTATE_MISSING_LINES = True - main.config.BRANCH_COVERAGE = True - main.coverage = coverage_obj - main.diff_coverage = diff_coverage_obj - assert main._generate_annotations() is None - - def test_generate_annotations(self, test_config, gh, coverage_obj, diff_coverage_obj): - with patch.object(Main, '_init_config', return_value=test_config): - with patch.object(Main, '_init_github', return_value=gh): - main = Main() - main.config.ANNOTATE_MISSING_LINES = True - main.coverage = coverage_obj - main.diff_coverage = diff_coverage_obj - assert main._generate_annotations() is None - - with patch.object(Main, '_init_config', return_value=test_config): - with patch.object(Main, '_init_github', return_value=gh): - main = Main() - main.config.BRANCH_COVERAGE = True - main.config.ANNOTATE_MISSING_LINES = True - main.coverage = coverage_obj - main.diff_coverage = diff_coverage_obj - assert main._generate_annotations() is None - def test_run(self, test_config, gh): with patch.object(Main, '_init_config', return_value=test_config): with patch.object(Main, '_init_github', return_value=gh): main = Main() main._process_coverage = MagicMock() main._create_comment = MagicMock() - main._generate_annotations = MagicMock() assert main.run() is None main._process_coverage.assert_called_once() main._create_comment.assert_called_once() - main._generate_annotations.assert_called_once()