diff options
author | rozza@x103086.gcapmedia.com <rozza@x103086.gcapmedia.com> | 2009-11-20 11:55:07 +0000 |
---|---|---|
committer | rozza@x103086.gcapmedia.com <rozza@x103086.gcapmedia.com> | 2009-11-20 11:55:07 +0000 |
commit | 91a9f43a8d8f372f7e7b84e40bf09dbad781045e (patch) | |
tree | fca2196f27376a8a874fb9b6feb4701ceafa9a51 /coverage | |
parent | e128c5d16c8ef7f83884e911a5e65d0d2ac1f191 (diff) | |
parent | e813a7992e9697a34cb08033862c90a22cb0050a (diff) | |
download | python-coveragepy-91a9f43a8d8f372f7e7b84e40bf09dbad781045e.tar.gz |
Merging in changes from http://bitbucket.org/ned/coveragepy/
Diffstat (limited to 'coverage')
-rw-r--r-- | coverage/codeunit.py | 27 | ||||
-rw-r--r-- | coverage/parser.py | 6 | ||||
-rw-r--r-- | coverage/runner.py | 138 | ||||
-rw-r--r-- | coverage/test_plugins/nose_coverage.py | 111 | ||||
-rw-r--r-- | coverage/test_plugins/pytest_coverage.py | 69 |
5 files changed, 339 insertions, 12 deletions
diff --git a/coverage/codeunit.py b/coverage/codeunit.py index e310705..28fa055 100644 --- a/coverage/codeunit.py +++ b/coverage/codeunit.py @@ -34,19 +34,26 @@ def code_unit_factory(morfs, file_locator, omit_prefixes=None): code_units = [CodeUnit(morf, file_locator) for morf in morfs] if omit_prefixes: - prefixes = [file_locator.abs_file(p) for p in omit_prefixes] - filtered = [] - for cu in code_units: - for prefix in prefixes: - if cu.filename.startswith(prefix): - break - else: - filtered.append(cu) - - code_units = filtered + code_units = omit_filter(omit_prefixes, code_units) return code_units +def omit_filter(omit_prefixes, code_units): + """ + The filtering method removing any unwanted code_units + + Refactored out so you can easily monkeypatch if needs be + """ + prefixes = [file_locator.abs_file(p) for p in omit_prefixes] + filtered = [] + for cu in code_units: + for prefix in prefixes: + if cu.filename.startswith(prefix): + break + else: + filtered.append(cu) + + return filtered class CodeUnit(object): """Code unit: a filename or module. diff --git a/coverage/parser.py b/coverage/parser.py index de0e0c7..01b38af 100644 --- a/coverage/parser.py +++ b/coverage/parser.py @@ -222,8 +222,10 @@ class CodeParser(object): # Class definitions have one extra exit, so remove one for each: for l in self.classdefs: - exit_counts[l] -= 1 - + # Ensure key is there - #pragma: no cover will mean its not + if l in exit_counts: + exit_counts[l] -= 1 + return exit_counts exit_counts = expensive(exit_counts) diff --git a/coverage/runner.py b/coverage/runner.py new file mode 100644 index 0000000..ba4861a --- /dev/null +++ b/coverage/runner.py @@ -0,0 +1,138 @@ +import optparse + +class CoverageTestWrapper(object): + """ + A Coverage Test Wrapper. + + 1) Setup the with the parsed options + 2) Call start() + 3) Run your tests + 4) Call finish() + 5) Improve your code coverage ;) + """ + + def __init__(self, options, _covpkg=None): + self.options = options + + # _covpkg is for dependency injection, so we can test this code. + if _covpkg: + self.covpkg = _covpkg + else: + import coverage + self.covpkg = coverage + + self.coverage = None + + def start(self): + # Set up coverage + self.coverage = self.covpkg.coverage( + data_suffix = bool(self.options.cover_parallel_mode), + cover_pylib = self.options.cover_pylib, + timid = self.options.cover_timid, + branch = self.options.cover_branch, + ) + + # Run the script. + self.coverage.start() + + def finish(self): + # end coverage and save the results + self.coverage.stop() + self.coverage.save() + + # Remaining actions are reporting, with some common self.options. + report_args = { + 'morfs': [], + 'ignore_errors': self.options.cover_ignore_errors, + } + + # Handle any omits + # Allow pointing to a file as well + try: + omit_file = open(self.options.cover_omit) + omit_prefixes = [line.strip() for line in omit_file.readlines()] + report_args['omit_prefixes'] = omit_prefixes + except: + omit = self.options.cover_omit.split(',') + report_args['omit_prefixes'] = omit + + if 'report' in self.options.cover_actions: + self.coverage.report( + show_missing=self.options.cover_show_missing, **report_args) + if 'annotate' in self.options.cover_actions: + self.coverage.annotate( + directory=self.options.cover_directory, **report_args) + if 'html' in self.options.cover_actions: + self.coverage.html_report( + directory=self.options.cover_directory, **report_args) + if 'xml' in self.options.cover_actions: + outfile = self.options.cover_outfile + if outfile == '-': + outfile = None + self.coverage.xml_report(outfile=outfile, **report_args) + + return + +class Options(object): + """A namespace class for individual options we'll build parsers from.""" + + action = optparse.Option('', + '--cover-action', action='append', default=None, + dest='cover_actions', type="choice", choices=['annotate', 'html', 'report', 'xml'], + help=""" + annotate Annotate source files with execution information. + html Create an HTML report. + report Report coverage stats on modules. + xml Create an XML report of coverage results. + """.strip()) + + branch = optparse.Option( + '--cover-branch', action='store_true', + help="Measure branch execution. HIGHLY EXPERIMENTAL!" + ) + directory = optparse.Option( + '--cover-directory', action='store', + metavar="DIR", + help="Write the output files to DIR." + ) + ignore_errors = optparse.Option( + '--cover-ignore-errors', action='store_true', + help="Ignore errors while reading source files." + ) + pylib = optparse.Option( + '--cover-pylib', action='store_true', + help="Measure coverage even inside the Python installed library, " + "which isn't done by default." + ) + show_missing = optparse.Option( + '--cover-show-missing', action='store_true', + help="Show line numbers of statements in each module that weren't " + "executed." + ) + omit = optparse.Option( + '--cover-omit', action='store', + metavar="PRE1,PRE2,...", + default='', + help="Omit files when their filename path starts with one of these " + "prefixes." + ) + output_xml = optparse.Option( + '--cover-outfile', action='store', + metavar="OUTFILE", + help="Write the XML report to this file. Defaults to 'coverage.xml'" + ) + parallel_mode = optparse.Option( + '--cover-parallel-mode', action='store_true', + help="Include the machine name and process id in the .coverage " + "data file name." + ) + timid = optparse.Option( + '--cover-timid', action='store_true', + help="Use a simpler but slower trace method. Try this if you get " + "seemingly impossible results!" + ) + append = optparse.Option( + '--cover-append', action='store_false', + help="Append coverage data to .coverage, otherwise it is started " + "clean with each run." + ) diff --git a/coverage/test_plugins/nose_coverage.py b/coverage/test_plugins/nose_coverage.py new file mode 100644 index 0000000..603ce43 --- /dev/null +++ b/coverage/test_plugins/nose_coverage.py @@ -0,0 +1,111 @@ +import logging +import unittest, os +from nose.plugins import Plugin, PluginTester + +import sys +import os +sys.path.insert(0, os.path.realpath(os.path.join(os.path.dirname(__file__), '../../'))) + +log = logging.getLogger(__name__) + + +class Coverage(Plugin): + """ + Activate a coverage report using Ned Batchelder's coverage module. + """ + + name = "coverage_new" + score = 1 + status = {} + + def options(self, parser, env): + """ + Add options to command line. + """ + + Plugin.options(self, parser, env) + + from coverage.runner import Options + # Loop the coverage options and append them to the plugin options + options = [a for a in dir(Options) if not a.startswith('_')] + for option in options: + opt = getattr(Options, option) + parser.add_option(opt) + + def configure(self, options, config): + """ + Configure plugin. + """ + try: + self.status.pop('active') + except KeyError: + pass + Plugin.configure(self, options, config) + if self.enabled: + try: + import coverage + except ImportError: + log.error("Coverage not available: " + "unable to import coverage module") + self.enabled = False + return + + self.config = config + self.status['active'] = True + self.options = options + + def begin(self): + """ + Begin recording coverage information. + """ + log.debug("Coverage begin") + # Load the runner and start it up + from coverage.runner import CoverageTestWrapper + self.coverage = CoverageTestWrapper(self.options) + self.coverage.start() + + def report(self, stream): + """ + Output code coverage report. + """ + log.debug("Coverage report") + stream.write("Processing Coverage...") + # finish up with coverage + self.coverage.finish() + + +# Monkey patch omit_filter to use regex patterns for file omits +def omit_filter(omit_prefixes, code_units): + import re + exclude_patterns = [re.compile(line.strip()) for line in omit_prefixes if line and not line.startswith('#')] + filtered = [] + for cu in code_units: + skip = False + for pattern in exclude_patterns: + if pattern.search(cu.filename): + skip = True + break + + if not skip: + filtered.append(cu) + return filtered + +try: + import coverage + coverage.codeunit.omit_filter = omit_filter +except: + pass + +class TestCoverage(PluginTester, unittest.TestCase): + activate = '--with-coverage_new' # enables the plugin + plugins = [Coverage()] + args = ['--cover-action=report'] + + def test_output(self): + assert "Processing Coverage..." in self.output, ( + "got: %s" % self.output) + def makeSuite(self): + class TC(unittest.TestCase): + def runTest(self): + raise ValueError("Coverage down") + return unittest.TestSuite([TC()])
\ No newline at end of file diff --git a/coverage/test_plugins/pytest_coverage.py b/coverage/test_plugins/pytest_coverage.py new file mode 100644 index 0000000..5b910e9 --- /dev/null +++ b/coverage/test_plugins/pytest_coverage.py @@ -0,0 +1,69 @@ +""" +Write and report coverage data with 'coverage.py'. +""" +import py + +coverage = py.test.importorskip("coverage") + +def pytest_configure(config): + # Load the runner and start it up + from coverage.runner import CoverageTestWrapper + + config.coverage = CoverageTestWrapper(config.option) + config.coverage.start() + +def pytest_terminal_summary(terminalreporter): + # Finished the tests start processing the coverage + config = terminalreporter.config + tw = terminalreporter._tw + tw.sep('-', 'coverage') + tw.line('Processing Coverage...') + + # finish up with coverage + config.coverage.finish() + +def pytest_addoption(parser): + """ + Get all the options from the coverage.runner and import them + """ + from coverage.runner import Options + + group = parser.getgroup('Coverage options') + # Loop the coverage options and append them to the plugin options + options = [a for a in dir(Options) if not a.startswith('_')] + for option in options: + opt = getattr(Options, option) + group._addoption_instance(opt, shortupper=True) + +# Monkey patch omit_filter to use regex patterns for file omits +def omit_filter(omit_prefixes, code_units): + import re + exclude_patterns = [re.compile(line.strip()) for line in omit_prefixes if line and not line.startswith('#')] + filtered = [] + for cu in code_units: + skip = False + for pattern in exclude_patterns: + if pattern.search(cu.filename): + skip = True + break + + if not skip: + filtered.append(cu) + return filtered + +coverage.codeunit.omit_filter = omit_filter + +def test_functional(testdir): + py.test.importorskip("coverage") + testdir.plugins.append("coverage") + testdir.makepyfile(""" + def f(): + x = 42 + def test_whatever(): + pass + """) + result = testdir.runpytest() + assert result.ret == 0 + assert result.stdout.fnmatch_lines([ + '*Processing Coverage*' + ])
\ No newline at end of file |