Home | History | Annotate | Download | only in test
      1 import unittest
      2 from ctypes import *
      3 import _ctypes_test
      4 
      5 class Callbacks(unittest.TestCase):
      6     functype = CFUNCTYPE
      7 
      8 ##    def tearDown(self):
      9 ##        import gc
     10 ##        gc.collect()
     11 
     12     def callback(self, *args):
     13         self.got_args = args
     14         return args[-1]
     15 
     16     def check_type(self, typ, arg):
     17         PROTO = self.functype.im_func(typ, typ)
     18         result = PROTO(self.callback)(arg)
     19         if typ == c_float:
     20             self.assertAlmostEqual(result, arg, places=5)
     21         else:
     22             self.assertEqual(self.got_args, (arg,))
     23             self.assertEqual(result, arg)
     24 
     25         PROTO = self.functype.im_func(typ, c_byte, typ)
     26         result = PROTO(self.callback)(-3, arg)
     27         if typ == c_float:
     28             self.assertAlmostEqual(result, arg, places=5)
     29         else:
     30             self.assertEqual(self.got_args, (-3, arg))
     31             self.assertEqual(result, arg)
     32 
     33     ################
     34 
     35     def test_byte(self):
     36         self.check_type(c_byte, 42)
     37         self.check_type(c_byte, -42)
     38 
     39     def test_ubyte(self):
     40         self.check_type(c_ubyte, 42)
     41 
     42     def test_short(self):
     43         self.check_type(c_short, 42)
     44         self.check_type(c_short, -42)
     45 
     46     def test_ushort(self):
     47         self.check_type(c_ushort, 42)
     48 
     49     def test_int(self):
     50         self.check_type(c_int, 42)
     51         self.check_type(c_int, -42)
     52 
     53     def test_uint(self):
     54         self.check_type(c_uint, 42)
     55 
     56     def test_long(self):
     57         self.check_type(c_long, 42)
     58         self.check_type(c_long, -42)
     59 
     60     def test_ulong(self):
     61         self.check_type(c_ulong, 42)
     62 
     63     def test_longlong(self):
     64         # test some 64-bit values, positive and negative
     65         self.check_type(c_longlong, 5948291757245277467)
     66         self.check_type(c_longlong, -5229388909784190580)
     67         self.check_type(c_longlong, 42)
     68         self.check_type(c_longlong, -42)
     69 
     70     def test_ulonglong(self):
     71         # test some 64-bit values, with and without msb set.
     72         self.check_type(c_ulonglong, 10955412242170339782)
     73         self.check_type(c_ulonglong, 3665885499841167458)
     74         self.check_type(c_ulonglong, 42)
     75 
     76     def test_float(self):
     77         # only almost equal: double -> float -> double
     78         import math
     79         self.check_type(c_float, math.e)
     80         self.check_type(c_float, -math.e)
     81 
     82     def test_double(self):
     83         self.check_type(c_double, 3.14)
     84         self.check_type(c_double, -3.14)
     85 
     86     def test_longdouble(self):
     87         self.check_type(c_longdouble, 3.14)
     88         self.check_type(c_longdouble, -3.14)
     89 
     90     def test_char(self):
     91         self.check_type(c_char, "x")
     92         self.check_type(c_char, "a")
     93 
     94     # disabled: would now (correctly) raise a RuntimeWarning about
     95     # a memory leak.  A callback function cannot return a non-integral
     96     # C type without causing a memory leak.
     97 ##    def test_char_p(self):
     98 ##        self.check_type(c_char_p, "abc")
     99 ##        self.check_type(c_char_p, "def")
    100 
    101     def test_pyobject(self):
    102         o = ()
    103         from sys import getrefcount as grc
    104         for o in (), [], object():
    105             initial = grc(o)
    106             # This call leaks a reference to 'o'...
    107             self.check_type(py_object, o)
    108             before = grc(o)
    109             # ...but this call doesn't leak any more.  Where is the refcount?
    110             self.check_type(py_object, o)
    111             after = grc(o)
    112             self.assertEqual((after, o), (before, o))
    113 
    114     def test_unsupported_restype_1(self):
    115         # Only "fundamental" result types are supported for callback
    116         # functions, the type must have a non-NULL stgdict->setfunc.
    117         # POINTER(c_double), for example, is not supported.
    118 
    119         prototype = self.functype.im_func(POINTER(c_double))
    120         # The type is checked when the prototype is called
    121         self.assertRaises(TypeError, prototype, lambda: None)
    122 
    123     def test_unsupported_restype_2(self):
    124         prototype = self.functype.im_func(object)
    125         self.assertRaises(TypeError, prototype, lambda: None)
    126 
    127     def test_issue_7959(self):
    128         proto = self.functype.im_func(None)
    129 
    130         class X(object):
    131             def func(self): pass
    132             def __init__(self):
    133                 self.v = proto(self.func)
    134 
    135         import gc
    136         for i in range(32):
    137             X()
    138         gc.collect()
    139         live = [x for x in gc.get_objects()
    140                 if isinstance(x, X)]
    141         self.assertEqual(len(live), 0)
    142 
    143     def test_issue12483(self):
    144         import gc
    145         class Nasty:
    146             def __del__(self):
    147                 gc.collect()
    148         CFUNCTYPE(None)(lambda x=Nasty(): None)
    149 
    150 
    151 try:
    152     WINFUNCTYPE
    153 except NameError:
    154     pass
    155 else:
    156     class StdcallCallbacks(Callbacks):
    157         functype = WINFUNCTYPE
    158 
    159 ################################################################
    160 
    161 class SampleCallbacksTestCase(unittest.TestCase):
    162 
    163     def test_integrate(self):
    164         # Derived from some then non-working code, posted by David Foster
    165         dll = CDLL(_ctypes_test.__file__)
    166 
    167         # The function prototype called by 'integrate': double func(double);
    168         CALLBACK = CFUNCTYPE(c_double, c_double)
    169 
    170         # The integrate function itself, exposed from the _ctypes_test dll
    171         integrate = dll.integrate
    172         integrate.argtypes = (c_double, c_double, CALLBACK, c_long)
    173         integrate.restype = c_double
    174 
    175         def func(x):
    176             return x**2
    177 
    178         result = integrate(0.0, 1.0, CALLBACK(func), 10)
    179         diff = abs(result - 1./3.)
    180 
    181         self.assertLess(diff, 0.01, "%s not less than 0.01" % diff)
    182 
    183     def test_issue_8959_a(self):
    184         from ctypes.util import find_library
    185         libc_path = find_library("c")
    186         if not libc_path:
    187             return # cannot test
    188         libc = CDLL(libc_path)
    189 
    190         @CFUNCTYPE(c_int, POINTER(c_int), POINTER(c_int))
    191         def cmp_func(a, b):
    192             return a[0] - b[0]
    193 
    194         array = (c_int * 5)(5, 1, 99, 7, 33)
    195 
    196         libc.qsort(array, len(array), sizeof(c_int), cmp_func)
    197         self.assertEqual(array[:], [1, 5, 7, 33, 99])
    198 
    199     try:
    200         WINFUNCTYPE
    201     except NameError:
    202         pass
    203     else:
    204         def test_issue_8959_b(self):
    205             from ctypes.wintypes import BOOL, HWND, LPARAM
    206             global windowCount
    207             windowCount = 0
    208 
    209             @WINFUNCTYPE(BOOL, HWND, LPARAM)
    210             def EnumWindowsCallbackFunc(hwnd, lParam):
    211                 global windowCount
    212                 windowCount += 1
    213                 return True #Allow windows to keep enumerating
    214 
    215             windll.user32.EnumWindows(EnumWindowsCallbackFunc, 0)
    216 
    217     def test_callback_register_int(self):
    218         # Issue #8275: buggy handling of callback args under Win64
    219         # NOTE: should be run on release builds as well
    220         dll = CDLL(_ctypes_test.__file__)
    221         CALLBACK = CFUNCTYPE(c_int, c_int, c_int, c_int, c_int, c_int)
    222         # All this function does is call the callback with its args squared
    223         func = dll._testfunc_cbk_reg_int
    224         func.argtypes = (c_int, c_int, c_int, c_int, c_int, CALLBACK)
    225         func.restype = c_int
    226 
    227         def callback(a, b, c, d, e):
    228             return a + b + c + d + e
    229 
    230         result = func(2, 3, 4, 5, 6, CALLBACK(callback))
    231         self.assertEqual(result, callback(2*2, 3*3, 4*4, 5*5, 6*6))
    232 
    233     def test_callback_register_double(self):
    234         # Issue #8275: buggy handling of callback args under Win64
    235         # NOTE: should be run on release builds as well
    236         dll = CDLL(_ctypes_test.__file__)
    237         CALLBACK = CFUNCTYPE(c_double, c_double, c_double, c_double,
    238                              c_double, c_double)
    239         # All this function does is call the callback with its args squared
    240         func = dll._testfunc_cbk_reg_double
    241         func.argtypes = (c_double, c_double, c_double,
    242                          c_double, c_double, CALLBACK)
    243         func.restype = c_double
    244 
    245         def callback(a, b, c, d, e):
    246             return a + b + c + d + e
    247 
    248         result = func(1.1, 2.2, 3.3, 4.4, 5.5, CALLBACK(callback))
    249         self.assertEqual(result,
    250                          callback(1.1*1.1, 2.2*2.2, 3.3*3.3, 4.4*4.4, 5.5*5.5))
    251 
    252 
    253 ################################################################
    254 
    255 if __name__ == '__main__':
    256     unittest.main()
    257