• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1import py, sys, os
2import subprocess, weakref
3from cffi import FFI
4from cffi.backend_ctypes import CTypesBackend
5from testing.support import u
6
7
8SOURCE = """\
9#include <errno.h>
10
11#ifdef _WIN32
12#define EXPORT __declspec(dllexport)
13#else
14#define EXPORT
15#endif
16
17EXPORT int test_getting_errno(void) {
18    errno = 123;
19    return -1;
20}
21
22EXPORT int test_setting_errno(void) {
23    return errno;
24};
25
26typedef struct {
27    long x;
28    long y;
29} POINT;
30
31typedef struct {
32    long left;
33    long top;
34    long right;
35    long bottom;
36} RECT;
37
38
39EXPORT int PointInRect(RECT *prc, POINT pt)
40{
41    if (pt.x < prc->left)
42        return 0;
43    if (pt.x > prc->right)
44        return 0;
45    if (pt.y < prc->top)
46        return 0;
47    if (pt.y > prc->bottom)
48        return 0;
49    return 1;
50};
51
52EXPORT long left = 10;
53EXPORT long top = 20;
54EXPORT long right = 30;
55EXPORT long bottom = 40;
56
57EXPORT RECT ReturnRect(int i, RECT ar, RECT* br, POINT cp, RECT dr,
58                        RECT *er, POINT fp, RECT gr)
59{
60    /*Check input */
61    if (ar.left + br->left + dr.left + er->left + gr.left != left * 5)
62    {
63        ar.left = 100;
64        return ar;
65    }
66    if (ar.right + br->right + dr.right + er->right + gr.right != right * 5)
67    {
68        ar.right = 100;
69        return ar;
70    }
71    if (cp.x != fp.x)
72    {
73        ar.left = -100;
74    }
75    if (cp.y != fp.y)
76    {
77        ar.left = -200;
78    }
79    switch(i)
80    {
81    case 0:
82        return ar;
83        break;
84    case 1:
85        return dr;
86        break;
87    case 2:
88        return gr;
89        break;
90
91    }
92    return ar;
93}
94
95EXPORT int my_array[7] = {0, 1, 2, 3, 4, 5, 6};
96
97EXPORT unsigned short foo_2bytes(unsigned short a)
98{
99    return (unsigned short)(a + 42);
100}
101EXPORT unsigned int foo_4bytes(unsigned int a)
102{
103    return (unsigned int)(a + 42);
104}
105
106EXPORT void modify_struct_value(RECT r)
107{
108    r.left = r.right = r.top = r.bottom = 500;
109}
110"""
111
112class TestOwnLib(object):
113    Backend = CTypesBackend
114
115    def setup_class(cls):
116        cls.module = None
117        from testing.udir import udir
118        udir.join('testownlib.c').write(SOURCE)
119        if sys.platform == 'win32':
120            # did we already build it?
121            if cls.Backend is CTypesBackend:
122                dll_path = str(udir) + '\\testownlib1.dll'   # only ascii for the ctypes backend
123            else:
124                dll_path = str(udir) + '\\' + (u+'testownlib\u03be.dll')   # non-ascii char
125            if os.path.exists(dll_path):
126                cls.module = dll_path
127                return
128            # try (not too hard) to find the version used to compile this python
129            # no mingw
130            from distutils.msvc9compiler import get_build_version
131            version = get_build_version()
132            toolskey = "VS%0.f0COMNTOOLS" % version
133            toolsdir = os.environ.get(toolskey, None)
134            if toolsdir is None:
135                return
136            productdir = os.path.join(toolsdir, os.pardir, os.pardir, "VC")
137            productdir = os.path.abspath(productdir)
138            vcvarsall = os.path.join(productdir, "vcvarsall.bat")
139            # 64?
140            arch = 'x86'
141            if sys.maxsize > 2**32:
142                arch = 'amd64'
143            if os.path.isfile(vcvarsall):
144                cmd = '"%s" %s' % (vcvarsall, arch) + ' & cl.exe testownlib.c ' \
145                        ' /LD /Fetestownlib.dll'
146                subprocess.check_call(cmd, cwd = str(udir), shell=True)
147                os.rename(str(udir) + '\\testownlib.dll', dll_path)
148                cls.module = dll_path
149        else:
150            encoded = None
151            if cls.Backend is not CTypesBackend:
152                try:
153                    unicode_name = u+'testownlibcaf\xe9'
154                    encoded = unicode_name.encode(sys.getfilesystemencoding())
155                    if sys.version_info >= (3,):
156                        encoded = str(unicode_name)
157                except UnicodeEncodeError:
158                    pass
159            if encoded is None:
160                unicode_name = u+'testownlib'
161                encoded = str(unicode_name)
162            subprocess.check_call(
163                "cc testownlib.c -shared -fPIC -o '%s.so'" % (encoded,),
164                cwd=str(udir), shell=True)
165            cls.module = os.path.join(str(udir), unicode_name + (u+'.so'))
166        print(repr(cls.module))
167
168    def test_getting_errno(self):
169        if self.module is None:
170            py.test.skip("fix the auto-generation of the tiny test lib")
171        if sys.platform == 'win32':
172            py.test.skip("fails, errno at multiple addresses")
173        ffi = FFI(backend=self.Backend())
174        ffi.cdef("""
175            int test_getting_errno(void);
176        """)
177        ownlib = ffi.dlopen(self.module)
178        res = ownlib.test_getting_errno()
179        assert res == -1
180        assert ffi.errno == 123
181
182    def test_setting_errno(self):
183        if self.module is None:
184            py.test.skip("fix the auto-generation of the tiny test lib")
185        if sys.platform == 'win32':
186            py.test.skip("fails, errno at multiple addresses")
187        if self.Backend is CTypesBackend and '__pypy__' in sys.modules:
188            py.test.skip("XXX errno issue with ctypes on pypy?")
189        ffi = FFI(backend=self.Backend())
190        ffi.cdef("""
191            int test_setting_errno(void);
192        """)
193        ownlib = ffi.dlopen(self.module)
194        ffi.errno = 42
195        res = ownlib.test_setting_errno()
196        assert res == 42
197        assert ffi.errno == 42
198
199    def test_my_array_7(self):
200        if self.module is None:
201            py.test.skip("fix the auto-generation of the tiny test lib")
202        ffi = FFI(backend=self.Backend())
203        ffi.cdef("""
204            int my_array[7];
205        """)
206        ownlib = ffi.dlopen(self.module)
207        for i in range(7):
208            assert ownlib.my_array[i] == i
209        assert len(ownlib.my_array) == 7
210        if self.Backend is CTypesBackend:
211            py.test.skip("not supported by the ctypes backend")
212        ownlib.my_array = list(range(10, 17))
213        for i in range(7):
214            assert ownlib.my_array[i] == 10 + i
215        ownlib.my_array = list(range(7))
216        for i in range(7):
217            assert ownlib.my_array[i] == i
218
219    def test_my_array_no_length(self):
220        if self.module is None:
221            py.test.skip("fix the auto-generation of the tiny test lib")
222        if self.Backend is CTypesBackend:
223            py.test.skip("not supported by the ctypes backend")
224        ffi = FFI(backend=self.Backend())
225        ffi.cdef("""
226            int my_array[];
227        """)
228        ownlib = ffi.dlopen(self.module)
229        for i in range(7):
230            assert ownlib.my_array[i] == i
231        py.test.raises(TypeError, len, ownlib.my_array)
232        ownlib.my_array = list(range(10, 17))
233        for i in range(7):
234            assert ownlib.my_array[i] == 10 + i
235        ownlib.my_array = list(range(7))
236        for i in range(7):
237            assert ownlib.my_array[i] == i
238
239    def test_keepalive_lib(self):
240        if self.module is None:
241            py.test.skip("fix the auto-generation of the tiny test lib")
242        ffi = FFI(backend=self.Backend())
243        ffi.cdef("""
244            int test_getting_errno(void);
245        """)
246        ownlib = ffi.dlopen(self.module)
247        ffi_r = weakref.ref(ffi)
248        ownlib_r = weakref.ref(ownlib)
249        func = ownlib.test_getting_errno
250        del ffi
251        import gc; gc.collect()       # ownlib stays alive
252        assert ownlib_r() is not None
253        assert ffi_r() is not None    # kept alive by ownlib
254        res = func()
255        assert res == -1
256
257    def test_keepalive_ffi(self):
258        if self.module is None:
259            py.test.skip("fix the auto-generation of the tiny test lib")
260        ffi = FFI(backend=self.Backend())
261        ffi.cdef("""
262            int test_getting_errno(void);
263        """)
264        ownlib = ffi.dlopen(self.module)
265        ffi_r = weakref.ref(ffi)
266        ownlib_r = weakref.ref(ownlib)
267        func = ownlib.test_getting_errno
268        del ownlib
269        import gc; gc.collect()       # ffi stays alive
270        assert ffi_r() is not None
271        assert ownlib_r() is not None # kept alive by ffi
272        res = func()
273        assert res == -1
274        if sys.platform != 'win32':  # else, errno at multiple addresses
275            assert ffi.errno == 123
276
277    def test_struct_by_value(self):
278        if self.module is None:
279            py.test.skip("fix the auto-generation of the tiny test lib")
280        ffi = FFI(backend=self.Backend())
281        ffi.cdef("""
282            typedef struct {
283                long x;
284                long y;
285            } POINT;
286
287            typedef struct {
288                long left;
289                long top;
290                long right;
291                long bottom;
292            } RECT;
293
294            long left, top, right, bottom;
295
296            RECT ReturnRect(int i, RECT ar, RECT* br, POINT cp, RECT dr,
297                        RECT *er, POINT fp, RECT gr);
298        """)
299        ownlib = ffi.dlopen(self.module)
300
301        rect = ffi.new('RECT[1]')
302        pt = ffi.new('POINT[1]')
303        pt[0].x = 15
304        pt[0].y = 25
305        rect[0].left = ownlib.left
306        rect[0].right = ownlib.right
307        rect[0].top = ownlib.top
308        rect[0].bottom = ownlib.bottom
309
310        for i in range(4):
311            ret = ownlib.ReturnRect(i, rect[0], rect, pt[0], rect[0],
312                                    rect, pt[0], rect[0])
313            assert ret.left == ownlib.left
314            assert ret.right == ownlib.right
315            assert ret.top == ownlib.top
316            assert ret.bottom == ownlib.bottom
317
318    def test_addressof_lib(self):
319        if self.module is None:
320            py.test.skip("fix the auto-generation of the tiny test lib")
321        if self.Backend is CTypesBackend:
322            py.test.skip("not implemented with the ctypes backend")
323        ffi = FFI(backend=self.Backend())
324        ffi.cdef("long left; int test_getting_errno(void);")
325        lib = ffi.dlopen(self.module)
326        lib.left = 123456
327        p = ffi.addressof(lib, "left")
328        assert ffi.typeof(p) == ffi.typeof("long *")
329        assert p[0] == 123456
330        p[0] += 1
331        assert lib.left == 123457
332        pfn = ffi.addressof(lib, "test_getting_errno")
333        assert ffi.typeof(pfn) == ffi.typeof("int(*)(void)")
334        assert pfn == lib.test_getting_errno
335
336    def test_char16_char32_t(self):
337        if self.module is None:
338            py.test.skip("fix the auto-generation of the tiny test lib")
339        if self.Backend is CTypesBackend:
340            py.test.skip("not implemented with the ctypes backend")
341        ffi = FFI(backend=self.Backend())
342        ffi.cdef("""
343            char16_t foo_2bytes(char16_t);
344            char32_t foo_4bytes(char32_t);
345        """)
346        lib = ffi.dlopen(self.module)
347        assert lib.foo_2bytes(u+'\u1234') == u+'\u125e'
348        assert lib.foo_4bytes(u+'\u1234') == u+'\u125e'
349        assert lib.foo_4bytes(u+'\U00012345') == u+'\U0001236f'
350
351    def test_modify_struct_value(self):
352        if self.module is None:
353            py.test.skip("fix the auto-generation of the tiny test lib")
354        if self.Backend is CTypesBackend:
355            py.test.skip("fails with the ctypes backend on some architectures")
356        ffi = FFI(backend=self.Backend())
357        ffi.cdef("""
358            typedef struct {
359                long left;
360                long top;
361                long right;
362                long bottom;
363            } RECT;
364
365            void modify_struct_value(RECT r);
366        """)
367        lib = ffi.dlopen(self.module)
368        s = ffi.new("RECT *", [11, 22, 33, 44])
369        lib.modify_struct_value(s[0])
370        assert s.left == 11
371        assert s.top == 22
372        assert s.right == 33
373        assert s.bottom == 44
374