diff-cover 10.4.2__tar.gz → 10.5.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {diff_cover-10.4.2 → diff_cover-10.5.1}/PKG-INFO +1 -1
- {diff_cover-10.4.2 → diff_cover-10.5.1}/diff_cover/command_runner.py +32 -1
- {diff_cover-10.4.2 → diff_cover-10.5.1}/diff_cover/diff_cover_tool.py +6 -1
- {diff_cover-10.4.2 → diff_cover-10.5.1}/diff_cover/diff_quality_tool.py +6 -1
- diff_cover-10.5.1/diff_cover/violationsreporters/clover.py +84 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/diff_cover/violationsreporters/violations_reporter.py +36 -20
- {diff_cover-10.4.2 → diff_cover-10.5.1}/pyproject.toml +1 -1
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/test_clover_violations_reporter.py +2 -3
- diff_cover-10.5.1/tests/test_command_runner_missing_executable.py +92 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/test_violations_reporter.py +122 -7
- {diff_cover-10.4.2 → diff_cover-10.5.1}/LICENSE +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/NOTICE +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/README.rst +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/diff_cover/__init__.py +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/diff_cover/config_parser.py +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/diff_cover/diff_reporter.py +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/diff_cover/git_diff.py +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/diff_cover/git_path.py +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/diff_cover/hook.py +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/diff_cover/hookspecs.py +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/diff_cover/report_generator.py +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/diff_cover/snippets.py +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/diff_cover/templates/console_coverage_report.txt +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/diff_cover/templates/console_quality_report.txt +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/diff_cover/templates/external_style.css +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/diff_cover/templates/github_coverage_annotations.txt +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/diff_cover/templates/html_coverage_report.html +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/diff_cover/templates/html_quality_report.html +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/diff_cover/templates/markdown_coverage_report.md +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/diff_cover/templates/markdown_quality_report.md +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/diff_cover/templates/snippet_content.html +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/diff_cover/templates/snippet_content.md +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/diff_cover/templates/snippet_content.txt +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/diff_cover/templates/snippet_style.html +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/diff_cover/util.py +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/diff_cover/violationsreporters/__init__.py +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/diff_cover/violationsreporters/base.py +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/diff_cover/violationsreporters/java_violations_reporter.py +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/__init__.py +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/conftest.py +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/add_console_report.txt +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/add_html_report.html +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/add_json_report.json +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/add_json_report_float.json +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/add_markdown_report.md +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/changed_console_report.txt +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/changed_html_report.html +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/coverage.xml +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/coverage1.xml +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/coverage2.xml +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/coverage_missing_lines.xml +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/cpp_functions_console_report.txt +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/cpp_functions_coverage.lcov +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/delete_console_report.txt +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/delete_html_report.html +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/dotnet_coverage.xml +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/empty.txt +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/empty_pycodestyle_violations.txt +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/expand_console_report.txt +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/external_css_html_report.html +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/external_style.css +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/git_diff_add.txt +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/git_diff_changed.txt +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/git_diff_code_dupe.txt +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/git_diff_cpp.txt +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/git_diff_cpp_functions.txt +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/git_diff_delete.txt +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/git_diff_dotnet.txt +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/git_diff_external_css.txt +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/git_diff_lua.txt +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/git_diff_moved.txt +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/git_diff_mult.txt +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/git_diff_python.txt +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/git_diff_subdir.txt +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/git_diff_typescript.txt +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/git_diff_typescript_branches.txt +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/git_diff_unicode.txt +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/git_diff_violations.txt +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/git_diff_violations_two_files.txt +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/hello.py +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/hi.py +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/html_report.html +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/html_report_empty.html +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/html_report_one_snippet.html +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/html_report_two_snippets.html +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/lcov.info +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/lua_console_report.txt +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/luacoverage.xml +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/markdown_report_one_snippet.md +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/markdown_report_two_snippets.md +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/moved_console_report.txt +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/moved_coverage.xml +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/moved_html_report.html +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/mult_inputs_console_report.txt +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/mult_inputs_html_report.html +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/pycodestyle_report.txt +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/pycodestyle_violations_report.html +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/pycodestyle_violations_report.json +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/pycodestyle_violations_report.md +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/pycodestyle_violations_report.txt +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/pycodestyle_violations_report_external_css.html +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/pycodestyle_violations_report_float.json +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/pyflakes_two_files.txt +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/pyflakes_violations_report.html +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/pyflakes_violations_report.txt +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/pylint_dupe.txt +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/pylint_dupe_violations_report.txt +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/pylint_report.txt +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/pylint_violations_console_report.txt +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/pylint_violations_report.html +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/pylint_violations_report.txt +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/pylintrc +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/real_cpp_console_report.txt +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/real_cpp_coverage.lcov +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/real_python_console_report.txt +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/real_python_coverage.lcov +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/real_typescript_console_report.txt +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/real_typescript_coverage.lcov +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/show_uncovered_lines_console.txt +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/snippet.css +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/snippet_8859.py +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/snippet_arabic_output.html +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/snippet_default.html +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/snippet_invalid_violations.html +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/snippet_list.html +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/snippet_list.md +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/snippet_list2.md +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/snippet_list3.md +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/snippet_no_filename_ext.html +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/snippet_src.py +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/snippet_src2.cpp +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/snippet_src3.cpp +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/snippet_unicode.html +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/snippet_unicode.py +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/snippet_unicode_html_output.html +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/subdir_coverage_console_report.txt +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/subdir_coverage_html_report.html +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/test_src.txt +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/typescript_branches_console_report.txt +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/typescript_branches_coverage.lcov +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/unicode_console_report.txt +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/unicode_coverage.xml +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/unicode_html_report.html +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/unicode_test_src.txt +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/violations_test_file.py +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/helpers.py +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/snippet_list_unicode.html +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/test_clover_violations_reporter/test.xml +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/test_config_parser.py +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/test_diff_cover_main.py +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/test_diff_cover_tool.py +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/test_diff_quality_main.py +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/test_diff_reporter.py +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/test_git_diff.py +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/test_git_diff_file.py +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/test_git_path.py +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/test_integration.py +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/test_java_violations_reporter.py +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/test_report_generator.py +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/test_snippets.py +0 -0
- {diff_cover-10.4.2 → diff_cover-10.5.1}/tests/test_util.py +0 -0
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import subprocess
|
|
2
2
|
import sys
|
|
3
3
|
|
|
4
|
+
GIT_INSTALL_URL = "https://git-scm.com/book/en/v2/Getting-Started-Installing-Git"
|
|
5
|
+
|
|
4
6
|
|
|
5
7
|
class CommandError(Exception):
|
|
6
8
|
"""
|
|
@@ -8,6 +10,16 @@ class CommandError(Exception):
|
|
|
8
10
|
"""
|
|
9
11
|
|
|
10
12
|
|
|
13
|
+
class ExecutableNotFoundError(CommandError):
|
|
14
|
+
"""
|
|
15
|
+
Error raised when the executable of a command cannot be found at all.
|
|
16
|
+
|
|
17
|
+
This is a `CommandError` so that existing handling keeps working, but a
|
|
18
|
+
distinct type so the command line tools can tell "your tooling is not
|
|
19
|
+
installed" apart from "the command ran and failed".
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
|
|
11
23
|
def execute(command, exit_codes=None):
|
|
12
24
|
"""Execute provided command returning the stdout
|
|
13
25
|
Args:
|
|
@@ -24,7 +36,12 @@ def execute(command, exit_codes=None):
|
|
|
24
36
|
exit_codes = [0]
|
|
25
37
|
|
|
26
38
|
stdout_pipe = subprocess.PIPE
|
|
27
|
-
|
|
39
|
+
try:
|
|
40
|
+
popen = subprocess.Popen(command, stdout=stdout_pipe, stderr=stdout_pipe)
|
|
41
|
+
except FileNotFoundError as exc:
|
|
42
|
+
raise ExecutableNotFoundError(_executable_not_found_message(command)) from exc
|
|
43
|
+
|
|
44
|
+
with popen as process:
|
|
28
45
|
try:
|
|
29
46
|
stdout, stderr = process.communicate()
|
|
30
47
|
except OSError:
|
|
@@ -52,6 +69,20 @@ def run_command_for_code(command):
|
|
|
52
69
|
return process.returncode
|
|
53
70
|
|
|
54
71
|
|
|
72
|
+
def _executable_not_found_message(command):
|
|
73
|
+
"""
|
|
74
|
+
Explain that the executable of `command` is not installed or not on PATH.
|
|
75
|
+
"""
|
|
76
|
+
executable = _ensure_unicode(command[0]) if command else ""
|
|
77
|
+
message = (
|
|
78
|
+
f"'{executable}' was not found. diff-cover needs '{executable}' to be "
|
|
79
|
+
"installed and on your PATH in order to run."
|
|
80
|
+
)
|
|
81
|
+
if executable == "git":
|
|
82
|
+
message += f" See {GIT_INSTALL_URL} for installation instructions."
|
|
83
|
+
return message
|
|
84
|
+
|
|
85
|
+
|
|
55
86
|
def _ensure_unicode(text):
|
|
56
87
|
"""
|
|
57
88
|
Ensures the text passed in becomes unicode
|
|
@@ -7,6 +7,7 @@ import warnings
|
|
|
7
7
|
import xml.etree.ElementTree as etree
|
|
8
8
|
|
|
9
9
|
from diff_cover import DESCRIPTION, VERSION
|
|
10
|
+
from diff_cover.command_runner import ExecutableNotFoundError
|
|
10
11
|
from diff_cover.config_parser import Tool, get_config
|
|
11
12
|
from diff_cover.diff_reporter import GitDiffReporter
|
|
12
13
|
from diff_cover.git_diff import GitDiffFileTool, GitDiffTool
|
|
@@ -418,7 +419,11 @@ def main(argv=None, directory=None):
|
|
|
418
419
|
level = logging.ERROR if quiet else logging.WARNING
|
|
419
420
|
logging.basicConfig(format="%(message)s", level=level)
|
|
420
421
|
|
|
421
|
-
|
|
422
|
+
try:
|
|
423
|
+
GitPathTool.set_cwd(directory)
|
|
424
|
+
except ExecutableNotFoundError as exc:
|
|
425
|
+
LOGGER.error("%s", exc)
|
|
426
|
+
return 1
|
|
422
427
|
fail_under = arg_dict.get("fail_under")
|
|
423
428
|
diff_tool = None
|
|
424
429
|
|
|
@@ -14,6 +14,7 @@ import pluggy
|
|
|
14
14
|
|
|
15
15
|
import diff_cover
|
|
16
16
|
from diff_cover import hookspecs
|
|
17
|
+
from diff_cover.command_runner import ExecutableNotFoundError
|
|
17
18
|
from diff_cover.config_parser import Tool, get_config
|
|
18
19
|
from diff_cover.diff_cover_tool import (
|
|
19
20
|
COMPARE_BRANCH_HELP,
|
|
@@ -339,7 +340,11 @@ def main(argv=None, directory=None):
|
|
|
339
340
|
level = logging.ERROR if quiet else logging.WARNING
|
|
340
341
|
logging.basicConfig(format="%(message)s", level=level)
|
|
341
342
|
|
|
342
|
-
|
|
343
|
+
try:
|
|
344
|
+
GitPathTool.set_cwd(directory)
|
|
345
|
+
except ExecutableNotFoundError as exc:
|
|
346
|
+
LOGGER.error("%s", exc)
|
|
347
|
+
return 1
|
|
343
348
|
fail_under = arg_dict.get("fail_under")
|
|
344
349
|
tool = arg_dict["violations"]
|
|
345
350
|
user_options = arg_dict.get("options")
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Reading the ``<file>`` elements of a Clover XML coverage report.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import itertools
|
|
6
|
+
from collections import defaultdict
|
|
7
|
+
|
|
8
|
+
from diff_cover import util
|
|
9
|
+
from diff_cover.git_path import GitPathTool
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class CloverFileIndex:
|
|
13
|
+
"""
|
|
14
|
+
The ``<file>`` elements of one Clover report, indexed for lookup by path.
|
|
15
|
+
|
|
16
|
+
Building the index costs one walk of the document. Looking a source path up
|
|
17
|
+
by walking the document instead costs that walk once per source path, which
|
|
18
|
+
is quadratic in a report with many files.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
def __init__(self, xml_document):
|
|
22
|
+
"""
|
|
23
|
+
Index every ``<file>`` element of `xml_document`.
|
|
24
|
+
|
|
25
|
+
A file is matched either by its path relative to the repository root
|
|
26
|
+
or, when the report carries absolute paths, by a suffix test. The
|
|
27
|
+
suffix can only match when the last path segment does, so candidates
|
|
28
|
+
for it are grouped by that segment and the test is applied to the few
|
|
29
|
+
that share it.
|
|
30
|
+
|
|
31
|
+
Both tables store the document position of each element so that
|
|
32
|
+
lookups can return matches in document order, as a direct walk would.
|
|
33
|
+
"""
|
|
34
|
+
self._by_relative_path = defaultdict(list)
|
|
35
|
+
self._by_last_segment = defaultdict(list)
|
|
36
|
+
for position, file_tree in enumerate(xml_document.findall(".//file")):
|
|
37
|
+
file_path = file_tree.get("path") or file_tree.get("name")
|
|
38
|
+
if not file_path:
|
|
39
|
+
continue
|
|
40
|
+
|
|
41
|
+
normalized_file_path = util.to_unix_path(file_path)
|
|
42
|
+
relative_file_path = util.to_unix_path(GitPathTool.relative_path(file_path))
|
|
43
|
+
self._by_relative_path[relative_file_path].append((position, file_tree))
|
|
44
|
+
self._by_last_segment[normalized_file_path.rsplit("/", 1)[-1]].append(
|
|
45
|
+
(position, normalized_file_path, file_tree)
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
def _files(self, src_path):
|
|
49
|
+
"""
|
|
50
|
+
Return the ``<file>`` elements for `src_path`, in document order.
|
|
51
|
+
"""
|
|
52
|
+
normalized_src_path = util.to_unix_path(src_path)
|
|
53
|
+
|
|
54
|
+
matches = {}
|
|
55
|
+
for position, file_tree in self._by_relative_path.get(normalized_src_path, ()):
|
|
56
|
+
matches[position] = file_tree
|
|
57
|
+
suffix = f"/{normalized_src_path}"
|
|
58
|
+
for position, normalized_file_path, file_tree in self._by_last_segment.get(
|
|
59
|
+
normalized_src_path.rsplit("/", 1)[-1], ()
|
|
60
|
+
):
|
|
61
|
+
if normalized_file_path.endswith(suffix):
|
|
62
|
+
matches[position] = file_tree
|
|
63
|
+
|
|
64
|
+
return [matches[position] for position in sorted(matches)]
|
|
65
|
+
|
|
66
|
+
def line_nodes(self, src_path):
|
|
67
|
+
"""
|
|
68
|
+
Return a list of nodes containing line information for `src_path`.
|
|
69
|
+
|
|
70
|
+
If the file is not present in the report, return None
|
|
71
|
+
"""
|
|
72
|
+
files = self._files(src_path)
|
|
73
|
+
if not files:
|
|
74
|
+
return None
|
|
75
|
+
lines = []
|
|
76
|
+
for file_tree in files:
|
|
77
|
+
# Clover marks an executable line as one of these three types. PHPUnit's
|
|
78
|
+
# writer emits `method` for the declaration line of every function it
|
|
79
|
+
# measured; leaving it out reported those lines as unmeasured.
|
|
80
|
+
# https://github.com/sebastianbergmann/php-code-coverage/blob/main/src/Report/Clover.php
|
|
81
|
+
lines.append(file_tree.findall('./line[@type="method"]'))
|
|
82
|
+
lines.append(file_tree.findall('./line[@type="stmt"]'))
|
|
83
|
+
lines.append(file_tree.findall('./line[@type="cond"]'))
|
|
84
|
+
return list(itertools.chain(*lines))
|
{diff_cover-10.4.2 → diff_cover-10.5.1}/diff_cover/violationsreporters/violations_reporter.py
RENAMED
|
@@ -17,6 +17,7 @@ from diff_cover.violationsreporters.base import (
|
|
|
17
17
|
RegexBasedDriver,
|
|
18
18
|
Violation,
|
|
19
19
|
)
|
|
20
|
+
from diff_cover.violationsreporters.clover import CloverFileIndex
|
|
20
21
|
|
|
21
22
|
|
|
22
23
|
class XmlCoverageReporter(BaseViolationReporter):
|
|
@@ -46,6 +47,14 @@ class XmlCoverageReporter(BaseViolationReporter):
|
|
|
46
47
|
# Values are output of `self._get_xml_classes()`
|
|
47
48
|
self._xml_cache = [{} for i in range(len(xml_roots))]
|
|
48
49
|
|
|
50
|
+
# Which format each report is in. Classifying costs a search through the
|
|
51
|
+
# document, and the answer cannot change, so do it once here rather than
|
|
52
|
+
# once per source file in `_cache_file`.
|
|
53
|
+
self._report_formats = [self._detect_report_format(root) for root in xml_roots]
|
|
54
|
+
|
|
55
|
+
# A `CloverFileIndex` for each Clover report, built on first use.
|
|
56
|
+
self._clover_file_index = [None] * len(xml_roots)
|
|
57
|
+
|
|
49
58
|
self._src_roots = src_roots or [""]
|
|
50
59
|
self._expand_coverage_report = expand_coverage_report
|
|
51
60
|
self._branch_coverage = branch_coverage
|
|
@@ -120,27 +129,36 @@ class XmlCoverageReporter(BaseViolationReporter):
|
|
|
120
129
|
lines = [clazz.findall("./lines/line") for clazz in classes]
|
|
121
130
|
return list(itertools.chain(*lines))
|
|
122
131
|
|
|
123
|
-
|
|
124
|
-
def get_src_path_line_nodes_clover(xml_document, src_path):
|
|
132
|
+
def get_src_path_line_nodes_clover(self, index, xml_document, src_path):
|
|
125
133
|
"""
|
|
126
134
|
Return a list of nodes containing line information for `src_path`
|
|
127
135
|
in `xml_document`.
|
|
128
136
|
|
|
129
137
|
If file is not present in `xml_document`, return None
|
|
130
138
|
"""
|
|
139
|
+
if self._clover_file_index[index] is None:
|
|
140
|
+
self._clover_file_index[index] = CloverFileIndex(xml_document)
|
|
141
|
+
return self._clover_file_index[index].line_nodes(src_path)
|
|
131
142
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
143
|
+
@staticmethod
|
|
144
|
+
def _detect_report_format(xml_document):
|
|
145
|
+
"""
|
|
146
|
+
Return which of the supported formats `xml_document` is in.
|
|
147
|
+
|
|
148
|
+
Clover writes a `clover` attribute on the root, but PHPUnit's Clover
|
|
149
|
+
writer does not, so fall back to the shape of its line elements.
|
|
150
|
+
"""
|
|
151
|
+
if (
|
|
152
|
+
xml_document.findall(".[@clover]")
|
|
153
|
+
or xml_document.find(".//file/line[@num][@count]") is not None
|
|
154
|
+
):
|
|
155
|
+
# see etc/schema/clover.xsd at https://bitbucket.org/atlassian/clover/src
|
|
156
|
+
return "clover"
|
|
157
|
+
if xml_document.findall(".[@name]"):
|
|
158
|
+
# https://github.com/jacoco/jacoco/blob/master/org.jacoco.report/src/org/jacoco/report/xml/report.dtd
|
|
159
|
+
return "jacoco"
|
|
160
|
+
# https://github.com/cobertura/web/blob/master/htdocs/xml/coverage-04.dtd
|
|
161
|
+
return "cobertura"
|
|
144
162
|
|
|
145
163
|
def _measured_source_path_matches(self, package_name, file_name, src_path):
|
|
146
164
|
# find src_path in any of the source roots
|
|
@@ -204,22 +222,20 @@ class XmlCoverageReporter(BaseViolationReporter):
|
|
|
204
222
|
|
|
205
223
|
# Loop through the files that contain the xml roots
|
|
206
224
|
for i, xml_document in enumerate(self._xml_roots):
|
|
207
|
-
|
|
208
|
-
|
|
225
|
+
report_format = self._report_formats[i]
|
|
226
|
+
if report_format == "clover":
|
|
209
227
|
line_nodes = self.get_src_path_line_nodes_clover(
|
|
210
|
-
xml_document, src_path
|
|
228
|
+
i, xml_document, src_path
|
|
211
229
|
)
|
|
212
230
|
_number = "num"
|
|
213
231
|
_hits = "count"
|
|
214
|
-
elif
|
|
215
|
-
# https://github.com/jacoco/jacoco/blob/master/org.jacoco.report/src/org/jacoco/report/xml/report.dtd
|
|
232
|
+
elif report_format == "jacoco":
|
|
216
233
|
line_nodes = self.get_src_path_line_nodes_jacoco(
|
|
217
234
|
xml_document, src_path
|
|
218
235
|
)
|
|
219
236
|
_number = "nr"
|
|
220
237
|
_hits = "ci"
|
|
221
238
|
else:
|
|
222
|
-
# https://github.com/cobertura/web/blob/master/htdocs/xml/coverage-04.dtd
|
|
223
239
|
line_nodes = self.get_src_path_line_nodes_cobertura(
|
|
224
240
|
i, xml_document, src_path
|
|
225
241
|
)
|
|
@@ -13,7 +13,6 @@ def test_get_src_path_clover(datadir):
|
|
|
13
13
|
GitPathTool._cwd = "/"
|
|
14
14
|
GitPathTool._root = "/"
|
|
15
15
|
clover_report = etree.parse(str(datadir / "test.xml"))
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
)
|
|
16
|
+
reporter = XmlCoverageReporter([clover_report])
|
|
17
|
+
result = reporter.get_src_path_line_nodes_clover(0, clover_report, "isLucky.js")
|
|
19
18
|
assert sorted([int(line.attrib["num"]) for line in result]) == [2, 3, 5, 6, 8, 12]
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# pylint: disable=missing-function-docstring
|
|
2
|
+
|
|
3
|
+
"""Tests for the message shown when a required executable is not on PATH.
|
|
4
|
+
|
|
5
|
+
See https://github.com/Bachmann1234/diff_cover/issues/303
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import pytest
|
|
9
|
+
|
|
10
|
+
from diff_cover.command_runner import (
|
|
11
|
+
GIT_INSTALL_URL,
|
|
12
|
+
CommandError,
|
|
13
|
+
ExecutableNotFoundError,
|
|
14
|
+
execute,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@pytest.fixture
|
|
19
|
+
def missing_git(mocker):
|
|
20
|
+
"""Make every subprocess launch fail the way a missing `git` does."""
|
|
21
|
+
return mocker.patch(
|
|
22
|
+
"diff_cover.command_runner.subprocess.Popen",
|
|
23
|
+
side_effect=FileNotFoundError(2, "No such file or directory", "git"),
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@pytest.fixture
|
|
28
|
+
def failing_git(mocker):
|
|
29
|
+
"""Make `git` run and fail, which is a different thing entirely."""
|
|
30
|
+
process = mocker.Mock()
|
|
31
|
+
process.returncode = 1
|
|
32
|
+
process.communicate.return_value = (b"", b"fatal: not a git repository")
|
|
33
|
+
popen = mocker.patch("diff_cover.command_runner.subprocess.Popen")
|
|
34
|
+
popen.return_value.__enter__.return_value = process
|
|
35
|
+
popen.return_value.__exit__.return_value = None
|
|
36
|
+
return popen
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def test_execute_turns_a_missing_executable_into_a_helpful_error(missing_git):
|
|
40
|
+
with pytest.raises(ExecutableNotFoundError) as exc_info:
|
|
41
|
+
execute(["git", "rev-parse", "--show-toplevel"])
|
|
42
|
+
|
|
43
|
+
message = str(exc_info.value)
|
|
44
|
+
assert "git" in message
|
|
45
|
+
assert "PATH" in message
|
|
46
|
+
assert message.endswith(f"See {GIT_INSTALL_URL} for installation instructions.")
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def test_the_new_error_stays_a_command_error(missing_git):
|
|
50
|
+
"""Callers that already catch CommandError must keep working."""
|
|
51
|
+
assert issubclass(ExecutableNotFoundError, CommandError)
|
|
52
|
+
with pytest.raises(CommandError):
|
|
53
|
+
execute(["git", "rev-parse", "--show-toplevel"])
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def test_execute_does_not_link_git_docs_for_other_executables(mocker):
|
|
57
|
+
mocker.patch(
|
|
58
|
+
"diff_cover.command_runner.subprocess.Popen",
|
|
59
|
+
side_effect=FileNotFoundError(2, "No such file or directory", "pycodestyle"),
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
with pytest.raises(ExecutableNotFoundError) as exc_info:
|
|
63
|
+
execute(["pycodestyle", "--version"])
|
|
64
|
+
|
|
65
|
+
message = str(exc_info.value)
|
|
66
|
+
assert "pycodestyle" in message
|
|
67
|
+
assert GIT_INSTALL_URL not in message
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def test_diff_cover_main_reports_missing_git_without_a_traceback(missing_git, caplog):
|
|
71
|
+
from diff_cover.diff_cover_tool import main
|
|
72
|
+
|
|
73
|
+
assert main(["diff-cover", "coverage.xml"]) == 1
|
|
74
|
+
assert "PATH" in caplog.text
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def test_diff_quality_main_reports_missing_git_without_a_traceback(missing_git, caplog):
|
|
78
|
+
from diff_cover.diff_quality_tool import main
|
|
79
|
+
|
|
80
|
+
assert main(["diff-quality", "--violations", "pycodestyle"]) == 1
|
|
81
|
+
assert "PATH" in caplog.text
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def test_a_command_that_runs_and_fails_is_left_alone(failing_git):
|
|
85
|
+
"""Control. Green before and after: only a *missing* binary is reworded."""
|
|
86
|
+
with pytest.raises(CommandError) as exc_info:
|
|
87
|
+
execute(["git", "status"])
|
|
88
|
+
|
|
89
|
+
assert not isinstance(exc_info.value, ExecutableNotFoundError)
|
|
90
|
+
message = str(exc_info.value)
|
|
91
|
+
assert "fatal: not a git repository" in message
|
|
92
|
+
assert "PATH" not in message
|
|
@@ -464,12 +464,15 @@ class TestCloverXmlCoverageReporterTest:
|
|
|
464
464
|
|
|
465
465
|
@pytest.fixture(autouse=True)
|
|
466
466
|
def patch_git_patch(self, mocker):
|
|
467
|
-
# Paths generated by git_path are always the given argument
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
467
|
+
# Paths generated by git_path are always the given argument. The Clover
|
|
468
|
+
# lookup resolves them in its own module, so patch both.
|
|
469
|
+
for module in (
|
|
470
|
+
"diff_cover.violationsreporters.violations_reporter",
|
|
471
|
+
"diff_cover.violationsreporters.clover",
|
|
472
|
+
):
|
|
473
|
+
_git_path_mock = mocker.patch(f"{module}.GitPathTool")
|
|
474
|
+
_git_path_mock.relative_path = lambda path: path
|
|
475
|
+
_git_path_mock.absolute_path = lambda path: path
|
|
473
476
|
|
|
474
477
|
def test_violations(self):
|
|
475
478
|
# Construct the XML report
|
|
@@ -497,6 +500,109 @@ class TestCloverXmlCoverageReporterTest:
|
|
|
497
500
|
result = coverage.violations("file1.java")
|
|
498
501
|
assert result == violations
|
|
499
502
|
|
|
503
|
+
def test_phpunit_clover_without_clover_attribute(self):
|
|
504
|
+
xml = self._coverage_xml(
|
|
505
|
+
["/workspace/project/subdir/file.java"],
|
|
506
|
+
self.FEW_VIOLATIONS,
|
|
507
|
+
self.FEW_MEASURED,
|
|
508
|
+
)
|
|
509
|
+
del xml.attrib["clover"]
|
|
510
|
+
file_node = xml.find(".//file")
|
|
511
|
+
file_node.set("name", file_node.attrib.pop("path"))
|
|
512
|
+
|
|
513
|
+
coverage = XmlCoverageReporter([xml])
|
|
514
|
+
|
|
515
|
+
assert coverage.violations("subdir/file.java") == self.FEW_VIOLATIONS
|
|
516
|
+
assert coverage.measured_lines("subdir/file.java") == self.FEW_MEASURED
|
|
517
|
+
|
|
518
|
+
def test_method_lines_are_measured(self):
|
|
519
|
+
# PHPUnit's Clover writer emits type="method" for the declaration line of
|
|
520
|
+
# every measured function. Those lines carry a count like any other and
|
|
521
|
+
# must not be reported as unmeasured.
|
|
522
|
+
xml = self._coverage_xml(
|
|
523
|
+
["file1.java"], self.FEW_VIOLATIONS, self.FEW_MEASURED, method_lines={42}
|
|
524
|
+
)
|
|
525
|
+
|
|
526
|
+
coverage = XmlCoverageReporter([xml])
|
|
527
|
+
|
|
528
|
+
assert 42 in coverage.measured_lines("file1.java")
|
|
529
|
+
assert coverage.violations("file1.java") == self.FEW_VIOLATIONS
|
|
530
|
+
|
|
531
|
+
def test_method_line_can_be_a_violation(self):
|
|
532
|
+
xml = self._coverage_xml(
|
|
533
|
+
["file1.java"], self.FEW_VIOLATIONS, self.FEW_MEASURED, method_lines={42}
|
|
534
|
+
)
|
|
535
|
+
method_line = xml.find('.//line[@type="method"]')
|
|
536
|
+
method_line.set("count", "0")
|
|
537
|
+
|
|
538
|
+
coverage = XmlCoverageReporter([xml])
|
|
539
|
+
|
|
540
|
+
assert 42 in coverage.measured_lines("file1.java")
|
|
541
|
+
assert Violation(42, None) in coverage.violations("file1.java")
|
|
542
|
+
|
|
543
|
+
def test_report_format_is_detected_once(self):
|
|
544
|
+
# Classifying the report is a search through the whole document; doing it
|
|
545
|
+
# per source file was the review comment on #617.
|
|
546
|
+
xml = self._coverage_xml(
|
|
547
|
+
["file1.java", "file2.java"], self.FEW_VIOLATIONS, self.FEW_MEASURED
|
|
548
|
+
)
|
|
549
|
+
|
|
550
|
+
coverage = XmlCoverageReporter([xml])
|
|
551
|
+
|
|
552
|
+
assert coverage._report_formats == ["clover"]
|
|
553
|
+
coverage.violations("file1.java")
|
|
554
|
+
coverage.violations("file2.java")
|
|
555
|
+
assert coverage._report_formats == ["clover"]
|
|
556
|
+
|
|
557
|
+
def test_file_lookup_is_built_once(self, mocker):
|
|
558
|
+
# Locating a source file used to walk every `.//file` again, so a report
|
|
559
|
+
# with N files cost N walks for N source paths. Each `<file>` is now
|
|
560
|
+
# classified once, which shows up as one `relative_path` call per file
|
|
561
|
+
# however many source paths are looked up.
|
|
562
|
+
file_paths = ["file1.java", "subdir/file2.java", "subdir/file3.java"]
|
|
563
|
+
xml = self._coverage_xml(file_paths, self.FEW_VIOLATIONS, self.FEW_MEASURED)
|
|
564
|
+
|
|
565
|
+
relative_path = mocker.patch(
|
|
566
|
+
"diff_cover.violationsreporters.clover.GitPathTool.relative_path",
|
|
567
|
+
side_effect=lambda path: path,
|
|
568
|
+
)
|
|
569
|
+
coverage = XmlCoverageReporter([xml])
|
|
570
|
+
for src_path in file_paths:
|
|
571
|
+
assert coverage.violations(src_path) == self.FEW_VIOLATIONS
|
|
572
|
+
|
|
573
|
+
assert relative_path.call_count == len(file_paths), (
|
|
574
|
+
"expected each <file> to be classified once, got "
|
|
575
|
+
f"{relative_path.call_count} calls for {len(file_paths)} files"
|
|
576
|
+
)
|
|
577
|
+
|
|
578
|
+
def test_absolute_paths_still_match_by_suffix(self):
|
|
579
|
+
# Reports that carry absolute paths are matched by suffix, not equality.
|
|
580
|
+
# The lookup groups those candidates by their last path segment, so a
|
|
581
|
+
# file whose segment collides with another must still resolve correctly.
|
|
582
|
+
xml = self._coverage_xml(
|
|
583
|
+
["/build/one/shared.java", "/build/two/shared.java"],
|
|
584
|
+
self.FEW_VIOLATIONS,
|
|
585
|
+
self.FEW_MEASURED,
|
|
586
|
+
)
|
|
587
|
+
|
|
588
|
+
coverage = XmlCoverageReporter([xml])
|
|
589
|
+
|
|
590
|
+
assert coverage.violations("two/shared.java") == self.FEW_VIOLATIONS
|
|
591
|
+
assert coverage.measured_lines("two/shared.java") == self.FEW_MEASURED
|
|
592
|
+
assert coverage.violations("nowhere/shared.java") == set()
|
|
593
|
+
|
|
594
|
+
def test_file_without_a_path_is_skipped(self):
|
|
595
|
+
# A `<file>` carries its path in `path`, or in `name` from PHPUnit's
|
|
596
|
+
# writer. One with neither cannot be matched against a source path, and
|
|
597
|
+
# it must not keep the rest of the report from being indexed.
|
|
598
|
+
xml = self._coverage_xml(["file1.java"], self.FEW_VIOLATIONS, self.FEW_MEASURED)
|
|
599
|
+
xml.find(".//package").insert(0, etree.Element("file"))
|
|
600
|
+
|
|
601
|
+
coverage = XmlCoverageReporter([xml])
|
|
602
|
+
|
|
603
|
+
assert coverage.violations("file1.java") == self.FEW_VIOLATIONS
|
|
604
|
+
assert coverage.measured_lines("file1.java") == self.FEW_MEASURED
|
|
605
|
+
|
|
500
606
|
def test_two_inputs_first_violate(self):
|
|
501
607
|
# Construct the XML report
|
|
502
608
|
file_paths = ["file1.java"]
|
|
@@ -620,7 +726,7 @@ class TestCloverXmlCoverageReporterTest:
|
|
|
620
726
|
result = coverage.violations("file.java")
|
|
621
727
|
assert result == set()
|
|
622
728
|
|
|
623
|
-
def _coverage_xml(self, file_paths, violations, measured):
|
|
729
|
+
def _coverage_xml(self, file_paths, violations, measured, method_lines=None):
|
|
624
730
|
"""
|
|
625
731
|
Build an XML tree with source files specified by `file_paths`.
|
|
626
732
|
Each source fill will have the same set of covered and
|
|
@@ -630,6 +736,9 @@ class TestCloverXmlCoverageReporterTest:
|
|
|
630
736
|
`line_dict` is a dictionary with keys that are line numbers
|
|
631
737
|
and values that are True/False indicating whether the line
|
|
632
738
|
is covered
|
|
739
|
+
`method_lines` is an optional set of line numbers to emit as
|
|
740
|
+
`type="method"` instead of `type="stmt"`, the way PHPUnit's Clover
|
|
741
|
+
writer marks the declaration line of a measured function
|
|
633
742
|
|
|
634
743
|
This leaves out some attributes of the Cobertura format,
|
|
635
744
|
but includes all the elements.
|
|
@@ -654,6 +763,12 @@ class TestCloverXmlCoverageReporterTest:
|
|
|
654
763
|
line.set("count", str(hits))
|
|
655
764
|
line.set("num", str(line_num))
|
|
656
765
|
line.set("type", "stmt")
|
|
766
|
+
|
|
767
|
+
for line_num in method_lines or ():
|
|
768
|
+
line = etree.SubElement(src_node, "line")
|
|
769
|
+
line.set("count", "1")
|
|
770
|
+
line.set("num", str(line_num))
|
|
771
|
+
line.set("type", "method")
|
|
657
772
|
return root
|
|
658
773
|
|
|
659
774
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{diff_cover-10.4.2 → diff_cover-10.5.1}/diff_cover/templates/github_coverage_annotations.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{diff_cover-10.4.2 → diff_cover-10.5.1}/diff_cover/violationsreporters/java_violations_reporter.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/pycodestyle_violations_report_float.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{diff_cover-10.4.2 → diff_cover-10.5.1}/tests/fixtures/typescript_branches_console_report.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|