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.

220 lines
6.4KB

  1. // MIT License
  2. //
  3. // Copyright (c) 2017-2019 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("construction with move", "[UniqueResource]")
  37. {
  38. REQUIRE_CALL(m, deleter(3));
  39. [[maybe_unused]] auto guard = sr::unique_resource{Handle{3}, deleter};
  40. }
  41. TEST_CASE("construction with copy", "[UniqueResource]")
  42. {
  43. REQUIRE_CALL(m, deleter(3));
  44. const Handle h{3};
  45. const auto d = [](auto v) { m.deleter(v); };
  46. [[maybe_unused]] auto guard = sr::unique_resource{h, d};
  47. }
  48. TEST_CASE("construction with copy calls deleter and rethrows on failed copy", "[UniqueResource]")
  49. {
  50. REQUIRE_THROWS([] {
  51. const ThrowOnCopyMock noMove;
  52. const auto d = [](const auto&) { m.deleter(3); };
  53. REQUIRE_CALL(m, deleter(3));
  54. [[maybe_unused]] sr::unique_resource guard{noMove, d};
  55. }());
  56. }
  57. TEST_CASE("move-construction with move", "[UniqueResource]")
  58. {
  59. REQUIRE_CALL(m, deleter(3));
  60. auto movedFrom = sr::unique_resource{Handle{3}, deleter};
  61. auto guard = std::move(movedFrom);
  62. CHECK(guard.get() == 3);
  63. }
  64. TEST_CASE("move-construction with copy", "[UniqueResource]")
  65. {
  66. REQUIRE_CALL(m, deleter(7));
  67. auto d = [](auto) { deleter(7); };
  68. const CopyMock copyMock;
  69. sr::unique_resource movedFrom{copyMock, d};
  70. [[maybe_unused]] auto guard = std::move(movedFrom);
  71. }
  72. TEST_CASE("move-construction prevents double release", "[UniqueResource]")
  73. {
  74. auto movedFrom = sr::unique_resource{Handle{3}, ConditionalThrowOnCopyDeleter{}};
  75. movedFrom.release();
  76. ConditionalThrowOnCopyDeleter::throwOnNextCopy = true;
  77. REQUIRE_THROWS([&movedFrom] { [[maybe_unused]] auto guard = std::move(movedFrom); }());
  78. }
  79. TEST_CASE("move assignment calls deleter", "[UniqueResource]")
  80. {
  81. auto moveFrom = sr::unique_resource{Handle{3}, deleter};
  82. REQUIRE_CALL(m, deleter(4));
  83. {
  84. REQUIRE_CALL(m, deleter(3));
  85. auto guard = sr::unique_resource{Handle{4}, deleter};
  86. guard = std::move(moveFrom);
  87. }
  88. }
  89. TEST_CASE("deleter called on destruction", "[UniqueResource]")
  90. {
  91. REQUIRE_CALL(m, deleter(3));
  92. [[maybe_unused]] auto guard = sr::unique_resource{Handle{3}, deleter};
  93. }
  94. TEST_CASE("reset calls deleter", "[UniqueResource]")
  95. {
  96. auto guard = sr::unique_resource{Handle{3}, deleter};
  97. {
  98. REQUIRE_CALL(m, deleter(3));
  99. guard.reset();
  100. }
  101. }
  102. TEST_CASE("reset does not call deleter if released", "[UniqueResource]")
  103. {
  104. REQUIRE_CALL(m, deleter(3)).TIMES(0);
  105. auto guard = sr::unique_resource{Handle{3}, deleter};
  106. guard.release();
  107. guard.reset();
  108. }
  109. TEST_CASE("reset sets new rvalue and calls deleter on previous", "[UniqueResource]")
  110. {
  111. REQUIRE_CALL(m, deleter(3));
  112. REQUIRE_CALL(m, deleter(7));
  113. auto guard = sr::unique_resource{Handle{3}, deleter};
  114. guard.reset(Handle{7});
  115. }
  116. TEST_CASE("reset sets new lvalue and calls deleter on previous", "[UniqueResource]")
  117. {
  118. REQUIRE_CALL(m, deleter(3));
  119. REQUIRE_CALL(m, deleter(7));
  120. auto d = [](const auto& v) { deleter(v.value); };
  121. auto guard = sr::unique_resource{NotNothrowAssignable{3}, d};
  122. const NotNothrowAssignable h{7};
  123. guard.reset(h);
  124. }
  125. TEST_CASE("reset handles exception on assignment", "[UniqueResource]")
  126. {
  127. REQUIRE_CALL(m, deleter(3));
  128. REQUIRE_CALL(m, deleter(7));
  129. auto d = [](const auto& v) { deleter(v.handle); };
  130. auto guard = sr::unique_resource{ConditionalThrowOnCopyMock{3, false}, d};
  131. guard.reset(ConditionalThrowOnCopyMock{7, true});
  132. }
  133. TEST_CASE("release disables deleter", "[UniqueResource]")
  134. {
  135. REQUIRE_CALL(m, deleter(3)).TIMES(0);
  136. auto guard = sr::unique_resource{Handle{3}, deleter};
  137. guard.release();
  138. }
  139. TEST_CASE("get returns resource", "[UniqueResource]")
  140. {
  141. REQUIRE_CALL(m, deleter(3));
  142. auto guard = sr::unique_resource{Handle{3}, deleter};
  143. CHECK(guard.get() == 3);
  144. }
  145. TEST_CASE("pointer access returns resource" "[UniqueResource]")
  146. {
  147. const auto p = std::make_pair(3, 4);
  148. auto guard = sr::unique_resource{&p, [](auto*) { }};
  149. REQUIRE(guard->first == 3);
  150. REQUIRE(guard->second == 4);
  151. }
  152. TEST_CASE("pointer dereference returns resource" "[UniqueResource]")
  153. {
  154. Handle h{5};
  155. auto guard = sr::unique_resource{PtrHandle{&h}, [](auto*) { }};
  156. REQUIRE(*guard == 5);
  157. }
  158. TEST_CASE("deleter access", "[UniqueResource]")
  159. {
  160. auto guard = sr::unique_resource{Handle{3}, deleter};
  161. guard.release();
  162. {
  163. REQUIRE_CALL(m, deleter(8));
  164. guard.get_deleter()(8);
  165. }
  166. }
  167. TEST_CASE("make unique resource", "[UniqueResource]")
  168. {
  169. REQUIRE_CALL(m, deleter(7));
  170. [[maybe_unused]] auto guard = sr::unique_resource{Handle{7}, deleter};
  171. }
  172. TEST_CASE("make unique resource with reference wrapper", "[UniqueResource]")
  173. {
  174. REQUIRE_CALL(m, deleter(3));
  175. Handle h{3};
  176. [[maybe_unused]] auto guard = sr::unique_resource{std::ref(h), deleter};
  177. }
  178. TEST_CASE("make unique resource checked", "[UniqueResource]")
  179. {
  180. REQUIRE_CALL(m, deleter(4));
  181. [[maybe_unused]] auto guard = sr::make_unique_resource_checked(Handle{4}, Handle{-1}, deleter);
  182. }
  183. TEST_CASE("make unique resource checked releases if invalid", "[UniqueResource]")
  184. {
  185. [[maybe_unused]] auto guard = sr::make_unique_resource_checked(Handle{-1}, Handle{-1}, deleter);
  186. }