選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

329 行
8.2KB

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