summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNed Batchelder <ned@nedbatchelder.com>2019-07-19 21:41:58 -0400
committerNed Batchelder <ned@nedbatchelder.com>2019-07-19 21:41:58 -0400
commit0a93630dc92de59194794f5f0b9ae1987157f327 (patch)
tree0b4e37ac38aba2f8a3a46a0bfcecaed2d7209a34
parente4054498c6eb2f0b97de80bcf9835f416a91207c (diff)
downloadpython-coveragepy-git-0a93630dc92de59194794f5f0b9ae1987157f327.tar.gz
z-compressed dumps and loads
-rw-r--r--coverage/backward.py8
-rw-r--r--coverage/sqldata.py29
-rw-r--r--tests/test_data.py28
3 files changed, 53 insertions, 12 deletions
diff --git a/coverage/backward.py b/coverage/backward.py
index 720cd3e1..0df2a41e 100644
--- a/coverage/backward.py
+++ b/coverage/backward.py
@@ -106,6 +106,10 @@ if env.PY3:
"""Convert string `s` to bytes."""
return s.encode('utf8')
+ def to_string(b):
+ """Convert bytes `b` to string."""
+ return b.decode('utf8')
+
def binary_bytes(byte_values):
"""Produce a byte string with the ints from `byte_values`."""
return bytes(byte_values)
@@ -120,6 +124,10 @@ else:
"""Convert string `s` to bytes (no-op in 2.x)."""
return s
+ def to_string(b):
+ """Convert bytes `b` to string."""
+ return b
+
def binary_bytes(byte_values):
"""Produce a byte string with the ints from `byte_values`."""
return "".join(chr(b) for b in byte_values)
diff --git a/coverage/sqldata.py b/coverage/sqldata.py
index ea4922ca..00a72b9f 100644
--- a/coverage/sqldata.py
+++ b/coverage/sqldata.py
@@ -15,9 +15,10 @@ import itertools
import os
import sqlite3
import sys
+import zlib
from coverage.backward import get_thread_id, iitems
-from coverage.backward import bytes_to_ints, binary_bytes, zip_longest
+from coverage.backward import bytes_to_ints, binary_bytes, zip_longest, to_bytes, to_string
from coverage.debug import NoDebugging, SimpleReprMixin
from coverage import env
from coverage.files import PathAliases
@@ -219,8 +220,11 @@ class CoverageData(SimpleReprMixin):
def _open_db(self):
if self._debug.should('dataio'):
self._debug.write("Opening data file {!r}".format(self._filename))
- self._dbs[get_thread_id()] = db = SqliteDb(self._filename, self._debug)
- with db:
+ self._dbs[get_thread_id()] = SqliteDb(self._filename, self._debug)
+ self._read_db()
+
+ def _read_db(self):
+ with self._dbs[get_thread_id()] as db:
try:
schema_version, = db.execute("select version from coverage_schema").fetchone()
except Exception as exc:
@@ -264,22 +268,23 @@ class CoverageData(SimpleReprMixin):
__bool__ = __nonzero__
- def dump(self): # pragma: debugging
- """Write a dump of the database."""
- if self._debug:
- with self._connect() as con:
- self._debug.write(con.dump())
-
+ @contract(returns='bytes')
def dumps(self):
with self._connect() as con:
- return con.dump()
+ return b'z' + zlib.compress(to_bytes(con.dump()))
+ @contract(data='bytes')
def loads(self, data):
if self._debug.should('dataio'):
self._debug.write("Loading data into data file {!r}".format(self._filename))
+ if data[:1] != b'z':
+ raise CoverageException("Unrecognized serialization: {!r} (head of {} bytes)".format(data[:40], len(data)))
+ script = to_string(zlib.decompress(data[1:]))
self._dbs[get_thread_id()] = db = SqliteDb(self._filename, self._debug)
with db:
- db.executescript(data)
+ db.executescript(script)
+ self._read_db()
+ self._have_used = True
def _file_id(self, filename, add=False):
"""Get the file id for `filename`.
@@ -858,7 +863,7 @@ class SqliteDb(SimpleReprMixin):
self.debug.write("Executing script with {} chars".format(len(script)))
self.con.executescript(script)
- def dump(self): # pragma: debugging
+ def dump(self):
"""Return a multi-line string, the dump of the database."""
return "\n".join(self.con.iterdump())
diff --git a/tests/test_data.py b/tests/test_data.py
index 5ac08bb6..ad752d65 100644
--- a/tests/test_data.py
+++ b/tests/test_data.py
@@ -7,6 +7,7 @@ import glob
import os
import os.path
import random
+import re
import sqlite3
import threading
@@ -809,6 +810,33 @@ class CoverageDataFilesTest(DataTestHelpers, CoverageTest):
covdata2.add_lines(LINES_1)
+class DumpsLoadsTest(DataTestHelpers, CoverageTest):
+ """Tests of CoverageData.dumps and loads."""
+
+ run_in_temp_dir = False
+
+ def test_serialization(self):
+ covdata1 = CoverageData(no_disk=True)
+ covdata1.add_lines(LINES_1)
+ covdata1.add_lines(LINES_2)
+ serial = covdata1.dumps()
+
+ covdata2 = CoverageData(no_disk=True)
+ covdata2.loads(serial)
+ self.assert_line_counts(covdata2, SUMMARY_1_2)
+ self.assert_measured_files(covdata2, MEASURED_FILES_1_2)
+
+ def test_misfed_serialization(self):
+ covdata = CoverageData(no_disk=True)
+ bad_data = b'Hello, world!\x07 ' + b'z' * 100
+ msg = r"Unrecognized serialization: {} \(head of {} bytes\)".format(
+ re.escape(repr(bad_data[:40])),
+ len(bad_data),
+ )
+ with self.assertRaisesRegex(CoverageException, msg):
+ covdata.loads(bad_data)
+
+
class BitmapOpTest(CoverageTest):
"""Tests of the bitmap operations in sqldata.py."""