Skip to content

Commit

Permalink
More tests.
Browse files Browse the repository at this point in the history
  • Loading branch information
knutin committed Feb 17, 2016
1 parent c6cef91 commit 0ee082d
Show file tree
Hide file tree
Showing 4 changed files with 144 additions and 101 deletions.
3 changes: 2 additions & 1 deletion .gitignore
@@ -1,2 +1,3 @@
*~
/build/
/build/
*.pyc
171 changes: 110 additions & 61 deletions test/test.py
@@ -1,67 +1,100 @@
import os
import shutil
import subprocess
import unittest
import datetime

from traildb import TrailDB, TrailDBConstructor
from traildb import TrailDBError, TrailDBCursor

class TestAPI(unittest.TestCase):
def setUp(self):
self.cookie = '12345678123456781234567812345678'
cons = TrailDBConstructor('testtrail.tdb', ['field1', 'field2'])
cons.add(self.cookie, 1, ['a', '1'])
cons.add(self.cookie, 2, ['b', '2'])
cons.add(self.cookie, 3, ['c', '3'])
cons.finalize()

def tearDown(self):
shutil.rmtree('testtrail.tdb', True)

def test_trails(self):
db = TrailDB('testtrail.tdb')
self.assertEqual(1, db.num_trails)

trail = db.trail(0)
self.assertIsInstance(trail, TrailDBCursor)

events = list(trail) # Force evaluation of generator
self.assertEqual(3, len(events))
for event in events:
self.assertTrue(hasattr(event, 'time'))
self.assertTrue(hasattr(event, 'field1'))
self.assertTrue(hasattr(event, 'field2'))

def test_crumbs(self):
db = TrailDB('testtrail.tdb')

n = 0
for cookie, trail in db.crumbs():
n += 1
self.assertEqual(self.cookie, cookie)
self.assertIsInstance(trail, TrailDBCursor)
self.assertEqual(3, len(list(trail)))

self.assertEqual(1, n)

def test_silly_open(self):
self.assertTrue(os.path.exists('testtrail.tdb'))
self.assertFalse(os.path.exists('testtrail'))

db1 = TrailDB('testtrail.tdb')
db2 = TrailDB('testtrail')

with self.assertRaises(TrailDBError):
TrailDB('foo.tdb')

def test_fields(self):
db = TrailDB('testtrail.tdb')
self.assertEqual(['time', 'field1', 'field2'], db.fields)

def test_cookies(self):
db = TrailDB('testtrail.tdb')
self.assertEqual(0, db.cookie_id(self.cookie))
self.assertEqual(self.cookie, db.cookie(0))
self.assertTrue(self.cookie in db)


def test_lexicons(self):
db = TrailDB('testtrail.tdb')

# First field
self.assertEqual(4, db.lexicon_size(1))
self.assertEqual(['a', 'b', 'c'], db.lexicon(1))

# Second field
self.assertEqual(['1', '2', '3'], db.lexicon(2))

with self.assertRaises(TrailDBError):
db.lexicon(3) # Out of bounds


def test_metadata(self):
db = TrailDB('testtrail.tdb')
self.assertEqual(1, db.min_timestamp())
self.assertEqual(3, db.max_timestamp())
self.assertEqual((1, 3), db.time_range())

self.assertEqual((datetime.datetime(1970, 1, 1, 0, 0, 1),
datetime.datetime(1970, 1, 1, 0, 0, 3)),
db.time_range(parsetime = True))

# class TestAPI(unittest.TestCase):
# def setUp(self):
# subprocess.Popen(('test/test.sh', 'test.tdb')).wait()
# self.traildb = TrailDB('test.tdb')

# def tearDown(self):
# shutil.rmtree('test.tdb')

# def test_trails(self):
# db = self.traildb
# print list(db.trail(0, ptime=True))
# print list(db[0])

# for trail in db:
# for event in trail:
# print event.time, event.z

# for cookie, trail in db.crumbs():
# print cookie, len(list(trail))

# def test_fields(self):
# db = self.traildb
# print db.fields

# def test_cookies(self):
# db = self.traildb
# print db.cookie(0)
# print db.has_cookie_index()
# print db.cookie_id('12345678123456781234567812345678')
# #print db.cookie_id('abc')
# #print 'abc' in db

# def test_values(self):
# db = self.traildb
# print db.value(1, 1)

# def test_lexicons(self):
# db = self.traildb
# print db.lexicon_size(1)
# print db.lexicon(1)
# print db.lexicon('z')
# print dict((f, db.lexicon(f)) for f in db.fields[1:])

# def test_metadata(self):
# db = self.traildb
# print db.time_range()
# print db.time_range(ptime=True)

# def test_fold(self):
# db = self.traildb
# def fold_fn(db, id, ev, acc):
# acc.append((id, ev))
# print db.fold(fold_fn, [])

class TestCons(unittest.TestCase):
def test_cursor(self):
cookie = '12345678123456781234567812345678'
cons = TrailDBConstructor('test.tdb', ['field1', 'field2'])
cons = TrailDBConstructor('testtrail.tdb', ['field1', 'field2'])
cons.add(cookie, 1, ['a', '1'])
cons.add(cookie, 2, ['b', '2'])
cons.add(cookie, 3, ['c', '3'])
Expand All @@ -70,10 +103,13 @@ def test_cursor(self):
tdb = cons.finalize()

trail = tdb.trail(tdb.cookie_id(cookie))
with self.assertRaises(TypeError):
len(trail)

j = 1
for event in trail:
self.assertEqual(j, int(event.field2))
self.assertEqual(j, int(event.time))
j += 1
self.assertEqual(6, j)

Expand All @@ -83,10 +119,24 @@ def test_cursor(self):
field1_values = [e.field1 for e in tdb.trail(tdb.cookie_id(cookie))]
self.assertEqual(['a', 'b', 'c', 'd', 'e'], field1_values)

def test_cursor_parsetime(self):
cookie = '12345678123456781234567812345678'
cons = TrailDBConstructor('testtrail.tdb', ['field1'])

events = [(datetime.datetime(2016, 1, 1, 1, 1), ['1']),
(datetime.datetime(2016, 1, 1, 1, 2), ['2']),
(datetime.datetime(2016, 1, 1, 1, 3), ['3'])]
[cons.add(cookie, time, fields) for time, fields in events]
tdb = cons.finalize()

timestamps = [e.time for e in tdb.trail(0, parsetime = True)]
self.assertIsInstance(timestamps[0], datetime.datetime)
self.assertEqual([time for time, _ in events], timestamps)


def test_cons(self):
cookie = '12345678123456781234567812345678'
cons = TrailDBConstructor('test.tdb', ['field1', 'field2'])
cons = TrailDBConstructor('testtrail.tdb', ['field1', 'field2'])
cons.add(cookie, 123, ['a'])
cons.add(cookie, 124, ['b', 'c'])
tdb = cons.finalize()
Expand Down Expand Up @@ -116,19 +166,17 @@ def test_cons(self):
self.assertEqual('b', trail[1].field1)
self.assertEqual('c', trail[1].field2)



def test_append(self):
# TODO: Currently, values must be larger than 7 bytes due to a
# bug in the lexicon code. When it's fixed, test also smaller
# input.

cookie = '12345678123456781234567812345678'
cons = TrailDBConstructor('test.tdb', ['field1'])
cons = TrailDBConstructor('testtrail.tdb', ['field1'])
cons.add(cookie, 123, ['foobarbaz'])
tdb = cons.finalize()

cons = TrailDBConstructor('test2.tdb', ['field1'])
cons = TrailDBConstructor('testtrail2.tdb', ['field1'])
cons.add(cookie, 124, ['barquuxmoo'])
cons.append(tdb)
tdb = cons.finalize()
Expand All @@ -141,8 +189,9 @@ def test_append(self):


def tearDown(self):
shutil.rmtree('test.tdb', True)
shutil.rmtree('test2.tdb', True)
shutil.rmtree('testtrail.tdb', True)
shutil.rmtree('testtrail2.tdb', True)


if __name__ == '__main__':
unittest.main()
3 changes: 1 addition & 2 deletions traildb/__init__.py
@@ -1,2 +1 @@
from .traildb import TrailDBError, TrailDBConstructor, TrailDB

from .traildb import TrailDBError, TrailDBConstructor, TrailDB, TrailDBCursor
68 changes: 31 additions & 37 deletions traildb/traildb.py
Expand Up @@ -124,7 +124,7 @@ def __del__(self):
if hasattr(self, '_cons'):
lib.tdb_cons_close(self._cons)

def add(self, cookie, time, values=()):
def add(self, cookie, time, values):
if isinstance(time, datetime):
time = int(time.strftime('%s'))
n = len(self.ofields)
Expand All @@ -150,10 +150,11 @@ def finalize(self, flags=0):


class TrailDBCursor(object):
def __init__(self, cursor, cls, valuefun):
def __init__(self, cursor, cls, valuefun, parsetime):
self.cursor = cursor
self.cls = cls
self.valuefun = valuefun
self.parsetime = parsetime

def __del__(self):
if self.cursor:
Expand All @@ -175,26 +176,32 @@ def next(self):
values.append(self.valuefun(tdb_item_field(items[j]),
tdb_item_val(items[j])))

return self.cls(event.contents.timestamp, *values)
timestamp = event.contents.timestamp
if self.parsetime:
timestamp = datetime.utcfromtimestamp(event.contents.timestamp)

return self.cls(timestamp, *values)


class TrailDB(object):
def __init__(self, path):
#if path.endswith('.tdb'):
# raise TrailDBError("The path to open should *not* include '.tdb' suffix.")

self._db = db = lib.tdb_init()
if lib.tdb_open(self._db, path) != 0:
raise TrailDBError("Could not open %s" % path)
res = lib.tdb_open(self._db, path)
if res != 0:
raise TrailDBError("Could not open %s, error code %d" % (path, res))

self.num_trails = lib.tdb_num_trails(db)
self.num_events = lib.tdb_num_events(db)
self.num_fields = lib.tdb_num_fields(db)
self.fields = [lib.tdb_get_field_name(db, i) for i in xrange(self.num_fields)]
self._evcls = namedtuple('event', self.fields, rename=True)
self._cursor = None

def __del__(self):
if self._cursor:
lib.tdb_cursor_free(self._cursor)
lib.tdb_close(self._db)
if hasattr(self, '_db'):
lib.tdb_close(self._db)

def __contains__(self, cookieish):
try:
Expand All @@ -208,25 +215,19 @@ def __getitem__(self, cookieish):
return self.trail(self.cookie_id(cookieish))
return self.trail(cookieish)


def __len__(self):
return self.num_trails

def crumbs(self, **kwds):
for i in xrange(len(self)):
yield self.cookie(i), self.trail(i, **kwds)


def trail(self, i):
if self._cursor:
raise TrailDBError("Cursor already created")

def trail(self, i, parsetime = False):
cursor = lib.tdb_cursor_new(self._db)
if lib.tdb_get_trail(cursor, i) != 0:
raise TrailDBError("Failed to create cursor")

return TrailDBCursor(cursor, self._evcls, self.value)

return TrailDBCursor(cursor, self._evcls, self.value, parsetime)

def field(self, fieldish):
if isinstance(fieldish, basestring):
Expand All @@ -235,13 +236,13 @@ def field(self, fieldish):

def lexicon(self, fieldish):
field = self.field(fieldish)
return [self.value(field, i) for i in xrange(self.lexicon_size(field))]
return [self.value(field, i) for i in xrange(1, self.lexicon_size(field))]

def lexicon_size(self, fieldish):
field = self.field(fieldish)
value = lib.tdb_lexicon_size(self._db, field)
if not value:
raise TrailDBError(lib.tdb_error(self._db))
if value == 0:
raise TrailDBError("Invalid field index")
return value

def val(self, fieldish, value):
Expand All @@ -256,7 +257,7 @@ def value(self, fieldish, val):
value_size = c_uint64()
value = lib.tdb_get_value(self._db, field, val, value_size)
if value is None:
raise TrailDBError(lib.tdb_error(self._db))
raise TrailDBError("Error reading value, error: %s" % lib.tdb_error(self._db))
return value[0:value_size.value]

def cookie(self, id, raw=False):
Expand All @@ -274,25 +275,18 @@ def cookie_id(self, cookie):
return cookie_id
raise IndexError("Cookie '%s' not found" % cookie)

def has_cookie_index(self):
return True if lib.tdb_has_cookie_index(self._db) else False

def has_overflow_vals(self, fieldish):
field = self.field(fieldish)
return lib.tdb_field_has_overflow_vals(self._db, field) != 0

def time_range(self, ptime=False):
tmin = lib.tdb_min_timestamp(self._db)
tmax = lib.tdb_max_timestamp(self._db)
if ptime:
def time_range(self, parsetime=False):
tmin = self.min_timestamp()
tmax = self.max_timestamp()
if parsetime:
return datetime.utcfromtimestamp(tmin), datetime.utcfromtimestamp(tmax)
return tmin, tmax

def split(self, num_parts, fmt='a.%02d.tdb', flags=0):
ret = lib.tdb_split(self._db, num_parts, fmt, flags)
if ret:
raise TrailDBError("Could not split into %d parts" % num_parts)
return [TrailDB(fmt % n) for n in xrange(num_parts)]
def min_timestamp(self):
return lib.tdb_min_timestamp(self._db)

def max_timestamp(self):
return lib.tdb_max_timestamp(self._db)

def _parse_filter(self, filter_expr):
# filter_expr syntax in CNF:
Expand Down

0 comments on commit 0ee082d

Please sign in to comment.