proxygen
ScopeGuardTest.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2011-present 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 
19 #include <glog/logging.h>
20 
21 #include <functional>
22 #include <stdexcept>
23 
25 
26 using folly::makeGuard;
27 using std::vector;
28 
29 double returnsDouble() {
30  return 0.0;
31 }
32 
33 class MyFunctor {
34  public:
35  explicit MyFunctor(int* ptr) : ptr_(ptr) {}
36 
37  void operator()() {
38  ++*ptr_;
39  }
40 
41  private:
42  int* ptr_;
43 };
44 
45 TEST(ScopeGuard, DifferentWaysToBind) {
46  {
47  // There is implicit conversion from func pointer
48  // double (*)() to function<void()>.
49  auto g = makeGuard(returnsDouble);
50  (void)g;
51  }
52 
53  vector<int> v;
54  void (vector<int>::*push_back)(int const&) = &vector<int>::push_back;
55 
56  v.push_back(1);
57  {
58  // binding to member function.
59  auto g = makeGuard(std::bind(&vector<int>::pop_back, &v));
60  (void)g;
61  }
62  EXPECT_EQ(0, v.size());
63 
64  {
65  // bind member function with args. v is passed-by-value!
66  auto g = makeGuard(std::bind(push_back, v, 2));
67  (void)g;
68  }
69  EXPECT_EQ(0, v.size()); // push_back happened on a copy of v... fail!
70 
71  // pass in an argument by pointer so to avoid copy.
72  {
73  auto g = makeGuard(std::bind(push_back, &v, 4));
74  (void)g;
75  }
76  EXPECT_EQ(1, v.size());
77 
78  {
79  // pass in an argument by reference so to avoid copy.
80  auto g = makeGuard(std::bind(push_back, std::ref(v), 4));
81  (void)g;
82  }
83  EXPECT_EQ(2, v.size());
84 
85  // lambda with a reference to v
86  {
87  auto g = makeGuard([&] { v.push_back(5); });
88  (void)g;
89  }
90  EXPECT_EQ(3, v.size());
91 
92  // lambda with a copy of v
93  {
94  auto g = makeGuard([v]() mutable { v.push_back(6); });
95  (void)g;
96  }
97  EXPECT_EQ(3, v.size());
98 
99  // functor object
100  int n = 0;
101  {
102  MyFunctor f(&n);
103  auto g = makeGuard(f);
104  (void)g;
105  }
106  EXPECT_EQ(1, n);
107 
108  // temporary functor object
109  n = 0;
110  {
111  auto g = makeGuard(MyFunctor(&n));
112  (void)g;
113  }
114  EXPECT_EQ(1, n);
115 
116  // Use auto instead of ScopeGuard
117  n = 2;
118  {
119  auto g = makeGuard(MyFunctor(&n));
120  (void)g;
121  }
122  EXPECT_EQ(3, n);
123 
124  // Use const auto& instead of ScopeGuard
125  n = 10;
126  {
127  const auto& g = makeGuard(MyFunctor(&n));
128  (void)g;
129  }
130  EXPECT_EQ(11, n);
131 }
132 
133 TEST(ScopeGuard, GuardException) {
134  EXPECT_DEATH(
135  (void)makeGuard(
136  [] { throw std::runtime_error("dtors should never throw!"); }),
137  "dtors should never throw!");
138 }
139 
145 void testUndoAction(bool failure) {
146  vector<int64_t> v;
147  { // defines a "mini" scope
148 
149  // be optimistic and insert this into memory
150  v.push_back(1);
151 
152  // The guard is triggered to undo the insertion unless dismiss() is called.
153  auto guard = makeGuard([&] { v.pop_back(); });
154 
155  // Do some action; Use the failure argument to pretend
156  // if it failed or succeeded.
157 
158  // if there was no failure, dismiss the undo guard action.
159  if (!failure) {
160  guard.dismiss();
161  }
162  } // all stack allocated in the mini-scope will be destroyed here.
163 
164  if (failure) {
165  EXPECT_EQ(0, v.size()); // the action failed => undo insertion
166  } else {
167  EXPECT_EQ(1, v.size()); // the action succeeded => keep insertion
168  }
169 }
170 
171 TEST(ScopeGuard, UndoAction) {
172  testUndoAction(true);
173  testUndoAction(false);
174 }
175 
195 enum class ErrorBehavior {
196  SUCCESS,
199 };
200 
202  bool cleanupOccurred = false;
203 
204  try {
205  auto guard = makeGuard([&] { cleanupOccurred = true; });
206  (void)guard;
207 
208  try {
209  if (error == ErrorBehavior::HANDLED_ERROR) {
210  throw std::runtime_error("throwing an expected error");
211  } else if (error == ErrorBehavior::UNHANDLED_ERROR) {
212  throw "never throw raw strings";
213  }
214  } catch (const std::runtime_error&) {
215  }
216  } catch (...) {
217  // Outer catch to swallow the error for the UNHANDLED_ERROR behavior
218  }
219 
220  EXPECT_TRUE(cleanupOccurred);
221 }
222 
223 TEST(ScopeGuard, TryCatchFinally) {
227 }
228 
229 TEST(ScopeGuard, TEST_SCOPE_EXIT) {
230  int x = 0;
231  {
232  SCOPE_EXIT {
233  ++x;
234  };
235  EXPECT_EQ(0, x);
236  }
237  EXPECT_EQ(1, x);
238 }
239 
240 class Foo {
241  public:
242  Foo() {}
243  ~Foo() {
244  try {
245  auto e = std::current_exception();
246  int test = 0;
247  {
248  SCOPE_EXIT {
249  ++test;
250  };
251  EXPECT_EQ(0, test);
252  }
253  EXPECT_EQ(1, test);
254  } catch (const std::exception& ex) {
255  LOG(FATAL) << "Unexpected exception: " << ex.what();
256  }
257  }
258 };
259 
260 TEST(ScopeGuard, TEST_SCOPE_FAILURE2) {
261  try {
262  Foo f;
263  throw std::runtime_error("test");
264  } catch (...) {
265  }
266 }
267 
269  bool scopeFailExecuted = false;
270  bool scopeSuccessExecuted = false;
271 
272  try {
273  SCOPE_FAIL {
274  scopeFailExecuted = true;
275  };
276  SCOPE_SUCCESS {
277  scopeSuccessExecuted = true;
278  };
279 
280  try {
281  if (error == ErrorBehavior::HANDLED_ERROR) {
282  throw std::runtime_error("throwing an expected error");
283  } else if (error == ErrorBehavior::UNHANDLED_ERROR) {
284  throw "never throw raw strings";
285  }
286  } catch (const std::runtime_error&) {
287  }
288  } catch (...) {
289  // Outer catch to swallow the error for the UNHANDLED_ERROR behavior
290  }
291 
292  EXPECT_EQ(expectFail, scopeFailExecuted);
293  EXPECT_EQ(!expectFail, scopeSuccessExecuted);
294 }
295 
296 TEST(ScopeGuard, TEST_SCOPE_FAIL_AND_SCOPE_SUCCESS) {
300 }
301 
302 TEST(ScopeGuard, TEST_SCOPE_SUCCESS_THROW) {
303  auto lambda = []() {
304  SCOPE_SUCCESS {
305  throw std::runtime_error("ehm");
306  };
307  };
308  EXPECT_THROW(lambda(), std::runtime_error);
309 }
310 
311 TEST(ScopeGuard, TEST_THROWING_CLEANUP_ACTION) {
312  struct ThrowingCleanupAction {
313  // clang-format off
314  explicit ThrowingCleanupAction(int& scopeExitExecuted)
315  : scopeExitExecuted_(scopeExitExecuted) {}
316  [[noreturn]] ThrowingCleanupAction(const ThrowingCleanupAction& other)
317  : scopeExitExecuted_(other.scopeExitExecuted_) {
318  throw std::runtime_error("whoa");
319  }
320  // clang-format on
321  void operator()() {
322  ++scopeExitExecuted_;
323  }
324 
325  private:
326  int& scopeExitExecuted_;
327  };
328  int scopeExitExecuted = 0;
329  ThrowingCleanupAction onExit(scopeExitExecuted);
330  EXPECT_THROW((void)makeGuard(onExit), std::runtime_error);
331  EXPECT_EQ(scopeExitExecuted, 1);
332 }
Definition: InvokeTest.cpp:58
void * ptr
auto f
auto v
#define EXPECT_THROW(statement, expected_exception)
Definition: gtest.h:1843
void testScopeFailAndScopeSuccess(ErrorBehavior error, bool expectFail)
#define EXPECT_EQ(val1, val2)
Definition: gtest.h:1922
static uint64_t test(std::string name, bool fc_, bool dedicated_, bool tc_, bool syncops_, uint64_t base)
const int x
#define SCOPE_EXIT
Definition: ScopeGuard.h:274
TEST(ScopeGuard, DifferentWaysToBind)
MyFunctor(int *ptr)
requires And< SemiMovable< VN >... > &&SemiMovable< E > auto error(E e)
Definition: error.h:48
void BENCHFUN() push_back(size_t iters, size_t arg)
void testFinally(ErrorBehavior error)
GuardImpl guard(ErrorHandler &&handler)
Definition: Base.h:840
void operator()()
#define EXPECT_TRUE(condition)
Definition: gtest.h:1859
FOLLY_NODISCARD detail::ScopeGuardImplDecay< F, true > makeGuard(F &&f) noexcept(noexcept(detail::ScopeGuardImplDecay< F, true >(static_cast< F && >(f))))
Definition: ScopeGuard.h:184
void testUndoAction(bool failure)
int bind(NetworkSocket s, const sockaddr *name, socklen_t namelen)
Definition: NetOps.cpp:76
g_t g(f_t)
double returnsDouble()
ErrorBehavior