Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for LCOV output #536

Merged
merged 1 commit into from
Sep 28, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -21,6 +21,9 @@ Changelog
concurrency = multiprocessing
parallel = true
sigterm = true
* 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