import pytest
import gc

def test_alias_delay_initialization(capture, msg):

    # A only initializes its trampoline class when we inherit from it; if we
    # just create and use an A instance directly, the trampoline initialization
    # is bypassed and we only initialize an A() instead (for performance
    # reasons)
    from pybind11_tests import A, call_f

    class B(A):
        def __init__(self):
            super(B, self).__init__()

        def f(self):
            print("In python f()")

    # C++ version
    with capture:
        a = A()
        call_f(a)
        del a
        gc.collect()
    assert capture == "A.f()"

    # Python version
    with capture:
        b = B()
        call_f(b)
        del b
        gc.collect()
    assert capture == """
        PyA.PyA()
        PyA.f()
        In python f()
        PyA.~PyA()
    """

def test_alias_delay_initialization(capture, msg):
    from pybind11_tests import A2, call_f

    # A2, unlike the above, is configured to always initialize the alias; while
    # the extra initialization and extra class layer has small virtual dispatch
    # performance penalty, it also allows us to do more things with the
    # trampoline class such as defining local variables and performing
    # construction/destruction.

    class B2(A2):
        def __init__(self):
            super(B2, self).__init__()

        def f(self):
            print("In python B2.f()")

    # No python subclass version
    with capture:
        a2 = A2()
        call_f(a2)
        del a2
        gc.collect()
    assert capture == """
        PyA2.PyA2()
        PyA2.f()
        A2.f()
        PyA2.~PyA2()
    """

    # Python subclass version
    with capture:
        b2 = B2()
        call_f(b2)
        del b2
        gc.collect()
    assert capture == """
        PyA2.PyA2()
        PyA2.f()
        In python B2.f()
        PyA2.~PyA2()
    """
