Skip to content

Commit

Permalink
Made pytest happy.
Browse files Browse the repository at this point in the history
  • Loading branch information
Leechael committed Nov 28, 2016
1 parent b9711ae commit 0fa918e
Show file tree
Hide file tree
Showing 7 changed files with 72 additions and 10 deletions.
12 changes: 12 additions & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
[run]
branch = true
omit = */tests/*
[report]
exclude_lines =
# Have to re-enable the standard pragma
pragma: no cover

# Don't complain about missing debug-only code:
def __repr__
if self\.debug

# Don't complain if non-runnable code isn't run:
if 0:
if __name__ == .__main__.:
7 changes: 6 additions & 1 deletion cuckoofilter/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,6 @@
from .cuckoofilter import CuckooFilter
from .cuckoofilter import (
CuckooFilter,
CuckooFilterError,
Fullfill,
StreamValueError
)
2 changes: 1 addition & 1 deletion cuckoofilter/bucket.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,5 +66,5 @@ def __contains__(self, fingerprint):
def __repr__(self):
return '<Bucket: ' + str(self.b) + '>'

def __sizeof__(self):
def __sizeof__(self): # pragma: no cover
return super().__sizeof__() + self.b.__sizeof__()
16 changes: 13 additions & 3 deletions cuckoofilter/cuckoofilter.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,16 @@
from . import bucket


class CuckooFilterError(Exception):
pass

class Fullfill(CuckooFilterError):
pass

class StreamValueError(CuckooFilterError):
pass


class CuckooFilter:
'''
A Cuckoo filter is a data structure for probablistic set-membership queries.
Expand Down Expand Up @@ -66,7 +76,7 @@ def insert(self, item):
return i

self.size = self.size - 1
raise Exception('Filter is full')
raise Fullfill()

def contains(self, item):
'''Checks if a string was inserted into the filter.'''
Expand Down Expand Up @@ -114,7 +124,7 @@ def __contains__(self, item):
def __repr__(self):
return '<CuckooFilter: capacity=' + str(self.capacity) + ', fingerprint size=' + str(self.fingerprint_size) + ' byte(s)>'

def __sizeof__(self):
def __sizeof__(self): # pragma: no cover
return super().__sizeof__() + sum(b.__sizeof__() for b in self.buckets)


Expand Down Expand Up @@ -146,7 +156,7 @@ def serialize(self):
@classmethod
def unserialize(cls, io):
if io.read(6) != cls.MAGIC_STR:
raise Exception("Unexpected input IO")
raise StreamValueError()
(capacity, size, bucket_size, fingerprint_size, max_kicks) = unpack(
"2Q3I", io.read(28))
cf = cls(capacity, fingerprint_size, bucket_size, max_kicks)
Expand Down
1 change: 1 addition & 0 deletions cuckoofilter/tests/test_bucket.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ def bucket():
def test_initialization(bucket):
assert bucket.size == 4
assert bucket.b == []
assert bucket.is_empty()

def test_insert(bucket):
assert bucket.insert('hello')
Expand Down
37 changes: 33 additions & 4 deletions cuckoofilter/tests/test_filter.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import pytest
import cuckoofilter
from io import BytesIO

@pytest.fixture
def cf():
Expand All @@ -22,17 +23,15 @@ def test_insert_full(cf):
for _ in range(cf.bucket_size * 2):
cf.insert('hello')

with pytest.raises(Exception) as e:
with pytest.raises(cuckoofilter.Fullfill) as e:
cf.insert('hello')

assert str(e.value) == 'Filter is full'
assert cf.size == (cf.bucket_size * 2)

def test_insert_over_capacitiy(cf):
with pytest.raises(Exception) as e:
with pytest.raises(cuckoofilter.Fullfill) as e:
for i in range((cf.capacity * cf.bucket_size) + 1):
cf.insert(str(i))
assert str(e.value) == 'Filter is full'
assert cf.load_factor() > 0.9

def test_contains(cf):
Expand Down Expand Up @@ -65,3 +64,33 @@ def test_load_factor_empty(cf):
def test_load_factor_non_empty(cf):
cf.insert('hello')
assert cf.load_factor() == (1 / (cf.capacity * cf.bucket_size))

def test_serialize(cf):
for _ in range(cf.bucket_size):
cf.insert('hello')
io = cf.serialize()
assert isinstance(io, BytesIO)
assert io.tell() == 0

def test_unserialize(cf):
for _ in range(cf.bucket_size):
cf.insert(str(_))
io = cf.serialize()
ncf = cuckoofilter.CuckooFilter.unserialize(io)
assert cf.capacity == ncf.capacity
assert cf.size == ncf.size
assert cf.fingerprint_size == ncf.fingerprint_size
assert cf.bucket_size == ncf.bucket_size
assert cf.max_kicks == ncf.max_kicks
for _ in range(ncf.bucket_size):
assert ncf.contains(str(_))

def test_unserialize_with_invalid_file(cf):
with pytest.raises(cuckoofilter.StreamValueError) as e:
with open("/dev/random", "rb") as f:
cf = cuckoofilter.CuckooFilter.unserialize(f)

def test_unserialize_with_reading_in_utf8(cf):
with pytest.raises(UnicodeDecodeError) as e:
with open("/dev/random") as f:
cf = cuckoofilter.CuckooFilter.unserialize(f)
7 changes: 6 additions & 1 deletion example.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@
'''

import cuckoofilter
import gzip

if __name__ == '__main__':
total_items = 100000
cf = cuckoofilter.CuckooFilter(total_items, 2)

num_inserted = 0
for i in range(total_items):
cf.insert(str(i))
Expand All @@ -23,4 +24,8 @@
false_queries = false_queries + 1
total_queries = total_queries + 1

serialized = cf.serialize().read()

print('False positive rate is {:%}'.format(false_queries / total_queries))
print("size after serialize: {:}".format(len(serialized)))
print("size after serialize + gzip: {:}".format(len(gzip.compress(serialized))))

0 comments on commit 0fa918e

Please sign in to comment.