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