File: README.rst

package info (click to toggle)
diff-cover 10.2.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 1,256 kB
  • sloc: python: 6,452; xml: 218; cpp: 18; sh: 12; makefile: 10
file content (527 lines) | stat: -rw-r--r-- 17,906 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
diff-cover |pypi-version| |conda-version| |build-status|
========================================================================================

Automatically find diff lines that need test coverage.
Also finds diff lines that have violations (according to tools such
as pycodestyle, pyflakes, flake8, or pylint).
This is used as a code quality metric during code reviews.

Overview
--------

Diff coverage is the percentage of new or modified
lines that are covered by tests.  This provides a clear
and achievable standard for code review: If you touch a line
of code, that line should be covered.  Code coverage
is *every* developer's responsibility!

The ``diff-cover`` command line tool compares an XML coverage report
with the output of ``git diff``.  It then reports coverage information
for lines in the diff.

Currently, ``diff-cover`` requires that:

- You are using ``git`` for version control.
- Your test runner generates coverage reports in Cobertura, Clover
  or JaCoCo XML format, or LCov format.

Supported XML or LCov coverage reports can be generated with many coverage tools,
including:

- Cobertura__ (Java)
- Clover__ (Java)
- JaCoCo__ (Java)
- coverage.py__ (Python)
- JSCover__ (JavaScript)
- lcov__ (C/C++)

__ http://cobertura.sourceforge.net/
__ http://openclover.org/
__ https://www.jacoco.org/
__ http://nedbatchelder.com/code/coverage/
__ http://tntim96.github.io/JSCover/
__ https://ltp.sourceforge.net/coverage/lcov.php


``diff-cover`` is designed to be extended.  If you are interested
in adding support for other version control systems or coverage
report formats, see below for information on how to contribute!


Installation
------------

To install the latest release:

.. code:: bash

    pip install diff-cover


To install the development version:

.. code:: bash

    git clone https://github.com/Bachmann1234/diff-cover.git
    cd diff-cover
    poetry install
    poetry shell


Getting Started
---------------

1. Set the current working directory to a ``git`` repository.

2. Run your test suite under coverage and generate a [Cobertura, Clover or JaCoCo] XML report.
   For example, using `pytest-cov`__:

.. code:: bash

    pytest --cov --cov-report=xml

__ https://pypi.org/project/pytest-cov

This will create a ``coverage.xml`` file in the current working directory.

**NOTE**: If you are using a different coverage generator, you will
need to use different commands to generate the coverage XML report.


3. Run ``diff-cover``:

.. code:: bash

    diff-cover coverage.xml

This will compare the current ``git`` branch to ``origin/main`` and print
the diff coverage report to the console.

You can also generate an HTML, JSON or Markdown version of the report:

.. code:: bash

    diff-cover coverage.xml --format html:report.html
    diff-cover coverage.xml --format json:report.json
    diff-cover coverage.xml --format markdown:report.md
    diff-cover coverage.xml --format html:report.html,markdown:report.md

Multiple XML Coverage Reports
-------------------------------

In the case that one has multiple xml reports form multiple test suites, you
can get a combined coverage report (a line is counted  as covered if it is
covered in ANY of the xml reports) by running ``diff-cover`` with multiple
coverage reports as arguments. You may specify any arbitrary number of coverage
reports:

.. code:: bash

    diff-cover coverage1.xml coverage2.xml

Quality Coverage
-----------------
You can use diff-cover to see quality reports on the diff as well by running
``diff-quality``.

.. code :: bash

    diff-quality --violations=<tool>

Where ``tool`` is the quality checker to use. Currently ``pycodestyle``, ``pyflakes``,
``flake8``, ``pylint``, ``checkstyle``, ``checkstylexml``, ``ruff.check``, ``clang`` are supported, but more
checkers can (and should!) be supported. See the section "Adding `diff-quality``
Support for a New Quality Checker".

NOTE: There's no way to run ``findbugs`` from ``diff-quality`` as it operating
over the generated java bytecode and should be integrated into the build
framework.

Like ``diff-cover``, HTML, JSON or Markdown reports can be generated with

.. code:: bash

    diff-quality --violations=<tool> --format html:report.html
    diff-quality --violations=<tool> --format json:report.json
    diff-quality --violations=<tool> --format markdown:report.md
    diff-quality --violations=<tool> --format html:report.html,markdown:report.md

If you have already generated a report using ``pycodestyle``, ``pyflakes``, ``flake8``,
``pylint``, ``checkstyle``, ``checkstylexml``, or ``findbugs`` you can pass the report
to ``diff-quality``.  This is more efficient than letting ``diff-quality`` re-run
``pycodestyle``, ``pyflakes``, ``flake8``, ``pylint``, ``checkstyle``, or ``checkstylexml``.

.. code:: bash

    # For pylint < 1.0
    pylint -f parseable > pylint_report.txt

    # For pylint >= 1.0
    pylint --msg-template="{path}:{line}: [{msg_id}({symbol}), {obj}] {msg}" > pylint_report.txt

    # Use the generated pylint report when running diff-quality
    diff-quality --violations=pylint pylint_report.txt

    # Use a generated pycodestyle report when running diff-quality.
    pycodestyle > pycodestyle_report.txt
    diff-quality --violations=pycodestyle pycodestyle_report.txt

Note that you must use the ``-f parseable`` option to generate
the ``pylint`` report for pylint versions less than 1.0 and the
``--msg-template`` option for versions >= 1.0.

``diff-quality`` will also accept multiple ``pycodestyle``, ``pyflakes``, ``flake8``,
or ``pylint`` reports:

.. code:: bash

    diff-quality --violations=pylint report_1.txt report_2.txt

If you need to pass in additional options you can with the ``options`` flag

.. code:: bash

    diff-quality --violations=pycodestyle --options="--exclude='*/migrations*' --statistics" pycodestyle_report.txt

Compare Branch
--------------

By default, ``diff-cover`` compares the current branch to ``origin/main``.  To specify a different compare branch:

.. code:: bash

    diff-cover coverage.xml --compare-branch=origin/release

Diff File
--------------

You may provide a file containing the output of ``git diff`` to ``diff-cover`` instead of using a branch name.

For example, Say you have 2 branches ``main`` and ``feature``. Lets say after creating and checking out the feature branch,
you make commits ``A``, ``B``, and ``C`` in that order.


If you want to see all changes between the ``feature`` and ``main`` branch, you can generate a diff file like this:

.. code:: bash

    git diff main..feature > diff.txt

If you want to see the changes between the ``feature`` branch and the commit ``A``, you can generate a diff file using the following command:

.. code:: bash

    git diff A..feature > diff.txt

You can then run ``diff-cover`` with the diff file as an argument:

.. code:: bash

    diff-cover coverage.xml --diff-file=diff.txt

Total Percent Formatting
------------------------

By default, ``diff-cover`` and ``diff-quality`` round the total coverage/quality
percentage to an integer. To keep up to two decimal places, use
``--total-percent-float``:

.. code:: bash

    diff-cover coverage.xml --total-percent-float
    diff-quality --violations=pycodestyle --total-percent-float

Fail Under
----------

To have ``diff-cover`` and ``diff-quality`` return a non zero status code if the report quality/coverage percentage is
below a certain threshold specify the fail-under parameter

.. code:: bash

    diff-cover coverage.xml --fail-under=80
    diff-quality --violations=pycodestyle --fail-under=80

The above will return a non zero status if the coverage or quality score was below 80%.

Exclude/Include paths
---------------------

Explicit exclusion of paths is possible for both ``diff-cover`` and ``diff-quality``, while inclusion is
only supported for ``diff-quality`` (since 5.1.0).

The exclude option works with ``fnmatch``, include with ``glob``. Both options can consume multiple values.
Include options should be wrapped in double quotes to prevent shell globbing. Also they should be relative to
the current git directory.

.. code:: bash

    diff-cover coverage.xml --exclude setup.py
    diff-quality --violations=pycodestyle --exclude setup.py

    diff-quality --violations=pycodestyle --include project/foo/**

The following is executed for every changed file:

#. check if any include pattern was specified
#. if yes, check if the changed file is part of at least one include pattern
#. check if the file is part of any exclude pattern

Ignore/Include based on file status in git
------------------------------------------
Both ``diff-cover`` and ``diff-quality`` allow users to ignore and include files based on the git
status: staged, unstaged, untracked:

* ``--ignore-staged``: ignore all staged files (by default include them)
* ``--ignore-unstaged``: ignore all unstaged files (by default include them)
* ``--include-untracked``: include all untracked files (by default ignore them)

Quiet mode
----------
Both ``diff-cover`` and ``diff-quality`` support a quiet mode which is disable by default.
It can be enabled by using the ``-q``/``--quiet`` flag:

.. code:: bash

    diff-cover coverage.xml -q
    diff-quality --violations=pycodestyle -q

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`:

.. code:: bash

    diff-cover coverage.xml --config-file myconfig.toml
    diff-quality --violations=pycodestyle --config-file myconfig.toml

Currently, only TOML files are supported.
Please note, that only non-mandatory options are supported.
If an option is specified in the configuration file and over the command line, the value of the
command line is used.

TOML configuration
~~~~~~~~~~~~~~~~~~

The parser will only react to configuration files ending with `.toml`.
To use it, install `diff-cover` with the extra requirement `toml`.

The option names are the same as on the command line, but all dashes should be underscores.
If an option can be specified multiple times, the configuration value should be specified as a list.

.. code:: toml

    [tool.diff_cover]
    compare_branch = "origin/feature"
    quiet = true

    [tool.diff_quality]
    compare_branch = "origin/feature"
    ignore_staged = true


Troubleshooting
----------------------

**Issue**: ``diff-cover`` always reports: "No lines with coverage information in this diff."

**Solution**: ``diff-cover`` matches source files in the coverage XML report with
source files in the ``git diff``.  For this reason, it's important
that the relative paths to the files match.  If you are using `coverage.py`__
to generate the coverage XML report, then make sure you run
``diff-cover`` from the same working directory.

__ http://nedbatchelder.com/code/coverage/

**Issue**: ``GitDiffTool._execute()`` raises the error:

.. code:: bash

    fatal: ambiguous argument 'origin/main...HEAD': unknown revision or path not in the working tree.

This is known to occur when running ``diff-cover`` in `Travis CI`__

__ http://travis-ci.org

**Solution**: Fetch the remote main branch before running ``diff-cover``:

.. code:: bash

    git fetch origin master:refs/remotes/origin/main

**Issue**: ``diff-quality`` reports "diff_cover.violations_reporter.QualityReporterError:
No config file found, using default configuration"

**Solution**: Your project needs a `pylintrc` file.
Provide this file (it can be empty) and ``diff-quality`` should run without issue.

**Issue**: ``diff-quality`` reports "Quality tool not installed"

**Solution**: ``diff-quality`` assumes you have the tool you wish to run against your diff installed.
If you do not have it then install it with your favorite package manager.

**Issue**: ``diff-quality`` reports no quality issues

**Solution**: You might use a pattern like ``diff-quality --violations foo *.py``. The last argument
is not used to specify the files but for the quality tool report. Remove it to resolve the issue

License
-------

The code in this repository is licensed under the Apache 2.0 license.
Please see ``LICENSE.txt`` for details.


How to Contribute
-----------------

Contributions are very welcome. The easiest way is to fork this repo, and then
make a pull request from your fork.

NOTE: ``diff-quality`` supports a plugin model, so new tools can be integrated
without requiring changes to this repo. See the section "Adding `diff-quality``
Support for a New Quality Checker".

Setting Up For Development
~~~~~~~~~~~~~~~~~~~~~~~~~~

This project is managed with `poetry` this can be installed with `pip`
poetry manages a python virtual environment and organizes dependencies. It also
packages this project.

.. code:: bash

    pip install poetry

.. code:: bash

    poetry install

I would also suggest running this command after. This will make it so git blame ignores the commit
that formatted the entire codebase.

.. code:: bash

    git config blame.ignoreRevsFile .git-blame-ignore-revs


Adding `diff-quality`` Support for a New Quality Checker
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Adding support for a new quality checker is simple. ``diff-quality`` supports
plugins using the popular Python
`pluggy package <https://pluggy.readthedocs.io/en/latest/>`_.

If the quality checker is already implemented as a Python package, great! If not,
`create a Python package <https://packaging.python.org/tutorials/packaging-projects/>`_
to host the plugin implementation.

In the Python package's ``setup.py`` file, define an entry point for the plugin,
e.g.

.. code:: python

    setup(
        ...
        entry_points={
            'diff_cover': [
                'sqlfluff = sqlfluff.diff_quality_plugin'
            ],
        },
        ...
    )

Notes:

* The dictionary key for the entry point must be named ``diff_cover``
* The value must be in the format ``TOOL_NAME = YOUR_PACKAGE.PLUGIN_MODULE``

When your package is installed, ``diff-quality`` uses this information to
look up the tool package and module based on the tool name provided to the
``--violations`` option of the ``diff-quality`` command, e.g.:

.. code:: bash

    $ diff-quality --violations sqlfluff

The plugin implementation will look something like the example below. This is
a simplified example based on a working plugin implementation.

.. code:: python

    from diff_cover.hook import hookimpl as diff_cover_hookimpl
    from diff_cover.violationsreporters.base import BaseViolationReporter, Violation

    class SQLFluffViolationReporter(BaseViolationReporter):
        supported_extensions = ['sql']

        def __init__(self):
            super(SQLFluffViolationReporter, self).__init__('sqlfluff')

        def violations(self, src_path):
            return [
                Violation(violation.line_number, violation.description)
                for violation in get_linter().get_violations(src_path)
            ]

        def measured_lines(self, src_path):
            return None

        @staticmethod
        def installed():
            return True


    @diff_cover_hookimpl
    def diff_cover_report_quality():
        return SQLFluffViolationReporter()

Important notes:

* ``diff-quality`` is looking for a plugin function:

  * Located in your package's module that was listed in the ``setup.py`` entry point.
  * Marked with the ``@diff_cover_hookimpl`` decorator
  * Named ``diff_cover_report_quality``. (This distinguishes it from any other
    plugin types ``diff_cover`` may support.)
* The function should return an object with the following properties and methods:

  * ``supported_extensions`` property with a list of supported file extensions
  * ``violations()`` function that returns a list of ``Violation`` objects for
    the specified ``src_path``. For more details on this function and other
    possible reporting-related methods, see the ``BaseViolationReporter`` class
    `here <https://github.com/Bachmann1234/diff_cover/blob/main/diff_cover/violationsreporters/base.py>`_.

Special Thanks
-------------------------

Shout out to the original author of diff-cover `Will Daly
<https://github.com/wedaly>`_ and the original author of diff-quality `Sarina Canelake
<https://github.com/sarina>`_.

Originally created with the support of `edX
<https://github.com/edx>`_.


.. |pypi-version| image:: https://img.shields.io/pypi/v/diff-cover.svg
    :target: https://pypi.org/project/diff-cover
    :alt: PyPI version
.. |conda-version| image:: https://img.shields.io/conda/vn/conda-forge/diff-cover.svg
    :target: https://anaconda.org/conda-forge/diff-cover
    :alt: Conda version
.. |build-status| image:: https://github.com/bachmann1234/diff_cover/actions/workflows/verify.yaml/badge.svg?branch=main
    :target: https://github.com/Bachmann1234/diff_cover/actions/workflows/verify.yaml
    :alt: Build Status