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.

237 lines
5.9KB

  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. // TODO: Implement move assignment
  142. TEST_CASE("deleter called on destruction", "[UniqueResource]")
  143. {
  144. REQUIRE_CALL(m, deleter(3));
  145. auto guard = sr::make_unique_resource(Handle{3}, deleter);
  146. static_cast<void>(guard);
  147. }
  148. TEST_CASE("reset calls deleter", "[UniqueResource]")
  149. {
  150. auto guard = sr::make_unique_resource(Handle{3}, deleter);
  151. {
  152. REQUIRE_CALL(m, deleter(3));
  153. guard.reset();
  154. }
  155. }
  156. TEST_CASE("reset does not call deleter if released", "[UniqueResource]")
  157. {
  158. REQUIRE_CALL(m, deleter(3)).TIMES(0);
  159. auto guard = sr::make_unique_resource(Handle{3}, deleter);
  160. guard.release();
  161. guard.reset();
  162. }
  163. TEST_CASE("reset sets new value and calls deleter on previous", "[UniqueResource]")
  164. {
  165. REQUIRE_CALL(m, deleter(3));
  166. REQUIRE_CALL(m, deleter(7));
  167. auto guard = sr::make_unique_resource(Handle{3}, deleter);
  168. guard.reset(Handle{7});
  169. }
  170. TEST_CASE("release disables deleter", "[UniqueResource]")
  171. {
  172. REQUIRE_CALL(m, deleter(3)).TIMES(0);
  173. auto guard = sr::make_unique_resource(Handle{3}, deleter);
  174. guard.release();
  175. }
  176. TEST_CASE("get returns resource", "[UniqueResource]")
  177. {
  178. REQUIRE_CALL(m, deleter(3));
  179. auto guard = sr::make_unique_resource(Handle{3}, deleter);
  180. CHECK(guard.get() == 3);
  181. }
  182. // TODO: Pointer access functions
  183. TEST_CASE("deleter access", "[UniqueResource]")
  184. {
  185. auto guard = sr::make_unique_resource(Handle{3}, deleter);
  186. guard.release();
  187. {
  188. REQUIRE_CALL(m, deleter(8));
  189. guard.get_deleter()(8);
  190. }
  191. }