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 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