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