import sys
import python_director_pyobject

# A bytearray is mutable, so it is never interned nor immortal and its
# reference count is always tracked.
obj = bytearray(b"test data")

# Records whether each upcall received the object itself rather than a copy or a
# proxy. A bool is stored rather than the object so that the reference count of
# obj is not disturbed.
received = []


class PyCallback(python_director_pyobject.Callback):
    def callback(self, param1):
        received.append(param1 is obj)

    def callback_ptr(self, param1):
        received.append(param1 is obj)

    def callback_ptr_ref(self, param1):
        received.append(param1 is obj)

    def callback_var(self, param1):
        received.append(param1 is obj)

    def callback_var_ref(self, param1):
        received.append(param1 is obj)


handler = PyCallback()

callers = [
    python_director_pyobject.call_callback_ptr,
    python_director_pyobject.call_callback_ptr_ref,
    python_director_pyobject.call_callback_var,
    python_director_pyobject.call_callback_var_ref,
]

for caller in callers:
    del received[:]
    before = sys.getrefcount(obj)
    for i in range(100):
        caller(handler, obj)
        after = sys.getrefcount(obj)
        if before != after:
            raise RuntimeError("%s changed the reference count at iteration %d: %d -> %d" % (caller.__name__, i, before, after))
    if received != [True] * 100:
        raise RuntimeError("%s did not pass the object itself to the director method" % caller.__name__)

# The object must still be usable, that is, not freed while in use.
if obj != bytearray(b"test data"):
    raise RuntimeError("object was corrupted")

# A null PyObject * argument must not crash.
if python_director_pyobject.call_callback_null(handler):
    raise RuntimeError("call_callback_null should have failed the upcall")
