Browse Source

Implement some more tests; squash bugs in SmartList/_ListProxy

tags/v0.2
Ben Kurtovic 11 years ago
parent
commit
740db6ddfa
2 changed files with 173 additions and 145 deletions
  1. +36
    -8
      mwparserfromhell/smart_list.py
  2. +137
    -137
      tests/test_smart_list.py

+ 36
- 8
mwparserfromhell/smart_list.py View File

@@ -76,8 +76,8 @@ class SmartList(list):
def __getitem__(self, key): def __getitem__(self, key):
if not isinstance(key, slice): if not isinstance(key, slice):
return super(SmartList, self).__getitem__(key) return super(SmartList, self).__getitem__(key)
sliceinfo = [key.start or 0, maxsize if key.stop is None else key.stop,
key.step or 1]
keystop = maxsize if key.stop is None else key.stop
sliceinfo = [key.start or 0, keystop, key.step or 1]
child = _ListProxy(self, sliceinfo) child = _ListProxy(self, sliceinfo)
self._children[id(child)] = (child, sliceinfo) self._children[id(child)] = (child, sliceinfo)
return child return child
@@ -100,8 +100,8 @@ class SmartList(list):
def __delitem__(self, key): def __delitem__(self, key):
super(SmartList, self).__delitem__(key) super(SmartList, self).__delitem__(key)
if isinstance(key, slice): if isinstance(key, slice):
key = slice(key.start or 0,
maxsize if key.stop is None else key.stop)
keystop = maxsize if key.stop is None else key.stop
key = slice(key.start or 0, keystop)
else: else:
key = slice(key, key + 1) key = slice(key, key + 1)
diff = key.stop - key.start diff = key.stop - key.start
@@ -241,18 +241,36 @@ class _ListProxy(list):


def __setitem__(self, key, item): def __setitem__(self, key, item):
if isinstance(key, slice): if isinstance(key, slice):
adjusted = slice(key.start + self._start, key.stop + self._stop,
key.step)
keystart = (key.start or 0) + self._start
if key.stop is None or key.stop == maxsize:
keystop = self._stop
else:
keystop = key.stop + self._start
adjusted = slice(keystart, keystop, key.step)
self._parent[adjusted] = item self._parent[adjusted] = item
else: else:
length = len(self)
if key < 0:
key = length + key
if key < 0 or key >= length:
raise IndexError("list assignment index out of range")
self._parent[self._start + key] = item self._parent[self._start + key] = item


def __delitem__(self, key): def __delitem__(self, key):
if isinstance(key, slice): if isinstance(key, slice):
adjusted = slice(key.start + self._start, key.stop + self._stop,
key.step)
keystart = (key.start or 0) + self._start
if key.stop is None or key.stop == maxsize:
keystop = self._stop
else:
keystop = key.stop + self._start
adjusted = slice(keystart, keystop, key.step)
del self._parent[adjusted] del self._parent[adjusted]
else: else:
length = len(self)
if key < 0:
key = length + key
if key < 0 or key >= length:
raise IndexError("list assignment index out of range")
del self._parent[self._start + key] del self._parent[self._start + key]


def __iter__(self): def __iter__(self):
@@ -290,6 +308,16 @@ class _ListProxy(list):
self.extend(other) self.extend(other)
return self return self


def __mul__(self, other):
return SmartList(list(self) * other)

def __rmul__(self, other):
return SmartList(other * list(self))

def __imul__(self, other):
self.extend(list(self) * (other - 1))
return self

@property @property
def _start(self): def _start(self):
"""The starting index of this list, inclusive.""" """The starting index of this list, inclusive."""


+ 137
- 137
tests/test_smart_list.py View File

@@ -29,100 +29,15 @@ from mwparserfromhell.smart_list import SmartList, _ListProxy
class TestSmartList(unittest.TestCase): class TestSmartList(unittest.TestCase):
"""Test cases for the SmartList class and its child, _ListProxy.""" """Test cases for the SmartList class and its child, _ListProxy."""


def _test_list_methods(self, builder):
"""Run tests on the public methods of a list built with *builder*."""
list1 = builder(range(5))
list2 = builder(["foo"])
list3 = builder([("a", 5), ("d", 2), ("b", 8), ("c", 3)])

list1.append(5)
list1.append(1)
list1.append(2)
self.assertEquals([0, 1, 2, 3, 4, 5, 1, 2], list1)

self.assertEquals(0, list1.count(6))
self.assertEquals(2, list1.count(1))

list1.extend(range(5, 8))
self.assertEquals([0, 1, 2, 3, 4, 5, 1, 2, 5, 6, 7], list1)

self.assertEquals(1, list1.index(1))
self.assertEquals(6, list1.index(1, 3))
self.assertEquals(6, list1.index(1, 3, 7))
self.assertRaises(ValueError, list1.index, 1, 3, 5)

list1.insert(0, -1)
self.assertEquals([-1, 0, 1, 2, 3, 4, 5, 1, 2, 5, 6, 7], list1)
list1.insert(-1, 6.5)
self.assertEquals([-1, 0, 1, 2, 3, 4, 5, 1, 2, 5, 6, 6.5, 7], list1)
list1.insert(13, 8)
self.assertEquals([-1, 0, 1, 2, 3, 4, 5, 1, 2, 5, 6, 6.5, 7, 8], list1)

self.assertEquals(8, list1.pop())
self.assertEquals(7, list1.pop())
self.assertEquals([-1, 0, 1, 2, 3, 4, 5, 1, 2, 5, 6, 6.5], list1)
self.assertEquals(-1, list1.pop(0))
self.assertEquals(5, list1.pop(5))
self.assertEquals(6.5, list1.pop(-1))
self.assertEquals([0, 1, 2, 3, 4, 1, 2, 5, 6], list1)
self.assertEquals("foo", list2.pop())
self.assertRaises(IndexError, list2.pop)
self.assertEquals([], list2)

list1.remove(6)
self.assertEquals([0, 1, 2, 3, 4, 1, 2, 5], list1)
list1.remove(1)
self.assertEquals([0, 2, 3, 4, 1, 2, 5], list1)
list1.remove(1)
self.assertEquals([0, 2, 3, 4, 2, 5], list1)
self.assertRaises(ValueError, list1.remove, 1)

list1.reverse()
self.assertEquals([5, 2, 4, 3, 2, 0], list1)

list1.sort()
self.assertEquals([0, 2, 2, 3, 4, 5], list1)
list1.sort(reverse=True)
self.assertEquals([5, 4, 3, 2, 2, 0], list1)
list1.sort(cmp=lambda x, y: abs(3 - x) - abs(3 - y)) # Distance from 3
self.assertEquals([3, 4, 2, 2, 5, 0], list1)
list1.sort(cmp=lambda x, y: abs(3 - x) - abs(3 - y), reverse=True)
self.assertEquals([0, 5, 4, 2, 2, 3], list1)
list3.sort(key=lambda i: i[1])
self.assertEquals([("d", 2), ("c", 3), ("a", 5), ("b", 8)], list3)
list3.sort(key=lambda i: i[1], reverse=True)
self.assertEquals([("b", 8), ("a", 5), ("c", 3), ("d", 2)], list3)

def test_docs(self):
"""make sure the methods of SmartList/_ListProxy have docstrings"""
methods = ["append", "count", "extend", "index", "insert", "pop",
"remove", "reverse", "sort"]
for meth in methods:
expected = getattr(list, meth).__doc__
smartlist_doc = getattr(SmartList, meth).__doc__
listproxy_doc = getattr(_ListProxy, meth).__doc__
self.assertEquals(expected, smartlist_doc)
self.assertEquals(expected, listproxy_doc)

def test_doctest(self):
"""make sure the test embedded in SmartList's docstring passes"""
parent = SmartList([0, 1, 2, 3])
self.assertEquals([0, 1, 2, 3], parent)
child = parent[2:]
self.assertEquals([2, 3], child)
child.append(4)
self.assertEquals([2, 3, 4], child)
self.assertEquals([0, 1, 2, 3, 4], parent)

def test_parent_get_set_del(self):
"""make sure SmartList's getitem/setitem/delitem work"""
def _test_get_set_del_item(self, builder):
"""Run tests on __get/set/delitem__ of a list built with *builder*."""
def assign(L, s1, s2, s3, val): def assign(L, s1, s2, s3, val):
L[s1:s2:s3] = val L[s1:s2:s3] = val
def delete(L, s1): def delete(L, s1):
del L[s1] del L[s1]


list1 = SmartList([0, 1, 2, 3, "one", "two"])
list2 = SmartList(list(range(10)))
list1 = builder([0, 1, 2, 3, "one", "two"])
list2 = builder(list(range(10)))


self.assertEquals(1, list1[1]) self.assertEquals(1, list1[1])
self.assertEquals("one", list1[-2]) self.assertEquals("one", list1[-2])
@@ -152,9 +67,11 @@ class TestSmartList(unittest.TestCase):


list1[3] = 100 list1[3] = 100
self.assertEquals(100, list1[3]) self.assertEquals(100, list1[3])
list1[-3] = 101
self.assertEquals([0, 1, 2, 101, "one", "two"], list1)
list1[5:] = [6, 7, 8] list1[5:] = [6, 7, 8]
self.assertEquals([6, 7, 8], list1[5:]) self.assertEquals([6, 7, 8], list1[5:])
self.assertEquals([0, 1, 2, 100, "one", 6, 7, 8], list1)
self.assertEquals([0, 1, 2, 101, "one", 6, 7, 8], list1)
list1[2:4] = [-1, -2, -3, -4, -5] list1[2:4] = [-1, -2, -3, -4, -5]
self.assertEquals([0, 1, -1, -2, -3, -4, -5, "one", 6, 7, 8], list1) self.assertEquals([0, 1, -1, -2, -3, -4, -5, "one", 6, 7, 8], list1)
list1[0:-3] = [99] list1[0:-3] = [99]
@@ -185,10 +102,10 @@ class TestSmartList(unittest.TestCase):
del list2[2:8:2] del list2[2:8:2]
self.assertEquals([0, 1, 3, 5, 7, 8, 9], list2) self.assertEquals([0, 1, 3, 5, 7, 8, 9], list2)


def test_parent_add(self):
"""make sure SmartList's add/radd/iadd work"""
list1 = SmartList(range(5))
list2 = SmartList(range(5, 10))
def _test_add_radd_iadd(self, builder):
"""Run tests on __r/i/add__ of a list built with *builder*."""
list1 = builder(range(5))
list2 = builder(range(5, 10))
self.assertEquals([0, 1, 2, 3, 4, 5, 6], list1 + [5, 6]) self.assertEquals([0, 1, 2, 3, 4, 5, 6], list1 + [5, 6])
self.assertEquals([0, 1, 2, 3, 4], list1) self.assertEquals([0, 1, 2, 3, 4], list1)
self.assertEquals(list(range(10)), list1 + list2) self.assertEquals(list(range(10)), list1 + list2)
@@ -197,12 +114,12 @@ class TestSmartList(unittest.TestCase):
list1 += ["foo", "bar", "baz"] list1 += ["foo", "bar", "baz"]
self.assertEquals([0, 1, 2, 3, 4, "foo", "bar", "baz"], list1) self.assertEquals([0, 1, 2, 3, 4, "foo", "bar", "baz"], list1)


def test_parent_unaffected_magics(self):
"""sanity checks against SmartList features that were not modified"""
list1 = SmartList([0, 1, 2, 3, "one", "two"])
list2 = SmartList([])
list3 = SmartList([0, 2, 3, 4])
list4 = SmartList([0, 1, 2])
def _test_other_magic_methods(self, builder):
"""Run tests on other magic methods of a list built with *builder*."""
list1 = builder([0, 1, 2, 3, "one", "two"])
list2 = builder([])
list3 = builder([0, 2, 3, 4])
list4 = builder([0, 1, 2])


if py3k: if py3k:
self.assertEquals("[0, 1, 2, 3, 'one', 'two']", str(list1)) self.assertEquals("[0, 1, 2, 3, 'one', 'two']", str(list1))
@@ -284,47 +201,130 @@ class TestSmartList(unittest.TestCase):
list4 *= 2 list4 *= 2
self.assertEquals([0, 1, 2, 0, 1, 2], list4) self.assertEquals([0, 1, 2, 0, 1, 2], list4)


def _test_list_methods(self, builder):
"""Run tests on the public methods of a list built with *builder*."""
list1 = builder(range(5))
list2 = builder(["foo"])
list3 = builder([("a", 5), ("d", 2), ("b", 8), ("c", 3)])

list1.append(5)
list1.append(1)
list1.append(2)
self.assertEquals([0, 1, 2, 3, 4, 5, 1, 2], list1)

self.assertEquals(0, list1.count(6))
self.assertEquals(2, list1.count(1))

list1.extend(range(5, 8))
self.assertEquals([0, 1, 2, 3, 4, 5, 1, 2, 5, 6, 7], list1)

self.assertEquals(1, list1.index(1))
self.assertEquals(6, list1.index(1, 3))
self.assertEquals(6, list1.index(1, 3, 7))
self.assertRaises(ValueError, list1.index, 1, 3, 5)

list1.insert(0, -1)
self.assertEquals([-1, 0, 1, 2, 3, 4, 5, 1, 2, 5, 6, 7], list1)
list1.insert(-1, 6.5)
self.assertEquals([-1, 0, 1, 2, 3, 4, 5, 1, 2, 5, 6, 6.5, 7], list1)
list1.insert(13, 8)
self.assertEquals([-1, 0, 1, 2, 3, 4, 5, 1, 2, 5, 6, 6.5, 7, 8], list1)

self.assertEquals(8, list1.pop())
self.assertEquals(7, list1.pop())
self.assertEquals([-1, 0, 1, 2, 3, 4, 5, 1, 2, 5, 6, 6.5], list1)
self.assertEquals(-1, list1.pop(0))
self.assertEquals(5, list1.pop(5))
self.assertEquals(6.5, list1.pop(-1))
self.assertEquals([0, 1, 2, 3, 4, 1, 2, 5, 6], list1)
self.assertEquals("foo", list2.pop())
self.assertRaises(IndexError, list2.pop)
self.assertEquals([], list2)

list1.remove(6)
self.assertEquals([0, 1, 2, 3, 4, 1, 2, 5], list1)
list1.remove(1)
self.assertEquals([0, 2, 3, 4, 1, 2, 5], list1)
list1.remove(1)
self.assertEquals([0, 2, 3, 4, 2, 5], list1)
self.assertRaises(ValueError, list1.remove, 1)

list1.reverse()
self.assertEquals([5, 2, 4, 3, 2, 0], list1)

list1.sort()
self.assertEquals([0, 2, 2, 3, 4, 5], list1)
list1.sort(reverse=True)
self.assertEquals([5, 4, 3, 2, 2, 0], list1)
list1.sort(cmp=lambda x, y: abs(3 - x) - abs(3 - y)) # Distance from 3
self.assertEquals([3, 4, 2, 2, 5, 0], list1)
list1.sort(cmp=lambda x, y: abs(3 - x) - abs(3 - y), reverse=True)
self.assertEquals([0, 5, 4, 2, 2, 3], list1)
list3.sort(key=lambda i: i[1])
self.assertEquals([("d", 2), ("c", 3), ("a", 5), ("b", 8)], list3)
list3.sort(key=lambda i: i[1], reverse=True)
self.assertEquals([("b", 8), ("a", 5), ("c", 3), ("d", 2)], list3)

def test_docs(self):
"""make sure the methods of SmartList/_ListProxy have docstrings"""
methods = ["append", "count", "extend", "index", "insert", "pop",
"remove", "reverse", "sort"]
for meth in methods:
expected = getattr(list, meth).__doc__
smartlist_doc = getattr(SmartList, meth).__doc__
listproxy_doc = getattr(_ListProxy, meth).__doc__
self.assertEquals(expected, smartlist_doc)
self.assertEquals(expected, listproxy_doc)

def test_doctest(self):
"""make sure the test embedded in SmartList's docstring passes"""
parent = SmartList([0, 1, 2, 3])
self.assertEquals([0, 1, 2, 3], parent)
child = parent[2:]
self.assertEquals([2, 3], child)
child.append(4)
self.assertEquals([2, 3, 4], child)
self.assertEquals([0, 1, 2, 3, 4], parent)

def test_parent_get_set_del(self):
"""make sure SmartList's getitem/setitem/delitem work"""
self._test_get_set_del_item(lambda L: SmartList(L))

def test_parent_add(self):
"""make sure SmartList's add/radd/iadd work"""
self._test_add_radd_iadd(lambda L: SmartList(L))

def test_parent_unaffected_magics(self):
"""sanity checks against SmartList features that were not modified"""
self._test_other_magic_methods(lambda L: SmartList(L))

def test_parent_methods(self): def test_parent_methods(self):
"""make sure SmartList's non-magic methods work, like append()""" """make sure SmartList's non-magic methods work, like append()"""
self._test_list_methods(lambda L: SmartList(L)) self._test_list_methods(lambda L: SmartList(L))


def test_child_magics(self):
"""make sure _ListProxy's magically implemented features work"""
pass
# if py3k:
# __str__
# __bytes__
# else:
# __unicode__
# __str__
# __repr__
# __lt__
# __le__
# __eq__
# __ne__
# __gt__
# __ge__
# if py3k:
# __bool__
# else:
# __nonzero__
# __len__
# __getitem__
# __setitem__
# __delitem__
# __iter__
# __reversed__
# __contains__
# if not py3k:
# __getslice__
# __setslice__
# __delslice__
# __add__
# __radd__
# __iadd__
# __mul__
# __rmul__
# __imul__
def test_child_get_set_del(self):
"""make sure _ListProxy's getitem/setitem/delitem work"""
self._test_get_set_del_item(lambda L: SmartList(list(L))[:])
self._test_get_set_del_item(lambda L: SmartList([999] + list(L))[1:])
# self._test_get_set_del_item(lambda L: SmartList(list(L) + [999])[:-1])
# builder = lambda L: SmartList([101, 102] + list(L) + [201, 202])[2:-2]
# self._test_get_set_del_item(builder)

def test_child_add(self):
"""make sure _ListProxy's add/radd/iadd work"""
self._test_add_radd_iadd(lambda L: SmartList(list(L))[:])
self._test_add_radd_iadd(lambda L: SmartList([999] + list(L))[1:])
self._test_add_radd_iadd(lambda L: SmartList(list(L) + [999])[:-1])
builder = lambda L: SmartList([101, 102] + list(L) + [201, 202])[2:-2]
self._test_add_radd_iadd(builder)

def test_child_other_magics(self):
"""make sure _ListProxy's other magically implemented features work"""
self._test_other_magic_methods(lambda L: SmartList(list(L))[:])
self._test_other_magic_methods(lambda L: SmartList([999] + list(L))[1:])
self._test_other_magic_methods(lambda L: SmartList(list(L) + [999])[:-1])
builder = lambda L: SmartList([101, 102] + list(L) + [201, 202])[2:-2]
self._test_other_magic_methods(builder)


def test_child_methods(self): def test_child_methods(self):
"""make sure _ListProxy's non-magic methods work, like append()""" """make sure _ListProxy's non-magic methods work, like append()"""


Loading…
Cancel
Save