diff options
author | Kevin Cheng <kevcheng@google.com> | 2019-04-18 11:31:16 -0700 |
---|---|---|
committer | Kevin Cheng <kevcheng@google.com> | 2019-05-02 13:59:40 -0700 |
commit | 757c264bc10ebc71074ee3f5fb66d670667a09bc (patch) | |
tree | 26c7f7b74c752db99d9b0ac1f94fc592aca1e53a /testing/cffi1 | |
parent | 99013222844839c42437f16eace25f4e6a8a8b20 (diff) | |
download | cffi-757c264bc10ebc71074ee3f5fb66d670667a09bc.tar.gz |
Add in cffi 1.12.2 (e0c7666)
Since this is a mercurial repo, d/led zip of src:
https://bitbucket.org/cffi/cffi/get/v1.12.2.zip
Also add in misc METADATA/NOTICE/Android.bp/etc files.
Bug: 122778810
Test: None
Change-Id: I36c58ed07a2cdd4d9d11831908175a5c988f33c1
Diffstat (limited to 'testing/cffi1')
-rw-r--r-- | testing/cffi1/__init__.py | 0 | ||||
-rw-r--r-- | testing/cffi1/test_cffi_binary.py | 20 | ||||
-rw-r--r-- | testing/cffi1/test_commontypes.py | 34 | ||||
-rw-r--r-- | testing/cffi1/test_dlopen.py | 225 | ||||
-rw-r--r-- | testing/cffi1/test_dlopen_unicode_literals.py | 9 | ||||
-rw-r--r-- | testing/cffi1/test_ffi_obj.py | 532 | ||||
-rw-r--r-- | testing/cffi1/test_new_ffi_1.py | 1793 | ||||
-rw-r--r-- | testing/cffi1/test_parse_c_type.py | 372 | ||||
-rw-r--r-- | testing/cffi1/test_pkgconfig.py | 94 | ||||
-rw-r--r-- | testing/cffi1/test_re_python.py | 256 | ||||
-rw-r--r-- | testing/cffi1/test_realize_c_type.py | 73 | ||||
-rw-r--r-- | testing/cffi1/test_recompiler.py | 2316 | ||||
-rw-r--r-- | testing/cffi1/test_unicode_literals.py | 43 | ||||
-rw-r--r-- | testing/cffi1/test_verify1.py | 2363 | ||||
-rw-r--r-- | testing/cffi1/test_zdist.py | 426 |
15 files changed, 8556 insertions, 0 deletions
diff --git a/testing/cffi1/__init__.py b/testing/cffi1/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/testing/cffi1/__init__.py diff --git a/testing/cffi1/test_cffi_binary.py b/testing/cffi1/test_cffi_binary.py new file mode 100644 index 0000000..25953db --- /dev/null +++ b/testing/cffi1/test_cffi_binary.py @@ -0,0 +1,20 @@ +import py, sys, os +import _cffi_backend + +def test_no_unknown_exported_symbols(): + if not hasattr(_cffi_backend, '__file__'): + py.test.skip("_cffi_backend module is built-in") + if not sys.platform.startswith('linux'): + py.test.skip("linux-only") + g = os.popen("objdump -T '%s'" % _cffi_backend.__file__, 'r') + for line in g: + if not line.startswith('0'): + continue + if '*UND*' in line: + continue + name = line.split()[-1] + if name.startswith('_') or name.startswith('.'): + continue + if name not in ('init_cffi_backend', 'PyInit__cffi_backend'): + raise Exception("Unexpected exported name %r" % (name,)) + g.close() diff --git a/testing/cffi1/test_commontypes.py b/testing/cffi1/test_commontypes.py new file mode 100644 index 0000000..ea7ffde --- /dev/null +++ b/testing/cffi1/test_commontypes.py @@ -0,0 +1,34 @@ +import py, os, cffi, re +import _cffi_backend + + +def getlines(): + try: + f = open(os.path.join(os.path.dirname(cffi.__file__), + '..', 'c', 'commontypes.c')) + except IOError: + py.test.skip("cannot find ../c/commontypes.c") + lines = [line for line in f.readlines() if line.strip().startswith('EQ(')] + f.close() + return lines + +def test_alphabetical_order(): + lines = getlines() + assert lines == sorted(lines) + +def test_dependencies(): + r = re.compile(r'EQ[(]"([^"]+)",(?:\s*"([A-Z0-9_]+)\s*[*]*"[)])?') + lines = getlines() + d = {} + for line in lines: + match = r.search(line) + if match is not None: + d[match.group(1)] = match.group(2) + for value in d.values(): + if value: + assert value in d + +def test_get_common_types(): + d = {} + _cffi_backend._get_common_types(d) + assert d["bool"] == "_Bool" diff --git a/testing/cffi1/test_dlopen.py b/testing/cffi1/test_dlopen.py new file mode 100644 index 0000000..1c20550 --- /dev/null +++ b/testing/cffi1/test_dlopen.py @@ -0,0 +1,225 @@ +import py +from cffi import FFI, VerificationError, CDefError +from cffi.recompiler import make_py_source +from testing.udir import udir + + +def test_simple(): + ffi = FFI() + ffi.cdef("int close(int); static const int BB = 42; int somevar;") + target = udir.join('test_simple.py') + make_py_source(ffi, 'test_simple', str(target)) + assert target.read() == r"""# auto-generated file +import _cffi_backend + +ffi = _cffi_backend.FFI('test_simple', + _version = 0x2601, + _types = b'\x00\x00\x01\x0D\x00\x00\x07\x01\x00\x00\x00\x0F', + _globals = (b'\xFF\xFF\xFF\x1FBB',42,b'\x00\x00\x00\x23close',0,b'\x00\x00\x01\x21somevar',0), +) +""" + +def test_global_constant(): + ffi = FFI() + ffi.cdef("static const long BB; static const float BF = 12;") + target = udir.join('test_valid_global_constant.py') + make_py_source(ffi, 'test_valid_global_constant', str(target)) + assert target.read() == r"""# auto-generated file +import _cffi_backend + +ffi = _cffi_backend.FFI('test_valid_global_constant', + _version = 0x2601, + _types = b'\x00\x00\x0D\x01\x00\x00\x09\x01', + _globals = (b'\x00\x00\x01\x25BB',0,b'\x00\x00\x00\x25BF',0), +) +""" + +def test_invalid_global_constant_3(): + ffi = FFI() + e = py.test.raises(CDefError, ffi.cdef, "#define BB 12.34") + assert str(e.value).startswith( + "only supports one of the following syntax:") + +def test_invalid_dotdotdot_in_macro(): + ffi = FFI() + ffi.cdef("#define FOO ...") + target = udir.join('test_invalid_dotdotdot_in_macro.py') + e = py.test.raises(VerificationError, make_py_source, ffi, + 'test_invalid_dotdotdot_in_macro', str(target)) + assert str(e.value) == ("macro FOO: cannot use the syntax '...' in " + "'#define FOO ...' when using the ABI mode") + +def test_typename(): + ffi = FFI() + ffi.cdef("typedef int foobar_t;") + target = udir.join('test_typename.py') + make_py_source(ffi, 'test_typename', str(target)) + assert target.read() == r"""# auto-generated file +import _cffi_backend + +ffi = _cffi_backend.FFI('test_typename', + _version = 0x2601, + _types = b'\x00\x00\x07\x01', + _typenames = (b'\x00\x00\x00\x00foobar_t',), +) +""" + +def test_enum(): + ffi = FFI() + ffi.cdef("enum myenum_e { AA, BB, CC=-42 };") + target = udir.join('test_enum.py') + make_py_source(ffi, 'test_enum', str(target)) + assert target.read() == r"""# auto-generated file +import _cffi_backend + +ffi = _cffi_backend.FFI('test_enum', + _version = 0x2601, + _types = b'\x00\x00\x00\x0B', + _globals = (b'\xFF\xFF\xFF\x0BAA',0,b'\xFF\xFF\xFF\x0BBB',1,b'\xFF\xFF\xFF\x0BCC',-42), + _enums = (b'\x00\x00\x00\x00\x00\x00\x00\x15myenum_e\x00AA,BB,CC',), +) +""" + +def test_struct(): + ffi = FFI() + ffi.cdef("struct foo_s { int a; signed char b[]; }; struct bar_s;") + target = udir.join('test_struct.py') + make_py_source(ffi, 'test_struct', str(target)) + assert target.read() == r"""# auto-generated file +import _cffi_backend + +ffi = _cffi_backend.FFI('test_struct', + _version = 0x2601, + _types = b'\x00\x00\x07\x01\x00\x00\x03\x01\x00\x00\x01\x07\x00\x00\x00\x09\x00\x00\x01\x09', + _struct_unions = ((b'\x00\x00\x00\x03\x00\x00\x00\x10bar_s',),(b'\x00\x00\x00\x04\x00\x00\x00\x02foo_s',b'\x00\x00\x00\x11a',b'\x00\x00\x02\x11b')), +) +""" + +def test_include(): + ffi = FFI() + ffi.cdef("#define ABC 123") + ffi.set_source('test_include', None) + target = udir.join('test_include.py') + make_py_source(ffi, 'test_include', str(target)) + assert target.read() == r"""# auto-generated file +import _cffi_backend + +ffi = _cffi_backend.FFI('test_include', + _version = 0x2601, + _types = b'', + _globals = (b'\xFF\xFF\xFF\x1FABC',123,), +) +""" + # + ffi2 = FFI() + ffi2.include(ffi) + target2 = udir.join('test2_include.py') + make_py_source(ffi2, 'test2_include', str(target2)) + assert target2.read() == r"""# auto-generated file +import _cffi_backend +from test_include import ffi as _ffi0 + +ffi = _cffi_backend.FFI('test2_include', + _version = 0x2601, + _types = b'', + _includes = (_ffi0,), +) +""" + +def test_negative_constant(): + ffi = FFI() + ffi.cdef("static const int BB = -42;") + target = udir.join('test_negative_constant.py') + make_py_source(ffi, 'test_negative_constant', str(target)) + assert target.read() == r"""# auto-generated file +import _cffi_backend + +ffi = _cffi_backend.FFI('test_negative_constant', + _version = 0x2601, + _types = b'', + _globals = (b'\xFF\xFF\xFF\x1FBB',-42,), +) +""" + +def test_struct_included(): + baseffi = FFI() + baseffi.cdef("struct foo_s { int x; };") + baseffi.set_source('test_struct_included_base', None) + # + ffi = FFI() + ffi.include(baseffi) + target = udir.join('test_struct_included.py') + make_py_source(ffi, 'test_struct_included', str(target)) + assert target.read() == r"""# auto-generated file +import _cffi_backend +from test_struct_included_base import ffi as _ffi0 + +ffi = _cffi_backend.FFI('test_struct_included', + _version = 0x2601, + _types = b'\x00\x00\x00\x09', + _struct_unions = ((b'\x00\x00\x00\x00\x00\x00\x00\x08foo_s',),), + _includes = (_ffi0,), +) +""" + +def test_no_cross_include(): + baseffi = FFI() + baseffi.set_source('test_no_cross_include_base', "..source..") + # + ffi = FFI() + ffi.include(baseffi) + target = udir.join('test_no_cross_include.py') + py.test.raises(VerificationError, make_py_source, + ffi, 'test_no_cross_include', str(target)) + +def test_array(): + ffi = FFI() + ffi.cdef("typedef int32_t my_array_t[42];") + target = udir.join('test_array.py') + make_py_source(ffi, 'test_array', str(target)) + assert target.read() == r"""# auto-generated file +import _cffi_backend + +ffi = _cffi_backend.FFI('test_array', + _version = 0x2601, + _types = b'\x00\x00\x15\x01\x00\x00\x00\x05\x00\x00\x00\x2A', + _typenames = (b'\x00\x00\x00\x01my_array_t',), +) +""" + +def test_array_overflow(): + ffi = FFI() + ffi.cdef("typedef int32_t my_array_t[3000000000];") + target = udir.join('test_array_overflow.py') + py.test.raises(OverflowError, make_py_source, + ffi, 'test_array_overflow', str(target)) + +def test_global_var(): + ffi = FFI() + ffi.cdef("int myglob;") + target = udir.join('test_global_var.py') + make_py_source(ffi, 'test_global_var', str(target)) + assert target.read() == r"""# auto-generated file +import _cffi_backend + +ffi = _cffi_backend.FFI('test_global_var', + _version = 0x2601, + _types = b'\x00\x00\x07\x01', + _globals = (b'\x00\x00\x00\x21myglob',0,), +) +""" + +def test_bitfield(): + ffi = FFI() + ffi.cdef("struct foo_s { int y:10; short x:5; };") + target = udir.join('test_bitfield.py') + make_py_source(ffi, 'test_bitfield', str(target)) + assert target.read() == r"""# auto-generated file +import _cffi_backend + +ffi = _cffi_backend.FFI('test_bitfield', + _version = 0x2601, + _types = b'\x00\x00\x07\x01\x00\x00\x05\x01\x00\x00\x00\x09', + _struct_unions = ((b'\x00\x00\x00\x02\x00\x00\x00\x02foo_s',b'\x00\x00\x00\x13\x00\x00\x00\x0Ay',b'\x00\x00\x01\x13\x00\x00\x00\x05x'),), +) +""" diff --git a/testing/cffi1/test_dlopen_unicode_literals.py b/testing/cffi1/test_dlopen_unicode_literals.py new file mode 100644 index 0000000..e792866 --- /dev/null +++ b/testing/cffi1/test_dlopen_unicode_literals.py @@ -0,0 +1,9 @@ +import py, os + +s = """from __future__ import unicode_literals +""" + +with open(os.path.join(os.path.dirname(__file__), 'test_dlopen.py')) as f: + s += f.read() + +exec(py.code.compile(s)) diff --git a/testing/cffi1/test_ffi_obj.py b/testing/cffi1/test_ffi_obj.py new file mode 100644 index 0000000..e07d6f9 --- /dev/null +++ b/testing/cffi1/test_ffi_obj.py @@ -0,0 +1,532 @@ +import py, sys +import _cffi_backend as _cffi1_backend + + +def test_ffi_new(): + ffi = _cffi1_backend.FFI() + p = ffi.new("int *") + p[0] = -42 + assert p[0] == -42 + assert type(ffi) is ffi.__class__ is _cffi1_backend.FFI + +def test_ffi_subclass(): + class FOO(_cffi1_backend.FFI): + def __init__(self, x): + self.x = x + foo = FOO(42) + assert foo.x == 42 + p = foo.new("int *") + assert p[0] == 0 + assert type(foo) is foo.__class__ is FOO + +def test_ffi_no_argument(): + py.test.raises(TypeError, _cffi1_backend.FFI, 42) + +def test_ffi_cache_type(): + ffi = _cffi1_backend.FFI() + t1 = ffi.typeof("int **") + t2 = ffi.typeof("int *") + assert t2.item is t1.item.item + assert t2 is t1.item + assert ffi.typeof("int[][10]") is ffi.typeof("int[][10]") + assert ffi.typeof("int(*)()") is ffi.typeof("int(*)()") + +def test_ffi_type_not_immortal(): + import weakref, gc + ffi = _cffi1_backend.FFI() + t1 = ffi.typeof("int **") + t2 = ffi.typeof("int *") + w1 = weakref.ref(t1) + w2 = weakref.ref(t2) + del t1, ffi + gc.collect() + assert w1() is None + assert w2() is t2 + ffi = _cffi1_backend.FFI() + assert ffi.typeof(ffi.new("int **")[0]) is t2 + # + ffi = _cffi1_backend.FFI() + t1 = ffi.typeof("int ***") + t2 = ffi.typeof("int **") + w1 = weakref.ref(t1) + w2 = weakref.ref(t2) + del t2, ffi + gc.collect() + assert w1() is t1 + assert w2() is not None # kept alive by t1 + ffi = _cffi1_backend.FFI() + assert ffi.typeof("int * *") is t1.item + +def test_ffi_cache_type_globally(): + ffi1 = _cffi1_backend.FFI() + ffi2 = _cffi1_backend.FFI() + t1 = ffi1.typeof("int *") + t2 = ffi2.typeof("int *") + assert t1 is t2 + +def test_ffi_invalid(): + ffi = _cffi1_backend.FFI() + # array of 10 times an "int[]" is invalid + py.test.raises(ValueError, ffi.typeof, "int[10][]") + +def test_ffi_docstrings(): + # check that all methods of the FFI class have a docstring. + check_type = type(_cffi1_backend.FFI.new) + for methname in dir(_cffi1_backend.FFI): + if not methname.startswith('_'): + method = getattr(_cffi1_backend.FFI, methname) + if isinstance(method, check_type): + assert method.__doc__, "method FFI.%s() has no docstring" % ( + methname,) + +def test_ffi_NULL(): + NULL = _cffi1_backend.FFI.NULL + assert _cffi1_backend.FFI().typeof(NULL).cname == "void *" + +def test_ffi_no_attr(): + ffi = _cffi1_backend.FFI() + py.test.raises(AttributeError, "ffi.no_such_name") + py.test.raises(AttributeError, "ffi.no_such_name = 42") + py.test.raises(AttributeError, "del ffi.no_such_name") + +def test_ffi_string(): + ffi = _cffi1_backend.FFI() + p = ffi.new("char[]", init=b"foobar\x00baz") + assert ffi.string(p) == b"foobar" + assert ffi.string(cdata=p, maxlen=3) == b"foo" + +def test_ffi_errno(): + # xxx not really checking errno, just checking that we can read/write it + ffi = _cffi1_backend.FFI() + ffi.errno = 42 + assert ffi.errno == 42 + +def test_ffi_alignof(): + ffi = _cffi1_backend.FFI() + assert ffi.alignof("int") == 4 + assert ffi.alignof("int[]") == 4 + assert ffi.alignof("int[41]") == 4 + assert ffi.alignof("short[41]") == 2 + assert ffi.alignof(ffi.new("int[41]")) == 4 + assert ffi.alignof(ffi.new("int[]", 41)) == 4 + +def test_ffi_sizeof(): + ffi = _cffi1_backend.FFI() + assert ffi.sizeof("int") == 4 + py.test.raises(ffi.error, ffi.sizeof, "int[]") + assert ffi.sizeof("int[41]") == 41 * 4 + assert ffi.sizeof(ffi.new("int[41]")) == 41 * 4 + assert ffi.sizeof(ffi.new("int[]", 41)) == 41 * 4 + +def test_ffi_callback(): + ffi = _cffi1_backend.FFI() + assert ffi.callback("int(int)", lambda x: x + 42)(10) == 52 + assert ffi.callback("int(*)(int)", lambda x: x + 42)(10) == 52 + assert ffi.callback("int(int)", lambda x: x + "", -66)(10) == -66 + assert ffi.callback("int(int)", lambda x: x + "", error=-66)(10) == -66 + +def test_ffi_callback_decorator(): + ffi = _cffi1_backend.FFI() + assert ffi.callback(ffi.typeof("int(*)(int)"))(lambda x: x + 42)(10) == 52 + deco = ffi.callback("int(int)", error=-66) + assert deco(lambda x: x + "")(10) == -66 + assert deco(lambda x: x + 42)(10) == 52 + +def test_ffi_callback_onerror(): + ffi = _cffi1_backend.FFI() + seen = [] + def oops(*args): + seen.append(args) + + @ffi.callback("int(int)", onerror=oops) + def fn1(x): + return x + "" + assert fn1(10) == 0 + + @ffi.callback("int(int)", onerror=oops, error=-66) + def fn2(x): + return x + "" + assert fn2(10) == -66 + + assert len(seen) == 2 + exc, val, tb = seen[0] + assert exc is TypeError + assert isinstance(val, TypeError) + assert tb.tb_frame.f_code.co_name == "fn1" + exc, val, tb = seen[1] + assert exc is TypeError + assert isinstance(val, TypeError) + assert tb.tb_frame.f_code.co_name == "fn2" + # + py.test.raises(TypeError, ffi.callback, "int(int)", + lambda x: x, onerror=42) # <- not callable + +def test_ffi_getctype(): + ffi = _cffi1_backend.FFI() + assert ffi.getctype("int") == "int" + assert ffi.getctype("int", 'x') == "int x" + assert ffi.getctype("int*") == "int *" + assert ffi.getctype("int*", '') == "int *" + assert ffi.getctype("int*", 'x') == "int * x" + assert ffi.getctype("int", '*') == "int *" + assert ffi.getctype("int", replace_with=' * x ') == "int * x" + assert ffi.getctype(ffi.typeof("int*"), '*') == "int * *" + assert ffi.getctype("int", '[5]') == "int[5]" + assert ffi.getctype("int[5]", '[6]') == "int[6][5]" + assert ffi.getctype("int[5]", '(*)') == "int(*)[5]" + # special-case for convenience: automatically put '()' around '*' + assert ffi.getctype("int[5]", '*') == "int(*)[5]" + assert ffi.getctype("int[5]", '*foo') == "int(*foo)[5]" + assert ffi.getctype("int[5]", ' ** foo ') == "int(** foo)[5]" + +def test_addressof(): + ffi = _cffi1_backend.FFI() + a = ffi.new("int[10]") + b = ffi.addressof(a, 5) + b[2] = -123 + assert a[7] == -123 + +def test_handle(): + ffi = _cffi1_backend.FFI() + x = [2, 4, 6] + xp = ffi.new_handle(x) + assert ffi.typeof(xp) == ffi.typeof("void *") + assert ffi.from_handle(xp) is x + yp = ffi.new_handle([6, 4, 2]) + assert ffi.from_handle(yp) == [6, 4, 2] + +def test_handle_unique(): + ffi = _cffi1_backend.FFI() + assert ffi.new_handle(None) is not ffi.new_handle(None) + assert ffi.new_handle(None) != ffi.new_handle(None) + +def test_ffi_cast(): + ffi = _cffi1_backend.FFI() + assert ffi.cast("int(*)(int)", 0) == ffi.NULL + ffi.callback("int(int)") # side-effect of registering this string + py.test.raises(ffi.error, ffi.cast, "int(int)", 0) + +def test_ffi_invalid_type(): + ffi = _cffi1_backend.FFI() + e = py.test.raises(ffi.error, ffi.cast, "", 0) + assert str(e.value) == ("identifier expected\n" + "\n" + "^") + e = py.test.raises(ffi.error, ffi.cast, "struct struct", 0) + assert str(e.value) == ("struct or union name expected\n" + "struct struct\n" + " ^") + e = py.test.raises(ffi.error, ffi.cast, "struct never_heard_of_s", 0) + assert str(e.value) == ("undefined struct/union name\n" + "struct never_heard_of_s\n" + " ^") + e = py.test.raises(ffi.error, ffi.cast, "\t\n\x01\x1f~\x7f\x80\xff", 0) + marks = "?" if sys.version_info < (3,) else "??" + assert str(e.value) == ("identifier expected\n" + " ??~?%s%s\n" + " ^" % (marks, marks)) + e = py.test.raises(ffi.error, ffi.cast, "X" * 600, 0) + assert str(e.value) == ("undefined type name") + +def test_ffi_buffer(): + ffi = _cffi1_backend.FFI() + a = ffi.new("signed char[]", [5, 6, 7]) + assert ffi.buffer(a)[:] == b'\x05\x06\x07' + assert ffi.buffer(cdata=a, size=2)[:] == b'\x05\x06' + assert type(ffi.buffer(a)) is ffi.buffer + +def test_ffi_from_buffer(): + import array + ffi = _cffi1_backend.FFI() + a = array.array('H', [10000, 20000, 30000, 40000]) + c = ffi.from_buffer(a) + assert ffi.typeof(c) is ffi.typeof("char[]") + assert len(c) == 8 + ffi.cast("unsigned short *", c)[1] += 500 + assert list(a) == [10000, 20500, 30000, 40000] + py.test.raises(TypeError, ffi.from_buffer, a, True) + assert c == ffi.from_buffer("char[]", a, True) + assert c == ffi.from_buffer(a, require_writable=True) + # + c = ffi.from_buffer("unsigned short[]", a) + assert len(c) == 4 + assert c[1] == 20500 + # + c = ffi.from_buffer("unsigned short[2][2]", a) + assert len(c) == 2 + assert len(c[0]) == 2 + assert c[0][1] == 20500 + # + p = ffi.from_buffer(b"abcd") + assert p[2] == b"c" + # + assert p == ffi.from_buffer(b"abcd", require_writable=False) + py.test.raises((TypeError, BufferError), ffi.from_buffer, + "char[]", b"abcd", True) + py.test.raises((TypeError, BufferError), ffi.from_buffer, b"abcd", + require_writable=True) + +def test_memmove(): + ffi = _cffi1_backend.FFI() + p = ffi.new("short[]", [-1234, -2345, -3456, -4567, -5678]) + ffi.memmove(p, p + 1, 4) + assert list(p) == [-2345, -3456, -3456, -4567, -5678] + p[2] = 999 + ffi.memmove(p + 2, p, 6) + assert list(p) == [-2345, -3456, -2345, -3456, 999] + ffi.memmove(p + 4, ffi.new("char[]", b"\x71\x72"), 2) + if sys.byteorder == 'little': + assert list(p) == [-2345, -3456, -2345, -3456, 0x7271] + else: + assert list(p) == [-2345, -3456, -2345, -3456, 0x7172] + +def test_memmove_buffer(): + import array + ffi = _cffi1_backend.FFI() + a = array.array('H', [10000, 20000, 30000]) + p = ffi.new("short[]", 5) + ffi.memmove(p, a, 6) + assert list(p) == [10000, 20000, 30000, 0, 0] + ffi.memmove(p + 1, a, 6) + assert list(p) == [10000, 10000, 20000, 30000, 0] + b = array.array('h', [-1000, -2000, -3000]) + ffi.memmove(b, a, 4) + assert b.tolist() == [10000, 20000, -3000] + assert a.tolist() == [10000, 20000, 30000] + p[0] = 999 + p[1] = 998 + p[2] = 997 + p[3] = 996 + p[4] = 995 + ffi.memmove(b, p, 2) + assert b.tolist() == [999, 20000, -3000] + ffi.memmove(b, p + 2, 4) + assert b.tolist() == [997, 996, -3000] + p[2] = -p[2] + p[3] = -p[3] + ffi.memmove(b, p + 2, 6) + assert b.tolist() == [-997, -996, 995] + +def test_memmove_readonly_readwrite(): + ffi = _cffi1_backend.FFI() + p = ffi.new("signed char[]", 5) + ffi.memmove(p, b"abcde", 3) + assert list(p) == [ord("a"), ord("b"), ord("c"), 0, 0] + ffi.memmove(p, bytearray(b"ABCDE"), 2) + assert list(p) == [ord("A"), ord("B"), ord("c"), 0, 0] + py.test.raises((TypeError, BufferError), ffi.memmove, b"abcde", p, 3) + ba = bytearray(b"xxxxx") + ffi.memmove(dest=ba, src=p, n=3) + assert ba == bytearray(b"ABcxx") + +def test_ffi_types(): + CData = _cffi1_backend.FFI.CData + CType = _cffi1_backend.FFI.CType + ffi = _cffi1_backend.FFI() + assert isinstance(ffi.cast("int", 42), CData) + assert isinstance(ffi.typeof("int"), CType) + +def test_ffi_getwinerror(): + if sys.platform != "win32": + py.test.skip("for windows") + ffi = _cffi1_backend.FFI() + n = (1 << 29) + 42 + code, message = ffi.getwinerror(code=n) + assert code == n + +def test_ffi_new_allocator_1(): + ffi = _cffi1_backend.FFI() + alloc1 = ffi.new_allocator() + alloc2 = ffi.new_allocator(should_clear_after_alloc=False) + for retry in range(100): + p1 = alloc1("int[10]") + p2 = alloc2("int[10]") + combination = 0 + for i in range(10): + assert p1[i] == 0 + combination |= p2[i] + p1[i] = -42 + p2[i] = -43 + if combination != 0: + break + del p1, p2 + import gc; gc.collect() + else: + raise AssertionError("cannot seem to get an int[10] not " + "completely cleared") + +def test_ffi_new_allocator_2(): + ffi = _cffi1_backend.FFI() + seen = [] + def myalloc(size): + seen.append(size) + return ffi.new("char[]", b"X" * size) + def myfree(raw): + seen.append(raw) + alloc1 = ffi.new_allocator(myalloc, myfree) + alloc2 = ffi.new_allocator(alloc=myalloc, free=myfree, + should_clear_after_alloc=False) + p1 = alloc1("int[10]") + p2 = alloc2("int[]", 10) + assert seen == [40, 40] + assert ffi.typeof(p1) == ffi.typeof("int[10]") + assert ffi.sizeof(p1) == 40 + assert ffi.typeof(p2) == ffi.typeof("int[]") + assert ffi.sizeof(p2) == 40 + assert p1[5] == 0 + assert p2[6] == ord('X') * 0x01010101 + raw1 = ffi.cast("char *", p1) + raw2 = ffi.cast("char *", p2) + del p1, p2 + retries = 0 + while len(seen) != 4: + retries += 1 + assert retries <= 5 + import gc; gc.collect() + assert (seen == [40, 40, raw1, raw2] or + seen == [40, 40, raw2, raw1]) + assert repr(seen[2]) == "<cdata 'char[]' owning 41 bytes>" + assert repr(seen[3]) == "<cdata 'char[]' owning 41 bytes>" + +def test_ffi_new_allocator_3(): + ffi = _cffi1_backend.FFI() + seen = [] + def myalloc(size): + seen.append(size) + return ffi.new("char[]", b"X" * size) + alloc1 = ffi.new_allocator(myalloc) # no 'free' + p1 = alloc1("int[10]") + assert seen == [40] + assert ffi.typeof(p1) == ffi.typeof("int[10]") + assert ffi.sizeof(p1) == 40 + assert p1[5] == 0 + +def test_ffi_new_allocator_4(): + ffi = _cffi1_backend.FFI() + py.test.raises(TypeError, ffi.new_allocator, free=lambda x: None) + # + def myalloc2(size): + raise LookupError + alloc2 = ffi.new_allocator(myalloc2) + py.test.raises(LookupError, alloc2, "int[5]") + # + def myalloc3(size): + return 42 + alloc3 = ffi.new_allocator(myalloc3) + e = py.test.raises(TypeError, alloc3, "int[5]") + assert str(e.value) == "alloc() must return a cdata object (got int)" + # + def myalloc4(size): + return ffi.cast("int", 42) + alloc4 = ffi.new_allocator(myalloc4) + e = py.test.raises(TypeError, alloc4, "int[5]") + assert str(e.value) == "alloc() must return a cdata pointer, not 'int'" + # + def myalloc5(size): + return ffi.NULL + alloc5 = ffi.new_allocator(myalloc5) + py.test.raises(MemoryError, alloc5, "int[5]") + +def test_bool_issue228(): + ffi = _cffi1_backend.FFI() + fntype = ffi.typeof("int(*callback)(bool is_valid)") + assert repr(fntype.args[0]) == "<ctype '_Bool'>" + +def test_FILE_issue228(): + fntype1 = _cffi1_backend.FFI().typeof("FILE *") + fntype2 = _cffi1_backend.FFI().typeof("FILE *") + assert repr(fntype1) == "<ctype 'FILE *'>" + assert fntype1 is fntype2 + +def test_cast_from_int_type_to_bool(): + ffi = _cffi1_backend.FFI() + for basetype in ['char', 'short', 'int', 'long', 'long long']: + for sign in ['signed', 'unsigned']: + type = '%s %s' % (sign, basetype) + assert int(ffi.cast("_Bool", ffi.cast(type, 42))) == 1 + assert int(ffi.cast("bool", ffi.cast(type, 42))) == 1 + assert int(ffi.cast("_Bool", ffi.cast(type, 0))) == 0 + +def test_init_once(): + def do_init(): + seen.append(1) + return 42 + ffi = _cffi1_backend.FFI() + seen = [] + for i in range(3): + res = ffi.init_once(do_init, "tag1") + assert res == 42 + assert seen == [1] + for i in range(3): + res = ffi.init_once(do_init, "tag2") + assert res == 42 + assert seen == [1, 1] + +def test_init_once_multithread(): + if sys.version_info < (3,): + import thread + else: + import _thread as thread + import time + # + def do_init(): + print('init!') + seen.append('init!') + time.sleep(1) + seen.append('init done') + print('init done') + return 7 + ffi = _cffi1_backend.FFI() + seen = [] + for i in range(6): + def f(): + res = ffi.init_once(do_init, "tag") + seen.append(res) + thread.start_new_thread(f, ()) + time.sleep(1.5) + assert seen == ['init!', 'init done'] + 6 * [7] + +def test_init_once_failure(): + def do_init(): + seen.append(1) + raise ValueError + ffi = _cffi1_backend.FFI() + seen = [] + for i in range(5): + py.test.raises(ValueError, ffi.init_once, do_init, "tag") + assert seen == [1] * (i + 1) + +def test_init_once_multithread_failure(): + if sys.version_info < (3,): + import thread + else: + import _thread as thread + import time + def do_init(): + seen.append('init!') + time.sleep(1) + seen.append('oops') + raise ValueError + ffi = _cffi1_backend.FFI() + seen = [] + for i in range(3): + def f(): + py.test.raises(ValueError, ffi.init_once, do_init, "tag") + thread.start_new_thread(f, ()) + i = 0 + while len(seen) < 6: + i += 1 + assert i < 20 + time.sleep(0.51) + assert seen == ['init!', 'oops'] * 3 + +def test_unpack(): + ffi = _cffi1_backend.FFI() + p = ffi.new("char[]", b"abc\x00def") + assert ffi.unpack(p+1, 7) == b"bc\x00def\x00" + p = ffi.new("int[]", [-123456789]) + assert ffi.unpack(p, 1) == [-123456789] + +def test_negative_array_size(): + ffi = _cffi1_backend.FFI() + py.test.raises(ffi.error, ffi.cast, "int[-5]", 0) diff --git a/testing/cffi1/test_new_ffi_1.py b/testing/cffi1/test_new_ffi_1.py new file mode 100644 index 0000000..209cb30 --- /dev/null +++ b/testing/cffi1/test_new_ffi_1.py @@ -0,0 +1,1793 @@ +import py +import platform, imp +import sys, os, ctypes +import cffi +from testing.udir import udir +from testing.support import * +from cffi.recompiler import recompile +from cffi.cffi_opcode import PRIMITIVE_TO_INDEX + +SIZE_OF_INT = ctypes.sizeof(ctypes.c_int) +SIZE_OF_LONG = ctypes.sizeof(ctypes.c_long) +SIZE_OF_SHORT = ctypes.sizeof(ctypes.c_short) +SIZE_OF_PTR = ctypes.sizeof(ctypes.c_void_p) +SIZE_OF_WCHAR = ctypes.sizeof(ctypes.c_wchar) + + +def setup_module(): + global ffi, construction_params + ffi1 = cffi.FFI() + DEFS = r""" + struct repr { short a, b, c; }; + struct simple { int a; short b, c; }; + struct array { int a[2]; char b[3]; }; + struct recursive { int value; struct recursive *next; }; + union simple_u { int a; short b, c; }; + union init_u { char a; int b; }; + struct four_s { int a; short b, c, d; }; + union four_u { int a; short b, c, d; }; + struct string { const char *name; }; + struct ustring { const wchar_t *name; }; + struct voidp { void *p; int *q; short *r; }; + struct ab { int a, b; }; + struct abc { int a, b, c; }; + + /* don't use A0, B0, CC0, D0 because termios.h might be included + and it has its own #defines for these names */ + enum foq { cffiA0, cffiB0, cffiCC0, cffiD0 }; + enum bar { A1, B1=-2, CC1, D1, E1 }; + enum baz { A2=0x1000, B2=0x2000 }; + enum foo2 { A3, B3, C3, D3 }; + struct bar_with_e { enum foo2 e; }; + enum noncont { A4, B4=42, C4 }; + enum etypes {A5='!', B5='\'', C5=0x10, D5=010, E5=- 0x10, F5=-010}; + typedef enum { Value0 = 0 } e_t, *pe_t; + enum e_noninj { AA3=0, BB3=0, CC3=0, DD3=0 }; + enum e_prev { AA4, BB4=2, CC4=4, DD4=BB4, EE4, FF4=CC4, GG4=FF4 }; + + struct nesting { struct abc d, e; }; + struct array2 { int a, b; int c[99]; }; + struct align { char a; short b; char c; }; + struct bitfield { int a:10, b:20, c:3; }; + typedef enum { AA2, BB2, CC2 } foo_e_t; + typedef struct { foo_e_t f:2; } bfenum_t; + typedef struct { int a; } anon_foo_t; + typedef struct { char b, c; } anon_bar_t; + typedef struct named_foo_s { int a; } named_foo_t, *named_foo_p; + typedef struct { int a; } unnamed_foo_t, *unnamed_foo_p; + struct nonpacked { char a; int b; }; + struct array0 { int len; short data[0]; }; + struct array_no_length { int x; int a[]; }; + + struct nested_anon { + struct { int a, b; }; + union { int c, d; }; + }; + struct nested_field_ofs_s { + struct { int a; char b; }; + union { char c; }; + }; + union nested_anon_u { + struct { int a, b; }; + union { int c, d; }; + }; + struct abc50 { int a, b; int c[50]; }; + struct ints_and_bitfield { int a,b,c,d,e; int x:1; }; + """ + DEFS_PACKED = """ + struct is_packed { char a; int b; } /*here*/; + """ + if sys.platform == "win32": + DEFS = DEFS.replace('data[0]', 'data[1]') # not supported + CCODE = (DEFS + "\n#pragma pack(push,1)\n" + DEFS_PACKED + + "\n#pragma pack(pop)\n") + else: + CCODE = (DEFS + + DEFS_PACKED.replace('/*here*/', '__attribute__((packed))')) + + ffi1.cdef(DEFS) + ffi1.cdef(DEFS_PACKED, packed=True) + ffi1.set_source("test_new_ffi_1", CCODE) + + outputfilename = recompile(ffi1, "test_new_ffi_1", CCODE, + tmpdir=str(udir)) + module = imp.load_dynamic("test_new_ffi_1", outputfilename) + ffi = module.ffi + construction_params = (ffi1, CCODE) + + +class TestNewFFI1: + + def test_integer_ranges(self): + for (c_type, size) in [('char', 1), + ('short', 2), + ('short int', 2), + ('', 4), + ('int', 4), + ('long', SIZE_OF_LONG), + ('long int', SIZE_OF_LONG), + ('long long', 8), + ('long long int', 8), + ]: + for unsigned in [None, False, True]: + c_decl = {None: '', + False: 'signed ', + True: 'unsigned '}[unsigned] + c_type + if c_decl == 'char' or c_decl == '': + continue + self._test_int_type(ffi, c_decl, size, unsigned) + + def test_fixedsize_int(self): + for size in [1, 2, 4, 8]: + self._test_int_type(ffi, 'int%d_t' % (8*size), size, False) + self._test_int_type(ffi, 'uint%d_t' % (8*size), size, True) + self._test_int_type(ffi, 'intptr_t', SIZE_OF_PTR, False) + self._test_int_type(ffi, 'uintptr_t', SIZE_OF_PTR, True) + self._test_int_type(ffi, 'ptrdiff_t', SIZE_OF_PTR, False) + self._test_int_type(ffi, 'size_t', SIZE_OF_PTR, True) + self._test_int_type(ffi, 'ssize_t', SIZE_OF_PTR, False) + + def _test_int_type(self, ffi, c_decl, size, unsigned): + if unsigned: + min = 0 + max = (1 << (8*size)) - 1 + else: + min = -(1 << (8*size-1)) + max = (1 << (8*size-1)) - 1 + min = int(min) + max = int(max) + p = ffi.cast(c_decl, min) + assert p == min + assert bool(p) is bool(min) + assert int(p) == min + p = ffi.cast(c_decl, max) + assert int(p) == max + p = ffi.cast(c_decl, long(max)) + assert int(p) == max + q = ffi.cast(c_decl, min - 1) + assert ffi.typeof(q) is ffi.typeof(p) and int(q) == max + q = ffi.cast(c_decl, long(min - 1)) + assert ffi.typeof(q) is ffi.typeof(p) and int(q) == max + assert q == p + assert int(q) == int(p) + assert hash(q) == hash(p) + c_decl_ptr = '%s *' % c_decl + py.test.raises(OverflowError, ffi.new, c_decl_ptr, min - 1) + py.test.raises(OverflowError, ffi.new, c_decl_ptr, max + 1) + py.test.raises(OverflowError, ffi.new, c_decl_ptr, long(min - 1)) + py.test.raises(OverflowError, ffi.new, c_decl_ptr, long(max + 1)) + assert ffi.new(c_decl_ptr, min)[0] == min + assert ffi.new(c_decl_ptr, max)[0] == max + assert ffi.new(c_decl_ptr, long(min))[0] == min + assert ffi.new(c_decl_ptr, long(max))[0] == max + + def test_new_unsupported_type(self): + e = py.test.raises(TypeError, ffi.new, "int") + assert str(e.value) == "expected a pointer or array ctype, got 'int'" + + def test_new_single_integer(self): + p = ffi.new("int *") # similar to ffi.new("int[1]") + assert p[0] == 0 + p[0] = -123 + assert p[0] == -123 + p = ffi.new("int *", -42) + assert p[0] == -42 + assert repr(p) == "<cdata 'int *' owning %d bytes>" % SIZE_OF_INT + + def test_new_array_no_arg(self): + p = ffi.new("int[10]") + # the object was zero-initialized: + for i in range(10): + assert p[i] == 0 + + def test_array_indexing(self): + p = ffi.new("int[10]") + p[0] = 42 + p[9] = 43 + assert p[0] == 42 + assert p[9] == 43 + py.test.raises(IndexError, "p[10]") + py.test.raises(IndexError, "p[10] = 44") + py.test.raises(IndexError, "p[-1]") + py.test.raises(IndexError, "p[-1] = 44") + + def test_new_array_args(self): + # this tries to be closer to C: where we say "int x[5] = {10, 20, ..}" + # then here we must enclose the items in a list + p = ffi.new("int[5]", [10, 20, 30, 40, 50]) + assert p[0] == 10 + assert p[1] == 20 + assert p[2] == 30 + assert p[3] == 40 + assert p[4] == 50 + p = ffi.new("int[4]", [25]) + assert p[0] == 25 + assert p[1] == 0 # follow C convention rather than LuaJIT's + assert p[2] == 0 + assert p[3] == 0 + p = ffi.new("int[4]", [ffi.cast("int", -5)]) + assert p[0] == -5 + assert repr(p) == "<cdata 'int[4]' owning %d bytes>" % (4*SIZE_OF_INT) + + def test_new_array_varsize(self): + p = ffi.new("int[]", 10) # a single integer is the length + assert p[9] == 0 + py.test.raises(IndexError, "p[10]") + # + py.test.raises(TypeError, ffi.new, "int[]") + # + p = ffi.new("int[]", [-6, -7]) # a list is all the items, like C + assert p[0] == -6 + assert p[1] == -7 + py.test.raises(IndexError, "p[2]") + assert repr(p) == "<cdata 'int[]' owning %d bytes>" % (2*SIZE_OF_INT) + # + p = ffi.new("int[]", 0) + py.test.raises(IndexError, "p[0]") + py.test.raises(ValueError, ffi.new, "int[]", -1) + assert repr(p) == "<cdata 'int[]' owning 0 bytes>" + + def test_pointer_init(self): + n = ffi.new("int *", 24) + a = ffi.new("int *[10]", [ffi.NULL, ffi.NULL, n, n, ffi.NULL]) + for i in range(10): + if i not in (2, 3): + assert a[i] == ffi.NULL + assert a[2] == a[3] == n + + def test_cannot_cast(self): + a = ffi.new("short int[10]") + e = py.test.raises(TypeError, ffi.new, "long int **", a) + msg = str(e.value) + assert "'short[10]'" in msg and "'long *'" in msg + + def test_new_pointer_to_array(self): + a = ffi.new("int[4]", [100, 102, 104, 106]) + p = ffi.new("int **", a) + assert p[0] == ffi.cast("int *", a) + assert p[0][2] == 104 + p = ffi.cast("int *", a) + assert p[0] == 100 + assert p[1] == 102 + assert p[2] == 104 + assert p[3] == 106 + # keepalive: a + + def test_pointer_direct(self): + p = ffi.cast("int*", 0) + assert p is not None + assert bool(p) is False + assert p == ffi.cast("int*", 0) + assert p != None + assert repr(p) == "<cdata 'int *' NULL>" + a = ffi.new("int[]", [123, 456]) + p = ffi.cast("int*", a) + assert bool(p) is True + assert p == ffi.cast("int*", a) + assert p != ffi.cast("int*", 0) + assert p[0] == 123 + assert p[1] == 456 + + def test_repr(self): + typerepr = "<ctype '%s'>" + p = ffi.cast("short unsigned int", 0) + assert repr(p) == "<cdata 'unsigned short' 0>" + assert repr(ffi.typeof(p)) == typerepr % "unsigned short" + p = ffi.cast("unsigned short int", 0) + assert repr(p) == "<cdata 'unsigned short' 0>" + assert repr(ffi.typeof(p)) == typerepr % "unsigned short" + p = ffi.cast("int*", 0) + assert repr(p) == "<cdata 'int *' NULL>" + assert repr(ffi.typeof(p)) == typerepr % "int *" + # + p = ffi.new("int*") + assert repr(p) == "<cdata 'int *' owning %d bytes>" % SIZE_OF_INT + assert repr(ffi.typeof(p)) == typerepr % "int *" + p = ffi.new("int**") + assert repr(p) == "<cdata 'int * *' owning %d bytes>" % SIZE_OF_PTR + assert repr(ffi.typeof(p)) == typerepr % "int * *" + p = ffi.new("int [2]") + assert repr(p) == "<cdata 'int[2]' owning %d bytes>" % (2*SIZE_OF_INT) + assert repr(ffi.typeof(p)) == typerepr % "int[2]" + p = ffi.new("int*[2][3]") + assert repr(p) == "<cdata 'int *[2][3]' owning %d bytes>" % ( + 6*SIZE_OF_PTR) + assert repr(ffi.typeof(p)) == typerepr % "int *[2][3]" + p = ffi.new("struct repr *") + assert repr(p) == "<cdata 'struct repr *' owning %d bytes>" % ( + 3*SIZE_OF_SHORT) + assert repr(ffi.typeof(p)) == typerepr % "struct repr *" + # + q = ffi.cast("short", -123) + assert repr(q) == "<cdata 'short' -123>" + assert repr(ffi.typeof(q)) == typerepr % "short" + p = ffi.new("int*") + q = ffi.cast("short*", p) + assert repr(q).startswith("<cdata 'short *' 0x") + assert repr(ffi.typeof(q)) == typerepr % "short *" + p = ffi.new("int [2]") + q = ffi.cast("int*", p) + assert repr(q).startswith("<cdata 'int *' 0x") + assert repr(ffi.typeof(q)) == typerepr % "int *" + p = ffi.new("struct repr*") + q = ffi.cast("struct repr *", p) + assert repr(q).startswith("<cdata 'struct repr *' 0x") + assert repr(ffi.typeof(q)) == typerepr % "struct repr *" + prevrepr = repr(q) + q = q[0] + assert repr(q) == prevrepr.replace(' *', ' &') + assert repr(ffi.typeof(q)) == typerepr % "struct repr" + + def test_new_array_of_array(self): + p = ffi.new("int[3][4]") + p[0][0] = 10 + p[2][3] = 33 + assert p[0][0] == 10 + assert p[2][3] == 33 + py.test.raises(IndexError, "p[1][-1]") + + def test_constructor_array_of_array(self): + p = ffi.new("int[3][2]", [[10, 11], [12, 13], [14, 15]]) + assert p[2][1] == 15 + + def test_new_array_of_pointer_1(self): + n = ffi.new("int*", 99) + p = ffi.new("int*[4]") + p[3] = n + a = p[3] + assert repr(a).startswith("<cdata 'int *' 0x") + assert a[0] == 99 + + def test_new_array_of_pointer_2(self): + n = ffi.new("int[1]", [99]) + p = ffi.new("int*[4]") + p[3] = n + a = p[3] + assert repr(a).startswith("<cdata 'int *' 0x") + assert a[0] == 99 + + def test_char(self): + assert ffi.new("char*", b"\xff")[0] == b'\xff' + assert ffi.new("char*")[0] == b'\x00' + assert int(ffi.cast("char", 300)) == 300 - 256 + assert not bool(ffi.cast("char", 0)) + assert bool(ffi.cast("char", 1)) + assert bool(ffi.cast("char", 255)) + py.test.raises(TypeError, ffi.new, "char*", 32) + py.test.raises(TypeError, ffi.new, "char*", u+"x") + py.test.raises(TypeError, ffi.new, "char*", b"foo") + # + p = ffi.new("char[]", [b'a', b'b', b'\x9c']) + assert len(p) == 3 + assert p[0] == b'a' + assert p[1] == b'b' + assert p[2] == b'\x9c' + p[0] = b'\xff' + assert p[0] == b'\xff' + p = ffi.new("char[]", b"abcd") + assert len(p) == 5 + assert p[4] == b'\x00' # like in C, with: char[] p = "abcd"; + # + p = ffi.new("char[4]", b"ab") + assert len(p) == 4 + assert [p[i] for i in range(4)] == [b'a', b'b', b'\x00', b'\x00'] + p = ffi.new("char[2]", b"ab") + assert len(p) == 2 + assert [p[i] for i in range(2)] == [b'a', b'b'] + py.test.raises(IndexError, ffi.new, "char[2]", b"abc") + + def check_wchar_t(self, ffi): + try: + ffi.cast("wchar_t", 0) + except NotImplementedError: + py.test.skip("NotImplementedError: wchar_t") + + def test_wchar_t(self): + self.check_wchar_t(ffi) + assert ffi.new("wchar_t*", u+'x')[0] == u+'x' + assert ffi.new("wchar_t*", u+'\u1234')[0] == u+'\u1234' + if SIZE_OF_WCHAR > 2: + assert ffi.new("wchar_t*", u+'\U00012345')[0] == u+'\U00012345' + else: + py.test.raises(TypeError, ffi.new, "wchar_t*", u+'\U00012345') + assert ffi.new("wchar_t*")[0] == u+'\x00' + assert int(ffi.cast("wchar_t", 300)) == 300 + assert not bool(ffi.cast("wchar_t", 0)) + assert bool(ffi.cast("wchar_t", 1)) + assert bool(ffi.cast("wchar_t", 65535)) + if SIZE_OF_WCHAR > 2: + assert bool(ffi.cast("wchar_t", 65536)) + py.test.raises(TypeError, ffi.new, "wchar_t*", 32) + py.test.raises(TypeError, ffi.new, "wchar_t*", "foo") + # + p = ffi.new("wchar_t[]", [u+'a', u+'b', u+'\u1234']) + assert len(p) == 3 + assert p[0] == u+'a' + assert p[1] == u+'b' and type(p[1]) is unicode + assert p[2] == u+'\u1234' + p[0] = u+'x' + assert p[0] == u+'x' and type(p[0]) is unicode + p[1] = u+'\u1357' + assert p[1] == u+'\u1357' + p = ffi.new("wchar_t[]", u+"abcd") + assert len(p) == 5 + assert p[4] == u+'\x00' + p = ffi.new("wchar_t[]", u+"a\u1234b") + assert len(p) == 4 + assert p[1] == u+'\u1234' + # + p = ffi.new("wchar_t[]", u+'\U00023456') + if SIZE_OF_WCHAR == 2: + assert len(p) == 3 + assert p[0] == u+'\ud84d' + assert p[1] == u+'\udc56' + assert p[2] == u+'\x00' + else: + assert len(p) == 2 + assert p[0] == u+'\U00023456' + assert p[1] == u+'\x00' + # + p = ffi.new("wchar_t[4]", u+"ab") + assert len(p) == 4 + assert [p[i] for i in range(4)] == [u+'a', u+'b', u+'\x00', u+'\x00'] + p = ffi.new("wchar_t[2]", u+"ab") + assert len(p) == 2 + assert [p[i] for i in range(2)] == [u+'a', u+'b'] + py.test.raises(IndexError, ffi.new, "wchar_t[2]", u+"abc") + + def test_none_as_null_doesnt_work(self): + p = ffi.new("int*[1]") + assert p[0] is not None + assert p[0] != None + assert p[0] == ffi.NULL + assert repr(p[0]) == "<cdata 'int *' NULL>" + # + n = ffi.new("int*", 99) + p = ffi.new("int*[]", [n]) + assert p[0][0] == 99 + py.test.raises(TypeError, "p[0] = None") + p[0] = ffi.NULL + assert p[0] == ffi.NULL + + def test_float(self): + p = ffi.new("float[]", [-2, -2.5]) + assert p[0] == -2.0 + assert p[1] == -2.5 + p[1] += 17.75 + assert p[1] == 15.25 + # + p = ffi.new("float*", 15.75) + assert p[0] == 15.75 + py.test.raises(TypeError, int, p) + py.test.raises(TypeError, float, p) + p[0] = 0.0 + assert bool(p) is True + # + p = ffi.new("float*", 1.1) + f = p[0] + assert f != 1.1 # because of rounding effect + assert abs(f - 1.1) < 1E-7 + # + INF = 1E200 * 1E200 + assert 1E200 != INF + p[0] = 1E200 + assert p[0] == INF # infinite, not enough precision + + def test_struct_simple(self): + s = ffi.new("struct simple*") + assert s.a == s.b == s.c == 0 + s.b = -23 + assert s.b == -23 + py.test.raises(OverflowError, "s.b = 32768") + # + s = ffi.new("struct simple*", [-2, -3]) + assert s.a == -2 + assert s.b == -3 + assert s.c == 0 + py.test.raises((AttributeError, TypeError), "del s.a") + assert repr(s) == "<cdata 'struct simple *' owning %d bytes>" % ( + SIZE_OF_INT + 2 * SIZE_OF_SHORT) + # + py.test.raises(ValueError, ffi.new, "struct simple*", [1, 2, 3, 4]) + + def test_constructor_struct_from_dict(self): + s = ffi.new("struct simple*", {'b': 123, 'c': 456}) + assert s.a == 0 + assert s.b == 123 + assert s.c == 456 + py.test.raises(KeyError, ffi.new, "struct simple*", {'d': 456}) + + def test_struct_pointer(self): + s = ffi.new("struct simple*") + assert s[0].a == s[0].b == s[0].c == 0 + s[0].b = -23 + assert s[0].b == s.b == -23 + py.test.raises(OverflowError, "s[0].b = -32769") + py.test.raises(IndexError, "s[1]") + + def test_struct_opaque(self): + py.test.raises(ffi.error, ffi.new, "struct baz*") + # should 'ffi.new("struct baz **") work? it used to, but it was + # not particularly useful... + py.test.raises(ffi.error, ffi.new, "struct baz**") + + def test_pointer_to_struct(self): + s = ffi.new("struct simple *") + s.a = -42 + assert s[0].a == -42 + p = ffi.new("struct simple **", s) + assert p[0].a == -42 + assert p[0][0].a == -42 + p[0].a = -43 + assert s.a == -43 + assert s[0].a == -43 + p[0][0].a = -44 + assert s.a == -44 + assert s[0].a == -44 + s.a = -45 + assert p[0].a == -45 + assert p[0][0].a == -45 + s[0].a = -46 + assert p[0].a == -46 + assert p[0][0].a == -46 + + def test_constructor_struct_of_array(self): + s = ffi.new("struct array *", [[10, 11], [b'a', b'b', b'c']]) + assert s.a[1] == 11 + assert s.b[2] == b'c' + s.b[1] = b'X' + assert s.b[0] == b'a' + assert s.b[1] == b'X' + assert s.b[2] == b'c' + + def test_recursive_struct(self): + s = ffi.new("struct recursive*") + t = ffi.new("struct recursive*") + s.value = 123 + s.next = t + t.value = 456 + assert s.value == 123 + assert s.next.value == 456 + + def test_union_simple(self): + u = ffi.new("union simple_u*") + assert u.a == u.b == u.c == 0 + u.b = -23 + assert u.b == -23 + assert u.a != 0 + py.test.raises(OverflowError, "u.b = 32768") + # + u = ffi.new("union simple_u*", [-2]) + assert u.a == -2 + py.test.raises((AttributeError, TypeError), "del u.a") + assert repr(u) == "<cdata 'union simple_u *' owning %d bytes>" % ( + SIZE_OF_INT,) + + def test_union_opaque(self): + py.test.raises(ffi.error, ffi.new, "union baz*") + # should 'ffi.new("union baz **") work? it used to, but it was + # not particularly useful... + py.test.raises(ffi.error, ffi.new, "union baz**") + + def test_union_initializer(self): + py.test.raises(TypeError, ffi.new, "union init_u*", b'A') + py.test.raises(TypeError, ffi.new, "union init_u*", 5) + py.test.raises(ValueError, ffi.new, "union init_u*", [b'A', 5]) + u = ffi.new("union init_u*", [b'A']) + assert u.a == b'A' + py.test.raises(TypeError, ffi.new, "union init_u*", [1005]) + u = ffi.new("union init_u*", {'b': 12345}) + assert u.b == 12345 + u = ffi.new("union init_u*", []) + assert u.a == b'\x00' + assert u.b == 0 + + def test_sizeof_type(self): + for c_type, expected_size in [ + ('char', 1), + ('unsigned int', 4), + ('char *', SIZE_OF_PTR), + ('int[5]', 20), + ('struct four_s', 12), + ('union four_u', 4), + ]: + size = ffi.sizeof(c_type) + assert size == expected_size, (size, expected_size, ctype) + + def test_sizeof_cdata(self): + assert ffi.sizeof(ffi.new("short*")) == SIZE_OF_PTR + assert ffi.sizeof(ffi.cast("short", 123)) == SIZE_OF_SHORT + # + a = ffi.new("int[]", [10, 11, 12, 13, 14]) + assert len(a) == 5 + assert ffi.sizeof(a) == 5 * SIZE_OF_INT + + def test_string_from_char_pointer(self): + x = ffi.new("char*", b"x") + assert str(x) == repr(x) + assert ffi.string(x) == b"x" + assert ffi.string(ffi.new("char*", b"\x00")) == b"" + py.test.raises(TypeError, ffi.new, "char*", unicode("foo")) + + def test_unicode_from_wchar_pointer(self): + self.check_wchar_t(ffi) + x = ffi.new("wchar_t*", u+"x") + assert unicode(x) == unicode(repr(x)) + assert ffi.string(x) == u+"x" + assert ffi.string(ffi.new("wchar_t*", u+"\x00")) == u+"" + + def test_string_from_char_array(self): + p = ffi.new("char[]", b"hello.") + p[5] = b'!' + assert ffi.string(p) == b"hello!" + p[6] = b'?' + assert ffi.string(p) == b"hello!?" + p[3] = b'\x00' + assert ffi.string(p) == b"hel" + assert ffi.string(p, 2) == b"he" + py.test.raises(IndexError, "p[7] = b'X'") + # + a = ffi.new("char[]", b"hello\x00world") + assert len(a) == 12 + p = ffi.cast("char *", a) + assert ffi.string(p) == b'hello' + + def test_string_from_wchar_array(self): + self.check_wchar_t(ffi) + assert ffi.string(ffi.cast("wchar_t", "x")) == u+"x" + assert ffi.string(ffi.cast("wchar_t", u+"x")) == u+"x" + x = ffi.cast("wchar_t", "x") + assert str(x) == repr(x) + assert ffi.string(x) == u+"x" + # + p = ffi.new("wchar_t[]", u+"hello.") + p[5] = u+'!' + assert ffi.string(p) == u+"hello!" + p[6] = u+'\u04d2' + assert ffi.string(p) == u+"hello!\u04d2" + p[3] = u+'\x00' + assert ffi.string(p) == u+"hel" + assert ffi.string(p, 123) == u+"hel" + py.test.raises(IndexError, "p[7] = u+'X'") + # + a = ffi.new("wchar_t[]", u+"hello\x00world") + assert len(a) == 12 + p = ffi.cast("wchar_t *", a) + assert ffi.string(p) == u+'hello' + assert ffi.string(p, 123) == u+'hello' + assert ffi.string(p, 5) == u+'hello' + assert ffi.string(p, 2) == u+'he' + + def test_fetch_const_char_p_field(self): + # 'const' is ignored so far, in the declaration of 'struct string' + t = ffi.new("const char[]", b"testing") + s = ffi.new("struct string*", [t]) + assert type(s.name) not in (bytes, str, unicode) + assert ffi.string(s.name) == b"testing" + py.test.raises(TypeError, "s.name = None") + s.name = ffi.NULL + assert s.name == ffi.NULL + + def test_fetch_const_wchar_p_field(self): + # 'const' is ignored so far + self.check_wchar_t(ffi) + t = ffi.new("const wchar_t[]", u+"testing") + s = ffi.new("struct ustring*", [t]) + assert type(s.name) not in (bytes, str, unicode) + assert ffi.string(s.name) == u+"testing" + s.name = ffi.NULL + assert s.name == ffi.NULL + + def test_voidp(self): + py.test.raises(TypeError, ffi.new, "void*") + p = ffi.new("void **") + assert p[0] == ffi.NULL + a = ffi.new("int[]", [10, 11, 12]) + p = ffi.new("void **", a) + vp = p[0] + py.test.raises(TypeError, "vp[0]") + py.test.raises(TypeError, ffi.new, "short **", a) + # + s = ffi.new("struct voidp *") + s.p = a # works + s.q = a # works + py.test.raises(TypeError, "s.r = a") # fails + b = ffi.cast("int *", a) + s.p = b # works + s.q = b # works + py.test.raises(TypeError, "s.r = b") # fails + + def test_functionptr_simple(self): + py.test.raises(TypeError, ffi.callback, "int(*)(int)", 0) + def cb(n): + return n + 1 + cb.__qualname__ = 'cb' + p = ffi.callback("int(*)(int)", cb) + res = p(41) # calling an 'int(*)(int)', i.e. a function pointer + assert res == 42 and type(res) is int + res = p(ffi.cast("int", -41)) + assert res == -40 and type(res) is int + assert repr(p).startswith( + "<cdata 'int(*)(int)' calling <function cb at 0x") + assert ffi.typeof(p) is ffi.typeof("int(*)(int)") + q = ffi.new("int(**)(int)", p) + assert repr(q) == "<cdata 'int(* *)(int)' owning %d bytes>" % ( + SIZE_OF_PTR) + py.test.raises(TypeError, "q(43)") + res = q[0](43) + assert res == 44 + q = ffi.cast("int(*)(int)", p) + assert repr(q).startswith("<cdata 'int(*)(int)' 0x") + res = q(45) + assert res == 46 + + def test_functionptr_advanced(self): + t = ffi.typeof("int(*(*)(int))(int)") + assert repr(t) == "<ctype '%s'>" % "int(*(*)(int))(int)" + + def test_functionptr_voidptr_return(self): + def cb(): + return ffi.NULL + p = ffi.callback("void*(*)()", cb) + res = p() + assert res is not None + assert res == ffi.NULL + int_ptr = ffi.new('int*') + void_ptr = ffi.cast('void*', int_ptr) + def cb(): + return void_ptr + p = ffi.callback("void*(*)()", cb) + res = p() + assert res == void_ptr + + def test_functionptr_intptr_return(self): + def cb(): + return ffi.NULL + p = ffi.callback("int*(*)()", cb) + res = p() + assert res == ffi.NULL + int_ptr = ffi.new('int*') + def cb(): + return int_ptr + p = ffi.callback("int*(*)()", cb) + res = p() + assert repr(res).startswith("<cdata 'int *' 0x") + assert res == int_ptr + int_array_ptr = ffi.new('int[1]') + def cb(): + return int_array_ptr + p = ffi.callback("int*(*)()", cb) + res = p() + assert repr(res).startswith("<cdata 'int *' 0x") + assert res == int_array_ptr + + def test_functionptr_void_return(self): + def foo(): + pass + foo_cb = ffi.callback("void foo()", foo) + result = foo_cb() + assert result is None + + def test_char_cast(self): + p = ffi.cast("int", b'\x01') + assert ffi.typeof(p) is ffi.typeof("int") + assert int(p) == 1 + p = ffi.cast("int", ffi.cast("char", b"a")) + assert int(p) == ord("a") + p = ffi.cast("int", ffi.cast("char", b"\x80")) + assert int(p) == 0x80 # "char" is considered unsigned in this case + p = ffi.cast("int", b"\x81") + assert int(p) == 0x81 + + def test_wchar_cast(self): + self.check_wchar_t(ffi) + p = ffi.cast("int", ffi.cast("wchar_t", u+'\u1234')) + assert int(p) == 0x1234 + p = ffi.cast("long long", ffi.cast("wchar_t", -1)) + if SIZE_OF_WCHAR == 2: # 2 bytes, unsigned + assert int(p) == 0xffff + elif (sys.platform.startswith('linux') and + platform.machine().startswith('x86')): # known to be signed + assert int(p) == -1 + else: # in general, it can be either signed or not + assert int(p) in [-1, 0xffffffff] # e.g. on arm, both cases occur + p = ffi.cast("int", u+'\u1234') + assert int(p) == 0x1234 + + def test_cast_array_to_charp(self): + a = ffi.new("short int[]", [0x1234, 0x5678]) + p = ffi.cast("char*", a) + data = b''.join([p[i] for i in range(4)]) + if sys.byteorder == 'little': + assert data == b'\x34\x12\x78\x56' + else: + assert data == b'\x12\x34\x56\x78' + + def test_cast_between_pointers(self): + a = ffi.new("short int[]", [0x1234, 0x5678]) + p = ffi.cast("short*", a) + p2 = ffi.cast("int*", p) + q = ffi.cast("char*", p2) + data = b''.join([q[i] for i in range(4)]) + if sys.byteorder == 'little': + assert data == b'\x34\x12\x78\x56' + else: + assert data == b'\x12\x34\x56\x78' + + def test_cast_pointer_and_int(self): + a = ffi.new("short int[]", [0x1234, 0x5678]) + l1 = ffi.cast("intptr_t", a) + p = ffi.cast("short*", a) + l2 = ffi.cast("intptr_t", p) + assert int(l1) == int(l2) != 0 + q = ffi.cast("short*", l1) + assert q == ffi.cast("short*", int(l1)) + assert q[0] == 0x1234 + assert int(ffi.cast("intptr_t", ffi.NULL)) == 0 + + def test_cast_functionptr_and_int(self): + def cb(n): + return n + 1 + a = ffi.callback("int(*)(int)", cb) + p = ffi.cast("void *", a) + assert p + b = ffi.cast("int(*)(int)", p) + assert b(41) == 42 + assert a == b + assert hash(a) == hash(b) + + def test_callback_crash(self): + def cb(n): + raise Exception + a = ffi.callback("int(*)(int)", cb, error=42) + res = a(1) # and the error reported to stderr + assert res == 42 + + def test_structptr_argument(self): + def cb(p): + return p[0].a * 1000 + p[0].b * 100 + p[1].a * 10 + p[1].b + a = ffi.callback("int(*)(struct ab[])", cb) + res = a([[5, 6], {'a': 7, 'b': 8}]) + assert res == 5678 + res = a([[5], {'b': 8}]) + assert res == 5008 + + def test_array_argument_as_list(self): + seen = [] + def cb(argv): + seen.append(ffi.string(argv[0])) + seen.append(ffi.string(argv[1])) + a = ffi.callback("void(*)(char *[])", cb) + a([ffi.new("char[]", b"foobar"), ffi.new("char[]", b"baz")]) + assert seen == [b"foobar", b"baz"] + + def test_cast_float(self): + a = ffi.cast("float", 12) + assert float(a) == 12.0 + a = ffi.cast("float", 12.5) + assert float(a) == 12.5 + a = ffi.cast("float", b"A") + assert float(a) == ord("A") + a = ffi.cast("int", 12.9) + assert int(a) == 12 + a = ffi.cast("char", 66.9 + 256) + assert ffi.string(a) == b"B" + # + a = ffi.cast("float", ffi.cast("int", 12)) + assert float(a) == 12.0 + a = ffi.cast("float", ffi.cast("double", 12.5)) + assert float(a) == 12.5 + a = ffi.cast("float", ffi.cast("char", b"A")) + assert float(a) == ord("A") + a = ffi.cast("int", ffi.cast("double", 12.9)) + assert int(a) == 12 + a = ffi.cast("char", ffi.cast("double", 66.9 + 256)) + assert ffi.string(a) == b"B" + + def test_enum(self): + # enum foq { A0, B0, CC0, D0 }; + assert ffi.string(ffi.cast("enum foq", 0)) == "cffiA0" + assert ffi.string(ffi.cast("enum foq", 2)) == "cffiCC0" + assert ffi.string(ffi.cast("enum foq", 3)) == "cffiD0" + assert ffi.string(ffi.cast("enum foq", 4)) == "4" + # enum bar { A1, B1=-2, CC1, D1, E1 }; + assert ffi.string(ffi.cast("enum bar", 0)) == "A1" + assert ffi.string(ffi.cast("enum bar", -2)) == "B1" + assert ffi.string(ffi.cast("enum bar", -1)) == "CC1" + assert ffi.string(ffi.cast("enum bar", 1)) == "E1" + assert ffi.cast("enum bar", -2) == ffi.cast("enum bar", -2) + assert ffi.cast("enum foq", 0) == ffi.cast("enum bar", 0) + assert ffi.cast("enum bar", 0) == ffi.cast("int", 0) + assert repr(ffi.cast("enum bar", -1)) == "<cdata 'enum bar' -1: CC1>" + assert repr(ffi.cast("enum foq", -1)) == ( # enums are unsigned, if + "<cdata 'enum foq' 4294967295>") or ( # they contain no neg value + sys.platform == "win32") # (but not on msvc) + # enum baz { A2=0x1000, B2=0x2000 }; + assert ffi.string(ffi.cast("enum baz", 0x1000)) == "A2" + assert ffi.string(ffi.cast("enum baz", 0x2000)) == "B2" + + def test_enum_in_struct(self): + # enum foo2 { A3, B3, C3, D3 }; + # struct bar_with_e { enum foo2 e; }; + s = ffi.new("struct bar_with_e *") + s.e = 0 + assert s.e == 0 + s.e = 3 + assert s.e == 3 + assert s[0].e == 3 + s[0].e = 2 + assert s.e == 2 + assert s[0].e == 2 + s.e = ffi.cast("enum foo2", -1) + assert s.e in (4294967295, -1) # two choices + assert s[0].e in (4294967295, -1) + s.e = s.e + py.test.raises(TypeError, "s.e = 'B3'") + py.test.raises(TypeError, "s.e = '2'") + py.test.raises(TypeError, "s.e = '#2'") + py.test.raises(TypeError, "s.e = '#7'") + + def test_enum_non_contiguous(self): + # enum noncont { A4, B4=42, C4 }; + assert ffi.string(ffi.cast("enum noncont", 0)) == "A4" + assert ffi.string(ffi.cast("enum noncont", 42)) == "B4" + assert ffi.string(ffi.cast("enum noncont", 43)) == "C4" + invalid_value = ffi.cast("enum noncont", 2) + assert int(invalid_value) == 2 + assert ffi.string(invalid_value) == "2" + + def test_enum_char_hex_oct(self): + # enum etypes {A5='!', B5='\'', C5=0x10, D5=010, E5=- 0x10, F5=-010}; + assert ffi.string(ffi.cast("enum etypes", ord('!'))) == "A5" + assert ffi.string(ffi.cast("enum etypes", ord("'"))) == "B5" + assert ffi.string(ffi.cast("enum etypes", 16)) == "C5" + assert ffi.string(ffi.cast("enum etypes", 8)) == "D5" + assert ffi.string(ffi.cast("enum etypes", -16)) == "E5" + assert ffi.string(ffi.cast("enum etypes", -8)) == "F5" + + def test_array_of_struct(self): + s = ffi.new("struct ab[1]") + py.test.raises(AttributeError, 's.b') + py.test.raises(AttributeError, 's.b = 412') + s[0].b = 412 + assert s[0].b == 412 + py.test.raises(IndexError, 's[1]') + + def test_pointer_to_array(self): + p = ffi.new("int(**)[5]") + assert repr(p) == "<cdata 'int(* *)[5]' owning %d bytes>" % SIZE_OF_PTR + + def test_iterate_array(self): + a = ffi.new("char[]", b"hello") + assert list(a) == [b"h", b"e", b"l", b"l", b"o", b"\0"] + assert list(iter(a)) == [b"h", b"e", b"l", b"l", b"o", b"\0"] + # + py.test.raises(TypeError, iter, ffi.cast("char *", a)) + py.test.raises(TypeError, list, ffi.cast("char *", a)) + py.test.raises(TypeError, iter, ffi.new("int *")) + py.test.raises(TypeError, list, ffi.new("int *")) + + def test_offsetof(self): + # struct abc { int a, b, c; }; + assert ffi.offsetof("struct abc", "a") == 0 + assert ffi.offsetof("struct abc", "b") == 4 + assert ffi.offsetof("struct abc", "c") == 8 + + def test_offsetof_nested(self): + # struct nesting { struct abc d, e; }; + assert ffi.offsetof("struct nesting", "e") == 12 + py.test.raises(KeyError, ffi.offsetof, "struct nesting", "e.a") + assert ffi.offsetof("struct nesting", "e", "a") == 12 + assert ffi.offsetof("struct nesting", "e", "b") == 16 + assert ffi.offsetof("struct nesting", "e", "c") == 20 + + def test_offsetof_array(self): + assert ffi.offsetof("int[]", 51) == 51 * ffi.sizeof("int") + assert ffi.offsetof("int *", 51) == 51 * ffi.sizeof("int") + # struct array2 { int a, b; int c[99]; }; + assert ffi.offsetof("struct array2", "c") == 2 * ffi.sizeof("int") + assert ffi.offsetof("struct array2", "c", 0) == 2 * ffi.sizeof("int") + assert ffi.offsetof("struct array2", "c", 51) == 53 * ffi.sizeof("int") + + def test_alignof(self): + # struct align { char a; short b; char c; }; + assert ffi.alignof("int") == 4 + assert ffi.alignof("double") in (4, 8) + assert ffi.alignof("struct align") == 2 + + def test_bitfield(self): + # struct bitfield { int a:10, b:20, c:3; }; + assert ffi.sizeof("struct bitfield") == 8 + s = ffi.new("struct bitfield *") + s.a = 511 + py.test.raises(OverflowError, "s.a = 512") + py.test.raises(OverflowError, "s[0].a = 512") + assert s.a == 511 + s.a = -512 + py.test.raises(OverflowError, "s.a = -513") + py.test.raises(OverflowError, "s[0].a = -513") + assert s.a == -512 + s.c = 3 + assert s.c == 3 + py.test.raises(OverflowError, "s.c = 4") + py.test.raises(OverflowError, "s[0].c = 4") + s.c = -4 + assert s.c == -4 + + def test_bitfield_enum(self): + # typedef enum { AA1, BB1, CC1 } foo_e_t; + # typedef struct { foo_e_t f:2; } bfenum_t; + if sys.platform == "win32": + py.test.skip("enums are not unsigned") + s = ffi.new("bfenum_t *") + s.f = 2 + assert s.f == 2 + + def test_anonymous_struct(self): + # typedef struct { int a; } anon_foo_t; + # typedef struct { char b, c; } anon_bar_t; + f = ffi.new("anon_foo_t *", [12345]) + b = ffi.new("anon_bar_t *", [b"B", b"C"]) + assert f.a == 12345 + assert b.b == b"B" + assert b.c == b"C" + assert repr(b).startswith("<cdata 'anon_bar_t *'") + + def test_struct_with_two_usages(self): + # typedef struct named_foo_s { int a; } named_foo_t, *named_foo_p; + # typedef struct { int a; } unnamed_foo_t, *unnamed_foo_p; + f = ffi.new("named_foo_t *", [12345]) + ps = ffi.new("named_foo_p[]", [f]) + f = ffi.new("unnamed_foo_t *", [12345]) + ps = ffi.new("unnamed_foo_p[]", [f]) + + def test_pointer_arithmetic(self): + s = ffi.new("short[]", list(range(100, 110))) + p = ffi.cast("short *", s) + assert p[2] == 102 + assert p+1 == p+1 + assert p+1 != p+0 + assert p == p+0 == p-0 + assert (p+1)[0] == 101 + assert (p+19)[-10] == 109 + assert (p+5) - (p+1) == 4 + assert p == s+0 + assert p+1 == s+1 + + def test_pointer_comparison(self): + s = ffi.new("short[]", list(range(100))) + p = ffi.cast("short *", s) + assert (p < s) is False + assert (p <= s) is True + assert (p == s) is True + assert (p != s) is False + assert (p > s) is False + assert (p >= s) is True + assert (s < p) is False + assert (s <= p) is True + assert (s == p) is True + assert (s != p) is False + assert (s > p) is False + assert (s >= p) is True + q = p + 1 + assert (q < s) is False + assert (q <= s) is False + assert (q == s) is False + assert (q != s) is True + assert (q > s) is True + assert (q >= s) is True + assert (s < q) is True + assert (s <= q) is True + assert (s == q) is False + assert (s != q) is True + assert (s > q) is False + assert (s >= q) is False + assert (q < p) is False + assert (q <= p) is False + assert (q == p) is False + assert (q != p) is True + assert (q > p) is True + assert (q >= p) is True + assert (p < q) is True + assert (p <= q) is True + assert (p == q) is False + assert (p != q) is True + assert (p > q) is False + assert (p >= q) is False + # + assert (None == s) is False + assert (None != s) is True + assert (s == None) is False + assert (s != None) is True + assert (None == q) is False + assert (None != q) is True + assert (q == None) is False + assert (q != None) is True + + def test_integer_comparison(self): + x = ffi.cast("int", 123) + y = ffi.cast("int", 456) + assert x < y + # + z = ffi.cast("double", 78.9) + assert x > z + assert y > z + + def test_ffi_buffer_ptr(self): + a = ffi.new("short *", 100) + try: + b = ffi.buffer(a) + except NotImplementedError as e: + py.test.skip(str(e)) + content = b[:] + assert len(content) == len(b) == 2 + if sys.byteorder == 'little': + assert content == b'\x64\x00' + assert b[0] == b'\x64' + b[0] = b'\x65' + else: + assert content == b'\x00\x64' + assert b[1] == b'\x64' + b[1] = b'\x65' + assert a[0] == 101 + + def test_ffi_buffer_array(self): + a = ffi.new("int[]", list(range(100, 110))) + try: + b = ffi.buffer(a) + except NotImplementedError as e: + py.test.skip(str(e)) + content = b[:] + if sys.byteorder == 'little': + assert content.startswith(b'\x64\x00\x00\x00\x65\x00\x00\x00') + b[4] = b'\x45' + else: + assert content.startswith(b'\x00\x00\x00\x64\x00\x00\x00\x65') + b[7] = b'\x45' + assert len(content) == 4 * 10 + assert a[1] == 0x45 + + def test_ffi_buffer_ptr_size(self): + a = ffi.new("short *", 0x4243) + try: + b = ffi.buffer(a, 1) + except NotImplementedError as e: + py.test.skip(str(e)) + content = b[:] + assert len(content) == 1 + if sys.byteorder == 'little': + assert content == b'\x43' + b[0] = b'\x62' + assert a[0] == 0x4262 + else: + assert content == b'\x42' + b[0] = b'\x63' + assert a[0] == 0x6343 + + def test_ffi_buffer_array_size(self): + a1 = ffi.new("int[]", list(range(100, 110))) + a2 = ffi.new("int[]", list(range(100, 115))) + try: + ffi.buffer(a1) + except NotImplementedError as e: + py.test.skip(str(e)) + assert ffi.buffer(a1)[:] == ffi.buffer(a2, 4*10)[:] + + def test_ffi_buffer_with_file(self): + import tempfile, os, array + fd, filename = tempfile.mkstemp() + f = os.fdopen(fd, 'r+b') + a = ffi.new("int[]", list(range(1005))) + try: + ffi.buffer(a, 512) + except NotImplementedError as e: + py.test.skip(str(e)) + f.write(ffi.buffer(a, 1000 * ffi.sizeof("int"))) + f.seek(0) + assert f.read() == array.array('i', range(1000)).tostring() + f.seek(0) + b = ffi.new("int[]", 1005) + f.readinto(ffi.buffer(b, 1000 * ffi.sizeof("int"))) + assert list(a)[:1000] + [0] * (len(a)-1000) == list(b) + f.close() + os.unlink(filename) + + def test_ffi_buffer_with_io(self): + import io, array + f = io.BytesIO() + a = ffi.new("int[]", list(range(1005))) + try: + ffi.buffer(a, 512) + except NotImplementedError as e: + py.test.skip(str(e)) + f.write(ffi.buffer(a, 1000 * ffi.sizeof("int"))) + f.seek(0) + assert f.read() == array.array('i', range(1000)).tostring() + f.seek(0) + b = ffi.new("int[]", 1005) + f.readinto(ffi.buffer(b, 1000 * ffi.sizeof("int"))) + assert list(a)[:1000] + [0] * (len(a)-1000) == list(b) + f.close() + + def test_array_in_struct(self): + # struct array { int a[2]; char b[3]; }; + p = ffi.new("struct array *") + p.a[1] = 5 + assert p.a[1] == 5 + assert repr(p.a).startswith("<cdata 'int[2]' 0x") + + def test_struct_containing_array_varsize_workaround(self): + if sys.platform == "win32": + py.test.skip("array of length 0 not supported") + # struct array0 { int len; short data[0]; }; + p = ffi.new("char[]", ffi.sizeof("struct array0") + 7 * SIZE_OF_SHORT) + q = ffi.cast("struct array0 *", p) + assert q.len == 0 + # 'q.data' gets not a 'short[0]', but just a 'short *' instead + assert repr(q.data).startswith("<cdata 'short *' 0x") + assert q.data[6] == 0 + q.data[6] = 15 + assert q.data[6] == 15 + + def test_new_struct_containing_array_varsize(self): + py.test.skip("later?") + ffi.cdef("struct foo_s { int len; short data[]; };") + p = ffi.new("struct foo_s *", 10) # a single integer is the length + assert p.len == 0 + assert p.data[9] == 0 + py.test.raises(IndexError, "p.data[10]") + + def test_ffi_typeof_getcname(self): + assert ffi.getctype("int") == "int" + assert ffi.getctype("int", 'x') == "int x" + assert ffi.getctype("int*") == "int *" + assert ffi.getctype("int*", '') == "int *" + assert ffi.getctype("int*", 'x') == "int * x" + assert ffi.getctype("int", '*') == "int *" + assert ffi.getctype("int", ' * x ') == "int * x" + assert ffi.getctype(ffi.typeof("int*"), '*') == "int * *" + assert ffi.getctype("int", '[5]') == "int[5]" + assert ffi.getctype("int[5]", '[6]') == "int[6][5]" + assert ffi.getctype("int[5]", '(*)') == "int(*)[5]" + # special-case for convenience: automatically put '()' around '*' + assert ffi.getctype("int[5]", '*') == "int(*)[5]" + assert ffi.getctype("int[5]", '*foo') == "int(*foo)[5]" + assert ffi.getctype("int[5]", ' ** foo ') == "int(** foo)[5]" + + def test_array_of_func_ptr(self): + f = ffi.cast("int(*)(int)", 42) + assert f != ffi.NULL + py.test.raises(ffi.error, ffi.cast, "int(int)", 42) + py.test.raises(ffi.error, ffi.new, "int([5])(int)") + a = ffi.new("int(*[5])(int)", [f]) + assert ffi.getctype(ffi.typeof(a)) == "int(*[5])(int)" + assert len(a) == 5 + assert a[0] == f + assert a[1] == ffi.NULL + py.test.raises(TypeError, ffi.cast, "int(*)(int)[5]", 0) + # + def cb(n): + return n + 1 + f = ffi.callback("int(*)(int)", cb) + a = ffi.new("int(*[5])(int)", [f, f]) + assert a[1](42) == 43 + + def test_callback_as_function_argument(self): + # In C, function arguments can be declared with a function type, + # which is automatically replaced with the ptr-to-function type. + def cb(a, b): + return chr(ord(a) + ord(b)).encode() + f = ffi.callback("char cb(char, char)", cb) + assert f(b'A', b'\x01') == b'B' + def g(callback): + return callback(b'A', b'\x01') + g = ffi.callback("char g(char cb(char, char))", g) + assert g(f) == b'B' + + def test_vararg_callback(self): + py.test.skip("callback with '...'") + def cb(i, va_list): + j = ffi.va_arg(va_list, "int") + k = ffi.va_arg(va_list, "long long") + return i * 2 + j * 3 + k * 5 + f = ffi.callback("long long cb(long i, ...)", cb) + res = f(10, ffi.cast("int", 100), ffi.cast("long long", 1000)) + assert res == 20 + 300 + 5000 + + def test_callback_decorator(self): + # + @ffi.callback("long(long, long)", error=42) + def cb(a, b): + return a - b + # + assert cb(-100, -10) == -90 + sz = ffi.sizeof("long") + assert cb((1 << (sz*8-1)) - 1, -10) == 42 + + def test_anonymous_enum(self): + # typedef enum { Value0 = 0 } e_t, *pe_t; + assert ffi.getctype("e_t*") == 'e_t *' + assert ffi.getctype("pe_t") == 'e_t *' + assert ffi.getctype("foo_e_t*") == 'foo_e_t *' + + def test_new_ctype(self): + p = ffi.new("int *") + py.test.raises(TypeError, ffi.new, p) + p = ffi.new(ffi.typeof("int *"), 42) + assert p[0] == 42 + + def test_enum_with_non_injective_mapping(self): + # enum e_noninj { AA3=0, BB3=0, CC3=0, DD3=0 }; + e = ffi.cast("enum e_noninj", 0) + assert ffi.string(e) == "AA3" # pick the first one arbitrarily + + def test_enum_refer_previous_enum_value(self): + # enum e_prev { AA4, BB4=2, CC4=4, DD4=BB4, EE4, FF4=CC4, GG4=FF4 }; + assert ffi.string(ffi.cast("enum e_prev", 2)) == "BB4" + assert ffi.string(ffi.cast("enum e_prev", 3)) == "EE4" + assert ffi.sizeof("char[DD4]") == 2 + assert ffi.sizeof("char[EE4]") == 3 + assert ffi.sizeof("char[FF4]") == 4 + assert ffi.sizeof("char[GG4]") == 4 + + def test_nested_anonymous_struct(self): + # struct nested_anon { + # struct { int a, b; }; + # union { int c, d; }; + # }; + assert ffi.sizeof("struct nested_anon") == 3 * SIZE_OF_INT + p = ffi.new("struct nested_anon *", [1, 2, 3]) + assert p.a == 1 + assert p.b == 2 + assert p.c == 3 + assert p.d == 3 + p.d = 17 + assert p.c == 17 + p.b = 19 + assert p.a == 1 + assert p.b == 19 + assert p.c == 17 + assert p.d == 17 + p = ffi.new("struct nested_anon *", {'b': 12, 'd': 14}) + assert p.a == 0 + assert p.b == 12 + assert p.c == 14 + assert p.d == 14 + + def test_nested_field_offset_align(self): + # struct nested_field_ofs_s { + # struct { int a; char b; }; + # union { char c; }; + # }; + assert ffi.offsetof("struct nested_field_ofs_s", "c") == 2 * SIZE_OF_INT + assert ffi.sizeof("struct nested_field_ofs_s") == 3 * SIZE_OF_INT + + def test_nested_anonymous_union(self): + # union nested_anon_u { + # struct { int a, b; }; + # union { int c, d; }; + # }; + assert ffi.sizeof("union nested_anon_u") == 2 * SIZE_OF_INT + p = ffi.new("union nested_anon_u *", [5]) + assert p.a == 5 + assert p.b == 0 + assert p.c == 5 + assert p.d == 5 + p.d = 17 + assert p.c == 17 + assert p.a == 17 + p.b = 19 + assert p.a == 17 + assert p.b == 19 + assert p.c == 17 + assert p.d == 17 + p = ffi.new("union nested_anon_u *", {'d': 14}) + assert p.a == 14 + assert p.b == 0 + assert p.c == 14 + assert p.d == 14 + p = ffi.new("union nested_anon_u *", {'b': 12}) + assert p.a == 0 + assert p.b == 12 + assert p.c == 0 + assert p.d == 0 + # we cannot specify several items in the dict, even though + # in theory in this particular case it would make sense + # to give both 'a' and 'b' + + def test_cast_to_array_type(self): + p = ffi.new("int[4]", [-5]) + q = ffi.cast("int[3]", p) + assert q[0] == -5 + assert repr(q).startswith("<cdata 'int[3]' 0x") + + def test_gc(self): + p = ffi.new("int *", 123) + seen = [] + def destructor(p1): + assert p1 is p + assert p1[0] == 123 + seen.append(1) + q = ffi.gc(p, destructor=destructor) + import gc; gc.collect() + assert seen == [] + del q + import gc; gc.collect(); gc.collect(); gc.collect() + assert seen == [1] + + def test_gc_2(self): + p = ffi.new("int *", 123) + seen = [] + q1 = ffi.gc(p, lambda p: seen.append(1)) + q2 = ffi.gc(q1, lambda p: seen.append(2)) + import gc; gc.collect() + assert seen == [] + del q1, q2 + import gc; gc.collect(); gc.collect(); gc.collect(); gc.collect() + assert seen == [2, 1] + + def test_gc_3(self): + p = ffi.new("int *", 123) + r = ffi.new("int *", 123) + seen = [] + seen_r = [] + q1 = ffi.gc(p, lambda p: seen.append(1)) + s1 = ffi.gc(r, lambda r: seen_r.append(4)) + q2 = ffi.gc(q1, lambda p: seen.append(2)) + s2 = ffi.gc(s1, lambda r: seen_r.append(5)) + q3 = ffi.gc(q2, lambda p: seen.append(3)) + import gc; gc.collect() + assert seen == [] + assert seen_r == [] + del q1, q2, q3, s2, s1 + import gc; gc.collect(); gc.collect(); gc.collect(); gc.collect() + assert seen == [3, 2, 1] + assert seen_r == [5, 4] + + def test_gc_4(self): + p = ffi.new("int *", 123) + seen = [] + q1 = ffi.gc(p, lambda p: seen.append(1)) + q2 = ffi.gc(q1, lambda p: seen.append(2)) + q3 = ffi.gc(q2, lambda p: seen.append(3)) + import gc; gc.collect() + assert seen == [] + del q1, q3 # q2 remains, and has a hard ref to q1 + import gc; gc.collect(); gc.collect(); gc.collect() + assert seen == [3] + + def test_release(self): + p = ffi.new("int[]", 123) + ffi.release(p) + # here, reading p[0] might give garbage or segfault... + ffi.release(p) # no effect + + def test_release_new_allocator(self): + seen = [] + def myalloc(size): + seen.append(size) + return ffi.new("char[]", b"X" * size) + def myfree(raw): + seen.append(raw) + alloc2 = ffi.new_allocator(alloc=myalloc, free=myfree) + p = alloc2("int[]", 15) + assert seen == [15 * 4] + ffi.release(p) + assert seen == [15 * 4, p] + ffi.release(p) # no effect + assert seen == [15 * 4, p] + # + del seen[:] + p = alloc2("struct ab *") + assert seen == [2 * 4] + ffi.release(p) + assert seen == [2 * 4, p] + ffi.release(p) # no effect + assert seen == [2 * 4, p] + + def test_CData_CType(self): + assert isinstance(ffi.cast("int", 0), ffi.CData) + assert isinstance(ffi.new("int *"), ffi.CData) + assert not isinstance(ffi.typeof("int"), ffi.CData) + assert not isinstance(ffi.cast("int", 0), ffi.CType) + assert not isinstance(ffi.new("int *"), ffi.CType) + + def test_CData_CType_2(self): + assert isinstance(ffi.typeof("int"), ffi.CType) + + def test_bool(self): + assert int(ffi.cast("_Bool", 0.1)) == 1 + assert int(ffi.cast("_Bool", -0.0)) == 0 + assert int(ffi.cast("_Bool", b'\x02')) == 1 + assert int(ffi.cast("_Bool", b'\x00')) == 0 + assert int(ffi.cast("_Bool", b'\x80')) == 1 + assert ffi.new("_Bool *", False)[0] == 0 + assert ffi.new("_Bool *", 1)[0] == 1 + py.test.raises(OverflowError, ffi.new, "_Bool *", 2) + py.test.raises(TypeError, ffi.string, ffi.cast("_Bool", 2)) + + def test_addressof(self): + p = ffi.new("struct ab *") + a = ffi.addressof(p[0]) + assert repr(a).startswith("<cdata 'struct ab *' 0x") + assert a == p + py.test.raises(TypeError, ffi.addressof, p) + py.test.raises((AttributeError, TypeError), ffi.addressof, 5) + py.test.raises(TypeError, ffi.addressof, ffi.cast("int", 5)) + + def test_addressof_field(self): + p = ffi.new("struct ab *") + b = ffi.addressof(p[0], 'b') + assert repr(b).startswith("<cdata 'int *' 0x") + assert int(ffi.cast("uintptr_t", b)) == ( + int(ffi.cast("uintptr_t", p)) + ffi.sizeof("int")) + assert b == ffi.addressof(p, 'b') + assert b != ffi.addressof(p, 'a') + + def test_addressof_field_nested(self): + # struct nesting { struct abc d, e; }; + p = ffi.new("struct nesting *") + py.test.raises(KeyError, ffi.addressof, p[0], 'e.b') + a = ffi.addressof(p[0], 'e', 'b') + assert int(ffi.cast("uintptr_t", a)) == ( + int(ffi.cast("uintptr_t", p)) + + ffi.sizeof("struct abc") + ffi.sizeof("int")) + + def test_addressof_anonymous_struct(self): + # typedef struct { int a; } anon_foo_t; + p = ffi.new("anon_foo_t *") + a = ffi.addressof(p[0]) + assert a == p + + def test_addressof_array(self): + p = ffi.new("int[52]") + p0 = ffi.addressof(p) + assert p0 == p + assert ffi.typeof(p0) is ffi.typeof("int(*)[52]") + py.test.raises(TypeError, ffi.addressof, p0) + # + p1 = ffi.addressof(p, 25) + assert ffi.typeof(p1) is ffi.typeof("int *") + assert (p1 - p) == 25 + assert ffi.addressof(p, 0) == p + + def test_addressof_pointer(self): + array = ffi.new("int[50]") + p = ffi.cast("int *", array) + py.test.raises(TypeError, ffi.addressof, p) + assert ffi.addressof(p, 0) == p + assert ffi.addressof(p, 25) == p + 25 + assert ffi.typeof(ffi.addressof(p, 25)) == ffi.typeof(p) + # + array = ffi.new("struct ab[50]") + p = ffi.cast("int *", array) + py.test.raises(TypeError, ffi.addressof, p) + assert ffi.addressof(p, 0) == p + assert ffi.addressof(p, 25) == p + 25 + assert ffi.typeof(ffi.addressof(p, 25)) == ffi.typeof(p) + + def test_addressof_array_in_struct(self): + # struct abc50 { int a, b; int c[50]; }; + p = ffi.new("struct abc50 *") + p1 = ffi.addressof(p, "c", 25) + assert ffi.typeof(p1) is ffi.typeof("int *") + assert p1 == ffi.cast("int *", p) + 27 + assert ffi.addressof(p, "c") == ffi.cast("int *", p) + 2 + assert ffi.addressof(p, "c", 0) == ffi.cast("int *", p) + 2 + p2 = ffi.addressof(p, 1) + assert ffi.typeof(p2) is ffi.typeof("struct abc50 *") + assert p2 == p + 1 + + def test_multiple_independent_structs(self): + CDEF2 = "struct ab { int x; };" + ffi2 = cffi.FFI(); ffi2.cdef(CDEF2) + outputfilename = recompile(ffi2, "test_multiple_independent_structs", + CDEF2, tmpdir=str(udir)) + module = imp.load_dynamic("test_multiple_independent_structs", + outputfilename) + ffi1 = module.ffi + foo1 = ffi1.new("struct ab *", [10]) + foo2 = ffi .new("struct ab *", [20, 30]) + assert foo1.x == 10 + assert foo2.a == 20 + assert foo2.b == 30 + + def test_include_struct_union_enum_typedef(self): + ffi1, CCODE = construction_params + ffi2 = cffi.FFI() + ffi2.include(ffi1) + outputfilename = recompile(ffi2, + "test_include_struct_union_enum_typedef", + CCODE, tmpdir=str(udir)) + module = imp.load_dynamic("test_include_struct_union_enum_typedef", + outputfilename) + ffi2 = module.ffi + # + p = ffi2.new("struct nonpacked *", [b'A', -43141]) + assert p.a == b'A' + assert p.b == -43141 + # + p = ffi.new("union simple_u *", [-52525]) + assert p.a == -52525 + # + p = ffi.cast("enum foq", 2) + assert ffi.string(p) == "cffiCC0" + assert ffi2.sizeof("char[cffiCC0]") == 2 + # + p = ffi.new("anon_foo_t *", [-52526]) + assert p.a == -52526 + p = ffi.new("named_foo_p", [-52527]) + assert p.a == -52527 + + def test_struct_packed(self): + # struct nonpacked { char a; int b; }; + # struct is_packed { char a; int b; } __attribute__((packed)); + assert ffi.sizeof("struct nonpacked") == 8 + assert ffi.sizeof("struct is_packed") == 5 + assert ffi.alignof("struct nonpacked") == 4 + assert ffi.alignof("struct is_packed") == 1 + s = ffi.new("struct is_packed[2]") + s[0].b = 42623381 + s[0].a = b'X' + s[1].b = -4892220 + s[1].a = b'Y' + assert s[0].b == 42623381 + assert s[0].a == b'X' + assert s[1].b == -4892220 + assert s[1].a == b'Y' + + def test_not_supported_bitfield_in_result(self): + # struct ints_and_bitfield { int a,b,c,d,e; int x:1; }; + e = py.test.raises(NotImplementedError, ffi.callback, + "struct ints_and_bitfield foo(void)", lambda: 42) + assert str(e.value) == ("struct ints_and_bitfield(*)(): " + "callback with unsupported argument or return type or with '...'") + + def test_inspecttype(self): + assert ffi.typeof("long").kind == "primitive" + assert ffi.typeof("long(*)(long, long**, ...)").cname == ( + "long(*)(long, long * *, ...)") + assert ffi.typeof("long(*)(long, long**, ...)").ellipsis is True + + def test_new_handle(self): + o = [2, 3, 4] + p = ffi.new_handle(o) + assert ffi.typeof(p) == ffi.typeof("void *") + assert ffi.from_handle(p) is o + assert ffi.from_handle(ffi.cast("char *", p)) is o + py.test.raises(RuntimeError, ffi.from_handle, ffi.NULL) + + def test_struct_array_no_length(self): + # struct array_no_length { int x; int a[]; }; + p = ffi.new("struct array_no_length *", [100, [200, 300, 400]]) + assert p.x == 100 + assert ffi.typeof(p.a) is ffi.typeof("int[]") # length available + assert p.a[0] == 200 + assert p.a[1] == 300 + assert p.a[2] == 400 + assert len(p.a) == 3 + assert list(p.a) == [200, 300, 400] + q = ffi.cast("struct array_no_length *", p) + assert ffi.typeof(q.a) is ffi.typeof("int *") # no length available + assert q.a[0] == 200 + assert q.a[1] == 300 + assert q.a[2] == 400 + py.test.raises(TypeError, len, q.a) + py.test.raises(TypeError, list, q.a) + + def test_all_primitives(self): + assert set(PRIMITIVE_TO_INDEX) == set([ + "char", + "short", + "int", + "long", + "long long", + "signed char", + "unsigned char", + "unsigned short", + "unsigned int", + "unsigned long", + "unsigned long long", + "float", + "double", + "long double", + "wchar_t", + "char16_t", + "char32_t", + "_Bool", + "int8_t", + "uint8_t", + "int16_t", + "uint16_t", + "int32_t", + "uint32_t", + "int64_t", + "uint64_t", + "int_least8_t", + "uint_least8_t", + "int_least16_t", + "uint_least16_t", + "int_least32_t", + "uint_least32_t", + "int_least64_t", + "uint_least64_t", + "int_fast8_t", + "uint_fast8_t", + "int_fast16_t", + "uint_fast16_t", + "int_fast32_t", + "uint_fast32_t", + "int_fast64_t", + "uint_fast64_t", + "intptr_t", + "uintptr_t", + "intmax_t", + "uintmax_t", + "ptrdiff_t", + "size_t", + "ssize_t", + 'float _Complex', + 'double _Complex', + ]) + for name in PRIMITIVE_TO_INDEX: + x = ffi.sizeof(name) + assert 1 <= x <= 16 + + def test_emit_c_code(self): + ffi = cffi.FFI() + ffi.set_source("foobar", "??") + c_file = str(udir.join('test_emit_c_code')) + ffi.emit_c_code(c_file) + assert os.path.isfile(c_file) + + def test_import_from_lib(self): + ffi2 = cffi.FFI() + ffi2.cdef("int myfunc(int); int myvar;\n#define MYFOO ...\n") + outputfilename = recompile(ffi2, "_test_import_from_lib", + "int myfunc(int x) { return x + 1; }\n" + "int myvar = -5;\n" + "#define MYFOO 42", tmpdir=str(udir)) + imp.load_dynamic("_test_import_from_lib", outputfilename) + from _test_import_from_lib.lib import myfunc, myvar, MYFOO + assert MYFOO == 42 + assert myfunc(43) == 44 + assert myvar == -5 # but can't be changed, so not very useful + py.test.raises(ImportError, "from _test_import_from_lib.lib import bar") + d = {} + exec("from _test_import_from_lib.lib import *", d) + assert (set(key for key in d if not key.startswith('_')) == + set(['myfunc', 'MYFOO'])) + # + # also test "import *" on the module itself, which should be + # equivalent to "import ffi, lib" + d = {} + exec("from _test_import_from_lib import *", d) + assert (sorted([x for x in d.keys() if not x.startswith('__')]) == + ['ffi', 'lib']) + + def test_char16_t(self): + x = ffi.new("char16_t[]", 5) + assert len(x) == 5 and ffi.sizeof(x) == 10 + x[2] = u+'\u1324' + assert x[2] == u+'\u1324' + y = ffi.new("char16_t[]", u+'\u1234\u5678') + assert len(y) == 3 + assert list(y) == [u+'\u1234', u+'\u5678', u+'\x00'] + assert ffi.string(y) == u+'\u1234\u5678' + z = ffi.new("char16_t[]", u+'\U00012345') + assert len(z) == 3 + assert list(z) == [u+'\ud808', u+'\udf45', u+'\x00'] + assert ffi.string(z) == u+'\U00012345' + + def test_char32_t(self): + x = ffi.new("char32_t[]", 5) + assert len(x) == 5 and ffi.sizeof(x) == 20 + x[3] = u+'\U00013245' + assert x[3] == u+'\U00013245' + y = ffi.new("char32_t[]", u+'\u1234\u5678') + assert len(y) == 3 + assert list(y) == [u+'\u1234', u+'\u5678', u+'\x00'] + z = ffi.new("char32_t[]", u+'\U00012345') + assert len(z) == 2 + assert list(z) == [u+'\U00012345', u+'\x00'] # maybe a 2-unichars strin + assert ffi.string(z) == u+'\U00012345' diff --git a/testing/cffi1/test_parse_c_type.py b/testing/cffi1/test_parse_c_type.py new file mode 100644 index 0000000..a9f5fcb --- /dev/null +++ b/testing/cffi1/test_parse_c_type.py @@ -0,0 +1,372 @@ +import sys, re, os, py +import cffi +from cffi import cffi_opcode + +if '__pypy__' in sys.builtin_module_names: + try: + # pytest >= 4.0 + py.test.skip("not available on pypy", allow_module_level=True) + except TypeError: + # older pytest + py.test.skip("not available on pypy") + +cffi_dir = os.path.dirname(cffi_opcode.__file__) + +r_macro = re.compile(r"#define \w+[(][^\n]*|#include [^\n]*") +r_define = re.compile(r"(#define \w+) [^\n]*") +r_ifdefs = re.compile(r"(#ifdef |#endif)[^\n]*") +header = open(os.path.join(cffi_dir, 'parse_c_type.h')).read() +header = r_macro.sub(r"", header) +header = r_define.sub(r"\1 ...", header) +header = r_ifdefs.sub(r"", header) + +ffi = cffi.FFI() +ffi.cdef(header) + +lib = ffi.verify( + open(os.path.join(cffi_dir, '..', 'c', 'parse_c_type.c')).read() + """ +static const char *get_common_type(const char *search, size_t search_len) { + return NULL; +} +""", include_dirs=[cffi_dir]) + +class ParseError(Exception): + pass + +struct_names = ["bar_s", "foo", "foo_", "foo_s", "foo_s1", "foo_s12"] +assert struct_names == sorted(struct_names) + +enum_names = ["ebar_s", "efoo", "efoo_", "efoo_s", "efoo_s1", "efoo_s12"] +assert enum_names == sorted(enum_names) + +identifier_names = ["id", "id0", "id05", "id05b", "tail"] +assert identifier_names == sorted(identifier_names) + +global_names = ["FIVE", "NEG", "ZERO"] +assert global_names == sorted(global_names) + +ctx = ffi.new("struct _cffi_type_context_s *") +c_struct_names = [ffi.new("char[]", _n.encode('ascii')) for _n in struct_names] +ctx_structs = ffi.new("struct _cffi_struct_union_s[]", len(struct_names)) +for _i in range(len(struct_names)): + ctx_structs[_i].name = c_struct_names[_i] +ctx_structs[3].flags = lib._CFFI_F_UNION +ctx.struct_unions = ctx_structs +ctx.num_struct_unions = len(struct_names) + +c_enum_names = [ffi.new("char[]", _n.encode('ascii')) for _n in enum_names] +ctx_enums = ffi.new("struct _cffi_enum_s[]", len(enum_names)) +for _i in range(len(enum_names)): + ctx_enums[_i].name = c_enum_names[_i] +ctx.enums = ctx_enums +ctx.num_enums = len(enum_names) + +c_identifier_names = [ffi.new("char[]", _n.encode('ascii')) + for _n in identifier_names] +ctx_identifiers = ffi.new("struct _cffi_typename_s[]", len(identifier_names)) +for _i in range(len(identifier_names)): + ctx_identifiers[_i].name = c_identifier_names[_i] + ctx_identifiers[_i].type_index = 100 + _i +ctx.typenames = ctx_identifiers +ctx.num_typenames = len(identifier_names) + +@ffi.callback("int(unsigned long long *)") +def fetch_constant_five(p): + p[0] = 5 + return 0 +@ffi.callback("int(unsigned long long *)") +def fetch_constant_zero(p): + p[0] = 0 + return 1 +@ffi.callback("int(unsigned long long *)") +def fetch_constant_neg(p): + p[0] = 123321 + return 1 + +ctx_globals = ffi.new("struct _cffi_global_s[]", len(global_names)) +c_glob_names = [ffi.new("char[]", _n.encode('ascii')) for _n in global_names] +for _i, _fn in enumerate([fetch_constant_five, + fetch_constant_neg, + fetch_constant_zero]): + ctx_globals[_i].name = c_glob_names[_i] + ctx_globals[_i].address = _fn + ctx_globals[_i].type_op = ffi.cast("_cffi_opcode_t", + cffi_opcode.OP_CONSTANT_INT if _i != 1 + else cffi_opcode.OP_ENUM) +ctx.globals = ctx_globals +ctx.num_globals = len(global_names) + + +def parse(input): + out = ffi.new("_cffi_opcode_t[]", 100) + info = ffi.new("struct _cffi_parse_info_s *") + info.ctx = ctx + info.output = out + info.output_size = len(out) + for j in range(len(out)): + out[j] = ffi.cast("void *", -424242) + res = lib.parse_c_type(info, input.encode('ascii')) + if res < 0: + raise ParseError(ffi.string(info.error_message).decode('ascii'), + info.error_location) + assert 0 <= res < len(out) + result = [] + for j in range(len(out)): + if out[j] == ffi.cast("void *", -424242): + assert res < j + break + i = int(ffi.cast("intptr_t", out[j])) + if j == res: + result.append('->') + result.append(i) + return result + +def parsex(input): + result = parse(input) + def str_if_int(x): + if isinstance(x, str): + return x + return '%d,%d' % (x & 255, x >> 8) + return ' '.join(map(str_if_int, result)) + +def parse_error(input, expected_msg, expected_location): + e = py.test.raises(ParseError, parse, input) + assert e.value.args[0] == expected_msg + assert e.value.args[1] == expected_location + +def make_getter(name): + opcode = getattr(lib, '_CFFI_OP_' + name) + def getter(value): + return opcode | (value << 8) + return getter + +Prim = make_getter('PRIMITIVE') +Pointer = make_getter('POINTER') +Array = make_getter('ARRAY') +OpenArray = make_getter('OPEN_ARRAY') +NoOp = make_getter('NOOP') +Func = make_getter('FUNCTION') +FuncEnd = make_getter('FUNCTION_END') +Struct = make_getter('STRUCT_UNION') +Enum = make_getter('ENUM') +Typename = make_getter('TYPENAME') + + +def test_simple(): + for simple_type, expected in [ + ("int", lib._CFFI_PRIM_INT), + ("signed int", lib._CFFI_PRIM_INT), + (" long ", lib._CFFI_PRIM_LONG), + ("long int", lib._CFFI_PRIM_LONG), + ("unsigned short", lib._CFFI_PRIM_USHORT), + ("long double", lib._CFFI_PRIM_LONGDOUBLE), + (" float _Complex", lib._CFFI_PRIM_FLOATCOMPLEX), + ("double _Complex ", lib._CFFI_PRIM_DOUBLECOMPLEX), + ]: + assert parse(simple_type) == ['->', Prim(expected)] + +def test_array(): + assert parse("int[5]") == [Prim(lib._CFFI_PRIM_INT), '->', Array(0), 5] + assert parse("int[]") == [Prim(lib._CFFI_PRIM_INT), '->', OpenArray(0)] + assert parse("int[5][8]") == [Prim(lib._CFFI_PRIM_INT), + '->', Array(3), + 5, + Array(0), + 8] + assert parse("int[][8]") == [Prim(lib._CFFI_PRIM_INT), + '->', OpenArray(2), + Array(0), + 8] + +def test_pointer(): + assert parse("int*") == [Prim(lib._CFFI_PRIM_INT), '->', Pointer(0)] + assert parse("int***") == [Prim(lib._CFFI_PRIM_INT), + Pointer(0), Pointer(1), '->', Pointer(2)] + +def test_grouping(): + assert parse("int*[]") == [Prim(lib._CFFI_PRIM_INT), + Pointer(0), '->', OpenArray(1)] + assert parse("int**[][8]") == [Prim(lib._CFFI_PRIM_INT), + Pointer(0), Pointer(1), + '->', OpenArray(4), Array(2), 8] + assert parse("int(*)[]") == [Prim(lib._CFFI_PRIM_INT), + NoOp(3), '->', Pointer(1), OpenArray(0)] + assert parse("int(*)[][8]") == [Prim(lib._CFFI_PRIM_INT), + NoOp(3), '->', Pointer(1), + OpenArray(4), Array(0), 8] + assert parse("int**(**)") == [Prim(lib._CFFI_PRIM_INT), + Pointer(0), Pointer(1), + NoOp(2), Pointer(3), '->', Pointer(4)] + assert parse("int**(**)[]") == [Prim(lib._CFFI_PRIM_INT), + Pointer(0), Pointer(1), + NoOp(6), Pointer(3), '->', Pointer(4), + OpenArray(2)] + +def test_simple_function(): + assert parse("int()") == [Prim(lib._CFFI_PRIM_INT), + '->', Func(0), FuncEnd(0), 0] + assert parse("int(int)") == [Prim(lib._CFFI_PRIM_INT), + '->', Func(0), NoOp(4), FuncEnd(0), + Prim(lib._CFFI_PRIM_INT)] + assert parse("int(long, char)") == [ + Prim(lib._CFFI_PRIM_INT), + '->', Func(0), NoOp(5), NoOp(6), FuncEnd(0), + Prim(lib._CFFI_PRIM_LONG), + Prim(lib._CFFI_PRIM_CHAR)] + assert parse("int(int*)") == [Prim(lib._CFFI_PRIM_INT), + '->', Func(0), NoOp(5), FuncEnd(0), + Prim(lib._CFFI_PRIM_INT), + Pointer(4)] + assert parse("int*(void)") == [Prim(lib._CFFI_PRIM_INT), + Pointer(0), + '->', Func(1), FuncEnd(0), 0] + assert parse("int(int, ...)") == [Prim(lib._CFFI_PRIM_INT), + '->', Func(0), NoOp(5), FuncEnd(1), 0, + Prim(lib._CFFI_PRIM_INT)] + +def test_internal_function(): + assert parse("int(*)()") == [Prim(lib._CFFI_PRIM_INT), + NoOp(3), '->', Pointer(1), + Func(0), FuncEnd(0), 0] + assert parse("int(*())[]") == [Prim(lib._CFFI_PRIM_INT), + NoOp(6), Pointer(1), + '->', Func(2), FuncEnd(0), 0, + OpenArray(0)] + assert parse("int(char(*)(long, short))") == [ + Prim(lib._CFFI_PRIM_INT), + '->', Func(0), NoOp(6), FuncEnd(0), + Prim(lib._CFFI_PRIM_CHAR), + NoOp(7), Pointer(5), + Func(4), NoOp(11), NoOp(12), FuncEnd(0), + Prim(lib._CFFI_PRIM_LONG), + Prim(lib._CFFI_PRIM_SHORT)] + +def test_fix_arg_types(): + assert parse("int(char(long, short))") == [ + Prim(lib._CFFI_PRIM_INT), + '->', Func(0), Pointer(5), FuncEnd(0), + Prim(lib._CFFI_PRIM_CHAR), + Func(4), NoOp(9), NoOp(10), FuncEnd(0), + Prim(lib._CFFI_PRIM_LONG), + Prim(lib._CFFI_PRIM_SHORT)] + assert parse("int(char[])") == [ + Prim(lib._CFFI_PRIM_INT), + '->', Func(0), Pointer(4), FuncEnd(0), + Prim(lib._CFFI_PRIM_CHAR), + OpenArray(4)] + +def test_enum(): + for i in range(len(enum_names)): + assert parse("enum %s" % (enum_names[i],)) == ['->', Enum(i)] + assert parse("enum %s*" % (enum_names[i],)) == [Enum(i), + '->', Pointer(0)] + +def test_error(): + parse_error("short short int", "'short' after another 'short' or 'long'", 6) + parse_error("long long long", "'long long long' is too long", 10) + parse_error("short long", "'long' after 'short'", 6) + parse_error("signed unsigned int", "multiple 'signed' or 'unsigned'", 7) + parse_error("unsigned signed int", "multiple 'signed' or 'unsigned'", 9) + parse_error("long char", "invalid combination of types", 5) + parse_error("short char", "invalid combination of types", 6) + parse_error("signed void", "invalid combination of types", 7) + parse_error("unsigned struct", "invalid combination of types", 9) + # + parse_error("", "identifier expected", 0) + parse_error("]", "identifier expected", 0) + parse_error("*", "identifier expected", 0) + parse_error("int ]**", "unexpected symbol", 4) + parse_error("char char", "unexpected symbol", 5) + parse_error("int(int]", "expected ')'", 7) + parse_error("int(*]", "expected ')'", 5) + parse_error("int(]", "identifier expected", 4) + parse_error("int[?]", "expected a positive integer constant", 4) + parse_error("int[24)", "expected ']'", 6) + parse_error("struct", "struct or union name expected", 6) + parse_error("struct 24", "struct or union name expected", 7) + parse_error("int[5](*)", "unexpected symbol", 6) + parse_error("int a(*)", "identifier expected", 6) + parse_error("int[123456789012345678901234567890]", "number too large", 4) + # + parse_error("_Complex", "identifier expected", 0) + parse_error("int _Complex", "_Complex type combination unsupported", 4) + parse_error("long double _Complex", "_Complex type combination unsupported", + 12) + +def test_number_too_large(): + num_max = sys.maxsize + assert parse("char[%d]" % num_max) == [Prim(lib._CFFI_PRIM_CHAR), + '->', Array(0), num_max] + parse_error("char[%d]" % (num_max + 1), "number too large", 5) + +def test_complexity_limit(): + parse_error("int" + "[]" * 2500, "internal type complexity limit reached", + 202) + +def test_struct(): + for i in range(len(struct_names)): + if i == 3: + tag = "union" + else: + tag = "struct" + assert parse("%s %s" % (tag, struct_names[i])) == ['->', Struct(i)] + assert parse("%s %s*" % (tag, struct_names[i])) == [Struct(i), + '->', Pointer(0)] + +def test_exchanging_struct_union(): + parse_error("union %s" % (struct_names[0],), + "wrong kind of tag: struct vs union", 6) + parse_error("struct %s" % (struct_names[3],), + "wrong kind of tag: struct vs union", 7) + +def test_identifier(): + for i in range(len(identifier_names)): + assert parse("%s" % (identifier_names[i])) == ['->', Typename(i)] + assert parse("%s*" % (identifier_names[i])) == [Typename(i), + '->', Pointer(0)] + +def test_cffi_opcode_sync(): + import cffi.model + for name in dir(lib): + if name.startswith('_CFFI_'): + assert getattr(cffi_opcode, name[6:]) == getattr(lib, name) + assert sorted(cffi_opcode.PRIMITIVE_TO_INDEX.keys()) == ( + sorted(cffi.model.PrimitiveType.ALL_PRIMITIVE_TYPES.keys())) + +def test_array_length_from_constant(): + parse_error("int[UNKNOWN]", "expected a positive integer constant", 4) + assert parse("int[FIVE]") == [Prim(lib._CFFI_PRIM_INT), '->', Array(0), 5] + assert parse("int[ZERO]") == [Prim(lib._CFFI_PRIM_INT), '->', Array(0), 0] + parse_error("int[NEG]", "expected a positive integer constant", 4) + +def test_various_constant_exprs(): + def array(n): + return [Prim(lib._CFFI_PRIM_CHAR), '->', Array(0), n] + assert parse("char[21]") == array(21) + assert parse("char[0x10]") == array(16) + assert parse("char[0X21]") == array(33) + assert parse("char[0Xb]") == array(11) + assert parse("char[0x1C]") == array(0x1C) + assert parse("char[0xc6]") == array(0xC6) + assert parse("char[010]") == array(8) + assert parse("char[021]") == array(17) + parse_error("char[08]", "invalid number", 5) + parse_error("char[1C]", "invalid number", 5) + parse_error("char[0C]", "invalid number", 5) + # not supported (really obscure): + # "char[+5]" + # "char['A']" + +def test_stdcall_cdecl(): + assert parse("int __stdcall(int)") == [Prim(lib._CFFI_PRIM_INT), + '->', Func(0), NoOp(4), FuncEnd(2), + Prim(lib._CFFI_PRIM_INT)] + assert parse("int __stdcall func(int)") == parse("int __stdcall(int)") + assert parse("int (__stdcall *)()") == [Prim(lib._CFFI_PRIM_INT), + NoOp(3), '->', Pointer(1), + Func(0), FuncEnd(2), 0] + assert parse("int (__stdcall *p)()") == parse("int (__stdcall*)()") + parse_error("__stdcall int", "identifier expected", 0) + parse_error("__cdecl int", "identifier expected", 0) + parse_error("int __stdcall", "expected '('", 13) + parse_error("int __cdecl", "expected '('", 11) diff --git a/testing/cffi1/test_pkgconfig.py b/testing/cffi1/test_pkgconfig.py new file mode 100644 index 0000000..c725cca --- /dev/null +++ b/testing/cffi1/test_pkgconfig.py @@ -0,0 +1,94 @@ +import sys +import subprocess +import py +import cffi.pkgconfig as pkgconfig +from cffi import PkgConfigError + + +def mock_call(libname, flag): + assert libname=="foobarbaz" + flags = { + "--cflags": "-I/usr/include/python3.6m -DABCD -DCFFI_TEST=1 -O42\n", + "--libs": "-L/usr/lib64 -lpython3.6 -shared\n", + } + return flags[flag] + + +def test_merge_flags(): + d1 = {"ham": [1, 2, 3], "spam" : ["a", "b", "c"], "foo" : []} + d2 = {"spam" : ["spam", "spam", "spam"], "bar" : ["b", "a", "z"]} + + pkgconfig.merge_flags(d1, d2) + assert d1 == { + "ham": [1, 2, 3], + "spam" : ["a", "b", "c", "spam", "spam", "spam"], + "bar" : ["b", "a", "z"], + "foo" : []} + + +def test_pkgconfig(): + assert pkgconfig.flags_from_pkgconfig([]) == {} + + saved = pkgconfig.call + try: + pkgconfig.call = mock_call + flags = pkgconfig.flags_from_pkgconfig(["foobarbaz"]) + finally: + pkgconfig.call = saved + assert flags == { + 'include_dirs': ['/usr/include/python3.6m'], + 'library_dirs': ['/usr/lib64'], + 'libraries': ['python3.6'], + 'define_macros': [('ABCD', None), ('CFFI_TEST', '1')], + 'extra_compile_args': ['-O42'], + 'extra_link_args': ['-shared'] + } + +class mock_subprocess: + PIPE = Ellipsis + class Popen: + def __init__(self, cmd, stdout, stderr): + if mock_subprocess.RESULT is None: + raise OSError("oops can't run") + assert cmd == ['pkg-config', '--print-errors', '--cflags', 'libfoo'] + def communicate(self): + bout, berr, rc = mock_subprocess.RESULT + self.returncode = rc + return bout, berr + +def test_call(): + saved = pkgconfig.subprocess + try: + pkgconfig.subprocess = mock_subprocess + + mock_subprocess.RESULT = None + e = py.test.raises(PkgConfigError, pkgconfig.call, "libfoo", "--cflags") + assert str(e.value) == "cannot run pkg-config: oops can't run" + + mock_subprocess.RESULT = b"", "Foo error!\n", 1 + e = py.test.raises(PkgConfigError, pkgconfig.call, "libfoo", "--cflags") + assert str(e.value) == "Foo error!" + + mock_subprocess.RESULT = b"abc\\def\n", "", 0 + e = py.test.raises(PkgConfigError, pkgconfig.call, "libfoo", "--cflags") + assert str(e.value).startswith("pkg-config --cflags libfoo returned an " + "unsupported backslash-escaped output:") + + mock_subprocess.RESULT = b"abc def\n", "", 0 + result = pkgconfig.call("libfoo", "--cflags") + assert result == "abc def\n" + + mock_subprocess.RESULT = b"abc def\n", "", 0 + result = pkgconfig.call("libfoo", "--cflags") + assert result == "abc def\n" + + if sys.version_info >= (3,): + mock_subprocess.RESULT = b"\xff\n", "", 0 + e = py.test.raises(PkgConfigError, pkgconfig.call, + "libfoo", "--cflags", encoding="utf-8") + assert str(e.value) == ( + "pkg-config --cflags libfoo returned bytes that cannot be " + "decoded with encoding 'utf-8':\nb'\\xff\\n'") + + finally: + pkgconfig.subprocess = saved diff --git a/testing/cffi1/test_re_python.py b/testing/cffi1/test_re_python.py new file mode 100644 index 0000000..377c29b --- /dev/null +++ b/testing/cffi1/test_re_python.py @@ -0,0 +1,256 @@ +import sys, os +import py +from cffi import FFI +from cffi import recompiler, ffiplatform, VerificationMissing +from testing.udir import udir +from testing.support import u + + +def setup_module(mod): + SRC = """ + #include <string.h> + #define FOOBAR (-42) + static const int FOOBAZ = -43; + #define BIGPOS 420000000000L + #define BIGNEG -420000000000L + int add42(int x) { return x + 42; } + int add43(int x, ...) { return x; } + int globalvar42 = 1234; + const int globalconst42 = 4321; + const char *const globalconsthello = "hello"; + struct foo_s; + typedef struct bar_s { int x; signed char a[]; } bar_t; + enum foo_e { AA, BB, CC }; + + void init_test_re_python(void) { } /* windows hack */ + void PyInit__test_re_python(void) { } /* windows hack */ + """ + tmpdir = udir.join('test_re_python') + tmpdir.ensure(dir=1) + c_file = tmpdir.join('_test_re_python.c') + c_file.write(SRC) + ext = ffiplatform.get_extension( + str(c_file), + '_test_re_python', + export_symbols=['add42', 'add43', 'globalvar42', + 'globalconst42', 'globalconsthello'] + ) + outputfilename = ffiplatform.compile(str(tmpdir), ext) + + # test with a non-ascii char + ofn, oext = os.path.splitext(outputfilename) + if sys.platform == "win32": + unicode_name = ofn + (u+'\u03be') + oext + else: + unicode_name = ofn + (u+'\xe9') + oext + try: + unicode_name.encode(sys.getfilesystemencoding()) + except UnicodeEncodeError: + unicode_name = None + if unicode_name is not None: + print(repr(outputfilename) + ' ==> ' + repr(unicode_name)) + os.rename(outputfilename, unicode_name) + outputfilename = unicode_name + + mod.extmod = outputfilename + mod.tmpdir = tmpdir + # + ffi = FFI() + ffi.cdef(""" + #define FOOBAR -42 + static const int FOOBAZ = -43; + #define BIGPOS 420000000000L + #define BIGNEG -420000000000L + int add42(int); + int add43(int, ...); + int globalvar42; + const int globalconst42; + const char *const globalconsthello = "hello"; + int no_such_function(int); + int no_such_globalvar; + struct foo_s; + typedef struct bar_s { int x; signed char a[]; } bar_t; + enum foo_e { AA, BB, CC }; + int strlen(const char *); + struct with_union { union { int a; char b; }; }; + union with_struct { struct { int a; char b; }; }; + struct NVGcolor { union { float rgba[4]; struct { float r,g,b,a; }; }; }; + """) + ffi.set_source('re_python_pysrc', None) + ffi.emit_python_code(str(tmpdir.join('re_python_pysrc.py'))) + mod.original_ffi = ffi + # + sys.path.insert(0, str(tmpdir)) + + +def test_constant(): + from re_python_pysrc import ffi + assert ffi.integer_const('FOOBAR') == -42 + assert ffi.integer_const('FOOBAZ') == -43 + +def test_large_constant(): + from re_python_pysrc import ffi + assert ffi.integer_const('BIGPOS') == 420000000000 + assert ffi.integer_const('BIGNEG') == -420000000000 + +def test_function(): + import _cffi_backend + from re_python_pysrc import ffi + lib = ffi.dlopen(extmod) + assert lib.add42(-10) == 32 + assert type(lib.add42) is _cffi_backend.FFI.CData + +def test_function_with_varargs(): + import _cffi_backend + from re_python_pysrc import ffi + lib = ffi.dlopen(extmod, 0) + assert lib.add43(45, ffi.cast("int", -5)) == 45 + assert type(lib.add43) is _cffi_backend.FFI.CData + +def test_dlopen_none(): + import _cffi_backend + from re_python_pysrc import ffi + name = None + if sys.platform == 'win32': + import ctypes.util + name = ctypes.util.find_msvcrt() + if name is None: + py.test.skip("dlopen(None) cannot work on Windows with Python 3") + lib = ffi.dlopen(name) + assert lib.strlen(b"hello") == 5 + +def test_dlclose(): + import _cffi_backend + from re_python_pysrc import ffi + lib = ffi.dlopen(extmod) + ffi.dlclose(lib) + if type(extmod) is not str: # unicode, on python 2 + str_extmod = extmod.encode('utf-8') + else: + str_extmod = extmod + e = py.test.raises(ffi.error, getattr, lib, 'add42') + assert str(e.value) == ( + "library '%s' has been closed" % (str_extmod,)) + ffi.dlclose(lib) # does not raise + +def test_constant_via_lib(): + from re_python_pysrc import ffi + lib = ffi.dlopen(extmod) + assert lib.FOOBAR == -42 + assert lib.FOOBAZ == -43 + +def test_opaque_struct(): + from re_python_pysrc import ffi + ffi.cast("struct foo_s *", 0) + py.test.raises(TypeError, ffi.new, "struct foo_s *") + +def test_nonopaque_struct(): + from re_python_pysrc import ffi + for p in [ffi.new("struct bar_s *", [5, b"foobar"]), + ffi.new("bar_t *", [5, b"foobar"])]: + assert p.x == 5 + assert p.a[0] == ord('f') + assert p.a[5] == ord('r') + +def test_enum(): + from re_python_pysrc import ffi + assert ffi.integer_const("BB") == 1 + e = ffi.cast("enum foo_e", 2) + assert ffi.string(e) == "CC" + +def test_include_1(): + sub_ffi = FFI() + sub_ffi.cdef("static const int k2 = 121212;") + sub_ffi.include(original_ffi) + assert 'macro FOOBAR' in original_ffi._parser._declarations + assert 'macro FOOBAZ' in original_ffi._parser._declarations + sub_ffi.set_source('re_python_pysrc', None) + sub_ffi.emit_python_code(str(tmpdir.join('_re_include_1.py'))) + # + if sys.version_info[:2] >= (3, 3): + import importlib + importlib.invalidate_caches() # issue 197 (but can't reproduce myself) + # + from _re_include_1 import ffi + assert ffi.integer_const('FOOBAR') == -42 + assert ffi.integer_const('FOOBAZ') == -43 + assert ffi.integer_const('k2') == 121212 + lib = ffi.dlopen(extmod) # <- a random unrelated library would be fine + assert lib.FOOBAR == -42 + assert lib.FOOBAZ == -43 + assert lib.k2 == 121212 + # + p = ffi.new("bar_t *", [5, b"foobar"]) + assert p.a[4] == ord('a') + +def test_global_var(): + from re_python_pysrc import ffi + lib = ffi.dlopen(extmod) + assert lib.globalvar42 == 1234 + p = ffi.addressof(lib, 'globalvar42') + lib.globalvar42 += 5 + assert p[0] == 1239 + p[0] -= 1 + assert lib.globalvar42 == 1238 + +def test_global_const_int(): + from re_python_pysrc import ffi + lib = ffi.dlopen(extmod) + assert lib.globalconst42 == 4321 + py.test.raises(AttributeError, ffi.addressof, lib, 'globalconst42') + +def test_global_const_nonint(): + from re_python_pysrc import ffi + lib = ffi.dlopen(extmod) + assert ffi.string(lib.globalconsthello, 8) == b"hello" + py.test.raises(AttributeError, ffi.addressof, lib, 'globalconsthello') + +def test_rtld_constants(): + from re_python_pysrc import ffi + ffi.RTLD_NOW # check that we have the attributes + ffi.RTLD_LAZY + ffi.RTLD_GLOBAL + +def test_no_such_function_or_global_var(): + from re_python_pysrc import ffi + lib = ffi.dlopen(extmod) + e = py.test.raises(ffi.error, getattr, lib, 'no_such_function') + assert str(e.value).startswith( + "symbol 'no_such_function' not found in library '") + e = py.test.raises(ffi.error, getattr, lib, 'no_such_globalvar') + assert str(e.value).startswith( + "symbol 'no_such_globalvar' not found in library '") + +def test_check_version(): + import _cffi_backend + e = py.test.raises(ImportError, _cffi_backend.FFI, + "foobar", _version=0x2594) + assert str(e.value).startswith( + "cffi out-of-line Python module 'foobar' has unknown version") + +def test_partial_enum(): + ffi = FFI() + ffi.cdef("enum foo { A, B, ... };") + ffi.set_source('test_partial_enum', None) + py.test.raises(VerificationMissing, ffi.emit_python_code, + str(tmpdir.join('test_partial_enum.py'))) + +def test_anonymous_union_inside_struct(): + # based on issue #357 + from re_python_pysrc import ffi + INT = ffi.sizeof("int") + assert ffi.offsetof("struct with_union", "a") == 0 + assert ffi.offsetof("struct with_union", "b") == 0 + assert ffi.sizeof("struct with_union") == INT + # + assert ffi.offsetof("union with_struct", "a") == 0 + assert ffi.offsetof("union with_struct", "b") == INT + assert ffi.sizeof("union with_struct") >= INT + 1 + # + FLOAT = ffi.sizeof("float") + assert ffi.sizeof("struct NVGcolor") == FLOAT * 4 + assert ffi.offsetof("struct NVGcolor", "rgba") == 0 + assert ffi.offsetof("struct NVGcolor", "r") == 0 + assert ffi.offsetof("struct NVGcolor", "g") == FLOAT + assert ffi.offsetof("struct NVGcolor", "b") == FLOAT * 2 + assert ffi.offsetof("struct NVGcolor", "a") == FLOAT * 3 diff --git a/testing/cffi1/test_realize_c_type.py b/testing/cffi1/test_realize_c_type.py new file mode 100644 index 0000000..a1f31e6 --- /dev/null +++ b/testing/cffi1/test_realize_c_type.py @@ -0,0 +1,73 @@ +import py, sys +from cffi import cffi_opcode + + +def check(input, expected_output=None, expected_ffi_error=False): + import _cffi_backend + ffi = _cffi_backend.FFI() + if not expected_ffi_error: + ct = ffi.typeof(input) + assert isinstance(ct, ffi.CType) + assert ct.cname == (expected_output or input) + else: + e = py.test.raises(ffi.error, ffi.typeof, input) + if isinstance(expected_ffi_error, str): + assert str(e.value) == expected_ffi_error + +def test_void(): + check("void", "void") + check(" void ", "void") + +def test_int_star(): + check("int") + check("int *") + check("int*", "int *") + check("long int", "long") + check("long") + +def test_noop(): + check("int(*)", "int *") + +def test_array(): + check("int[6]") + +def test_funcptr(): + check("int(*)(long)") + check("int(long)", expected_ffi_error="the type 'int(long)' is a" + " function type, not a pointer-to-function type") + check("int(void)", expected_ffi_error="the type 'int()' is a" + " function type, not a pointer-to-function type") + +def test_funcptr_rewrite_args(): + check("int(*)(int(int))", "int(*)(int(*)(int))") + check("int(*)(long[])", "int(*)(long *)") + check("int(*)(long[5])", "int(*)(long *)") + +def test_all_primitives(): + for name in cffi_opcode.PRIMITIVE_TO_INDEX: + check(name, name) + +def check_func(input, expected_output=None): + import _cffi_backend + ffi = _cffi_backend.FFI() + ct = ffi.typeof(ffi.callback(input, lambda: None)) + assert isinstance(ct, ffi.CType) + if sys.platform != 'win32' or sys.maxsize > 2**32: + expected_output = expected_output.replace('__stdcall *', '*') + assert ct.cname == expected_output + +def test_funcptr_stdcall(): + check_func("int(int)", "int(*)(int)") + check_func("int foobar(int)", "int(*)(int)") + check_func("int __stdcall(int)", "int(__stdcall *)(int)") + check_func("int __stdcall foobar(int)", "int(__stdcall *)(int)") + check_func("void __cdecl(void)", "void(*)()") + check_func("void __cdecl foobar(void)", "void(*)()") + check_func("void __stdcall(void)", "void(__stdcall *)()") + check_func("void __stdcall foobar(long, short)", + "void(__stdcall *)(long, short)") + check_func("void(void __cdecl(void), void __stdcall(void))", + "void(*)(void(*)(), void(__stdcall *)())") + +def test_variadic_overrides_stdcall(): + check("void (__stdcall*)(int, ...)", "void(*)(int, ...)") diff --git a/testing/cffi1/test_recompiler.py b/testing/cffi1/test_recompiler.py new file mode 100644 index 0000000..6a31110 --- /dev/null +++ b/testing/cffi1/test_recompiler.py @@ -0,0 +1,2316 @@ + +import sys, os, py +from cffi import FFI, VerificationError, FFIError, CDefError +from cffi import recompiler +from testing.udir import udir +from testing.support import u, long +from testing.support import FdWriteCapture, StdErrCapture, _verify + +try: + import importlib +except ImportError: + importlib = None + + +def check_type_table(input, expected_output, included=None): + ffi = FFI() + if included: + ffi1 = FFI() + ffi1.cdef(included) + ffi.include(ffi1) + ffi.cdef(input) + recomp = recompiler.Recompiler(ffi, 'testmod') + recomp.collect_type_table() + assert ''.join(map(str, recomp.cffi_types)) == expected_output + +def verify(ffi, module_name, source, *args, **kwds): + no_cpp = kwds.pop('no_cpp', False) + kwds.setdefault('undef_macros', ['NDEBUG']) + module_name = '_CFFI_' + module_name + ffi.set_source(module_name, source) + if not os.environ.get('NO_CPP') and not no_cpp: # test the .cpp mode too + kwds.setdefault('source_extension', '.cpp') + source = 'extern "C" {\n%s\n}' % (source,) + elif sys.platform != 'win32': + # add '-Werror' to the existing 'extra_compile_args' flags + kwds['extra_compile_args'] = (kwds.get('extra_compile_args', []) + + ['-Werror']) + return _verify(ffi, module_name, source, *args, **kwds) + +def test_set_source_no_slashes(): + ffi = FFI() + py.test.raises(ValueError, ffi.set_source, "abc/def", None) + py.test.raises(ValueError, ffi.set_source, "abc/def", "C code") + + +def test_type_table_func(): + check_type_table("double sin(double);", + "(FUNCTION 1)(PRIMITIVE 14)(FUNCTION_END 0)") + check_type_table("float sin(double);", + "(FUNCTION 3)(PRIMITIVE 14)(FUNCTION_END 0)(PRIMITIVE 13)") + check_type_table("float sin(void);", + "(FUNCTION 2)(FUNCTION_END 0)(PRIMITIVE 13)") + check_type_table("double sin(float); double cos(float);", + "(FUNCTION 3)(PRIMITIVE 13)(FUNCTION_END 0)(PRIMITIVE 14)") + check_type_table("double sin(float); double cos(double);", + "(FUNCTION 1)(PRIMITIVE 14)(FUNCTION_END 0)" # cos + "(FUNCTION 1)(PRIMITIVE 13)(FUNCTION_END 0)") # sin + check_type_table("float sin(double); float cos(float);", + "(FUNCTION 4)(PRIMITIVE 14)(FUNCTION_END 0)" # sin + "(FUNCTION 4)(PRIMITIVE 13)(FUNCTION_END 0)") # cos + +def test_type_table_use_noop_for_repeated_args(): + check_type_table("double sin(double *, double *);", + "(FUNCTION 4)(POINTER 4)(NOOP 1)(FUNCTION_END 0)" + "(PRIMITIVE 14)") + check_type_table("double sin(double *, double *, double);", + "(FUNCTION 3)(POINTER 3)(NOOP 1)(PRIMITIVE 14)" + "(FUNCTION_END 0)") + +def test_type_table_dont_use_noop_for_primitives(): + check_type_table("double sin(double, double);", + "(FUNCTION 1)(PRIMITIVE 14)(PRIMITIVE 14)(FUNCTION_END 0)") + +def test_type_table_funcptr_as_argument(): + check_type_table("int sin(double(float));", + "(FUNCTION 6)(PRIMITIVE 13)(FUNCTION_END 0)" + "(FUNCTION 7)(POINTER 0)(FUNCTION_END 0)" + "(PRIMITIVE 14)(PRIMITIVE 7)") + +def test_type_table_variadic_function(): + check_type_table("int sin(int, ...);", + "(FUNCTION 1)(PRIMITIVE 7)(FUNCTION_END 1)(POINTER 0)") + +def test_type_table_array(): + check_type_table("int a[100];", + "(PRIMITIVE 7)(ARRAY 0)(None 100)") + +def test_type_table_typedef(): + check_type_table("typedef int foo_t;", + "(PRIMITIVE 7)") + +def test_type_table_prebuilt_type(): + check_type_table("int32_t f(void);", + "(FUNCTION 2)(FUNCTION_END 0)(PRIMITIVE 21)") + +def test_type_table_struct_opaque(): + check_type_table("struct foo_s;", + "(STRUCT_UNION 0)") + +def test_type_table_struct(): + check_type_table("struct foo_s { int a; long b; };", + "(PRIMITIVE 7)(PRIMITIVE 9)(STRUCT_UNION 0)") + +def test_type_table_union(): + check_type_table("union foo_u { int a; long b; };", + "(PRIMITIVE 7)(PRIMITIVE 9)(STRUCT_UNION 0)") + +def test_type_table_struct_used(): + check_type_table("struct foo_s { int a; long b; }; int f(struct foo_s*);", + "(FUNCTION 3)(POINTER 5)(FUNCTION_END 0)" + "(PRIMITIVE 7)(PRIMITIVE 9)" + "(STRUCT_UNION 0)") + +def test_type_table_anonymous_struct_with_typedef(): + check_type_table("typedef struct { int a; long b; } foo_t;", + "(STRUCT_UNION 0)(PRIMITIVE 7)(PRIMITIVE 9)") + +def test_type_table_enum(): + check_type_table("enum foo_e { AA, BB, ... };", + "(ENUM 0)") + +def test_type_table_include_1(): + check_type_table("foo_t sin(foo_t);", + "(FUNCTION 1)(PRIMITIVE 14)(FUNCTION_END 0)", + included="typedef double foo_t;") + +def test_type_table_include_2(): + check_type_table("struct foo_s *sin(struct foo_s *);", + "(FUNCTION 1)(POINTER 3)(FUNCTION_END 0)(STRUCT_UNION 0)", + included="struct foo_s { int x, y; };") + + +def test_math_sin(): + import math + ffi = FFI() + ffi.cdef("float sin(double); double cos(double);") + lib = verify(ffi, 'test_math_sin', '#include <math.h>') + assert lib.cos(1.43) == math.cos(1.43) + +def test_repr_lib(): + ffi = FFI() + lib = verify(ffi, 'test_repr_lib', '') + assert repr(lib) == "<Lib object for '_CFFI_test_repr_lib'>" + +def test_funcarg_ptr(): + ffi = FFI() + ffi.cdef("int foo(int *);") + lib = verify(ffi, 'test_funcarg_ptr', 'int foo(int *p) { return *p; }') + assert lib.foo([-12345]) == -12345 + +def test_funcres_ptr(): + ffi = FFI() + ffi.cdef("int *foo(void);") + lib = verify(ffi, 'test_funcres_ptr', + 'int *foo(void) { static int x=-12345; return &x; }') + assert lib.foo()[0] == -12345 + +def test_global_var_array(): + ffi = FFI() + ffi.cdef("int a[100];") + lib = verify(ffi, 'test_global_var_array', 'int a[100] = { 9999 };') + lib.a[42] = 123456 + assert lib.a[42] == 123456 + assert lib.a[0] == 9999 + +def test_verify_typedef(): + ffi = FFI() + ffi.cdef("typedef int **foo_t;") + lib = verify(ffi, 'test_verify_typedef', 'typedef int **foo_t;') + assert ffi.sizeof("foo_t") == ffi.sizeof("void *") + +def test_verify_typedef_dotdotdot(): + ffi = FFI() + ffi.cdef("typedef ... foo_t;") + verify(ffi, 'test_verify_typedef_dotdotdot', 'typedef int **foo_t;') + +def test_verify_typedef_star_dotdotdot(): + ffi = FFI() + ffi.cdef("typedef ... *foo_t;") + verify(ffi, 'test_verify_typedef_star_dotdotdot', 'typedef int **foo_t;') + +def test_global_var_int(): + ffi = FFI() + ffi.cdef("int a, b, c;") + lib = verify(ffi, 'test_global_var_int', 'int a = 999, b, c;') + assert lib.a == 999 + lib.a -= 1001 + assert lib.a == -2 + lib.a = -2147483648 + assert lib.a == -2147483648 + py.test.raises(OverflowError, "lib.a = 2147483648") + py.test.raises(OverflowError, "lib.a = -2147483649") + lib.b = 525 # try with the first access being in setattr, too + assert lib.b == 525 + py.test.raises(AttributeError, "del lib.a") + py.test.raises(AttributeError, "del lib.c") + py.test.raises(AttributeError, "del lib.foobarbaz") + +def test_macro(): + ffi = FFI() + ffi.cdef("#define FOOBAR ...") + lib = verify(ffi, 'test_macro', "#define FOOBAR (-6912)") + assert lib.FOOBAR == -6912 + py.test.raises(AttributeError, "lib.FOOBAR = 2") + +def test_macro_check_value(): + # the value '-0x80000000' in C sources does not have a clear meaning + # to me; it appears to have a different effect than '-2147483648'... + # Moreover, on 32-bits, -2147483648 is actually equal to + # -2147483648U, which in turn is equal to 2147483648U and so positive. + vals = ['42', '-42', '0x80000000', '-2147483648', + '0', '9223372036854775809ULL', + '-9223372036854775807LL'] + if sys.maxsize <= 2**32 or sys.platform == 'win32': + vals.remove('-2147483648') + ffi = FFI() + cdef_lines = ['#define FOO_%d_%d %s' % (i, j, vals[i]) + for i in range(len(vals)) + for j in range(len(vals))] + ffi.cdef('\n'.join(cdef_lines)) + + verify_lines = ['#define FOO_%d_%d %s' % (i, j, vals[j]) # [j], not [i] + for i in range(len(vals)) + for j in range(len(vals))] + lib = verify(ffi, 'test_macro_check_value_ok', + '\n'.join(verify_lines)) + # + for j in range(len(vals)): + c_got = int(vals[j].replace('U', '').replace('L', ''), 0) + c_compiler_msg = str(c_got) + if c_got > 0: + c_compiler_msg += ' (0x%x)' % (c_got,) + # + for i in range(len(vals)): + attrname = 'FOO_%d_%d' % (i, j) + if i == j: + x = getattr(lib, attrname) + assert x == c_got + else: + e = py.test.raises(ffi.error, getattr, lib, attrname) + assert str(e.value) == ( + "the C compiler says '%s' is equal to " + "%s, but the cdef disagrees" % (attrname, c_compiler_msg)) + +def test_constant(): + ffi = FFI() + ffi.cdef("static const int FOOBAR;") + lib = verify(ffi, 'test_constant', "#define FOOBAR (-6912)") + assert lib.FOOBAR == -6912 + py.test.raises(AttributeError, "lib.FOOBAR = 2") + +def test_check_value_of_static_const(): + ffi = FFI() + ffi.cdef("static const int FOOBAR = 042;") + lib = verify(ffi, 'test_check_value_of_static_const', + "#define FOOBAR (-6912)") + e = py.test.raises(ffi.error, getattr, lib, 'FOOBAR') + assert str(e.value) == ( + "the C compiler says 'FOOBAR' is equal to -6912, but the cdef disagrees") + +def test_constant_nonint(): + ffi = FFI() + ffi.cdef("static const double FOOBAR;") + lib = verify(ffi, 'test_constant_nonint', "#define FOOBAR (-6912.5)") + assert lib.FOOBAR == -6912.5 + py.test.raises(AttributeError, "lib.FOOBAR = 2") + +def test_constant_ptr(): + ffi = FFI() + ffi.cdef("static double *const FOOBAR;") + lib = verify(ffi, 'test_constant_ptr', "#define FOOBAR NULL") + assert lib.FOOBAR == ffi.NULL + assert ffi.typeof(lib.FOOBAR) == ffi.typeof("double *") + +def test_dir(): + ffi = FFI() + ffi.cdef("int ff(int); int aa; static const int my_constant;") + lib = verify(ffi, 'test_dir', """ + #define my_constant (-45) + int aa; + int ff(int x) { return x+aa; } + """) + lib.aa = 5 + assert dir(lib) == ['aa', 'ff', 'my_constant'] + # + aaobj = lib.__dict__['aa'] + assert not isinstance(aaobj, int) # some internal object instead + assert lib.__dict__ == { + 'ff': lib.ff, + 'aa': aaobj, + 'my_constant': -45} + lib.__dict__['ff'] = "??" + assert lib.ff(10) == 15 + +def test_verify_opaque_struct(): + ffi = FFI() + ffi.cdef("struct foo_s;") + lib = verify(ffi, 'test_verify_opaque_struct', "struct foo_s;") + assert ffi.typeof("struct foo_s").cname == "struct foo_s" + +def test_verify_opaque_union(): + ffi = FFI() + ffi.cdef("union foo_s;") + lib = verify(ffi, 'test_verify_opaque_union', "union foo_s;") + assert ffi.typeof("union foo_s").cname == "union foo_s" + +def test_verify_struct(): + ffi = FFI() + ffi.cdef("""struct foo_s { int b; short a; ...; }; + struct bar_s { struct foo_s *f; };""") + lib = verify(ffi, 'test_verify_struct', + """struct foo_s { short a; int b; }; + struct bar_s { struct foo_s *f; };""") + ffi.typeof("struct bar_s *") + p = ffi.new("struct foo_s *", {'a': -32768, 'b': -2147483648}) + assert p.a == -32768 + assert p.b == -2147483648 + py.test.raises(OverflowError, "p.a -= 1") + py.test.raises(OverflowError, "p.b -= 1") + q = ffi.new("struct bar_s *", {'f': p}) + assert q.f == p + # + assert ffi.offsetof("struct foo_s", "a") == 0 + assert ffi.offsetof("struct foo_s", "b") == 4 + assert ffi.offsetof(u+"struct foo_s", u+"b") == 4 + # + py.test.raises(TypeError, ffi.addressof, p) + assert ffi.addressof(p[0]) == p + assert ffi.typeof(ffi.addressof(p[0])) is ffi.typeof("struct foo_s *") + assert ffi.typeof(ffi.addressof(p, "b")) is ffi.typeof("int *") + assert ffi.addressof(p, "b")[0] == p.b + +def test_verify_exact_field_offset(): + ffi = FFI() + ffi.cdef("""struct foo_s { int b; short a; };""") + lib = verify(ffi, 'test_verify_exact_field_offset', + """struct foo_s { short a; int b; };""") + e = py.test.raises(ffi.error, ffi.new, "struct foo_s *", []) # lazily + assert str(e.value) == ("struct foo_s: wrong offset for field 'b' (cdef " + 'says 0, but C compiler says 4). fix it or use "...;" ' + "in the cdef for struct foo_s to make it flexible") + +def test_type_caching(): + ffi1 = FFI(); ffi1.cdef("struct foo_s;") + ffi2 = FFI(); ffi2.cdef("struct foo_s;") # different one! + lib1 = verify(ffi1, 'test_type_caching_1', 'struct foo_s;') + lib2 = verify(ffi2, 'test_type_caching_2', 'struct foo_s;') + # shared types + assert ffi1.typeof("long") is ffi2.typeof("long") + assert ffi1.typeof("long**") is ffi2.typeof("long * *") + assert ffi1.typeof("long(*)(int, ...)") is ffi2.typeof("long(*)(int, ...)") + # non-shared types + assert ffi1.typeof("struct foo_s") is not ffi2.typeof("struct foo_s") + assert ffi1.typeof("struct foo_s *") is not ffi2.typeof("struct foo_s *") + assert ffi1.typeof("struct foo_s*(*)()") is not ( + ffi2.typeof("struct foo_s*(*)()")) + assert ffi1.typeof("void(*)(struct foo_s*)") is not ( + ffi2.typeof("void(*)(struct foo_s*)")) + +def test_verify_enum(): + ffi = FFI() + ffi.cdef("""enum e1 { B1, A1, ... }; enum e2 { B2, A2, ... };""") + lib = verify(ffi, 'test_verify_enum', + "enum e1 { A1, B1, C1=%d };" % sys.maxsize + + "enum e2 { A2, B2, C2 };") + ffi.typeof("enum e1") + ffi.typeof("enum e2") + assert lib.A1 == 0 + assert lib.B1 == 1 + assert lib.A2 == 0 + assert lib.B2 == 1 + assert ffi.sizeof("enum e1") == ffi.sizeof("long") + assert ffi.sizeof("enum e2") == ffi.sizeof("int") + assert repr(ffi.cast("enum e1", 0)) == "<cdata 'enum e1' 0: A1>" + +def test_duplicate_enum(): + ffi = FFI() + ffi.cdef("enum e1 { A1, ... }; enum e2 { A1, ... };") + py.test.raises(VerificationError, verify, ffi, 'test_duplicate_enum', + "enum e1 { A1 }; enum e2 { B1 };") + +def test_dotdotdot_length_of_array_field(): + ffi = FFI() + ffi.cdef("struct foo_s { int a[...]; int b[...]; };") + verify(ffi, 'test_dotdotdot_length_of_array_field', + "struct foo_s { int a[42]; int b[11]; };") + assert ffi.sizeof("struct foo_s") == (42 + 11) * 4 + p = ffi.new("struct foo_s *") + assert p.a[41] == p.b[10] == 0 + py.test.raises(IndexError, "p.a[42]") + py.test.raises(IndexError, "p.b[11]") + +def test_dotdotdot_global_array(): + ffi = FFI() + ffi.cdef("int aa[...]; int bb[...];") + lib = verify(ffi, 'test_dotdotdot_global_array', + "int aa[41]; int bb[12];") + assert ffi.sizeof(lib.aa) == 41 * 4 + assert ffi.sizeof(lib.bb) == 12 * 4 + assert lib.aa[40] == lib.bb[11] == 0 + py.test.raises(IndexError, "lib.aa[41]") + py.test.raises(IndexError, "lib.bb[12]") + +def test_misdeclared_field_1(): + ffi = FFI() + ffi.cdef("struct foo_s { int a[5]; };") + try: + verify(ffi, 'test_misdeclared_field_1', + "struct foo_s { int a[6]; };") + except VerificationError: + pass # ok, fail during compilation already (e.g. C++) + else: + assert ffi.sizeof("struct foo_s") == 24 # found by the actual C code + try: + # lazily build the fields and boom: + p = ffi.new("struct foo_s *") + p.a + assert False, "should have raised" + except ffi.error as e: + assert str(e).startswith("struct foo_s: wrong size for field 'a' " + "(cdef says 20, but C compiler says 24)") + +def test_open_array_in_struct(): + ffi = FFI() + ffi.cdef("struct foo_s { int b; int a[]; };") + verify(ffi, 'test_open_array_in_struct', + "struct foo_s { int b; int a[]; };") + assert ffi.sizeof("struct foo_s") == 4 + p = ffi.new("struct foo_s *", [5, [10, 20, 30, 40]]) + assert p.a[2] == 30 + assert ffi.sizeof(p) == ffi.sizeof("void *") + assert ffi.sizeof(p[0]) == 5 * ffi.sizeof("int") + +def test_math_sin_type(): + ffi = FFI() + ffi.cdef("double sin(double); void *xxtestfunc();") + lib = verify(ffi, 'test_math_sin_type', """ + #include <math.h> + void *xxtestfunc(void) { return 0; } + """) + # 'lib.sin' is typed as a <built-in method> object on lib + assert ffi.typeof(lib.sin).cname == "double(*)(double)" + # 'x' is another <built-in method> object on lib, made very indirectly + x = type(lib).__dir__.__get__(lib) + py.test.raises(TypeError, ffi.typeof, x) + # + # present on built-in functions on CPython; must be emulated on PyPy: + assert lib.sin.__name__ == 'sin' + assert lib.sin.__module__ == '_CFFI_test_math_sin_type' + assert lib.sin.__doc__ == ( + "double sin(double);\n" + "\n" + "CFFI C function from _CFFI_test_math_sin_type.lib") + + assert ffi.typeof(lib.xxtestfunc).cname == "void *(*)()" + assert lib.xxtestfunc.__doc__ == ( + "void *xxtestfunc();\n" + "\n" + "CFFI C function from _CFFI_test_math_sin_type.lib") + +def test_verify_anonymous_struct_with_typedef(): + ffi = FFI() + ffi.cdef("typedef struct { int a; long b; ...; } foo_t;") + verify(ffi, 'test_verify_anonymous_struct_with_typedef', + "typedef struct { long b; int hidden, a; } foo_t;") + p = ffi.new("foo_t *", {'b': 42}) + assert p.b == 42 + assert repr(p).startswith("<cdata 'foo_t *' ") + +def test_verify_anonymous_struct_with_star_typedef(): + ffi = FFI() + ffi.cdef("typedef struct { int a; long b; } *foo_t;") + verify(ffi, 'test_verify_anonymous_struct_with_star_typedef', + "typedef struct { int a; long b; } *foo_t;") + p = ffi.new("foo_t", {'b': 42}) + assert p.b == 42 + +def test_verify_anonymous_enum_with_typedef(): + ffi = FFI() + ffi.cdef("typedef enum { AA, ... } e1;") + lib = verify(ffi, 'test_verify_anonymous_enum_with_typedef1', + "typedef enum { BB, CC, AA } e1;") + assert lib.AA == 2 + assert ffi.sizeof("e1") == ffi.sizeof("int") + assert repr(ffi.cast("e1", 2)) == "<cdata 'e1' 2: AA>" + # + ffi = FFI() + ffi.cdef("typedef enum { AA=%d } e1;" % sys.maxsize) + lib = verify(ffi, 'test_verify_anonymous_enum_with_typedef2', + "typedef enum { AA=%d } e1;" % sys.maxsize) + assert lib.AA == int(ffi.cast("long", sys.maxsize)) + assert ffi.sizeof("e1") == ffi.sizeof("long") + +def test_unique_types(): + CDEF = "struct foo_s; union foo_u; enum foo_e { AA };" + ffi1 = FFI(); ffi1.cdef(CDEF); verify(ffi1, "test_unique_types_1", CDEF) + ffi2 = FFI(); ffi2.cdef(CDEF); verify(ffi2, "test_unique_types_2", CDEF) + # + assert ffi1.typeof("char") is ffi2.typeof("char ") + assert ffi1.typeof("long") is ffi2.typeof("signed long int") + assert ffi1.typeof("double *") is ffi2.typeof("double*") + assert ffi1.typeof("int ***") is ffi2.typeof(" int * * *") + assert ffi1.typeof("int[]") is ffi2.typeof("signed int[]") + assert ffi1.typeof("signed int*[17]") is ffi2.typeof("int *[17]") + assert ffi1.typeof("void") is ffi2.typeof("void") + assert ffi1.typeof("int(*)(int,int)") is ffi2.typeof("int(*)(int,int)") + # + # these depend on user-defined data, so should not be shared + for name in ["struct foo_s", + "union foo_u *", + "enum foo_e", + "struct foo_s *(*)()", + "void(*)(struct foo_s *)", + "struct foo_s *(*[5])[8]", + ]: + assert ffi1.typeof(name) is not ffi2.typeof(name) + # sanity check: twice 'ffi1' + assert ffi1.typeof("struct foo_s*") is ffi1.typeof("struct foo_s *") + +def test_module_name_in_package(): + ffi = FFI() + ffi.cdef("int foo(int);") + recompiler.recompile(ffi, "test_module_name_in_package.mymod", + "int foo(int x) { return x + 32; }", + tmpdir=str(udir)) + old_sys_path = sys.path[:] + try: + package_dir = udir.join('test_module_name_in_package') + for name in os.listdir(str(udir)): + assert not name.startswith('test_module_name_in_package.') + assert os.path.isdir(str(package_dir)) + assert len(os.listdir(str(package_dir))) > 0 + assert os.path.exists(str(package_dir.join('mymod.c'))) + package_dir.join('__init__.py').write('') + # + getattr(importlib, 'invalidate_caches', object)() + # + sys.path.insert(0, str(udir)) + import test_module_name_in_package.mymod + assert test_module_name_in_package.mymod.lib.foo(10) == 42 + assert test_module_name_in_package.mymod.__name__ == ( + 'test_module_name_in_package.mymod') + finally: + sys.path[:] = old_sys_path + +def test_bad_size_of_global_1(): + ffi = FFI() + ffi.cdef("short glob;") + py.test.raises(VerificationError, verify, ffi, + "test_bad_size_of_global_1", "long glob;") + +def test_bad_size_of_global_2(): + ffi = FFI() + ffi.cdef("int glob[10];") + py.test.raises(VerificationError, verify, ffi, + "test_bad_size_of_global_2", "int glob[9];") + +def test_unspecified_size_of_global_1(): + ffi = FFI() + ffi.cdef("int glob[];") + lib = verify(ffi, "test_unspecified_size_of_global_1", "int glob[10];") + assert ffi.typeof(lib.glob) == ffi.typeof("int *") + +def test_unspecified_size_of_global_2(): + ffi = FFI() + ffi.cdef("int glob[][5];") + lib = verify(ffi, "test_unspecified_size_of_global_2", "int glob[10][5];") + assert ffi.typeof(lib.glob) == ffi.typeof("int(*)[5]") + +def test_unspecified_size_of_global_3(): + ffi = FFI() + ffi.cdef("int glob[][...];") + lib = verify(ffi, "test_unspecified_size_of_global_3", "int glob[10][5];") + assert ffi.typeof(lib.glob) == ffi.typeof("int(*)[5]") + +def test_unspecified_size_of_global_4(): + ffi = FFI() + ffi.cdef("int glob[...][...];") + lib = verify(ffi, "test_unspecified_size_of_global_4", "int glob[10][5];") + assert ffi.typeof(lib.glob) == ffi.typeof("int[10][5]") + +def test_include_1(): + ffi1 = FFI() + ffi1.cdef("typedef double foo_t;") + verify(ffi1, "test_include_1_parent", "typedef double foo_t;") + ffi = FFI() + ffi.include(ffi1) + ffi.cdef("foo_t ff1(foo_t);") + lib = verify(ffi, "test_include_1", "double ff1(double x) { return 42.5; }") + assert lib.ff1(0) == 42.5 + assert ffi1.typeof("foo_t") is ffi.typeof("foo_t") is ffi.typeof("double") + +def test_include_1b(): + ffi1 = FFI() + ffi1.cdef("int foo1(int);") + lib1 = verify(ffi1, "test_include_1b_parent", + "int foo1(int x) { return x + 10; }") + ffi = FFI() + ffi.include(ffi1) + ffi.cdef("int foo2(int);") + lib = verify(ffi, "test_include_1b", "int foo2(int x) { return x - 5; }") + assert lib.foo2(42) == 37 + assert lib.foo1(42) == 52 + assert lib.foo1 is lib1.foo1 + +def test_include_2(): + ffi1 = FFI() + ffi1.cdef("struct foo_s { int x, y; };") + verify(ffi1, "test_include_2_parent", "struct foo_s { int x, y; };") + ffi = FFI() + ffi.include(ffi1) + ffi.cdef("struct foo_s *ff2(struct foo_s *);") + lib = verify(ffi, "test_include_2", + "struct foo_s { int x, y; }; //usually from a #include\n" + "struct foo_s *ff2(struct foo_s *p) { p->y++; return p; }") + p = ffi.new("struct foo_s *") + p.y = 41 + q = lib.ff2(p) + assert q == p + assert p.y == 42 + assert ffi1.typeof("struct foo_s") is ffi.typeof("struct foo_s") + +def test_include_3(): + ffi1 = FFI() + ffi1.cdef("typedef short sshort_t;") + verify(ffi1, "test_include_3_parent", "typedef short sshort_t;") + ffi = FFI() + ffi.include(ffi1) + ffi.cdef("sshort_t ff3(sshort_t);") + lib = verify(ffi, "test_include_3", + "typedef short sshort_t; //usually from a #include\n" + "sshort_t ff3(sshort_t x) { return x + 42; }") + assert lib.ff3(10) == 52 + assert ffi.typeof(ffi.cast("sshort_t", 42)) is ffi.typeof("short") + assert ffi1.typeof("sshort_t") is ffi.typeof("sshort_t") + +def test_include_4(): + ffi1 = FFI() + ffi1.cdef("typedef struct { int x; } mystruct_t;") + verify(ffi1, "test_include_4_parent", + "typedef struct { int x; } mystruct_t;") + ffi = FFI() + ffi.include(ffi1) + ffi.cdef("mystruct_t *ff4(mystruct_t *);") + lib = verify(ffi, "test_include_4", + "typedef struct {int x; } mystruct_t; //usually from a #include\n" + "mystruct_t *ff4(mystruct_t *p) { p->x += 42; return p; }") + p = ffi.new("mystruct_t *", [10]) + q = lib.ff4(p) + assert q == p + assert p.x == 52 + assert ffi1.typeof("mystruct_t") is ffi.typeof("mystruct_t") + +def test_include_5(): + ffi1 = FFI() + ffi1.cdef("typedef struct { int x[2]; int y; } *mystruct_p;") + verify(ffi1, "test_include_5_parent", + "typedef struct { int x[2]; int y; } *mystruct_p;") + ffi = FFI() + ffi.include(ffi1) + ffi.cdef("mystruct_p ff5(mystruct_p);") + lib = verify(ffi, "test_include_5", + "typedef struct {int x[2]; int y; } *mystruct_p; //usually #include\n" + "mystruct_p ff5(mystruct_p p) { p->x[1] += 42; return p; }") + assert ffi.alignof(ffi.typeof("mystruct_p").item) == 4 + assert ffi1.typeof("mystruct_p") is ffi.typeof("mystruct_p") + p = ffi.new("mystruct_p", [[5, 10], -17]) + q = lib.ff5(p) + assert q == p + assert p.x[0] == 5 + assert p.x[1] == 52 + assert p.y == -17 + assert ffi.alignof(ffi.typeof(p[0])) == 4 + +def test_include_6(): + ffi1 = FFI() + ffi1.cdef("typedef ... mystruct_t;") + verify(ffi1, "test_include_6_parent", + "typedef struct _mystruct_s mystruct_t;") + ffi = FFI() + ffi.include(ffi1) + ffi.cdef("mystruct_t *ff6(void); int ff6b(mystruct_t *);") + lib = verify(ffi, "test_include_6", + "typedef struct _mystruct_s mystruct_t; //usually from a #include\n" + "struct _mystruct_s { int x; };\n" + "static mystruct_t result_struct = { 42 };\n" + "mystruct_t *ff6(void) { return &result_struct; }\n" + "int ff6b(mystruct_t *p) { return p->x; }") + p = lib.ff6() + assert ffi.cast("int *", p)[0] == 42 + assert lib.ff6b(p) == 42 + +def test_include_7(): + ffi1 = FFI() + ffi1.cdef("typedef ... mystruct_t;\n" + "int ff7b(mystruct_t *);") + verify(ffi1, "test_include_7_parent", + "typedef struct { int x; } mystruct_t;\n" + "int ff7b(mystruct_t *p) { return p->x; }") + ffi = FFI() + ffi.include(ffi1) + ffi.cdef("mystruct_t *ff7(void);") + lib = verify(ffi, "test_include_7", + "typedef struct { int x; } mystruct_t; //usually from a #include\n" + "static mystruct_t result_struct = { 42 };" + "mystruct_t *ff7(void) { return &result_struct; }") + p = lib.ff7() + assert ffi.cast("int *", p)[0] == 42 + assert lib.ff7b(p) == 42 + +def test_include_8(): + ffi1 = FFI() + ffi1.cdef("struct foo_s;") + verify(ffi1, "test_include_8_parent", "struct foo_s;") + ffi = FFI() + ffi.include(ffi1) + ffi.cdef("struct foo_s { int x, y; };") + verify(ffi, "test_include_8", "struct foo_s { int x, y; };") + e = py.test.raises(NotImplementedError, ffi.new, "struct foo_s *") + assert str(e.value) == ( + "'struct foo_s' is opaque in the ffi.include(), but no longer in " + "the ffi doing the include (workaround: don't use ffi.include() but" + " duplicate the declarations of everything using struct foo_s)") + +def test_unicode_libraries(): + try: + unicode + except NameError: + py.test.skip("for python 2.x") + # + import math + lib_m = "m" + if sys.platform == 'win32': + #there is a small chance this fails on Mingw via environ $CC + import distutils.ccompiler + if distutils.ccompiler.get_default_compiler() == 'msvc': + lib_m = 'msvcrt' + ffi = FFI() + ffi.cdef(unicode("float sin(double); double cos(double);")) + lib = verify(ffi, 'test_math_sin_unicode', unicode('#include <math.h>'), + libraries=[unicode(lib_m)]) + assert lib.cos(1.43) == math.cos(1.43) + +def test_incomplete_struct_as_arg(): + ffi = FFI() + ffi.cdef("struct foo_s { int x; ...; }; int f(int, struct foo_s);") + lib = verify(ffi, "test_incomplete_struct_as_arg", + "struct foo_s { int a, x, z; };\n" + "int f(int b, struct foo_s s) { return s.x * b; }") + s = ffi.new("struct foo_s *", [21]) + assert s.x == 21 + assert ffi.sizeof(s[0]) == 12 + assert ffi.offsetof(ffi.typeof(s), 'x') == 4 + assert lib.f(2, s[0]) == 42 + assert ffi.typeof(lib.f) == ffi.typeof("int(*)(int, struct foo_s)") + +def test_incomplete_struct_as_result(): + ffi = FFI() + ffi.cdef("struct foo_s { int x; ...; }; struct foo_s f(int);") + lib = verify(ffi, "test_incomplete_struct_as_result", + "struct foo_s { int a, x, z; };\n" + "struct foo_s f(int x) { struct foo_s r; r.x = x * 2; return r; }") + s = lib.f(21) + assert s.x == 42 + assert ffi.typeof(lib.f) == ffi.typeof("struct foo_s(*)(int)") + +def test_incomplete_struct_as_both(): + ffi = FFI() + ffi.cdef("struct foo_s { int x; ...; }; struct bar_s { int y; ...; };\n" + "struct foo_s f(int, struct bar_s);") + lib = verify(ffi, "test_incomplete_struct_as_both", + "struct foo_s { int a, x, z; };\n" + "struct bar_s { int b, c, y, d; };\n" + "struct foo_s f(int x, struct bar_s b) {\n" + " struct foo_s r; r.x = x * b.y; return r;\n" + "}") + b = ffi.new("struct bar_s *", [7]) + s = lib.f(6, b[0]) + assert s.x == 42 + assert ffi.typeof(lib.f) == ffi.typeof( + "struct foo_s(*)(int, struct bar_s)") + s = lib.f(14, {'y': -3}) + assert s.x == -42 + +def test_name_of_unnamed_struct(): + ffi = FFI() + ffi.cdef("typedef struct { int x; } foo_t;\n" + "typedef struct { int y; } *bar_p;\n" + "typedef struct { int y; } **baz_pp;\n") + verify(ffi, "test_name_of_unnamed_struct", + "typedef struct { int x; } foo_t;\n" + "typedef struct { int y; } *bar_p;\n" + "typedef struct { int y; } **baz_pp;\n") + assert repr(ffi.typeof("foo_t")) == "<ctype 'foo_t'>" + assert repr(ffi.typeof("bar_p")) == "<ctype 'struct $1 *'>" + assert repr(ffi.typeof("baz_pp")) == "<ctype 'struct $2 * *'>" + +def test_address_of_global_var(): + ffi = FFI() + ffi.cdef(""" + long bottom, bottoms[2]; + long FetchRectBottom(void); + long FetchRectBottoms1(void); + #define FOOBAR 42 + """) + lib = verify(ffi, "test_address_of_global_var", """ + long bottom, bottoms[2]; + long FetchRectBottom(void) { return bottom; } + long FetchRectBottoms1(void) { return bottoms[1]; } + #define FOOBAR 42 + """) + lib.bottom = 300 + assert lib.FetchRectBottom() == 300 + lib.bottom += 1 + assert lib.FetchRectBottom() == 301 + lib.bottoms[1] = 500 + assert lib.FetchRectBottoms1() == 500 + lib.bottoms[1] += 2 + assert lib.FetchRectBottoms1() == 502 + # + p = ffi.addressof(lib, 'bottom') + assert ffi.typeof(p) == ffi.typeof("long *") + assert p[0] == 301 + p[0] += 1 + assert lib.FetchRectBottom() == 302 + p = ffi.addressof(lib, 'bottoms') + assert ffi.typeof(p) == ffi.typeof("long(*)[2]") + assert p[0] == lib.bottoms + # + py.test.raises(AttributeError, ffi.addressof, lib, 'unknown_var') + py.test.raises(AttributeError, ffi.addressof, lib, "FOOBAR") + +def test_defines__CFFI_(): + # Check that we define the macro _CFFI_ automatically. + # It should be done before including Python.h, so that PyPy's Python.h + # can check for it. + ffi = FFI() + ffi.cdef(""" + #define CORRECT 1 + """) + lib = verify(ffi, "test_defines__CFFI_", """ + #ifdef _CFFI_ + # define CORRECT 1 + #endif + """) + assert lib.CORRECT == 1 + +def test_unpack_args(): + ffi = FFI() + ffi.cdef("void foo0(void); void foo1(int); void foo2(int, int);") + lib = verify(ffi, "test_unpack_args", """ + void foo0(void) { } + void foo1(int x) { } + void foo2(int x, int y) { } + """) + assert 'foo0' in repr(lib.foo0) + assert 'foo1' in repr(lib.foo1) + assert 'foo2' in repr(lib.foo2) + lib.foo0() + lib.foo1(42) + lib.foo2(43, 44) + e1 = py.test.raises(TypeError, lib.foo0, 42) + e2 = py.test.raises(TypeError, lib.foo0, 43, 44) + e3 = py.test.raises(TypeError, lib.foo1) + e4 = py.test.raises(TypeError, lib.foo1, 43, 44) + e5 = py.test.raises(TypeError, lib.foo2) + e6 = py.test.raises(TypeError, lib.foo2, 42) + e7 = py.test.raises(TypeError, lib.foo2, 45, 46, 47) + assert str(e1.value) == "foo0() takes no arguments (1 given)" + assert str(e2.value) == "foo0() takes no arguments (2 given)" + assert str(e3.value) == "foo1() takes exactly one argument (0 given)" + assert str(e4.value) == "foo1() takes exactly one argument (2 given)" + assert str(e5.value) in ["foo2 expected 2 arguments, got 0", + "foo2() takes exactly 2 arguments (0 given)"] + assert str(e6.value) in ["foo2 expected 2 arguments, got 1", + "foo2() takes exactly 2 arguments (1 given)"] + assert str(e7.value) in ["foo2 expected 2 arguments, got 3", + "foo2() takes exactly 2 arguments (3 given)"] + +def test_address_of_function(): + ffi = FFI() + ffi.cdef("long myfunc(long x);") + lib = verify(ffi, "test_addressof_function", """ + char myfunc(char x) { return (char)(x + 42); } + """) + assert lib.myfunc(5) == 47 + assert lib.myfunc(0xABC05) == 47 + assert not isinstance(lib.myfunc, ffi.CData) + assert ffi.typeof(lib.myfunc) == ffi.typeof("long(*)(long)") + addr = ffi.addressof(lib, 'myfunc') + assert addr(5) == 47 + assert addr(0xABC05) == 47 + assert isinstance(addr, ffi.CData) + assert ffi.typeof(addr) == ffi.typeof("long(*)(long)") + +def test_address_of_function_with_struct(): + ffi = FFI() + ffi.cdef("struct foo_s { int x; }; long myfunc(struct foo_s);") + lib = verify(ffi, "test_addressof_function_with_struct", """ + struct foo_s { int x; }; + char myfunc(struct foo_s input) { return (char)(input.x + 42); } + """) + s = ffi.new("struct foo_s *", [5])[0] + assert lib.myfunc(s) == 47 + assert not isinstance(lib.myfunc, ffi.CData) + assert ffi.typeof(lib.myfunc) == ffi.typeof("long(*)(struct foo_s)") + addr = ffi.addressof(lib, 'myfunc') + assert addr(s) == 47 + assert isinstance(addr, ffi.CData) + assert ffi.typeof(addr) == ffi.typeof("long(*)(struct foo_s)") + +def test_issue198(): + ffi = FFI() + ffi.cdef(""" + typedef struct{...;} opaque_t; + const opaque_t CONSTANT; + int toint(opaque_t); + """) + lib = verify(ffi, 'test_issue198', """ + typedef int opaque_t; + #define CONSTANT ((opaque_t)42) + static int toint(opaque_t o) { return o; } + """) + def random_stuff(): + pass + assert lib.toint(lib.CONSTANT) == 42 + random_stuff() + assert lib.toint(lib.CONSTANT) == 42 + +def test_constant_is_not_a_compiler_constant(): + ffi = FFI() + ffi.cdef("static const float almost_forty_two;") + lib = verify(ffi, 'test_constant_is_not_a_compiler_constant', """ + static float f(void) { return 42.25; } + #define almost_forty_two (f()) + """) + assert lib.almost_forty_two == 42.25 + +def test_constant_of_unknown_size(): + ffi = FFI() + ffi.cdef(""" + typedef ... opaque_t; + const opaque_t CONSTANT; + """) + lib = verify(ffi, 'test_constant_of_unknown_size', + "typedef int opaque_t;" + "const int CONSTANT = 42;") + e = py.test.raises(ffi.error, getattr, lib, 'CONSTANT') + assert str(e.value) == ("constant 'CONSTANT' is of " + "type 'opaque_t', whose size is not known") + +def test_variable_of_unknown_size(): + ffi = FFI() + ffi.cdef(""" + typedef ... opaque_t; + opaque_t globvar; + """) + lib = verify(ffi, 'test_variable_of_unknown_size', """ + typedef char opaque_t[6]; + opaque_t globvar = "hello"; + """) + # can't read or write it at all + e = py.test.raises(TypeError, getattr, lib, 'globvar') + assert str(e.value) in ["cdata 'opaque_t' is opaque", + "'opaque_t' is opaque or not completed yet"] #pypy + e = py.test.raises(TypeError, setattr, lib, 'globvar', []) + assert str(e.value) in ["'opaque_t' is opaque", + "'opaque_t' is opaque or not completed yet"] #pypy + # but we can get its address + p = ffi.addressof(lib, 'globvar') + assert ffi.typeof(p) == ffi.typeof('opaque_t *') + assert ffi.string(ffi.cast("char *", p), 8) == b"hello" + +def test_constant_of_value_unknown_to_the_compiler(): + extra_c_source = udir.join( + 'extra_test_constant_of_value_unknown_to_the_compiler.c') + extra_c_source.write('const int external_foo = 42;\n') + ffi = FFI() + ffi.cdef("const int external_foo;") + lib = verify(ffi, 'test_constant_of_value_unknown_to_the_compiler', """ + extern const int external_foo; + """, sources=[str(extra_c_source)]) + assert lib.external_foo == 42 + +def test_dotdot_in_source_file_names(): + extra_c_source = udir.join( + 'extra_test_dotdot_in_source_file_names.c') + extra_c_source.write('const int external_foo = 42;\n') + ffi = FFI() + ffi.cdef("const int external_foo;") + lib = verify(ffi, 'test_dotdot_in_source_file_names', """ + extern const int external_foo; + """, sources=[os.path.join(os.path.dirname(str(extra_c_source)), + 'foobar', '..', + os.path.basename(str(extra_c_source)))]) + assert lib.external_foo == 42 + +def test_call_with_incomplete_structs(): + ffi = FFI() + ffi.cdef("typedef struct {...;} foo_t; " + "foo_t myglob; " + "foo_t increment(foo_t s); " + "double getx(foo_t s);") + lib = verify(ffi, 'test_call_with_incomplete_structs', """ + typedef double foo_t; + double myglob = 42.5; + double getx(double x) { return x; } + double increment(double x) { return x + 1; } + """) + assert lib.getx(lib.myglob) == 42.5 + assert lib.getx(lib.increment(lib.myglob)) == 43.5 + +def test_struct_array_guess_length_2(): + ffi = FFI() + ffi.cdef("struct foo_s { int a[...][...]; };") + lib = verify(ffi, 'test_struct_array_guess_length_2', + "struct foo_s { int x; int a[5][8]; int y; };") + assert ffi.sizeof('struct foo_s') == 42 * ffi.sizeof('int') + s = ffi.new("struct foo_s *") + assert ffi.typeof(s.a) == ffi.typeof("int[5][8]") + assert ffi.sizeof(s.a) == 40 * ffi.sizeof('int') + assert s.a[4][7] == 0 + py.test.raises(IndexError, 's.a[4][8]') + py.test.raises(IndexError, 's.a[5][0]') + assert ffi.typeof(s.a) == ffi.typeof("int[5][8]") + assert ffi.typeof(s.a[0]) == ffi.typeof("int[8]") + +def test_struct_array_guess_length_3(): + ffi = FFI() + ffi.cdef("struct foo_s { int a[][...]; };") + lib = verify(ffi, 'test_struct_array_guess_length_3', + "struct foo_s { int x; int a[5][7]; int y; };") + assert ffi.sizeof('struct foo_s') == 37 * ffi.sizeof('int') + s = ffi.new("struct foo_s *") + assert ffi.typeof(s.a) == ffi.typeof("int[][7]") + assert s.a[4][6] == 0 + py.test.raises(IndexError, 's.a[4][7]') + assert ffi.typeof(s.a[0]) == ffi.typeof("int[7]") + +def test_global_var_array_2(): + ffi = FFI() + ffi.cdef("int a[...][...];") + lib = verify(ffi, 'test_global_var_array_2', 'int a[10][8];') + lib.a[9][7] = 123456 + assert lib.a[9][7] == 123456 + py.test.raises(IndexError, 'lib.a[0][8]') + py.test.raises(IndexError, 'lib.a[10][0]') + assert ffi.typeof(lib.a) == ffi.typeof("int[10][8]") + assert ffi.typeof(lib.a[0]) == ffi.typeof("int[8]") + +def test_global_var_array_3(): + ffi = FFI() + ffi.cdef("int a[][...];") + lib = verify(ffi, 'test_global_var_array_3', 'int a[10][8];') + lib.a[9][7] = 123456 + assert lib.a[9][7] == 123456 + py.test.raises(IndexError, 'lib.a[0][8]') + assert ffi.typeof(lib.a) == ffi.typeof("int(*)[8]") + assert ffi.typeof(lib.a[0]) == ffi.typeof("int[8]") + +def test_global_var_array_4(): + ffi = FFI() + ffi.cdef("int a[10][...];") + lib = verify(ffi, 'test_global_var_array_4', 'int a[10][8];') + lib.a[9][7] = 123456 + assert lib.a[9][7] == 123456 + py.test.raises(IndexError, 'lib.a[0][8]') + py.test.raises(IndexError, 'lib.a[10][8]') + assert ffi.typeof(lib.a) == ffi.typeof("int[10][8]") + assert ffi.typeof(lib.a[0]) == ffi.typeof("int[8]") + +def test_some_integer_type(): + ffi = FFI() + ffi.cdef(""" + typedef int... foo_t; + typedef unsigned long... bar_t; + typedef struct { foo_t a, b; } mystruct_t; + foo_t foobar(bar_t, mystruct_t); + static const bar_t mu = -20; + static const foo_t nu = 20; + """) + lib = verify(ffi, 'test_some_integer_type', """ + typedef unsigned long long foo_t; + typedef short bar_t; + typedef struct { foo_t a, b; } mystruct_t; + static foo_t foobar(bar_t x, mystruct_t s) { + return (foo_t)x + s.a + s.b; + } + static const bar_t mu = -20; + static const foo_t nu = 20; + """) + assert ffi.sizeof("foo_t") == ffi.sizeof("unsigned long long") + assert ffi.sizeof("bar_t") == ffi.sizeof("short") + maxulonglong = 2 ** 64 - 1 + assert int(ffi.cast("foo_t", -1)) == maxulonglong + assert int(ffi.cast("bar_t", -1)) == -1 + assert lib.foobar(-1, [0, 0]) == maxulonglong + assert lib.foobar(2 ** 15 - 1, [0, 0]) == 2 ** 15 - 1 + assert lib.foobar(10, [20, 31]) == 61 + assert lib.foobar(0, [0, maxulonglong]) == maxulonglong + py.test.raises(OverflowError, lib.foobar, 2 ** 15, [0, 0]) + py.test.raises(OverflowError, lib.foobar, -(2 ** 15) - 1, [0, 0]) + py.test.raises(OverflowError, ffi.new, "mystruct_t *", [0, -1]) + assert lib.mu == -20 + assert lib.nu == 20 + +def test_some_float_type(): + ffi = FFI() + ffi.cdef(""" + typedef double... foo_t; + typedef float... bar_t; + foo_t sum(foo_t[]); + bar_t neg(bar_t); + """) + lib = verify(ffi, 'test_some_float_type', """ + typedef float foo_t; + static foo_t sum(foo_t x[]) { return x[0] + x[1]; } + typedef double bar_t; + static double neg(double x) { return -x; } + """) + assert lib.sum([40.0, 2.25]) == 42.25 + assert lib.sum([12.3, 45.6]) != 12.3 + 45.6 # precision loss + assert lib.neg(12.3) == -12.3 # no precision loss + assert ffi.sizeof("foo_t") == ffi.sizeof("float") + assert ffi.sizeof("bar_t") == ffi.sizeof("double") + +def test_some_float_invalid_1(): + ffi = FFI() + py.test.raises((FFIError, # with pycparser <= 2.17 + CDefError), # with pycparser >= 2.18 + ffi.cdef, "typedef long double... foo_t;") + +def test_some_float_invalid_2(): + ffi = FFI() + ffi.cdef("typedef double... foo_t; foo_t neg(foo_t);") + lib = verify(ffi, 'test_some_float_invalid_2', """ + typedef unsigned long foo_t; + foo_t neg(foo_t x) { return -x; } + """) + e = py.test.raises(ffi.error, getattr, lib, 'neg') + assert str(e.value) == ("primitive floating-point type with an unexpected " + "size (or not a float type at all)") + +def test_some_float_invalid_3(): + ffi = FFI() + ffi.cdef("typedef double... foo_t; foo_t neg(foo_t);") + lib = verify(ffi, 'test_some_float_invalid_3', """ + typedef long double foo_t; + foo_t neg(foo_t x) { return -x; } + """) + if ffi.sizeof("long double") == ffi.sizeof("double"): + assert lib.neg(12.3) == -12.3 + else: + e = py.test.raises(ffi.error, getattr, lib, 'neg') + assert str(e.value) == ("primitive floating-point type is " + "'long double', not supported for now with " + "the syntax 'typedef double... xxx;'") + +def test_issue200(): + ffi = FFI() + ffi.cdef(""" + typedef void (function_t)(void*); + void function(void *); + """) + lib = verify(ffi, 'test_issue200', """ + static void function(void *p) { (void)p; } + """) + ffi.typeof('function_t*') + lib.function(ffi.NULL) + # assert did not crash + +def test_alignment_of_longlong(): + ffi = FFI() + x1 = ffi.alignof('unsigned long long') + assert x1 in [4, 8] + ffi.cdef("struct foo_s { unsigned long long x; };") + lib = verify(ffi, 'test_alignment_of_longlong', + "struct foo_s { unsigned long long x; };") + assert ffi.alignof('unsigned long long') == x1 + assert ffi.alignof('struct foo_s') == x1 + +def test_import_from_lib(): + ffi = FFI() + ffi.cdef("int mybar(int); int myvar;\n#define MYFOO ...") + lib = verify(ffi, 'test_import_from_lib', + "#define MYFOO 42\n" + "static int mybar(int x) { return x + 1; }\n" + "static int myvar = -5;") + assert sys.modules['_CFFI_test_import_from_lib'].lib is lib + assert sys.modules['_CFFI_test_import_from_lib.lib'] is lib + from _CFFI_test_import_from_lib.lib import MYFOO + assert MYFOO == 42 + assert hasattr(lib, '__dict__') + assert lib.__all__ == ['MYFOO', 'mybar'] # but not 'myvar' + assert lib.__name__ == '_CFFI_test_import_from_lib.lib' + assert lib.__class__ is type(sys) # !! hack for help() + +def test_macro_var_callback(): + ffi = FFI() + ffi.cdef("int my_value; int *(*get_my_value)(void);") + lib = verify(ffi, 'test_macro_var_callback', + "int *(*get_my_value)(void);\n" + "#define my_value (*get_my_value())") + # + values = ffi.new("int[50]") + def it(): + for i in range(50): + yield i + it = it() + # + @ffi.callback("int *(*)(void)") + def get_my_value(): + for nextvalue in it: + return values + nextvalue + lib.get_my_value = get_my_value + # + values[0] = 41 + assert lib.my_value == 41 # [0] + p = ffi.addressof(lib, 'my_value') # [1] + assert p == values + 1 + assert p[-1] == 41 + assert p[+1] == 0 + lib.my_value = 42 # [2] + assert values[2] == 42 + assert p[-1] == 41 + assert p[+1] == 42 + # + # if get_my_value raises or returns nonsense, the exception is printed + # to stderr like with any callback, but then the C expression 'my_value' + # expand to '*NULL'. We assume here that '&my_value' will return NULL + # without segfaulting, and check for NULL when accessing the variable. + @ffi.callback("int *(*)(void)") + def get_my_value(): + raise LookupError + lib.get_my_value = get_my_value + py.test.raises(ffi.error, getattr, lib, 'my_value') + py.test.raises(ffi.error, setattr, lib, 'my_value', 50) + py.test.raises(ffi.error, ffi.addressof, lib, 'my_value') + @ffi.callback("int *(*)(void)") + def get_my_value(): + return "hello" + lib.get_my_value = get_my_value + py.test.raises(ffi.error, getattr, lib, 'my_value') + e = py.test.raises(ffi.error, setattr, lib, 'my_value', 50) + assert str(e.value) == "global variable 'my_value' is at address NULL" + +def test_const_fields(): + ffi = FFI() + ffi.cdef("""struct foo_s { const int a; void *const b; };""") + lib = verify(ffi, 'test_const_fields', """ + struct foo_s { const int a; void *const b; };""") + foo_s = ffi.typeof("struct foo_s") + assert foo_s.fields[0][0] == 'a' + assert foo_s.fields[0][1].type is ffi.typeof("int") + assert foo_s.fields[1][0] == 'b' + assert foo_s.fields[1][1].type is ffi.typeof("void *") + +def test_restrict_fields(): + ffi = FFI() + ffi.cdef("""struct foo_s { void * restrict b; };""") + lib = verify(ffi, 'test_restrict_fields', """ + struct foo_s { void * __restrict b; };""") + foo_s = ffi.typeof("struct foo_s") + assert foo_s.fields[0][0] == 'b' + assert foo_s.fields[0][1].type is ffi.typeof("void *") + +def test_volatile_fields(): + ffi = FFI() + ffi.cdef("""struct foo_s { void * volatile b; };""") + lib = verify(ffi, 'test_volatile_fields', """ + struct foo_s { void * volatile b; };""") + foo_s = ffi.typeof("struct foo_s") + assert foo_s.fields[0][0] == 'b' + assert foo_s.fields[0][1].type is ffi.typeof("void *") + +def test_const_array_fields(): + ffi = FFI() + ffi.cdef("""struct foo_s { const int a[4]; };""") + lib = verify(ffi, 'test_const_array_fields', """ + struct foo_s { const int a[4]; };""") + foo_s = ffi.typeof("struct foo_s") + assert foo_s.fields[0][0] == 'a' + assert foo_s.fields[0][1].type is ffi.typeof("int[4]") + +def test_const_array_fields_varlength(): + ffi = FFI() + ffi.cdef("""struct foo_s { const int a[]; ...; };""") + lib = verify(ffi, 'test_const_array_fields_varlength', """ + struct foo_s { const int a[4]; };""") + foo_s = ffi.typeof("struct foo_s") + assert foo_s.fields[0][0] == 'a' + assert foo_s.fields[0][1].type is ffi.typeof("int[]") + +def test_const_array_fields_unknownlength(): + ffi = FFI() + ffi.cdef("""struct foo_s { const int a[...]; ...; };""") + lib = verify(ffi, 'test_const_array_fields_unknownlength', """ + struct foo_s { const int a[4]; };""") + foo_s = ffi.typeof("struct foo_s") + assert foo_s.fields[0][0] == 'a' + assert foo_s.fields[0][1].type is ffi.typeof("int[4]") + +def test_const_function_args(): + ffi = FFI() + ffi.cdef("""int foobar(const int a, const int *b, const int c[]);""") + lib = verify(ffi, 'test_const_function_args', """ + int foobar(const int a, const int *b, const int c[]) { + return a + *b + *c; + } + """) + assert lib.foobar(100, ffi.new("int *", 40), ffi.new("int *", 2)) == 142 + +def test_const_function_type_args(): + ffi = FFI() + ffi.cdef("""int (*foobar)(const int a, const int *b, const int c[]);""") + lib = verify(ffi, 'test_const_function_type_args', """ + int (*foobar)(const int a, const int *b, const int c[]); + """) + t = ffi.typeof(lib.foobar) + assert t.args[0] is ffi.typeof("int") + assert t.args[1] is ffi.typeof("int *") + assert t.args[2] is ffi.typeof("int *") + +def test_const_constant(): + ffi = FFI() + ffi.cdef("""struct foo_s { int x,y; }; const struct foo_s myfoo;""") + lib = verify(ffi, 'test_const_constant', """ + struct foo_s { int x,y; }; const struct foo_s myfoo = { 40, 2 }; + """) + assert lib.myfoo.x == 40 + assert lib.myfoo.y == 2 + +def test_const_via_typedef(): + ffi = FFI() + ffi.cdef("""typedef const int const_t; const_t aaa;""") + lib = verify(ffi, 'test_const_via_typedef', """ + typedef const int const_t; + #define aaa 42 + """) + assert lib.aaa == 42 + py.test.raises(AttributeError, "lib.aaa = 43") + +def test_win32_calling_convention_0(): + ffi = FFI() + ffi.cdef(""" + int call1(int(__cdecl *cb)(int)); + int (*const call2)(int(__stdcall *cb)(int)); + """) + lib = verify(ffi, 'test_win32_calling_convention_0', r""" + #ifndef _MSC_VER + # define __stdcall /* nothing */ + #endif + int call1(int(*cb)(int)) { + int i, result = 0; + //printf("call1: cb = %p\n", cb); + for (i = 0; i < 1000; i++) + result += cb(i); + //printf("result = %d\n", result); + return result; + } + int call2(int(__stdcall *cb)(int)) { + int i, result = 0; + //printf("call2: cb = %p\n", cb); + for (i = 0; i < 1000; i++) + result += cb(-i); + //printf("result = %d\n", result); + return result; + } + """) + @ffi.callback("int(int)") + def cb1(x): + return x * 2 + @ffi.callback("int __stdcall(int)") + def cb2(x): + return x * 3 + res = lib.call1(cb1) + assert res == 500*999*2 + assert res == ffi.addressof(lib, 'call1')(cb1) + res = lib.call2(cb2) + assert res == -500*999*3 + assert res == ffi.addressof(lib, 'call2')(cb2) + if sys.platform == 'win32' and not sys.maxsize > 2**32: + assert '__stdcall' in str(ffi.typeof(cb2)) + assert '__stdcall' not in str(ffi.typeof(cb1)) + py.test.raises(TypeError, lib.call1, cb2) + py.test.raises(TypeError, lib.call2, cb1) + else: + assert '__stdcall' not in str(ffi.typeof(cb2)) + assert ffi.typeof(cb2) is ffi.typeof(cb1) + +def test_win32_calling_convention_1(): + ffi = FFI() + ffi.cdef(""" + int __cdecl call1(int(__cdecl *cb)(int)); + int __stdcall call2(int(__stdcall *cb)(int)); + int (__cdecl *const cb1)(int); + int (__stdcall *const cb2)(int); + """) + lib = verify(ffi, 'test_win32_calling_convention_1', r""" + #ifndef _MSC_VER + # define __cdecl + # define __stdcall + #endif + int __cdecl cb1(int x) { return x * 2; } + int __stdcall cb2(int x) { return x * 3; } + + int __cdecl call1(int(__cdecl *cb)(int)) { + int i, result = 0; + //printf("here1\n"); + //printf("cb = %p, cb1 = %p\n", cb, (void *)cb1); + for (i = 0; i < 1000; i++) + result += cb(i); + //printf("result = %d\n", result); + return result; + } + int __stdcall call2(int(__stdcall *cb)(int)) { + int i, result = 0; + //printf("here1\n"); + //printf("cb = %p, cb2 = %p\n", cb, (void *)cb2); + for (i = 0; i < 1000; i++) + result += cb(-i); + //printf("result = %d\n", result); + return result; + } + """) + #print '<<< cb1 =', ffi.addressof(lib, 'cb1') + ptr_call1 = ffi.addressof(lib, 'call1') + assert lib.call1(ffi.addressof(lib, 'cb1')) == 500*999*2 + assert ptr_call1(ffi.addressof(lib, 'cb1')) == 500*999*2 + #print '<<< cb2 =', ffi.addressof(lib, 'cb2') + ptr_call2 = ffi.addressof(lib, 'call2') + assert lib.call2(ffi.addressof(lib, 'cb2')) == -500*999*3 + assert ptr_call2(ffi.addressof(lib, 'cb2')) == -500*999*3 + #print '<<< done' + +def test_win32_calling_convention_2(): + # any mistake in the declaration of plain function (including the + # precise argument types and, here, the calling convention) are + # automatically corrected. But this does not apply to the 'cb' + # function pointer argument. + ffi = FFI() + ffi.cdef(""" + int __stdcall call1(int(__cdecl *cb)(int)); + int __cdecl call2(int(__stdcall *cb)(int)); + int (__cdecl *const cb1)(int); + int (__stdcall *const cb2)(int); + """) + lib = verify(ffi, 'test_win32_calling_convention_2', """ + #ifndef _MSC_VER + # define __cdecl + # define __stdcall + #endif + int __cdecl call1(int(__cdecl *cb)(int)) { + int i, result = 0; + for (i = 0; i < 1000; i++) + result += cb(i); + return result; + } + int __stdcall call2(int(__stdcall *cb)(int)) { + int i, result = 0; + for (i = 0; i < 1000; i++) + result += cb(-i); + return result; + } + int __cdecl cb1(int x) { return x * 2; } + int __stdcall cb2(int x) { return x * 3; } + """) + ptr_call1 = ffi.addressof(lib, 'call1') + ptr_call2 = ffi.addressof(lib, 'call2') + if sys.platform == 'win32' and not sys.maxsize > 2**32: + py.test.raises(TypeError, lib.call1, ffi.addressof(lib, 'cb2')) + py.test.raises(TypeError, ptr_call1, ffi.addressof(lib, 'cb2')) + py.test.raises(TypeError, lib.call2, ffi.addressof(lib, 'cb1')) + py.test.raises(TypeError, ptr_call2, ffi.addressof(lib, 'cb1')) + assert lib.call1(ffi.addressof(lib, 'cb1')) == 500*999*2 + assert ptr_call1(ffi.addressof(lib, 'cb1')) == 500*999*2 + assert lib.call2(ffi.addressof(lib, 'cb2')) == -500*999*3 + assert ptr_call2(ffi.addressof(lib, 'cb2')) == -500*999*3 + +def test_win32_calling_convention_3(): + ffi = FFI() + ffi.cdef(""" + struct point { int x, y; }; + + int (*const cb1)(struct point); + int (__stdcall *const cb2)(struct point); + + struct point __stdcall call1(int(*cb)(struct point)); + struct point call2(int(__stdcall *cb)(struct point)); + """) + lib = verify(ffi, 'test_win32_calling_convention_3', r""" + #ifndef _MSC_VER + # define __cdecl + # define __stdcall + #endif + struct point { int x, y; }; + int cb1(struct point pt) { return pt.x + 10 * pt.y; } + int __stdcall cb2(struct point pt) { return pt.x + 100 * pt.y; } + struct point __stdcall call1(int(__cdecl *cb)(struct point)) { + int i; + struct point result = { 0, 0 }; + //printf("here1\n"); + //printf("cb = %p, cb1 = %p\n", cb, (void *)cb1); + for (i = 0; i < 1000; i++) { + struct point p = { i, -i }; + int r = cb(p); + result.x += r; + result.y -= r; + } + return result; + } + struct point __cdecl call2(int(__stdcall *cb)(struct point)) { + int i; + struct point result = { 0, 0 }; + for (i = 0; i < 1000; i++) { + struct point p = { -i, i }; + int r = cb(p); + result.x += r; + result.y -= r; + } + return result; + } + """) + ptr_call1 = ffi.addressof(lib, 'call1') + ptr_call2 = ffi.addressof(lib, 'call2') + if sys.platform == 'win32' and not sys.maxsize > 2**32: + py.test.raises(TypeError, lib.call1, ffi.addressof(lib, 'cb2')) + py.test.raises(TypeError, ptr_call1, ffi.addressof(lib, 'cb2')) + py.test.raises(TypeError, lib.call2, ffi.addressof(lib, 'cb1')) + py.test.raises(TypeError, ptr_call2, ffi.addressof(lib, 'cb1')) + pt = lib.call1(ffi.addressof(lib, 'cb1')) + assert (pt.x, pt.y) == (-9*500*999, 9*500*999) + pt = ptr_call1(ffi.addressof(lib, 'cb1')) + assert (pt.x, pt.y) == (-9*500*999, 9*500*999) + pt = lib.call2(ffi.addressof(lib, 'cb2')) + assert (pt.x, pt.y) == (99*500*999, -99*500*999) + pt = ptr_call2(ffi.addressof(lib, 'cb2')) + assert (pt.x, pt.y) == (99*500*999, -99*500*999) + +def test_extern_python_1(): + import warnings + ffi = FFI() + with warnings.catch_warnings(record=True) as log: + ffi.cdef(""" + extern "Python" { + int bar(int, int); + void baz(int, int); + int bok(void); + void boz(void); + } + """) + assert len(log) == 0, "got a warning: %r" % (log,) + lib = verify(ffi, 'test_extern_python_1', """ + static void baz(int, int); /* forward */ + """) + assert ffi.typeof(lib.bar) == ffi.typeof("int(*)(int, int)") + with FdWriteCapture() as f: + res = lib.bar(4, 5) + assert res == 0 + assert f.getvalue() == ( + b"extern \"Python\": function _CFFI_test_extern_python_1.bar() called, " + b"but no code was attached " + b"to it yet with @ffi.def_extern(). Returning 0.\n") + + @ffi.def_extern("bar") + def my_bar(x, y): + seen.append(("Bar", x, y)) + return x * y + assert my_bar != lib.bar + seen = [] + res = lib.bar(6, 7) + assert seen == [("Bar", 6, 7)] + assert res == 42 + + def baz(x, y): + seen.append(("Baz", x, y)) + baz1 = ffi.def_extern()(baz) + assert baz1 is baz + seen = [] + baz(long(40), long(4)) + res = lib.baz(long(50), long(8)) + assert res is None + assert seen == [("Baz", 40, 4), ("Baz", 50, 8)] + assert type(seen[0][1]) is type(seen[0][2]) is long + assert type(seen[1][1]) is type(seen[1][2]) is int + + @ffi.def_extern(name="bok") + def bokk(): + seen.append("Bok") + return 42 + seen = [] + assert lib.bok() == 42 + assert seen == ["Bok"] + + @ffi.def_extern() + def boz(): + seen.append("Boz") + seen = [] + assert lib.boz() is None + assert seen == ["Boz"] + +def test_extern_python_bogus_name(): + ffi = FFI() + ffi.cdef("int abc;") + lib = verify(ffi, 'test_extern_python_bogus_name', "int abc;") + def fn(): + pass + py.test.raises(ffi.error, ffi.def_extern("unknown_name"), fn) + py.test.raises(ffi.error, ffi.def_extern("abc"), fn) + assert lib.abc == 0 + e = py.test.raises(ffi.error, ffi.def_extern("abc"), fn) + assert str(e.value) == ("ffi.def_extern('abc'): no 'extern \"Python\"' " + "function with this name") + e = py.test.raises(ffi.error, ffi.def_extern(), fn) + assert str(e.value) == ("ffi.def_extern('fn'): no 'extern \"Python\"' " + "function with this name") + # + py.test.raises(TypeError, ffi.def_extern(42), fn) + py.test.raises((TypeError, AttributeError), ffi.def_extern(), "foo") + class X: + pass + x = X() + x.__name__ = x + py.test.raises(TypeError, ffi.def_extern(), x) + +def test_extern_python_bogus_result_type(): + ffi = FFI() + ffi.cdef("""extern "Python" void bar(int);""") + lib = verify(ffi, 'test_extern_python_bogus_result_type', "") + # + @ffi.def_extern() + def bar(n): + return n * 10 + with StdErrCapture() as f: + res = lib.bar(321) + assert res is None + assert f.getvalue() == ( + "From cffi callback %r:\n" % (bar,) + + "Trying to convert the result back to C:\n" + "TypeError: callback with the return type 'void' must return None\n") + +def test_extern_python_redefine(): + ffi = FFI() + ffi.cdef("""extern "Python" int bar(int);""") + lib = verify(ffi, 'test_extern_python_redefine', "") + # + @ffi.def_extern() + def bar(n): + return n * 10 + assert lib.bar(42) == 420 + # + @ffi.def_extern() + def bar(n): + return -n + assert lib.bar(42) == -42 + +def test_extern_python_struct(): + ffi = FFI() + ffi.cdef(""" + struct foo_s { int a, b, c; }; + extern "Python" int bar(int, struct foo_s, int); + extern "Python" { struct foo_s baz(int, int); + struct foo_s bok(void); } + """) + lib = verify(ffi, 'test_extern_python_struct', + "struct foo_s { int a, b, c; };") + # + @ffi.def_extern() + def bar(x, s, z): + return x + s.a + s.b + s.c + z + res = lib.bar(1000, [1001, 1002, 1004], 1008) + assert res == 5015 + # + @ffi.def_extern() + def baz(x, y): + return [x + y, x - y, x * y] + res = lib.baz(1000, 42) + assert res.a == 1042 + assert res.b == 958 + assert res.c == 42000 + # + @ffi.def_extern() + def bok(): + return [10, 20, 30] + res = lib.bok() + assert [res.a, res.b, res.c] == [10, 20, 30] + +def test_extern_python_long_double(): + ffi = FFI() + ffi.cdef(""" + extern "Python" int bar(int, long double, int); + extern "Python" long double baz(int, int); + extern "Python" long double bok(void); + """) + lib = verify(ffi, 'test_extern_python_long_double', "") + # + @ffi.def_extern() + def bar(x, l, z): + seen.append((x, l, z)) + return 6 + seen = [] + lib.bar(10, 3.5, 20) + expected = ffi.cast("long double", 3.5) + assert repr(seen) == repr([(10, expected, 20)]) + # + @ffi.def_extern() + def baz(x, z): + assert x == 10 and z == 20 + return expected + res = lib.baz(10, 20) + assert repr(res) == repr(expected) + # + @ffi.def_extern() + def bok(): + return expected + res = lib.bok() + assert repr(res) == repr(expected) + +def test_extern_python_signature(): + ffi = FFI() + lib = verify(ffi, 'test_extern_python_signature', "") + py.test.raises(TypeError, ffi.def_extern(425), None) + py.test.raises(TypeError, ffi.def_extern, 'a', 'b', 'c', 'd') + +def test_extern_python_errors(): + ffi = FFI() + ffi.cdef(""" + extern "Python" int bar(int); + """) + lib = verify(ffi, 'test_extern_python_errors', "") + + seen = [] + def oops(*args): + seen.append(args) + + @ffi.def_extern(onerror=oops) + def bar(x): + return x + "" + assert lib.bar(10) == 0 + + @ffi.def_extern(name="bar", onerror=oops, error=-66) + def bar2(x): + return x + "" + assert lib.bar(10) == -66 + + assert len(seen) == 2 + exc, val, tb = seen[0] + assert exc is TypeError + assert isinstance(val, TypeError) + assert tb.tb_frame.f_code.co_name == "bar" + exc, val, tb = seen[1] + assert exc is TypeError + assert isinstance(val, TypeError) + assert tb.tb_frame.f_code.co_name == "bar2" + # + # a case where 'onerror' is not callable + py.test.raises(TypeError, ffi.def_extern(name='bar', onerror=42), + lambda x: x) + +def test_extern_python_stdcall(): + ffi = FFI() + ffi.cdef(""" + extern "Python" int __stdcall foo(int); + extern "Python" int WINAPI bar(int); + int (__stdcall * mycb1)(int); + int indirect_call(int); + """) + lib = verify(ffi, 'test_extern_python_stdcall', """ + #ifndef _MSC_VER + # define __stdcall + #endif + static int (__stdcall * mycb1)(int); + static int indirect_call(int x) { + return mycb1(x); + } + """) + # + @ffi.def_extern() + def foo(x): + return x + 42 + @ffi.def_extern() + def bar(x): + return x + 43 + assert lib.foo(100) == 142 + assert lib.bar(100) == 143 + lib.mycb1 = lib.foo + assert lib.mycb1(200) == 242 + assert lib.indirect_call(300) == 342 + +def test_extern_python_plus_c(): + ffi = FFI() + ffi.cdef(""" + extern "Python+C" int foo(int); + extern "C +\tPython" int bar(int); + int call_me(int); + """) + lib = verify(ffi, 'test_extern_python_plus_c', """ + int foo(int); + #ifdef __GNUC__ + __attribute__((visibility("hidden"))) + #endif + int bar(int); + + static int call_me(int x) { + return foo(x) - bar(x); + } + """) + # + @ffi.def_extern() + def foo(x): + return x * 42 + @ffi.def_extern() + def bar(x): + return x * 63 + assert lib.foo(100) == 4200 + assert lib.bar(100) == 6300 + assert lib.call_me(100) == -2100 + +def test_introspect_function(): + ffi = FFI() + ffi.cdef("float f1(double);") + lib = verify(ffi, 'test_introspect_function', """ + float f1(double x) { return x; } + """) + assert dir(lib) == ['f1'] + FUNC = ffi.typeof(lib.f1) + assert FUNC.kind == 'function' + assert FUNC.args[0].cname == 'double' + assert FUNC.result.cname == 'float' + assert ffi.typeof(ffi.addressof(lib, 'f1')) is FUNC + +def test_introspect_global_var(): + ffi = FFI() + ffi.cdef("float g1;") + lib = verify(ffi, 'test_introspect_global_var', """ + float g1; + """) + assert dir(lib) == ['g1'] + FLOATPTR = ffi.typeof(ffi.addressof(lib, 'g1')) + assert FLOATPTR.kind == 'pointer' + assert FLOATPTR.item.cname == 'float' + +def test_introspect_global_var_array(): + ffi = FFI() + ffi.cdef("float g1[100];") + lib = verify(ffi, 'test_introspect_global_var_array', """ + float g1[100]; + """) + assert dir(lib) == ['g1'] + FLOATARRAYPTR = ffi.typeof(ffi.addressof(lib, 'g1')) + assert FLOATARRAYPTR.kind == 'pointer' + assert FLOATARRAYPTR.item.kind == 'array' + assert FLOATARRAYPTR.item.length == 100 + assert ffi.typeof(lib.g1) is FLOATARRAYPTR.item + +def test_introspect_integer_const(): + ffi = FFI() + ffi.cdef("#define FOO 42") + lib = verify(ffi, 'test_introspect_integer_const', """ + #define FOO 42 + """) + assert dir(lib) == ['FOO'] + assert lib.FOO == ffi.integer_const('FOO') == 42 + +def test_introspect_typedef(): + ffi = FFI() + ffi.cdef("typedef int foo_t;") + lib = verify(ffi, 'test_introspect_typedef', """ + typedef int foo_t; + """) + assert ffi.list_types() == (['foo_t'], [], []) + assert ffi.typeof('foo_t').kind == 'primitive' + assert ffi.typeof('foo_t').cname == 'int' + +def test_introspect_typedef_multiple(): + ffi = FFI() + ffi.cdef("typedef signed char a_t, c_t, g_t, b_t;") + lib = verify(ffi, 'test_introspect_typedef_multiple', """ + typedef signed char a_t, c_t, g_t, b_t; + """) + assert ffi.list_types() == (['a_t', 'b_t', 'c_t', 'g_t'], [], []) + +def test_introspect_struct(): + ffi = FFI() + ffi.cdef("struct foo_s { int a; };") + lib = verify(ffi, 'test_introspect_struct', """ + struct foo_s { int a; }; + """) + assert ffi.list_types() == ([], ['foo_s'], []) + assert ffi.typeof('struct foo_s').kind == 'struct' + assert ffi.typeof('struct foo_s').cname == 'struct foo_s' + +def test_introspect_union(): + ffi = FFI() + ffi.cdef("union foo_s { int a; };") + lib = verify(ffi, 'test_introspect_union', """ + union foo_s { int a; }; + """) + assert ffi.list_types() == ([], [], ['foo_s']) + assert ffi.typeof('union foo_s').kind == 'union' + assert ffi.typeof('union foo_s').cname == 'union foo_s' + +def test_introspect_struct_and_typedef(): + ffi = FFI() + ffi.cdef("typedef struct { int a; } foo_t;") + lib = verify(ffi, 'test_introspect_struct_and_typedef', """ + typedef struct { int a; } foo_t; + """) + assert ffi.list_types() == (['foo_t'], [], []) + assert ffi.typeof('foo_t').kind == 'struct' + assert ffi.typeof('foo_t').cname == 'foo_t' + +def test_introspect_included_type(): + SOURCE = """ + typedef signed char schar_t; + struct sint_t { int x; }; + """ + ffi1 = FFI() + ffi1.cdef(SOURCE) + ffi2 = FFI() + ffi2.include(ffi1) + verify(ffi1, "test_introspect_included_type_parent", SOURCE) + verify(ffi2, "test_introspect_included_type", SOURCE) + assert ffi1.list_types() == ffi2.list_types() == ( + ['schar_t'], ['sint_t'], []) + +def test_introspect_order(): + ffi = FFI() + ffi.cdef("union CFFIaaa { int a; }; typedef struct CFFIccc { int a; } CFFIb;") + ffi.cdef("union CFFIg { int a; }; typedef struct CFFIcc { int a; } CFFIbbb;") + ffi.cdef("union CFFIaa { int a; }; typedef struct CFFIa { int a; } CFFIbb;") + verify(ffi, "test_introspect_order", """ + union CFFIaaa { int a; }; typedef struct CFFIccc { int a; } CFFIb; + union CFFIg { int a; }; typedef struct CFFIcc { int a; } CFFIbbb; + union CFFIaa { int a; }; typedef struct CFFIa { int a; } CFFIbb; + """) + assert ffi.list_types() == (['CFFIb', 'CFFIbb', 'CFFIbbb'], + ['CFFIa', 'CFFIcc', 'CFFIccc'], + ['CFFIaa', 'CFFIaaa', 'CFFIg']) + +def test_bool_in_cpp(): + # this works when compiled as C, but in cffi < 1.7 it fails as C++ + ffi = FFI() + ffi.cdef("bool f(void);") + lib = verify(ffi, "test_bool_in_cpp", "char f(void) { return 2; }") + assert lib.f() is True + +def test_bool_in_cpp_2(): + ffi = FFI() + ffi.cdef('int add(int a, int b);') + lib = verify(ffi, "test_bool_bug_cpp", ''' + typedef bool _Bool; /* there is a Windows header with this line */ + int add(int a, int b) + { + return a + b; + }''', source_extension='.cpp') + c = lib.add(2, 3) + assert c == 5 + +def test_struct_field_opaque(): + ffi = FFI() + ffi.cdef("struct a { struct b b; };") + e = py.test.raises(TypeError, verify, + ffi, "test_struct_field_opaque", "?") + assert str(e.value) == ("struct a: field 'a.b' is of an opaque" + " type (not declared in cdef())") + ffi = FFI() + ffi.cdef("struct a { struct b b[2]; };") + e = py.test.raises(TypeError, verify, + ffi, "test_struct_field_opaque", "?") + assert str(e.value) == ("struct a: field 'a.b' is of an opaque" + " type (not declared in cdef())") + ffi = FFI() + ffi.cdef("struct a { struct b b[]; };") + e = py.test.raises(TypeError, verify, + ffi, "test_struct_field_opaque", "?") + assert str(e.value) == ("struct a: field 'a.b' is of an opaque" + " type (not declared in cdef())") + +def test_function_arg_opaque(): + py.test.skip("can currently declare a function with an opaque struct " + "as argument, but AFAICT it's impossible to call it later") + +def test_function_returns_opaque(): + ffi = FFI() + ffi.cdef("struct a foo(int);") + e = py.test.raises(TypeError, verify, + ffi, "test_function_returns_opaque", "?") + assert str(e.value) == ("function foo: 'struct a' is used as result type," + " but is opaque") + +def test_function_returns_union(): + ffi = FFI() + ffi.cdef("union u1 { int a, b; }; union u1 f1(int);") + lib = verify(ffi, "test_function_returns_union", """ + union u1 { int a, b; }; + static union u1 f1(int x) { union u1 u; u.b = x; return u; } + """) + assert lib.f1(51).a == 51 + +def test_function_returns_partial_struct(): + ffi = FFI() + ffi.cdef("struct aaa { int a; ...; }; struct aaa f1(int);") + lib = verify(ffi, "test_function_returns_partial_struct", """ + struct aaa { int b, a, c; }; + static struct aaa f1(int x) { struct aaa s = {0}; s.a = x; return s; } + """) + assert lib.f1(52).a == 52 + +def test_function_returns_float_complex(): + if sys.platform == 'win32': + py.test.skip("MSVC may not support _Complex") + ffi = FFI() + ffi.cdef("float _Complex f1(float a, float b);"); + lib = verify(ffi, "test_function_returns_float_complex", """ + #include <complex.h> + static float _Complex f1(float a, float b) { return a + I*2.0*b; } + """, no_cpp=True) # <complex.h> fails on some systems with C++ + result = lib.f1(1.25, 5.1) + assert type(result) == complex + assert result.real == 1.25 # exact + assert (result.imag != 2*5.1) and (abs(result.imag - 2*5.1) < 1e-5) # inexact + +def test_function_returns_double_complex(): + if sys.platform == 'win32': + py.test.skip("MSVC may not support _Complex") + ffi = FFI() + ffi.cdef("double _Complex f1(double a, double b);"); + lib = verify(ffi, "test_function_returns_double_complex", """ + #include <complex.h> + static double _Complex f1(double a, double b) { return a + I*2.0*b; } + """, no_cpp=True) # <complex.h> fails on some systems with C++ + result = lib.f1(1.25, 5.1) + assert type(result) == complex + assert result.real == 1.25 # exact + assert result.imag == 2*5.1 # exact + +def test_function_argument_float_complex(): + if sys.platform == 'win32': + py.test.skip("MSVC may not support _Complex") + ffi = FFI() + ffi.cdef("float f1(float _Complex x);"); + lib = verify(ffi, "test_function_argument_float_complex", """ + #include <complex.h> + static float f1(float _Complex x) { return cabsf(x); } + """, no_cpp=True) # <complex.h> fails on some systems with C++ + x = complex(12.34, 56.78) + result = lib.f1(x) + assert abs(result - abs(x)) < 1e-5 + +def test_function_argument_double_complex(): + if sys.platform == 'win32': + py.test.skip("MSVC may not support _Complex") + ffi = FFI() + ffi.cdef("double f1(double _Complex);"); + lib = verify(ffi, "test_function_argument_double_complex", """ + #include <complex.h> + static double f1(double _Complex x) { return cabs(x); } + """, no_cpp=True) # <complex.h> fails on some systems with C++ + x = complex(12.34, 56.78) + result = lib.f1(x) + assert abs(result - abs(x)) < 1e-11 + +def test_typedef_array_dotdotdot(): + ffi = FFI() + ffi.cdef(""" + typedef int foo_t[...], bar_t[...]; + int gv[...]; + typedef int mat_t[...][...]; + typedef int vmat_t[][...]; + """) + lib = verify(ffi, "test_typedef_array_dotdotdot", """ + typedef int foo_t[50], bar_t[50]; + int gv[23]; + typedef int mat_t[6][7]; + typedef int vmat_t[][8]; + """) + assert ffi.sizeof("foo_t") == 50 * ffi.sizeof("int") + assert ffi.sizeof("bar_t") == 50 * ffi.sizeof("int") + assert len(ffi.new("foo_t")) == 50 + assert len(ffi.new("bar_t")) == 50 + assert ffi.sizeof(lib.gv) == 23 * ffi.sizeof("int") + assert ffi.sizeof("mat_t") == 6 * 7 * ffi.sizeof("int") + assert len(ffi.new("mat_t")) == 6 + assert len(ffi.new("mat_t")[3]) == 7 + py.test.raises(ffi.error, ffi.sizeof, "vmat_t") + p = ffi.new("vmat_t", 4) + assert ffi.sizeof(p[3]) == 8 * ffi.sizeof("int") + +def test_call_with_custom_field_pos(): + ffi = FFI() + ffi.cdef(""" + struct foo { int x; ...; }; + struct foo f(void); + struct foo g(int, ...); + """) + lib = verify(ffi, "test_call_with_custom_field_pos", """ + struct foo { int y, x; }; + struct foo f(void) { + struct foo s = { 40, 200 }; + return s; + } + struct foo g(int a, ...) { return f(); } + """) + assert lib.f().x == 200 + e = py.test.raises(NotImplementedError, lib.g, 0) + assert str(e.value) == ( + 'ctype \'struct foo\' not supported as return value. It is a ' + 'struct declared with "...;", but the C calling convention may ' + 'depend on the missing fields; or, it contains anonymous ' + 'struct/unions. Such structs are only supported ' + 'as return value if the function is \'API mode\' and non-variadic ' + '(i.e. declared inside ffibuilder.cdef()+ffibuilder.set_source() ' + 'and not taking a final \'...\' argument)') + +def test_call_with_nested_anonymous_struct(): + if sys.platform == 'win32': + py.test.skip("needs a GCC extension") + ffi = FFI() + ffi.cdef(""" + struct foo { int a; union { int b, c; }; }; + struct foo f(void); + struct foo g(int, ...); + """) + lib = verify(ffi, "test_call_with_nested_anonymous_struct", """ + struct foo { int a; union { int b, c; }; }; + struct foo f(void) { + struct foo s = { 40 }; + s.b = 200; + return s; + } + struct foo g(int a, ...) { return f(); } + """) + assert lib.f().b == 200 + e = py.test.raises(NotImplementedError, lib.g, 0) + assert str(e.value) == ( + 'ctype \'struct foo\' not supported as return value. It is a ' + 'struct declared with "...;", but the C calling convention may ' + 'depend on the missing fields; or, it contains anonymous ' + 'struct/unions. Such structs are only supported ' + 'as return value if the function is \'API mode\' and non-variadic ' + '(i.e. declared inside ffibuilder.cdef()+ffibuilder.set_source() ' + 'and not taking a final \'...\' argument)') + +def test_call_with_bitfield(): + ffi = FFI() + ffi.cdef(""" + struct foo { int x:5; }; + struct foo f(void); + struct foo g(int, ...); + """) + lib = verify(ffi, "test_call_with_bitfield", """ + struct foo { int x:5; }; + struct foo f(void) { + struct foo s = { 11 }; + return s; + } + struct foo g(int a, ...) { return f(); } + """) + assert lib.f().x == 11 + e = py.test.raises(NotImplementedError, lib.g, 0) + assert str(e.value) == ( + "ctype 'struct foo' not supported as return value. It is a struct " + "with bit fields, which libffi does not support. Such structs are " + "only supported as return value if the function is 'API mode' and " + "non-variadic (i.e. declared inside ffibuilder.cdef()+ffibuilder." + "set_source() and not taking a final '...' argument)") + +def test_call_with_zero_length_field(): + if sys.platform == 'win32': + py.test.skip("zero-length field not supported by MSVC") + ffi = FFI() + ffi.cdef(""" + struct foo { int a; int x[0]; }; + struct foo f(void); + struct foo g(int, ...); + """) + lib = verify(ffi, "test_call_with_zero_length_field", """ + struct foo { int a; int x[0]; }; + struct foo f(void) { + struct foo s = { 42 }; + return s; + } + struct foo g(int a, ...) { return f(); } + """) + assert lib.f().a == 42 + e = py.test.raises(NotImplementedError, lib.g, 0) + assert str(e.value) == ( + "ctype 'struct foo' not supported as return value. It is a " + "struct with a zero-length array, which libffi does not support." + " Such structs are only supported as return value if the function is " + "'API mode' and non-variadic (i.e. declared inside ffibuilder.cdef()" + "+ffibuilder.set_source() and not taking a final '...' argument)") + +def test_call_with_union(): + ffi = FFI() + ffi.cdef(""" + union foo { int a; char b; }; + union foo f(void); + union foo g(int, ...); + """) + lib = verify(ffi, "test_call_with_union", """ + union foo { int a; char b; }; + union foo f(void) { + union foo s = { 42 }; + return s; + } + union foo g(int a, ...) { return f(); } + """) + assert lib.f().a == 42 + e = py.test.raises(NotImplementedError, lib.g, 0) + assert str(e.value) == ( + "ctype 'union foo' not supported as return value by libffi. " + "Unions are only supported as return value if the function is " + "'API mode' and non-variadic (i.e. declared inside ffibuilder.cdef()" + "+ffibuilder.set_source() and not taking a final '...' argument)") + +def test_call_with_packed_struct(): + if sys.platform == 'win32': + py.test.skip("needs a GCC extension") + ffi = FFI() + ffi.cdef(""" + struct foo { char y; int x; }; + struct foo f(void); + struct foo g(int, ...); + """, packed=True) + lib = verify(ffi, "test_call_with_packed_struct", """ + struct foo { char y; int x; } __attribute__((packed)); + struct foo f(void) { + struct foo s = { 40, 200 }; + return s; + } + struct foo g(int a, ...) { + struct foo s = { 41, 201 }; + return s; + } + """) + assert ord(lib.f().y) == 40 + assert lib.f().x == 200 + e = py.test.raises(NotImplementedError, lib.g, 0) + assert str(e.value) == ( + "ctype 'struct foo' not supported as return value. It is a " + "'packed' structure, with a different layout than expected by libffi." + " Such structs are only supported as return value if the function is " + "'API mode' and non-variadic (i.e. declared inside ffibuilder.cdef()" + "+ffibuilder.set_source() and not taking a final '...' argument)") + +def test_pack_not_supported(): + ffi = FFI() + ffi.cdef("""struct foo { char y; int x; };""", pack=2) + py.test.raises(NotImplementedError, verify, + ffi, "test_pack_not_supported", "") + +def test_gcc_visibility_hidden(): + if sys.platform == 'win32': + py.test.skip("test for gcc/clang") + ffi = FFI() + ffi.cdef(""" + int f(int); + """) + lib = verify(ffi, "test_gcc_visibility_hidden", """ + int f(int a) { return a + 40; } + """, extra_compile_args=['-fvisibility=hidden']) + assert lib.f(2) == 42 + +def test_override_default_definition(): + ffi = FFI() + ffi.cdef("typedef long int16_t, char16_t;") + lib = verify(ffi, "test_override_default_definition", "") + assert ffi.typeof("int16_t") is ffi.typeof("char16_t") is ffi.typeof("long") + +def test_char16_char32_type(no_cpp=False): + if no_cpp is False and sys.platform == "win32": + py.test.skip("aaaaaaa why do modern MSVC compilers still define " + "a very old __cplusplus value") + ffi = FFI() + ffi.cdef(""" + char16_t foo_2bytes(char16_t); + char32_t foo_4bytes(char32_t); + """) + lib = verify(ffi, "test_char16_char32_type" + no_cpp * "_nocpp", """ + #if !defined(__cplusplus) || (!defined(_LIBCPP_VERSION) && __cplusplus < 201103L) + typedef uint_least16_t char16_t; + typedef uint_least32_t char32_t; + #endif + + char16_t foo_2bytes(char16_t a) { return (char16_t)(a + 42); } + char32_t foo_4bytes(char32_t a) { return (char32_t)(a + 42); } + """, no_cpp=no_cpp) + assert lib.foo_2bytes(u+'\u1234') == u+'\u125e' + assert lib.foo_4bytes(u+'\u1234') == u+'\u125e' + assert lib.foo_4bytes(u+'\U00012345') == u+'\U0001236f' + py.test.raises(TypeError, lib.foo_2bytes, u+'\U00012345') + py.test.raises(TypeError, lib.foo_2bytes, 1234) + py.test.raises(TypeError, lib.foo_4bytes, 1234) + +def test_char16_char32_plain_c(): + test_char16_char32_type(no_cpp=True) + +def test_loader_spec(): + ffi = FFI() + lib = verify(ffi, "test_loader_spec", "") + if sys.version_info < (3,): + assert not hasattr(lib, '__loader__') + assert not hasattr(lib, '__spec__') + else: + assert lib.__loader__ is None + assert lib.__spec__ is None + +def test_realize_struct_error(): + ffi = FFI() + ffi.cdef("""typedef ... foo_t; struct foo_s { void (*x)(foo_t); };""") + lib = verify(ffi, "test_realize_struct_error", """ + typedef int foo_t; struct foo_s { void (*x)(foo_t); }; + """) + py.test.raises(TypeError, ffi.new, "struct foo_s *") diff --git a/testing/cffi1/test_unicode_literals.py b/testing/cffi1/test_unicode_literals.py new file mode 100644 index 0000000..e9825db --- /dev/null +++ b/testing/cffi1/test_unicode_literals.py @@ -0,0 +1,43 @@ +# +# ---------------------------------------------- +# WARNING, ALL LITERALS IN THIS FILE ARE UNICODE +# ---------------------------------------------- +# +from __future__ import unicode_literals +# +# +# +from _cffi_backend import FFI + + +def test_cast(): + ffi = FFI() + assert int(ffi.cast("int", 3.14)) == 3 # unicode literal + +def test_new(): + ffi = FFI() + assert ffi.new("int[]", [3, 4, 5])[2] == 5 # unicode literal + +def test_typeof(): + ffi = FFI() + tp = ffi.typeof("int[51]") # unicode literal + assert tp.length == 51 + +def test_sizeof(): + ffi = FFI() + assert ffi.sizeof("int[51]") == 51 * 4 # unicode literal + +def test_alignof(): + ffi = FFI() + assert ffi.alignof("int[51]") == 4 # unicode literal + +def test_getctype(): + ffi = FFI() + assert ffi.getctype("int**") == "int * *" # unicode literal + assert type(ffi.getctype("int**")) is str + +def test_callback(): + ffi = FFI() + cb = ffi.callback("int(int)", # unicode literal + lambda x: x + 42) + assert cb(5) == 47 diff --git a/testing/cffi1/test_verify1.py b/testing/cffi1/test_verify1.py new file mode 100644 index 0000000..75f113d --- /dev/null +++ b/testing/cffi1/test_verify1.py @@ -0,0 +1,2363 @@ +import os, sys, math, py +from cffi import FFI, FFIError, VerificationError, VerificationMissing, model +from cffi import CDefError +from cffi import recompiler +from testing.support import * +from testing.support import _verify +import _cffi_backend + +lib_m = ['m'] +if sys.platform == 'win32': + #there is a small chance this fails on Mingw via environ $CC + import distutils.ccompiler + if distutils.ccompiler.get_default_compiler() == 'msvc': + lib_m = ['msvcrt'] + extra_compile_args = [] # no obvious -Werror equivalent on MSVC +else: + if (sys.platform == 'darwin' and + [int(x) for x in os.uname()[2].split('.')] >= [11, 0, 0]): + # assume a standard clang or gcc + extra_compile_args = ['-Werror', '-Wall', '-Wextra', '-Wconversion'] + # special things for clang + extra_compile_args.append('-Qunused-arguments') + else: + # assume a standard gcc + extra_compile_args = ['-Werror', '-Wall', '-Wextra', '-Wconversion'] + +class FFI(FFI): + error = _cffi_backend.FFI.error + _extra_compile_args = extra_compile_args + _verify_counter = 0 + + def verify(self, preamble='', *args, **kwds): + # HACK to reuse the tests from ../cffi0/test_verify.py + FFI._verify_counter += 1 + module_name = 'verify%d' % FFI._verify_counter + try: + del self._assigned_source + except AttributeError: + pass + self.set_source(module_name, preamble) + return _verify(self, module_name, preamble, *args, + extra_compile_args=self._extra_compile_args, **kwds) + +class FFI_warnings_not_error(FFI): + _extra_compile_args = [] + + +def test_missing_function(ffi=None): + # uses the FFI hacked above with '-Werror' + if ffi is None: + ffi = FFI() + ffi.cdef("void some_completely_unknown_function();") + try: + lib = ffi.verify() + except (VerificationError, OSError, ImportError): + pass # expected case: we get a VerificationError + else: + # but depending on compiler and loader details, maybe + # 'lib' could actually be imported but will fail if we + # actually try to call the unknown function... Hard + # to test anything more. + pass + +def test_missing_function_import_error(): + # uses the original FFI that just gives a warning during compilation + test_missing_function(ffi=FFI_warnings_not_error()) + +def test_simple_case(): + ffi = FFI() + ffi.cdef("double sin(double x);") + lib = ffi.verify('#include <math.h>', libraries=lib_m) + assert lib.sin(1.23) == math.sin(1.23) + +def _Wconversion(cdef, source, **kargs): + if sys.platform in ('win32', 'darwin'): + py.test.skip("needs GCC") + ffi = FFI() + ffi.cdef(cdef) + py.test.raises(VerificationError, ffi.verify, source, **kargs) + extra_compile_args_orig = extra_compile_args[:] + extra_compile_args.remove('-Wconversion') + try: + lib = ffi.verify(source, **kargs) + finally: + extra_compile_args[:] = extra_compile_args_orig + return lib + +def test_Wconversion_unsigned(): + _Wconversion("unsigned foo(void);", + "int foo(void) { return -1;}") + +def test_Wconversion_integer(): + _Wconversion("short foo(void);", + "long long foo(void) { return 1<<sizeof(short);}") + +def test_Wconversion_floating(): + lib = _Wconversion("float sin(double);", + "#include <math.h>", libraries=lib_m) + res = lib.sin(1.23) + assert res != math.sin(1.23) # not exact, because of double->float + assert abs(res - math.sin(1.23)) < 1E-5 + +def test_Wconversion_float2int(): + _Wconversion("int sinf(float);", + "#include <math.h>", libraries=lib_m) + +def test_Wconversion_double2int(): + _Wconversion("int sin(double);", + "#include <math.h>", libraries=lib_m) + +def test_rounding_1(): + ffi = FFI() + ffi.cdef("double sinf(float x);") + lib = ffi.verify('#include <math.h>', libraries=lib_m) + res = lib.sinf(1.23) + assert res != math.sin(1.23) # not exact, because of double->float + assert abs(res - math.sin(1.23)) < 1E-5 + +def test_rounding_2(): + ffi = FFI() + ffi.cdef("double sin(float x);") + lib = ffi.verify('#include <math.h>', libraries=lib_m) + res = lib.sin(1.23) + assert res != math.sin(1.23) # not exact, because of double->float + assert abs(res - math.sin(1.23)) < 1E-5 + +def test_strlen_exact(): + ffi = FFI() + ffi.cdef("size_t strlen(const char *s);") + lib = ffi.verify("#include <string.h>") + assert lib.strlen(b"hi there!") == 9 + +def test_strlen_approximate(): + lib = _Wconversion("int strlen(char *s);", + "#include <string.h>") + assert lib.strlen(b"hi there!") == 9 + +def test_return_approximate(): + for typename in ['short', 'int', 'long', 'long long']: + ffi = FFI() + ffi.cdef("%s foo(signed char x);" % typename) + lib = ffi.verify("signed char foo(signed char x) { return x;}") + assert lib.foo(-128) == -128 + assert lib.foo(+127) == +127 + +def test_strlen_array_of_char(): + ffi = FFI() + ffi.cdef("size_t strlen(char[]);") + lib = ffi.verify("#include <string.h>") + assert lib.strlen(b"hello") == 5 + +def test_longdouble(): + ffi = FFI() + ffi.cdef("long double sinl(long double x);") + lib = ffi.verify('#include <math.h>', libraries=lib_m) + for input in [1.23, + ffi.cast("double", 1.23), + ffi.cast("long double", 1.23)]: + x = lib.sinl(input) + assert repr(x).startswith("<cdata 'long double'") + assert (float(x) - math.sin(1.23)) < 1E-10 + +def test_longdouble_precision(): + # Test that we don't loose any precision of 'long double' when + # passing through Python and CFFI. + ffi = FFI() + ffi.cdef("long double step1(long double x);") + SAME_SIZE = ffi.sizeof("long double") == ffi.sizeof("double") + lib = ffi.verify(""" + long double step1(long double x) + { + return 4*x-x*x; + } + """) + def do(cast_to_double): + x = 0.9789 + for i in range(10000): + x = lib.step1(x) + if cast_to_double: + x = float(x) + return float(x) + + more_precise = do(False) + less_precise = do(True) + if SAME_SIZE: + assert more_precise == less_precise + else: + assert abs(more_precise - less_precise) > 0.1 + # Check the particular results on Intel + import platform + if (platform.machine().startswith('i386') or + platform.machine().startswith('i486') or + platform.machine().startswith('i586') or + platform.machine().startswith('i686') or + platform.machine().startswith('x86')): + assert abs(more_precise - 0.656769) < 0.001 + assert abs(less_precise - 3.99091) < 0.001 + else: + py.test.skip("don't know the very exact precision of 'long double'") + + +all_primitive_types = model.PrimitiveType.ALL_PRIMITIVE_TYPES +if sys.platform == 'win32': + all_primitive_types = all_primitive_types.copy() + del all_primitive_types['ssize_t'] +all_integer_types = sorted(tp for tp in all_primitive_types + if all_primitive_types[tp] == 'i') +all_float_types = sorted(tp for tp in all_primitive_types + if all_primitive_types[tp] == 'f') + +def all_signed_integer_types(ffi): + return [x for x in all_integer_types if int(ffi.cast(x, -1)) < 0] + +def all_unsigned_integer_types(ffi): + return [x for x in all_integer_types if int(ffi.cast(x, -1)) > 0] + + +def test_primitive_category(): + for typename in all_primitive_types: + tp = model.PrimitiveType(typename) + C = tp.is_char_type() + F = tp.is_float_type() + X = tp.is_complex_type() + I = tp.is_integer_type() + assert C == (typename in ('char', 'wchar_t', 'char16_t', 'char32_t')) + assert F == (typename in ('float', 'double', 'long double')) + assert X == (typename in ('float _Complex', 'double _Complex')) + assert I + F + C + X == 1 # one and only one of them is true + +def test_all_integer_and_float_types(): + typenames = [] + for typename in all_primitive_types: + if (all_primitive_types[typename] == 'c' or + all_primitive_types[typename] == 'j' or # complex + typename == '_Bool' or typename == 'long double'): + pass + else: + typenames.append(typename) + # + ffi = FFI() + ffi.cdef('\n'.join(["%s foo_%s(%s);" % (tp, tp.replace(' ', '_'), tp) + for tp in typenames])) + lib = ffi.verify('\n'.join(["%s foo_%s(%s x) { return (%s)(x+1); }" % + (tp, tp.replace(' ', '_'), tp, tp) + for tp in typenames])) + for typename in typenames: + foo = getattr(lib, 'foo_%s' % typename.replace(' ', '_')) + assert foo(42) == 43 + if sys.version < '3': + assert foo(long(44)) == 45 + assert foo(ffi.cast(typename, 46)) == 47 + py.test.raises(TypeError, foo, ffi.NULL) + # + # check for overflow cases + if all_primitive_types[typename] == 'f': + continue + for value in [-2**80, -2**40, -2**20, -2**10, -2**5, -1, + 2**5, 2**10, 2**20, 2**40, 2**80]: + overflows = int(ffi.cast(typename, value)) != value + if overflows: + py.test.raises(OverflowError, foo, value) + else: + assert foo(value) == value + 1 + +def test_var_signed_integer_types(): + ffi = FFI() + lst = all_signed_integer_types(ffi) + csource = "\n".join(["%s somevar_%s;" % (tp, tp.replace(' ', '_')) + for tp in lst]) + ffi.cdef(csource) + lib = ffi.verify(csource) + for tp in lst: + varname = 'somevar_%s' % tp.replace(' ', '_') + sz = ffi.sizeof(tp) + max = (1 << (8*sz-1)) - 1 + min = -(1 << (8*sz-1)) + setattr(lib, varname, max) + assert getattr(lib, varname) == max + setattr(lib, varname, min) + assert getattr(lib, varname) == min + py.test.raises(OverflowError, setattr, lib, varname, max+1) + py.test.raises(OverflowError, setattr, lib, varname, min-1) + +def test_var_unsigned_integer_types(): + ffi = FFI() + lst = all_unsigned_integer_types(ffi) + csource = "\n".join(["%s somevar_%s;" % (tp, tp.replace(' ', '_')) + for tp in lst]) + ffi.cdef(csource) + lib = ffi.verify(csource) + for tp in lst: + varname = 'somevar_%s' % tp.replace(' ', '_') + sz = ffi.sizeof(tp) + if tp != '_Bool': + max = (1 << (8*sz)) - 1 + else: + max = 1 + setattr(lib, varname, max) + assert getattr(lib, varname) == max + setattr(lib, varname, 0) + assert getattr(lib, varname) == 0 + py.test.raises(OverflowError, setattr, lib, varname, max+1) + py.test.raises(OverflowError, setattr, lib, varname, -1) + +def test_fn_signed_integer_types(): + ffi = FFI() + lst = all_signed_integer_types(ffi) + cdefsrc = "\n".join(["%s somefn_%s(%s);" % (tp, tp.replace(' ', '_'), tp) + for tp in lst]) + ffi.cdef(cdefsrc) + verifysrc = "\n".join(["%s somefn_%s(%s x) { return x; }" % + (tp, tp.replace(' ', '_'), tp) for tp in lst]) + lib = ffi.verify(verifysrc) + for tp in lst: + fnname = 'somefn_%s' % tp.replace(' ', '_') + sz = ffi.sizeof(tp) + max = (1 << (8*sz-1)) - 1 + min = -(1 << (8*sz-1)) + fn = getattr(lib, fnname) + assert fn(max) == max + assert fn(min) == min + py.test.raises(OverflowError, fn, max + 1) + py.test.raises(OverflowError, fn, min - 1) + +def test_fn_unsigned_integer_types(): + ffi = FFI() + lst = all_unsigned_integer_types(ffi) + cdefsrc = "\n".join(["%s somefn_%s(%s);" % (tp, tp.replace(' ', '_'), tp) + for tp in lst]) + ffi.cdef(cdefsrc) + verifysrc = "\n".join(["%s somefn_%s(%s x) { return x; }" % + (tp, tp.replace(' ', '_'), tp) for tp in lst]) + lib = ffi.verify(verifysrc) + for tp in lst: + fnname = 'somefn_%s' % tp.replace(' ', '_') + sz = ffi.sizeof(tp) + if tp != '_Bool': + max = (1 << (8*sz)) - 1 + else: + max = 1 + fn = getattr(lib, fnname) + assert fn(max) == max + assert fn(0) == 0 + py.test.raises(OverflowError, fn, max + 1) + py.test.raises(OverflowError, fn, -1) + +def test_char_type(): + ffi = FFI() + ffi.cdef("char foo(char);") + lib = ffi.verify("char foo(char x) { return ++x; }") + assert lib.foo(b"A") == b"B" + py.test.raises(TypeError, lib.foo, b"bar") + py.test.raises(TypeError, lib.foo, "bar") + +def test_wchar_type(): + ffi = FFI() + if ffi.sizeof('wchar_t') == 2: + uniexample1 = u+'\u1234' + uniexample2 = u+'\u1235' + else: + uniexample1 = u+'\U00012345' + uniexample2 = u+'\U00012346' + # + ffi.cdef("wchar_t foo(wchar_t);") + lib = ffi.verify("wchar_t foo(wchar_t x) { return x+1; }") + assert lib.foo(uniexample1) == uniexample2 + +def test_no_argument(): + ffi = FFI() + ffi.cdef("int foo(void);") + lib = ffi.verify("int foo(void) { return 42; }") + assert lib.foo() == 42 + +def test_two_arguments(): + ffi = FFI() + ffi.cdef("int foo(int, int);") + lib = ffi.verify("int foo(int a, int b) { return a - b; }") + assert lib.foo(40, -2) == 42 + +def test_macro(): + ffi = FFI() + ffi.cdef("int foo(int, int);") + lib = ffi.verify("#define foo(a, b) ((a) * (b))") + assert lib.foo(-6, -7) == 42 + +def test_ptr(): + ffi = FFI() + ffi.cdef("int *foo(int *);") + lib = ffi.verify("int *foo(int *a) { return a; }") + assert lib.foo(ffi.NULL) == ffi.NULL + p = ffi.new("int *", 42) + q = ffi.new("int *", 42) + assert lib.foo(p) == p + assert lib.foo(q) != p + +def test_bogus_ptr(): + ffi = FFI() + ffi.cdef("int *foo(int *);") + lib = ffi.verify("int *foo(int *a) { return a; }") + py.test.raises(TypeError, lib.foo, ffi.new("short *", 42)) + + +def test_verify_typedefs(): + py.test.skip("ignored so far") + types = ['signed char', 'unsigned char', 'int', 'long'] + for cdefed in types: + for real in types: + ffi = FFI() + ffi.cdef("typedef %s foo_t;" % cdefed) + if cdefed == real: + ffi.verify("typedef %s foo_t;" % real) + else: + py.test.raises(VerificationError, ffi.verify, + "typedef %s foo_t;" % real) + +def test_nondecl_struct(): + ffi = FFI() + ffi.cdef("typedef struct foo_s foo_t; int bar(foo_t *);") + lib = ffi.verify("typedef struct foo_s foo_t;\n" + "int bar(foo_t *f) { (void)f; return 42; }\n") + assert lib.bar(ffi.NULL) == 42 + +def test_ffi_full_struct(): + def check(verified_code): + ffi = FFI() + ffi.cdef("struct foo_s { char x; int y; long *z; };") + ffi.verify(verified_code) + ffi.new("struct foo_s *", {}) + + check("struct foo_s { char x; int y; long *z; };") + # + if sys.platform != 'win32': # XXX fixme: only gives warnings + py.test.raises(VerificationError, check, + "struct foo_s { char x; int y; int *z; };") + # + py.test.raises(VerificationError, check, + "struct foo_s { int y; long *z; };") # cdef'ed field x is missing + # + e = py.test.raises(FFI.error, check, + "struct foo_s { int y; char x; long *z; };") + assert str(e.value).startswith( + "struct foo_s: wrong offset for field 'x'" + " (cdef says 0, but C compiler says 4)") + # + e = py.test.raises(FFI.error, check, + "struct foo_s { char x; int y; long *z; char extra; };") + assert str(e.value).startswith( + "struct foo_s: wrong total size" + " (cdef says %d, but C compiler says %d)" % ( + 8 + FFI().sizeof('long *'), + 8 + FFI().sizeof('long *') * 2)) + # + # a corner case that we cannot really detect, but where it has no + # bad consequences: the size is the same, but there is an extra field + # that replaces what is just padding in our declaration above + check("struct foo_s { char x, extra; int y; long *z; };") + # + e = py.test.raises(FFI.error, check, + "struct foo_s { char x; short pad; short y; long *z; };") + assert str(e.value).startswith( + "struct foo_s: wrong size for field 'y'" + " (cdef says 4, but C compiler says 2)") + +def test_ffi_nonfull_struct(): + ffi = FFI() + ffi.cdef(""" + struct foo_s { + int x; + ...; + }; + """) + py.test.raises(VerificationMissing, ffi.sizeof, 'struct foo_s') + py.test.raises(VerificationMissing, ffi.offsetof, 'struct foo_s', 'x') + py.test.raises(VerificationMissing, ffi.new, 'struct foo_s *') + ffi.verify(""" + struct foo_s { + int a, b, x, c, d, e; + }; + """) + assert ffi.sizeof('struct foo_s') == 6 * ffi.sizeof('int') + assert ffi.offsetof('struct foo_s', 'x') == 2 * ffi.sizeof('int') + +def test_ffi_nonfull_alignment(): + ffi = FFI() + ffi.cdef("struct foo_s { char x; ...; };") + ffi.verify("struct foo_s { int a, b; char x; };") + assert ffi.sizeof('struct foo_s') == 3 * ffi.sizeof('int') + assert ffi.alignof('struct foo_s') == ffi.sizeof('int') + +def _check_field_match(typename, real, expect_mismatch): + ffi = FFI() + testing_by_size = (expect_mismatch == 'by_size') + if testing_by_size: + expect_mismatch = ffi.sizeof(typename) != ffi.sizeof(real) + ffi.cdef("struct foo_s { %s x; ...; };" % typename) + try: + ffi.verify("struct foo_s { %s x; };" % real) + ffi.new("struct foo_s *", []) # because some mismatches show up lazily + except (VerificationError, ffi.error): + if not expect_mismatch: + if testing_by_size and typename != real: + print("ignoring mismatch between %s* and %s* even though " + "they have the same size" % (typename, real)) + return + raise AssertionError("unexpected mismatch: %s should be accepted " + "as equal to %s" % (typename, real)) + else: + if expect_mismatch: + raise AssertionError("mismatch not detected: " + "%s != %s" % (typename, real)) + +def test_struct_bad_sized_integer(): + for typename in ['int8_t', 'int16_t', 'int32_t', 'int64_t']: + for real in ['int8_t', 'int16_t', 'int32_t', 'int64_t']: + _check_field_match(typename, real, "by_size") + +def test_struct_bad_sized_float(): + for typename in all_float_types: + for real in all_float_types: + _check_field_match(typename, real, "by_size") + +def test_struct_signedness_ignored(): + _check_field_match("int", "unsigned int", expect_mismatch=False) + _check_field_match("unsigned short", "signed short", expect_mismatch=False) + +def test_struct_float_vs_int(): + if sys.platform == 'win32': + py.test.skip("XXX fixme: only gives warnings") + ffi = FFI() + for typename in all_signed_integer_types(ffi): + for real in all_float_types: + _check_field_match(typename, real, expect_mismatch=True) + for typename in all_float_types: + for real in all_signed_integer_types(ffi): + _check_field_match(typename, real, expect_mismatch=True) + +def test_struct_array_field(): + ffi = FFI() + ffi.cdef("struct foo_s { int a[17]; ...; };") + ffi.verify("struct foo_s { int x; int a[17]; int y; };") + assert ffi.sizeof('struct foo_s') == 19 * ffi.sizeof('int') + s = ffi.new("struct foo_s *") + assert ffi.sizeof(s.a) == 17 * ffi.sizeof('int') + +def test_struct_array_no_length(): + ffi = FFI() + ffi.cdef("struct foo_s { int a[]; int y; ...; };\n" + "int bar(struct foo_s *);\n") + lib = ffi.verify("struct foo_s { int x; int a[17]; int y; };\n" + "int bar(struct foo_s *f) { return f->a[14]; }\n") + assert ffi.sizeof('struct foo_s') == 19 * ffi.sizeof('int') + s = ffi.new("struct foo_s *") + assert ffi.typeof(s.a) is ffi.typeof('int[]') # implicit max length + assert len(s.a) == 18 # max length, computed from the size and start offset + s.a[14] = 4242 + assert lib.bar(s) == 4242 + # with no declared length, out-of-bound accesses are not detected + s.a[17] = -521 + assert s.y == s.a[17] == -521 + # + s = ffi.new("struct foo_s *", {'a': list(range(17))}) + assert s.a[16] == 16 + # overflows at construction time not detected either + s = ffi.new("struct foo_s *", {'a': list(range(18))}) + assert s.y == s.a[17] == 17 + +def test_struct_array_guess_length(): + ffi = FFI() + ffi.cdef("struct foo_s { int a[...]; };") + ffi.verify("struct foo_s { int x; int a[17]; int y; };") + assert ffi.sizeof('struct foo_s') == 19 * ffi.sizeof('int') + s = ffi.new("struct foo_s *") + assert ffi.sizeof(s.a) == 17 * ffi.sizeof('int') + py.test.raises(IndexError, 's.a[17]') + +def test_struct_array_c99_1(): + if sys.platform == 'win32': + py.test.skip("requires C99") + ffi = FFI() + ffi.cdef("struct foo_s { int x; int a[]; };") + ffi.verify("struct foo_s { int x; int a[]; };") + assert ffi.sizeof('struct foo_s') == 1 * ffi.sizeof('int') + s = ffi.new("struct foo_s *", [424242, 4]) + assert ffi.sizeof(ffi.typeof(s[0])) == 1 * ffi.sizeof('int') + assert ffi.sizeof(s[0]) == 5 * ffi.sizeof('int') + # ^^^ explanation: if you write in C: "char x[5];", then + # "sizeof(x)" will evaluate to 5. The behavior above is + # a generalization of that to "struct foo_s[len(a)=5] x;" + # if you could do that in C. + assert s.a[3] == 0 + s = ffi.new("struct foo_s *", [424242, [-40, -30, -20, -10]]) + assert ffi.sizeof(s[0]) == 5 * ffi.sizeof('int') + assert s.a[3] == -10 + s = ffi.new("struct foo_s *") + assert ffi.sizeof(s[0]) == 1 * ffi.sizeof('int') + s = ffi.new("struct foo_s *", [424242]) + assert ffi.sizeof(s[0]) == 1 * ffi.sizeof('int') + +def test_struct_array_c99_2(): + if sys.platform == 'win32': + py.test.skip("requires C99") + ffi = FFI() + ffi.cdef("struct foo_s { int x; int a[]; ...; };") + ffi.verify("struct foo_s { int x, y; int a[]; };") + assert ffi.sizeof('struct foo_s') == 2 * ffi.sizeof('int') + s = ffi.new("struct foo_s *", [424242, 4]) + assert ffi.sizeof(s[0]) == 6 * ffi.sizeof('int') + assert s.a[3] == 0 + s = ffi.new("struct foo_s *", [424242, [-40, -30, -20, -10]]) + assert ffi.sizeof(s[0]) == 6 * ffi.sizeof('int') + assert s.a[3] == -10 + s = ffi.new("struct foo_s *") + assert ffi.sizeof(s[0]) == 2 * ffi.sizeof('int') + s = ffi.new("struct foo_s *", [424242]) + assert ffi.sizeof(s[0]) == 2 * ffi.sizeof('int') + +def test_struct_ptr_to_array_field(): + ffi = FFI() + ffi.cdef("struct foo_s { int (*a)[17]; ...; }; struct bar_s { ...; };") + ffi.verify("struct foo_s { int x; int (*a)[17]; int y; };\n" + "struct bar_s { int x; int *a; int y; };") + assert ffi.sizeof('struct foo_s') == ffi.sizeof("struct bar_s") + s = ffi.new("struct foo_s *") + assert ffi.sizeof(s.a) == ffi.sizeof('int(*)[17]') == ffi.sizeof("int *") + +def test_struct_with_bitfield_exact(): + ffi = FFI() + ffi.cdef("struct foo_s { int a:2, b:3; };") + ffi.verify("struct foo_s { int a:2, b:3; };") + s = ffi.new("struct foo_s *") + s.b = 3 + py.test.raises(OverflowError, "s.b = 4") + assert s.b == 3 + +def test_struct_with_bitfield_enum(): + ffi = FFI() + code = """ + typedef enum { AA, BB, CC } foo_e; + typedef struct { foo_e f:2; } foo_s; + """ + ffi.cdef(code) + ffi.verify(code) + s = ffi.new("foo_s *") + s.f = 1 + assert s.f == 1 + if int(ffi.cast("foo_e", -1)) < 0: + two = -2 + else: + two = 2 + s.f = two + assert s.f == two + +def test_unsupported_struct_with_bitfield_ellipsis(): + ffi = FFI() + py.test.raises(NotImplementedError, ffi.cdef, + "struct foo_s { int a:2, b:3; ...; };") + +def test_global_constants(): + ffi = FFI() + # use 'static const int', as generally documented, although in this + # case the 'static' is completely ignored. + ffi.cdef("static const int AA, BB, CC, DD;") + lib = ffi.verify("#define AA 42\n" + "#define BB (-43) // blah\n" + "#define CC (22*2) /* foobar */\n" + "#define DD ((unsigned int)142) /* foo\nbar */\n") + assert lib.AA == 42 + assert lib.BB == -43 + assert lib.CC == 44 + assert lib.DD == 142 + +def test_global_const_int_size(): + # integer constants: ignore the declared type, always just use the value + for value in [-2**63, -2**31, -2**15, + 2**15-1, 2**15, 2**31-1, 2**31, 2**32-1, 2**32, + 2**63-1, 2**63, 2**64-1]: + ffi = FFI() + if value == int(ffi.cast("long long", value)): + if value < 0: + vstr = '(-%dLL-1)' % (~value,) + else: + vstr = '%dLL' % value + elif value == int(ffi.cast("unsigned long long", value)): + vstr = '%dULL' % value + else: + raise AssertionError(value) + ffi.cdef("static const unsigned short AA;") + lib = ffi.verify("#define AA %s\n" % vstr) + assert lib.AA == value + assert type(lib.AA) is type(int(lib.AA)) + +def test_global_constants_non_int(): + ffi = FFI() + ffi.cdef("static char *const PP;") + lib = ffi.verify('static char *const PP = "testing!";\n') + assert ffi.typeof(lib.PP) == ffi.typeof("char *") + assert ffi.string(lib.PP) == b"testing!" + +def test_nonfull_enum(): + ffi = FFI() + ffi.cdef("enum ee { EE1, EE2, EE3, ... \n \t };") + py.test.raises(VerificationMissing, ffi.cast, 'enum ee', 'EE2') + ffi.verify("enum ee { EE1=10, EE2, EE3=-10, EE4 };") + assert ffi.string(ffi.cast('enum ee', 11)) == "EE2" + assert ffi.string(ffi.cast('enum ee', -10)) == "EE3" + # + assert ffi.typeof("enum ee").relements == {'EE1': 10, 'EE2': 11, 'EE3': -10} + assert ffi.typeof("enum ee").elements == {10: 'EE1', 11: 'EE2', -10: 'EE3'} + +def test_full_enum(): + ffi = FFI() + ffi.cdef("enum ee { EE1, EE2, EE3 };") + lib = ffi.verify("enum ee { EE1, EE2, EE3 };") + assert [lib.EE1, lib.EE2, lib.EE3] == [0, 1, 2] + +def test_enum_usage(): + ffi = FFI() + ffi.cdef("enum ee { EE1,EE2 }; typedef struct { enum ee x; } *sp;") + lib = ffi.verify("enum ee { EE1,EE2 }; typedef struct { enum ee x; } *sp;") + assert lib.EE2 == 1 + s = ffi.new("sp", [lib.EE2]) + assert s.x == 1 + s.x = 17 + assert s.x == 17 + +def test_anonymous_enum(): + ffi = FFI() + ffi.cdef("enum { EE1 }; enum { EE2, EE3 };") + lib = ffi.verify("enum { EE1 }; enum { EE2, EE3 };") + assert lib.EE1 == 0 + assert lib.EE2 == 0 + assert lib.EE3 == 1 + +def test_nonfull_anonymous_enum(): + ffi = FFI() + ffi.cdef("enum { EE1, ... }; enum { EE3, ... };") + lib = ffi.verify("enum { EE2, EE1 }; enum { EE3 };") + assert lib.EE1 == 1 + assert lib.EE3 == 0 + +def test_nonfull_enum_syntax2(): + ffi = FFI() + ffi.cdef("enum ee { EE1, EE2=\t..., EE3 };") + py.test.raises(VerificationMissing, ffi.cast, 'enum ee', 'EE1') + ffi.verify("enum ee { EE1=10, EE2, EE3=-10, EE4 };") + assert ffi.string(ffi.cast('enum ee', 11)) == 'EE2' + assert ffi.string(ffi.cast('enum ee', -10)) == 'EE3' + # + ffi = FFI() + ffi.cdef("enum ee { EE1, EE2=\t... };") + py.test.raises(VerificationMissing, ffi.cast, 'enum ee', 'EE1') + ffi.verify("enum ee { EE1=10, EE2, EE3=-10, EE4 };") + assert ffi.string(ffi.cast('enum ee', 11)) == 'EE2' + # + ffi = FFI() + ffi.cdef("enum ee2 { EE4=..., EE5=..., ... };") + ffi.verify("enum ee2 { EE4=-1234-5, EE5 }; ") + assert ffi.string(ffi.cast('enum ee2', -1239)) == 'EE4' + assert ffi.string(ffi.cast('enum ee2', -1238)) == 'EE5' + +def test_get_set_errno(): + ffi = FFI() + ffi.cdef("int foo(int);") + lib = ffi.verify(""" + static int foo(int x) + { + errno += 1; + return x * 7; + } + """) + ffi.errno = 15 + assert lib.foo(6) == 42 + assert ffi.errno == 16 + +def test_define_int(): + ffi = FFI() + ffi.cdef("#define FOO ...\n" + "\t#\tdefine\tBAR\t...\t\n" + "#define BAZ ...\n") + lib = ffi.verify("#define FOO 42\n" + "#define BAR (-44)\n" + "#define BAZ 0xffffffffffffffffULL\n") + assert lib.FOO == 42 + assert lib.BAR == -44 + assert lib.BAZ == 0xffffffffffffffff + +def test_access_variable(): + ffi = FFI() + ffi.cdef("int foo(void);\n" + "int somenumber;") + lib = ffi.verify(""" + static int somenumber = 2; + static int foo(void) { + return somenumber * 7; + } + """) + assert lib.somenumber == 2 + assert lib.foo() == 14 + lib.somenumber = -6 + assert lib.foo() == -42 + assert lib.somenumber == -6 + lib.somenumber = 2 # reset for the next run, if any + +def test_access_address_of_variable(): + # access the address of 'somenumber': need a trick + ffi = FFI() + ffi.cdef("int somenumber; static int *const somenumberptr;") + lib = ffi.verify(""" + static int somenumber = 2; + #define somenumberptr (&somenumber) + """) + assert lib.somenumber == 2 + lib.somenumberptr[0] = 42 + assert lib.somenumber == 42 + lib.somenumber = 2 # reset for the next run, if any + +def test_access_array_variable(length=5): + ffi = FFI() + ffi.cdef("int foo(int);\n" + "int somenumber[%s];" % (length,)) + lib = ffi.verify(""" + static int somenumber[] = {2, 2, 3, 4, 5}; + static int foo(int i) { + return somenumber[i] * 7; + } + """) + if length == '': + # a global variable of an unknown array length is implicitly + # transformed into a global pointer variable, because we can only + # work with array instances whose length we know. using a pointer + # instead of an array gives the correct effects. + assert repr(lib.somenumber).startswith("<cdata 'int *' 0x") + py.test.raises(TypeError, len, lib.somenumber) + else: + assert repr(lib.somenumber).startswith("<cdata 'int[%s]' 0x" % length) + assert len(lib.somenumber) == 5 + assert lib.somenumber[3] == 4 + assert lib.foo(3) == 28 + lib.somenumber[3] = -6 + assert lib.foo(3) == -42 + assert lib.somenumber[3] == -6 + assert lib.somenumber[4] == 5 + lib.somenumber[3] = 4 # reset for the next run, if any + +def test_access_array_variable_length_hidden(): + test_access_array_variable(length='') + +def test_access_struct_variable(): + ffi = FFI() + ffi.cdef("struct foo { int x; ...; };\n" + "int foo(int);\n" + "struct foo stuff;") + lib = ffi.verify(""" + struct foo { int x, y, z; }; + static struct foo stuff = {2, 5, 8}; + static int foo(int i) { + switch (i) { + case 0: return stuff.x * 7; + case 1: return stuff.y * 7; + case 2: return stuff.z * 7; + } + return -1; + } + """) + assert lib.stuff.x == 2 + assert lib.foo(0) == 14 + assert lib.foo(1) == 35 + assert lib.foo(2) == 56 + lib.stuff.x = -6 + assert lib.foo(0) == -42 + assert lib.foo(1) == 35 + lib.stuff.x = 2 # reset for the next run, if any + +def test_access_callback(): + ffi = FFI() + ffi.cdef("int (*cb)(int);\n" + "int foo(int);\n" + "void reset_cb(void);") + lib = ffi.verify(""" + static int g(int x) { return x * 7; } + static int (*cb)(int); + static int foo(int i) { return cb(i) - 1; } + static void reset_cb(void) { cb = g; } + """) + lib.reset_cb() + assert lib.foo(6) == 41 + my_callback = ffi.callback("int(*)(int)", lambda n: n * 222) + lib.cb = my_callback + assert lib.foo(4) == 887 + +def test_access_callback_function_typedef(): + ffi = FFI() + ffi.cdef("typedef int mycallback_t(int);\n" + "mycallback_t *cb;\n" + "int foo(int);\n" + "void reset_cb(void);") + lib = ffi.verify(""" + static int g(int x) { return x * 7; } + static int (*cb)(int); + static int foo(int i) { return cb(i) - 1; } + static void reset_cb(void) { cb = g; } + """) + lib.reset_cb() + assert lib.foo(6) == 41 + my_callback = ffi.callback("int(*)(int)", lambda n: n * 222) + lib.cb = my_callback + assert lib.foo(4) == 887 + +def test_call_with_struct_ptr(): + ffi = FFI() + ffi.cdef("typedef struct { int x; ...; } foo_t; int foo(foo_t *);") + lib = ffi.verify(""" + typedef struct { int y, x; } foo_t; + static int foo(foo_t *f) { return f->x * 7; } + """) + f = ffi.new("foo_t *") + f.x = 6 + assert lib.foo(f) == 42 + +def test_unknown_type(): + ffi = FFI() + ffi.cdef(""" + typedef ... token_t; + int foo(token_t *); + #define TOKEN_SIZE ... + """) + lib = ffi.verify(""" + typedef float token_t; + static int foo(token_t *tk) { + if (!tk) + return -42; + *tk += 1.601f; + return (int)*tk; + } + #define TOKEN_SIZE sizeof(token_t) + """) + # we cannot let ffi.new("token_t *") work, because we don't know ahead of + # time if it's ok to ask 'sizeof(token_t)' in the C code or not. + # See test_unknown_type_2. Workaround. + tkmem = ffi.new("char[]", lib.TOKEN_SIZE) # zero-initialized + tk = ffi.cast("token_t *", tkmem) + results = [lib.foo(tk) for i in range(6)] + assert results == [1, 3, 4, 6, 8, 9] + assert lib.foo(ffi.NULL) == -42 + +def test_unknown_type_2(): + ffi = FFI() + ffi.cdef("typedef ... token_t;") + lib = ffi.verify("typedef struct token_s token_t;") + # assert did not crash, even though 'sizeof(token_t)' is not valid in C. + +def test_unknown_type_3(): + ffi = FFI() + ffi.cdef(""" + typedef ... *token_p; + token_p foo(token_p); + """) + lib = ffi.verify(""" + typedef struct _token_s *token_p; + token_p foo(token_p arg) { + if (arg) + return (token_p)0x12347; + else + return (token_p)0x12345; + } + """) + p = lib.foo(ffi.NULL) + assert int(ffi.cast("intptr_t", p)) == 0x12345 + q = lib.foo(p) + assert int(ffi.cast("intptr_t", q)) == 0x12347 + +def test_varargs(): + ffi = FFI() + ffi.cdef("int foo(int x, ...);") + lib = ffi.verify(""" + int foo(int x, ...) { + va_list vargs; + va_start(vargs, x); + x -= va_arg(vargs, int); + x -= va_arg(vargs, int); + va_end(vargs); + return x; + } + """) + assert lib.foo(50, ffi.cast("int", 5), ffi.cast("int", 3)) == 42 + +def test_varargs_exact(): + if sys.platform == 'win32': + py.test.skip("XXX fixme: only gives warnings") + ffi = FFI() + ffi.cdef("int foo(int x, ...);") + py.test.raises(VerificationError, ffi.verify, """ + int foo(long long x, ...) { + return x; + } + """) + +def test_varargs_struct(): + ffi = FFI() + ffi.cdef("struct foo_s { char a; int b; }; int foo(int x, ...);") + lib = ffi.verify(""" + struct foo_s { + char a; int b; + }; + int foo(int x, ...) { + va_list vargs; + struct foo_s s; + va_start(vargs, x); + s = va_arg(vargs, struct foo_s); + va_end(vargs); + return s.a - s.b; + } + """) + s = ffi.new("struct foo_s *", [b'B', 1]) + assert lib.foo(50, s[0]) == ord('A') + +def test_autofilled_struct_as_argument(): + ffi = FFI() + ffi.cdef("struct foo_s { long a; double b; ...; };\n" + "int foo(struct foo_s);") + lib = ffi.verify(""" + struct foo_s { + double b; + long a; + }; + int foo(struct foo_s s) { + return (int)s.a - (int)s.b; + } + """) + s = ffi.new("struct foo_s *", [100, 1]) + assert lib.foo(s[0]) == 99 + assert lib.foo([100, 1]) == 99 + +def test_autofilled_struct_as_argument_dynamic(): + ffi = FFI() + ffi.cdef("struct foo_s { long a; ...; };\n" + "int (*foo)(struct foo_s);") + lib = ffi.verify(""" + struct foo_s { + double b; + long a; + }; + int foo1(struct foo_s s) { + return (int)s.a - (int)s.b; + } + int (*foo)(struct foo_s s) = &foo1; + """) + e = py.test.raises(NotImplementedError, lib.foo, "?") + msg = ("ctype 'struct foo_s' not supported as argument. It is a struct " + 'declared with "...;", but the C calling convention may depend on ' + "the missing fields; or, it contains anonymous struct/unions. " + "Such structs are only supported as argument " + "if the function is 'API mode' and non-variadic (i.e. declared " + "inside ffibuilder.cdef()+ffibuilder.set_source() and not taking " + "a final '...' argument)") + assert str(e.value) == msg + +def test_func_returns_struct(): + ffi = FFI() + ffi.cdef(""" + struct foo_s { int aa, bb; }; + struct foo_s foo(int a, int b); + """) + lib = ffi.verify(""" + struct foo_s { int aa, bb; }; + struct foo_s foo(int a, int b) { + struct foo_s r; + r.aa = a*a; + r.bb = b*b; + return r; + } + """) + s = lib.foo(6, 7) + assert repr(s) == "<cdata 'struct foo_s' owning 8 bytes>" + assert s.aa == 36 + assert s.bb == 49 + +def test_func_as_funcptr(): + ffi = FFI() + ffi.cdef("int *(*const fooptr)(void);") + lib = ffi.verify(""" + int *foo(void) { + return (int*)"foobar"; + } + int *(*fooptr)(void) = foo; + """) + foochar = ffi.cast("char *(*)(void)", lib.fooptr) + s = foochar() + assert ffi.string(s) == b"foobar" + +def test_funcptr_as_argument(): + ffi = FFI() + ffi.cdef(""" + void qsort(void *base, size_t nel, size_t width, + int (*compar)(const void *, const void *)); + """) + ffi.verify("#include <stdlib.h>") + +def test_func_as_argument(): + ffi = FFI() + ffi.cdef(""" + void qsort(void *base, size_t nel, size_t width, + int compar(const void *, const void *)); + """) + ffi.verify("#include <stdlib.h>") + +def test_array_as_argument(): + ffi = FFI() + ffi.cdef(""" + size_t strlen(char string[]); + """) + ffi.verify("#include <string.h>") + +def test_enum_as_argument(): + ffi = FFI() + ffi.cdef(""" + enum foo_e { AA, BB, ... }; + int foo_func(enum foo_e); + """) + lib = ffi.verify(""" + enum foo_e { AA, CC, BB }; + int foo_func(enum foo_e e) { return (int)e; } + """) + assert lib.foo_func(lib.BB) == 2 + py.test.raises(TypeError, lib.foo_func, "BB") + +def test_enum_as_function_result(): + ffi = FFI() + ffi.cdef(""" + enum foo_e { AA, BB, ... }; + enum foo_e foo_func(int x); + """) + lib = ffi.verify(""" + enum foo_e { AA, CC, BB }; + enum foo_e foo_func(int x) { return (enum foo_e)x; } + """) + assert lib.foo_func(lib.BB) == lib.BB == 2 + +def test_enum_values(): + ffi = FFI() + ffi.cdef("enum enum1_e { AA, BB };") + lib = ffi.verify("enum enum1_e { AA, BB };") + assert lib.AA == 0 + assert lib.BB == 1 + assert ffi.string(ffi.cast("enum enum1_e", 1)) == 'BB' + +def test_typedef_complete_enum(): + ffi = FFI() + ffi.cdef("typedef enum { AA, BB } enum1_t;") + lib = ffi.verify("typedef enum { AA, BB } enum1_t;") + assert ffi.string(ffi.cast("enum1_t", 1)) == 'BB' + assert lib.AA == 0 + assert lib.BB == 1 + +def test_typedef_broken_complete_enum(): + # xxx this is broken in old cffis, but works with recompiler.py + ffi = FFI() + ffi.cdef("typedef enum { AA, BB } enum1_t;") + lib = ffi.verify("typedef enum { AA, CC, BB } enum1_t;") + assert lib.AA == 0 + assert lib.BB == 2 + +def test_typedef_incomplete_enum(): + ffi = FFI() + ffi.cdef("typedef enum { AA, BB, ... } enum1_t;") + lib = ffi.verify("typedef enum { AA, CC, BB } enum1_t;") + assert ffi.string(ffi.cast("enum1_t", 1)) == '1' + assert ffi.string(ffi.cast("enum1_t", 2)) == 'BB' + assert lib.AA == 0 + assert lib.BB == 2 + +def test_typedef_enum_as_argument(): + ffi = FFI() + ffi.cdef(""" + typedef enum { AA, BB, ... } foo_t; + int foo_func(foo_t); + """) + lib = ffi.verify(""" + typedef enum { AA, CC, BB } foo_t; + int foo_func(foo_t e) { return (int)e; } + """) + assert lib.foo_func(lib.BB) == lib.BB == 2 + py.test.raises(TypeError, lib.foo_func, "BB") + +def test_typedef_enum_as_function_result(): + ffi = FFI() + ffi.cdef(""" + typedef enum { AA, BB, ... } foo_t; + foo_t foo_func(int x); + """) + lib = ffi.verify(""" + typedef enum { AA, CC, BB } foo_t; + foo_t foo_func(int x) { return (foo_t)x; } + """) + assert lib.foo_func(lib.BB) == lib.BB == 2 + +def test_function_typedef(): + ffi = FFI() + ffi.cdef(""" + typedef double func_t(double); + func_t sin; + """) + lib = ffi.verify('#include <math.h>', libraries=lib_m) + assert lib.sin(1.23) == math.sin(1.23) + +def test_opaque_integer_as_function_result(): + #import platform + #if platform.machine().startswith('sparc'): + # py.test.skip('Breaks horribly on sparc (SIGILL + corrupted stack)') + #elif platform.machine() == 'mips64' and sys.maxsize > 2**32: + # py.test.skip('Segfaults on mips64el') + # XXX bad abuse of "struct { ...; }". It only works a bit by chance + # anyway. XXX think about something better :-( + ffi = FFI() + ffi.cdef(""" + typedef struct { ...; } myhandle_t; + myhandle_t foo(void); + """) + lib = ffi.verify(""" + typedef short myhandle_t; + myhandle_t foo(void) { return 42; } + """) + h = lib.foo() + assert ffi.sizeof(h) == ffi.sizeof("short") + +def test_return_partial_struct(): + ffi = FFI() + ffi.cdef(""" + typedef struct { int x; ...; } foo_t; + foo_t foo(void); + """) + lib = ffi.verify(""" + typedef struct { int y, x; } foo_t; + foo_t foo(void) { foo_t r = { 45, 81 }; return r; } + """) + h = lib.foo() + assert ffi.sizeof(h) == 2 * ffi.sizeof("int") + assert h.x == 81 + +def test_take_and_return_partial_structs(): + ffi = FFI() + ffi.cdef(""" + typedef struct { int x; ...; } foo_t; + foo_t foo(foo_t, foo_t); + """) + lib = ffi.verify(""" + typedef struct { int y, x; } foo_t; + foo_t foo(foo_t a, foo_t b) { + foo_t r = { 100, a.x * 5 + b.x * 7 }; + return r; + } + """) + args = ffi.new("foo_t[3]") + args[0].x = 1000 + args[2].x = -498 + h = lib.foo(args[0], args[2]) + assert ffi.sizeof(h) == 2 * ffi.sizeof("int") + assert h.x == 1000 * 5 - 498 * 7 + +def test_cannot_name_struct_type(): + ffi = FFI() + ffi.cdef("typedef struct { int x; } **sp; void foo(sp);") + e = py.test.raises(VerificationError, ffi.verify, + "typedef struct { int x; } **sp; void foo(sp x) { }") + assert 'in argument of foo: unknown type name' in str(e.value) + +def test_dont_check_unnamable_fields(): + ffi = FFI() + ffi.cdef("struct foo_s { struct { int x; } someone; };") + ffi.verify("struct foo_s { struct { int x; } someone; };") + # assert did not crash + +def test_nested_anonymous_struct_exact(): + if sys.platform == 'win32': + py.test.skip("nested anonymous struct/union") + ffi = FFI() + ffi.cdef(""" + struct foo_s { struct { int a; char b; }; union { char c, d; }; }; + """) + assert ffi.offsetof("struct foo_s", "c") == 2 * ffi.sizeof("int") + assert ffi.sizeof("struct foo_s") == 3 * ffi.sizeof("int") + ffi.verify(""" + struct foo_s { struct { int a; char b; }; union { char c, d; }; }; + """) + p = ffi.new("struct foo_s *") + assert ffi.sizeof(p[0]) == 3 * ffi.sizeof("int") # with alignment + p.a = 1234567 + p.b = b'X' + p.c = b'Y' + assert p.a == 1234567 + assert p.b == b'X' + assert p.c == b'Y' + assert p.d == b'Y' + +def test_nested_anonymous_struct_exact_error(): + if sys.platform == 'win32': + py.test.skip("nested anonymous struct/union") + ffi = FFI() + ffi.cdef(""" + struct foo_s { struct { int a; char b; }; union { char c, d; }; }; + """) + py.test.raises(VerificationError, ffi.verify, """ + struct foo_s { struct { int a; short b; }; union { char c, d; }; }; + """) + # works fine now + #py.test.raises(VerificationError, ffi.verify, """ + # struct foo_s { struct { int a; char e, b; }; union { char c, d; }; }; + #""") + +def test_nested_anonymous_struct_inexact_1(): + ffi = FFI() + ffi.cdef(""" + struct foo_s { struct { char b; ...; }; union { char c, d; }; }; + """) + ffi.verify(""" + struct foo_s { int a, padding; char c, d, b; }; + """) + assert ffi.sizeof("struct foo_s") == 3 * ffi.sizeof("int") + +def test_nested_anonymous_struct_inexact_2(): + ffi = FFI() + ffi.cdef(""" + struct foo_s { union { char c, d; }; struct { int a; char b; }; ...; }; + """) + ffi.verify(""" + struct foo_s { int a, padding; char c, d, b; }; + """) + assert ffi.sizeof("struct foo_s") == 3 * ffi.sizeof("int") + +def test_ffi_union(): + ffi = FFI() + ffi.cdef("union foo_u { char x; long *z; };") + ffi.verify("union foo_u { char x; int y; long *z; };") + +def test_ffi_union_partial(): + ffi = FFI() + ffi.cdef("union foo_u { char x; ...; };") + ffi.verify("union foo_u { char x; int y; };") + assert ffi.sizeof("union foo_u") == 4 + +def test_ffi_union_with_partial_struct(): + ffi = FFI() + ffi.cdef("struct foo_s { int x; ...; }; union foo_u { struct foo_s s; };") + ffi.verify("struct foo_s { int a; int x; }; " + "union foo_u { char b[32]; struct foo_s s; };") + assert ffi.sizeof("struct foo_s") == 8 + assert ffi.sizeof("union foo_u") == 32 + +def test_ffi_union_partial_2(): + ffi = FFI() + ffi.cdef("typedef union { char x; ...; } u1;") + ffi.verify("typedef union { char x; int y; } u1;") + assert ffi.sizeof("u1") == 4 + +def test_ffi_union_with_partial_struct_2(): + ffi = FFI() + ffi.cdef("typedef struct { int x; ...; } s1;" + "typedef union { s1 s; } u1;") + ffi.verify("typedef struct { int a; int x; } s1; " + "typedef union { char b[32]; s1 s; } u1;") + assert ffi.sizeof("s1") == 8 + assert ffi.sizeof("u1") == 32 + assert ffi.offsetof("u1", "s") == 0 + +def test_ffi_struct_packed(): + if sys.platform == 'win32': + py.test.skip("needs a GCC extension") + ffi = FFI() + ffi.cdef("struct foo_s { int b; ...; };") + ffi.verify(""" + struct foo_s { + char a; + int b; + } __attribute__((packed)); + """) + +def test_tmpdir(): + import tempfile, os + from testing.udir import udir + tmpdir = tempfile.mkdtemp(dir=str(udir)) + ffi = FFI() + ffi.cdef("int foo(int);") + lib = ffi.verify("int foo(int a) { return a + 42; }", tmpdir=tmpdir) + assert os.listdir(tmpdir) + assert lib.foo(100) == 142 + +def test_relative_to(): + py.test.skip("not available") + import tempfile, os + from testing.udir import udir + tmpdir = tempfile.mkdtemp(dir=str(udir)) + ffi = FFI() + ffi.cdef("int foo(int);") + f = open(os.path.join(tmpdir, 'foo.h'), 'w') + f.write("int foo(int a) { return a + 42; }\n") + f.close() + lib = ffi.verify('#include "foo.h"', + include_dirs=['.'], + relative_to=os.path.join(tmpdir, 'x')) + assert lib.foo(100) == 142 + +def test_bug1(): + ffi = FFI() + ffi.cdef(""" + typedef struct tdlhandle_s { ...; } *tdl_handle_t; + typedef struct my_error_code_ { + tdl_handle_t *rh; + } my_error_code_t; + """) + ffi.verify(""" + typedef struct tdlhandle_s { int foo; } *tdl_handle_t; + typedef struct my_error_code_ { + tdl_handle_t *rh; + } my_error_code_t; + """) + +def test_bool(): + if sys.platform == 'win32': + py.test.skip("_Bool not in MSVC") + ffi = FFI() + ffi.cdef("struct foo_s { _Bool x; };" + "_Bool foo(_Bool); _Bool (*foop)(_Bool);") + lib = ffi.verify(""" + struct foo_s { _Bool x; }; + int foo(int arg) { + return !arg; + } + _Bool _foofunc(_Bool x) { + return !x; + } + _Bool (*foop)(_Bool) = _foofunc; + """) + p = ffi.new("struct foo_s *") + p.x = 1 + assert p.x is True + py.test.raises(OverflowError, "p.x = -1") + py.test.raises(TypeError, "p.x = 0.0") + assert lib.foop(1) is False + assert lib.foop(True) is False + assert lib.foop(0) is True + py.test.raises(OverflowError, lib.foop, 42) + py.test.raises(TypeError, lib.foop, 0.0) + assert lib.foo(1) is False + assert lib.foo(True) is False + assert lib.foo(0) is True + py.test.raises(OverflowError, lib.foo, 42) + py.test.raises(TypeError, lib.foo, 0.0) + assert int(ffi.cast("_Bool", long(1))) == 1 + assert int(ffi.cast("_Bool", long(0))) == 0 + assert int(ffi.cast("_Bool", long(-1))) == 1 + assert int(ffi.cast("_Bool", 10**200)) == 1 + assert int(ffi.cast("_Bool", 10**40000)) == 1 + # + class Foo(object): + def __int__(self): + self.seen = 1 + return result + f = Foo() + f.seen = 0 + result = 42 + assert int(ffi.cast("_Bool", f)) == 1 + assert f.seen + f.seen = 0 + result = 0 + assert int(ffi.cast("_Bool", f)) == 0 + assert f.seen + # + py.test.raises(TypeError, ffi.cast, "_Bool", []) + +def test_bool_on_long_double(): + if sys.platform == 'win32': + py.test.skip("_Bool not in MSVC") + f = 1E-250 + if f == 0.0 or f*f != 0.0: + py.test.skip("unexpected precision") + ffi = FFI() + ffi.cdef("long double square(long double f); _Bool opposite(_Bool);") + lib = ffi.verify("long double square(long double f) { return f*f; }\n" + "_Bool opposite(_Bool x) { return !x; }") + f0 = lib.square(0.0) + f2 = lib.square(f) + f3 = lib.square(f * 2.0) + if repr(f2) == repr(f3): + py.test.skip("long double doesn't have enough precision") + assert float(f0) == float(f2) == float(f3) == 0.0 # too tiny for 'double' + assert int(ffi.cast("_Bool", f2)) == 1 + assert int(ffi.cast("_Bool", f3)) == 1 + assert int(ffi.cast("_Bool", f0)) == 0 + py.test.raises(TypeError, lib.opposite, f2) + +def test_cannot_pass_float(): + for basetype in ['char', 'short', 'int', 'long', 'long long']: + for sign in ['signed', 'unsigned']: + type = '%s %s' % (sign, basetype) + ffi = FFI() + ffi.cdef("struct foo_s { %s x; };\n" + "int foo(%s);" % (type, type)) + lib = ffi.verify(""" + struct foo_s { %s x; }; + int foo(%s arg) { + return !arg; + } + """ % (type, type)) + p = ffi.new("struct foo_s *") + py.test.raises(TypeError, "p.x = 0.0") + assert lib.foo(42) == 0 + assert lib.foo(0) == 1 + py.test.raises(TypeError, lib.foo, 0.0) + +def test_addressof(): + ffi = FFI() + ffi.cdef(""" + struct point_s { int x, y; }; + struct foo_s { int z; struct point_s point; }; + struct point_s sum_coord(struct point_s *); + """) + lib = ffi.verify(""" + struct point_s { int x, y; }; + struct foo_s { int z; struct point_s point; }; + struct point_s sum_coord(struct point_s *point) { + struct point_s r; + r.x = point->x + point->y; + r.y = point->x - point->y; + return r; + } + """) + p = ffi.new("struct foo_s *") + p.point.x = 16 + p.point.y = 9 + py.test.raises(TypeError, lib.sum_coord, p.point) + res = lib.sum_coord(ffi.addressof(p.point)) + assert res.x == 25 + assert res.y == 7 + res2 = lib.sum_coord(ffi.addressof(res)) + assert res2.x == 32 + assert res2.y == 18 + py.test.raises(TypeError, lib.sum_coord, res2) + +def test_callback_in_thread(): + py.test.xfail("adapt or remove") + if sys.platform == 'win32': + py.test.skip("pthread only") + import os, subprocess, imp + arg = os.path.join(os.path.dirname(__file__), 'callback_in_thread.py') + g = subprocess.Popen([sys.executable, arg, + os.path.dirname(imp.find_module('cffi')[1])]) + result = g.wait() + assert result == 0 + +def test_keepalive_lib(): + py.test.xfail("adapt or remove") + ffi = FFI() + ffi.cdef("int foobar(void);") + lib = ffi.verify("int foobar(void) { return 42; }") + func = lib.foobar + ffi_r = weakref.ref(ffi) + lib_r = weakref.ref(lib) + del ffi + import gc; gc.collect() # lib stays alive + assert lib_r() is not None + assert ffi_r() is not None + assert func() == 42 + +def test_keepalive_ffi(): + py.test.xfail("adapt or remove") + ffi = FFI() + ffi.cdef("int foobar(void);") + lib = ffi.verify("int foobar(void) { return 42; }") + func = lib.foobar + ffi_r = weakref.ref(ffi) + lib_r = weakref.ref(lib) + del lib + import gc; gc.collect() # ffi stays alive + assert ffi_r() is not None + assert lib_r() is not None + assert func() == 42 + +def test_FILE_stored_in_stdout(): + if not sys.platform.startswith('linux'): + py.test.skip("likely, we cannot assign to stdout") + ffi = FFI() + ffi.cdef("int printf(const char *, ...); FILE *setstdout(FILE *);") + lib = ffi.verify(""" + #include <stdio.h> + FILE *setstdout(FILE *f) { + FILE *result = stdout; + stdout = f; + return result; + } + """) + import os + fdr, fdw = os.pipe() + fw1 = os.fdopen(fdw, 'wb', 256) + old_stdout = lib.setstdout(fw1) + try: + # + fw1.write(b"X") + r = lib.printf(b"hello, %d!\n", ffi.cast("int", 42)) + fw1.close() + assert r == len("hello, 42!\n") + # + finally: + lib.setstdout(old_stdout) + # + result = os.read(fdr, 256) + os.close(fdr) + # the 'X' might remain in the user-level buffer of 'fw1' and + # end up showing up after the 'hello, 42!\n' + assert result == b"Xhello, 42!\n" or result == b"hello, 42!\nX" + +def test_FILE_stored_explicitly(): + ffi = FFI() + ffi.cdef("int myprintf11(const char *, int); FILE *myfile;") + lib = ffi.verify(""" + #include <stdio.h> + FILE *myfile; + int myprintf11(const char *out, int value) { + return fprintf(myfile, out, value); + } + """) + import os + fdr, fdw = os.pipe() + fw1 = os.fdopen(fdw, 'wb', 256) + lib.myfile = ffi.cast("FILE *", fw1) + # + fw1.write(b"X") + r = lib.myprintf11(b"hello, %d!\n", ffi.cast("int", 42)) + fw1.close() + assert r == len("hello, 42!\n") + # + result = os.read(fdr, 256) + os.close(fdr) + # the 'X' might remain in the user-level buffer of 'fw1' and + # end up showing up after the 'hello, 42!\n' + assert result == b"Xhello, 42!\n" or result == b"hello, 42!\nX" + +def test_global_array_with_missing_length(): + ffi = FFI() + ffi.cdef("int fooarray[];") + lib = ffi.verify("int fooarray[50];") + assert repr(lib.fooarray).startswith("<cdata 'int *'") + +def test_global_array_with_dotdotdot_length(): + ffi = FFI() + ffi.cdef("int fooarray[...];") + lib = ffi.verify("int fooarray[50];") + assert repr(lib.fooarray).startswith("<cdata 'int[50]'") + +def test_bad_global_array_with_dotdotdot_length(): + py.test.xfail("was detected only because 23 bytes cannot be divided by 4; " + "redo more generally") + ffi = FFI() + ffi.cdef("int fooarray[...];") + py.test.raises(VerificationError, ffi.verify, "char fooarray[23];") + +def test_struct_containing_struct(): + ffi = FFI() + ffi.cdef("struct foo_s { ...; }; struct bar_s { struct foo_s f; ...; };") + ffi.verify("struct foo_s { int x; }; struct bar_s { struct foo_s f; };") + # + ffi = FFI() + ffi.cdef("struct foo_s { struct bar_s f; ...; }; struct bar_s { ...; };") + ffi.verify("struct bar_s { int x; }; struct foo_s { struct bar_s f; };") + +def test_struct_returned_by_func(): + ffi = FFI() + ffi.cdef("typedef ... foo_t; foo_t myfunc(void);") + e = py.test.raises(TypeError, ffi.verify, + "typedef struct { int x; } foo_t; " + "foo_t myfunc(void) { foo_t x = { 42 }; return x; }") + assert str(e.value) == ( + "function myfunc: 'foo_t' is used as result type, but is opaque") + +def test_include(): + ffi1 = FFI() + ffi1.cdef("typedef struct { int x; ...; } foo_t;") + ffi1.verify("typedef struct { int y, x; } foo_t;") + ffi2 = FFI() + ffi2.include(ffi1) + ffi2.cdef("int myfunc(foo_t *);") + lib = ffi2.verify("typedef struct { int y, x; } foo_t;" + "int myfunc(foo_t *p) { return 42 * p->x; }") + res = lib.myfunc(ffi2.new("foo_t *", {'x': 10})) + assert res == 420 + res = lib.myfunc(ffi1.new("foo_t *", {'x': -10})) + assert res == -420 + +def test_include_enum(): + ffi1 = FFI() + ffi1.cdef("enum foo_e { AA, ... };") + lib1 = ffi1.verify("enum foo_e { CC, BB, AA };") + ffi2 = FFI() + ffi2.include(ffi1) + ffi2.cdef("int myfunc(enum foo_e);") + lib2 = ffi2.verify("enum foo_e { CC, BB, AA };" + "int myfunc(enum foo_e x) { return (int)x; }") + res = lib2.myfunc(lib2.AA) + assert res == 2 + +def test_named_pointer_as_argument(): + ffi = FFI() + ffi.cdef("typedef struct { int x; } *mystruct_p;\n" + "mystruct_p ff5a(mystruct_p);") + lib = ffi.verify("typedef struct { int x; } *mystruct_p;\n" + "mystruct_p ff5a(mystruct_p p) { p->x += 40; return p; }") + p = ffi.new("mystruct_p", [-2]) + q = lib.ff5a(p) + assert q == p + assert p.x == 38 + +def test_enum_size(): + cases = [('123', 4, 4294967295), + ('4294967295U', 4, 4294967295), + ('-123', 4, -1), + ('-2147483647-1', 4, -1), + ] + if FFI().sizeof("long") == 8: + cases += [('4294967296L', 8, 2**64-1), + ('%dUL' % (2**64-1), 8, 2**64-1), + ('-2147483649L', 8, -1), + ('%dL-1L' % (1-2**63), 8, -1)] + for hidden_value, expected_size, expected_minus1 in cases: + if sys.platform == 'win32' and 'U' in hidden_value: + continue # skipped on Windows + ffi = FFI() + ffi.cdef("enum foo_e { AA, BB, ... };") + lib = ffi.verify("enum foo_e { AA, BB=%s };" % hidden_value) + assert lib.AA == 0 + assert lib.BB == eval(hidden_value.replace('U', '').replace('L', '')) + assert ffi.sizeof("enum foo_e") == expected_size + if sys.platform != 'win32': + assert int(ffi.cast("enum foo_e", -1)) == expected_minus1 + # test with the large value hidden: + # disabled so far, doesn't work +## for hidden_value, expected_size, expected_minus1 in cases: +## ffi = FFI() +## ffi.cdef("enum foo_e { AA, BB, ... };") +## lib = ffi.verify("enum foo_e { AA, BB=%s };" % hidden_value) +## assert lib.AA == 0 +## assert ffi.sizeof("enum foo_e") == expected_size +## assert int(ffi.cast("enum foo_e", -1)) == expected_minus1 + +def test_enum_bug118(): + maxulong = 256 ** FFI().sizeof("unsigned long") - 1 + for c2, c2c in [(-1, ''), + (-1, ''), + (0xffffffff, 'U'), + (maxulong, 'UL'), + (-int(maxulong / 3), 'L')]: + if c2c and sys.platform == 'win32': + continue # enums may always be signed with MSVC + ffi = FFI() + ffi.cdef("enum foo_e { AA };") + lib = ffi.verify("enum foo_e { AA=%s%s };" % (c2, c2c)) + assert lib.AA == c2 + +def test_string_to_voidp_arg(): + ffi = FFI() + ffi.cdef("int myfunc(void *);") + lib = ffi.verify("int myfunc(void *p) { return ((signed char *)p)[0]; }") + res = lib.myfunc(b"hi!") + assert res == ord(b"h") + p = ffi.new("char[]", b"gah") + res = lib.myfunc(p) + assert res == ord(b"g") + res = lib.myfunc(ffi.cast("void *", p)) + assert res == ord(b"g") + res = lib.myfunc(ffi.cast("int *", p)) + assert res == ord(b"g") + +def test_callback_indirection(): + ffi = FFI() + ffi.cdef(""" + int (*python_callback)(int how_many, int *values); + int (*const c_callback)(int,...); /* pass this ptr to C routines */ + int some_c_function(int(*cb)(int,...)); + """) + lib = ffi.verify(""" + #include <stdarg.h> + #ifdef _WIN32 + #include <malloc.h> + #define alloca _alloca + #else + # ifdef __FreeBSD__ + # include <stdlib.h> + # else + # include <alloca.h> + # endif + #endif + static int (*python_callback)(int how_many, int *values); + static int c_callback(int how_many, ...) { + va_list ap; + /* collect the "..." arguments into the values[] array */ + int i, *values = alloca((size_t)how_many * sizeof(int)); + va_start(ap, how_many); + for (i=0; i<how_many; i++) + values[i] = va_arg(ap, int); + va_end(ap); + return python_callback(how_many, values); + } + int some_c_function(int(*cb)(int,...)) { + int result = cb(2, 10, 20); + result += cb(3, 30, 40, 50); + return result; + } + """) + seen = [] + @ffi.callback("int(int, int*)") + def python_callback(how_many, values): + seen.append([values[i] for i in range(how_many)]) + return 42 + lib.python_callback = python_callback + + res = lib.some_c_function(lib.c_callback) + assert res == 84 + assert seen == [[10, 20], [30, 40, 50]] + +def test_floatstar_argument(): + ffi = FFI() + ffi.cdef("float sum3floats(float *);") + lib = ffi.verify(""" + float sum3floats(float *f) { + return f[0] + f[1] + f[2]; + } + """) + assert lib.sum3floats((1.5, 2.5, 3.5)) == 7.5 + p = ffi.new("float[]", (1.5, 2.5, 3.5)) + assert lib.sum3floats(p) == 7.5 + +def test_charstar_argument(): + ffi = FFI() + ffi.cdef("char sum3chars(char *);") + lib = ffi.verify(""" + char sum3chars(char *f) { + return (char)(f[0] + f[1] + f[2]); + } + """) + assert lib.sum3chars((b'\x10', b'\x20', b'\x30')) == b'\x60' + p = ffi.new("char[]", b'\x10\x20\x30') + assert lib.sum3chars(p) == b'\x60' + +def test_passing_string_or_NULL(): + ffi = FFI() + ffi.cdef("int seeme1(char *); int seeme2(int *);") + lib = ffi.verify(""" + int seeme1(char *x) { + return (x == NULL); + } + int seeme2(int *x) { + return (x == NULL); + } + """) + assert lib.seeme1(b"foo") == 0 + assert lib.seeme1(ffi.NULL) == 1 + assert lib.seeme2([42, 43]) == 0 + assert lib.seeme2(ffi.NULL) == 1 + py.test.raises(TypeError, lib.seeme1, None) + py.test.raises(TypeError, lib.seeme2, None) + py.test.raises(TypeError, lib.seeme1, 0.0) + py.test.raises(TypeError, lib.seeme2, 0.0) + py.test.raises(TypeError, lib.seeme1, 0) + py.test.raises(TypeError, lib.seeme2, 0) + zeroL = 99999999999999999999 + zeroL -= 99999999999999999999 + py.test.raises(TypeError, lib.seeme2, zeroL) + +def test_typeof_function(): + ffi = FFI() + ffi.cdef("int foo(int, char);") + lib = ffi.verify("int foo(int x, char y) { (void)x; (void)y; return 42; }") + ctype = ffi.typeof(lib.foo) + assert len(ctype.args) == 2 + assert ctype.result == ffi.typeof("int") + +def test_call_with_voidstar_arg(): + ffi = FFI() + ffi.cdef("int f(void *);") + lib = ffi.verify("int f(void *x) { return ((char*)x)[0]; }") + assert lib.f(b"foobar") == ord(b"f") + +def test_dir(): + ffi = FFI() + ffi.cdef("""void somefunc(void); + extern int somevar, somearray[2]; + static char *const sv2; + enum my_e { AA, BB, ... }; + #define FOO ...""") + lib = ffi.verify("""void somefunc(void) { } + int somevar, somearray[2]; + #define sv2 "text" + enum my_e { AA, BB }; + #define FOO 42""") + assert dir(lib) == ['AA', 'BB', 'FOO', 'somearray', + 'somefunc', 'somevar', 'sv2'] + +def test_typeof_func_with_struct_argument(): + ffi = FFI() + ffi.cdef("""struct s { int a; }; int foo(struct s);""") + lib = ffi.verify("""struct s { int a; }; + int foo(struct s x) { return x.a; }""") + s = ffi.new("struct s *", [-1234]) + m = lib.foo(s[0]) + assert m == -1234 + assert repr(ffi.typeof(lib.foo)) == "<ctype 'int(*)(struct s)'>" + +def test_bug_const_char_ptr_array_1(): + ffi = FFI() + ffi.cdef("""const char *a[...];""") + lib = ffi.verify("""const char *a[5];""") + assert repr(ffi.typeof(lib.a)) == "<ctype 'char *[5]'>" + +def test_bug_const_char_ptr_array_2(): + ffi = FFI() + ffi.cdef("""const int a[];""") + lib = ffi.verify("""const int a[5];""") + assert repr(ffi.typeof(lib.a)) == "<ctype 'int *'>" + +def _test_various_calls(force_libffi): + cdef_source = """ + int xvalue; + long long ivalue, rvalue; + float fvalue; + double dvalue; + long double Dvalue; + signed char tf_bb(signed char x, signed char c); + unsigned char tf_bB(signed char x, unsigned char c); + short tf_bh(signed char x, short c); + unsigned short tf_bH(signed char x, unsigned short c); + int tf_bi(signed char x, int c); + unsigned int tf_bI(signed char x, unsigned int c); + long tf_bl(signed char x, long c); + unsigned long tf_bL(signed char x, unsigned long c); + long long tf_bq(signed char x, long long c); + unsigned long long tf_bQ(signed char x, unsigned long long c); + float tf_bf(signed char x, float c); + double tf_bd(signed char x, double c); + long double tf_bD(signed char x, long double c); + """ + if force_libffi: + cdef_source = (cdef_source + .replace('tf_', '(*const tf_') + .replace('(signed char x', ')(signed char x')) + ffi = FFI() + ffi.cdef(cdef_source) + lib = ffi.verify(""" + int xvalue; + long long ivalue, rvalue; + float fvalue; + double dvalue; + long double Dvalue; + + typedef signed char b_t; + typedef unsigned char B_t; + typedef short h_t; + typedef unsigned short H_t; + typedef int i_t; + typedef unsigned int I_t; + typedef long l_t; + typedef unsigned long L_t; + typedef long long q_t; + typedef unsigned long long Q_t; + typedef float f_t; + typedef double d_t; + typedef long double D_t; + #define S(letter) xvalue = (int)x; letter##value = (letter##_t)c; + #define R(letter) return (letter##_t)rvalue; + + signed char tf_bb(signed char x, signed char c) { S(i) R(b) } + unsigned char tf_bB(signed char x, unsigned char c) { S(i) R(B) } + short tf_bh(signed char x, short c) { S(i) R(h) } + unsigned short tf_bH(signed char x, unsigned short c) { S(i) R(H) } + int tf_bi(signed char x, int c) { S(i) R(i) } + unsigned int tf_bI(signed char x, unsigned int c) { S(i) R(I) } + long tf_bl(signed char x, long c) { S(i) R(l) } + unsigned long tf_bL(signed char x, unsigned long c) { S(i) R(L) } + long long tf_bq(signed char x, long long c) { S(i) R(q) } + unsigned long long tf_bQ(signed char x, unsigned long long c) { S(i) R(Q) } + float tf_bf(signed char x, float c) { S(f) R(f) } + double tf_bd(signed char x, double c) { S(d) R(d) } + long double tf_bD(signed char x, long double c) { S(D) R(D) } + """) + lib.rvalue = 0x7182838485868788 + for kind, cname in [('b', 'signed char'), + ('B', 'unsigned char'), + ('h', 'short'), + ('H', 'unsigned short'), + ('i', 'int'), + ('I', 'unsigned int'), + ('l', 'long'), + ('L', 'unsigned long'), + ('q', 'long long'), + ('Q', 'unsigned long long'), + ('f', 'float'), + ('d', 'double'), + ('D', 'long double')]: + sign = +1 if 'unsigned' in cname else -1 + lib.xvalue = 0 + lib.ivalue = 0 + lib.fvalue = 0 + lib.dvalue = 0 + lib.Dvalue = 0 + fun = getattr(lib, 'tf_b' + kind) + res = fun(-42, sign * 99) + if kind == 'D': + res = float(res) + assert res == int(ffi.cast(cname, 0x7182838485868788)) + assert lib.xvalue == -42 + if kind in 'fdD': + assert float(getattr(lib, kind + 'value')) == -99.0 + else: + assert lib.ivalue == sign * 99 + +def test_various_calls_direct(): + _test_various_calls(force_libffi=False) + +def test_various_calls_libffi(): + _test_various_calls(force_libffi=True) + +def test_ptr_to_opaque(): + ffi = FFI() + ffi.cdef("typedef ... foo_t; int f1(foo_t*); foo_t *f2(int);") + lib = ffi.verify(""" + #include <stdlib.h> + typedef struct { int x; } foo_t; + int f1(foo_t* p) { + int x = p->x; + free(p); + return x; + } + foo_t *f2(int x) { + foo_t *p = malloc(sizeof(foo_t)); + p->x = x; + return p; + } + """) + p = lib.f2(42) + x = lib.f1(p) + assert x == 42 + +def _run_in_multiple_threads(test1): + test1() + import sys + try: + import thread + except ImportError: + import _thread as thread + errors = [] + def wrapper(lock): + try: + test1() + except: + errors.append(sys.exc_info()) + lock.release() + locks = [] + for i in range(10): + _lock = thread.allocate_lock() + _lock.acquire() + thread.start_new_thread(wrapper, (_lock,)) + locks.append(_lock) + for _lock in locks: + _lock.acquire() + if errors: + raise errors[0][1] + +def test_errno_working_even_with_pypys_jit(): + ffi = FFI() + ffi.cdef("int f(int);") + lib = ffi.verify(""" + #include <errno.h> + int f(int x) { return (errno = errno + x); } + """) + @_run_in_multiple_threads + def test1(): + ffi.errno = 0 + for i in range(10000): + e = lib.f(1) + assert e == i + 1 + assert ffi.errno == e + for i in range(10000): + ffi.errno = i + e = lib.f(42) + assert e == i + 42 + +def test_getlasterror_working_even_with_pypys_jit(): + if sys.platform != 'win32': + py.test.skip("win32-only test") + ffi = FFI() + ffi.cdef("void SetLastError(DWORD);") + lib = ffi.dlopen("Kernel32.dll") + @_run_in_multiple_threads + def test1(): + for i in range(10000): + n = (1 << 29) + i + lib.SetLastError(n) + assert ffi.getwinerror()[0] == n + +def test_verify_dlopen_flags(): + if not hasattr(sys, 'setdlopenflags'): + py.test.skip("requires sys.setdlopenflags()") + # Careful with RTLD_GLOBAL. If by chance the FFI is not deleted + # promptly, like on PyPy, then other tests may see the same + # exported symbols as well. So we must not export a simple name + # like 'foo'! + old = sys.getdlopenflags() + try: + ffi1 = FFI() + ffi1.cdef("int foo_verify_dlopen_flags_1;") + sys.setdlopenflags(ffi1.RTLD_GLOBAL | ffi1.RTLD_NOW) + lib1 = ffi1.verify("int foo_verify_dlopen_flags_1;") + finally: + sys.setdlopenflags(old) + + ffi2 = FFI() + ffi2.cdef("int *getptr(void);") + lib2 = ffi2.verify(""" + extern int foo_verify_dlopen_flags_1; + static int *getptr(void) { return &foo_verify_dlopen_flags_1; } + """) + p = lib2.getptr() + assert ffi1.addressof(lib1, 'foo_verify_dlopen_flags_1') == p + +def test_consider_not_implemented_function_type(): + ffi = FFI() + ffi.cdef("typedef union { int a; float b; } Data;" + "typedef struct { int a:2; } MyStr;" + "typedef void (*foofunc_t)(Data);" + "typedef Data (*bazfunc_t)(void);" + "typedef MyStr (*barfunc_t)(void);") + fooptr = ffi.cast("foofunc_t", 123) + bazptr = ffi.cast("bazfunc_t", 123) + barptr = ffi.cast("barfunc_t", 123) + # assert did not crash so far + e = py.test.raises(NotImplementedError, fooptr, ffi.new("Data *")) + assert str(e.value) == ( + "ctype 'Data' not supported as argument by libffi. Unions are only " + "supported as argument if the function is 'API mode' and " + "non-variadic (i.e. declared inside ffibuilder.cdef()+" + "ffibuilder.set_source() and not taking a final '...' argument)") + e = py.test.raises(NotImplementedError, bazptr) + assert str(e.value) == ( + "ctype 'Data' not supported as return value by libffi. Unions are " + "only supported as return value if the function is 'API mode' and " + "non-variadic (i.e. declared inside ffibuilder.cdef()+" + "ffibuilder.set_source() and not taking a final '...' argument)") + e = py.test.raises(NotImplementedError, barptr) + assert str(e.value) == ( + "ctype 'MyStr' not supported as return value. It is a struct with " + "bit fields, which libffi does not support. Such structs are only " + "supported as return value if the function is 'API mode' and non-" + "variadic (i.e. declared inside ffibuilder.cdef()+ffibuilder." + "set_source() and not taking a final '...' argument)") + +def test_verify_extra_arguments(): + ffi = FFI() + ffi.cdef("#define ABA ...") + lib = ffi.verify("", define_macros=[('ABA', '42')]) + assert lib.ABA == 42 + +def test_implicit_unicode_on_windows(): + from cffi import FFIError + if sys.platform != 'win32': + py.test.skip("win32-only test") + ffi = FFI() + e = py.test.raises(FFIError, ffi.cdef, "int foo(LPTSTR);") + assert str(e.value) == ("The Windows type 'LPTSTR' is only available after" + " you call ffi.set_unicode()") + for with_unicode in [True, False]: + ffi = FFI() + ffi.set_unicode(with_unicode) + ffi.cdef(""" + DWORD GetModuleFileName(HMODULE hModule, LPTSTR lpFilename, + DWORD nSize); + """) + lib = ffi.verify(""" + #include <windows.h> + """, libraries=['Kernel32']) + outbuf = ffi.new("TCHAR[]", 200) + n = lib.GetModuleFileName(ffi.NULL, outbuf, 500) + assert 0 < n < 500 + for i in range(n): + #print repr(outbuf[i]) + assert ord(outbuf[i]) != 0 + assert ord(outbuf[n]) == 0 + assert ord(outbuf[0]) < 128 # should be a letter, or '\' + +def test_define_known_value(): + ffi = FFI() + ffi.cdef("#define FOO 0x123") + lib = ffi.verify("#define FOO 0x123") + assert lib.FOO == 0x123 + +def test_define_wrong_value(): + ffi = FFI() + ffi.cdef("#define FOO 123") + lib = ffi.verify("#define FOO 124") # used to complain + e = py.test.raises(ffi.error, "lib.FOO") + assert str(e.value) == ("the C compiler says 'FOO' is equal to 124 (0x7c)," + " but the cdef disagrees") + +def test_some_integer_type_for_issue73(): + ffi = FFI() + ffi.cdef(""" + typedef int... AnIntegerWith32Bits; + typedef AnIntegerWith32Bits (*AFunctionReturningInteger) (void); + AnIntegerWith32Bits InvokeFunction(AFunctionReturningInteger); + """) + lib = ffi.verify(""" + #ifdef __LP64__ + typedef int AnIntegerWith32Bits; + #else + typedef long AnIntegerWith32Bits; + #endif + typedef AnIntegerWith32Bits (*AFunctionReturningInteger) (void); + AnIntegerWith32Bits InvokeFunction(AFunctionReturningInteger f) { + return f(); + } + """) + @ffi.callback("AFunctionReturningInteger") + def add(): + return 3 + 4 + x = lib.InvokeFunction(add) + assert x == 7 + +def test_unsupported_some_primitive_types(): + ffi = FFI() + py.test.raises((FFIError, # with pycparser <= 2.17 + CDefError), # with pycparser >= 2.18 + ffi.cdef, """typedef void... foo_t;""") + # + ffi.cdef("typedef int... foo_t;") + py.test.raises(VerificationError, ffi.verify, "typedef float foo_t;") + +def test_windows_dllimport_data(): + if sys.platform != 'win32': + py.test.skip("Windows only") + from testing.udir import udir + tmpfile = udir.join('dllimport_data.c') + tmpfile.write('int my_value = 42;\n') + ffi = FFI() + ffi.cdef("int my_value;") + lib = ffi.verify("extern __declspec(dllimport) int my_value;", + sources = [str(tmpfile)]) + assert lib.my_value == 42 + +def test_macro_var(): + ffi = FFI() + ffi.cdef("int myarray[50], my_value;") + lib = ffi.verify(""" + int myarray[50]; + int *get_my_value(void) { + static int index = 0; + return &myarray[index++]; + } + #define my_value (*get_my_value()) + """) + assert lib.my_value == 0 # [0] + lib.my_value = 42 # [1] + assert lib.myarray[1] == 42 + assert lib.my_value == 0 # [2] + lib.myarray[3] = 63 + assert lib.my_value == 63 # [3] + p = ffi.addressof(lib, 'my_value') # [4] + assert p[-1] == 63 + assert p[0] == 0 + assert p == lib.myarray + 4 + p[1] = 82 + assert lib.my_value == 82 # [5] + +def test_const_pointer_to_pointer(): + ffi = FFI() + ffi.cdef("struct s { char *const *a; };") + ffi.verify("struct s { char *const *a; };") + +def test_share_FILE(): + ffi1 = FFI() + ffi1.cdef("void do_stuff(FILE *);") + lib1 = ffi1.verify("void do_stuff(FILE *f) { (void)f; }") + ffi2 = FFI() + ffi2.cdef("FILE *barize(void);") + lib2 = ffi2.verify("FILE *barize(void) { return NULL; }") + lib1.do_stuff(lib2.barize()) + +def test_win_common_types(): + if sys.platform != 'win32': + py.test.skip("Windows only") + ffi = FFI() + ffi.set_unicode(True) + ffi.verify("") + assert ffi.typeof("PBYTE") is ffi.typeof("unsigned char *") + if sys.maxsize > 2**32: + expected = "unsigned long long" + else: + expected = "unsigned int" + assert ffi.typeof("UINT_PTR") is ffi.typeof(expected) + assert ffi.typeof("PTSTR") is ffi.typeof("wchar_t *") + +def _only_test_on_linux_intel(): + if not sys.platform.startswith('linux'): + py.test.skip('only running the memory-intensive test on Linux') + import platform + machine = platform.machine() + if 'x86' not in machine and 'x64' not in machine: + py.test.skip('only running the memory-intensive test on x86/x64') + +def test_ffi_gc_size_arg(): + _only_test_on_linux_intel() + ffi = FFI() + ffi.cdef("void *malloc(size_t); void free(void *);") + lib = ffi.verify(r""" + #include <stdlib.h> + """) + for i in range(2000): + p = lib.malloc(20*1024*1024) # 20 MB + p1 = ffi.cast("char *", p) + for j in range(0, 20*1024*1024, 4096): + p1[j] = b'!' + p = ffi.gc(p, lib.free, 20*1024*1024) + del p + # with PyPy's GC, the above would rapidly consume 40 GB of RAM + # without the third argument to ffi.gc() + +def test_ffi_gc_size_arg_2(): + # a variant of the above: this "attack" works on cpython's cyclic gc too + # and I found no obvious way to prevent that. So for now, this test + # is skipped on CPython, where it eats all the memory. + if '__pypy__' not in sys.builtin_module_names: + py.test.skip("find a way to tweak the cyclic GC of CPython") + _only_test_on_linux_intel() + ffi = FFI() + ffi.cdef("void *malloc(size_t); void free(void *);") + lib = ffi.verify(r""" + #include <stdlib.h> + """) + class X(object): + pass + for i in range(2000): + p = lib.malloc(50*1024*1024) # 50 MB + p1 = ffi.cast("char *", p) + for j in range(0, 50*1024*1024, 4096): + p1[j] = b'!' + p = ffi.gc(p, lib.free, 50*1024*1024) + x = X() + x.p = p + x.cyclic = x + del p, x + +def test_ffi_new_with_cycles(): + # still another variant, with ffi.new() + if '__pypy__' not in sys.builtin_module_names: + py.test.skip("find a way to tweak the cyclic GC of CPython") + ffi = FFI() + ffi.cdef("") + lib = ffi.verify("") + class X(object): + pass + for i in range(2000): + p = ffi.new("char[]", 50*1024*1024) # 50 MB + for j in range(0, 50*1024*1024, 4096): + p[j] = b'!' + x = X() + x.p = p + x.cyclic = x + del p, x diff --git a/testing/cffi1/test_zdist.py b/testing/cffi1/test_zdist.py new file mode 100644 index 0000000..efc1d86 --- /dev/null +++ b/testing/cffi1/test_zdist.py @@ -0,0 +1,426 @@ +import sys, os, py +import subprocess +import cffi +from testing.udir import udir +from shutil import rmtree +from tempfile import mkdtemp + + +def chdir_to_tmp(f): + f.chdir_to_tmp = True + return f + +def from_outside(f): + f.chdir_to_tmp = False + return f + + +class TestDist(object): + + def setup_method(self, meth): + self.executable = os.path.abspath(sys.executable) + self.rootdir = os.path.abspath(os.path.dirname(os.path.dirname( + cffi.__file__))) + self.udir = udir.join(meth.__name__) + os.mkdir(str(self.udir)) + if meth.chdir_to_tmp: + self.saved_cwd = os.getcwd() + os.chdir(str(self.udir)) + + def teardown_method(self, meth): + if hasattr(self, 'saved_cwd'): + os.chdir(self.saved_cwd) + + def run(self, args, cwd=None): + env = os.environ.copy() + # a horrible hack to prevent distutils from finding ~/.pydistutils.cfg + # (there is the --no-user-cfg option, but not in Python 2.6...) + # NOTE: pointing $HOME to a nonexistent directory can break certain things + # that look there for configuration (like ccache). + tmp_home = mkdtemp() + assert tmp_home != None, "cannot create temporary homedir" + env['HOME'] = tmp_home + if cwd is None: + newpath = self.rootdir + if 'PYTHONPATH' in env: + newpath += os.pathsep + env['PYTHONPATH'] + env['PYTHONPATH'] = newpath + try: + subprocess.check_call([self.executable] + args, cwd=cwd, env=env) + finally: + rmtree(tmp_home) + + def _prepare_setuptools(self): + if hasattr(TestDist, '_setuptools_ready'): + return + try: + import setuptools + except ImportError: + py.test.skip("setuptools not found") + if os.path.exists(os.path.join(self.rootdir, 'setup.py')): + self.run(['setup.py', 'egg_info'], cwd=self.rootdir) + TestDist._setuptools_ready = True + + def check_produced_files(self, content, curdir=None): + if curdir is None: + curdir = str(self.udir) + found_so = None + for name in os.listdir(curdir): + if (name.endswith('.so') or name.endswith('.pyd') or + name.endswith('.dylib') or name.endswith('.dll')): + found_so = os.path.join(curdir, name) + # foo.so => foo + parts = name.split('.') + del parts[-1] + if len(parts) > 1 and parts[-1] != 'bar': + # foo.cpython-34m.so => foo, but foo.bar.so => foo.bar + del parts[-1] + name = '.'.join(parts) + # foo_d => foo (Python 2 debug builds) + if name.endswith('_d') and hasattr(sys, 'gettotalrefcount'): + name = name[:-2] + name += '.SO' + if name.startswith('pycparser') and name.endswith('.egg'): + continue # no clue why this shows up sometimes and not others + if name == '.eggs': + continue # seems new in 3.5, ignore it + assert name in content, "found unexpected file %r" % ( + os.path.join(curdir, name),) + value = content.pop(name) + if value is None: + assert name.endswith('.SO') or ( + os.path.isfile(os.path.join(curdir, name))) + else: + subdir = os.path.join(curdir, name) + assert os.path.isdir(subdir) + if value == '?': + continue + found_so = self.check_produced_files(value, subdir) or found_so + assert content == {}, "files or dirs not produced in %r: %r" % ( + curdir, content.keys()) + return found_so + + @chdir_to_tmp + def test_empty(self): + self.check_produced_files({}) + + @chdir_to_tmp + def test_abi_emit_python_code_1(self): + ffi = cffi.FFI() + ffi.set_source("package_name_1.mymod", None) + ffi.emit_python_code('xyz.py') + self.check_produced_files({'xyz.py': None}) + + @chdir_to_tmp + def test_abi_emit_python_code_2(self): + ffi = cffi.FFI() + ffi.set_source("package_name_1.mymod", None) + py.test.raises(IOError, ffi.emit_python_code, 'unexisting/xyz.py') + + @from_outside + def test_abi_emit_python_code_3(self): + ffi = cffi.FFI() + ffi.set_source("package_name_1.mymod", None) + ffi.emit_python_code(str(self.udir.join('xyt.py'))) + self.check_produced_files({'xyt.py': None}) + + @chdir_to_tmp + def test_abi_compile_1(self): + ffi = cffi.FFI() + ffi.set_source("mod_name_in_package.mymod", None) + x = ffi.compile() + self.check_produced_files({'mod_name_in_package': {'mymod.py': None}}) + assert x == os.path.join('.', 'mod_name_in_package', 'mymod.py') + + @chdir_to_tmp + def test_abi_compile_2(self): + ffi = cffi.FFI() + ffi.set_source("mod_name_in_package.mymod", None) + x = ffi.compile('build2') + self.check_produced_files({'build2': { + 'mod_name_in_package': {'mymod.py': None}}}) + assert x == os.path.join('build2', 'mod_name_in_package', 'mymod.py') + + @from_outside + def test_abi_compile_3(self): + ffi = cffi.FFI() + ffi.set_source("mod_name_in_package.mymod", None) + tmpdir = str(self.udir.join('build3')) + x = ffi.compile(tmpdir) + self.check_produced_files({'build3': { + 'mod_name_in_package': {'mymod.py': None}}}) + assert x == os.path.join(tmpdir, 'mod_name_in_package', 'mymod.py') + + @chdir_to_tmp + def test_api_emit_c_code_1(self): + ffi = cffi.FFI() + ffi.set_source("package_name_1.mymod", "/*code would be here*/") + ffi.emit_c_code('xyz.c') + self.check_produced_files({'xyz.c': None}) + + @chdir_to_tmp + def test_api_emit_c_code_2(self): + ffi = cffi.FFI() + ffi.set_source("package_name_1.mymod", "/*code would be here*/") + py.test.raises(IOError, ffi.emit_c_code, 'unexisting/xyz.c') + + @from_outside + def test_api_emit_c_code_3(self): + ffi = cffi.FFI() + ffi.set_source("package_name_1.mymod", "/*code would be here*/") + ffi.emit_c_code(str(self.udir.join('xyu.c'))) + self.check_produced_files({'xyu.c': None}) + + @chdir_to_tmp + def test_api_compile_1(self): + ffi = cffi.FFI() + ffi.set_source("mod_name_in_package.mymod", "/*code would be here*/") + x = ffi.compile() + if sys.platform != 'win32': + sofile = self.check_produced_files({ + 'mod_name_in_package': {'mymod.SO': None, + 'mymod.c': None, + 'mymod.o': None}}) + assert os.path.isabs(x) and os.path.samefile(x, sofile) + else: + self.check_produced_files({ + 'mod_name_in_package': {'mymod.SO': None, + 'mymod.c': None}, + 'Release': '?'}) + + @chdir_to_tmp + def test_api_compile_2(self): + ffi = cffi.FFI() + ffi.set_source("mod_name_in_package.mymod", "/*code would be here*/") + x = ffi.compile('output') + if sys.platform != 'win32': + sofile = self.check_produced_files({ + 'output': {'mod_name_in_package': {'mymod.SO': None, + 'mymod.c': None, + 'mymod.o': None}}}) + assert os.path.isabs(x) and os.path.samefile(x, sofile) + else: + self.check_produced_files({ + 'output': {'mod_name_in_package': {'mymod.SO': None, + 'mymod.c': None}, + 'Release': '?'}}) + + @from_outside + def test_api_compile_3(self): + ffi = cffi.FFI() + ffi.set_source("mod_name_in_package.mymod", "/*code would be here*/") + x = ffi.compile(str(self.udir.join('foo'))) + if sys.platform != 'win32': + sofile = self.check_produced_files({ + 'foo': {'mod_name_in_package': {'mymod.SO': None, + 'mymod.c': None, + 'mymod.o': None}}}) + assert os.path.isabs(x) and os.path.samefile(x, sofile) + else: + self.check_produced_files({ + 'foo': {'mod_name_in_package': {'mymod.SO': None, + 'mymod.c': None}, + 'Release': '?'}}) + + @chdir_to_tmp + def test_api_compile_explicit_target_1(self): + ffi = cffi.FFI() + ffi.set_source("mod_name_in_package.mymod", "/*code would be here*/") + x = ffi.compile(target="foo.bar.*") + if sys.platform != 'win32': + sofile = self.check_produced_files({ + 'mod_name_in_package': {'foo.bar.SO': None, + 'mymod.c': None, + 'mymod.o': None}}) + assert os.path.isabs(x) and os.path.samefile(x, sofile) + else: + self.check_produced_files({ + 'mod_name_in_package': {'foo.bar.SO': None, + 'mymod.c': None}, + 'Release': '?'}) + + @chdir_to_tmp + def test_api_compile_explicit_target_3(self): + ffi = cffi.FFI() + ffi.set_source("mod_name_in_package.mymod", "/*code would be here*/") + x = ffi.compile(target="foo.bar.baz") + if sys.platform != 'win32': + self.check_produced_files({ + 'mod_name_in_package': {'foo.bar.baz': None, + 'mymod.c': None, + 'mymod.o': None}}) + sofile = os.path.join(str(self.udir), + 'mod_name_in_package', 'foo.bar.baz') + assert os.path.isabs(x) and os.path.samefile(x, sofile) + else: + self.check_produced_files({ + 'mod_name_in_package': {'foo.bar.baz': None, + 'mymod.c': None}, + 'Release': '?'}) + + @chdir_to_tmp + def test_api_distutils_extension_1(self): + ffi = cffi.FFI() + ffi.set_source("mod_name_in_package.mymod", "/*code would be here*/") + ext = ffi.distutils_extension() + self.check_produced_files({'build': { + 'mod_name_in_package': {'mymod.c': None}}}) + if hasattr(os.path, 'samefile'): + assert os.path.samefile(ext.sources[0], + 'build/mod_name_in_package/mymod.c') + + @from_outside + def test_api_distutils_extension_2(self): + ffi = cffi.FFI() + ffi.set_source("mod_name_in_package.mymod", "/*code would be here*/") + ext = ffi.distutils_extension(str(self.udir.join('foo'))) + self.check_produced_files({'foo': { + 'mod_name_in_package': {'mymod.c': None}}}) + if hasattr(os.path, 'samefile'): + assert os.path.samefile(ext.sources[0], + str(self.udir.join('foo/mod_name_in_package/mymod.c'))) + + + def _make_distutils_api(self): + os.mkdir("src") + os.mkdir(os.path.join("src", "pack1")) + with open(os.path.join("src", "pack1", "__init__.py"), "w") as f: + pass + with open("setup.py", "w") as f: + f.write("""if 1: + # https://bugs.python.org/issue23246 + import sys + if sys.platform == 'win32': + try: + import setuptools + except ImportError: + pass + + import cffi + ffi = cffi.FFI() + ffi.set_source("pack1.mymod", "/*code would be here*/") + + from distutils.core import setup + setup(name='example1', + version='0.1', + packages=['pack1'], + package_dir={'': 'src'}, + ext_modules=[ffi.distutils_extension()]) + """) + + @chdir_to_tmp + def test_distutils_api_1(self): + self._make_distutils_api() + self.run(["setup.py", "build"]) + self.check_produced_files({'setup.py': None, + 'build': '?', + 'src': {'pack1': {'__init__.py': None}}}) + + @chdir_to_tmp + def test_distutils_api_2(self): + self._make_distutils_api() + self.run(["setup.py", "build_ext", "-i"]) + self.check_produced_files({'setup.py': None, + 'build': '?', + 'src': {'pack1': {'__init__.py': None, + 'mymod.SO': None}}}) + + def _make_setuptools_abi(self): + self._prepare_setuptools() + os.mkdir("src0") + os.mkdir(os.path.join("src0", "pack2")) + with open(os.path.join("src0", "pack2", "__init__.py"), "w") as f: + pass + with open(os.path.join("src0", "pack2", "_build.py"), "w") as f: + f.write("""if 1: + import cffi + ffi = cffi.FFI() + ffi.set_source("pack2.mymod", None) + """) + with open("setup.py", "w") as f: + f.write("""if 1: + from setuptools import setup + setup(name='example1', + version='0.1', + packages=['pack2'], + package_dir={'': 'src0'}, + cffi_modules=["src0/pack2/_build.py:ffi"]) + """) + + @chdir_to_tmp + def test_setuptools_abi_1(self): + self._make_setuptools_abi() + self.run(["setup.py", "build"]) + self.check_produced_files({'setup.py': None, + 'build': '?', + 'src0': {'pack2': {'__init__.py': None, + '_build.py': None}}}) + + @chdir_to_tmp + def test_setuptools_abi_2(self): + self._make_setuptools_abi() + self.run(["setup.py", "build_ext", "-i"]) + self.check_produced_files({'setup.py': None, + 'src0': {'pack2': {'__init__.py': None, + '_build.py': None, + 'mymod.py': None}}}) + + def _make_setuptools_api(self): + self._prepare_setuptools() + os.mkdir("src1") + os.mkdir(os.path.join("src1", "pack3")) + with open(os.path.join("src1", "pack3", "__init__.py"), "w") as f: + pass + with open(os.path.join("src1", "pack3", "_build.py"), "w") as f: + f.write("""if 1: + import cffi + ffi = cffi.FFI() + ffi.set_source("pack3.mymod", "/*code would be here*/") + ffi._hi_there = 42 + """) + with open("setup.py", "w") as f: + f.write("from __future__ import print_function\n" + """if 1: + from setuptools import setup + from distutils.command.build_ext import build_ext + import os + + class TestBuildExt(build_ext): + def pre_run(self, ext, ffi): + print('_make_setuptools_api: in pre_run:', end=" ") + assert ffi._hi_there == 42 + assert ext.name == "pack3.mymod" + fn = os.path.join(os.path.dirname(self.build_lib), + '..', 'see_me') + print('creating %r' % (fn,)) + open(fn, 'w').close() + + setup(name='example1', + version='0.1', + packages=['pack3'], + package_dir={'': 'src1'}, + cffi_modules=["src1/pack3/_build.py:ffi"], + cmdclass={'build_ext': TestBuildExt}, + ) + """) + + @chdir_to_tmp + def test_setuptools_api_1(self): + self._make_setuptools_api() + self.run(["setup.py", "build"]) + self.check_produced_files({'setup.py': None, + 'build': '?', + 'see_me': None, + 'src1': {'pack3': {'__init__.py': None, + '_build.py': None}}}) + + @chdir_to_tmp + def test_setuptools_api_2(self): + self._make_setuptools_api() + self.run(["setup.py", "build_ext", "-i"]) + self.check_produced_files({'setup.py': None, + 'build': '?', + 'see_me': None, + 'src1': {'pack3': {'__init__.py': None, + '_build.py': None, + 'mymod.SO': None}}}) |