blob: be0c17e93c870b00c7a1613bfb50683abb31112d [file] [log] [blame]
Dan Albert287553d2017-02-16 10:47:51 -08001import unittest
2from ctypes import *
3import _ctypes_test
4
5class 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
151try:
152 WINFUNCTYPE
153except NameError:
154 pass
155else:
156 class StdcallCallbacks(Callbacks):
157 functype = WINFUNCTYPE
158
159################################################################
160
161class 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
255if __name__ == '__main__':
256 unittest.main()