| """Unit tests for the memoryview |
| |
| XXX We need more tests! Some tests are in test_bytes |
| """ |
| |
| import unittest |
| import sys |
| import gc |
| import weakref |
| import array |
| from test import test_support |
| import io |
| |
| |
| class AbstractMemoryTests: |
| source_bytes = b"abcdef" |
| |
| @property |
| def _source(self): |
| return self.source_bytes |
| |
| @property |
| def _types(self): |
| return filter(None, [self.ro_type, self.rw_type]) |
| |
| def check_getitem_with_type(self, tp): |
| item = self.getitem_type |
| b = tp(self._source) |
| oldrefcount = sys.getrefcount(b) |
| m = self._view(b) |
| self.assertEqual(m[0], item(b"a")) |
| self.assertIsInstance(m[0], bytes) |
| self.assertEqual(m[5], item(b"f")) |
| self.assertEqual(m[-1], item(b"f")) |
| self.assertEqual(m[-6], item(b"a")) |
| # Bounds checking |
| self.assertRaises(IndexError, lambda: m[6]) |
| self.assertRaises(IndexError, lambda: m[-7]) |
| self.assertRaises(IndexError, lambda: m[sys.maxsize]) |
| self.assertRaises(IndexError, lambda: m[-sys.maxsize]) |
| # Type checking |
| self.assertRaises(TypeError, lambda: m[None]) |
| self.assertRaises(TypeError, lambda: m[0.0]) |
| self.assertRaises(TypeError, lambda: m["a"]) |
| m = None |
| self.assertEqual(sys.getrefcount(b), oldrefcount) |
| |
| def test_getitem(self): |
| for tp in self._types: |
| self.check_getitem_with_type(tp) |
| |
| def test_iter(self): |
| for tp in self._types: |
| b = tp(self._source) |
| m = self._view(b) |
| self.assertEqual(list(m), [m[i] for i in range(len(m))]) |
| |
| def test_repr(self): |
| for tp in self._types: |
| b = tp(self._source) |
| m = self._view(b) |
| self.assertIsInstance(m.__repr__(), str) |
| |
| def test_setitem_readonly(self): |
| if not self.ro_type: |
| return |
| b = self.ro_type(self._source) |
| oldrefcount = sys.getrefcount(b) |
| m = self._view(b) |
| def setitem(value): |
| m[0] = value |
| self.assertRaises(TypeError, setitem, b"a") |
| self.assertRaises(TypeError, setitem, 65) |
| self.assertRaises(TypeError, setitem, memoryview(b"a")) |
| m = None |
| self.assertEqual(sys.getrefcount(b), oldrefcount) |
| |
| def test_setitem_writable(self): |
| if not self.rw_type: |
| return |
| tp = self.rw_type |
| b = self.rw_type(self._source) |
| oldrefcount = sys.getrefcount(b) |
| m = self._view(b) |
| m[0] = tp(b"0") |
| self._check_contents(tp, b, b"0bcdef") |
| m[1:3] = tp(b"12") |
| self._check_contents(tp, b, b"012def") |
| m[1:1] = tp(b"") |
| self._check_contents(tp, b, b"012def") |
| m[:] = tp(b"abcdef") |
| self._check_contents(tp, b, b"abcdef") |
| |
| # Overlapping copies of a view into itself |
| m[0:3] = m[2:5] |
| self._check_contents(tp, b, b"cdedef") |
| m[:] = tp(b"abcdef") |
| m[2:5] = m[0:3] |
| self._check_contents(tp, b, b"ababcf") |
| |
| def setitem(key, value): |
| m[key] = tp(value) |
| # Bounds checking |
| self.assertRaises(IndexError, setitem, 6, b"a") |
| self.assertRaises(IndexError, setitem, -7, b"a") |
| self.assertRaises(IndexError, setitem, sys.maxsize, b"a") |
| self.assertRaises(IndexError, setitem, -sys.maxsize, b"a") |
| # Wrong index/slice types |
| self.assertRaises(TypeError, setitem, 0.0, b"a") |
| self.assertRaises(TypeError, setitem, (0,), b"a") |
| self.assertRaises(TypeError, setitem, "a", b"a") |
| # Trying to resize the memory object |
| self.assertRaises(ValueError, setitem, 0, b"") |
| self.assertRaises(ValueError, setitem, 0, b"ab") |
| self.assertRaises(ValueError, setitem, slice(1,1), b"a") |
| self.assertRaises(ValueError, setitem, slice(0,2), b"a") |
| |
| m = None |
| self.assertEqual(sys.getrefcount(b), oldrefcount) |
| |
| def test_delitem(self): |
| for tp in self._types: |
| b = tp(self._source) |
| m = self._view(b) |
| with self.assertRaises(TypeError): |
| del m[1] |
| with self.assertRaises(TypeError): |
| del m[1:4] |
| |
| def test_tobytes(self): |
| for tp in self._types: |
| m = self._view(tp(self._source)) |
| b = m.tobytes() |
| # This calls self.getitem_type() on each separate byte of b"abcdef" |
| expected = b"".join( |
| self.getitem_type(c) for c in b"abcdef") |
| self.assertEqual(b, expected) |
| self.assertIsInstance(b, bytes) |
| |
| def test_tolist(self): |
| for tp in self._types: |
| m = self._view(tp(self._source)) |
| l = m.tolist() |
| self.assertEqual(l, map(ord, b"abcdef")) |
| |
| def test_compare(self): |
| # memoryviews can compare for equality with other objects |
| # having the buffer interface. |
| for tp in self._types: |
| m = self._view(tp(self._source)) |
| for tp_comp in self._types: |
| self.assertTrue(m == tp_comp(b"abcdef")) |
| self.assertFalse(m != tp_comp(b"abcdef")) |
| self.assertFalse(m == tp_comp(b"abcde")) |
| self.assertTrue(m != tp_comp(b"abcde")) |
| self.assertFalse(m == tp_comp(b"abcde1")) |
| self.assertTrue(m != tp_comp(b"abcde1")) |
| self.assertTrue(m == m) |
| self.assertTrue(m == m[:]) |
| self.assertTrue(m[0:6] == m[:]) |
| self.assertFalse(m[0:5] == m) |
| |
| # Comparison with objects which don't support the buffer API |
| self.assertFalse(m == u"abcdef") |
| self.assertTrue(m != u"abcdef") |
| self.assertFalse(u"abcdef" == m) |
| self.assertTrue(u"abcdef" != m) |
| |
| # Unordered comparisons are unimplemented, and therefore give |
| # arbitrary results (they raise a TypeError in py3k) |
| |
| def check_attributes_with_type(self, tp): |
| m = self._view(tp(self._source)) |
| self.assertEqual(m.format, self.format) |
| self.assertIsInstance(m.format, str) |
| self.assertEqual(m.itemsize, self.itemsize) |
| self.assertEqual(m.ndim, 1) |
| self.assertEqual(m.shape, (6,)) |
| self.assertEqual(len(m), 6) |
| self.assertEqual(m.strides, (self.itemsize,)) |
| self.assertEqual(m.suboffsets, None) |
| return m |
| |
| def test_attributes_readonly(self): |
| if not self.ro_type: |
| return |
| m = self.check_attributes_with_type(self.ro_type) |
| self.assertEqual(m.readonly, True) |
| |
| def test_attributes_writable(self): |
| if not self.rw_type: |
| return |
| m = self.check_attributes_with_type(self.rw_type) |
| self.assertEqual(m.readonly, False) |
| |
| # Disabled: unicode uses the old buffer API in 2.x |
| |
| #def test_getbuffer(self): |
| ## Test PyObject_GetBuffer() on a memoryview object. |
| #for tp in self._types: |
| #b = tp(self._source) |
| #oldrefcount = sys.getrefcount(b) |
| #m = self._view(b) |
| #oldviewrefcount = sys.getrefcount(m) |
| #s = unicode(m, "utf-8") |
| #self._check_contents(tp, b, s.encode("utf-8")) |
| #self.assertEqual(sys.getrefcount(m), oldviewrefcount) |
| #m = None |
| #self.assertEqual(sys.getrefcount(b), oldrefcount) |
| |
| def test_gc(self): |
| for tp in self._types: |
| if not isinstance(tp, type): |
| # If tp is a factory rather than a plain type, skip |
| continue |
| |
| class MySource(tp): |
| pass |
| class MyObject: |
| pass |
| |
| # Create a reference cycle through a memoryview object |
| b = MySource(tp(b'abc')) |
| m = self._view(b) |
| o = MyObject() |
| b.m = m |
| b.o = o |
| wr = weakref.ref(o) |
| b = m = o = None |
| # The cycle must be broken |
| gc.collect() |
| self.assertTrue(wr() is None, wr()) |
| |
| def test_writable_readonly(self): |
| # Issue #10451: memoryview incorrectly exposes a readonly |
| # buffer as writable causing a segfault if using mmap |
| tp = self.ro_type |
| if tp is None: |
| return |
| b = tp(self._source) |
| m = self._view(b) |
| i = io.BytesIO(b'ZZZZ') |
| self.assertRaises(TypeError, i.readinto, m) |
| |
| # Variations on source objects for the buffer: bytes-like objects, then arrays |
| # with itemsize > 1. |
| # NOTE: support for multi-dimensional objects is unimplemented. |
| |
| class BaseBytesMemoryTests(AbstractMemoryTests): |
| ro_type = bytes |
| rw_type = bytearray |
| getitem_type = bytes |
| itemsize = 1 |
| format = 'B' |
| |
| # Disabled: array.array() does not support the new buffer API in 2.x |
| |
| #class BaseArrayMemoryTests(AbstractMemoryTests): |
| #ro_type = None |
| #rw_type = lambda self, b: array.array('i', map(ord, b)) |
| #getitem_type = lambda self, b: array.array('i', map(ord, b)).tostring() |
| #itemsize = array.array('i').itemsize |
| #format = 'i' |
| |
| #def test_getbuffer(self): |
| ## XXX Test should be adapted for non-byte buffers |
| #pass |
| |
| #def test_tolist(self): |
| ## XXX NotImplementedError: tolist() only supports byte views |
| #pass |
| |
| |
| # Variations on indirection levels: memoryview, slice of memoryview, |
| # slice of slice of memoryview. |
| # This is important to test allocation subtleties. |
| |
| class BaseMemoryviewTests: |
| def _view(self, obj): |
| return memoryview(obj) |
| |
| def _check_contents(self, tp, obj, contents): |
| self.assertEqual(obj, tp(contents)) |
| |
| class BaseMemorySliceTests: |
| source_bytes = b"XabcdefY" |
| |
| def _view(self, obj): |
| m = memoryview(obj) |
| return m[1:7] |
| |
| def _check_contents(self, tp, obj, contents): |
| self.assertEqual(obj[1:7], tp(contents)) |
| |
| def test_refs(self): |
| for tp in self._types: |
| m = memoryview(tp(self._source)) |
| oldrefcount = sys.getrefcount(m) |
| m[1:2] |
| self.assertEqual(sys.getrefcount(m), oldrefcount) |
| |
| class BaseMemorySliceSliceTests: |
| source_bytes = b"XabcdefY" |
| |
| def _view(self, obj): |
| m = memoryview(obj) |
| return m[:7][1:] |
| |
| def _check_contents(self, tp, obj, contents): |
| self.assertEqual(obj[1:7], tp(contents)) |
| |
| |
| # Concrete test classes |
| |
| class BytesMemoryviewTest(unittest.TestCase, |
| BaseMemoryviewTests, BaseBytesMemoryTests): |
| |
| def test_constructor(self): |
| for tp in self._types: |
| ob = tp(self._source) |
| self.assertTrue(memoryview(ob)) |
| self.assertTrue(memoryview(object=ob)) |
| self.assertRaises(TypeError, memoryview) |
| self.assertRaises(TypeError, memoryview, ob, ob) |
| self.assertRaises(TypeError, memoryview, argument=ob) |
| self.assertRaises(TypeError, memoryview, ob, argument=True) |
| |
| #class ArrayMemoryviewTest(unittest.TestCase, |
| #BaseMemoryviewTests, BaseArrayMemoryTests): |
| |
| #def test_array_assign(self): |
| ## Issue #4569: segfault when mutating a memoryview with itemsize != 1 |
| #a = array.array('i', range(10)) |
| #m = memoryview(a) |
| #new_a = array.array('i', range(9, -1, -1)) |
| #m[:] = new_a |
| #self.assertEqual(a, new_a) |
| |
| |
| class BytesMemorySliceTest(unittest.TestCase, |
| BaseMemorySliceTests, BaseBytesMemoryTests): |
| pass |
| |
| #class ArrayMemorySliceTest(unittest.TestCase, |
| #BaseMemorySliceTests, BaseArrayMemoryTests): |
| #pass |
| |
| class BytesMemorySliceSliceTest(unittest.TestCase, |
| BaseMemorySliceSliceTests, BaseBytesMemoryTests): |
| pass |
| |
| #class ArrayMemorySliceSliceTest(unittest.TestCase, |
| #BaseMemorySliceSliceTests, BaseArrayMemoryTests): |
| #pass |
| |
| |
| def test_main(): |
| test_support.run_unittest(__name__) |
| |
| if __name__ == "__main__": |
| test_main() |