diff options
Diffstat (limited to 'lib/python2.7/ctypes/test/test_callbacks.py')
-rw-r--r-- | lib/python2.7/ctypes/test/test_callbacks.py | 256 |
1 files changed, 256 insertions, 0 deletions
diff --git a/lib/python2.7/ctypes/test/test_callbacks.py b/lib/python2.7/ctypes/test/test_callbacks.py new file mode 100644 index 0000000..be0c17e --- /dev/null +++ b/lib/python2.7/ctypes/test/test_callbacks.py @@ -0,0 +1,256 @@ +import unittest +from ctypes import * +import _ctypes_test + +class Callbacks(unittest.TestCase): + functype = CFUNCTYPE + +## def tearDown(self): +## import gc +## gc.collect() + + def callback(self, *args): + self.got_args = args + return args[-1] + + def check_type(self, typ, arg): + PROTO = self.functype.im_func(typ, typ) + result = PROTO(self.callback)(arg) + if typ == c_float: + self.assertAlmostEqual(result, arg, places=5) + else: + self.assertEqual(self.got_args, (arg,)) + self.assertEqual(result, arg) + + PROTO = self.functype.im_func(typ, c_byte, typ) + result = PROTO(self.callback)(-3, arg) + if typ == c_float: + self.assertAlmostEqual(result, arg, places=5) + else: + self.assertEqual(self.got_args, (-3, arg)) + self.assertEqual(result, arg) + + ################ + + def test_byte(self): + self.check_type(c_byte, 42) + self.check_type(c_byte, -42) + + def test_ubyte(self): + self.check_type(c_ubyte, 42) + + def test_short(self): + self.check_type(c_short, 42) + self.check_type(c_short, -42) + + def test_ushort(self): + self.check_type(c_ushort, 42) + + def test_int(self): + self.check_type(c_int, 42) + self.check_type(c_int, -42) + + def test_uint(self): + self.check_type(c_uint, 42) + + def test_long(self): + self.check_type(c_long, 42) + self.check_type(c_long, -42) + + def test_ulong(self): + self.check_type(c_ulong, 42) + + def test_longlong(self): + # test some 64-bit values, positive and negative + self.check_type(c_longlong, 5948291757245277467) + self.check_type(c_longlong, -5229388909784190580) + self.check_type(c_longlong, 42) + self.check_type(c_longlong, -42) + + def test_ulonglong(self): + # test some 64-bit values, with and without msb set. + self.check_type(c_ulonglong, 10955412242170339782) + self.check_type(c_ulonglong, 3665885499841167458) + self.check_type(c_ulonglong, 42) + + def test_float(self): + # only almost equal: double -> float -> double + import math + self.check_type(c_float, math.e) + self.check_type(c_float, -math.e) + + def test_double(self): + self.check_type(c_double, 3.14) + self.check_type(c_double, -3.14) + + def test_longdouble(self): + self.check_type(c_longdouble, 3.14) + self.check_type(c_longdouble, -3.14) + + def test_char(self): + self.check_type(c_char, "x") + self.check_type(c_char, "a") + + # disabled: would now (correctly) raise a RuntimeWarning about + # a memory leak. A callback function cannot return a non-integral + # C type without causing a memory leak. +## def test_char_p(self): +## self.check_type(c_char_p, "abc") +## self.check_type(c_char_p, "def") + + def test_pyobject(self): + o = () + from sys import getrefcount as grc + for o in (), [], object(): + initial = grc(o) + # This call leaks a reference to 'o'... + self.check_type(py_object, o) + before = grc(o) + # ...but this call doesn't leak any more. Where is the refcount? + self.check_type(py_object, o) + after = grc(o) + self.assertEqual((after, o), (before, o)) + + def test_unsupported_restype_1(self): + # Only "fundamental" result types are supported for callback + # functions, the type must have a non-NULL stgdict->setfunc. + # POINTER(c_double), for example, is not supported. + + prototype = self.functype.im_func(POINTER(c_double)) + # The type is checked when the prototype is called + self.assertRaises(TypeError, prototype, lambda: None) + + def test_unsupported_restype_2(self): + prototype = self.functype.im_func(object) + self.assertRaises(TypeError, prototype, lambda: None) + + def test_issue_7959(self): + proto = self.functype.im_func(None) + + class X(object): + def func(self): pass + def __init__(self): + self.v = proto(self.func) + + import gc + for i in range(32): + X() + gc.collect() + live = [x for x in gc.get_objects() + if isinstance(x, X)] + self.assertEqual(len(live), 0) + + def test_issue12483(self): + import gc + class Nasty: + def __del__(self): + gc.collect() + CFUNCTYPE(None)(lambda x=Nasty(): None) + + +try: + WINFUNCTYPE +except NameError: + pass +else: + class StdcallCallbacks(Callbacks): + functype = WINFUNCTYPE + +################################################################ + +class SampleCallbacksTestCase(unittest.TestCase): + + def test_integrate(self): + # Derived from some then non-working code, posted by David Foster + dll = CDLL(_ctypes_test.__file__) + + # The function prototype called by 'integrate': double func(double); + CALLBACK = CFUNCTYPE(c_double, c_double) + + # The integrate function itself, exposed from the _ctypes_test dll + integrate = dll.integrate + integrate.argtypes = (c_double, c_double, CALLBACK, c_long) + integrate.restype = c_double + + def func(x): + return x**2 + + result = integrate(0.0, 1.0, CALLBACK(func), 10) + diff = abs(result - 1./3.) + + self.assertLess(diff, 0.01, "%s not less than 0.01" % diff) + + def test_issue_8959_a(self): + from ctypes.util import find_library + libc_path = find_library("c") + if not libc_path: + return # cannot test + libc = CDLL(libc_path) + + @CFUNCTYPE(c_int, POINTER(c_int), POINTER(c_int)) + def cmp_func(a, b): + return a[0] - b[0] + + array = (c_int * 5)(5, 1, 99, 7, 33) + + libc.qsort(array, len(array), sizeof(c_int), cmp_func) + self.assertEqual(array[:], [1, 5, 7, 33, 99]) + + try: + WINFUNCTYPE + except NameError: + pass + else: + def test_issue_8959_b(self): + from ctypes.wintypes import BOOL, HWND, LPARAM + global windowCount + windowCount = 0 + + @WINFUNCTYPE(BOOL, HWND, LPARAM) + def EnumWindowsCallbackFunc(hwnd, lParam): + global windowCount + windowCount += 1 + return True #Allow windows to keep enumerating + + windll.user32.EnumWindows(EnumWindowsCallbackFunc, 0) + + def test_callback_register_int(self): + # Issue #8275: buggy handling of callback args under Win64 + # NOTE: should be run on release builds as well + dll = CDLL(_ctypes_test.__file__) + CALLBACK = CFUNCTYPE(c_int, c_int, c_int, c_int, c_int, c_int) + # All this function does is call the callback with its args squared + func = dll._testfunc_cbk_reg_int + func.argtypes = (c_int, c_int, c_int, c_int, c_int, CALLBACK) + func.restype = c_int + + def callback(a, b, c, d, e): + return a + b + c + d + e + + result = func(2, 3, 4, 5, 6, CALLBACK(callback)) + self.assertEqual(result, callback(2*2, 3*3, 4*4, 5*5, 6*6)) + + def test_callback_register_double(self): + # Issue #8275: buggy handling of callback args under Win64 + # NOTE: should be run on release builds as well + dll = CDLL(_ctypes_test.__file__) + CALLBACK = CFUNCTYPE(c_double, c_double, c_double, c_double, + c_double, c_double) + # All this function does is call the callback with its args squared + func = dll._testfunc_cbk_reg_double + func.argtypes = (c_double, c_double, c_double, + c_double, c_double, CALLBACK) + func.restype = c_double + + def callback(a, b, c, d, e): + return a + b + c + d + e + + result = func(1.1, 2.2, 3.3, 4.4, 5.5, CALLBACK(callback)) + self.assertEqual(result, + callback(1.1*1.1, 2.2*2.2, 3.3*3.3, 4.4*4.4, 5.5*5.5)) + + +################################################################ + +if __name__ == '__main__': + unittest.main() |