| import py, sys, platform |
| import pytest |
| from testing.cffi0 import backend_tests, test_function, test_ownlib |
| from testing.support import u |
| from cffi import FFI |
| import _cffi_backend |
| |
| |
| class TestFFI(backend_tests.BackendTests, |
| test_function.TestFunction, |
| test_ownlib.TestOwnLib): |
| TypeRepr = "<ctype '%s'>" |
| |
| @staticmethod |
| def Backend(): |
| return _cffi_backend |
| |
| def test_not_supported_bitfield_in_result(self): |
| ffi = FFI(backend=self.Backend()) |
| ffi.cdef("struct foo_s { int a,b,c,d,e; int x:1; };") |
| e = py.test.raises(NotImplementedError, ffi.callback, |
| "struct foo_s foo(void)", lambda: 42) |
| assert str(e.value) == ("struct foo_s(*)(): " |
| "callback with unsupported argument or return type or with '...'") |
| |
| def test_inspecttype(self): |
| ffi = FFI(backend=self.Backend()) |
| assert ffi.typeof("long").kind == "primitive" |
| assert ffi.typeof("long(*)(long, long**, ...)").cname == ( |
| "long(*)(long, long * *, ...)") |
| assert ffi.typeof("long(*)(long, long**, ...)").ellipsis is True |
| |
| def test_new_handle(self): |
| ffi = FFI(backend=self.Backend()) |
| o = [2, 3, 4] |
| p = ffi.new_handle(o) |
| assert ffi.typeof(p) == ffi.typeof("void *") |
| assert ffi.from_handle(p) is o |
| assert ffi.from_handle(ffi.cast("char *", p)) is o |
| py.test.raises(RuntimeError, ffi.from_handle, ffi.NULL) |
| |
| def test_callback_onerror(self): |
| ffi = FFI(backend=self.Backend()) |
| seen = [] |
| def oops(*args): |
| seen.append(args) |
| def otherfunc(): |
| raise LookupError |
| def cb(n): |
| otherfunc() |
| a = ffi.callback("int(*)(int)", cb, error=42, onerror=oops) |
| res = a(234) |
| assert res == 42 |
| assert len(seen) == 1 |
| exc, val, tb = seen[0] |
| assert exc is LookupError |
| assert isinstance(val, LookupError) |
| assert tb.tb_frame.f_code.co_name == 'cb' |
| assert tb.tb_frame.f_locals['n'] == 234 |
| |
| def test_ffi_new_allocator_2(self): |
| ffi = FFI(backend=self.Backend()) |
| seen = [] |
| def myalloc(size): |
| seen.append(size) |
| return ffi.new("char[]", b"X" * size) |
| def myfree(raw): |
| seen.append(raw) |
| alloc1 = ffi.new_allocator(myalloc, myfree) |
| alloc2 = ffi.new_allocator(alloc=myalloc, free=myfree, |
| should_clear_after_alloc=False) |
| p1 = alloc1("int[10]") |
| p2 = alloc2("int[]", 10) |
| assert seen == [40, 40] |
| assert ffi.typeof(p1) == ffi.typeof("int[10]") |
| assert ffi.sizeof(p1) == 40 |
| assert ffi.typeof(p2) == ffi.typeof("int[]") |
| assert ffi.sizeof(p2) == 40 |
| assert p1[5] == 0 |
| assert p2[6] == ord('X') * 0x01010101 |
| raw1 = ffi.cast("char *", p1) |
| raw2 = ffi.cast("char *", p2) |
| del p1, p2 |
| retries = 0 |
| while len(seen) != 4: |
| retries += 1 |
| assert retries <= 5 |
| import gc; gc.collect() |
| assert seen == [40, 40, raw1, raw2] |
| assert repr(seen[2]) == "<cdata 'char[]' owning 41 bytes>" |
| assert repr(seen[3]) == "<cdata 'char[]' owning 41 bytes>" |
| |
| def test_ffi_new_allocator_3(self): |
| ffi = FFI(backend=self.Backend()) |
| seen = [] |
| def myalloc(size): |
| seen.append(size) |
| return ffi.new("char[]", b"X" * size) |
| alloc1 = ffi.new_allocator(myalloc) # no 'free' |
| p1 = alloc1("int[10]") |
| assert seen == [40] |
| assert ffi.typeof(p1) == ffi.typeof("int[10]") |
| assert ffi.sizeof(p1) == 40 |
| assert p1[5] == 0 |
| |
| def test_ffi_new_allocator_4(self): |
| ffi = FFI(backend=self.Backend()) |
| py.test.raises(TypeError, ffi.new_allocator, free=lambda x: None) |
| # |
| def myalloc2(size): |
| raise LookupError |
| alloc2 = ffi.new_allocator(myalloc2) |
| py.test.raises(LookupError, alloc2, "int[5]") |
| # |
| def myalloc3(size): |
| return 42 |
| alloc3 = ffi.new_allocator(myalloc3) |
| e = py.test.raises(TypeError, alloc3, "int[5]") |
| assert str(e.value) == "alloc() must return a cdata object (got int)" |
| # |
| def myalloc4(size): |
| return ffi.cast("int", 42) |
| alloc4 = ffi.new_allocator(myalloc4) |
| e = py.test.raises(TypeError, alloc4, "int[5]") |
| assert str(e.value) == "alloc() must return a cdata pointer, not 'int'" |
| # |
| def myalloc5(size): |
| return ffi.NULL |
| alloc5 = ffi.new_allocator(myalloc5) |
| py.test.raises(MemoryError, alloc5, "int[5]") |
| |
| |
| class TestBitfield: |
| def check(self, source, expected_ofs_y, expected_align, expected_size): |
| # NOTE: 'expected_*' is the numbers expected from GCC. |
| # The numbers expected from MSVC are not explicitly written |
| # in this file, and will just be taken from the compiler. |
| ffi = FFI() |
| ffi.cdef("struct s1 { %s };" % source) |
| ctype = ffi.typeof("struct s1") |
| # verify the information with gcc |
| ffi1 = FFI() |
| ffi1.cdef(""" |
| static const int Gofs_y, Galign, Gsize; |
| struct s1 *try_with_value(int fieldnum, long long value); |
| """) |
| fnames = [name for name, cfield in ctype.fields |
| if name and cfield.bitsize > 0] |
| setters = ['case %d: s.%s = value; break;' % iname |
| for iname in enumerate(fnames)] |
| lib = ffi1.verify(""" |
| struct s1 { %s }; |
| struct sa { char a; struct s1 b; }; |
| #define Gofs_y offsetof(struct s1, y) |
| #define Galign offsetof(struct sa, b) |
| #define Gsize sizeof(struct s1) |
| struct s1 *try_with_value(int fieldnum, long long value) |
| { |
| static struct s1 s; |
| memset(&s, 0, sizeof(s)); |
| switch (fieldnum) { %s } |
| return &s; |
| } |
| """ % (source, ' '.join(setters))) |
| if sys.platform == 'win32': |
| expected_ofs_y = lib.Gofs_y |
| expected_align = lib.Galign |
| expected_size = lib.Gsize |
| else: |
| assert (lib.Gofs_y, lib.Galign, lib.Gsize) == ( |
| expected_ofs_y, expected_align, expected_size) |
| # the real test follows |
| assert ffi.offsetof("struct s1", "y") == expected_ofs_y |
| assert ffi.alignof("struct s1") == expected_align |
| assert ffi.sizeof("struct s1") == expected_size |
| # compare the actual storage of the two |
| for name, cfield in ctype.fields: |
| if cfield.bitsize < 0 or not name: |
| continue |
| if int(ffi.cast(cfield.type, -1)) == -1: # signed |
| min_value = -(1 << (cfield.bitsize-1)) |
| max_value = (1 << (cfield.bitsize-1)) - 1 |
| else: |
| min_value = 0 |
| max_value = (1 << cfield.bitsize) - 1 |
| for t in [1, 2, 4, 8, 16, 128, 2813, 89728, 981729, |
| -1,-2,-4,-8,-16,-128,-2813,-89728,-981729]: |
| if min_value <= t <= max_value: |
| self._fieldcheck(ffi, lib, fnames, name, t) |
| |
| def _fieldcheck(self, ffi, lib, fnames, name, value): |
| s = ffi.new("struct s1 *") |
| setattr(s, name, value) |
| assert getattr(s, name) == value |
| raw1 = ffi.buffer(s)[:] |
| buff1 = ffi.buffer(s) |
| t = lib.try_with_value(fnames.index(name), value) |
| raw2 = ffi.buffer(t, len(raw1))[:] |
| assert raw1 == raw2 |
| buff2 = ffi.buffer(t, len(buff1)) |
| assert buff1 == buff2 |
| |
| def test_bitfield_basic(self): |
| self.check("int a; int b:9; int c:20; int y;", 8, 4, 12) |
| self.check("int a; short b:9; short c:7; int y;", 8, 4, 12) |
| self.check("int a; short b:9; short c:9; int y;", 8, 4, 12) |
| |
| def test_bitfield_reuse_if_enough_space(self): |
| self.check("int a:2; char y;", 1, 4, 4) |
| self.check("int a:1; char b ; int c:1; char y;", 3, 4, 4) |
| self.check("int a:1; char b:8; int c:1; char y;", 3, 4, 4) |
| self.check("char a; int b:9; char y;", 3, 4, 4) |
| self.check("char a; short b:9; char y;", 4, 2, 6) |
| self.check("int a:2; char b:6; char y;", 1, 4, 4) |
| self.check("int a:2; char b:7; char y;", 2, 4, 4) |
| self.check("int a:2; short b:15; char c:2; char y;", 5, 4, 8) |
| self.check("int a:2; char b:1; char c:1; char y;", 1, 4, 4) |
| |
| @pytest.mark.skipif("platform.machine().startswith(('arm', 'aarch64'))") |
| def test_bitfield_anonymous_no_align(self): |
| L = FFI().alignof("long long") |
| self.check("char y; int :1;", 0, 1, 2) |
| self.check("char x; int z:1; char y;", 2, 4, 4) |
| self.check("char x; int :1; char y;", 2, 1, 3) |
| self.check("char x; long long z:48; char y;", 7, L, 8) |
| self.check("char x; long long :48; char y;", 7, 1, 8) |
| self.check("char x; long long z:56; char y;", 8, L, 8 + L) |
| self.check("char x; long long :56; char y;", 8, 1, 9) |
| self.check("char x; long long z:57; char y;", L + 8, L, L + 8 + L) |
| self.check("char x; long long :57; char y;", L + 8, 1, L + 9) |
| |
| @pytest.mark.skipif( |
| "not platform.machine().startswith(('arm', 'aarch64'))") |
| def test_bitfield_anonymous_align_arm(self): |
| L = FFI().alignof("long long") |
| self.check("char y; int :1;", 0, 4, 4) |
| self.check("char x; int z:1; char y;", 2, 4, 4) |
| self.check("char x; int :1; char y;", 2, 4, 4) |
| self.check("char x; long long z:48; char y;", 7, L, 8) |
| self.check("char x; long long :48; char y;", 7, 8, 8) |
| self.check("char x; long long z:56; char y;", 8, L, 8 + L) |
| self.check("char x; long long :56; char y;", 8, L, 8 + L) |
| self.check("char x; long long z:57; char y;", L + 8, L, L + 8 + L) |
| self.check("char x; long long :57; char y;", L + 8, L, L + 8 + L) |
| |
| @pytest.mark.skipif("platform.machine().startswith(('arm', 'aarch64'))") |
| def test_bitfield_zero(self): |
| L = FFI().alignof("long long") |
| self.check("char y; int :0;", 0, 1, 4) |
| self.check("char x; int :0; char y;", 4, 1, 5) |
| self.check("char x; int :0; int :0; char y;", 4, 1, 5) |
| self.check("char x; long long :0; char y;", L, 1, L + 1) |
| self.check("short x, y; int :0; int :0;", 2, 2, 4) |
| self.check("char x; int :0; short b:1; char y;", 5, 2, 6) |
| self.check("int a:1; int :0; int b:1; char y;", 5, 4, 8) |
| |
| @pytest.mark.skipif( |
| "not platform.machine().startswith(('arm', 'aarch64'))") |
| def test_bitfield_zero_arm(self): |
| L = FFI().alignof("long long") |
| self.check("char y; int :0;", 0, 4, 4) |
| self.check("char x; int :0; char y;", 4, 4, 8) |
| self.check("char x; int :0; int :0; char y;", 4, 4, 8) |
| self.check("char x; long long :0; char y;", L, 8, L + 8) |
| self.check("short x, y; int :0; int :0;", 2, 4, 4) |
| self.check("char x; int :0; short b:1; char y;", 5, 4, 8) |
| self.check("int a:1; int :0; int b:1; char y;", 5, 4, 8) |
| |
| def test_error_cases(self): |
| ffi = FFI() |
| py.test.raises(TypeError, |
| 'ffi.cdef("struct s1 { float x:1; };"); ffi.new("struct s1 *")') |
| py.test.raises(TypeError, |
| 'ffi.cdef("struct s2 { char x:0; };"); ffi.new("struct s2 *")') |
| py.test.raises(TypeError, |
| 'ffi.cdef("struct s3 { char x:9; };"); ffi.new("struct s3 *")') |
| |
| def test_struct_with_typedef(self): |
| ffi = FFI() |
| ffi.cdef("typedef struct { float x; } foo_t;") |
| p = ffi.new("foo_t *", [5.2]) |
| assert repr(p).startswith("<cdata 'foo_t *' ") |
| |
| def test_struct_array_no_length(self): |
| ffi = FFI() |
| ffi.cdef("struct foo_s { int x; int a[]; };") |
| p = ffi.new("struct foo_s *", [100, [200, 300, 400]]) |
| assert p.x == 100 |
| assert ffi.typeof(p.a) is ffi.typeof("int[]") |
| assert len(p.a) == 3 # length recorded |
| assert p.a[0] == 200 |
| assert p.a[1] == 300 |
| assert p.a[2] == 400 |
| assert list(p.a) == [200, 300, 400] |
| q = ffi.cast("struct foo_s *", p) |
| assert q.x == 100 |
| assert ffi.typeof(q.a) is ffi.typeof("int *") # no length recorded |
| py.test.raises(TypeError, len, q.a) |
| assert q.a[0] == 200 |
| assert q.a[1] == 300 |
| assert q.a[2] == 400 |
| py.test.raises(TypeError, list, q.a) |
| |
| @pytest.mark.skipif("sys.platform != 'win32'") |
| def test_getwinerror(self): |
| ffi = FFI() |
| code, message = ffi.getwinerror(1155) |
| assert code == 1155 |
| assert message == ("No application is associated with the " |
| "specified file for this operation") |
| ffi.cdef("void SetLastError(int);") |
| lib = ffi.dlopen("Kernel32.dll") |
| lib.SetLastError(2) |
| code, message = ffi.getwinerror() |
| assert code == 2 |
| assert message == "The system cannot find the file specified" |
| code, message = ffi.getwinerror(-1) |
| assert code == 2 |
| assert message == "The system cannot find the file specified" |
| |
| def test_from_buffer(self): |
| import array |
| ffi = FFI() |
| a = array.array('H', [10000, 20000, 30000]) |
| c = ffi.from_buffer(a) |
| assert ffi.typeof(c) is ffi.typeof("char[]") |
| assert len(c) == 6 |
| ffi.cast("unsigned short *", c)[1] += 500 |
| assert list(a) == [10000, 20500, 30000] |
| assert c == ffi.from_buffer("char[]", a, True) |
| assert c == ffi.from_buffer(a, require_writable=True) |
| # |
| c = ffi.from_buffer("unsigned short[]", a) |
| assert len(c) == 3 |
| assert c[1] == 20500 |
| # |
| p = ffi.from_buffer(b"abcd") |
| assert p[2] == b"c" |
| # |
| assert p == ffi.from_buffer(b"abcd", require_writable=False) |
| py.test.raises((TypeError, BufferError), ffi.from_buffer, |
| "char[]", b"abcd", True) |
| py.test.raises((TypeError, BufferError), ffi.from_buffer, b"abcd", |
| require_writable=True) |
| |
| def test_release(self): |
| ffi = FFI() |
| p = ffi.new("int[]", 123) |
| ffi.release(p) |
| # here, reading p[0] might give garbage or segfault... |
| ffi.release(p) # no effect |
| |
| def test_memmove(self): |
| ffi = FFI() |
| p = ffi.new("short[]", [-1234, -2345, -3456, -4567, -5678]) |
| ffi.memmove(p, p + 1, 4) |
| assert list(p) == [-2345, -3456, -3456, -4567, -5678] |
| p[2] = 999 |
| ffi.memmove(p + 2, p, 6) |
| assert list(p) == [-2345, -3456, -2345, -3456, 999] |
| ffi.memmove(p + 4, ffi.new("char[]", b"\x71\x72"), 2) |
| if sys.byteorder == 'little': |
| assert list(p) == [-2345, -3456, -2345, -3456, 0x7271] |
| else: |
| assert list(p) == [-2345, -3456, -2345, -3456, 0x7172] |
| |
| def test_memmove_buffer(self): |
| import array |
| ffi = FFI() |
| a = array.array('H', [10000, 20000, 30000]) |
| p = ffi.new("short[]", 5) |
| ffi.memmove(p, a, 6) |
| assert list(p) == [10000, 20000, 30000, 0, 0] |
| ffi.memmove(p + 1, a, 6) |
| assert list(p) == [10000, 10000, 20000, 30000, 0] |
| b = array.array('h', [-1000, -2000, -3000]) |
| ffi.memmove(b, a, 4) |
| assert b.tolist() == [10000, 20000, -3000] |
| assert a.tolist() == [10000, 20000, 30000] |
| p[0] = 999 |
| p[1] = 998 |
| p[2] = 997 |
| p[3] = 996 |
| p[4] = 995 |
| ffi.memmove(b, p, 2) |
| assert b.tolist() == [999, 20000, -3000] |
| ffi.memmove(b, p + 2, 4) |
| assert b.tolist() == [997, 996, -3000] |
| p[2] = -p[2] |
| p[3] = -p[3] |
| ffi.memmove(b, p + 2, 6) |
| assert b.tolist() == [-997, -996, 995] |
| |
| def test_memmove_readonly_readwrite(self): |
| ffi = FFI() |
| p = ffi.new("signed char[]", 5) |
| ffi.memmove(p, b"abcde", 3) |
| assert list(p) == [ord("a"), ord("b"), ord("c"), 0, 0] |
| ffi.memmove(p, bytearray(b"ABCDE"), 2) |
| assert list(p) == [ord("A"), ord("B"), ord("c"), 0, 0] |
| py.test.raises((TypeError, BufferError), ffi.memmove, b"abcde", p, 3) |
| ba = bytearray(b"xxxxx") |
| ffi.memmove(dest=ba, src=p, n=3) |
| assert ba == bytearray(b"ABcxx") |
| |
| def test_all_primitives(self): |
| ffi = FFI() |
| for name in [ |
| "char", |
| "short", |
| "int", |
| "long", |
| "long long", |
| "signed char", |
| "unsigned char", |
| "unsigned short", |
| "unsigned int", |
| "unsigned long", |
| "unsigned long long", |
| "float", |
| "double", |
| "long double", |
| "wchar_t", |
| "char16_t", |
| "char32_t", |
| "_Bool", |
| "int8_t", |
| "uint8_t", |
| "int16_t", |
| "uint16_t", |
| "int32_t", |
| "uint32_t", |
| "int64_t", |
| "uint64_t", |
| "int_least8_t", |
| "uint_least8_t", |
| "int_least16_t", |
| "uint_least16_t", |
| "int_least32_t", |
| "uint_least32_t", |
| "int_least64_t", |
| "uint_least64_t", |
| "int_fast8_t", |
| "uint_fast8_t", |
| "int_fast16_t", |
| "uint_fast16_t", |
| "int_fast32_t", |
| "uint_fast32_t", |
| "int_fast64_t", |
| "uint_fast64_t", |
| "intptr_t", |
| "uintptr_t", |
| "intmax_t", |
| "uintmax_t", |
| "ptrdiff_t", |
| "size_t", |
| "ssize_t", |
| ]: |
| x = ffi.sizeof(name) |
| assert 1 <= x <= 16 |
| |
| def test_ffi_def_extern(self): |
| ffi = FFI() |
| py.test.raises(ValueError, ffi.def_extern) |
| |
| def test_introspect_typedef(self): |
| ffi = FFI() |
| ffi.cdef("typedef int foo_t;") |
| assert ffi.list_types() == (['foo_t'], [], []) |
| assert ffi.typeof('foo_t').kind == 'primitive' |
| assert ffi.typeof('foo_t').cname == 'int' |
| # |
| ffi.cdef("typedef signed char a_t, c_t, g_t, b_t;") |
| assert ffi.list_types() == (['a_t', 'b_t', 'c_t', 'foo_t', 'g_t'], |
| [], []) |
| |
| def test_introspect_struct(self): |
| ffi = FFI() |
| ffi.cdef("struct foo_s { int a; };") |
| assert ffi.list_types() == ([], ['foo_s'], []) |
| assert ffi.typeof('struct foo_s').kind == 'struct' |
| assert ffi.typeof('struct foo_s').cname == 'struct foo_s' |
| |
| def test_introspect_union(self): |
| ffi = FFI() |
| ffi.cdef("union foo_s { int a; };") |
| assert ffi.list_types() == ([], [], ['foo_s']) |
| assert ffi.typeof('union foo_s').kind == 'union' |
| assert ffi.typeof('union foo_s').cname == 'union foo_s' |
| |
| def test_introspect_struct_and_typedef(self): |
| ffi = FFI() |
| ffi.cdef("typedef struct { int a; } foo_t;") |
| assert ffi.list_types() == (['foo_t'], [], []) |
| assert ffi.typeof('foo_t').kind == 'struct' |
| assert ffi.typeof('foo_t').cname == 'foo_t' |
| |
| def test_introspect_included_type(self): |
| ffi1 = FFI() |
| ffi2 = FFI() |
| ffi1.cdef("typedef signed char schar_t; struct sint_t { int x; };") |
| ffi2.include(ffi1) |
| assert ffi1.list_types() == ffi2.list_types() == ( |
| ['schar_t'], ['sint_t'], []) |
| |
| def test_introspect_order(self): |
| ffi = FFI() |
| ffi.cdef("union CFFIaaa { int a; }; typedef struct CFFIccc { int a; } CFFIb;") |
| ffi.cdef("union CFFIg { int a; }; typedef struct CFFIcc { int a; } CFFIbbb;") |
| ffi.cdef("union CFFIaa { int a; }; typedef struct CFFIa { int a; } CFFIbb;") |
| assert ffi.list_types() == (['CFFIb', 'CFFIbb', 'CFFIbbb'], |
| ['CFFIa', 'CFFIcc', 'CFFIccc'], |
| ['CFFIaa', 'CFFIaaa', 'CFFIg']) |
| |
| def test_unpack(self): |
| ffi = FFI() |
| p = ffi.new("char[]", b"abc\x00def") |
| assert ffi.unpack(p+1, 7) == b"bc\x00def\x00" |
| p = ffi.new("int[]", [-123456789]) |
| assert ffi.unpack(p, 1) == [-123456789] |
| |
| def test_negative_array_size(self): |
| ffi = FFI() |
| py.test.raises(ValueError, ffi.cast, "int[-5]", 0) |
| |
| def test_cannot_instantiate_manually(self): |
| ffi = FFI() |
| ct = type(ffi.typeof("void *")) |
| py.test.raises(TypeError, ct) |
| py.test.raises(TypeError, ct, ffi.NULL) |
| for cd in [type(ffi.cast("void *", 0)), |
| type(ffi.new("char[]", 3)), |
| type(ffi.gc(ffi.NULL, lambda x: None))]: |
| py.test.raises(TypeError, cd) |
| py.test.raises(TypeError, cd, ffi.NULL) |
| py.test.raises(TypeError, cd, ffi.typeof("void *")) |
| |
| def test_explicitly_defined_char16_t(self): |
| ffi = FFI() |
| ffi.cdef("typedef uint16_t char16_t;") |
| x = ffi.cast("char16_t", 1234) |
| assert ffi.typeof(x) is ffi.typeof("uint16_t") |
| |
| def test_char16_t(self): |
| ffi = FFI() |
| x = ffi.new("char16_t[]", 5) |
| assert len(x) == 5 and ffi.sizeof(x) == 10 |
| x[2] = u+'\u1324' |
| assert x[2] == u+'\u1324' |
| y = ffi.new("char16_t[]", u+'\u1234\u5678') |
| assert len(y) == 3 |
| assert list(y) == [u+'\u1234', u+'\u5678', u+'\x00'] |
| assert ffi.string(y) == u+'\u1234\u5678' |
| z = ffi.new("char16_t[]", u+'\U00012345') |
| assert len(z) == 3 |
| assert list(z) == [u+'\ud808', u+'\udf45', u+'\x00'] |
| assert ffi.string(z) == u+'\U00012345' |
| |
| def test_char32_t(self): |
| ffi = FFI() |
| x = ffi.new("char32_t[]", 5) |
| assert len(x) == 5 and ffi.sizeof(x) == 20 |
| x[3] = u+'\U00013245' |
| assert x[3] == u+'\U00013245' |
| y = ffi.new("char32_t[]", u+'\u1234\u5678') |
| assert len(y) == 3 |
| assert list(y) == [u+'\u1234', u+'\u5678', u+'\x00'] |
| py_uni = u+'\U00012345' |
| z = ffi.new("char32_t[]", py_uni) |
| assert len(z) == 2 |
| assert list(z) == [py_uni, u+'\x00'] # maybe a 2-unichars string |
| assert ffi.string(z) == py_uni |
| if len(py_uni) == 1: # 4-bytes unicodes in Python |
| s = ffi.new("char32_t[]", u+'\ud808\udf00') |
| assert len(s) == 3 |
| assert list(s) == [u+'\ud808', u+'\udf00', u+'\x00'] |