Skip to content

Commit

Permalink
100% coverage for ricecode.py
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
jamadden committed Nov 2, 2017
1 parent f6fa1f3 commit 7e6d1cf
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 32 deletions.
3 changes: 3 additions & 0 deletions CHANGES.rst
Expand Up @@ -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)
==================

Expand Down
49 changes: 18 additions & 31 deletions src/zope/index/text/ricecode.py
Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
69 changes: 69 additions & 0 deletions 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))
2 changes: 1 addition & 1 deletion tox.ini
Expand Up @@ -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
Expand Down

0 comments on commit 7e6d1cf

Please sign in to comment.