1import unittest
2from test.support import bigmemtest, _2G
3import sys
4from ctypes import *
5
6from ctypes.test import need_symbol
7
8formats = "bBhHiIlLqQfd"
9
10formats = c_byte, c_ubyte, c_short, c_ushort, c_int, c_uint, \
11          c_long, c_ulonglong, c_float, c_double, c_longdouble
12
13class ArrayTestCase(unittest.TestCase):
14    def test_simple(self):
15        # create classes holding simple numeric types, and check
16        # various properties.
17
18        init = list(range(15, 25))
19
20        for fmt in formats:
21            alen = len(init)
22            int_array = ARRAY(fmt, alen)
23
24            ia = int_array(*init)
25            # length of instance ok?
26            self.assertEqual(len(ia), alen)
27
28            # slot values ok?
29            values = [ia[i] for i in range(alen)]
30            self.assertEqual(values, init)
31
32            # out-of-bounds accesses should be caught
33            with self.assertRaises(IndexError): ia[alen]
34            with self.assertRaises(IndexError): ia[-alen-1]
35
36            # change the items
37            from operator import setitem
38            new_values = list(range(42, 42+alen))
39            [setitem(ia, n, new_values[n]) for n in range(alen)]
40            values = [ia[i] for i in range(alen)]
41            self.assertEqual(values, new_values)
42
43            # are the items initialized to 0?
44            ia = int_array()
45            values = [ia[i] for i in range(alen)]
46            self.assertEqual(values, [0] * alen)
47
48            # Too many initializers should be caught
49            self.assertRaises(IndexError, int_array, *range(alen*2))
50
51        CharArray = ARRAY(c_char, 3)
52
53        ca = CharArray(b"a", b"b", b"c")
54
55        # Should this work? It doesn't:
56        # CharArray("abc")
57        self.assertRaises(TypeError, CharArray, "abc")
58
59        self.assertEqual(ca[0], b"a")
60        self.assertEqual(ca[1], b"b")
61        self.assertEqual(ca[2], b"c")
62        self.assertEqual(ca[-3], b"a")
63        self.assertEqual(ca[-2], b"b")
64        self.assertEqual(ca[-1], b"c")
65
66        self.assertEqual(len(ca), 3)
67
68        # cannot delete items
69        from operator import delitem
70        self.assertRaises(TypeError, delitem, ca, 0)
71
72    def test_step_overflow(self):
73        a = (c_int * 5)()
74        a[3::sys.maxsize] = (1,)
75        self.assertListEqual(a[3::sys.maxsize], [1])
76        a = (c_char * 5)()
77        a[3::sys.maxsize] = b"A"
78        self.assertEqual(a[3::sys.maxsize], b"A")
79        a = (c_wchar * 5)()
80        a[3::sys.maxsize] = u"X"
81        self.assertEqual(a[3::sys.maxsize], u"X")
82
83    def test_numeric_arrays(self):
84
85        alen = 5
86
87        numarray = ARRAY(c_int, alen)
88
89        na = numarray()
90        values = [na[i] for i in range(alen)]
91        self.assertEqual(values, [0] * alen)
92
93        na = numarray(*[c_int()] * alen)
94        values = [na[i] for i in range(alen)]
95        self.assertEqual(values, [0]*alen)
96
97        na = numarray(1, 2, 3, 4, 5)
98        values = [i for i in na]
99        self.assertEqual(values, [1, 2, 3, 4, 5])
100
101        na = numarray(*map(c_int, (1, 2, 3, 4, 5)))
102        values = [i for i in na]
103        self.assertEqual(values, [1, 2, 3, 4, 5])
104
105    def test_classcache(self):
106        self.assertIsNot(ARRAY(c_int, 3), ARRAY(c_int, 4))
107        self.assertIs(ARRAY(c_int, 3), ARRAY(c_int, 3))
108
109    def test_from_address(self):
110        # Failed with 0.9.8, reported by JUrner
111        p = create_string_buffer(b"foo")
112        sz = (c_char * 3).from_address(addressof(p))
113        self.assertEqual(sz[:], b"foo")
114        self.assertEqual(sz[::], b"foo")
115        self.assertEqual(sz[::-1], b"oof")
116        self.assertEqual(sz[::3], b"f")
117        self.assertEqual(sz[1:4:2], b"o")
118        self.assertEqual(sz.value, b"foo")
119
120    @need_symbol('create_unicode_buffer')
121    def test_from_addressW(self):
122        p = create_unicode_buffer("foo")
123        sz = (c_wchar * 3).from_address(addressof(p))
124        self.assertEqual(sz[:], "foo")
125        self.assertEqual(sz[::], "foo")
126        self.assertEqual(sz[::-1], "oof")
127        self.assertEqual(sz[::3], "f")
128        self.assertEqual(sz[1:4:2], "o")
129        self.assertEqual(sz.value, "foo")
130
131    def test_cache(self):
132        # Array types are cached internally in the _ctypes extension,
133        # in a WeakValueDictionary.  Make sure the array type is
134        # removed from the cache when the itemtype goes away.  This
135        # test will not fail, but will show a leak in the testsuite.
136
137        # Create a new type:
138        class my_int(c_int):
139            pass
140        # Create a new array type based on it:
141        t1 = my_int * 1
142        t2 = my_int * 1
143        self.assertIs(t1, t2)
144
145    def test_subclass(self):
146        class T(Array):
147            _type_ = c_int
148            _length_ = 13
149        class U(T):
150            pass
151        class V(U):
152            pass
153        class W(V):
154            pass
155        class X(T):
156            _type_ = c_short
157        class Y(T):
158            _length_ = 187
159
160        for c in [T, U, V, W]:
161            self.assertEqual(c._type_, c_int)
162            self.assertEqual(c._length_, 13)
163            self.assertEqual(c()._type_, c_int)
164            self.assertEqual(c()._length_, 13)
165
166        self.assertEqual(X._type_, c_short)
167        self.assertEqual(X._length_, 13)
168        self.assertEqual(X()._type_, c_short)
169        self.assertEqual(X()._length_, 13)
170
171        self.assertEqual(Y._type_, c_int)
172        self.assertEqual(Y._length_, 187)
173        self.assertEqual(Y()._type_, c_int)
174        self.assertEqual(Y()._length_, 187)
175
176    def test_bad_subclass(self):
177        with self.assertRaises(AttributeError):
178            class T(Array):
179                pass
180        with self.assertRaises(AttributeError):
181            class T(Array):
182                _type_ = c_int
183        with self.assertRaises(AttributeError):
184            class T(Array):
185                _length_ = 13
186
187    def test_bad_length(self):
188        with self.assertRaises(ValueError):
189            class T(Array):
190                _type_ = c_int
191                _length_ = - sys.maxsize * 2
192        with self.assertRaises(ValueError):
193            class T(Array):
194                _type_ = c_int
195                _length_ = -1
196        with self.assertRaises(TypeError):
197            class T(Array):
198                _type_ = c_int
199                _length_ = 1.87
200        with self.assertRaises(OverflowError):
201            class T(Array):
202                _type_ = c_int
203                _length_ = sys.maxsize * 2
204
205    def test_zero_length(self):
206        # _length_ can be zero.
207        class T(Array):
208            _type_ = c_int
209            _length_ = 0
210
211    def test_empty_element_struct(self):
212        class EmptyStruct(Structure):
213            _fields_ = []
214
215        obj = (EmptyStruct * 2)()  # bpo37188: Floating point exception
216        self.assertEqual(sizeof(obj), 0)
217
218    def test_empty_element_array(self):
219        class EmptyArray(Array):
220            _type_ = c_int
221            _length_ = 0
222
223        obj = (EmptyArray * 2)()  # bpo37188: Floating point exception
224        self.assertEqual(sizeof(obj), 0)
225
226    def test_bpo36504_signed_int_overflow(self):
227        # The overflow check in PyCArrayType_new() could cause signed integer
228        # overflow.
229        with self.assertRaises(OverflowError):
230            c_char * sys.maxsize * 2
231
232    @unittest.skipUnless(sys.maxsize > 2**32, 'requires 64bit platform')
233    @bigmemtest(size=_2G, memuse=1, dry_run=False)
234    def test_large_array(self, size):
235        c_char * size
236
237if __name__ == '__main__':
238    unittest.main()
239