You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

314 lines
8.0KB

  1. /*
  2. * Scope Guard
  3. * Copyright (C) 2017 offa
  4. *
  5. * This file is part of Scope Guard.
  6. *
  7. * Scope Guard is free software: you can redistribute it and/or modify
  8. * it under the terms of the GNU General Public License as published by
  9. * the Free Software Foundation, either version 3 of the License, or
  10. * (at your option) any later version.
  11. *
  12. * Scope Guard is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with Scope Guard. If not, see <http://www.gnu.org/licenses/>.
  19. */
  20. #include "unique_resource.h"
  21. #include <catch.hpp>
  22. #include <trompeloeil.hpp>
  23. using namespace trompeloeil;
  24. namespace
  25. {
  26. using Handle = int;
  27. using PtrHandle = std::add_pointer_t<Handle>;
  28. struct CallMock
  29. {
  30. MAKE_MOCK1(deleter, void(Handle));
  31. };
  32. struct ThrowOnCopyMock
  33. {
  34. ThrowOnCopyMock() { }
  35. ThrowOnCopyMock(const ThrowOnCopyMock&) noexcept(false)
  36. {
  37. throw std::exception{};
  38. }
  39. ThrowOnCopyMock(ThrowOnCopyMock&&) = delete;
  40. MAKE_CONST_MOCK1(deleter, void(ThrowOnCopyMock));
  41. ThrowOnCopyMock& operator=(const ThrowOnCopyMock&) noexcept(false)
  42. {
  43. throw std::exception{};
  44. }
  45. ThrowOnCopyMock& operator=(ThrowOnCopyMock&&) = delete;
  46. };
  47. struct NotNothrowMoveMock
  48. {
  49. NotNothrowMoveMock(CallMock* mo) : m_mock(mo) { }
  50. NotNothrowMoveMock(const NotNothrowMoveMock& other) : m_mock(other.m_mock)
  51. {
  52. throw std::exception{};
  53. }
  54. NotNothrowMoveMock(NotNothrowMoveMock&& other) noexcept(false) : m_mock(other.m_mock) { }
  55. void operator()(Handle h) const
  56. {
  57. m_mock->deleter(h);
  58. }
  59. NotNothrowMoveMock& operator=(const NotNothrowMoveMock&)
  60. {
  61. throw "Not implemented";
  62. }
  63. NotNothrowMoveMock& operator=(NotNothrowMoveMock&&)
  64. {
  65. throw "Not implemented";
  66. }
  67. CallMock* m_mock;
  68. };
  69. struct ConditialThrowOnCopyMock
  70. {
  71. explicit ConditialThrowOnCopyMock(Handle h, bool shouldThrow) : m_handle(h), m_shouldThrow(shouldThrow)
  72. {
  73. }
  74. ConditialThrowOnCopyMock(const ConditialThrowOnCopyMock& other) : m_handle(other.m_handle), m_shouldThrow(other.m_shouldThrow)
  75. {
  76. if( m_shouldThrow == true )
  77. {
  78. throw std::exception{};
  79. }
  80. }
  81. ConditialThrowOnCopyMock(ConditialThrowOnCopyMock&&) = default;
  82. ConditialThrowOnCopyMock& operator=(const ConditialThrowOnCopyMock& other)
  83. {
  84. if( &other != this )
  85. {
  86. m_handle = other.m_handle;
  87. m_shouldThrow = other.m_shouldThrow;
  88. if( m_shouldThrow == true )
  89. {
  90. throw std::exception{};
  91. }
  92. }
  93. return *this;
  94. }
  95. ConditialThrowOnCopyMock& operator=(ConditialThrowOnCopyMock&&) = default;
  96. Handle m_handle;
  97. bool m_shouldThrow;
  98. };
  99. struct CopyMock
  100. {
  101. CopyMock() {}
  102. CopyMock(const CopyMock&) { }
  103. };
  104. CallMock m;
  105. void deleter(Handle h)
  106. {
  107. m.deleter(h);
  108. }
  109. }
  110. TEST_CASE("construction with move", "[UniqueResource]")
  111. {
  112. REQUIRE_CALL(m, deleter(3));
  113. auto guard = sr::make_unique_resource(Handle{3}, deleter);
  114. static_cast<void>(guard);
  115. }
  116. TEST_CASE("construction with copy", "[UniqueResource]")
  117. {
  118. REQUIRE_CALL(m, deleter(3));
  119. const Handle h{3};
  120. const auto d = [](auto v) { m.deleter(v); };
  121. auto guard = sr::make_unique_resource(h, d);
  122. static_cast<void>(guard);
  123. }
  124. TEST_CASE("construction with copy calls deleter and rethrows on failed copy", "[UniqueResource]")
  125. {
  126. REQUIRE_THROWS([] {
  127. const ThrowOnCopyMock noMove;
  128. const auto d = [](const auto&) { m.deleter(3); };
  129. REQUIRE_CALL(m, deleter(3));
  130. sr::unique_resource<decltype(noMove), decltype(d)> guard{noMove, d};
  131. static_cast<void>(guard);
  132. }());
  133. }
  134. TEST_CASE("move-construction with move", "[UniqueResource]")
  135. {
  136. REQUIRE_CALL(m, deleter(3));
  137. auto movedFrom = sr::make_unique_resource(Handle{3}, deleter);
  138. auto guard = std::move(movedFrom);
  139. CHECK(guard.get() == 3);
  140. }
  141. TEST_CASE("move-construction with copy", "[UniqueResource]")
  142. {
  143. REQUIRE_CALL(m, deleter(7));
  144. auto d = [](auto) { deleter(7); };
  145. const CopyMock copyMock;
  146. sr::unique_resource<CopyMock, decltype(d)> movedFrom{copyMock, d};
  147. auto guard = std::move(movedFrom);
  148. static_cast<void>(guard);
  149. }
  150. TEST_CASE("move assignment calls deleter", "[UniqueResource]")
  151. {
  152. auto moveFrom = sr::make_unique_resource(Handle{3}, deleter);
  153. REQUIRE_CALL(m, deleter(4));
  154. {
  155. REQUIRE_CALL(m, deleter(3));
  156. auto guard = sr::make_unique_resource(Handle{4}, deleter);
  157. guard = std::move(moveFrom);
  158. }
  159. }
  160. TEST_CASE("deleter called on destruction", "[UniqueResource]")
  161. {
  162. REQUIRE_CALL(m, deleter(3));
  163. auto guard = sr::make_unique_resource(Handle{3}, deleter);
  164. static_cast<void>(guard);
  165. }
  166. TEST_CASE("reset calls deleter", "[UniqueResource]")
  167. {
  168. auto guard = sr::make_unique_resource(Handle{3}, deleter);
  169. {
  170. REQUIRE_CALL(m, deleter(3));
  171. guard.reset();
  172. }
  173. }
  174. TEST_CASE("reset does not call deleter if released", "[UniqueResource]")
  175. {
  176. REQUIRE_CALL(m, deleter(3)).TIMES(0);
  177. auto guard = sr::make_unique_resource(Handle{3}, deleter);
  178. guard.release();
  179. guard.reset();
  180. }
  181. TEST_CASE("reset sets new value and calls deleter on previous", "[UniqueResource]")
  182. {
  183. REQUIRE_CALL(m, deleter(3));
  184. REQUIRE_CALL(m, deleter(7));
  185. auto guard = sr::make_unique_resource(Handle{3}, deleter);
  186. guard.reset(Handle{7});
  187. }
  188. TEST_CASE("reset handles exception on assignment", "[UniqueResource]")
  189. {
  190. REQUIRE_CALL(m, deleter(3));
  191. REQUIRE_CALL(m, deleter(7));
  192. auto d = [](const auto& v) { deleter(v.m_handle); };
  193. auto guard = sr::make_unique_resource(ConditialThrowOnCopyMock{3, false}, d);
  194. guard.reset(ConditialThrowOnCopyMock{7, true});
  195. }
  196. TEST_CASE("release disables deleter", "[UniqueResource]")
  197. {
  198. REQUIRE_CALL(m, deleter(3)).TIMES(0);
  199. auto guard = sr::make_unique_resource(Handle{3}, deleter);
  200. guard.release();
  201. }
  202. TEST_CASE("get returns resource", "[UniqueResource]")
  203. {
  204. REQUIRE_CALL(m, deleter(3));
  205. auto guard = sr::make_unique_resource(Handle{3}, deleter);
  206. CHECK(guard.get() == 3);
  207. }
  208. TEST_CASE("pointer access returns resource" "[UniqueResource]")
  209. {
  210. const auto p = std::make_pair(3, 4);
  211. auto guard = sr::make_unique_resource(&p, [](auto*) { });
  212. REQUIRE(guard->first == 3);
  213. REQUIRE(guard->second == 4);
  214. }
  215. TEST_CASE("pointer dereference returns resource" "[UniqueResource]")
  216. {
  217. Handle h{5};
  218. auto guard = sr::make_unique_resource(PtrHandle{&h}, [](auto*) { });
  219. REQUIRE(*guard == 5);
  220. }
  221. TEST_CASE("deleter access", "[UniqueResource]")
  222. {
  223. auto guard = sr::make_unique_resource(Handle{3}, deleter);
  224. guard.release();
  225. {
  226. REQUIRE_CALL(m, deleter(8));
  227. guard.get_deleter()(8);
  228. }
  229. }
  230. TEST_CASE("make unique resource", "[UniqueResource]")
  231. {
  232. REQUIRE_CALL(m, deleter(7));
  233. auto guard = sr::make_unique_resource(Handle{7}, deleter);
  234. static_cast<void>(guard);
  235. }
  236. TEST_CASE("make unique resource with reference wrapper", "[UniqueResource]")
  237. {
  238. REQUIRE_CALL(m, deleter(3));
  239. Handle h{3};
  240. auto guard = sr::make_unique_resource(std::ref(h), deleter);
  241. static_cast<void>(guard);
  242. }
  243. TEST_CASE("make unique resource checked", "[UniqueResource]")
  244. {
  245. REQUIRE_CALL(m, deleter(4));
  246. auto guard = sr::make_unique_resource_checked(Handle{4}, Handle{-1}, deleter);
  247. static_cast<void>(guard);
  248. }
  249. TEST_CASE("make unique resource checked releases if invalid", "[UniqueResource]")
  250. {
  251. auto guard = sr::make_unique_resource_checked(Handle{-1}, Handle{-1}, deleter);
  252. static_cast<void>(guard);
  253. }