diff options
author | JP <jpellerin@gmail.com> | 2011-12-15 07:03:32 -0800 |
---|---|---|
committer | JP <jpellerin@gmail.com> | 2011-12-15 07:03:32 -0800 |
commit | 23688664f9cacff80c64dd066138b62b2ed46761 (patch) | |
tree | a04aac5495a6e7fd76f05e9f603bd1eb6a57cb51 | |
parent | 79efaa3d4cabff40e0194d2e6680a5070c6476e2 (diff) | |
parent | 6347cd149f5f5f9f06ade7d635dc52245fd50124 (diff) | |
download | nose-23688664f9cacff80c64dd066138b62b2ed46761.tar.gz |
Merge pull request #477 from cyberdelia/coverage
Remove support for old coverage module and rewrite coverage tests
-rw-r--r-- | functional_tests/doc_tests/test_coverage_html/coverage_html.rst | 57 | ||||
-rw-r--r-- | functional_tests/doc_tests/test_coverage_html/coverage_html.rst.py3.patch | 16 | ||||
-rw-r--r-- | functional_tests/doc_tests/test_coverage_html/coverage_html_fixtures.py | 26 | ||||
-rw-r--r-- | functional_tests/support/coverage/blah.py (renamed from functional_tests/doc_tests/test_coverage_html/support/blah.py) | 0 | ||||
-rw-r--r-- | functional_tests/support/coverage/tests/test_covered.py (renamed from functional_tests/doc_tests/test_coverage_html/support/tests/test_covered.py) | 0 | ||||
-rw-r--r-- | functional_tests/test_coverage_plugin.py | 37 | ||||
-rw-r--r-- | nose/plugins/cover.py | 153 |
7 files changed, 50 insertions, 239 deletions
diff --git a/functional_tests/doc_tests/test_coverage_html/coverage_html.rst b/functional_tests/doc_tests/test_coverage_html/coverage_html.rst deleted file mode 100644 index 95f9e8a..0000000 --- a/functional_tests/doc_tests/test_coverage_html/coverage_html.rst +++ /dev/null @@ -1,57 +0,0 @@ -Generating HTML Coverage with nose ----------------------------------- - -.. Note :: - - HTML coverage requires Ned Batchelder's `coverage.py`_ module. -.. - -Console coverage output is useful but terse. For a more browseable view of -code coverage, the coverage plugin supports basic HTML coverage output. - -.. hide this from the actual documentation: - >>> from nose.plugins.plugintest import run_buffered as run - >>> import os - >>> support = os.path.join(os.path.dirname(__file__), 'support') - >>> cover_html_dir = os.path.join(support, 'cover') - >>> cover_file = os.path.join(os.getcwd(), '.coverage') - >>> if os.path.exists(cover_file): - ... os.unlink(cover_file) - ... - - -The console coverage output is printed, as normal. - - >>> from nose.plugins.cover import Coverage - >>> cover_html_dir = os.path.join(support, 'cover') - >>> run(argv=[__file__, '-v', '--with-coverage', '--cover-package=blah', - ... '--cover-html', '--cover-html-dir=' + cover_html_dir, - ... support, ], - ... plugins=[Coverage()]) # doctest: +REPORT_NDIFF - test_covered.test_blah ... hi - ok - <BLANKLINE> - Name Stmts Miss Cover Missing - ------------------------------------- - blah 4 1 75% 6 - ---------------------------------------------------------------------- - Ran 1 test in ...s - <BLANKLINE> - OK - -The html coverage reports are saved to disk in the directory specified by the -``--cover-html-dir`` option. In that directory you'll find ``index.html`` -which links to a detailed coverage report for each module in the report. The -detail pages show the module source, colorized to indicated which lines are -covered and which are not. There is an example of this HTML output in the -`coverage.py`_ docs. - -.. hide this from the actual documentation: - >>> os.path.exists(cover_file) - True - >>> os.path.exists(os.path.join(cover_html_dir, 'index.html')) - True - >>> os.path.exists(os.path.join(cover_html_dir, 'blah.html')) - True - -.. _`coverage.py`: http://nedbatchelder.com/code/coverage/ diff --git a/functional_tests/doc_tests/test_coverage_html/coverage_html.rst.py3.patch b/functional_tests/doc_tests/test_coverage_html/coverage_html.rst.py3.patch deleted file mode 100644 index f325a01..0000000 --- a/functional_tests/doc_tests/test_coverage_html/coverage_html.rst.py3.patch +++ /dev/null @@ -1,16 +0,0 @@ ---- coverage_html.rst.orig 2010-08-31 23:13:33.000000000 -0700 -+++ coverage_html.rst 2010-08-31 23:14:25.000000000 -0700 -@@ -78,11 +78,11 @@ - </div> - <div class="coverage"> - <div class="cov"><span class="num"><pre>1</pre></span><pre>def dostuff():</pre></div> -- <div class="cov"><span class="num"><pre>2</pre></span><pre> print 'hi'</pre></div> -+ <div class="cov"><span class="num"><pre>2</pre></span><pre> print('hi')</pre></div> - <div class="skip"><span class="num"><pre>3</pre></span><pre></pre></div> - <div class="skip"><span class="num"><pre>4</pre></span><pre></pre></div> - <div class="cov"><span class="num"><pre>5</pre></span><pre>def notcov():</pre></div> -- <div class="nocov"><span class="num"><pre>6</pre></span><pre> print 'not covered'</pre></div> -+ <div class="nocov"><span class="num"><pre>6</pre></span><pre> print('not covered')</pre></div> - <div class="skip"><span class="num"><pre>7</pre></span><pre></pre></div> - </div> - </body> diff --git a/functional_tests/doc_tests/test_coverage_html/coverage_html_fixtures.py b/functional_tests/doc_tests/test_coverage_html/coverage_html_fixtures.py deleted file mode 100644 index 6829dc2..0000000 --- a/functional_tests/doc_tests/test_coverage_html/coverage_html_fixtures.py +++ /dev/null @@ -1,26 +0,0 @@ -import sys -import os -import shutil -from nose.plugins.skip import SkipTest -from nose.plugins.cover import Coverage -from nose.plugins.plugintest import munge_nose_output_for_doctest - -# This fixture is not reentrant because we have to cleanup the files that -# coverage produces once all tests have finished running. -_multiprocess_shared_ = True - -def setup_module(): - try: - import coverage - if 'active' in Coverage.status: - raise SkipTest("Coverage plugin is active. Skipping tests of " - "plugin itself.") - except ImportError: - raise SkipTest("coverage module not available") - -def teardown_module(): - # Clean up the files produced by coverage - cover_html_dir = os.path.join(os.path.dirname(__file__), 'support', 'cover') - if os.path.exists(cover_html_dir): - shutil.rmtree(cover_html_dir) - diff --git a/functional_tests/doc_tests/test_coverage_html/support/blah.py b/functional_tests/support/coverage/blah.py index ef6657c..ef6657c 100644 --- a/functional_tests/doc_tests/test_coverage_html/support/blah.py +++ b/functional_tests/support/coverage/blah.py diff --git a/functional_tests/doc_tests/test_coverage_html/support/tests/test_covered.py b/functional_tests/support/coverage/tests/test_covered.py index c669c5c..c669c5c 100644 --- a/functional_tests/doc_tests/test_coverage_html/support/tests/test_covered.py +++ b/functional_tests/support/coverage/tests/test_covered.py diff --git a/functional_tests/test_coverage_plugin.py b/functional_tests/test_coverage_plugin.py new file mode 100644 index 0000000..d1b2632 --- /dev/null +++ b/functional_tests/test_coverage_plugin.py @@ -0,0 +1,37 @@ +"""Test the coverage plugin.""" +import os +import unittest +import shutil + +from nose.plugins import PluginTester +from nose.plugins.cover import Coverage + +support = os.path.join(os.path.dirname(__file__), 'support') + + +class TestCoveragePlugin(PluginTester, unittest.TestCase): + activate = "--with-coverage" + args = ['-v', '--cover-package=blah', '--cover-html'] + plugins = [Coverage()] + suitepath = os.path.join(support, 'coverage') + + def setUp(self): + self.cover_file = os.path.join(os.getcwd(), '.coverage') + self.cover_html_dir = os.path.join(os.getcwd(), 'cover') + if os.path.exists(self.cover_file): + os.unlink(self.cover_file) + if os.path.exists(self.cover_html_dir): + shutil.rmtree(self.cover_html_dir) + super(TestCoveragePlugin, self).setUp() + + def runTest(self): + self.assertTrue("blah 4 1 75% 6" in self.output) + self.assertTrue("Ran 1 test in""" in self.output) + # Assert coverage html report exists + self.assertTrue(os.path.exists(os.path.join(self.cover_html_dir, + 'index.html'))) + # Assert coverage data is saved + self.assertTrue(os.path.exists(self.cover_file)) + +if __name__ == '__main__': + unittest.main() diff --git a/nose/plugins/cover.py b/nose/plugins/cover.py index a055c92..f91e12a 100644 --- a/nose/plugins/cover.py +++ b/nose/plugins/cover.py @@ -11,7 +11,6 @@ variable. .. _coverage: http://www.nedbatchelder.com/code/modules/coverage.html """ import logging -import os import re import sys from nose.plugins.base import Plugin @@ -19,36 +18,6 @@ from nose.util import src, tolist log = logging.getLogger(__name__) -COVERAGE_TEMPLATE = '''<html> -<head> -%(title)s -</head> -<body> -%(header)s -<style> -.coverage pre {float: left; margin: 0px 1em; border: none; - padding: 0px; } -.num pre { margin: 0px } -.nocov, .nocov pre {background-color: #faa} -.cov, .cov pre {background-color: #cfc} -div.coverage div { clear: both; height: 1.1em} -</style> -<div class="stats"> -%(stats)s -</div> -<div class="coverage"> -%(body)s -</div> -</body> -</html> -''' - -COVERAGE_STATS_TEMPLATE = '''Covered: %(covered)s lines<br/> -Missed: %(missed)s lines<br/> -Skipped %(skipped)s lines<br/> -Percent: %(percent)s %%<br/> -''' - class Coverage(Plugin): """ @@ -56,25 +25,16 @@ class Coverage(Plugin): """ coverTests = False coverPackages = None - _coverInstance = None + coverInstance = None + coverErase = False score = 200 status = {} - def coverInstance(self): - if not self._coverInstance: - import coverage - try: - self._coverInstance = coverage.coverage(branch=self.coverBranches) - except coverage.CoverageException: - self._coverInstance = coverage - return self._coverInstance - coverInstance = property(coverInstance) - def options(self, parser, env): """ Add options to command line. """ - Plugin.options(self, parser, env) + super(Coverage, self).options(parser, env) parser.add_option("--cover-package", action="append", default=env.get('NOSE_COVER_PACKAGE'), metavar="PACKAGE", @@ -123,7 +83,7 @@ class Coverage(Plugin): metavar="FILE", help="Produce XML coverage information in file") - def configure(self, options, config): + def configure(self, options, conf): """ Configure plugin. """ @@ -131,8 +91,8 @@ class Coverage(Plugin): self.status.pop('active') except KeyError: pass - Plugin.configure(self, options, config) - if config.worker: + super(Coverage, self).configure(options, conf) + if conf.worker: return if self.enabled: try: @@ -142,7 +102,7 @@ class Coverage(Plugin): "unable to import coverage module") self.enabled = False return - self.conf = config + self.conf = conf self.coverErase = options.cover_erase self.coverTests = options.cover_tests self.coverPackages = [] @@ -164,6 +124,8 @@ class Coverage(Plugin): log.debug('Will put XML coverage report in %s', self.coverXmlFile) if self.enabled: self.status['active'] = True + self.coverInstance = coverage.coverage(auto_data=False, + branch=self.coverBranches, data_suffix=None) def begin(self): """ @@ -173,8 +135,10 @@ class Coverage(Plugin): self.skipModules = sys.modules.keys()[:] if self.coverErase: log.debug("Clearing previously collected coverage statistics") + self.coverInstance.combine() self.coverInstance.erase() self.coverInstance.exclude('#pragma[: ]+[nN][oO] [cC][oO][vV][eE][rR]') + self.coverInstance.load() self.coverInstance.start() def report(self, stream): @@ -183,6 +147,7 @@ class Coverage(Plugin): """ log.debug("Coverage report") self.coverInstance.stop() + self.coverInstance.combine() self.coverInstance.save() modules = [module for name, module in sys.modules.items() @@ -191,103 +156,11 @@ class Coverage(Plugin): self.coverInstance.report(modules, file=stream) if self.coverHtmlDir: log.debug("Generating HTML coverage report") - if hasattr(self.coverInstance, 'html_report'): - self.coverInstance.html_report(modules, self.coverHtmlDir) - else: - self.report_html(modules) + self.coverInstance.html_report(modules, self.coverHtmlDir) if self.coverXmlFile: log.debug("Generating XML coverage report") self.coverInstance.xml_report(modules, self.coverXmlFile) - def report_html(self, modules): - if not os.path.exists(self.coverHtmlDir): - os.makedirs(self.coverHtmlDir) - files = {} - for m in modules: - if hasattr(m, '__name__') and hasattr(m, '__file__'): - files[m.__name__] = m.__file__ - self.coverInstance.annotate(files.values()) - global_stats = {'covered': 0, 'missed': 0, 'skipped': 0} - file_list = [] - for m, f in files.iteritems(): - if f.endswith('pyc'): - f = f[:-1] - coverfile = f + ',cover' - outfile, stats = self.htmlAnnotate(m, f, coverfile, - self.coverHtmlDir) - for field in ('covered', 'missed', 'skipped'): - global_stats[field] += stats[field] - file_list.append((stats['percent'], m, outfile, stats)) - os.unlink(coverfile) - file_list.sort() - global_stats['percent'] = self.computePercent( - global_stats['covered'], global_stats['missed']) - # Now write out an index file for the coverage HTML - index = open(os.path.join(self.coverHtmlDir, 'index.html'), 'w') - index.write('<html><head><title>Coverage Index</title></head>' - '<body><p>') - index.write(COVERAGE_STATS_TEMPLATE % global_stats) - index.write('<table><tr><td>File</td><td>Covered</td><td>Missed' - '</td><td>Skipped</td><td>Percent</td></tr>') - for junk, name, outfile, stats in file_list: - stats['a'] = '<a href="%s">%s</a>' % (outfile, name) - index.write('<tr><td>%(a)s</td><td>%(covered)s</td><td>' - '%(missed)s</td><td>%(skipped)s</td><td>' - '%(percent)s %%</td></tr>' % stats) - index.write('</table></p></html') - index.close() - - def htmlAnnotate(self, name, file, coverfile, outputDir): - log.debug('Name: %s file: %s' % (name, file, )) - rows = [] - data = open(coverfile, 'r').read().split('\n') - padding = len(str(len(data))) - stats = {'covered': 0, 'missed': 0, 'skipped': 0} - for lineno, line in enumerate(data): - lineno += 1 - if line: - status = line[0] - line = line[2:] - else: - status = '' - line = '' - lineno = (' ' * (padding - len(str(lineno)))) + str(lineno) - for old, new in (('&', '&'), ('<', '<'), ('>', '>'), - ('"', '"'), ): - line = line.replace(old, new) - if status == '!': - rows.append('<div class="nocov"><span class="num"><pre>' - '%s</pre></span><pre>%s</pre></div>' % (lineno, - line)) - stats['missed'] += 1 - elif status == '>': - rows.append('<div class="cov"><span class="num"><pre>%s</pre>' - '</span><pre>%s</pre></div>' % (lineno, line)) - stats['covered'] += 1 - else: - rows.append('<div class="skip"><span class="num"><pre>%s</pre>' - '</span><pre>%s</pre></div>' % (lineno, line)) - stats['skipped'] += 1 - stats['percent'] = self.computePercent(stats['covered'], - stats['missed']) - html = COVERAGE_TEMPLATE % {'title': '<title>%s</title>' % name, - 'header': name, - 'body': '\n'.join(rows), - 'stats': COVERAGE_STATS_TEMPLATE % stats, - } - outfilename = name + '.html' - outfile = open(os.path.join(outputDir, outfilename), 'w') - outfile.write(html) - outfile.close() - return outfilename, stats - - def computePercent(self, covered, missed): - if covered + missed == 0: - percent = 1 - else: - percent = covered / (covered + missed + 0.0) - return int(percent * 100) - def wantModuleCoverage(self, name, module): if not hasattr(module, '__file__'): log.debug("no coverage of %s: no __file__", name) |