diff options
author | Armin Rigo <arigo@tunes.org> | 2015-05-12 11:07:25 +0200 |
---|---|---|
committer | Armin Rigo <arigo@tunes.org> | 2015-05-12 11:07:25 +0200 |
commit | fe4bb73d2191ea7b2ee5586848e1c5bbbcbaa72b (patch) | |
tree | 5c80d5c2ee50bc8e99039c7483522e2febc08cb7 /testing/cffi0 | |
parent | 34dbd9932de50a5de29f0fdaab9e9a06526d93a7 (diff) | |
download | cffi-fe4bb73d2191ea7b2ee5586848e1c5bbbcbaa72b.tar.gz |
the big Moving Files Around step
Diffstat (limited to 'testing/cffi0')
33 files changed, 6230 insertions, 0 deletions
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..d3b5ca1 --- /dev/null +++ b/testing/cffi0/backend_tests.py @@ -0,0 +1,1705 @@ +import py +import platform +import sys, ctypes +from cffi import FFI, CDefError, FFIError +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) + + +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 # no __eq__(int) + assert bool(p) is True + 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) # unlikely + 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 bool(ffi.cast("char", 0)) + 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 bool(ffi.cast("wchar_t", 0)) + 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 sys.maxunicode == 0xffff + 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 platform.machine() == 'aarch64': # 4 bytes, unsigned + assert int(p) == 0xffffffff + else: # 4 bytes, signed + assert int(p) == -1 + 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_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_no_integer_comparison(self): + ffi = FFI(backend=self.Backend()) + x = ffi.cast("int", 123) + y = ffi.cast("int", 456) + py.test.raises(TypeError, "x < y") + # + z = ffi.cast("double", 78.9) + py.test.raises(TypeError, "x < z") + py.test.raises(TypeError, "z < y") + + 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)) + 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_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*") + 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_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 + + 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 *", {'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): + 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) + import gc; gc.collect() + assert seen == [] + del q + import gc; gc.collect(); gc.collect(); gc.collect() + assert seen == [1] + + 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) + 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_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}; + """) + 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 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..116d0b3 --- /dev/null +++ b/testing/cffi0/test_cdata.py @@ -0,0 +1,39 @@ +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" + + +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..1b88473 --- /dev/null +++ b/testing/cffi0/test_ctypes.py @@ -0,0 +1,40 @@ +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_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..72bc650 --- /dev/null +++ b/testing/cffi0/test_ffi_backend.py @@ -0,0 +1,278 @@ +import py, sys, platform +import pytest +from testing.cffi0 import backend_tests, test_function, test_ownlib +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) + + +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)[:] + t = lib.try_with_value(fnames.index(name), value) + raw2 = ffi.buffer(t, len(raw1))[:] + assert raw1 == raw2 + + 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 *") # no length available + assert p.a[0] == 200 + assert p.a[1] == 300 + assert p.a[2] == 400 + + @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[]") + ffi.cast("unsigned short *", c)[1] += 500 + assert list(a) == [10000, 20500, 30000] + + 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", + "_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 diff --git a/testing/cffi0/test_function.py b/testing/cffi0/test_function.py new file mode 100644 index 0000000..99bf63f --- /dev/null +++ b/testing/cffi0/test_function.py @@ -0,0 +1,429 @@ +import py +from cffi import FFI +import math, os, sys +import ctypes.util +from cffi.backend_ctypes import CTypesBackend +from testing.udir import udir + +try: + from StringIO import StringIO +except ImportError: + from io import StringIO + + +class FdWriteCapture(object): + """xxx limited to capture at most 512 bytes of output, according + to the Posix manual.""" + + def __init__(self, capture_fd): + self.capture_fd = capture_fd + + def __enter__(self): + 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): + 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 + +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_sin_no_return_value(self): + # check that 'void'-returning functions work too + ffi = FFI(backend=self.Backend()) + ffi.cdef(""" + void sin(double x); + """) + m = ffi.dlopen(lib_m) + x = m.sin(1.23) + 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; + """) + ffi.C = ffi.dlopen(None) + ffi.C.fputs # fetch before capturing, for easier debugging + with FdWriteCapture(2) 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; + """) + ffi.C = ffi.dlopen(None) + ffi.C.fputs # fetch before capturing, for easier debugging + with FdWriteCapture(2) 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; + """) + ffi.C = ffi.dlopen(None) + with FdWriteCapture(2) 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, ...); + """) + 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 *); + """) + 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; + """) + 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(2) 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[]); + """) + 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; + """) + 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); + """) + 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); + """) + 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 *);") + 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;""") + 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 *);""") + 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 *);""") + 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 *);""") + 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 diff --git a/testing/cffi0/test_model.py b/testing/cffi0/test_model.py new file mode 100644 index 0000000..5e58c6e --- /dev/null +++ b/testing/cffi0/test_model.py @@ -0,0 +1,106 @@ +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_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..b56fb95 --- /dev/null +++ b/testing/cffi0/test_ownlib.py @@ -0,0 +1,284 @@ +import py, sys +import subprocess, weakref +from cffi import FFI +from cffi.backend_ctypes import CTypesBackend + + +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}; +""" + +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': + import os + # did we already build it? + if os.path.exists(str(udir.join('testownlib.dll'))): + cls.module = str(udir.join('testownlib.dll')) + 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) + cls.module = str(udir.join('testownlib.dll')) + else: + subprocess.check_call( + 'gcc testownlib.c -shared -fPIC -o testownlib.so', + cwd=str(udir), shell=True) + cls.module = str(udir.join('testownlib.so')) + + 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 diff --git a/testing/cffi0/test_parsing.py b/testing/cffi0/test_parsing.py new file mode 100644 index 0000000..adcf468 --- /dev/null +++ b/testing/cffi0/test_parsing.py @@ -0,0 +1,306 @@ +import py, sys, re +from cffi import FFI, FFIError, CDefError, VerificationError + +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" + +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]);") + 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, ...);") + 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); + """) + 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); + """) + 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[]); + """) + 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_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()) + 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) == \ + "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 re.match(r'cannot parse "x y z"\n:\d+:', str(e.value)) + +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 + +def test_bool(): + ffi = FFI() + ffi.cdef("void f(bool);") + # + ffi = FFI() + ffi.cdef("typedef _Bool bool; void f(bool);") + +def test_win_common_types(): + from cffi.commontypes import COMMON_TYPES, _CACHE + from cffi.commontypes import win_common_types, resolve_common_type + # + def clear_all(extra={}, old_dict=COMMON_TYPES.copy()): + COMMON_TYPES.clear() + COMMON_TYPES.update(old_dict) + COMMON_TYPES.update(extra) + _CACHE.clear() + # + for maxsize in [2**32-1, 2**64-1]: + ct = win_common_types(maxsize) + clear_all(ct) + for key in sorted(ct): + if ct[key] != 'set-unicode-needed': + resolve_common_type(key) + # assert did not crash + # now try to use e.g. WPARAM (-> UINT_PTR -> unsigned 32/64-bit) + for maxsize in [2**32-1, 2**64-1]: + ct = win_common_types(maxsize) + clear_all(ct) + ffi = FFI() + value = int(ffi.cast("WPARAM", -1)) + assert value == maxsize + # + clear_all() + +def test_WPARAM_on_windows(): + if sys.platform != 'win32': + py.test.skip("Only for Windows") + ffi = FFI() + ffi.cdef("void f(WPARAM);") + +def test__is_constant_globalvar(): + from cffi.cparser import Parser, _get_parser + 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), + ]: + p = Parser() + ast = _get_parser().parse(input) + decl = ast.children()[0][1] + node = decl.type + assert p._is_constant_globalvar(node) == expected_output + +def test_enum(): + ffi = FFI() + ffi.cdef(""" + enum Enum { POS = +1, TWO = 2, NIL = 0, NEG = -1}; + """) + C = ffi.dlopen(None) + assert C.POS == 1 + assert C.TWO == 2 + assert C.NIL == 0 + assert C.NEG == -1 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..729c095 --- /dev/null +++ b/testing/cffi0/test_verify.py @@ -0,0 +1,2212 @@ +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 == 'win32': + py.test.skip("needs GCC or Clang") + 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('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() + I = tp.is_integer_type() + assert C == (typename in ('char', 'wchar_t')) + assert F == (typename in ('float', 'double', 'long double')) + assert I + F + C == 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 + 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(): + 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 *') # because no length + 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(s[0]) == 1 * ffi.sizeof('int') # the same in C + assert s.a[3] == 0 + s = ffi.new("struct foo_s *", [424242, [-40, -30, -20, -10]]) + assert ffi.sizeof(s[0]) == 1 * 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]) == 2 * ffi.sizeof('int') + assert s.a[3] == 0 + s = ffi.new("struct foo_s *", [424242, [-40, -30, -20, -10]]) + assert ffi.sizeof(s[0]) == 2 * 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_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)') + 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_callback_calling_convention(): + py.test.skip("later") + if sys.platform != 'win32': + py.test.skip("Windows only") + ffi = FFI() + ffi.cdef(""" + int call1(int(*__cdecl cb)(int)); + int call2(int(*__stdcall cb)(int)); + """) + lib = ffi.verify(""" + int call1(int(*__cdecl cb)(int)) { + return cb(42) + 1; + } + int call2(int(*__stdcall cb)(int)) { + return cb(-42) - 6; + } + """) + xxx + +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);") + lib = ffi.verify(""" + struct foo_s { _Bool x; }; + int foo(int arg) { + return !arg; + } + """) + p = ffi.new("struct foo_s *") + p.x = 1 + assert p.x == 1 + py.test.raises(OverflowError, "p.x = -1") + py.test.raises(TypeError, "p.x = 0.0") + assert lib.foo(1) == 0 + assert lib.foo(0) == 1 + 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 myprintf(const char *, int); FILE *myfile;") + lib = ffi.verify(""" + #include <stdio.h> + FILE *myfile; + int myprintf(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.myprintf(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' (size 4) not supported as argument") + e = py.test.raises(NotImplementedError, bazptr) + assert str(e.value) == ( + "ctype 'Data' (size 4) not supported as return value") + e = py.test.raises(NotImplementedError, barptr) + assert str(e.value) == ( + "ctype 'MyStr' not supported as return value " + "(it is a struct with bit fields)") + +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") 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..a36db5e --- /dev/null +++ b/testing/cffi0/test_version.py @@ -0,0 +1,55 @@ +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(__file__)) + p = os.path.join(parent, 'doc', 'source', 'conf.py') + content = open(p).read() + # + v = cffi.__version__ + assert ("version = '%s'\n" % v[:3]) in content + assert ("release = '%s'\n" % v) in content + +def test_doc_version_file(): + parent = os.path.dirname(os.path.dirname(__file__)) + v = cffi.__version__.replace('+', '') + p = os.path.join(parent, 'doc', 'source', 'index.rst') + content = open(p).read() + assert ("cffi/cffi-%s.tar.gz" % v) in content + +def test_setup_version(): + parent = os.path.dirname(os.path.dirname(__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(__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) 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..b313ae7 --- /dev/null +++ b/testing/cffi0/test_zdistutils.py @@ -0,0 +1,287 @@ +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) + + 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..25eec0b --- /dev/null +++ b/testing/cffi0/test_zintegration.py @@ -0,0 +1,150 @@ +import py, os, sys, shutil +import imp +import subprocess +from testing.udir import udir + +if sys.platform == 'win32': + py.test.skip('snippets do not run on win32') + +def create_venv(name): + tmpdir = udir.join(name) + try: + subprocess.check_call(['virtualenv', '--distribute', + '-p', os.path.abspath(sys.executable), + str(tmpdir)]) + except OSError as e: + py.test.skip("Cannot execute virtualenv: %s" % (e,)) + + try: + deepcopy = os.symlink + except: + import shutil, errno + def deepcopy(src, dst): + try: + shutil.copytree(src, dst) + except OSError as e: + if e.errno in (errno.ENOTDIR, errno.EINVAL): + shutil.copy(src, dst) + else: + print('got errno') + print(e.errno) + print('not') + print(errno.ENOTDIR) + raise + + site_packages = None + for dirpath, dirnames, filenames in os.walk(str(tmpdir)): + if os.path.basename(dirpath) == 'site-packages': + site_packages = dirpath + break + 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 + for module in modules: + target = imp.find_module(module)[1] + deepcopy(target, os.path.join(site_packages, + os.path.basename(target))) + return tmpdir + +SNIPPET_DIR = py.path.local(__file__).join('..', 'snippets') + +def really_run_setup_and_program(dirname, venv_dir, python_snippet): + 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')) + subprocess.check_call((vp, 'setup.py', 'clean')) + subprocess.check_call((vp, 'setup.py', 'install')) + subprocess.check_call((vp, str(python_f))) + 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) + + 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" + ''') |