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