interoptopus_backend_cpython_cffi 0.13.8

Generates CPython CFFI bindings.
Documentation
import sys
import unittest
import ctypes
import random

import reference_project as r
from reference_project import api

r.init_api("../../../../target/debug/interoptopus_reference_project.dll")


# https://stackoverflow.com/questions/52475749/maximum-and-minimum-value-of-c-types-integers-from-python
def limits(c_int_type):
    signed = c_int_type(-1).value < c_int_type(0).value
    bit_size = ctypes.sizeof(c_int_type) * 8
    signed_limit = 2 ** (bit_size - 1)
    return (-signed_limit, signed_limit - 1) if signed else (0, 2 * signed_limit - 1)


i8_min = limits(ctypes.c_int8)[0]
i16_min = limits(ctypes.c_int16)[0]
i32_min = limits(ctypes.c_int32)[0]
i64_min = limits(ctypes.c_int64)[0]

i8_max = limits(ctypes.c_int8)[1]
i16_max = limits(ctypes.c_int16)[1]
i32_max = limits(ctypes.c_int32)[1]
i64_max = limits(ctypes.c_int64)[1]

u8_max = limits(ctypes.c_uint8)[1]
u16_max = limits(ctypes.c_uint16)[1]
u32_max = limits(ctypes.c_uint32)[1]
u64_max = limits(ctypes.c_uint64)[1]

null = r.ffi.NULL


class TestConstants(unittest.TestCase):

    def test_U8(self):
        self.assertEqual(u8_max, r.U8)

    def test_COMPUTED_I32(self):
        self.assertEqual(-i32_max, r.COMPUTED_I32)

    def test_F32_MIN_POSITIVE(self):
        self.assertEqual(0.000000000000000000000000000000000000011754944, r.F32_MIN_POSITIVE)


class TestFunctions(unittest.TestCase):

    def test_U8(self):
        self.assertEqual(u8_max, r.U8)

    def test_primitives(self):
        api.primitive_void()
        api.primitive_void2()

        self.assertEqual(True, api.primitive_bool(False))

        self.assertEqual(0, api.primitive_u8(u8_max))
        self.assertEqual(0, api.primitive_u16(u16_max))
        self.assertEqual(0, api.primitive_u32(u32_max))
        self.assertEqual(0, api.primitive_u64(u64_max))

        self.assertEqual(-i8_max, api.primitive_i8(i8_max))
        self.assertEqual(-i16_max, api.primitive_i16(i16_max))
        self.assertEqual(-i32_max, api.primitive_i32(i32_max))
        self.assertEqual(-i64_max, api.primitive_i64(i64_max))

    def test_ptr(self):
        ptr = r.int64_t(100)
        # ptr = r.ffi.new("int64_t*", 100)
        ptr_ptr = r.ffi.new("int64_t**", ptr.c_ptr())

        self.assertEqual(ptr.c_ptr(), api.ptr(ptr))
        self.assertEqual(ptr_ptr, api.ptr_ptr(ptr_ptr))

        self.assertEqual(True, api.ref_option(ptr))
        self.assertEqual(True, api.ref_mut_option(ptr))

        self.assertEqual(ptr.c_ptr(), api.ptr_mut(ptr))
        self.assertEqual(ptr.c_value(), -100)

        self.assertEqual(ptr.c_ptr(), api.ref_mut_simple(ptr))
        self.assertEqual(ptr.c_value(), 100)

    def test_tuple(self):
        tupled = r.Tupled(x0=100)
        self.assertEqual(200, api.tupled(tupled).x0)

    def test_complex(self):
        vec = r.Vec3f32()
        foreign = r.SomeForeignType()

        self.assertEqual(r.FFIError.Ok, api.complex_args_1(vec, null))
        self.assertEqual(null, api.complex_args_2(foreign))

    def test_callback(self):
        def my_callback(param):
            return param * 3

        self.assertEqual(9, api.callback(my_callback, 3))

    def test_generic(self):
        uint32 = r.uint32_t(10)
        uint8 = r.uint8_t(10)

        genericu32 = r.Genericu32(x=uint32.c_ptr())
        genericu8 = r.Genericu8(x=uint8.c_ptr())
        phantom = r.Phantomu8()

        self.assertEqual(10, api.generic_1a(genericu32, phantom))
        # self.assertEqual(10, api.generic_2(genericu8.ptr()))

    def test_documented(self):
        documented = r.StructDocumented()

        self.assertEqual(r.EnumDocumented.A, api.documented(documented))

    def test_ambiguous(self):
        vec1 = r.Vec1(x=10.0)
        vec2 = r.Vec2(x=10.0, z=11.0)

        for i in range(1000):
            self.assertEqual(10.0, api.ambiguous_1(vec1).x)
            self.assertEqual(11.0, api.ambiguous_2(vec2).z)
            self.assertEqual(True, api.ambiguous_3(vec1, vec2))


    def test_namespaces(self):
        vec = r.Vec(x=10)

        self.assertEqual(10.0, api.namespaced_type(vec).x)

    def test_panics(self):
        worked = False
        try:
            api.panics()
            worked = True
        except:
            pass

        self.assertFalse(worked)


class TestPatterns(unittest.TestCase):

    def test_ascii_pointers(self):
        some_str_5 = b"01234"
        some_str_5_ascii = r.ascii_string(some_str_5)
        use_ascii = r.UseAsciiStringPattern(ascii_string=some_str_5_ascii)

        self.assertEqual(0, api.pattern_ascii_pointer_1(null))
        self.assertEqual(10, api.pattern_ascii_pointer_1(b"0123456789"))

        self.assertEqual(10, api.pattern_ascii_pointer_len(some_str_5, use_ascii))
        self.assertEqual(5, api.pattern_ascii_pointer_len(null, use_ascii))

        # Test these really hard because we've had some spurious errors
        for i in range(1000):
            s = b"x" * random.randint(0, 100)
            self.assertEqual(len(s), api.pattern_ascii_pointer_1(s))


    def test_slices(self):
        # uint32 = r.CArray("uint32_t", 10_000)
        uints = r.uint32_t.c_array(10_000)
        vecs = r.Vec3f32.c_array(100)

        api.pattern_ffi_slice_1(uints)
        api.pattern_ffi_slice_2(vecs, 1000)

        some_value = 0

        def my_callback(param):
            nonlocal some_value
            some_value = param[4]
            return 0

        api.pattern_ffi_slice_delegate(my_callback)

        self.assertEqual(some_value, 4)

    def test_services(self):
        s1 = r.SimpleService.new_without()
        s2 = r.SimpleService.new_with(123)

        try:
            s3 = r.SimpleService.new_failing()
            s3.method_void()
        except:
            pass

        assert (s1.method_value(123) == s2.method_value(123))


if __name__ == '__main__':
    unittest.main()
    sys.exit(0)