proxygen
ScopeGuardTest.cpp File Reference
#include <folly/ScopeGuard.h>
#include <glog/logging.h>
#include <functional>
#include <stdexcept>
#include <folly/portability/GTest.h>

Go to the source code of this file.

Classes

class  MyFunctor
 
class  Foo
 

Enumerations

enum  ErrorBehavior { ErrorBehavior::SUCCESS, ErrorBehavior::HANDLED_ERROR, ErrorBehavior::UNHANDLED_ERROR }
 

Functions

double returnsDouble ()
 
 TEST (ScopeGuard, DifferentWaysToBind)
 
 TEST (ScopeGuard, GuardException)
 
void testUndoAction (bool failure)
 
 TEST (ScopeGuard, UndoAction)
 
void testFinally (ErrorBehavior error)
 
 TEST (ScopeGuard, TryCatchFinally)
 
 TEST (ScopeGuard, TEST_SCOPE_EXIT)
 
 TEST (ScopeGuard, TEST_SCOPE_FAILURE2)
 
void testScopeFailAndScopeSuccess (ErrorBehavior error, bool expectFail)
 
 TEST (ScopeGuard, TEST_SCOPE_FAIL_AND_SCOPE_SUCCESS)
 
 TEST (ScopeGuard, TEST_SCOPE_SUCCESS_THROW)
 
 TEST (ScopeGuard, TEST_THROWING_CLEANUP_ACTION)
 

Enumeration Type Documentation

enum ErrorBehavior
strong

Sometimes in a try catch block we want to execute a piece of code regardless if an exception happened or not. For example, you want to close a db connection regardless if an exception was thrown during insertion. In Java and other languages there is a finally clause that helps accomplish this:

try { dbConn.doInsert(sql); } catch (const DbException& dbe) { dbConn.recordFailure(dbe); } catch (const CriticalException& e) { throw e; // re-throw the exception } finally { dbConn.closeConnection(); // executes no matter what! }

We can approximate this behavior in C++ with ScopeGuard.

Enumerator
SUCCESS 
HANDLED_ERROR 
UNHANDLED_ERROR 

Definition at line 195 of file ScopeGuardTest.cpp.

Function Documentation

double returnsDouble ( )

Definition at line 29 of file ScopeGuardTest.cpp.

Referenced by TEST().

29  {
30  return 0.0;
31 }
TEST ( ScopeGuard  ,
DifferentWaysToBind   
)

Definition at line 45 of file ScopeGuardTest.cpp.

References folly::netops::bind(), EXPECT_EQ, f, g(), folly::makeGuard(), MyFunctor::MyFunctor(), push_back(), returnsDouble(), and v.

45  {
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 }
auto f
auto v
#define EXPECT_EQ(val1, val2)
Definition: gtest.h:1922
void BENCHFUN() push_back(size_t iters, size_t arg)
FOLLY_NODISCARD detail::ScopeGuardImplDecay< F, true > makeGuard(F &&f) noexcept(noexcept(detail::ScopeGuardImplDecay< F, true >(static_cast< F && >(f))))
Definition: ScopeGuard.h:184
int bind(NetworkSocket s, const sockaddr *name, socklen_t namelen)
Definition: NetOps.cpp:76
g_t g(f_t)
double returnsDouble()
TEST ( ScopeGuard  ,
GuardException   
)

Definition at line 133 of file ScopeGuardTest.cpp.

References folly::makeGuard().

133  {
134  EXPECT_DEATH(
135  (void)makeGuard(
136  [] { throw std::runtime_error("dtors should never throw!"); }),
137  "dtors should never throw!");
138 }
FOLLY_NODISCARD detail::ScopeGuardImplDecay< F, true > makeGuard(F &&f) noexcept(noexcept(detail::ScopeGuardImplDecay< F, true >(static_cast< F && >(f))))
Definition: ScopeGuard.h:184
TEST ( ScopeGuard  ,
UndoAction   
)

Definition at line 171 of file ScopeGuardTest.cpp.

References testUndoAction().

171  {
172  testUndoAction(true);
173  testUndoAction(false);
174 }
void testUndoAction(bool failure)
TEST ( ScopeGuard  ,
TEST_SCOPE_EXIT   
)

Definition at line 229 of file ScopeGuardTest.cpp.

References EXPECT_EQ, SCOPE_EXIT, and x.

229  {
230  int x = 0;
231  {
232  SCOPE_EXIT {
233  ++x;
234  };
235  EXPECT_EQ(0, x);
236  }
237  EXPECT_EQ(1, x);
238 }
Definition: InvokeTest.cpp:58
#define EXPECT_EQ(val1, val2)
Definition: gtest.h:1922
const int x
#define SCOPE_EXIT
Definition: ScopeGuard.h:274
TEST ( ScopeGuard  ,
TEST_SCOPE_FAILURE2   
)

Definition at line 260 of file ScopeGuardTest.cpp.

References f.

260  {
261  try {
262  Foo f;
263  throw std::runtime_error("test");
264  } catch (...) {
265  }
266 }
auto f
TEST ( ScopeGuard  ,
TEST_SCOPE_FAIL_AND_SCOPE_SUCCESS   
)
TEST ( ScopeGuard  ,
TEST_SCOPE_SUCCESS_THROW   
)

Definition at line 302 of file ScopeGuardTest.cpp.

References EXPECT_THROW.

302  {
303  auto lambda = []() {
304  SCOPE_SUCCESS {
305  throw std::runtime_error("ehm");
306  };
307  };
308  EXPECT_THROW(lambda(), std::runtime_error);
309 }
#define EXPECT_THROW(statement, expected_exception)
Definition: gtest.h:1843
TEST ( ScopeGuard  ,
TEST_THROWING_CLEANUP_ACTION   
)

Definition at line 311 of file ScopeGuardTest.cpp.

References EXPECT_EQ, EXPECT_THROW, folly::makeGuard(), and MyFunctor::operator()().

311  {
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 }
#define EXPECT_THROW(statement, expected_exception)
Definition: gtest.h:1843
#define EXPECT_EQ(val1, val2)
Definition: gtest.h:1922
FOLLY_NODISCARD detail::ScopeGuardImplDecay< F, true > makeGuard(F &&f) noexcept(noexcept(detail::ScopeGuardImplDecay< F, true >(static_cast< F && >(f))))
Definition: ScopeGuard.h:184
void testFinally ( ErrorBehavior  error)

Definition at line 201 of file ScopeGuardTest.cpp.

References EXPECT_TRUE, folly::gen::guard(), HANDLED_ERROR, folly::makeGuard(), and UNHANDLED_ERROR.

Referenced by TEST().

201  {
202  bool cleanupOccurred = false;
203 
204  try {
205  auto guard = makeGuard([&] { cleanupOccurred = true; });
206  (void)guard;
207 
208  try {
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 }
requires And< SemiMovable< VN >... > &&SemiMovable< E > auto error(E e)
Definition: error.h:48
GuardImpl guard(ErrorHandler &&handler)
Definition: Base.h:840
#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 testScopeFailAndScopeSuccess ( ErrorBehavior  error,
bool  expectFail 
)

Definition at line 268 of file ScopeGuardTest.cpp.

References EXPECT_EQ, HANDLED_ERROR, and UNHANDLED_ERROR.

Referenced by TEST().

268  {
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 {
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 }
#define EXPECT_EQ(val1, val2)
Definition: gtest.h:1922
requires And< SemiMovable< VN >... > &&SemiMovable< E > auto error(E e)
Definition: error.h:48
void testUndoAction ( bool  failure)

Add an integer to a vector iff it was inserted into the db successfuly. Here is a schematic of how you would accomplish this with scope guard.

Definition at line 145 of file ScopeGuardTest.cpp.

References EXPECT_EQ, folly::gen::guard(), folly::makeGuard(), and v.

Referenced by TEST().

145  {
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 }
auto v
#define EXPECT_EQ(val1, val2)
Definition: gtest.h:1922
GuardImpl guard(ErrorHandler &&handler)
Definition: Base.h:840
FOLLY_NODISCARD detail::ScopeGuardImplDecay< F, true > makeGuard(F &&f) noexcept(noexcept(detail::ScopeGuardImplDecay< F, true >(static_cast< F && >(f))))
Definition: ScopeGuard.h:184