2 * Copyright 2012 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 <gflags/gflags.h>
20 #include <gtest/gtest.h>
25 using folly::ScopeGuard;
26 using folly::makeGuard;
29 double returnsDouble() {
35 explicit MyFunctor(int* ptr) : ptr_(ptr) {}
45 TEST(ScopeGuard, DifferentWaysToBind) {
47 // There is implicit conversion from func pointer
48 // double (*)() to function<void()>.
49 ScopeGuard g = makeGuard(returnsDouble);
53 void (vector<int>::*push_back)(int const&) = &vector<int>::push_back;
57 // binding to member function.
58 ScopeGuard g = makeGuard(std::bind(&vector<int>::pop_back, &v));
60 EXPECT_EQ(0, v.size());
63 // bind member function with args. v is passed-by-value!
64 ScopeGuard g = makeGuard(std::bind(push_back, v, 2));
66 EXPECT_EQ(0, v.size()); // push_back happened on a copy of v... fail!
68 // pass in an argument by pointer so to avoid copy.
70 ScopeGuard g = makeGuard(std::bind(push_back, &v, 4));
72 EXPECT_EQ(1, v.size());
75 // pass in an argument by reference so to avoid copy.
76 ScopeGuard g = makeGuard(std::bind(push_back, std::ref(v), 4));
78 EXPECT_EQ(2, v.size());
80 // lambda with a reference to v
82 ScopeGuard g = makeGuard([&] { v.push_back(5); });
84 EXPECT_EQ(3, v.size());
86 // lambda with a copy of v
88 ScopeGuard g = makeGuard([v] () mutable { v.push_back(6); });
90 EXPECT_EQ(3, v.size());
96 ScopeGuard g = makeGuard(f);
100 // temporary functor object
103 ScopeGuard g = makeGuard(MyFunctor(&n));
107 // Use auto instead of ScopeGuard
110 auto g = makeGuard(MyFunctor(&n));
114 // Use const auto& instead of ScopeGuard
117 const auto& g = makeGuard(MyFunctor(&n));
122 TEST(ScopeGuard, GuardException) {
124 ScopeGuard g = makeGuard([&] {
125 throw std::runtime_error("destructors should never throw!");
128 "destructors should never throw");
132 * Add an integer to a vector iff it was inserted into the
133 * db successfuly. Here is a schematic of how you would accomplish
134 * this with scope guard.
136 void testUndoAction(bool failure) {
138 { // defines a "mini" scope
140 // be optimistic and insert this into memory
143 // The guard is triggered to undo the insertion unless dismiss() is called.
144 ScopeGuard guard = makeGuard([&] { v.pop_back(); });
146 // Do some action; Use the failure argument to pretend
147 // if it failed or succeeded.
149 // if there was no failure, dismiss the undo guard action.
153 } // all stack allocated in the mini-scope will be destroyed here.
156 EXPECT_EQ(0, v.size()); // the action failed => undo insertion
158 EXPECT_EQ(1, v.size()); // the action succeeded => keep insertion
162 TEST(ScopeGuard, UndoAction) {
163 testUndoAction(true);
164 testUndoAction(false);
168 * Sometimes in a try catch block we want to execute a piece of code
169 * regardless if an exception happened or not. For example, you want
170 * to close a db connection regardless if an exception was thrown during
171 * insertion. In Java and other languages there is a finally clause that
172 * helps accomplish this:
175 * dbConn.doInsert(sql);
176 * } catch (const DbException& dbe) {
177 * dbConn.recordFailure(dbe);
178 * } catch (const CriticalException& e) {
179 * throw e; // re-throw the exception
181 * dbConn.closeConnection(); // executes no matter what!
184 * We can approximate this behavior in C++ with ScopeGuard.
186 enum class ErrorBehavior {
192 void testFinally(ErrorBehavior error) {
193 bool cleanupOccurred = false;
196 ScopeGuard guard = makeGuard([&] { cleanupOccurred = true; });
199 if (error == ErrorBehavior::HANDLED_ERROR) {
200 throw std::runtime_error("throwing an expected error");
201 } else if (error == ErrorBehavior::UNHANDLED_ERROR) {
202 throw "never throw raw strings";
204 } catch (const std::runtime_error&) {
207 // Outer catch to swallow the error for the UNHANDLED_ERROR behavior
210 EXPECT_TRUE(cleanupOccurred);
213 TEST(ScopeGuard, TryCatchFinally) {
214 testFinally(ErrorBehavior::SUCCESS);
215 testFinally(ErrorBehavior::HANDLED_ERROR);
216 testFinally(ErrorBehavior::UNHANDLED_ERROR);
219 TEST(ScopeGuard, TEST_SCOPE_EXIT) {
233 auto e = std::current_exception();
236 SCOPE_EXIT { ++test; };
240 } catch (const std::exception& ex) {
241 LOG(FATAL) << "Unexpected exception: " << ex.what();
246 TEST(ScopeGuard, TEST_SCOPE_FAILURE2) {
249 throw std::runtime_error("test");
254 int main(int argc, char** argv) {
255 testing::InitGoogleTest(&argc, argv);
256 google::ParseCommandLineFlags(&argc, &argv, true);
257 return RUN_ALL_TESTS();