1import unittest
2from ctypes import *
3import _ctypes_test
4import sys
5
6dll = CDLL(_ctypes_test.__file__)
7
8try:
9    CALLBACK_FUNCTYPE = WINFUNCTYPE
10except NameError:
11    # fake to enable this test on Linux
12    CALLBACK_FUNCTYPE = CFUNCTYPE
13
14class POINT(Structure):
15    _fields_ = [("x", c_int), ("y", c_int)]
16
17class BasicWrapTestCase(unittest.TestCase):
18    def wrap(self, param):
19        return param
20
21    def test_wchar_parm(self):
22        try:
23            c_wchar
24        except NameError:
25            return
26        f = dll._testfunc_i_bhilfd
27        f.argtypes = [c_byte, c_wchar, c_int, c_long, c_float, c_double]
28        result = f(self.wrap(1), self.wrap(u"x"), self.wrap(3), self.wrap(4), self.wrap(5.0), self.wrap(6.0))
29        self.assertEqual(result, 139)
30        self.assertTrue(type(result), int)
31
32    def test_pointers(self):
33        f = dll._testfunc_p_p
34        f.restype = POINTER(c_int)
35        f.argtypes = [POINTER(c_int)]
36
37        # This only works if the value c_int(42) passed to the
38        # function is still alive while the pointer (the result) is
39        # used.
40
41        v = c_int(42)
42
43        self.assertEqual(pointer(v).contents.value, 42)
44        result = f(self.wrap(pointer(v)))
45        self.assertEqual(type(result), POINTER(c_int))
46        self.assertEqual(result.contents.value, 42)
47
48        # This on works...
49        result = f(self.wrap(pointer(v)))
50        self.assertEqual(result.contents.value, v.value)
51
52        p = pointer(c_int(99))
53        result = f(self.wrap(p))
54        self.assertEqual(result.contents.value, 99)
55
56    def test_shorts(self):
57        f = dll._testfunc_callback_i_if
58
59        args = []
60        expected = [262144, 131072, 65536, 32768, 16384, 8192, 4096, 2048,
61                    1024, 512, 256, 128, 64, 32, 16, 8, 4, 2, 1]
62
63        def callback(v):
64            args.append(v)
65            return v
66
67        CallBack = CFUNCTYPE(c_int, c_int)
68
69        cb = CallBack(callback)
70        f(self.wrap(2**18), self.wrap(cb))
71        self.assertEqual(args, expected)
72
73    ################################################################
74
75    def test_callbacks(self):
76        f = dll._testfunc_callback_i_if
77        f.restype = c_int
78        f.argtypes = None
79
80        MyCallback = CFUNCTYPE(c_int, c_int)
81
82        def callback(value):
83            #print "called back with", value
84            return value
85
86        cb = MyCallback(callback)
87
88        result = f(self.wrap(-10), self.wrap(cb))
89        self.assertEqual(result, -18)
90
91        # test with prototype
92        f.argtypes = [c_int, MyCallback]
93        cb = MyCallback(callback)
94
95        result = f(self.wrap(-10), self.wrap(cb))
96        self.assertEqual(result, -18)
97
98        result = f(self.wrap(-10), self.wrap(cb))
99        self.assertEqual(result, -18)
100
101        AnotherCallback = CALLBACK_FUNCTYPE(c_int, c_int, c_int, c_int, c_int)
102
103        # check that the prototype works: we call f with wrong
104        # argument types
105        cb = AnotherCallback(callback)
106        self.assertRaises(ArgumentError, f, self.wrap(-10), self.wrap(cb))
107
108    def test_callbacks_2(self):
109        # Can also use simple datatypes as argument type specifiers
110        # for the callback function.
111        # In this case the call receives an instance of that type
112        f = dll._testfunc_callback_i_if
113        f.restype = c_int
114
115        MyCallback = CFUNCTYPE(c_int, c_int)
116
117        f.argtypes = [c_int, MyCallback]
118
119        def callback(value):
120            #print "called back with", value
121            self.assertEqual(type(value), int)
122            return value
123
124        cb = MyCallback(callback)
125        result = f(self.wrap(-10), self.wrap(cb))
126        self.assertEqual(result, -18)
127
128    def test_longlong_callbacks(self):
129
130        f = dll._testfunc_callback_q_qf
131        f.restype = c_longlong
132
133        MyCallback = CFUNCTYPE(c_longlong, c_longlong)
134
135        f.argtypes = [c_longlong, MyCallback]
136
137        def callback(value):
138            self.assertTrue(isinstance(value, (int, long)))
139            return value & 0x7FFFFFFF
140
141        cb = MyCallback(callback)
142
143        self.assertEqual(13577625587, int(f(self.wrap(1000000000000), self.wrap(cb))))
144
145    def test_byval(self):
146        # without prototype
147        ptin = POINT(1, 2)
148        ptout = POINT()
149        # EXPORT int _testfunc_byval(point in, point *pout)
150        result = dll._testfunc_byval(ptin, byref(ptout))
151        got = result, ptout.x, ptout.y
152        expected = 3, 1, 2
153        self.assertEqual(got, expected)
154
155        # with prototype
156        ptin = POINT(101, 102)
157        ptout = POINT()
158        dll._testfunc_byval.argtypes = (POINT, POINTER(POINT))
159        dll._testfunc_byval.restype = c_int
160        result = dll._testfunc_byval(self.wrap(ptin), byref(ptout))
161        got = result, ptout.x, ptout.y
162        expected = 203, 101, 102
163        self.assertEqual(got, expected)
164
165    def test_struct_return_2H(self):
166        class S2H(Structure):
167            _fields_ = [("x", c_short),
168                        ("y", c_short)]
169        dll.ret_2h_func.restype = S2H
170        dll.ret_2h_func.argtypes = [S2H]
171        inp = S2H(99, 88)
172        s2h = dll.ret_2h_func(self.wrap(inp))
173        self.assertEqual((s2h.x, s2h.y), (99*2, 88*3))
174
175    # This is known cdecl incompatibility between GCC
176    # and MSVC. It is addressed in GCC issue #36834.
177    # Python libffi detect it and complain.
178    @unittest.skipIf(sys.platform == "win32" and sys.version.find("GCC") >= 0, 'XFAIL GCC(mingw)')
179    def test_struct_return_8H(self):
180        class S8I(Structure):
181            _fields_ = [("a", c_int),
182                        ("b", c_int),
183                        ("c", c_int),
184                        ("d", c_int),
185                        ("e", c_int),
186                        ("f", c_int),
187                        ("g", c_int),
188                        ("h", c_int)]
189        dll.ret_8i_func.restype = S8I
190        dll.ret_8i_func.argtypes = [S8I]
191        inp = S8I(9, 8, 7, 6, 5, 4, 3, 2)
192        s8i = dll.ret_8i_func(self.wrap(inp))
193        self.assertEqual((s8i.a, s8i.b, s8i.c, s8i.d, s8i.e, s8i.f, s8i.g, s8i.h),
194                             (9*2, 8*3, 7*4, 6*5, 5*6, 4*7, 3*8, 2*9))
195
196    def test_recursive_as_param(self):
197        from ctypes import c_int
198
199        class A(object):
200            pass
201
202        a = A()
203        a._as_parameter_ = a
204        with self.assertRaises(RuntimeError):
205            c_int.from_param(a)
206
207
208#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
209
210class AsParamWrapper(object):
211    def __init__(self, param):
212        self._as_parameter_ = param
213
214class AsParamWrapperTestCase(BasicWrapTestCase):
215    wrap = AsParamWrapper
216
217#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
218
219class AsParamPropertyWrapper(object):
220    def __init__(self, param):
221        self._param = param
222
223    def getParameter(self):
224        return self._param
225    _as_parameter_ = property(getParameter)
226
227class AsParamPropertyWrapperTestCase(BasicWrapTestCase):
228    wrap = AsParamPropertyWrapper
229
230#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
231
232if __name__ == '__main__':
233    unittest.main()
234