diff options
author | Ned Batchelder <ned@nedbatchelder.com> | 2019-07-19 21:41:58 -0400 |
---|---|---|
committer | Ned Batchelder <ned@nedbatchelder.com> | 2019-07-19 21:41:58 -0400 |
commit | 0a93630dc92de59194794f5f0b9ae1987157f327 (patch) | |
tree | 0b4e37ac38aba2f8a3a46a0bfcecaed2d7209a34 | |
parent | e4054498c6eb2f0b97de80bcf9835f416a91207c (diff) | |
download | python-coveragepy-git-0a93630dc92de59194794f5f0b9ae1987157f327.tar.gz |
z-compressed dumps and loads
-rw-r--r-- | coverage/backward.py | 8 | ||||
-rw-r--r-- | coverage/sqldata.py | 29 | ||||
-rw-r--r-- | tests/test_data.py | 28 |
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.""" |