summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNed Batchelder <nedbat@gmail.com>2015-07-21 21:06:05 -0400
committerNed Batchelder <nedbat@gmail.com>2015-07-21 21:06:05 -0400
commitcf43af31d35ba527e778267c14e51c56c9c3a773 (patch)
tree75d7a4c751862f64f79474d41815ef11d224486a
parent130f0dcdff98a1f947784f6989d1984b73b28335 (diff)
parenta591430903ed9108c8cb50369be0d9d9c1a0b200 (diff)
downloadpython-coveragepy-cf43af31d35ba527e778267c14e51c56c9c3a773.tar.gz
Merged in twexler/coverage.py (pull request #58)
Don't use SourceForge anymore for Cobertura DTD
-rw-r--r--CHANGES.txt9
-rw-r--r--Makefile6
-rw-r--r--coverage/backunittest.py11
-rw-r--r--coverage/backward.py6
-rw-r--r--coverage/collector.py6
-rw-r--r--coverage/control.py4
-rw-r--r--coverage/data.py315
-rw-r--r--doc/api.rst2
-rw-r--r--doc/conf.py38
-rw-r--r--tests/test_cmdline.py4
-rw-r--r--tests/test_data.py146
-rw-r--r--tests/test_testing.py5
12 files changed, 297 insertions, 255 deletions
diff --git a/CHANGES.txt b/CHANGES.txt
index 5a598d8..3b9b9d2 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -6,6 +6,10 @@ Change history for Coverage.py
Latest
------
+- The data storage has been completely revamped. The data file is now JSON
+ instead of a pickle, closing `issue 236`_. The `CoverageData` class is now
+ a public supported API to the data file.
+
- All the reporting functions now behave the same if no data had been
collected, exiting with a status code of 1. Fixed ``fail_under`` to be
applied even when the report is empty. Thanks, Ionel Cristian Mărieș.
@@ -22,10 +26,11 @@ Latest
``Coverage.get_data``, and returns the ``CoverageData`` containing the
collected data.
-.. 41 issues closed in 4.0 so far
-
+.. _issue 236: https://bitbucket.org/ned/coveragepy/issues/236/pickles-are-bad-and-you-should-feel-bad
.. _issue 380: https://bitbucket.org/ned/coveragepy/issues/380/code-executed-by-exec-excluded-from
+.. 41 issues closed in 4.0 below here
+
Version 4.0a6 --- 21 June 2015
------------------------------
diff --git a/Makefile b/Makefile
index d141f3b..8e7299d 100644
--- a/Makefile
+++ b/Makefile
@@ -88,17 +88,17 @@ WEBHOME = ~/web/stellated/pages/code/coverage
docreqs:
pip install -r doc/requirements.txt
-px: docreqs
+px:
$(SPHINXBUILD) -b px $(SPHINXOPTS) doc/_build/px
rm doc/_build/px/search.px
python doc/_ext/px_cleaner.py doc/_build/px/*.px
-dochtml: docreqs
+dochtml:
$(SPHINXBUILD) -b html $(SPHINXOPTS) doc/_build/html
@echo
@echo "Build finished. The HTML pages are in doc/_build/html."
-docspell: docreqs
+docspell:
$(SPHINXBUILD) -b spelling $(SPHINXOPTS) doc/_spell
publish: px
diff --git a/coverage/backunittest.py b/coverage/backunittest.py
index 95b6fcc..5aff043 100644
--- a/coverage/backunittest.py
+++ b/coverage/backunittest.py
@@ -22,10 +22,13 @@ class TestCase(unittest.TestCase):
"""
# pylint: disable=missing-docstring
- if not unittest_has('assertCountEqual'):
- def assertCountEqual(self, s1, s2):
- """Assert these have the same elements, regardless of order."""
- self.assertEqual(set(s1), set(s2))
+ # Many Pythons have this method defined. But PyPy3 has a bug with it
+ # somehow (https://bitbucket.org/pypy/pypy/issues/2092), so always use our
+ # own implementation that works everywhere, at least for the ways we're
+ # calling it.
+ def assertCountEqual(self, s1, s2):
+ """Assert these have the same elements, regardless of order."""
+ self.assertEqual(sorted(s1), sorted(s2))
if not unittest_has('assertRaisesRegex'):
def assertRaisesRegex(self, *args, **kwargs):
diff --git a/coverage/backward.py b/coverage/backward.py
index 58d9cfe..46c70fb 100644
--- a/coverage/backward.py
+++ b/coverage/backward.py
@@ -28,12 +28,6 @@ try:
except NameError:
unicode_class = str
-# Where do pickles come from?
-try:
- import cPickle as pickle
-except ImportError:
- import pickle
-
# range or xrange?
try:
range = xrange
diff --git a/coverage/collector.py b/coverage/collector.py
index eec8703..8ea0427 100644
--- a/coverage/collector.py
+++ b/coverage/collector.py
@@ -303,9 +303,9 @@ class Collector(object):
return dict((abs_file(k), v) for k, v in iitems(d))
if self.branch:
- covdata.add_arcs(abs_file_dict(self.data))
+ covdata.set_arcs(abs_file_dict(self.data))
else:
- covdata.add_lines(abs_file_dict(self.data))
- covdata.add_plugins(abs_file_dict(self.plugin_data))
+ covdata.set_lines(abs_file_dict(self.data))
+ covdata.set_plugins(abs_file_dict(self.plugin_data))
self.reset()
diff --git a/coverage/control.py b/coverage/control.py
index 3f6f5ac..e1931a5 100644
--- a/coverage/control.py
+++ b/coverage/control.py
@@ -268,9 +268,7 @@ class Coverage(object):
# Create the data file. We do this at construction time so that the
# data file will be written into the directory where the process
# started rather than wherever the process eventually chdir'd to.
- self.data = CoverageData(
- collector="coverage v%s" % __version__,
- )
+ self.data = CoverageData(debug=self.debug)
self.data_files = CoverageDataFiles(basename=self.config.data_file)
# The dirs for files considered "installed with the interpreter".
diff --git a/coverage/data.py b/coverage/data.py
index 9a8a397..68b0212 100644
--- a/coverage/data.py
+++ b/coverage/data.py
@@ -1,11 +1,12 @@
"""Coverage data for Coverage."""
import glob
+import json
import os
import random
import socket
-from coverage.backward import iitems, pickle
+from coverage.backward import iitems
from coverage.debug import _TEST_NAME_FILE
from coverage.files import PathAliases
from coverage.misc import CoverageException, file_be_gone
@@ -14,76 +15,116 @@ from coverage.misc import CoverageException, file_be_gone
class CoverageData(object):
"""Manages collected coverage data, including file storage.
- The data file format is a pickled dict, with these keys:
+ This class is the public supported API to coverage.py's data.
- * collector: a string identifying the collecting software
+ .. note::
- * lines: a dict mapping filenames to lists of line numbers
- executed::
+ The file format is not documented or guaranteed. It will change in
+ the future, in possibly complicated ways. Use this API to avoid
+ disruption.
- { 'file1': [17,23,45], 'file2': [1,2,3], ... }
+ There are three kinds of data that can be collected:
- * arcs: a dict mapping filenames to lists of line number pairs::
+ * **lines**: the line numbers of source lines that were executed.
+ These are always available.
- { 'file1': [(17,23), (17,25), (25,26)], ... }
+ * **arcs**: pairs of source and destination line numbers for transitions
+ between source lines. These are only available if branch coverage was
+ used.
- * plugins: a dict mapping filenames to plugin names::
+ * **plugin names**: the module names of the plugin that handled each file
+ in the data.
- { '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.
+ To read a coverage.py data file, use :meth:`read_file`, or :meth:`read` if
+ you have an already-opened file. You can then access the line, arc, or
+ plugin data with :meth:`lines`, :meth:`arcs`, or :meth:`plugin_name`.
+
+ The :meth:`has_arcs` method indicates whether arc data is available. You
+ can get a list of the files in the data with :meth:`measured_files`.
+ A summary of the line data is available from :meth:`line_counts`. As with
+ most Python containers, you can determine if there is any data at all by
+ using this object as a boolean value.
+
+
+ Most data files will be created by coverage.py itself, but you can use
+ methods here to create data files if you like. The :meth:`set_lines`,
+ :meth:`set_arcs`, and :meth:`set_plugins` methods add data, in ways that
+ are convenient for coverage.py. To add a file without any measured data,
+ use :meth:`touch_file`.
+
+ You write to a named file with :meth:`write_file`, or to an already opened
+ file with :meth:`write`.
+
+ You can clear the data in memory with :meth:`erase`. Two data collections
+ can be combined by using :meth:`update` on one `CoverageData`, passing it
+ the other.
"""
- def __init__(self, collector=None, debug=None):
+ # The data file format is JSON, with these keys:
+ #
+ # * 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 lists of line number pairs::
+ #
+ # { 'file1': [[17,23], [17,25], [25,26]], ... }
+ #
+ # * plugins: a dict mapping filenames to plugin names::
+ #
+ # { '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, debug=None):
"""Create a CoverageData.
- `collector` is a string describing the coverage measurement software.
-
`debug` is a `DebugControl` object for writing debug messages.
"""
- self._collector = collector
self._debug = debug
# A map from canonical Python source file name to a dictionary in
# which there's an entry for each line number that has been
# executed:
#
- # {
- # 'filename1.py': { 12: None, 47: None, ... },
- # ...
- # }
+ # { 'filename1.py': [12, 47, 1001], ... }
#
self._lines = {}
# A map from canonical Python source file name to a dictionary with an
# entry for each pair of line numbers forming an arc:
#
- # {
- # 'filename1.py': { (12,14): None, (47,48): None, ... },
- # ...
- # }
+ # { 'filename1.py': [(12,14), (47,48), ... ], ... }
#
self._arcs = {}
# A map from canonical source file name to a plugin module name:
#
- # {
- # 'filename1.py': 'django.coverage',
- # ...
- # }
+ # { 'filename1.py': 'django.coverage', ... }
#
self._plugins = {}
- def erase(self):
- """Erase the data in this object."""
- self._lines = {}
- self._arcs = {}
- self._plugins = {}
+ ##
+ ## Reading data
+ ##
+
+ def has_arcs(self):
+ """Does this data have arcs?
+
+ Arc data is only available if branch coverage was used during
+ collection.
+
+ Returns a boolean.
+
+ """
+ return self._has_arcs()
def lines(self, filename):
"""Get the list of lines executed for a file.
@@ -97,7 +138,7 @@ class CoverageData(object):
return [s for s, __ in self._arcs[filename] if s > 0]
else:
if filename in self._lines:
- return list(self._lines[filename])
+ return self._lines[filename]
return None
def arcs(self, filename):
@@ -108,7 +149,7 @@ class CoverageData(object):
"""
if filename in self._arcs:
- return list((self._arcs[filename]).keys())
+ return self._arcs[filename]
return None
def plugin_name(self, filename):
@@ -130,32 +171,56 @@ class CoverageData(object):
return self._plugins.get(filename, "")
return None
+ def measured_files(self):
+ """A list of all files that had been measured."""
+ return list(self._arcs or self._lines)
+
+ def line_counts(self, fullpath=False):
+ """Return a dict summarizing the line coverage data.
+
+ Keys are based on the filenames, and values are the number of executed
+ lines. If `fullpath` is true, then the keys are the full pathnames of
+ the files, otherwise they are the basenames of the files.
+
+ Returns:
+ dict mapping filenames to counts of lines.
+
+ """
+ summ = {}
+ if fullpath:
+ filename_fn = lambda f: f
+ else:
+ filename_fn = os.path.basename
+ for filename in self.measured_files():
+ summ[filename_fn(filename)] = len(self.lines(filename))
+ return summ
+
+ def __nonzero__(self):
+ return bool(self._lines) or bool(self._arcs)
+
+ __bool__ = __nonzero__
+
def read(self, file_obj):
"""Read the coverage data from the given file object.
Should only be used on an empty CoverageData object.
"""
- 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', {}))
- ])
+ data = json.load(file_obj)
+
+ self._lines = data.get('lines', {})
+ self._arcs = dict(
+ (fname, [tuple(pair) for pair in arcs])
+ for fname, arcs in iitems(data.get('arcs', {}))
+ )
self._plugins = data.get('plugins', {})
def read_file(self, filename):
- """Read the coverage data from `filename`."""
+ """Read the coverage data from `filename` into this object."""
if self._debug and self._debug.should('dataio'):
self._debug.write("Reading data from %r" % (filename,))
try:
- with open(filename, "rb") as f:
+ with open(filename, "r") as f:
self.read(f)
except Exception as exc:
raise CoverageException(
@@ -164,57 +229,43 @@ class CoverageData(object):
)
)
- def write(self, file_obj):
- """Write the coverage data to `file_obj`."""
-
- # Create the file data.
- file_data = {}
-
- if self._arcs:
- file_data['arcs'] = dict((f, list(amap.keys())) for f, amap in iitems(self._arcs))
- else:
- file_data['lines'] = dict((f, list(lmap.keys())) for f, lmap in iitems(self._lines))
-
- if self._collector:
- file_data['collector'] = self._collector
+ ##
+ ## Writing data
+ ##
- file_data['plugins'] = self._plugins
-
- # Write the pickle to the file.
- pickle.dump(file_data, file_obj, 2)
+ def set_lines(self, line_data):
+ """Add executed line data.
- def write_file(self, filename):
- """Write the coverage data to `filename`."""
- if self._debug and self._debug.should('dataio'):
- self._debug.write("Writing data to %r" % (filename,))
- with open(filename, 'wb') as fdata:
- self.write(fdata)
+ `line_data` is a dictionary mapping filenames to dictionaries::
- def add_lines(self, line_data):
- """Add executed line data.
+ { filename: { lineno: None, ... }, ...}
- `line_data` is { filename: { lineno: None, ... }, ...}
+ Do not call this more than once, it will not update data, it only sets
+ data.
"""
- if self.has_arcs():
+ if self._has_arcs():
raise CoverageException("Can't add lines to existing arc data")
for filename, linenos in iitems(line_data):
- self._lines.setdefault(filename, {}).update(linenos)
+ self._lines[filename] = list(linenos)
- def add_arcs(self, arc_data):
+ def set_arcs(self, arc_data):
"""Add measured arc data.
`arc_data` is { filename: { (l1,l2): None, ... }, ...}
+ Do not call this more than once, it will not update data, it only sets
+ data.
+
"""
- if self.has_lines():
+ if self._has_lines():
raise CoverageException("Can't add arcs to existing line data")
for filename, arcs in iitems(arc_data):
- self._arcs.setdefault(filename, {}).update(arcs)
+ self._arcs[filename] = list(arcs)
- def add_plugins(self, plugin_data):
+ def set_plugins(self, plugin_data):
"""Add per-file plugin information.
`plugin_data` is { filename: plugin_name, ... }
@@ -235,6 +286,39 @@ class CoverageData(object):
)
self._plugins[filename] = plugin_name
+ def touch_file(self, filename):
+ """Ensure that `filename` appears in the data, empty if needed."""
+ (self._arcs or self._lines).setdefault(filename, [])
+
+ def write(self, file_obj):
+ """Write the coverage data to `file_obj`."""
+
+ # Create the file data.
+ file_data = {}
+
+ if self._arcs:
+ file_data['arcs'] = self._arcs
+ else:
+ file_data['lines'] = self._lines
+
+ file_data['plugins'] = self._plugins
+
+ # Write the data to the file.
+ json.dump(file_data, file_obj)
+
+ def write_file(self, filename):
+ """Write the coverage data to `filename`."""
+ if self._debug and self._debug.should('dataio'):
+ self._debug.write("Writing data to %r" % (filename,))
+ with open(filename, 'w') as fdata:
+ self.write(fdata)
+
+ def erase(self):
+ """Erase the data in this object."""
+ self._lines = {}
+ self._arcs = {}
+ self._plugins = {}
+
def update(self, other_data, aliases=None):
"""Update this data with data from another `CoverageData`.
@@ -242,9 +326,9 @@ class CoverageData(object):
re-map paths to match the local machine's.
"""
- if self.has_lines() and other_data.has_arcs():
+ if self._has_lines() and other_data._has_arcs():
raise CoverageException("Can't combine arc data with line data")
- if self.has_arcs() and other_data.has_lines():
+ if self._has_arcs() and other_data._has_lines():
raise CoverageException("Can't combine line data with arc data")
aliases = aliases or PathAliases()
@@ -266,22 +350,26 @@ class CoverageData(object):
)
# _lines: merge dicts.
- for filename, file_data in iitems(other_data._lines):
+ for filename, file_lines in iitems(other_data._lines):
filename = aliases.map(filename)
- self._lines.setdefault(filename, {}).update(file_data)
+ if filename in self._lines:
+ lines = set(self._lines[filename])
+ lines.update(file_lines)
+ file_lines = list(lines)
+ self._lines[filename] = file_lines
# _arcs: merge dicts.
- for filename, file_data in iitems(other_data._arcs):
+ for filename, file_arcs in iitems(other_data._arcs):
filename = aliases.map(filename)
- self._arcs.setdefault(filename, {}).update(file_data)
-
- def touch_file(self, filename):
- """Ensure that `filename` appears in the data, empty if needed."""
- (self._arcs or self._lines).setdefault(filename, {})
+ if filename in self._arcs:
+ arcs = set(self._arcs[filename])
+ arcs.update(file_arcs)
+ file_arcs = list(arcs)
+ self._arcs[filename] = file_arcs
- def measured_files(self):
- """A list of all files that had been measured."""
- return list(self._arcs or self._lines)
+ ##
+ ## Miscellaneous
+ ##
def add_to_hash(self, filename, hasher):
"""Contribute `filename`'s data to the `hasher`.
@@ -298,37 +386,16 @@ class CoverageData(object):
hasher.update(sorted(self.lines(filename)))
hasher.update(self.plugin_name(filename))
- def line_counts(self, fullpath=False):
- """Return a dict summarizing the line coverage data.
-
- Keys are based on the filenames, and values are the number of executed
- lines. If `fullpath` is true, then the keys are the full pathnames of
- the files, otherwise they are the basenames of the files.
-
- Returns:
- dict mapping filenames to counts of lines.
+ ##
+ ## Internal
+ ##
- """
- summ = {}
- if fullpath:
- filename_fn = lambda f: f
- else:
- filename_fn = os.path.basename
- for filename in self.measured_files():
- summ[filename_fn(filename)] = len(self.lines(filename))
- return summ
-
- def __nonzero__(self):
- return bool(self._lines) or bool(self._arcs)
-
- __bool__ = __nonzero__
-
- def has_lines(self):
- """Does this data have lines?"""
+ def _has_lines(self):
+ """Do we have data in self._lines?"""
return bool(self._lines)
- def has_arcs(self):
- """Does this data have arcs?"""
+ def _has_arcs(self):
+ """Do we have data in self._arcs?"""
return bool(self._arcs)
diff --git a/doc/api.rst b/doc/api.rst
index 268c613..684bc2d 100644
--- a/doc/api.rst
+++ b/doc/api.rst
@@ -38,6 +38,7 @@ The Coverage class
.. autoclass:: Coverage
:members:
:exclude-members: use_cache
+ :special-members: __init__
The CoverageData class
@@ -45,6 +46,7 @@ The CoverageData class
.. autoclass:: CoverageData
:members:
+ :special-members: __init__
Starting coverage automatically
diff --git a/doc/conf.py b/doc/conf.py
index 07e47a8..9d55e9d 100644
--- a/doc/conf.py
+++ b/doc/conf.py
@@ -101,7 +101,7 @@ pygments_style = 'sphinx'
# The theme to use for HTML and HTML Help pages. Major themes that come with
# Sphinx are currently 'default' and 'sphinxdoc'.
-html_theme = 'default'
+html_theme = 'classic'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
@@ -110,7 +110,7 @@ html_theme = 'default'
#html_style = "neds.css"
-html_add_permalinks = False
+#html_add_permalinks = ""
# Add any paths that contain custom themes here, relative to this directory.
html_theme_path = ['_templates']
@@ -179,42 +179,10 @@ htmlhelp_basename = 'coveragepydoc'
spelling_word_list_filename = 'dict.txt'
spelling_show_suggestions = False
-# -- Options for LaTeX output --------------------------------------------------
-
-# The paper size ('letter' or 'a4').
-#latex_paper_size = 'letter'
-
-# The font size ('10pt', '11pt' or '12pt').
-#latex_font_size = '10pt'
-
-# Grouping the document tree into LaTeX files. List of tuples
-# (source start file, target name, title, author, documentclass [howto/manual]).
-latex_documents = [
- ('index', 'coveragepy.tex', u'coverage.py Documentation',
- u'Ned Batchelder', 'manual'),
-]
-
-# The name of an image file (relative to this directory) to place at the top of
-# the title page.
-#latex_logo = None
-
-# For "manual" documents, if this is true, then toplevel headings are parts,
-# not chapters.
-#latex_use_parts = False
-
-# Additional stuff for the LaTeX preamble.
-#latex_preamble = ''
-
-# Documents to append as an appendix to all manuals.
-#latex_appendices = []
-
-# If false, no module index is generated.
-#latex_use_modindex = True
-
# When auto-doc'ing a class, write the class' docstring and the __init__ docstring
# into the class docs.
-autoclass_content = "both"
+autoclass_content = "class"
diff --git a/tests/test_cmdline.py b/tests/test_cmdline.py
index 36a16fb..5e4379e 100644
--- a/tests/test_cmdline.py
+++ b/tests/test_cmdline.py
@@ -522,11 +522,11 @@ class CmdLineWithFilesTest(BaseCmdLineTest):
def test_debug_data(self):
data = CoverageData()
- data.add_lines({
+ data.set_lines({
"file1.py": dict.fromkeys(range(1, 18)),
"file2.py": dict.fromkeys(range(1, 24)),
})
- data.add_plugins({"file1.py": "a_plugin"})
+ data.set_plugins({"file1.py": "a_plugin"})
data_files = CoverageDataFiles()
data_files.write(data)
diff --git a/tests/test_data.py b/tests/test_data.py
index 7a02adc..ea33a58 100644
--- a/tests/test_data.py
+++ b/tests/test_data.py
@@ -1,12 +1,12 @@
"""Tests for coverage.data"""
import glob
+import json
import os
import os.path
import mock
-from coverage.backward import pickle
from coverage.data import CoverageData, CoverageDataFiles
from coverage.files import PathAliases, canonical_filename
from coverage.misc import CoverageException
@@ -88,65 +88,67 @@ class CoverageDataTest(DataTestHelpers, CoverageTest):
def test_line_data_is_true(self):
covdata = CoverageData()
- covdata.add_lines(LINES_1)
+ covdata.set_lines(LINES_1)
self.assertTrue(covdata)
def test_arc_data_is_true(self):
covdata = CoverageData()
- covdata.add_arcs(ARCS_3)
+ covdata.set_arcs(ARCS_3)
self.assertTrue(covdata)
def test_adding_lines(self):
covdata = CoverageData()
- covdata.add_lines(LINES_1)
+ covdata.set_lines(LINES_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)
+ self.assertFalse(covdata.has_arcs())
def test_adding_arcs(self):
covdata = CoverageData()
- covdata.add_arcs(ARCS_3)
+ covdata.set_arcs(ARCS_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)
self.assertCountEqual(covdata.lines("y.py"), Y_PY_LINES_3)
self.assertCountEqual(covdata.arcs("y.py"), Y_PY_ARCS_3)
+ self.assertTrue(covdata.has_arcs())
- def test_cant_add_arcs_to_lines(self):
+ def test_cant_set_arcs_with_lines(self):
covdata = CoverageData()
- covdata.add_lines(LINES_1)
+ covdata.set_lines(LINES_1)
with self.assertRaisesRegex(CoverageException, "Can't add arcs to existing line data"):
- covdata.add_arcs(ARCS_3)
+ covdata.set_arcs(ARCS_3)
- def test_cant_add_lines_to_arcs(self):
+ def test_cant_set_lines_with_arcs(self):
covdata = CoverageData()
- covdata.add_arcs(ARCS_3)
+ covdata.set_arcs(ARCS_3)
with self.assertRaisesRegex(CoverageException, "Can't add lines to existing arc data"):
- covdata.add_lines(LINES_1)
+ covdata.set_lines(LINES_1)
def test_touch_file_with_lines(self):
covdata = CoverageData()
- covdata.add_lines(LINES_1)
+ covdata.set_lines(LINES_1)
covdata.touch_file('zzz.py')
self.assert_measured_files(covdata, MEASURED_FILES_1 + ['zzz.py'])
def test_touch_file_with_arcs(self):
covdata = CoverageData()
- covdata.add_arcs(ARCS_3)
+ covdata.set_arcs(ARCS_3)
covdata.touch_file('zzz.py')
self.assert_measured_files(covdata, MEASURED_FILES_3 + ['zzz.py'])
def test_no_lines_vs_unmeasured_file(self):
covdata = CoverageData()
- covdata.add_lines(LINES_1)
+ covdata.set_lines(LINES_1)
covdata.touch_file('zzz.py')
self.assertEqual(covdata.lines('zzz.py'), [])
self.assertIsNone(covdata.lines('no_such_file.py'))
def test_no_arcs_vs_unmeasured_file(self):
covdata = CoverageData()
- covdata.add_arcs(ARCS_3)
+ covdata.set_arcs(ARCS_3)
covdata.touch_file('zzz.py')
self.assertEqual(covdata.lines('zzz.py'), [])
self.assertIsNone(covdata.lines('no_such_file.py'))
@@ -155,12 +157,12 @@ class CoverageDataTest(DataTestHelpers, CoverageTest):
def test_plugin_name(self):
covdata = CoverageData()
- covdata.add_lines({
+ covdata.set_lines({
"p1.foo": dict.fromkeys([1, 2, 3]),
"p2.html": dict.fromkeys([10, 11, 12]),
"main.py": dict.fromkeys([20]),
})
- covdata.add_plugins({"p1.foo": "p1.plugin", "p2.html": "p2.plugin"})
+ covdata.set_plugins({"p1.foo": "p1.plugin", "p2.html": "p2.plugin"})
self.assertEqual(covdata.plugin_name("p1.foo"), "p1.plugin")
self.assertEqual(covdata.plugin_name("main.py"), "")
self.assertIsNone(covdata.plugin_name("p3.not_here"))
@@ -169,27 +171,27 @@ class CoverageDataTest(DataTestHelpers, CoverageTest):
covdata = CoverageData()
msg = "Can't add plugin data for unmeasured file 'p1.foo'"
with self.assertRaisesRegex(CoverageException, msg):
- covdata.add_plugins({"p1.foo": "p1.plugin"})
+ covdata.set_plugins({"p1.foo": "p1.plugin"})
- covdata.add_lines({"p2.html": dict.fromkeys([10, 11, 12])})
+ covdata.set_lines({"p2.html": dict.fromkeys([10, 11, 12])})
with self.assertRaisesRegex(CoverageException, msg):
- covdata.add_plugins({"p1.foo": "p1.plugin"})
+ covdata.set_plugins({"p1.foo": "p1.plugin"})
def test_cant_change_plugin_name(self):
covdata = CoverageData()
- covdata.add_lines({"p1.foo": dict.fromkeys([1, 2, 3])})
- covdata.add_plugins({"p1.foo": "p1.plugin"})
+ covdata.set_lines({"p1.foo": dict.fromkeys([1, 2, 3])})
+ covdata.set_plugins({"p1.foo": "p1.plugin"})
msg = "Conflicting plugin name for 'p1.foo': 'p1.plugin' vs 'p1.plugin.foo'"
with self.assertRaisesRegex(CoverageException, msg):
- covdata.add_plugins({"p1.foo": "p1.plugin.foo"})
+ covdata.set_plugins({"p1.foo": "p1.plugin.foo"})
def test_update_lines(self):
covdata1 = CoverageData()
- covdata1.add_lines(LINES_1)
+ covdata1.set_lines(LINES_1)
covdata2 = CoverageData()
- covdata2.add_lines(LINES_2)
+ covdata2.set_lines(LINES_2)
covdata3 = CoverageData()
covdata3.update(covdata1)
@@ -200,10 +202,10 @@ class CoverageDataTest(DataTestHelpers, CoverageTest):
def test_update_arcs(self):
covdata1 = CoverageData()
- covdata1.add_arcs(ARCS_3)
+ covdata1.set_arcs(ARCS_3)
covdata2 = CoverageData()
- covdata2.add_arcs(ARCS_4)
+ covdata2.set_arcs(ARCS_4)
covdata3 = CoverageData()
covdata3.update(covdata1)
@@ -214,10 +216,10 @@ class CoverageDataTest(DataTestHelpers, CoverageTest):
def test_update_cant_mix_lines_and_arcs(self):
covdata1 = CoverageData()
- covdata1.add_lines(LINES_1)
+ covdata1.set_lines(LINES_1)
covdata2 = CoverageData()
- covdata2.add_arcs(ARCS_3)
+ covdata2.set_arcs(ARCS_3)
with self.assertRaisesRegex(CoverageException, "Can't combine arc data with line data"):
covdata1.update(covdata2)
@@ -227,24 +229,24 @@ class CoverageDataTest(DataTestHelpers, CoverageTest):
def test_update_plugins(self):
covdata1 = CoverageData()
- covdata1.add_lines({
+ covdata1.set_lines({
"p1.html": dict.fromkeys([1, 2, 3, 4]),
"p2.html": dict.fromkeys([5, 6, 7]),
"main.py": dict.fromkeys([10, 11, 12]),
})
- covdata1.add_plugins({
+ covdata1.set_plugins({
"p1.html": "html.plugin",
"p2.html": "html.plugin2",
})
covdata2 = CoverageData()
- covdata2.add_lines({
+ covdata2.set_lines({
"p1.html": dict.fromkeys([3, 4, 5, 6]),
"p2.html": dict.fromkeys([7, 8, 9]),
"p3.foo": dict.fromkeys([1000, 1001]),
"main.py": dict.fromkeys([10, 11, 12]),
})
- covdata2.add_plugins({
+ covdata2.set_plugins({
"p1.html": "html.plugin",
"p2.html": "html.plugin2",
"p3.foo": "foo_plugin",
@@ -260,12 +262,12 @@ class CoverageDataTest(DataTestHelpers, CoverageTest):
def test_update_conflicting_plugins(self):
covdata1 = CoverageData()
- covdata1.add_lines({"p1.html": dict.fromkeys([1, 2, 3])})
- covdata1.add_plugins({"p1.html": "html.plugin"})
+ covdata1.set_lines({"p1.html": dict.fromkeys([1, 2, 3])})
+ covdata1.set_plugins({"p1.html": "html.plugin"})
covdata2 = CoverageData()
- covdata2.add_lines({"p1.html": dict.fromkeys([1, 2, 3])})
- covdata2.add_plugins({"p1.html": "html.other_plugin"})
+ covdata2.set_lines({"p1.html": dict.fromkeys([1, 2, 3])})
+ covdata2.set_plugins({"p1.html": "html.other_plugin"})
msg = "Conflicting plugin name for 'p1.html': 'html.plugin' vs 'html.other_plugin'"
with self.assertRaisesRegex(CoverageException, msg):
@@ -277,11 +279,11 @@ class CoverageDataTest(DataTestHelpers, CoverageTest):
def test_update_plugin_vs_no_plugin(self):
covdata1 = CoverageData()
- covdata1.add_lines({"p1.html": dict.fromkeys([1, 2, 3])})
- covdata1.add_plugins({"p1.html": "html.plugin"})
+ covdata1.set_lines({"p1.html": dict.fromkeys([1, 2, 3])})
+ covdata1.set_plugins({"p1.html": "html.plugin"})
covdata2 = CoverageData()
- covdata2.add_lines({"p1.html": dict.fromkeys([1, 2, 3])})
+ covdata2.set_lines({"p1.html": dict.fromkeys([1, 2, 3])})
msg = "Conflicting plugin name for 'p1.html': 'html.plugin' vs ''"
with self.assertRaisesRegex(CoverageException, msg):
@@ -293,7 +295,7 @@ class CoverageDataTest(DataTestHelpers, CoverageTest):
def test_add_to_hash_with_lines(self):
covdata = CoverageData()
- covdata.add_lines(LINES_1)
+ covdata.set_lines(LINES_1)
hasher = mock.Mock()
covdata.add_to_hash("a.py", hasher)
self.assertEqual(hasher.method_calls, [
@@ -303,8 +305,8 @@ class CoverageDataTest(DataTestHelpers, CoverageTest):
def test_add_to_hash_with_arcs(self):
covdata = CoverageData()
- covdata.add_arcs(ARCS_3)
- covdata.add_plugins({"y.py": "hologram_plugin"})
+ covdata.set_arcs(ARCS_3)
+ covdata.set_plugins({"y.py": "hologram_plugin"})
hasher = mock.Mock()
covdata.add_to_hash("y.py", hasher)
self.assertEqual(hasher.method_calls, [
@@ -317,8 +319,8 @@ class CoverageDataTestInTempDir(DataTestHelpers, CoverageTest):
"""Tests of CoverageData that need a temp dir to make files."""
def test_read_write_lines(self):
- covdata1 = CoverageData(collector="coverage tests")
- covdata1.add_lines(LINES_1)
+ covdata1 = CoverageData()
+ covdata1.set_lines(LINES_1)
covdata1.write_file("lines.dat")
covdata2 = CoverageData()
@@ -328,8 +330,8 @@ class CoverageDataTestInTempDir(DataTestHelpers, CoverageTest):
self.assertCountEqual(covdata2.lines("a.py"), A_PY_LINES_1)
def test_read_write_arcs(self):
- covdata1 = CoverageData(collector="coverage tests")
- covdata1.add_arcs(ARCS_3)
+ covdata1 = CoverageData()
+ covdata1.set_arcs(ARCS_3)
covdata1.write_file("arcs.dat")
covdata2 = CoverageData()
@@ -377,7 +379,7 @@ class CoverageDataFilesTest(DataTestHelpers, CoverageTest):
def test_writing_and_reading(self):
covdata1 = CoverageData()
- covdata1.add_lines(LINES_1)
+ covdata1.set_lines(LINES_1)
self.data_files.write(covdata1)
covdata2 = CoverageData()
@@ -389,7 +391,7 @@ class CoverageDataFilesTest(DataTestHelpers, CoverageTest):
# writing files.
debug = DebugControlString(options=["dataio"])
covdata1 = CoverageData(debug=debug)
- covdata1.add_lines(LINES_1)
+ covdata1.set_lines(LINES_1)
self.data_files.write(covdata1)
covdata2 = CoverageData(debug=debug)
@@ -407,7 +409,7 @@ class CoverageDataFilesTest(DataTestHelpers, CoverageTest):
# output.
debug = DebugControlString(options=[])
covdata1 = CoverageData(debug=debug)
- covdata1.add_lines(LINES_1)
+ covdata1.set_lines(LINES_1)
self.data_files.write(covdata1)
covdata2 = CoverageData(debug=debug)
@@ -419,7 +421,7 @@ class CoverageDataFilesTest(DataTestHelpers, CoverageTest):
def test_explicit_suffix(self):
self.assert_doesnt_exist(".coverage.SUFFIX")
covdata = CoverageData()
- covdata.add_lines(LINES_1)
+ covdata.set_lines(LINES_1)
self.data_files.write(covdata, suffix='SUFFIX')
self.assert_exists(".coverage.SUFFIX")
self.assert_doesnt_exist(".coverage")
@@ -429,7 +431,7 @@ class CoverageDataFilesTest(DataTestHelpers, CoverageTest):
# suffix=True will make a randomly named data file.
covdata1 = CoverageData()
- covdata1.add_lines(LINES_1)
+ covdata1.set_lines(LINES_1)
self.data_files.write(covdata1, suffix=True)
self.assert_doesnt_exist(".coverage")
data_files1 = glob.glob(".coverage.*")
@@ -437,7 +439,7 @@ class CoverageDataFilesTest(DataTestHelpers, CoverageTest):
# Another suffix=True will choose a different name.
covdata2 = CoverageData()
- covdata2.add_lines(LINES_1)
+ covdata2.set_lines(LINES_1)
self.data_files.write(covdata2, suffix=True)
self.assert_doesnt_exist(".coverage")
data_files2 = glob.glob(".coverage.*")
@@ -451,13 +453,13 @@ class CoverageDataFilesTest(DataTestHelpers, CoverageTest):
self.assert_doesnt_exist(".coverage.2")
covdata1 = CoverageData()
- covdata1.add_lines(LINES_1)
+ covdata1.set_lines(LINES_1)
self.data_files.write(covdata1, suffix='1')
self.assert_exists(".coverage.1")
self.assert_doesnt_exist(".coverage.2")
covdata2 = CoverageData()
- covdata2.add_lines(LINES_2)
+ covdata2.set_lines(LINES_2)
self.data_files.write(covdata2, suffix='2')
self.assert_exists(".coverage.2")
@@ -470,7 +472,7 @@ class CoverageDataFilesTest(DataTestHelpers, CoverageTest):
def test_erasing(self):
covdata1 = CoverageData()
- covdata1.add_lines(LINES_1)
+ covdata1.set_lines(LINES_1)
self.data_files.write(covdata1)
covdata1.erase()
@@ -482,13 +484,13 @@ class CoverageDataFilesTest(DataTestHelpers, CoverageTest):
self.assert_line_counts(covdata2, {})
def test_file_format(self):
- # Write with CoverageData, then read the pickle explicitly.
+ # Write with CoverageData, then read the JSON explicitly.
covdata = CoverageData()
- covdata.add_lines(LINES_1)
+ covdata.set_lines(LINES_1)
self.data_files.write(covdata)
- with open(".coverage", 'rb') as fdata:
- data = pickle.load(fdata)
+ with open(".coverage", 'r') as fdata:
+ data = json.load(fdata)
lines = data['lines']
self.assertCountEqual(lines.keys(), MEASURED_FILES_1)
@@ -498,24 +500,24 @@ class CoverageDataFilesTest(DataTestHelpers, CoverageTest):
self.assertNotIn('arcs', data)
def test_file_format_with_arcs(self):
- # Write with CoverageData, then read the pickle explicitly.
+ # Write with CoverageData, then read the JSON explicitly.
covdata = CoverageData()
- covdata.add_arcs(ARCS_3)
+ covdata.set_arcs(ARCS_3)
self.data_files.write(covdata)
- with open(".coverage", 'rb') as fdata:
- data = pickle.load(fdata)
+ with open(".coverage", 'r') as fdata:
+ data = json.load(fdata)
self.assertNotIn('lines', data)
arcs = data['arcs']
self.assertCountEqual(arcs.keys(), MEASURED_FILES_3)
- self.assertCountEqual(arcs['x.py'], X_PY_ARCS_3)
- self.assertCountEqual(arcs['y.py'], Y_PY_ARCS_3)
+ self.assertCountEqual(arcs['x.py'], map(list, X_PY_ARCS_3))
+ self.assertCountEqual(arcs['y.py'], map(list, Y_PY_ARCS_3))
def test_writing_to_other_file(self):
data_files = CoverageDataFiles(".otherfile")
covdata = CoverageData()
- covdata.add_lines(LINES_1)
+ covdata.set_lines(LINES_1)
data_files.write(covdata)
self.assert_doesnt_exist(".coverage")
self.assert_exists(".otherfile")
@@ -526,18 +528,18 @@ class CoverageDataFilesTest(DataTestHelpers, CoverageTest):
def test_combining_with_aliases(self):
covdata1 = CoverageData()
- covdata1.add_lines({
+ covdata1.set_lines({
'/home/ned/proj/src/a.py': {1: None, 2: None},
'/home/ned/proj/src/sub/b.py': {3: None},
'/home/ned/proj/src/template.html': {10: None},
})
- covdata1.add_plugins({
+ covdata1.set_plugins({
'/home/ned/proj/src/template.html': 'html.plugin',
})
self.data_files.write(covdata1, suffix='1')
covdata2 = CoverageData()
- covdata2.add_lines({
+ covdata2.set_lines({
r'c:\ned\test\a.py': {4: None, 5: None},
r'c:\ned\test\sub\b.py': {3: None, 6: None},
})
@@ -559,12 +561,12 @@ class CoverageDataFilesTest(DataTestHelpers, CoverageTest):
def test_combining_from_different_directories(self):
covdata1 = CoverageData()
- covdata1.add_lines(LINES_1)
+ covdata1.set_lines(LINES_1)
os.makedirs('cov1')
covdata1.write_file('cov1/.coverage.1')
covdata2 = CoverageData()
- covdata2.add_lines(LINES_2)
+ covdata2.set_lines(LINES_2)
os.makedirs('cov2')
covdata2.write_file('cov2/.coverage.2')
diff --git a/tests/test_testing.py b/tests/test_testing.py
index b01cb74..5e54268 100644
--- a/tests/test_testing.py
+++ b/tests/test_testing.py
@@ -66,7 +66,10 @@ class CoverageTestTest(CoverageTest):
def test_arcz_to_arcs(self):
self.assertEqual(self.arcz_to_arcs(".1 12 2."), [(-1, 1), (1, 2), (2, -1)])
self.assertEqual(self.arcz_to_arcs("-11 12 2-5"), [(-1, 1), (1, 2), (2, -5)])
- self.assertEqual(self.arcz_to_arcs("-QA CB IT Z-A"), [(-26, 10), (12, 11), (18, 29), (35, -10)])
+ self.assertEqual(
+ self.arcz_to_arcs("-QA CB IT Z-A"),
+ [(-26, 10), (12, 11), (18, 29), (35, -10)]
+ )
def test_file_exists(self):
self.make_file("whoville.txt", "We are here!")