112391Sjason@lowepower.comimport pytest
212391Sjason@lowepower.comimport re
312391Sjason@lowepower.com
412391Sjason@lowepower.comfrom pybind11_tests import factory_constructors as m
512391Sjason@lowepower.comfrom pybind11_tests.factory_constructors import tag
612391Sjason@lowepower.comfrom pybind11_tests import ConstructorStats
712391Sjason@lowepower.com
812391Sjason@lowepower.com
912391Sjason@lowepower.comdef test_init_factory_basic():
1012391Sjason@lowepower.com    """Tests py::init_factory() wrapper around various ways of returning the object"""
1112391Sjason@lowepower.com
1212391Sjason@lowepower.com    cstats = [ConstructorStats.get(c) for c in [m.TestFactory1, m.TestFactory2, m.TestFactory3]]
1312391Sjason@lowepower.com    cstats[0].alive()  # force gc
1412391Sjason@lowepower.com    n_inst = ConstructorStats.detail_reg_inst()
1512391Sjason@lowepower.com
1612391Sjason@lowepower.com    x1 = m.TestFactory1(tag.unique_ptr, 3)
1712391Sjason@lowepower.com    assert x1.value == "3"
1812391Sjason@lowepower.com    y1 = m.TestFactory1(tag.pointer)
1912391Sjason@lowepower.com    assert y1.value == "(empty)"
2012391Sjason@lowepower.com    z1 = m.TestFactory1("hi!")
2112391Sjason@lowepower.com    assert z1.value == "hi!"
2212391Sjason@lowepower.com
2312391Sjason@lowepower.com    assert ConstructorStats.detail_reg_inst() == n_inst + 3
2412391Sjason@lowepower.com
2512391Sjason@lowepower.com    x2 = m.TestFactory2(tag.move)
2612391Sjason@lowepower.com    assert x2.value == "(empty2)"
2712391Sjason@lowepower.com    y2 = m.TestFactory2(tag.pointer, 7)
2812391Sjason@lowepower.com    assert y2.value == "7"
2912391Sjason@lowepower.com    z2 = m.TestFactory2(tag.unique_ptr, "hi again")
3012391Sjason@lowepower.com    assert z2.value == "hi again"
3112391Sjason@lowepower.com
3212391Sjason@lowepower.com    assert ConstructorStats.detail_reg_inst() == n_inst + 6
3312391Sjason@lowepower.com
3412391Sjason@lowepower.com    x3 = m.TestFactory3(tag.shared_ptr)
3512391Sjason@lowepower.com    assert x3.value == "(empty3)"
3612391Sjason@lowepower.com    y3 = m.TestFactory3(tag.pointer, 42)
3712391Sjason@lowepower.com    assert y3.value == "42"
3812391Sjason@lowepower.com    z3 = m.TestFactory3("bye")
3912391Sjason@lowepower.com    assert z3.value == "bye"
4012391Sjason@lowepower.com
4112391Sjason@lowepower.com    with pytest.raises(TypeError) as excinfo:
4212391Sjason@lowepower.com        m.TestFactory3(tag.null_ptr)
4312391Sjason@lowepower.com    assert str(excinfo.value) == "pybind11::init(): factory function returned nullptr"
4412391Sjason@lowepower.com
4512391Sjason@lowepower.com    assert [i.alive() for i in cstats] == [3, 3, 3]
4612391Sjason@lowepower.com    assert ConstructorStats.detail_reg_inst() == n_inst + 9
4712391Sjason@lowepower.com
4812391Sjason@lowepower.com    del x1, y2, y3, z3
4912391Sjason@lowepower.com    assert [i.alive() for i in cstats] == [2, 2, 1]
5012391Sjason@lowepower.com    assert ConstructorStats.detail_reg_inst() == n_inst + 5
5112391Sjason@lowepower.com    del x2, x3, y1, z1, z2
5212391Sjason@lowepower.com    assert [i.alive() for i in cstats] == [0, 0, 0]
5312391Sjason@lowepower.com    assert ConstructorStats.detail_reg_inst() == n_inst
5412391Sjason@lowepower.com
5512391Sjason@lowepower.com    assert [i.values() for i in cstats] == [
5612391Sjason@lowepower.com        ["3", "hi!"],
5712391Sjason@lowepower.com        ["7", "hi again"],
5812391Sjason@lowepower.com        ["42", "bye"]
5912391Sjason@lowepower.com    ]
6012391Sjason@lowepower.com    assert [i.default_constructions for i in cstats] == [1, 1, 1]
6112391Sjason@lowepower.com
6212391Sjason@lowepower.com
6312391Sjason@lowepower.comdef test_init_factory_signature(msg):
6412391Sjason@lowepower.com    with pytest.raises(TypeError) as excinfo:
6512391Sjason@lowepower.com        m.TestFactory1("invalid", "constructor", "arguments")
6612391Sjason@lowepower.com    assert msg(excinfo.value) == """
6712391Sjason@lowepower.com        __init__(): incompatible constructor arguments. The following argument types are supported:
6812391Sjason@lowepower.com            1. m.factory_constructors.TestFactory1(arg0: m.factory_constructors.tag.unique_ptr_tag, arg1: int)
6912391Sjason@lowepower.com            2. m.factory_constructors.TestFactory1(arg0: str)
7012391Sjason@lowepower.com            3. m.factory_constructors.TestFactory1(arg0: m.factory_constructors.tag.pointer_tag)
7112391Sjason@lowepower.com            4. m.factory_constructors.TestFactory1(arg0: handle, arg1: int, arg2: handle)
7212391Sjason@lowepower.com
7312391Sjason@lowepower.com        Invoked with: 'invalid', 'constructor', 'arguments'
7412391Sjason@lowepower.com    """  # noqa: E501 line too long
7512391Sjason@lowepower.com
7612391Sjason@lowepower.com    assert msg(m.TestFactory1.__init__.__doc__) == """
7712391Sjason@lowepower.com        __init__(*args, **kwargs)
7812391Sjason@lowepower.com        Overloaded function.
7912391Sjason@lowepower.com
8012391Sjason@lowepower.com        1. __init__(self: m.factory_constructors.TestFactory1, arg0: m.factory_constructors.tag.unique_ptr_tag, arg1: int) -> None
8112391Sjason@lowepower.com
8212391Sjason@lowepower.com        2. __init__(self: m.factory_constructors.TestFactory1, arg0: str) -> None
8312391Sjason@lowepower.com
8412391Sjason@lowepower.com        3. __init__(self: m.factory_constructors.TestFactory1, arg0: m.factory_constructors.tag.pointer_tag) -> None
8512391Sjason@lowepower.com
8612391Sjason@lowepower.com        4. __init__(self: m.factory_constructors.TestFactory1, arg0: handle, arg1: int, arg2: handle) -> None
8712391Sjason@lowepower.com    """  # noqa: E501 line too long
8812391Sjason@lowepower.com
8912391Sjason@lowepower.com
9012391Sjason@lowepower.comdef test_init_factory_casting():
9112391Sjason@lowepower.com    """Tests py::init_factory() wrapper with various upcasting and downcasting returns"""
9212391Sjason@lowepower.com
9312391Sjason@lowepower.com    cstats = [ConstructorStats.get(c) for c in [m.TestFactory3, m.TestFactory4, m.TestFactory5]]
9412391Sjason@lowepower.com    cstats[0].alive()  # force gc
9512391Sjason@lowepower.com    n_inst = ConstructorStats.detail_reg_inst()
9612391Sjason@lowepower.com
9712391Sjason@lowepower.com    # Construction from derived references:
9812391Sjason@lowepower.com    a = m.TestFactory3(tag.pointer, tag.TF4, 4)
9912391Sjason@lowepower.com    assert a.value == "4"
10012391Sjason@lowepower.com    b = m.TestFactory3(tag.shared_ptr, tag.TF4, 5)
10112391Sjason@lowepower.com    assert b.value == "5"
10212391Sjason@lowepower.com    c = m.TestFactory3(tag.pointer, tag.TF5, 6)
10312391Sjason@lowepower.com    assert c.value == "6"
10412391Sjason@lowepower.com    d = m.TestFactory3(tag.shared_ptr, tag.TF5, 7)
10512391Sjason@lowepower.com    assert d.value == "7"
10612391Sjason@lowepower.com
10712391Sjason@lowepower.com    assert ConstructorStats.detail_reg_inst() == n_inst + 4
10812391Sjason@lowepower.com
10912391Sjason@lowepower.com    # Shared a lambda with TF3:
11012391Sjason@lowepower.com    e = m.TestFactory4(tag.pointer, tag.TF4, 8)
11112391Sjason@lowepower.com    assert e.value == "8"
11212391Sjason@lowepower.com
11312391Sjason@lowepower.com    assert ConstructorStats.detail_reg_inst() == n_inst + 5
11412391Sjason@lowepower.com    assert [i.alive() for i in cstats] == [5, 3, 2]
11512391Sjason@lowepower.com
11612391Sjason@lowepower.com    del a
11712391Sjason@lowepower.com    assert [i.alive() for i in cstats] == [4, 2, 2]
11812391Sjason@lowepower.com    assert ConstructorStats.detail_reg_inst() == n_inst + 4
11912391Sjason@lowepower.com
12012391Sjason@lowepower.com    del b, c, e
12112391Sjason@lowepower.com    assert [i.alive() for i in cstats] == [1, 0, 1]
12212391Sjason@lowepower.com    assert ConstructorStats.detail_reg_inst() == n_inst + 1
12312391Sjason@lowepower.com
12412391Sjason@lowepower.com    del d
12512391Sjason@lowepower.com    assert [i.alive() for i in cstats] == [0, 0, 0]
12612391Sjason@lowepower.com    assert ConstructorStats.detail_reg_inst() == n_inst
12712391Sjason@lowepower.com
12812391Sjason@lowepower.com    assert [i.values() for i in cstats] == [
12912391Sjason@lowepower.com        ["4", "5", "6", "7", "8"],
13012391Sjason@lowepower.com        ["4", "5", "8"],
13112391Sjason@lowepower.com        ["6", "7"]
13212391Sjason@lowepower.com    ]
13312391Sjason@lowepower.com
13412391Sjason@lowepower.com
13512391Sjason@lowepower.comdef test_init_factory_alias():
13612391Sjason@lowepower.com    """Tests py::init_factory() wrapper with value conversions and alias types"""
13712391Sjason@lowepower.com
13812391Sjason@lowepower.com    cstats = [m.TestFactory6.get_cstats(), m.TestFactory6.get_alias_cstats()]
13912391Sjason@lowepower.com    cstats[0].alive()  # force gc
14012391Sjason@lowepower.com    n_inst = ConstructorStats.detail_reg_inst()
14112391Sjason@lowepower.com
14212391Sjason@lowepower.com    a = m.TestFactory6(tag.base, 1)
14312391Sjason@lowepower.com    assert a.get() == 1
14412391Sjason@lowepower.com    assert not a.has_alias()
14512391Sjason@lowepower.com    b = m.TestFactory6(tag.alias, "hi there")
14612391Sjason@lowepower.com    assert b.get() == 8
14712391Sjason@lowepower.com    assert b.has_alias()
14812391Sjason@lowepower.com    c = m.TestFactory6(tag.alias, 3)
14912391Sjason@lowepower.com    assert c.get() == 3
15012391Sjason@lowepower.com    assert c.has_alias()
15112391Sjason@lowepower.com    d = m.TestFactory6(tag.alias, tag.pointer, 4)
15212391Sjason@lowepower.com    assert d.get() == 4
15312391Sjason@lowepower.com    assert d.has_alias()
15412391Sjason@lowepower.com    e = m.TestFactory6(tag.base, tag.pointer, 5)
15512391Sjason@lowepower.com    assert e.get() == 5
15612391Sjason@lowepower.com    assert not e.has_alias()
15712391Sjason@lowepower.com    f = m.TestFactory6(tag.base, tag.alias, tag.pointer, 6)
15812391Sjason@lowepower.com    assert f.get() == 6
15912391Sjason@lowepower.com    assert f.has_alias()
16012391Sjason@lowepower.com
16112391Sjason@lowepower.com    assert ConstructorStats.detail_reg_inst() == n_inst + 6
16212391Sjason@lowepower.com    assert [i.alive() for i in cstats] == [6, 4]
16312391Sjason@lowepower.com
16412391Sjason@lowepower.com    del a, b, e
16512391Sjason@lowepower.com    assert [i.alive() for i in cstats] == [3, 3]
16612391Sjason@lowepower.com    assert ConstructorStats.detail_reg_inst() == n_inst + 3
16712391Sjason@lowepower.com    del f, c, d
16812391Sjason@lowepower.com    assert [i.alive() for i in cstats] == [0, 0]
16912391Sjason@lowepower.com    assert ConstructorStats.detail_reg_inst() == n_inst
17012391Sjason@lowepower.com
17112391Sjason@lowepower.com    class MyTest(m.TestFactory6):
17212391Sjason@lowepower.com        def __init__(self, *args):
17312391Sjason@lowepower.com            m.TestFactory6.__init__(self, *args)
17412391Sjason@lowepower.com
17512391Sjason@lowepower.com        def get(self):
17612391Sjason@lowepower.com            return -5 + m.TestFactory6.get(self)
17712391Sjason@lowepower.com
17812391Sjason@lowepower.com    # Return Class by value, moved into new alias:
17912391Sjason@lowepower.com    z = MyTest(tag.base, 123)
18012391Sjason@lowepower.com    assert z.get() == 118
18112391Sjason@lowepower.com    assert z.has_alias()
18212391Sjason@lowepower.com
18312391Sjason@lowepower.com    # Return alias by value, moved into new alias:
18412391Sjason@lowepower.com    y = MyTest(tag.alias, "why hello!")
18512391Sjason@lowepower.com    assert y.get() == 5
18612391Sjason@lowepower.com    assert y.has_alias()
18712391Sjason@lowepower.com
18812391Sjason@lowepower.com    # Return Class by pointer, moved into new alias then original destroyed:
18912391Sjason@lowepower.com    x = MyTest(tag.base, tag.pointer, 47)
19012391Sjason@lowepower.com    assert x.get() == 42
19112391Sjason@lowepower.com    assert x.has_alias()
19212391Sjason@lowepower.com
19312391Sjason@lowepower.com    assert ConstructorStats.detail_reg_inst() == n_inst + 3
19412391Sjason@lowepower.com    assert [i.alive() for i in cstats] == [3, 3]
19512391Sjason@lowepower.com    del x, y, z
19612391Sjason@lowepower.com    assert [i.alive() for i in cstats] == [0, 0]
19712391Sjason@lowepower.com    assert ConstructorStats.detail_reg_inst() == n_inst
19812391Sjason@lowepower.com
19912391Sjason@lowepower.com    assert [i.values() for i in cstats] == [
20012391Sjason@lowepower.com        ["1", "8", "3", "4", "5", "6", "123", "10", "47"],
20112391Sjason@lowepower.com        ["hi there", "3", "4", "6", "move", "123", "why hello!", "move", "47"]
20212391Sjason@lowepower.com    ]
20312391Sjason@lowepower.com
20412391Sjason@lowepower.com
20512391Sjason@lowepower.comdef test_init_factory_dual():
20612391Sjason@lowepower.com    """Tests init factory functions with dual main/alias factory functions"""
20712391Sjason@lowepower.com    from pybind11_tests.factory_constructors import TestFactory7
20812391Sjason@lowepower.com
20912391Sjason@lowepower.com    cstats = [TestFactory7.get_cstats(), TestFactory7.get_alias_cstats()]
21012391Sjason@lowepower.com    cstats[0].alive()  # force gc
21112391Sjason@lowepower.com    n_inst = ConstructorStats.detail_reg_inst()
21212391Sjason@lowepower.com
21312391Sjason@lowepower.com    class PythFactory7(TestFactory7):
21412391Sjason@lowepower.com        def get(self):
21512391Sjason@lowepower.com            return 100 + TestFactory7.get(self)
21612391Sjason@lowepower.com
21712391Sjason@lowepower.com    a1 = TestFactory7(1)
21812391Sjason@lowepower.com    a2 = PythFactory7(2)
21912391Sjason@lowepower.com    assert a1.get() == 1
22012391Sjason@lowepower.com    assert a2.get() == 102
22112391Sjason@lowepower.com    assert not a1.has_alias()
22212391Sjason@lowepower.com    assert a2.has_alias()
22312391Sjason@lowepower.com
22412391Sjason@lowepower.com    b1 = TestFactory7(tag.pointer, 3)
22512391Sjason@lowepower.com    b2 = PythFactory7(tag.pointer, 4)
22612391Sjason@lowepower.com    assert b1.get() == 3
22712391Sjason@lowepower.com    assert b2.get() == 104
22812391Sjason@lowepower.com    assert not b1.has_alias()
22912391Sjason@lowepower.com    assert b2.has_alias()
23012391Sjason@lowepower.com
23112391Sjason@lowepower.com    c1 = TestFactory7(tag.mixed, 5)
23212391Sjason@lowepower.com    c2 = PythFactory7(tag.mixed, 6)
23312391Sjason@lowepower.com    assert c1.get() == 5
23412391Sjason@lowepower.com    assert c2.get() == 106
23512391Sjason@lowepower.com    assert not c1.has_alias()
23612391Sjason@lowepower.com    assert c2.has_alias()
23712391Sjason@lowepower.com
23812391Sjason@lowepower.com    d1 = TestFactory7(tag.base, tag.pointer, 7)
23912391Sjason@lowepower.com    d2 = PythFactory7(tag.base, tag.pointer, 8)
24012391Sjason@lowepower.com    assert d1.get() == 7
24112391Sjason@lowepower.com    assert d2.get() == 108
24212391Sjason@lowepower.com    assert not d1.has_alias()
24312391Sjason@lowepower.com    assert d2.has_alias()
24412391Sjason@lowepower.com
24512391Sjason@lowepower.com    # Both return an alias; the second multiplies the value by 10:
24612391Sjason@lowepower.com    e1 = TestFactory7(tag.alias, tag.pointer, 9)
24712391Sjason@lowepower.com    e2 = PythFactory7(tag.alias, tag.pointer, 10)
24812391Sjason@lowepower.com    assert e1.get() == 9
24912391Sjason@lowepower.com    assert e2.get() == 200
25012391Sjason@lowepower.com    assert e1.has_alias()
25112391Sjason@lowepower.com    assert e2.has_alias()
25212391Sjason@lowepower.com
25312391Sjason@lowepower.com    f1 = TestFactory7(tag.shared_ptr, tag.base, 11)
25412391Sjason@lowepower.com    f2 = PythFactory7(tag.shared_ptr, tag.base, 12)
25512391Sjason@lowepower.com    assert f1.get() == 11
25612391Sjason@lowepower.com    assert f2.get() == 112
25712391Sjason@lowepower.com    assert not f1.has_alias()
25812391Sjason@lowepower.com    assert f2.has_alias()
25912391Sjason@lowepower.com
26012391Sjason@lowepower.com    g1 = TestFactory7(tag.shared_ptr, tag.invalid_base, 13)
26112391Sjason@lowepower.com    assert g1.get() == 13
26212391Sjason@lowepower.com    assert not g1.has_alias()
26312391Sjason@lowepower.com    with pytest.raises(TypeError) as excinfo:
26412391Sjason@lowepower.com        PythFactory7(tag.shared_ptr, tag.invalid_base, 14)
26512391Sjason@lowepower.com    assert (str(excinfo.value) ==
26612391Sjason@lowepower.com            "pybind11::init(): construction failed: returned holder-wrapped instance is not an "
26712391Sjason@lowepower.com            "alias instance")
26812391Sjason@lowepower.com
26912391Sjason@lowepower.com    assert [i.alive() for i in cstats] == [13, 7]
27012391Sjason@lowepower.com    assert ConstructorStats.detail_reg_inst() == n_inst + 13
27112391Sjason@lowepower.com
27212391Sjason@lowepower.com    del a1, a2, b1, d1, e1, e2
27312391Sjason@lowepower.com    assert [i.alive() for i in cstats] == [7, 4]
27412391Sjason@lowepower.com    assert ConstructorStats.detail_reg_inst() == n_inst + 7
27512391Sjason@lowepower.com    del b2, c1, c2, d2, f1, f2, g1
27612391Sjason@lowepower.com    assert [i.alive() for i in cstats] == [0, 0]
27712391Sjason@lowepower.com    assert ConstructorStats.detail_reg_inst() == n_inst
27812391Sjason@lowepower.com
27912391Sjason@lowepower.com    assert [i.values() for i in cstats] == [
28012391Sjason@lowepower.com        ["1", "2", "3", "4", "5", "6", "7", "8", "9", "100", "11", "12", "13", "14"],
28112391Sjason@lowepower.com        ["2", "4", "6", "8", "9", "100", "12"]
28212391Sjason@lowepower.com    ]
28312391Sjason@lowepower.com
28412391Sjason@lowepower.com
28512391Sjason@lowepower.comdef test_no_placement_new(capture):
28612391Sjason@lowepower.com    """Prior to 2.2, `py::init<...>` relied on the type supporting placement
28712391Sjason@lowepower.com    new; this tests a class without placement new support."""
28812391Sjason@lowepower.com    with capture:
28912391Sjason@lowepower.com        a = m.NoPlacementNew(123)
29012391Sjason@lowepower.com
29112391Sjason@lowepower.com    found = re.search(r'^operator new called, returning (\d+)\n$', str(capture))
29212391Sjason@lowepower.com    assert found
29312391Sjason@lowepower.com    assert a.i == 123
29412391Sjason@lowepower.com    with capture:
29512391Sjason@lowepower.com        del a
29612391Sjason@lowepower.com        pytest.gc_collect()
29712391Sjason@lowepower.com    assert capture == "operator delete called on " + found.group(1)
29812391Sjason@lowepower.com
29912391Sjason@lowepower.com    with capture:
30012391Sjason@lowepower.com        b = m.NoPlacementNew()
30112391Sjason@lowepower.com
30212391Sjason@lowepower.com    found = re.search(r'^operator new called, returning (\d+)\n$', str(capture))
30312391Sjason@lowepower.com    assert found
30412391Sjason@lowepower.com    assert b.i == 100
30512391Sjason@lowepower.com    with capture:
30612391Sjason@lowepower.com        del b
30712391Sjason@lowepower.com        pytest.gc_collect()
30812391Sjason@lowepower.com    assert capture == "operator delete called on " + found.group(1)
30912391Sjason@lowepower.com
31012391Sjason@lowepower.com
31112391Sjason@lowepower.comdef test_multiple_inheritance():
31212391Sjason@lowepower.com    class MITest(m.TestFactory1, m.TestFactory2):
31312391Sjason@lowepower.com        def __init__(self):
31412391Sjason@lowepower.com            m.TestFactory1.__init__(self, tag.unique_ptr, 33)
31512391Sjason@lowepower.com            m.TestFactory2.__init__(self, tag.move)
31612391Sjason@lowepower.com
31712391Sjason@lowepower.com    a = MITest()
31812391Sjason@lowepower.com    assert m.TestFactory1.value.fget(a) == "33"
31912391Sjason@lowepower.com    assert m.TestFactory2.value.fget(a) == "(empty2)"
32012391Sjason@lowepower.com
32112391Sjason@lowepower.com
32212391Sjason@lowepower.comdef create_and_destroy(*args):
32312391Sjason@lowepower.com    a = m.NoisyAlloc(*args)
32412391Sjason@lowepower.com    print("---")
32512391Sjason@lowepower.com    del a
32612391Sjason@lowepower.com    pytest.gc_collect()
32712391Sjason@lowepower.com
32812391Sjason@lowepower.com
32912391Sjason@lowepower.comdef strip_comments(s):
33012391Sjason@lowepower.com    return re.sub(r'\s+#.*', '', s)
33112391Sjason@lowepower.com
33212391Sjason@lowepower.com
33312391Sjason@lowepower.comdef test_reallocations(capture, msg):
33412391Sjason@lowepower.com    """When the constructor is overloaded, previous overloads can require a preallocated value.
33512391Sjason@lowepower.com    This test makes sure that such preallocated values only happen when they might be necessary,
33612391Sjason@lowepower.com    and that they are deallocated properly"""
33712391Sjason@lowepower.com
33812391Sjason@lowepower.com    pytest.gc_collect()
33912391Sjason@lowepower.com
34012391Sjason@lowepower.com    with capture:
34112391Sjason@lowepower.com        create_and_destroy(1)
34212391Sjason@lowepower.com    assert msg(capture) == """
34312391Sjason@lowepower.com        noisy new
34412391Sjason@lowepower.com        noisy placement new
34512391Sjason@lowepower.com        NoisyAlloc(int 1)
34612391Sjason@lowepower.com        ---
34712391Sjason@lowepower.com        ~NoisyAlloc()
34812391Sjason@lowepower.com        noisy delete
34912391Sjason@lowepower.com    """
35012391Sjason@lowepower.com    with capture:
35112391Sjason@lowepower.com        create_and_destroy(1.5)
35212391Sjason@lowepower.com    assert msg(capture) == strip_comments("""
35312391Sjason@lowepower.com        noisy new               # allocation required to attempt first overload
35412391Sjason@lowepower.com        noisy delete            # have to dealloc before considering factory init overload
35512391Sjason@lowepower.com        noisy new               # pointer factory calling "new", part 1: allocation
35612391Sjason@lowepower.com        NoisyAlloc(double 1.5)  # ... part two, invoking constructor
35712391Sjason@lowepower.com        ---
35812391Sjason@lowepower.com        ~NoisyAlloc()  # Destructor
35912391Sjason@lowepower.com        noisy delete   # operator delete
36012391Sjason@lowepower.com    """)
36112391Sjason@lowepower.com
36212391Sjason@lowepower.com    with capture:
36312391Sjason@lowepower.com        create_and_destroy(2, 3)
36412391Sjason@lowepower.com    assert msg(capture) == strip_comments("""
36512391Sjason@lowepower.com        noisy new          # pointer factory calling "new", allocation
36612391Sjason@lowepower.com        NoisyAlloc(int 2)  # constructor
36712391Sjason@lowepower.com        ---
36812391Sjason@lowepower.com        ~NoisyAlloc()  # Destructor
36912391Sjason@lowepower.com        noisy delete   # operator delete
37012391Sjason@lowepower.com    """)
37112391Sjason@lowepower.com
37212391Sjason@lowepower.com    with capture:
37312391Sjason@lowepower.com        create_and_destroy(2.5, 3)
37412391Sjason@lowepower.com    assert msg(capture) == strip_comments("""
37512391Sjason@lowepower.com        NoisyAlloc(double 2.5)  # construction (local func variable: operator_new not called)
37612391Sjason@lowepower.com        noisy new               # return-by-value "new" part 1: allocation
37712391Sjason@lowepower.com        ~NoisyAlloc()           # moved-away local func variable destruction
37812391Sjason@lowepower.com        ---
37912391Sjason@lowepower.com        ~NoisyAlloc()  # Destructor
38012391Sjason@lowepower.com        noisy delete   # operator delete
38112391Sjason@lowepower.com    """)
38212391Sjason@lowepower.com
38312391Sjason@lowepower.com    with capture:
38412391Sjason@lowepower.com        create_and_destroy(3.5, 4.5)
38512391Sjason@lowepower.com    assert msg(capture) == strip_comments("""
38612391Sjason@lowepower.com        noisy new               # preallocation needed before invoking placement-new overload
38712391Sjason@lowepower.com        noisy placement new     # Placement new
38812391Sjason@lowepower.com        NoisyAlloc(double 3.5)  # construction
38912391Sjason@lowepower.com        ---
39012391Sjason@lowepower.com        ~NoisyAlloc()  # Destructor
39112391Sjason@lowepower.com        noisy delete   # operator delete
39212391Sjason@lowepower.com    """)
39312391Sjason@lowepower.com
39412391Sjason@lowepower.com    with capture:
39512391Sjason@lowepower.com        create_and_destroy(4, 0.5)
39612391Sjason@lowepower.com    assert msg(capture) == strip_comments("""
39712391Sjason@lowepower.com        noisy new          # preallocation needed before invoking placement-new overload
39812391Sjason@lowepower.com        noisy delete       # deallocation of preallocated storage
39912391Sjason@lowepower.com        noisy new          # Factory pointer allocation
40012391Sjason@lowepower.com        NoisyAlloc(int 4)  # factory pointer construction
40112391Sjason@lowepower.com        ---
40212391Sjason@lowepower.com        ~NoisyAlloc()  # Destructor
40312391Sjason@lowepower.com        noisy delete   # operator delete
40412391Sjason@lowepower.com    """)
40512391Sjason@lowepower.com
40612391Sjason@lowepower.com    with capture:
40712391Sjason@lowepower.com        create_and_destroy(5, "hi")
40812391Sjason@lowepower.com    assert msg(capture) == strip_comments("""
40912391Sjason@lowepower.com        noisy new            # preallocation needed before invoking first placement new
41012391Sjason@lowepower.com        noisy delete         # delete before considering new-style constructor
41112391Sjason@lowepower.com        noisy new            # preallocation for second placement new
41212391Sjason@lowepower.com        noisy placement new  # Placement new in the second placement new overload
41312391Sjason@lowepower.com        NoisyAlloc(int 5)    # construction
41412391Sjason@lowepower.com        ---
41512391Sjason@lowepower.com        ~NoisyAlloc()  # Destructor
41612391Sjason@lowepower.com        noisy delete   # operator delete
41712391Sjason@lowepower.com    """)
41812391Sjason@lowepower.com
41912391Sjason@lowepower.com
42012391Sjason@lowepower.com@pytest.unsupported_on_py2
42112391Sjason@lowepower.comdef test_invalid_self():
42212391Sjason@lowepower.com    """Tests invocation of the pybind-registered base class with an invalid `self` argument.  You
42312391Sjason@lowepower.com    can only actually do this on Python 3: Python 2 raises an exception itself if you try."""
42412391Sjason@lowepower.com    class NotPybindDerived(object):
42512391Sjason@lowepower.com        pass
42612391Sjason@lowepower.com
42712391Sjason@lowepower.com    # Attempts to initialize with an invalid type passed as `self`:
42812391Sjason@lowepower.com    class BrokenTF1(m.TestFactory1):
42912391Sjason@lowepower.com        def __init__(self, bad):
43012391Sjason@lowepower.com            if bad == 1:
43112391Sjason@lowepower.com                a = m.TestFactory2(tag.pointer, 1)
43212391Sjason@lowepower.com                m.TestFactory1.__init__(a, tag.pointer)
43312391Sjason@lowepower.com            elif bad == 2:
43412391Sjason@lowepower.com                a = NotPybindDerived()
43512391Sjason@lowepower.com                m.TestFactory1.__init__(a, tag.pointer)
43612391Sjason@lowepower.com
43712391Sjason@lowepower.com    # Same as above, but for a class with an alias:
43812391Sjason@lowepower.com    class BrokenTF6(m.TestFactory6):
43912391Sjason@lowepower.com        def __init__(self, bad):
44012391Sjason@lowepower.com            if bad == 1:
44112391Sjason@lowepower.com                a = m.TestFactory2(tag.pointer, 1)
44212391Sjason@lowepower.com                m.TestFactory6.__init__(a, tag.base, 1)
44312391Sjason@lowepower.com            elif bad == 2:
44412391Sjason@lowepower.com                a = m.TestFactory2(tag.pointer, 1)
44512391Sjason@lowepower.com                m.TestFactory6.__init__(a, tag.alias, 1)
44612391Sjason@lowepower.com            elif bad == 3:
44712391Sjason@lowepower.com                m.TestFactory6.__init__(NotPybindDerived.__new__(NotPybindDerived), tag.base, 1)
44812391Sjason@lowepower.com            elif bad == 4:
44912391Sjason@lowepower.com                m.TestFactory6.__init__(NotPybindDerived.__new__(NotPybindDerived), tag.alias, 1)
45012391Sjason@lowepower.com
45112391Sjason@lowepower.com    for arg in (1, 2):
45212391Sjason@lowepower.com        with pytest.raises(TypeError) as excinfo:
45312391Sjason@lowepower.com            BrokenTF1(arg)
45412391Sjason@lowepower.com        assert str(excinfo.value) == "__init__(self, ...) called with invalid `self` argument"
45512391Sjason@lowepower.com
45612391Sjason@lowepower.com    for arg in (1, 2, 3, 4):
45712391Sjason@lowepower.com        with pytest.raises(TypeError) as excinfo:
45812391Sjason@lowepower.com            BrokenTF6(arg)
45912391Sjason@lowepower.com        assert str(excinfo.value) == "__init__(self, ...) called with invalid `self` argument"
460