Commits

Lenard Lindstrom  committed 88b3181

Add a test array type to pygame.tests.test_utils.arrinter

  • Participants
  • Parent commits 52175e8

Comments (0)

Files changed (1)

File test/test_utils/arrinter.py

 import sys
+import ctypes
 from ctypes import *
+import unittest
 
 __all__ = ['PAI_CONTIGUOUS', 'PAI_FORTRAN', 'PAI_ALIGNED',
            'PAI_NOTSWAPPED', 'PAI_WRITEABLE', 'PAI_ARR_HAS_DESCR',
                 else:
                     return True
         return False
+
+class Array(Exporter):
+    _ctypes = {('u', 1): c_uint8,
+               ('u', 2): c_uint16,
+               ('u', 4): c_uint32,
+               ('u', 8): c_uint64,
+               ('i', 1): c_int8,
+               ('i', 2): c_int16,
+               ('i', 4): c_int32,
+               ('i', 8): c_int64}
+
+    def __init__(self, *args, **kwds):
+        super(Array, self).__init__(*args, **kwds)
+        try:
+            if self.flags & PAI_NOTSWAPPED:
+                ct = self._ctypes[self.typekind, self.itemsize]
+            elif c_int.__ctype_le__ is c_int:
+                ct = self._ctypes[self.typekind, self.itemsize].__ctype_be__
+            else:
+                ct = self._ctypes[self.typekind, self.itemsize].__ctype_le__
+        except KeyError:
+            ct = c_uint8 * self.itemsize
+        self._ctype = ct
+        self._ctype_p = POINTER(ct)
+    def __getitem__(self, key):
+        return cast(self._addr_at(key), self._ctype_p)[0]
+    def __setitem__(self, key, value):
+        cast(self._addr_at(key), self._ctype_p)[0] = value
+    def _addr_at(self, key):
+        if not isinstance(key, tuple):
+            key = key,
+        if len(key) != self.nd:
+            raise ValueError("wrong number of indexes")
+        for i in range(self.nd):
+            if not (0 <= key[i] < self.shape[i]):
+                raise IndexError("index {} out of range".format(i))
+        return self.data + sum(i * s for i, s in zip(key, self.strides))
+
+class ExporterTest(unittest.TestCase):
+    def test_strides(self):
+        self.check_args(0, (10,), 'u', (2,), 20, 20, 2)
+        self.check_args(0, (5, 3), 'u', (6, 2), 30, 30, 2)
+        self.check_args(0, (7, 3, 5), 'u', (30, 10, 2), 210, 210, 2)
+        self.check_args(0, (13, 5, 11, 3), 'u', (330, 66, 6, 2), 4290, 4290, 2)
+        self.check_args(3, (7, 3, 5), 'i', (2, 14, 42), 210, 210, 2)
+        self.check_args(3, (7, 3, 5), 'x', (2, 16, 48), 210, 240, 2)
+        self.check_args(3, (13, 5, 11, 3), '%', (440, 88, 8, 2), 4290, 5720, 2)
+        self.check_args(3, (7, 5), '-', (15, 3), 105, 105, 3)
+        self.check_args(3, (7, 5), '*', (3, 21), 105, 105, 3)
+        self.check_args(3, (7, 5), ' ', (3, 24), 105, 120, 3)
+
+    def test_is_contiguous(self):
+        a = Exporter((10,), itemsize=2)
+        self.assertTrue(a.is_contiguous('C'))
+        self.assertTrue(a.is_contiguous('F'))
+        self.assertTrue(a.is_contiguous('A'))
+        a = Exporter((10, 4), itemsize=2)
+        self.assertTrue(a.is_contiguous('C'))
+        self.assertTrue(a.is_contiguous('A'))
+        self.assertFalse(a.is_contiguous('F'))
+        a = Exporter((13, 5, 11, 3), itemsize=2, strides=(330, 66, 6, 2))
+        self.assertTrue(a.is_contiguous('C'))
+        self.assertTrue(a.is_contiguous('A'))
+        self.assertFalse(a.is_contiguous('F'))
+        a = Exporter((10, 4), itemsize=2, strides=(2, 20))
+        self.assertTrue(a.is_contiguous('F'))
+        self.assertTrue(a.is_contiguous('A'))
+        self.assertFalse(a.is_contiguous('C'))
+        a = Exporter((13, 5, 11, 3), itemsize=2, strides=(2, 26, 130, 1430))
+        self.assertTrue(a.is_contiguous('F'))
+        self.assertTrue(a.is_contiguous('A'))
+        self.assertFalse(a.is_contiguous('C'))
+        a = Exporter((2, 11, 6, 4), itemsize=2, strides=(576, 48, 8, 2))
+        self.assertFalse(a.is_contiguous('A'))
+        a = Exporter((2, 11, 6, 4), itemsize=2, strides=(2, 4, 48, 288))
+        self.assertFalse(a.is_contiguous('A'))
+        a = Exporter((3, 2, 2), itemsize=2, strides=(16, 8, 4))
+        self.assertFalse(a.is_contiguous('A'))
+        a = Exporter((3, 2, 2), itemsize=2, strides=(4, 12, 24))
+        self.assertFalse(a.is_contiguous('A'))
+
+    def check_args(self, call_flags,
+                   shape, typekind, strides, length, bufsize, itemsize,
+                   offset=0):
+        if call_flags & 1:
+            typekind_arg = typekind
+        else:
+            typekind_arg = None
+        if call_flags & 2:
+            strides_arg = strides
+        else:
+            strides_arg = None
+        a = Exporter(shape, itemsize=itemsize, strides=strides_arg)
+        self.assertEqual(sizeof(a._data), bufsize)
+        self.assertEqual(a.data, ctypes.addressof(a._data) + offset)
+        m = ArrayInterface(a)
+        self.assertEqual(m.data, a.data)
+        self.assertEqual(m.itemsize, itemsize)
+        self.assertEqual(tuple(m.shape[0:m.nd]), shape)
+        self.assertEqual(tuple(m.strides[0:m.nd]), strides)
+
+class ArrayTest(unittest.TestCase):
+
+    def __init__(self, *args, **kwds):
+        unittest.TestCase.__init__(self, *args, **kwds)
+        self.a = Array((20, 15), 'i', 4)
+
+    def setUp(self):
+        # Every test starts with a zeroed array.
+        memset(self.a.data, 0, sizeof(self.a._data))
+
+    def test__addr_at(self):
+        a = self.a
+        self.assertEqual(a._addr_at((0, 0)), a.data)
+        self.assertEqual(a._addr_at((0, 1)), a.data + 4)
+        self.assertEqual(a._addr_at((1, 0)), a.data + 60)
+        self.assertEqual(a._addr_at((1, 1)), a.data + 64)
+
+    def test_indices(self):
+        a = self.a
+        self.assertEqual(a[0, 0], 0)
+        self.assertEqual(a[19, 0], 0)
+        self.assertEqual(a[0, 14], 0)
+        self.assertEqual(a[19, 14], 0)
+        self.assertEqual(a[5, 8], 0)
+        a[0, 0] = 12
+        a[5, 8] = 99
+        self.assertEqual(a[0, 0], 12)
+        self.assertEqual(a[5, 8], 99)
+        self.assertRaises(IndexError, a.__getitem__, (-1, 0))
+        self.assertRaises(IndexError, a.__getitem__, (0, -1))
+        self.assertRaises(IndexError, a.__getitem__, (20, 0))
+        self.assertRaises(IndexError, a.__getitem__, (0, 15))
+        self.assertRaises(ValueError, a.__getitem__, 0)
+        self.assertRaises(ValueError, a.__getitem__, (0, 0, 0))
+        a = Array((3,), 'i', 4)
+        a[1] = 333
+        self.assertEqual(a[1], 333)
+
+    def test_typekind(self):
+        a = Array((1,), 'i', 4)
+        self.assertTrue(a._ctype is c_int32)
+        self.assertTrue(a._ctype_p is POINTER(c_int32))
+        a = Array((1,), 'u', 4)
+        self.assertTrue(a._ctype is c_uint32)
+        self.assertTrue(a._ctype_p is POINTER(c_uint32))
+        a = Array((1,), 'f', 4) # float types unsupported: size system dependent
+        ct = a._ctype
+        self.assertTrue(issubclass(ct, ctypes.Array))
+        self.assertEqual(sizeof(ct), 4)
+
+    def test_itemsize(self):
+        for size in [1, 2, 4, 8]:
+            a = Array((1,), 'i', size)
+            ct = a._ctype
+            self.assertTrue(issubclass(ct, ctypes._SimpleCData))
+            self.assertEqual(sizeof(ct), size)
+
+    def test_oddball_itemsize(self):
+        for size in [3, 5, 6, 7, 9]:
+            a = Array((1,), 'i', size)
+            ct = a._ctype
+            self.assertTrue(issubclass(ct, ctypes.Array))
+            self.assertEqual(sizeof(ct), size)
+
+    def test_byteswapped(self):
+        a = Array((1,), 'u', 4, flags=(PAI_ALIGNED | PAI_WRITEABLE))
+        ct = a._ctype
+        self.assertTrue(ct is not c_uint32)
+        if sys.byteorder == 'little':
+            self.assertTrue(ct is c_uint32.__ctype_be__)
+        else:
+            self.assertTrue(ct is c_uint32.__ctype_le__)
+        i = 0xa0b0c0d
+        n = c_uint32(i)
+        a[0] = i
+        self.assertEqual(a[0], i)
+        self.assertEqual(a._data[0:4],
+                         cast(addressof(n), POINTER(c_uint8))[3:-1:-1])
+
+
+if __name__ == '__main__':
+    unittest.main()