diff options
Diffstat (limited to 'testing/cffi0')
-rw-r--r-- | testing/cffi0/backend_tests.py | 119 | ||||
-rw-r--r-- | testing/cffi0/test_ffi_backend.py | 60 | ||||
-rw-r--r-- | testing/cffi0/test_function.py | 58 | ||||
-rw-r--r-- | testing/cffi0/test_ownlib.py | 66 | ||||
-rw-r--r-- | testing/cffi0/test_parsing.py | 152 | ||||
-rw-r--r-- | testing/cffi0/test_verify.py | 130 | ||||
-rw-r--r-- | testing/cffi0/test_version.py | 8 | ||||
-rw-r--r-- | testing/cffi0/test_zdistutils.py | 2 | ||||
-rw-r--r-- | testing/cffi0/test_zintegration.py | 11 |
9 files changed, 482 insertions, 124 deletions
diff --git a/testing/cffi0/backend_tests.py b/testing/cffi0/backend_tests.py index 13a4c78..ab013a1 100644 --- a/testing/cffi0/backend_tests.py +++ b/testing/cffi0/backend_tests.py @@ -1,6 +1,7 @@ import py +import pytest import platform -import sys, ctypes +import sys, ctypes, ctypes.util from cffi import FFI, CDefError, FFIError, VerificationMissing from testing.support import * @@ -11,8 +12,8 @@ SIZE_OF_PTR = ctypes.sizeof(ctypes.c_void_p) SIZE_OF_WCHAR = ctypes.sizeof(ctypes.c_wchar) def needs_dlopen_none(): - if sys.platform == 'win32' and sys.version_info >= (3,): - py.test.skip("dlopen(None) cannot work on Windows for Python 3") + if sys.platform == 'win32' and not ctypes.util.find_library('c'): + py.test.skip("dlopen(None) cannot work on Windows with this runtime") class BackendTests: @@ -112,10 +113,14 @@ class BackendTests: 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") + with pytest.raises(IndexError): + p[10] + with pytest.raises(IndexError): + p[10] = 44 + with pytest.raises(IndexError): + p[-1] + with pytest.raises(IndexError): + p[-1] = 44 def test_new_array_args(self): ffi = FFI(backend=self.Backend()) @@ -140,18 +145,21 @@ class BackendTests: 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]") + with pytest.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]") + with pytest.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]") + with pytest.raises(IndexError): + p[0] py.test.raises(ValueError, ffi.new, "int[]", -1) assert repr(p) == "<cdata 'int[]' owning 0 bytes>" @@ -259,7 +267,8 @@ class BackendTests: p[2][3] = 33 assert p[0][0] == 10 assert p[2][3] == 33 - py.test.raises(IndexError, "p[1][-1]") + with pytest.raises(IndexError): + p[1][-1] def test_constructor_array_of_array(self): ffi = FFI(backend=self.Backend()) @@ -386,7 +395,8 @@ class BackendTests: n = ffi.new("int*", 99) p = ffi.new("int*[]", [n]) assert p[0][0] == 99 - py.test.raises(TypeError, "p[0] = None") + with pytest.raises(TypeError): + p[0] = None p[0] = ffi.NULL assert p[0] == ffi.NULL @@ -422,13 +432,15 @@ class BackendTests: assert s.a == s.b == s.c == 0 s.b = -23 assert s.b == -23 - py.test.raises(OverflowError, "s.b = 32768") + with pytest.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") + with pytest.raises((AttributeError, TypeError)): + del s.a assert repr(s) == "<cdata 'struct foo *' owning %d bytes>" % ( SIZE_OF_INT + 2 * SIZE_OF_SHORT) # @@ -450,8 +462,10 @@ class BackendTests: 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]") + with pytest.raises(OverflowError): + s[0].b = -32769 + with pytest.raises(IndexError): + s[1] def test_struct_opaque(self): ffi = FFI(backend=self.Backend()) @@ -511,11 +525,13 @@ class BackendTests: u.b = -23 assert u.b == -23 assert u.a != 0 - py.test.raises(OverflowError, "u.b = 32768") + with pytest.raises(OverflowError): + u.b = 32768 # u = ffi.new("union foo*", [-2]) assert u.a == -2 - py.test.raises((AttributeError, TypeError), "del u.a") + with pytest.raises((AttributeError, TypeError)): + del u.a assert repr(u) == "<cdata 'union foo *' owning %d bytes>" % SIZE_OF_INT def test_union_opaque(self): @@ -591,7 +607,8 @@ class BackendTests: 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'") + with pytest.raises(IndexError): + p[7] = b'X' # a = ffi.new("char[]", b"hello\x00world") assert len(a) == 12 @@ -615,7 +632,8 @@ class BackendTests: 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'") + with pytest.raises(IndexError): + p[7] = u+'X' # a = ffi.new("wchar_t[]", u+"hello\x00world") assert len(a) == 12 @@ -633,7 +651,8 @@ class BackendTests: 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") + with pytest.raises(TypeError): + s.name = None s.name = ffi.NULL assert s.name == ffi.NULL @@ -657,18 +676,21 @@ class BackendTests: a = ffi.new("int[]", [10, 11, 12]) p = ffi.new("void **", a) vp = p[0] - py.test.raises(TypeError, "vp[0]") + with pytest.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 + with pytest.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 + with pytest.raises(TypeError): + s.r = b # fails def test_functionptr_simple(self): ffi = FFI(backend=self.Backend()) @@ -687,7 +709,8 @@ class BackendTests: q = ffi.new("int(**)(int)", p) assert repr(q) == "<cdata 'int(* *)(int)' owning %d bytes>" % ( SIZE_OF_PTR) - py.test.raises(TypeError, "q(43)") + with pytest.raises(TypeError): + q(43) res = q[0](43) assert res == 44 q = ffi.cast("int(*)(int)", p) @@ -912,10 +935,14 @@ class BackendTests: 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'") + with pytest.raises(TypeError): + s.e = 'B' + with pytest.raises(TypeError): + s.e = '2' + with pytest.raises(TypeError): + s.e = '#2' + with pytest.raises(TypeError): + s.e = '#7' def test_enum_non_contiguous(self): ffi = FFI(backend=self.Backend()) @@ -950,11 +977,14 @@ class BackendTests: 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') + with pytest.raises(AttributeError): + s.b + with pytest.raises(AttributeError): + s.b = 412 s[0].b = 412 assert s[0].b == 412 - py.test.raises(IndexError, 's[1]') + with pytest.raises(IndexError): + s[1] def test_pointer_to_array(self): ffi = FFI(backend=self.Backend()) @@ -1011,17 +1041,23 @@ class BackendTests: 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") + with pytest.raises(OverflowError): + s.a = 512 + with pytest.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") + with pytest.raises(OverflowError): + s.a = -513 + with pytest.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") + with pytest.raises(OverflowError): + s.c = 4 + with pytest.raises(OverflowError): + s[0].c = 4 s.c = -4 assert s.c == -4 @@ -1205,7 +1241,7 @@ class BackendTests: 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() + assert f.read() == arraytostring(array.array('i', range(1000))) f.seek(0) b = ffi.new("int[]", 1005) f.readinto(ffi.buffer(b, 1000 * ffi.sizeof("int"))) @@ -1224,7 +1260,7 @@ class BackendTests: 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() + assert f.read() == arraytostring(array.array('i', range(1000))) f.seek(0) b = ffi.new("int[]", 1005) f.readinto(ffi.buffer(b, 1000 * ffi.sizeof("int"))) @@ -1279,7 +1315,8 @@ class BackendTests: 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]") + with pytest.raises(IndexError): + p.data[10] def test_ffi_typeof_getcname(self): ffi = FFI(backend=self.Backend()) diff --git a/testing/cffi0/test_ffi_backend.py b/testing/cffi0/test_ffi_backend.py index 12ecaee..8e29bc4 100644 --- a/testing/cffi0/test_ffi_backend.py +++ b/testing/cffi0/test_ffi_backend.py @@ -129,6 +129,36 @@ class TestFFI(backend_tests.BackendTests, alloc5 = ffi.new_allocator(myalloc5) py.test.raises(MemoryError, alloc5, "int[5]") + def test_new_struct_containing_struct_containing_array_varsize(self): + ffi = FFI(backend=self.Backend()) + ffi.cdef(""" + struct foo_s { int len[100]; short data[]; }; + struct bar_s { int abc[100]; struct foo_s tail; }; + """) + # loop to try to detect heap overwrites, if the size allocated + # is too small + for i in range(1, 501, 100): + p = ffi.new("struct bar_s *", [[10], [[20], [3,4,5,6,7,8,9] * i]]) + assert p.abc[0] == 10 + assert p.tail.len[0] == 20 + assert p.tail.data[0] == 3 + assert p.tail.data[6] == 9 + assert p.tail.data[7 * i - 1] == 9 + + def test_bogus_struct_containing_struct_containing_array_varsize(self): + ffi = FFI(backend=self.Backend()) + ffi.cdef(""" + struct foo_s { signed char len; signed char data[]; }; + struct bar_s { struct foo_s foo; int bcd; }; + """) + p = ffi.new("struct bar_s *", [[123, [45, 56, 67, 78]], 9999999]) + assert p.foo.len == 123 + assert p.foo.data[0] == 45 + assert p.foo.data[1] == 56 + assert p.foo.data[2] == 67 + assert p.bcd == 9999999 + assert p.foo.data[3] != 78 # has been overwritten with 9999999 + class TestBitfield: def check(self, source, expected_ofs_y, expected_align, expected_size): @@ -149,6 +179,7 @@ class TestBitfield: setters = ['case %d: s.%s = value; break;' % iname for iname in enumerate(fnames)] lib = ffi1.verify(""" + #include <string.h> struct s1 { %s }; struct sa { char a; struct s1 b; }; #define Gofs_y offsetof(struct s1, y) @@ -216,7 +247,10 @@ class TestBitfield: 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'))") + @pytest.mark.skipif( + "not (sys.platform == 'darwin' and platform.machine() == 'arm64')" + " and " + "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) @@ -230,6 +264,8 @@ class TestBitfield: self.check("char x; long long :57; char y;", L + 8, 1, L + 9) @pytest.mark.skipif( + "(sys.platform == 'darwin' and platform.machine() == 'arm64')" + " or " "not platform.machine().startswith(('arm', 'aarch64'))") def test_bitfield_anonymous_align_arm(self): L = FFI().alignof("long long") @@ -243,7 +279,10 @@ class TestBitfield: 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'))") + @pytest.mark.skipif( + "not (sys.platform == 'darwin' and platform.machine() == 'arm64')" + " and " + "platform.machine().startswith(('arm', 'aarch64'))") def test_bitfield_zero(self): L = FFI().alignof("long long") self.check("char y; int :0;", 0, 1, 4) @@ -255,6 +294,8 @@ class TestBitfield: self.check("int a:1; int :0; int b:1; char y;", 5, 4, 8) @pytest.mark.skipif( + "(sys.platform == 'darwin' and platform.machine() == 'arm64')" + " or " "not platform.machine().startswith(('arm', 'aarch64'))") def test_bitfield_zero_arm(self): L = FFI().alignof("long long") @@ -268,12 +309,15 @@ class TestBitfield: 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 *")') + ffi.cdef("struct s1 { float x:1; };") + with pytest.raises(TypeError): + ffi.new("struct s1 *") + ffi.cdef("struct s2 { char x:0; };") + with pytest.raises(TypeError): + ffi.new("struct s2 *") + ffi.cdef("struct s3 { char x:9; };") + with pytest.raises(TypeError): + ffi.new("struct s3 *") def test_struct_with_typedef(self): ffi = FFI() diff --git a/testing/cffi0/test_function.py b/testing/cffi0/test_function.py index ca2353f..b4bb23d 100644 --- a/testing/cffi0/test_function.py +++ b/testing/cffi0/test_function.py @@ -1,10 +1,11 @@ import py +import pytest from cffi import FFI, CDefError import math, os, sys import ctypes.util from cffi.backend_ctypes import CTypesBackend from testing.udir import udir -from testing.support import FdWriteCapture +from testing.support import FdWriteCapture, StdErrCapture from .backend_tests import needs_dlopen_none try: @@ -90,7 +91,8 @@ class TestFunction(object): """) m = ffi.dlopen(lib_m) assert m.FOOBAR == 42 - py.test.raises(NotImplementedError, "m.baz") + with pytest.raises(NotImplementedError): + m.baz def test_tlsalloc(self): if sys.platform != 'win32': @@ -111,7 +113,7 @@ class TestFunction(object): ffi = FFI(backend=self.Backend()) ffi.cdef(""" int fputs(const char *, void *); - void *stderr; + extern void *stderr; """) needs_dlopen_none() ffi.C = ffi.dlopen(None) @@ -128,7 +130,7 @@ class TestFunction(object): ffi = FFI(backend=self.Backend()) ffi.cdef(""" int fputs(char *, void *); - void *stderr; + extern void *stderr; """) needs_dlopen_none() ffi.C = ffi.dlopen(None) @@ -145,7 +147,7 @@ class TestFunction(object): ffi = FFI(backend=self.Backend()) ffi.cdef(""" int fprintf(void *, const char *format, ...); - void *stderr; + extern void *stderr; """) needs_dlopen_none() ffi.C = ffi.dlopen(None) @@ -207,7 +209,7 @@ class TestFunction(object): py.test.skip("probably no symbol 'stderr' in the lib") ffi.cdef(""" int fputs(const char *, void *); - void *stderr; + extern void *stderr; """) needs_dlopen_none() ffi.C = ffi.dlopen(None) @@ -225,19 +227,32 @@ class TestFunction(object): def cb(): return returnvalue fptr = ffi.callback("void(*)(void)", cb) - old_stderr = sys.stderr - try: - sys.stderr = StringIO() + with StdErrCapture() as f: returned = fptr() - printed = sys.stderr.getvalue() - finally: - sys.stderr = old_stderr + printed = f.getvalue() assert returned is None if returnvalue is None: assert printed == '' else: assert "None" in printed + def test_callback_returning_struct_three_bytes(self): + if self.Backend is CTypesBackend: + py.test.skip("not supported with the ctypes backend") + ffi = FFI(backend=self.Backend()) + ffi.cdef(""" + typedef struct { + unsigned char a, b, c; + } THREEBYTES; + """) + def cb(): + return (12, 34, 56) + fptr = ffi.callback("THREEBYTES(*)(void)", cb) + tb = fptr() + assert tb.a == 12 + assert tb.b == 34 + assert tb.c == 56 + def test_passing_array(self): ffi = FFI(backend=self.Backend()) ffi.cdef(""" @@ -254,7 +269,7 @@ class TestFunction(object): py.test.skip("probably no symbol 'stdout' in the lib") ffi = FFI(backend=self.Backend()) ffi.cdef(""" - void *stdout; + extern void *stdout; """) needs_dlopen_none() C = ffi.dlopen(None) @@ -494,7 +509,7 @@ class TestFunction(object): ffi.cdef(""" typedef enum { MYE1, MYE2 } myenum_t; double myfunc(double); - double myvar; + extern double myvar; const double myconst; #define MYFOO 42 """) @@ -505,7 +520,7 @@ class TestFunction(object): if self.Backend is CTypesBackend: py.test.skip("not with the ctypes backend") ffi = FFI(backend=self.Backend()) - ffi.cdef("int foobar(void); int foobaz;") + ffi.cdef("int foobar(void); extern int foobaz;") lib = ffi.dlopen(lib_m) ffi.dlclose(lib) e = py.test.raises(ValueError, getattr, lib, 'foobar') @@ -518,3 +533,16 @@ class TestFunction(object): assert str(e.value).startswith("library '") assert str(e.value).endswith("' has already been closed") ffi.dlclose(lib) # does not raise + + def test_passing_large_list(self): + if self.Backend is CTypesBackend: + py.test.skip("the ctypes backend doesn't support this") + ffi = FFI(backend=self.Backend()) + ffi.cdef(""" + void getenv(char *); + """) + needs_dlopen_none() + m = ffi.dlopen(None) + arg = [b"F", b"O", b"O"] + [b"\x00"] * 20000000 + x = m.getenv(arg) + assert x is None diff --git a/testing/cffi0/test_ownlib.py b/testing/cffi0/test_ownlib.py index a06df20..ffad879 100644 --- a/testing/cffi0/test_ownlib.py +++ b/testing/cffi0/test_ownlib.py @@ -35,6 +35,10 @@ typedef struct { long bottom; } RECT; +typedef struct { + unsigned char a, b, c; +} THREEBYTES; + EXPORT int PointInRect(RECT *prc, POINT pt) { @@ -107,6 +111,15 @@ EXPORT void modify_struct_value(RECT r) { r.left = r.right = r.top = r.bottom = 500; } + +EXPORT THREEBYTES return_three_bytes(void) +{ + THREEBYTES result; + result.a = 12; + result.b = 34; + result.c = 56; + return result; +} """ class TestOwnLib(object): @@ -201,7 +214,7 @@ class TestOwnLib(object): py.test.skip("fix the auto-generation of the tiny test lib") ffi = FFI(backend=self.Backend()) ffi.cdef(""" - int my_array[7]; + extern int my_array[7]; """) ownlib = ffi.dlopen(self.module) for i in range(7): @@ -223,7 +236,7 @@ class TestOwnLib(object): py.test.skip("not supported by the ctypes backend") ffi = FFI(backend=self.Backend()) ffi.cdef(""" - int my_array[]; + extern int my_array[]; """) ownlib = ffi.dlopen(self.module) for i in range(7): @@ -291,7 +304,7 @@ class TestOwnLib(object): long bottom; } RECT; - long left, top, right, bottom; + extern long left, top, right, bottom; RECT ReturnRect(int i, RECT ar, RECT* br, POINT cp, RECT dr, RECT *er, POINT fp, RECT gr); @@ -321,7 +334,7 @@ class TestOwnLib(object): if self.Backend is CTypesBackend: py.test.skip("not implemented with the ctypes backend") ffi = FFI(backend=self.Backend()) - ffi.cdef("long left; int test_getting_errno(void);") + ffi.cdef("extern long left; int test_getting_errno(void);") lib = ffi.dlopen(self.module) lib.left = 123456 p = ffi.addressof(lib, "left") @@ -371,3 +384,48 @@ class TestOwnLib(object): assert s.top == 22 assert s.right == 33 assert s.bottom == 44 + + def test_dlopen_handle(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("uses 'dl' explicitly") + if self.__class__.Backend is CTypesBackend: + py.test.skip("not for the ctypes backend") + backend = self.Backend() + ffi1 = FFI(backend=backend) + ffi1.cdef("""void *dlopen(const char *filename, int flags); + int dlclose(void *handle);""") + lib1 = ffi1.dlopen('dl') + handle = lib1.dlopen(self.module.encode(sys.getfilesystemencoding()), + backend.RTLD_LAZY) + assert ffi1.typeof(handle) == ffi1.typeof("void *") + assert handle + + ffi = FFI(backend=backend) + ffi.cdef("""unsigned short foo_2bytes(unsigned short a);""") + lib = ffi.dlopen(handle) + x = lib.foo_2bytes(1000) + assert x == 1042 + + err = lib1.dlclose(handle) + assert err == 0 + + def test_return_three_bytes(self): + if self.module is None: + py.test.skip("fix the auto-generation of the tiny test lib") + if self.__class__.Backend is CTypesBackend: + py.test.skip("not working on win32 on the ctypes backend") + ffi = FFI(backend=self.Backend()) + ffi.cdef(""" + typedef struct { + unsigned char a, b, c; + } THREEBYTES; + + THREEBYTES return_three_bytes(void); + """) + lib = ffi.dlopen(self.module) + tb = lib.return_three_bytes() + assert tb.a == 12 + assert tb.b == 34 + assert tb.c == 56 diff --git a/testing/cffi0/test_parsing.py b/testing/cffi0/test_parsing.py index 2d75850..a5e4587 100644 --- a/testing/cffi0/test_parsing.py +++ b/testing/cffi0/test_parsing.py @@ -174,7 +174,7 @@ def test_remove_line_continuation_comments(): double // blah \\ more comments x(void); - double // blah\\\\ + double // blah // blah\\\\ y(void); double // blah\\ \ etc @@ -185,6 +185,93 @@ def test_remove_line_continuation_comments(): m.y m.z +def test_dont_remove_comment_in_line_directives(): + ffi = FFI(backend=FakeBackend()) + e = py.test.raises(CDefError, ffi.cdef, """ + \t # \t line \t 8 \t "baz.c" \t + + some syntax error here + """) + assert str(e.value) == "parse error\nbaz.c:9:14: before: syntax" + # + e = py.test.raises(CDefError, ffi.cdef, """ + #line 7 "foo//bar.c" + + some syntax error here + """) + # + assert str(e.value) == "parse error\nfoo//bar.c:8:14: before: syntax" + ffi = FFI(backend=FakeBackend()) + e = py.test.raises(CDefError, ffi.cdef, """ + \t # \t 8 \t "baz.c" \t + + some syntax error here + """) + assert str(e.value) == "parse error\nbaz.c:9:14: before: syntax" + # + e = py.test.raises(CDefError, ffi.cdef, """ + # 7 "foo//bar.c" + + some syntax error here + """) + assert str(e.value) == "parse error\nfoo//bar.c:8:14: before: syntax" + +def test_multiple_line_directives(): + ffi = FFI(backend=FakeBackend()) + e = py.test.raises(CDefError, ffi.cdef, + """ #line 5 "foo.c" + extern int xx; + #line 6 "bar.c" + extern int yy; + #line 7 "baz.c" + some syntax error here + #line 8 "yadda.c" + extern int zz; + """) + assert str(e.value) == "parse error\nbaz.c:7:14: before: syntax" + # + e = py.test.raises(CDefError, ffi.cdef, + """ # 5 "foo.c" + extern int xx; + # 6 "bar.c" + extern int yy; + # 7 "baz.c" + some syntax error here + # 8 "yadda.c" + extern int zz; + """) + assert str(e.value) == "parse error\nbaz.c:7:14: before: syntax" + +def test_commented_line_directive(): + ffi = FFI(backend=FakeBackend()) + e = py.test.raises(CDefError, ffi.cdef, """ + /* + #line 5 "foo.c" + */ + void xx(void); + + #line 6 "bar.c" + /* + #line 35 "foo.c" + */ + some syntax error + """) + # + assert str(e.value) == "parse error\nbar.c:9:14: before: syntax" + e = py.test.raises(CDefError, ffi.cdef, """ + /* + # 5 "foo.c" + */ + void xx(void); + + # 6 "bar.c" + /* + # 35 "foo.c" + */ + some syntax error + """) + assert str(e.value) == "parse error\nbar.c:9:14: before: syntax" + def test_line_continuation_in_defines(): ffi = FFI(backend=FakeBackend()) ffi.cdef(""" @@ -324,6 +411,7 @@ def test_WPARAM_on_windows(): assert value == sys.maxsize * 2 - 40 def test__is_constant_globalvar(): + import warnings for input, expected_output in [ ("int a;", False), ("const int a;", True), @@ -341,10 +429,13 @@ def test__is_constant_globalvar(): ("const int a[5][6];", False), ]: ffi = FFI() - ffi.cdef(input) + with warnings.catch_warnings(record=True) as log: + warnings.simplefilter("always") + ffi.cdef(input) declarations = ffi._parser._declarations assert ('constant a' in declarations) == expected_output assert ('variable a' in declarations) == (not expected_output) + assert len(log) == (1 - expected_output) def test_restrict(): from cffi import model @@ -354,7 +445,7 @@ def test_restrict(): ("int *a;", False), ]: ffi = FFI() - ffi.cdef(input) + ffi.cdef("extern " + input) tp, quals = ffi._parser._declarations['variable a'] assert bool(quals & model.Q_RESTRICT) == expected_output @@ -409,7 +500,17 @@ def test_const_pointer_to_pointer(): def test_enum(): ffi = FFI() ffi.cdef(""" - enum Enum { POS = +1, TWO = 2, NIL = 0, NEG = -1, OP = (POS+TWO)-1}; + enum Enum { + POS = +1, + TWO = 2, + NIL = 0, + NEG = -1, + ADDSUB = (POS+TWO)-1, + DIVMULINT = (3 * 3) / 2, + SHIFT = (1 << 3) >> 1, + BINOPS = (0x7 & 0x1) | 0x8, + XOR = 0xf ^ 0xa + }; """) needs_dlopen_none() C = ffi.dlopen(None) @@ -417,7 +518,11 @@ def test_enum(): assert C.TWO == 2 assert C.NIL == 0 assert C.NEG == -1 - assert C.OP == 2 + assert C.ADDSUB == 2 + assert C.DIVMULINT == 4 + assert C.SHIFT == 4 + assert C.BINOPS == 0b1001 + assert C.XOR == 0b0101 def test_stdcall(): ffi = FFI() @@ -466,3 +571,40 @@ def test_error_invalid_syntax_for_cdef(): e = py.test.raises(CDefError, ffi.cdef, 'void foo(void) {}') assert str(e.value) == ('<cdef source string>:1: unexpected <FuncDef>: ' 'this construct is valid C but not valid in cdef()') + +def test_unsigned_int_suffix_for_constant(): + ffi = FFI() + ffi.cdef("""enum e { + bin_0=0b10, + bin_1=0b10u, + bin_2=0b10U, + bin_3=0b10l, + bin_4=0b10L, + bin_5=0b10ll, + bin_6=0b10LL, + oct_0=010, + oct_1=010u, + oct_2=010U, + oct_3=010l, + oct_4=010L, + oct_5=010ll, + oct_6=010LL, + dec_0=10, + dec_1=10u, + dec_2=10U, + dec_3=10l, + dec_4=10L, + dec_5=10ll, + dec_6=10LL, + hex_0=0x10, + hex_1=0x10u, + hex_2=0x10U, + hex_3=0x10l, + hex_4=0x10L, + hex_5=0x10ll, + hex_6=0x10LL,};""") + needs_dlopen_none() + C = ffi.dlopen(None) + for base, expected_result in (('bin', 2), ('oct', 8), ('dec', 10), ('hex', 16)): + for index in range(7): + assert getattr(C, '{base}_{index}'.format(base=base, index=index)) == expected_result diff --git a/testing/cffi0/test_verify.py b/testing/cffi0/test_verify.py index 79e1c6c..3a1c0b9 100644 --- a/testing/cffi0/test_verify.py +++ b/testing/cffi0/test_verify.py @@ -1,7 +1,9 @@ import py, re +import pytest import sys, os, math, weakref from cffi import FFI, VerificationError, VerificationMissing, model, FFIError from testing.support import * +from testing.support import extra_compile_args lib_m = ['m'] @@ -12,16 +14,6 @@ if sys.platform == 'win32': 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( @@ -284,7 +276,7 @@ def test_all_integer_and_float_types(): def test_var_signed_integer_types(): ffi = FFI() lst = all_signed_integer_types(ffi) - csource = "\n".join(["%s somevar_%s;" % (tp, tp.replace(' ', '_')) + csource = "\n".join(["static %s somevar_%s;" % (tp, tp.replace(' ', '_')) for tp in lst]) ffi.cdef(csource) lib = ffi.verify(csource) @@ -303,7 +295,7 @@ def test_var_signed_integer_types(): def test_var_unsigned_integer_types(): ffi = FFI() lst = all_unsigned_integer_types(ffi) - csource = "\n".join(["%s somevar_%s;" % (tp, tp.replace(' ', '_')) + csource = "\n".join(["static %s somevar_%s;" % (tp, tp.replace(' ', '_')) for tp in lst]) ffi.cdef(csource) lib = ffi.verify(csource) @@ -589,7 +581,8 @@ def test_struct_array_guess_length(): 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]') + with pytest.raises(IndexError): + s.a[17] def test_struct_array_c99_1(): if sys.platform == 'win32': @@ -647,7 +640,8 @@ def test_struct_with_bitfield_exact(): 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") + with pytest.raises(OverflowError): + s.b = 4 assert s.b == 3 def test_struct_with_bitfield_enum(): @@ -813,8 +807,8 @@ def test_define_int(): def test_access_variable(): ffi = FFI() - ffi.cdef("int foo(void);\n" - "int somenumber;") + ffi.cdef("static int foo(void);\n" + "static int somenumber;") lib = ffi.verify(""" static int somenumber = 2; static int foo(void) { @@ -831,7 +825,7 @@ def test_access_variable(): def test_access_address_of_variable(): # access the address of 'somenumber': need a trick ffi = FFI() - ffi.cdef("int somenumber; static int *const somenumberptr;") + ffi.cdef("static int somenumber; static int *const somenumberptr;") lib = ffi.verify(""" static int somenumber = 2; #define somenumberptr (&somenumber) @@ -844,7 +838,7 @@ def test_access_address_of_variable(): def test_access_array_variable(length=5): ffi = FFI() ffi.cdef("int foo(int);\n" - "int somenumber[%s];" % (length,)) + "static int somenumber[%s];" % (length,)) lib = ffi.verify(""" static int somenumber[] = {2, 2, 3, 4, 5}; static int foo(int i) { @@ -876,7 +870,7 @@ def test_access_struct_variable(): ffi = FFI() ffi.cdef("struct foo { int x; ...; };\n" "int foo(int);\n" - "struct foo stuff;") + "static struct foo stuff;") lib = ffi.verify(""" struct foo { int x, y, z; }; static struct foo stuff = {2, 5, 8}; @@ -900,9 +894,9 @@ def test_access_struct_variable(): def test_access_callback(): ffi = FFI() - ffi.cdef("int (*cb)(int);\n" - "int foo(int);\n" - "void reset_cb(void);") + ffi.cdef("static int (*cb)(int);\n" + "static int foo(int);\n" + "static void reset_cb(void);") lib = ffi.verify(""" static int g(int x) { return x * 7; } static int (*cb)(int); @@ -918,9 +912,9 @@ def test_access_callback(): 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);") + "static mycallback_t *cb;\n" + "static int foo(int);\n" + "static void reset_cb(void);") lib = ffi.verify(""" static int g(int x) { return x * 7; } static int (*cb)(int); @@ -1070,7 +1064,7 @@ def test_autofilled_struct_as_argument(): def test_autofilled_struct_as_argument_dynamic(): ffi = FFI() ffi.cdef("struct foo_s { long a; ...; };\n" - "int (*foo)(struct foo_s);") + "static int (*foo)(struct foo_s);") lib = ffi.verify(""" struct foo_s { double b; @@ -1079,7 +1073,7 @@ def test_autofilled_struct_as_argument_dynamic(): int foo1(struct foo_s s) { return (int)s.a - (int)s.b; } - int (*foo)(struct foo_s s) = &foo1; + static 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 " @@ -1449,7 +1443,7 @@ def test_bool(): py.test.skip("_Bool not in MSVC") ffi = FFI() ffi.cdef("struct foo_s { _Bool x; };" - "_Bool foo(_Bool); _Bool (*foop)(_Bool);") + "_Bool foo(_Bool); static _Bool (*foop)(_Bool);") lib = ffi.verify(""" struct foo_s { _Bool x; }; int foo(int arg) { @@ -1458,13 +1452,15 @@ def test_bool(): _Bool _foofunc(_Bool x) { return !x; } - _Bool (*foop)(_Bool) = _foofunc; + static _Bool (*foop)(_Bool) = _foofunc; """) p = ffi.new("struct foo_s *") p.x = 1 assert p.x is True - py.test.raises(OverflowError, "p.x = -1") - py.test.raises(TypeError, "p.x = 0.0") + with pytest.raises(OverflowError): + p.x = -1 + with pytest.raises(TypeError): + p.x = 0.0 assert lib.foop(1) is False assert lib.foop(True) is False assert lib.foop(0) is True @@ -1532,7 +1528,8 @@ def test_cannot_pass_float(): } """ % (type, type)) p = ffi.new("struct foo_s *") - py.test.raises(TypeError, "p.x = 0.0") + with pytest.raises(TypeError): + p.x = 0.0 assert lib.foo(42) == 0 assert lib.foo(0) == 1 py.test.raises(TypeError, lib.foo, 0.0) @@ -1646,7 +1643,7 @@ def test_FILE_stored_in_stdout(): def test_FILE_stored_explicitly(): ffi = FFI() - ffi.cdef("int myprintf11(const char *, int); FILE *myfile;") + ffi.cdef("int myprintf11(const char *, int); extern FILE *myfile;") lib = ffi.verify(""" #include <stdio.h> FILE *myfile; @@ -1672,19 +1669,19 @@ def test_FILE_stored_explicitly(): def test_global_array_with_missing_length(): ffi = FFI() - ffi.cdef("int fooarray[];") + ffi.cdef("extern 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[...];") + ffi.cdef("extern 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[...];") + ffi.cdef("extern int fooarray[...];") py.test.raises(VerificationError, ffi.verify, "char fooarray[23];") def test_struct_containing_struct(): @@ -1805,7 +1802,7 @@ def test_string_to_voidp_arg(): def test_callback_indirection(): ffi = FFI() ffi.cdef(""" - int (*python_callback)(int how_many, int *values); + static 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,...)); """) @@ -1939,24 +1936,24 @@ def test_typeof_func_with_struct_argument(): def test_bug_const_char_ptr_array_1(): ffi = FFI() - ffi.cdef("""const char *a[...];""") + ffi.cdef("""extern 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[];""") + ffi.cdef("""extern 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; + extern int xvalue; + extern long long ivalue, rvalue; + extern float fvalue; + extern double dvalue; + extern 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); @@ -2098,6 +2095,11 @@ def _run_in_multiple_threads(test1): raise errors[0][1] def test_errno_working_even_with_pypys_jit(): + # NOTE: on some platforms, to work correctly, this test needs to be + # compiled with -pthread. Otherwise, the accesses to errno done from f() + # are compiled by assuming this small library won't be used from multiple + # threads, which is wrong. If you see failures _and_ if you pass your + # own CFLAGS environment variable, please make sure "-pthread" is in it. ffi = FFI() ffi.cdef("int f(int);") lib = ffi.verify(""" @@ -2135,7 +2137,7 @@ def test_verify_dlopen_flags(): # exported symbols as well. So we must not export a simple name # like 'foo'! ffi1 = FFI() - ffi1.cdef("int foo_verify_dlopen_flags;") + ffi1.cdef("extern int foo_verify_dlopen_flags;") lib1 = ffi1.verify("int foo_verify_dlopen_flags;", flags=ffi1.RTLD_GLOBAL | ffi1.RTLD_LAZY) @@ -2149,7 +2151,7 @@ def test_verify_dlopen_flags(): def get_second_lib(): # Hack, using modulename makes the test fail ffi2 = FFI() - ffi2.cdef("int foo_verify_dlopen_flags;") + ffi2.cdef("extern int foo_verify_dlopen_flags;") lib2 = ffi2.verify("int foo_verify_dlopen_flags;", flags=ffi2.RTLD_GLOBAL | ffi2.RTLD_LAZY) return lib2 @@ -2522,3 +2524,39 @@ def test_ffi_new_with_cycles(): x.p = p x.cyclic = x del p, x + +def test_arithmetic_in_cdef(): + for a in [0, 11, 15]: + ffi = FFI() + ffi.cdef(""" + enum FOO { + DIVNN = ((-?) / (-3)), + DIVNP = ((-?) / (+3)), + DIVPN = ((+?) / (-3)), + MODNN = ((-?) % (-3)), + MODNP = ((-?) % (+3)), + MODPN = ((+?) % (-3)), + }; + """.replace('?', str(a))) + lib = ffi.verify(""" + enum FOO { + DIVNN = ((-?) / (-3)), + DIVNP = ((-?) / (+3)), + DIVPN = ((+?) / (-3)), + MODNN = ((-?) % (-3)), + MODNP = ((-?) % (+3)), + MODPN = ((+?) % (-3)), + }; + """.replace('?', str(a))) + # the verify() crashes if the values in the enum are different from + # the values we computed ourselves from the cdef() + +def test_passing_large_list(): + ffi = FFI() + ffi.cdef("""void passing_large_list(long[]);""") + lib = ffi.verify(""" + static void passing_large_list(long a[]) { } + """) + arg = list(range(20000000)) + lib.passing_large_list(arg) + # assert did not segfault diff --git a/testing/cffi0/test_version.py b/testing/cffi0/test_version.py index 9325685..facb84c 100644 --- a/testing/cffi0/test_version.py +++ b/testing/cffi0/test_version.py @@ -18,6 +18,7 @@ def test_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', '+') + version_info = version_info.replace('.rc', 'rc') assert v == version_info #v = BACKEND_VERSIONS.get(v, v) assert v == _cffi_backend.__version__ @@ -36,7 +37,12 @@ def test_doc_version_file(): v = cffi.__version__.replace('+', '') p = os.path.join(parent, 'doc', 'source', 'installation.rst') content = open(p).read() - assert (" package version %s:" % v) in content + if " package version %s:" % v not in content: + for i in range(5): + if " package version %s-%d:" % (v, i) in content: + break + else: + assert 0, "doc/source/installation.rst needs updating" def test_setup_version(): parent = os.path.dirname(os.path.dirname(cffi.__file__)) diff --git a/testing/cffi0/test_zdistutils.py b/testing/cffi0/test_zdistutils.py index b67b105..35b3d0c 100644 --- a/testing/cffi0/test_zdistutils.py +++ b/testing/cffi0/test_zdistutils.py @@ -89,7 +89,7 @@ class DistUtilsTest(object): 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' + basename = self.__class__.__name__[:10] + '_test_compile_module' v.modulefilename = filename = str(udir.join(basename + '.so')) v.compile_module() assert filename == v.modulefilename diff --git a/testing/cffi0/test_zintegration.py b/testing/cffi0/test_zintegration.py index d56dac2..ce925b8 100644 --- a/testing/cffi0/test_zintegration.py +++ b/testing/cffi0/test_zintegration.py @@ -1,11 +1,13 @@ import py, os, sys, shutil import subprocess from testing.udir import udir +import pytest if sys.platform == 'win32': - py.test.skip('snippets do not run on win32') + pytestmark = pytest.mark.skip('snippets do not run on win32') if sys.version_info < (2, 7): - py.test.skip('fails e.g. on a Debian/Ubuntu which patches virtualenv' + pytestmark = pytest.mark.skip( + 'fails e.g. on a Debian/Ubuntu which patches virtualenv' ' in a non-2.6-friendly way') def create_venv(name): @@ -75,7 +77,10 @@ def really_run_setup_and_program(dirname, venv_dir_and_paths, python_snippet): env = os.environ.copy() env['PYTHONPATH'] = paths subprocess.check_call((vp, 'setup.py', 'clean'), env=env) - subprocess.check_call((vp, 'setup.py', 'install'), env=env) + # there's a setuptools/easy_install bug that causes this to fail when the build/install occur together and + # we're in the same directory with the build (it tries to look up dependencies for itself on PyPI); + # subsequent runs will succeed because this test doesn't properly clean up the build- use pip for now. + subprocess.check_call((vp, '-m', 'pip', 'install', '.'), env=env) subprocess.check_call((vp, str(python_f)), env=env) finally: os.chdir(olddir) |