Thomas Heller | babddfc | 2006-03-08 19:56:54 +0000 | [diff] [blame] | 1 | from ctypes import * |
| 2 | import unittest |
Christian Heimes | 05e8be1 | 2008-02-23 18:30:17 +0000 | [diff] [blame] | 3 | import struct |
Thomas Heller | babddfc | 2006-03-08 19:56:54 +0000 | [diff] [blame] | 4 | |
| 5 | def 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 Heller | 9004427 | 2007-07-11 12:25:26 +0000 | [diff] [blame] | 15 | 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 Heller | babddfc | 2006-03-08 19:56:54 +0000 | [diff] [blame] | 19 | result.append((min(a, b, c, d), max(a, b, c, d))) |
| 20 | return result |
| 21 | |
Thomas Wouters | 00ee7ba | 2006-08-21 19:07:27 +0000 | [diff] [blame] | 22 | ArgType = type(byref(c_int(0))) |
Thomas Heller | babddfc | 2006-03-08 19:56:54 +0000 | [diff] [blame] | 23 | |
| 24 | unsigned_types = [c_ubyte, c_ushort, c_uint, c_ulong] |
| 25 | signed_types = [c_byte, c_short, c_int, c_long, c_longlong] |
| 26 | |
Guido van Rossum | d8faa36 | 2007-04-27 19:54:29 +0000 | [diff] [blame] | 27 | bool_types = [] |
| 28 | |
Thomas Heller | babddfc | 2006-03-08 19:56:54 +0000 | [diff] [blame] | 29 | float_types = [c_double, c_float] |
| 30 | |
| 31 | try: |
| 32 | c_ulonglong |
| 33 | c_longlong |
| 34 | except NameError: |
| 35 | pass |
| 36 | else: |
| 37 | unsigned_types.append(c_ulonglong) |
| 38 | signed_types.append(c_longlong) |
Tim Peters | e8d09e5 | 2006-03-09 01:15:05 +0000 | [diff] [blame] | 39 | |
Guido van Rossum | d8faa36 | 2007-04-27 19:54:29 +0000 | [diff] [blame] | 40 | try: |
| 41 | c_bool |
| 42 | except NameError: |
| 43 | pass |
| 44 | else: |
| 45 | bool_types.append(c_bool) |
| 46 | |
Thomas Heller | babddfc | 2006-03-08 19:56:54 +0000 | [diff] [blame] | 47 | unsigned_ranges = valid_ranges(*unsigned_types) |
| 48 | signed_ranges = valid_ranges(*signed_types) |
Guido van Rossum | d8faa36 | 2007-04-27 19:54:29 +0000 | [diff] [blame] | 49 | bool_values = [True, False, 0, 1, -1, 5000, 'test', [], [1]] |
Thomas Heller | babddfc | 2006-03-08 19:56:54 +0000 | [diff] [blame] | 50 | |
| 51 | ################################################################ |
| 52 | |
| 53 | class NumberTestCase(unittest.TestCase): |
Tim Peters | e8d09e5 | 2006-03-09 01:15:05 +0000 | [diff] [blame] | 54 | |
Thomas Heller | babddfc | 2006-03-08 19:56:54 +0000 | [diff] [blame] | 55 | def test_default_init(self): |
| 56 | # default values are set to zero |
| 57 | for t in signed_types + unsigned_types + float_types: |
Benjamin Peterson | c9c0f20 | 2009-06-30 23:06:06 +0000 | [diff] [blame] | 58 | self.assertEqual(t().value, 0) |
Thomas Heller | babddfc | 2006-03-08 19:56:54 +0000 | [diff] [blame] | 59 | |
| 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 Peterson | c9c0f20 | 2009-06-30 23:06:06 +0000 | [diff] [blame] | 64 | self.assertEqual(t(l).value, l) |
| 65 | self.assertEqual(t(h).value, h) |
Thomas Heller | babddfc | 2006-03-08 19:56:54 +0000 | [diff] [blame] | 66 | |
| 67 | def test_signed_values(self): |
| 68 | # see above |
| 69 | for t, (l, h) in zip(signed_types, signed_ranges): |
Benjamin Peterson | c9c0f20 | 2009-06-30 23:06:06 +0000 | [diff] [blame] | 70 | self.assertEqual(t(l).value, l) |
| 71 | self.assertEqual(t(h).value, h) |
Thomas Heller | babddfc | 2006-03-08 19:56:54 +0000 | [diff] [blame] | 72 | |
Guido van Rossum | d8faa36 | 2007-04-27 19:54:29 +0000 | [diff] [blame] | 73 | def test_bool_values(self): |
| 74 | from operator import truth |
| 75 | for t, v in zip(bool_types, bool_values): |
Benjamin Peterson | c9c0f20 | 2009-06-30 23:06:06 +0000 | [diff] [blame] | 76 | self.assertEqual(t(v).value, truth(v)) |
Guido van Rossum | d8faa36 | 2007-04-27 19:54:29 +0000 | [diff] [blame] | 77 | |
Thomas Heller | babddfc | 2006-03-08 19:56:54 +0000 | [diff] [blame] | 78 | 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 Peterson | c9c0f20 | 2009-06-30 23:06:06 +0000 | [diff] [blame] | 96 | self.assertEqual(ArgType, type(t.from_param(0))) |
Thomas Heller | babddfc | 2006-03-08 19:56:54 +0000 | [diff] [blame] | 97 | |
Thomas Heller | babddfc | 2006-03-08 19:56:54 +0000 | [diff] [blame] | 98 | def test_byref(self): |
| 99 | # calling byref returns also a PyCArgObject instance |
Guido van Rossum | d8faa36 | 2007-04-27 19:54:29 +0000 | [diff] [blame] | 100 | for t in signed_types + unsigned_types + float_types + bool_types: |
Thomas Heller | babddfc | 2006-03-08 19:56:54 +0000 | [diff] [blame] | 101 | parm = byref(t()) |
Benjamin Peterson | c9c0f20 | 2009-06-30 23:06:06 +0000 | [diff] [blame] | 102 | self.assertEqual(ArgType, type(parm)) |
Thomas Heller | babddfc | 2006-03-08 19:56:54 +0000 | [diff] [blame] | 103 | |
| 104 | |
| 105 | def test_floats(self): |
| 106 | # c_float and c_double can be created from |
| 107 | # Python int, long and float |
Christian Heimes | 969fe57 | 2008-01-25 11:23:10 +0000 | [diff] [blame] | 108 | class FloatLike(object): |
| 109 | def __float__(self): |
| 110 | return 2.0 |
| 111 | f = FloatLike() |
Thomas Heller | babddfc | 2006-03-08 19:56:54 +0000 | [diff] [blame] | 112 | for t in float_types: |
Benjamin Peterson | c9c0f20 | 2009-06-30 23:06:06 +0000 | [diff] [blame] | 113 | 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 Heller | babddfc | 2006-03-08 19:56:54 +0000 | [diff] [blame] | 117 | |
| 118 | def test_integers(self): |
Christian Heimes | 969fe57 | 2008-01-25 11:23:10 +0000 | [diff] [blame] | 119 | 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 Heller | babddfc | 2006-03-08 19:56:54 +0000 | [diff] [blame] | 129 | for t in signed_types + unsigned_types: |
| 130 | self.assertRaises(TypeError, t, 3.14) |
Christian Heimes | 969fe57 | 2008-01-25 11:23:10 +0000 | [diff] [blame] | 131 | self.assertRaises(TypeError, t, f) |
Benjamin Peterson | c9c0f20 | 2009-06-30 23:06:06 +0000 | [diff] [blame] | 132 | self.assertEqual(t(i).value, 2) |
Thomas Heller | babddfc | 2006-03-08 19:56:54 +0000 | [diff] [blame] | 133 | |
| 134 | def test_sizes(self): |
Guido van Rossum | d8faa36 | 2007-04-27 19:54:29 +0000 | [diff] [blame] | 135 | for t in signed_types + unsigned_types + float_types + bool_types: |
Guido van Rossum | cd16bf6 | 2007-06-13 18:07:49 +0000 | [diff] [blame] | 136 | try: |
| 137 | size = struct.calcsize(t._type_) |
| 138 | except struct.error: |
| 139 | continue |
Thomas Heller | babddfc | 2006-03-08 19:56:54 +0000 | [diff] [blame] | 140 | # sizeof of the type... |
Benjamin Peterson | c9c0f20 | 2009-06-30 23:06:06 +0000 | [diff] [blame] | 141 | self.assertEqual(sizeof(t), size) |
Thomas Heller | babddfc | 2006-03-08 19:56:54 +0000 | [diff] [blame] | 142 | # and sizeof of an instance |
Benjamin Peterson | c9c0f20 | 2009-06-30 23:06:06 +0000 | [diff] [blame] | 143 | self.assertEqual(sizeof(t()), size) |
Thomas Heller | babddfc | 2006-03-08 19:56:54 +0000 | [diff] [blame] | 144 | |
| 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 Peterson | c9c0f20 | 2009-06-30 23:06:06 +0000 | [diff] [blame] | 151 | self.assertEqual((code, alignment(t)), |
Thomas Heller | babddfc | 2006-03-08 19:56:54 +0000 | [diff] [blame] | 152 | (code, align)) |
| 153 | # and alignment of an instance |
Benjamin Peterson | c9c0f20 | 2009-06-30 23:06:06 +0000 | [diff] [blame] | 154 | self.assertEqual((code, alignment(t())), |
Thomas Heller | babddfc | 2006-03-08 19:56:54 +0000 | [diff] [blame] | 155 | (code, align)) |
Tim Peters | e8d09e5 | 2006-03-09 01:15:05 +0000 | [diff] [blame] | 156 | |
Thomas Heller | babddfc | 2006-03-08 19:56:54 +0000 | [diff] [blame] | 157 | 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 Peterson | c9c0f20 | 2009-06-30 23:06:06 +0000 | [diff] [blame] | 170 | self.assertEqual(v.value, a[0]) |
| 171 | self.assertEqual(type(v), t) |
Thomas Heller | babddfc | 2006-03-08 19:56:54 +0000 | [diff] [blame] | 172 | |
| 173 | # changing the value at the memory location changes v's value also |
| 174 | a[0] = 42 |
Benjamin Peterson | c9c0f20 | 2009-06-30 23:06:06 +0000 | [diff] [blame] | 175 | self.assertEqual(v.value, a[0]) |
Tim Peters | e8d09e5 | 2006-03-09 01:15:05 +0000 | [diff] [blame] | 176 | |
Thomas Heller | babddfc | 2006-03-08 19:56:54 +0000 | [diff] [blame] | 177 | |
| 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 Peterson | c9c0f20 | 2009-06-30 23:06:06 +0000 | [diff] [blame] | 183 | self.assertEqual(v.value, a[0]) |
| 184 | self.assertTrue(type(v) is t) |
Thomas Heller | babddfc | 2006-03-08 19:56:54 +0000 | [diff] [blame] | 185 | a[0] = 2.3456e17 |
Benjamin Peterson | c9c0f20 | 2009-06-30 23:06:06 +0000 | [diff] [blame] | 186 | self.assertEqual(v.value, a[0]) |
| 187 | self.assertTrue(type(v) is t) |
Thomas Heller | babddfc | 2006-03-08 19:56:54 +0000 | [diff] [blame] | 188 | |
| 189 | def test_char_from_address(self): |
| 190 | from ctypes import c_char |
| 191 | from array import array |
Tim Peters | e8d09e5 | 2006-03-09 01:15:05 +0000 | [diff] [blame] | 192 | |
Thomas Heller | 9004427 | 2007-07-11 12:25:26 +0000 | [diff] [blame] | 193 | a = array('b', [0]) |
| 194 | a[0] = ord('x') |
Thomas Heller | babddfc | 2006-03-08 19:56:54 +0000 | [diff] [blame] | 195 | v = c_char.from_address(a.buffer_info()[0]) |
Benjamin Peterson | c9c0f20 | 2009-06-30 23:06:06 +0000 | [diff] [blame] | 196 | self.assertEqual(v.value, b'x') |
| 197 | self.assertTrue(type(v) is c_char) |
Thomas Heller | babddfc | 2006-03-08 19:56:54 +0000 | [diff] [blame] | 198 | |
Thomas Heller | 9004427 | 2007-07-11 12:25:26 +0000 | [diff] [blame] | 199 | a[0] = ord('?') |
Benjamin Peterson | c9c0f20 | 2009-06-30 23:06:06 +0000 | [diff] [blame] | 200 | self.assertEqual(v.value, b'?') |
Thomas Heller | babddfc | 2006-03-08 19:56:54 +0000 | [diff] [blame] | 201 | |
Guido van Rossum | d8faa36 | 2007-04-27 19:54:29 +0000 | [diff] [blame] | 202 | # 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 Peterson | c9c0f20 | 2009-06-30 23:06:06 +0000 | [diff] [blame] | 208 | # self.assertEqual(v.value, a[0]) |
| 209 | # self.assertEqual(type(v) is t) |
Guido van Rossum | d8faa36 | 2007-04-27 19:54:29 +0000 | [diff] [blame] | 210 | # a[0] = False |
Benjamin Peterson | c9c0f20 | 2009-06-30 23:06:06 +0000 | [diff] [blame] | 211 | # self.assertEqual(v.value, a[0]) |
| 212 | # self.assertEqual(type(v) is t) |
Guido van Rossum | d8faa36 | 2007-04-27 19:54:29 +0000 | [diff] [blame] | 213 | |
Thomas Heller | babddfc | 2006-03-08 19:56:54 +0000 | [diff] [blame] | 214 | 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 Peters | e8d09e5 | 2006-03-09 01:15:05 +0000 | [diff] [blame] | 222 | |
Thomas Heller | babddfc | 2006-03-08 19:56:54 +0000 | [diff] [blame] | 223 | from ctypes import _SimpleCData |
| 224 | class c_int_S(_SimpleCData): |
| 225 | _type_ = "i" |
| 226 | __slots__ = [] |
| 227 | |
| 228 | def 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 Rossum | be19ed7 | 2007-02-09 05:37:30 +0000 | [diff] [blame] | 242 | print("%15s: %.2f us" % (msg, ((stop-start)*1e6/5/rep))) |
Thomas Heller | babddfc | 2006-03-08 19:56:54 +0000 | [diff] [blame] | 243 | |
| 244 | def 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 Peters | e8d09e5 | 2006-03-09 01:15:05 +0000 | [diff] [blame] | 264 | # c_int_S(999): 3.24 us |
Thomas Heller | babddfc | 2006-03-08 19:56:54 +0000 | [diff] [blame] | 265 | |
| 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 | |
| 275 | if __name__ == '__main__': |
| 276 | ## check_perf() |
| 277 | unittest.main() |