1 from ctypes import * 2 import unittest 3 import struct 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) 15 a = struct.unpack(fmt, ("\x00"*32)[:size])[0] 16 b = struct.unpack(fmt, ("\xFF"*32)[:size])[0] 17 c = struct.unpack(fmt, ("\x7F"+"\x00"*32)[:size])[0] 18 d = struct.unpack(fmt, ("\x80"+"\xFF"*32)[:size])[0] 19 result.append((min(a, b, c, d), max(a, b, c, d))) 20 return result 21 22 ArgType = type(byref(c_int(0))) 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 27 bool_types = [] 28 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) 39 40 try: 41 c_bool 42 except NameError: 43 pass 44 else: 45 bool_types.append(c_bool) 46 47 unsigned_ranges = valid_ranges(*unsigned_types) 48 signed_ranges = valid_ranges(*signed_types) 49 bool_values = [True, False, 0, 1, -1, 5000, 'test', [], [1]] 50 51 ################################################################ 52 53 class NumberTestCase(unittest.TestCase): 54 55 def test_default_init(self): 56 # default values are set to zero 57 for t in signed_types + unsigned_types + float_types: 58 self.assertEqual(t().value, 0) 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): 64 self.assertEqual(t(l).value, l) 65 self.assertEqual(t(h).value, h) 66 67 def test_signed_values(self): 68 # see above 69 for t, (l, h) in zip(signed_types, signed_ranges): 70 self.assertEqual(t(l).value, l) 71 self.assertEqual(t(h).value, h) 72 73 def test_bool_values(self): 74 from operator import truth 75 for t, v in zip(bool_types, bool_values): 76 self.assertEqual(t(v).value, truth(v)) 77 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: 96 self.assertEqual(ArgType, type(t.from_param(0))) 97 98 def test_byref(self): 99 # calling byref returns also a PyCArgObject instance 100 for t in signed_types + unsigned_types + float_types + bool_types: 101 parm = byref(t()) 102 self.assertEqual(ArgType, type(parm)) 103 104 105 def test_floats(self): 106 # c_float and c_double can be created from 107 # Python int, long and float 108 class FloatLike(object): 109 def __float__(self): 110 return 2.0 111 f = FloatLike() 112 for t in float_types: 113 self.assertEqual(t(2.0).value, 2.0) 114 self.assertEqual(t(2).value, 2.0) 115 self.assertEqual(t(2L).value, 2.0) 116 self.assertEqual(t(f).value, 2.0) 117 118 def test_integers(self): 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 129 for t in signed_types + unsigned_types: 130 self.assertRaises(TypeError, t, 3.14) 131 self.assertRaises(TypeError, t, f) 132 self.assertEqual(t(i).value, 2) 133 134 def test_sizes(self): 135 for t in signed_types + unsigned_types + float_types + bool_types: 136 try: 137 size = struct.calcsize(t._type_) 138 except struct.error: 139 continue 140 # sizeof of the type... 141 self.assertEqual(sizeof(t), size) 142 # and sizeof of an instance 143 self.assertEqual(sizeof(t()), size) 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... 151 self.assertEqual((code, alignment(t)), 152 (code, align)) 153 # and alignment of an instance 154 self.assertEqual((code, alignment(t())), 155 (code, align)) 156 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 support 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]) 170 self.assertEqual(v.value, a[0]) 171 self.assertEqual(type(v), t) 172 173 # changing the value at the memory location changes v's value also 174 a[0] = 42 175 self.assertEqual(v.value, a[0]) 176 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]) 183 self.assertEqual(v.value, a[0]) 184 self.assertTrue(type(v) is t) 185 a[0] = 2.3456e17 186 self.assertEqual(v.value, a[0]) 187 self.assertTrue(type(v) is t) 188 189 def test_char_from_address(self): 190 from ctypes import c_char 191 from array import array 192 193 a = array('c', 'x') 194 v = c_char.from_address(a.buffer_info()[0]) 195 self.assertEqual(v.value, a[0]) 196 self.assertTrue(type(v) is c_char) 197 198 a[0] = '?' 199 self.assertEqual(v.value, a[0]) 200 201 # array does not support c_bool / 't' 202 # def test_bool_from_address(self): 203 # from ctypes import c_bool 204 # from array import array 205 # a = array(c_bool._type_, [True]) 206 # v = t.from_address(a.buffer_info()[0]) 207 # self.assertEqual(v.value, a[0]) 208 # self.assertEqual(type(v) is t) 209 # a[0] = False 210 # self.assertEqual(v.value, a[0]) 211 # self.assertEqual(type(v) is t) 212 213 def test_init(self): 214 # c_int() can be initialized from Python's int, and c_int. 215 # Not from c_long or so, which seems strange, abd should 216 # probably be changed: 217 self.assertRaises(TypeError, c_int, c_long(42)) 218 219 def test_float_overflow(self): 220 import sys 221 big_int = int(sys.float_info.max) * 2 222 for t in float_types + [c_longdouble]: 223 self.assertRaises(OverflowError, t, big_int) 224 if (hasattr(t, "__ctype_be__")): 225 self.assertRaises(OverflowError, t.__ctype_be__, big_int) 226 if (hasattr(t, "__ctype_le__")): 227 self.assertRaises(OverflowError, t.__ctype_le__, big_int) 228 229 ## def test_perf(self): 230 ## check_perf() 231 232 from ctypes import _SimpleCData 233 class c_int_S(_SimpleCData): 234 _type_ = "i" 235 __slots__ = [] 236 237 def run_test(rep, msg, func, arg=None): 238 ## items = [None] * rep 239 items = range(rep) 240 from time import clock 241 if arg is not None: 242 start = clock() 243 for i in items: 244 func(arg); func(arg); func(arg); func(arg); func(arg) 245 stop = clock() 246 else: 247 start = clock() 248 for i in items: 249 func(); func(); func(); func(); func() 250 stop = clock() 251 print "%15s: %.2f us" % (msg, ((stop-start)*1e6/5/rep)) 252 253 def check_perf(): 254 # Construct 5 objects 255 from ctypes import c_int 256 257 REP = 200000 258 259 run_test(REP, "int()", int) 260 run_test(REP, "int(999)", int) 261 run_test(REP, "c_int()", c_int) 262 run_test(REP, "c_int(999)", c_int) 263 run_test(REP, "c_int_S()", c_int_S) 264 run_test(REP, "c_int_S(999)", c_int_S) 265 266 # Python 2.3 -OO, win2k, P4 700 MHz: 267 # 268 # int(): 0.87 us 269 # int(999): 0.87 us 270 # c_int(): 3.35 us 271 # c_int(999): 3.34 us 272 # c_int_S(): 3.23 us 273 # c_int_S(999): 3.24 us 274 275 # Python 2.2 -OO, win2k, P4 700 MHz: 276 # 277 # int(): 0.89 us 278 # int(999): 0.89 us 279 # c_int(): 9.99 us 280 # c_int(999): 10.02 us 281 # c_int_S(): 9.87 us 282 # c_int_S(999): 9.85 us 283 284 if __name__ == '__main__': 285 ## check_perf() 286 unittest.main() 287