9f4c76ed4b77ccbca0d17597a20851e82f4498ce
[folly.git] / folly / test / ExceptionWrapperTest.cpp
1 /*
2  * Copyright 2017 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 <stdexcept>
18
19 #include <folly/ExceptionWrapper.h>
20 #include <folly/Conv.h>
21 #include <folly/portability/GTest.h>
22
23 using namespace folly;
24
25 class AbstractIntException : public std::exception {
26  public:
27   virtual int getInt() const = 0;
28 };
29
30 class IntException : public AbstractIntException {
31  public:
32   explicit IntException(int i) : i_(i) {}
33
34   int getInt() const override { return i_; }
35   const char* what() const noexcept override {
36     what_ = folly::to<std::string>("int == ", i_);
37     return what_.c_str();
38   }
39
40  private:
41   int i_;
42   mutable std::string what_;
43 };
44
45 const static std::string kExceptionClassName =
46     demangle(typeid(std::exception)).toStdString();
47 const static std::string kRuntimeErrorClassName =
48     demangle(typeid(std::runtime_error)).toStdString();
49 const static std::string kIntExceptionClassName =
50     demangle(typeid(IntException)).toStdString();
51 const static std::string kIntClassName = demangle(typeid(int)).toStdString();
52
53 // Tests that when we call throwException, the proper type is thrown (derived)
54 TEST(ExceptionWrapper, throw_test) {
55   std::runtime_error e("payload");
56   auto ew = make_exception_wrapper<std::runtime_error>(e);
57
58   std::vector<exception_wrapper> container;
59   container.push_back(ew);
60
61   try {
62     container[0].throwException();
63   } catch (std::runtime_error& err) {
64     std::string expected = "payload";
65     std::string actual = err.what();
66     EXPECT_EQ(expected, actual);
67   }
68 }
69
70 TEST(ExceptionWrapper, members) {
71   auto ew = exception_wrapper();
72   EXPECT_FALSE(bool(ew));
73   EXPECT_EQ(ew.what(), "");
74   EXPECT_EQ(ew.class_name(), "");
75   ew = make_exception_wrapper<std::runtime_error>("payload");
76   EXPECT_TRUE(bool(ew));
77   EXPECT_EQ(ew.what(), kRuntimeErrorClassName + ": payload");
78   EXPECT_EQ(ew.class_name(), kRuntimeErrorClassName);
79 }
80
81 TEST(ExceptionWrapper, equals) {
82   std::runtime_error e("payload");
83   auto ew1 = make_exception_wrapper<std::runtime_error>(e);
84   auto ew2 = ew1;
85   EXPECT_EQ(ew1, ew2);
86
87   auto ew3 = try_and_catch<std::exception>([&]() {
88     throw std::runtime_error("payload");
89   });
90   auto ew4 = try_and_catch<std::exception>([&]() {
91     ew3.throwException();
92   });
93   EXPECT_EQ(ew3, ew4);
94 }
95
96 TEST(ExceptionWrapper, not_equals) {
97   std::runtime_error e1("payload");
98   std::runtime_error e2("payload");
99   auto ew1 = make_exception_wrapper<std::runtime_error>(e1);
100   auto ew2 = make_exception_wrapper<std::runtime_error>(e2);
101   EXPECT_NE(ew1, ew2);
102
103   auto ew3 = make_exception_wrapper<std::runtime_error>(e1);
104   auto ew4 = make_exception_wrapper<std::runtime_error>(e1);
105   EXPECT_NE(ew3, ew4);
106
107   auto ew5 = try_and_catch<std::exception>([&]() {
108     throw e1;
109   });
110   auto ew6 = try_and_catch<std::exception>([&]() {
111     throw e1;
112   });
113   EXPECT_NE(ew5, ew6);
114 }
115
116 TEST(ExceptionWrapper, try_and_catch_test) {
117   std::string expected = "payload";
118
119   // Catch rightmost matching exception type
120   exception_wrapper ew = try_and_catch<std::exception, std::runtime_error>(
121     [=]() {
122       throw std::runtime_error(expected);
123     });
124   EXPECT_TRUE(bool(ew));
125   EXPECT_TRUE(ew.getCopied());
126   EXPECT_EQ(ew.what(), kRuntimeErrorClassName + ": payload");
127   EXPECT_EQ(ew.class_name(), kRuntimeErrorClassName);
128   auto rep = ew.is_compatible_with<std::runtime_error>();
129   EXPECT_TRUE(rep);
130
131   // Changing order is like catching in wrong order. Beware of this in your
132   // code.
133   auto ew2 = try_and_catch<std::runtime_error, std::exception>([=]() {
134     throw std::runtime_error(expected);
135   });
136   EXPECT_TRUE(bool(ew2));
137   // We are catching a std::exception, not std::runtime_error.
138   EXPECT_FALSE(ew2.getCopied());
139   // But, we can still get the actual type if we want it.
140   rep = ew2.is_compatible_with<std::runtime_error>();
141   EXPECT_TRUE(rep);
142
143   // Catches even if not rightmost.
144   auto ew3 = try_and_catch<std::exception, std::runtime_error>([]() {
145     throw std::exception();
146   });
147   EXPECT_TRUE(bool(ew3));
148   EXPECT_EQ(ew3.what(), kExceptionClassName + ": std::exception");
149   EXPECT_EQ(ew3.class_name(), kExceptionClassName);
150   rep = ew3.is_compatible_with<std::runtime_error>();
151   EXPECT_FALSE(rep);
152
153   // If does not catch, throws.
154   EXPECT_THROW(
155     try_and_catch<std::runtime_error>([]() {
156       throw std::exception();
157     }),
158     std::exception);
159 }
160
161 TEST(ExceptionWrapper, with_exception_test) {
162   int expected = 23;
163
164   // This works, and doesn't slice.
165   exception_wrapper ew = try_and_catch<std::exception, std::runtime_error>(
166     [=]() {
167       throw IntException(expected);
168     });
169   EXPECT_TRUE(bool(ew));
170   EXPECT_EQ(ew.what(), kIntExceptionClassName + ": int == 23");
171   EXPECT_EQ(ew.class_name(), kIntExceptionClassName);
172   EXPECT_TRUE(ew.with_exception(
173       [&](const IntException& ie) { EXPECT_EQ(ie.getInt(), expected); }));
174
175   // I can try_and_catch a non-copyable base class.  This will use
176   // std::exception_ptr internally.
177   exception_wrapper ew2 = try_and_catch<AbstractIntException>(
178     [=]() {
179       throw IntException(expected);
180     });
181   EXPECT_TRUE(bool(ew2));
182   EXPECT_EQ(ew2.what(), kIntExceptionClassName + ": int == 23");
183   EXPECT_EQ(ew2.class_name(), kIntExceptionClassName);
184   EXPECT_TRUE(ew2.with_exception([&](AbstractIntException& ie) {
185     EXPECT_EQ(ie.getInt(), expected);
186     EXPECT_TRUE(dynamic_cast<IntException*>(&ie));
187   }));
188
189   // Test with const this.  If this compiles and does not crash due to
190   // infinite loop when it runs, it succeeds.
191   const exception_wrapper& cew = ew;
192   EXPECT_TRUE(
193       cew.with_exception([&](const IntException& /* ie */) { SUCCEED(); }));
194
195   // Test with empty ew.
196   exception_wrapper empty_ew;
197   EXPECT_FALSE(
198       empty_ew.with_exception([&](const std::exception& /* ie */) { FAIL(); }));
199
200   // Testing with const exception_wrapper; sanity check first:
201   EXPECT_FALSE(cew.with_exception([&](const std::runtime_error&) {}));
202   EXPECT_FALSE(cew.with_exception([&](const int&) {}));
203   // This won't even compile.  You can't use a function which takes a
204   // non-const reference with a const exception_wrapper.
205   /*
206   EXPECT_FALSE(cew.with_exception([&](std::runtime_error&) {}));
207   EXPECT_FALSE(cew.with_exception([&](int&) {}));
208   */
209 }
210
211 TEST(ExceptionWrapper, getExceptionPtr_test) {
212   int expected = 23;
213
214   // This works, and doesn't slice.
215   exception_wrapper ew = try_and_catch<std::exception, std::runtime_error>(
216       [=]() { throw IntException(expected); });
217   std::exception_ptr eptr = ew.getExceptionPtr();
218   EXPECT_THROW(std::rethrow_exception(eptr), IntException);
219
220   // I can try_and_catch a non-copyable base class.  This will use
221   // std::exception_ptr internally.
222   exception_wrapper ew2 = try_and_catch<AbstractIntException>(
223       [=]() { throw IntException(expected); });
224   eptr = ew2.getExceptionPtr();
225   EXPECT_THROW(std::rethrow_exception(eptr), IntException);
226
227   // Test with const this.
228   const exception_wrapper& cew = ew;
229   eptr = cew.getExceptionPtr();
230   EXPECT_THROW(std::rethrow_exception(eptr), IntException);
231
232   // Test with empty ew.
233   exception_wrapper empty_ew;
234   eptr = empty_ew.getExceptionPtr();
235   EXPECT_FALSE(eptr);
236 }
237
238 TEST(ExceptionWrapper, with_exception_deduction) {
239   auto ew = make_exception_wrapper<std::runtime_error>("hi");
240   EXPECT_TRUE(ew.with_exception([](std::runtime_error&) {}));
241   EXPECT_TRUE(ew.with_exception([](std::exception&) {}));
242   EXPECT_FALSE(ew.with_exception([](std::logic_error&) {}));
243 }
244
245 TEST(ExceptionWrapper, with_exception_deduction_exn_const) {
246   auto ew = make_exception_wrapper<std::runtime_error>("hi");
247   EXPECT_TRUE(ew.with_exception([](const std::runtime_error&) {}));
248   EXPECT_TRUE(ew.with_exception([](const std::exception&) {}));
249   EXPECT_FALSE(ew.with_exception([](const std::logic_error&) {}));
250 }
251
252 TEST(ExceptionWrapper, with_exception_deduction_wrap_const_exn_const) {
253   const auto cew = make_exception_wrapper<std::runtime_error>("hi");
254   EXPECT_TRUE(cew.with_exception([](const std::runtime_error&) {}));
255   EXPECT_TRUE(cew.with_exception([](const std::exception&) {}));
256   EXPECT_FALSE(cew.with_exception([](const std::logic_error&) {}));
257 }
258
259 TEST(ExceptionWrapper, with_exception_deduction_returning) {
260   auto ew = make_exception_wrapper<std::runtime_error>("hi");
261   EXPECT_TRUE(ew.with_exception([](std::runtime_error&) { return 3; }));
262   EXPECT_TRUE(ew.with_exception([](std::exception&) { return "hello"; }));
263   EXPECT_FALSE(ew.with_exception([](std::logic_error&) { return nullptr; }));
264 }
265
266 namespace {
267 template <typename T>
268 T& r_to_l(T v) { return std::ref(v); }
269 }
270
271 TEST(ExceptionWrapper, with_exception_deduction_functor_lvalue) {
272   auto ew = make_exception_wrapper<std::runtime_error>("hi");
273   EXPECT_TRUE(ew.with_exception(r_to_l([](std::runtime_error&) {})));
274   EXPECT_TRUE(ew.with_exception(r_to_l([](std::exception&) {})));
275   EXPECT_FALSE(ew.with_exception(r_to_l([](std::logic_error&) {})));
276 }
277
278 TEST(ExceptionWrapper, non_std_exception_test) {
279   int expected = 17;
280
281   exception_wrapper ew = try_and_catch<std::exception, int>(
282     [=]() {
283       throw expected;
284     });
285   EXPECT_TRUE(bool(ew));
286   EXPECT_FALSE(ew.is_compatible_with<std::exception>());
287   EXPECT_TRUE(ew.is_compatible_with<int>());
288   EXPECT_EQ(ew.what(), kIntClassName);
289   EXPECT_EQ(ew.class_name(), kIntClassName);
290   // non-std::exception types are supported, but the only way to
291   // access their value is to explicity rethrow and catch it.
292   try {
293     ew.throwException();
294   } catch /* nolint */ (int& i) {
295     EXPECT_EQ(i, expected);
296   }
297 }
298
299
300 TEST(ExceptionWrapper, exceptionStr) {
301   auto ew = make_exception_wrapper<std::runtime_error>("argh");
302   EXPECT_EQ(kRuntimeErrorClassName + ": argh", exceptionStr(ew));
303 }
304
305 TEST(ExceptionWrapper, throwException_noException) {
306   exception_wrapper ew;
307   ASSERT_DEATH(ew.throwException(), "empty folly::exception_wrapper");
308 }
309
310 namespace {
311 class TestException : public std::exception { };
312 void testEW(const exception_wrapper& ew) {
313   EXPECT_THROW(ew.throwException(), TestException);
314 }
315 }  // namespace
316
317 TEST(ExceptionWrapper, implicitConstruction) {
318   // Try with both lvalue and rvalue references
319   TestException e;
320   testEW(e);
321   testEW(TestException());
322 }