summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNed Batchelder <ned@nedbatchelder.com>2010-09-03 23:15:20 -0400
committerNed Batchelder <ned@nedbatchelder.com>2010-09-03 23:15:20 -0400
commit8153341439f1eddddad18ee6ca8ca10a88549129 (patch)
treed9e6c8a5d9c7e642157a2bf15e1d7a659969c843
parent9da5c35a04dd117dda5f868ab1057b2f0603708b (diff)
downloadpython-coveragepy-git-8153341439f1eddddad18ee6ca8ca10a88549129.tar.gz
Now completely unexecuted source files can be included in reporting. Specifying --source tells coverage.py where to search for files that haven't been executed.
-rw-r--r--CHANGES.txt4
-rw-r--r--coverage/control.py7
-rw-r--r--coverage/data.py4
-rw-r--r--doc/source.rst5
-rw-r--r--test/test_api.py5
-rw-r--r--test/test_data.py6
6 files changed, 29 insertions, 2 deletions
diff --git a/CHANGES.txt b/CHANGES.txt
index cca83310..62336f6b 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -6,6 +6,10 @@ Change history for Coverage.py
Version 3.4b2
-------------
+- Completely unexecuted files can now be included in coverage results, reported
+ as 0% covered. This only happens if the --source option is specified, since
+ coverage.py needs guidance about where to look for source files.
+
- Coverage percentages are now displayed uniformly across reporting methods. A
percentage is only reported as 0% or 100% if they are truly 0 or 100, and
are rounded otherwise. Fixes `issue 41` and issue 70`.
diff --git a/coverage/control.py b/coverage/control.py
index dd7d705b..28c8850f 100644
--- a/coverage/control.py
+++ b/coverage/control.py
@@ -9,6 +9,7 @@ from coverage.collector import Collector
from coverage.config import CoverageConfig
from coverage.data import CoverageData
from coverage.files import FileLocator, TreeMatcher, FnmatchMatcher
+from coverage.files import find_python_files
from coverage.html import HtmlReporter
from coverage.misc import CoverageException, bool_or_none
from coverage.results import Analysis, Numbers
@@ -303,6 +304,7 @@ class coverage(object):
else:
pkg_file = self._source_for_file(pkg_file)
pkg_file = self.file_locator.canonical_filename(pkg_file)
+ self.source.append(pkg_file)
self.source_match.add(pkg_file)
for pkg in found:
@@ -437,6 +439,11 @@ class coverage(object):
if not summary:
self._warn("No data was collected.")
+ # Find files that were never executed at all.
+ for src in self.source:
+ for py_file in find_python_files(src):
+ self.data.touch_file(py_file)
+
self._harvested = True
# Backward compatibility with version 1.
diff --git a/coverage/data.py b/coverage/data.py
index 6d49f710..30119d9c 100644
--- a/coverage/data.py
+++ b/coverage/data.py
@@ -207,6 +207,10 @@ class CoverageData(object):
for filename, arcs in arc_data.items():
self.arcs.setdefault(filename, {}).update(arcs)
+ def touch_file(self, filename):
+ """Ensure that `filename` appears in the data, empty if needed."""
+ self.lines.setdefault(filename, {})
+
def executed_files(self):
"""A list of all files that had been measured as executed."""
return list(self.lines.keys())
diff --git a/doc/source.rst b/doc/source.rst
index e39090a4..3f0a1566 100644
--- a/doc/source.rst
+++ b/doc/source.rst
@@ -28,7 +28,9 @@ all code, unless it is part of the Python standard library.
You can specify source to measure with the ``--source`` command-line switch,
or the ``[run] source`` configuration value. The value is a list of directories
or package names. If specified, only source inside these directories or
-packages will be measured.
+packages will be measured. Specifying the source option also enables
+coverage.py to report on unexecuted files, since it can search the source tree
+for files that haven't been measured at all.
You can further fine-tune coverage.py's attention with the ``--include`` and
``--omit`` switches (or ``[run] include`` and ``[run] omit`` configuration
@@ -72,4 +74,3 @@ reporting.
Note that these are ways of specifying files to measure. You can also exclude
individual source lines. See :ref:`excluding` for details.
-
diff --git a/test/test_api.py b/test/test_api.py
index aa6f42cd..5952dfe7 100644
--- a/test/test_api.py
+++ b/test/test_api.py
@@ -324,6 +324,8 @@ class SourceOmitIncludeTest(CoverageTest):
self.assertEqual(lines['p1b.py'], 3)
self.assertEqual(lines['p2a.py'], 3)
self.assertEqual(lines['p2b.py'], 3)
+ # Because there was no source= specified, we don't search for
+ # unexecuted files.
self.assert_('p1c.py' not in lines)
def test_source_package(self):
@@ -332,6 +334,8 @@ class SourceOmitIncludeTest(CoverageTest):
self.assertEqual(lines['p1b.py'], 3)
self.assert_('p2a.py' not in lines)
self.assert_('p2b.py' not in lines)
+ # Because source= was specified, we do search for unexecuted files.
+ self.assertEqual(lines['p1c.py'], 0)
def test_source_package_dotted(self):
lines = self.coverage_usepkgs_summary(source=["pkg1.p1b"])
@@ -339,6 +343,7 @@ class SourceOmitIncludeTest(CoverageTest):
self.assertEqual(lines['p1b.py'], 3)
self.assert_('p2a.py' not in lines)
self.assert_('p2b.py' not in lines)
+ self.assert_('p1c.py' not in lines)
def test_include(self):
lines = self.coverage_usepkgs_summary(include=["*/p1a.py"])
diff --git a/test/test_data.py b/test/test_data.py
index 83a5b8ae..fd7e4878 100644
--- a/test/test_data.py
+++ b/test/test_data.py
@@ -45,6 +45,12 @@ class DataTest(CoverageTest):
self.assert_summary(covdata, SUMMARY_1)
self.assert_executed_files(covdata, EXECED_FILES_1)
+ def test_touch_file(self):
+ covdata = CoverageData()
+ covdata.add_line_data(DATA_1)
+ covdata.touch_file('x.py')
+ self.assert_executed_files(covdata, EXECED_FILES_1 + ['x.py'])
+
def test_writing_and_reading(self):
covdata1 = CoverageData()
covdata1.add_line_data(DATA_1)