summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--TODO.txt6
-rw-r--r--allcoverage.cmd44
-rw-r--r--coverage/codeunit.py1
-rw-r--r--coverage/collector.py5
-rw-r--r--coverage/control.py10
-rw-r--r--test/coverage_coverage.py107
-rw-r--r--test/coveragetest.py13
-rw-r--r--test/test_api.py3
8 files changed, 141 insertions, 48 deletions
diff --git a/TODO.txt b/TODO.txt
index 6c5dc46f..2a528879 100644
--- a/TODO.txt
+++ b/TODO.txt
@@ -64,7 +64,9 @@ x Tricky swapping of collector like figleaf, pycov, et al. (Don't need to do
- Track callers of functions (ala std module trace)
- Method/Class/Module coverage reporting.
- .coverage files that can be kept separate, rather than accumulated.
-
+- test/coverage map: http://rbtcollins.wordpress.com/2009/09/16/back-from-hiatus/
+ - Similar to figleaf's sections.
+
* Convenience
@@ -97,6 +99,7 @@ x Why can't you specify execute (-x) and report (-r) in the same invocation?
- Some way to focus in on red and yellow
- Show only lines near highlights?
- Jump to next highlight?
+ - Cookie for changes to pyfile.html state.
+ Clickable column headers on the index page.
+ Syntax coloring in HTML report.
+ Dynamic effects in HTML report.
@@ -123,6 +126,7 @@ x Why can't you specify execute (-x) and report (-r) in the same invocation?
- Double function decorators: all decorator lines count as executable code.
- Document the .coverage file format.
+ HTML reporting.
+ - Much more detail about what's in the report.
- References between pages are off:
- They have <em> tags around them.
- They use #anchors that don't survive the px->html conversion.
diff --git a/allcoverage.cmd b/allcoverage.cmd
new file mode 100644
index 00000000..5da7e15f
--- /dev/null
+++ b/allcoverage.cmd
@@ -0,0 +1,44 @@
+@echo off
+make --quiet testdata
+
+call \ned\bin\switchpy 23
+python setup.py -q develop
+set COVERAGE_TEST_TRACER=c
+python test\coverage_coverage.py run %1 %2 %3 %4 %5 %6 %7 %8 %9
+del coverage\tracer.pyd
+set COVERAGE_TEST_TRACER=py
+python test\coverage_coverage.py run %1 %2 %3 %4 %5 %6 %7 %8 %9
+
+call \ned\bin\switchpy 24
+python setup.py -q develop
+set COVERAGE_TEST_TRACER=c
+python test\coverage_coverage.py run %1 %2 %3 %4 %5 %6 %7 %8 %9
+del coverage\tracer.pyd
+set COVERAGE_TEST_TRACER=py
+python test\coverage_coverage.py run %1 %2 %3 %4 %5 %6 %7 %8 %9
+
+call \ned\bin\switchpy 25
+python setup.py -q develop
+set COVERAGE_TEST_TRACER=c
+python test\coverage_coverage.py run %1 %2 %3 %4 %5 %6 %7 %8 %9
+del coverage\tracer.pyd
+set COVERAGE_TEST_TRACER=py
+python test\coverage_coverage.py run %1 %2 %3 %4 %5 %6 %7 %8 %9
+
+call \ned\bin\switchpy 26
+python setup.py -q develop
+set COVERAGE_TEST_TRACER=c
+python test\coverage_coverage.py run %1 %2 %3 %4 %5 %6 %7 %8 %9
+del coverage\tracer.pyd
+set COVERAGE_TEST_TRACER=py
+python test\coverage_coverage.py run %1 %2 %3 %4 %5 %6 %7 %8 %9
+
+call \ned\bin\switchpy 31
+python setup.py -q install
+set COVERAGE_TEST_TRACER=c
+python test\coverage_coverage.py run %1 %2 %3 %4 %5 %6 %7 %8 %9
+del \python31\lib\site-packages\coverage\tracer.pyd
+set COVERAGE_TEST_TRACER=py
+python test\coverage_coverage.py run %1 %2 %3 %4 %5 %6 %7 %8 %9
+
+python test\coverage_coverage.py report
diff --git a/coverage/codeunit.py b/coverage/codeunit.py
index e3107052..6d156da7 100644
--- a/coverage/codeunit.py
+++ b/coverage/codeunit.py
@@ -34,6 +34,7 @@ def code_unit_factory(morfs, file_locator, omit_prefixes=None):
code_units = [CodeUnit(morf, file_locator) for morf in morfs]
if omit_prefixes:
+ assert not isinstance(omit_prefixes, string_class) # common mistake
prefixes = [file_locator.abs_file(p) for p in omit_prefixes]
filtered = []
for cu in code_units:
diff --git a/coverage/collector.py b/coverage/collector.py
index 5bbd02e0..6ea419ee 100644
--- a/coverage/collector.py
+++ b/coverage/collector.py
@@ -148,6 +148,9 @@ class Collector(object):
# trace function.
self._trace_class = Tracer or PyTracer
+ def __repr__(self):
+ return "<Collector at 0x%x>" % id(self)
+
def tracer_name(self):
"""Return the class name of the tracer we're using."""
return self._trace_class.__name__
@@ -196,6 +199,7 @@ class Collector(object):
if self._collectors:
self._collectors[-1].pause()
self._collectors.append(self)
+ #print >>sys.stderr, "Started: %r" % self._collectors
# Install the tracer on this thread.
self._start_tracer()
# Install our installation tracer in threading, to jump start other
@@ -204,6 +208,7 @@ class Collector(object):
def stop(self):
"""Stop collecting trace information."""
+ #print >>sys.stderr, "Stopping: %r" % self._collectors
assert self._collectors
assert self._collectors[-1] is self
diff --git a/coverage/control.py b/coverage/control.py
index 674bb15e..7efc3492 100644
--- a/coverage/control.py
+++ b/coverage/control.py
@@ -1,6 +1,6 @@
"""Core control stuff for Coverage."""
-import os, socket
+import atexit, os, socket
from coverage.annotate import AnnotateReporter
from coverage.backward import string_class # pylint: disable-msg=W0622
@@ -56,7 +56,8 @@ class coverage(object):
self.cover_pylib = cover_pylib
self.auto_data = auto_data
-
+ self.atexit_registered = False
+
self.exclude_re = ""
self.exclude_list = []
@@ -168,8 +169,9 @@ class coverage(object):
if self.auto_data:
self.load()
# Save coverage data when Python exits.
- import atexit
- atexit.register(self.save)
+ if not self.atexit_registered:
+ atexit.register(self.save)
+ self.atexit_registered = True
self.collector.start()
def stop(self):
diff --git a/test/coverage_coverage.py b/test/coverage_coverage.py
index e1e7674f..a1cb13fc 100644
--- a/test/coverage_coverage.py
+++ b/test/coverage_coverage.py
@@ -1,45 +1,78 @@
-"""Coverage-test Coverage itself."""
+"""Coverage-test Coverage.py itself."""
-import coverage
import os, shutil, sys
+import nose
HTML_DIR = "htmlcov"
-if os.path.exists(HTML_DIR):
- shutil.rmtree(HTML_DIR)
-
-cov = coverage.coverage(branch=True)
-# Cheap trick: the coverage code itself is excluded from measurement, but if
-# we clobber the cover_prefix in the coverage object, we can defeat the
-# self-detection.
-cov.cover_prefix = "Please measure coverage.py!"
-cov.erase()
-cov.start()
-
-# Re-import coverage to get it coverage tested! I don't understand all the
-# mechanics here, but if I don't carry over the imported modules (in covmods),
-# then things go haywire (os == None eventually).
-covmods = {}
-covdir = os.path.split(coverage.__file__)
-for name, mod in sys.modules.items():
- if name.startswith('coverage'):
- if hasattr(mod, '__file__') and mod.__file__.startswith(covdir):
- covmods[name] = mod
- del sys.modules[name]
-import coverage # don't warn about re-import: pylint: disable-msg=W0404
-sys.modules.update(covmods)
-
-# Run nosetests, with the arguments from our command line.
-import nose
-nose.run(sys.argv[1:])
+def run_tests_with_coverage():
+ import coverage
+
+ tracer = os.environ.get('COVERAGE_TEST_TRACER', 'c')
+ version = "%s%s" % sys.version_info[:2]
+ suffix = ".%s_%s" % (version, tracer)
+
+ cov = coverage.coverage(branch=True, data_suffix=suffix)
+ # Cheap trick: the coverage code itself is excluded from measurement, but
+ # if we clobber the cover_prefix in the coverage object, we can defeat the
+ # self-detection.
+ cov.cover_prefix = "Please measure coverage.py!"
+ cov.erase()
+ cov.start()
+
+ # Re-import coverage to get it coverage tested! I don't understand all the
+ # mechanics here, but if I don't carry over the imported modules (in
+ # covmods), then things go haywire (os == None, eventually).
+ covmods = {}
+ covdir = os.path.split(coverage.__file__)[0]
+ # We have to make a list since we'll be deleting in the loop.
+ modules = list(sys.modules.items())
+ for name, mod in modules:
+ if name.startswith('coverage'):
+ if hasattr(mod, '__file__') and mod.__file__.startswith(covdir):
+ covmods[name] = mod
+ del sys.modules[name]
+ import coverage # don't warn about re-import: pylint: disable-msg=W0404
+ sys.modules.update(covmods)
+
+ # Run nosetests, with the arguments from our command line.
+ print(":: Running nosetests %s" % " ".join(sys.argv[1:]))
+ nose.run()
+
+ cov.stop()
+ print(":: Saving .coverage%s" % suffix)
+ cov.save()
+
+def report_on_combined_files():
+
+ if os.path.exists(HTML_DIR):
+ shutil.rmtree(HTML_DIR)
-cov.stop()
-cov.save()
+ print(":: Writing HTML report to %s/index.html" % HTML_DIR)
+ import coverage
+ cov = coverage.coverage()
+ cov.combine()
+ cov.save()
+ cov.clear_exclude()
+ cov.exclude("#pragma: no cover")
+ cov.exclude("def __repr__")
+ cov.exclude("if __name__ == .__main__.:")
+ cov.exclude("raise AssertionError")
+
+ cov.html_report(directory=HTML_DIR, ignore_errors=True, omit_prefixes=["mock"])
-cov.clear_exclude()
-cov.exclude("#pragma: no cover")
-cov.exclude("def __repr__")
-cov.exclude("if __name__ == .__main__.:")
-cov.exclude("raise AssertionError")
-cov.html_report(directory=HTML_DIR, ignore_errors=True)
+try:
+ cmd = sys.argv[1]
+except IndexError:
+ cmd = ''
+
+if cmd == 'run':
+ # Ugly hack: nose.run reads sys.argv directly, so here I delete my command
+ # argument so that sys.argv is left as just nose arguments.
+ del sys.argv[1]
+ run_tests_with_coverage()
+elif cmd == 'report':
+ report_on_combined_files()
+else:
+ print("Need 'run' or 'report'")
diff --git a/test/coveragetest.py b/test/coveragetest.py
index 1fb04721..073dc39f 100644
--- a/test/coveragetest.py
+++ b/test/coveragetest.py
@@ -168,12 +168,13 @@ class CoverageTest(TestCase):
cov.exclude(exc)
cov.start()
- # Import the python file, executing it.
- mod = self.import_module(modname)
-
- # Stop Coverage.
- cov.stop()
-
+ try:
+ # Import the python file, executing it.
+ mod = self.import_module(modname)
+ finally:
+ # Stop Coverage.
+ cov.stop()
+
# Clean up our side effects
del sys.modules[modname]
diff --git a/test/test_api.py b/test/test_api.py
index 932606fd..7308cdc6 100644
--- a/test/test_api.py
+++ b/test/test_api.py
@@ -8,6 +8,9 @@ from coverage.backward import StringIO
sys.path.insert(0, os.path.split(__file__)[0]) # Force relative import for Py3k
from coveragetest import CoverageTest
+# This file uses the singleton module interface. Prevent it from writing
+# .coverage files at exit.
+coverage.use_cache(0)
class ApiTest(CoverageTest):
"""Api-oriented tests for Coverage."""