test_smart_ptr.py 8.3 KB

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