| import py, sys, os |
| import subprocess, weakref |
| from cffi import FFI |
| from cffi.backend_ctypes import CTypesBackend |
| from testing.support import u |
| |
| |
| SOURCE = """\ |
| #include <errno.h> |
| |
| #ifdef _WIN32 |
| #define EXPORT __declspec(dllexport) |
| #else |
| #define EXPORT |
| #endif |
| |
| EXPORT int test_getting_errno(void) { |
| errno = 123; |
| return -1; |
| } |
| |
| EXPORT int test_setting_errno(void) { |
| return errno; |
| }; |
| |
| typedef struct { |
| long x; |
| long y; |
| } POINT; |
| |
| typedef struct { |
| long left; |
| long top; |
| long right; |
| long bottom; |
| } RECT; |
| |
| |
| EXPORT int PointInRect(RECT *prc, POINT pt) |
| { |
| if (pt.x < prc->left) |
| return 0; |
| if (pt.x > prc->right) |
| return 0; |
| if (pt.y < prc->top) |
| return 0; |
| if (pt.y > prc->bottom) |
| return 0; |
| return 1; |
| }; |
| |
| EXPORT long left = 10; |
| EXPORT long top = 20; |
| EXPORT long right = 30; |
| EXPORT long bottom = 40; |
| |
| EXPORT RECT ReturnRect(int i, RECT ar, RECT* br, POINT cp, RECT dr, |
| RECT *er, POINT fp, RECT gr) |
| { |
| /*Check input */ |
| if (ar.left + br->left + dr.left + er->left + gr.left != left * 5) |
| { |
| ar.left = 100; |
| return ar; |
| } |
| if (ar.right + br->right + dr.right + er->right + gr.right != right * 5) |
| { |
| ar.right = 100; |
| return ar; |
| } |
| if (cp.x != fp.x) |
| { |
| ar.left = -100; |
| } |
| if (cp.y != fp.y) |
| { |
| ar.left = -200; |
| } |
| switch(i) |
| { |
| case 0: |
| return ar; |
| break; |
| case 1: |
| return dr; |
| break; |
| case 2: |
| return gr; |
| break; |
| |
| } |
| return ar; |
| } |
| |
| EXPORT int my_array[7] = {0, 1, 2, 3, 4, 5, 6}; |
| |
| EXPORT unsigned short foo_2bytes(unsigned short a) |
| { |
| return (unsigned short)(a + 42); |
| } |
| EXPORT unsigned int foo_4bytes(unsigned int a) |
| { |
| return (unsigned int)(a + 42); |
| } |
| |
| EXPORT void modify_struct_value(RECT r) |
| { |
| r.left = r.right = r.top = r.bottom = 500; |
| } |
| """ |
| |
| class TestOwnLib(object): |
| Backend = CTypesBackend |
| |
| def setup_class(cls): |
| cls.module = None |
| from testing.udir import udir |
| udir.join('testownlib.c').write(SOURCE) |
| if sys.platform == 'win32': |
| # did we already build it? |
| if cls.Backend is CTypesBackend: |
| dll_path = str(udir) + '\\testownlib1.dll' # only ascii for the ctypes backend |
| else: |
| dll_path = str(udir) + '\\' + (u+'testownlib\u03be.dll') # non-ascii char |
| if os.path.exists(dll_path): |
| cls.module = dll_path |
| return |
| # try (not too hard) to find the version used to compile this python |
| # no mingw |
| from distutils.msvc9compiler import get_build_version |
| version = get_build_version() |
| toolskey = "VS%0.f0COMNTOOLS" % version |
| toolsdir = os.environ.get(toolskey, None) |
| if toolsdir is None: |
| return |
| productdir = os.path.join(toolsdir, os.pardir, os.pardir, "VC") |
| productdir = os.path.abspath(productdir) |
| vcvarsall = os.path.join(productdir, "vcvarsall.bat") |
| # 64? |
| arch = 'x86' |
| if sys.maxsize > 2**32: |
| arch = 'amd64' |
| if os.path.isfile(vcvarsall): |
| cmd = '"%s" %s' % (vcvarsall, arch) + ' & cl.exe testownlib.c ' \ |
| ' /LD /Fetestownlib.dll' |
| subprocess.check_call(cmd, cwd = str(udir), shell=True) |
| os.rename(str(udir) + '\\testownlib.dll', dll_path) |
| cls.module = dll_path |
| else: |
| encoded = None |
| if cls.Backend is not CTypesBackend: |
| try: |
| unicode_name = u+'testownlibcaf\xe9' |
| encoded = unicode_name.encode(sys.getfilesystemencoding()) |
| if sys.version_info >= (3,): |
| encoded = str(unicode_name) |
| except UnicodeEncodeError: |
| pass |
| if encoded is None: |
| unicode_name = u+'testownlib' |
| encoded = str(unicode_name) |
| subprocess.check_call( |
| "cc testownlib.c -shared -fPIC -o '%s.so'" % (encoded,), |
| cwd=str(udir), shell=True) |
| cls.module = os.path.join(str(udir), unicode_name + (u+'.so')) |
| print(repr(cls.module)) |
| |
| def test_getting_errno(self): |
| if self.module is None: |
| py.test.skip("fix the auto-generation of the tiny test lib") |
| if sys.platform == 'win32': |
| py.test.skip("fails, errno at multiple addresses") |
| ffi = FFI(backend=self.Backend()) |
| ffi.cdef(""" |
| int test_getting_errno(void); |
| """) |
| ownlib = ffi.dlopen(self.module) |
| res = ownlib.test_getting_errno() |
| assert res == -1 |
| assert ffi.errno == 123 |
| |
| def test_setting_errno(self): |
| if self.module is None: |
| py.test.skip("fix the auto-generation of the tiny test lib") |
| if sys.platform == 'win32': |
| py.test.skip("fails, errno at multiple addresses") |
| if self.Backend is CTypesBackend and '__pypy__' in sys.modules: |
| py.test.skip("XXX errno issue with ctypes on pypy?") |
| ffi = FFI(backend=self.Backend()) |
| ffi.cdef(""" |
| int test_setting_errno(void); |
| """) |
| ownlib = ffi.dlopen(self.module) |
| ffi.errno = 42 |
| res = ownlib.test_setting_errno() |
| assert res == 42 |
| assert ffi.errno == 42 |
| |
| def test_my_array_7(self): |
| if self.module is None: |
| py.test.skip("fix the auto-generation of the tiny test lib") |
| ffi = FFI(backend=self.Backend()) |
| ffi.cdef(""" |
| int my_array[7]; |
| """) |
| ownlib = ffi.dlopen(self.module) |
| for i in range(7): |
| assert ownlib.my_array[i] == i |
| assert len(ownlib.my_array) == 7 |
| if self.Backend is CTypesBackend: |
| py.test.skip("not supported by the ctypes backend") |
| ownlib.my_array = list(range(10, 17)) |
| for i in range(7): |
| assert ownlib.my_array[i] == 10 + i |
| ownlib.my_array = list(range(7)) |
| for i in range(7): |
| assert ownlib.my_array[i] == i |
| |
| def test_my_array_no_length(self): |
| if self.module is None: |
| py.test.skip("fix the auto-generation of the tiny test lib") |
| if self.Backend is CTypesBackend: |
| py.test.skip("not supported by the ctypes backend") |
| ffi = FFI(backend=self.Backend()) |
| ffi.cdef(""" |
| int my_array[]; |
| """) |
| ownlib = ffi.dlopen(self.module) |
| for i in range(7): |
| assert ownlib.my_array[i] == i |
| py.test.raises(TypeError, len, ownlib.my_array) |
| ownlib.my_array = list(range(10, 17)) |
| for i in range(7): |
| assert ownlib.my_array[i] == 10 + i |
| ownlib.my_array = list(range(7)) |
| for i in range(7): |
| assert ownlib.my_array[i] == i |
| |
| def test_keepalive_lib(self): |
| if self.module is None: |
| py.test.skip("fix the auto-generation of the tiny test lib") |
| ffi = FFI(backend=self.Backend()) |
| ffi.cdef(""" |
| int test_getting_errno(void); |
| """) |
| ownlib = ffi.dlopen(self.module) |
| ffi_r = weakref.ref(ffi) |
| ownlib_r = weakref.ref(ownlib) |
| func = ownlib.test_getting_errno |
| del ffi |
| import gc; gc.collect() # ownlib stays alive |
| assert ownlib_r() is not None |
| assert ffi_r() is not None # kept alive by ownlib |
| res = func() |
| assert res == -1 |
| |
| def test_keepalive_ffi(self): |
| if self.module is None: |
| py.test.skip("fix the auto-generation of the tiny test lib") |
| ffi = FFI(backend=self.Backend()) |
| ffi.cdef(""" |
| int test_getting_errno(void); |
| """) |
| ownlib = ffi.dlopen(self.module) |
| ffi_r = weakref.ref(ffi) |
| ownlib_r = weakref.ref(ownlib) |
| func = ownlib.test_getting_errno |
| del ownlib |
| import gc; gc.collect() # ffi stays alive |
| assert ffi_r() is not None |
| assert ownlib_r() is not None # kept alive by ffi |
| res = func() |
| assert res == -1 |
| if sys.platform != 'win32': # else, errno at multiple addresses |
| assert ffi.errno == 123 |
| |
| def test_struct_by_value(self): |
| if self.module is None: |
| py.test.skip("fix the auto-generation of the tiny test lib") |
| ffi = FFI(backend=self.Backend()) |
| ffi.cdef(""" |
| typedef struct { |
| long x; |
| long y; |
| } POINT; |
| |
| typedef struct { |
| long left; |
| long top; |
| long right; |
| long bottom; |
| } RECT; |
| |
| long left, top, right, bottom; |
| |
| RECT ReturnRect(int i, RECT ar, RECT* br, POINT cp, RECT dr, |
| RECT *er, POINT fp, RECT gr); |
| """) |
| ownlib = ffi.dlopen(self.module) |
| |
| rect = ffi.new('RECT[1]') |
| pt = ffi.new('POINT[1]') |
| pt[0].x = 15 |
| pt[0].y = 25 |
| rect[0].left = ownlib.left |
| rect[0].right = ownlib.right |
| rect[0].top = ownlib.top |
| rect[0].bottom = ownlib.bottom |
| |
| for i in range(4): |
| ret = ownlib.ReturnRect(i, rect[0], rect, pt[0], rect[0], |
| rect, pt[0], rect[0]) |
| assert ret.left == ownlib.left |
| assert ret.right == ownlib.right |
| assert ret.top == ownlib.top |
| assert ret.bottom == ownlib.bottom |
| |
| def test_addressof_lib(self): |
| if self.module is None: |
| py.test.skip("fix the auto-generation of the tiny test lib") |
| if self.Backend is CTypesBackend: |
| py.test.skip("not implemented with the ctypes backend") |
| ffi = FFI(backend=self.Backend()) |
| ffi.cdef("long left; int test_getting_errno(void);") |
| lib = ffi.dlopen(self.module) |
| lib.left = 123456 |
| p = ffi.addressof(lib, "left") |
| assert ffi.typeof(p) == ffi.typeof("long *") |
| assert p[0] == 123456 |
| p[0] += 1 |
| assert lib.left == 123457 |
| pfn = ffi.addressof(lib, "test_getting_errno") |
| assert ffi.typeof(pfn) == ffi.typeof("int(*)(void)") |
| assert pfn == lib.test_getting_errno |
| |
| def test_char16_char32_t(self): |
| if self.module is None: |
| py.test.skip("fix the auto-generation of the tiny test lib") |
| if self.Backend is CTypesBackend: |
| py.test.skip("not implemented with the ctypes backend") |
| ffi = FFI(backend=self.Backend()) |
| ffi.cdef(""" |
| char16_t foo_2bytes(char16_t); |
| char32_t foo_4bytes(char32_t); |
| """) |
| lib = ffi.dlopen(self.module) |
| assert lib.foo_2bytes(u+'\u1234') == u+'\u125e' |
| assert lib.foo_4bytes(u+'\u1234') == u+'\u125e' |
| assert lib.foo_4bytes(u+'\U00012345') == u+'\U0001236f' |
| |
| def test_modify_struct_value(self): |
| if self.module is None: |
| py.test.skip("fix the auto-generation of the tiny test lib") |
| if self.Backend is CTypesBackend: |
| py.test.skip("fails with the ctypes backend on some architectures") |
| ffi = FFI(backend=self.Backend()) |
| ffi.cdef(""" |
| typedef struct { |
| long left; |
| long top; |
| long right; |
| long bottom; |
| } RECT; |
| |
| void modify_struct_value(RECT r); |
| """) |
| lib = ffi.dlopen(self.module) |
| s = ffi.new("RECT *", [11, 22, 33, 44]) |
| lib.modify_struct_value(s[0]) |
| assert s.left == 11 |
| assert s.top == 22 |
| assert s.right == 33 |
| assert s.bottom == 44 |