anderssontree/test/test_aatree.py

310 lines
10 KiB
Python

#!/usr/bin/env python
# Author: Darko Poljak <darko.poljak@gmail.com>
# License: GPLv3
import unittest
import pickle
from random import randint, shuffle
from anderssontree import AnderssonTree
class AATreeException(Exception):
pass
def test_items_seq(maxval=200):
x = list(range(0, maxval))
y = list(zip(x, x))
return y
def test_items_dict(maxval=200):
return {x: x for x in range(0, maxval)}
class TestAATree(unittest.TestCase):
def test_init_empty(self):
tree = AnderssonTree()
self.assertEqual(len(tree), 0)
def test_init_dict(self):
d = test_items_dict()
tree = AnderssonTree(d)
self.assertEqual(len(tree), len(d))
self.check_aatree_properties(tree)
def test_init_seq(self):
s = test_items_seq()
tree = AnderssonTree(s)
self.assertEqual(len(tree), len(s))
self.check_aatree_properties(tree)
def test_pickle_protocol(self):
s = test_items_seq()
tree1 = AnderssonTree(s)
pickle_str = pickle.dumps(tree1, -1)
tree2 = pickle.loads(pickle_str)
self.assertEqual(len(tree1), len(tree2))
self.assertEqual(list(tree1.keys()), list(tree2.keys()))
self.assertEqual(list(tree1.values()), list(tree2.values()))
self.check_aatree_properties(tree1)
self.check_aatree_properties(tree2)
def test_max_btree_level(self):
def _map_levels(node, level, map_):
if node is not None:
map_[node.key] = level
next_level = level + 1
_map_levels(node.left, next_level, map_)
_map_levels(node.right, next_level, map_)
def _map_bin_tree_levels(root):
""" level is binary tree level, not AATree level """
map_ = {}
_map_levels(root, 0, map_)
max_level = max(map_.values())
for k, v in map_.items():
map_[k] = max_level - map_[k] + 1
return map_
keys = range(1, 14)
tree = AnderssonTree(zip(keys, keys))
mapping = _map_bin_tree_levels(tree.root())
maxl = max(mapping.values())
self.assertEqual(maxl, 5, 'Invalid max level %s!' % maxl)
def check_aatree_properties(self, tree, dump=True):
"""
The level of every leaf node is one.
The level of every left child is exactly one less than that of
its parent.
The level of every right child is equal to or one less than
that of its parent.
The level of every right grandchild is strictly less than that
of its grandparent.
Every node of level greater than one has two children.
"""
def _check_child_level(node, maxlevel):
if node is not None:
if node.level >= maxlevel:
msg = 'grandchild level >= grandparent level ' \
'at %s' % str(node.key)
raise AATreeException(msg)
_check_child_level(node.left, maxlevel)
_check_child_level(node.right, maxlevel)
def _check_properties(node, parent, grandparent):
if node is not None:
if node.left is None and node.right is None:
if node.level != 1:
raise AATreeException('leaf level is not 1 at '
'%s' % node.key)
if parent is not None and node.left is not None:
if node.left.level != node.level - 1:
raise AATreeException(
'left child level not exactly'
' one less than that of its parent at '
'%s' % str(node.key))
if parent is not None and node.right is not None:
if not (node.right.level == node.level or
node.right.level == node.level - 1):
raise AATreeException(
'right child level is'
' not equal to or one less than of its'
' parent at %s' % str(node.key))
if grandparent is not None:
maxl = grandparent.level
x = grandparent.right
if x:
_check_child_level(x.right, maxl)
_check_child_level(x.left, maxl)
if node.level > 1:
if node.left is None or node.right is None:
raise AATreeException(
'node with level > 1'
' does not have two children at'
' %s' % str(node.key))
_check_properties(node.left, node, parent)
_check_properties(node.right, node, parent)
try:
_check_properties(tree._root, None, None)
except AATreeException as e:
if dump:
print('')
tree.dump()
self.fail(str(e) + " - see tree dump")
def test_update(self):
d = test_items_seq()
tree = AnderssonTree()
tree.update(d)
self.assertEqual(len(tree), len(d))
self.assertEqual(set(tree.keys()), set(x[0] for x in d))
self.assertEqual(set(tree.values()), set(x[1] for x in d))
self.check_aatree_properties(tree)
def test_root(self):
x = [5, 6, 4]
tree = AnderssonTree(zip(x, x))
self.assertEqual(tree.root().key, 5)
def test_insert(self):
s = test_items_seq()
tree = AnderssonTree()
size = len(tree)
keys = [x[0] for x in s]
keyset = set()
shuffle(keys)
for k in keys:
keyset.add(k)
tree.insert(k, k)
size += 1
self.assertEqual(len(tree), size)
self.assertEqual(set(tree.keys()), keyset)
self.check_aatree_properties(tree)
def test_clear(self):
s = test_items_seq()
tree = AnderssonTree(s)
self.check_aatree_properties(tree)
tree.clear()
self.assertEqual(len(tree), 0)
def test_get(self):
s = test_items_seq()
tree = AnderssonTree(s)
self.check_aatree_properties(tree)
keys = [x[0] for x in s]
shuffle(keys)
for k in keys:
self.assertEqual(tree.get(k), k)
self.assertEqual(tree[k], k)
max_ = max(keys)
self.assertEqual(tree[max_ + 1], None)
def test_setitem(self):
s = test_items_seq()
tree = AnderssonTree(s)
self.check_aatree_properties(tree)
keys = [x[0] for x in s]
shuffle(keys)
for k in keys:
val = k * 10
tree[k] = val
self.assertEqual(tree.get(k), val)
def test_contains(self):
s = test_items_seq()
tree = AnderssonTree(s)
self.check_aatree_properties(tree)
keys = [x[0] for x in s]
shuffle(keys)
for k in keys:
self.assertTrue(k in tree)
self.assertFalse(max(keys) + 10 in tree)
def test_is_empty(self):
s = test_items_seq()
tree = AnderssonTree()
self.assertTrue(tree.is_empty())
tree.update(s)
self.assertFalse(tree.is_empty())
tree.clear()
self.assertTrue(tree.is_empty())
def test_keys(self):
s = test_items_seq()
tree = AnderssonTree(s)
keys = set(x[0] for x in s)
self.assertEqual(set(tree.keys()), keys)
def test_values(self):
s = test_items_seq()
tree = AnderssonTree(s)
values = set(x[1] for x in s)
self.assertEqual(set(tree.values()), values)
def test_reversed(self):
s = test_items_seq()
tree = AnderssonTree(s)
keys = list(reversed([x[0] for x in s]))
self.assertEqual(list(reversed(tree)), keys)
def test_copy(self):
s = test_items_seq()
tree1 = AnderssonTree(s)
tree2 = tree1.copy()
self.assertEqual(len(tree1), len(tree2))
self.assertEqual(repr(tree1), repr(tree2))
def test_repr(self):
s = [test_items_seq()]
keys = [x[0] for x in s]
shuffle(keys)
s = zip(keys, keys)
tree = AnderssonTree(s)
gen = ("(%r, %r)" % (x, x) for x in keys)
spam = ", ".join(gen)
spam = "%s([%s])" % (tree.__class__.__name__, spam)
self.assertEqual(repr(tree), spam)
def test_iter_items(self):
s = test_items_seq()
tree = AnderssonTree(s)
items = [x for x in tree.iter_items()]
self.assertEqual(items, sorted(s))
def test_iter_items_reversed(self):
s = test_items_seq()
tree = AnderssonTree(s)
items = [x for x in tree.iter_items(reverse=True)]
self.assertEqual(items, sorted(s, reverse=True))
def test_traverse(self):
s = test_items_seq()
tree = AnderssonTree(s)
visited = set()
def visit(node):
visited.add(node.key)
tree.traverse(visit)
keys = set([x[0] for x in s])
self.assertEqual(keys, visited)
def test_remove(self):
for i in range(10):
s = test_items_seq(100)
tree = AnderssonTree(s)
self.check_aatree_properties(tree)
size = len(tree)
keys = [x[0] for x in s]
keyset = set(keys)
shuffle(keys)
for k in keys:
keyset.remove(k)
tree.remove(k)
size -= 1
self.assertEqual(len(tree), size)
self.assertEqual(set(tree.keys()), keyset)
self.check_aatree_properties(tree, dump=False)
def test_remove_root(self):
s = test_items_seq(1000)
tree = AnderssonTree(s)
self.check_aatree_properties(tree)
size = len(tree)
keys = [x[0] for x in s]
keyset = set(keys)
while len(keyset) > 0:
k = tree.root().key
keyset.remove(k)
tree.remove(k)
size -= 1
self.assertEqual(len(tree), size)
self.assertEqual(set(tree.keys()), keyset)
self.check_aatree_properties(tree)
if __name__ == '__main__':
unittest.main()