import pytest import sys is_musl = False if sys.platform == 'linux': try: from packaging.tags import platform_tags is_musl = any(t.startswith('musllinux') for t in platform_tags()) del platform_tags except ImportError: pass def _setup_path(): import os, sys sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..')) _setup_path() from _cffi_backend import * from _cffi_backend import _get_types, _get_common_types try: from _cffi_backend import _testfunc except ImportError: def _testfunc(num): pytest.skip("_testunc() not available") from _cffi_backend import __version__ # ____________________________________________________________ import sys assert __version__ == "1.15.1", ("This test_c.py file is for testing a version" " of cffi that differs from the one that we" " get from 'import _cffi_backend'") if sys.version_info < (3,): type_or_class = "type" mandatory_b_prefix = '' mandatory_u_prefix = 'u' bytechr = chr bitem2bchr = lambda x: x class U(object): def __add__(self, other): return eval('u'+repr(other).replace(r'\\u', r'\u') .replace(r'\\U', r'\U')) u = U() str2bytes = str strict_compare = False else: type_or_class = "class" long = int unicode = str unichr = chr mandatory_b_prefix = 'b' mandatory_u_prefix = '' bytechr = lambda n: bytes([n]) bitem2bchr = bytechr u = "" str2bytes = lambda s: bytes(s, "ascii") strict_compare = True def size_of_int(): BInt = new_primitive_type("int") return sizeof(BInt) def size_of_long(): BLong = new_primitive_type("long") return sizeof(BLong) def size_of_ptr(): BInt = new_primitive_type("int") BPtr = new_pointer_type(BInt) return sizeof(BPtr) def find_and_load_library(name, flags=RTLD_NOW): import ctypes.util if name is None: path = None else: path = ctypes.util.find_library(name) if path is None and name == 'c': assert sys.platform == 'win32' assert (sys.version_info >= (3,) or '__pypy__' in sys.builtin_module_names) pytest.skip("dlopen(None) cannot work on Windows " "with PyPy or Python 3") return load_library(path, flags) def test_load_library(): x = find_and_load_library('c') assert repr(x).startswith("" def check_dir(p, expected): got = [name for name in dir(p) if not name.startswith('_')] assert got == sorted(expected) def test_inspect_primitive_type(): p = new_primitive_type("signed char") assert p.kind == "primitive" assert p.cname == "signed char" check_dir(p, ['cname', 'kind']) def test_cast_to_signed_char(): p = new_primitive_type("signed char") x = cast(p, -65 + 17*256) assert repr(x) == "" assert repr(type(x)) == "<%s '_cffi_backend._CDataBase'>" % type_or_class assert int(x) == -65 x = cast(p, -66 + (1<<199)*256) assert repr(x) == "" assert int(x) == -66 assert (x == cast(p, -66)) is True assert (x != cast(p, -66)) is False q = new_primitive_type("short") assert (x == cast(q, -66)) is True assert (x != cast(q, -66)) is False def test_sizeof_type(): pytest.raises(TypeError, sizeof, 42.5) p = new_primitive_type("short") assert sizeof(p) == 2 def test_integer_types(): for name in ['signed char', 'short', 'int', 'long', 'long long']: p = new_primitive_type(name) size = sizeof(p) min = -(1 << (8*size-1)) max = (1 << (8*size-1)) - 1 assert int(cast(p, min)) == min assert int(cast(p, max)) == max assert int(cast(p, min - 1)) == max assert int(cast(p, max + 1)) == min pytest.raises(TypeError, cast, p, None) assert long(cast(p, min - 1)) == max assert int(cast(p, b'\x08')) == 8 assert int(cast(p, u+'\x08')) == 8 for name in ['char', 'short', 'int', 'long', 'long long']: p = new_primitive_type('unsigned ' + name) size = sizeof(p) max = (1 << (8*size)) - 1 assert int(cast(p, 0)) == 0 assert int(cast(p, max)) == max assert int(cast(p, -1)) == max assert int(cast(p, max + 1)) == 0 assert long(cast(p, -1)) == max assert int(cast(p, b'\xFE')) == 254 assert int(cast(p, u+'\xFE')) == 254 def test_no_float_on_int_types(): p = new_primitive_type('long') pytest.raises(TypeError, float, cast(p, 42)) pytest.raises(TypeError, complex, cast(p, 42)) def test_float_types(): INF = 1E200 * 1E200 for name in ["float", "double"]: p = new_primitive_type(name) assert bool(cast(p, 0)) is False # since 1.7 assert bool(cast(p, -0.0)) is False # since 1.7 assert bool(cast(p, 1e-42)) is True assert bool(cast(p, -1e-42)) is True assert bool(cast(p, INF)) assert bool(cast(p, -INF)) assert bool(cast(p, float("nan"))) assert int(cast(p, -150)) == -150 assert int(cast(p, 61.91)) == 61 assert long(cast(p, 61.91)) == 61 assert type(int(cast(p, 61.91))) is int assert type(int(cast(p, 1E22))) is long assert type(long(cast(p, 61.91))) is long assert type(long(cast(p, 1E22))) is long pytest.raises(OverflowError, int, cast(p, INF)) pytest.raises(OverflowError, int, cast(p, -INF)) assert float(cast(p, 1.25)) == 1.25 assert float(cast(p, INF)) == INF assert float(cast(p, -INF)) == -INF if name == "float": assert float(cast(p, 1.1)) != 1.1 # rounding error assert float(cast(p, 1E200)) == INF # limited range assert cast(p, -1.1) == cast(p, -1.1) assert repr(float(cast(p, -0.0))) == '-0.0' assert float(cast(p, b'\x09')) == 9.0 assert float(cast(p, u+'\x09')) == 9.0 assert float(cast(p, True)) == 1.0 pytest.raises(TypeError, cast, p, None) def test_complex_types(): INF = 1E200 * 1E200 for name in ["float", "double"]: p = new_primitive_type(name + " _Complex") assert bool(cast(p, 0)) is False assert bool(cast(p, INF)) assert bool(cast(p, -INF)) assert bool(cast(p, 0j)) is False assert bool(cast(p, INF*1j)) assert bool(cast(p, -INF*1j)) # "can't convert complex to float", like CPython's "float(0j)" pytest.raises(TypeError, int, cast(p, -150)) pytest.raises(TypeError, long, cast(p, -150)) pytest.raises(TypeError, float, cast(p, -150)) assert complex(cast(p, 1.25)) == 1.25 assert complex(cast(p, 1.25j)) == 1.25j assert complex(cast(p, complex(0,INF))) == complex(0,INF) assert complex(cast(p, -INF)) == -INF if name == "float": assert complex(cast(p, 1.1j)) != 1.1j # rounding error assert complex(cast(p, 1E200+3j)) == INF+3j # limited range assert complex(cast(p, complex(3,1E200))) == complex(3,INF) # limited range assert cast(p, -1.1j) == cast(p, -1.1j) assert repr(complex(cast(p, -0.0)).real) == '-0.0' #assert repr(complex(cast(p, -0j))) == '-0j' # http://bugs.python.org/issue29602 assert complex(cast(p, b'\x09')) == 9.0 + 0j assert complex(cast(p, u+'\x09')) == 9.0 + 0j assert complex(cast(p, True)) == 1.0 + 0j pytest.raises(TypeError, cast, p, None) # pytest.raises(TypeError, cast, new_primitive_type(name), 1+0j) # for basetype in ["char", "int", "uint64_t", "float", "double", "long double"]: baseobj = cast(new_primitive_type(basetype), 65) pytest.raises(TypeError, complex, baseobj) # BArray = new_array_type(new_pointer_type(p), 10) x = newp(BArray, None) x[5] = 12.34 + 56.78j assert type(x[5]) is complex assert abs(x[5] - (12.34 + 56.78j)) < 1e-5 assert (x[5] == 12.34 + 56.78j) == (name == "double") # rounding error # class Foo: def __complex__(self): return 2 + 3j assert complex(Foo()) == 2 + 3j assert complex(cast(p, Foo())) == 2 + 3j pytest.raises(TypeError, cast, new_primitive_type("int"), 1+0j) def test_character_type(): p = new_primitive_type("char") assert bool(cast(p, 'A')) is True assert bool(cast(p, '\x00')) is False # since 1.7 assert cast(p, '\x00') == cast(p, -17*256) assert int(cast(p, 'A')) == 65 assert long(cast(p, 'A')) == 65 assert type(int(cast(p, 'A'))) is int assert type(long(cast(p, 'A'))) is long assert str(cast(p, 'A')) == repr(cast(p, 'A')) assert repr(cast(p, 'A')) == "" % mandatory_b_prefix assert repr(cast(p, 255)) == r"" % mandatory_b_prefix assert repr(cast(p, 0)) == r"" % mandatory_b_prefix def test_pointer_type(): p = new_primitive_type("int") assert repr(p) == "" p = new_pointer_type(p) assert repr(p) == "" p = new_pointer_type(p) assert repr(p) == "" p = new_pointer_type(p) assert repr(p) == "" def test_inspect_pointer_type(): p1 = new_primitive_type("int") p2 = new_pointer_type(p1) assert p2.kind == "pointer" assert p2.cname == "int *" assert p2.item is p1 check_dir(p2, ['cname', 'kind', 'item']) p3 = new_pointer_type(p2) assert p3.item is p2 def test_pointer_to_int(): BInt = new_primitive_type("int") pytest.raises(TypeError, newp, BInt) pytest.raises(TypeError, newp, BInt, None) BPtr = new_pointer_type(BInt) p = newp(BPtr) assert repr(p) == "" % size_of_int() p = newp(BPtr, None) assert repr(p) == "" % size_of_int() p = newp(BPtr, 5000) assert repr(p) == "" % size_of_int() q = cast(BPtr, p) assert repr(q).startswith("" % size_of_ptr() def test_reading_pointer_to_int(): BInt = new_primitive_type("int") BPtr = new_pointer_type(BInt) p = newp(BPtr, None) assert p[0] == 0 p = newp(BPtr, 5000) assert p[0] == 5000 with pytest.raises(IndexError): p[1] with pytest.raises(IndexError): p[-1] def test_reading_pointer_to_float(): BFloat = new_primitive_type("float") pytest.raises(TypeError, newp, BFloat, None) BPtr = new_pointer_type(BFloat) p = newp(BPtr, None) assert p[0] == 0.0 and type(p[0]) is float p = newp(BPtr, 1.25) assert p[0] == 1.25 and type(p[0]) is float p = newp(BPtr, 1.1) assert p[0] != 1.1 and abs(p[0] - 1.1) < 1E-5 # rounding errors def test_cast_float_to_int(): for type in ["int", "unsigned int", "long", "unsigned long", "long long", "unsigned long long"]: p = new_primitive_type(type) assert int(cast(p, 4.2)) == 4 pytest.raises(TypeError, newp, new_pointer_type(p), 4.2) def test_newp_integer_types(): for name in ['signed char', 'short', 'int', 'long', 'long long']: p = new_primitive_type(name) pp = new_pointer_type(p) size = sizeof(p) min = -(1 << (8*size-1)) max = (1 << (8*size-1)) - 1 assert newp(pp, min)[0] == min assert newp(pp, max)[0] == max pytest.raises(OverflowError, newp, pp, min - 2 ** 32) pytest.raises(OverflowError, newp, pp, min - 2 ** 64) pytest.raises(OverflowError, newp, pp, max + 2 ** 32) pytest.raises(OverflowError, newp, pp, max + 2 ** 64) pytest.raises(OverflowError, newp, pp, min - 1) pytest.raises(OverflowError, newp, pp, max + 1) pytest.raises(OverflowError, newp, pp, min - 1 - 2 ** 32) pytest.raises(OverflowError, newp, pp, min - 1 - 2 ** 64) pytest.raises(OverflowError, newp, pp, max + 1) pytest.raises(OverflowError, newp, pp, max + 1 + 2 ** 32) pytest.raises(OverflowError, newp, pp, max + 1 + 2 ** 64) pytest.raises(TypeError, newp, pp, 1.0) for name in ['char', 'short', 'int', 'long', 'long long']: p = new_primitive_type('unsigned ' + name) pp = new_pointer_type(p) size = sizeof(p) max = (1 << (8*size)) - 1 assert newp(pp, 0)[0] == 0 assert newp(pp, max)[0] == max pytest.raises(OverflowError, newp, pp, -1) pytest.raises(OverflowError, newp, pp, max + 1) def test_reading_pointer_to_char(): BChar = new_primitive_type("char") pytest.raises(TypeError, newp, BChar, None) BPtr = new_pointer_type(BChar) p = newp(BPtr, None) assert p[0] == b'\x00' p = newp(BPtr, b'A') assert p[0] == b'A' pytest.raises(TypeError, newp, BPtr, 65) pytest.raises(TypeError, newp, BPtr, b"foo") pytest.raises(TypeError, newp, BPtr, u+"foo") c = cast(BChar, b'A') assert str(c) == repr(c) assert int(c) == ord(b'A') pytest.raises(TypeError, cast, BChar, b'foo') pytest.raises(TypeError, cast, BChar, u+'foo') e = pytest.raises(TypeError, newp, new_array_type(BPtr, None), 12.3) assert str(e.value) == ( "expected new array length or list/tuple/str, not float") def test_reading_pointer_to_pointer(): BVoidP = new_pointer_type(new_void_type()) BCharP = new_pointer_type(new_primitive_type("char")) BInt = new_primitive_type("int") BIntPtr = new_pointer_type(BInt) BIntPtrPtr = new_pointer_type(BIntPtr) q = newp(BIntPtr, 42) assert q[0] == 42 p = newp(BIntPtrPtr, None) assert p[0] is not None assert p[0] == cast(BVoidP, 0) assert p[0] == cast(BCharP, 0) assert p[0] != None assert repr(p[0]) == "" p[0] = q assert p[0] != cast(BVoidP, 0) assert p[0] != cast(BCharP, 0) assert p[0][0] == 42 q[0] += 1 assert p[0][0] == 43 p = newp(BIntPtrPtr, q) assert p[0][0] == 43 def test_load_standard_library(): if sys.platform == "win32": pytest.raises(OSError, find_and_load_library, None) return x = find_and_load_library(None) BVoidP = new_pointer_type(new_void_type()) assert x.load_function(BVoidP, 'strcpy') pytest.raises(AttributeError, x.load_function, BVoidP, 'xxx_this_function_does_not_exist') # the next one is from 'libm', not 'libc', but we assume # that it is already loaded too, so it should work assert x.load_function(BVoidP, 'sqrt') # x.close_lib() pytest.raises(ValueError, x.load_function, BVoidP, 'sqrt') x.close_lib() def test_no_len_on_nonarray(): p = new_primitive_type("int") pytest.raises(TypeError, len, cast(p, 42)) def test_cmp_none(): p = new_primitive_type("int") x = cast(p, 42) assert (x == None) is False assert (x != None) is True assert (x == ["hello"]) is False assert (x != ["hello"]) is True y = cast(p, 0) assert (y == None) is False def test_invalid_indexing(): p = new_primitive_type("int") x = cast(p, 42) with pytest.raises(TypeError): x[0] def test_default_str(): BChar = new_primitive_type("char") x = cast(BChar, 42) assert str(x) == repr(x) BInt = new_primitive_type("int") x = cast(BInt, 42) assert str(x) == repr(x) BArray = new_array_type(new_pointer_type(BInt), 10) x = newp(BArray, None) assert str(x) == repr(x) def test_default_unicode(): BInt = new_primitive_type("int") x = cast(BInt, 42) assert unicode(x) == unicode(repr(x)) BArray = new_array_type(new_pointer_type(BInt), 10) x = newp(BArray, None) assert unicode(x) == unicode(repr(x)) def test_cast_from_cdataint(): BInt = new_primitive_type("int") x = cast(BInt, 0) y = cast(new_pointer_type(BInt), x) assert bool(y) is False # x = cast(BInt, 42) y = cast(BInt, x) assert int(y) == 42 y = cast(new_primitive_type("char"), x) assert int(y) == 42 y = cast(new_primitive_type("float"), x) assert float(y) == 42.0 # z = cast(BInt, 42.5) assert int(z) == 42 z = cast(BInt, y) assert int(z) == 42 def test_void_type(): p = new_void_type() assert p.kind == "void" assert p.cname == "void" check_dir(p, ['kind', 'cname']) def test_array_type(): p = new_primitive_type("int") assert repr(p) == "" # pytest.raises(TypeError, new_array_type, new_pointer_type(p), "foo") pytest.raises(ValueError, new_array_type, new_pointer_type(p), -42) # p1 = new_array_type(new_pointer_type(p), None) assert repr(p1) == "" pytest.raises(ValueError, new_array_type, new_pointer_type(p1), 42) # p1 = new_array_type(new_pointer_type(p), 42) p2 = new_array_type(new_pointer_type(p1), 25) assert repr(p2) == "" p2 = new_array_type(new_pointer_type(p1), None) assert repr(p2) == "" # pytest.raises(OverflowError, new_array_type, new_pointer_type(p), sys.maxsize+1) pytest.raises(OverflowError, new_array_type, new_pointer_type(p), sys.maxsize // 3) def test_inspect_array_type(): p = new_primitive_type("int") p1 = new_array_type(new_pointer_type(p), None) assert p1.kind == "array" assert p1.cname == "int[]" assert p1.item is p assert p1.length is None check_dir(p1, ['cname', 'kind', 'item', 'length']) p1 = new_array_type(new_pointer_type(p), 42) assert p1.kind == "array" assert p1.cname == "int[42]" assert p1.item is p assert p1.length == 42 check_dir(p1, ['cname', 'kind', 'item', 'length']) def test_array_instance(): LENGTH = 1423 p = new_primitive_type("int") p1 = new_array_type(new_pointer_type(p), LENGTH) a = newp(p1, None) assert repr(a) == "" % ( LENGTH, LENGTH * size_of_int()) assert len(a) == LENGTH for i in range(LENGTH): assert a[i] == 0 with pytest.raises(IndexError): a[LENGTH] with pytest.raises(IndexError): a[-1] for i in range(LENGTH): a[i] = i * i + 1 for i in range(LENGTH): assert a[i] == i * i + 1 with pytest.raises(IndexError) as e: a[LENGTH+100] = 500 assert ('(expected %d < %d)' % (LENGTH+100, LENGTH)) in str(e.value) pytest.raises(TypeError, int, a) def test_array_of_unknown_length_instance(): p = new_primitive_type("int") p1 = new_array_type(new_pointer_type(p), None) pytest.raises(TypeError, newp, p1, None) pytest.raises(ValueError, newp, p1, -42) a = newp(p1, 42) assert len(a) == 42 for i in range(42): a[i] -= i for i in range(42): assert a[i] == -i with pytest.raises(IndexError): a[42] with pytest.raises(IndexError): a[-1] with pytest.raises(IndexError): a[42] = 123 with pytest.raises(IndexError): a[-1] = 456 def test_array_of_unknown_length_instance_with_initializer(): p = new_primitive_type("int") p1 = new_array_type(new_pointer_type(p), None) a = newp(p1, list(range(42))) assert len(a) == 42 a = newp(p1, tuple(range(142))) assert len(a) == 142 def test_array_initializer(): p = new_primitive_type("int") p1 = new_array_type(new_pointer_type(p), None) a = newp(p1, list(range(100, 142))) for i in range(42): assert a[i] == 100 + i # p2 = new_array_type(new_pointer_type(p), 43) a = newp(p2, tuple(range(100, 142))) for i in range(42): assert a[i] == 100 + i assert a[42] == 0 # extra uninitialized item def test_array_add(): p = new_primitive_type("int") p1 = new_array_type(new_pointer_type(p), 5) # int[5] p2 = new_array_type(new_pointer_type(p1), 3) # int[3][5] a = newp(p2, [list(range(n, n+5)) for n in [100, 200, 300]]) assert repr(a) == "" % ( 3*5*size_of_int(),) assert repr(a + 0).startswith("" BStruct = new_struct_type("struct foo") assert repr(BStruct) == "" BPtr = new_pointer_type(BStruct) assert repr(BPtr) == "" pytest.raises(ValueError, sizeof, BStruct) pytest.raises(ValueError, alignof, BStruct) def test_new_union_type(): BUnion = new_union_type("union foo") assert repr(BUnion) == "" BPtr = new_pointer_type(BUnion) assert repr(BPtr) == "" def test_complete_struct(): BLong = new_primitive_type("long") BChar = new_primitive_type("char") BShort = new_primitive_type("short") BStruct = new_struct_type("struct foo") assert BStruct.kind == "struct" assert BStruct.cname == "struct foo" assert BStruct.fields is None check_dir(BStruct, ['cname', 'kind', 'fields']) # complete_struct_or_union(BStruct, [('a1', BLong, -1), ('a2', BChar, -1), ('a3', BShort, -1)]) d = BStruct.fields assert len(d) == 3 assert d[0][0] == 'a1' assert d[0][1].type is BLong assert d[0][1].offset == 0 assert d[0][1].bitshift == -1 assert d[0][1].bitsize == -1 assert d[1][0] == 'a2' assert d[1][1].type is BChar assert d[1][1].offset == sizeof(BLong) assert d[1][1].bitshift == -1 assert d[1][1].bitsize == -1 assert d[2][0] == 'a3' assert d[2][1].type is BShort assert d[2][1].offset == sizeof(BLong) + sizeof(BShort) assert d[2][1].bitshift == -1 assert d[2][1].bitsize == -1 assert sizeof(BStruct) == 2 * sizeof(BLong) assert alignof(BStruct) == alignof(BLong) def test_complete_union(): BLong = new_primitive_type("long") BChar = new_primitive_type("char") BUnion = new_union_type("union foo") assert BUnion.kind == "union" assert BUnion.cname == "union foo" assert BUnion.fields is None complete_struct_or_union(BUnion, [('a1', BLong, -1), ('a2', BChar, -1)]) d = BUnion.fields assert len(d) == 2 assert d[0][0] == 'a1' assert d[0][1].type is BLong assert d[0][1].offset == 0 assert d[1][0] == 'a2' assert d[1][1].type is BChar assert d[1][1].offset == 0 assert sizeof(BUnion) == sizeof(BLong) assert alignof(BUnion) == alignof(BLong) def test_struct_instance(): BInt = new_primitive_type("int") BStruct = new_struct_type("struct foo") BStructPtr = new_pointer_type(BStruct) p = cast(BStructPtr, 42) with pytest.raises(AttributeError) as e: p.a1 # opaque assert str(e.value) == ("cdata 'struct foo *' points to an opaque type: " "cannot read fields") with pytest.raises(AttributeError) as e: p.a1 = 10 # opaque assert str(e.value) == ("cdata 'struct foo *' points to an opaque type: " "cannot write fields") complete_struct_or_union(BStruct, [('a1', BInt, -1), ('a2', BInt, -1)]) p = newp(BStructPtr, None) s = p[0] assert s.a1 == 0 s.a2 = 123 assert s.a1 == 0 assert s.a2 == 123 with pytest.raises(OverflowError): s.a1 = sys.maxsize+1 assert s.a1 == 0 with pytest.raises(AttributeError) as e: p.foobar assert str(e.value) == "cdata 'struct foo *' has no field 'foobar'" with pytest.raises(AttributeError) as e: p.foobar = 42 assert str(e.value) == "cdata 'struct foo *' has no field 'foobar'" with pytest.raises(AttributeError) as e: s.foobar assert str(e.value) == "cdata 'struct foo' has no field 'foobar'" with pytest.raises(AttributeError) as e: s.foobar = 42 assert str(e.value) == "cdata 'struct foo' has no field 'foobar'" j = cast(BInt, 42) with pytest.raises(AttributeError) as e: j.foobar assert str(e.value) == "cdata 'int' has no attribute 'foobar'" with pytest.raises(AttributeError) as e: j.foobar = 42 assert str(e.value) == "cdata 'int' has no attribute 'foobar'" j = cast(new_pointer_type(BInt), 42) with pytest.raises(AttributeError) as e: j.foobar assert str(e.value) == "cdata 'int *' has no attribute 'foobar'" with pytest.raises(AttributeError) as e: j.foobar = 42 assert str(e.value) == "cdata 'int *' has no attribute 'foobar'" pp = newp(new_pointer_type(BStructPtr), p) with pytest.raises(AttributeError) as e: pp.a1 assert str(e.value) == "cdata 'struct foo * *' has no attribute 'a1'" with pytest.raises(AttributeError) as e: pp.a1 = 42 assert str(e.value) == "cdata 'struct foo * *' has no attribute 'a1'" def test_union_instance(): BInt = new_primitive_type("int") BUInt = new_primitive_type("unsigned int") BUnion = new_union_type("union bar") complete_struct_or_union(BUnion, [('a1', BInt, -1), ('a2', BUInt, -1)]) p = newp(new_pointer_type(BUnion), [-42]) bigval = -42 + (1 << (8*size_of_int())) assert p.a1 == -42 assert p.a2 == bigval p = newp(new_pointer_type(BUnion), {'a2': bigval}) assert p.a1 == -42 assert p.a2 == bigval pytest.raises(OverflowError, newp, new_pointer_type(BUnion), {'a1': bigval}) p = newp(new_pointer_type(BUnion), []) assert p.a1 == p.a2 == 0 def test_struct_pointer(): BInt = new_primitive_type("int") BStruct = new_struct_type("struct foo") BStructPtr = new_pointer_type(BStruct) complete_struct_or_union(BStruct, [('a1', BInt, -1), ('a2', BInt, -1)]) p = newp(BStructPtr, None) assert p.a1 == 0 # read/write via the pointer (C equivalent: '->') p.a2 = 123 assert p.a1 == 0 assert p.a2 == 123 def test_struct_init_list(): BVoidP = new_pointer_type(new_void_type()) BInt = new_primitive_type("int") BIntPtr = new_pointer_type(BInt) BStruct = new_struct_type("struct foo") BStructPtr = new_pointer_type(BStruct) complete_struct_or_union(BStruct, [('a1', BInt, -1), ('a2', BInt, -1), ('a3', BInt, -1), ('p4', BIntPtr, -1)]) s = newp(BStructPtr, [123, 456]) assert s.a1 == 123 assert s.a2 == 456 assert s.a3 == 0 assert s.p4 == cast(BVoidP, 0) assert s.p4 != 0 # s = newp(BStructPtr, {'a2': 41122, 'a3': -123}) assert s.a1 == 0 assert s.a2 == 41122 assert s.a3 == -123 assert s.p4 == cast(BVoidP, 0) # pytest.raises(KeyError, newp, BStructPtr, {'foobar': 0}) # p = newp(BIntPtr, 14141) s = newp(BStructPtr, [12, 34, 56, p]) assert s.p4 == p assert s.p4 # s = newp(BStructPtr, [12, 34, 56, cast(BVoidP, 0)]) assert s.p4 == cast(BVoidP, 0) assert not s.p4 # pytest.raises(TypeError, newp, BStructPtr, [12, 34, 56, None]) def test_array_in_struct(): BInt = new_primitive_type("int") BStruct = new_struct_type("struct foo") BArrayInt5 = new_array_type(new_pointer_type(BInt), 5) complete_struct_or_union(BStruct, [('a1', BArrayInt5, -1)]) s = newp(new_pointer_type(BStruct), [[20, 24, 27, 29, 30]]) assert s.a1[2] == 27 assert repr(s.a1).startswith("" BFunc2 = new_function_type((), BFunc, False) assert repr(BFunc2) == "" def test_inspect_function_type(): BInt = new_primitive_type("int") BFunc = new_function_type((BInt, BInt), BInt, False) assert BFunc.kind == "function" assert BFunc.cname == "int(*)(int, int)" assert BFunc.args == (BInt, BInt) assert BFunc.result is BInt assert BFunc.ellipsis is False assert BFunc.abi == FFI_DEFAULT_ABI def test_function_type_taking_struct(): BChar = new_primitive_type("char") BShort = new_primitive_type("short") BStruct = new_struct_type("struct foo") complete_struct_or_union(BStruct, [('a1', BChar, -1), ('a2', BShort, -1)]) BFunc = new_function_type((BStruct,), BShort, False) assert repr(BFunc) == "" def test_function_void_result(): BVoid = new_void_type() BInt = new_primitive_type("int") BFunc = new_function_type((BInt, BInt), BVoid, False) assert repr(BFunc) == "" def test_function_void_arg(): BVoid = new_void_type() BInt = new_primitive_type("int") pytest.raises(TypeError, new_function_type, (BVoid,), BInt, False) def test_call_function_0(): BSignedChar = new_primitive_type("signed char") BFunc0 = new_function_type((BSignedChar, BSignedChar), BSignedChar, False) f = cast(BFunc0, _testfunc(0)) assert f(40, 2) == 42 assert f(-100, -100) == -200 + 256 pytest.raises(OverflowError, f, 128, 0) pytest.raises(OverflowError, f, 0, 128) def test_call_function_0_pretend_bool_result(): BSignedChar = new_primitive_type("signed char") BBool = new_primitive_type("_Bool") BFunc0 = new_function_type((BSignedChar, BSignedChar), BBool, False) f = cast(BFunc0, _testfunc(0)) assert f(40, -39) is True assert f(40, -40) is False pytest.raises(ValueError, f, 40, 2) def test_call_function_1(): BInt = new_primitive_type("int") BLong = new_primitive_type("long") BFunc1 = new_function_type((BInt, BLong), BLong, False) f = cast(BFunc1, _testfunc(1)) assert f(40, 2) == 42 assert f(-100, -100) == -200 int_max = (1 << (8*size_of_int()-1)) - 1 long_max = (1 << (8*size_of_long()-1)) - 1 if int_max == long_max: assert f(int_max, 1) == - int_max - 1 else: assert f(int_max, 1) == int_max + 1 def test_call_function_2(): BLongLong = new_primitive_type("long long") BFunc2 = new_function_type((BLongLong, BLongLong), BLongLong, False) f = cast(BFunc2, _testfunc(2)) longlong_max = (1 << (8*sizeof(BLongLong)-1)) - 1 assert f(longlong_max - 42, 42) == longlong_max assert f(43, longlong_max - 42) == - longlong_max - 1 def test_call_function_3(): BFloat = new_primitive_type("float") BDouble = new_primitive_type("double") BFunc3 = new_function_type((BFloat, BDouble), BDouble, False) f = cast(BFunc3, _testfunc(3)) assert f(1.25, 5.1) == 1.25 + 5.1 # exact res = f(1.3, 5.1) assert res != 6.4 and abs(res - 6.4) < 1E-5 # inexact def test_call_function_4(): BFloat = new_primitive_type("float") BDouble = new_primitive_type("double") BFunc4 = new_function_type((BFloat, BDouble), BFloat, False) f = cast(BFunc4, _testfunc(4)) res = f(1.25, 5.1) assert res != 6.35 and abs(res - 6.35) < 1E-5 # inexact def test_call_function_5(): BVoid = new_void_type() BFunc5 = new_function_type((), BVoid, False) f = cast(BFunc5, _testfunc(5)) f() # did not crash def test_call_function_6(): BInt = new_primitive_type("int") BIntPtr = new_pointer_type(BInt) BFunc6 = new_function_type((BIntPtr,), BIntPtr, False) f = cast(BFunc6, _testfunc(6)) x = newp(BIntPtr, 42) res = f(x) assert typeof(res) is BIntPtr assert res[0] == 42 - 1000 # BIntArray = new_array_type(BIntPtr, None) BFunc6bis = new_function_type((BIntArray,), BIntPtr, False) f = cast(BFunc6bis, _testfunc(6)) # res = f([142]) assert typeof(res) is BIntPtr assert res[0] == 142 - 1000 # res = f((143,)) assert typeof(res) is BIntPtr assert res[0] == 143 - 1000 # x = newp(BIntArray, [242]) res = f(x) assert typeof(res) is BIntPtr assert res[0] == 242 - 1000 # pytest.raises(TypeError, f, 123456) pytest.raises(TypeError, f, "foo") pytest.raises(TypeError, f, u+"bar") def test_call_function_7(): BChar = new_primitive_type("char") BShort = new_primitive_type("short") BStruct = new_struct_type("struct foo") BStructPtr = new_pointer_type(BStruct) complete_struct_or_union(BStruct, [('a1', BChar, -1), ('a2', BShort, -1)]) BFunc7 = new_function_type((BStruct,), BShort, False) f = cast(BFunc7, _testfunc(7)) res = f({'a1': b'A', 'a2': -4042}) assert res == -4042 + ord(b'A') # x = newp(BStructPtr, {'a1': b'A', 'a2': -4042}) res = f(x[0]) assert res == -4042 + ord(b'A') def test_call_function_20(): BChar = new_primitive_type("char") BShort = new_primitive_type("short") BStruct = new_struct_type("struct foo") BStructPtr = new_pointer_type(BStruct) complete_struct_or_union(BStruct, [('a1', BChar, -1), ('a2', BShort, -1)]) BFunc20 = new_function_type((BStructPtr,), BShort, False) f = cast(BFunc20, _testfunc(20)) x = newp(BStructPtr, {'a1': b'A', 'a2': -4042}) # can't pass a 'struct foo' pytest.raises(TypeError, f, x[0]) def test_call_function_21(): BInt = new_primitive_type("int") BStruct = new_struct_type("struct foo") complete_struct_or_union(BStruct, [('a', BInt, -1), ('b', BInt, -1), ('c', BInt, -1), ('d', BInt, -1), ('e', BInt, -1), ('f', BInt, -1), ('g', BInt, -1), ('h', BInt, -1), ('i', BInt, -1), ('j', BInt, -1)]) BFunc21 = new_function_type((BStruct,), BInt, False) f = cast(BFunc21, _testfunc(21)) res = f(list(range(13, 3, -1))) lst = [(n << i) for (i, n) in enumerate(range(13, 3, -1))] assert res == sum(lst) def test_call_function_22(): BInt = new_primitive_type("int") BArray10 = new_array_type(new_pointer_type(BInt), 10) BStruct = new_struct_type("struct foo") BStructP = new_pointer_type(BStruct) complete_struct_or_union(BStruct, [('a', BArray10, -1)]) BFunc22 = new_function_type((BStruct, BStruct), BStruct, False) f = cast(BFunc22, _testfunc(22)) p1 = newp(BStructP, {'a': list(range(100, 110))}) p2 = newp(BStructP, {'a': list(range(1000, 1100, 10))}) res = f(p1[0], p2[0]) for i in range(10): assert res.a[i] == p1.a[i] - p2.a[i] def test_call_function_23(): BVoid = new_void_type() # declaring the function as int(void*) BVoidP = new_pointer_type(BVoid) BInt = new_primitive_type("int") BFunc23 = new_function_type((BVoidP,), BInt, False) f = cast(BFunc23, _testfunc(23)) res = f(b"foo") assert res == 1000 * ord(b'f') res = f(cast(BVoidP, 0)) # NULL assert res == -42 pytest.raises(TypeError, f, None) pytest.raises(TypeError, f, 0) pytest.raises(TypeError, f, 0.0) def test_call_function_23_bis(): # declaring the function as int(unsigned char*) BUChar = new_primitive_type("unsigned char") BUCharP = new_pointer_type(BUChar) BInt = new_primitive_type("int") BFunc23 = new_function_type((BUCharP,), BInt, False) f = cast(BFunc23, _testfunc(23)) res = f(b"foo") assert res == 1000 * ord(b'f') def test_call_function_23_bool_array(): # declaring the function as int(_Bool*) BBool = new_primitive_type("_Bool") BBoolP = new_pointer_type(BBool) BInt = new_primitive_type("int") BFunc23 = new_function_type((BBoolP,), BInt, False) f = cast(BFunc23, _testfunc(23)) res = f(b"\x01\x01") assert res == 1000 pytest.raises(ValueError, f, b"\x02\x02") def test_cannot_pass_struct_with_array_of_length_0(): BInt = new_primitive_type("int") BArray0 = new_array_type(new_pointer_type(BInt), 0) BStruct = new_struct_type("struct foo") BStructP = new_pointer_type(BStruct) complete_struct_or_union(BStruct, [('a', BArray0)]) BFunc = new_function_type((BStruct,), BInt, False) pytest.raises(NotImplementedError, cast(BFunc, 123), cast(BStructP, 123)) BFunc2 = new_function_type((BInt,), BStruct, False) pytest.raises(NotImplementedError, cast(BFunc2, 123), 123) def test_call_function_9(): BInt = new_primitive_type("int") BFunc9 = new_function_type((BInt,), BInt, True) # vararg f = cast(BFunc9, _testfunc(9)) assert f(0) == 0 assert f(1, cast(BInt, 42)) == 42 assert f(2, cast(BInt, 40), cast(BInt, 2)) == 42 pytest.raises(TypeError, f, 1, 42) pytest.raises(TypeError, f, 2, None) # promotion of chars and shorts to ints BSChar = new_primitive_type("signed char") BUChar = new_primitive_type("unsigned char") BSShort = new_primitive_type("short") assert f(3, cast(BSChar, -3), cast(BUChar, 200), cast(BSShort, -5)) == 192 def test_call_function_24(): BFloat = new_primitive_type("float") BFloatComplex = new_primitive_type("float _Complex") BFunc3 = new_function_type((BFloat, BFloat), BFloatComplex, False) if 0: # libffi returning nonsense silently, so logic disabled for now f = cast(BFunc3, _testfunc(24)) result = f(1.25, 5.1) assert type(result) == complex assert result.real == 1.25 # exact assert (result.imag != 2*5.1) and (abs(result.imag - 2*5.1) < 1e-5) # inexact else: f = cast(BFunc3, _testfunc(9)) pytest.raises(NotImplementedError, f, 12.3, 34.5) def test_call_function_25(): BDouble = new_primitive_type("double") BDoubleComplex = new_primitive_type("double _Complex") BFunc3 = new_function_type((BDouble, BDouble), BDoubleComplex, False) if 0: # libffi returning nonsense silently, so logic disabled for now f = cast(BFunc3, _testfunc(25)) result = f(1.25, 5.1) assert type(result) == complex assert result.real == 1.25 # exact assert (result.imag != 2*5.1) and (abs(result.imag - 2*5.1) < 1e-10) # inexact else: f = cast(BFunc3, _testfunc(9)) pytest.raises(NotImplementedError, f, 12.3, 34.5) def test_cannot_call_with_a_autocompleted_struct(): BSChar = new_primitive_type("signed char") BDouble = new_primitive_type("double") BStruct = new_struct_type("struct foo") BStructPtr = new_pointer_type(BStruct) complete_struct_or_union(BStruct, [('c', BDouble, -1, 8), ('a', BSChar, -1, 2), ('b', BSChar, -1, 0)]) BFunc = new_function_type((BStruct,), BDouble) # internally not callable dummy_func = cast(BFunc, 42) e = pytest.raises(NotImplementedError, dummy_func, "?") msg = ("ctype 'struct foo' not supported as argument. It is a struct " 'declared with "...;", but the C calling convention may depend ' "on the missing fields; or, it contains anonymous struct/unions. " "Such structs are only supported as argument if the function is " "'API mode' and non-variadic (i.e. declared inside ffibuilder." "cdef()+ffibuilder.set_source() and not taking a final '...' " "argument)") assert str(e.value) == msg def test_new_charp(): BChar = new_primitive_type("char") BCharP = new_pointer_type(BChar) BCharA = new_array_type(BCharP, None) x = newp(BCharA, 42) assert len(x) == 42 x = newp(BCharA, b"foobar") assert len(x) == 7 def test_load_and_call_function(): BChar = new_primitive_type("char") BCharP = new_pointer_type(BChar) BLong = new_primitive_type("long") BFunc = new_function_type((BCharP,), BLong, False) ll = find_and_load_library('c') strlen = ll.load_function(BFunc, "strlen") input = newp(new_array_type(BCharP, None), b"foobar") assert strlen(input) == 6 # assert strlen(b"foobarbaz") == 9 # BVoidP = new_pointer_type(new_void_type()) strlenaddr = ll.load_function(BVoidP, "strlen") assert strlenaddr == cast(BVoidP, strlen) def test_read_variable(): ## FIXME: this test assumes glibc specific behavior, it's not compliant with C standard ## https://bugs.pypy.org/issue1643 if not sys.platform.startswith("linux"): pytest.skip("untested") BVoidP = new_pointer_type(new_void_type()) ll = find_and_load_library('c') stderr = ll.read_variable(BVoidP, "stderr") assert stderr == cast(BVoidP, _testfunc(8)) # ll.close_lib() pytest.raises(ValueError, ll.read_variable, BVoidP, "stderr") def test_read_variable_as_unknown_length_array(): ## FIXME: this test assumes glibc specific behavior, it's not compliant with C standard ## https://bugs.pypy.org/issue1643 if not sys.platform.startswith("linux"): pytest.skip("untested") BCharP = new_pointer_type(new_primitive_type("char")) BArray = new_array_type(BCharP, None) ll = find_and_load_library('c') stderr = ll.read_variable(BArray, "stderr") assert repr(stderr).startswith("= (3, 8): ipattern = ipattern38 if sys.version_info >= (3, 11): ipattern = ipattern311 or ipattern38 str, pattern = istr, ipattern while '$' in pattern: i = pattern.index('$') assert str[:i] == pattern[:i] j = str.find(pattern[i+1], i) assert i + 1 <= j <= str.find('\n', i) str = str[j:] pattern = pattern[i+1:] assert str == pattern return True def check_value(x): if x == 10000: raise ValueError(42) def Zcb1(x): check_value(x) return x * 3 BShort = new_primitive_type("short") BFunc = new_function_type((BShort,), BShort, False) f = callback(BFunc, Zcb1, -42) # seen = [] oops_result = None def oops(*args): seen.append(args) return oops_result ff = callback(BFunc, Zcb1, -42, oops) # orig_stderr = sys.stderr orig_getline = linecache.getline try: linecache.getline = lambda *args: 'LINE' # hack: speed up PyPy tests sys.stderr = cStringIO.StringIO() if hasattr(sys, '__unraisablehook__'): # work around pytest sys.unraisablehook = sys.__unraisablehook__ # on recent CPythons assert f(100) == 300 assert sys.stderr.getvalue() == '' assert f(10000) == -42 assert matches(sys.stderr.getvalue(), """\ From cffi callback : Traceback (most recent call last): File "$", line $, in Zcb1 $ File "$", line $, in check_value $ ValueError: 42 """, """\ Exception ignored from cffi callback : Traceback (most recent call last): File "$", line $, in Zcb1 $ File "$", line $, in check_value $ ValueError: 42 """) sys.stderr = cStringIO.StringIO() bigvalue = 20000 assert f(bigvalue) == -42 assert matches(sys.stderr.getvalue(), """\ From cffi callback : Trying to convert the result back to C: OverflowError: integer 60000 does not fit 'short' """, """\ Exception ignored from cffi callback , trying to convert the result back to C: Traceback (most recent call last): File "$", line $, in test_callback_exception $ OverflowError: integer 60000 does not fit 'short' """) sys.stderr = cStringIO.StringIO() bigvalue = 20000 assert len(seen) == 0 assert ff(bigvalue) == -42 assert sys.stderr.getvalue() == "" assert len(seen) == 1 exc, val, tb = seen[0] assert exc is OverflowError assert str(val) == "integer 60000 does not fit 'short'" # sys.stderr = cStringIO.StringIO() bigvalue = 20000 del seen[:] oops_result = 81 assert ff(bigvalue) == 81 oops_result = None assert sys.stderr.getvalue() == "" assert len(seen) == 1 exc, val, tb = seen[0] assert exc is OverflowError assert str(val) == "integer 60000 does not fit 'short'" # sys.stderr = cStringIO.StringIO() bigvalue = 20000 del seen[:] oops_result = "xy" # not None and not an int! assert ff(bigvalue) == -42 oops_result = None assert matches(sys.stderr.getvalue(), """\ From cffi callback : Trying to convert the result back to C: OverflowError: integer 60000 does not fit 'short' During the call to 'onerror', another exception occurred: TypeError: $integer$ """, """\ Exception ignored from cffi callback , trying to convert the result back to C: Traceback (most recent call last): File "$", line $, in test_callback_exception $ OverflowError: integer 60000 does not fit 'short' Exception ignored during handling of the above exception by 'onerror': Traceback (most recent call last): File "$", line $, in test_callback_exception $ TypeError: $integer$ """) # sys.stderr = cStringIO.StringIO() seen = "not a list" # this makes the oops() function crash assert ff(bigvalue) == -42 # the $ after the AttributeError message are for the suggestions that # will be added in Python 3.10 assert matches(sys.stderr.getvalue(), """\ From cffi callback : Trying to convert the result back to C: OverflowError: integer 60000 does not fit 'short' During the call to 'onerror', another exception occurred: Traceback (most recent call last): File "$", line $, in oops $ AttributeError: 'str' object has no attribute 'append$ """, """\ Exception ignored from cffi callback , trying to convert the result back to C: Traceback (most recent call last): File "$", line $, in test_callback_exception $ OverflowError: integer 60000 does not fit 'short' Exception ignored during handling of the above exception by 'onerror': Traceback (most recent call last): File "$", line $, in oops $ AttributeError: 'str' object has no attribute 'append$ """, """\ Exception ignored from cffi callback , trying to convert the result back to C: Traceback (most recent call last): File "$", line $, in test_callback_exception $ OverflowError: integer 60000 does not fit 'short' Exception ignored during handling of the above exception by 'onerror': Traceback (most recent call last): File "$", line $, in oops $ $ AttributeError: 'str' object has no attribute 'append$ """) finally: sys.stderr = orig_stderr linecache.getline = orig_getline def test_callback_return_type(): for rettype in ["signed char", "short", "int", "long", "long long", "unsigned char", "unsigned short", "unsigned int", "unsigned long", "unsigned long long"]: BRet = new_primitive_type(rettype) def cb(n): return n + 1 BFunc = new_function_type((BRet,), BRet) f = callback(BFunc, cb, 42) assert f(41) == 42 if rettype.startswith("unsigned "): min = 0 max = (1 << (8*sizeof(BRet))) - 1 else: min = -(1 << (8*sizeof(BRet)-1)) max = (1 << (8*sizeof(BRet)-1)) - 1 assert f(min) == min + 1 assert f(max - 1) == max assert f(max) == 42 def test_a_lot_of_callbacks(): BIGNUM = 10000 if 'PY_DOT_PY' in globals(): BIGNUM = 100 # tests on pypy # BInt = new_primitive_type("int") BFunc = new_function_type((BInt,), BInt, False) def make_callback(m): def cb(n): return n + m return callback(BFunc, cb, 42) # 'cb' goes out of scope # flist = [make_callback(i) for i in range(BIGNUM)] for i, f in enumerate(flist): assert f(-142) == -142 + i def test_callback_receiving_tiny_struct(): BSChar = new_primitive_type("signed char") BInt = new_primitive_type("int") BStruct = new_struct_type("struct foo") BStructPtr = new_pointer_type(BStruct) complete_struct_or_union(BStruct, [('a', BSChar, -1), ('b', BSChar, -1)]) def cb(s): return s.a + 10 * s.b BFunc = new_function_type((BStruct,), BInt) f = callback(BFunc, cb) p = newp(BStructPtr, [-2, -4]) n = f(p[0]) assert n == -42 def test_callback_returning_tiny_struct(): BSChar = new_primitive_type("signed char") BInt = new_primitive_type("int") BStruct = new_struct_type("struct foo") BStructPtr = new_pointer_type(BStruct) complete_struct_or_union(BStruct, [('a', BSChar, -1), ('b', BSChar, -1)]) def cb(n): return newp(BStructPtr, [-n, -3*n])[0] BFunc = new_function_type((BInt,), BStruct) f = callback(BFunc, cb) s = f(10) assert typeof(s) is BStruct assert repr(s) == "" assert s.a == -10 assert s.b == -30 def test_callback_receiving_struct(): BSChar = new_primitive_type("signed char") BInt = new_primitive_type("int") BDouble = new_primitive_type("double") BStruct = new_struct_type("struct foo") BStructPtr = new_pointer_type(BStruct) complete_struct_or_union(BStruct, [('a', BSChar, -1), ('b', BDouble, -1)]) def cb(s): return s.a + int(s.b) BFunc = new_function_type((BStruct,), BInt) f = callback(BFunc, cb) p = newp(BStructPtr, [-2, 44.444]) n = f(p[0]) assert n == 42 def test_callback_returning_struct(): BSChar = new_primitive_type("signed char") BInt = new_primitive_type("int") BDouble = new_primitive_type("double") BStruct = new_struct_type("struct foo") BStructPtr = new_pointer_type(BStruct) complete_struct_or_union(BStruct, [('a', BSChar, -1), ('b', BDouble, -1)]) def cb(n): return newp(BStructPtr, [-n, 1E-42])[0] BFunc = new_function_type((BInt,), BStruct) f = callback(BFunc, cb) s = f(10) assert typeof(s) is BStruct assert repr(s) in ["", ""] assert s.a == -10 assert s.b == 1E-42 def test_callback_receiving_big_struct(): BInt = new_primitive_type("int") BStruct = new_struct_type("struct foo") BStructPtr = new_pointer_type(BStruct) complete_struct_or_union(BStruct, [('a', BInt, -1), ('b', BInt, -1), ('c', BInt, -1), ('d', BInt, -1), ('e', BInt, -1), ('f', BInt, -1), ('g', BInt, -1), ('h', BInt, -1), ('i', BInt, -1), ('j', BInt, -1)]) def cb(s): for i, name in enumerate("abcdefghij"): assert getattr(s, name) == 13 - i return 42 BFunc = new_function_type((BStruct,), BInt) f = callback(BFunc, cb) p = newp(BStructPtr, list(range(13, 3, -1))) n = f(p[0]) assert n == 42 def test_callback_returning_big_struct(): BInt = new_primitive_type("int") BStruct = new_struct_type("struct foo") BStructPtr = new_pointer_type(BStruct) complete_struct_or_union(BStruct, [('a', BInt, -1), ('b', BInt, -1), ('c', BInt, -1), ('d', BInt, -1), ('e', BInt, -1), ('f', BInt, -1), ('g', BInt, -1), ('h', BInt, -1), ('i', BInt, -1), ('j', BInt, -1)]) def cb(): return newp(BStructPtr, list(range(13, 3, -1)))[0] BFunc = new_function_type((), BStruct) f = callback(BFunc, cb) s = f() assert typeof(s) is BStruct assert repr(s) in ["", ""] for i, name in enumerate("abcdefghij"): assert getattr(s, name) == 13 - i def test_callback_returning_void(): BVoid = new_void_type() BFunc = new_function_type((), BVoid, False) def cb(): seen.append(42) f = callback(BFunc, cb) seen = [] f() assert seen == [42] pytest.raises(TypeError, callback, BFunc, cb, -42) def test_enum_type(): BUInt = new_primitive_type("unsigned int") BEnum = new_enum_type("foo", (), (), BUInt) assert repr(BEnum) == "" assert BEnum.kind == "enum" assert BEnum.cname == "foo" assert BEnum.elements == {} # BInt = new_primitive_type("int") BEnum = new_enum_type("enum foo", ('def', 'c', 'ab'), (0, 1, -20), BInt) assert BEnum.kind == "enum" assert BEnum.cname == "enum foo" assert BEnum.elements == {-20: 'ab', 0: 'def', 1: 'c'} # 'elements' is not the real dict, but merely a copy BEnum.elements[2] = '??' assert BEnum.elements == {-20: 'ab', 0: 'def', 1: 'c'} # BEnum = new_enum_type("enum bar", ('ab', 'cd'), (5, 5), BUInt) assert BEnum.elements == {5: 'ab'} assert BEnum.relements == {'ab': 5, 'cd': 5} def test_cast_to_enum(): BInt = new_primitive_type("int") BEnum = new_enum_type("enum foo", ('def', 'c', 'ab'), (0, 1, -20), BInt) assert sizeof(BEnum) == sizeof(BInt) e = cast(BEnum, 0) assert repr(e) == "" assert repr(cast(BEnum, -42)) == "" assert repr(cast(BEnum, -20)) == "" assert string(e) == 'def' assert string(cast(BEnum, -20)) == 'ab' assert int(cast(BEnum, 1)) == 1 assert int(cast(BEnum, 0)) == 0 assert int(cast(BEnum, -242 + 2**128)) == -242 assert string(cast(BEnum, -242 + 2**128)) == '-242' # BUInt = new_primitive_type("unsigned int") BEnum = new_enum_type("enum bar", ('def', 'c', 'ab'), (0, 1, 20), BUInt) e = cast(BEnum, -1) assert repr(e) == "" # unsigned int # BLong = new_primitive_type("long") BEnum = new_enum_type("enum baz", (), (), BLong) assert sizeof(BEnum) == sizeof(BLong) e = cast(BEnum, -1) assert repr(e) == "" def test_enum_with_non_injective_mapping(): BInt = new_primitive_type("int") BEnum = new_enum_type("enum foo", ('ab', 'cd'), (7, 7), BInt) e = cast(BEnum, 7) assert repr(e) == "" assert string(e) == 'ab' def test_enum_in_struct(): BInt = new_primitive_type("int") BEnum = new_enum_type("enum foo", ('def', 'c', 'ab'), (0, 1, -20), BInt) BStruct = new_struct_type("struct bar") BStructPtr = new_pointer_type(BStruct) complete_struct_or_union(BStruct, [('a1', BEnum, -1)]) p = newp(BStructPtr, [-20]) assert p.a1 == -20 p = newp(BStructPtr, [12]) assert p.a1 == 12 e = pytest.raises(TypeError, newp, BStructPtr, [None]) msg = str(e.value) assert ("an integer is required" in msg or # CPython "unsupported operand type for int(): 'NoneType'" in msg or # old PyPys "expected integer, got NoneType object" in msg) # newer PyPys with pytest.raises(TypeError): p.a1 = "def" if sys.version_info < (3,): BEnum2 = new_enum_type(unicode("foo"), (unicode('abc'),), (5,), BInt) assert string(cast(BEnum2, 5)) == 'abc' assert type(string(cast(BEnum2, 5))) is str def test_enum_overflow(): max_uint = 2 ** (size_of_int()*8) - 1 max_int = max_uint // 2 max_ulong = 2 ** (size_of_long()*8) - 1 max_long = max_ulong // 2 for BPrimitive in [new_primitive_type("int"), new_primitive_type("unsigned int"), new_primitive_type("long"), new_primitive_type("unsigned long")]: for x in [max_uint, max_int, max_ulong, max_long]: for testcase in [x, x+1, -x-1, -x-2]: if int(cast(BPrimitive, testcase)) == testcase: # fits BEnum = new_enum_type("foo", ("AA",), (testcase,), BPrimitive) assert int(cast(BEnum, testcase)) == testcase else: # overflows pytest.raises(OverflowError, new_enum_type, "foo", ("AA",), (testcase,), BPrimitive) def test_callback_returning_enum(): BInt = new_primitive_type("int") BEnum = new_enum_type("foo", ('def', 'c', 'ab'), (0, 1, -20), BInt) def cb(n): if n & 1: return cast(BEnum, n) else: return n BFunc = new_function_type((BInt,), BEnum) f = callback(BFunc, cb) assert f(0) == 0 assert f(1) == 1 assert f(-20) == -20 assert f(20) == 20 assert f(21) == 21 def test_callback_returning_enum_unsigned(): BInt = new_primitive_type("int") BUInt = new_primitive_type("unsigned int") BEnum = new_enum_type("foo", ('def', 'c', 'ab'), (0, 1, 20), BUInt) def cb(n): if n & 1: return cast(BEnum, n) else: return n BFunc = new_function_type((BInt,), BEnum) f = callback(BFunc, cb) assert f(0) == 0 assert f(1) == 1 assert f(-21) == 2**32 - 21 assert f(20) == 20 assert f(21) == 21 def test_callback_returning_char(): BInt = new_primitive_type("int") BChar = new_primitive_type("char") def cb(n): return bytechr(n) BFunc = new_function_type((BInt,), BChar) f = callback(BFunc, cb) assert f(0) == b'\x00' assert f(255) == b'\xFF' def _hacked_pypy_uni4(): pyuni4 = {1: True, 2: False}[len(u+'\U00012345')] return 'PY_DOT_PY' in globals() and not pyuni4 def test_callback_returning_wchar_t(): BInt = new_primitive_type("int") BWChar = new_primitive_type("wchar_t") def cb(n): if n == -1: return u+'\U00012345' if n == -2: raise ValueError return unichr(n) BFunc = new_function_type((BInt,), BWChar) f = callback(BFunc, cb) assert f(0) == unichr(0) assert f(255) == unichr(255) assert f(0x1234) == u+'\u1234' if sizeof(BWChar) == 4 and not _hacked_pypy_uni4(): assert f(-1) == u+'\U00012345' assert f(-2) == u+'\x00' # and an exception printed to stderr def test_struct_with_bitfields(): BLong = new_primitive_type("long") BStruct = new_struct_type("struct foo") LONGBITS = 8 * sizeof(BLong) complete_struct_or_union(BStruct, [('a1', BLong, 1), ('a2', BLong, 2), ('a3', BLong, 3), ('a4', BLong, LONGBITS - 5)]) d = BStruct.fields assert d[0][1].offset == d[1][1].offset == d[2][1].offset == 0 assert d[3][1].offset == sizeof(BLong) def f(m, r): if sys.byteorder == 'little': return r else: return LONGBITS - m - r assert d[0][1].bitshift == f(1, 0) assert d[0][1].bitsize == 1 assert d[1][1].bitshift == f(2, 1) assert d[1][1].bitsize == 2 assert d[2][1].bitshift == f(3, 3) assert d[2][1].bitsize == 3 assert d[3][1].bitshift == f(LONGBITS - 5, 0) assert d[3][1].bitsize == LONGBITS - 5 assert sizeof(BStruct) == 2 * sizeof(BLong) assert alignof(BStruct) == alignof(BLong) def test_bitfield_instance(): BInt = new_primitive_type("int") BUnsignedInt = new_primitive_type("unsigned int") BStruct = new_struct_type("struct foo") complete_struct_or_union(BStruct, [('a1', BInt, 1), ('a2', BUnsignedInt, 2), ('a3', BInt, 3)]) p = newp(new_pointer_type(BStruct), None) p.a1 = -1 assert p.a1 == -1 p.a1 = 0 with pytest.raises(OverflowError): p.a1 = 2 assert p.a1 == 0 # p.a1 = -1 p.a2 = 3 p.a3 = -4 with pytest.raises(OverflowError): p.a3 = 4 with pytest.raises(OverflowError) as e: p.a3 = -5 assert str(e.value) == ("value -5 outside the range allowed by the " "bit field width: -4 <= x <= 3") assert p.a1 == -1 and p.a2 == 3 and p.a3 == -4 # # special case for convenience: "int x:1", while normally signed, # allows also setting the value "1" (it still gets read back as -1) p.a1 = 1 assert p.a1 == -1 with pytest.raises(OverflowError) as e: p.a1 = -2 assert str(e.value) == ("value -2 outside the range allowed by the " "bit field width: -1 <= x <= 1") def test_bitfield_instance_init(): BInt = new_primitive_type("int") BStruct = new_struct_type("struct foo") complete_struct_or_union(BStruct, [('a1', BInt, 1)]) p = newp(new_pointer_type(BStruct), [-1]) assert p.a1 == -1 p = newp(new_pointer_type(BStruct), {'a1': -1}) assert p.a1 == -1 # BUnion = new_union_type("union bar") complete_struct_or_union(BUnion, [('a1', BInt, 1)]) p = newp(new_pointer_type(BUnion), [-1]) assert p.a1 == -1 def test_weakref(): import _weakref BInt = new_primitive_type("int") BPtr = new_pointer_type(BInt) rlist = [_weakref.ref(BInt), _weakref.ref(newp(BPtr, 42)), _weakref.ref(cast(BPtr, 42)), _weakref.ref(cast(BInt, 42)), _weakref.ref(buffer(newp(BPtr, 42))), ] for i in range(5): import gc; gc.collect() if [r() for r in rlist] == [None for r in rlist]: break def test_no_inheritance(): BInt = new_primitive_type("int") try: class foo(type(BInt)): pass except TypeError: pass else: raise AssertionError x = cast(BInt, 42) try: class foo(type(x)): pass except TypeError: pass else: raise AssertionError def test_assign_string(): BChar = new_primitive_type("char") BArray1 = new_array_type(new_pointer_type(BChar), 5) BArray2 = new_array_type(new_pointer_type(BArray1), 5) a = newp(BArray2, [b"abc", b"de", b"ghij"]) assert string(a[1]) == b"de" assert string(a[2]) == b"ghij" a[2] = b"." assert string(a[2]) == b"." a[2] = b"12345" assert string(a[2]) == b"12345" with pytest.raises(IndexError) as e: a[2] = b"123456" assert 'char[5]' in str(e.value) assert 'got 6 characters' in str(e.value) def test_add_error(): x = cast(new_primitive_type("int"), 42) with pytest.raises(TypeError): x + 1 with pytest.raises(TypeError): x - 1 def test_void_errors(): pytest.raises(ValueError, alignof, new_void_type()) pytest.raises(TypeError, newp, new_pointer_type(new_void_type()), None) def test_too_many_items(): BChar = new_primitive_type("char") BArray = new_array_type(new_pointer_type(BChar), 5) pytest.raises(IndexError, newp, BArray, tuple(b'123456')) pytest.raises(IndexError, newp, BArray, list(b'123456')) pytest.raises(IndexError, newp, BArray, b'123456') BStruct = new_struct_type("struct foo") complete_struct_or_union(BStruct, []) pytest.raises(TypeError, newp, new_pointer_type(BStruct), b'') pytest.raises(ValueError, newp, new_pointer_type(BStruct), [b'1']) def test_more_type_errors(): BInt = new_primitive_type("int") BChar = new_primitive_type("char") BArray = new_array_type(new_pointer_type(BChar), 5) pytest.raises(TypeError, newp, BArray, 12.34) BArray = new_array_type(new_pointer_type(BInt), 5) pytest.raises(TypeError, newp, BArray, 12.34) BFloat = new_primitive_type("float") pytest.raises(TypeError, cast, BFloat, newp(BArray, None)) def test_more_overflow_errors(): BUInt = new_primitive_type("unsigned int") pytest.raises(OverflowError, newp, new_pointer_type(BUInt), -1) pytest.raises(OverflowError, newp, new_pointer_type(BUInt), 2**32) def test_newp_copying(): """Test that we can do newp(, ) for most types, including same-type arrays. """ BInt = new_primitive_type("int") p = newp(new_pointer_type(BInt), cast(BInt, 42)) assert p[0] == 42 # BUInt = new_primitive_type("unsigned int") p = newp(new_pointer_type(BUInt), cast(BUInt, 42)) assert p[0] == 42 # BChar = new_primitive_type("char") p = newp(new_pointer_type(BChar), cast(BChar, '!')) assert p[0] == b'!' # BFloat = new_primitive_type("float") p = newp(new_pointer_type(BFloat), cast(BFloat, 12.25)) assert p[0] == 12.25 # BStruct = new_struct_type("struct foo_s") BStructPtr = new_pointer_type(BStruct) complete_struct_or_union(BStruct, [('a1', BInt, -1)]) s1 = newp(BStructPtr, [42]) p1 = newp(new_pointer_type(BStructPtr), s1) assert p1[0] == s1 # BArray = new_array_type(new_pointer_type(BInt), None) a1 = newp(BArray, [1, 2, 3, 4]) pytest.raises(TypeError, newp, BArray, a1) BArray6 = new_array_type(new_pointer_type(BInt), 6) a1 = newp(BArray6, [10, 20, 30]) a2 = newp(BArray6, a1) assert list(a2) == [10, 20, 30, 0, 0, 0] # s1 = newp(BStructPtr, [42]) s2 = newp(BStructPtr, s1[0]) assert s2.a1 == 42 # BUnion = new_union_type("union foo_u") BUnionPtr = new_pointer_type(BUnion) complete_struct_or_union(BUnion, [('a1', BInt, -1)]) u1 = newp(BUnionPtr, [42]) u2 = newp(BUnionPtr, u1[0]) assert u2.a1 == 42 # BFunc = new_function_type((BInt,), BUInt) p1 = cast(BFunc, 42) p2 = newp(new_pointer_type(BFunc), p1) assert p2[0] == p1 def test_string(): BChar = new_primitive_type("char") assert string(cast(BChar, 42)) == b'*' assert string(cast(BChar, 0)) == b'\x00' BCharP = new_pointer_type(BChar) BArray = new_array_type(BCharP, 10) a = newp(BArray, b"hello") assert len(a) == 10 assert string(a) == b"hello" p = a + 2 assert string(p) == b"llo" assert string(newp(new_array_type(BCharP, 4), b"abcd")) == b"abcd" pytest.raises(RuntimeError, string, cast(BCharP, 0)) assert string(a, 4) == b"hell" assert string(a, 5) == b"hello" assert string(a, 6) == b"hello" def test_string_byte(): BByte = new_primitive_type("signed char") assert string(cast(BByte, 42)) == b'*' assert string(cast(BByte, 0)) == b'\x00' BArray = new_array_type(new_pointer_type(BByte), None) a = newp(BArray, [65, 66, 67]) assert type(string(a)) is bytes and string(a) == b'ABC' # BByte = new_primitive_type("unsigned char") assert string(cast(BByte, 42)) == b'*' assert string(cast(BByte, 0)) == b'\x00' BArray = new_array_type(new_pointer_type(BByte), None) a = newp(BArray, [65, 66, 67]) assert type(string(a)) is bytes and string(a) == b'ABC' if 'PY_DOT_PY' not in globals() and sys.version_info < (3,): assert string(a, 8).startswith(b'ABC') # may contain additional garbage def test_string_wchar(): for typename in ["wchar_t", "char16_t", "char32_t"]: _test_string_wchar_variant(typename) def _test_string_wchar_variant(typename): BWChar = new_primitive_type(typename) assert string(cast(BWChar, 42)) == u+'*' assert string(cast(BWChar, 0x4253)) == u+'\u4253' assert string(cast(BWChar, 0)) == u+'\x00' BArray = new_array_type(new_pointer_type(BWChar), None) a = newp(BArray, [u+'A', u+'B', u+'C']) assert type(string(a)) is unicode and string(a) == u+'ABC' if 'PY_DOT_PY' not in globals() and sys.version_info < (3,): try: # may contain additional garbage assert string(a, 8).startswith(u+'ABC') except ValueError: # garbage contains values > 0x10FFFF assert sizeof(BWChar) == 4 def test_string_typeerror(): BShort = new_primitive_type("short") BArray = new_array_type(new_pointer_type(BShort), None) a = newp(BArray, [65, 66, 67]) pytest.raises(TypeError, string, a) def test_bug_convert_to_ptr(): BChar = new_primitive_type("char") BCharP = new_pointer_type(BChar) BDouble = new_primitive_type("double") x = cast(BDouble, 42) pytest.raises(TypeError, newp, new_pointer_type(BCharP), x) def test_set_struct_fields(): BChar = new_primitive_type("char") BCharP = new_pointer_type(BChar) BCharArray10 = new_array_type(BCharP, 10) BStruct = new_struct_type("struct foo") BStructPtr = new_pointer_type(BStruct) complete_struct_or_union(BStruct, [('a1', BCharArray10, -1)]) p = newp(BStructPtr, None) assert string(p.a1) == b'' p.a1 = b'foo' assert string(p.a1) == b'foo' assert list(p.a1) == [b'f', b'o', b'o'] + [b'\x00'] * 7 p.a1 = [b'x', b'y'] assert string(p.a1) == b'xyo' def test_invalid_function_result_types(): BFunc = new_function_type((), new_void_type()) BArray = new_array_type(new_pointer_type(BFunc), 5) # works new_function_type((), BFunc) # works new_function_type((), new_primitive_type("int")) new_function_type((), new_pointer_type(BFunc)) BUnion = new_union_type("union foo_u") complete_struct_or_union(BUnion, []) BFunc = new_function_type((), BUnion) pytest.raises(NotImplementedError, cast(BFunc, 123)) pytest.raises(TypeError, new_function_type, (), BArray) def test_struct_return_in_func(): BChar = new_primitive_type("char") BShort = new_primitive_type("short") BFloat = new_primitive_type("float") BDouble = new_primitive_type("double") BInt = new_primitive_type("int") BStruct = new_struct_type("struct foo_s") complete_struct_or_union(BStruct, [('a1', BChar, -1), ('a2', BShort, -1)]) BFunc10 = new_function_type((BInt,), BStruct) f = cast(BFunc10, _testfunc(10)) s = f(40) assert repr(s) == "" assert s.a1 == bytechr(40) assert s.a2 == 40 * 40 # BStruct11 = new_struct_type("struct test11") complete_struct_or_union(BStruct11, [('a1', BInt, -1), ('a2', BInt, -1)]) BFunc11 = new_function_type((BInt,), BStruct11) f = cast(BFunc11, _testfunc(11)) s = f(40) assert repr(s) == "" assert s.a1 == 40 assert s.a2 == 40 * 40 # BStruct12 = new_struct_type("struct test12") complete_struct_or_union(BStruct12, [('a1', BDouble, -1), ]) BFunc12 = new_function_type((BInt,), BStruct12) f = cast(BFunc12, _testfunc(12)) s = f(40) assert repr(s) == "" assert s.a1 == 40.0 # BStruct13 = new_struct_type("struct test13") complete_struct_or_union(BStruct13, [('a1', BInt, -1), ('a2', BInt, -1), ('a3', BInt, -1)]) BFunc13 = new_function_type((BInt,), BStruct13) f = cast(BFunc13, _testfunc(13)) s = f(40) assert repr(s) == "" assert s.a1 == 40 assert s.a2 == 40 * 40 assert s.a3 == 40 * 40 * 40 # BStruct14 = new_struct_type("struct test14") complete_struct_or_union(BStruct14, [('a1', BFloat, -1), ]) BFunc14 = new_function_type((BInt,), BStruct14) f = cast(BFunc14, _testfunc(14)) s = f(40) assert repr(s) == "" assert s.a1 == 40.0 # BStruct15 = new_struct_type("struct test15") complete_struct_or_union(BStruct15, [('a1', BFloat, -1), ('a2', BInt, -1)]) BFunc15 = new_function_type((BInt,), BStruct15) f = cast(BFunc15, _testfunc(15)) s = f(40) assert repr(s) == "" assert s.a1 == 40.0 assert s.a2 == 40 * 40 # BStruct16 = new_struct_type("struct test16") complete_struct_or_union(BStruct16, [('a1', BFloat, -1), ('a2', BFloat, -1)]) BFunc16 = new_function_type((BInt,), BStruct16) f = cast(BFunc16, _testfunc(16)) s = f(40) assert repr(s) == "" assert s.a1 == 40.0 assert s.a2 == -40.0 # BStruct17 = new_struct_type("struct test17") complete_struct_or_union(BStruct17, [('a1', BInt, -1), ('a2', BFloat, -1)]) BFunc17 = new_function_type((BInt,), BStruct17) f = cast(BFunc17, _testfunc(17)) s = f(40) assert repr(s) == "" assert s.a1 == 40 assert s.a2 == 40.0 * 40.0 # BStruct17Ptr = new_pointer_type(BStruct17) BFunc18 = new_function_type((BStruct17Ptr,), BInt) f = cast(BFunc18, _testfunc(18)) x = f([[40, 2.5]]) assert x == 42 x = f([{'a2': 43.1}]) assert x == 43 def test_cast_with_functionptr(): BFunc = new_function_type((), new_void_type()) BFunc2 = new_function_type((), new_primitive_type("short")) BCharP = new_pointer_type(new_primitive_type("char")) BIntP = new_pointer_type(new_primitive_type("int")) BStruct = new_struct_type("struct foo") BStructPtr = new_pointer_type(BStruct) complete_struct_or_union(BStruct, [('a1', BFunc, -1)]) newp(BStructPtr, [cast(BFunc, 0)]) newp(BStructPtr, [cast(BCharP, 0)]) pytest.raises(TypeError, newp, BStructPtr, [cast(BIntP, 0)]) pytest.raises(TypeError, newp, BStructPtr, [cast(BFunc2, 0)]) def test_wchar(): _test_wchar_variant("wchar_t") if sys.platform.startswith("linux"): BWChar = new_primitive_type("wchar_t") assert sizeof(BWChar) == 4 # wchar_t is often signed on Linux, but not always (e.g. on ARM) assert int(cast(BWChar, -1)) in (-1, 4294967295) def test_char16(): BChar16 = new_primitive_type("char16_t") assert sizeof(BChar16) == 2 _test_wchar_variant("char16_t") assert int(cast(BChar16, -1)) == 0xffff # always unsigned def test_char32(): BChar32 = new_primitive_type("char32_t") assert sizeof(BChar32) == 4 _test_wchar_variant("char32_t") assert int(cast(BChar32, -1)) == 0xffffffff # always unsigned def _test_wchar_variant(typename): BWChar = new_primitive_type(typename) BInt = new_primitive_type("int") pyuni4 = {1: True, 2: False}[len(u+'\U00012345')] wchar4 = {2: False, 4: True}[sizeof(BWChar)] assert str(cast(BWChar, 0x45)) == "" % ( typename, mandatory_u_prefix) assert str(cast(BWChar, 0x1234)) == "" % ( typename, mandatory_u_prefix) if not _hacked_pypy_uni4(): if wchar4: x = cast(BWChar, 0x12345) assert str(x) == "" % ( typename, mandatory_u_prefix) assert int(x) == 0x12345 else: x = cast(BWChar, 0x18345) assert str(x) == "" % ( typename, mandatory_u_prefix) assert int(x) == 0x8345 # BWCharP = new_pointer_type(BWChar) BStruct = new_struct_type("struct foo_s") BStructPtr = new_pointer_type(BStruct) complete_struct_or_union(BStruct, [('a1', BWChar, -1), ('a2', BWCharP, -1)]) s = newp(BStructPtr) s.a1 = u+'\x00' assert s.a1 == u+'\x00' with pytest.raises(TypeError): s.a1 = b'a' with pytest.raises(TypeError): s.a1 = bytechr(0xFF) s.a1 = u+'\u1234' assert s.a1 == u+'\u1234' if pyuni4: if wchar4: s.a1 = u+'\U00012345' assert s.a1 == u+'\U00012345' elif wchar4: if not _hacked_pypy_uni4(): s.a1 = cast(BWChar, 0x12345) assert s.a1 == u+'\ud808\udf45' s.a1 = u+'\ud807\udf44' assert s.a1 == u+'\U00011f44' else: with pytest.raises(TypeError): s.a1 = u+'\U00012345' # BWCharArray = new_array_type(BWCharP, None) a = newp(BWCharArray, u+'hello \u1234 world') assert len(a) == 14 # including the final null assert string(a) == u+'hello \u1234 world' a[13] = u+'!' assert string(a) == u+'hello \u1234 world!' assert str(a) == repr(a) assert a[6] == u+'\u1234' a[6] = u+'-' assert string(a) == u+'hello - world!' assert str(a) == repr(a) # if wchar4 and not _hacked_pypy_uni4(): u1 = u+'\U00012345\U00012346\U00012347' a = newp(BWCharArray, u1) assert len(a) == 4 assert string(a) == u1 assert len(list(a)) == 4 expected = [u+'\U00012345', u+'\U00012346', u+'\U00012347', unichr(0)] assert list(a) == expected got = [a[i] for i in range(4)] assert got == expected with pytest.raises(IndexError): a[4] # w = cast(BWChar, 'a') assert repr(w) == "" % (typename, mandatory_u_prefix) assert str(w) == repr(w) assert string(w) == u+'a' assert int(w) == ord('a') w = cast(BWChar, 0x1234) assert repr(w) == "" % (typename, mandatory_u_prefix) assert str(w) == repr(w) assert string(w) == u+'\u1234' assert int(w) == 0x1234 w = cast(BWChar, u+'\u8234') assert repr(w) == "" % (typename, mandatory_u_prefix) assert str(w) == repr(w) assert string(w) == u+'\u8234' assert int(w) == 0x8234 w = cast(BInt, u+'\u1234') assert repr(w) == "" if wchar4 and not _hacked_pypy_uni4(): w = cast(BWChar, u+'\U00012345') assert repr(w) == "" % ( typename, mandatory_u_prefix) assert str(w) == repr(w) assert string(w) == u+'\U00012345' assert int(w) == 0x12345 w = cast(BInt, u+'\U00012345') assert repr(w) == "" pytest.raises(TypeError, cast, BInt, u+'') pytest.raises(TypeError, cast, BInt, u+'XX') assert int(cast(BInt, u+'a')) == ord('a') # a = newp(BWCharArray, u+'hello - world') p = cast(BWCharP, a) assert string(p) == u+'hello - world' p[6] = u+'\u2345' assert string(p) == u+'hello \u2345 world' # s = newp(BStructPtr, [u+'\u1234', p]) assert s.a1 == u+'\u1234' assert s.a2 == p assert str(s.a2) == repr(s.a2) assert string(s.a2) == u+'hello \u2345 world' # q = cast(BWCharP, 0) assert str(q) == repr(q) pytest.raises(RuntimeError, string, q) # def cb(p): assert repr(p).startswith("" q = p[0] assert repr(q) == "" q.a1 = 123456 assert p.a1 == 123456 r = cast(BStructPtr, p) assert repr(r[0]).startswith("" assert q.a1 == 123456 def test_nokeepalive_struct(): BStruct = new_struct_type("struct foo") BStructPtr = new_pointer_type(BStruct) BStructPtrPtr = new_pointer_type(BStructPtr) complete_struct_or_union(BStruct, [('a1', new_primitive_type("int"), -1)]) p = newp(BStructPtr) pp = newp(BStructPtrPtr) pp[0] = p s = pp[0][0] assert repr(s).startswith("" assert sizeof(p) == 28 # BArray = new_array_type(new_pointer_type(BInt), 7) # int[7] p = newp(BArray, None) assert repr(p) == "" assert sizeof(p) == 28 def test_cannot_dereference_void(): BVoidP = new_pointer_type(new_void_type()) p = cast(BVoidP, 123456) with pytest.raises(TypeError): p[0] p = cast(BVoidP, 0) with pytest.raises((TypeError, RuntimeError)): p[0] def test_iter(): BInt = new_primitive_type("int") BIntP = new_pointer_type(BInt) BArray = new_array_type(BIntP, None) # int[] p = newp(BArray, 7) assert list(p) == list(iter(p)) == [0] * 7 # pytest.raises(TypeError, iter, cast(BInt, 5)) pytest.raises(TypeError, iter, cast(BIntP, 123456)) def test_cmp(): BInt = new_primitive_type("int") BIntP = new_pointer_type(BInt) BVoidP = new_pointer_type(new_void_type()) p = newp(BIntP, 123) q = cast(BInt, 124) assert (p == q) is False assert (p != q) is True assert (q == p) is False assert (q != p) is True if strict_compare: with pytest.raises(TypeError): p < q with pytest.raises(TypeError): p <= q with pytest.raises(TypeError): q < p with pytest.raises(TypeError): q <= p with pytest.raises(TypeError): p > q with pytest.raises(TypeError): p >= q r = cast(BVoidP, p) assert (p < r) is False assert (p <= r) is True assert (p == r) is True assert (p != r) is False assert (p > r) is False assert (p >= r) is True s = newp(BIntP, 125) assert (p == s) is False assert (p != s) is True assert (p < s) is (p <= s) is (s > p) is (s >= p) assert (p > s) is (p >= s) is (s < p) is (s <= p) assert (p < s) ^ (p > s) def test_buffer(): try: import __builtin__ except ImportError: import builtins as __builtin__ BShort = new_primitive_type("short") s = newp(new_pointer_type(BShort), 100) assert sizeof(s) == size_of_ptr() assert sizeof(BShort) == 2 assert len(buffer(s)) == 2 # BChar = new_primitive_type("char") BCharArray = new_array_type(new_pointer_type(BChar), None) c = newp(BCharArray, b"hi there") # buf = buffer(c) assert repr(buf).startswith('<_cffi_backend.buffer object at 0x') assert bytes(buf) == b"hi there\x00" assert type(buf) is buffer if sys.version_info < (3,): assert str(buf) == "hi there\x00" assert unicode(buf) == u+"hi there\x00" else: assert str(buf) == repr(buf) # --mb_length-- assert len(buf) == len(b"hi there\x00") # --mb_item-- for i in range(-12, 12): try: expected = b"hi there\x00"[i] except IndexError: with pytest.raises(IndexError): buf[i] else: assert buf[i] == bitem2bchr(expected) # --mb_slice-- assert buf[:] == b"hi there\x00" for i in range(-12, 12): assert buf[i:] == b"hi there\x00"[i:] assert buf[:i] == b"hi there\x00"[:i] for j in range(-12, 12): assert buf[i:j] == b"hi there\x00"[i:j] # --misc-- assert list(buf) == list(map(bitem2bchr, b"hi there\x00")) # --mb_as_buffer-- if hasattr(__builtin__, 'buffer'): # Python <= 2.7 pytest.raises(TypeError, __builtin__.buffer, c) bf1 = __builtin__.buffer(buf) assert len(bf1) == len(buf) and bf1[3] == "t" if hasattr(__builtin__, 'memoryview'): # Python >= 2.7 pytest.raises(TypeError, memoryview, c) mv1 = memoryview(buf) assert len(mv1) == len(buf) and mv1[3] in (b"t", ord(b"t")) # --mb_ass_item-- expected = list(map(bitem2bchr, b"hi there\x00")) for i in range(-12, 12): try: expected[i] = bytechr(i & 0xff) except IndexError: with pytest.raises(IndexError): buf[i] = bytechr(i & 0xff) else: buf[i] = bytechr(i & 0xff) assert list(buf) == expected # --mb_ass_slice-- buf[:] = b"hi there\x00" assert list(buf) == list(c) == list(map(bitem2bchr, b"hi there\x00")) with pytest.raises(ValueError): buf[:] = b"shorter" with pytest.raises(ValueError): buf[:] = b"this is much too long!" buf[4:2] = b"" # no effect, but should work assert buf[:] == b"hi there\x00" buf[:2] = b"HI" assert buf[:] == b"HI there\x00" buf[:2] = b"hi" expected = list(map(bitem2bchr, b"hi there\x00")) x = 0 for i in range(-12, 12): for j in range(-12, 12): start = i if i >= 0 else i + len(buf) stop = j if j >= 0 else j + len(buf) start = max(0, min(len(buf), start)) stop = max(0, min(len(buf), stop)) sample = bytechr(x & 0xff) * (stop - start) x += 1 buf[i:j] = sample expected[i:j] = map(bitem2bchr, sample) assert list(buf) == expected def test_getcname(): BUChar = new_primitive_type("unsigned char") BArray = new_array_type(new_pointer_type(BUChar), 123) assert getcname(BArray, "<-->") == "unsigned char<-->[123]" def test_errno(): BVoid = new_void_type() BFunc5 = new_function_type((), BVoid) f = cast(BFunc5, _testfunc(5)) set_errno(50) f() assert get_errno() == 65 f(); f() assert get_errno() == 95 def test_errno_callback(): if globals().get('PY_DOT_PY'): pytest.skip("cannot run this test on py.py (e.g. fails on Windows)") set_errno(95) def cb(): e = get_errno() set_errno(e - 6) BVoid = new_void_type() BFunc5 = new_function_type((), BVoid) f = callback(BFunc5, cb) f() assert get_errno() == 89 f(); f() assert get_errno() == 77 def test_cast_to_array(): # not valid in C! extension to get a non-owning BInt = new_primitive_type("int") BIntP = new_pointer_type(BInt) BArray = new_array_type(BIntP, 3) x = cast(BArray, 0) assert repr(x) == "" def test_cast_invalid(): BStruct = new_struct_type("struct foo") complete_struct_or_union(BStruct, []) p = cast(new_pointer_type(BStruct), 123456) s = p[0] pytest.raises(TypeError, cast, BStruct, s) def test_bug_float_convertion(): BDouble = new_primitive_type("double") BDoubleP = new_pointer_type(BDouble) pytest.raises(TypeError, newp, BDoubleP, "foobar") def test_bug_delitem(): BChar = new_primitive_type("char") BCharP = new_pointer_type(BChar) x = newp(BCharP) with pytest.raises(TypeError): del x[0] def test_bug_delattr(): BLong = new_primitive_type("long") BStruct = new_struct_type("struct foo") complete_struct_or_union(BStruct, [('a1', BLong, -1)]) x = newp(new_pointer_type(BStruct)) with pytest.raises(AttributeError): del x.a1 def test_variable_length_struct(): pytest.skip("later") BLong = new_primitive_type("long") BArray = new_array_type(new_pointer_type(BLong), None) BStruct = new_struct_type("struct foo") BStructP = new_pointer_type(BStruct) complete_struct_or_union(BStruct, [('a1', BLong, -1), ('a2', BArray, -1)]) assert sizeof(BStruct) == size_of_long() assert alignof(BStruct) == alignof(BLong) # pytest.raises(TypeError, newp, BStructP, None) x = newp(BStructP, 5) assert sizeof(x) == 6 * size_of_long() x[4] = 123 assert x[4] == 123 with pytest.raises(IndexError): x[5] assert len(x.a2) == 5 # pytest.raises(TypeError, newp, BStructP, [123]) x = newp(BStructP, [123, 5]) assert x.a1 == 123 assert len(x.a2) == 5 assert list(x.a2) == [0] * 5 # x = newp(BStructP, {'a2': 5}) assert x.a1 == 0 assert len(x.a2) == 5 assert list(x.a2) == [0] * 5 # x = newp(BStructP, [123, (4, 5)]) assert x.a1 == 123 assert len(x.a2) == 2 assert list(x.a2) == [4, 5] # x = newp(BStructP, {'a2': (4, 5)}) assert x.a1 == 0 assert len(x.a2) == 2 assert list(x.a2) == [4, 5] def test_autocast_int(): BInt = new_primitive_type("int") BIntPtr = new_pointer_type(BInt) BLongLong = new_primitive_type("long long") BULongLong = new_primitive_type("unsigned long long") BULongLongPtr = new_pointer_type(BULongLong) x = newp(BIntPtr, cast(BInt, 42)) assert x[0] == 42 x = newp(BIntPtr, cast(BLongLong, 42)) assert x[0] == 42 x = newp(BIntPtr, cast(BULongLong, 42)) assert x[0] == 42 x = newp(BULongLongPtr, cast(BInt, 42)) assert x[0] == 42 pytest.raises(OverflowError, newp, BULongLongPtr, cast(BInt, -42)) x = cast(BInt, cast(BInt, 42)) assert int(x) == 42 x = cast(BInt, cast(BLongLong, 42)) assert int(x) == 42 x = cast(BInt, cast(BULongLong, 42)) assert int(x) == 42 x = cast(BULongLong, cast(BInt, 42)) assert int(x) == 42 x = cast(BULongLong, cast(BInt, -42)) assert int(x) == 2 ** 64 - 42 x = cast(BIntPtr, cast(BInt, 42)) assert int(cast(BInt, x)) == 42 def test_autocast_float(): BFloat = new_primitive_type("float") BDouble = new_primitive_type("float") BFloatPtr = new_pointer_type(BFloat) x = newp(BFloatPtr, cast(BDouble, 12.5)) assert x[0] == 12.5 x = cast(BFloat, cast(BDouble, 12.5)) assert float(x) == 12.5 def test_longdouble(): py_py = 'PY_DOT_PY' in globals() BInt = new_primitive_type("int") BLongDouble = new_primitive_type("long double") BLongDoublePtr = new_pointer_type(BLongDouble) BLongDoubleArray = new_array_type(BLongDoublePtr, None) a = newp(BLongDoubleArray, 1) x = a[0] if not py_py: assert repr(x).startswith(" sizeof(new_primitive_type("double")): assert float(lstart) != start assert repr(lstart).startswith("" s = p[0] assert repr(s) == "" a = rawaddressof(BStructPtr, s, 0) assert repr(a).startswith("= (3,): try: import posix, io posix.fdopen = io.open except ImportError: pass # win32 def test_FILE(): if sys.platform == "win32": pytest.skip("testing FILE not implemented") # BFILE = new_struct_type("struct _IO_FILE") BFILEP = new_pointer_type(BFILE) BChar = new_primitive_type("char") BCharP = new_pointer_type(BChar) BInt = new_primitive_type("int") BFunc = new_function_type((BCharP, BFILEP), BInt, False) BFunc2 = new_function_type((BFILEP, BCharP), BInt, True) ll = find_and_load_library('c') fputs = ll.load_function(BFunc, "fputs") fscanf = ll.load_function(BFunc2, "fscanf") # import posix fdr, fdw = posix.pipe() fr1 = posix.fdopen(fdr, 'rb', 256) fw1 = posix.fdopen(fdw, 'wb', 256) # fw1.write(b"X") res = fputs(b"hello world\n", fw1) assert res >= 0 fw1.flush() # should not be needed # p = newp(new_array_type(BCharP, 100), None) res = fscanf(fr1, b"%s\n", p) assert res == 1 assert string(p) == b"Xhello" fr1.close() fw1.close() def test_FILE_only_for_FILE_arg(): if sys.platform == "win32": pytest.skip("testing FILE not implemented") # B_NOT_FILE = new_struct_type("struct NOT_FILE") B_NOT_FILEP = new_pointer_type(B_NOT_FILE) BChar = new_primitive_type("char") BCharP = new_pointer_type(BChar) BInt = new_primitive_type("int") BFunc = new_function_type((BCharP, B_NOT_FILEP), BInt, False) ll = find_and_load_library('c') fputs = ll.load_function(BFunc, "fputs") # import posix fdr, fdw = posix.pipe() fr1 = posix.fdopen(fdr, 'r') fw1 = posix.fdopen(fdw, 'w') # e = pytest.raises(TypeError, fputs, b"hello world\n", fw1) assert str(e.value).startswith( "initializer for ctype 'struct NOT_FILE *' must " "be a cdata pointer, not ") def test_FILE_object(): if sys.platform == "win32": pytest.skip("testing FILE not implemented") # BFILE = new_struct_type("FILE") BFILEP = new_pointer_type(BFILE) BChar = new_primitive_type("char") BCharP = new_pointer_type(BChar) BInt = new_primitive_type("int") BFunc = new_function_type((BCharP, BFILEP), BInt, False) BFunc2 = new_function_type((BFILEP,), BInt, False) ll = find_and_load_library('c') fputs = ll.load_function(BFunc, "fputs") fileno = ll.load_function(BFunc2, "fileno") # import posix fdr, fdw = posix.pipe() fw1 = posix.fdopen(fdw, 'wb', 256) # fw1p = cast(BFILEP, fw1) fw1.write(b"X") fw1.flush() res = fputs(b"hello\n", fw1p) assert res >= 0 res = fileno(fw1p) assert (res == fdw) == (sys.version_info < (3,)) fw1.close() # data = posix.read(fdr, 256) assert data == b"Xhello\n" posix.close(fdr) def test_errno_saved(): set_errno(42) # a random function that will reset errno to 0 (at least on non-windows) import os; os.stat('.') # res = get_errno() assert res == 42 def test_GetLastError(): if sys.platform != "win32": pytest.skip("GetLastError(): only for Windows") # lib = find_and_load_library('KERNEL32.DLL') BInt = new_primitive_type("int") BVoid = new_void_type() BFunc1 = new_function_type((BInt,), BVoid, False) BFunc2 = new_function_type((), BInt, False) SetLastError = lib.load_function(BFunc1, "SetLastError") GetLastError = lib.load_function(BFunc2, "GetLastError") # SetLastError(42) # a random function that will reset the real GetLastError() to 0 import nt; nt.stat('.') # res = GetLastError() assert res == 42 # SetLastError(2) code, message = getwinerror() assert code == 2 assert message == "The system cannot find the file specified" # code, message = getwinerror(1155) assert code == 1155 assert message == ("No application is associated with the " "specified file for this operation") def test_nonstandard_integer_types(): for typename in ['int8_t', 'uint8_t', 'int16_t', 'uint16_t', 'int32_t', 'uint32_t', 'int64_t', 'uint64_t', 'intptr_t', 'uintptr_t', 'ptrdiff_t', 'size_t', 'ssize_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', 'intmax_t', 'uintmax_t']: new_primitive_type(typename) # works def test_cannot_convert_unicode_to_charp(): BCharP = new_pointer_type(new_primitive_type("char")) BCharArray = new_array_type(BCharP, None) pytest.raises(TypeError, newp, BCharArray, u+'foobar') def test_buffer_keepalive(): BCharP = new_pointer_type(new_primitive_type("char")) BCharArray = new_array_type(BCharP, None) buflist = [] for i in range(20): c = newp(BCharArray, str2bytes("hi there %d" % i)) buflist.append(buffer(c)) import gc; gc.collect() for i in range(20): buf = buflist[i] assert buf[:] == str2bytes("hi there %d\x00" % i) def test_slice(): BIntP = new_pointer_type(new_primitive_type("int")) BIntArray = new_array_type(BIntP, None) c = newp(BIntArray, 5) assert len(c) == 5 assert repr(c) == "" d = c[1:4] assert len(d) == 3 assert repr(d) == "" d[0] = 123 d[2] = 456 assert c[1] == 123 assert c[3] == 456 assert d[2] == 456 with pytest.raises(IndexError): d[3] with pytest.raises(IndexError): d[-1] def test_slice_ptr(): BIntP = new_pointer_type(new_primitive_type("int")) BIntArray = new_array_type(BIntP, None) c = newp(BIntArray, 5) d = (c+1)[0:2] assert len(d) == 2 assert repr(d) == "" d[1] += 50 assert c[2] == 50 def test_slice_array_checkbounds(): BIntP = new_pointer_type(new_primitive_type("int")) BIntArray = new_array_type(BIntP, None) c = newp(BIntArray, 5) c[0:5] assert len(c[5:5]) == 0 with pytest.raises(IndexError): c[-1:1] cp = c + 0 cp[-1:1] def test_nonstandard_slice(): BIntP = new_pointer_type(new_primitive_type("int")) BIntArray = new_array_type(BIntP, None) c = newp(BIntArray, 5) with pytest.raises(IndexError) as e: c[:5] assert str(e.value) == "slice start must be specified" with pytest.raises(IndexError) as e: c[4:] assert str(e.value) == "slice stop must be specified" with pytest.raises(IndexError) as e: c[1:2:3] assert str(e.value) == "slice with step not supported" with pytest.raises(IndexError) as e: c[1:2:1] assert str(e.value) == "slice with step not supported" with pytest.raises(IndexError) as e: c[4:2] assert str(e.value) == "slice start > stop" with pytest.raises(IndexError) as e: c[6:6] assert str(e.value) == "index too large (expected 6 <= 5)" def test_setslice(): BIntP = new_pointer_type(new_primitive_type("int")) BIntArray = new_array_type(BIntP, None) c = newp(BIntArray, 5) c[1:3] = [100, 200] assert list(c) == [0, 100, 200, 0, 0] cp = c + 3 cp[-1:1] = [300, 400] assert list(c) == [0, 100, 300, 400, 0] cp[-1:1] = iter([500, 600]) assert list(c) == [0, 100, 500, 600, 0] with pytest.raises(ValueError): cp[-1:1] = [1000] assert list(c) == [0, 100, 1000, 600, 0] with pytest.raises(ValueError): cp[-1:1] = (700, 800, 900) assert list(c) == [0, 100, 700, 800, 0] def test_setslice_array(): BIntP = new_pointer_type(new_primitive_type("int")) BIntArray = new_array_type(BIntP, None) c = newp(BIntArray, 5) d = newp(BIntArray, [10, 20, 30]) c[1:4] = d assert list(c) == [0, 10, 20, 30, 0] # BShortP = new_pointer_type(new_primitive_type("short")) BShortArray = new_array_type(BShortP, None) d = newp(BShortArray, [40, 50]) c[1:3] = d assert list(c) == [0, 40, 50, 30, 0] def test_cdata_name_module_doc(): p = new_primitive_type("signed char") x = cast(p, 17) assert x.__module__ == '_cffi_backend' assert x.__name__ == '' assert hasattr(x, '__doc__') def test_different_types_of_ptr_equality(): BVoidP = new_pointer_type(new_void_type()) BIntP = new_pointer_type(new_primitive_type("int")) x = cast(BVoidP, 12345) assert x == cast(BIntP, 12345) assert x != cast(BIntP, 12344) assert hash(x) == hash(cast(BIntP, 12345)) def test_new_handle(): import _weakref BVoidP = new_pointer_type(new_void_type()) BCharP = new_pointer_type(new_primitive_type("char")) class mylist(list): pass o = mylist([2, 3, 4]) x = newp_handle(BVoidP, o) assert repr(x) == "" assert x assert from_handle(x) is o assert from_handle(cast(BCharP, x)) is o wr = _weakref.ref(o) del o import gc; gc.collect() assert wr() is not None assert from_handle(x) == list((2, 3, 4)) assert from_handle(cast(BCharP, x)) == list((2, 3, 4)) del x for i in range(3): if wr() is not None: import gc; gc.collect() assert wr() is None pytest.raises(RuntimeError, from_handle, cast(BCharP, 0)) def test_new_handle_cycle(): import _weakref BVoidP = new_pointer_type(new_void_type()) class A(object): pass o = A() o.cycle = newp_handle(BVoidP, o) wr = _weakref.ref(o) del o for i in range(3): if wr() is not None: import gc; gc.collect() assert wr() is None def _test_bitfield_details(flag): BChar = new_primitive_type("char") BShort = new_primitive_type("short") BInt = new_primitive_type("int") BUInt = new_primitive_type("unsigned int") BStruct = new_struct_type("struct foo1") complete_struct_or_union(BStruct, [('a', BChar, -1), ('b1', BInt, 9), ('b2', BUInt, 7), ('c', BChar, -1)], -1, -1, -1, flag) if not (flag & SF_MSVC_BITFIELDS): # gcc, any variant assert typeoffsetof(BStruct, 'c') == (BChar, 3) assert sizeof(BStruct) == 4 else: # msvc assert typeoffsetof(BStruct, 'c') == (BChar, 8) assert sizeof(BStruct) == 12 assert alignof(BStruct) == 4 # p = newp(new_pointer_type(BStruct), None) p.a = b'A' p.b1 = -201 p.b2 = 99 p.c = b'\x9D' raw = buffer(p)[:] if sys.byteorder == 'little': if flag & SF_MSVC_BITFIELDS: assert raw == b'A\x00\x00\x007\xC7\x00\x00\x9D\x00\x00\x00' elif flag & SF_GCC_LITTLE_ENDIAN: assert raw == b'A7\xC7\x9D' elif flag & SF_GCC_BIG_ENDIAN: assert raw == b'A\xE3\x9B\x9D' else: raise AssertionError("bad flag") else: if flag & SF_MSVC_BITFIELDS: assert raw == b'A\x00\x00\x00\x00\x00\xC77\x9D\x00\x00\x00' elif flag & SF_GCC_LITTLE_ENDIAN: assert raw == b'A\xC77\x9D' elif flag & SF_GCC_BIG_ENDIAN: assert raw == b'A\x9B\xE3\x9D' else: raise AssertionError("bad flag") # BStruct = new_struct_type("struct foo2") complete_struct_or_union(BStruct, [('a', BChar, -1), ('', BShort, 9), ('c', BChar, -1)], -1, -1, -1, flag) assert typeoffsetof(BStruct, 'c') == (BChar, 4) if flag & SF_MSVC_BITFIELDS: assert sizeof(BStruct) == 6 assert alignof(BStruct) == 2 elif flag & SF_GCC_X86_BITFIELDS: assert sizeof(BStruct) == 5 assert alignof(BStruct) == 1 elif flag & SF_GCC_ARM_BITFIELDS: assert sizeof(BStruct) == 6 assert alignof(BStruct) == 2 else: raise AssertionError("bad flag") # BStruct = new_struct_type("struct foo2") complete_struct_or_union(BStruct, [('a', BChar, -1), ('', BInt, 0), ('', BInt, 0), ('c', BChar, -1)], -1, -1, -1, flag) if flag & SF_MSVC_BITFIELDS: assert typeoffsetof(BStruct, 'c') == (BChar, 1) assert sizeof(BStruct) == 2 assert alignof(BStruct) == 1 elif flag & SF_GCC_X86_BITFIELDS: assert typeoffsetof(BStruct, 'c') == (BChar, 4) assert sizeof(BStruct) == 5 assert alignof(BStruct) == 1 elif flag & SF_GCC_ARM_BITFIELDS: assert typeoffsetof(BStruct, 'c') == (BChar, 4) assert sizeof(BStruct) == 8 assert alignof(BStruct) == 4 else: raise AssertionError("bad flag") SF_MSVC_BITFIELDS = 0x01 SF_GCC_ARM_BITFIELDS = 0x02 SF_GCC_X86_BITFIELDS = 0x10 SF_GCC_BIG_ENDIAN = 0x04 SF_GCC_LITTLE_ENDIAN = 0x40 SF_PACKED = 0x08 def test_bitfield_as_x86_gcc(): _test_bitfield_details(flag=SF_GCC_X86_BITFIELDS|SF_GCC_LITTLE_ENDIAN) def test_bitfield_as_msvc(): _test_bitfield_details(flag=SF_MSVC_BITFIELDS|SF_GCC_LITTLE_ENDIAN) def test_bitfield_as_arm_gcc(): _test_bitfield_details(flag=SF_GCC_ARM_BITFIELDS|SF_GCC_LITTLE_ENDIAN) def test_bitfield_as_ppc_gcc(): # PowerPC uses the same format as X86, but is big-endian _test_bitfield_details(flag=SF_GCC_X86_BITFIELDS|SF_GCC_BIG_ENDIAN) def buffer_warning(cdata): import warnings buf = buffer(cdata) bytes = len(buf) with warnings.catch_warnings(record=True) as w: warnings.simplefilter("always") buffer(cdata, bytes) assert len(w) == 0 buffer(cdata, bytes + 1) assert len(w) <= 1 return len(w) == 1 def test_struct_array_no_length(): BInt = new_primitive_type("int") BIntP = new_pointer_type(BInt) BArray = new_array_type(BIntP, None) BStruct = new_struct_type("foo") pytest.raises(TypeError, complete_struct_or_union, BStruct, [('x', BArray), ('y', BInt)]) # BStruct = new_struct_type("foo") complete_struct_or_union(BStruct, [('x', BInt), ('y', BArray)]) assert sizeof(BStruct) == size_of_int() d = BStruct.fields assert len(d) == 2 assert d[0][0] == 'x' assert d[0][1].type is BInt assert d[0][1].offset == 0 assert d[0][1].bitshift == -1 assert d[0][1].bitsize == -1 assert d[1][0] == 'y' assert d[1][1].type is BArray assert d[1][1].offset == size_of_int() assert d[1][1].bitshift == -2 assert d[1][1].bitsize == -1 # p = newp(new_pointer_type(BStruct)) p.x = 42 assert p.x == 42 assert typeof(p.y) is BArray assert len(p.y) == 0 assert p.y == cast(BIntP, p) + 1 # p = newp(new_pointer_type(BStruct), [100]) assert p.x == 100 assert len(p.y) == 0 # # Tests for # ffi.new("struct_with_var_array *", [field.., [the_array_items..]]) # ffi.new("struct_with_var_array *", [field.., array_size]) plist = [] for i in range(20): if i % 2 == 0: p = newp(new_pointer_type(BStruct), [100, [200, i, 400]]) else: p = newp(new_pointer_type(BStruct), [100, 3]) p.y[1] = i p.y[0] = 200 assert p.y[2] == 0 p.y[2] = 400 assert len(p.y) == 3 assert len(p[0].y) == 3 assert len(buffer(p)) == sizeof(BInt) * 4 assert sizeof(p[0]) == sizeof(BInt) * 4 plist.append(p) for i in range(20): p = plist[i] assert p.x == 100 assert p.y[0] == 200 assert p.y[1] == i assert p.y[2] == 400 assert list(p.y) == [200, i, 400] # # the following assignment works, as it normally would, for any array field p.y = [501, 601] assert list(p.y) == [501, 601, 400] p[0].y = [500, 600] assert list(p[0].y) == [500, 600, 400] assert repr(p) == "" % ( sizeof(BStruct) + 3 * sizeof(BInt),) assert repr(p[0]) == "" % ( sizeof(BStruct) + 3 * sizeof(BInt),) assert sizeof(p[0]) == sizeof(BStruct) + 3 * sizeof(BInt) # # from a non-owning pointer, we can't get the length q = cast(new_pointer_type(BStruct), p) assert q.y[0] == 500 assert q[0].y[0] == 500 pytest.raises(TypeError, len, q.y) pytest.raises(TypeError, len, q[0].y) assert typeof(q.y) is BIntP assert typeof(q[0].y) is BIntP assert sizeof(q[0]) == sizeof(BStruct) # # error cases with pytest.raises(IndexError): p.y[4] with pytest.raises(TypeError): p.y = cast(BIntP, 0) with pytest.raises(TypeError): p.y = 15 with pytest.raises(TypeError): p.y = None # # accepting this may be specified by the C99 standard, # or a GCC strangeness... BStruct2 = new_struct_type("bar") complete_struct_or_union(BStruct2, [('f', BStruct), ('n', BInt)]) p = newp(new_pointer_type(BStruct2), {'n': 42}) assert p.n == 42 # # more error cases pytest.raises(TypeError, newp, new_pointer_type(BStruct), [100, None]) BArray4 = new_array_type(BIntP, 4) BStruct4 = new_struct_type("test4") complete_struct_or_union(BStruct4, [('a', BArray4)]) # not varsized pytest.raises(TypeError, newp, new_pointer_type(BStruct4), [None]) pytest.raises(TypeError, newp, new_pointer_type(BStruct4), [4]) p = newp(new_pointer_type(BStruct4), [[10, 20, 30]]) assert p.a[0] == 10 assert p.a[1] == 20 assert p.a[2] == 30 assert p.a[3] == 0 assert buffer_warning(p) # # struct of struct of varsized array BStruct2 = new_struct_type("bar") complete_struct_or_union(BStruct2, [('head', BInt), ('tail', BStruct)]) for i in range(2): # try to detect heap overwrites p = newp(new_pointer_type(BStruct2), [100, [200, list(range(50))]]) assert p.tail.y[49] == 49 assert buffer_warning(p) assert not buffer_warning(cast(new_pointer_type(BStruct2), p)) assert not buffer_warning(cast(BIntP, p)) def test_more_buffer_warning(): BChar = new_primitive_type("unsigned char") BCharP = new_pointer_type(BChar) BArray = new_array_type(BCharP, 10) # char[10] p = newp(BArray) assert buffer_warning(p) assert not buffer_warning(cast(BCharP, p)) p = newp(BCharP) assert buffer_warning(p) assert not buffer_warning(cast(BCharP, p)) def test_struct_array_no_length_explicit_position(): BInt = new_primitive_type("int") BIntP = new_pointer_type(BInt) BArray = new_array_type(BIntP, None) BStruct = new_struct_type("foo") complete_struct_or_union(BStruct, [('x', BArray, -1, 0), # actually 3 items ('y', BInt, -1, 12)]) p = newp(new_pointer_type(BStruct), [[10, 20], 30]) assert p.x[0] == 10 assert p.x[1] == 20 assert p.x[2] == 0 assert p.y == 30 p = newp(new_pointer_type(BStruct), {'x': [40], 'y': 50}) assert p.x[0] == 40 assert p.x[1] == 0 assert p.x[2] == 0 assert p.y == 50 p = newp(new_pointer_type(BStruct), {'y': 60}) assert p.x[0] == 0 assert p.x[1] == 0 assert p.x[2] == 0 assert p.y == 60 # # This "should" work too, allocating a larger structure # (a bit strange in this case, but useful in general) plist = [] for i in range(20): p = newp(new_pointer_type(BStruct), [[10, 20, 30, 40, 50, 60, 70]]) plist.append(p) for i in range(20): p = plist[i] assert p.x[0] == 10 assert p.x[1] == 20 assert p.x[2] == 30 assert p.x[3] == 40 == p.y assert p.x[4] == 50 assert p.x[5] == 60 assert p.x[6] == 70 def test_struct_array_not_aligned(): # struct a { int x; char y; char z[]; }; # ends up of size 8, but 'z' is at offset 5 BChar = new_primitive_type("char") BInt = new_primitive_type("int") BCharP = new_pointer_type(BChar) BArray = new_array_type(BCharP, None) BStruct = new_struct_type("foo") complete_struct_or_union(BStruct, [('x', BInt), ('y', BChar), ('z', BArray)]) assert sizeof(BStruct) == 2 * size_of_int() def offsetof(BType, fieldname): return typeoffsetof(BType, fieldname)[1] base = offsetof(BStruct, 'z') assert base == size_of_int() + 1 # p = newp(new_pointer_type(BStruct), {'z': 3}) assert sizeof(p[0]) == base + 3 q = newp(new_pointer_type(BStruct), {'z': size_of_int()}) assert sizeof(q) == size_of_ptr() assert sizeof(q[0]) == base + size_of_int() assert len(p.z) == 3 assert len(p[0].z) == 3 assert len(q.z) == size_of_int() assert len(q[0].z) == size_of_int() def test_ass_slice(): BChar = new_primitive_type("char") BArray = new_array_type(new_pointer_type(BChar), None) p = newp(BArray, b"foobar") p[2:5] = [b"*", b"Z", b"T"] p[1:3] = b"XY" assert list(p) == [b"f", b"X", b"Y", b"Z", b"T", b"r", b"\x00"] with pytest.raises(TypeError): p[1:5] = u+'XYZT' with pytest.raises(TypeError): p[1:5] = [1, 2, 3, 4] # for typename in ["wchar_t", "char16_t", "char32_t"]: BUniChar = new_primitive_type(typename) BArray = new_array_type(new_pointer_type(BUniChar), None) p = newp(BArray, u+"foobar") p[2:5] = [u+"*", u+"Z", u+"T"] p[1:3] = u+"XY" assert list(p) == [u+"f", u+"X", u+"Y", u+"Z", u+"T", u+"r", u+"\x00"] with pytest.raises(TypeError): p[1:5] = b'XYZT' with pytest.raises(TypeError): p[1:5] = [1, 2, 3, 4] def test_void_p_arithmetic(): BVoid = new_void_type() BInt = new_primitive_type("intptr_t") p = cast(new_pointer_type(BVoid), 100000) assert int(cast(BInt, p)) == 100000 assert int(cast(BInt, p + 42)) == 100042 assert int(cast(BInt, p - (-42))) == 100042 assert (p + 42) - p == 42 q = cast(new_pointer_type(new_primitive_type("char")), 100000) with pytest.raises(TypeError): p - q with pytest.raises(TypeError): q - p with pytest.raises(TypeError): p + cast(new_primitive_type('int'), 42) with pytest.raises(TypeError): p - cast(new_primitive_type('int'), 42) def test_sizeof_sliced_array(): BInt = new_primitive_type("int") BArray = new_array_type(new_pointer_type(BInt), 10) p = newp(BArray, None) assert sizeof(p[2:9]) == 7 * sizeof(BInt) def test_packed(): BLong = new_primitive_type("long") BChar = new_primitive_type("char") BShort = new_primitive_type("short") for extra_args in [(SF_PACKED,), (0, 1)]: BStruct = new_struct_type("struct foo") complete_struct_or_union(BStruct, [('a1', BLong, -1), ('a2', BChar, -1), ('a3', BShort, -1)], None, -1, -1, *extra_args) d = BStruct.fields assert len(d) == 3 assert d[0][0] == 'a1' assert d[0][1].type is BLong assert d[0][1].offset == 0 assert d[0][1].bitshift == -1 assert d[0][1].bitsize == -1 assert d[1][0] == 'a2' assert d[1][1].type is BChar assert d[1][1].offset == sizeof(BLong) assert d[1][1].bitshift == -1 assert d[1][1].bitsize == -1 assert d[2][0] == 'a3' assert d[2][1].type is BShort assert d[2][1].offset == sizeof(BLong) + sizeof(BChar) assert d[2][1].bitshift == -1 assert d[2][1].bitsize == -1 assert sizeof(BStruct) == sizeof(BLong) + sizeof(BChar) + sizeof(BShort) assert alignof(BStruct) == 1 # BStruct2 = new_struct_type("struct foo") complete_struct_or_union(BStruct2, [('b1', BChar, -1), ('b2', BLong, -1)], None, -1, -1, 0, 2) d = BStruct2.fields assert len(d) == 2 assert d[0][0] == 'b1' assert d[0][1].type is BChar assert d[0][1].offset == 0 assert d[0][1].bitshift == -1 assert d[0][1].bitsize == -1 assert d[1][0] == 'b2' assert d[1][1].type is BLong assert d[1][1].offset == 2 assert d[1][1].bitshift == -1 assert d[1][1].bitsize == -1 assert sizeof(BStruct2) == 2 + sizeof(BLong) assert alignof(BStruct2) == 2 def test_packed_with_bitfields(): if sys.platform == "win32": pytest.skip("testing gcc behavior") BLong = new_primitive_type("long") BChar = new_primitive_type("char") BStruct = new_struct_type("struct foo") pytest.raises(NotImplementedError, complete_struct_or_union, BStruct, [('a1', BLong, 30), ('a2', BChar, 5)], None, -1, -1, SF_PACKED) def test_from_buffer(): import array a = array.array('H', [10000, 20000, 30000]) BChar = new_primitive_type("char") BCharP = new_pointer_type(BChar) BCharA = new_array_type(BCharP, None) c = from_buffer(BCharA, a) assert typeof(c) is BCharA assert len(c) == 6 assert repr(c) == "" p = new_pointer_type(new_primitive_type("unsigned short")) cast(p, c)[1] += 500 assert list(a) == [10000, 20500, 30000] def test_from_buffer_not_str_unicode(): BChar = new_primitive_type("char") BCharP = new_pointer_type(BChar) BCharA = new_array_type(BCharP, None) p1 = from_buffer(BCharA, b"foo") assert p1 == from_buffer(BCharA, b"foo") import gc; gc.collect() assert p1 == from_buffer(BCharA, b"foo") pytest.raises(TypeError, from_buffer, BCharA, u+"foo") try: from __builtin__ import buffer except ImportError: pass else: # Python 2 only contents = from_buffer(BCharA, buffer(b"foo")) assert len(contents) == len(p1) for i in range(len(contents)): assert contents[i] == p1[i] p4 = buffer(u+"foo") contents = from_buffer(BCharA, buffer(u+"foo")) assert len(contents) == len(p4) for i in range(len(contents)): assert contents[i] == p4[i] try: from __builtin__ import memoryview except ImportError: pass else: contents = from_buffer(BCharA, memoryview(b"foo")) assert len(contents) == len(p1) for i in range(len(contents)): assert contents[i] == p1[i] def test_from_buffer_bytearray(): a = bytearray(b"xyz") BChar = new_primitive_type("char") BCharP = new_pointer_type(BChar) BCharA = new_array_type(BCharP, None) p = from_buffer(BCharA, a) assert typeof(p) is BCharA assert len(p) == 3 assert repr(p) == "" assert p[2] == b"z" p[2] = b"." assert a[2] == ord(".") a[2] = ord("?") assert p[2] == b"?" def test_from_buffer_more_cases(): try: from _cffi_backend import _testbuff except ImportError: pytest.skip("not for pypy") BChar = new_primitive_type("char") BCharP = new_pointer_type(BChar) BCharA = new_array_type(BCharP, None) # def check1(bufobj, expected): c = from_buffer(BCharA, bufobj) assert typeof(c) is BCharA if sys.version_info >= (3,): expected = [bytes(c, "ascii") for c in expected] assert list(c) == list(expected) # def check(methods, expected, expected_for_memoryview=None): if sys.version_info >= (3,): if methods <= 7: return if expected_for_memoryview is not None: expected = expected_for_memoryview class X(object): pass _testbuff(X, methods) bufobj = X() check1(bufobj, expected) try: from __builtin__ import buffer bufobjb = buffer(bufobj) except (TypeError, ImportError): pass else: check1(bufobjb, expected) try: bufobjm = memoryview(bufobj) except (TypeError, NameError): pass else: check1(bufobjm, expected_for_memoryview or expected) # check(1, "RDB") check(2, "WRB") check(4, "CHB") check(8, "GTB") check(16, "ROB") # check(1 | 2, "RDB") check(1 | 4, "RDB") check(2 | 4, "CHB") check(1 | 8, "RDB", "GTB") check(1 | 16, "RDB", "ROB") check(2 | 8, "WRB", "GTB") check(2 | 16, "WRB", "ROB") check(4 | 8, "CHB", "GTB") check(4 | 16, "CHB", "ROB") def test_from_buffer_require_writable(): BChar = new_primitive_type("char") BCharP = new_pointer_type(BChar) BCharA = new_array_type(BCharP, None) p1 = from_buffer(BCharA, b"foo", False) assert p1 == from_buffer(BCharA, b"foo", False) pytest.raises((TypeError, BufferError), from_buffer, BCharA, b"foo", True) ba = bytearray(b"foo") p1 = from_buffer(BCharA, ba, True) p1[0] = b"g" assert ba == b"goo" def test_from_buffer_types(): BInt = new_primitive_type("int") BIntP = new_pointer_type(BInt) BIntA = new_array_type(BIntP, None) lst = [-12345678, 87654321, 489148] bytestring = bytearray(buffer(newp(BIntA, lst))[:] + b'XYZ') lst2 = lst + [42, -999999999] bytestring2 = bytearray(buffer(newp(BIntA, lst2))[:] + b'XYZ') # p1 = from_buffer(BIntA, bytestring) # int[] assert typeof(p1) is BIntA assert len(p1) == 3 assert p1[0] == lst[0] assert p1[1] == lst[1] assert p1[2] == lst[2] with pytest.raises(IndexError): p1[3] with pytest.raises(IndexError): p1[-1] # pytest.raises(TypeError, from_buffer, BInt, bytestring) # p2 = from_buffer(BIntP, bytestring) # int * assert p2 == p1 or 'PY_DOT_PY' in globals() # note: on pypy ^^^, bytearray buffers are not emulated well enough assert typeof(p2) is BIntP assert p2[0] == lst[0] assert p2[1] == lst[1] assert p2[2] == lst[2] # hopefully does not crash, but doesn't raise an exception: p2[3] p2[-1] # not enough data even for one, but this is not enforced: from_buffer(BIntP, b"") # BIntA2 = new_array_type(BIntP, 2) p2 = from_buffer(BIntA2, bytestring) # int[2] assert typeof(p2) is BIntA2 assert len(p2) == 2 assert p2[0] == lst[0] assert p2[1] == lst[1] with pytest.raises(IndexError): p2[2] with pytest.raises(IndexError): p2[-1] assert p2 == p1 or 'PY_DOT_PY' in globals() # BIntA4 = new_array_type(BIntP, 4) # int[4]: too big pytest.raises(ValueError, from_buffer, BIntA4, bytestring) # BStruct = new_struct_type("foo") complete_struct_or_union(BStruct, [('a1', BInt, -1), ('a2', BInt, -1)]) BStructP = new_pointer_type(BStruct) BStructA = new_array_type(BStructP, None) p1 = from_buffer(BStructA, bytestring2) # struct[] assert len(p1) == 2 assert typeof(p1) is BStructA assert p1[0].a1 == lst2[0] assert p1[0].a2 == lst2[1] assert p1[1].a1 == lst2[2] assert p1[1].a2 == lst2[3] with pytest.raises(IndexError): p1[2] with pytest.raises(IndexError): p1[-1] assert repr(p1) == "" # p2 = from_buffer(BStructP, bytestring2) # 'struct *' assert p2 == p1 or 'PY_DOT_PY' in globals() assert typeof(p2) is BStructP assert p2.a1 == lst2[0] assert p2.a2 == lst2[1] assert p2[0].a1 == lst2[0] assert p2[0].a2 == lst2[1] assert p2[1].a1 == lst2[2] assert p2[1].a2 == lst2[3] # does not crash: p2[2] p2[-1] # not enough data even for one, but this is not enforced: from_buffer(BStructP, b"") from_buffer(BStructP, b"1234567") # release(p1) assert repr(p1) == "" # BEmptyStruct = new_struct_type("empty") complete_struct_or_union(BEmptyStruct, [], Ellipsis, 0) assert sizeof(BEmptyStruct) == 0 BEmptyStructP = new_pointer_type(BEmptyStruct) BEmptyStructA = new_array_type(BEmptyStructP, None) pytest.raises(ZeroDivisionError, from_buffer, # empty[] BEmptyStructA, bytestring) # BEmptyStructA5 = new_array_type(BEmptyStructP, 5) p1 = from_buffer(BEmptyStructA5, bytestring) # struct empty[5] assert typeof(p1) is BEmptyStructA5 assert len(p1) == 5 assert (cast(BIntP, p1) == from_buffer(BIntA, bytestring) or 'PY_DOT_PY' in globals()) # BVarStruct = new_struct_type("varfoo") BVarStructP = new_pointer_type(BVarStruct) complete_struct_or_union(BVarStruct, [('a1', BInt, -1), ('va', BIntA, -1)]) with pytest.raises(TypeError): from_buffer(BVarStruct, bytestring) pv = from_buffer(BVarStructP, bytestring) # varfoo * assert pv.a1 == lst[0] assert pv.va[0] == lst[1] assert pv.va[1] == lst[2] assert sizeof(pv[0]) == 1 * size_of_int() with pytest.raises(TypeError): len(pv.va) # hopefully does not crash, but doesn't raise an exception: pv.va[2] pv.va[-1] # not enough data even for one, but this is not enforced: from_buffer(BVarStructP, b"") assert repr(pv) == "" assert repr(pv[0]).startswith("" assert repr(pv[0]).startswith("" else: assert repr(BFunc) == "" def test_get_common_types(): d = {} _get_common_types(d) assert d['bool'] == '_Bool' def test_unpack(): BChar = new_primitive_type("char") BArray = new_array_type(new_pointer_type(BChar), 10) # char[10] p = newp(BArray, b"abc\x00def") p0 = p assert unpack(p, 10) == b"abc\x00def\x00\x00\x00" assert unpack(p+1, 5) == b"bc\x00de" for typename in ["wchar_t", "char16_t", "char32_t"]: BWChar = new_primitive_type(typename) BArray = new_array_type(new_pointer_type(BWChar), 10) # wchar_t[10] p = newp(BArray, u"abc\x00def") assert unpack(p, 10) == u"abc\x00def\x00\x00\x00" for typename, samples in [ ("uint8_t", [0, 2**8-1]), ("uint16_t", [0, 2**16-1]), ("uint32_t", [0, 2**32-1]), ("uint64_t", [0, 2**64-1]), ("int8_t", [-2**7, 2**7-1]), ("int16_t", [-2**15, 2**15-1]), ("int32_t", [-2**31, 2**31-1]), ("int64_t", [-2**63, 2**63-1]), ("_Bool", [False, True]), ("float", [0.0, 10.5]), ("double", [12.34, 56.78]), ]: BItem = new_primitive_type(typename) BArray = new_array_type(new_pointer_type(BItem), 10) p = newp(BArray, samples) result = unpack(p, len(samples)) assert result == samples for i in range(len(samples)): assert result[i] == p[i] and type(result[i]) is type(p[i]) assert (type(result[i]) is bool) == (type(samples[i]) is bool) # BInt = new_primitive_type("int") pytest.raises(TypeError, unpack, p) pytest.raises(TypeError, unpack, b"foobar", 6) pytest.raises(TypeError, unpack, cast(BInt, 42), 1) # BPtr = new_pointer_type(BInt) random_ptr = cast(BPtr, -424344) other_ptr = cast(BPtr, 54321) BArray = new_array_type(new_pointer_type(BPtr), None) lst = unpack(newp(BArray, [random_ptr, other_ptr]), 2) assert lst == [random_ptr, other_ptr] # BFunc = new_function_type((BInt, BInt), BInt, False) BFuncPtr = new_pointer_type(BFunc) lst = unpack(newp(new_array_type(BFuncPtr, None), 2), 2) assert len(lst) == 2 assert not lst[0] and not lst[1] assert typeof(lst[0]) is BFunc # BStruct = new_struct_type("foo") BStructPtr = new_pointer_type(BStruct) e = pytest.raises(ValueError, unpack, cast(BStructPtr, 42), 5) assert str(e.value) == "'foo *' points to items of unknown size" complete_struct_or_union(BStruct, [('a1', BInt, -1), ('a2', BInt, -1)]) array_of_structs = newp(new_array_type(BStructPtr, None), [[4,5], [6,7]]) lst = unpack(array_of_structs, 2) assert typeof(lst[0]) is BStruct assert lst[0].a1 == 4 and lst[1].a2 == 7 # pytest.raises(RuntimeError, unpack, cast(new_pointer_type(BChar), 0), 0) pytest.raises(RuntimeError, unpack, cast(new_pointer_type(BChar), 0), 10) # pytest.raises(ValueError, unpack, p0, -1) pytest.raises(ValueError, unpack, p, -1) def test_cdata_dir(): BInt = new_primitive_type("int") p = cast(BInt, 42) check_dir(p, []) p = newp(new_array_type(new_pointer_type(BInt), None), 5) check_dir(p, []) BStruct = new_struct_type("foo") p = cast(new_pointer_type(BStruct), 0) check_dir(p, []) # opaque complete_struct_or_union(BStruct, [('a2', BInt, -1), ('a1', BInt, -1)]) check_dir(p, ['a1', 'a2']) # always sorted p = newp(new_pointer_type(BStruct), None) check_dir(p, ['a1', 'a2']) check_dir(p[0], ['a1', 'a2']) pp = newp(new_pointer_type(new_pointer_type(BStruct)), p) check_dir(pp, []) check_dir(pp[0], ['a1', 'a2']) check_dir(pp[0][0], ['a1', 'a2']) def test_char_pointer_conversion(): import warnings assert __version__.startswith("1."), ( "the warning will be an error if we ever release cffi 2.x") BCharP = new_pointer_type(new_primitive_type("char")) BIntP = new_pointer_type(new_primitive_type("int")) BVoidP = new_pointer_type(new_void_type()) BUCharP = new_pointer_type(new_primitive_type("unsigned char")) z1 = cast(BCharP, 0) z2 = cast(BIntP, 0) z3 = cast(BVoidP, 0) z4 = cast(BUCharP, 0) with warnings.catch_warnings(record=True) as w: warnings.simplefilter("always") newp(new_pointer_type(BIntP), z1) # warn assert len(w) == 1 newp(new_pointer_type(BVoidP), z1) # fine assert len(w) == 1 newp(new_pointer_type(BCharP), z2) # warn assert len(w) == 2 newp(new_pointer_type(BVoidP), z2) # fine assert len(w) == 2 newp(new_pointer_type(BCharP), z3) # fine assert len(w) == 2 newp(new_pointer_type(BIntP), z3) # fine assert len(w) == 2 newp(new_pointer_type(BCharP), z4) # fine (ignore signedness here) assert len(w) == 2 newp(new_pointer_type(BUCharP), z1) # fine (ignore signedness here) assert len(w) == 2 newp(new_pointer_type(BUCharP), z3) # fine assert len(w) == 2 # check that the warnings are associated with lines in this file assert w[1].lineno == w[0].lineno + 4 def test_primitive_comparison(): def assert_eq(a, b): assert (a == b) is True assert (b == a) is True assert (a != b) is False assert (b != a) is False assert (a < b) is False assert (a <= b) is True assert (a > b) is False assert (a >= b) is True assert (b < a) is False assert (b <= a) is True assert (b > a) is False assert (b >= a) is True assert hash(a) == hash(b) def assert_lt(a, b, check_hash=True): assert (a == b) is False assert (b == a) is False assert (a != b) is True assert (b != a) is True assert (a < b) is True assert (a <= b) is True assert (a > b) is False assert (a >= b) is False assert (b < a) is False assert (b <= a) is False assert (b > a) is True assert (b >= a) is True if check_hash: assert hash(a) != hash(b) # (or at least, it is unlikely) def assert_gt(a, b, check_hash=True): assert_lt(b, a, check_hash) def assert_ne(a, b): assert (a == b) is False assert (b == a) is False assert (a != b) is True assert (b != a) is True if strict_compare: with pytest.raises(TypeError): a < b with pytest.raises(TypeError): a <= b with pytest.raises(TypeError): a > b with pytest.raises(TypeError): a >= b with pytest.raises(TypeError): b < a with pytest.raises(TypeError): b <= a with pytest.raises(TypeError): b > a with pytest.raises(TypeError): b >= a elif a < b: assert_lt(a, b) else: assert_lt(b, a) assert_eq(5, 5) assert_lt(3, 5) assert_ne('5', 5) # t1 = new_primitive_type("char") t2 = new_primitive_type("int") t3 = new_primitive_type("unsigned char") t4 = new_primitive_type("unsigned int") t5 = new_primitive_type("float") t6 = new_primitive_type("double") assert_eq(cast(t1, 65), b'A') assert_lt(cast(t1, 64), b'\x99') assert_gt(cast(t1, 200), b'A') assert_ne(cast(t1, 65), 65) assert_eq(cast(t2, -25), -25) assert_lt(cast(t2, -25), -24) assert_gt(cast(t2, -25), -26) assert_eq(cast(t3, 65), 65) assert_ne(cast(t3, 65), b'A') assert_ne(cast(t3, 65), cast(t1, 65)) assert_gt(cast(t4, -1), -1, check_hash=False) assert_gt(cast(t4, -1), cast(t2, -1), check_hash=False) assert_gt(cast(t4, -1), 99999) assert_eq(cast(t4, -1), 256 ** size_of_int() - 1) assert_eq(cast(t5, 3.0), 3) assert_eq(cast(t5, 3.5), 3.5) assert_lt(cast(t5, 3.3), 3.3) # imperfect rounding assert_eq(cast(t6, 3.3), 3.3) assert_eq(cast(t5, 3.5), cast(t6, 3.5)) assert_lt(cast(t5, 3.1), cast(t6, 3.1)) # imperfect rounding assert_eq(cast(t5, 7.0), cast(t3, 7)) assert_lt(cast(t5, 3.1), 3.101) assert_gt(cast(t5, 3.1), 3) def test_explicit_release_new(): # release() on a ffi.new() object has no effect on CPython, but # really releases memory on PyPy. We can't test that effect # though, because a released cdata is not marked. BIntP = new_pointer_type(new_primitive_type("int")) p = newp(BIntP) p[0] = 42 with pytest.raises(IndexError): p[1] release(p) # here, reading p[0] might give garbage or segfault... release(p) # no effect # BStruct = new_struct_type("struct foo") BStructP = new_pointer_type(BStruct) complete_struct_or_union(BStruct, [('p', BIntP, -1)]) pstruct = newp(BStructP) assert pstruct.p == cast(BIntP, 0) release(pstruct) # here, reading pstruct.p might give garbage or segfault... release(pstruct) # no effect def test_explicit_release_new_contextmgr(): BIntP = new_pointer_type(new_primitive_type("int")) with newp(BIntP) as p: p[0] = 42 assert p[0] == 42 # here, reading p[0] might give garbage or segfault... release(p) # no effect def test_explicit_release_badtype(): BIntP = new_pointer_type(new_primitive_type("int")) p = cast(BIntP, 12345) pytest.raises(ValueError, release, p) pytest.raises(ValueError, release, p) BStruct = new_struct_type("struct foo") BStructP = new_pointer_type(BStruct) complete_struct_or_union(BStruct, [('p', BIntP, -1)]) pstruct = newp(BStructP) pytest.raises(ValueError, release, pstruct[0]) def test_explicit_release_badtype_contextmgr(): BIntP = new_pointer_type(new_primitive_type("int")) p = cast(BIntP, 12345) with pytest.raises(ValueError): with p: pass with pytest.raises(ValueError): with p: pass def test_explicit_release_gc(): BIntP = new_pointer_type(new_primitive_type("int")) seen = [] intp1 = newp(BIntP, 12345) p1 = cast(BIntP, intp1) p = gcp(p1, seen.append) assert seen == [] release(p) assert seen == [p1] assert p1[0] == 12345 assert p[0] == 12345 # true so far, but might change to raise RuntimeError release(p) # no effect def test_explicit_release_gc_contextmgr(): BIntP = new_pointer_type(new_primitive_type("int")) seen = [] intp1 = newp(BIntP, 12345) p1 = cast(BIntP, intp1) p = gcp(p1, seen.append) with p: assert p[0] == 12345 assert seen == [] assert seen == [p1] assert p1[0] == 12345 assert p[0] == 12345 # true so far, but might change to raise RuntimeError release(p) # no effect def test_explicit_release_from_buffer(): a = bytearray(b"xyz") BChar = new_primitive_type("char") BCharP = new_pointer_type(BChar) BCharA = new_array_type(BCharP, None) p = from_buffer(BCharA, a) assert p[2] == b"z" assert repr(p) == "" release(p) assert p[2] == b"z" # true so far, but might change to raise RuntimeError assert repr(p) == "" release(p) # no effect def test_explicit_release_from_buffer_contextmgr(): a = bytearray(b"xyz") BChar = new_primitive_type("char") BCharP = new_pointer_type(BChar) BCharA = new_array_type(BCharP, None) p = from_buffer(BCharA, a) with p: assert p[2] == b"z" assert p[2] == b"z" # true so far, but might change to raise RuntimeError assert repr(p) == "" release(p) # no effect def test_explicit_release_bytearray_on_cpython(): if '__pypy__' in sys.builtin_module_names: pytest.skip("pypy's bytearray are never locked") a = bytearray(b"xyz") BChar = new_primitive_type("char") BCharP = new_pointer_type(BChar) BCharA = new_array_type(BCharP, None) a += b't' * 10 p = from_buffer(BCharA, a) with pytest.raises(BufferError): a += b'u' * 100 release(p) a += b'v' * 100 release(p) # no effect a += b'w' * 1000 assert a == bytearray(b"xyz" + b't' * 10 + b'v' * 100 + b'w' * 1000) def test_int_doesnt_give_bool(): BBool = new_primitive_type("_Bool") x = int(cast(BBool, 42)) assert type(x) is int and x == 1 x = long(cast(BBool, 42)) assert type(x) is long and x == 1 with pytest.raises(TypeError): float(cast(BBool, 42)) with pytest.raises(TypeError): complex(cast(BBool, 42)) def test_cannot_call_null_function_pointer(): BInt = new_primitive_type("int") BFunc = new_function_type((BInt, BInt), BInt, False) f = cast(BFunc, 0) with pytest.raises(RuntimeError): f(40, 2) def test_huge_structure(): BChar = new_primitive_type("char") BArray = new_array_type(new_pointer_type(BChar), sys.maxsize) assert sizeof(BArray) == sys.maxsize BStruct = new_struct_type("struct foo") complete_struct_or_union(BStruct, [('a1', BArray, -1)]) assert sizeof(BStruct) == sys.maxsize def test_get_types(): import _cffi_backend CData, CType = _get_types() assert CData is _cffi_backend._CDataBase assert CType is _cffi_backend.CType def test_type_available_with_correct_names(): import _cffi_backend check_names = [ 'CType', 'CField', 'CLibrary', '_CDataBase', 'FFI', 'Lib', 'buffer', ] if '__pypy__' in sys.builtin_module_names: check_names += [ '__CData_iterator', '__FFIGlobSupport', '__FFIAllocator', '__FFIFunctionWrapper', ] else: check_names += [ '__CDataOwn', '__CDataOwnGC', '__CDataFromBuf', '__CDataGCP', '__CData_iterator', '__FFIGlobSupport', ] for name in check_names: tp = getattr(_cffi_backend, name) assert isinstance(tp, type) assert (tp.__module__, tp.__name__) == ('_cffi_backend', name) def test_unaligned_types(): BByteArray = new_array_type( new_pointer_type(new_primitive_type("unsigned char")), None) pbuf = newp(BByteArray, 40) buf = buffer(pbuf) # for name in ['short', 'int', 'long', 'long long', 'float', 'double', 'float _Complex', 'double _Complex']: p = new_primitive_type(name) if name.endswith(' _Complex'): num = cast(p, 1.23 - 4.56j) else: num = cast(p, 0x0123456789abcdef) size = sizeof(p) buf[0:40] = b"\x00" * 40 pbuf1 = cast(new_pointer_type(p), pbuf + 1) pbuf1[0] = num assert pbuf1[0] == num assert buf[0] == b'\x00' assert buf[1 + size] == b'\x00'