2 * Copyright 2017 Facebook, Inc.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 #include <folly/ScopeGuard.h>
19 #include <glog/logging.h>
24 #include <folly/portability/GTest.h>
26 using folly::ScopeGuard;
27 using folly::makeGuard;
30 double returnsDouble() {
36 explicit MyFunctor(int* ptr) : ptr_(ptr) {}
46 TEST(ScopeGuard, DifferentWaysToBind) {
48 // There is implicit conversion from func pointer
49 // double (*)() to function<void()>.
50 ScopeGuard g = makeGuard(returnsDouble);
54 void (vector<int>::*push_back)(int const&) = &vector<int>::push_back;
58 // binding to member function.
59 ScopeGuard g = makeGuard(std::bind(&vector<int>::pop_back, &v));
61 EXPECT_EQ(0, v.size());
64 // bind member function with args. v is passed-by-value!
65 ScopeGuard g = makeGuard(std::bind(push_back, v, 2));
67 EXPECT_EQ(0, v.size()); // push_back happened on a copy of v... fail!
69 // pass in an argument by pointer so to avoid copy.
71 ScopeGuard g = makeGuard(std::bind(push_back, &v, 4));
73 EXPECT_EQ(1, v.size());
76 // pass in an argument by reference so to avoid copy.
77 ScopeGuard g = makeGuard(std::bind(push_back, std::ref(v), 4));
79 EXPECT_EQ(2, v.size());
81 // lambda with a reference to v
83 ScopeGuard g = makeGuard([&] { v.push_back(5); });
85 EXPECT_EQ(3, v.size());
87 // lambda with a copy of v
89 ScopeGuard g = makeGuard([v] () mutable { v.push_back(6); });
91 EXPECT_EQ(3, v.size());
97 ScopeGuard g = makeGuard(f);
101 // temporary functor object
104 ScopeGuard g = makeGuard(MyFunctor(&n));
108 // Use auto instead of ScopeGuard
111 auto g = makeGuard(MyFunctor(&n));
115 // Use const auto& instead of ScopeGuard
118 const auto& g = makeGuard(MyFunctor(&n));
123 TEST(ScopeGuard, GuardException) {
125 ScopeGuard g = makeGuard([&] {
126 throw std::runtime_error("destructors should never throw!");
129 "destructors should never throw!"
134 * Add an integer to a vector iff it was inserted into the
135 * db successfuly. Here is a schematic of how you would accomplish
136 * this with scope guard.
138 void testUndoAction(bool failure) {
140 { // defines a "mini" scope
142 // be optimistic and insert this into memory
145 // The guard is triggered to undo the insertion unless dismiss() is called.
146 ScopeGuard guard = makeGuard([&] { v.pop_back(); });
148 // Do some action; Use the failure argument to pretend
149 // if it failed or succeeded.
151 // if there was no failure, dismiss the undo guard action.
155 } // all stack allocated in the mini-scope will be destroyed here.
158 EXPECT_EQ(0, v.size()); // the action failed => undo insertion
160 EXPECT_EQ(1, v.size()); // the action succeeded => keep insertion
164 TEST(ScopeGuard, UndoAction) {
165 testUndoAction(true);
166 testUndoAction(false);
170 * Sometimes in a try catch block we want to execute a piece of code
171 * regardless if an exception happened or not. For example, you want
172 * to close a db connection regardless if an exception was thrown during
173 * insertion. In Java and other languages there is a finally clause that
174 * helps accomplish this:
177 * dbConn.doInsert(sql);
178 * } catch (const DbException& dbe) {
179 * dbConn.recordFailure(dbe);
180 * } catch (const CriticalException& e) {
181 * throw e; // re-throw the exception
183 * dbConn.closeConnection(); // executes no matter what!
186 * We can approximate this behavior in C++ with ScopeGuard.
188 enum class ErrorBehavior {
194 void testFinally(ErrorBehavior error) {
195 bool cleanupOccurred = false;
198 ScopeGuard guard = makeGuard([&] { cleanupOccurred = true; });
201 if (error == ErrorBehavior::HANDLED_ERROR) {
202 throw std::runtime_error("throwing an expected error");
203 } else if (error == ErrorBehavior::UNHANDLED_ERROR) {
204 throw "never throw raw strings";
206 } catch (const std::runtime_error&) {
209 // Outer catch to swallow the error for the UNHANDLED_ERROR behavior
212 EXPECT_TRUE(cleanupOccurred);
215 TEST(ScopeGuard, TryCatchFinally) {
216 testFinally(ErrorBehavior::SUCCESS);
217 testFinally(ErrorBehavior::HANDLED_ERROR);
218 testFinally(ErrorBehavior::UNHANDLED_ERROR);
221 TEST(ScopeGuard, TEST_SCOPE_EXIT) {
235 auto e = std::current_exception();
238 SCOPE_EXIT { ++test; };
242 } catch (const std::exception& ex) {
243 LOG(FATAL) << "Unexpected exception: " << ex.what();
248 TEST(ScopeGuard, TEST_SCOPE_FAILURE2) {
251 throw std::runtime_error("test");
256 void testScopeFailAndScopeSuccess(ErrorBehavior error, bool expectFail) {
257 bool scopeFailExecuted = false;
258 bool scopeSuccessExecuted = false;
261 SCOPE_FAIL { scopeFailExecuted = true; };
262 SCOPE_SUCCESS { scopeSuccessExecuted = true; };
265 if (error == ErrorBehavior::HANDLED_ERROR) {
266 throw std::runtime_error("throwing an expected error");
267 } else if (error == ErrorBehavior::UNHANDLED_ERROR) {
268 throw "never throw raw strings";
270 } catch (const std::runtime_error&) {
273 // Outer catch to swallow the error for the UNHANDLED_ERROR behavior
276 EXPECT_EQ(expectFail, scopeFailExecuted);
277 EXPECT_EQ(!expectFail, scopeSuccessExecuted);
280 TEST(ScopeGuard, TEST_SCOPE_FAIL_AND_SCOPE_SUCCESS) {
281 testScopeFailAndScopeSuccess(ErrorBehavior::SUCCESS, false);
282 testScopeFailAndScopeSuccess(ErrorBehavior::HANDLED_ERROR, false);
283 testScopeFailAndScopeSuccess(ErrorBehavior::UNHANDLED_ERROR, true);
286 TEST(ScopeGuard, TEST_SCOPE_SUCCESS_THROW) {
288 SCOPE_SUCCESS { throw std::runtime_error("ehm"); };
290 EXPECT_THROW(lambda(), std::runtime_error);
293 TEST(ScopeGuard, TEST_THROWING_CLEANUP_ACTION) {
294 struct ThrowingCleanupAction {
295 explicit ThrowingCleanupAction(int& scopeExitExecuted)
296 : scopeExitExecuted_(scopeExitExecuted) {}
298 ThrowingCleanupAction(const ThrowingCleanupAction& other)
299 : scopeExitExecuted_(other.scopeExitExecuted_) {
300 throw std::runtime_error("whoa");
302 void operator()() { ++scopeExitExecuted_; }
305 int& scopeExitExecuted_;
307 int scopeExitExecuted = 0;
308 ThrowingCleanupAction onExit(scopeExitExecuted);
309 EXPECT_THROW(makeGuard(onExit), std::runtime_error);
310 EXPECT_EQ(scopeExitExecuted, 1);