• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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