| using namespace trompeloeil; | using namespace trompeloeil; | ||||
| struct CallMock | |||||
| namespace mock | |||||
| { | { | ||||
| MAKE_MOCK0(deleter, void()); | |||||
| }; | |||||
| struct CallMock | |||||
| { | |||||
| MAKE_MOCK0(deleter, void()); | |||||
| }; | |||||
| } | |||||
| namespace | namespace | ||||
| { | { | ||||
| CallMock m; | |||||
| mock::CallMock m; | |||||
| void deleter() | void deleter() | ||||
| { | { | ||||
| m.deleter(); | m.deleter(); | ||||
| } | } | ||||
| void deleterThrow() | |||||
| { | |||||
| throw std::exception{}; | |||||
| } | |||||
| } | } | ||||
| TEST_CASE("deleter called on destruction", "[ScopeGuard]") | TEST_CASE("deleter called on destruction", "[ScopeGuard]") | ||||
| { | { | ||||
| { | |||||
| REQUIRE_CALL(m, deleter()); | |||||
| auto guard = sr::scope_guard(deleter); | |||||
| static_cast<void>(guard); | |||||
| } | |||||
| REQUIRE_CALL(m, deleter()); | |||||
| auto guard = sr::scope_guard(deleter); | |||||
| static_cast<void>(guard); | |||||
| } | } | ||||
| TEST_CASE("deleter is not called if released", "[ScopeGuard]") | TEST_CASE("deleter is not called if released", "[ScopeGuard]") | ||||
| { | { | ||||
| std::size_t calls{0}; | |||||
| { | |||||
| auto guard = sr::scope_guard([&calls] { ++calls; }); | |||||
| guard.release(); | |||||
| } | |||||
| REQUIRE_CALL(m, deleter()).TIMES(0); | |||||
| auto guard = sr::scope_guard(deleter); | |||||
| guard.release(); | |||||
| } | |||||
| REQUIRE(calls == 0); | |||||
| TEST_CASE("deleter lambda called on destruction", "[ScopeGuard]") | |||||
| { | |||||
| mock::CallMock cm; | |||||
| REQUIRE_CALL(cm, deleter()); | |||||
| auto guard = sr::scope_guard([&cm] { cm.deleter(); }); | |||||
| static_cast<void>(guard); | |||||
| } | } | ||||
| TEST_CASE("move releases moved-from object", "[ScopeGuard]") | TEST_CASE("move releases moved-from object", "[ScopeGuard]") | ||||
| { | { | ||||
| std::size_t calls{0}; | |||||
| { | |||||
| auto movedFrom = sr::scope_guard([&calls] { ++calls; }); | |||||
| auto guard = std::move(movedFrom); | |||||
| static_cast<void>(guard); | |||||
| } | |||||
| REQUIRE(calls == 1); | |||||
| REQUIRE_CALL(m, deleter()); | |||||
| auto movedFrom = sr::scope_guard(deleter); | |||||
| auto guard = std::move(movedFrom); | |||||
| static_cast<void>(guard); | |||||
| } | } | ||||
| TEST_CASE("move transfers state", "[ScopeGuard]") | TEST_CASE("move transfers state", "[ScopeGuard]") | ||||
| { | { | ||||
| std::size_t calls{0}; | |||||
| { | |||||
| auto movedFrom = sr::scope_guard([&calls] { ++calls; }); | |||||
| auto guard = std::move(movedFrom); | |||||
| static_cast<void>(guard); | |||||
| } | |||||
| REQUIRE(calls == 1); | |||||
| REQUIRE_CALL(m, deleter()); | |||||
| auto movedFrom = sr::scope_guard(deleter); | |||||
| auto guard = std::move(movedFrom); | |||||
| static_cast<void>(guard); | |||||
| } | } | ||||
| TEST_CASE("move transfers state if released", "[ScopeGuard]") | TEST_CASE("move transfers state if released", "[ScopeGuard]") | ||||
| { | { | ||||
| std::size_t calls{0}; | |||||
| { | |||||
| auto movedFrom = sr::scope_guard([&calls] { ++calls; }); | |||||
| movedFrom.release(); | |||||
| auto guard = std::move(movedFrom); | |||||
| static_cast<void>(guard); | |||||
| } | |||||
| REQUIRE(calls == 0); | |||||
| REQUIRE_CALL(m, deleter()).TIMES(0); | |||||
| auto movedFrom = sr::scope_guard(deleter); | |||||
| movedFrom.release(); | |||||
| auto guard = std::move(movedFrom); | |||||
| static_cast<void>(guard); | |||||
| } | } | ||||
| TEST_CASE("no exception propagation from deleter", "[ScopeGuard]") | TEST_CASE("no exception propagation from deleter", "[ScopeGuard]") | ||||
| { | { | ||||
| REQUIRE_NOTHROW([] { | REQUIRE_NOTHROW([] { | ||||
| auto guard = sr::scope_guard([] { throw "Don't propagate this!"; }); | |||||
| auto guard = sr::scope_guard(deleterThrow); | |||||
| static_cast<void>(guard); | static_cast<void>(guard); | ||||
| }()); | }()); | ||||
| } | } |