Skip to content

Commit

Permalink
Add support for LCOV output
Browse files Browse the repository at this point in the history
Coverage.py 6.3 gained support for the LCOV output format. Add support
for this to pytest-cov via '--cov-report=lcov[:dest]'.

Fix: #535
  • Loading branch information
fetzerch committed May 27, 2022
1 parent 4cfbe14 commit 0b60995
Show file tree
Hide file tree
Showing 10 changed files with 82 additions and 24 deletions.
16 changes: 8 additions & 8 deletions .appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,22 @@ environment:
matrix:
- TOXENV: check
- TOXENV: 'py36-pytest46-xdist127-coverage55,py36-pytest46-xdist133-coverage55,py36-pytest54-xdist133-coverage55,py36-pytest62-xdist202-coverage55'
- TOXENV: 'py37-pytest46-xdist127-coverage55,py37-pytest46-xdist133-coverage55,py37-pytest54-xdist133-coverage55,py37-pytest62-xdist202-coverage55'
- TOXENV: 'py38-pytest46-xdist133-coverage55,py38-pytest54-xdist133-coverage55,py38-pytest62-xdist202-coverage55'
- TOXENV: 'py39-pytest62-xdist202-coverage55'
- TOXENV: 'pypy3-pytest46-xdist127-coverage55,pypy3-pytest46-xdist133-coverage55,pypy3-pytest54-xdist133-coverage55,pypy3-pytest62-xdist202-coverage55'
- TOXENV: 'py37-pytest46-xdist127-coverage63,py37-pytest46-xdist133-coverage63,py37-pytest54-xdist133-coverage63,py37-pytest62-xdist202-coverage63'
- TOXENV: 'py38-pytest46-xdist133-coverage63,py38-pytest54-xdist133-coverage63,py38-pytest62-xdist202-coverage63'
- TOXENV: 'py39-pytest62-xdist202-coverage63'
- TOXENV: 'pypy3-pytest46-xdist127-coverage63,pypy3-pytest46-xdist133-coverage63,pypy3-pytest54-xdist133-coverage63,pypy3-pytest62-xdist202-coverage63'
matrix:
exclude:
- image: Visual Studio 2015
TOXENV: 'py36-pytest46-xdist127-coverage55,py36-pytest46-xdist133-coverage55,py36-pytest54-xdist133-coverage55,py36-pytest62-xdist202-coverage55'
- image: Visual Studio 2015
TOXENV: 'py37-pytest46-xdist127-coverage55,py37-pytest46-xdist133-coverage55,py37-pytest54-xdist133-coverage55,py37-pytest62-xdist202-coverage55'
TOXENV: 'py37-pytest46-xdist127-coverage63,py37-pytest46-xdist133-coverage63,py37-pytest54-xdist133-coverage63,py37-pytest62-xdist202-coverage63'
- image: Visual Studio 2015
TOXENV: 'py38-pytest46-xdist133-coverage55,py38-pytest54-xdist133-coverage55,py38-pytest62-xdist202-coverage55'
TOXENV: 'py38-pytest46-xdist133-coverage63,py38-pytest54-xdist133-coverage63,py38-pytest62-xdist202-coverage63'
- image: Visual Studio 2015
TOXENV: 'py39-pytest62-xdist202-coverage55'
TOXENV: 'py39-pytest62-xdist202-coverage63'
- image: Visual Studio 2015
TOXENV: 'pypy3-pytest46-xdist127-coverage55,pypy3-pytest46-xdist133-coverage55,pypy3-pytest54-xdist133-coverage55,pypy3-pytest62-xdist202-coverage55'
TOXENV: 'pypy3-pytest46-xdist127-coverage63,pypy3-pytest46-xdist133-coverage63,pypy3-pytest54-xdist133-coverage63,pypy3-pytest62-xdist202-coverage63'
init:
- ps: echo $env:TOXENV
- ps: ls C:\Python*
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ jobs:
- name: Tox tests
run: |
tox -v -e ${{ matrix.tox-python-version }}-${{ matrix.tox-extra-versions }}-coverage55
tox -v -e ${{ matrix.tox-python-version }}-${{ matrix.tox-extra-versions }}-coverage63
allgood:
needs: test
Expand Down
1 change: 1 addition & 0 deletions AUTHORS.rst
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,4 @@ Authors
* Danilo Šegan - https://github.com/dsegan
* Michał Bielawski - https://github.com/D3X
* Zac Hatfield-Dodds - https://github.com/Zac-HD
* Christian Fetzer - https://github.com/fetzerch
3 changes: 3 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ Changelog
* `--cov-fail-under` no longer causes `pytest --collect-only` to fail
Contributed by Zac Hatfield-Dodds in
`#511 <https://github.com/pytest-dev/pytest-cov/pull/511>`_.
* Added support for LCOV output format via `--cov-report=lcov`. Only works with coverage 6.3+.
Contributed by Christian Fetzer in
`#536 <https://github.com/pytest-dev/pytest-cov/issues/536>`_.


3.0.0 (2021-10-04)
Expand Down
4 changes: 2 additions & 2 deletions docs/config.rst
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,9 @@ The complete list of command line options is:

--cov=PATH Measure coverage for filesystem path. (multi-allowed)
--cov-report=type Type of report to generate: term, term-missing,
annotate, html, xml (multi-allowed). term, term-
annotate, html, xml, lcov (multi-allowed). term, term-
missing may be followed by ":skip-covered". annotate,
html and xml may be followed by ":DEST" where DEST
html, xml and lcov may be followed by ":DEST" where DEST
specifies the output location. Use --cov-report= to
not generate any output.
--cov-config=path Config file for coverage. Default: .coveragerc
Expand Down
8 changes: 5 additions & 3 deletions docs/reporting.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ Reporting

It is possible to generate any combination of the reports for a single test run.

The available reports are terminal (with or without missing line numbers shown), HTML, XML and
The available reports are terminal (with or without missing line numbers shown), HTML, XML, LCOV and
annotated source code.

The terminal report without line numbers (default)::
Expand Down Expand Up @@ -49,19 +49,21 @@ The terminal report with skip covered::

You can use ``skip-covered`` with ``term-missing`` as well. e.g. ``--cov-report term-missing:skip-covered``

These three report options output to files without showing anything on the terminal::
These four report options output to files without showing anything on the terminal::

pytest --cov-report html
--cov-report xml
--cov-report lcov
--cov-report annotate
--cov=myproj tests/

The output location for each of these reports can be specified. The output location for the XML
The output location for each of these reports can be specified. The output location for the XML and LCOV
report is a file. Where as the output location for the HTML and annotated source code reports are
directories::

pytest --cov-report html:cov_html
--cov-report xml:cov.xml
--cov-report lcov:cov.info
--cov-report annotate:cov_annotate
--cov=myproj tests/

Expand Down
12 changes: 12 additions & 0 deletions src/pytest_cov/engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,18 @@ def summary(self, stream):
total = self.cov.xml_report(ignore_errors=True, outfile=output)
stream.write('Coverage XML written to file %s\n' % (self.cov.config.xml_output if output is None else output))

# Produce lcov report if wanted.
if 'lcov' in self.cov_report:
output = self.cov_report['lcov']
with _backup(self.cov, "config"):
self.cov.lcov_report(ignore_errors=True, outfile=output)

# We need to call Coverage.report here, just to get the total
# Coverage.lcov_report doesn't return any total and we need it for --cov-fail-under.
total = self.cov.report(ignore_errors=True, file=_NullFile)

stream.write('Coverage LCOV written to file %s\n' % (self.cov.config.lcov_output if output is None else output))

return total


Expand Down
9 changes: 6 additions & 3 deletions src/pytest_cov/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class CovReportWarning(PytestCovWarning):


def validate_report(arg):
file_choices = ['annotate', 'html', 'xml']
file_choices = ['annotate', 'html', 'xml', 'lcov']
term_choices = ['term', 'term-missing']
term_modifier_choices = ['skip-covered']
all_choices = term_choices + file_choices
Expand All @@ -39,6 +39,9 @@ def validate_report(arg):
msg = f'invalid choice: "{arg}" (choose from "{all_choices}")'
raise argparse.ArgumentTypeError(msg)

if report_type == 'lcov' and coverage.version_info <= (6, 3):
raise argparse.ArgumentTypeError('LCOV output is only supported with coverage.py >= 6.3')

if len(values) == 1:
return report_type, None

Expand Down Expand Up @@ -96,9 +99,9 @@ def pytest_addoption(parser):
group.addoption('--cov-report', action=StoreReport, default={},
metavar='TYPE', type=validate_report,
help='Type of report to generate: term, term-missing, '
'annotate, html, xml (multi-allowed). '
'annotate, html, xml, lcov (multi-allowed). '
'term, term-missing may be followed by ":skip-covered". '
'annotate, html and xml may be followed by ":DEST" '
'annotate, html, xml and lcov may be followed by ":DEST" '
'where DEST specifies the output location. '
'Use --cov-report= to not generate any output.')
group.addoption('--cov-config', action='store', default='.coveragerc',
Expand Down
41 changes: 37 additions & 4 deletions tests/test_pytest_cov.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,8 @@ def test_foo(cov):
CHILD_SCRIPT_RESULT = '[56] * 100%'
PARENT_SCRIPT_RESULT = '9 * 100%'
DEST_DIR = 'cov_dest'
REPORT_NAME = 'cov.xml'
XML_REPORT_NAME = 'cov.xml'
LCOV_REPORT_NAME = 'cov.info'

xdist_params = pytest.mark.parametrize('opts', [
'',
Expand Down Expand Up @@ -333,18 +334,50 @@ def test_xml_output_dir(testdir):

result = testdir.runpytest('-v',
'--cov=%s' % script.dirpath(),
'--cov-report=xml:' + REPORT_NAME,
'--cov-report=xml:' + XML_REPORT_NAME,
script)

result.stdout.fnmatch_lines([
'*- coverage: platform *, python * -*',
'Coverage XML written to file ' + REPORT_NAME,
'Coverage XML written to file ' + XML_REPORT_NAME,
'*10 passed*',
])
assert testdir.tmpdir.join(REPORT_NAME).check()
assert testdir.tmpdir.join(XML_REPORT_NAME).check()
assert result.ret == 0


@pytest.mark.skipif("coverage.version_info < (6, 3)")
def test_lcov_output_dir(testdir):
script = testdir.makepyfile(SCRIPT)

result = testdir.runpytest('-v',
'--cov=%s' % script.dirpath(),
'--cov-report=lcov:' + LCOV_REPORT_NAME,
script)

result.stdout.fnmatch_lines([
'*- coverage: platform *, python * -*',
'Coverage LCOV written to file ' + LCOV_REPORT_NAME,
'*10 passed*',
])
assert testdir.tmpdir.join(LCOV_REPORT_NAME).check()
assert result.ret == 0


@pytest.mark.skipif("coverage.version_info >= (6, 3)")
def test_lcov_not_supported(testdir):
script = testdir.makepyfile("a = 1")
result = testdir.runpytest('-v',
'--cov=%s' % script.dirpath(),
'--cov-report=lcov',
script,
)
result.stderr.fnmatch_lines([
'*argument --cov-report: LCOV output is only supported with coverage.py >= 6.3',
])
assert result.ret != 0


def test_term_output_dir(testdir):
script = testdir.makepyfile(SCRIPT)

Expand Down
10 changes: 7 additions & 3 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,12 @@ passenv =
[tox]
envlist =
check
py{36,37,py,py3}-pytest46-xdist127-coverage{55}
py{36,37,38,py3}-pytest{46,54}-xdist133-coverage{55}
py{36,37,38,39,310,py3}-pytest{62}-xdist202-coverage{55}
py36-pytest46-xdist127-coverage{55}
py36-pytest{46,54}-xdist133-coverage{55}
py36-pytest{62}-xdist202-coverage{55}
py{37,py,py3}-pytest46-xdist127-coverage{63}
py{37,38,py3}-pytest{46,54}-xdist133-coverage{63}
py{37,38,39,310,py3}-pytest{62}-xdist202-coverage{63}
docs

[testenv]
Expand Down Expand Up @@ -49,6 +52,7 @@ setenv =
coverage53: _DEP_COVERAGE=coverage==5.3.1
coverage54: _DEP_COVERAGE=coverage==5.4
coverage55: _DEP_COVERAGE=coverage==5.5
coverage63: _DEP_COVERAGE=coverage==6.3
# For testing against a coverage.py working tree.
coveragedev: _DEP_COVERAGE=-e{env:COVERAGE_HOME}
passenv =
Expand Down

0 comments on commit 0b60995

Please sign in to comment.