Skip to content

Commit

Permalink
Merge pull request #113 from unholysampler/specify-file-output
Browse files Browse the repository at this point in the history
Specify file output
  • Loading branch information
ionelmc committed May 9, 2016
2 parents f110b8f + 5e1fcc6 commit 0696bc7
Show file tree
Hide file tree
Showing 6 changed files with 154 additions and 11 deletions.
1 change: 1 addition & 0 deletions AUTHORS.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ Authors
* Ionel Cristian Mărieș - http://blog.ionelmc.ro
* Christian Ledermann - https://github.com/cleder
* Alec Nikolas Reiter - https://github.com/justanr
* Patrick Lannigan - https://github.com/unholysampler
5 changes: 5 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
Changelog
=========

2.2.2 (tba)
------------------

* Add support for specifying output location for html, xml, and annotate report.

2.2.1 (2016-01-30)
------------------

Expand Down
9 changes: 9 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,15 @@ These three report options output to files without showing anything on the termi
--cov-report annotate
--cov=myproj tests/

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

py.test --cov-report html:cov_html
--cov-report xml:cov.xml
--cov-report annotate:cov_annotate
--cov=myproj tests/

The final report option can also suppress printing to the terminal::

py.test --cov-report= --cov=myproj tests/
Expand Down
12 changes: 8 additions & 4 deletions src/pytest_cov/engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,20 +91,24 @@ def summary(self, stream):

# Produce annotated source code report if wanted.
if 'annotate' in self.cov_report:
self.cov.annotate(ignore_errors=True)
annotate_dir = self.cov_report['annotate']
self.cov.annotate(ignore_errors=True, directory=annotate_dir)
# We need to call Coverage.report here, just to get the total
# Coverage.annotate don't return any total and we need it for --cov-fail-under.
total = self.cov.report(ignore_errors=True, file=StringIO())
stream.write('Coverage annotated source written next to source\n')
if annotate_dir:
stream.write('Coverage annotated source written to dir %s\n' % annotate_dir)
else:
stream.write('Coverage annotated source written next to source\n')

# Produce html report if wanted.
if 'html' in self.cov_report:
total = self.cov.html_report(ignore_errors=True)
total = self.cov.html_report(ignore_errors=True, directory=self.cov_report['html'])
stream.write('Coverage HTML written to dir %s\n' % self.cov.config.html_dir)

# Produce xml report if wanted.
if 'xml' in self.cov_report:
total = self.cov.xml_report(ignore_errors=True)
total = self.cov.xml_report(ignore_errors=True, outfile=self.cov_report['xml'])
stream.write('Coverage XML written to file %s\n' % self.cov.config.xml_output)

# Report on any failed slaves.
Expand Down
46 changes: 39 additions & 7 deletions src/pytest_cov/plugin.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,44 @@
"""Coverage plugin for pytest."""
import os
import pytest
import argparse
from coverage.misc import CoverageException

from . import embed
from . import engine


class CoverageError(Exception):
"""Indicates that our coverage is too low"""


def validate_report(arg):
file_choices = ['annotate', 'html', 'xml']
term_choices = ['term', 'term-missing']
all_choices = term_choices + file_choices
values = arg.split(":", 1)
report_type = values[0]
if report_type not in all_choices + ['']:
msg = 'invalid choice: "{}" (choose from "{}")'.format(arg, all_choices)
raise argparse.ArgumentTypeError(msg)

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

if report_type not in file_choices:
msg = 'output specifier not supported for: "{}" (choose from "{}")'.format(arg,
file_choices)
raise argparse.ArgumentTypeError(msg)

return values


class StoreReport(argparse.Action):
def __call__(self, parser, namespace, values, option_string=None):
report_type, file = values
namespace.cov_report[report_type] = file


def pytest_addoption(parser):
"""Add options to control coverage."""

Expand All @@ -16,12 +48,12 @@ def pytest_addoption(parser):
nargs='?', const=True, dest='cov_source',
help='measure coverage for filesystem path '
'(multi-allowed)')
group.addoption('--cov-report', action='append', default=[],
metavar='type',
choices=['term', 'term-missing', 'annotate', 'html',
'xml', ''],
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 (multi-allowed). '
'annotate, html and xml may be be followed by ":DEST" '
'where DEST specifies the output location.')
group.addoption('--cov-config', action='store', default='.coveragerc',
metavar='path',
help='config file for coverage, default: .coveragerc')
Expand All @@ -45,8 +77,8 @@ def pytest_load_initial_conftests(early_config, parser, args):

if not ns.cov_report:
ns.cov_report = ['term']
elif ns.cov_report == ['']:
ns.cov_report = []
elif len(ns.cov_report) == 1 and '' in ns.cov_report:
ns.cov_report = {}

if ns.cov:
plugin = CovPlugin(ns, early_config.pluginmanager)
Expand Down
92 changes: 92 additions & 0 deletions tests/test_pytest_cov.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,8 @@ def test_fail():
SCRIPT2_RESULT = '3 * 100%'
CHILD_SCRIPT_RESULT = '[56] * 100%'
PARENT_SCRIPT_RESULT = '8 * 100%'
DEST_DIR = 'cov_dest'
REPORT_NAME = 'cov.xml'

xdist = pytest.mark.parametrize('opts', ['', '-n 1'], ids=['nodist', 'xdist'])

Expand Down Expand Up @@ -193,6 +195,96 @@ def test_annotate(testdir):
assert result.ret == 0


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

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

result.stdout.fnmatch_lines([
'*- coverage: platform *, python * -*',
'Coverage annotated source written to dir ' + DEST_DIR,
'*10 passed*',
])
dest_dir = testdir.tmpdir.join(DEST_DIR)
assert dest_dir.check(dir=True)
assert dest_dir.join(script.basename + ",cover").check()
assert result.ret == 0


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

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

result.stdout.fnmatch_lines([
'*- coverage: platform *, python * -*',
'Coverage HTML written to dir ' + DEST_DIR,
'*10 passed*',
])
dest_dir = testdir.tmpdir.join(DEST_DIR)
assert dest_dir.check(dir=True)
assert dest_dir.join("index.html").check()
assert result.ret == 0


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

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

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


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

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

# backport of argparse to py26 doesn't display ArgumentTypeError message
result.stderr.fnmatch_lines([
'*argument --cov-report: *',
] if tuple(sys.version_info[:2]) == (2, 6) else [
'*argument --cov-report: output specifier not supported for: "term:%s"*' % DEST_DIR,
])
assert result.ret != 0


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

result = testdir.runpytest('-v',
'--cov=%s' % script.dirpath(),
'--cov-report=term-missing:' + DEST_DIR,
script)

# backport of argparse to py26 doesn't display ArgumentTypeError message
result.stderr.fnmatch_lines([
'*argument --cov-report: *',
] if tuple(sys.version_info[:2]) == (2, 6) else [
'*argument --cov-report: output specifier not supported for: '
'"term-missing:%s"*' % DEST_DIR,
])
assert result.ret != 0


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

Expand Down

0 comments on commit 0696bc7

Please sign in to comment.