summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--coverage/collector.py52
-rw-r--r--coverage/control.py10
-rw-r--r--coverage/data.py63
-rw-r--r--tests/test_arcs.py2
-rw-r--r--tests/test_data.py17
5 files changed, 66 insertions, 78 deletions
diff --git a/coverage/collector.py b/coverage/collector.py
index 57c35605..eec8703e 100644
--- a/coverage/collector.py
+++ b/coverage/collector.py
@@ -3,6 +3,8 @@
import os, sys
from coverage import env
+from coverage.backward import iitems
+from coverage.files import abs_file
from coverage.misc import CoverageException
from coverage.pytracer import PyTracer
@@ -20,7 +22,7 @@ except ImportError:
# exception here causes all sorts of other noise in unittest.
sys.stderr.write(
"*** COVERAGE_TEST_TRACER is 'c' but can't import CTracer!\n"
- )
+ )
sys.exit(1)
CTracer = None
@@ -46,7 +48,8 @@ class Collector(object):
# the top, and resumed when they become the top again.
_collectors = []
- def __init__(self,
+ def __init__(
+ self,
should_trace, check_include, timid, branch, warn, concurrency,
):
"""Create a collector.
@@ -289,45 +292,20 @@ class Collector(object):
else:
self._start_tracer()
- def get_line_data(self):
- """Return the line data collected.
+ def save_data(self, covdata):
+ """Save the collected data to a `CoverageData`.
- Data is { filename: { lineno: None, ...}, ...}
+ Also resets the collector.
"""
- if self.branch:
- # If we were measuring branches, then we have to re-build the dict
- # to show line data. We'll use the first lines of all the arcs,
- # if they are actual lines. We don't need the second lines, because
- # the second lines will also be first lines, sometimes to exits.
- line_data = {}
- for f, arcs in self.data.items():
- line_data[f] = dict(
- (l1, None) for l1, _ in arcs.keys() if l1 > 0
- )
- return line_data
- else:
- return self.data
-
- def get_arc_data(self):
- """Return the arc data collected.
-
- Data is { filename: { (l1, l2): None, ...}, ...}
+ def abs_file_dict(d):
+ """Return a dict like d, but with keys modified by `abs_file`."""
+ return dict((abs_file(k), v) for k, v in iitems(d))
- Note that no data is collected or returned if the Collector wasn't
- created with `branch` true.
-
- """
if self.branch:
- return self.data
+ covdata.add_arcs(abs_file_dict(self.data))
else:
- return {}
-
- def get_plugin_data(self):
- """Return the mapping of source files to plugins.
+ covdata.add_lines(abs_file_dict(self.data))
+ covdata.add_plugins(abs_file_dict(self.plugin_data))
- Returns:
- dict: { filename: plugin_name, ... }
-
- """
- return self.plugin_data
+ self.reset()
diff --git a/coverage/control.py b/coverage/control.py
index 7c14e1b0..3f6f5aca 100644
--- a/coverage/control.py
+++ b/coverage/control.py
@@ -744,15 +744,7 @@ class Coverage(object):
if not self._measured:
return
- def abs_file_dict(d):
- """Return a dict like d, but with keys modified by `abs_file`."""
- return dict((abs_file(k), v) for k,v in iitems(d))
-
- # TODO: seems like this parallel structure is getting kinda old...
- self.data.add_lines(abs_file_dict(self.collector.get_line_data()))
- self.data.add_arcs(abs_file_dict(self.collector.get_arc_data()))
- self.data.add_plugins(abs_file_dict(self.collector.get_plugin_data()))
- self.collector.reset()
+ self.collector.save_data(self.data)
# If there are still entries in the source_pkgs list, then we never
# encountered those packages.
diff --git a/coverage/data.py b/coverage/data.py
index db205811..adacaecc 100644
--- a/coverage/data.py
+++ b/coverage/data.py
@@ -8,7 +8,7 @@ import socket
from coverage.backward import iitems, pickle
from coverage.debug import _TEST_NAME_FILE
from coverage.files import PathAliases
-from coverage.misc import file_be_gone
+from coverage.misc import CoverageException, file_be_gone
class CoverageData(object):
@@ -18,12 +18,12 @@ class CoverageData(object):
* collector: a string identifying the collecting software
- * lines: a dict mapping filenames to sorted lists of line numbers
+ * lines: a dict mapping filenames to lists of line numbers
executed::
{ 'file1': [17,23,45], 'file2': [1,2,3], ... }
- * arcs: a dict mapping filenames to sorted lists of line number pairs::
+ * arcs: a dict mapping filenames to lists of line number pairs::
{ 'file1': [(17,23), (17,25), (25,26)], ... }
@@ -31,6 +31,11 @@ class CoverageData(object):
{ 'file1': "django.coverage", ... }
+ Only one of `lines` or `arcs` will be present: with branch coverage, data
+ is stored as arcs. Without branch coverage, it is stored as lines. The
+ line data is easily recovered from the arcs: it is all the first elements
+ of the pairs that are greater than zero.
+
"""
def __init__(self, collector=None, debug=None):
@@ -82,7 +87,12 @@ class CoverageData(object):
def lines(self, filename):
"""Get the list of lines executed for a file."""
- return list((self._lines.get(filename) or {}).keys())
+ if self._arcs:
+ arcs = self._arcs.get(filename) or {}
+ return [s for s, __ in arcs if s > 0]
+ else:
+ lines = self._lines.get(filename) or {}
+ return list(lines)
def arcs(self, filename):
"""Get the list of arcs executed for a file."""
@@ -107,30 +117,29 @@ class CoverageData(object):
Should only be used on an empty CoverageData object.
"""
- try:
- data = pickle.load(file_obj)
- if isinstance(data, dict):
- # Unpack the 'lines' item.
- self._lines = dict([
- (f, dict.fromkeys(linenos, None))
- for f, linenos in iitems(data.get('lines', {}))
- ])
- # Unpack the 'arcs' item.
- self._arcs = dict([
- (f, dict.fromkeys(arcpairs, None))
- for f, arcpairs in iitems(data.get('arcs', {}))
- ])
- self._plugins = data.get('plugins', {})
- except Exception:
- # TODO: this used to handle file-doesnt-exist problems. Do we still need it?
- pass
+ data = pickle.load(file_obj)
+
+ # Unpack the 'lines' item.
+ self._lines = dict([
+ (f, dict.fromkeys(linenos, None))
+ for f, linenos in iitems(data.get('lines', {}))
+ ])
+ # Unpack the 'arcs' item.
+ self._arcs = dict([
+ (f, dict.fromkeys(arcpairs, None))
+ for f, arcpairs in iitems(data.get('arcs', {}))
+ ])
+ self._plugins = data.get('plugins', {})
def read_file(self, filename):
"""Read the coverage data from `filename`."""
if self._debug and self._debug.should('dataio'):
self._debug.write("Reading data from %r" % (filename,))
- with open(filename, "rb") as f:
- self.read(f)
+ try:
+ with open(filename, "rb") as f:
+ self.read(f)
+ except Exception as exc:
+ raise CoverageException("Couldn't read data from '%s': %s" % (filename, exc))
def write(self, file_obj):
"""Write the coverage data to `file_obj`."""
@@ -202,11 +211,11 @@ class CoverageData(object):
def touch_file(self, filename):
"""Ensure that `filename` appears in the data, empty if needed."""
- self._lines.setdefault(filename, {})
+ (self._arcs or self._lines).setdefault(filename, {})
def measured_files(self):
"""A list of all files that had been measured."""
- return list(self._lines.keys())
+ return list(self._arcs or self._lines)
def add_to_hash(self, filename, hasher):
"""Contribute `filename`'s data to the Md5Hash `hasher`."""
@@ -231,8 +240,8 @@ class CoverageData(object):
filename_fn = lambda f: f
else:
filename_fn = os.path.basename
- for filename, lines in iitems(self._lines):
- summ[filename_fn(filename)] = len(lines)
+ for filename in self.measured_files():
+ summ[filename_fn(filename)] = len(self.lines(filename))
return summ
def __nonzero__(self):
diff --git a/tests/test_arcs.py b/tests/test_arcs.py
index 7e8541a4..4e0ef397 100644
--- a/tests/test_arcs.py
+++ b/tests/test_arcs.py
@@ -762,4 +762,4 @@ class LineDataTest(CoverageTest):
data = cov.get_data()
fun1_lines = data.lines(abs_file("fun1.py"))
- self.assertEqual(fun1_lines, [1, 2, 5])
+ self.assertCountEqual(fun1_lines, [1, 2, 5])
diff --git a/tests/test_data.py b/tests/test_data.py
index cd9fbf35..734c3423 100644
--- a/tests/test_data.py
+++ b/tests/test_data.py
@@ -41,6 +41,9 @@ ARC_DATA_3 = {
}
X_PY_ARCS_3 = [(-1, 1), (1, 2), (2, 3), (3, -1)]
Y_PY_ARCS_3 = [(-1, 17), (17, 23), (23, -1)]
+SUMMARY_3 = {'x.py': 3, 'y.py': 2}
+MEASURED_FILES_3 = ['x.py', 'y.py']
+X_PY_LINES_3 = [1, 2, 3]
class DataTestHelpers(CoverageTest):
@@ -74,11 +77,20 @@ class DataTest(DataTestHelpers, CoverageTest):
covdata.add_arcs(ARC_DATA_3)
self.assertTrue(covdata)
- def test_adding_data(self):
+ def test_adding_lines(self):
covdata = CoverageData()
covdata.add_lines(DATA_1)
self.assert_line_counts(covdata, SUMMARY_1)
self.assert_measured_files(covdata, MEASURED_FILES_1)
+ self.assertCountEqual(covdata.lines("a.py"), A_PY_LINES_1)
+
+ def test_adding_arcs(self):
+ covdata = CoverageData()
+ covdata.add_arcs(ARC_DATA_3)
+ self.assert_line_counts(covdata, SUMMARY_3)
+ self.assert_measured_files(covdata, MEASURED_FILES_3)
+ self.assertCountEqual(covdata.lines("x.py"), X_PY_LINES_3)
+ self.assertCountEqual(covdata.arcs("x.py"), X_PY_ARCS_3)
def test_touch_file(self):
covdata = CoverageData()
@@ -97,9 +109,6 @@ class DataFilesTest(DataTestHelpers, CoverageTest):
self.data_files = CoverageDataFiles()
def test_reading_empty(self):
- # Make sure there is no .coverage data file here.
- if os.path.exists(".coverage"):
- os.remove(".coverage")
covdata = CoverageData()
self.data_files.read(covdata)
self.assert_line_counts(covdata, {})