diff options
author | Ned Batchelder <nedbat@gmail.com> | 2015-07-21 21:06:05 -0400 |
---|---|---|
committer | Ned Batchelder <nedbat@gmail.com> | 2015-07-21 21:06:05 -0400 |
commit | cf43af31d35ba527e778267c14e51c56c9c3a773 (patch) | |
tree | 75d7a4c751862f64f79474d41815ef11d224486a | |
parent | 130f0dcdff98a1f947784f6989d1984b73b28335 (diff) | |
parent | a591430903ed9108c8cb50369be0d9d9c1a0b200 (diff) | |
download | python-coveragepy-cf43af31d35ba527e778267c14e51c56c9c3a773.tar.gz |
Merged in twexler/coverage.py (pull request #58)
Don't use SourceForge anymore for Cobertura DTD
-rw-r--r-- | CHANGES.txt | 9 | ||||
-rw-r--r-- | Makefile | 6 | ||||
-rw-r--r-- | coverage/backunittest.py | 11 | ||||
-rw-r--r-- | coverage/backward.py | 6 | ||||
-rw-r--r-- | coverage/collector.py | 6 | ||||
-rw-r--r-- | coverage/control.py | 4 | ||||
-rw-r--r-- | coverage/data.py | 315 | ||||
-rw-r--r-- | doc/api.rst | 2 | ||||
-rw-r--r-- | doc/conf.py | 38 | ||||
-rw-r--r-- | tests/test_cmdline.py | 4 | ||||
-rw-r--r-- | tests/test_data.py | 146 | ||||
-rw-r--r-- | tests/test_testing.py | 5 |
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 ------------------------------ @@ -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!") |