test_smart_ptr.py revision 12037:d28054ac6ec9
1import pytest
2from pybind11_tests import ConstructorStats
3
4
5def test_smart_ptr(capture):
6    # Object1
7    from pybind11_tests import (MyObject1, make_object_1, make_object_2,
8                                print_object_1, print_object_2, print_object_3, print_object_4)
9
10    for i, o in enumerate([make_object_1(), make_object_2(), MyObject1(3)], start=1):
11        assert o.getRefCount() == 1
12        with capture:
13            print_object_1(o)
14            print_object_2(o)
15            print_object_3(o)
16            print_object_4(o)
17        assert capture == "MyObject1[{i}]\n".format(i=i) * 4
18
19    from pybind11_tests import (make_myobject1_1, make_myobject1_2,
20                                print_myobject1_1, print_myobject1_2,
21                                print_myobject1_3, print_myobject1_4)
22
23    for i, o in enumerate([make_myobject1_1(), make_myobject1_2(), MyObject1(6), 7], start=4):
24        print(o)
25        with capture:
26            if not isinstance(o, int):
27                print_object_1(o)
28                print_object_2(o)
29                print_object_3(o)
30                print_object_4(o)
31            print_myobject1_1(o)
32            print_myobject1_2(o)
33            print_myobject1_3(o)
34            print_myobject1_4(o)
35        assert capture == "MyObject1[{i}]\n".format(i=i) * (4 if isinstance(o, int) else 8)
36
37    cstats = ConstructorStats.get(MyObject1)
38    assert cstats.alive() == 0
39    expected_values = ['MyObject1[{}]'.format(i) for i in range(1, 7)] + ['MyObject1[7]'] * 4
40    assert cstats.values() == expected_values
41    assert cstats.default_constructions == 0
42    assert cstats.copy_constructions == 0
43    # assert cstats.move_constructions >= 0 # Doesn't invoke any
44    assert cstats.copy_assignments == 0
45    assert cstats.move_assignments == 0
46
47    # Object2
48    from pybind11_tests import (MyObject2, make_myobject2_1, make_myobject2_2,
49                                make_myobject3_1, make_myobject3_2,
50                                print_myobject2_1, print_myobject2_2,
51                                print_myobject2_3, print_myobject2_4)
52
53    for i, o in zip([8, 6, 7], [MyObject2(8), make_myobject2_1(), make_myobject2_2()]):
54        print(o)
55        with capture:
56            print_myobject2_1(o)
57            print_myobject2_2(o)
58            print_myobject2_3(o)
59            print_myobject2_4(o)
60        assert capture == "MyObject2[{i}]\n".format(i=i) * 4
61
62    cstats = ConstructorStats.get(MyObject2)
63    assert cstats.alive() == 1
64    o = None
65    assert cstats.alive() == 0
66    assert cstats.values() == ['MyObject2[8]', 'MyObject2[6]', 'MyObject2[7]']
67    assert cstats.default_constructions == 0
68    assert cstats.copy_constructions == 0
69    # assert cstats.move_constructions >= 0 # Doesn't invoke any
70    assert cstats.copy_assignments == 0
71    assert cstats.move_assignments == 0
72
73    # Object3
74    from pybind11_tests import (MyObject3, print_myobject3_1, print_myobject3_2,
75                                print_myobject3_3, print_myobject3_4)
76
77    for i, o in zip([9, 8, 9], [MyObject3(9), make_myobject3_1(), make_myobject3_2()]):
78        print(o)
79        with capture:
80            print_myobject3_1(o)
81            print_myobject3_2(o)
82            print_myobject3_3(o)
83            print_myobject3_4(o)
84        assert capture == "MyObject3[{i}]\n".format(i=i) * 4
85
86    cstats = ConstructorStats.get(MyObject3)
87    assert cstats.alive() == 1
88    o = None
89    assert cstats.alive() == 0
90    assert cstats.values() == ['MyObject3[9]', 'MyObject3[8]', 'MyObject3[9]']
91    assert cstats.default_constructions == 0
92    assert cstats.copy_constructions == 0
93    # assert cstats.move_constructions >= 0 # Doesn't invoke any
94    assert cstats.copy_assignments == 0
95    assert cstats.move_assignments == 0
96
97    # Object and ref
98    from pybind11_tests import Object, cstats_ref
99
100    cstats = ConstructorStats.get(Object)
101    assert cstats.alive() == 0
102    assert cstats.values() == []
103    assert cstats.default_constructions == 10
104    assert cstats.copy_constructions == 0
105    # assert cstats.move_constructions >= 0 # Doesn't invoke any
106    assert cstats.copy_assignments == 0
107    assert cstats.move_assignments == 0
108
109    cstats = cstats_ref()
110    assert cstats.alive() == 0
111    assert cstats.values() == ['from pointer'] * 10
112    assert cstats.default_constructions == 30
113    assert cstats.copy_constructions == 12
114    # assert cstats.move_constructions >= 0 # Doesn't invoke any
115    assert cstats.copy_assignments == 30
116    assert cstats.move_assignments == 0
117
118
119def test_smart_ptr_refcounting():
120    from pybind11_tests import test_object1_refcounting
121    assert test_object1_refcounting()
122
123
124def test_unique_nodelete():
125    from pybind11_tests import MyObject4
126    o = MyObject4(23)
127    assert o.value == 23
128    cstats = ConstructorStats.get(MyObject4)
129    assert cstats.alive() == 1
130    del o
131    cstats = ConstructorStats.get(MyObject4)
132    assert cstats.alive() == 1  # Leak, but that's intentional
133
134
135def test_shared_ptr_and_references():
136    from pybind11_tests.smart_ptr import SharedPtrRef, A
137
138    s = SharedPtrRef()
139    stats = ConstructorStats.get(A)
140    assert stats.alive() == 2
141
142    ref = s.ref  # init_holder_helper(holder_ptr=false, owned=false)
143    assert stats.alive() == 2
144    assert s.set_ref(ref)
145    with pytest.raises(RuntimeError) as excinfo:
146        assert s.set_holder(ref)
147    assert "Unable to cast from non-held to held instance" in str(excinfo.value)
148
149    copy = s.copy  # init_holder_helper(holder_ptr=false, owned=true)
150    assert stats.alive() == 3
151    assert s.set_ref(copy)
152    assert s.set_holder(copy)
153
154    holder_ref = s.holder_ref  # init_holder_helper(holder_ptr=true, owned=false)
155    assert stats.alive() == 3
156    assert s.set_ref(holder_ref)
157    assert s.set_holder(holder_ref)
158
159    holder_copy = s.holder_copy  # init_holder_helper(holder_ptr=true, owned=true)
160    assert stats.alive() == 3
161    assert s.set_ref(holder_copy)
162    assert s.set_holder(holder_copy)
163
164    del ref, copy, holder_ref, holder_copy, s
165    assert stats.alive() == 0
166
167
168def test_shared_ptr_from_this_and_references():
169    from pybind11_tests.smart_ptr import SharedFromThisRef, B
170
171    s = SharedFromThisRef()
172    stats = ConstructorStats.get(B)
173    assert stats.alive() == 2
174
175    ref = s.ref  # init_holder_helper(holder_ptr=false, owned=false, bad_wp=false)
176    assert stats.alive() == 2
177    assert s.set_ref(ref)
178    assert s.set_holder(ref)  # std::enable_shared_from_this can create a holder from a reference
179
180    bad_wp = s.bad_wp  # init_holder_helper(holder_ptr=false, owned=false, bad_wp=true)
181    assert stats.alive() == 2
182    assert s.set_ref(bad_wp)
183    with pytest.raises(RuntimeError) as excinfo:
184        assert s.set_holder(bad_wp)
185    assert "Unable to cast from non-held to held instance" in str(excinfo.value)
186
187    copy = s.copy  # init_holder_helper(holder_ptr=false, owned=true, bad_wp=false)
188    assert stats.alive() == 3
189    assert s.set_ref(copy)
190    assert s.set_holder(copy)
191
192    holder_ref = s.holder_ref  # init_holder_helper(holder_ptr=true, owned=false, bad_wp=false)
193    assert stats.alive() == 3
194    assert s.set_ref(holder_ref)
195    assert s.set_holder(holder_ref)
196
197    holder_copy = s.holder_copy  # init_holder_helper(holder_ptr=true, owned=true, bad_wp=false)
198    assert stats.alive() == 3
199    assert s.set_ref(holder_copy)
200    assert s.set_holder(holder_copy)
201
202    del ref, bad_wp, copy, holder_ref, holder_copy, s
203    assert stats.alive() == 0
204
205
206def test_move_only_holder():
207    from pybind11_tests.smart_ptr import TypeWithMoveOnlyHolder
208
209    a = TypeWithMoveOnlyHolder.make()
210    stats = ConstructorStats.get(TypeWithMoveOnlyHolder)
211    assert stats.alive() == 1
212    del a
213    assert stats.alive() == 0
214
215
216def test_smart_ptr_from_default():
217    from pybind11_tests.smart_ptr import HeldByDefaultHolder
218
219    instance = HeldByDefaultHolder()
220    with pytest.raises(RuntimeError) as excinfo:
221        HeldByDefaultHolder.load_shared_ptr(instance)
222    assert "Unable to load a custom holder type from a default-holder instance" in str(excinfo)
223