• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1"""
2Here is probably the place to write the docs, since the test-cases
3show how the type behave.
4
5Later...
6"""
7
8from ctypes import *
9from ctypes.test import need_symbol
10import sys, unittest
11
12try:
13    WINFUNCTYPE
14except NameError:
15    # fake to enable this test on Linux
16    WINFUNCTYPE = CFUNCTYPE
17
18import _ctypes_test
19dll = CDLL(_ctypes_test.__file__)
20if sys.platform == "win32":
21    windll = WinDLL(_ctypes_test.__file__)
22
23class POINT(Structure):
24    _fields_ = [("x", c_int), ("y", c_int)]
25class RECT(Structure):
26    _fields_ = [("left", c_int), ("top", c_int),
27                ("right", c_int), ("bottom", c_int)]
28class FunctionTestCase(unittest.TestCase):
29
30    def test_mro(self):
31        # in Python 2.3, this raises TypeError: MRO conflict among bases classes,
32        # in Python 2.2 it works.
33        #
34        # But in early versions of _ctypes.c, the result of tp_new
35        # wasn't checked, and it even crashed Python.
36        # Found by Greg Chapman.
37
38        with self.assertRaises(TypeError):
39            class X(object, Array):
40                _length_ = 5
41                _type_ = "i"
42
43        from _ctypes import _Pointer
44        with self.assertRaises(TypeError):
45            class X(object, _Pointer):
46                pass
47
48        from _ctypes import _SimpleCData
49        with self.assertRaises(TypeError):
50            class X(object, _SimpleCData):
51                _type_ = "i"
52
53        with self.assertRaises(TypeError):
54            class X(object, Structure):
55                _fields_ = []
56
57    @need_symbol('c_wchar')
58    def test_wchar_parm(self):
59        f = dll._testfunc_i_bhilfd
60        f.argtypes = [c_byte, c_wchar, c_int, c_long, c_float, c_double]
61        result = f(1, "x", 3, 4, 5.0, 6.0)
62        self.assertEqual(result, 139)
63        self.assertEqual(type(result), int)
64
65    @need_symbol('c_wchar')
66    def test_wchar_result(self):
67        f = dll._testfunc_i_bhilfd
68        f.argtypes = [c_byte, c_short, c_int, c_long, c_float, c_double]
69        f.restype = c_wchar
70        result = f(0, 0, 0, 0, 0, 0)
71        self.assertEqual(result, '\x00')
72
73    def test_voidresult(self):
74        f = dll._testfunc_v
75        f.restype = None
76        f.argtypes = [c_int, c_int, POINTER(c_int)]
77        result = c_int()
78        self.assertEqual(None, f(1, 2, byref(result)))
79        self.assertEqual(result.value, 3)
80
81    def test_intresult(self):
82        f = dll._testfunc_i_bhilfd
83        f.argtypes = [c_byte, c_short, c_int, c_long, c_float, c_double]
84        f.restype = c_int
85        result = f(1, 2, 3, 4, 5.0, 6.0)
86        self.assertEqual(result, 21)
87        self.assertEqual(type(result), int)
88
89        result = f(-1, -2, -3, -4, -5.0, -6.0)
90        self.assertEqual(result, -21)
91        self.assertEqual(type(result), int)
92
93        # If we declare the function to return a short,
94        # is the high part split off?
95        f.restype = c_short
96        result = f(1, 2, 3, 4, 5.0, 6.0)
97        self.assertEqual(result, 21)
98        self.assertEqual(type(result), int)
99
100        result = f(1, 2, 3, 0x10004, 5.0, 6.0)
101        self.assertEqual(result, 21)
102        self.assertEqual(type(result), int)
103
104        # You cannot assign character format codes as restype any longer
105        self.assertRaises(TypeError, setattr, f, "restype", "i")
106
107    def test_floatresult(self):
108        f = dll._testfunc_f_bhilfd
109        f.argtypes = [c_byte, c_short, c_int, c_long, c_float, c_double]
110        f.restype = c_float
111        result = f(1, 2, 3, 4, 5.0, 6.0)
112        self.assertEqual(result, 21)
113        self.assertEqual(type(result), float)
114
115        result = f(-1, -2, -3, -4, -5.0, -6.0)
116        self.assertEqual(result, -21)
117        self.assertEqual(type(result), float)
118
119    def test_doubleresult(self):
120        f = dll._testfunc_d_bhilfd
121        f.argtypes = [c_byte, c_short, c_int, c_long, c_float, c_double]
122        f.restype = c_double
123        result = f(1, 2, 3, 4, 5.0, 6.0)
124        self.assertEqual(result, 21)
125        self.assertEqual(type(result), float)
126
127        result = f(-1, -2, -3, -4, -5.0, -6.0)
128        self.assertEqual(result, -21)
129        self.assertEqual(type(result), float)
130
131    def test_longdoubleresult(self):
132        f = dll._testfunc_D_bhilfD
133        f.argtypes = [c_byte, c_short, c_int, c_long, c_float, c_longdouble]
134        f.restype = c_longdouble
135        result = f(1, 2, 3, 4, 5.0, 6.0)
136        self.assertEqual(result, 21)
137        self.assertEqual(type(result), float)
138
139        result = f(-1, -2, -3, -4, -5.0, -6.0)
140        self.assertEqual(result, -21)
141        self.assertEqual(type(result), float)
142
143    @need_symbol('c_longlong')
144    def test_longlongresult(self):
145        f = dll._testfunc_q_bhilfd
146        f.restype = c_longlong
147        f.argtypes = [c_byte, c_short, c_int, c_long, c_float, c_double]
148        result = f(1, 2, 3, 4, 5.0, 6.0)
149        self.assertEqual(result, 21)
150
151        f = dll._testfunc_q_bhilfdq
152        f.restype = c_longlong
153        f.argtypes = [c_byte, c_short, c_int, c_long, c_float, c_double, c_longlong]
154        result = f(1, 2, 3, 4, 5.0, 6.0, 21)
155        self.assertEqual(result, 42)
156
157    def test_stringresult(self):
158        f = dll._testfunc_p_p
159        f.argtypes = None
160        f.restype = c_char_p
161        result = f(b"123")
162        self.assertEqual(result, b"123")
163
164        result = f(None)
165        self.assertEqual(result, None)
166
167    def test_pointers(self):
168        f = dll._testfunc_p_p
169        f.restype = POINTER(c_int)
170        f.argtypes = [POINTER(c_int)]
171
172        # This only works if the value c_int(42) passed to the
173        # function is still alive while the pointer (the result) is
174        # used.
175
176        v = c_int(42)
177
178        self.assertEqual(pointer(v).contents.value, 42)
179        result = f(pointer(v))
180        self.assertEqual(type(result), POINTER(c_int))
181        self.assertEqual(result.contents.value, 42)
182
183        # This on works...
184        result = f(pointer(v))
185        self.assertEqual(result.contents.value, v.value)
186
187        p = pointer(c_int(99))
188        result = f(p)
189        self.assertEqual(result.contents.value, 99)
190
191        arg = byref(v)
192        result = f(arg)
193        self.assertNotEqual(result.contents, v.value)
194
195        self.assertRaises(ArgumentError, f, byref(c_short(22)))
196
197        # It is dangerous, however, because you don't control the lifetime
198        # of the pointer:
199        result = f(byref(c_int(99)))
200        self.assertNotEqual(result.contents, 99)
201
202    def test_errors(self):
203        f = dll._testfunc_p_p
204        f.restype = c_int
205
206        class X(Structure):
207            _fields_ = [("y", c_int)]
208
209        self.assertRaises(TypeError, f, X()) #cannot convert parameter
210
211    ################################################################
212    def test_shorts(self):
213        f = dll._testfunc_callback_i_if
214
215        args = []
216        expected = [262144, 131072, 65536, 32768, 16384, 8192, 4096, 2048,
217                    1024, 512, 256, 128, 64, 32, 16, 8, 4, 2, 1]
218
219        def callback(v):
220            args.append(v)
221            return v
222
223        CallBack = CFUNCTYPE(c_int, c_int)
224
225        cb = CallBack(callback)
226        f(2**18, cb)
227        self.assertEqual(args, expected)
228
229    ################################################################
230
231
232    def test_callbacks(self):
233        f = dll._testfunc_callback_i_if
234        f.restype = c_int
235        f.argtypes = None
236
237        MyCallback = CFUNCTYPE(c_int, c_int)
238
239        def callback(value):
240            #print "called back with", value
241            return value
242
243        cb = MyCallback(callback)
244        result = f(-10, cb)
245        self.assertEqual(result, -18)
246
247        # test with prototype
248        f.argtypes = [c_int, MyCallback]
249        cb = MyCallback(callback)
250        result = f(-10, cb)
251        self.assertEqual(result, -18)
252
253        AnotherCallback = WINFUNCTYPE(c_int, c_int, c_int, c_int, c_int)
254
255        # check that the prototype works: we call f with wrong
256        # argument types
257        cb = AnotherCallback(callback)
258        self.assertRaises(ArgumentError, f, -10, cb)
259
260
261    def test_callbacks_2(self):
262        # Can also use simple datatypes as argument type specifiers
263        # for the callback function.
264        # In this case the call receives an instance of that type
265        f = dll._testfunc_callback_i_if
266        f.restype = c_int
267
268        MyCallback = CFUNCTYPE(c_int, c_int)
269
270        f.argtypes = [c_int, MyCallback]
271
272        def callback(value):
273            #print "called back with", value
274            self.assertEqual(type(value), int)
275            return value
276
277        cb = MyCallback(callback)
278        result = f(-10, cb)
279        self.assertEqual(result, -18)
280
281    @need_symbol('c_longlong')
282    def test_longlong_callbacks(self):
283
284        f = dll._testfunc_callback_q_qf
285        f.restype = c_longlong
286
287        MyCallback = CFUNCTYPE(c_longlong, c_longlong)
288
289        f.argtypes = [c_longlong, MyCallback]
290
291        def callback(value):
292            self.assertIsInstance(value, int)
293            return value & 0x7FFFFFFF
294
295        cb = MyCallback(callback)
296
297        self.assertEqual(13577625587, f(1000000000000, cb))
298
299    def test_errors(self):
300        self.assertRaises(AttributeError, getattr, dll, "_xxx_yyy")
301        self.assertRaises(ValueError, c_int.in_dll, dll, "_xxx_yyy")
302
303    def test_byval(self):
304
305        # without prototype
306        ptin = POINT(1, 2)
307        ptout = POINT()
308        # EXPORT int _testfunc_byval(point in, point *pout)
309        result = dll._testfunc_byval(ptin, byref(ptout))
310        got = result, ptout.x, ptout.y
311        expected = 3, 1, 2
312        self.assertEqual(got, expected)
313
314        # with prototype
315        ptin = POINT(101, 102)
316        ptout = POINT()
317        dll._testfunc_byval.argtypes = (POINT, POINTER(POINT))
318        dll._testfunc_byval.restype = c_int
319        result = dll._testfunc_byval(ptin, byref(ptout))
320        got = result, ptout.x, ptout.y
321        expected = 203, 101, 102
322        self.assertEqual(got, expected)
323
324    def test_struct_return_2H(self):
325        class S2H(Structure):
326            _fields_ = [("x", c_short),
327                        ("y", c_short)]
328        dll.ret_2h_func.restype = S2H
329        dll.ret_2h_func.argtypes = [S2H]
330        inp = S2H(99, 88)
331        s2h = dll.ret_2h_func(inp)
332        self.assertEqual((s2h.x, s2h.y), (99*2, 88*3))
333
334    @unittest.skipUnless(sys.platform == "win32", 'Windows-specific test')
335    def test_struct_return_2H_stdcall(self):
336        class S2H(Structure):
337            _fields_ = [("x", c_short),
338                        ("y", c_short)]
339
340        windll.s_ret_2h_func.restype = S2H
341        windll.s_ret_2h_func.argtypes = [S2H]
342        s2h = windll.s_ret_2h_func(S2H(99, 88))
343        self.assertEqual((s2h.x, s2h.y), (99*2, 88*3))
344
345    def test_struct_return_8H(self):
346        class S8I(Structure):
347            _fields_ = [("a", c_int),
348                        ("b", c_int),
349                        ("c", c_int),
350                        ("d", c_int),
351                        ("e", c_int),
352                        ("f", c_int),
353                        ("g", c_int),
354                        ("h", c_int)]
355        dll.ret_8i_func.restype = S8I
356        dll.ret_8i_func.argtypes = [S8I]
357        inp = S8I(9, 8, 7, 6, 5, 4, 3, 2)
358        s8i = dll.ret_8i_func(inp)
359        self.assertEqual((s8i.a, s8i.b, s8i.c, s8i.d, s8i.e, s8i.f, s8i.g, s8i.h),
360                             (9*2, 8*3, 7*4, 6*5, 5*6, 4*7, 3*8, 2*9))
361
362    @unittest.skipUnless(sys.platform == "win32", 'Windows-specific test')
363    def test_struct_return_8H_stdcall(self):
364        class S8I(Structure):
365            _fields_ = [("a", c_int),
366                        ("b", c_int),
367                        ("c", c_int),
368                        ("d", c_int),
369                        ("e", c_int),
370                        ("f", c_int),
371                        ("g", c_int),
372                        ("h", c_int)]
373        windll.s_ret_8i_func.restype = S8I
374        windll.s_ret_8i_func.argtypes = [S8I]
375        inp = S8I(9, 8, 7, 6, 5, 4, 3, 2)
376        s8i = windll.s_ret_8i_func(inp)
377        self.assertEqual(
378                (s8i.a, s8i.b, s8i.c, s8i.d, s8i.e, s8i.f, s8i.g, s8i.h),
379                (9*2, 8*3, 7*4, 6*5, 5*6, 4*7, 3*8, 2*9))
380
381    def test_sf1651235(self):
382        # see https://www.python.org/sf/1651235
383
384        proto = CFUNCTYPE(c_int, RECT, POINT)
385        def callback(*args):
386            return 0
387
388        callback = proto(callback)
389        self.assertRaises(ArgumentError, lambda: callback((1, 2, 3, 4), POINT()))
390
391if __name__ == '__main__':
392    unittest.main()
393