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 # Leak, but that's intentional 129 130 131def test_unique_nodelete4a(): 132 o = m.MyObject4a(23) 133 assert o.value == 23 134 cstats = ConstructorStats.get(m.MyObject4a) 135 assert cstats.alive() == 1 136 del o 137 assert cstats.alive() == 1 # Leak, but that's intentional 138 139 140def test_unique_deleter(): 141 o = m.MyObject4b(23) 142 assert o.value == 23 143 cstats4a = ConstructorStats.get(m.MyObject4a) 144 assert cstats4a.alive() == 2 # Two because of previous test 145 cstats4b = ConstructorStats.get(m.MyObject4b) 146 assert cstats4b.alive() == 1 147 del o 148 assert cstats4a.alive() == 1 # Should now only be one leftover from previous test 149 assert cstats4b.alive() == 0 # Should be deleted 150 151 152def test_large_holder(): 153 o = m.MyObject5(5) 154 assert o.value == 5 155 cstats = ConstructorStats.get(m.MyObject5) 156 assert cstats.alive() == 1 157 del o 158 assert cstats.alive() == 0 159 160 161def test_shared_ptr_and_references(): 162 s = m.SharedPtrRef() 163 stats = ConstructorStats.get(m.A) 164 assert stats.alive() == 2 165 166 ref = s.ref # init_holder_helper(holder_ptr=false, owned=false) 167 assert stats.alive() == 2 168 assert s.set_ref(ref) 169 with pytest.raises(RuntimeError) as excinfo: 170 assert s.set_holder(ref) 171 assert "Unable to cast from non-held to held instance" in str(excinfo.value) 172 173 copy = s.copy # init_holder_helper(holder_ptr=false, owned=true) 174 assert stats.alive() == 3 175 assert s.set_ref(copy) 176 assert s.set_holder(copy) 177 178 holder_ref = s.holder_ref # init_holder_helper(holder_ptr=true, owned=false) 179 assert stats.alive() == 3 180 assert s.set_ref(holder_ref) 181 assert s.set_holder(holder_ref) 182 183 holder_copy = s.holder_copy # init_holder_helper(holder_ptr=true, owned=true) 184 assert stats.alive() == 3 185 assert s.set_ref(holder_copy) 186 assert s.set_holder(holder_copy) 187 188 del ref, copy, holder_ref, holder_copy, s 189 assert stats.alive() == 0 190 191 192def test_shared_ptr_from_this_and_references(): 193 s = m.SharedFromThisRef() 194 stats = ConstructorStats.get(m.B) 195 assert stats.alive() == 2 196 197 ref = s.ref # init_holder_helper(holder_ptr=false, owned=false, bad_wp=false) 198 assert stats.alive() == 2 199 assert s.set_ref(ref) 200 assert s.set_holder( 201 ref 202 ) # std::enable_shared_from_this can create a holder from a reference 203 204 bad_wp = s.bad_wp # init_holder_helper(holder_ptr=false, owned=false, bad_wp=true) 205 assert stats.alive() == 2 206 assert s.set_ref(bad_wp) 207 with pytest.raises(RuntimeError) as excinfo: 208 assert s.set_holder(bad_wp) 209 assert "Unable to cast from non-held to held instance" in str(excinfo.value) 210 211 copy = s.copy # init_holder_helper(holder_ptr=false, owned=true, bad_wp=false) 212 assert stats.alive() == 3 213 assert s.set_ref(copy) 214 assert s.set_holder(copy) 215 216 holder_ref = ( 217 s.holder_ref 218 ) # init_holder_helper(holder_ptr=true, owned=false, bad_wp=false) 219 assert stats.alive() == 3 220 assert s.set_ref(holder_ref) 221 assert s.set_holder(holder_ref) 222 223 holder_copy = ( 224 s.holder_copy 225 ) # init_holder_helper(holder_ptr=true, owned=true, bad_wp=false) 226 assert stats.alive() == 3 227 assert s.set_ref(holder_copy) 228 assert s.set_holder(holder_copy) 229 230 del ref, bad_wp, copy, holder_ref, holder_copy, s 231 assert stats.alive() == 0 232 233 z = m.SharedFromThisVirt.get() 234 y = m.SharedFromThisVirt.get() 235 assert y is z 236 237 238def test_move_only_holder(): 239 a = m.TypeWithMoveOnlyHolder.make() 240 b = m.TypeWithMoveOnlyHolder.make_as_object() 241 stats = ConstructorStats.get(m.TypeWithMoveOnlyHolder) 242 assert stats.alive() == 2 243 del b 244 assert stats.alive() == 1 245 del a 246 assert stats.alive() == 0 247 248 249def test_holder_with_addressof_operator(): 250 # this test must not throw exception from c++ 251 a = m.TypeForHolderWithAddressOf.make() 252 a.print_object_1() 253 a.print_object_2() 254 a.print_object_3() 255 a.print_object_4() 256 257 stats = ConstructorStats.get(m.TypeForHolderWithAddressOf) 258 assert stats.alive() == 1 259 260 np = m.TypeForHolderWithAddressOf.make() 261 assert stats.alive() == 2 262 del a 263 assert stats.alive() == 1 264 del np 265 assert stats.alive() == 0 266 267 b = m.TypeForHolderWithAddressOf.make() 268 c = b 269 assert b.get() is c.get() 270 assert stats.alive() == 1 271 272 del b 273 assert stats.alive() == 1 274 275 del c 276 assert stats.alive() == 0 277 278 279def test_move_only_holder_with_addressof_operator(): 280 a = m.TypeForMoveOnlyHolderWithAddressOf.make() 281 a.print_object() 282 283 stats = ConstructorStats.get(m.TypeForMoveOnlyHolderWithAddressOf) 284 assert stats.alive() == 1 285 286 a.value = 42 287 assert a.value == 42 288 289 del a 290 assert stats.alive() == 0 291 292 293def test_smart_ptr_from_default(): 294 instance = m.HeldByDefaultHolder() 295 with pytest.raises(RuntimeError) as excinfo: 296 m.HeldByDefaultHolder.load_shared_ptr(instance) 297 assert ( 298 "Unable to load a custom holder type from a " 299 "default-holder instance" in str(excinfo.value) 300 ) 301 302 303def test_shared_ptr_gc(): 304 """#187: issue involving std::shared_ptr<> return value policy & garbage collection""" 305 el = m.ElementList() 306 for i in range(10): 307 el.add(m.ElementA(i)) 308 pytest.gc_collect() 309 for i, v in enumerate(el.get()): 310 assert i == v.value() 311