summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--coverage/control.py3
-rw-r--r--coverage/data.py62
-rw-r--r--test/coveragetest.py11
-rw-r--r--test/test_data.py31
4 files changed, 81 insertions, 26 deletions
diff --git a/coverage/control.py b/coverage/control.py
index 3c674b5d..f5cee740 100644
--- a/coverage/control.py
+++ b/coverage/control.py
@@ -12,6 +12,7 @@ from coverage.summary import SummaryReporter
class coverage:
def __init__(self):
from coverage.collector import Collector
+ from coverage import __version__
self.parallel_mode = False
self.exclude_re = ''
@@ -22,7 +23,7 @@ class coverage:
self.collector = Collector(self.should_trace)
- self.data = CoverageData()
+ self.data = CoverageData(collector="coverage.py v%s" % __version__)
# The default exclude pattern.
self.exclude('# *pragma[: ]*[nN][oO] *[cC][oO][vV][eE][rR]')
diff --git a/coverage/data.py b/coverage/data.py
index 19e00087..721f9c0f 100644
--- a/coverage/data.py
+++ b/coverage/data.py
@@ -3,6 +3,14 @@
import os, types
import cPickle as pickle
+# Data file format is a pickled dict, with these keys:
+#
+# collector: a string identifying the collecting software
+#
+# lines: a dict mapping filenames to lists of line numbers executed:
+# { 'file1': [17,23,45], 'file2': [1,2,3], ... }
+
+
class CoverageData:
"""Manages collected coverage data."""
# Name of the data file (unless environment variable is set).
@@ -11,7 +19,10 @@ class CoverageData:
# Environment variable naming the data file.
filename_env = "COVERAGE_FILE"
- def __init__(self):
+ def __init__(self, collector=None):
+
+ self.collector = collector
+
self.use_file = True
self.filename = None
self.suffix = None
@@ -25,7 +36,7 @@ class CoverageData:
# ...
# }
#
- self.executed = {}
+ self.lines = {}
def usefile(self, use_file=True):
"""Set whether or not to use a disk file for data."""
@@ -41,6 +52,7 @@ class CoverageData:
self.suffix = suffix
def _make_filename(self):
+ """Construct the filename that will be used for data file storage."""
assert self.use_file
if not self.filename:
self.filename = os.environ.get(
@@ -54,7 +66,7 @@ class CoverageData:
if self.use_file:
self._make_filename()
data = self._read_file(self.filename)
- self.executed = data
+ self.lines = data
def write(self):
"""Write the collected coverage data to a file."""
@@ -63,22 +75,35 @@ class CoverageData:
self.write_file(self.filename)
def erase(self):
+ """Erase the data, both in this object, and from its file storage."""
if self.use_file:
self._make_filename()
if self.filename and os.path.exists(self.filename):
os.remove(self.filename)
- self.executed = {}
+ self.lines = {}
def write_file(self, filename):
"""Write the coverage data to `filename`."""
- f = open(filename, 'wb')
+
+ # Create the file data.
+ data = {}
+
+ data['lines'] = dict(
+ [(f, list(linemap.keys())) for f, linemap in self.lines.items()]
+ )
+
+ if self.collector:
+ data['collector'] = self.collector
+
+ # Write the pickle to the file.
+ fdata = open(filename, 'wb')
try:
- pickle.dump(self.executed, f)
+ pickle.dump(data, fdata)
finally:
- f.close()
+ fdata.close()
def read_file(self, filename):
- self.executed = self._read_file(filename)
+ self.lines = self._read_file(filename)
def _read_file(self, filename):
""" Return the stored coverage data from the given file.
@@ -86,11 +111,16 @@ class CoverageData:
try:
fdata = open(filename, 'rb')
try:
- executed = pickle.load(fdata)
+ data = pickle.load(fdata)
finally:
fdata.close()
- if isinstance(executed, types.DictType):
- return executed
+ if isinstance(data, types.DictType):
+ # Unpack the 'lines' item.
+ lines = dict([
+ (f, dict([(l, True) for l in linenos]))
+ for f,linenos in data['lines'].items()
+ ])
+ return lines
else:
return {}
except:
@@ -107,7 +137,7 @@ class CoverageData:
full_path = os.path.join(data_dir, f)
new_data = self._read_file(full_path)
for filename, file_data in new_data.items():
- self.executed.setdefault(filename, {}).update(file_data)
+ self.lines.setdefault(filename, {}).update(file_data)
def add_line_data(self, data_points):
"""Add executed line data.
@@ -116,11 +146,11 @@ class CoverageData:
"""
for filename, lineno in data_points:
- self.executed.setdefault(filename, {})[lineno] = True
+ self.lines.setdefault(filename, {})[lineno] = True
def executed_files(self):
"""A list of all files that had been measured as executed."""
- return self.executed.keys()
+ return self.lines.keys()
def executed_lines(self, filename):
"""A map containing all the line numbers executed in `filename`.
@@ -128,7 +158,7 @@ class CoverageData:
If `filename` hasn't been collected at all (because it wasn't executed)
then return an empty map.
"""
- return self.executed.get(filename) or {}
+ return self.lines.get(filename) or {}
def summary(self):
"""Return a dict summarizing the coverage data.
@@ -138,6 +168,6 @@ class CoverageData:
"""
summ = {}
- for filename, lines in self.executed.items():
+ for filename, lines in self.lines.items():
summ[os.path.basename(filename)] = len(lines)
return summ
diff --git a/test/coveragetest.py b/test/coveragetest.py
index 90ef2f53..c96e32ca 100644
--- a/test/coveragetest.py
+++ b/test/coveragetest.py
@@ -3,6 +3,13 @@
import imp, os, random, shutil, sys, tempfile, textwrap, unittest
from cStringIO import StringIO
+try:
+ set()
+except NameError:
+ # pylint: disable-msg=W0622
+ # (Redefining built-in 'set')
+ from sets import Set as set
+
import coverage
@@ -197,3 +204,7 @@ class CoverageTest(unittest.TestCase):
output = stdouterr.read()
print output
return output
+
+ def assert_equal_sets(self, s1, s2):
+ """Assert that the two arguments are equal as sets."""
+ self.assertEqual(set(s1), set(s2))
diff --git a/test/test_data.py b/test/test_data.py
index 568fd380..39d5ecd3 100644
--- a/test/test_data.py
+++ b/test/test_data.py
@@ -1,32 +1,28 @@
"""Tests for coverage.data"""
+import cPickle as pickle
from coverage.data import CoverageData
from coveragetest import CoverageTest
-try:
- set()
-except NameError:
- # pylint: disable-msg=W0622
- # (Redefining built-in 'set')
- from sets import Set as set
DATA_1 = [ ('a.py',1), ('a.py',2), ('b.py',3) ]
SUMMARY_1 = { 'a.py':2, 'b.py':1 }
EXECED_FILES_1 = [ 'a.py', 'b.py' ]
+A_PY_LINES_1 = [1,2]
+B_PY_LINES_1 = [3]
DATA_2 = [ ('a.py',1), ('a.py',5), ('c.py',17) ]
SUMMARY_1_2 = { 'a.py':3, 'b.py':1, 'c.py':1 }
EXECED_FILES_1_2 = [ 'a.py', 'b.py', 'c.py' ]
+
class DataTest(CoverageTest):
def assert_summary(self, covdata, summary):
self.assertEqual(covdata.summary(), summary)
def assert_executed_files(self, covdata, execed):
- e1 = set(covdata.executed_files())
- e2 = set(execed)
- self.assertEqual(e1, e2)
+ self.assert_equal_sets(covdata.executed_files(), execed)
def test_reading_empty(self):
covdata = CoverageData()
@@ -74,3 +70,20 @@ class DataTest(CoverageTest):
covdata2 = CoverageData()
covdata2.read()
self.assert_summary(covdata2, {})
+
+ def test_file_format(self):
+ # Write with CoverageData, then read the pickle explicitly.
+ covdata = CoverageData()
+ covdata.add_line_data(DATA_1)
+ covdata.write()
+
+ fdata = open(".coverage", 'rb')
+ try:
+ data = pickle.load(fdata)
+ finally:
+ fdata.close()
+
+ lines = data['lines']
+ self.assert_equal_sets(lines.keys(), EXECED_FILES_1)
+ self.assert_equal_sets(lines['a.py'], A_PY_LINES_1)
+ self.assert_equal_sets(lines['b.py'], B_PY_LINES_1)