test_smart_ptr.py revision 11986
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_unique_nodelete():
120    from pybind11_tests import MyObject4
121    o = MyObject4(23)
122    assert o.value == 23
123    cstats = ConstructorStats.get(MyObject4)
124    assert cstats.alive() == 1
125    del o
126    cstats = ConstructorStats.get(MyObject4)
127    assert cstats.alive() == 1  # Leak, but that's intentional
128
129
130def test_shared_ptr_and_references():
131    from pybind11_tests.smart_ptr import SharedPtrRef, A
132
133    s = SharedPtrRef()
134    stats = ConstructorStats.get(A)
135    assert stats.alive() == 2
136
137    ref = s.ref  # init_holder_helper(holder_ptr=false, owned=false)
138    assert stats.alive() == 2
139    assert s.set_ref(ref)
140    with pytest.raises(RuntimeError) as excinfo:
141        assert s.set_holder(ref)
142    assert "Unable to cast from non-held to held instance" in str(excinfo.value)
143
144    copy = s.copy  # init_holder_helper(holder_ptr=false, owned=true)
145    assert stats.alive() == 3
146    assert s.set_ref(copy)
147    assert s.set_holder(copy)
148
149    holder_ref = s.holder_ref  # init_holder_helper(holder_ptr=true, owned=false)
150    assert stats.alive() == 3
151    assert s.set_ref(holder_ref)
152    assert s.set_holder(holder_ref)
153
154    holder_copy = s.holder_copy  # init_holder_helper(holder_ptr=true, owned=true)
155    assert stats.alive() == 3
156    assert s.set_ref(holder_copy)
157    assert s.set_holder(holder_copy)
158
159    del ref, copy, holder_ref, holder_copy, s
160    assert stats.alive() == 0
161
162
163def test_shared_ptr_from_this_and_references():
164    from pybind11_tests.smart_ptr import SharedFromThisRef, B
165
166    s = SharedFromThisRef()
167    stats = ConstructorStats.get(B)
168    assert stats.alive() == 2
169
170    ref = s.ref  # init_holder_helper(holder_ptr=false, owned=false, bad_wp=false)
171    assert stats.alive() == 2
172    assert s.set_ref(ref)
173    assert s.set_holder(ref)  # std::enable_shared_from_this can create a holder from a reference
174
175    bad_wp = s.bad_wp  # init_holder_helper(holder_ptr=false, owned=false, bad_wp=true)
176    assert stats.alive() == 2
177    assert s.set_ref(bad_wp)
178    with pytest.raises(RuntimeError) as excinfo:
179        assert s.set_holder(bad_wp)
180    assert "Unable to cast from non-held to held instance" in str(excinfo.value)
181
182    copy = s.copy  # init_holder_helper(holder_ptr=false, owned=true, bad_wp=false)
183    assert stats.alive() == 3
184    assert s.set_ref(copy)
185    assert s.set_holder(copy)
186
187    holder_ref = s.holder_ref  # init_holder_helper(holder_ptr=true, owned=false, bad_wp=false)
188    assert stats.alive() == 3
189    assert s.set_ref(holder_ref)
190    assert s.set_holder(holder_ref)
191
192    holder_copy = s.holder_copy  # init_holder_helper(holder_ptr=true, owned=true, bad_wp=false)
193    assert stats.alive() == 3
194    assert s.set_ref(holder_copy)
195    assert s.set_holder(holder_copy)
196
197    del ref, bad_wp, copy, holder_ref, holder_copy, s
198    assert stats.alive() == 0
199