1# -*- coding: utf-8 -*- 2import pytest 3 4m = pytest.importorskip("pybind11_tests.smart_ptr") 5from pybind11_tests import ConstructorStats # noqa: E402 6 7 8def test_smart_ptr(capture): 9 # Object1 10 for i, o in enumerate( 11 [m.make_object_1(), m.make_object_2(), m.MyObject1(3)], start=1 12 ): 13 assert o.getRefCount() == 1 14 with capture: 15 m.print_object_1(o) 16 m.print_object_2(o) 17 m.print_object_3(o) 18 m.print_object_4(o) 19 assert capture == "MyObject1[{i}]\n".format(i=i) * 4 20 21 for i, o in enumerate( 22 [m.make_myobject1_1(), m.make_myobject1_2(), m.MyObject1(6), 7], start=4 23 ): 24 print(o) 25 with capture: 26 if not isinstance(o, int): 27 m.print_object_1(o) 28 m.print_object_2(o) 29 m.print_object_3(o) 30 m.print_object_4(o) 31 m.print_myobject1_1(o) 32 m.print_myobject1_2(o) 33 m.print_myobject1_3(o) 34 m.print_myobject1_4(o) 35 36 times = 4 if isinstance(o, int) else 8 37 assert capture == "MyObject1[{i}]\n".format(i=i) * times 38 39 cstats = ConstructorStats.get(m.MyObject1) 40 assert cstats.alive() == 0 41 expected_values = ["MyObject1[{}]".format(i) for i in range(1, 7)] + [ 42 "MyObject1[7]" 43 ] * 4 44 assert cstats.values() == expected_values 45 assert cstats.default_constructions == 0 46 assert cstats.copy_constructions == 0 47 # assert cstats.move_constructions >= 0 # Doesn't invoke any 48 assert cstats.copy_assignments == 0 49 assert cstats.move_assignments == 0 50 51 # Object2 52 for i, o in zip( 53 [8, 6, 7], [m.MyObject2(8), m.make_myobject2_1(), m.make_myobject2_2()] 54 ): 55 print(o) 56 with capture: 57 m.print_myobject2_1(o) 58 m.print_myobject2_2(o) 59 m.print_myobject2_3(o) 60 m.print_myobject2_4(o) 61 assert capture == "MyObject2[{i}]\n".format(i=i) * 4 62 63 cstats = ConstructorStats.get(m.MyObject2) 64 assert cstats.alive() == 1 65 o = None 66 assert cstats.alive() == 0 67 assert cstats.values() == ["MyObject2[8]", "MyObject2[6]", "MyObject2[7]"] 68 assert cstats.default_constructions == 0 69 assert cstats.copy_constructions == 0 70 # assert cstats.move_constructions >= 0 # Doesn't invoke any 71 assert cstats.copy_assignments == 0 72 assert cstats.move_assignments == 0 73 74 # Object3 75 for i, o in zip( 76 [9, 8, 9], [m.MyObject3(9), m.make_myobject3_1(), m.make_myobject3_2()] 77 ): 78 print(o) 79 with capture: 80 m.print_myobject3_1(o) 81 m.print_myobject3_2(o) 82 m.print_myobject3_3(o) 83 m.print_myobject3_4(o) 84 assert capture == "MyObject3[{i}]\n".format(i=i) * 4 85 86 cstats = ConstructorStats.get(m.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 98 cstats = ConstructorStats.get(m.Object) 99 assert cstats.alive() == 0 100 assert cstats.values() == [] 101 assert cstats.default_constructions == 10 102 assert cstats.copy_constructions == 0 103 # assert cstats.move_constructions >= 0 # Doesn't invoke any 104 assert cstats.copy_assignments == 0 105 assert cstats.move_assignments == 0 106 107 # ref<> 108 cstats = m.cstats_ref() 109 assert cstats.alive() == 0 110 assert cstats.values() == ["from pointer"] * 10 111 assert cstats.default_constructions == 30 112 assert cstats.copy_constructions == 12 113 # assert cstats.move_constructions >= 0 # Doesn't invoke any 114 assert cstats.copy_assignments == 30 115 assert cstats.move_assignments == 0 116 117 118def test_smart_ptr_refcounting(): 119 assert m.test_object1_refcounting() 120 121 122def test_unique_nodelete(): 123 o = m.MyObject4(23) 124 assert o.value == 23 125 cstats = ConstructorStats.get(m.MyObject4) 126 assert cstats.alive() == 1 127 del o 128 assert cstats.alive() == 1 129 m.MyObject4.cleanup_all_instances() 130 assert cstats.alive() == 0 131 132 133def test_unique_nodelete4a(): 134 o = m.MyObject4a(23) 135 assert o.value == 23 136 cstats = ConstructorStats.get(m.MyObject4a) 137 assert cstats.alive() == 1 138 del o 139 assert cstats.alive() == 1 140 m.MyObject4a.cleanup_all_instances() 141 assert cstats.alive() == 0 142 143 144def test_unique_deleter(): 145 m.MyObject4a(0) 146 o = m.MyObject4b(23) 147 assert o.value == 23 148 cstats4a = ConstructorStats.get(m.MyObject4a) 149 assert cstats4a.alive() == 2 150 cstats4b = ConstructorStats.get(m.MyObject4b) 151 assert cstats4b.alive() == 1 152 del o 153 assert cstats4a.alive() == 1 # Should now only be one leftover 154 assert cstats4b.alive() == 0 # Should be deleted 155 m.MyObject4a.cleanup_all_instances() 156 assert cstats4a.alive() == 0 157 assert cstats4b.alive() == 0 158 159 160def test_large_holder(): 161 o = m.MyObject5(5) 162 assert o.value == 5 163 cstats = ConstructorStats.get(m.MyObject5) 164 assert cstats.alive() == 1 165 del o 166 assert cstats.alive() == 0 167 168 169def test_shared_ptr_and_references(): 170 s = m.SharedPtrRef() 171 stats = ConstructorStats.get(m.A) 172 assert stats.alive() == 2 173 174 ref = s.ref # init_holder_helper(holder_ptr=false, owned=false) 175 assert stats.alive() == 2 176 assert s.set_ref(ref) 177 with pytest.raises(RuntimeError) as excinfo: 178 assert s.set_holder(ref) 179 assert "Unable to cast from non-held to held instance" in str(excinfo.value) 180 181 copy = s.copy # init_holder_helper(holder_ptr=false, owned=true) 182 assert stats.alive() == 3 183 assert s.set_ref(copy) 184 assert s.set_holder(copy) 185 186 holder_ref = s.holder_ref # init_holder_helper(holder_ptr=true, owned=false) 187 assert stats.alive() == 3 188 assert s.set_ref(holder_ref) 189 assert s.set_holder(holder_ref) 190 191 holder_copy = s.holder_copy # init_holder_helper(holder_ptr=true, owned=true) 192 assert stats.alive() == 3 193 assert s.set_ref(holder_copy) 194 assert s.set_holder(holder_copy) 195 196 del ref, copy, holder_ref, holder_copy, s 197 assert stats.alive() == 0 198 199 200def test_shared_ptr_from_this_and_references(): 201 s = m.SharedFromThisRef() 202 stats = ConstructorStats.get(m.B) 203 assert stats.alive() == 2 204 205 ref = s.ref # init_holder_helper(holder_ptr=false, owned=false, bad_wp=false) 206 assert stats.alive() == 2 207 assert s.set_ref(ref) 208 assert s.set_holder( 209 ref 210 ) # std::enable_shared_from_this can create a holder from a reference 211 212 bad_wp = s.bad_wp # init_holder_helper(holder_ptr=false, owned=false, bad_wp=true) 213 assert stats.alive() == 2 214 assert s.set_ref(bad_wp) 215 with pytest.raises(RuntimeError) as excinfo: 216 assert s.set_holder(bad_wp) 217 assert "Unable to cast from non-held to held instance" in str(excinfo.value) 218 219 copy = s.copy # init_holder_helper(holder_ptr=false, owned=true, bad_wp=false) 220 assert stats.alive() == 3 221 assert s.set_ref(copy) 222 assert s.set_holder(copy) 223 224 holder_ref = ( 225 s.holder_ref 226 ) # init_holder_helper(holder_ptr=true, owned=false, bad_wp=false) 227 assert stats.alive() == 3 228 assert s.set_ref(holder_ref) 229 assert s.set_holder(holder_ref) 230 231 holder_copy = ( 232 s.holder_copy 233 ) # init_holder_helper(holder_ptr=true, owned=true, bad_wp=false) 234 assert stats.alive() == 3 235 assert s.set_ref(holder_copy) 236 assert s.set_holder(holder_copy) 237 238 del ref, bad_wp, copy, holder_ref, holder_copy, s 239 assert stats.alive() == 0 240 241 z = m.SharedFromThisVirt.get() 242 y = m.SharedFromThisVirt.get() 243 assert y is z 244 245 246def test_move_only_holder(): 247 a = m.TypeWithMoveOnlyHolder.make() 248 b = m.TypeWithMoveOnlyHolder.make_as_object() 249 stats = ConstructorStats.get(m.TypeWithMoveOnlyHolder) 250 assert stats.alive() == 2 251 del b 252 assert stats.alive() == 1 253 del a 254 assert stats.alive() == 0 255 256 257def test_holder_with_addressof_operator(): 258 # this test must not throw exception from c++ 259 a = m.TypeForHolderWithAddressOf.make() 260 a.print_object_1() 261 a.print_object_2() 262 a.print_object_3() 263 a.print_object_4() 264 265 stats = ConstructorStats.get(m.TypeForHolderWithAddressOf) 266 assert stats.alive() == 1 267 268 np = m.TypeForHolderWithAddressOf.make() 269 assert stats.alive() == 2 270 del a 271 assert stats.alive() == 1 272 del np 273 assert stats.alive() == 0 274 275 b = m.TypeForHolderWithAddressOf.make() 276 c = b 277 assert b.get() is c.get() 278 assert stats.alive() == 1 279 280 del b 281 assert stats.alive() == 1 282 283 del c 284 assert stats.alive() == 0 285 286 287def test_move_only_holder_with_addressof_operator(): 288 a = m.TypeForMoveOnlyHolderWithAddressOf.make() 289 a.print_object() 290 291 stats = ConstructorStats.get(m.TypeForMoveOnlyHolderWithAddressOf) 292 assert stats.alive() == 1 293 294 a.value = 42 295 assert a.value == 42 296 297 del a 298 assert stats.alive() == 0 299 300 301def test_smart_ptr_from_default(): 302 instance = m.HeldByDefaultHolder() 303 with pytest.raises(RuntimeError) as excinfo: 304 m.HeldByDefaultHolder.load_shared_ptr(instance) 305 assert ( 306 "Unable to load a custom holder type from a " 307 "default-holder instance" in str(excinfo.value) 308 ) 309 310 311def test_shared_ptr_gc(): 312 """#187: issue involving std::shared_ptr<> return value policy & garbage collection""" 313 el = m.ElementList() 314 for i in range(10): 315 el.add(m.ElementA(i)) 316 pytest.gc_collect() 317 for i, v in enumerate(el.get()): 318 assert i == v.value() 319