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 constructor, 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 @unittest.skip('test disabled') 86 def test_valid_ranges(self): 87 # invalid values of the correct type 88 # raise ValueError (not OverflowError) 89 for t, (l, h) in zip(unsigned_types, unsigned_ranges): 90 self.assertRaises(ValueError, t, l-1) 91 self.assertRaises(ValueError, t, h+1) 92 93 def test_from_param(self): 94 # the from_param class method attribute always 95 # returns PyCArgObject instances 96 for t in signed_types + unsigned_types + float_types: 97 self.assertEqual(ArgType, type(t.from_param(0))) 98 99 def test_byref(self): 100 # calling byref returns also a PyCArgObject instance 101 for t in signed_types + unsigned_types + float_types + bool_types: 102 parm = byref(t()) 103 self.assertEqual(ArgType, type(parm)) 104 105 106 def test_floats(self): 107 # c_float and c_double can be created from 108 # Python int, long and float 109 class FloatLike(object): 110 def __float__(self): 111 return 2.0 112 f = FloatLike() 113 for t in float_types: 114 self.assertEqual(t(2.0).value, 2.0) 115 self.assertEqual(t(2).value, 2.0) 116 self.assertEqual(t(2L).value, 2.0) 117 self.assertEqual(t(f).value, 2.0) 118 119 def test_integers(self): 120 class FloatLike(object): 121 def __float__(self): 122 return 2.0 123 f = FloatLike() 124 class IntLike(object): 125 def __int__(self): 126 return 2 127 i = IntLike() 128 # integers cannot be constructed from floats, 129 # but from integer-like objects 130 for t in signed_types + unsigned_types: 131 self.assertRaises(TypeError, t, 3.14) 132 self.assertRaises(TypeError, t, f) 133 self.assertEqual(t(i).value, 2) 134 135 def test_sizes(self): 136 for t in signed_types + unsigned_types + float_types + bool_types: 137 try: 138 size = struct.calcsize(t._type_) 139 except struct.error: 140 continue 141 # sizeof of the type... 142 self.assertEqual(sizeof(t), size) 143 # and sizeof of an instance 144 self.assertEqual(sizeof(t()), size) 145 146 def test_alignments(self): 147 for t in signed_types + unsigned_types + float_types: 148 code = t._type_ # the typecode 149 align = struct.calcsize("c%c" % code) - struct.calcsize(code) 150 151 # alignment of the type... 152 self.assertEqual((code, alignment(t)), 153 (code, align)) 154 # and alignment of an instance 155 self.assertEqual((code, alignment(t())), 156 (code, align)) 157 158 def test_int_from_address(self): 159 from array import array 160 for t in signed_types + unsigned_types: 161 # the array module doesn't support all format codes 162 # (no 'q' or 'Q') 163 try: 164 array(t._type_) 165 except ValueError: 166 continue 167 a = array(t._type_, [100]) 168 169 # v now is an integer at an 'external' memory location 170 v = t.from_address(a.buffer_info()[0]) 171 self.assertEqual(v.value, a[0]) 172 self.assertEqual(type(v), t) 173 174 # changing the value at the memory location changes v's value also 175 a[0] = 42 176 self.assertEqual(v.value, a[0]) 177 178 179 def test_float_from_address(self): 180 from array import array 181 for t in float_types: 182 a = array(t._type_, [3.14]) 183 v = t.from_address(a.buffer_info()[0]) 184 self.assertEqual(v.value, a[0]) 185 self.assertIs(type(v), t) 186 a[0] = 2.3456e17 187 self.assertEqual(v.value, a[0]) 188 self.assertIs(type(v), t) 189 190 def test_char_from_address(self): 191 from ctypes import c_char 192 from array import array 193 194 a = array('c', 'x') 195 v = c_char.from_address(a.buffer_info()[0]) 196 self.assertEqual(v.value, a[0]) 197 self.assertIs(type(v), c_char) 198 199 a[0] = '?' 200 self.assertEqual(v.value, a[0]) 201 202 # array does not support c_bool / 't' 203 @unittest.skip('test disabled') 204 def test_bool_from_address(self): 205 from ctypes import c_bool 206 from array import array 207 a = array(c_bool._type_, [True]) 208 v = t.from_address(a.buffer_info()[0]) 209 self.assertEqual(v.value, a[0]) 210 self.assertEqual(type(v) is t) 211 a[0] = False 212 self.assertEqual(v.value, a[0]) 213 self.assertEqual(type(v) is t) 214 215 def test_init(self): 216 # c_int() can be initialized from Python's int, and c_int. 217 # Not from c_long or so, which seems strange, abc should 218 # probably be changed: 219 self.assertRaises(TypeError, c_int, c_long(42)) 220 221 def test_float_overflow(self): 222 import sys 223 big_int = int(sys.float_info.max) * 2 224 for t in float_types + [c_longdouble]: 225 self.assertRaises(OverflowError, t, big_int) 226 if (hasattr(t, "__ctype_be__")): 227 self.assertRaises(OverflowError, t.__ctype_be__, big_int) 228 if (hasattr(t, "__ctype_le__")): 229 self.assertRaises(OverflowError, t.__ctype_le__, big_int) 230 231 @unittest.skip('test disabled') 232 def test_perf(self): 233 check_perf() 234 235 from ctypes import _SimpleCData 236 class c_int_S(_SimpleCData): 237 _type_ = "i" 238 __slots__ = [] 239 240 def run_test(rep, msg, func, arg=None): 241 ## items = [None] * rep 242 items = range(rep) 243 from time import clock 244 if arg is not None: 245 start = clock() 246 for i in items: 247 func(arg); func(arg); func(arg); func(arg); func(arg) 248 stop = clock() 249 else: 250 start = clock() 251 for i in items: 252 func(); func(); func(); func(); func() 253 stop = clock() 254 print "%15s: %.2f us" % (msg, ((stop-start)*1e6/5/rep)) 255 256 def check_perf(): 257 # Construct 5 objects 258 from ctypes import c_int 259 260 REP = 200000 261 262 run_test(REP, "int()", int) 263 run_test(REP, "int(999)", int) 264 run_test(REP, "c_int()", c_int) 265 run_test(REP, "c_int(999)", c_int) 266 run_test(REP, "c_int_S()", c_int_S) 267 run_test(REP, "c_int_S(999)", c_int_S) 268 269 # Python 2.3 -OO, win2k, P4 700 MHz: 270 # 271 # int(): 0.87 us 272 # int(999): 0.87 us 273 # c_int(): 3.35 us 274 # c_int(999): 3.34 us 275 # c_int_S(): 3.23 us 276 # c_int_S(999): 3.24 us 277 278 # Python 2.2 -OO, win2k, P4 700 MHz: 279 # 280 # int(): 0.89 us 281 # int(999): 0.89 us 282 # c_int(): 9.99 us 283 # c_int(999): 10.02 us 284 # c_int_S(): 9.87 us 285 # c_int_S(999): 9.85 us 286 287 if __name__ == '__main__': 288 ## check_perf() 289 unittest.main() 290