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