Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

255 lines
7.4KB

  1. // MIT License
  2. //
  3. // Copyright (c) 2017-2021 offa
  4. //
  5. // Permission is hereby granted, free of charge, to any person obtaining a copy
  6. // of this software and associated documentation files (the "Software"), to deal
  7. // in the Software without restriction, including without limitation the rights
  8. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9. // copies of the Software, and to permit persons to whom the Software is
  10. // furnished to do so, subject to the following conditions:
  11. //
  12. // The above copyright notice and this permission notice shall be included in all
  13. // copies or substantial portions of the Software.
  14. //
  15. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  21. // SOFTWARE.
  22. #include "unique_resource.h"
  23. #include "CallMocks.h"
  24. #include <catch2/catch.hpp>
  25. #include <trompeloeil.hpp>
  26. using namespace mock;
  27. using namespace trompeloeil;
  28. namespace
  29. {
  30. CallMock m;
  31. void deleter(Handle h)
  32. {
  33. m.deleter(h);
  34. }
  35. }
  36. TEST_CASE("default construction", "[UniqueResource]")
  37. {
  38. [[maybe_unused]] sr::unique_resource<int, MoveableMock> guard{};
  39. }
  40. TEST_CASE("construction with move", "[UniqueResource]")
  41. {
  42. REQUIRE_CALL(m, deleter(3));
  43. [[maybe_unused]] auto guard = sr::unique_resource{Handle{3}, deleter};
  44. }
  45. TEST_CASE("construction with copy", "[UniqueResource]")
  46. {
  47. REQUIRE_CALL(m, deleter(3));
  48. const Handle h{3};
  49. const auto d = [](auto v) { m.deleter(v); };
  50. [[maybe_unused]] auto guard = sr::unique_resource{h, d};
  51. }
  52. TEST_CASE("construction with copy calls deleter and rethrows on failed copy", "[UniqueResource]")
  53. {
  54. REQUIRE_THROWS([] {
  55. const ThrowOnCopyMock noMove;
  56. const auto d = [](const auto&) { m.deleter(3); };
  57. REQUIRE_CALL(m, deleter(3));
  58. [[maybe_unused]] sr::unique_resource guard{noMove, d};
  59. }());
  60. }
  61. TEST_CASE("move-construction with move", "[UniqueResource]")
  62. {
  63. REQUIRE_CALL(m, deleter(3));
  64. auto movedFrom = sr::unique_resource{Handle{3}, deleter};
  65. auto guard = std::move(movedFrom);
  66. CHECK(guard.get() == 3);
  67. }
  68. TEST_CASE("move-construction with copy", "[UniqueResource]")
  69. {
  70. REQUIRE_CALL(m, deleter(7));
  71. auto d = [](auto) { deleter(7); };
  72. const CopyMock copyMock;
  73. sr::unique_resource movedFrom{copyMock, d};
  74. [[maybe_unused]] auto guard = std::move(movedFrom);
  75. }
  76. TEST_CASE("move-construction prevents double release", "[UniqueResource]")
  77. {
  78. auto movedFrom = sr::unique_resource{Handle{3}, ConditionalThrowOnCopyDeleter{}};
  79. movedFrom.release();
  80. ConditionalThrowOnCopyDeleter::throwOnNextCopy = true;
  81. REQUIRE_THROWS([&movedFrom] { [[maybe_unused]] auto guard = std::move(movedFrom); }());
  82. }
  83. TEST_CASE("move assignment calls deleter", "[UniqueResource]")
  84. {
  85. auto moveFrom = sr::unique_resource{Handle{3}, deleter};
  86. REQUIRE_CALL(m, deleter(4));
  87. {
  88. REQUIRE_CALL(m, deleter(3));
  89. auto guard = sr::unique_resource{Handle{4}, deleter};
  90. guard = std::move(moveFrom);
  91. }
  92. }
  93. TEST_CASE("deleter called on destruction", "[UniqueResource]")
  94. {
  95. REQUIRE_CALL(m, deleter(3));
  96. [[maybe_unused]] auto guard = sr::unique_resource{Handle{3}, deleter};
  97. }
  98. TEST_CASE("reset calls deleter", "[UniqueResource]")
  99. {
  100. auto guard = sr::unique_resource{Handle{3}, deleter};
  101. {
  102. REQUIRE_CALL(m, deleter(3));
  103. guard.reset();
  104. }
  105. }
  106. TEST_CASE("reset does not call deleter if released", "[UniqueResource]")
  107. {
  108. REQUIRE_CALL(m, deleter(3)).TIMES(0);
  109. auto guard = sr::unique_resource{Handle{3}, deleter};
  110. guard.release();
  111. guard.reset();
  112. }
  113. TEST_CASE("reset sets new rvalue and calls deleter on previous", "[UniqueResource]")
  114. {
  115. REQUIRE_CALL(m, deleter(3));
  116. REQUIRE_CALL(m, deleter(7));
  117. auto guard = sr::unique_resource{Handle{3}, deleter};
  118. guard.reset(Handle{7});
  119. }
  120. TEST_CASE("reset sets new lvalue and calls deleter on previous", "[UniqueResource]")
  121. {
  122. REQUIRE_CALL(m, deleter(3));
  123. REQUIRE_CALL(m, deleter(7));
  124. auto d = [](const auto& v) { deleter(v.value); };
  125. auto guard = sr::unique_resource{NotNothrowAssignable{3}, d};
  126. const NotNothrowAssignable h{7};
  127. guard.reset(h);
  128. }
  129. TEST_CASE("reset handles exception on assignment", "[UniqueResource]")
  130. {
  131. REQUIRE_CALL(m, deleter(3));
  132. REQUIRE_CALL(m, deleter(7));
  133. auto d = [](const auto& v) { deleter(v.handle); };
  134. auto guard = sr::unique_resource{ConditionalThrowOnCopyMock{3, false}, d};
  135. guard.reset(ConditionalThrowOnCopyMock{7, true});
  136. }
  137. TEST_CASE("release disables deleter", "[UniqueResource]")
  138. {
  139. REQUIRE_CALL(m, deleter(3)).TIMES(0);
  140. auto guard = sr::unique_resource{Handle{3}, deleter};
  141. guard.release();
  142. }
  143. TEST_CASE("get returns resource", "[UniqueResource]")
  144. {
  145. REQUIRE_CALL(m, deleter(3));
  146. auto guard = sr::unique_resource{Handle{3}, deleter};
  147. CHECK(guard.get() == 3);
  148. }
  149. TEST_CASE("pointer access returns resource" "[UniqueResource]")
  150. {
  151. const auto p = std::make_pair(3, 4);
  152. auto guard = sr::unique_resource{&p, [](auto*) { }};
  153. REQUIRE(guard->first == 3);
  154. REQUIRE(guard->second == 4);
  155. }
  156. TEST_CASE("pointer dereference returns resource" "[UniqueResource]")
  157. {
  158. Handle h{5};
  159. auto guard = sr::unique_resource{PtrHandle{&h}, [](auto*) { }};
  160. REQUIRE(*guard == 5);
  161. }
  162. TEST_CASE("deleter access", "[UniqueResource]")
  163. {
  164. auto guard = sr::unique_resource{Handle{3}, deleter};
  165. guard.release();
  166. {
  167. REQUIRE_CALL(m, deleter(8));
  168. guard.get_deleter()(8);
  169. }
  170. }
  171. TEST_CASE("make unique resource", "[UniqueResource]")
  172. {
  173. REQUIRE_CALL(m, deleter(7));
  174. [[maybe_unused]] auto guard = sr::unique_resource{Handle{7}, deleter};
  175. }
  176. TEST_CASE("make unique resource with reference wrapper", "[UniqueResource]")
  177. {
  178. REQUIRE_CALL(m, deleter(3));
  179. Handle h{3};
  180. [[maybe_unused]] auto guard = sr::unique_resource{std::ref(h), deleter};
  181. }
  182. TEST_CASE("make unique resource checked", "[UniqueResource]")
  183. {
  184. REQUIRE_CALL(m, deleter(4));
  185. [[maybe_unused]] auto guard = sr::make_unique_resource_checked(Handle{4}, Handle{-1}, deleter);
  186. }
  187. TEST_CASE("make unique resource checked releases if invalid", "[UniqueResource]")
  188. {
  189. [[maybe_unused]] auto guard = sr::make_unique_resource_checked(Handle{-1}, Handle{-1}, deleter);
  190. }
  191. TEST_CASE("not noexcept move", "[UniqueResource]")
  192. {
  193. NoexceptDeleter<false> deleter{};
  194. auto guard = sr::unique_resource{NoexceptResource<false>{}, deleter};
  195. auto temp = std::move(guard);
  196. guard = std::move(temp);
  197. }
  198. TEST_CASE("noexcept move", "[UniqueResource]")
  199. {
  200. NoexceptDeleter<true> deleter{};
  201. auto guard = sr::unique_resource{NoexceptResource<true>{}, deleter};
  202. auto temp = std::move(guard);
  203. guard = std::move(temp);
  204. }
  205. TEST_CASE("std::function deleter", "[UniqueResource]")
  206. {
  207. const auto deleter = std::function<void(Handle)>{[]([[maybe_unused]] Handle h) { }};
  208. sr::unique_resource movedFrom{Handle{3}, deleter};
  209. sr::unique_resource guard2{Handle{4}, deleter};
  210. guard2 = std::move(movedFrom);
  211. }
  212. TEST_CASE("not noexcept move and copy assignable deleter", "[UniqueResource]")
  213. {
  214. sr::unique_resource movedFrom{0, FunctionDeleter{}};
  215. sr::unique_resource guard{0, FunctionDeleter{}};
  216. guard = std::move(movedFrom);
  217. }