folly copyright 2015 -> copyright 2016
[folly.git] / folly / test / ScopeGuardTest.cpp
1 /*
2  * Copyright 2016 Facebook, Inc.
3  *
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
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 #include <folly/ScopeGuard.h>
18 #include <folly/Portability.h>
19
20 #include <gflags/gflags.h>
21 #include <gtest/gtest.h>
22 #include <glog/logging.h>
23
24 #include <functional>
25 #include <stdexcept>
26
27 using folly::ScopeGuard;
28 using folly::makeGuard;
29 using std::vector;
30
31 double returnsDouble() {
32   return 0.0;
33 }
34
35 class MyFunctor {
36  public:
37   explicit MyFunctor(int* ptr) : ptr_(ptr) {}
38
39   void operator()() {
40     ++*ptr_;
41   }
42
43  private:
44   int* ptr_;
45 };
46
47 TEST(ScopeGuard, DifferentWaysToBind) {
48   {
49     // There is implicit conversion from func pointer
50     // double (*)() to function<void()>.
51     ScopeGuard g = makeGuard(returnsDouble);
52   }
53
54   vector<int> v;
55   void (vector<int>::*push_back)(int const&) = &vector<int>::push_back;
56
57   v.push_back(1);
58   {
59     // binding to member function.
60     ScopeGuard g = makeGuard(std::bind(&vector<int>::pop_back, &v));
61   }
62   EXPECT_EQ(0, v.size());
63
64   {
65     // bind member function with args. v is passed-by-value!
66     ScopeGuard g = makeGuard(std::bind(push_back, v, 2));
67   }
68   EXPECT_EQ(0, v.size()); // push_back happened on a copy of v... fail!
69
70   // pass in an argument by pointer so to avoid copy.
71   {
72     ScopeGuard g = makeGuard(std::bind(push_back, &v, 4));
73   }
74   EXPECT_EQ(1, v.size());
75
76   {
77     // pass in an argument by reference so to avoid copy.
78     ScopeGuard g = makeGuard(std::bind(push_back, std::ref(v), 4));
79   }
80   EXPECT_EQ(2, v.size());
81
82   // lambda with a reference to v
83   {
84     ScopeGuard g = makeGuard([&] { v.push_back(5); });
85   }
86   EXPECT_EQ(3, v.size());
87
88   // lambda with a copy of v
89   {
90     ScopeGuard g = makeGuard([v] () mutable { v.push_back(6); });
91   }
92   EXPECT_EQ(3, v.size());
93
94   // functor object
95   int n = 0;
96   {
97     MyFunctor f(&n);
98     ScopeGuard g = makeGuard(f);
99   }
100   EXPECT_EQ(1, n);
101
102   // temporary functor object
103   n = 0;
104   {
105     ScopeGuard g = makeGuard(MyFunctor(&n));
106   }
107   EXPECT_EQ(1, n);
108
109   // Use auto instead of ScopeGuard
110   n = 2;
111   {
112     auto g = makeGuard(MyFunctor(&n));
113   }
114   EXPECT_EQ(3, n);
115
116   // Use const auto& instead of ScopeGuard
117   n = 10;
118   {
119     const auto& g = makeGuard(MyFunctor(&n));
120   }
121   EXPECT_EQ(11, n);
122 }
123
124 TEST(ScopeGuard, GuardException) {
125   EXPECT_DEATH({
126     ScopeGuard g = makeGuard([&] {
127       throw std::runtime_error("destructors should never throw!");
128     });
129   },
130   "destructors should never throw!"
131   );
132 }
133
134 /**
135  * Add an integer to a vector iff it was inserted into the
136  * db successfuly. Here is a schematic of how you would accomplish
137  * this with scope guard.
138  */
139 void testUndoAction(bool failure) {
140   vector<int64_t> v;
141   { // defines a "mini" scope
142
143     // be optimistic and insert this into memory
144     v.push_back(1);
145
146     // The guard is triggered to undo the insertion unless dismiss() is called.
147     ScopeGuard guard = makeGuard([&] { v.pop_back(); });
148
149     // Do some action; Use the failure argument to pretend
150     // if it failed or succeeded.
151
152     // if there was no failure, dismiss the undo guard action.
153     if (!failure) {
154       guard.dismiss();
155     }
156   } // all stack allocated in the mini-scope will be destroyed here.
157
158   if (failure) {
159     EXPECT_EQ(0, v.size()); // the action failed => undo insertion
160   } else {
161     EXPECT_EQ(1, v.size()); // the action succeeded => keep insertion
162   }
163 }
164
165 TEST(ScopeGuard, UndoAction) {
166   testUndoAction(true);
167   testUndoAction(false);
168 }
169
170 /**
171  * Sometimes in a try catch block we want to execute a piece of code
172  * regardless if an exception happened or not. For example, you want
173  * to close a db connection regardless if an exception was thrown during
174  * insertion. In Java and other languages there is a finally clause that
175  * helps accomplish this:
176  *
177  *   try {
178  *     dbConn.doInsert(sql);
179  *   } catch (const DbException& dbe) {
180  *     dbConn.recordFailure(dbe);
181  *   } catch (const CriticalException& e) {
182  *     throw e; // re-throw the exception
183  *   } finally {
184  *     dbConn.closeConnection(); // executes no matter what!
185  *   }
186  *
187  * We can approximate this behavior in C++ with ScopeGuard.
188  */
189 enum class ErrorBehavior {
190   SUCCESS,
191   HANDLED_ERROR,
192   UNHANDLED_ERROR,
193 };
194
195 void testFinally(ErrorBehavior error) {
196   bool cleanupOccurred = false;
197
198   try {
199     ScopeGuard guard = makeGuard([&] { cleanupOccurred = true; });
200
201     try {
202       if (error == ErrorBehavior::HANDLED_ERROR) {
203         throw std::runtime_error("throwing an expected error");
204       } else if (error == ErrorBehavior::UNHANDLED_ERROR) {
205         throw "never throw raw strings";
206       }
207     } catch (const std::runtime_error&) {
208     }
209   } catch (...) {
210     // Outer catch to swallow the error for the UNHANDLED_ERROR behavior
211   }
212
213   EXPECT_TRUE(cleanupOccurred);
214 }
215
216 TEST(ScopeGuard, TryCatchFinally) {
217   testFinally(ErrorBehavior::SUCCESS);
218   testFinally(ErrorBehavior::HANDLED_ERROR);
219   testFinally(ErrorBehavior::UNHANDLED_ERROR);
220 }
221
222 TEST(ScopeGuard, TEST_SCOPE_EXIT) {
223   int x = 0;
224   {
225     SCOPE_EXIT { ++x; };
226     EXPECT_EQ(0, x);
227   }
228   EXPECT_EQ(1, x);
229 }
230
231 class Foo {
232 public:
233   Foo() {}
234   ~Foo() {
235     try {
236       auto e = std::current_exception();
237       int test = 0;
238       {
239         SCOPE_EXIT { ++test; };
240         EXPECT_EQ(0, test);
241       }
242       EXPECT_EQ(1, test);
243     } catch (const std::exception& ex) {
244       LOG(FATAL) << "Unexpected exception: " << ex.what();
245     }
246   }
247 };
248
249 TEST(ScopeGuard, TEST_SCOPE_FAILURE2) {
250   try {
251     Foo f;
252     throw std::runtime_error("test");
253   } catch (...) {
254   }
255 }
256
257 void testScopeFailAndScopeSuccess(ErrorBehavior error, bool expectFail) {
258   bool scopeFailExecuted = false;
259   bool scopeSuccessExecuted = false;
260
261   try {
262     SCOPE_FAIL { scopeFailExecuted = true; };
263     SCOPE_SUCCESS { scopeSuccessExecuted = true; };
264
265     try {
266       if (error == ErrorBehavior::HANDLED_ERROR) {
267         throw std::runtime_error("throwing an expected error");
268       } else if (error == ErrorBehavior::UNHANDLED_ERROR) {
269         throw "never throw raw strings";
270       }
271     } catch (const std::runtime_error&) {
272     }
273   } catch (...) {
274     // Outer catch to swallow the error for the UNHANDLED_ERROR behavior
275   }
276
277   EXPECT_EQ(expectFail, scopeFailExecuted);
278   EXPECT_EQ(!expectFail, scopeSuccessExecuted);
279 }
280
281 TEST(ScopeGuard, TEST_SCOPE_FAIL_AND_SCOPE_SUCCESS) {
282   testScopeFailAndScopeSuccess(ErrorBehavior::SUCCESS, false);
283   testScopeFailAndScopeSuccess(ErrorBehavior::HANDLED_ERROR, false);
284   testScopeFailAndScopeSuccess(ErrorBehavior::UNHANDLED_ERROR, true);
285 }
286
287 TEST(ScopeGuard, TEST_SCOPE_SUCCESS_THROW) {
288   auto lambda = []() {
289     SCOPE_SUCCESS { throw std::runtime_error("ehm"); };
290   };
291   EXPECT_THROW(lambda(), std::runtime_error);
292 }
293
294 int main(int argc, char** argv) {
295   testing::InitGoogleTest(&argc, argv);
296   gflags::ParseCommandLineFlags(&argc, &argv, true);
297   return RUN_ALL_TESTS();
298 }