blob: 0b1ca45b5a20835462b9152549731b663aa63aef [file] [log] [blame]
Henry Schreinerd8c7ee02020-07-20 13:35:21 -04001# -*- coding: utf-8 -*-
Dean Moldovanab90ec62016-12-07 02:36:44 +01002import pytest
andriish38370a82020-09-12 04:06:52 +02003
4m = pytest.importorskip("pybind11_tests.smart_ptr")
5from pybind11_tests import ConstructorStats # noqa: E402
Dean Moldovana0c1ccf2016-08-12 13:50:00 +02006
7
8def test_smart_ptr(capture):
9 # Object1
Jason Rhinelander391c7542017-07-25 16:47:36 -040010 for i, o in enumerate([m.make_object_1(), m.make_object_2(), m.MyObject1(3)], start=1):
Dean Moldovana0c1ccf2016-08-12 13:50:00 +020011 assert o.getRefCount() == 1
12 with capture:
Jason Rhinelander391c7542017-07-25 16:47:36 -040013 m.print_object_1(o)
14 m.print_object_2(o)
15 m.print_object_3(o)
16 m.print_object_4(o)
Dean Moldovana0c1ccf2016-08-12 13:50:00 +020017 assert capture == "MyObject1[{i}]\n".format(i=i) * 4
18
Jason Rhinelander391c7542017-07-25 16:47:36 -040019 for i, o in enumerate([m.make_myobject1_1(), m.make_myobject1_2(), m.MyObject1(6), 7],
20 start=4):
Dean Moldovana0c1ccf2016-08-12 13:50:00 +020021 print(o)
22 with capture:
23 if not isinstance(o, int):
Jason Rhinelander391c7542017-07-25 16:47:36 -040024 m.print_object_1(o)
25 m.print_object_2(o)
26 m.print_object_3(o)
27 m.print_object_4(o)
28 m.print_myobject1_1(o)
29 m.print_myobject1_2(o)
30 m.print_myobject1_3(o)
31 m.print_myobject1_4(o)
Dean Moldovana0c1ccf2016-08-12 13:50:00 +020032 assert capture == "MyObject1[{i}]\n".format(i=i) * (4 if isinstance(o, int) else 8)
33
Jason Rhinelander391c7542017-07-25 16:47:36 -040034 cstats = ConstructorStats.get(m.MyObject1)
Dean Moldovana0c1ccf2016-08-12 13:50:00 +020035 assert cstats.alive() == 0
36 expected_values = ['MyObject1[{}]'.format(i) for i in range(1, 7)] + ['MyObject1[7]'] * 4
37 assert cstats.values() == expected_values
38 assert cstats.default_constructions == 0
39 assert cstats.copy_constructions == 0
40 # assert cstats.move_constructions >= 0 # Doesn't invoke any
41 assert cstats.copy_assignments == 0
42 assert cstats.move_assignments == 0
43
44 # Object2
Jason Rhinelander391c7542017-07-25 16:47:36 -040045 for i, o in zip([8, 6, 7], [m.MyObject2(8), m.make_myobject2_1(), m.make_myobject2_2()]):
Dean Moldovana0c1ccf2016-08-12 13:50:00 +020046 print(o)
47 with capture:
Jason Rhinelander391c7542017-07-25 16:47:36 -040048 m.print_myobject2_1(o)
49 m.print_myobject2_2(o)
50 m.print_myobject2_3(o)
51 m.print_myobject2_4(o)
Dean Moldovana0c1ccf2016-08-12 13:50:00 +020052 assert capture == "MyObject2[{i}]\n".format(i=i) * 4
53
Jason Rhinelander391c7542017-07-25 16:47:36 -040054 cstats = ConstructorStats.get(m.MyObject2)
Dean Moldovana0c1ccf2016-08-12 13:50:00 +020055 assert cstats.alive() == 1
56 o = None
57 assert cstats.alive() == 0
58 assert cstats.values() == ['MyObject2[8]', 'MyObject2[6]', 'MyObject2[7]']
59 assert cstats.default_constructions == 0
60 assert cstats.copy_constructions == 0
61 # assert cstats.move_constructions >= 0 # Doesn't invoke any
62 assert cstats.copy_assignments == 0
63 assert cstats.move_assignments == 0
64
65 # Object3
Jason Rhinelander391c7542017-07-25 16:47:36 -040066 for i, o in zip([9, 8, 9], [m.MyObject3(9), m.make_myobject3_1(), m.make_myobject3_2()]):
Dean Moldovana0c1ccf2016-08-12 13:50:00 +020067 print(o)
68 with capture:
Jason Rhinelander391c7542017-07-25 16:47:36 -040069 m.print_myobject3_1(o)
70 m.print_myobject3_2(o)
71 m.print_myobject3_3(o)
72 m.print_myobject3_4(o)
Dean Moldovana0c1ccf2016-08-12 13:50:00 +020073 assert capture == "MyObject3[{i}]\n".format(i=i) * 4
74
Jason Rhinelander391c7542017-07-25 16:47:36 -040075 cstats = ConstructorStats.get(m.MyObject3)
Dean Moldovana0c1ccf2016-08-12 13:50:00 +020076 assert cstats.alive() == 1
77 o = None
78 assert cstats.alive() == 0
79 assert cstats.values() == ['MyObject3[9]', 'MyObject3[8]', 'MyObject3[9]']
80 assert cstats.default_constructions == 0
81 assert cstats.copy_constructions == 0
82 # assert cstats.move_constructions >= 0 # Doesn't invoke any
83 assert cstats.copy_assignments == 0
84 assert cstats.move_assignments == 0
85
Jason Rhinelander391c7542017-07-25 16:47:36 -040086 # Object
87 cstats = ConstructorStats.get(m.Object)
Dean Moldovana0c1ccf2016-08-12 13:50:00 +020088 assert cstats.alive() == 0
89 assert cstats.values() == []
90 assert cstats.default_constructions == 10
91 assert cstats.copy_constructions == 0
92 # assert cstats.move_constructions >= 0 # Doesn't invoke any
93 assert cstats.copy_assignments == 0
94 assert cstats.move_assignments == 0
95
Jason Rhinelander391c7542017-07-25 16:47:36 -040096 # ref<>
97 cstats = m.cstats_ref()
Dean Moldovana0c1ccf2016-08-12 13:50:00 +020098 assert cstats.alive() == 0
99 assert cstats.values() == ['from pointer'] * 10
100 assert cstats.default_constructions == 30
101 assert cstats.copy_constructions == 12
102 # assert cstats.move_constructions >= 0 # Doesn't invoke any
103 assert cstats.copy_assignments == 30
104 assert cstats.move_assignments == 0
Jason Rhinelandera6495af2016-09-04 18:23:55 -0400105
Dean Moldovanbad17402016-11-20 21:21:54 +0100106
Wenzel Jakob20291712016-12-15 23:44:23 +0100107def test_smart_ptr_refcounting():
Jason Rhinelander391c7542017-07-25 16:47:36 -0400108 assert m.test_object1_refcounting()
Wenzel Jakob20291712016-12-15 23:44:23 +0100109
110
Dean Moldovanbad17402016-11-20 21:21:54 +0100111def test_unique_nodelete():
Jason Rhinelander391c7542017-07-25 16:47:36 -0400112 o = m.MyObject4(23)
Jason Rhinelandera6495af2016-09-04 18:23:55 -0400113 assert o.value == 23
Jason Rhinelander391c7542017-07-25 16:47:36 -0400114 cstats = ConstructorStats.get(m.MyObject4)
Jason Rhinelandera6495af2016-09-04 18:23:55 -0400115 assert cstats.alive() == 1
116 del o
Dean Moldovanbad17402016-11-20 21:21:54 +0100117 assert cstats.alive() == 1 # Leak, but that's intentional
Dean Moldovanab90ec62016-12-07 02:36:44 +0100118
119
Trevor Laughlin63c2a972018-11-11 13:36:55 -0500120def test_unique_nodelete4a():
121 o = m.MyObject4a(23)
122 assert o.value == 23
123 cstats = ConstructorStats.get(m.MyObject4a)
124 assert cstats.alive() == 1
125 del o
126 assert cstats.alive() == 1 # Leak, but that's intentional
127
128
129def test_unique_deleter():
130 o = m.MyObject4b(23)
131 assert o.value == 23
132 cstats4a = ConstructorStats.get(m.MyObject4a)
luzpaz21bf16f2019-06-10 12:56:38 -0700133 assert cstats4a.alive() == 2 # Two because of previous test
Trevor Laughlin63c2a972018-11-11 13:36:55 -0500134 cstats4b = ConstructorStats.get(m.MyObject4b)
135 assert cstats4b.alive() == 1
136 del o
137 assert cstats4a.alive() == 1 # Should now only be one leftover from previous test
138 assert cstats4b.alive() == 0 # Should be deleted
139
140
Jason Rhinelandere45c2112017-02-22 21:36:09 -0500141def test_large_holder():
Jason Rhinelander391c7542017-07-25 16:47:36 -0400142 o = m.MyObject5(5)
Jason Rhinelandere45c2112017-02-22 21:36:09 -0500143 assert o.value == 5
Jason Rhinelander391c7542017-07-25 16:47:36 -0400144 cstats = ConstructorStats.get(m.MyObject5)
Jason Rhinelandere45c2112017-02-22 21:36:09 -0500145 assert cstats.alive() == 1
146 del o
147 assert cstats.alive() == 0
148
149
Dean Moldovanab90ec62016-12-07 02:36:44 +0100150def test_shared_ptr_and_references():
Jason Rhinelander391c7542017-07-25 16:47:36 -0400151 s = m.SharedPtrRef()
152 stats = ConstructorStats.get(m.A)
Dean Moldovanab90ec62016-12-07 02:36:44 +0100153 assert stats.alive() == 2
154
155 ref = s.ref # init_holder_helper(holder_ptr=false, owned=false)
156 assert stats.alive() == 2
157 assert s.set_ref(ref)
158 with pytest.raises(RuntimeError) as excinfo:
159 assert s.set_holder(ref)
160 assert "Unable to cast from non-held to held instance" in str(excinfo.value)
161
162 copy = s.copy # init_holder_helper(holder_ptr=false, owned=true)
163 assert stats.alive() == 3
164 assert s.set_ref(copy)
165 assert s.set_holder(copy)
166
167 holder_ref = s.holder_ref # init_holder_helper(holder_ptr=true, owned=false)
168 assert stats.alive() == 3
169 assert s.set_ref(holder_ref)
170 assert s.set_holder(holder_ref)
171
172 holder_copy = s.holder_copy # init_holder_helper(holder_ptr=true, owned=true)
173 assert stats.alive() == 3
174 assert s.set_ref(holder_copy)
175 assert s.set_holder(holder_copy)
176
177 del ref, copy, holder_ref, holder_copy, s
178 assert stats.alive() == 0
179
180
181def test_shared_ptr_from_this_and_references():
Jason Rhinelander391c7542017-07-25 16:47:36 -0400182 s = m.SharedFromThisRef()
183 stats = ConstructorStats.get(m.B)
Dean Moldovanab90ec62016-12-07 02:36:44 +0100184 assert stats.alive() == 2
185
186 ref = s.ref # init_holder_helper(holder_ptr=false, owned=false, bad_wp=false)
187 assert stats.alive() == 2
188 assert s.set_ref(ref)
189 assert s.set_holder(ref) # std::enable_shared_from_this can create a holder from a reference
190
191 bad_wp = s.bad_wp # init_holder_helper(holder_ptr=false, owned=false, bad_wp=true)
192 assert stats.alive() == 2
193 assert s.set_ref(bad_wp)
194 with pytest.raises(RuntimeError) as excinfo:
195 assert s.set_holder(bad_wp)
196 assert "Unable to cast from non-held to held instance" in str(excinfo.value)
197
198 copy = s.copy # init_holder_helper(holder_ptr=false, owned=true, bad_wp=false)
199 assert stats.alive() == 3
200 assert s.set_ref(copy)
201 assert s.set_holder(copy)
202
203 holder_ref = s.holder_ref # init_holder_helper(holder_ptr=true, owned=false, bad_wp=false)
204 assert stats.alive() == 3
205 assert s.set_ref(holder_ref)
206 assert s.set_holder(holder_ref)
207
208 holder_copy = s.holder_copy # init_holder_helper(holder_ptr=true, owned=true, bad_wp=false)
209 assert stats.alive() == 3
210 assert s.set_ref(holder_copy)
211 assert s.set_holder(holder_copy)
212
213 del ref, bad_wp, copy, holder_ref, holder_copy, s
214 assert stats.alive() == 0
Dean Moldovanec009a72017-01-31 17:05:44 +0100215
Jason Rhinelander391c7542017-07-25 16:47:36 -0400216 z = m.SharedFromThisVirt.get()
217 y = m.SharedFromThisVirt.get()
Jason Rhinelanderb8ac4382017-05-19 13:34:55 -0400218 assert y is z
219
Dean Moldovanec009a72017-01-31 17:05:44 +0100220
221def test_move_only_holder():
Jason Rhinelander391c7542017-07-25 16:47:36 -0400222 a = m.TypeWithMoveOnlyHolder.make()
Yannick Jadould54d6d82020-07-01 01:53:09 +0200223 b = m.TypeWithMoveOnlyHolder.make_as_object()
Jason Rhinelander391c7542017-07-25 16:47:36 -0400224 stats = ConstructorStats.get(m.TypeWithMoveOnlyHolder)
Yannick Jadould54d6d82020-07-01 01:53:09 +0200225 assert stats.alive() == 2
226 del b
Dean Moldovanec009a72017-01-31 17:05:44 +0100227 assert stats.alive() == 1
228 del a
229 assert stats.alive() == 0
Dean Moldovancd3d1fc2017-03-20 21:53:24 +0100230
231
Khachajantc Michaele3cb2a62018-06-20 18:33:50 +0300232def test_holder_with_addressof_operator():
233 # this test must not throw exception from c++
234 a = m.TypeForHolderWithAddressOf.make()
235 a.print_object_1()
236 a.print_object_2()
237 a.print_object_3()
238 a.print_object_4()
239
240 stats = ConstructorStats.get(m.TypeForHolderWithAddressOf)
241 assert stats.alive() == 1
242
243 np = m.TypeForHolderWithAddressOf.make()
244 assert stats.alive() == 2
245 del a
246 assert stats.alive() == 1
247 del np
248 assert stats.alive() == 0
249
250 b = m.TypeForHolderWithAddressOf.make()
251 c = b
252 assert b.get() is c.get()
253 assert stats.alive() == 1
254
255 del b
256 assert stats.alive() == 1
257
258 del c
259 assert stats.alive() == 0
260
261
262def test_move_only_holder_with_addressof_operator():
263 a = m.TypeForMoveOnlyHolderWithAddressOf.make()
264 a.print_object()
265
266 stats = ConstructorStats.get(m.TypeForMoveOnlyHolderWithAddressOf)
267 assert stats.alive() == 1
268
269 a.value = 42
270 assert a.value == 42
271
272 del a
273 assert stats.alive() == 0
274
275
Dean Moldovancd3d1fc2017-03-20 21:53:24 +0100276def test_smart_ptr_from_default():
Jason Rhinelander391c7542017-07-25 16:47:36 -0400277 instance = m.HeldByDefaultHolder()
Dean Moldovancd3d1fc2017-03-20 21:53:24 +0100278 with pytest.raises(RuntimeError) as excinfo:
Jason Rhinelander391c7542017-07-25 16:47:36 -0400279 m.HeldByDefaultHolder.load_shared_ptr(instance)
Wenzel Jakob9fd47122019-07-06 16:57:17 +0200280 assert "Unable to load a custom holder type from a " \
281 "default-holder instance" in str(excinfo.value)
Dean Moldovanbdfb50f2017-06-07 16:52:50 +0200282
283
284def test_shared_ptr_gc():
285 """#187: issue involving std::shared_ptr<> return value policy & garbage collection"""
Jason Rhinelander391c7542017-07-25 16:47:36 -0400286 el = m.ElementList()
Dean Moldovanbdfb50f2017-06-07 16:52:50 +0200287 for i in range(10):
Jason Rhinelander391c7542017-07-25 16:47:36 -0400288 el.add(m.ElementA(i))
Dean Moldovanbdfb50f2017-06-07 16:52:50 +0200289 pytest.gc_collect()
290 for i, v in enumerate(el.get()):
291 assert i == v.value()