Skip to content

Commit

Permalink
Merge pull request #2 from MathieuLamiot/chore/270-review-readme-tests
Browse files Browse the repository at this point in the history
Chore/270 review readme tests
  • Loading branch information
MathieuLamiot authored Sep 4, 2024
2 parents 02237c6 + bcbf039 commit ee9a395
Show file tree
Hide file tree
Showing 10 changed files with 149 additions and 23 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
.idea
venv
27venv
.venv

# C extensions
*.so
Expand Down
12 changes: 12 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,18 @@ It can be enabled by using the ``-q``/``--quiet`` flag:
If enabled, the tool will only print errors and failures but no information or warning messages.

Compatibility with multi-line statements
----------
``diff-cover`` relies on the comparison of diff reports and coverage reports, and does not report
lines that appear in one and not in the other. While diff reports list all lines that changed,
coverage reports usually list code statements. As a result, a change in a multi-line statement may not be analyzed by ``diff-cover``.

As a workaround, you can use the argument ``--expand-coverage-report``: lines not appearing in the coverage reports will be added to them with the same number of hits as the previously reported line. ``diff-cover`` will then perform diff coverage analysis on all changed lines.

Notes:
- This argument is only available for XML coverage reports.
- This workaround is designed under the assumption that the coverage tool reports untested statements with hits set to 0, and it reports statements based on the opening line.

Configuration files
-------------------
Both tools allow users to specify the options in a configuration file with `--config-file`/`-c`:
Expand Down
2 changes: 1 addition & 1 deletion diff_cover/diff_cover_tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ def parse_coverage_args(argv):
)

parser.add_argument(
"--expand_coverage_report",
"--expand-coverage-report",
action="store_true",
default=None,
help=EXPAND_COVERAGE_REPORT,
Expand Down
28 changes: 16 additions & 12 deletions diff_cover/violationsreporters/violations_reporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -224,17 +224,20 @@ def _cache_file(self, src_path):
reported_line_hits[int(line.get(_number))] = int(
line.get(_hits, 0)
)
last_hit_number = 0
for line_number in range(
min(reported_line_hits.keys()), max(reported_line_hits.keys())
):
if line_number in reported_line_hits:
last_hit_number = reported_line_hits[line_number]
else:
# This is an unreported line. We add it with the previous line hit score
line_nodes.append(
{_hits: last_hit_number, _number: line_number}
)
if reported_line_hits:
last_hit_number = 0
for line_number in range(
min(reported_line_hits.keys()),
max(reported_line_hits.keys()),
):
if line_number in reported_line_hits:
last_hit_number = reported_line_hits[line_number]
else:
# This is an unreported line.
# We add it with the previous line hit score
line_nodes.append(
{_hits: last_hit_number, _number: line_number}
)

# First case, need to define violations initially
if violations is None:
Expand Down Expand Up @@ -284,7 +287,7 @@ def measured_lines(self, src_path):

class LcovCoverageReporter(BaseViolationReporter):
"""
Query information from a Cobertura|Clover|JaCoCo XML coverage report.
Query information from a LCov coverage report.
"""

def __init__(self, lcov_roots, src_roots=None):
Expand Down Expand Up @@ -344,6 +347,7 @@ def parse(lcov_file):
"BRF",
"BRH",
"BRDA",
"VER",
]:
# these are valid lines, but not we don't need them
continue
Expand Down
18 changes: 9 additions & 9 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ pytest-mock = "^3.14.0"
pycodestyle = ">=2.9.1"
flake8 = "^5.0.4"
pyflakes = "^2.5.0"
pylint = "^3.2.5"
pylint = "^3.2.6"
pylint-pytest = "^1.1.8"
pydocstyle = "^6.1.1"
black = "^24.4.2"
Expand Down
24 changes: 24 additions & 0 deletions tests/fixtures/coverage_missing_lines.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?xml version="1.0" ?>
<!DOCTYPE coverage
SYSTEM 'http://cobertura.sourceforge.net/xml/coverage-03.dtd'>
<coverage branch-rate="0" line-rate="0.8792" timestamp="1371471706873" version="3.6">
<packages>
<package branch-rate="0" complexity="0" line-rate="0.9915" name="">
<classes>
<class branch-rate="0" complexity="0" filename="test_src.txt" line-rate="0.9643" name="test_src.txt">
<methods/>
<lines>
<line hits="1" number="1"/>
<line hits="0" number="2"/>
<line hits="0" number="4"/>
<line hits="1" number="5"/>
<line hits="1" number="7"/>
<line hits="0" number="8"/>
<line hits="1" number="9"/>
<line hits="0" number="10"/>
</lines>
</class>
</classes>
</package>
</packages>
</coverage>
10 changes: 10 additions & 0 deletions tests/fixtures/expand_console_report.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
-------------
Diff Coverage
Diff: origin/main...HEAD, staged and unstaged changes
-------------
test_src.txt (50.0%): Missing lines 2-4,8,10
-------------
Total: 10 lines
Missing: 5 lines
Coverage: 50%
-------------
14 changes: 14 additions & 0 deletions tests/test_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,20 @@ def test_show_uncovered_lines_console(self):
["diff-cover", "--show-uncovered", "coverage.xml"],
)

def test_expand_coverage_report_complete_report(self):
self._check_console_report(
"git_diff_add.txt",
"add_console_report.txt",
["diff-cover", "coverage.xml", "--expand-coverage-report"],
)

def test_expand_coverage_report_uncomplete_report(self):
self._check_console_report(
"git_diff_add.txt",
"expand_console_report.txt",
["diff-cover", "coverage_missing_lines.xml", "--expand-coverage-report"],
)


class TestDiffQualityIntegration(ToolsIntegrationBase):
"""
Expand Down
61 changes: 61 additions & 0 deletions tests/test_violations_reporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,21 @@ class TestXmlCoverageReporterTest:
ONE_VIOLATION = {Violation(11, None)}
VERY_MANY_MEASURED = {2, 3, 5, 7, 11, 13, 17, 23, 24, 25, 26, 26, 27}

MANY_VIOLATIONS_EXPANDED_MANY_MEASURED = {
Violation(3, None),
Violation(4, None),
Violation(7, None),
Violation(8, None),
Violation(9, None),
Violation(10, None),
Violation(11, None),
Violation(12, None),
Violation(13, None),
Violation(14, None),
Violation(15, None),
Violation(16, None),
}

@pytest.fixture(autouse=True)
def patch_git_patch(self, mocker):
# Paths generated by git_path are always the given argument
Expand Down Expand Up @@ -266,6 +281,52 @@ def test_no_such_file(self):
result = coverage.violations("file.py")
assert result == set()

def test_expand_unreported_lines_when_configured(self):
# Construct the XML report
file_paths = ["file1.java"]
# fixture
violations = self.MANY_VIOLATIONS
measured = self.MANY_MEASURED
xml = self._coverage_xml(file_paths, violations, measured)

# Parse the reports
coverage = XmlCoverageReporter([xml], expand_coverage_report=True)

# Expect that the name is set
assert coverage.name() == "XML"

# By construction, each file has the same set
# of covered/uncovered lines
assert self.MANY_VIOLATIONS_EXPANDED_MANY_MEASURED == coverage.violations(
"file1.java"
)

def test_expand_unreported_lines_without_violations(self):
# Construct the XML report
file_paths = ["file1.java"]
# fixture
violations = {}
measured = self.MANY_MEASURED
xml = self._coverage_xml(file_paths, violations, measured)

# Parse the reports
coverage = XmlCoverageReporter([xml], expand_coverage_report=True)

assert set() == coverage.violations("file1.java")

def test_expand_unreported_lines_without_measured(self):
# Construct the XML report
file_paths = ["file1.java"]
# fixture
violations = {}
measured = {}
xml = self._coverage_xml(file_paths, violations, measured)

# Parse the reports
coverage = XmlCoverageReporter([xml], expand_coverage_report=True)

assert set() == coverage.violations("file1.java")

def _coverage_xml(self, file_paths, violations, measured, source_paths=None):
"""
Build an XML tree with source files specified by `file_paths`.
Expand Down

0 comments on commit ee9a395

Please sign in to comment.