summaryrefslogtreecommitdiff
path: root/testing
diff options
context:
space:
mode:
authorKevin Cheng <kevcheng@google.com>2019-04-18 11:31:16 -0700
committerKevin Cheng <kevcheng@google.com>2019-05-02 13:59:40 -0700
commit757c264bc10ebc71074ee3f5fb66d670667a09bc (patch)
tree26c7f7b74c752db99d9b0ac1f94fc592aca1e53a /testing
parent99013222844839c42437f16eace25f4e6a8a8b20 (diff)
downloadcffi-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')
-rw-r--r--testing/__init__.py0
-rw-r--r--testing/cffi0/__init__.py0
-rw-r--r--testing/cffi0/backend_tests.py1990
-rw-r--r--testing/cffi0/callback_in_thread.py42
-rw-r--r--testing/cffi0/snippets/distutils_module/setup.py7
-rw-r--r--testing/cffi0/snippets/distutils_module/snip_basic_verify.py17
-rw-r--r--testing/cffi0/snippets/distutils_package_1/setup.py7
-rw-r--r--testing/cffi0/snippets/distutils_package_1/snip_basic_verify1/__init__.py17
-rw-r--r--testing/cffi0/snippets/distutils_package_2/setup.py8
-rw-r--r--testing/cffi0/snippets/distutils_package_2/snip_basic_verify2/__init__.py18
-rw-r--r--testing/cffi0/snippets/infrastructure/setup.py5
-rw-r--r--testing/cffi0/snippets/infrastructure/snip_infrastructure/__init__.py3
-rw-r--r--testing/cffi0/snippets/setuptools_module/setup.py8
-rw-r--r--testing/cffi0/snippets/setuptools_module/snip_setuptools_verify.py17
-rw-r--r--testing/cffi0/snippets/setuptools_package_1/setup.py8
-rw-r--r--testing/cffi0/snippets/setuptools_package_1/snip_setuptools_verify1/__init__.py17
-rw-r--r--testing/cffi0/snippets/setuptools_package_2/setup.py9
-rw-r--r--testing/cffi0/snippets/setuptools_package_2/snip_setuptools_verify2/__init__.py18
-rw-r--r--testing/cffi0/test_cdata.py41
-rw-r--r--testing/cffi0/test_ctypes.py43
-rw-r--r--testing/cffi0/test_ffi_backend.py576
-rw-r--r--testing/cffi0/test_function.py520
-rw-r--r--testing/cffi0/test_model.py111
-rw-r--r--testing/cffi0/test_ownlib.py373
-rw-r--r--testing/cffi0/test_parsing.py468
-rw-r--r--testing/cffi0/test_platform.py25
-rw-r--r--testing/cffi0/test_unicode_literals.py79
-rw-r--r--testing/cffi0/test_verify.py2524
-rw-r--r--testing/cffi0/test_verify2.py9
-rw-r--r--testing/cffi0/test_version.py62
-rw-r--r--testing/cffi0/test_vgen.py12
-rw-r--r--testing/cffi0/test_vgen2.py13
-rw-r--r--testing/cffi0/test_zdistutils.py288
-rw-r--r--testing/cffi0/test_zintegration.py176
-rw-r--r--testing/cffi1/__init__.py0
-rw-r--r--testing/cffi1/test_cffi_binary.py20
-rw-r--r--testing/cffi1/test_commontypes.py34
-rw-r--r--testing/cffi1/test_dlopen.py225
-rw-r--r--testing/cffi1/test_dlopen_unicode_literals.py9
-rw-r--r--testing/cffi1/test_ffi_obj.py532
-rw-r--r--testing/cffi1/test_new_ffi_1.py1793
-rw-r--r--testing/cffi1/test_parse_c_type.py372
-rw-r--r--testing/cffi1/test_pkgconfig.py94
-rw-r--r--testing/cffi1/test_re_python.py256
-rw-r--r--testing/cffi1/test_realize_c_type.py73
-rw-r--r--testing/cffi1/test_recompiler.py2316
-rw-r--r--testing/cffi1/test_unicode_literals.py43
-rw-r--r--testing/cffi1/test_verify1.py2363
-rw-r--r--testing/cffi1/test_zdist.py426
-rw-r--r--testing/embedding/__init__.py0
-rw-r--r--testing/embedding/add1-test.c21
-rw-r--r--testing/embedding/add1.py33
-rw-r--r--testing/embedding/add2-test.c14
-rw-r--r--testing/embedding/add2.py29
-rw-r--r--testing/embedding/add3.py24
-rw-r--r--testing/embedding/add_recursive-test.c27
-rw-r--r--testing/embedding/add_recursive.py33
-rw-r--r--testing/embedding/empty.py10
-rw-r--r--testing/embedding/initerror.py18
-rw-r--r--testing/embedding/perf-test.c90
-rw-r--r--testing/embedding/perf.py21
-rw-r--r--testing/embedding/test_basic.py207
-rw-r--r--testing/embedding/test_performance.py52
-rw-r--r--testing/embedding/test_recursive.py15
-rw-r--r--testing/embedding/test_thread.py61
-rw-r--r--testing/embedding/test_tlocal.py10
-rw-r--r--testing/embedding/thread-test.h96
-rw-r--r--testing/embedding/thread1-test.c43
-rw-r--r--testing/embedding/thread2-test.c57
-rw-r--r--testing/embedding/thread3-test.c56
-rw-r--r--testing/embedding/tlocal-test.c47
-rw-r--r--testing/embedding/tlocal.py33
-rw-r--r--testing/support.py88
-rw-r--r--testing/udir.py13
74 files changed, 17165 insertions, 0 deletions
diff --git a/testing/__init__.py b/testing/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/testing/__init__.py
diff --git a/testing/cffi0/__init__.py b/testing/cffi0/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/testing/cffi0/__init__.py
diff --git a/testing/cffi0/backend_tests.py b/testing/cffi0/backend_tests.py
new file mode 100644
index 0000000..13a4c78
--- /dev/null
+++ b/testing/cffi0/backend_tests.py
@@ -0,0 +1,1990 @@
+import py
+import platform
+import sys, ctypes
+from cffi import FFI, CDefError, FFIError, VerificationMissing
+from testing.support import *
+
+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 needs_dlopen_none():
+ if sys.platform == 'win32' and sys.version_info >= (3,):
+ py.test.skip("dlopen(None) cannot work on Windows for Python 3")
+
+
+class BackendTests:
+
+ def test_integer_ranges(self):
+ ffi = FFI(backend=self.Backend())
+ 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):
+ ffi = FFI(backend=self.Backend())
+ 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 hash(p) == hash(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):
+ ffi = FFI(backend=self.Backend())
+ 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):
+ ffi = FFI(backend=self.Backend())
+ 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):
+ ffi = FFI(backend=self.Backend())
+ p = ffi.new("int[10]")
+ # the object was zero-initialized:
+ for i in range(10):
+ assert p[i] == 0
+
+ def test_array_indexing(self):
+ ffi = FFI(backend=self.Backend())
+ 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):
+ ffi = FFI(backend=self.Backend())
+ # 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):
+ ffi = FFI(backend=self.Backend())
+ 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):
+ ffi = FFI(backend=self.Backend())
+ 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):
+ ffi = FFI(backend=self.Backend())
+ 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):
+ ffi = FFI(backend=self.Backend())
+ 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):
+ ffi = FFI(backend=self.Backend())
+ 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 = self.TypeRepr
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("struct foo { short a, b, c; };")
+ 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 foo *")
+ assert repr(p) == "<cdata 'struct foo *' owning %d bytes>" % (
+ 3*SIZE_OF_SHORT)
+ assert repr(ffi.typeof(p)) == typerepr % "struct foo *"
+ #
+ 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 foo*")
+ q = ffi.cast("struct foo *", p)
+ assert repr(q).startswith("<cdata 'struct foo *' 0x")
+ assert repr(ffi.typeof(q)) == typerepr % "struct foo *"
+ prevrepr = repr(q)
+ q = q[0]
+ assert repr(q) == prevrepr.replace(' *', ' &')
+ assert repr(ffi.typeof(q)) == typerepr % "struct foo"
+
+ def test_new_array_of_array(self):
+ ffi = FFI(backend=self.Backend())
+ 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):
+ ffi = FFI(backend=self.Backend())
+ 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):
+ ffi = FFI(backend=self.Backend())
+ 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):
+ ffi = FFI(backend=self.Backend())
+ 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):
+ ffi = FFI(backend=self.Backend())
+ 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):
+ ffi = FFI(backend=self.Backend())
+ 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):
+ ffi = FFI(backend=self.Backend())
+ 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):
+ ffi = FFI(backend=self.Backend())
+ 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):
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("struct foo { int a; short b, c; };")
+ s = ffi.new("struct foo*")
+ 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 foo*", [-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 foo *' owning %d bytes>" % (
+ SIZE_OF_INT + 2 * SIZE_OF_SHORT)
+ #
+ py.test.raises(ValueError, ffi.new, "struct foo*", [1, 2, 3, 4])
+
+ def test_constructor_struct_from_dict(self):
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("struct foo { int a; short b, c; };")
+ s = ffi.new("struct foo*", {'b': 123, 'c': 456})
+ assert s.a == 0
+ assert s.b == 123
+ assert s.c == 456
+ py.test.raises(KeyError, ffi.new, "struct foo*", {'d': 456})
+
+ def test_struct_pointer(self):
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("struct foo { int a; short b, c; };")
+ s = ffi.new("struct foo*")
+ 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):
+ ffi = FFI(backend=self.Backend())
+ py.test.raises(TypeError, ffi.new, "struct baz*")
+ p = ffi.new("struct baz **") # this works
+ assert p[0] == ffi.NULL
+
+ def test_pointer_to_struct(self):
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("struct foo { int a; short b, c; };")
+ s = ffi.new("struct foo *")
+ s.a = -42
+ assert s[0].a == -42
+ p = ffi.new("struct foo **", 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):
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("struct foo { int a[2]; char b[3]; };")
+ s = ffi.new("struct foo *", [[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):
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("struct foo { int value; struct foo *next; };")
+ s = ffi.new("struct foo*")
+ t = ffi.new("struct foo*")
+ s.value = 123
+ s.next = t
+ t.value = 456
+ assert s.value == 123
+ assert s.next.value == 456
+
+ def test_union_simple(self):
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("union foo { int a; short b, c; };")
+ u = ffi.new("union foo*")
+ 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 foo*", [-2])
+ assert u.a == -2
+ py.test.raises((AttributeError, TypeError), "del u.a")
+ assert repr(u) == "<cdata 'union foo *' owning %d bytes>" % SIZE_OF_INT
+
+ def test_union_opaque(self):
+ ffi = FFI(backend=self.Backend())
+ py.test.raises(TypeError, ffi.new, "union baz *")
+ u = ffi.new("union baz **") # this works
+ assert u[0] == ffi.NULL
+
+ def test_union_initializer(self):
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("union foo { char a; int b; };")
+ py.test.raises(TypeError, ffi.new, "union foo*", b'A')
+ py.test.raises(TypeError, ffi.new, "union foo*", 5)
+ py.test.raises(ValueError, ffi.new, "union foo*", [b'A', 5])
+ u = ffi.new("union foo*", [b'A'])
+ assert u.a == b'A'
+ py.test.raises(TypeError, ffi.new, "union foo*", [1005])
+ u = ffi.new("union foo*", {'b': 12345})
+ assert u.b == 12345
+ u = ffi.new("union foo*", [])
+ assert u.a == b'\x00'
+ assert u.b == 0
+
+ def test_sizeof_type(self):
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("""
+ struct foo { int a; short b, c, d; };
+ union foo { int a; short b, c, d; };
+ """)
+ for c_type, expected_size in [
+ ('char', 1),
+ ('unsigned int', 4),
+ ('char *', SIZE_OF_PTR),
+ ('int[5]', 20),
+ ('struct foo', 12),
+ ('union foo', 4),
+ ]:
+ size = ffi.sizeof(c_type)
+ assert size == expected_size, (size, expected_size, ctype)
+
+ def test_sizeof_cdata(self):
+ ffi = FFI(backend=self.Backend())
+ 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):
+ ffi = FFI(backend=self.Backend())
+ 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):
+ ffi = FFI(backend=self.Backend())
+ 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):
+ ffi = FFI(backend=self.Backend())
+ 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):
+ ffi = FFI(backend=self.Backend())
+ 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
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("struct foo { const char *name; };")
+ t = ffi.new("const char[]", b"testing")
+ s = ffi.new("struct foo*", [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
+ ffi = FFI(backend=self.Backend())
+ self.check_wchar_t(ffi)
+ ffi.cdef("struct foo { const wchar_t *name; };")
+ t = ffi.new("const wchar_t[]", u+"testing")
+ s = ffi.new("struct foo*", [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):
+ ffi = FFI(backend=self.Backend())
+ 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)
+ #
+ ffi.cdef("struct foo { void *p; int *q; short *r; };")
+ s = ffi.new("struct foo *")
+ 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):
+ ffi = FFI(backend=self.Backend())
+ 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):
+ ffi = FFI(backend=self.Backend())
+ t = ffi.typeof("int(*(*)(int))(int)")
+ assert repr(t) == self.TypeRepr % "int(*(*)(int))(int)"
+
+ def test_functionptr_voidptr_return(self):
+ ffi = FFI(backend=self.Backend())
+ 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):
+ ffi = FFI(backend=self.Backend())
+ 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):
+ ffi = FFI(backend=self.Backend())
+ def foo():
+ pass
+ foo_cb = ffi.callback("void foo()", foo)
+ result = foo_cb()
+ assert result is None
+
+ def test_char_cast(self):
+ ffi = FFI(backend=self.Backend())
+ 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):
+ ffi = FFI(backend=self.Backend())
+ 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):
+ ffi = FFI(backend=self.Backend())
+ 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):
+ ffi = FFI(backend=self.Backend())
+ 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):
+ ffi = FFI(backend=self.Backend())
+ 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):
+ ffi = FFI(backend=self.Backend())
+ 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):
+ ffi = FFI(backend=self.Backend())
+ 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):
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("struct foo_s { int a, b; };")
+ def cb(p):
+ return p[0].a * 1000 + p[0].b * 100 + p[1].a * 10 + p[1].b
+ a = ffi.callback("int(*)(struct foo_s[])", 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):
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("struct foo_s { int a, b; };")
+ 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):
+ ffi = FFI(backend=self.Backend())
+ 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):
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("enum foo { A0, B0, CC0, D0 };")
+ assert ffi.string(ffi.cast("enum foo", 0)) == "A0"
+ assert ffi.string(ffi.cast("enum foo", 2)) == "CC0"
+ assert ffi.string(ffi.cast("enum foo", 3)) == "D0"
+ assert ffi.string(ffi.cast("enum foo", 4)) == "4"
+ ffi.cdef("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 foo", 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 foo", -1)) == ( # enums are unsigned, if
+ "<cdata 'enum foo' 4294967295>") # they contain no neg value
+ ffi.cdef("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):
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("enum foo { A, B, C, D }; struct bar { enum foo e; };")
+ s = ffi.new("struct bar *")
+ 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 foo", -1)
+ assert s.e == 4294967295
+ assert s[0].e == 4294967295
+ s.e = s.e
+ py.test.raises(TypeError, "s.e = 'B'")
+ 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):
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("enum foo { A, B=42, C };")
+ assert ffi.string(ffi.cast("enum foo", 0)) == "A"
+ assert ffi.string(ffi.cast("enum foo", 42)) == "B"
+ assert ffi.string(ffi.cast("enum foo", 43)) == "C"
+ invalid_value = ffi.cast("enum foo", 2)
+ assert int(invalid_value) == 2
+ assert ffi.string(invalid_value) == "2"
+
+ def test_enum_char_hex_oct(self):
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef(r"enum foo{A='!', B='\'', C=0x10, D=010, E=- 0x10, F=-010};")
+ assert ffi.string(ffi.cast("enum foo", ord('!'))) == "A"
+ assert ffi.string(ffi.cast("enum foo", ord("'"))) == "B"
+ assert ffi.string(ffi.cast("enum foo", 16)) == "C"
+ assert ffi.string(ffi.cast("enum foo", 8)) == "D"
+ assert ffi.string(ffi.cast("enum foo", -16)) == "E"
+ assert ffi.string(ffi.cast("enum foo", -8)) == "F"
+
+ def test_enum_partial(self):
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef(r"enum foo {A, ...}; enum bar { B, C };")
+ needs_dlopen_none()
+ lib = ffi.dlopen(None)
+ assert lib.B == 0
+ py.test.raises(VerificationMissing, getattr, lib, "A")
+ assert lib.C == 1
+
+ def test_array_of_struct(self):
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("struct foo { int a, b; };")
+ s = ffi.new("struct foo[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):
+ ffi = FFI(backend=self.Backend())
+ p = ffi.new("int(**)[5]")
+ assert repr(p) == "<cdata 'int(* *)[5]' owning %d bytes>" % SIZE_OF_PTR
+
+ def test_iterate_array(self):
+ ffi = FFI(backend=self.Backend())
+ 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):
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("struct foo { int a, b, c; };")
+ assert ffi.offsetof("struct foo", "a") == 0
+ assert ffi.offsetof("struct foo", "b") == 4
+ assert ffi.offsetof("struct foo", "c") == 8
+
+ def test_offsetof_nested(self):
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("struct foo { int a, b, c; };"
+ "struct bar { struct foo d, e; };")
+ assert ffi.offsetof("struct bar", "e") == 12
+ py.test.raises(KeyError, ffi.offsetof, "struct bar", "e.a")
+ assert ffi.offsetof("struct bar", "e", "a") == 12
+ assert ffi.offsetof("struct bar", "e", "b") == 16
+ assert ffi.offsetof("struct bar", "e", "c") == 20
+
+ def test_offsetof_array(self):
+ ffi = FFI(backend=self.Backend())
+ assert ffi.offsetof("int[]", 51) == 51 * ffi.sizeof("int")
+ assert ffi.offsetof("int *", 51) == 51 * ffi.sizeof("int")
+ ffi.cdef("struct bar { int a, b; int c[99]; };")
+ assert ffi.offsetof("struct bar", "c") == 2 * ffi.sizeof("int")
+ assert ffi.offsetof("struct bar", "c", 0) == 2 * ffi.sizeof("int")
+ assert ffi.offsetof("struct bar", "c", 51) == 53 * ffi.sizeof("int")
+
+ def test_alignof(self):
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("struct foo { char a; short b; char c; };")
+ assert ffi.alignof("int") == 4
+ assert ffi.alignof("double") in (4, 8)
+ assert ffi.alignof("struct foo") == 2
+
+ def test_bitfield(self):
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("struct foo { int a:10, b:20, c:3; };")
+ assert ffi.sizeof("struct foo") == 8
+ s = ffi.new("struct foo *")
+ 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):
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("""
+ typedef enum { AA, BB, CC } foo_e;
+ typedef struct { foo_e f:2; } foo_s;
+ """)
+ s = ffi.new("foo_s *")
+ s.f = 2
+ assert s.f == 2
+
+ def test_anonymous_struct(self):
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("typedef struct { int a; } foo_t;")
+ ffi.cdef("typedef struct { char b, c; } bar_t;")
+ f = ffi.new("foo_t *", [12345])
+ b = ffi.new("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 'bar_t *'")
+
+ def test_struct_with_two_usages(self):
+ for name in ['foo_s', '']: # anonymous or not
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("typedef struct %s { int a; } foo_t, *foo_p;" % name)
+ f = ffi.new("foo_t *", [12345])
+ ps = ffi.new("foo_p[]", [f])
+
+ def test_pointer_arithmetic(self):
+ ffi = FFI(backend=self.Backend())
+ 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):
+ ffi = FFI(backend=self.Backend())
+ 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):
+ ffi = FFI(backend=self.Backend())
+ 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):
+ ffi = FFI(backend=self.Backend())
+ a = ffi.new("short *", 100)
+ try:
+ b = ffi.buffer(a)
+ except NotImplementedError as e:
+ py.test.skip(str(e))
+ assert type(b) is ffi.buffer
+ 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):
+ ffi = FFI(backend=self.Backend())
+ 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):
+ ffi = FFI(backend=self.Backend())
+ 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):
+ ffi = FFI(backend=self.Backend())
+ 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):
+ ffi = FFI(backend=self.Backend())
+ 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):
+ ffi = FFI(backend=self.Backend())
+ 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_ffi_buffer_comparisons(self):
+ ffi = FFI(backend=self.Backend())
+ ba = bytearray(range(100, 110))
+ if sys.version_info >= (2, 7):
+ assert ba == memoryview(ba) # justification for the following
+ a = ffi.new("uint8_t[]", list(ba))
+ c = ffi.new("uint8_t[]", [99] + list(ba))
+ try:
+ b_full = ffi.buffer(a)
+ b_short = ffi.buffer(a, 3)
+ b_mid = ffi.buffer(a, 6)
+ b_other = ffi.buffer(c, 6)
+ except NotImplementedError as e:
+ py.test.skip(str(e))
+ else:
+ content = b_full[:]
+ assert content == b_full == ba
+ assert b_other < b_short < b_mid < b_full
+ assert ba > b_mid > ba[0:2]
+ assert b_short != ba[1:4]
+
+ def test_array_in_struct(self):
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("struct foo_s { int len; short data[5]; };")
+ p = ffi.new("struct foo_s *")
+ p.data[3] = 5
+ assert p.data[3] == 5
+ assert repr(p.data).startswith("<cdata 'short[5]' 0x")
+
+ def test_struct_containing_array_varsize_workaround(self):
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("struct foo_s { int len; short data[0]; };")
+ p = ffi.new("char[]", ffi.sizeof("struct foo_s") + 7 * SIZE_OF_SHORT)
+ q = ffi.cast("struct foo_s *", 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 = FFI(backend=self.Backend())
+ 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):
+ ffi = FFI(backend=self.Backend())
+ 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):
+ ffi = FFI(backend=self.Backend())
+ f = ffi.cast("int(*)(int)", 42)
+ assert f != ffi.NULL
+ py.test.raises(CDefError, ffi.cast, "int(int)", 42)
+ py.test.raises(CDefError, 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.
+ ffi = FFI(backend=self.Backend())
+ 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 '...'")
+ ffi = FFI(backend=self.Backend())
+ 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 = FFI(backend=self.Backend())
+ #
+ @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_unique_types(self):
+ ffi1 = FFI(backend=self.Backend())
+ ffi2 = FFI(backend=self.Backend())
+ 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
+ assert ffi1.typeof("struct foo") is not ffi2.typeof("struct foo")
+ assert ffi1.typeof("union foo *") is not ffi2.typeof("union foo*")
+ # the following test is an opaque enum, which we no longer support
+ #assert ffi1.typeof("enum foo") is not ffi2.typeof("enum foo")
+ # sanity check: twice 'ffi1'
+ assert ffi1.typeof("struct foo*") is ffi1.typeof("struct foo *")
+
+ def test_anonymous_enum(self):
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("typedef enum { Value0 = 0 } e, *pe;\n"
+ "typedef enum { Value1 = 1 } e1;")
+ assert ffi.getctype("e*") == 'e *'
+ assert ffi.getctype("pe") == 'e *'
+ assert ffi.getctype("e1*") == 'e1 *'
+
+ def test_opaque_enum(self):
+ import warnings
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("enum foo;")
+ with warnings.catch_warnings(record=True) as log:
+ warnings.simplefilter("always")
+ n = ffi.cast("enum foo", -1)
+ assert int(n) == 0xffffffff
+ assert str(log[0].message) == (
+ "'enum foo' has no values explicitly defined; "
+ "guessing that it is equivalent to 'unsigned int'")
+
+ def test_new_ctype(self):
+ ffi = FFI(backend=self.Backend())
+ 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):
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("enum e { AA=0, BB=0, CC=0, DD=0 };")
+ e = ffi.cast("enum e", 0)
+ assert ffi.string(e) == "AA" # pick the first one arbitrarily
+
+ def test_enum_refer_previous_enum_value(self):
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("enum e { AA, BB=2, CC=4, DD=BB, EE, FF=CC, GG=FF };")
+ assert ffi.string(ffi.cast("enum e", 2)) == "BB"
+ assert ffi.string(ffi.cast("enum e", 3)) == "EE"
+ assert ffi.sizeof("char[DD]") == 2
+ assert ffi.sizeof("char[EE]") == 3
+ assert ffi.sizeof("char[FF]") == 4
+ assert ffi.sizeof("char[GG]") == 4
+
+ def test_nested_anonymous_struct(self):
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("""
+ struct foo_s {
+ struct { int a, b; };
+ union { int c, d; };
+ };
+ """)
+ assert ffi.sizeof("struct foo_s") == 3 * SIZE_OF_INT
+ p = ffi.new("struct foo_s *", [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 foo_s *", {'b': 12, 'd': 14})
+ assert p.a == 0
+ assert p.b == 12
+ assert p.c == 14
+ assert p.d == 14
+ py.test.raises(ValueError, ffi.new, "struct foo_s *", [0, 0, 0, 0])
+
+ def test_nested_field_offset_align(self):
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("""
+ struct foo_s {
+ struct { int a; char b; };
+ union { char c; };
+ };
+ """)
+ assert ffi.offsetof("struct foo_s", "c") == 2 * SIZE_OF_INT
+ assert ffi.sizeof("struct foo_s") == 3 * SIZE_OF_INT
+
+ def test_nested_anonymous_union(self):
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("""
+ union foo_u {
+ struct { int a, b; };
+ union { int c, d; };
+ };
+ """)
+ assert ffi.sizeof("union foo_u") == 2 * SIZE_OF_INT
+ p = ffi.new("union foo_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 foo_u *", {'d': 14})
+ assert p.a == 14
+ assert p.b == 0
+ assert p.c == 14
+ assert p.d == 14
+ p = ffi.new("union foo_u *", {'a': -63, 'b': 12})
+ assert p.a == -63
+ assert p.b == 12
+ assert p.c == -63
+ assert p.d == -63
+ p = ffi.new("union foo_u *", [123, 456])
+ assert p.a == 123
+ assert p.b == 456
+ assert p.c == 123
+ assert p.d == 123
+ py.test.raises(ValueError, ffi.new, "union foo_u *", [0, 0, 0])
+
+ def test_nested_anonymous_struct_2(self):
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("""
+ struct foo_s {
+ int a;
+ union { int b; union { int c, d; }; };
+ int e;
+ };
+ """)
+ assert ffi.sizeof("struct foo_s") == 3 * SIZE_OF_INT
+ p = ffi.new("struct foo_s *", [11, 22, 33])
+ assert p.a == 11
+ assert p.b == p.c == p.d == 22
+ assert p.e == 33
+ py.test.raises(ValueError, ffi.new, "struct foo_s *", [11, 22, 33, 44])
+ FOO = ffi.typeof("struct foo_s")
+ fields = [(name, fld.offset, fld.flags) for (name, fld) in FOO.fields]
+ assert fields == [
+ ('a', 0 * SIZE_OF_INT, 0),
+ ('b', 1 * SIZE_OF_INT, 0),
+ ('c', 1 * SIZE_OF_INT, 1),
+ ('d', 1 * SIZE_OF_INT, 1),
+ ('e', 2 * SIZE_OF_INT, 0),
+ ]
+
+ def test_cast_to_array_type(self):
+ ffi = FFI(backend=self.Backend())
+ 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):
+ ffi = FFI(backend=self.Backend())
+ 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)
+ assert ffi.typeof(q) is ffi.typeof(p)
+ import gc; gc.collect()
+ assert seen == []
+ del q
+ import gc; gc.collect(); gc.collect(); gc.collect()
+ assert seen == [1]
+
+ def test_gc_2(self):
+ ffi = FFI(backend=self.Backend())
+ 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):
+ ffi = FFI(backend=self.Backend())
+ 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):
+ ffi = FFI(backend=self.Backend())
+ 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_gc_disable(self):
+ ffi = FFI(backend=self.Backend())
+ p = ffi.new("int *", 123)
+ py.test.raises(TypeError, ffi.gc, p, None)
+ 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 == []
+ assert ffi.gc(q1, None) is None
+ del q1, q2
+ import gc; gc.collect(); gc.collect(); gc.collect()
+ assert seen == [2]
+
+ def test_gc_finite_list(self):
+ ffi = FFI(backend=self.Backend())
+ p = ffi.new("int *", 123)
+ keepalive = []
+ for i in range(10):
+ keepalive.append(ffi.gc(p, lambda p: None))
+ del keepalive[:]
+ import gc; gc.collect(); gc.collect()
+ for i in range(10):
+ keepalive.append(ffi.gc(p, lambda p: None))
+
+ def test_CData_CType(self):
+ ffi = FFI(backend=self.Backend())
+ 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):
+ ffi = FFI(backend=self.Backend())
+ assert isinstance(ffi.typeof("int"), ffi.CType)
+
+ def test_bool(self):
+ ffi = FFI(backend=self.Backend())
+ 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_use_own_bool(self):
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("""typedef int bool;""")
+
+ def test_ordering_bug1(self):
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("""
+ struct foo_s {
+ struct bar_s *p;
+ };
+ struct bar_s {
+ struct foo_s foo;
+ };
+ """)
+ q = ffi.new("struct foo_s *")
+ bar = ffi.new("struct bar_s *")
+ q.p = bar
+ assert q.p.foo.p == ffi.NULL
+
+ def test_ordering_bug2(self):
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("""
+ struct bar_s;
+
+ struct foo_s {
+ void (*foo)(struct bar_s[]);
+ };
+
+ struct bar_s {
+ struct foo_s foo;
+ };
+ """)
+ q = ffi.new("struct foo_s *")
+
+ def test_addressof(self):
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("struct foo_s { int x, y; };")
+ p = ffi.new("struct foo_s *")
+ a = ffi.addressof(p[0])
+ assert repr(a).startswith("<cdata 'struct foo_s *' 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):
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("struct foo_s { int x, y; };")
+ p = ffi.new("struct foo_s *")
+ a = ffi.addressof(p[0], 'y')
+ assert repr(a).startswith("<cdata 'int *' 0x")
+ assert int(ffi.cast("uintptr_t", a)) == (
+ int(ffi.cast("uintptr_t", p)) + ffi.sizeof("int"))
+ assert a == ffi.addressof(p, 'y')
+ assert a != ffi.addressof(p, 'x')
+
+ def test_addressof_field_nested(self):
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("struct foo_s { int x, y; };"
+ "struct bar_s { struct foo_s a, b; };")
+ p = ffi.new("struct bar_s *")
+ py.test.raises(KeyError, ffi.addressof, p[0], 'b.y')
+ a = ffi.addressof(p[0], 'b', 'y')
+ assert int(ffi.cast("uintptr_t", a)) == (
+ int(ffi.cast("uintptr_t", p)) +
+ ffi.sizeof("struct foo_s") + ffi.sizeof("int"))
+
+ def test_addressof_anonymous_struct(self):
+ ffi = FFI()
+ ffi.cdef("typedef struct { int x; } foo_t;")
+ p = ffi.new("foo_t *")
+ a = ffi.addressof(p[0])
+ assert a == p
+
+ def test_addressof_array(self):
+ ffi = FFI()
+ 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):
+ ffi = FFI()
+ 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)
+ #
+ ffi.cdef("struct foo { int a, b; };")
+ array = ffi.new("struct foo[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):
+ ffi = FFI()
+ ffi.cdef("struct foo { int a, b; int c[50]; };")
+ p = ffi.new("struct foo *")
+ 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 foo *")
+ assert p2 == p + 1
+
+ def test_multiple_independent_structs(self):
+ ffi1 = FFI(); ffi1.cdef("struct foo { int x; };")
+ ffi2 = FFI(); ffi2.cdef("struct foo { int y, z; };")
+ foo1 = ffi1.new("struct foo *", [10])
+ foo2 = ffi2.new("struct foo *", [20, 30])
+ assert foo1.x == 10
+ assert foo2.y == 20
+ assert foo2.z == 30
+
+ def test_missing_include(self):
+ backend = self.Backend()
+ ffi1 = FFI(backend=backend)
+ ffi2 = FFI(backend=backend)
+ ffi1.cdef("typedef signed char schar_t;")
+ py.test.raises(CDefError, ffi2.cast, "schar_t", 142)
+
+ def test_include_typedef(self):
+ backend = self.Backend()
+ ffi1 = FFI(backend=backend)
+ ffi2 = FFI(backend=backend)
+ ffi1.cdef("typedef signed char schar_t;")
+ ffi2.include(ffi1)
+ p = ffi2.cast("schar_t", 142)
+ assert int(p) == 142 - 256
+
+ def test_include_struct(self):
+ backend = self.Backend()
+ ffi1 = FFI(backend=backend)
+ ffi2 = FFI(backend=backend)
+ ffi1.cdef("struct foo { int x; };")
+ ffi2.include(ffi1)
+ p = ffi2.new("struct foo *", [142])
+ assert p.x == 142
+
+ def test_include_union(self):
+ backend = self.Backend()
+ ffi1 = FFI(backend=backend)
+ ffi2 = FFI(backend=backend)
+ ffi1.cdef("union foo { int x; };")
+ ffi2.include(ffi1)
+ p = ffi2.new("union foo *", [142])
+ assert p.x == 142
+
+ def test_include_enum(self):
+ backend = self.Backend()
+ ffi1 = FFI(backend=backend)
+ ffi2 = FFI(backend=backend)
+ ffi1.cdef("enum foo { FA, FB, FC };")
+ ffi2.include(ffi1)
+ p = ffi2.cast("enum foo", 1)
+ assert ffi2.string(p) == "FB"
+ assert ffi2.sizeof("char[FC]") == 2
+
+ def test_include_typedef_2(self):
+ backend = self.Backend()
+ ffi1 = FFI(backend=backend)
+ ffi2 = FFI(backend=backend)
+ ffi1.cdef("typedef struct { int x; } *foo_p;")
+ ffi2.include(ffi1)
+ p = ffi2.new("foo_p", [142])
+ assert p.x == 142
+
+ def test_ignore_multiple_declarations_of_constant(self):
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("#define FOO 42")
+ ffi.cdef("#define FOO 42")
+ py.test.raises(FFIError, ffi.cdef, "#define FOO 43")
+
+ def test_struct_packed(self):
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("struct nonpacked { char a; int b; };")
+ ffi.cdef("struct is_packed { char a; int b; };", packed=True)
+ ffi.cdef("struct is_packed1 { char a; int b; };", pack=1)
+ ffi.cdef("struct is_packed2 { char a; int b; };", pack=2)
+ ffi.cdef("struct is_packed4 { char a; int b; };", pack=4)
+ ffi.cdef("struct is_packed8 { char a; int b; };", pack=8)
+ assert ffi.sizeof("struct nonpacked") == 8
+ assert ffi.sizeof("struct is_packed") == 5
+ assert ffi.sizeof("struct is_packed1") == 5
+ assert ffi.sizeof("struct is_packed2") == 6
+ assert ffi.sizeof("struct is_packed4") == 8
+ assert ffi.sizeof("struct is_packed8") == 8
+ assert ffi.alignof("struct nonpacked") == 4
+ assert ffi.alignof("struct is_packed") == 1
+ assert ffi.alignof("struct is_packed1") == 1
+ assert ffi.alignof("struct is_packed2") == 2
+ assert ffi.alignof("struct is_packed4") == 4
+ assert ffi.alignof("struct is_packed8") == 4
+ for name in ['is_packed', 'is_packed1', 'is_packed2',
+ 'is_packed4', 'is_packed8']:
+ s = ffi.new("struct %s[2]" % name)
+ 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_pack_valueerror(self):
+ ffi = FFI(backend=self.Backend())
+ py.test.raises(ValueError, ffi.cdef, "", pack=3)
+ py.test.raises(ValueError, ffi.cdef, "", packed=2)
+ py.test.raises(ValueError, ffi.cdef, "", packed=True, pack=1)
+
+ def test_define_integer_constant(self):
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("""
+ #define DOT_0 0
+ #define DOT 100
+ #define DOT_OCT 0100l
+ #define DOT_HEX 0x100u
+ #define DOT_HEX2 0X10
+ #define DOT_UL 1000UL
+ enum foo {AA, BB=DOT, CC};
+ """)
+ needs_dlopen_none()
+ lib = ffi.dlopen(None)
+ assert ffi.string(ffi.cast("enum foo", 100)) == "BB"
+ assert lib.DOT_0 == 0
+ assert lib.DOT == 100
+ assert lib.DOT_OCT == 0o100
+ assert lib.DOT_HEX == 0x100
+ assert lib.DOT_HEX2 == 0x10
+ assert lib.DOT_UL == 1000
+
+ def test_opaque_struct_becomes_nonopaque(self):
+ # Issue #193: if we use a struct between the first cdef() where it is
+ # declared and another cdef() where its fields are defined, then the
+ # definition was ignored.
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("struct foo_s;")
+ py.test.raises(TypeError, ffi.new, "struct foo_s *")
+ ffi.cdef("struct foo_s { int x; };")
+ ffi.new("struct foo_s *")
+
+ def test_ffi_self_include(self):
+ ffi = FFI(backend=self.Backend())
+ py.test.raises(ValueError, ffi.include, ffi)
+
+ def test_anonymous_enum_include(self):
+ ffi1 = FFI()
+ ffi1.cdef("enum { EE1 };")
+ ffi = FFI()
+ ffi.include(ffi1)
+ ffi.cdef("enum { EE2, EE3 };")
+ needs_dlopen_none()
+ lib = ffi.dlopen(None)
+ assert lib.EE1 == 0
+ assert lib.EE2 == 0
+ assert lib.EE3 == 1
+
+ def test_init_once(self):
+ def do_init():
+ seen.append(1)
+ return 42
+ ffi = 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(self):
+ import sys, time
+ if sys.version_info < (3,):
+ import thread
+ else:
+ import _thread as thread
+ #
+ def do_init():
+ seen.append('init!')
+ time.sleep(1)
+ seen.append('init done')
+ return 7
+ ffi = 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_sizeof_struct_directly(self):
+ # only works with the Python FFI instances
+ ffi = FFI(backend=self.Backend())
+ assert ffi.sizeof("struct{int a;}") == ffi.sizeof("int")
+
+ def test_callback_large_struct(self):
+ ffi = FFI(backend=self.Backend())
+ # more than 8 bytes
+ ffi.cdef("struct foo_s { unsigned long a, b, c; };")
+ #
+ @ffi.callback("void(struct foo_s)")
+ def cb(s):
+ seen.append(ffi.typeof(s))
+ s.a += 1
+ s.b += 2
+ s.c += 3
+ seen.append(s.a)
+ seen.append(s.b)
+ seen.append(s.c)
+ #
+ s1 = ffi.new("struct foo_s *", {'a': 100, 'b': 200, 'c': 300})
+ seen = []
+ cb(s1[0])
+ assert len(seen) == 4
+ assert s1.a == 100 # unmodified
+ assert s1.b == 200
+ assert s1.c == 300
+ assert seen[0] == ffi.typeof("struct foo_s")
+ assert seen[1] == 101
+ assert seen[2] == 202
+ assert seen[3] == 303
+
+ def test_ffi_array_as_init(self):
+ ffi = FFI(backend=self.Backend())
+ p = ffi.new("int[4]", [10, 20, 30, 400])
+ q = ffi.new("int[4]", p)
+ assert list(q) == [10, 20, 30, 400]
+ py.test.raises(TypeError, ffi.new, "int[3]", p)
+ py.test.raises(TypeError, ffi.new, "int[5]", p)
+ py.test.raises(TypeError, ffi.new, "int16_t[4]", p)
+ s = ffi.new("struct {int i[4];}*", {'i': p})
+ assert list(s.i) == [10, 20, 30, 400]
+
+ def test_too_many_initializers(self):
+ ffi = FFI(backend=self.Backend())
+ py.test.raises(IndexError, ffi.new, "int[4]", [10, 20, 30, 40, 50])
diff --git a/testing/cffi0/callback_in_thread.py b/testing/cffi0/callback_in_thread.py
new file mode 100644
index 0000000..c98605c
--- /dev/null
+++ b/testing/cffi0/callback_in_thread.py
@@ -0,0 +1,42 @@
+import sys, time
+sys.path.insert(0, sys.argv[1])
+from cffi import FFI
+
+def _run_callback_in_thread():
+ ffi = FFI()
+ ffi.cdef("""
+ typedef int (*mycallback_func_t)(int, int);
+ int threaded_ballback_test(mycallback_func_t mycb);
+ """)
+ lib = ffi.verify("""
+ #include <pthread.h>
+ typedef int (*mycallback_func_t)(int, int);
+ void *my_wait_function(void *ptr) {
+ mycallback_func_t cbfunc = (mycallback_func_t)ptr;
+ cbfunc(10, 10);
+ cbfunc(12, 15);
+ return NULL;
+ }
+ int threaded_ballback_test(mycallback_func_t mycb) {
+ pthread_t thread;
+ pthread_create(&thread, NULL, my_wait_function, (void*)mycb);
+ return 0;
+ }
+ """, extra_compile_args=['-pthread'])
+ seen = []
+ @ffi.callback('int(*)(int,int)')
+ def mycallback(x, y):
+ time.sleep(0.022)
+ seen.append((x, y))
+ return 0
+ lib.threaded_ballback_test(mycallback)
+ count = 300
+ while len(seen) != 2:
+ time.sleep(0.01)
+ count -= 1
+ assert count > 0, "timeout"
+ assert seen == [(10, 10), (12, 15)]
+
+print('STARTING')
+_run_callback_in_thread()
+print('DONE')
diff --git a/testing/cffi0/snippets/distutils_module/setup.py b/testing/cffi0/snippets/distutils_module/setup.py
new file mode 100644
index 0000000..a4d5551
--- /dev/null
+++ b/testing/cffi0/snippets/distutils_module/setup.py
@@ -0,0 +1,7 @@
+
+from distutils.core import setup
+import snip_basic_verify
+
+setup(
+ py_modules=['snip_basic_verify'],
+ ext_modules=[snip_basic_verify.ffi.verifier.get_extension()])
diff --git a/testing/cffi0/snippets/distutils_module/snip_basic_verify.py b/testing/cffi0/snippets/distutils_module/snip_basic_verify.py
new file mode 100644
index 0000000..e8a867e
--- /dev/null
+++ b/testing/cffi0/snippets/distutils_module/snip_basic_verify.py
@@ -0,0 +1,17 @@
+
+from cffi import FFI
+import sys
+
+ffi = FFI()
+ffi.cdef(""" // some declarations from the man page
+ struct passwd {
+ char *pw_name;
+ ...;
+ };
+ struct passwd *getpwuid(int uid);
+""")
+C = ffi.verify(""" // passed to the real C compiler
+#include <sys/types.h>
+#include <pwd.h>
+""", libraries=[], # or a list of libraries to link with
+ force_generic_engine=hasattr(sys, '_force_generic_engine_'))
diff --git a/testing/cffi0/snippets/distutils_package_1/setup.py b/testing/cffi0/snippets/distutils_package_1/setup.py
new file mode 100644
index 0000000..e3d28a5
--- /dev/null
+++ b/testing/cffi0/snippets/distutils_package_1/setup.py
@@ -0,0 +1,7 @@
+
+from distutils.core import setup
+import snip_basic_verify1
+
+setup(
+ packages=['snip_basic_verify1'],
+ ext_modules=[snip_basic_verify1.ffi.verifier.get_extension()])
diff --git a/testing/cffi0/snippets/distutils_package_1/snip_basic_verify1/__init__.py b/testing/cffi0/snippets/distutils_package_1/snip_basic_verify1/__init__.py
new file mode 100644
index 0000000..e8a867e
--- /dev/null
+++ b/testing/cffi0/snippets/distutils_package_1/snip_basic_verify1/__init__.py
@@ -0,0 +1,17 @@
+
+from cffi import FFI
+import sys
+
+ffi = FFI()
+ffi.cdef(""" // some declarations from the man page
+ struct passwd {
+ char *pw_name;
+ ...;
+ };
+ struct passwd *getpwuid(int uid);
+""")
+C = ffi.verify(""" // passed to the real C compiler
+#include <sys/types.h>
+#include <pwd.h>
+""", libraries=[], # or a list of libraries to link with
+ force_generic_engine=hasattr(sys, '_force_generic_engine_'))
diff --git a/testing/cffi0/snippets/distutils_package_2/setup.py b/testing/cffi0/snippets/distutils_package_2/setup.py
new file mode 100644
index 0000000..6d8f72a
--- /dev/null
+++ b/testing/cffi0/snippets/distutils_package_2/setup.py
@@ -0,0 +1,8 @@
+
+from distutils.core import setup
+import snip_basic_verify2
+
+setup(
+ packages=['snip_basic_verify2'],
+ ext_package='snip_basic_verify2',
+ ext_modules=[snip_basic_verify2.ffi.verifier.get_extension()])
diff --git a/testing/cffi0/snippets/distutils_package_2/snip_basic_verify2/__init__.py b/testing/cffi0/snippets/distutils_package_2/snip_basic_verify2/__init__.py
new file mode 100644
index 0000000..b4ee686
--- /dev/null
+++ b/testing/cffi0/snippets/distutils_package_2/snip_basic_verify2/__init__.py
@@ -0,0 +1,18 @@
+
+from cffi import FFI
+import sys
+
+ffi = FFI()
+ffi.cdef(""" // some declarations from the man page
+ struct passwd {
+ char *pw_name;
+ ...;
+ };
+ struct passwd *getpwuid(int uid);
+""")
+C = ffi.verify(""" // passed to the real C compiler
+#include <sys/types.h>
+#include <pwd.h>
+""", libraries=[], # or a list of libraries to link with
+ ext_package='snip_basic_verify2',
+ force_generic_engine=hasattr(sys, '_force_generic_engine_'))
diff --git a/testing/cffi0/snippets/infrastructure/setup.py b/testing/cffi0/snippets/infrastructure/setup.py
new file mode 100644
index 0000000..ea89f50
--- /dev/null
+++ b/testing/cffi0/snippets/infrastructure/setup.py
@@ -0,0 +1,5 @@
+
+from distutils.core import setup
+
+setup(packages=['snip_infrastructure'],
+ requires=['cffi'])
diff --git a/testing/cffi0/snippets/infrastructure/snip_infrastructure/__init__.py b/testing/cffi0/snippets/infrastructure/snip_infrastructure/__init__.py
new file mode 100644
index 0000000..dad950d
--- /dev/null
+++ b/testing/cffi0/snippets/infrastructure/snip_infrastructure/__init__.py
@@ -0,0 +1,3 @@
+
+def func():
+ return 42
diff --git a/testing/cffi0/snippets/setuptools_module/setup.py b/testing/cffi0/snippets/setuptools_module/setup.py
new file mode 100644
index 0000000..30f2e04
--- /dev/null
+++ b/testing/cffi0/snippets/setuptools_module/setup.py
@@ -0,0 +1,8 @@
+
+from setuptools import setup
+import snip_setuptools_verify
+
+setup(
+ zip_safe=False,
+ py_modules=['snip_setuptools_verify'],
+ ext_modules=[snip_setuptools_verify.ffi.verifier.get_extension()])
diff --git a/testing/cffi0/snippets/setuptools_module/snip_setuptools_verify.py b/testing/cffi0/snippets/setuptools_module/snip_setuptools_verify.py
new file mode 100644
index 0000000..e8a867e
--- /dev/null
+++ b/testing/cffi0/snippets/setuptools_module/snip_setuptools_verify.py
@@ -0,0 +1,17 @@
+
+from cffi import FFI
+import sys
+
+ffi = FFI()
+ffi.cdef(""" // some declarations from the man page
+ struct passwd {
+ char *pw_name;
+ ...;
+ };
+ struct passwd *getpwuid(int uid);
+""")
+C = ffi.verify(""" // passed to the real C compiler
+#include <sys/types.h>
+#include <pwd.h>
+""", libraries=[], # or a list of libraries to link with
+ force_generic_engine=hasattr(sys, '_force_generic_engine_'))
diff --git a/testing/cffi0/snippets/setuptools_package_1/setup.py b/testing/cffi0/snippets/setuptools_package_1/setup.py
new file mode 100644
index 0000000..18ea3f6
--- /dev/null
+++ b/testing/cffi0/snippets/setuptools_package_1/setup.py
@@ -0,0 +1,8 @@
+
+from setuptools import setup
+import snip_setuptools_verify1
+
+setup(
+ zip_safe=False,
+ packages=['snip_setuptools_verify1'],
+ ext_modules=[snip_setuptools_verify1.ffi.verifier.get_extension()])
diff --git a/testing/cffi0/snippets/setuptools_package_1/snip_setuptools_verify1/__init__.py b/testing/cffi0/snippets/setuptools_package_1/snip_setuptools_verify1/__init__.py
new file mode 100644
index 0000000..e8a867e
--- /dev/null
+++ b/testing/cffi0/snippets/setuptools_package_1/snip_setuptools_verify1/__init__.py
@@ -0,0 +1,17 @@
+
+from cffi import FFI
+import sys
+
+ffi = FFI()
+ffi.cdef(""" // some declarations from the man page
+ struct passwd {
+ char *pw_name;
+ ...;
+ };
+ struct passwd *getpwuid(int uid);
+""")
+C = ffi.verify(""" // passed to the real C compiler
+#include <sys/types.h>
+#include <pwd.h>
+""", libraries=[], # or a list of libraries to link with
+ force_generic_engine=hasattr(sys, '_force_generic_engine_'))
diff --git a/testing/cffi0/snippets/setuptools_package_2/setup.py b/testing/cffi0/snippets/setuptools_package_2/setup.py
new file mode 100644
index 0000000..87fb22b
--- /dev/null
+++ b/testing/cffi0/snippets/setuptools_package_2/setup.py
@@ -0,0 +1,9 @@
+
+from setuptools import setup
+import snip_setuptools_verify2
+
+setup(
+ zip_safe=False,
+ packages=['snip_setuptools_verify2'],
+ ext_package='snip_setuptools_verify2',
+ ext_modules=[snip_setuptools_verify2.ffi.verifier.get_extension()])
diff --git a/testing/cffi0/snippets/setuptools_package_2/snip_setuptools_verify2/__init__.py b/testing/cffi0/snippets/setuptools_package_2/snip_setuptools_verify2/__init__.py
new file mode 100644
index 0000000..5f4bd13
--- /dev/null
+++ b/testing/cffi0/snippets/setuptools_package_2/snip_setuptools_verify2/__init__.py
@@ -0,0 +1,18 @@
+
+from cffi import FFI
+import sys
+
+ffi = FFI()
+ffi.cdef(""" // some declarations from the man page
+ struct passwd {
+ char *pw_name;
+ ...;
+ };
+ struct passwd *getpwuid(int uid);
+""")
+C = ffi.verify(""" // passed to the real C compiler
+#include <sys/types.h>
+#include <pwd.h>
+""", libraries=[], # or a list of libraries to link with
+ ext_package='snip_setuptools_verify2',
+ force_generic_engine=hasattr(sys, '_force_generic_engine_'))
diff --git a/testing/cffi0/test_cdata.py b/testing/cffi0/test_cdata.py
new file mode 100644
index 0000000..23989ab
--- /dev/null
+++ b/testing/cffi0/test_cdata.py
@@ -0,0 +1,41 @@
+import py
+from cffi import FFI
+
+class FakeBackend(object):
+
+ def nonstandard_integer_types(self):
+ return {}
+
+ def sizeof(self, name):
+ return 1
+
+ def load_library(self, path):
+ return "fake library"
+
+ def new_primitive_type(self, name):
+ return FakeType("primitive " + name)
+
+ def new_void_type(self):
+ return FakeType("void")
+ def new_pointer_type(self, x):
+ return FakeType('ptr-to-%r' % (x,))
+ def new_array_type(self, x, y):
+ return FakeType('array-from-%r-len-%r' % (x, y))
+ def cast(self, x, y):
+ return 'casted!'
+ def _get_types(self):
+ return "CData", "CType"
+
+ buffer = "buffer type"
+
+
+class FakeType(object):
+ def __init__(self, cdecl):
+ self.cdecl = cdecl
+
+
+def test_typeof():
+ ffi = FFI(backend=FakeBackend())
+ clong = ffi.typeof("signed long int")
+ assert isinstance(clong, FakeType)
+ assert clong.cdecl == 'primitive long'
diff --git a/testing/cffi0/test_ctypes.py b/testing/cffi0/test_ctypes.py
new file mode 100644
index 0000000..a70c8f0
--- /dev/null
+++ b/testing/cffi0/test_ctypes.py
@@ -0,0 +1,43 @@
+import py, sys
+from testing.cffi0 import backend_tests
+from cffi.backend_ctypes import CTypesBackend
+
+
+class TestCTypes(backend_tests.BackendTests):
+ # for individual tests see
+ # ====> backend_tests.py
+
+ Backend = CTypesBackend
+ TypeRepr = "<class 'ffi.CData<%s>'>"
+
+ def test_array_of_func_ptr(self):
+ py.test.skip("ctypes backend: not supported: "
+ "initializers for function pointers")
+
+ def test_structptr_argument(self):
+ py.test.skip("ctypes backend: not supported: passing a list "
+ "for a pointer argument")
+
+ def test_array_argument_as_list(self):
+ py.test.skip("ctypes backend: not supported: passing a list "
+ "for a pointer argument")
+
+ def test_cast_to_array_type(self):
+ py.test.skip("ctypes backend: not supported: casting to array")
+
+ def test_nested_anonymous_struct(self):
+ py.test.skip("ctypes backend: not supported: nested anonymous struct")
+
+ def test_nested_field_offset_align(self):
+ py.test.skip("ctypes backend: not supported: nested anonymous struct")
+
+ def test_nested_anonymous_union(self):
+ py.test.skip("ctypes backend: not supported: nested anonymous union")
+
+ def test_nested_anonymous_struct_2(self):
+ py.test.skip("ctypes backend: not supported: nested anonymous union")
+
+ def test_CData_CType_2(self):
+ if sys.version_info >= (3,):
+ py.test.skip("ctypes backend: not supported in Python 3: CType")
+ backend_tests.BackendTests.test_CData_CType_2(self)
diff --git a/testing/cffi0/test_ffi_backend.py b/testing/cffi0/test_ffi_backend.py
new file mode 100644
index 0000000..12ecaee
--- /dev/null
+++ b/testing/cffi0/test_ffi_backend.py
@@ -0,0 +1,576 @@
+import py, sys, platform
+import pytest
+from testing.cffi0 import backend_tests, test_function, test_ownlib
+from testing.support import u
+from cffi import FFI
+import _cffi_backend
+
+
+class TestFFI(backend_tests.BackendTests,
+ test_function.TestFunction,
+ test_ownlib.TestOwnLib):
+ TypeRepr = "<ctype '%s'>"
+
+ @staticmethod
+ def Backend():
+ return _cffi_backend
+
+ def test_not_supported_bitfield_in_result(self):
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("struct foo_s { int a,b,c,d,e; int x:1; };")
+ e = py.test.raises(NotImplementedError, ffi.callback,
+ "struct foo_s foo(void)", lambda: 42)
+ assert str(e.value) == ("struct foo_s(*)(): "
+ "callback with unsupported argument or return type or with '...'")
+
+ def test_inspecttype(self):
+ ffi = FFI(backend=self.Backend())
+ 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):
+ ffi = FFI(backend=self.Backend())
+ 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_callback_onerror(self):
+ ffi = FFI(backend=self.Backend())
+ seen = []
+ def oops(*args):
+ seen.append(args)
+ def otherfunc():
+ raise LookupError
+ def cb(n):
+ otherfunc()
+ a = ffi.callback("int(*)(int)", cb, error=42, onerror=oops)
+ res = a(234)
+ assert res == 42
+ assert len(seen) == 1
+ exc, val, tb = seen[0]
+ assert exc is LookupError
+ assert isinstance(val, LookupError)
+ assert tb.tb_frame.f_code.co_name == 'cb'
+ assert tb.tb_frame.f_locals['n'] == 234
+
+ def test_ffi_new_allocator_2(self):
+ ffi = FFI(backend=self.Backend())
+ 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]
+ assert repr(seen[2]) == "<cdata 'char[]' owning 41 bytes>"
+ assert repr(seen[3]) == "<cdata 'char[]' owning 41 bytes>"
+
+ def test_ffi_new_allocator_3(self):
+ ffi = FFI(backend=self.Backend())
+ 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(self):
+ ffi = FFI(backend=self.Backend())
+ 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]")
+
+
+class TestBitfield:
+ def check(self, source, expected_ofs_y, expected_align, expected_size):
+ # NOTE: 'expected_*' is the numbers expected from GCC.
+ # The numbers expected from MSVC are not explicitly written
+ # in this file, and will just be taken from the compiler.
+ ffi = FFI()
+ ffi.cdef("struct s1 { %s };" % source)
+ ctype = ffi.typeof("struct s1")
+ # verify the information with gcc
+ ffi1 = FFI()
+ ffi1.cdef("""
+ static const int Gofs_y, Galign, Gsize;
+ struct s1 *try_with_value(int fieldnum, long long value);
+ """)
+ fnames = [name for name, cfield in ctype.fields
+ if name and cfield.bitsize > 0]
+ setters = ['case %d: s.%s = value; break;' % iname
+ for iname in enumerate(fnames)]
+ lib = ffi1.verify("""
+ struct s1 { %s };
+ struct sa { char a; struct s1 b; };
+ #define Gofs_y offsetof(struct s1, y)
+ #define Galign offsetof(struct sa, b)
+ #define Gsize sizeof(struct s1)
+ struct s1 *try_with_value(int fieldnum, long long value)
+ {
+ static struct s1 s;
+ memset(&s, 0, sizeof(s));
+ switch (fieldnum) { %s }
+ return &s;
+ }
+ """ % (source, ' '.join(setters)))
+ if sys.platform == 'win32':
+ expected_ofs_y = lib.Gofs_y
+ expected_align = lib.Galign
+ expected_size = lib.Gsize
+ else:
+ assert (lib.Gofs_y, lib.Galign, lib.Gsize) == (
+ expected_ofs_y, expected_align, expected_size)
+ # the real test follows
+ assert ffi.offsetof("struct s1", "y") == expected_ofs_y
+ assert ffi.alignof("struct s1") == expected_align
+ assert ffi.sizeof("struct s1") == expected_size
+ # compare the actual storage of the two
+ for name, cfield in ctype.fields:
+ if cfield.bitsize < 0 or not name:
+ continue
+ if int(ffi.cast(cfield.type, -1)) == -1: # signed
+ min_value = -(1 << (cfield.bitsize-1))
+ max_value = (1 << (cfield.bitsize-1)) - 1
+ else:
+ min_value = 0
+ max_value = (1 << cfield.bitsize) - 1
+ for t in [1, 2, 4, 8, 16, 128, 2813, 89728, 981729,
+ -1,-2,-4,-8,-16,-128,-2813,-89728,-981729]:
+ if min_value <= t <= max_value:
+ self._fieldcheck(ffi, lib, fnames, name, t)
+
+ def _fieldcheck(self, ffi, lib, fnames, name, value):
+ s = ffi.new("struct s1 *")
+ setattr(s, name, value)
+ assert getattr(s, name) == value
+ raw1 = ffi.buffer(s)[:]
+ buff1 = ffi.buffer(s)
+ t = lib.try_with_value(fnames.index(name), value)
+ raw2 = ffi.buffer(t, len(raw1))[:]
+ assert raw1 == raw2
+ buff2 = ffi.buffer(t, len(buff1))
+ assert buff1 == buff2
+
+ def test_bitfield_basic(self):
+ self.check("int a; int b:9; int c:20; int y;", 8, 4, 12)
+ self.check("int a; short b:9; short c:7; int y;", 8, 4, 12)
+ self.check("int a; short b:9; short c:9; int y;", 8, 4, 12)
+
+ def test_bitfield_reuse_if_enough_space(self):
+ self.check("int a:2; char y;", 1, 4, 4)
+ self.check("int a:1; char b ; int c:1; char y;", 3, 4, 4)
+ self.check("int a:1; char b:8; int c:1; char y;", 3, 4, 4)
+ self.check("char a; int b:9; char y;", 3, 4, 4)
+ self.check("char a; short b:9; char y;", 4, 2, 6)
+ self.check("int a:2; char b:6; char y;", 1, 4, 4)
+ self.check("int a:2; char b:7; char y;", 2, 4, 4)
+ self.check("int a:2; short b:15; char c:2; char y;", 5, 4, 8)
+ self.check("int a:2; char b:1; char c:1; char y;", 1, 4, 4)
+
+ @pytest.mark.skipif("platform.machine().startswith(('arm', 'aarch64'))")
+ def test_bitfield_anonymous_no_align(self):
+ L = FFI().alignof("long long")
+ self.check("char y; int :1;", 0, 1, 2)
+ self.check("char x; int z:1; char y;", 2, 4, 4)
+ self.check("char x; int :1; char y;", 2, 1, 3)
+ self.check("char x; long long z:48; char y;", 7, L, 8)
+ self.check("char x; long long :48; char y;", 7, 1, 8)
+ self.check("char x; long long z:56; char y;", 8, L, 8 + L)
+ self.check("char x; long long :56; char y;", 8, 1, 9)
+ self.check("char x; long long z:57; char y;", L + 8, L, L + 8 + L)
+ self.check("char x; long long :57; char y;", L + 8, 1, L + 9)
+
+ @pytest.mark.skipif(
+ "not platform.machine().startswith(('arm', 'aarch64'))")
+ def test_bitfield_anonymous_align_arm(self):
+ L = FFI().alignof("long long")
+ self.check("char y; int :1;", 0, 4, 4)
+ self.check("char x; int z:1; char y;", 2, 4, 4)
+ self.check("char x; int :1; char y;", 2, 4, 4)
+ self.check("char x; long long z:48; char y;", 7, L, 8)
+ self.check("char x; long long :48; char y;", 7, 8, 8)
+ self.check("char x; long long z:56; char y;", 8, L, 8 + L)
+ self.check("char x; long long :56; char y;", 8, L, 8 + L)
+ self.check("char x; long long z:57; char y;", L + 8, L, L + 8 + L)
+ self.check("char x; long long :57; char y;", L + 8, L, L + 8 + L)
+
+ @pytest.mark.skipif("platform.machine().startswith(('arm', 'aarch64'))")
+ def test_bitfield_zero(self):
+ L = FFI().alignof("long long")
+ self.check("char y; int :0;", 0, 1, 4)
+ self.check("char x; int :0; char y;", 4, 1, 5)
+ self.check("char x; int :0; int :0; char y;", 4, 1, 5)
+ self.check("char x; long long :0; char y;", L, 1, L + 1)
+ self.check("short x, y; int :0; int :0;", 2, 2, 4)
+ self.check("char x; int :0; short b:1; char y;", 5, 2, 6)
+ self.check("int a:1; int :0; int b:1; char y;", 5, 4, 8)
+
+ @pytest.mark.skipif(
+ "not platform.machine().startswith(('arm', 'aarch64'))")
+ def test_bitfield_zero_arm(self):
+ L = FFI().alignof("long long")
+ self.check("char y; int :0;", 0, 4, 4)
+ self.check("char x; int :0; char y;", 4, 4, 8)
+ self.check("char x; int :0; int :0; char y;", 4, 4, 8)
+ self.check("char x; long long :0; char y;", L, 8, L + 8)
+ self.check("short x, y; int :0; int :0;", 2, 4, 4)
+ self.check("char x; int :0; short b:1; char y;", 5, 4, 8)
+ self.check("int a:1; int :0; int b:1; char y;", 5, 4, 8)
+
+ def test_error_cases(self):
+ ffi = FFI()
+ py.test.raises(TypeError,
+ 'ffi.cdef("struct s1 { float x:1; };"); ffi.new("struct s1 *")')
+ py.test.raises(TypeError,
+ 'ffi.cdef("struct s2 { char x:0; };"); ffi.new("struct s2 *")')
+ py.test.raises(TypeError,
+ 'ffi.cdef("struct s3 { char x:9; };"); ffi.new("struct s3 *")')
+
+ def test_struct_with_typedef(self):
+ ffi = FFI()
+ ffi.cdef("typedef struct { float x; } foo_t;")
+ p = ffi.new("foo_t *", [5.2])
+ assert repr(p).startswith("<cdata 'foo_t *' ")
+
+ def test_struct_array_no_length(self):
+ ffi = FFI()
+ ffi.cdef("struct foo_s { int x; int a[]; };")
+ p = ffi.new("struct foo_s *", [100, [200, 300, 400]])
+ assert p.x == 100
+ assert ffi.typeof(p.a) is ffi.typeof("int[]")
+ assert len(p.a) == 3 # length recorded
+ assert p.a[0] == 200
+ assert p.a[1] == 300
+ assert p.a[2] == 400
+ assert list(p.a) == [200, 300, 400]
+ q = ffi.cast("struct foo_s *", p)
+ assert q.x == 100
+ assert ffi.typeof(q.a) is ffi.typeof("int *") # no length recorded
+ py.test.raises(TypeError, len, q.a)
+ assert q.a[0] == 200
+ assert q.a[1] == 300
+ assert q.a[2] == 400
+ py.test.raises(TypeError, list, q.a)
+
+ @pytest.mark.skipif("sys.platform != 'win32'")
+ def test_getwinerror(self):
+ ffi = FFI()
+ code, message = ffi.getwinerror(1155)
+ assert code == 1155
+ assert message == ("No application is associated with the "
+ "specified file for this operation")
+ ffi.cdef("void SetLastError(int);")
+ lib = ffi.dlopen("Kernel32.dll")
+ lib.SetLastError(2)
+ code, message = ffi.getwinerror()
+ assert code == 2
+ assert message == "The system cannot find the file specified"
+ code, message = ffi.getwinerror(-1)
+ assert code == 2
+ assert message == "The system cannot find the file specified"
+
+ def test_from_buffer(self):
+ import array
+ ffi = FFI()
+ a = array.array('H', [10000, 20000, 30000])
+ c = ffi.from_buffer(a)
+ assert ffi.typeof(c) is ffi.typeof("char[]")
+ assert len(c) == 6
+ ffi.cast("unsigned short *", c)[1] += 500
+ assert list(a) == [10000, 20500, 30000]
+ 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) == 3
+ assert c[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_release(self):
+ ffi = FFI()
+ p = ffi.new("int[]", 123)
+ ffi.release(p)
+ # here, reading p[0] might give garbage or segfault...
+ ffi.release(p) # no effect
+
+ def test_memmove(self):
+ ffi = 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(self):
+ import array
+ ffi = 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(self):
+ ffi = 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_all_primitives(self):
+ ffi = FFI()
+ for name in [
+ "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",
+ ]:
+ x = ffi.sizeof(name)
+ assert 1 <= x <= 16
+
+ def test_ffi_def_extern(self):
+ ffi = FFI()
+ py.test.raises(ValueError, ffi.def_extern)
+
+ def test_introspect_typedef(self):
+ ffi = FFI()
+ ffi.cdef("typedef int foo_t;")
+ assert ffi.list_types() == (['foo_t'], [], [])
+ assert ffi.typeof('foo_t').kind == 'primitive'
+ assert ffi.typeof('foo_t').cname == 'int'
+ #
+ ffi.cdef("typedef signed char a_t, c_t, g_t, b_t;")
+ assert ffi.list_types() == (['a_t', 'b_t', 'c_t', 'foo_t', 'g_t'],
+ [], [])
+
+ def test_introspect_struct(self):
+ ffi = FFI()
+ ffi.cdef("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(self):
+ ffi = FFI()
+ ffi.cdef("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(self):
+ ffi = FFI()
+ ffi.cdef("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(self):
+ ffi1 = FFI()
+ ffi2 = FFI()
+ ffi1.cdef("typedef signed char schar_t; struct sint_t { int x; };")
+ ffi2.include(ffi1)
+ assert ffi1.list_types() == ffi2.list_types() == (
+ ['schar_t'], ['sint_t'], [])
+
+ def test_introspect_order(self):
+ 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;")
+ assert ffi.list_types() == (['CFFIb', 'CFFIbb', 'CFFIbbb'],
+ ['CFFIa', 'CFFIcc', 'CFFIccc'],
+ ['CFFIaa', 'CFFIaaa', 'CFFIg'])
+
+ def test_unpack(self):
+ ffi = 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(self):
+ ffi = FFI()
+ py.test.raises(ValueError, ffi.cast, "int[-5]", 0)
+
+ def test_cannot_instantiate_manually(self):
+ ffi = FFI()
+ ct = type(ffi.typeof("void *"))
+ py.test.raises(TypeError, ct)
+ py.test.raises(TypeError, ct, ffi.NULL)
+ for cd in [type(ffi.cast("void *", 0)),
+ type(ffi.new("char[]", 3)),
+ type(ffi.gc(ffi.NULL, lambda x: None))]:
+ py.test.raises(TypeError, cd)
+ py.test.raises(TypeError, cd, ffi.NULL)
+ py.test.raises(TypeError, cd, ffi.typeof("void *"))
+
+ def test_explicitly_defined_char16_t(self):
+ ffi = FFI()
+ ffi.cdef("typedef uint16_t char16_t;")
+ x = ffi.cast("char16_t", 1234)
+ assert ffi.typeof(x) is ffi.typeof("uint16_t")
+
+ def test_char16_t(self):
+ ffi = FFI()
+ 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):
+ ffi = FFI()
+ 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']
+ py_uni = u+'\U00012345'
+ z = ffi.new("char32_t[]", py_uni)
+ assert len(z) == 2
+ assert list(z) == [py_uni, u+'\x00'] # maybe a 2-unichars string
+ assert ffi.string(z) == py_uni
+ if len(py_uni) == 1: # 4-bytes unicodes in Python
+ s = ffi.new("char32_t[]", u+'\ud808\udf00')
+ assert len(s) == 3
+ assert list(s) == [u+'\ud808', u+'\udf00', u+'\x00']
diff --git a/testing/cffi0/test_function.py b/testing/cffi0/test_function.py
new file mode 100644
index 0000000..ca2353f
--- /dev/null
+++ b/testing/cffi0/test_function.py
@@ -0,0 +1,520 @@
+import py
+from cffi import FFI, CDefError
+import math, os, sys
+import ctypes.util
+from cffi.backend_ctypes import CTypesBackend
+from testing.udir import udir
+from testing.support import FdWriteCapture
+from .backend_tests import needs_dlopen_none
+
+try:
+ from StringIO import StringIO
+except ImportError:
+ from io import StringIO
+
+
+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'
+
+class TestFunction(object):
+ Backend = CTypesBackend
+
+ def test_sin(self):
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("""
+ double sin(double x);
+ """)
+ m = ffi.dlopen(lib_m)
+ x = m.sin(1.23)
+ assert x == math.sin(1.23)
+
+ def test_sinf(self):
+ if sys.platform == 'win32':
+ py.test.skip("no sinf found in the Windows stdlib")
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("""
+ float sinf(float x);
+ """)
+ m = ffi.dlopen(lib_m)
+ x = m.sinf(1.23)
+ assert type(x) is float
+ assert x != math.sin(1.23) # rounding effects
+ assert abs(x - math.sin(1.23)) < 1E-6
+
+ def test_getenv_no_return_value(self):
+ # check that 'void'-returning functions work too
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("""
+ void getenv(char *);
+ """)
+ needs_dlopen_none()
+ m = ffi.dlopen(None)
+ x = m.getenv(b"FOO")
+ assert x is None
+
+ def test_dlopen_filename(self):
+ path = ctypes.util.find_library(lib_m)
+ if not path:
+ py.test.skip("%s not found" % lib_m)
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("""
+ double cos(double x);
+ """)
+ m = ffi.dlopen(path)
+ x = m.cos(1.23)
+ assert x == math.cos(1.23)
+
+ m = ffi.dlopen(os.path.basename(path))
+ x = m.cos(1.23)
+ assert x == math.cos(1.23)
+
+ def test_dlopen_flags(self):
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("""
+ double cos(double x);
+ """)
+ m = ffi.dlopen(lib_m, ffi.RTLD_LAZY | ffi.RTLD_LOCAL)
+ x = m.cos(1.23)
+ assert x == math.cos(1.23)
+
+ def test_dlopen_constant(self):
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("""
+ #define FOOBAR 42
+ static const float baz = 42.5; /* not visible */
+ double sin(double x);
+ """)
+ m = ffi.dlopen(lib_m)
+ assert m.FOOBAR == 42
+ py.test.raises(NotImplementedError, "m.baz")
+
+ def test_tlsalloc(self):
+ if sys.platform != 'win32':
+ py.test.skip("win32 only")
+ if self.Backend is CTypesBackend:
+ py.test.skip("ctypes complains on wrong calling conv")
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("long TlsAlloc(void); int TlsFree(long);")
+ lib = ffi.dlopen('KERNEL32.DLL')
+ x = lib.TlsAlloc()
+ assert x != 0
+ y = lib.TlsFree(x)
+ assert y != 0
+
+ def test_fputs(self):
+ if not sys.platform.startswith('linux'):
+ py.test.skip("probably no symbol 'stderr' in the lib")
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("""
+ int fputs(const char *, void *);
+ void *stderr;
+ """)
+ needs_dlopen_none()
+ ffi.C = ffi.dlopen(None)
+ ffi.C.fputs # fetch before capturing, for easier debugging
+ with FdWriteCapture() as fd:
+ ffi.C.fputs(b"hello\n", ffi.C.stderr)
+ ffi.C.fputs(b" world\n", ffi.C.stderr)
+ res = fd.getvalue()
+ assert res == b'hello\n world\n'
+
+ def test_fputs_without_const(self):
+ if not sys.platform.startswith('linux'):
+ py.test.skip("probably no symbol 'stderr' in the lib")
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("""
+ int fputs(char *, void *);
+ void *stderr;
+ """)
+ needs_dlopen_none()
+ ffi.C = ffi.dlopen(None)
+ ffi.C.fputs # fetch before capturing, for easier debugging
+ with FdWriteCapture() as fd:
+ ffi.C.fputs(b"hello\n", ffi.C.stderr)
+ ffi.C.fputs(b" world\n", ffi.C.stderr)
+ res = fd.getvalue()
+ assert res == b'hello\n world\n'
+
+ def test_vararg(self):
+ if not sys.platform.startswith('linux'):
+ py.test.skip("probably no symbol 'stderr' in the lib")
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("""
+ int fprintf(void *, const char *format, ...);
+ void *stderr;
+ """)
+ needs_dlopen_none()
+ ffi.C = ffi.dlopen(None)
+ with FdWriteCapture() as fd:
+ ffi.C.fprintf(ffi.C.stderr, b"hello with no arguments\n")
+ ffi.C.fprintf(ffi.C.stderr,
+ b"hello, %s!\n", ffi.new("char[]", b"world"))
+ ffi.C.fprintf(ffi.C.stderr,
+ ffi.new("char[]", b"hello, %s!\n"),
+ ffi.new("char[]", b"world2"))
+ ffi.C.fprintf(ffi.C.stderr,
+ b"hello int %d long %ld long long %lld\n",
+ ffi.cast("int", 42),
+ ffi.cast("long", 84),
+ ffi.cast("long long", 168))
+ ffi.C.fprintf(ffi.C.stderr, b"hello %p\n", ffi.NULL)
+ res = fd.getvalue()
+ assert res == (b"hello with no arguments\n"
+ b"hello, world!\n"
+ b"hello, world2!\n"
+ b"hello int 42 long 84 long long 168\n"
+ b"hello (nil)\n")
+
+ def test_must_specify_type_of_vararg(self):
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("""
+ int printf(const char *format, ...);
+ """)
+ needs_dlopen_none()
+ ffi.C = ffi.dlopen(None)
+ e = py.test.raises(TypeError, ffi.C.printf, b"hello %d\n", 42)
+ assert str(e.value) == ("argument 2 passed in the variadic part "
+ "needs to be a cdata object (got int)")
+
+ def test_function_has_a_c_type(self):
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("""
+ int puts(const char *);
+ """)
+ needs_dlopen_none()
+ ffi.C = ffi.dlopen(None)
+ fptr = ffi.C.puts
+ assert ffi.typeof(fptr) == ffi.typeof("int(*)(const char*)")
+ if self.Backend is CTypesBackend:
+ assert repr(fptr).startswith("<cdata 'int puts(char *)' 0x")
+
+ def test_function_pointer(self):
+ ffi = FFI(backend=self.Backend())
+ def cb(charp):
+ assert repr(charp).startswith("<cdata 'char *' 0x")
+ return 42
+ fptr = ffi.callback("int(*)(const char *txt)", cb)
+ assert fptr != ffi.callback("int(*)(const char *)", cb)
+ assert repr(fptr) == "<cdata 'int(*)(char *)' calling %r>" % (cb,)
+ res = fptr(b"Hello")
+ assert res == 42
+ #
+ if not sys.platform.startswith('linux'):
+ py.test.skip("probably no symbol 'stderr' in the lib")
+ ffi.cdef("""
+ int fputs(const char *, void *);
+ void *stderr;
+ """)
+ needs_dlopen_none()
+ ffi.C = ffi.dlopen(None)
+ fptr = ffi.cast("int(*)(const char *txt, void *)", ffi.C.fputs)
+ assert fptr == ffi.C.fputs
+ assert repr(fptr).startswith("<cdata 'int(*)(char *, void *)' 0x")
+ with FdWriteCapture() as fd:
+ fptr(b"world\n", ffi.C.stderr)
+ res = fd.getvalue()
+ assert res == b'world\n'
+
+ def test_callback_returning_void(self):
+ ffi = FFI(backend=self.Backend())
+ for returnvalue in [None, 42]:
+ def cb():
+ return returnvalue
+ fptr = ffi.callback("void(*)(void)", cb)
+ old_stderr = sys.stderr
+ try:
+ sys.stderr = StringIO()
+ returned = fptr()
+ printed = sys.stderr.getvalue()
+ finally:
+ sys.stderr = old_stderr
+ assert returned is None
+ if returnvalue is None:
+ assert printed == ''
+ else:
+ assert "None" in printed
+
+ def test_passing_array(self):
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("""
+ int strlen(char[]);
+ """)
+ needs_dlopen_none()
+ ffi.C = ffi.dlopen(None)
+ p = ffi.new("char[]", b"hello")
+ res = ffi.C.strlen(p)
+ assert res == 5
+
+ def test_write_variable(self):
+ if not sys.platform.startswith('linux'):
+ py.test.skip("probably no symbol 'stdout' in the lib")
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("""
+ void *stdout;
+ """)
+ needs_dlopen_none()
+ C = ffi.dlopen(None)
+ pout = C.stdout
+ C.stdout = ffi.NULL
+ assert C.stdout == ffi.NULL
+ C.stdout = pout
+ assert C.stdout == pout
+
+ def test_strchr(self):
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("""
+ char *strchr(const char *s, int c);
+ """)
+ needs_dlopen_none()
+ ffi.C = ffi.dlopen(None)
+ p = ffi.new("char[]", b"hello world!")
+ q = ffi.C.strchr(p, ord('w'))
+ assert ffi.string(q) == b"world!"
+
+ def test_function_with_struct_argument(self):
+ if sys.platform == 'win32':
+ py.test.skip("no 'inet_ntoa'")
+ if (self.Backend is CTypesBackend and
+ '__pypy__' in sys.builtin_module_names):
+ py.test.skip("ctypes limitation on pypy")
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("""
+ struct in_addr { unsigned int s_addr; };
+ char *inet_ntoa(struct in_addr in);
+ """)
+ needs_dlopen_none()
+ ffi.C = ffi.dlopen(None)
+ ina = ffi.new("struct in_addr *", [0x04040404])
+ a = ffi.C.inet_ntoa(ina[0])
+ assert ffi.string(a) == b'4.4.4.4'
+
+ def test_function_typedef(self):
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("""
+ typedef double func_t(double);
+ func_t sin;
+ """)
+ m = ffi.dlopen(lib_m)
+ x = m.sin(1.23)
+ assert x == math.sin(1.23)
+
+ def test_fputs_custom_FILE(self):
+ if self.Backend is CTypesBackend:
+ py.test.skip("FILE not supported with the ctypes backend")
+ filename = str(udir.join('fputs_custom_FILE'))
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("int fputs(const char *, FILE *);")
+ needs_dlopen_none()
+ C = ffi.dlopen(None)
+ with open(filename, 'wb') as f:
+ f.write(b'[')
+ C.fputs(b"hello from custom file", f)
+ f.write(b'][')
+ C.fputs(b"some more output", f)
+ f.write(b']')
+ with open(filename, 'rb') as f:
+ res = f.read()
+ assert res == b'[hello from custom file][some more output]'
+
+ def test_constants_on_lib(self):
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("""enum foo_e { AA, BB, CC=5, DD };
+ typedef enum { EE=-5, FF } some_enum_t;""")
+ needs_dlopen_none()
+ lib = ffi.dlopen(None)
+ assert lib.AA == 0
+ assert lib.BB == 1
+ assert lib.CC == 5
+ assert lib.DD == 6
+ assert lib.EE == -5
+ assert lib.FF == -4
+
+ def test_void_star_accepts_string(self):
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("""int strlen(const void *);""")
+ needs_dlopen_none()
+ lib = ffi.dlopen(None)
+ res = lib.strlen(b"hello")
+ assert res == 5
+
+ def test_signed_char_star_accepts_string(self):
+ if self.Backend is CTypesBackend:
+ py.test.skip("not supported by the ctypes backend")
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("""int strlen(signed char *);""")
+ needs_dlopen_none()
+ lib = ffi.dlopen(None)
+ res = lib.strlen(b"hello")
+ assert res == 5
+
+ def test_unsigned_char_star_accepts_string(self):
+ if self.Backend is CTypesBackend:
+ py.test.skip("not supported by the ctypes backend")
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("""int strlen(unsigned char *);""")
+ needs_dlopen_none()
+ lib = ffi.dlopen(None)
+ res = lib.strlen(b"hello")
+ assert res == 5
+
+ def test_missing_function(self):
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("""
+ int nonexistent();
+ """)
+ m = ffi.dlopen(lib_m)
+ assert not hasattr(m, 'nonexistent')
+
+ def test_wraps_from_stdlib(self):
+ import functools
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("""
+ double sin(double x);
+ """)
+ def my_decorator(f):
+ @functools.wraps(f)
+ def wrapper(*args):
+ return f(*args) + 100
+ return wrapper
+ m = ffi.dlopen(lib_m)
+ sin100 = my_decorator(m.sin)
+ x = sin100(1.23)
+ assert x == math.sin(1.23) + 100
+
+ def test_free_callback_cycle(self):
+ if self.Backend is CTypesBackend:
+ py.test.skip("seems to fail with the ctypes backend on windows")
+ import weakref
+ def make_callback(data):
+ container = [data]
+ callback = ffi.callback('int()', lambda: len(container))
+ container.append(callback)
+ # Ref cycle: callback -> lambda (closure) -> container -> callback
+ return callback
+
+ class Data(object):
+ pass
+ ffi = FFI(backend=self.Backend())
+ data = Data()
+ callback = make_callback(data)
+ wr = weakref.ref(data)
+ del callback, data
+ for i in range(3):
+ if wr() is not None:
+ import gc; gc.collect()
+ assert wr() is None # 'data' does not leak
+
+ def test_windows_stdcall(self):
+ if sys.platform != 'win32':
+ py.test.skip("Windows-only test")
+ if self.Backend is CTypesBackend:
+ py.test.skip("not with the ctypes backend")
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("""
+ BOOL QueryPerformanceFrequency(LONGLONG *lpFrequency);
+ """)
+ m = ffi.dlopen("Kernel32.dll")
+ p_freq = ffi.new("LONGLONG *")
+ res = m.QueryPerformanceFrequency(p_freq)
+ assert res != 0
+ assert p_freq[0] != 0
+
+ def test_explicit_cdecl_stdcall(self):
+ if sys.platform != 'win32':
+ py.test.skip("Windows-only test")
+ if self.Backend is CTypesBackend:
+ py.test.skip("not with the ctypes backend")
+ win64 = (sys.maxsize > 2**32)
+ #
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("""
+ BOOL QueryPerformanceFrequency(LONGLONG *lpFrequency);
+ """)
+ m = ffi.dlopen("Kernel32.dll")
+ tp = ffi.typeof(m.QueryPerformanceFrequency)
+ assert str(tp) == "<ctype 'int(*)(long long *)'>"
+ #
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("""
+ BOOL __cdecl QueryPerformanceFrequency(LONGLONG *lpFrequency);
+ """)
+ m = ffi.dlopen("Kernel32.dll")
+ tpc = ffi.typeof(m.QueryPerformanceFrequency)
+ assert tpc is tp
+ #
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("""
+ BOOL WINAPI QueryPerformanceFrequency(LONGLONG *lpFrequency);
+ """)
+ m = ffi.dlopen("Kernel32.dll")
+ tps = ffi.typeof(m.QueryPerformanceFrequency)
+ if win64:
+ assert tps is tpc
+ else:
+ assert tps is not tpc
+ assert str(tps) == "<ctype 'int(__stdcall *)(long long *)'>"
+ #
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("typedef int (__cdecl *fnc_t)(int);")
+ ffi.cdef("typedef int (__stdcall *fns_t)(int);")
+ tpc = ffi.typeof("fnc_t")
+ tps = ffi.typeof("fns_t")
+ assert str(tpc) == "<ctype 'int(*)(int)'>"
+ if win64:
+ assert tps is tpc
+ else:
+ assert str(tps) == "<ctype 'int(__stdcall *)(int)'>"
+ #
+ fnc = ffi.cast("fnc_t", 0)
+ fns = ffi.cast("fns_t", 0)
+ ffi.new("fnc_t[]", [fnc])
+ if not win64:
+ py.test.raises(TypeError, ffi.new, "fnc_t[]", [fns])
+ py.test.raises(TypeError, ffi.new, "fns_t[]", [fnc])
+ ffi.new("fns_t[]", [fns])
+
+ def test_stdcall_only_on_windows(self):
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("double __stdcall sin(double x);") # stdcall ignored
+ m = ffi.dlopen(lib_m)
+ if (sys.platform == 'win32' and sys.maxsize < 2**32 and
+ self.Backend is not CTypesBackend):
+ assert "double(__stdcall *)(double)" in str(ffi.typeof(m.sin))
+ else:
+ assert "double(*)(double)" in str(ffi.typeof(m.sin))
+ x = m.sin(1.23)
+ assert x == math.sin(1.23)
+
+ def test_dir_on_dlopen_lib(self):
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("""
+ typedef enum { MYE1, MYE2 } myenum_t;
+ double myfunc(double);
+ double myvar;
+ const double myconst;
+ #define MYFOO 42
+ """)
+ m = ffi.dlopen(lib_m)
+ assert dir(m) == ['MYE1', 'MYE2', 'MYFOO', 'myconst', 'myfunc', 'myvar']
+
+ def test_dlclose(self):
+ if self.Backend is CTypesBackend:
+ py.test.skip("not with the ctypes backend")
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("int foobar(void); int foobaz;")
+ lib = ffi.dlopen(lib_m)
+ ffi.dlclose(lib)
+ e = py.test.raises(ValueError, getattr, lib, 'foobar')
+ assert str(e.value).startswith("library '")
+ assert str(e.value).endswith("' has already been closed")
+ e = py.test.raises(ValueError, getattr, lib, 'foobaz')
+ assert str(e.value).startswith("library '")
+ assert str(e.value).endswith("' has already been closed")
+ e = py.test.raises(ValueError, setattr, lib, 'foobaz', 42)
+ assert str(e.value).startswith("library '")
+ assert str(e.value).endswith("' has already been closed")
+ ffi.dlclose(lib) # does not raise
diff --git a/testing/cffi0/test_model.py b/testing/cffi0/test_model.py
new file mode 100644
index 0000000..bb653ca
--- /dev/null
+++ b/testing/cffi0/test_model.py
@@ -0,0 +1,111 @@
+from cffi.model import *
+
+
+def test_void_type():
+ assert void_type.get_c_name() == "void"
+ assert void_type.get_c_name("foo") == "void foo"
+ assert void_type.get_c_name("*foo") == "void *foo"
+
+def test_primitive_type():
+ int_type = PrimitiveType("int")
+ assert int_type.get_c_name() == "int"
+ assert int_type.get_c_name("foo") == "int foo"
+ assert int_type.get_c_name("*foo") == "int *foo"
+ assert int_type.get_c_name("[5]") == "int[5]"
+
+def test_raw_function_type():
+ int_type = PrimitiveType("int")
+ fn_type = RawFunctionType([], int_type, False)
+ assert fn_type.get_c_name() == "int()(void)"
+ assert fn_type.get_c_name("*") == "int( *)(void)"
+ assert fn_type.get_c_name("*foo") == "int( *foo)(void)"
+ fn_type = RawFunctionType([int_type], int_type, False)
+ assert fn_type.get_c_name() == "int()(int)"
+ fn_type = RawFunctionType([int_type] * 2, int_type, False)
+ assert fn_type.get_c_name() == "int()(int, int)"
+ #
+ fn_type = RawFunctionType([int_type], int_type, True)
+ assert fn_type.get_c_name() == "int()(int, ...)"
+ assert fn_type.get_c_name("*foo") == "int( *foo)(int, ...)"
+ #
+ res_type = FunctionPtrType([int_type], int_type, True)
+ fn_type = RawFunctionType([int_type], res_type, True)
+ assert fn_type.get_c_name("x") == "int(*( x)(int, ...))(int, ...)"
+
+def test_function_ptr_type():
+ int_type = PrimitiveType("int")
+ fn_type = FunctionPtrType([], int_type, False)
+ assert fn_type.get_c_name() == "int(*)(void)"
+ assert fn_type.get_c_name("*") == "int(* *)(void)"
+ assert fn_type.get_c_name("*foo") == "int(* *foo)(void)"
+ fn_type = FunctionPtrType([int_type], int_type, False)
+ assert fn_type.get_c_name() == "int(*)(int)"
+ fn_type = FunctionPtrType([int_type] * 2, int_type, False)
+ assert fn_type.get_c_name() == "int(*)(int, int)"
+ #
+ fn_type = FunctionPtrType([int_type], int_type, True)
+ assert fn_type.get_c_name() == "int(*)(int, ...)"
+
+def test_pointer_type():
+ ptr_type = PointerType(PrimitiveType("int"))
+ assert ptr_type.get_c_name("x") == "int * x"
+
+def test_const_pointer_type():
+ ptr_type = ConstPointerType(PrimitiveType("int"))
+ assert ptr_type.get_c_name("x") == "int const * x"
+ ptr_type = ConstPointerType(ArrayType(PrimitiveType("int"), 5))
+ assert ptr_type.get_c_name("") == "int(const *)[5]"
+ assert ptr_type.get_c_name("*x") == "int(const * *x)[5]"
+
+def test_qual_pointer_type():
+ ptr_type = PointerType(PrimitiveType("long long"), Q_RESTRICT)
+ assert ptr_type.get_c_name("") == "long long __restrict *"
+ assert const_voidp_type.get_c_name("") == "void const *"
+
+def test_unknown_pointer_type():
+ ptr_type = unknown_ptr_type("foo_p")
+ assert ptr_type.get_c_name("") == "foo_p"
+ assert ptr_type.get_c_name("x") == "foo_p x"
+
+def test_unknown_type():
+ u_type = unknown_type("foo_t")
+ assert u_type.get_c_name("") == "foo_t"
+ assert u_type.get_c_name("x") == "foo_t x"
+
+def test_array_type():
+ a_type = ArrayType(PrimitiveType("int"), None)
+ assert a_type.get_c_name("") == "int[]"
+ assert a_type.get_c_name("x") == "int x[]"
+ assert a_type.get_c_name("*x") == "int(*x)[]"
+ assert a_type.get_c_name(" *x") == "int(*x)[]"
+ assert a_type.get_c_name("[5]") == "int[5][]"
+ a_type = ArrayType(unknown_type("foo_t"), 5)
+ assert a_type.get_c_name("") == "foo_t[5]"
+ assert a_type.get_c_name("x") == "foo_t x[5]"
+ assert a_type.get_c_name("*x") == "foo_t(*x)[5]"
+ a_type = ArrayType(unknown_ptr_type("foo_p"), None)
+ assert a_type.get_c_name("") == "foo_p[]"
+ assert a_type.get_c_name("x") == "foo_p x[]"
+ assert a_type.get_c_name("*x") == "foo_p(*x)[]"
+ a_type = ArrayType(ConstPointerType(PrimitiveType("int")), None)
+ assert a_type.get_c_name("") == "int const *[]"
+ assert a_type.get_c_name("x") == "int const * x[]"
+ assert a_type.get_c_name("*x") == "int const *(*x)[]"
+ fn_type = FunctionPtrType([], PrimitiveType("int"), False)
+ a_type = ArrayType(fn_type, 5)
+ assert a_type.get_c_name("") == "int(*[5])(void)"
+ assert a_type.get_c_name("x") == "int(* x[5])(void)"
+ assert a_type.get_c_name("*x") == "int(*(*x)[5])(void)"
+
+def test_struct_type():
+ struct_type = StructType("foo_s", None, None, None)
+ assert struct_type.get_c_name() == "struct foo_s"
+ assert struct_type.get_c_name("*x") == "struct foo_s *x"
+
+def test_union_type():
+ union_type = UnionType("foo_s", None, None, None)
+ assert union_type.get_c_name() == "union foo_s"
+
+def test_enum_type():
+ enum_type = EnumType("foo_e", [], [])
+ assert enum_type.get_c_name() == "enum foo_e"
diff --git a/testing/cffi0/test_ownlib.py b/testing/cffi0/test_ownlib.py
new file mode 100644
index 0000000..a06df20
--- /dev/null
+++ b/testing/cffi0/test_ownlib.py
@@ -0,0 +1,373 @@
+import py, sys, os
+import subprocess, weakref
+from cffi import FFI
+from cffi.backend_ctypes import CTypesBackend
+from testing.support import u
+
+
+SOURCE = """\
+#include <errno.h>
+
+#ifdef _WIN32
+#define EXPORT __declspec(dllexport)
+#else
+#define EXPORT
+#endif
+
+EXPORT int test_getting_errno(void) {
+ errno = 123;
+ return -1;
+}
+
+EXPORT int test_setting_errno(void) {
+ return errno;
+};
+
+typedef struct {
+ long x;
+ long y;
+} POINT;
+
+typedef struct {
+ long left;
+ long top;
+ long right;
+ long bottom;
+} RECT;
+
+
+EXPORT int PointInRect(RECT *prc, POINT pt)
+{
+ if (pt.x < prc->left)
+ return 0;
+ if (pt.x > prc->right)
+ return 0;
+ if (pt.y < prc->top)
+ return 0;
+ if (pt.y > prc->bottom)
+ return 0;
+ return 1;
+};
+
+EXPORT long left = 10;
+EXPORT long top = 20;
+EXPORT long right = 30;
+EXPORT long bottom = 40;
+
+EXPORT RECT ReturnRect(int i, RECT ar, RECT* br, POINT cp, RECT dr,
+ RECT *er, POINT fp, RECT gr)
+{
+ /*Check input */
+ if (ar.left + br->left + dr.left + er->left + gr.left != left * 5)
+ {
+ ar.left = 100;
+ return ar;
+ }
+ if (ar.right + br->right + dr.right + er->right + gr.right != right * 5)
+ {
+ ar.right = 100;
+ return ar;
+ }
+ if (cp.x != fp.x)
+ {
+ ar.left = -100;
+ }
+ if (cp.y != fp.y)
+ {
+ ar.left = -200;
+ }
+ switch(i)
+ {
+ case 0:
+ return ar;
+ break;
+ case 1:
+ return dr;
+ break;
+ case 2:
+ return gr;
+ break;
+
+ }
+ return ar;
+}
+
+EXPORT int my_array[7] = {0, 1, 2, 3, 4, 5, 6};
+
+EXPORT unsigned short foo_2bytes(unsigned short a)
+{
+ return (unsigned short)(a + 42);
+}
+EXPORT unsigned int foo_4bytes(unsigned int a)
+{
+ return (unsigned int)(a + 42);
+}
+
+EXPORT void modify_struct_value(RECT r)
+{
+ r.left = r.right = r.top = r.bottom = 500;
+}
+"""
+
+class TestOwnLib(object):
+ Backend = CTypesBackend
+
+ def setup_class(cls):
+ cls.module = None
+ from testing.udir import udir
+ udir.join('testownlib.c').write(SOURCE)
+ if sys.platform == 'win32':
+ # did we already build it?
+ if cls.Backend is CTypesBackend:
+ dll_path = str(udir) + '\\testownlib1.dll' # only ascii for the ctypes backend
+ else:
+ dll_path = str(udir) + '\\' + (u+'testownlib\u03be.dll') # non-ascii char
+ if os.path.exists(dll_path):
+ cls.module = dll_path
+ return
+ # try (not too hard) to find the version used to compile this python
+ # no mingw
+ from distutils.msvc9compiler import get_build_version
+ version = get_build_version()
+ toolskey = "VS%0.f0COMNTOOLS" % version
+ toolsdir = os.environ.get(toolskey, None)
+ if toolsdir is None:
+ return
+ productdir = os.path.join(toolsdir, os.pardir, os.pardir, "VC")
+ productdir = os.path.abspath(productdir)
+ vcvarsall = os.path.join(productdir, "vcvarsall.bat")
+ # 64?
+ arch = 'x86'
+ if sys.maxsize > 2**32:
+ arch = 'amd64'
+ if os.path.isfile(vcvarsall):
+ cmd = '"%s" %s' % (vcvarsall, arch) + ' & cl.exe testownlib.c ' \
+ ' /LD /Fetestownlib.dll'
+ subprocess.check_call(cmd, cwd = str(udir), shell=True)
+ os.rename(str(udir) + '\\testownlib.dll', dll_path)
+ cls.module = dll_path
+ else:
+ encoded = None
+ if cls.Backend is not CTypesBackend:
+ try:
+ unicode_name = u+'testownlibcaf\xe9'
+ encoded = unicode_name.encode(sys.getfilesystemencoding())
+ if sys.version_info >= (3,):
+ encoded = str(unicode_name)
+ except UnicodeEncodeError:
+ pass
+ if encoded is None:
+ unicode_name = u+'testownlib'
+ encoded = str(unicode_name)
+ subprocess.check_call(
+ "cc testownlib.c -shared -fPIC -o '%s.so'" % (encoded,),
+ cwd=str(udir), shell=True)
+ cls.module = os.path.join(str(udir), unicode_name + (u+'.so'))
+ print(repr(cls.module))
+
+ def test_getting_errno(self):
+ if self.module is None:
+ py.test.skip("fix the auto-generation of the tiny test lib")
+ if sys.platform == 'win32':
+ py.test.skip("fails, errno at multiple addresses")
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("""
+ int test_getting_errno(void);
+ """)
+ ownlib = ffi.dlopen(self.module)
+ res = ownlib.test_getting_errno()
+ assert res == -1
+ assert ffi.errno == 123
+
+ def test_setting_errno(self):
+ if self.module is None:
+ py.test.skip("fix the auto-generation of the tiny test lib")
+ if sys.platform == 'win32':
+ py.test.skip("fails, errno at multiple addresses")
+ if self.Backend is CTypesBackend and '__pypy__' in sys.modules:
+ py.test.skip("XXX errno issue with ctypes on pypy?")
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("""
+ int test_setting_errno(void);
+ """)
+ ownlib = ffi.dlopen(self.module)
+ ffi.errno = 42
+ res = ownlib.test_setting_errno()
+ assert res == 42
+ assert ffi.errno == 42
+
+ def test_my_array_7(self):
+ if self.module is None:
+ py.test.skip("fix the auto-generation of the tiny test lib")
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("""
+ int my_array[7];
+ """)
+ ownlib = ffi.dlopen(self.module)
+ for i in range(7):
+ assert ownlib.my_array[i] == i
+ assert len(ownlib.my_array) == 7
+ if self.Backend is CTypesBackend:
+ py.test.skip("not supported by the ctypes backend")
+ ownlib.my_array = list(range(10, 17))
+ for i in range(7):
+ assert ownlib.my_array[i] == 10 + i
+ ownlib.my_array = list(range(7))
+ for i in range(7):
+ assert ownlib.my_array[i] == i
+
+ def test_my_array_no_length(self):
+ if self.module is None:
+ py.test.skip("fix the auto-generation of the tiny test lib")
+ if self.Backend is CTypesBackend:
+ py.test.skip("not supported by the ctypes backend")
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("""
+ int my_array[];
+ """)
+ ownlib = ffi.dlopen(self.module)
+ for i in range(7):
+ assert ownlib.my_array[i] == i
+ py.test.raises(TypeError, len, ownlib.my_array)
+ ownlib.my_array = list(range(10, 17))
+ for i in range(7):
+ assert ownlib.my_array[i] == 10 + i
+ ownlib.my_array = list(range(7))
+ for i in range(7):
+ assert ownlib.my_array[i] == i
+
+ def test_keepalive_lib(self):
+ if self.module is None:
+ py.test.skip("fix the auto-generation of the tiny test lib")
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("""
+ int test_getting_errno(void);
+ """)
+ ownlib = ffi.dlopen(self.module)
+ ffi_r = weakref.ref(ffi)
+ ownlib_r = weakref.ref(ownlib)
+ func = ownlib.test_getting_errno
+ del ffi
+ import gc; gc.collect() # ownlib stays alive
+ assert ownlib_r() is not None
+ assert ffi_r() is not None # kept alive by ownlib
+ res = func()
+ assert res == -1
+
+ def test_keepalive_ffi(self):
+ if self.module is None:
+ py.test.skip("fix the auto-generation of the tiny test lib")
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("""
+ int test_getting_errno(void);
+ """)
+ ownlib = ffi.dlopen(self.module)
+ ffi_r = weakref.ref(ffi)
+ ownlib_r = weakref.ref(ownlib)
+ func = ownlib.test_getting_errno
+ del ownlib
+ import gc; gc.collect() # ffi stays alive
+ assert ffi_r() is not None
+ assert ownlib_r() is not None # kept alive by ffi
+ res = func()
+ assert res == -1
+ if sys.platform != 'win32': # else, errno at multiple addresses
+ assert ffi.errno == 123
+
+ def test_struct_by_value(self):
+ if self.module is None:
+ py.test.skip("fix the auto-generation of the tiny test lib")
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("""
+ typedef struct {
+ long x;
+ long y;
+ } POINT;
+
+ typedef struct {
+ long left;
+ long top;
+ long right;
+ long bottom;
+ } RECT;
+
+ long left, top, right, bottom;
+
+ RECT ReturnRect(int i, RECT ar, RECT* br, POINT cp, RECT dr,
+ RECT *er, POINT fp, RECT gr);
+ """)
+ ownlib = ffi.dlopen(self.module)
+
+ rect = ffi.new('RECT[1]')
+ pt = ffi.new('POINT[1]')
+ pt[0].x = 15
+ pt[0].y = 25
+ rect[0].left = ownlib.left
+ rect[0].right = ownlib.right
+ rect[0].top = ownlib.top
+ rect[0].bottom = ownlib.bottom
+
+ for i in range(4):
+ ret = ownlib.ReturnRect(i, rect[0], rect, pt[0], rect[0],
+ rect, pt[0], rect[0])
+ assert ret.left == ownlib.left
+ assert ret.right == ownlib.right
+ assert ret.top == ownlib.top
+ assert ret.bottom == ownlib.bottom
+
+ def test_addressof_lib(self):
+ if self.module is None:
+ py.test.skip("fix the auto-generation of the tiny test lib")
+ if self.Backend is CTypesBackend:
+ py.test.skip("not implemented with the ctypes backend")
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("long left; int test_getting_errno(void);")
+ lib = ffi.dlopen(self.module)
+ lib.left = 123456
+ p = ffi.addressof(lib, "left")
+ assert ffi.typeof(p) == ffi.typeof("long *")
+ assert p[0] == 123456
+ p[0] += 1
+ assert lib.left == 123457
+ pfn = ffi.addressof(lib, "test_getting_errno")
+ assert ffi.typeof(pfn) == ffi.typeof("int(*)(void)")
+ assert pfn == lib.test_getting_errno
+
+ def test_char16_char32_t(self):
+ if self.module is None:
+ py.test.skip("fix the auto-generation of the tiny test lib")
+ if self.Backend is CTypesBackend:
+ py.test.skip("not implemented with the ctypes backend")
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("""
+ char16_t foo_2bytes(char16_t);
+ char32_t foo_4bytes(char32_t);
+ """)
+ lib = ffi.dlopen(self.module)
+ assert lib.foo_2bytes(u+'\u1234') == u+'\u125e'
+ assert lib.foo_4bytes(u+'\u1234') == u+'\u125e'
+ assert lib.foo_4bytes(u+'\U00012345') == u+'\U0001236f'
+
+ def test_modify_struct_value(self):
+ if self.module is None:
+ py.test.skip("fix the auto-generation of the tiny test lib")
+ if self.Backend is CTypesBackend:
+ py.test.skip("fails with the ctypes backend on some architectures")
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("""
+ typedef struct {
+ long left;
+ long top;
+ long right;
+ long bottom;
+ } RECT;
+
+ void modify_struct_value(RECT r);
+ """)
+ lib = ffi.dlopen(self.module)
+ s = ffi.new("RECT *", [11, 22, 33, 44])
+ lib.modify_struct_value(s[0])
+ assert s.left == 11
+ assert s.top == 22
+ assert s.right == 33
+ assert s.bottom == 44
diff --git a/testing/cffi0/test_parsing.py b/testing/cffi0/test_parsing.py
new file mode 100644
index 0000000..2d75850
--- /dev/null
+++ b/testing/cffi0/test_parsing.py
@@ -0,0 +1,468 @@
+import py, sys, re
+from cffi import FFI, FFIError, CDefError, VerificationError
+from .backend_tests import needs_dlopen_none
+
+
+class FakeBackend(object):
+
+ def nonstandard_integer_types(self):
+ return {}
+
+ def sizeof(self, name):
+ return 1
+
+ def load_library(self, name, flags):
+ if sys.platform == 'win32':
+ assert name is None or "msvcr" in name
+ else:
+ assert name is None or "libc" in name or "libm" in name
+ return FakeLibrary()
+
+ def new_function_type(self, args, result, has_varargs):
+ args = [arg.cdecl for arg in args]
+ result = result.cdecl
+ return FakeType(
+ '<func (%s), %s, %s>' % (', '.join(args), result, has_varargs))
+
+ def new_primitive_type(self, name):
+ assert name == name.lower()
+ return FakeType('<%s>' % name)
+
+ def new_pointer_type(self, itemtype):
+ return FakeType('<pointer to %s>' % (itemtype,))
+
+ def new_struct_type(self, name):
+ return FakeStruct(name)
+
+ def complete_struct_or_union(self, s, fields, tp=None,
+ totalsize=-1, totalalignment=-1, sflags=0):
+ assert isinstance(s, FakeStruct)
+ s.fields = fields
+
+ def new_array_type(self, ptrtype, length):
+ return FakeType('<array %s x %s>' % (ptrtype, length))
+
+ def new_void_type(self):
+ return FakeType("<void>")
+ def cast(self, x, y):
+ return 'casted!'
+ def _get_types(self):
+ return "CData", "CType"
+
+ buffer = "buffer type"
+
+class FakeType(object):
+ def __init__(self, cdecl):
+ self.cdecl = cdecl
+ def __str__(self):
+ return self.cdecl
+
+class FakeStruct(object):
+ def __init__(self, name):
+ self.name = name
+ def __str__(self):
+ return ', '.join([str(y) + str(x) for x, y, z in self.fields])
+
+class FakeLibrary(object):
+
+ def load_function(self, BType, name):
+ return FakeFunction(BType, name)
+
+class FakeFunction(object):
+
+ def __init__(self, BType, name):
+ self.BType = str(BType)
+ self.name = name
+
+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'
+
+def test_simple():
+ ffi = FFI(backend=FakeBackend())
+ ffi.cdef("double sin(double x);")
+ m = ffi.dlopen(lib_m)
+ func = m.sin # should be a callable on real backends
+ assert func.name == 'sin'
+ assert func.BType == '<func (<double>), <double>, False>'
+
+def test_pipe():
+ ffi = FFI(backend=FakeBackend())
+ ffi.cdef("int pipe(int pipefd[2]);")
+ needs_dlopen_none()
+ C = ffi.dlopen(None)
+ func = C.pipe
+ assert func.name == 'pipe'
+ assert func.BType == '<func (<pointer to <int>>), <int>, False>'
+
+def test_vararg():
+ ffi = FFI(backend=FakeBackend())
+ ffi.cdef("short foo(int, ...);")
+ needs_dlopen_none()
+ C = ffi.dlopen(None)
+ func = C.foo
+ assert func.name == 'foo'
+ assert func.BType == '<func (<int>), <short>, True>'
+
+def test_no_args():
+ ffi = FFI(backend=FakeBackend())
+ ffi.cdef("""
+ int foo(void);
+ """)
+ needs_dlopen_none()
+ C = ffi.dlopen(None)
+ assert C.foo.BType == '<func (), <int>, False>'
+
+def test_typedef():
+ ffi = FFI(backend=FakeBackend())
+ ffi.cdef("""
+ typedef unsigned int UInt;
+ typedef UInt UIntReally;
+ UInt foo(void);
+ """)
+ needs_dlopen_none()
+ C = ffi.dlopen(None)
+ assert str(ffi.typeof("UIntReally")) == '<unsigned int>'
+ assert C.foo.BType == '<func (), <unsigned int>, False>'
+
+def test_typedef_more_complex():
+ ffi = FFI(backend=FakeBackend())
+ ffi.cdef("""
+ typedef struct { int a, b; } foo_t, *foo_p;
+ int foo(foo_p[]);
+ """)
+ needs_dlopen_none()
+ C = ffi.dlopen(None)
+ assert str(ffi.typeof("foo_t")) == '<int>a, <int>b'
+ assert str(ffi.typeof("foo_p")) == '<pointer to <int>a, <int>b>'
+ assert C.foo.BType == ('<func (<pointer to <pointer to '
+ '<int>a, <int>b>>), <int>, False>')
+
+def test_typedef_array_convert_array_to_pointer():
+ ffi = FFI(backend=FakeBackend())
+ ffi.cdef("""
+ typedef int (*fn_t)(int[5]);
+ """)
+ with ffi._lock:
+ type = ffi._parser.parse_type("fn_t")
+ BType = ffi._get_cached_btype(type)
+ assert str(BType) == '<func (<pointer to <int>>), <int>, False>'
+
+def test_remove_comments():
+ ffi = FFI(backend=FakeBackend())
+ ffi.cdef("""
+ double /*comment here*/ sin // blah blah
+ /* multi-
+ line-
+ //comment */ (
+ // foo
+ double // bar /* <- ignored, because it's in a comment itself
+ x, double/*several*//*comment*/y) /*on the same line*/
+ ;
+ """)
+ m = ffi.dlopen(lib_m)
+ func = m.sin
+ assert func.name == 'sin'
+ assert func.BType == '<func (<double>, <double>), <double>, False>'
+
+def test_remove_line_continuation_comments():
+ ffi = FFI(backend=FakeBackend())
+ ffi.cdef("""
+ double // blah \\
+ more comments
+ x(void);
+ double // blah\\\\
+ y(void);
+ double // blah\\ \
+ etc
+ z(void);
+ """)
+ m = ffi.dlopen(lib_m)
+ m.x
+ m.y
+ m.z
+
+def test_line_continuation_in_defines():
+ ffi = FFI(backend=FakeBackend())
+ ffi.cdef("""
+ #define ABC\\
+ 42
+ #define BCD \\
+ 43
+ """)
+ m = ffi.dlopen(lib_m)
+ assert m.ABC == 42
+ assert m.BCD == 43
+
+def test_define_not_supported_for_now():
+ ffi = FFI(backend=FakeBackend())
+ e = py.test.raises(CDefError, ffi.cdef, '#define FOO "blah"')
+ assert str(e.value) == (
+ 'only supports one of the following syntax:\n'
+ ' #define FOO ... (literally dot-dot-dot)\n'
+ ' #define FOO NUMBER (with NUMBER an integer'
+ ' constant, decimal/hex/octal)\n'
+ 'got:\n'
+ ' #define FOO "blah"')
+
+def test_unnamed_struct():
+ ffi = FFI(backend=FakeBackend())
+ ffi.cdef("typedef struct { int x; } foo_t;\n"
+ "typedef struct { int y; } *bar_p;\n")
+ assert 'typedef foo_t' in ffi._parser._declarations
+ assert 'typedef bar_p' in ffi._parser._declarations
+ assert 'anonymous foo_t' in ffi._parser._declarations
+ type_foo = ffi._parser.parse_type("foo_t")
+ type_bar = ffi._parser.parse_type("bar_p").totype
+ assert repr(type_foo) == "<foo_t>"
+ assert repr(type_bar) == "<struct $1>"
+ py.test.raises(VerificationError, type_bar.get_c_name)
+ assert type_foo.get_c_name() == "foo_t"
+
+def test_override():
+ ffi = FFI(backend=FakeBackend())
+ needs_dlopen_none()
+ C = ffi.dlopen(None)
+ ffi.cdef("int foo(void);")
+ py.test.raises(FFIError, ffi.cdef, "long foo(void);")
+ assert C.foo.BType == '<func (), <int>, False>'
+ ffi.cdef("long foo(void);", override=True)
+ assert C.foo.BType == '<func (), <long>, False>'
+
+def test_cannot_have_only_variadic_part():
+ # this checks that we get a sensible error if we try "int foo(...);"
+ ffi = FFI()
+ e = py.test.raises(CDefError, ffi.cdef, "int foo(...);")
+ assert str(e.value) == (
+ "<cdef source string>:1: foo: a function with only '(...)' "
+ "as argument is not correct C")
+
+def test_parse_error():
+ ffi = FFI()
+ e = py.test.raises(CDefError, ffi.cdef, " x y z ")
+ assert str(e.value).startswith(
+ 'cannot parse "x y z"\n<cdef source string>:1:')
+ e = py.test.raises(CDefError, ffi.cdef, "\n\n\n x y z ")
+ assert str(e.value).startswith(
+ 'cannot parse "x y z"\n<cdef source string>:4:')
+
+def test_error_custom_lineno():
+ ffi = FFI()
+ e = py.test.raises(CDefError, ffi.cdef, """
+# 42 "foobar"
+
+ a b c d
+ """)
+ assert str(e.value).startswith('parse error\nfoobar:43:')
+
+def test_cannot_declare_enum_later():
+ ffi = FFI()
+ e = py.test.raises(NotImplementedError, ffi.cdef,
+ "typedef enum foo_e foo_t; enum foo_e { AA, BB };")
+ assert str(e.value) == (
+ "enum foo_e: the '{}' declaration should appear on the "
+ "first time the enum is mentioned, not later")
+
+def test_unknown_name():
+ ffi = FFI()
+ e = py.test.raises(CDefError, ffi.cast, "foobarbazunknown", 0)
+ assert str(e.value) == "unknown identifier 'foobarbazunknown'"
+ e = py.test.raises(CDefError, ffi.cast, "foobarbazunknown*", 0)
+ assert str(e.value).startswith('cannot parse "foobarbazunknown*"')
+ e = py.test.raises(CDefError, ffi.cast, "int(*)(foobarbazunknown)", 0)
+ assert str(e.value).startswith('cannot parse "int(*)(foobarbazunknown)"')
+
+def test_redefine_common_type():
+ prefix = "" if sys.version_info < (3,) else "b"
+ ffi = FFI()
+ ffi.cdef("typedef char FILE;")
+ assert repr(ffi.cast("FILE", 123)) == "<cdata 'char' %s'{'>" % prefix
+ ffi.cdef("typedef char int32_t;")
+ assert repr(ffi.cast("int32_t", 123)) == "<cdata 'char' %s'{'>" % prefix
+ ffi = FFI()
+ ffi.cdef("typedef int bool, *FILE;")
+ assert repr(ffi.cast("bool", 123)) == "<cdata 'int' 123>"
+ assert re.match(r"<cdata 'int [*]' 0[xX]?0*7[bB]>",
+ repr(ffi.cast("FILE", 123)))
+ ffi = FFI()
+ ffi.cdef("typedef bool (*fn_t)(bool, bool);") # "bool," but within "( )"
+
+def test_bool():
+ ffi = FFI()
+ ffi.cdef("void f(bool);")
+ #
+ ffi = FFI()
+ ffi.cdef("typedef _Bool bool; void f(bool);")
+
+def test_unknown_argument_type():
+ ffi = FFI()
+ e = py.test.raises(CDefError, ffi.cdef, "void f(foobarbazzz);")
+ assert str(e.value) == ("<cdef source string>:1: f arg 1:"
+ " unknown type 'foobarbazzz' (if you meant"
+ " to use the old C syntax of giving untyped"
+ " arguments, it is not supported)")
+
+def test_void_renamed_as_only_arg():
+ ffi = FFI()
+ ffi.cdef("typedef void void_t1;"
+ "typedef void_t1 void_t;"
+ "typedef int (*func_t)(void_t);")
+ assert ffi.typeof("func_t").args == ()
+
+def test_WPARAM_on_windows():
+ if sys.platform != 'win32':
+ py.test.skip("Only for Windows")
+ ffi = FFI()
+ ffi.cdef("void f(WPARAM);")
+ #
+ # WPARAM -> UINT_PTR -> unsigned 32/64-bit integer
+ ffi = FFI()
+ value = int(ffi.cast("WPARAM", -42))
+ assert value == sys.maxsize * 2 - 40
+
+def test__is_constant_globalvar():
+ for input, expected_output in [
+ ("int a;", False),
+ ("const int a;", True),
+ ("int *a;", False),
+ ("const int *a;", False),
+ ("int const *a;", False),
+ ("int *const a;", True),
+ ("int a[5];", False),
+ ("const int a[5];", False),
+ ("int *a[5];", False),
+ ("const int *a[5];", False),
+ ("int const *a[5];", False),
+ ("int *const a[5];", False),
+ ("int a[5][6];", False),
+ ("const int a[5][6];", False),
+ ]:
+ ffi = FFI()
+ ffi.cdef(input)
+ declarations = ffi._parser._declarations
+ assert ('constant a' in declarations) == expected_output
+ assert ('variable a' in declarations) == (not expected_output)
+
+def test_restrict():
+ from cffi import model
+ for input, expected_output in [
+ ("int a;", False),
+ ("restrict int a;", True),
+ ("int *a;", False),
+ ]:
+ ffi = FFI()
+ ffi.cdef(input)
+ tp, quals = ffi._parser._declarations['variable a']
+ assert bool(quals & model.Q_RESTRICT) == expected_output
+
+def test_different_const_funcptr_types():
+ lst = []
+ for input in [
+ "int(*)(int *a)",
+ "int(*)(int const *a)",
+ "int(*)(int * const a)",
+ "int(*)(int const a[])"]:
+ ffi = FFI(backend=FakeBackend())
+ lst.append(ffi._parser.parse_type(input))
+ assert lst[0] != lst[1]
+ assert lst[0] == lst[2]
+ assert lst[1] == lst[3]
+
+def test_const_pointer_to_pointer():
+ from cffi import model
+ ffi = FFI(backend=FakeBackend())
+ #
+ tp, qual = ffi._parser.parse_type_and_quals("char * * (* const)")
+ assert (str(tp), qual) == ("<char * * *>", model.Q_CONST)
+ tp, qual = ffi._parser.parse_type_and_quals("char * (* const (*))")
+ assert (str(tp), qual) == ("<char * * const *>", 0)
+ tp, qual = ffi._parser.parse_type_and_quals("char (* const (* (*)))")
+ assert (str(tp), qual) == ("<char * const * *>", 0)
+ tp, qual = ffi._parser.parse_type_and_quals("char const * * *")
+ assert (str(tp), qual) == ("<char const * * *>", 0)
+ tp, qual = ffi._parser.parse_type_and_quals("const char * * *")
+ assert (str(tp), qual) == ("<char const * * *>", 0)
+ #
+ tp, qual = ffi._parser.parse_type_and_quals("char * * * const const")
+ assert (str(tp), qual) == ("<char * * *>", model.Q_CONST)
+ tp, qual = ffi._parser.parse_type_and_quals("char * * volatile *")
+ assert (str(tp), qual) == ("<char * * volatile *>", 0)
+ tp, qual = ffi._parser.parse_type_and_quals("char * volatile restrict * *")
+ assert (str(tp), qual) == ("<char * __restrict volatile * *>", 0)
+ tp, qual = ffi._parser.parse_type_and_quals("char const volatile * * *")
+ assert (str(tp), qual) == ("<char volatile const * * *>", 0)
+ tp, qual = ffi._parser.parse_type_and_quals("const char * * *")
+ assert (str(tp), qual) == ("<char const * * *>", 0)
+ #
+ tp, qual = ffi._parser.parse_type_and_quals(
+ "int(char*const*, short****const*)")
+ assert (str(tp), qual) == (
+ "<int()(char * const *, short * * * * const *)>", 0)
+ tp, qual = ffi._parser.parse_type_and_quals(
+ "char*const*(short*const****)")
+ assert (str(tp), qual) == (
+ "<char * const *()(short * const * * * *)>", 0)
+
+def test_enum():
+ ffi = FFI()
+ ffi.cdef("""
+ enum Enum { POS = +1, TWO = 2, NIL = 0, NEG = -1, OP = (POS+TWO)-1};
+ """)
+ needs_dlopen_none()
+ C = ffi.dlopen(None)
+ assert C.POS == 1
+ assert C.TWO == 2
+ assert C.NIL == 0
+ assert C.NEG == -1
+ assert C.OP == 2
+
+def test_stdcall():
+ ffi = FFI()
+ tp = ffi.typeof("int(*)(int __stdcall x(int),"
+ " long (__cdecl*y)(void),"
+ " short(WINAPI *z)(short))")
+ if sys.platform == 'win32' and sys.maxsize < 2**32:
+ stdcall = '__stdcall '
+ else:
+ stdcall = ''
+ assert str(tp) == (
+ "<ctype 'int(*)(int(%s*)(int), "
+ "long(*)(), "
+ "short(%s*)(short))'>" % (stdcall, stdcall))
+
+def test_extern_python():
+ ffi = FFI()
+ ffi.cdef("""
+ int bok(int, int);
+ extern "Python" int foobar(int, int);
+ int baz(int, int);
+ """)
+ assert sorted(ffi._parser._declarations) == [
+ 'extern_python foobar', 'function baz', 'function bok']
+ assert (ffi._parser._declarations['function bok'] ==
+ ffi._parser._declarations['extern_python foobar'] ==
+ ffi._parser._declarations['function baz'])
+
+def test_extern_python_group():
+ ffi = FFI()
+ ffi.cdef("""
+ int bok(int);
+ extern "Python" {int foobar(int, int);int bzrrr(int);}
+ int baz(int, int);
+ """)
+ assert sorted(ffi._parser._declarations) == [
+ 'extern_python bzrrr', 'extern_python foobar',
+ 'function baz', 'function bok']
+ assert (ffi._parser._declarations['function baz'] ==
+ ffi._parser._declarations['extern_python foobar'] !=
+ ffi._parser._declarations['function bok'] ==
+ ffi._parser._declarations['extern_python bzrrr'])
+
+def test_error_invalid_syntax_for_cdef():
+ ffi = FFI()
+ e = py.test.raises(CDefError, ffi.cdef, 'void foo(void) {}')
+ assert str(e.value) == ('<cdef source string>:1: unexpected <FuncDef>: '
+ 'this construct is valid C but not valid in cdef()')
diff --git a/testing/cffi0/test_platform.py b/testing/cffi0/test_platform.py
new file mode 100644
index 0000000..55446ec
--- /dev/null
+++ b/testing/cffi0/test_platform.py
@@ -0,0 +1,25 @@
+import os
+from cffi.ffiplatform import maybe_relative_path, flatten
+
+
+def test_not_absolute():
+ assert maybe_relative_path('foo/bar') == 'foo/bar'
+ assert maybe_relative_path('test_platform.py') == 'test_platform.py'
+
+def test_different_absolute():
+ p = os.path.join('..', 'baz.py')
+ assert maybe_relative_path(p) == p
+
+def test_absolute_mapping():
+ p = os.path.abspath('baz.py')
+ assert maybe_relative_path(p) == 'baz.py'
+ foobaz = os.path.join('foo', 'baz.py')
+ assert maybe_relative_path(os.path.abspath(foobaz)) == foobaz
+
+def test_flatten():
+ assert flatten("foo") == "3sfoo"
+ assert flatten(-10000000000000000000000000000) == \
+ "-10000000000000000000000000000i"
+ assert flatten([4, 5]) == "2l4i5i"
+ assert flatten({4: 5}) == "1d4i5i"
+ assert flatten({"foo": ("bar", "baaz")}) == "1d3sfoo2l3sbar4sbaaz"
diff --git a/testing/cffi0/test_unicode_literals.py b/testing/cffi0/test_unicode_literals.py
new file mode 100644
index 0000000..7b0a5cc
--- /dev/null
+++ b/testing/cffi0/test_unicode_literals.py
@@ -0,0 +1,79 @@
+#
+# ----------------------------------------------
+# WARNING, ALL LITERALS IN THIS FILE ARE UNICODE
+# ----------------------------------------------
+#
+from __future__ import unicode_literals
+#
+#
+#
+import sys, math
+from cffi import FFI
+
+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'
+
+
+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_cdef():
+ ffi = FFI()
+ ffi.cdef("typedef int foo_t[50];") # unicode literal
+
+def test_offsetof():
+ ffi = FFI()
+ ffi.cdef("typedef struct { int x, y; } foo_t;")
+ assert ffi.offsetof("foo_t", "y") == 4 # unicode literal
+
+def test_enum():
+ ffi = FFI()
+ ffi.cdef("enum foo_e { AA, BB, CC };") # unicode literal
+ x = ffi.cast("enum foo_e", 1)
+ assert int(ffi.cast("int", x)) == 1
+
+def test_dlopen():
+ ffi = FFI()
+ ffi.cdef("double sin(double x);")
+ m = ffi.dlopen(lib_m) # unicode literal
+ x = m.sin(1.23)
+ assert x == math.sin(1.23)
+
+def test_verify():
+ ffi = FFI()
+ ffi.cdef("double test_verify_1(double x);") # unicode literal
+ lib = ffi.verify("double test_verify_1(double x) { return x * 42.0; }")
+ assert lib.test_verify_1(-1.5) == -63.0
+
+def test_callback():
+ ffi = FFI()
+ cb = ffi.callback("int(int)", # unicode literal
+ lambda x: x + 42)
+ assert cb(5) == 47
diff --git a/testing/cffi0/test_verify.py b/testing/cffi0/test_verify.py
new file mode 100644
index 0000000..79e1c6c
--- /dev/null
+++ b/testing/cffi0/test_verify.py
@@ -0,0 +1,2524 @@
+import py, re
+import sys, os, math, weakref
+from cffi import FFI, VerificationError, VerificationMissing, model, FFIError
+from testing.support import *
+
+
+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']
+ pass # 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):
+ def verify(self, *args, **kwds):
+ return super(FFI, self).verify(
+ *args, extra_compile_args=extra_compile_args, **kwds)
+
+def setup_module():
+ import cffi.verifier
+ cffi.verifier.cleanup_tmpdir()
+ #
+ # check that no $ sign is produced in the C file; it used to be the
+ # case that anonymous enums would produce '$enum_$1', which was
+ # used as part of a function name. GCC accepts such names, but it's
+ # apparently non-standard.
+ _r_comment = re.compile(r"/\*.*?\*/|//.*?$", re.DOTALL | re.MULTILINE)
+ _r_string = re.compile(r'\".*?\"')
+ def _write_source_and_check(self, file=None):
+ base_write_source(self, file)
+ if file is None:
+ f = open(self.sourcefilename)
+ data = f.read()
+ f.close()
+ data = _r_comment.sub(' ', data)
+ data = _r_string.sub('"skipped"', data)
+ assert '$' not in data
+ base_write_source = cffi.verifier.Verifier._write_source
+ cffi.verifier.Verifier._write_source = _write_source_and_check
+
+
+def test_module_type():
+ import cffi.verifier
+ ffi = FFI()
+ lib = ffi.verify()
+ if hasattr(lib, '_cffi_python_module'):
+ print('verify got a PYTHON module')
+ if hasattr(lib, '_cffi_generic_module'):
+ print('verify got a GENERIC module')
+ expected_generic = (cffi.verifier._FORCE_GENERIC_ENGINE or
+ '__pypy__' in sys.builtin_module_names)
+ assert hasattr(lib, '_cffi_python_module') == (not expected_generic)
+ assert hasattr(lib, '_cffi_generic_module') == expected_generic
+
+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):
+ 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
+ import cffi
+ test_missing_function(ffi=cffi.FFI())
+
+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_char16_char32_type():
+ py.test.skip("XXX test or fully prevent char16_t and char32_t from "
+ "working in ffi.verify() mode")
+
+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():
+ ffi = FFI()
+ ffi.cdef("struct foo_s { char x; int y; long *z; };")
+ ffi.verify("struct foo_s { char x; int y; long *z; };")
+ #
+ if sys.platform != 'win32': # XXX fixme: only gives warnings
+ py.test.raises(VerificationError, ffi.verify,
+ "struct foo_s { char x; int y; int *z; };")
+ #
+ py.test.raises(VerificationError, ffi.verify,
+ "struct foo_s { int y; long *z; };")
+ #
+ e = py.test.raises(VerificationError, ffi.verify,
+ "struct foo_s { int y; char x; long *z; };")
+ assert str(e.value) == (
+ "struct foo_s: wrong offset for field 'x'"
+ " (we have 0, but C compiler says 4)")
+ #
+ e = py.test.raises(VerificationError, ffi.verify,
+ "struct foo_s { char x; int y; long *z; char extra; };")
+ assert str(e.value) == (
+ "struct foo_s: wrong total size"
+ " (we have %d, but C compiler says %d)" % (
+ ffi.sizeof("struct foo_s"),
+ ffi.sizeof("struct foo_s") + ffi.sizeof("long*")))
+ #
+ # 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
+ ffi.verify("struct foo_s { char x, extra; int y; long *z; };")
+ #
+ e = py.test.raises(VerificationError, ffi.verify,
+ "struct foo_s { char x; short pad; short y; long *z; };")
+ assert str(e.value) == (
+ "struct foo_s: wrong size for field 'y'"
+ " (we have 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)
+ except VerificationError:
+ 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 = 2
+ assert s.f == 2
+
+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"
+ #
+ # try again
+ ffi.verify("enum ee { EE1=10, EE2, EE3=-10, EE4 };")
+ assert ffi.string(ffi.cast('enum ee', 11)) == "EE2"
+ #
+ 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 };")
+ ffi.verify("enum ee { EE1, EE2, EE3 };")
+ py.test.raises(VerificationError, ffi.verify, "enum ee { EE1, EE2 };")
+ e = py.test.raises(VerificationError, ffi.verify,
+ "enum ee { EE1, EE3, EE2 };")
+ assert str(e.value) == 'enum ee: EE2 has the real value 2, not 1'
+ # extra items cannot be seen and have no bad consequence anyway
+ lib = ffi.verify("enum ee { EE1, EE2, EE3, EE4 };")
+ assert lib.EE3 == 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_nonfull_enum_bug3():
+ ffi = FFI()
+ ffi.cdef("enum ee2 { EE4=..., EE5=... };")
+ ffi.cdef("enum ee6 { EE7=10, EE8=..., EE9=... };")
+
+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_ctypes_backend_forces_generic_engine():
+ from cffi.backend_ctypes import CTypesBackend
+ ffi = FFI(backend=CTypesBackend())
+ ffi.cdef("int func(int a);")
+ lib = ffi.verify("int func(int a) { return a * 42; }")
+ assert not hasattr(lib, '_cffi_python_module')
+ assert hasattr(lib, '_cffi_generic_module')
+ assert lib.func(100) == 4200
+
+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():
+ ffi = FFI()
+ ffi.cdef("typedef enum { AA, BB } enum1_t;")
+ py.test.raises(VerificationError, ffi.verify,
+ "typedef enum { AA, CC, BB } enum1_t;")
+
+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; }; };
+ """)
+ 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; }; };
+ """)
+ 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():
+ 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_cast_from_int_type_to_bool():
+ ffi = 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_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():
+ 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():
+ 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():
+ 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():
+ 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
+ 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 c1, c2, c2c in [(0xffffffff, -1, ''),
+ (maxulong, -1, ''),
+ (-1, 0xffffffff, 'U'),
+ (-1, maxulong, 'UL')]:
+ if c2c and sys.platform == 'win32':
+ continue # enums may always be signed with MSVC
+ ffi = FFI()
+ ffi.cdef("enum foo_e { AA=%s };" % c1)
+ e = py.test.raises(VerificationError, ffi.verify,
+ "enum foo_e { AA=%s%s };" % (c2, c2c))
+ assert str(e.value) == ('enum foo_e: AA has the real value %d, not %d'
+ % (c2, c1))
+
+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():
+ from cffi import FFI # ignore warnings
+ 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():
+ # 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'!
+ ffi1 = FFI()
+ ffi1.cdef("int foo_verify_dlopen_flags;")
+
+ lib1 = ffi1.verify("int foo_verify_dlopen_flags;",
+ flags=ffi1.RTLD_GLOBAL | ffi1.RTLD_LAZY)
+ lib2 = get_second_lib()
+
+ lib1.foo_verify_dlopen_flags = 42
+ assert lib2.foo_verify_dlopen_flags == 42
+ lib2.foo_verify_dlopen_flags += 1
+ assert lib1.foo_verify_dlopen_flags == 43
+
+def get_second_lib():
+ # Hack, using modulename makes the test fail
+ ffi2 = FFI()
+ ffi2.cdef("int foo_verify_dlopen_flags;")
+ lib2 = ffi2.verify("int foo_verify_dlopen_flags;",
+ flags=ffi2.RTLD_GLOBAL | ffi2.RTLD_LAZY)
+ return lib2
+
+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():
+ 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_use_local_dir():
+ ffi = FFI()
+ lib = ffi.verify("", modulename="test_use_local_dir")
+ this_dir = os.path.dirname(__file__)
+ pycache_files = os.listdir(os.path.join(this_dir, '__pycache__'))
+ assert any('test_use_local_dir' in s for s in pycache_files)
+
+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")
+ e = py.test.raises(VerificationError, ffi.verify, "#define FOO 124")
+ assert str(e.value).endswith("FOO has the real value 124, not 123")
+
+def test_static_const_int_known_value():
+ ffi = FFI()
+ ffi.cdef("static const int FOO = 0x123;")
+ lib = ffi.verify("#define FOO 0x123")
+ assert lib.FOO == 0x123
+
+def test_static_const_int_wrong_value():
+ ffi = FFI()
+ ffi.cdef("static const int FOO = 123;")
+ e = py.test.raises(VerificationError, ffi.verify, "#define FOO 124")
+ assert str(e.value).endswith("FOO has the real value 124, not 123")
+
+def test_const_struct_global():
+ ffi = FFI()
+ ffi.cdef("typedef struct { int x; ...; } T; const T myglob;")
+ lib = ffi.verify("typedef struct { double y; int x; } T;"
+ "const T myglob = { 0.1, 42 };")
+ assert ffi.typeof(lib.myglob) == ffi.typeof("T")
+ assert lib.myglob.x == 42
+
+def test_dont_support_int_dotdotdot():
+ ffi = FFI()
+ ffi.cdef("typedef int... t1;")
+ e = py.test.raises(VerificationError, ffi.verify, "")
+ assert str(e.value) == ("feature not supported with ffi.verify(), but only "
+ "with ffi.set_source(): 'typedef int... t1'")
+ ffi = FFI()
+ ffi.cdef("typedef double ... t1;")
+ e = py.test.raises(VerificationError, ffi.verify, "")
+ assert str(e.value) == ("feature not supported with ffi.verify(), but only "
+ "with ffi.set_source(): 'typedef float... t1'")
+
+def test_const_fields():
+ ffi = FFI()
+ ffi.cdef("""struct foo_s { const int a; void *const b; };""")
+ ffi.verify("""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_win32_calling_convention_0():
+ ffi = FFI()
+ ffi.cdef("""
+ int call1(int(__cdecl *cb)(int));
+ int (*const call2)(int(__stdcall *cb)(int));
+ """)
+ lib = ffi.verify(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
+ #print 'cb1 =', cb1
+ res = lib.call1(cb1)
+ assert res == 500*999*2
+ #print 'cb2 =', cb2
+ #print ffi.typeof(lib.call2)
+ #print 'call2 =', lib.call2
+ res = lib.call2(cb2)
+ #print '...'
+ assert res == -500*999*3
+ #print 'done'
+ if sys.platform == 'win32' and 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 = ffi.verify(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;
+ }
+ """)
+ assert lib.call1(lib.cb1) == 500*999*2
+ assert lib.call2(lib.cb2) == -500*999*3
+
+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 = ffi.verify(r"""
+ #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; }
+ """)
+ assert lib.call1(lib.cb1) == 500*999*2
+ assert lib.call2(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 = ffi.verify(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;
+ }
+ """)
+ if sys.platform == 'win32' and sys.maxsize < 2**32:
+ py.test.raises(TypeError, lib.call1, lib.cb2)
+ py.test.raises(TypeError, lib.call2, lib.cb1)
+ pt = lib.call1(lib.cb1)
+ assert (pt.x, pt.y) == (-9*500*999, 9*500*999)
+ pt = lib.call2(lib.cb2)
+ assert (pt.x, pt.y) == (99*500*999, -99*500*999)
+
+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():
+ # with PyPy's GC, these calls to ffi.gc() would rapidly consume
+ # 40 GB of RAM without the third argument
+ _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
+
+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/cffi0/test_verify2.py b/testing/cffi0/test_verify2.py
new file mode 100644
index 0000000..a4af6d6
--- /dev/null
+++ b/testing/cffi0/test_verify2.py
@@ -0,0 +1,9 @@
+from .test_verify import *
+
+# This test file runs normally after test_verify. We only clean up the .c
+# sources, to check that it also works when we have only the .so. The
+# tests should run much faster than test_verify.
+
+def setup_module():
+ import cffi.verifier
+ cffi.verifier.cleanup_tmpdir(keep_so=True)
diff --git a/testing/cffi0/test_version.py b/testing/cffi0/test_version.py
new file mode 100644
index 0000000..9325685
--- /dev/null
+++ b/testing/cffi0/test_version.py
@@ -0,0 +1,62 @@
+import py, os, sys
+import cffi, _cffi_backend
+
+def setup_module(mod):
+ if '_cffi_backend' in sys.builtin_module_names:
+ py.test.skip("this is embedded version")
+
+#BACKEND_VERSIONS = {
+# '0.4.2': '0.4', # did not change
+# '0.7.1': '0.7', # did not change
+# '0.7.2': '0.7', # did not change
+# '0.8.1': '0.8', # did not change (essentially)
+# '0.8.4': '0.8.3', # did not change
+# }
+
+def test_version():
+ v = cffi.__version__
+ version_info = '.'.join(str(i) for i in cffi.__version_info__)
+ version_info = version_info.replace('.beta.', 'b')
+ version_info = version_info.replace('.plus', '+')
+ assert v == version_info
+ #v = BACKEND_VERSIONS.get(v, v)
+ assert v == _cffi_backend.__version__
+
+def test_doc_version():
+ parent = os.path.dirname(os.path.dirname(cffi.__file__))
+ p = os.path.join(parent, 'doc', 'source', 'conf.py')
+ content = open(p).read()
+ #
+ v = cffi.__version__
+ assert ("version = '%s'\n" % v[:4]) in content
+ assert ("release = '%s'\n" % v) in content
+
+def test_doc_version_file():
+ parent = os.path.dirname(os.path.dirname(cffi.__file__))
+ v = cffi.__version__.replace('+', '')
+ p = os.path.join(parent, 'doc', 'source', 'installation.rst')
+ content = open(p).read()
+ assert (" package version %s:" % v) in content
+
+def test_setup_version():
+ parent = os.path.dirname(os.path.dirname(cffi.__file__))
+ p = os.path.join(parent, 'setup.py')
+ content = open(p).read()
+ #
+ v = cffi.__version__.replace('+', '')
+ assert ("version='%s'" % v) in content
+
+def test_c_version():
+ parent = os.path.dirname(os.path.dirname(cffi.__file__))
+ v = cffi.__version__
+ p = os.path.join(parent, 'c', 'test_c.py')
+ content = open(p).read()
+ #v = BACKEND_VERSIONS.get(v, v)
+ assert (('assert __version__ == "%s"' % v) in content)
+
+def test_embedding_h():
+ parent = os.path.dirname(os.path.dirname(cffi.__file__))
+ v = cffi.__version__
+ p = os.path.join(parent, 'cffi', '_embedding.h')
+ content = open(p).read()
+ assert ('cffi version: %s"' % (v,)) in content
diff --git a/testing/cffi0/test_vgen.py b/testing/cffi0/test_vgen.py
new file mode 100644
index 0000000..1a7e05d
--- /dev/null
+++ b/testing/cffi0/test_vgen.py
@@ -0,0 +1,12 @@
+import cffi.verifier
+from .test_verify import *
+
+
+def setup_module():
+ cffi.verifier.cleanup_tmpdir()
+ cffi.verifier._FORCE_GENERIC_ENGINE = True
+ # Runs all tests with _FORCE_GENERIC_ENGINE = True, to make sure we
+ # also test vengine_gen.py.
+
+def teardown_module():
+ cffi.verifier._FORCE_GENERIC_ENGINE = False
diff --git a/testing/cffi0/test_vgen2.py b/testing/cffi0/test_vgen2.py
new file mode 100644
index 0000000..34147c8
--- /dev/null
+++ b/testing/cffi0/test_vgen2.py
@@ -0,0 +1,13 @@
+import cffi.verifier
+from .test_vgen import *
+
+# This test file runs normally after test_vgen. We only clean up the .c
+# sources, to check that it also works when we have only the .so. The
+# tests should run much faster than test_vgen.
+
+def setup_module():
+ cffi.verifier.cleanup_tmpdir(keep_so=True)
+ cffi.verifier._FORCE_GENERIC_ENGINE = True
+
+def teardown_module():
+ cffi.verifier._FORCE_GENERIC_ENGINE = False
diff --git a/testing/cffi0/test_zdistutils.py b/testing/cffi0/test_zdistutils.py
new file mode 100644
index 0000000..b67b105
--- /dev/null
+++ b/testing/cffi0/test_zdistutils.py
@@ -0,0 +1,288 @@
+import sys, os, imp, math, shutil
+import py
+from cffi import FFI, FFIError
+from cffi.verifier import Verifier, _locate_engine_class, _get_so_suffixes
+from cffi.ffiplatform import maybe_relative_path
+from testing.udir import udir
+
+
+class DistUtilsTest(object):
+ def setup_class(self):
+ self.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':
+ self.lib_m = 'msvcrt'
+
+ def teardown_class(self):
+ if udir.isdir():
+ udir.remove(ignore_errors=True)
+ udir.ensure(dir=1)
+
+ def test_locate_engine_class(self):
+ cls = _locate_engine_class(FFI(), self.generic)
+ if self.generic:
+ # asked for the generic engine, which must not generate a
+ # CPython extension module
+ assert not cls._gen_python_module
+ else:
+ # asked for the CPython engine: check that we got it, unless
+ # we are running on top of PyPy, where the generic engine is
+ # always better
+ if '__pypy__' not in sys.builtin_module_names:
+ assert cls._gen_python_module
+
+ def test_write_source(self):
+ ffi = FFI()
+ ffi.cdef("double sin(double x);")
+ csrc = '/*hi there %s!*/\n#include <math.h>\n' % self
+ v = Verifier(ffi, csrc, force_generic_engine=self.generic,
+ libraries=[self.lib_m])
+ v.write_source()
+ with open(v.sourcefilename, 'r') as f:
+ data = f.read()
+ assert csrc in data
+
+ def test_write_source_explicit_filename(self):
+ ffi = FFI()
+ ffi.cdef("double sin(double x);")
+ csrc = '/*hi there %s!*/\n#include <math.h>\n' % self
+ v = Verifier(ffi, csrc, force_generic_engine=self.generic,
+ libraries=[self.lib_m])
+ v.sourcefilename = filename = str(udir.join('write_source.c'))
+ v.write_source()
+ assert filename == v.sourcefilename
+ with open(filename, 'r') as f:
+ data = f.read()
+ assert csrc in data
+
+ def test_write_source_to_file_obj(self):
+ ffi = FFI()
+ ffi.cdef("double sin(double x);")
+ csrc = '/*hi there %s!*/\n#include <math.h>\n' % self
+ v = Verifier(ffi, csrc, force_generic_engine=self.generic,
+ libraries=[self.lib_m])
+ try:
+ from StringIO import StringIO
+ except ImportError:
+ from io import StringIO
+ f = StringIO()
+ v.write_source(file=f)
+ assert csrc in f.getvalue()
+
+ def test_compile_module(self):
+ ffi = FFI()
+ ffi.cdef("double sin(double x);")
+ csrc = '/*hi there %s!*/\n#include <math.h>\n' % self
+ v = Verifier(ffi, csrc, force_generic_engine=self.generic,
+ libraries=[self.lib_m])
+ v.compile_module()
+ assert v.get_module_name().startswith('_cffi_')
+ if v.generates_python_module():
+ mod = imp.load_dynamic(v.get_module_name(), v.modulefilename)
+ assert hasattr(mod, '_cffi_setup')
+
+ def test_compile_module_explicit_filename(self):
+ ffi = FFI()
+ ffi.cdef("double sin(double x);")
+ csrc = '/*hi there %s!2*/\n#include <math.h>\n' % self
+ v = Verifier(ffi, csrc, force_generic_engine=self.generic,
+ libraries=[self.lib_m])
+ basename = self.__class__.__name__ + 'test_compile_module'
+ v.modulefilename = filename = str(udir.join(basename + '.so'))
+ v.compile_module()
+ assert filename == v.modulefilename
+ assert v.get_module_name() == basename
+ if v.generates_python_module():
+ mod = imp.load_dynamic(v.get_module_name(), v.modulefilename)
+ assert hasattr(mod, '_cffi_setup')
+
+ def test_name_from_checksum_of_cdef(self):
+ names = []
+ for csrc in ['double', 'double', 'float']:
+ ffi = FFI()
+ ffi.cdef("%s sin(double x);" % csrc)
+ v = Verifier(ffi, "#include <math.h>",
+ force_generic_engine=self.generic,
+ libraries=[self.lib_m])
+ names.append(v.get_module_name())
+ assert names[0] == names[1] != names[2]
+
+ def test_name_from_checksum_of_csrc(self):
+ names = []
+ for csrc in ['123', '123', '1234']:
+ ffi = FFI()
+ ffi.cdef("double sin(double x);")
+ v = Verifier(ffi, csrc, force_generic_engine=self.generic)
+ names.append(v.get_module_name())
+ assert names[0] == names[1] != names[2]
+
+ def test_load_library(self):
+ ffi = FFI()
+ ffi.cdef("double sin(double x);")
+ csrc = '/*hi there %s!3*/\n#include <math.h>\n' % self
+ v = Verifier(ffi, csrc, force_generic_engine=self.generic,
+ libraries=[self.lib_m])
+ library = v.load_library()
+ assert library.sin(12.3) == math.sin(12.3)
+
+ def test_verifier_args(self):
+ ffi = FFI()
+ ffi.cdef("double sin(double x);")
+ csrc = '/*hi there %s!4*/#include "test_verifier_args.h"\n' % self
+ udir.join('test_verifier_args.h').write('#include <math.h>\n')
+ v = Verifier(ffi, csrc, include_dirs=[str(udir)],
+ force_generic_engine=self.generic,
+ libraries=[self.lib_m])
+ library = v.load_library()
+ assert library.sin(12.3) == math.sin(12.3)
+
+ def test_verifier_object_from_ffi(self):
+ ffi = FFI()
+ ffi.cdef("double sin(double x);")
+ csrc = "/*6%s*/\n#include <math.h>" % self
+ lib = ffi.verify(csrc, force_generic_engine=self.generic,
+ libraries=[self.lib_m])
+ assert lib.sin(12.3) == math.sin(12.3)
+ assert isinstance(ffi.verifier, Verifier)
+ with open(ffi.verifier.sourcefilename, 'r') as f:
+ data = f.read()
+ assert csrc in data
+
+ def test_extension_object(self):
+ ffi = FFI()
+ ffi.cdef("double sin(double x);")
+ csrc = '/*7%s*/' % self + '''
+ #include <math.h>
+ #ifndef TEST_EXTENSION_OBJECT
+ # error "define_macros missing"
+ #endif
+ '''
+ lib = ffi.verify(csrc, define_macros=[('TEST_EXTENSION_OBJECT', '1')],
+ force_generic_engine=self.generic,
+ libraries=[self.lib_m])
+ assert lib.sin(12.3) == math.sin(12.3)
+ v = ffi.verifier
+ ext = v.get_extension()
+ assert 'distutils.extension.Extension' in str(ext.__class__) or \
+ 'setuptools.extension.Extension' in str(ext.__class__)
+ assert ext.sources == [maybe_relative_path(v.sourcefilename)]
+ assert ext.name == v.get_module_name()
+ assert ext.define_macros == [('TEST_EXTENSION_OBJECT', '1')]
+
+ def test_extension_forces_write_source(self):
+ ffi = FFI()
+ ffi.cdef("double sin(double x);")
+ csrc = '/*hi there9!%s*/\n#include <math.h>\n' % self
+ v = Verifier(ffi, csrc, force_generic_engine=self.generic,
+ libraries=[self.lib_m])
+ assert not os.path.exists(v.sourcefilename)
+ v.get_extension()
+ assert os.path.exists(v.sourcefilename)
+
+ def test_extension_object_extra_sources(self):
+ ffi = FFI()
+ ffi.cdef("double test1eoes(double x);")
+ extra_source = str(udir.join('extension_extra_sources.c'))
+ with open(extra_source, 'w') as f:
+ f.write('double test1eoes(double x) { return x * 6.0; }\n')
+ csrc = '/*9%s*/' % self + '''
+ double test1eoes(double x); /* or #include "extra_sources.h" */
+ '''
+ lib = ffi.verify(csrc, sources=[extra_source],
+ force_generic_engine=self.generic)
+ assert lib.test1eoes(7.0) == 42.0
+ v = ffi.verifier
+ ext = v.get_extension()
+ assert 'distutils.extension.Extension' in str(ext.__class__) or \
+ 'setuptools.extension.Extension' in str(ext.__class__)
+ assert ext.sources == [maybe_relative_path(v.sourcefilename),
+ extra_source]
+ assert ext.name == v.get_module_name()
+
+ def test_install_and_reload_module(self, targetpackage='', ext_package=''):
+ KEY = repr(self)
+ if not hasattr(os, 'fork'):
+ py.test.skip("test requires os.fork()")
+
+ if targetpackage:
+ udir.ensure(targetpackage, dir=1).ensure('__init__.py')
+ sys.path.insert(0, str(udir))
+
+ def make_ffi(**verifier_args):
+ ffi = FFI()
+ ffi.cdef("/* %s, %s, %s */" % (KEY, targetpackage, ext_package))
+ ffi.cdef("double test1iarm(double x);")
+ csrc = "double test1iarm(double x) { return x * 42.0; }"
+ lib = ffi.verify(csrc, force_generic_engine=self.generic,
+ ext_package=ext_package,
+ **verifier_args)
+ return ffi, lib
+
+ childpid = os.fork()
+ if childpid == 0:
+ # in the child
+ ffi, lib = make_ffi()
+ assert lib.test1iarm(1.5) == 63.0
+ # "install" the module by moving it into udir (/targetpackage)
+ if targetpackage:
+ target = udir.join(targetpackage)
+ else:
+ target = udir
+ shutil.move(ffi.verifier.modulefilename, str(target))
+ os._exit(0)
+ # in the parent
+ _, status = os.waitpid(childpid, 0)
+ if not (os.WIFEXITED(status) and os.WEXITSTATUS(status) == 0):
+ raise AssertionError # see error above in subprocess
+
+ from cffi import ffiplatform
+ prev_compile = ffiplatform.compile
+ try:
+ if targetpackage == ext_package:
+ ffiplatform.compile = lambda *args: dont_call_me_any_more
+ # won't find it in tmpdir, but should find it correctly
+ # installed in udir
+ ffi, lib = make_ffi()
+ assert lib.test1iarm(0.5) == 21.0
+ finally:
+ ffiplatform.compile = prev_compile
+
+ def test_install_and_reload_module_package(self):
+ self.test_install_and_reload_module(targetpackage='foo_iarmp',
+ ext_package='foo_iarmp')
+
+ def test_install_and_reload_module_ext_package_not_found(self):
+ self.test_install_and_reload_module(targetpackage='foo_epnf',
+ ext_package='not_found')
+
+ def test_tag(self):
+ ffi = FFI()
+ ffi.cdef("/* %s test_tag */ double test1tag(double x);" % self)
+ csrc = "double test1tag(double x) { return x - 42.0; }"
+ lib = ffi.verify(csrc, force_generic_engine=self.generic,
+ tag='xxtest_tagxx')
+ assert lib.test1tag(143) == 101.0
+ assert '_cffi_xxtest_tagxx_' in ffi.verifier.modulefilename
+
+ def test_modulename(self):
+ ffi = FFI()
+ ffi.cdef("/* %s test_modulename */ double test1foo(double x);" % self)
+ csrc = "double test1foo(double x) { return x - 63.0; }"
+ modname = 'xxtest_modulenamexx%d' % (self.generic,)
+ lib = ffi.verify(csrc, force_generic_engine=self.generic,
+ modulename=modname)
+ assert lib.test1foo(143) == 80.0
+ suffix = _get_so_suffixes()[0]
+ fn1 = os.path.join(ffi.verifier.tmpdir, modname + '.c')
+ fn2 = os.path.join(ffi.verifier.tmpdir, modname + suffix)
+ assert ffi.verifier.sourcefilename == fn1
+ assert ffi.verifier.modulefilename == fn2
+
+
+class TestDistUtilsCPython(DistUtilsTest):
+ generic = False
+
+class TestDistUtilsGeneric(DistUtilsTest):
+ generic = True
diff --git a/testing/cffi0/test_zintegration.py b/testing/cffi0/test_zintegration.py
new file mode 100644
index 0000000..d56dac2
--- /dev/null
+++ b/testing/cffi0/test_zintegration.py
@@ -0,0 +1,176 @@
+import py, os, sys, shutil
+import subprocess
+from testing.udir import udir
+
+if sys.platform == 'win32':
+ py.test.skip('snippets do not run on win32')
+if sys.version_info < (2, 7):
+ py.test.skip('fails e.g. on a Debian/Ubuntu which patches virtualenv'
+ ' in a non-2.6-friendly way')
+
+def create_venv(name):
+ tmpdir = udir.join(name)
+ try:
+ subprocess.check_call(['virtualenv',
+ #'--never-download', <= could be added, but causes failures
+ # in random cases on random machines
+ '-p', os.path.abspath(sys.executable),
+ str(tmpdir)])
+ except OSError as e:
+ py.test.skip("Cannot execute virtualenv: %s" % (e,))
+
+ site_packages = None
+ for dirpath, dirnames, filenames in os.walk(str(tmpdir)):
+ if os.path.basename(dirpath) == 'site-packages':
+ site_packages = dirpath
+ break
+ paths = ""
+ if site_packages:
+ try:
+ from cffi import _pycparser
+ modules = ('cffi', '_cffi_backend')
+ except ImportError:
+ modules = ('cffi', '_cffi_backend', 'pycparser')
+ try:
+ import ply
+ except ImportError:
+ pass
+ else:
+ modules += ('ply',) # needed for older versions of pycparser
+ paths = []
+ for module in modules:
+ target = __import__(module, None, None, [])
+ if not hasattr(target, '__file__'): # for _cffi_backend on pypy
+ continue
+ src = os.path.abspath(target.__file__)
+ for end in ['__init__.pyc', '__init__.pyo', '__init__.py']:
+ if src.lower().endswith(end):
+ src = src[:-len(end)-1]
+ break
+ paths.append(os.path.dirname(src))
+ paths = os.pathsep.join(paths)
+ return tmpdir, paths
+
+SNIPPET_DIR = py.path.local(__file__).join('..', 'snippets')
+
+def really_run_setup_and_program(dirname, venv_dir_and_paths, python_snippet):
+ venv_dir, paths = venv_dir_and_paths
+ def remove(dir):
+ dir = str(SNIPPET_DIR.join(dirname, dir))
+ shutil.rmtree(dir, ignore_errors=True)
+ remove('build')
+ remove('__pycache__')
+ for basedir in os.listdir(str(SNIPPET_DIR.join(dirname))):
+ remove(os.path.join(basedir, '__pycache__'))
+ olddir = os.getcwd()
+ python_f = udir.join('x.py')
+ python_f.write(py.code.Source(python_snippet))
+ try:
+ os.chdir(str(SNIPPET_DIR.join(dirname)))
+ if os.name == 'nt':
+ bindir = 'Scripts'
+ else:
+ bindir = 'bin'
+ vp = str(venv_dir.join(bindir).join('python'))
+ env = os.environ.copy()
+ env['PYTHONPATH'] = paths
+ subprocess.check_call((vp, 'setup.py', 'clean'), env=env)
+ subprocess.check_call((vp, 'setup.py', 'install'), env=env)
+ subprocess.check_call((vp, str(python_f)), env=env)
+ finally:
+ os.chdir(olddir)
+
+def run_setup_and_program(dirname, python_snippet):
+ venv_dir = create_venv(dirname + '-cpy')
+ really_run_setup_and_program(dirname, venv_dir, python_snippet)
+ #
+ sys._force_generic_engine_ = True
+ try:
+ venv_dir = create_venv(dirname + '-gen')
+ really_run_setup_and_program(dirname, venv_dir, python_snippet)
+ finally:
+ del sys._force_generic_engine_
+ # the two files lextab.py and yacctab.py are created by not-correctly-
+ # installed versions of pycparser.
+ assert not os.path.exists(str(SNIPPET_DIR.join(dirname, 'lextab.py')))
+ assert not os.path.exists(str(SNIPPET_DIR.join(dirname, 'yacctab.py')))
+
+class TestZIntegration(object):
+ def teardown_class(self):
+ if udir.isdir():
+ udir.remove(ignore_errors=True)
+ udir.ensure(dir=1)
+
+ def test_infrastructure(self):
+ run_setup_and_program('infrastructure', '''
+ import snip_infrastructure
+ assert snip_infrastructure.func() == 42
+ ''')
+
+ def test_distutils_module(self):
+ run_setup_and_program("distutils_module", '''
+ import snip_basic_verify
+ p = snip_basic_verify.C.getpwuid(0)
+ assert snip_basic_verify.ffi.string(p.pw_name) == b"root"
+ ''')
+
+ def test_distutils_package_1(self):
+ run_setup_and_program("distutils_package_1", '''
+ import snip_basic_verify1
+ p = snip_basic_verify1.C.getpwuid(0)
+ assert snip_basic_verify1.ffi.string(p.pw_name) == b"root"
+ ''')
+
+ def test_distutils_package_2(self):
+ run_setup_and_program("distutils_package_2", '''
+ import snip_basic_verify2
+ p = snip_basic_verify2.C.getpwuid(0)
+ assert snip_basic_verify2.ffi.string(p.pw_name) == b"root"
+ ''')
+
+ def test_setuptools_module(self):
+ run_setup_and_program("setuptools_module", '''
+ import snip_setuptools_verify
+ p = snip_setuptools_verify.C.getpwuid(0)
+ assert snip_setuptools_verify.ffi.string(p.pw_name) == b"root"
+ ''')
+
+ def test_setuptools_package_1(self):
+ run_setup_and_program("setuptools_package_1", '''
+ import snip_setuptools_verify1
+ p = snip_setuptools_verify1.C.getpwuid(0)
+ assert snip_setuptools_verify1.ffi.string(p.pw_name) == b"root"
+ ''')
+
+ def test_setuptools_package_2(self):
+ run_setup_and_program("setuptools_package_2", '''
+ import snip_setuptools_verify2
+ p = snip_setuptools_verify2.C.getpwuid(0)
+ assert snip_setuptools_verify2.ffi.string(p.pw_name) == b"root"
+ ''')
+
+ def test_set_py_limited_api(self):
+ from cffi.setuptools_ext import _set_py_limited_api
+ try:
+ import setuptools
+ except ImportError as e:
+ py.test.skip(str(e))
+ orig_version = setuptools.__version__
+ expecting_limited_api = not hasattr(sys, 'gettotalrefcount')
+ try:
+ setuptools.__version__ = '26.0.0'
+ from setuptools import Extension
+
+ kwds = _set_py_limited_api(Extension, {})
+ assert kwds.get('py_limited_api', False) == expecting_limited_api
+
+ setuptools.__version__ = '25.0'
+ kwds = _set_py_limited_api(Extension, {})
+ assert kwds.get('py_limited_api', False) == False
+
+ setuptools.__version__ = 'development'
+ kwds = _set_py_limited_api(Extension, {})
+ assert kwds.get('py_limited_api', False) == expecting_limited_api
+
+ finally:
+ setuptools.__version__ = orig_version
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}}})
diff --git a/testing/embedding/__init__.py b/testing/embedding/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/testing/embedding/__init__.py
diff --git a/testing/embedding/add1-test.c b/testing/embedding/add1-test.c
new file mode 100644
index 0000000..b9ede18
--- /dev/null
+++ b/testing/embedding/add1-test.c
@@ -0,0 +1,21 @@
+#include <stdio.h>
+
+#ifdef _MSC_VER
+#include <windows.h>
+#endif
+
+extern int add1(int, int);
+
+
+int main(void)
+{
+ int x, y;
+ x = add1(40, 2);
+ y = add1(100, -5);
+ printf("got: %d %d\n", x, y);
+#ifdef _MSC_VER
+ if (x == 0 && y == 0)
+ Sleep(2000);
+#endif
+ return 0;
+}
diff --git a/testing/embedding/add1.py b/testing/embedding/add1.py
new file mode 100644
index 0000000..e5b3de1
--- /dev/null
+++ b/testing/embedding/add1.py
@@ -0,0 +1,33 @@
+import cffi
+
+ffi = cffi.FFI()
+
+ffi.embedding_api("""
+ int add1(int, int);
+""")
+
+ffi.embedding_init_code(r"""
+ import sys, time
+ sys.stdout.write("preparing")
+ for i in range(3):
+ sys.stdout.flush()
+ time.sleep(0.2)
+ sys.stdout.write(".")
+ sys.stdout.write("\n")
+
+ from _add1_cffi import ffi
+
+ int(ord("A")) # check that built-ins are there
+
+ @ffi.def_extern()
+ def add1(x, y):
+ sys.stdout.write("adding %d and %d\n" % (x, y))
+ sys.stdout.flush()
+ return x + y
+""")
+
+ffi.set_source("_add1_cffi", """
+""")
+
+fn = ffi.compile(verbose=True)
+print('FILENAME: %s' % (fn,))
diff --git a/testing/embedding/add2-test.c b/testing/embedding/add2-test.c
new file mode 100644
index 0000000..9620843
--- /dev/null
+++ b/testing/embedding/add2-test.c
@@ -0,0 +1,14 @@
+#include <stdio.h>
+
+extern int add1(int, int);
+extern int add2(int, int, int);
+
+
+int main(void)
+{
+ int x, y;
+ x = add1(40, 2);
+ y = add2(100, -5, -20);
+ printf("got: %d %d\n", x, y);
+ return 0;
+}
diff --git a/testing/embedding/add2.py b/testing/embedding/add2.py
new file mode 100644
index 0000000..311a464
--- /dev/null
+++ b/testing/embedding/add2.py
@@ -0,0 +1,29 @@
+import cffi
+
+ffi = cffi.FFI()
+
+ffi.embedding_api("""
+ int add2(int, int, int);
+""")
+
+ffi.embedding_init_code(r"""
+ import sys
+ sys.stdout.write("prepADD2\n")
+
+ assert '_add2_cffi' in sys.modules
+ m = sys.modules['_add2_cffi']
+ import _add2_cffi
+ ffi = _add2_cffi.ffi
+
+ @ffi.def_extern()
+ def add2(x, y, z):
+ sys.stdout.write("adding %d and %d and %d\n" % (x, y, z))
+ sys.stdout.flush()
+ return x + y + z
+""")
+
+ffi.set_source("_add2_cffi", """
+""")
+
+fn = ffi.compile(verbose=True)
+print('FILENAME: %s' % (fn,))
diff --git a/testing/embedding/add3.py b/testing/embedding/add3.py
new file mode 100644
index 0000000..1361912
--- /dev/null
+++ b/testing/embedding/add3.py
@@ -0,0 +1,24 @@
+import cffi
+
+ffi = cffi.FFI()
+
+ffi.embedding_api("""
+ int add3(int, int, int, int);
+""")
+
+ffi.embedding_init_code(r"""
+ from _add3_cffi import ffi
+ import sys
+
+ @ffi.def_extern()
+ def add3(x, y, z, t):
+ sys.stdout.write("adding %d, %d, %d, %d\n" % (x, y, z, t))
+ sys.stdout.flush()
+ return x + y + z + t
+""")
+
+ffi.set_source("_add3_cffi", """
+""")
+
+fn = ffi.compile(verbose=True)
+print('FILENAME: %s' % (fn,))
diff --git a/testing/embedding/add_recursive-test.c b/testing/embedding/add_recursive-test.c
new file mode 100644
index 0000000..cd29b79
--- /dev/null
+++ b/testing/embedding/add_recursive-test.c
@@ -0,0 +1,27 @@
+#include <stdio.h>
+
+#ifdef _MSC_VER
+# define DLLIMPORT __declspec(dllimport)
+#else
+# define DLLIMPORT extern
+#endif
+
+DLLIMPORT int add_rec(int, int);
+DLLIMPORT int (*my_callback)(int);
+
+static int some_callback(int x)
+{
+ printf("some_callback(%d)\n", x);
+ fflush(stdout);
+ return add_rec(x, 9);
+}
+
+int main(void)
+{
+ int x, y;
+ my_callback = some_callback;
+ x = add_rec(40, 2);
+ y = add_rec(100, -5);
+ printf("got: %d %d\n", x, y);
+ return 0;
+}
diff --git a/testing/embedding/add_recursive.py b/testing/embedding/add_recursive.py
new file mode 100644
index 0000000..9fa463d
--- /dev/null
+++ b/testing/embedding/add_recursive.py
@@ -0,0 +1,33 @@
+import cffi
+
+ffi = cffi.FFI()
+
+ffi.embedding_api("""
+ int (*my_callback)(int);
+ int add_rec(int, int);
+""")
+
+ffi.embedding_init_code(r"""
+ from _add_recursive_cffi import ffi, lib
+ import sys
+ print("preparing REC")
+ sys.stdout.flush()
+
+ @ffi.def_extern()
+ def add_rec(x, y):
+ print("adding %d and %d" % (x, y))
+ sys.stdout.flush()
+ return x + y
+
+ x = lib.my_callback(400)
+ print('<<< %d >>>' % (x,))
+""")
+
+ffi.set_source("_add_recursive_cffi", """
+/* use CFFI_DLLEXPORT: on windows, it expands to __declspec(dllexport),
+ which is needed to export a variable from a dll */
+CFFI_DLLEXPORT int (*my_callback)(int);
+""")
+
+fn = ffi.compile(verbose=True)
+print('FILENAME: %s' % (fn,))
diff --git a/testing/embedding/empty.py b/testing/embedding/empty.py
new file mode 100644
index 0000000..aa8d830
--- /dev/null
+++ b/testing/embedding/empty.py
@@ -0,0 +1,10 @@
+import cffi
+
+ffi = cffi.FFI()
+
+ffi.embedding_api("")
+
+ffi.set_source("_empty_cffi", "")
+
+fn = ffi.compile(verbose=True)
+print('FILENAME: %s' % (fn,))
diff --git a/testing/embedding/initerror.py b/testing/embedding/initerror.py
new file mode 100644
index 0000000..775cf56
--- /dev/null
+++ b/testing/embedding/initerror.py
@@ -0,0 +1,18 @@
+import cffi
+
+ffi = cffi.FFI()
+
+ffi.embedding_api("""
+ int add1(int, int);
+""")
+
+ffi.embedding_init_code(r"""
+ raise KeyError
+""")
+
+ffi.set_source("_initerror_cffi", """
+""")
+
+fn = ffi.compile(verbose=True)
+print('FILENAME: %s' % (fn,))
+
diff --git a/testing/embedding/perf-test.c b/testing/embedding/perf-test.c
new file mode 100644
index 0000000..2195bf5
--- /dev/null
+++ b/testing/embedding/perf-test.c
@@ -0,0 +1,90 @@
+#include <stdio.h>
+#include <assert.h>
+#include <sys/time.h>
+#ifdef PTEST_USE_THREAD
+# include <pthread.h>
+static pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t cond1 = PTHREAD_COND_INITIALIZER;
+static int remaining;
+#endif
+
+
+extern int add1(int, int);
+
+
+static double time_delta(struct timeval *stop, struct timeval *start)
+{
+ return (stop->tv_sec - start->tv_sec) +
+ 1e-6 * (stop->tv_usec - start->tv_usec);
+}
+
+static double measure(void)
+{
+ long long i, iterations;
+ int result;
+ struct timeval start, stop;
+ double elapsed;
+
+ add1(0, 0); /* prepare off-line */
+
+ i = 0;
+ iterations = 1000;
+ result = gettimeofday(&start, NULL);
+ assert(result == 0);
+
+ while (1) {
+ for (; i < iterations; i++) {
+ add1(((int)i) & 0xaaaaaa, ((int)i) & 0x555555);
+ }
+ result = gettimeofday(&stop, NULL);
+ assert(result == 0);
+
+ elapsed = time_delta(&stop, &start);
+ assert(elapsed >= 0.0);
+ if (elapsed > 2.5)
+ break;
+ iterations = iterations * 3 / 2;
+ }
+
+ return elapsed / (double)iterations;
+}
+
+static void *start_routine(void *arg)
+{
+ double t = measure();
+ printf("time per call: %.3g\n", t);
+
+#ifdef PTEST_USE_THREAD
+ pthread_mutex_lock(&mutex1);
+ remaining -= 1;
+ if (!remaining)
+ pthread_cond_signal(&cond1);
+ pthread_mutex_unlock(&mutex1);
+#endif
+
+ return arg;
+}
+
+
+int main(void)
+{
+#ifndef PTEST_USE_THREAD
+ start_routine(0);
+#else
+ pthread_t th;
+ int i, status;
+
+ add1(0, 0); /* this is the main thread */
+
+ remaining = PTEST_USE_THREAD;
+ for (i = 0; i < PTEST_USE_THREAD; i++) {
+ status = pthread_create(&th, NULL, start_routine, NULL);
+ assert(status == 0);
+ }
+ pthread_mutex_lock(&mutex1);
+ while (remaining)
+ pthread_cond_wait(&cond1, &mutex1);
+ pthread_mutex_unlock(&mutex1);
+#endif
+ return 0;
+}
diff --git a/testing/embedding/perf.py b/testing/embedding/perf.py
new file mode 100644
index 0000000..a8d20f4
--- /dev/null
+++ b/testing/embedding/perf.py
@@ -0,0 +1,21 @@
+import cffi
+
+ffi = cffi.FFI()
+
+ffi.embedding_api("""
+ int add1(int, int);
+""")
+
+ffi.embedding_init_code(r"""
+ from _perf_cffi import ffi
+
+ @ffi.def_extern()
+ def add1(x, y):
+ return x + y
+""")
+
+ffi.set_source("_perf_cffi", """
+""")
+
+fn = ffi.compile(verbose=True)
+print('FILENAME: %s' % (fn,))
diff --git a/testing/embedding/test_basic.py b/testing/embedding/test_basic.py
new file mode 100644
index 0000000..8463c3f
--- /dev/null
+++ b/testing/embedding/test_basic.py
@@ -0,0 +1,207 @@
+import py
+import sys, os, re
+import shutil, subprocess, time
+from testing.udir import udir
+import cffi
+
+
+local_dir = os.path.dirname(os.path.abspath(__file__))
+_link_error = '?'
+
+def check_lib_python_found(tmpdir):
+ global _link_error
+ if _link_error == '?':
+ ffi = cffi.FFI()
+ kwds = {}
+ ffi._apply_embedding_fix(kwds)
+ ffi.set_source("_test_lib_python_found", "", **kwds)
+ try:
+ ffi.compile(tmpdir=tmpdir, verbose=True)
+ except cffi.VerificationError as e:
+ _link_error = e
+ else:
+ _link_error = None
+ if _link_error:
+ py.test.skip(str(_link_error))
+
+
+def prefix_pythonpath():
+ cffi_base = os.path.dirname(os.path.dirname(local_dir))
+ pythonpath = org_env.get('PYTHONPATH', '').split(os.pathsep)
+ if cffi_base not in pythonpath:
+ pythonpath.insert(0, cffi_base)
+ return os.pathsep.join(pythonpath)
+
+def copy_away_env():
+ global org_env
+ try:
+ org_env
+ except NameError:
+ org_env = os.environ.copy()
+
+
+class EmbeddingTests:
+ _compiled_modules = {}
+
+ def setup_method(self, meth):
+ check_lib_python_found(str(udir.ensure('embedding', dir=1)))
+ self._path = udir.join('embedding', meth.__name__)
+ if sys.platform == "win32" or sys.platform == "darwin":
+ self._compiled_modules.clear() # workaround
+
+ def get_path(self):
+ return str(self._path.ensure(dir=1))
+
+ def _run_base(self, args, **kwds):
+ print('RUNNING:', args, kwds)
+ return subprocess.Popen(args, **kwds)
+
+ def _run(self, args):
+ popen = self._run_base(args, cwd=self.get_path(),
+ stdout=subprocess.PIPE,
+ universal_newlines=True)
+ output = popen.stdout.read()
+ err = popen.wait()
+ if err:
+ raise OSError("popen failed with exit code %r: %r" % (
+ err, args))
+ print(output.rstrip())
+ return output
+
+ def prepare_module(self, name):
+ self.patch_environment()
+ if name not in self._compiled_modules:
+ path = self.get_path()
+ filename = '%s.py' % name
+ # NOTE: if you have an .egg globally installed with an older
+ # version of cffi, this will not work, because sys.path ends
+ # up with the .egg before the PYTHONPATH entries. I didn't
+ # find a solution to that: we could hack sys.path inside the
+ # script run here, but we can't hack it in the same way in
+ # execute().
+ pathname = os.path.join(path, filename)
+ with open(pathname, 'w') as g:
+ g.write('''
+# https://bugs.python.org/issue23246
+import sys
+if sys.platform == 'win32':
+ try:
+ import setuptools
+ except ImportError:
+ pass
+''')
+ with open(os.path.join(local_dir, filename), 'r') as f:
+ g.write(f.read())
+
+ output = self._run([sys.executable, pathname])
+ match = re.compile(r"\bFILENAME: (.+)").search(output)
+ assert match
+ dynamic_lib_name = match.group(1)
+ if sys.platform == 'win32':
+ assert dynamic_lib_name.endswith('_cffi.dll')
+ elif sys.platform == 'darwin':
+ assert dynamic_lib_name.endswith('_cffi.dylib')
+ else:
+ assert dynamic_lib_name.endswith('_cffi.so')
+ self._compiled_modules[name] = dynamic_lib_name
+ return self._compiled_modules[name]
+
+ def compile(self, name, modules, opt=False, threads=False, defines={}):
+ path = self.get_path()
+ filename = '%s.c' % name
+ shutil.copy(os.path.join(local_dir, filename), path)
+ shutil.copy(os.path.join(local_dir, 'thread-test.h'), path)
+ import distutils.ccompiler
+ curdir = os.getcwd()
+ try:
+ os.chdir(self.get_path())
+ c = distutils.ccompiler.new_compiler()
+ print('compiling %s with %r' % (name, modules))
+ extra_preargs = []
+ debug = True
+ if sys.platform == 'win32':
+ libfiles = []
+ for m in modules:
+ m = os.path.basename(m)
+ assert m.endswith('.dll')
+ libfiles.append('Release\\%s.lib' % m[:-4])
+ modules = libfiles
+ extra_preargs.append('/MANIFEST')
+ debug = False # you need to install extra stuff
+ # for this to work
+ elif threads:
+ extra_preargs.append('-pthread')
+ objects = c.compile([filename], macros=sorted(defines.items()),
+ debug=debug)
+ c.link_executable(objects + modules, name, extra_preargs=extra_preargs)
+ finally:
+ os.chdir(curdir)
+
+ def patch_environment(self):
+ copy_away_env()
+ path = self.get_path()
+ # for libpypy-c.dll or Python27.dll
+ path = os.path.split(sys.executable)[0] + os.path.pathsep + path
+ env_extra = {'PYTHONPATH': prefix_pythonpath()}
+ if sys.platform == 'win32':
+ envname = 'PATH'
+ else:
+ envname = 'LD_LIBRARY_PATH'
+ libpath = org_env.get(envname)
+ if libpath:
+ libpath = path + os.path.pathsep + libpath
+ else:
+ libpath = path
+ env_extra[envname] = libpath
+ for key, value in sorted(env_extra.items()):
+ if os.environ.get(key) != value:
+ print('* setting env var %r to %r' % (key, value))
+ os.environ[key] = value
+
+ def execute(self, name):
+ path = self.get_path()
+ print('running %r in %r' % (name, path))
+ executable_name = name
+ if sys.platform == 'win32':
+ executable_name = os.path.join(path, executable_name + '.exe')
+ else:
+ executable_name = os.path.join('.', executable_name)
+ popen = self._run_base([executable_name], cwd=path,
+ stdout=subprocess.PIPE,
+ universal_newlines=True)
+ result = popen.stdout.read()
+ err = popen.wait()
+ if err:
+ raise OSError("%r failed with exit code %r" % (name, err))
+ return result
+
+
+class TestBasic(EmbeddingTests):
+ def test_empty(self):
+ empty_cffi = self.prepare_module('empty')
+
+ def test_basic(self):
+ add1_cffi = self.prepare_module('add1')
+ self.compile('add1-test', [add1_cffi])
+ output = self.execute('add1-test')
+ assert output == ("preparing...\n"
+ "adding 40 and 2\n"
+ "adding 100 and -5\n"
+ "got: 42 95\n")
+
+ def test_two_modules(self):
+ add1_cffi = self.prepare_module('add1')
+ add2_cffi = self.prepare_module('add2')
+ self.compile('add2-test', [add1_cffi, add2_cffi])
+ output = self.execute('add2-test')
+ assert output == ("preparing...\n"
+ "adding 40 and 2\n"
+ "prepADD2\n"
+ "adding 100 and -5 and -20\n"
+ "got: 42 75\n")
+
+ def test_init_time_error(self):
+ initerror_cffi = self.prepare_module('initerror')
+ self.compile('add1-test', [initerror_cffi])
+ output = self.execute('add1-test')
+ assert output == "got: 0 0\n" # plus lots of info to stderr
diff --git a/testing/embedding/test_performance.py b/testing/embedding/test_performance.py
new file mode 100644
index 0000000..f9f2605
--- /dev/null
+++ b/testing/embedding/test_performance.py
@@ -0,0 +1,52 @@
+import sys
+from testing.embedding.test_basic import EmbeddingTests
+
+if sys.platform == 'win32':
+ import py
+ py.test.skip("written with POSIX functions")
+
+
+class TestPerformance(EmbeddingTests):
+ def test_perf_single_threaded(self):
+ perf_cffi = self.prepare_module('perf')
+ self.compile('perf-test', [perf_cffi], opt=True)
+ output = self.execute('perf-test')
+ print('='*79)
+ print(output.rstrip())
+ print('='*79)
+
+ def test_perf_in_1_thread(self):
+ perf_cffi = self.prepare_module('perf')
+ self.compile('perf-test', [perf_cffi], opt=True, threads=True,
+ defines={'PTEST_USE_THREAD': '1'})
+ output = self.execute('perf-test')
+ print('='*79)
+ print(output.rstrip())
+ print('='*79)
+
+ def test_perf_in_2_threads(self):
+ perf_cffi = self.prepare_module('perf')
+ self.compile('perf-test', [perf_cffi], opt=True, threads=True,
+ defines={'PTEST_USE_THREAD': '2'})
+ output = self.execute('perf-test')
+ print('='*79)
+ print(output.rstrip())
+ print('='*79)
+
+ def test_perf_in_4_threads(self):
+ perf_cffi = self.prepare_module('perf')
+ self.compile('perf-test', [perf_cffi], opt=True, threads=True,
+ defines={'PTEST_USE_THREAD': '4'})
+ output = self.execute('perf-test')
+ print('='*79)
+ print(output.rstrip())
+ print('='*79)
+
+ def test_perf_in_8_threads(self):
+ perf_cffi = self.prepare_module('perf')
+ self.compile('perf-test', [perf_cffi], opt=True, threads=True,
+ defines={'PTEST_USE_THREAD': '8'})
+ output = self.execute('perf-test')
+ print('='*79)
+ print(output.rstrip())
+ print('='*79)
diff --git a/testing/embedding/test_recursive.py b/testing/embedding/test_recursive.py
new file mode 100644
index 0000000..b85e7ed
--- /dev/null
+++ b/testing/embedding/test_recursive.py
@@ -0,0 +1,15 @@
+from testing.embedding.test_basic import EmbeddingTests
+
+
+class TestRecursive(EmbeddingTests):
+ def test_recursive(self):
+ add_recursive_cffi = self.prepare_module('add_recursive')
+ self.compile('add_recursive-test', [add_recursive_cffi])
+ output = self.execute('add_recursive-test')
+ assert output == ("preparing REC\n"
+ "some_callback(400)\n"
+ "adding 400 and 9\n"
+ "<<< 409 >>>\n"
+ "adding 40 and 2\n"
+ "adding 100 and -5\n"
+ "got: 42 95\n")
diff --git a/testing/embedding/test_thread.py b/testing/embedding/test_thread.py
new file mode 100644
index 0000000..1895076
--- /dev/null
+++ b/testing/embedding/test_thread.py
@@ -0,0 +1,61 @@
+from testing.embedding.test_basic import EmbeddingTests
+
+
+class TestThread(EmbeddingTests):
+ def test_first_calls_in_parallel(self):
+ add1_cffi = self.prepare_module('add1')
+ self.compile('thread1-test', [add1_cffi], threads=True)
+ for i in range(20):
+ output = self.execute('thread1-test')
+ assert output == ("starting\n"
+ "preparing...\n" +
+ "adding 40 and 2\n" * 10 +
+ "done\n")
+
+ def _take_out(self, text, content):
+ assert content in text
+ i = text.index(content)
+ return text[:i] + text[i+len(content):]
+
+ def test_init_different_modules_in_different_threads(self):
+ add1_cffi = self.prepare_module('add1')
+ add2_cffi = self.prepare_module('add2')
+ self.compile('thread2-test', [add1_cffi, add2_cffi], threads=True)
+ output = self.execute('thread2-test')
+ output = self._take_out(output, "preparing")
+ output = self._take_out(output, ".")
+ output = self._take_out(output, ".")
+ # at least the 3rd dot should be after everything from ADD2
+ assert output == ("starting\n"
+ "prepADD2\n"
+ "adding 1000 and 200 and 30\n"
+ ".\n"
+ "adding 40 and 2\n"
+ "done\n")
+
+ def test_alt_issue(self):
+ add1_cffi = self.prepare_module('add1')
+ add2_cffi = self.prepare_module('add2')
+ self.compile('thread2-test', [add1_cffi, add2_cffi],
+ threads=True, defines={'T2TEST_AGAIN_ADD1': '1'})
+ output = self.execute('thread2-test')
+ output = self._take_out(output, "adding 40 and 2\n")
+ assert output == ("starting\n"
+ "preparing...\n"
+ "adding -1 and -1\n"
+ "prepADD2\n"
+ "adding 1000 and 200 and 30\n"
+ "done\n")
+
+ def test_load_in_parallel_more(self):
+ add2_cffi = self.prepare_module('add2')
+ add3_cffi = self.prepare_module('add3')
+ self.compile('thread3-test', [add2_cffi, add3_cffi], threads=True)
+ for i in range(150):
+ output = self.execute('thread3-test')
+ for j in range(10):
+ output = self._take_out(output, "adding 40 and 2 and 100\n")
+ output = self._take_out(output, "adding 1000, 200, 30, 4\n")
+ assert output == ("starting\n"
+ "prepADD2\n"
+ "done\n")
diff --git a/testing/embedding/test_tlocal.py b/testing/embedding/test_tlocal.py
new file mode 100644
index 0000000..6e7c5af
--- /dev/null
+++ b/testing/embedding/test_tlocal.py
@@ -0,0 +1,10 @@
+from testing.embedding.test_basic import EmbeddingTests
+
+
+class TestThreadLocal(EmbeddingTests):
+ def test_thread_local(self):
+ tlocal_cffi = self.prepare_module('tlocal')
+ self.compile('tlocal-test', [tlocal_cffi], threads=True)
+ for i in range(10):
+ output = self.execute('tlocal-test')
+ assert output == "done\n"
diff --git a/testing/embedding/thread-test.h b/testing/embedding/thread-test.h
new file mode 100644
index 0000000..f66cf70
--- /dev/null
+++ b/testing/embedding/thread-test.h
@@ -0,0 +1,96 @@
+/************************************************************/
+#ifndef _MSC_VER
+/************************************************************/
+
+
+#include <pthread.h>
+
+/* don't include <semaphore.h>, it is not available on OS/X */
+
+typedef struct {
+ pthread_mutex_t mutex1;
+ pthread_cond_t cond1;
+ unsigned int value;
+} sem_t;
+
+static int sem_init(sem_t *sem, int pshared, unsigned int value)
+{
+ assert(pshared == 0);
+ sem->value = value;
+ return (pthread_mutex_init(&sem->mutex1, NULL) ||
+ pthread_cond_init(&sem->cond1, NULL));
+}
+
+static int sem_post(sem_t *sem)
+{
+ pthread_mutex_lock(&sem->mutex1);
+ sem->value += 1;
+ pthread_cond_signal(&sem->cond1);
+ pthread_mutex_unlock(&sem->mutex1);
+ return 0;
+}
+
+static int sem_wait(sem_t *sem)
+{
+ pthread_mutex_lock(&sem->mutex1);
+ while (sem->value == 0)
+ pthread_cond_wait(&sem->cond1, &sem->mutex1);
+ sem->value -= 1;
+ pthread_mutex_unlock(&sem->mutex1);
+ return 0;
+}
+
+
+/************************************************************/
+#else
+/************************************************************/
+
+
+/* Very quick and dirty, just what I need for these tests.
+ Don't use directly in any real code!
+*/
+
+#include <Windows.h>
+#include <assert.h>
+
+typedef HANDLE sem_t;
+typedef HANDLE pthread_t;
+
+static int sem_init(sem_t *sem, int pshared, unsigned int value)
+{
+ assert(pshared == 0);
+ assert(value == 0);
+ *sem = CreateSemaphore(NULL, 0, 999, NULL);
+ return *sem ? 0 : -1;
+}
+
+static int sem_post(sem_t *sem)
+{
+ return ReleaseSemaphore(*sem, 1, NULL) ? 0 : -1;
+}
+
+static int sem_wait(sem_t *sem)
+{
+ WaitForSingleObject(*sem, INFINITE);
+ return 0;
+}
+
+static DWORD WINAPI myThreadProc(LPVOID lpParameter)
+{
+ void *(* start_routine)(void *) = (void *(*)(void *))lpParameter;
+ start_routine(NULL);
+ return 0;
+}
+
+static int pthread_create(pthread_t *thread, void *attr,
+ void *start_routine(void *), void *arg)
+{
+ assert(arg == NULL);
+ *thread = CreateThread(NULL, 0, myThreadProc, start_routine, 0, NULL);
+ return *thread ? 0 : -1;
+}
+
+
+/************************************************************/
+#endif
+/************************************************************/
diff --git a/testing/embedding/thread1-test.c b/testing/embedding/thread1-test.c
new file mode 100644
index 0000000..70bb861
--- /dev/null
+++ b/testing/embedding/thread1-test.c
@@ -0,0 +1,43 @@
+#include <stdio.h>
+#include <assert.h>
+#include "thread-test.h"
+
+#define NTHREADS 10
+
+
+extern int add1(int, int);
+
+static sem_t done;
+
+
+static void *start_routine(void *arg)
+{
+ int x, status;
+ x = add1(40, 2);
+ assert(x == 42);
+
+ status = sem_post(&done);
+ assert(status == 0);
+
+ return arg;
+}
+
+int main(void)
+{
+ pthread_t th;
+ int i, status = sem_init(&done, 0, 0);
+ assert(status == 0);
+
+ printf("starting\n");
+ fflush(stdout);
+ for (i = 0; i < NTHREADS; i++) {
+ status = pthread_create(&th, NULL, start_routine, NULL);
+ assert(status == 0);
+ }
+ for (i = 0; i < NTHREADS; i++) {
+ status = sem_wait(&done);
+ assert(status == 0);
+ }
+ printf("done\n");
+ return 0;
+}
diff --git a/testing/embedding/thread2-test.c b/testing/embedding/thread2-test.c
new file mode 100644
index 0000000..62f5ec8
--- /dev/null
+++ b/testing/embedding/thread2-test.c
@@ -0,0 +1,57 @@
+#include <stdio.h>
+#include <assert.h>
+#include "thread-test.h"
+
+extern int add1(int, int);
+extern int add2(int, int, int);
+
+static sem_t done;
+
+
+static void *start_routine_1(void *arg)
+{
+ int x, status;
+ x = add1(40, 2);
+ assert(x == 42);
+
+ status = sem_post(&done);
+ assert(status == 0);
+
+ return arg;
+}
+
+static void *start_routine_2(void *arg)
+{
+ int x, status;
+#ifdef T2TEST_AGAIN_ADD1
+ add1(-1, -1);
+#endif
+ x = add2(1000, 200, 30);
+ assert(x == 1230);
+
+ status = sem_post(&done);
+ assert(status == 0);
+
+ return arg;
+}
+
+int main(void)
+{
+ pthread_t th;
+ int i, status = sem_init(&done, 0, 0);
+ assert(status == 0);
+
+ printf("starting\n");
+ fflush(stdout);
+ status = pthread_create(&th, NULL, start_routine_1, NULL);
+ assert(status == 0);
+ status = pthread_create(&th, NULL, start_routine_2, NULL);
+ assert(status == 0);
+
+ for (i = 0; i < 2; i++) {
+ status = sem_wait(&done);
+ assert(status == 0);
+ }
+ printf("done\n");
+ return 0;
+}
diff --git a/testing/embedding/thread3-test.c b/testing/embedding/thread3-test.c
new file mode 100644
index 0000000..69ada27
--- /dev/null
+++ b/testing/embedding/thread3-test.c
@@ -0,0 +1,56 @@
+#include <stdio.h>
+#include <assert.h>
+#include "thread-test.h"
+
+extern int add2(int, int, int);
+extern int add3(int, int, int, int);
+
+static sem_t done;
+
+
+static void *start_routine_2(void *arg)
+{
+ int x, status;
+ x = add2(40, 2, 100);
+ assert(x == 142);
+
+ status = sem_post(&done);
+ assert(status == 0);
+
+ return arg;
+}
+
+static void *start_routine_3(void *arg)
+{
+ int x, status;
+ x = add3(1000, 200, 30, 4);
+ assert(x == 1234);
+
+ status = sem_post(&done);
+ assert(status == 0);
+
+ return arg;
+}
+
+int main(void)
+{
+ pthread_t th;
+ int i, status = sem_init(&done, 0, 0);
+ assert(status == 0);
+
+ printf("starting\n");
+ fflush(stdout);
+ for (i = 0; i < 10; i++) {
+ status = pthread_create(&th, NULL, start_routine_2, NULL);
+ assert(status == 0);
+ status = pthread_create(&th, NULL, start_routine_3, NULL);
+ assert(status == 0);
+ }
+ for (i = 0; i < 20; i++) {
+ status = sem_wait(&done);
+ assert(status == 0);
+ }
+ printf("done\n");
+ fflush(stdout); /* this is occasionally needed on Windows */
+ return 0;
+}
diff --git a/testing/embedding/tlocal-test.c b/testing/embedding/tlocal-test.c
new file mode 100644
index 0000000..b78a03d
--- /dev/null
+++ b/testing/embedding/tlocal-test.c
@@ -0,0 +1,47 @@
+#include <stdio.h>
+#include <assert.h>
+#include "thread-test.h"
+
+#define NTHREADS 10
+
+
+extern int add1(int, int);
+
+static sem_t done;
+
+
+static void *start_routine(void *arg)
+{
+ int i, x, expected, status;
+
+ expected = add1(40, 2);
+ assert((expected % 1000) == 42);
+
+ for (i=0; i<10; i++) {
+ x = add1(50, i);
+ assert(x == expected + 8 + i);
+ }
+
+ status = sem_post(&done);
+ assert(status == 0);
+
+ return arg;
+}
+
+int main(void)
+{
+ pthread_t th;
+ int i, status = sem_init(&done, 0, 0);
+ assert(status == 0);
+
+ for (i = 0; i < NTHREADS; i++) {
+ status = pthread_create(&th, NULL, start_routine, NULL);
+ assert(status == 0);
+ }
+ for (i = 0; i < NTHREADS; i++) {
+ status = sem_wait(&done);
+ assert(status == 0);
+ }
+ printf("done\n");
+ return 0;
+}
diff --git a/testing/embedding/tlocal.py b/testing/embedding/tlocal.py
new file mode 100644
index 0000000..7800dff
--- /dev/null
+++ b/testing/embedding/tlocal.py
@@ -0,0 +1,33 @@
+import cffi
+
+ffi = cffi.FFI()
+
+ffi.embedding_api("""
+ int add1(int, int);
+""")
+
+ffi.embedding_init_code(r"""
+ from _tlocal_cffi import ffi
+ import itertools
+ try:
+ import thread
+ g_seen = itertools.count().next
+ except ImportError:
+ import _thread as thread # py3
+ g_seen = itertools.count().__next__
+ tloc = thread._local()
+
+ @ffi.def_extern()
+ def add1(x, y):
+ try:
+ num = tloc.num
+ except AttributeError:
+ num = tloc.num = g_seen() * 1000
+ return x + y + num
+""")
+
+ffi.set_source("_tlocal_cffi", """
+""")
+
+fn = ffi.compile(verbose=True)
+print('FILENAME: %s' % (fn,))
diff --git a/testing/support.py b/testing/support.py
new file mode 100644
index 0000000..65f010c
--- /dev/null
+++ b/testing/support.py
@@ -0,0 +1,88 @@
+import sys
+
+if sys.version_info < (3,):
+ __all__ = ['u']
+
+ class U(object):
+ def __add__(self, other):
+ return eval('u'+repr(other).replace(r'\\u', r'\u')
+ .replace(r'\\U', r'\U'))
+ u = U()
+ long = long # for further "from testing.support import long"
+ assert u+'a\x00b' == eval(r"u'a\x00b'")
+ assert u+'a\u1234b' == eval(r"u'a\u1234b'")
+ assert u+'a\U00012345b' == eval(r"u'a\U00012345b'")
+
+else:
+ __all__ = ['u', 'unicode', 'long']
+ u = ""
+ unicode = str
+ long = int
+
+
+class StdErrCapture(object):
+ """Capture writes to sys.stderr (not to the underlying file descriptor)."""
+ def __enter__(self):
+ try:
+ from StringIO import StringIO
+ except ImportError:
+ from io import StringIO
+ self.old_stderr = sys.stderr
+ sys.stderr = f = StringIO()
+ return f
+ def __exit__(self, *args):
+ sys.stderr = self.old_stderr
+
+
+class FdWriteCapture(object):
+ """xxx limited to capture at most 512 bytes of output, according
+ to the Posix manual."""
+
+ def __init__(self, capture_fd=2): # stderr by default
+ if sys.platform == 'win32':
+ import py
+ py.test.skip("seems not to work, too bad")
+ self.capture_fd = capture_fd
+
+ def __enter__(self):
+ import os
+ self.read_fd, self.write_fd = os.pipe()
+ self.copy_fd = os.dup(self.capture_fd)
+ os.dup2(self.write_fd, self.capture_fd)
+ return self
+
+ def __exit__(self, *args):
+ import os
+ os.dup2(self.copy_fd, self.capture_fd)
+ os.close(self.copy_fd)
+ os.close(self.write_fd)
+ self._value = os.read(self.read_fd, 512)
+ os.close(self.read_fd)
+
+ def getvalue(self):
+ return self._value
+
+def _verify(ffi, module_name, preamble, *args, **kwds):
+ import imp
+ from cffi.recompiler import recompile
+ from .udir import udir
+ assert module_name not in sys.modules, "module name conflict: %r" % (
+ module_name,)
+ kwds.setdefault('tmpdir', str(udir))
+ outputfilename = recompile(ffi, module_name, preamble, *args, **kwds)
+ module = imp.load_dynamic(module_name, outputfilename)
+ #
+ # hack hack hack: copy all *bound methods* from module.ffi back to the
+ # ffi instance. Then calls like ffi.new() will invoke module.ffi.new().
+ for name in dir(module.ffi):
+ if not name.startswith('_'):
+ attr = getattr(module.ffi, name)
+ if attr is not getattr(ffi, name, object()):
+ setattr(ffi, name, attr)
+ def typeof_disabled(*args, **kwds):
+ raise NotImplementedError
+ ffi._typeof = typeof_disabled
+ for name in dir(ffi):
+ if not name.startswith('_') and not hasattr(module.ffi, name):
+ setattr(ffi, name, NotImplemented)
+ return module.lib
diff --git a/testing/udir.py b/testing/udir.py
new file mode 100644
index 0000000..4dd0a11
--- /dev/null
+++ b/testing/udir.py
@@ -0,0 +1,13 @@
+import py
+import sys
+
+udir = py.path.local.make_numbered_dir(prefix = 'ffi-')
+
+
+# Windows-only workaround for some configurations: see
+# https://bugs.python.org/issue23246 (Python 2.7.9)
+if sys.platform == 'win32':
+ try:
+ import setuptools
+ except ImportError:
+ pass