1import py, sys 2import pytest 3import _cffi_backend as _cffi1_backend 4 5 6def test_ffi_new(): 7 ffi = _cffi1_backend.FFI() 8 p = ffi.new("int *") 9 p[0] = -42 10 assert p[0] == -42 11 assert type(ffi) is ffi.__class__ is _cffi1_backend.FFI 12 13def test_ffi_subclass(): 14 class FOO(_cffi1_backend.FFI): 15 def __init__(self, x): 16 self.x = x 17 foo = FOO(42) 18 assert foo.x == 42 19 p = foo.new("int *") 20 assert p[0] == 0 21 assert type(foo) is foo.__class__ is FOO 22 23def test_ffi_no_argument(): 24 py.test.raises(TypeError, _cffi1_backend.FFI, 42) 25 26def test_ffi_cache_type(): 27 ffi = _cffi1_backend.FFI() 28 t1 = ffi.typeof("int **") 29 t2 = ffi.typeof("int *") 30 assert t2.item is t1.item.item 31 assert t2 is t1.item 32 assert ffi.typeof("int[][10]") is ffi.typeof("int[][10]") 33 assert ffi.typeof("int(*)()") is ffi.typeof("int(*)()") 34 35def test_ffi_type_not_immortal(): 36 import weakref, gc 37 ffi = _cffi1_backend.FFI() 38 t1 = ffi.typeof("int **") 39 t2 = ffi.typeof("int *") 40 w1 = weakref.ref(t1) 41 w2 = weakref.ref(t2) 42 del t1, ffi 43 gc.collect() 44 assert w1() is None 45 assert w2() is t2 46 ffi = _cffi1_backend.FFI() 47 assert ffi.typeof(ffi.new("int **")[0]) is t2 48 # 49 ffi = _cffi1_backend.FFI() 50 t1 = ffi.typeof("int ***") 51 t2 = ffi.typeof("int **") 52 w1 = weakref.ref(t1) 53 w2 = weakref.ref(t2) 54 del t2, ffi 55 gc.collect() 56 assert w1() is t1 57 assert w2() is not None # kept alive by t1 58 ffi = _cffi1_backend.FFI() 59 assert ffi.typeof("int * *") is t1.item 60 61def test_ffi_cache_type_globally(): 62 ffi1 = _cffi1_backend.FFI() 63 ffi2 = _cffi1_backend.FFI() 64 t1 = ffi1.typeof("int *") 65 t2 = ffi2.typeof("int *") 66 assert t1 is t2 67 68def test_ffi_invalid(): 69 ffi = _cffi1_backend.FFI() 70 # array of 10 times an "int[]" is invalid 71 py.test.raises(ValueError, ffi.typeof, "int[10][]") 72 73def test_ffi_docstrings(): 74 # check that all methods of the FFI class have a docstring. 75 check_type = type(_cffi1_backend.FFI.new) 76 for methname in dir(_cffi1_backend.FFI): 77 if not methname.startswith('_'): 78 method = getattr(_cffi1_backend.FFI, methname) 79 if isinstance(method, check_type): 80 assert method.__doc__, "method FFI.%s() has no docstring" % ( 81 methname,) 82 83def test_ffi_NULL(): 84 NULL = _cffi1_backend.FFI.NULL 85 assert _cffi1_backend.FFI().typeof(NULL).cname == "void *" 86 87def test_ffi_no_attr(): 88 ffi = _cffi1_backend.FFI() 89 with pytest.raises(AttributeError): 90 ffi.no_such_name 91 with pytest.raises(AttributeError): 92 ffi.no_such_name = 42 93 with pytest.raises(AttributeError): 94 del ffi.no_such_name 95 96def test_ffi_string(): 97 ffi = _cffi1_backend.FFI() 98 p = ffi.new("char[]", init=b"foobar\x00baz") 99 assert ffi.string(p) == b"foobar" 100 assert ffi.string(cdata=p, maxlen=3) == b"foo" 101 102def test_ffi_errno(): 103 # xxx not really checking errno, just checking that we can read/write it 104 ffi = _cffi1_backend.FFI() 105 ffi.errno = 42 106 assert ffi.errno == 42 107 108def test_ffi_alignof(): 109 ffi = _cffi1_backend.FFI() 110 assert ffi.alignof("int") == 4 111 assert ffi.alignof("int[]") == 4 112 assert ffi.alignof("int[41]") == 4 113 assert ffi.alignof("short[41]") == 2 114 assert ffi.alignof(ffi.new("int[41]")) == 4 115 assert ffi.alignof(ffi.new("int[]", 41)) == 4 116 117def test_ffi_sizeof(): 118 ffi = _cffi1_backend.FFI() 119 assert ffi.sizeof("int") == 4 120 py.test.raises(ffi.error, ffi.sizeof, "int[]") 121 assert ffi.sizeof("int[41]") == 41 * 4 122 assert ffi.sizeof(ffi.new("int[41]")) == 41 * 4 123 assert ffi.sizeof(ffi.new("int[]", 41)) == 41 * 4 124 125def test_ffi_callback(): 126 ffi = _cffi1_backend.FFI() 127 assert ffi.callback("int(int)", lambda x: x + 42)(10) == 52 128 assert ffi.callback("int(*)(int)", lambda x: x + 42)(10) == 52 129 assert ffi.callback("int(int)", lambda x: x + "", -66)(10) == -66 130 assert ffi.callback("int(int)", lambda x: x + "", error=-66)(10) == -66 131 132def test_ffi_callback_decorator(): 133 ffi = _cffi1_backend.FFI() 134 assert ffi.callback(ffi.typeof("int(*)(int)"))(lambda x: x + 42)(10) == 52 135 deco = ffi.callback("int(int)", error=-66) 136 assert deco(lambda x: x + "")(10) == -66 137 assert deco(lambda x: x + 42)(10) == 52 138 139def test_ffi_callback_onerror(): 140 ffi = _cffi1_backend.FFI() 141 seen = [] 142 def oops(*args): 143 seen.append(args) 144 145 @ffi.callback("int(int)", onerror=oops) 146 def fn1(x): 147 return x + "" 148 assert fn1(10) == 0 149 150 @ffi.callback("int(int)", onerror=oops, error=-66) 151 def fn2(x): 152 return x + "" 153 assert fn2(10) == -66 154 155 assert len(seen) == 2 156 exc, val, tb = seen[0] 157 assert exc is TypeError 158 assert isinstance(val, TypeError) 159 assert tb.tb_frame.f_code.co_name == "fn1" 160 exc, val, tb = seen[1] 161 assert exc is TypeError 162 assert isinstance(val, TypeError) 163 assert tb.tb_frame.f_code.co_name == "fn2" 164 # 165 py.test.raises(TypeError, ffi.callback, "int(int)", 166 lambda x: x, onerror=42) # <- not callable 167 168def test_ffi_getctype(): 169 ffi = _cffi1_backend.FFI() 170 assert ffi.getctype("int") == "int" 171 assert ffi.getctype("int", 'x') == "int x" 172 assert ffi.getctype("int*") == "int *" 173 assert ffi.getctype("int*", '') == "int *" 174 assert ffi.getctype("int*", 'x') == "int * x" 175 assert ffi.getctype("int", '*') == "int *" 176 assert ffi.getctype("int", replace_with=' * x ') == "int * x" 177 assert ffi.getctype(ffi.typeof("int*"), '*') == "int * *" 178 assert ffi.getctype("int", '[5]') == "int[5]" 179 assert ffi.getctype("int[5]", '[6]') == "int[6][5]" 180 assert ffi.getctype("int[5]", '(*)') == "int(*)[5]" 181 # special-case for convenience: automatically put '()' around '*' 182 assert ffi.getctype("int[5]", '*') == "int(*)[5]" 183 assert ffi.getctype("int[5]", '*foo') == "int(*foo)[5]" 184 assert ffi.getctype("int[5]", ' ** foo ') == "int(** foo)[5]" 185 186def test_addressof(): 187 ffi = _cffi1_backend.FFI() 188 a = ffi.new("int[10]") 189 b = ffi.addressof(a, 5) 190 b[2] = -123 191 assert a[7] == -123 192 193def test_handle(): 194 ffi = _cffi1_backend.FFI() 195 x = [2, 4, 6] 196 xp = ffi.new_handle(x) 197 assert ffi.typeof(xp) == ffi.typeof("void *") 198 assert ffi.from_handle(xp) is x 199 yp = ffi.new_handle([6, 4, 2]) 200 assert ffi.from_handle(yp) == [6, 4, 2] 201 202def test_handle_unique(): 203 ffi = _cffi1_backend.FFI() 204 assert ffi.new_handle(None) is not ffi.new_handle(None) 205 assert ffi.new_handle(None) != ffi.new_handle(None) 206 207def test_ffi_cast(): 208 ffi = _cffi1_backend.FFI() 209 assert ffi.cast("int(*)(int)", 0) == ffi.NULL 210 ffi.callback("int(int)") # side-effect of registering this string 211 py.test.raises(ffi.error, ffi.cast, "int(int)", 0) 212 213def test_ffi_invalid_type(): 214 ffi = _cffi1_backend.FFI() 215 e = py.test.raises(ffi.error, ffi.cast, "", 0) 216 assert str(e.value) == ("identifier expected\n" 217 "\n" 218 "^") 219 e = py.test.raises(ffi.error, ffi.cast, "struct struct", 0) 220 assert str(e.value) == ("struct or union name expected\n" 221 "struct struct\n" 222 " ^") 223 e = py.test.raises(ffi.error, ffi.cast, "struct never_heard_of_s", 0) 224 assert str(e.value) == ("undefined struct/union name\n" 225 "struct never_heard_of_s\n" 226 " ^") 227 e = py.test.raises(ffi.error, ffi.cast, "\t\n\x01\x1f~\x7f\x80\xff", 0) 228 marks = "?" if sys.version_info < (3,) else "??" 229 assert str(e.value) == ("identifier expected\n" 230 " ??~?%s%s\n" 231 " ^" % (marks, marks)) 232 e = py.test.raises(ffi.error, ffi.cast, "X" * 600, 0) 233 assert str(e.value) == ("undefined type name") 234 235def test_ffi_buffer(): 236 ffi = _cffi1_backend.FFI() 237 a = ffi.new("signed char[]", [5, 6, 7]) 238 assert ffi.buffer(a)[:] == b'\x05\x06\x07' 239 assert ffi.buffer(cdata=a, size=2)[:] == b'\x05\x06' 240 assert type(ffi.buffer(a)) is ffi.buffer 241 242def test_ffi_from_buffer(): 243 import array 244 ffi = _cffi1_backend.FFI() 245 a = array.array('H', [10000, 20000, 30000, 40000]) 246 c = ffi.from_buffer(a) 247 assert ffi.typeof(c) is ffi.typeof("char[]") 248 assert len(c) == 8 249 ffi.cast("unsigned short *", c)[1] += 500 250 assert list(a) == [10000, 20500, 30000, 40000] 251 py.test.raises(TypeError, ffi.from_buffer, a, True) 252 assert c == ffi.from_buffer("char[]", a, True) 253 assert c == ffi.from_buffer(a, require_writable=True) 254 # 255 c = ffi.from_buffer("unsigned short[]", a) 256 assert len(c) == 4 257 assert c[1] == 20500 258 # 259 c = ffi.from_buffer("unsigned short[2][2]", a) 260 assert len(c) == 2 261 assert len(c[0]) == 2 262 assert c[0][1] == 20500 263 # 264 p = ffi.from_buffer(b"abcd") 265 assert p[2] == b"c" 266 # 267 assert p == ffi.from_buffer(b"abcd", require_writable=False) 268 py.test.raises((TypeError, BufferError), ffi.from_buffer, 269 "char[]", b"abcd", True) 270 py.test.raises((TypeError, BufferError), ffi.from_buffer, b"abcd", 271 require_writable=True) 272 273def test_memmove(): 274 ffi = _cffi1_backend.FFI() 275 p = ffi.new("short[]", [-1234, -2345, -3456, -4567, -5678]) 276 ffi.memmove(p, p + 1, 4) 277 assert list(p) == [-2345, -3456, -3456, -4567, -5678] 278 p[2] = 999 279 ffi.memmove(p + 2, p, 6) 280 assert list(p) == [-2345, -3456, -2345, -3456, 999] 281 ffi.memmove(p + 4, ffi.new("char[]", b"\x71\x72"), 2) 282 if sys.byteorder == 'little': 283 assert list(p) == [-2345, -3456, -2345, -3456, 0x7271] 284 else: 285 assert list(p) == [-2345, -3456, -2345, -3456, 0x7172] 286 287def test_memmove_buffer(): 288 import array 289 ffi = _cffi1_backend.FFI() 290 a = array.array('H', [10000, 20000, 30000]) 291 p = ffi.new("short[]", 5) 292 ffi.memmove(p, a, 6) 293 assert list(p) == [10000, 20000, 30000, 0, 0] 294 ffi.memmove(p + 1, a, 6) 295 assert list(p) == [10000, 10000, 20000, 30000, 0] 296 b = array.array('h', [-1000, -2000, -3000]) 297 ffi.memmove(b, a, 4) 298 assert b.tolist() == [10000, 20000, -3000] 299 assert a.tolist() == [10000, 20000, 30000] 300 p[0] = 999 301 p[1] = 998 302 p[2] = 997 303 p[3] = 996 304 p[4] = 995 305 ffi.memmove(b, p, 2) 306 assert b.tolist() == [999, 20000, -3000] 307 ffi.memmove(b, p + 2, 4) 308 assert b.tolist() == [997, 996, -3000] 309 p[2] = -p[2] 310 p[3] = -p[3] 311 ffi.memmove(b, p + 2, 6) 312 assert b.tolist() == [-997, -996, 995] 313 314def test_memmove_readonly_readwrite(): 315 ffi = _cffi1_backend.FFI() 316 p = ffi.new("signed char[]", 5) 317 ffi.memmove(p, b"abcde", 3) 318 assert list(p) == [ord("a"), ord("b"), ord("c"), 0, 0] 319 ffi.memmove(p, bytearray(b"ABCDE"), 2) 320 assert list(p) == [ord("A"), ord("B"), ord("c"), 0, 0] 321 py.test.raises((TypeError, BufferError), ffi.memmove, b"abcde", p, 3) 322 ba = bytearray(b"xxxxx") 323 ffi.memmove(dest=ba, src=p, n=3) 324 assert ba == bytearray(b"ABcxx") 325 326def test_ffi_types(): 327 CData = _cffi1_backend.FFI.CData 328 CType = _cffi1_backend.FFI.CType 329 ffi = _cffi1_backend.FFI() 330 assert isinstance(ffi.cast("int", 42), CData) 331 assert isinstance(ffi.typeof("int"), CType) 332 333def test_ffi_getwinerror(): 334 if sys.platform != "win32": 335 py.test.skip("for windows") 336 ffi = _cffi1_backend.FFI() 337 n = (1 << 29) + 42 338 code, message = ffi.getwinerror(code=n) 339 assert code == n 340 341def test_ffi_new_allocator_1(): 342 ffi = _cffi1_backend.FFI() 343 alloc1 = ffi.new_allocator() 344 alloc2 = ffi.new_allocator(should_clear_after_alloc=False) 345 for retry in range(100): 346 p1 = alloc1("int[10]") 347 p2 = alloc2("int[10]") 348 combination = 0 349 for i in range(10): 350 assert p1[i] == 0 351 combination |= p2[i] 352 p1[i] = -42 353 p2[i] = -43 354 if combination != 0: 355 break 356 del p1, p2 357 import gc; gc.collect() 358 else: 359 raise AssertionError("cannot seem to get an int[10] not " 360 "completely cleared") 361 362def test_ffi_new_allocator_2(): 363 ffi = _cffi1_backend.FFI() 364 seen = [] 365 def myalloc(size): 366 seen.append(size) 367 return ffi.new("char[]", b"X" * size) 368 def myfree(raw): 369 seen.append(raw) 370 alloc1 = ffi.new_allocator(myalloc, myfree) 371 alloc2 = ffi.new_allocator(alloc=myalloc, free=myfree, 372 should_clear_after_alloc=False) 373 p1 = alloc1("int[10]") 374 p2 = alloc2("int[]", 10) 375 assert seen == [40, 40] 376 assert ffi.typeof(p1) == ffi.typeof("int[10]") 377 assert ffi.sizeof(p1) == 40 378 assert ffi.typeof(p2) == ffi.typeof("int[]") 379 assert ffi.sizeof(p2) == 40 380 assert p1[5] == 0 381 assert p2[6] == ord('X') * 0x01010101 382 raw1 = ffi.cast("char *", p1) 383 raw2 = ffi.cast("char *", p2) 384 del p1, p2 385 retries = 0 386 while len(seen) != 4: 387 retries += 1 388 assert retries <= 5 389 import gc; gc.collect() 390 assert (seen == [40, 40, raw1, raw2] or 391 seen == [40, 40, raw2, raw1]) 392 assert repr(seen[2]) == "<cdata 'char[]' owning 41 bytes>" 393 assert repr(seen[3]) == "<cdata 'char[]' owning 41 bytes>" 394 395def test_ffi_new_allocator_3(): 396 ffi = _cffi1_backend.FFI() 397 seen = [] 398 def myalloc(size): 399 seen.append(size) 400 return ffi.new("char[]", b"X" * size) 401 alloc1 = ffi.new_allocator(myalloc) # no 'free' 402 p1 = alloc1("int[10]") 403 assert seen == [40] 404 assert ffi.typeof(p1) == ffi.typeof("int[10]") 405 assert ffi.sizeof(p1) == 40 406 assert p1[5] == 0 407 408def test_ffi_new_allocator_4(): 409 ffi = _cffi1_backend.FFI() 410 py.test.raises(TypeError, ffi.new_allocator, free=lambda x: None) 411 # 412 def myalloc2(size): 413 raise LookupError 414 alloc2 = ffi.new_allocator(myalloc2) 415 py.test.raises(LookupError, alloc2, "int[5]") 416 # 417 def myalloc3(size): 418 return 42 419 alloc3 = ffi.new_allocator(myalloc3) 420 e = py.test.raises(TypeError, alloc3, "int[5]") 421 assert str(e.value) == "alloc() must return a cdata object (got int)" 422 # 423 def myalloc4(size): 424 return ffi.cast("int", 42) 425 alloc4 = ffi.new_allocator(myalloc4) 426 e = py.test.raises(TypeError, alloc4, "int[5]") 427 assert str(e.value) == "alloc() must return a cdata pointer, not 'int'" 428 # 429 def myalloc5(size): 430 return ffi.NULL 431 alloc5 = ffi.new_allocator(myalloc5) 432 py.test.raises(MemoryError, alloc5, "int[5]") 433 434def test_bool_issue228(): 435 ffi = _cffi1_backend.FFI() 436 fntype = ffi.typeof("int(*callback)(bool is_valid)") 437 assert repr(fntype.args[0]) == "<ctype '_Bool'>" 438 439def test_FILE_issue228(): 440 fntype1 = _cffi1_backend.FFI().typeof("FILE *") 441 fntype2 = _cffi1_backend.FFI().typeof("FILE *") 442 assert repr(fntype1) == "<ctype 'FILE *'>" 443 assert fntype1 is fntype2 444 445def test_cast_from_int_type_to_bool(): 446 ffi = _cffi1_backend.FFI() 447 for basetype in ['char', 'short', 'int', 'long', 'long long']: 448 for sign in ['signed', 'unsigned']: 449 type = '%s %s' % (sign, basetype) 450 assert int(ffi.cast("_Bool", ffi.cast(type, 42))) == 1 451 assert int(ffi.cast("bool", ffi.cast(type, 42))) == 1 452 assert int(ffi.cast("_Bool", ffi.cast(type, 0))) == 0 453 454def test_init_once(): 455 def do_init(): 456 seen.append(1) 457 return 42 458 ffi = _cffi1_backend.FFI() 459 seen = [] 460 for i in range(3): 461 res = ffi.init_once(do_init, "tag1") 462 assert res == 42 463 assert seen == [1] 464 for i in range(3): 465 res = ffi.init_once(do_init, "tag2") 466 assert res == 42 467 assert seen == [1, 1] 468 469def test_init_once_multithread(): 470 if sys.version_info < (3,): 471 import thread 472 else: 473 import _thread as thread 474 import time 475 # 476 def do_init(): 477 print('init!') 478 seen.append('init!') 479 time.sleep(1) 480 seen.append('init done') 481 print('init done') 482 return 7 483 ffi = _cffi1_backend.FFI() 484 seen = [] 485 for i in range(6): 486 def f(): 487 res = ffi.init_once(do_init, "tag") 488 seen.append(res) 489 thread.start_new_thread(f, ()) 490 time.sleep(1.5) 491 assert seen == ['init!', 'init done'] + 6 * [7] 492 493def test_init_once_failure(): 494 def do_init(): 495 seen.append(1) 496 raise ValueError 497 ffi = _cffi1_backend.FFI() 498 seen = [] 499 for i in range(5): 500 py.test.raises(ValueError, ffi.init_once, do_init, "tag") 501 assert seen == [1] * (i + 1) 502 503def test_init_once_multithread_failure(): 504 if sys.version_info < (3,): 505 import thread 506 else: 507 import _thread as thread 508 import time 509 def do_init(): 510 seen.append('init!') 511 time.sleep(1) 512 seen.append('oops') 513 raise ValueError 514 ffi = _cffi1_backend.FFI() 515 seen = [] 516 for i in range(3): 517 def f(): 518 py.test.raises(ValueError, ffi.init_once, do_init, "tag") 519 thread.start_new_thread(f, ()) 520 i = 0 521 while len(seen) < 6: 522 i += 1 523 assert i < 20 524 time.sleep(0.51) 525 assert seen == ['init!', 'oops'] * 3 526 527def test_unpack(): 528 ffi = _cffi1_backend.FFI() 529 p = ffi.new("char[]", b"abc\x00def") 530 assert ffi.unpack(p+1, 7) == b"bc\x00def\x00" 531 p = ffi.new("int[]", [-123456789]) 532 assert ffi.unpack(p, 1) == [-123456789] 533 534def test_negative_array_size(): 535 ffi = _cffi1_backend.FFI() 536 py.test.raises(ffi.error, ffi.cast, "int[-5]", 0) 537