blob: 24d7293e893e3ddb30fe8c760775b6261892e7de [file] [log] [blame]
Thomas Hellerbabddfc2006-03-08 19:56:54 +00001from ctypes import *
2import unittest
Christian Heimes05e8be12008-02-23 18:30:17 +00003import struct
Thomas Hellerbabddfc2006-03-08 19:56:54 +00004
5def valid_ranges(*types):
6 # given a sequence of numeric types, collect their _type_
7 # attribute, which is a single format character compatible with
8 # the struct module, use the struct module to calculate the
9 # minimum and maximum value allowed for this format.
10 # Returns a list of (min, max) values.
11 result = []
12 for t in types:
13 fmt = t._type_
14 size = struct.calcsize(fmt)
Thomas Heller90044272007-07-11 12:25:26 +000015 a = struct.unpack(fmt, (b"\x00"*32)[:size])[0]
16 b = struct.unpack(fmt, (b"\xFF"*32)[:size])[0]
17 c = struct.unpack(fmt, (b"\x7F"+b"\x00"*32)[:size])[0]
18 d = struct.unpack(fmt, (b"\x80"+b"\xFF"*32)[:size])[0]
Thomas Hellerbabddfc2006-03-08 19:56:54 +000019 result.append((min(a, b, c, d), max(a, b, c, d)))
20 return result
21
Thomas Wouters00ee7ba2006-08-21 19:07:27 +000022ArgType = type(byref(c_int(0)))
Thomas Hellerbabddfc2006-03-08 19:56:54 +000023
24unsigned_types = [c_ubyte, c_ushort, c_uint, c_ulong]
25signed_types = [c_byte, c_short, c_int, c_long, c_longlong]
26
Guido van Rossumd8faa362007-04-27 19:54:29 +000027bool_types = []
28
Thomas Hellerbabddfc2006-03-08 19:56:54 +000029float_types = [c_double, c_float]
30
31try:
32 c_ulonglong
33 c_longlong
34except NameError:
35 pass
36else:
37 unsigned_types.append(c_ulonglong)
38 signed_types.append(c_longlong)
Tim Peterse8d09e52006-03-09 01:15:05 +000039
Guido van Rossumd8faa362007-04-27 19:54:29 +000040try:
41 c_bool
42except NameError:
43 pass
44else:
45 bool_types.append(c_bool)
46
Thomas Hellerbabddfc2006-03-08 19:56:54 +000047unsigned_ranges = valid_ranges(*unsigned_types)
48signed_ranges = valid_ranges(*signed_types)
Guido van Rossumd8faa362007-04-27 19:54:29 +000049bool_values = [True, False, 0, 1, -1, 5000, 'test', [], [1]]
Thomas Hellerbabddfc2006-03-08 19:56:54 +000050
51################################################################
52
53class NumberTestCase(unittest.TestCase):
Tim Peterse8d09e52006-03-09 01:15:05 +000054
Thomas Hellerbabddfc2006-03-08 19:56:54 +000055 def test_default_init(self):
56 # default values are set to zero
57 for t in signed_types + unsigned_types + float_types:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +000058 self.assertEqual(t().value, 0)
Thomas Hellerbabddfc2006-03-08 19:56:54 +000059
60 def test_unsigned_values(self):
61 # the value given to the constructor is available
62 # as the 'value' attribute
63 for t, (l, h) in zip(unsigned_types, unsigned_ranges):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +000064 self.assertEqual(t(l).value, l)
65 self.assertEqual(t(h).value, h)
Thomas Hellerbabddfc2006-03-08 19:56:54 +000066
67 def test_signed_values(self):
68 # see above
69 for t, (l, h) in zip(signed_types, signed_ranges):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +000070 self.assertEqual(t(l).value, l)
71 self.assertEqual(t(h).value, h)
Thomas Hellerbabddfc2006-03-08 19:56:54 +000072
Guido van Rossumd8faa362007-04-27 19:54:29 +000073 def test_bool_values(self):
74 from operator import truth
75 for t, v in zip(bool_types, bool_values):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +000076 self.assertEqual(t(v).value, truth(v))
Guido van Rossumd8faa362007-04-27 19:54:29 +000077
Thomas Hellerbabddfc2006-03-08 19:56:54 +000078 def test_typeerror(self):
79 # Only numbers are allowed in the contructor,
80 # otherwise TypeError is raised
81 for t in signed_types + unsigned_types + float_types:
82 self.assertRaises(TypeError, t, "")
83 self.assertRaises(TypeError, t, None)
84
85## def test_valid_ranges(self):
86## # invalid values of the correct type
87## # raise ValueError (not OverflowError)
88## for t, (l, h) in zip(unsigned_types, unsigned_ranges):
89## self.assertRaises(ValueError, t, l-1)
90## self.assertRaises(ValueError, t, h+1)
91
92 def test_from_param(self):
93 # the from_param class method attribute always
94 # returns PyCArgObject instances
95 for t in signed_types + unsigned_types + float_types:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +000096 self.assertEqual(ArgType, type(t.from_param(0)))
Thomas Hellerbabddfc2006-03-08 19:56:54 +000097
Thomas Hellerbabddfc2006-03-08 19:56:54 +000098 def test_byref(self):
99 # calling byref returns also a PyCArgObject instance
Guido van Rossumd8faa362007-04-27 19:54:29 +0000100 for t in signed_types + unsigned_types + float_types + bool_types:
Thomas Hellerbabddfc2006-03-08 19:56:54 +0000101 parm = byref(t())
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000102 self.assertEqual(ArgType, type(parm))
Thomas Hellerbabddfc2006-03-08 19:56:54 +0000103
104
105 def test_floats(self):
106 # c_float and c_double can be created from
107 # Python int, long and float
Christian Heimes969fe572008-01-25 11:23:10 +0000108 class FloatLike(object):
109 def __float__(self):
110 return 2.0
111 f = FloatLike()
Thomas Hellerbabddfc2006-03-08 19:56:54 +0000112 for t in float_types:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000113 self.assertEqual(t(2.0).value, 2.0)
114 self.assertEqual(t(2).value, 2.0)
115 self.assertEqual(t(2).value, 2.0)
116 self.assertEqual(t(f).value, 2.0)
Thomas Hellerbabddfc2006-03-08 19:56:54 +0000117
118 def test_integers(self):
Christian Heimes969fe572008-01-25 11:23:10 +0000119 class FloatLike(object):
120 def __float__(self):
121 return 2.0
122 f = FloatLike()
123 class IntLike(object):
124 def __int__(self):
125 return 2
126 i = IntLike()
127 # integers cannot be constructed from floats,
128 # but from integer-like objects
Thomas Hellerbabddfc2006-03-08 19:56:54 +0000129 for t in signed_types + unsigned_types:
130 self.assertRaises(TypeError, t, 3.14)
Christian Heimes969fe572008-01-25 11:23:10 +0000131 self.assertRaises(TypeError, t, f)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000132 self.assertEqual(t(i).value, 2)
Thomas Hellerbabddfc2006-03-08 19:56:54 +0000133
134 def test_sizes(self):
Guido van Rossumd8faa362007-04-27 19:54:29 +0000135 for t in signed_types + unsigned_types + float_types + bool_types:
Guido van Rossumcd16bf62007-06-13 18:07:49 +0000136 try:
137 size = struct.calcsize(t._type_)
138 except struct.error:
139 continue
Thomas Hellerbabddfc2006-03-08 19:56:54 +0000140 # sizeof of the type...
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000141 self.assertEqual(sizeof(t), size)
Thomas Hellerbabddfc2006-03-08 19:56:54 +0000142 # and sizeof of an instance
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000143 self.assertEqual(sizeof(t()), size)
Thomas Hellerbabddfc2006-03-08 19:56:54 +0000144
145 def test_alignments(self):
146 for t in signed_types + unsigned_types + float_types:
147 code = t._type_ # the typecode
148 align = struct.calcsize("c%c" % code) - struct.calcsize(code)
149
150 # alignment of the type...
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000151 self.assertEqual((code, alignment(t)),
Thomas Hellerbabddfc2006-03-08 19:56:54 +0000152 (code, align))
153 # and alignment of an instance
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000154 self.assertEqual((code, alignment(t())),
Thomas Hellerbabddfc2006-03-08 19:56:54 +0000155 (code, align))
Tim Peterse8d09e52006-03-09 01:15:05 +0000156
Thomas Hellerbabddfc2006-03-08 19:56:54 +0000157 def test_int_from_address(self):
158 from array import array
159 for t in signed_types + unsigned_types:
160 # the array module doesn't suppport all format codes
161 # (no 'q' or 'Q')
162 try:
163 array(t._type_)
164 except ValueError:
165 continue
166 a = array(t._type_, [100])
167
168 # v now is an integer at an 'external' memory location
169 v = t.from_address(a.buffer_info()[0])
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000170 self.assertEqual(v.value, a[0])
171 self.assertEqual(type(v), t)
Thomas Hellerbabddfc2006-03-08 19:56:54 +0000172
173 # changing the value at the memory location changes v's value also
174 a[0] = 42
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000175 self.assertEqual(v.value, a[0])
Tim Peterse8d09e52006-03-09 01:15:05 +0000176
Thomas Hellerbabddfc2006-03-08 19:56:54 +0000177
178 def test_float_from_address(self):
179 from array import array
180 for t in float_types:
181 a = array(t._type_, [3.14])
182 v = t.from_address(a.buffer_info()[0])
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000183 self.assertEqual(v.value, a[0])
184 self.assertTrue(type(v) is t)
Thomas Hellerbabddfc2006-03-08 19:56:54 +0000185 a[0] = 2.3456e17
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000186 self.assertEqual(v.value, a[0])
187 self.assertTrue(type(v) is t)
Thomas Hellerbabddfc2006-03-08 19:56:54 +0000188
189 def test_char_from_address(self):
190 from ctypes import c_char
191 from array import array
Tim Peterse8d09e52006-03-09 01:15:05 +0000192
Thomas Heller90044272007-07-11 12:25:26 +0000193 a = array('b', [0])
194 a[0] = ord('x')
Thomas Hellerbabddfc2006-03-08 19:56:54 +0000195 v = c_char.from_address(a.buffer_info()[0])
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000196 self.assertEqual(v.value, b'x')
197 self.assertTrue(type(v) is c_char)
Thomas Hellerbabddfc2006-03-08 19:56:54 +0000198
Thomas Heller90044272007-07-11 12:25:26 +0000199 a[0] = ord('?')
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000200 self.assertEqual(v.value, b'?')
Thomas Hellerbabddfc2006-03-08 19:56:54 +0000201
Guido van Rossumd8faa362007-04-27 19:54:29 +0000202 # array does not support c_bool / 't'
203 # def test_bool_from_address(self):
204 # from ctypes import c_bool
205 # from array import array
206 # a = array(c_bool._type_, [True])
207 # v = t.from_address(a.buffer_info()[0])
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000208 # self.assertEqual(v.value, a[0])
209 # self.assertEqual(type(v) is t)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000210 # a[0] = False
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000211 # self.assertEqual(v.value, a[0])
212 # self.assertEqual(type(v) is t)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000213
Thomas Hellerbabddfc2006-03-08 19:56:54 +0000214 def test_init(self):
215 # c_int() can be initialized from Python's int, and c_int.
216 # Not from c_long or so, which seems strange, abd should
217 # probably be changed:
218 self.assertRaises(TypeError, c_int, c_long(42))
219
220## def test_perf(self):
221## check_perf()
Tim Peterse8d09e52006-03-09 01:15:05 +0000222
Thomas Hellerbabddfc2006-03-08 19:56:54 +0000223from ctypes import _SimpleCData
224class c_int_S(_SimpleCData):
225 _type_ = "i"
226 __slots__ = []
227
228def run_test(rep, msg, func, arg=None):
229## items = [None] * rep
230 items = range(rep)
231 from time import clock
232 if arg is not None:
233 start = clock()
234 for i in items:
235 func(arg); func(arg); func(arg); func(arg); func(arg)
236 stop = clock()
237 else:
238 start = clock()
239 for i in items:
240 func(); func(); func(); func(); func()
241 stop = clock()
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000242 print("%15s: %.2f us" % (msg, ((stop-start)*1e6/5/rep)))
Thomas Hellerbabddfc2006-03-08 19:56:54 +0000243
244def check_perf():
245 # Construct 5 objects
246 from ctypes import c_int
247
248 REP = 200000
249
250 run_test(REP, "int()", int)
251 run_test(REP, "int(999)", int)
252 run_test(REP, "c_int()", c_int)
253 run_test(REP, "c_int(999)", c_int)
254 run_test(REP, "c_int_S()", c_int_S)
255 run_test(REP, "c_int_S(999)", c_int_S)
256
257# Python 2.3 -OO, win2k, P4 700 MHz:
258#
259# int(): 0.87 us
260# int(999): 0.87 us
261# c_int(): 3.35 us
262# c_int(999): 3.34 us
263# c_int_S(): 3.23 us
Tim Peterse8d09e52006-03-09 01:15:05 +0000264# c_int_S(999): 3.24 us
Thomas Hellerbabddfc2006-03-08 19:56:54 +0000265
266# Python 2.2 -OO, win2k, P4 700 MHz:
267#
268# int(): 0.89 us
269# int(999): 0.89 us
270# c_int(): 9.99 us
271# c_int(999): 10.02 us
272# c_int_S(): 9.87 us
273# c_int_S(999): 9.85 us
274
275if __name__ == '__main__':
276## check_perf()
277 unittest.main()