Home | History | Annotate | Download | only in test
      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