diff-cover 10.2.1__tar.gz → 10.4.0__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.2.1 → diff_cover-10.4.0}/PKG-INFO +78 -1
- {diff_cover-10.2.1 → diff_cover-10.4.0}/README.rst +77 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/diff_cover/diff_cover_tool.py +36 -2
- {diff_cover-10.2.1 → diff_cover-10.4.0}/diff_cover/report_generator.py +10 -1
- {diff_cover-10.2.1 → diff_cover-10.4.0}/diff_cover/snippets.py +110 -14
- {diff_cover-10.2.1 → diff_cover-10.4.0}/diff_cover/violationsreporters/violations_reporter.py +46 -16
- {diff_cover-10.2.1 → diff_cover-10.4.0}/pyproject.toml +1 -1
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/test_diff_cover_tool.py +20 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/test_report_generator.py +33 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/test_snippets.py +128 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/test_violations_reporter.py +69 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/LICENSE +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/NOTICE +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/diff_cover/__init__.py +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/diff_cover/command_runner.py +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/diff_cover/config_parser.py +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/diff_cover/diff_quality_tool.py +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/diff_cover/diff_reporter.py +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/diff_cover/git_diff.py +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/diff_cover/git_path.py +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/diff_cover/hook.py +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/diff_cover/hookspecs.py +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/diff_cover/templates/console_coverage_report.txt +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/diff_cover/templates/console_quality_report.txt +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/diff_cover/templates/external_style.css +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/diff_cover/templates/github_coverage_annotations.txt +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/diff_cover/templates/html_coverage_report.html +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/diff_cover/templates/html_quality_report.html +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/diff_cover/templates/markdown_coverage_report.md +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/diff_cover/templates/markdown_quality_report.md +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/diff_cover/templates/snippet_content.html +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/diff_cover/templates/snippet_content.md +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/diff_cover/templates/snippet_content.txt +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/diff_cover/templates/snippet_style.html +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/diff_cover/util.py +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/diff_cover/violationsreporters/__init__.py +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/diff_cover/violationsreporters/base.py +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/diff_cover/violationsreporters/java_violations_reporter.py +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/__init__.py +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/conftest.py +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/add_console_report.txt +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/add_html_report.html +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/add_json_report.json +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/add_json_report_float.json +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/add_markdown_report.md +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/changed_console_report.txt +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/changed_html_report.html +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/coverage.xml +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/coverage1.xml +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/coverage2.xml +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/coverage_missing_lines.xml +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/cpp_functions_console_report.txt +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/cpp_functions_coverage.lcov +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/delete_console_report.txt +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/delete_html_report.html +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/dotnet_coverage.xml +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/empty.txt +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/empty_pycodestyle_violations.txt +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/expand_console_report.txt +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/external_css_html_report.html +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/external_style.css +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/git_diff_add.txt +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/git_diff_changed.txt +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/git_diff_code_dupe.txt +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/git_diff_cpp.txt +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/git_diff_cpp_functions.txt +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/git_diff_delete.txt +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/git_diff_dotnet.txt +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/git_diff_external_css.txt +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/git_diff_lua.txt +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/git_diff_moved.txt +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/git_diff_mult.txt +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/git_diff_python.txt +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/git_diff_subdir.txt +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/git_diff_typescript.txt +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/git_diff_typescript_branches.txt +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/git_diff_unicode.txt +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/git_diff_violations.txt +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/git_diff_violations_two_files.txt +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/hello.py +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/hi.py +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/html_report.html +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/html_report_empty.html +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/html_report_one_snippet.html +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/html_report_two_snippets.html +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/lcov.info +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/lua_console_report.txt +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/luacoverage.xml +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/markdown_report_one_snippet.md +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/markdown_report_two_snippets.md +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/moved_console_report.txt +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/moved_coverage.xml +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/moved_html_report.html +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/mult_inputs_console_report.txt +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/mult_inputs_html_report.html +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/pycodestyle_report.txt +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/pycodestyle_violations_report.html +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/pycodestyle_violations_report.json +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/pycodestyle_violations_report.md +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/pycodestyle_violations_report.txt +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/pycodestyle_violations_report_external_css.html +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/pycodestyle_violations_report_float.json +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/pyflakes_two_files.txt +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/pyflakes_violations_report.html +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/pyflakes_violations_report.txt +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/pylint_dupe.txt +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/pylint_dupe_violations_report.txt +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/pylint_report.txt +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/pylint_violations_console_report.txt +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/pylint_violations_report.html +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/pylint_violations_report.txt +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/pylintrc +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/real_cpp_console_report.txt +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/real_cpp_coverage.lcov +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/real_python_console_report.txt +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/real_python_coverage.lcov +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/real_typescript_console_report.txt +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/real_typescript_coverage.lcov +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/show_uncovered_lines_console.txt +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/snippet.css +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/snippet_8859.py +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/snippet_arabic_output.html +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/snippet_default.html +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/snippet_invalid_violations.html +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/snippet_list.html +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/snippet_list.md +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/snippet_list2.md +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/snippet_list3.md +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/snippet_no_filename_ext.html +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/snippet_src.py +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/snippet_src2.cpp +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/snippet_src3.cpp +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/snippet_unicode.html +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/snippet_unicode.py +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/snippet_unicode_html_output.html +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/subdir_coverage_console_report.txt +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/subdir_coverage_html_report.html +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/test_src.txt +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/typescript_branches_console_report.txt +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/typescript_branches_coverage.lcov +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/unicode_console_report.txt +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/unicode_coverage.xml +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/unicode_html_report.html +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/unicode_test_src.txt +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/fixtures/violations_test_file.py +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/helpers.py +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/snippet_list_unicode.html +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/test_clover_violations_reporter/test.xml +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/test_clover_violations_reporter.py +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/test_config_parser.py +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/test_diff_cover_main.py +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/test_diff_quality_main.py +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/test_diff_reporter.py +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/test_git_diff.py +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/test_git_diff_file.py +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/test_git_path.py +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/test_integration.py +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/test_java_violations_reporter.py +0 -0
- {diff_cover-10.2.1 → diff_cover-10.4.0}/tests/test_util.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: diff_cover
|
|
3
|
-
Version: 10.
|
|
3
|
+
Version: 10.4.0
|
|
4
4
|
Summary: Run coverage and linting reports on diffs
|
|
5
5
|
License: Apache-2.0
|
|
6
6
|
Author: See Contributors
|
|
@@ -320,6 +320,83 @@ It can be enabled by using the ``-q``/``--quiet`` flag:
|
|
|
320
320
|
|
|
321
321
|
If enabled, the tool will only print errors and failures but no information or warning messages.
|
|
322
322
|
|
|
323
|
+
Showing Covered and Uncovered Lines
|
|
324
|
+
-----------------------------------
|
|
325
|
+
By default the console report only lists the percentage of covered lines.
|
|
326
|
+
|
|
327
|
+
``diff-cover`` can also show the individual lines that lack coverage on the
|
|
328
|
+
console with ``--show-uncovered``:
|
|
329
|
+
|
|
330
|
+
.. code:: bash
|
|
331
|
+
|
|
332
|
+
diff-cover coverage.xml --show-uncovered
|
|
333
|
+
|
|
334
|
+
When generating an HTML report, ``--show-covered`` additionally highlights the
|
|
335
|
+
covered diff lines (in green), alongside the existing missing-line (red)
|
|
336
|
+
highlighting:
|
|
337
|
+
|
|
338
|
+
.. code:: bash
|
|
339
|
+
|
|
340
|
+
diff-cover coverage.xml --html-report report.html --show-covered
|
|
341
|
+
|
|
342
|
+
Source Roots
|
|
343
|
+
------------
|
|
344
|
+
For ``jacoco`` coverage reports, ``diff-cover`` needs to know the source
|
|
345
|
+
directories in order to map coverage data back to source files. Provide one or
|
|
346
|
+
more directories with ``--src-roots`` (defaults to ``src/main/java`` and
|
|
347
|
+
``src/test/java``):
|
|
348
|
+
|
|
349
|
+
.. code:: bash
|
|
350
|
+
|
|
351
|
+
diff-cover coverage.xml --src-roots src/main/java src/test/java
|
|
352
|
+
|
|
353
|
+
Report Root Path
|
|
354
|
+
----------------
|
|
355
|
+
``diff-quality`` resolves violation report paths relative to the current working
|
|
356
|
+
directory. If your quality tool was run from a different directory, use
|
|
357
|
+
``--report-root-path`` to specify the root path that should be used when
|
|
358
|
+
generating the report:
|
|
359
|
+
|
|
360
|
+
.. code:: bash
|
|
361
|
+
|
|
362
|
+
diff-quality --violations=pycodestyle --report-root-path=path/to/root
|
|
363
|
+
|
|
364
|
+
External CSS File
|
|
365
|
+
-----------------
|
|
366
|
+
By default an HTML report embeds its styles inline. To write the CSS into a
|
|
367
|
+
separate file instead, use ``--external-css-file``:
|
|
368
|
+
|
|
369
|
+
.. code:: bash
|
|
370
|
+
|
|
371
|
+
diff-cover coverage.xml --html-report report.html --external-css-file report.css
|
|
372
|
+
diff-quality --violations=pycodestyle --html-report report.html --external-css-file report.css
|
|
373
|
+
|
|
374
|
+
Diff Range Notation
|
|
375
|
+
-------------------
|
|
376
|
+
When comparing branches, ``diff-cover`` and ``diff-quality`` use the ``...``
|
|
377
|
+
git diff range notation by default (changes on the current branch since it
|
|
378
|
+
diverged from the compare branch). To use the ``..`` notation instead (all
|
|
379
|
+
differences between the two branches), pass ``--diff-range-notation``:
|
|
380
|
+
|
|
381
|
+
.. code:: bash
|
|
382
|
+
|
|
383
|
+
diff-cover coverage.xml --diff-range-notation=..
|
|
384
|
+
diff-quality --violations=pycodestyle --diff-range-notation=..
|
|
385
|
+
|
|
386
|
+
See the `git diff documentation`_ for details on the two notations.
|
|
387
|
+
|
|
388
|
+
.. _git diff documentation: https://git-scm.com/docs/git-diff
|
|
389
|
+
|
|
390
|
+
Ignore Whitespace
|
|
391
|
+
-----------------
|
|
392
|
+
To ignore any and all whitespace changes when computing the diff, use
|
|
393
|
+
``--ignore-whitespace``:
|
|
394
|
+
|
|
395
|
+
.. code:: bash
|
|
396
|
+
|
|
397
|
+
diff-cover coverage.xml --ignore-whitespace
|
|
398
|
+
diff-quality --violations=pycodestyle --ignore-whitespace
|
|
399
|
+
|
|
323
400
|
Compatibility with multi-line statements
|
|
324
401
|
----------------------------------------
|
|
325
402
|
``diff-cover`` relies on the comparison of diff reports and coverage reports, and does not report
|
|
@@ -288,6 +288,83 @@ It can be enabled by using the ``-q``/``--quiet`` flag:
|
|
|
288
288
|
|
|
289
289
|
If enabled, the tool will only print errors and failures but no information or warning messages.
|
|
290
290
|
|
|
291
|
+
Showing Covered and Uncovered Lines
|
|
292
|
+
-----------------------------------
|
|
293
|
+
By default the console report only lists the percentage of covered lines.
|
|
294
|
+
|
|
295
|
+
``diff-cover`` can also show the individual lines that lack coverage on the
|
|
296
|
+
console with ``--show-uncovered``:
|
|
297
|
+
|
|
298
|
+
.. code:: bash
|
|
299
|
+
|
|
300
|
+
diff-cover coverage.xml --show-uncovered
|
|
301
|
+
|
|
302
|
+
When generating an HTML report, ``--show-covered`` additionally highlights the
|
|
303
|
+
covered diff lines (in green), alongside the existing missing-line (red)
|
|
304
|
+
highlighting:
|
|
305
|
+
|
|
306
|
+
.. code:: bash
|
|
307
|
+
|
|
308
|
+
diff-cover coverage.xml --html-report report.html --show-covered
|
|
309
|
+
|
|
310
|
+
Source Roots
|
|
311
|
+
------------
|
|
312
|
+
For ``jacoco`` coverage reports, ``diff-cover`` needs to know the source
|
|
313
|
+
directories in order to map coverage data back to source files. Provide one or
|
|
314
|
+
more directories with ``--src-roots`` (defaults to ``src/main/java`` and
|
|
315
|
+
``src/test/java``):
|
|
316
|
+
|
|
317
|
+
.. code:: bash
|
|
318
|
+
|
|
319
|
+
diff-cover coverage.xml --src-roots src/main/java src/test/java
|
|
320
|
+
|
|
321
|
+
Report Root Path
|
|
322
|
+
----------------
|
|
323
|
+
``diff-quality`` resolves violation report paths relative to the current working
|
|
324
|
+
directory. If your quality tool was run from a different directory, use
|
|
325
|
+
``--report-root-path`` to specify the root path that should be used when
|
|
326
|
+
generating the report:
|
|
327
|
+
|
|
328
|
+
.. code:: bash
|
|
329
|
+
|
|
330
|
+
diff-quality --violations=pycodestyle --report-root-path=path/to/root
|
|
331
|
+
|
|
332
|
+
External CSS File
|
|
333
|
+
-----------------
|
|
334
|
+
By default an HTML report embeds its styles inline. To write the CSS into a
|
|
335
|
+
separate file instead, use ``--external-css-file``:
|
|
336
|
+
|
|
337
|
+
.. code:: bash
|
|
338
|
+
|
|
339
|
+
diff-cover coverage.xml --html-report report.html --external-css-file report.css
|
|
340
|
+
diff-quality --violations=pycodestyle --html-report report.html --external-css-file report.css
|
|
341
|
+
|
|
342
|
+
Diff Range Notation
|
|
343
|
+
-------------------
|
|
344
|
+
When comparing branches, ``diff-cover`` and ``diff-quality`` use the ``...``
|
|
345
|
+
git diff range notation by default (changes on the current branch since it
|
|
346
|
+
diverged from the compare branch). To use the ``..`` notation instead (all
|
|
347
|
+
differences between the two branches), pass ``--diff-range-notation``:
|
|
348
|
+
|
|
349
|
+
.. code:: bash
|
|
350
|
+
|
|
351
|
+
diff-cover coverage.xml --diff-range-notation=..
|
|
352
|
+
diff-quality --violations=pycodestyle --diff-range-notation=..
|
|
353
|
+
|
|
354
|
+
See the `git diff documentation`_ for details on the two notations.
|
|
355
|
+
|
|
356
|
+
.. _git diff documentation: https://git-scm.com/docs/git-diff
|
|
357
|
+
|
|
358
|
+
Ignore Whitespace
|
|
359
|
+
-----------------
|
|
360
|
+
To ignore any and all whitespace changes when computing the diff, use
|
|
361
|
+
``--ignore-whitespace``:
|
|
362
|
+
|
|
363
|
+
.. code:: bash
|
|
364
|
+
|
|
365
|
+
diff-cover coverage.xml --ignore-whitespace
|
|
366
|
+
diff-quality --violations=pycodestyle --ignore-whitespace
|
|
367
|
+
|
|
291
368
|
Compatibility with multi-line statements
|
|
292
369
|
----------------------------------------
|
|
293
370
|
``diff-cover`` relies on the comparison of diff reports and coverage reports, and does not report
|
|
@@ -45,9 +45,17 @@ DIFF_RANGE_NOTATION_HELP = (
|
|
|
45
45
|
)
|
|
46
46
|
QUIET_HELP = "Only print errors and failures"
|
|
47
47
|
SHOW_UNCOVERED = "Show uncovered lines on the console"
|
|
48
|
+
SHOW_COVERED_HELP = (
|
|
49
|
+
"Also highlight covered diff lines (in green) in the HTML coverage report, "
|
|
50
|
+
"in addition to the existing missing-line (red) highlighting."
|
|
51
|
+
)
|
|
48
52
|
EXPAND_COVERAGE_REPORT = (
|
|
49
53
|
"Append missing lines in coverage reports based on the hits of the previous line."
|
|
50
54
|
)
|
|
55
|
+
BRANCH_COVERAGE_HELP = (
|
|
56
|
+
"Treat partially covered branches as uncovered. Requires a coverage report "
|
|
57
|
+
'with branch coverage data (Cobertura branch="true" condition-coverage).'
|
|
58
|
+
)
|
|
51
59
|
INCLUDE_UNTRACKED_HELP = "Include untracked files"
|
|
52
60
|
CONFIG_FILE_HELP = "The configuration file to use"
|
|
53
61
|
DIFF_FILE_HELP = "The diff file to use"
|
|
@@ -99,6 +107,13 @@ def parse_coverage_args(argv):
|
|
|
99
107
|
"--show-uncovered", action="store_true", default=None, help=SHOW_UNCOVERED
|
|
100
108
|
)
|
|
101
109
|
|
|
110
|
+
parser.add_argument(
|
|
111
|
+
"--show-covered",
|
|
112
|
+
action="store_true",
|
|
113
|
+
default=None,
|
|
114
|
+
help=SHOW_COVERED_HELP,
|
|
115
|
+
)
|
|
116
|
+
|
|
102
117
|
parser.add_argument(
|
|
103
118
|
"--expand-coverage-report",
|
|
104
119
|
action="store_true",
|
|
@@ -106,6 +121,13 @@ def parse_coverage_args(argv):
|
|
|
106
121
|
help=EXPAND_COVERAGE_REPORT,
|
|
107
122
|
)
|
|
108
123
|
|
|
124
|
+
parser.add_argument(
|
|
125
|
+
"--branch-coverage",
|
|
126
|
+
action="store_true",
|
|
127
|
+
default=None,
|
|
128
|
+
help=BRANCH_COVERAGE_HELP,
|
|
129
|
+
)
|
|
130
|
+
|
|
109
131
|
parser.add_argument(
|
|
110
132
|
"--external-css-file",
|
|
111
133
|
metavar="FILENAME",
|
|
@@ -193,6 +215,7 @@ def parse_coverage_args(argv):
|
|
|
193
215
|
|
|
194
216
|
defaults = {
|
|
195
217
|
"show_uncovered": False,
|
|
218
|
+
"show_covered": False,
|
|
196
219
|
"compare_branch": "origin/main",
|
|
197
220
|
"fail_under": 0,
|
|
198
221
|
"ignore_staged": False,
|
|
@@ -203,6 +226,7 @@ def parse_coverage_args(argv):
|
|
|
203
226
|
"diff_range_notation": "...",
|
|
204
227
|
"quiet": False,
|
|
205
228
|
"expand_coverage_report": False,
|
|
229
|
+
"branch_coverage": False,
|
|
206
230
|
"total_percent_float": False,
|
|
207
231
|
}
|
|
208
232
|
|
|
@@ -223,7 +247,9 @@ def generate_coverage_report(
|
|
|
223
247
|
src_roots=None,
|
|
224
248
|
quiet=False,
|
|
225
249
|
show_uncovered=False,
|
|
250
|
+
show_covered=False,
|
|
226
251
|
expand_coverage_report=False,
|
|
252
|
+
branch_coverage=False,
|
|
227
253
|
total_percent_float=False,
|
|
228
254
|
):
|
|
229
255
|
"""
|
|
@@ -252,7 +278,9 @@ def generate_coverage_report(
|
|
|
252
278
|
if xml_roots and lcov_roots:
|
|
253
279
|
raise ValueError("Mixing LCov and XML reports is not supported yet")
|
|
254
280
|
if xml_roots:
|
|
255
|
-
coverage = XmlCoverageReporter(
|
|
281
|
+
coverage = XmlCoverageReporter(
|
|
282
|
+
xml_roots, src_roots, expand_coverage_report, branch_coverage
|
|
283
|
+
)
|
|
256
284
|
else:
|
|
257
285
|
coverage = LcovCoverageReporter(lcov_roots, src_roots)
|
|
258
286
|
|
|
@@ -263,7 +291,11 @@ def generate_coverage_report(
|
|
|
263
291
|
if css_url is not None:
|
|
264
292
|
css_url = os.path.relpath(css_file, os.path.dirname(html_report))
|
|
265
293
|
reporter = HtmlReportGenerator(
|
|
266
|
-
coverage,
|
|
294
|
+
coverage,
|
|
295
|
+
diff,
|
|
296
|
+
css_url=css_url,
|
|
297
|
+
total_percent_float=total_percent_float,
|
|
298
|
+
show_covered=show_covered,
|
|
267
299
|
)
|
|
268
300
|
with open_file(html_report, "wb") as output_file:
|
|
269
301
|
reporter.generate_report(output_file)
|
|
@@ -398,7 +430,9 @@ def main(argv=None, directory=None):
|
|
|
398
430
|
src_roots=arg_dict["src_roots"],
|
|
399
431
|
quiet=quiet,
|
|
400
432
|
show_uncovered=arg_dict["show_uncovered"],
|
|
433
|
+
show_covered=arg_dict["show_covered"],
|
|
401
434
|
expand_coverage_report=arg_dict["expand_coverage_report"],
|
|
435
|
+
branch_coverage=arg_dict["branch_coverage"],
|
|
402
436
|
total_percent_float=arg_dict["total_percent_float"],
|
|
403
437
|
)
|
|
404
438
|
|
|
@@ -295,6 +295,7 @@ class TemplateReportGenerator(BaseReportGenerator):
|
|
|
295
295
|
diff_reporter,
|
|
296
296
|
css_url=None,
|
|
297
297
|
total_percent_float=False,
|
|
298
|
+
show_covered=False,
|
|
298
299
|
):
|
|
299
300
|
super().__init__(
|
|
300
301
|
violations_reporter,
|
|
@@ -302,6 +303,11 @@ class TemplateReportGenerator(BaseReportGenerator):
|
|
|
302
303
|
total_percent_float=total_percent_float,
|
|
303
304
|
)
|
|
304
305
|
self.css_url = css_url
|
|
306
|
+
# When True, the rendered HTML snippet for each source file will
|
|
307
|
+
# additionally highlight covered diff lines (in green) on top of
|
|
308
|
+
# the existing violation (red) highlighting. Defaults to False to
|
|
309
|
+
# preserve historical behaviour.
|
|
310
|
+
self.show_covered = show_covered
|
|
305
311
|
|
|
306
312
|
def generate_report(self, output_file):
|
|
307
313
|
"""
|
|
@@ -406,9 +412,12 @@ class TemplateReportGenerator(BaseReportGenerator):
|
|
|
406
412
|
# If we cannot load the file, then fail gracefully
|
|
407
413
|
formatted_snippets = {"html": [], "markdown": [], "terminal": []}
|
|
408
414
|
if self.include_snippets:
|
|
415
|
+
covered_lines = stats["covered_lines"] if self.show_covered else None
|
|
409
416
|
with contextlib.suppress(OSError):
|
|
410
417
|
formatted_snippets = Snippet.load_formatted_snippets(
|
|
411
|
-
src_path,
|
|
418
|
+
src_path,
|
|
419
|
+
stats["violation_lines"],
|
|
420
|
+
covered_lines=covered_lines,
|
|
412
421
|
)
|
|
413
422
|
|
|
414
423
|
stats.update(
|
|
@@ -4,6 +4,7 @@ in HTML reports.
|
|
|
4
4
|
"""
|
|
5
5
|
|
|
6
6
|
import contextlib
|
|
7
|
+
import re
|
|
7
8
|
from tokenize import open as openpy
|
|
8
9
|
|
|
9
10
|
import chardet
|
|
@@ -23,7 +24,10 @@ class Snippet:
|
|
|
23
24
|
"""
|
|
24
25
|
|
|
25
26
|
VIOLATION_COLOR = "#ffcccc"
|
|
27
|
+
COVERED_COLOR = "#ddffdd"
|
|
26
28
|
DIV_CSS_CLASS = "snippet"
|
|
29
|
+
COVERED_LINE_CSS_CLASS = "diff-cover-covered-line"
|
|
30
|
+
LINESPANS_PREFIX = "diff-cover-src-line"
|
|
27
31
|
|
|
28
32
|
# Number of extra lines to include before and after
|
|
29
33
|
# each snippet to provide context.
|
|
@@ -50,6 +54,7 @@ class Snippet:
|
|
|
50
54
|
last_line,
|
|
51
55
|
violation_lines,
|
|
52
56
|
lexer_name,
|
|
57
|
+
covered_lines=None,
|
|
53
58
|
):
|
|
54
59
|
"""
|
|
55
60
|
Create a source code snippet.
|
|
@@ -75,6 +80,10 @@ class Snippet:
|
|
|
75
80
|
programming language for this snippet.
|
|
76
81
|
See https://pygments.org/docs/lexers/
|
|
77
82
|
|
|
83
|
+
`covered_lines` is an optional list of line numbers
|
|
84
|
+
to highlight as covered. When omitted (the default),
|
|
85
|
+
no covered-line highlighting is rendered.
|
|
86
|
+
|
|
78
87
|
Raises a `ValueError` if `start_line` is less than 1
|
|
79
88
|
"""
|
|
80
89
|
if start_line < 1:
|
|
@@ -86,6 +95,7 @@ class Snippet:
|
|
|
86
95
|
self._last_line = last_line
|
|
87
96
|
self._violation_lines = violation_lines
|
|
88
97
|
self._lexer_name = lexer_name
|
|
98
|
+
self._covered_lines = covered_lines or []
|
|
89
99
|
|
|
90
100
|
@classmethod
|
|
91
101
|
def style_defs(cls):
|
|
@@ -95,21 +105,74 @@ class Snippet:
|
|
|
95
105
|
"""
|
|
96
106
|
formatter = HtmlFormatter()
|
|
97
107
|
formatter.style.highlight_color = cls.VIOLATION_COLOR
|
|
98
|
-
|
|
108
|
+
base_styles = formatter.get_style_defs()
|
|
109
|
+
# Append a rule for covered-line highlighting.
|
|
110
|
+
covered_style = (
|
|
111
|
+
f".{cls.COVERED_LINE_CSS_CLASS} "
|
|
112
|
+
f"{{ background-color: {cls.COVERED_COLOR}; }}"
|
|
113
|
+
)
|
|
114
|
+
return f"{base_styles}\n{covered_style}"
|
|
99
115
|
|
|
100
116
|
def html(self):
|
|
101
117
|
"""
|
|
102
118
|
Return an HTML representation of the snippet.
|
|
119
|
+
|
|
120
|
+
Violation lines are highlighted via Pygments' built-in
|
|
121
|
+
`hl_lines` mechanism. When the snippet was constructed with
|
|
122
|
+
`covered_lines`, those lines are additionally marked by adding
|
|
123
|
+
the covered-line CSS class to their per-line span, produced via
|
|
124
|
+
the `linespans` formatter option.
|
|
103
125
|
"""
|
|
104
|
-
|
|
126
|
+
formatter_kwargs = dict(
|
|
105
127
|
cssclass=self.DIV_CSS_CLASS,
|
|
106
128
|
linenos=True,
|
|
107
129
|
linenostart=self._start_line,
|
|
108
130
|
hl_lines=self._shift_lines(self._violation_lines, self._start_line),
|
|
109
131
|
lineanchors=self._src_filename,
|
|
110
132
|
)
|
|
133
|
+
# Only enable per-line `<span id="...">` wrapping when we actually
|
|
134
|
+
# need it for covered-line highlighting. This keeps the rendered
|
|
135
|
+
# HTML byte-for-byte identical to the historical output when no
|
|
136
|
+
# covered lines are supplied.
|
|
137
|
+
if self._covered_lines:
|
|
138
|
+
formatter_kwargs["linespans"] = self.LINESPANS_PREFIX
|
|
111
139
|
|
|
112
|
-
|
|
140
|
+
rendered = pygments.format(self.src_tokens(), HtmlFormatter(**formatter_kwargs))
|
|
141
|
+
|
|
142
|
+
if self._covered_lines:
|
|
143
|
+
# NOTE: do NOT shift these line numbers. Unlike `hl_lines`
|
|
144
|
+
# (which is snippet-relative, 1-based), Pygments' `linespans`
|
|
145
|
+
# IDs are emitted using the same numbering as `linenostart`,
|
|
146
|
+
# i.e. the absolute file line number. So we match the raw
|
|
147
|
+
# values from `_covered_lines` directly.
|
|
148
|
+
rendered = self._mark_covered_lines(rendered, self._covered_lines)
|
|
149
|
+
|
|
150
|
+
return rendered
|
|
151
|
+
|
|
152
|
+
@classmethod
|
|
153
|
+
def _mark_covered_lines(cls, html, covered_file_lines):
|
|
154
|
+
"""
|
|
155
|
+
Add the covered-line CSS class to per-line spans whose line
|
|
156
|
+
number is in `covered_file_lines`.
|
|
157
|
+
|
|
158
|
+
`covered_file_lines` are absolute (file-relative) line numbers,
|
|
159
|
+
matching the IDs that Pygments emits via `linespans` when
|
|
160
|
+
`linenostart` is set to the snippet's starting line number.
|
|
161
|
+
"""
|
|
162
|
+
wanted = set(covered_file_lines)
|
|
163
|
+
if not wanted:
|
|
164
|
+
return html
|
|
165
|
+
|
|
166
|
+
prefix = cls.LINESPANS_PREFIX
|
|
167
|
+
css_class = cls.COVERED_LINE_CSS_CLASS
|
|
168
|
+
|
|
169
|
+
def _replace(match):
|
|
170
|
+
line_num = int(match.group(1))
|
|
171
|
+
if line_num in wanted:
|
|
172
|
+
return f'<span id="{prefix}-{line_num}" class="{css_class}">'
|
|
173
|
+
return match.group(0)
|
|
174
|
+
|
|
175
|
+
return re.sub(rf'<span id="{re.escape(prefix)}-(\d+)">', _replace, html)
|
|
113
176
|
|
|
114
177
|
def markdown(self):
|
|
115
178
|
"""
|
|
@@ -174,24 +237,38 @@ class Snippet:
|
|
|
174
237
|
return "".join([val for _, val in self._src_tokens])
|
|
175
238
|
|
|
176
239
|
@classmethod
|
|
177
|
-
def load_formatted_snippets(cls, src_path, violation_lines):
|
|
240
|
+
def load_formatted_snippets(cls, src_path, violation_lines, covered_lines=None):
|
|
178
241
|
"""
|
|
179
242
|
Load snippets from the file at `src_path` and format
|
|
180
243
|
them as HTML and as plain text.
|
|
181
244
|
Returns a dictionary containing the two types of formatting
|
|
182
245
|
results for code snippets.
|
|
183
246
|
|
|
247
|
+
If `covered_lines` is provided (non-empty), the HTML output will
|
|
248
|
+
additionally render snippets around those lines and highlight
|
|
249
|
+
them as covered. Markdown and terminal output is unchanged
|
|
250
|
+
regardless of `covered_lines`, to keep those formats stable.
|
|
251
|
+
|
|
184
252
|
See `load_snippets()` for details.
|
|
185
253
|
"""
|
|
186
254
|
|
|
187
|
-
#
|
|
188
|
-
|
|
255
|
+
# Snippets used for markdown/terminal output keep the historical
|
|
256
|
+
# behaviour: ranges only around violation lines, no covered-line
|
|
257
|
+
# information attached.
|
|
258
|
+
violation_only_snippets = cls.load_snippets(src_path, violation_lines)
|
|
259
|
+
|
|
260
|
+
if covered_lines:
|
|
261
|
+
# HTML rendering also visualises covered diff lines, so widen
|
|
262
|
+
# the snippet ranges to include them and pass the covered list
|
|
263
|
+
# through to the snippet for per-line highlighting.
|
|
264
|
+
html_snippets = cls.load_snippets(src_path, violation_lines, covered_lines)
|
|
265
|
+
else:
|
|
266
|
+
html_snippets = violation_only_snippets
|
|
189
267
|
|
|
190
|
-
# ...render twice in different formats
|
|
191
268
|
return {
|
|
192
|
-
"html": [snippet.html() for snippet in
|
|
193
|
-
"markdown": [snippet.markdown() for snippet in
|
|
194
|
-
"terminal": [snippet.terminal() for snippet in
|
|
269
|
+
"html": [snippet.html() for snippet in html_snippets],
|
|
270
|
+
"markdown": [snippet.markdown() for snippet in violation_only_snippets],
|
|
271
|
+
"terminal": [snippet.terminal() for snippet in violation_only_snippets],
|
|
195
272
|
}
|
|
196
273
|
|
|
197
274
|
@classmethod
|
|
@@ -223,12 +300,17 @@ class Snippet:
|
|
|
223
300
|
return contents
|
|
224
301
|
|
|
225
302
|
@classmethod
|
|
226
|
-
def load_snippets(cls, src_path, violation_lines):
|
|
303
|
+
def load_snippets(cls, src_path, violation_lines, covered_lines=None):
|
|
227
304
|
"""
|
|
228
305
|
Load snippets from the file at `src_path` to show
|
|
229
306
|
violations on lines in the list `violation_lines`
|
|
230
307
|
(list of line numbers, starting at index 0).
|
|
231
308
|
|
|
309
|
+
If `covered_lines` is provided, those lines are also treated as
|
|
310
|
+
"interesting" when computing snippet ranges (so a fully-covered
|
|
311
|
+
file still yields snippets) and are stored on the resulting
|
|
312
|
+
`Snippet` instances for use during rendering.
|
|
313
|
+
|
|
232
314
|
The file at `src_path` should be a text file (not binary).
|
|
233
315
|
|
|
234
316
|
Returns a list of `Snippet` instances.
|
|
@@ -237,9 +319,15 @@ class Snippet:
|
|
|
237
319
|
"""
|
|
238
320
|
contents = cls.load_contents(src_path)
|
|
239
321
|
|
|
240
|
-
# Construct a list of snippet ranges
|
|
322
|
+
# Construct a list of snippet ranges. When `covered_lines` is
|
|
323
|
+
# provided, expand the set of "interesting" lines so we also
|
|
324
|
+
# render context around covered diff lines, not just violations.
|
|
241
325
|
src_lines = contents.split("\n")
|
|
242
|
-
|
|
326
|
+
if covered_lines:
|
|
327
|
+
interesting_lines = sorted(set(violation_lines) | set(covered_lines))
|
|
328
|
+
else:
|
|
329
|
+
interesting_lines = violation_lines
|
|
330
|
+
snippet_ranges = cls._snippet_ranges(len(src_lines), interesting_lines)
|
|
243
331
|
|
|
244
332
|
# Parse the source into tokens
|
|
245
333
|
token_stream, lexer = cls._parse_src(contents, src_path)
|
|
@@ -248,7 +336,15 @@ class Snippet:
|
|
|
248
336
|
token_groups = cls._group_tokens(token_stream, snippet_ranges)
|
|
249
337
|
|
|
250
338
|
return [
|
|
251
|
-
Snippet(
|
|
339
|
+
Snippet(
|
|
340
|
+
tokens,
|
|
341
|
+
src_path,
|
|
342
|
+
start,
|
|
343
|
+
end,
|
|
344
|
+
violation_lines,
|
|
345
|
+
lexer.name,
|
|
346
|
+
covered_lines=covered_lines,
|
|
347
|
+
)
|
|
252
348
|
for (start, end), tokens in sorted(token_groups.items())
|
|
253
349
|
]
|
|
254
350
|
|
{diff_cover-10.2.1 → diff_cover-10.4.0}/diff_cover/violationsreporters/violations_reporter.py
RENAMED
|
@@ -24,7 +24,13 @@ class XmlCoverageReporter(BaseViolationReporter):
|
|
|
24
24
|
Query information from a Cobertura|Clover|JaCoCo XML coverage report.
|
|
25
25
|
"""
|
|
26
26
|
|
|
27
|
-
def __init__(
|
|
27
|
+
def __init__(
|
|
28
|
+
self,
|
|
29
|
+
xml_roots,
|
|
30
|
+
src_roots=None,
|
|
31
|
+
expand_coverage_report=False,
|
|
32
|
+
branch_coverage=False,
|
|
33
|
+
):
|
|
28
34
|
"""
|
|
29
35
|
Load the XML coverage report represented
|
|
30
36
|
by the cElementTree with root element `xml_root`.
|
|
@@ -42,6 +48,11 @@ class XmlCoverageReporter(BaseViolationReporter):
|
|
|
42
48
|
|
|
43
49
|
self._src_roots = src_roots or [""]
|
|
44
50
|
self._expand_coverage_report = expand_coverage_report
|
|
51
|
+
self._branch_coverage = branch_coverage
|
|
52
|
+
|
|
53
|
+
# Pulls the "(covered/total)" pair out of Cobertura's
|
|
54
|
+
# condition-coverage attribute, e.g. "50% (1/2)".
|
|
55
|
+
self._cobertura_condition_coverage_re = re.compile(r"\((\d+)/(\d+)\)")
|
|
45
56
|
|
|
46
57
|
def _get_xml_classes(self, xml_document):
|
|
47
58
|
"""
|
|
@@ -239,23 +250,19 @@ class XmlCoverageReporter(BaseViolationReporter):
|
|
|
239
250
|
{_hits: last_hit_number, _number: line_number}
|
|
240
251
|
)
|
|
241
252
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
if int(line.get(_hits, 0)) == 0
|
|
248
|
-
}
|
|
253
|
+
new_violations = {
|
|
254
|
+
Violation(int(line.get(_number)), None)
|
|
255
|
+
for line in line_nodes
|
|
256
|
+
if self._is_violation(line, _hits)
|
|
257
|
+
}
|
|
249
258
|
|
|
250
|
-
#
|
|
251
|
-
#
|
|
252
|
-
#
|
|
259
|
+
# The first report defines the violations set. Each subsequent
|
|
260
|
+
# report narrows it, since we only keep violations that show up
|
|
261
|
+
# in every report.
|
|
262
|
+
if violations is None:
|
|
263
|
+
violations = new_violations
|
|
253
264
|
else:
|
|
254
|
-
violations = violations &
|
|
255
|
-
Violation(int(line.get(_number)), None)
|
|
256
|
-
for line in line_nodes
|
|
257
|
-
if int(line.get(_hits, 0)) == 0
|
|
258
|
-
}
|
|
265
|
+
violations = violations & new_violations
|
|
259
266
|
|
|
260
267
|
# Measured is the union of itself and the new measured
|
|
261
268
|
measured = measured | {int(line.get(_number)) for line in line_nodes}
|
|
@@ -267,6 +274,29 @@ class XmlCoverageReporter(BaseViolationReporter):
|
|
|
267
274
|
|
|
268
275
|
self._info_cache[src_path] = (violations, measured)
|
|
269
276
|
|
|
277
|
+
def _is_violation(self, line, hits_attr):
|
|
278
|
+
"""
|
|
279
|
+
Return whether a coverage `line` node counts as a violation.
|
|
280
|
+
|
|
281
|
+
A line is a violation if it was never executed, or -- when branch
|
|
282
|
+
coverage is enabled -- if it is a partially covered branch.
|
|
283
|
+
"""
|
|
284
|
+
if int(line.get(hits_attr, 0)) == 0:
|
|
285
|
+
return True
|
|
286
|
+
return self._branch_coverage and self._is_partial_branch(line)
|
|
287
|
+
|
|
288
|
+
def _is_partial_branch(self, line):
|
|
289
|
+
"""Return whether a Cobertura `line` node is a partially covered branch."""
|
|
290
|
+
if line.get("branch") != "true":
|
|
291
|
+
return False
|
|
292
|
+
match = self._cobertura_condition_coverage_re.search(
|
|
293
|
+
line.get("condition-coverage", "")
|
|
294
|
+
)
|
|
295
|
+
if not match:
|
|
296
|
+
return False
|
|
297
|
+
covered, total = int(match.group(1)), int(match.group(2))
|
|
298
|
+
return covered < total
|
|
299
|
+
|
|
270
300
|
def violations(self, src_path):
|
|
271
301
|
"""
|
|
272
302
|
See base class comments.
|
|
@@ -37,6 +37,26 @@ def test_parse_with_no_report():
|
|
|
37
37
|
assert not arg_dict["ignore_unstaged"]
|
|
38
38
|
|
|
39
39
|
|
|
40
|
+
def test_show_covered_defaults_false():
|
|
41
|
+
arg_dict = parse_coverage_args(["reports/coverage.xml"])
|
|
42
|
+
assert arg_dict["show_covered"] is False
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def test_show_covered_flag():
|
|
46
|
+
arg_dict = parse_coverage_args(["reports/coverage.xml", "--show-covered"])
|
|
47
|
+
assert arg_dict["show_covered"] is True
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def test_branch_coverage_defaults_false():
|
|
51
|
+
arg_dict = parse_coverage_args(["reports/coverage.xml"])
|
|
52
|
+
assert arg_dict["branch_coverage"] is False
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def test_branch_coverage_flag():
|
|
56
|
+
arg_dict = parse_coverage_args(["reports/coverage.xml", "--branch-coverage"])
|
|
57
|
+
assert arg_dict["branch_coverage"] is True
|
|
58
|
+
|
|
59
|
+
|
|
40
60
|
def test_parse_with_multiple_reports():
|
|
41
61
|
argv = [
|
|
42
62
|
"reports/coverage.xml",
|