diff options
Diffstat (limited to 'blist/test/sortedlist_tests.py')
-rw-r--r-- | blist/test/sortedlist_tests.py | 615 |
1 files changed, 615 insertions, 0 deletions
diff --git a/blist/test/sortedlist_tests.py b/blist/test/sortedlist_tests.py new file mode 100644 index 0000000..1cff8b9 --- /dev/null +++ b/blist/test/sortedlist_tests.py @@ -0,0 +1,615 @@ +# This file based loosely on Python's list_tests.py. + +import sys +import collections, operator +import gc +import random +import blist +from blist.test import unittest +from blist.test import list_tests, seq_tests + +def CmpToKey(mycmp): + 'Convert a cmp= function into a key= function' + class K(object): + def __init__(self, obj): + self.obj = obj + def __lt__(self, other): + return mycmp(self.obj, other.obj) == -1 + return K + +class SortedBase(object): + def build_items(self, n): + return list(range(n)) + + def build_item(self, x): + return x + + def test_empty_repr(self): + self.assertEqual('%s()' % self.type2test.__name__, + repr(self.type2test())) + + def validate_comparison(self, instance): + if sys.version_info[0] < 3 and isinstance(instance, collections.Set): + ops = ['ne', 'or', 'and', 'xor', 'sub'] + else: + ops = ['lt', 'gt', 'le', 'ge', 'ne', 'or', 'and', 'xor', 'sub'] + operators = {} + for op in ops: + name = '__'+op+'__' + operators['__'+op+'__'] = getattr(operator, name) + + class Other(object): + def __init__(self): + self.right_side = False + def __eq__(self, other): + self.right_side = True + return True + __lt__ = __eq__ + __gt__ = __eq__ + __le__ = __eq__ + __ge__ = __eq__ + __ne__ = __eq__ + __ror__ = __eq__ + __rand__ = __eq__ + __rxor__ = __eq__ + __rsub__ = __eq__ + + for name, op in operators.items(): + if not hasattr(instance, name): continue + other = Other() + op(instance, other) + self.assertTrue(other.right_side,'Right side not called for %s.%s' + % (type(instance), name)) + + def test_right_side(self): + self.validate_comparison(self.type2test()) + + def test_delitem(self): + items = self.build_items(2) + a = self.type2test(items) + del a[1] + self.assertEqual(a, self.type2test(items[:1])) + del a[0] + self.assertEqual(a, self.type2test([])) + + a = self.type2test(items) + del a[-2] + self.assertEqual(a, self.type2test(items[1:])) + del a[-1] + self.assertEqual(a, self.type2test([])) + + a = self.type2test(items) + self.assertRaises(IndexError, a.__delitem__, -3) + self.assertRaises(IndexError, a.__delitem__, 2) + + a = self.type2test([]) + self.assertRaises(IndexError, a.__delitem__, 0) + + self.assertRaises(TypeError, a.__delitem__) + + def test_delslice(self): + items = self.build_items(2) + a = self.type2test(items) + del a[1:2] + del a[0:1] + self.assertEqual(a, self.type2test([])) + + a = self.type2test(items) + del a[1:2] + del a[0:1] + self.assertEqual(a, self.type2test([])) + + a = self.type2test(items) + del a[-2:-1] + self.assertEqual(a, self.type2test(items[1:])) + + a = self.type2test(items) + del a[-2:-1] + self.assertEqual(a, self.type2test(items[1:])) + + a = self.type2test(items) + del a[1:] + del a[:1] + self.assertEqual(a, self.type2test([])) + + a = self.type2test(items) + del a[1:] + del a[:1] + self.assertEqual(a, self.type2test([])) + + a = self.type2test(items) + del a[-1:] + self.assertEqual(a, self.type2test(items[:1])) + + a = self.type2test(items) + del a[-1:] + self.assertEqual(a, self.type2test(items[:1])) + + a = self.type2test(items) + del a[:] + self.assertEqual(a, self.type2test([])) + + def test_out_of_range(self): + u = self.type2test() + def del_test(): + del u[0] + self.assertRaises(IndexError, lambda: u[0]) + self.assertRaises(IndexError, del_test) + + def test_bad_mul(self): + u = self.type2test() + self.assertRaises(TypeError, lambda: u * 'q') + def imul_test(): + u = self.type2test() + u *= 'q' + self.assertRaises(TypeError, imul_test) + + def test_pop(self): + lst = self.build_items(20) + random.shuffle(lst) + u = self.type2test(lst) + for i in range(20-1,-1,-1): + x = u.pop(i) + self.assertEqual(x, i) + self.assertEqual(0, len(u)) + + def test_reversed(self): + lst = list(range(20)) + a = self.type2test(lst) + r = reversed(a) + self.assertEqual(list(r), list(range(19, -1, -1))) + if hasattr(r, '__next__'): # pragma: no cover + self.assertRaises(StopIteration, r.__next__) + else: # pragma: no cover + self.assertRaises(StopIteration, r.next) + self.assertEqual(self.type2test(reversed(self.type2test())), + self.type2test()) + + def test_mismatched_types(self): + class NotComparable: + def __lt__(self, other): # pragma: no cover + raise TypeError + def __cmp__(self, other): # pragma: no cover + raise TypeError + NotComparable = NotComparable() + + item = self.build_item(5) + sl = self.type2test() + sl.add(item) + self.assertRaises(TypeError, sl.add, NotComparable) + self.assertFalse(NotComparable in sl) + self.assertEqual(sl.count(NotComparable), 0) + sl.discard(NotComparable) + self.assertRaises(ValueError, sl.index, NotComparable) + + def test_order(self): + stuff = [self.build_item(random.randrange(1000000)) + for i in range(1000)] + if issubclass(self.type2test, collections.Set): + stuff = set(stuff) + sorted_stuff = list(sorted(stuff)) + u = self.type2test + + self.assertEqual(sorted_stuff, list(u(stuff))) + sl = u() + for x in stuff: + sl.add(x) + self.assertEqual(sorted_stuff, list(sl)) + x = sorted_stuff.pop(len(stuff)//2) + sl.discard(x) + self.assertEqual(sorted_stuff, list(sl)) + + def test_constructors(self): + # Based on the seq_test, but without adding incomparable types + # to the list. + + l0 = self.build_items(0) + l1 = self.build_items(1) + l2 = self.build_items(2) + + u = self.type2test() + u0 = self.type2test(l0) + u1 = self.type2test(l1) + u2 = self.type2test(l2) + + uu = self.type2test(u) + uu0 = self.type2test(u0) + uu1 = self.type2test(u1) + uu2 = self.type2test(u2) + + v = self.type2test(tuple(u)) + class OtherSeq: + def __init__(self, initseq): + self.__data = initseq + def __len__(self): + return len(self.__data) + def __getitem__(self, i): + return self.__data[i] + s = OtherSeq(u0) + v0 = self.type2test(s) + self.assertEqual(len(v0), len(s)) + + def test_sort(self): + # based on list_tests.py + lst = [1, 0] + lst = [self.build_item(x) for x in lst] + u = self.type2test(lst) + self.assertEqual(list(u), [0, 1]) + + lst = [2,1,0,-1,-2] + lst = [self.build_item(x) for x in lst] + u = self.type2test(lst) + self.assertEqual(list(u), [-2,-1,0,1,2]) + + lst = list(range(512)) + lst = [self.build_item(x) for x in lst] + a = self.type2test(reversed(lst)) + self.assertEqual(list(a), lst) + + def revcmp(a, b): # pragma: no cover + if a == b: + return 0 + elif a < b: + return 1 + else: # a > b + return -1 + u = self.type2test(u, key=CmpToKey(revcmp)) + self.assertEqual(list(u), [2,1,0,-1,-2]) + + # The following dumps core in unpatched Python 1.5: + def myComparison(x,y): + xmod, ymod = x%3, y%7 + if xmod == ymod: + return 0 + elif xmod < ymod: + return -1 + else: # xmod > ymod + return 1 + z = self.type2test(list(range(12)), key=CmpToKey(myComparison)) + + self.assertRaises(TypeError, self.type2test, 42, 42, 42, 42) + +class StrongSortedBase(SortedBase, seq_tests.CommonTest): + def not_applicable(self): + pass + test_repeat = not_applicable + test_imul = not_applicable + test_addmul = not_applicable + test_iadd = not_applicable + test_getslice = not_applicable + test_contains_order = not_applicable + test_contains_fake = not_applicable + + def test_constructors2(self): + s = "a seq" + vv = self.type2test(s) + self.assertEqual(len(vv), len(s)) + + # Create from various iteratables + for s in ("123", "", list(range(1000)), (1.5, 1.2), range(2000,2200,5)): + for g in (seq_tests.Sequence, seq_tests.IterFunc, + seq_tests.IterGen, seq_tests.itermulti, + seq_tests.iterfunc): + self.assertEqual(self.type2test(g(s)), self.type2test(s)) + self.assertEqual(self.type2test(seq_tests.IterFuncStop(s)), + self.type2test()) + self.assertEqual(self.type2test(c for c in "123"), + self.type2test("123")) + self.assertRaises(TypeError, self.type2test, + seq_tests.IterNextOnly(s)) + self.assertRaises(TypeError, self.type2test, + seq_tests.IterNoNext(s)) + self.assertRaises(ZeroDivisionError, self.type2test, + seq_tests.IterGenExc(s)) + +class weak_int: + def __init__(self, v): + self.value = v + def unwrap(self, other): + if isinstance(other, weak_int): + return other.value + return other + def __hash__(self): + return hash(self.value) + def __repr__(self): # pragma: no cover + return repr(self.value) + def __lt__(self, other): + return self.value < self.unwrap(other) + def __le__(self, other): + return self.value <= self.unwrap(other) + def __gt__(self, other): + return self.value > self.unwrap(other) + def __ge__(self, other): + return self.value >= self.unwrap(other) + def __eq__(self, other): + return self.value == self.unwrap(other) + def __ne__(self, other): + return self.value != self.unwrap(other) + def __mod__(self, other): + return self.value % self.unwrap(other) + def __neg__(self): + return weak_int(-self.value) + +class weak_manager(): + def __init__(self): + self.all = [weak_int(i) for i in range(10)] + self.live = [v for v in self.all if random.randrange(2)] + random.shuffle(self.all) + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + del self.all + gc.collect() + +class WeakSortedBase(SortedBase, unittest.TestCase): + def build_items(self, n): + return [weak_int(i) for i in range(n)] + + def build_item(self, x): + return weak_int(x) + + def test_collapse(self): + items = self.build_items(10) + u = self.type2test(items) + del items + gc.collect() + self.assertEqual(list(u), []) + + def test_sort(self): + # based on list_tests.py + x = [weak_int(i) for i in [1, 0]] + u = self.type2test(x) + self.assertEqual(list(u), list(reversed(x))) + + x = [weak_int(i) for i in [2,1,0,-1,-2]] + u = self.type2test(x) + self.assertEqual(list(u), list(reversed(x))) + + #y = [weak_int(i) for i in reversed(list(range(512)))] + #a = self.type2test(y) + #self.assertEqual(list(a), list(reversed(y))) + + def revcmp(a, b): # pragma: no cover + if a == b: + return 0 + elif a < b: + return 1 + else: # a > b + return -1 + u = self.type2test(u, key=CmpToKey(revcmp)) + self.assertEqual(list(u), x) + + # The following dumps core in unpatched Python 1.5: + def myComparison(x,y): + xmod, ymod = x%3, y%7 + if xmod == ymod: + return 0 + elif xmod < ymod: + return -1 + else: # xmod > ymod + return 1 + x = [weak_int(i) for i in range(12)] + z = self.type2test(x, key=CmpToKey(myComparison)) + + self.assertRaises(TypeError, self.type2test, 42, 42, 42, 42) + + def test_constructor(self): + with weak_manager() as m: + wsl = self.type2test(m.all) + self.assertEqual(list(wsl), m.live) + + def test_add(self): + with weak_manager() as m: + wsl = self.type2test() + for x in m.all: + wsl.add(x) + del x + self.assertEqual(list(wsl), m.live) + + def test_discard(self): + with weak_manager() as m: + wsl = self.type2test(m.all) + x = m.live.pop(len(m.live)//2) + wsl.discard(x) + self.assertEqual(list(wsl), m.live) + + def test_contains(self): + with weak_manager() as m: + wsl = self.type2test(m.all) + for x in m.live: + self.assertTrue(x in wsl) + self.assertFalse(weak_int(-1) in wsl) + + def test_iter(self): + with weak_manager() as m: + wsl = self.type2test(m.all) + for i, x in enumerate(wsl): + self.assertEqual(x, m.live[i]) + + def test_getitem(self): + with weak_manager() as m: + wsl = self.type2test(m.all) + for i in range(len(m.live)): + self.assertEqual(wsl[i], m.live[i]) + + def test_reversed(self): + with weak_manager() as m: + wsl = self.type2test(m.all) + r1 = list(reversed(wsl)) + r2 = list(reversed(m.live)) + self.assertEqual(r1, r2) + + all = [weak_int(i) for i in range(6)] + wsl = self.type2test(all) + del all[-1] + self.assertEqual(list(reversed(wsl)), list(reversed(all))) + + def test_index(self): + with weak_manager() as m: + wsl = self.type2test(m.all) + for x in m.live: + self.assertEqual(wsl[wsl.index(x)], x) + self.assertRaises(ValueError, wsl.index, weak_int(-1)) + + def test_count(self): + with weak_manager() as m: + wsl = self.type2test(m.all) + for x in m.live: + self.assertEqual(wsl.count(x), 1) + self.assertEqual(wsl.count(weak_int(-1)), 0) + + def test_getslice(self): + with weak_manager() as m: + wsl = self.type2test(m.all) + self.assertEqual(m.live, list(wsl[:])) + +class SortedListMixin: + def test_eq(self): + items = self.build_items(20) + u = self.type2test(items) + v = self.type2test(items, key=lambda x: -x) + self.assertNotEqual(u, v) + + def test_cmp(self): + items = self.build_items(20) + u = self.type2test(items) + low = u[:10] + high = u[10:] + self.assert_(low != high) + self.assert_(low == u[:10]) + self.assert_(low < high) + self.assert_(low <= high, str((low, high))) + self.assert_(high > low) + self.assert_(high >= low) + self.assertFalse(low == high) + self.assertFalse(high < low) + self.assertFalse(high <= low) + self.assertFalse(low > high) + self.assertFalse(low >= high) + + low = u[:5] + self.assert_(low != high) + self.assertFalse(low == high) + + def test_update(self): + items = self.build_items(20) + u = self.type2test() + u.update(items) + self.assertEqual(u, self.type2test(items)) + + def test_remove(self): + items = self.build_items(20) + u = self.type2test(items) + u.remove(items[-1]) + self.assertEqual(u, self.type2test(items[:19])) + self.assertRaises(ValueError, u.remove, items[-1]) + + def test_mul(self): + items = self.build_items(2) + u1 = self.type2test(items[:1]) + u2 = self.type2test(items) + self.assertEqual(self.type2test(), u2*0) + self.assertEqual(self.type2test(), 0*u2) + self.assertEqual(self.type2test(), u2*0) + self.assertEqual(self.type2test(), 0*u2) + self.assertEqual(u2, u2*1) + self.assertEqual(u2, 1*u2) + self.assertEqual(u2, u2*1) + self.assertEqual(u2, 1*u2) + self.assertEqual(self.type2test(items + items), u2*2) + self.assertEqual(self.type2test(items + items), 2*u2) + self.assertEqual(self.type2test(items + items + items), 3*u2) + self.assertEqual(self.type2test(items + items + items), u2*3) + + class subclass(self.type2test): + pass + u3 = subclass(items) + self.assertEqual(u3, u3*1) + self.assert_(u3 is not u3*1) + + def test_imul(self): + items = self.build_items(2) + items6 = items[:1]*3 + items[1:]*3 + u = self.type2test(items) + u *= 3 + self.assertEqual(u, self.type2test(items6)) + u *= 0 + self.assertEqual(u, self.type2test([])) + s = self.type2test([]) + oldid = id(s) + s *= 10 + self.assertEqual(id(s), oldid) + + def test_repr(self): + name = self.type2test.__name__ + u = self.type2test() + self.assertEqual(repr(u), '%s()' % name) + items = self.build_items(3) + u.update(items) + self.assertEqual(repr(u), '%s([0, 1, 2])' % name) + u = self.type2test() + u.update([u]) + self.assertEqual(repr(u), '%s([%s(...)])' % (name, name)) + + def test_bisect(self): + items = self.build_items(5) + del items[0] + del items[2] # We end up with [1, 2, 4] + u = self.type2test(items, key=lambda x: -x) # We end up with [4, 2, 1] + self.assertEqual(u.bisect_left(3), 1) + self.assertEqual(u.bisect(2), 2) # bisect == bisect_right + self.assertEqual(u.bisect_right(2), 2) + +class SortedSetMixin: + def test_duplicates(self): + u = self.type2test + ss = u() + stuff = [weak_int(random.randrange(100000)) for i in range(10)] + sorted_stuff = list(sorted(stuff)) + for x in stuff: + ss.add(x) + for x in stuff: + ss.add(x) + self.assertEqual(sorted_stuff, list(ss)) + x = sorted_stuff.pop(len(stuff)//2) + ss.discard(x) + self.assertEqual(sorted_stuff, list(ss)) + + def test_eq(self): + items = self.build_items(20) + u = self.type2test(items) + v = self.type2test(items, key=lambda x: -x) + self.assertEqual(u, v) + + def test_remove(self): + items = self.build_items(20) + u = self.type2test(items) + u.remove(items[-1]) + self.assertEqual(u, self.type2test(items[:19])) + self.assertRaises(KeyError, u.remove, items[-1]) + +class SortedListTest(StrongSortedBase, SortedListMixin): + type2test = blist.sortedlist + +class WeakSortedListTest(WeakSortedBase, SortedListMixin): + type2test = blist.weaksortedlist + + def test_advance(self): + items = [weak_int(0), weak_int(0)] + u = self.type2test(items) + del items[0] + gc.collect() + self.assertEqual(u.count(items[0]), 1) + +class SortedSetTest(StrongSortedBase, SortedSetMixin): + type2test = blist.sortedset + +class WeakSortedSetTest(WeakSortedBase, SortedSetMixin): + type2test = blist.weaksortedset + + def test_repr(self): + items = self.build_items(20) + u = self.type2test(items) + self.assertEqual(repr(u), 'weaksortedset(%s)' % repr(items)) |