• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1import array
2import struct
3import sys
4import unittest
5from operator import truth
6from ctypes import (byref, sizeof, alignment,
7                    c_char, c_byte, c_ubyte, c_short, c_ushort, c_int, c_uint,
8                    c_long, c_ulong, c_longlong, c_ulonglong,
9                    c_float, c_double, c_longdouble, c_bool)
10
11
12def valid_ranges(*types):
13    # given a sequence of numeric types, collect their _type_
14    # attribute, which is a single format character compatible with
15    # the struct module, use the struct module to calculate the
16    # minimum and maximum value allowed for this format.
17    # Returns a list of (min, max) values.
18    result = []
19    for t in types:
20        fmt = t._type_
21        size = struct.calcsize(fmt)
22        a = struct.unpack(fmt, (b"\x00"*32)[:size])[0]
23        b = struct.unpack(fmt, (b"\xFF"*32)[:size])[0]
24        c = struct.unpack(fmt, (b"\x7F"+b"\x00"*32)[:size])[0]
25        d = struct.unpack(fmt, (b"\x80"+b"\xFF"*32)[:size])[0]
26        result.append((min(a, b, c, d), max(a, b, c, d)))
27    return result
28
29
30ArgType = type(byref(c_int(0)))
31
32unsigned_types = [c_ubyte, c_ushort, c_uint, c_ulong, c_ulonglong]
33signed_types = [c_byte, c_short, c_int, c_long, c_longlong]
34bool_types = [c_bool]
35float_types = [c_double, c_float]
36
37unsigned_ranges = valid_ranges(*unsigned_types)
38signed_ranges = valid_ranges(*signed_types)
39bool_values = [True, False, 0, 1, -1, 5000, 'test', [], [1]]
40
41
42class NumberTestCase(unittest.TestCase):
43
44    def test_default_init(self):
45        # default values are set to zero
46        for t in signed_types + unsigned_types + float_types:
47            self.assertEqual(t().value, 0)
48
49    def test_unsigned_values(self):
50        # the value given to the constructor is available
51        # as the 'value' attribute
52        for t, (l, h) in zip(unsigned_types, unsigned_ranges):
53            self.assertEqual(t(l).value, l)
54            self.assertEqual(t(h).value, h)
55
56    def test_signed_values(self):
57        # see above
58        for t, (l, h) in zip(signed_types, signed_ranges):
59            self.assertEqual(t(l).value, l)
60            self.assertEqual(t(h).value, h)
61
62    def test_bool_values(self):
63        for t, v in zip(bool_types, bool_values):
64            self.assertEqual(t(v).value, truth(v))
65
66    def test_typeerror(self):
67        # Only numbers are allowed in the constructor,
68        # otherwise TypeError is raised
69        for t in signed_types + unsigned_types + float_types:
70            self.assertRaises(TypeError, t, "")
71            self.assertRaises(TypeError, t, None)
72
73    def test_from_param(self):
74        # the from_param class method attribute always
75        # returns PyCArgObject instances
76        for t in signed_types + unsigned_types + float_types:
77            self.assertEqual(ArgType, type(t.from_param(0)))
78
79    def test_byref(self):
80        # calling byref returns also a PyCArgObject instance
81        for t in signed_types + unsigned_types + float_types + bool_types:
82            parm = byref(t())
83            self.assertEqual(ArgType, type(parm))
84
85
86    def test_floats(self):
87        # c_float and c_double can be created from
88        # Python int and float
89        class FloatLike:
90            def __float__(self):
91                return 2.0
92        f = FloatLike()
93        for t in float_types:
94            self.assertEqual(t(2.0).value, 2.0)
95            self.assertEqual(t(2).value, 2.0)
96            self.assertEqual(t(2).value, 2.0)
97            self.assertEqual(t(f).value, 2.0)
98
99    def test_integers(self):
100        class FloatLike:
101            def __float__(self):
102                return 2.0
103        f = FloatLike()
104        class IntLike:
105            def __int__(self):
106                return 2
107        d = IntLike()
108        class IndexLike:
109            def __index__(self):
110                return 2
111        i = IndexLike()
112        # integers cannot be constructed from floats,
113        # but from integer-like objects
114        for t in signed_types + unsigned_types:
115            self.assertRaises(TypeError, t, 3.14)
116            self.assertRaises(TypeError, t, f)
117            self.assertRaises(TypeError, t, d)
118            self.assertEqual(t(i).value, 2)
119
120    def test_sizes(self):
121        for t in signed_types + unsigned_types + float_types + bool_types:
122            try:
123                size = struct.calcsize(t._type_)
124            except struct.error:
125                continue
126            # sizeof of the type...
127            self.assertEqual(sizeof(t), size)
128            # and sizeof of an instance
129            self.assertEqual(sizeof(t()), size)
130
131    def test_alignments(self):
132        for t in signed_types + unsigned_types + float_types:
133            code = t._type_ # the typecode
134            align = struct.calcsize("c%c" % code) - struct.calcsize(code)
135
136            # alignment of the type...
137            self.assertEqual((code, alignment(t)),
138                                 (code, align))
139            # and alignment of an instance
140            self.assertEqual((code, alignment(t())),
141                                 (code, align))
142
143    def test_int_from_address(self):
144        for t in signed_types + unsigned_types:
145            # the array module doesn't support all format codes
146            # (no 'q' or 'Q')
147            try:
148                array.array(t._type_)
149            except ValueError:
150                continue
151            a = array.array(t._type_, [100])
152
153            # v now is an integer at an 'external' memory location
154            v = t.from_address(a.buffer_info()[0])
155            self.assertEqual(v.value, a[0])
156            self.assertEqual(type(v), t)
157
158            # changing the value at the memory location changes v's value also
159            a[0] = 42
160            self.assertEqual(v.value, a[0])
161
162
163    def test_float_from_address(self):
164        for t in float_types:
165            a = array.array(t._type_, [3.14])
166            v = t.from_address(a.buffer_info()[0])
167            self.assertEqual(v.value, a[0])
168            self.assertIs(type(v), t)
169            a[0] = 2.3456e17
170            self.assertEqual(v.value, a[0])
171            self.assertIs(type(v), t)
172
173    def test_char_from_address(self):
174        a = array.array('b', [0])
175        a[0] = ord('x')
176        v = c_char.from_address(a.buffer_info()[0])
177        self.assertEqual(v.value, b'x')
178        self.assertIs(type(v), c_char)
179
180        a[0] = ord('?')
181        self.assertEqual(v.value, b'?')
182
183    def test_init(self):
184        # c_int() can be initialized from Python's int, and c_int.
185        # Not from c_long or so, which seems strange, abc should
186        # probably be changed:
187        self.assertRaises(TypeError, c_int, c_long(42))
188
189    def test_float_overflow(self):
190        big_int = int(sys.float_info.max) * 2
191        for t in float_types + [c_longdouble]:
192            self.assertRaises(OverflowError, t, big_int)
193            if (hasattr(t, "__ctype_be__")):
194                self.assertRaises(OverflowError, t.__ctype_be__, big_int)
195            if (hasattr(t, "__ctype_le__")):
196                self.assertRaises(OverflowError, t.__ctype_le__, big_int)
197
198
199if __name__ == '__main__':
200    unittest.main()
201