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.

289 line
7.4KB

  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&)
  36. {
  37. throw std::exception{};
  38. }
  39. MAKE_CONST_MOCK1(deleter, void(ThrowOnCopyMock));
  40. ThrowOnCopyMock& operator=(const ThrowOnCopyMock&)
  41. {
  42. throw std::exception{};
  43. }
  44. };
  45. struct NotNothrowMoveMock
  46. {
  47. NotNothrowMoveMock(CallMock* mo) : m_mock(mo) { }
  48. NotNothrowMoveMock(const NotNothrowMoveMock& other) : m_mock(other.m_mock) { }
  49. NotNothrowMoveMock(NotNothrowMoveMock&& other) noexcept(false) : m_mock(other.m_mock) { }
  50. void operator()(Handle h) const
  51. {
  52. m_mock->deleter(h);
  53. }
  54. NotNothrowMoveMock& operator=(const NotNothrowMoveMock&)
  55. {
  56. throw "Not implemented";
  57. }
  58. NotNothrowMoveMock& operator=(NotNothrowMoveMock&&)
  59. {
  60. throw "Not implemented";
  61. }
  62. CallMock* m_mock;
  63. };
  64. struct ConditialThrowOnCopyMock
  65. {
  66. explicit ConditialThrowOnCopyMock(Handle h, bool shouldThrow) : m_handle(h), m_shouldThrow(shouldThrow)
  67. {
  68. }
  69. ConditialThrowOnCopyMock(const ConditialThrowOnCopyMock& other) : m_handle(other.m_handle), m_shouldThrow(other.m_shouldThrow)
  70. {
  71. if( m_shouldThrow == true )
  72. {
  73. throw std::exception{};
  74. }
  75. }
  76. ConditialThrowOnCopyMock(ConditialThrowOnCopyMock&&) = default;
  77. ConditialThrowOnCopyMock& operator=(const ConditialThrowOnCopyMock& other)
  78. {
  79. if( &other != this )
  80. {
  81. m_handle = other.m_handle;
  82. m_shouldThrow = other.m_shouldThrow;
  83. if( m_shouldThrow == true )
  84. {
  85. throw std::exception{};
  86. }
  87. }
  88. return *this;
  89. }
  90. ConditialThrowOnCopyMock& operator=(ConditialThrowOnCopyMock&&) = default;
  91. Handle m_handle;
  92. bool m_shouldThrow;
  93. };
  94. CallMock m;
  95. void deleter(Handle h)
  96. {
  97. m.deleter(h);
  98. }
  99. }
  100. TEST_CASE("construction with move", "[UniqueResource]")
  101. {
  102. REQUIRE_CALL(m, deleter(3));
  103. auto guard = sr::make_unique_resource(Handle{3}, deleter);
  104. static_cast<void>(guard);
  105. }
  106. TEST_CASE("construction with copy", "[UniqueResource]")
  107. {
  108. REQUIRE_CALL(m, deleter(3));
  109. const Handle h{3};
  110. const auto d = [](auto v) { m.deleter(v); };
  111. auto guard = sr::make_unique_resource(h, d);
  112. static_cast<void>(guard);
  113. }
  114. TEST_CASE("construction with copy calls deleter and rethrows on failed copy", "[UniqueResource]")
  115. {
  116. REQUIRE_THROWS([] {
  117. const ThrowOnCopyMock noMove;
  118. const auto d = [](const auto&) { m.deleter(3); };
  119. REQUIRE_CALL(m, deleter(3));
  120. sr::unique_resource<decltype(noMove), decltype(d)> guard{noMove, d};
  121. static_cast<void>(guard);
  122. }());
  123. }
  124. TEST_CASE("move-construction with move", "[UniqueResource]")
  125. {
  126. REQUIRE_CALL(m, deleter(3));
  127. auto movedFrom = sr::make_unique_resource(Handle{3}, deleter);
  128. auto guard = std::move(movedFrom);
  129. CHECK(guard.get() == 3);
  130. }
  131. TEST_CASE("move-construction with copy", "[UniqueResource]")
  132. {
  133. CallMock mock;
  134. REQUIRE_CALL(mock, deleter(3));
  135. const NotNothrowMoveMock notNothrow{&mock};
  136. Handle h{3};
  137. sr::unique_resource<Handle, decltype(notNothrow)> movedFrom{h, notNothrow};
  138. auto guard = std::move(movedFrom);
  139. CHECK(guard.get() == 3);
  140. }
  141. TEST_CASE("move assignment calls deleter", "[UniqueResource]")
  142. {
  143. auto moveFrom = sr::make_unique_resource(Handle{3}, deleter);
  144. REQUIRE_CALL(m, deleter(4));
  145. {
  146. REQUIRE_CALL(m, deleter(3));
  147. auto guard = sr::make_unique_resource(Handle{4}, deleter);
  148. guard = std::move(moveFrom);
  149. }
  150. }
  151. TEST_CASE("deleter called on destruction", "[UniqueResource]")
  152. {
  153. REQUIRE_CALL(m, deleter(3));
  154. auto guard = sr::make_unique_resource(Handle{3}, deleter);
  155. static_cast<void>(guard);
  156. }
  157. TEST_CASE("reset calls deleter", "[UniqueResource]")
  158. {
  159. auto guard = sr::make_unique_resource(Handle{3}, deleter);
  160. {
  161. REQUIRE_CALL(m, deleter(3));
  162. guard.reset();
  163. }
  164. }
  165. TEST_CASE("reset does not call deleter if released", "[UniqueResource]")
  166. {
  167. REQUIRE_CALL(m, deleter(3)).TIMES(0);
  168. auto guard = sr::make_unique_resource(Handle{3}, deleter);
  169. guard.release();
  170. guard.reset();
  171. }
  172. TEST_CASE("reset sets new value and calls deleter on previous", "[UniqueResource]")
  173. {
  174. REQUIRE_CALL(m, deleter(3));
  175. REQUIRE_CALL(m, deleter(7));
  176. auto guard = sr::make_unique_resource(Handle{3}, deleter);
  177. guard.reset(Handle{7});
  178. }
  179. TEST_CASE("release disables deleter", "[UniqueResource]")
  180. {
  181. REQUIRE_CALL(m, deleter(3)).TIMES(0);
  182. auto guard = sr::make_unique_resource(Handle{3}, deleter);
  183. guard.release();
  184. }
  185. TEST_CASE("get returns resource", "[UniqueResource]")
  186. {
  187. REQUIRE_CALL(m, deleter(3));
  188. auto guard = sr::make_unique_resource(Handle{3}, deleter);
  189. CHECK(guard.get() == 3);
  190. }
  191. TEST_CASE("pointer access returns resource" "[UniqueResource]")
  192. {
  193. const auto p = std::make_pair(3, 4);
  194. auto guard = sr::make_unique_resource(&p, [](auto*) { });
  195. REQUIRE(guard->first == 3);
  196. REQUIRE(guard->second == 4);
  197. }
  198. TEST_CASE("pointer dereference returns resource" "[UniqueResource]")
  199. {
  200. Handle h{5};
  201. auto guard = sr::make_unique_resource(PtrHandle{&h}, [](auto*) { });
  202. REQUIRE(*guard == 5);
  203. }
  204. TEST_CASE("deleter access", "[UniqueResource]")
  205. {
  206. auto guard = sr::make_unique_resource(Handle{3}, deleter);
  207. guard.release();
  208. {
  209. REQUIRE_CALL(m, deleter(8));
  210. guard.get_deleter()(8);
  211. }
  212. }
  213. TEST_CASE("make unique resource", "[UniqueResource]")
  214. {
  215. REQUIRE_CALL(m, deleter(7));
  216. auto guard = sr::make_unique_resource(Handle{7}, deleter);
  217. static_cast<void>(guard);
  218. }
  219. TEST_CASE("make unique resource with reference wrapper", "[UniqueResource]")
  220. {
  221. REQUIRE_CALL(m, deleter(3));
  222. Handle h{3};
  223. auto guard = sr::make_unique_resource(std::ref(h), deleter);
  224. static_cast<void>(guard);
  225. }
  226. TEST_CASE("make unique resource checked", "[UniqueResource]")
  227. {
  228. REQUIRE_CALL(m, deleter(4));
  229. auto guard = sr::make_unique_resource_checked(Handle{4}, Handle{-1}, deleter);
  230. static_cast<void>(guard);
  231. }
  232. TEST_CASE("make unique resource checked releases if invalid", "[UniqueResource]")
  233. {
  234. auto guard = sr::make_unique_resource_checked(Handle{-1}, Handle{-1}, deleter);
  235. static_cast<void>(guard);
  236. }