From 7e6d1cf26f3dddefca5dacd0dc30c956e8d775c4 Mon Sep 17 00:00:00 2001 From: Jason Madden Date: Thu, 2 Nov 2017 08:51:15 -0500 Subject: [PATCH] 100% coverage for ricecode.py Coming from 0% coverage. Fix some pickling bugs this exposed. Remove the undocumented test function to the new test module. The undocumented pickle_efficiency function now takes parameters and returns results instead of being hardcoded and printing. --- CHANGES.rst | 3 + src/zope/index/text/ricecode.py | 49 ++++++--------- src/zope/index/text/tests/test_ricecode.py | 69 ++++++++++++++++++++++ tox.ini | 2 +- 4 files changed, 91 insertions(+), 32 deletions(-) create mode 100644 src/zope/index/text/tests/test_ricecode.py diff --git a/CHANGES.rst b/CHANGES.rst index 2f83b89..e7628cd 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -13,6 +13,9 @@ - Remove some internal test scripts that were never ported to Python 3. +- Reach and maintain 100% test coverage. + + 4.3.0 (2017-04-24) ================== diff --git a/src/zope/index/text/ricecode.py b/src/zope/index/text/ricecode.py index 6128da3..a3d4cd4 100644 --- a/src/zope/index/text/ricecode.py +++ b/src/zope/index/text/ricecode.py @@ -44,7 +44,9 @@ def __init__(self, buf=None): self.bytes = array.array('B') self.nbits = 0 self.bitsleft = 0 - self.tostring = self.bytes.tostring + + def tostring(self): + return self.bytes.tostring() def __getitem__(self, i): byte, offset = divmod(i, 8) @@ -87,13 +89,14 @@ class RiceCode(object): """ Rice coding. """ + len = 0 + def __init__(self, m): """Constructor a RiceCode for m-bit values.""" - if not (0 <= m <= 16): + if m < 0 or m > 16: raise ValueError("m must be between 0 and 16") self.init(m) self.bits = BitArray() - self.len = 0 def init(self, m): self.m = m @@ -183,36 +186,20 @@ def decode_deltas(start, enc_deltas): l.append(l[-1] + deltas[-1]) return l -def test(): - import random - for size in [10, 20, 50, 100, 200]: - l = [random.randint(1, size) for i in range(50)] - c = encode(random.randint(1, 16), l) - assert c.tolist() == l - for size in [10, 20, 50, 100, 200]: - l = list(range(random.randint(1, size), size + random.randint(1, size))) - t = encode_deltas(l) - l2 = decode_deltas(*t) - assert l == l2 - if l != l2: - print(l) - print(l2) - -def pickle_efficiency(): + +def pickle_efficiency(bits=(4, 8, 12), + sizes=(10, 20, 50, 100, 200, 500, 1000, 2000, 5000), + elt_ranges=(10, 20, 50, 100, 200, 500, 1000)): import pickle import random - for m in [4, 8, 12]: - for size in [10, 20, 50, 100, 200, 500, 1000, 2000, 5000]: - for elt_range in [10, 20, 50, 100, 200, 500, 1000]: + import collections + all_results = {} + for m in bits: + all_results[m] = collections.defaultdict(dict) + for size in sizes: + for elt_range in elt_ranges: l = [random.randint(1, elt_range) for i in range(size)] raw = pickle.dumps(l, 1) enc = pickle.dumps(encode(m, l), 1) - print("m=%2d size=%4d range=%4d" % (m, size, elt_range), end=' ') - print("%5d %5d" % (len(raw), len(enc)), end=' ') - if len(raw) > len(enc): - print("win") - else: - print("lose") - -if __name__ == "__main__": - test() + all_results[m][size][elt_range] = "win" if len(raw) > len(enc) else "lose" + return all_results diff --git a/src/zope/index/text/tests/test_ricecode.py b/src/zope/index/text/tests/test_ricecode.py new file mode 100644 index 0000000..1f8356e --- /dev/null +++ b/src/zope/index/text/tests/test_ricecode.py @@ -0,0 +1,69 @@ +# -*- coding: utf-8 -*- +""" +Tests for ricecode.py + +""" +import unittest + +from zope.index.text.ricecode import encode +from zope.index.text.ricecode import encode_deltas +from zope.index.text.ricecode import decode_deltas +from zope.index.text.ricecode import pickle_efficiency +from zope.index.text.ricecode import RiceCode + +class TestRiceCode(unittest.TestCase): + + def test_random(self): + import random + for size in [10, 20, 50, 100, 200]: + l = [random.randint(1, size) for i in range(50)] + c = encode(random.randint(1, 16), l) + self.assertEqual(c.tolist(), l) + for size in [10, 20, 50, 100, 200]: + l = list(range(random.randint(1, size), size + random.randint(1, size))) + l_0, deltas = encode_deltas(l) + l2 = decode_deltas(l_0, deltas) + self.assertEqual(l, l2) + + def test_encode_deltas_one_element(self): + self.assertEqual(('foo', []), + encode_deltas(['foo'])) + + def test_pickle_efficiency(self): + # This is random data so it's hard to say what wins + # and what loses. + results = pickle_efficiency(sizes=(10, 20), elt_ranges=(10, 20)) + self.assertEqual(3, len(results)) + + def test_bit_range(self): + with self.assertRaises(ValueError): + RiceCode(-1) + with self.assertRaises(ValueError): + RiceCode(17) + + def test_length(self): + code = RiceCode(6) + self.assertEqual(0, len(code)) + self.assertEqual(0, len(code.bits)) + + code.append(1) + self.assertEqual(1, len(code)) + self.assertEqual(7, len(code.bits)) + + def test_append_invalid(self): + with self.assertRaises(ValueError): + RiceCode(6).append(0) + + def test_pickle(self): + import pickle + code = RiceCode(6) + code.append(1) + + code2 = pickle.loads(pickle.dumps(code)) + + self.assertEqual(code.m, code2.m) + self.assertEqual(code.tostring(), code2.tostring()) + + # But length is not preserved + self.assertEqual(1, len(code)) + self.assertEqual(0, len(code2)) diff --git a/tox.ini b/tox.ini index c7fa238..eb9ae66 100644 --- a/tox.ini +++ b/tox.ini @@ -14,7 +14,7 @@ basepython = python3.6 commands = coverage run -m zope.testrunner --test-path=src [] - coverage report --fail-under=82 + coverage report --fail-under=100 deps = {[testenv]deps} coverage