1 | /* |
2 | * Copyright 2014-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 <stdexcept> |
18 | |
19 | #include <folly/Conv.h> |
20 | #include <folly/ExceptionWrapper.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), what_(to<std::string>("int == " , i_)) {} |
33 | |
34 | int getInt() const override { |
35 | return i_; |
36 | } |
37 | const char* what() const noexcept override { |
38 | return what_.c_str(); |
39 | } |
40 | |
41 | private: |
42 | int i_; |
43 | 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 | template <typename T> |
55 | T& from_eptr(std::exception_ptr& eptr) { |
56 | try { |
57 | std::rethrow_exception(eptr); |
58 | } catch (T& e) { |
59 | return e; |
60 | } catch (...) { |
61 | throw std::logic_error("impossible" ); |
62 | } |
63 | } |
64 | |
65 | TEST(ExceptionWrapper, nothrow) { |
66 | EXPECT_TRUE(std::is_nothrow_default_constructible<exception_wrapper>::value); |
67 | EXPECT_TRUE(std::is_nothrow_move_constructible<exception_wrapper>::value); |
68 | EXPECT_TRUE(std::is_nothrow_move_assignable<exception_wrapper>::value); |
69 | EXPECT_TRUE(std::is_nothrow_copy_constructible<exception_wrapper>::value); |
70 | EXPECT_TRUE(std::is_nothrow_copy_assignable<exception_wrapper>::value); |
71 | } |
72 | |
73 | // Tests that when we call throw_exception, the proper type is thrown (derived) |
74 | TEST(ExceptionWrapper, throw_test) { |
75 | std::runtime_error e("payload" ); |
76 | auto ew = make_exception_wrapper<std::runtime_error>(e); |
77 | |
78 | std::vector<exception_wrapper> container; |
79 | container.push_back(ew); |
80 | |
81 | try { |
82 | container[0].throw_exception(); |
83 | } catch (std::runtime_error& err) { |
84 | std::string expected = "payload" ; |
85 | std::string actual = err.what(); |
86 | EXPECT_EQ(expected, actual); |
87 | } |
88 | } |
89 | |
90 | // Tests that when we call throw_with_nested, we can unnest it later. |
91 | TEST(ExceptionWrapper, throw_with_nested) { |
92 | auto ew = make_exception_wrapper<std::runtime_error>("inner" ); |
93 | try { |
94 | ew.throw_with_nested(std::runtime_error("outer" )); |
95 | ADD_FAILURE(); |
96 | } catch (std::runtime_error& outer) { |
97 | EXPECT_STREQ(outer.what(), "outer" ); |
98 | try { |
99 | std::rethrow_if_nested(outer); |
100 | ADD_FAILURE(); |
101 | } catch (std::runtime_error& inner) { |
102 | EXPECT_STREQ(inner.what(), "inner" ); |
103 | } |
104 | } |
105 | } |
106 | |
107 | TEST(ExceptionWrapper, members) { |
108 | auto ew = exception_wrapper(); |
109 | EXPECT_FALSE(bool(ew)); |
110 | EXPECT_EQ(ew.what(), "" ); |
111 | EXPECT_EQ(ew.class_name(), "" ); |
112 | ew = make_exception_wrapper<std::runtime_error>("payload" ); |
113 | EXPECT_TRUE(bool(ew)); |
114 | EXPECT_EQ(ew.what(), kRuntimeErrorClassName + ": payload" ); |
115 | EXPECT_EQ(ew.class_name(), kRuntimeErrorClassName); |
116 | } |
117 | |
118 | TEST(ExceptionWrapper, try_and_catch_test) { |
119 | std::string expected = "payload" ; |
120 | |
121 | // Catch rightmost matching exception type |
122 | exception_wrapper ew = try_and_catch<std::exception, std::runtime_error>( |
123 | [=]() { throw std::runtime_error(expected); }); |
124 | EXPECT_TRUE(bool(ew)); |
125 | EXPECT_EQ(ew.what(), kRuntimeErrorClassName + ": payload" ); |
126 | EXPECT_EQ(ew.class_name(), kRuntimeErrorClassName); |
127 | auto rep = ew.is_compatible_with<std::runtime_error>(); |
128 | EXPECT_TRUE(rep); |
129 | |
130 | // Changing order is like catching in wrong order. Beware of this in your |
131 | // code. |
132 | auto ew2 = try_and_catch<std::runtime_error, std::exception>( |
133 | [=]() { throw std::runtime_error(expected); }); |
134 | EXPECT_TRUE(bool(ew2)); |
135 | // We are catching a std::exception, not std::runtime_error. |
136 | // But, we can still get the actual type if we want it. |
137 | rep = ew2.is_compatible_with<std::runtime_error>(); |
138 | EXPECT_TRUE(rep); |
139 | |
140 | // Catches even if not rightmost. |
141 | auto ew3 = try_and_catch<std::exception, std::runtime_error>( |
142 | []() { throw std::exception(); }); |
143 | EXPECT_TRUE(bool(ew3)); |
144 | EXPECT_EQ(ew3.what(), kExceptionClassName + ": std::exception" ); |
145 | EXPECT_EQ(ew3.class_name(), kExceptionClassName); |
146 | rep = ew3.is_compatible_with<std::runtime_error>(); |
147 | EXPECT_FALSE(rep); |
148 | |
149 | // If does not catch, throws. |
150 | EXPECT_THROW( |
151 | try_and_catch<std::runtime_error>([]() { throw std::exception(); }), |
152 | std::exception); |
153 | } |
154 | |
155 | TEST(ExceptionWrapper, with_exception_test) { |
156 | int expected = 23; |
157 | |
158 | // This works, and doesn't slice. |
159 | exception_wrapper ew = try_and_catch<std::exception, std::runtime_error>( |
160 | [=]() { throw IntException(expected); }); |
161 | EXPECT_TRUE(bool(ew)); |
162 | EXPECT_EQ(ew.what(), kIntExceptionClassName + ": int == 23" ); |
163 | EXPECT_EQ(ew.class_name(), kIntExceptionClassName); |
164 | EXPECT_TRUE(ew.with_exception( |
165 | [&](const IntException& ie) { EXPECT_EQ(ie.getInt(), expected); })); |
166 | |
167 | // I can try_and_catch a non-copyable base class. This will use |
168 | // std::exception_ptr internally. |
169 | exception_wrapper ew2 = try_and_catch<AbstractIntException>( |
170 | [=]() { throw IntException(expected); }); |
171 | EXPECT_TRUE(bool(ew2)); |
172 | EXPECT_EQ(ew2.what(), kIntExceptionClassName + ": int == 23" ); |
173 | EXPECT_EQ(ew2.class_name(), kIntExceptionClassName); |
174 | bool res = ew2.with_exception([&](AbstractIntException& ie) { |
175 | EXPECT_EQ(ie.getInt(), expected); |
176 | EXPECT_TRUE(dynamic_cast<IntException*>(&ie)); |
177 | }); |
178 | EXPECT_TRUE(res); |
179 | |
180 | // Test with const this. If this compiles and does not crash due to |
181 | // infinite loop when it runs, it succeeds. |
182 | const exception_wrapper& cew = ew; |
183 | EXPECT_TRUE( |
184 | cew.with_exception([&](const IntException& /* ie */) { SUCCEED(); })); |
185 | |
186 | // Test with empty ew. |
187 | exception_wrapper empty_ew; |
188 | EXPECT_FALSE( |
189 | empty_ew.with_exception([&](const std::exception& /* ie */) { FAIL(); })); |
190 | |
191 | // Testing with const exception_wrapper; sanity check first: |
192 | EXPECT_FALSE(cew.with_exception([&](const std::runtime_error&) {})); |
193 | EXPECT_FALSE(cew.with_exception([&](const int&) {})); |
194 | // This won't even compile. You can't use a function which takes a |
195 | // non-const reference with a const exception_wrapper. |
196 | /* |
197 | EXPECT_FALSE(cew.with_exception([&](std::runtime_error&) {})); |
198 | EXPECT_FALSE(cew.with_exception([&](int&) {})); |
199 | */ |
200 | } |
201 | |
202 | TEST(ExceptionWrapper, get_or_make_exception_ptr_test) { |
203 | int expected = 23; |
204 | |
205 | // This works, and doesn't slice. |
206 | exception_wrapper ew = try_and_catch<std::exception, std::runtime_error>( |
207 | [=]() { throw IntException(expected); }); |
208 | std::exception_ptr eptr = ew.to_exception_ptr(); |
209 | EXPECT_THROW(std::rethrow_exception(eptr), IntException); |
210 | |
211 | // I can try_and_catch a non-copyable base class. This will use |
212 | // std::exception_ptr internally. |
213 | exception_wrapper ew2 = try_and_catch<AbstractIntException>( |
214 | [=]() { throw IntException(expected); }); |
215 | eptr = ew2.to_exception_ptr(); |
216 | EXPECT_THROW(std::rethrow_exception(eptr), IntException); |
217 | |
218 | // Test with const this. |
219 | const exception_wrapper& cew = ew; |
220 | eptr = cew.to_exception_ptr(); |
221 | EXPECT_THROW(std::rethrow_exception(eptr), IntException); |
222 | |
223 | // Test with empty ew. |
224 | exception_wrapper empty_ew; |
225 | eptr = empty_ew.to_exception_ptr(); |
226 | EXPECT_FALSE(eptr); |
227 | } |
228 | |
229 | TEST(ExceptionWrapper, from_exception_ptr_empty) { |
230 | auto ep = std::exception_ptr(); |
231 | auto ew = exception_wrapper::from_exception_ptr(ep); |
232 | EXPECT_FALSE(bool(ew)); |
233 | } |
234 | |
235 | TEST(ExceptionWrapper, from_exception_ptr_exn) { |
236 | auto ep = std::make_exception_ptr(std::runtime_error("foo" )); |
237 | auto ew = exception_wrapper::from_exception_ptr(ep); |
238 | EXPECT_TRUE(bool(ew)); |
239 | EXPECT_EQ(ep, ew.to_exception_ptr()); |
240 | EXPECT_TRUE(ew.is_compatible_with<std::runtime_error>()); |
241 | } |
242 | |
243 | TEST(ExceptionWrapper, from_exception_ptr_any) { |
244 | auto ep = std::make_exception_ptr<int>(12); |
245 | auto ew = exception_wrapper::from_exception_ptr(ep); |
246 | EXPECT_TRUE(bool(ew)); |
247 | EXPECT_EQ(ep, ew.to_exception_ptr()); |
248 | EXPECT_TRUE(ew.is_compatible_with<int>()); |
249 | } |
250 | |
251 | TEST(ExceptionWrapper, with_exception_ptr_empty) { |
252 | auto ew = exception_wrapper(std::exception_ptr()); |
253 | EXPECT_EQ(exception_wrapper::none(), ew.type()); |
254 | EXPECT_FALSE(bool(ew)); |
255 | EXPECT_EQ(nullptr, ew.get_exception()); |
256 | EXPECT_EQ(nullptr, ew.get_exception<std::exception>()); |
257 | EXPECT_EQ(nullptr, ew.get_exception<int>()); |
258 | EXPECT_FALSE(ew.has_exception_ptr()); |
259 | EXPECT_EQ(nullptr, ew.to_exception_ptr()); |
260 | EXPECT_FALSE(ew.has_exception_ptr()); |
261 | EXPECT_EQ("" , ew.class_name()); |
262 | EXPECT_EQ("" , ew.what()); |
263 | EXPECT_FALSE(ew.is_compatible_with<std::exception>()); |
264 | EXPECT_FALSE(ew.is_compatible_with<int>()); |
265 | EXPECT_DEATH(ew.throw_exception(), "empty folly::exception_wrapper" ); |
266 | } |
267 | |
268 | TEST(ExceptionWrapper, with_shared_ptr_test) { |
269 | auto ew = exception_wrapper(std::runtime_error("foo" )); |
270 | EXPECT_TRUE(bool(ew)); |
271 | EXPECT_EQ(typeid(std::runtime_error), ew.type()); |
272 | EXPECT_NE(nullptr, ew.get_exception()); |
273 | EXPECT_NE(nullptr, ew.get_exception<std::exception>()); |
274 | EXPECT_STREQ("foo" , ew.get_exception<std::exception>()->what()); |
275 | EXPECT_EQ(nullptr, ew.get_exception<int>()); |
276 | EXPECT_FALSE(ew.has_exception_ptr()); |
277 | EXPECT_NE(nullptr, ew.to_exception_ptr()); |
278 | EXPECT_TRUE(ew.has_exception_ptr()); |
279 | EXPECT_EQ(kRuntimeErrorClassName, ew.class_name()); |
280 | EXPECT_EQ(kRuntimeErrorClassName + ": foo" , ew.what()); |
281 | EXPECT_TRUE(ew.is_compatible_with<std::exception>()); |
282 | EXPECT_TRUE(ew.is_compatible_with<std::runtime_error>()); |
283 | EXPECT_FALSE(ew.is_compatible_with<int>()); |
284 | EXPECT_THROW(ew.throw_exception(), std::runtime_error); |
285 | |
286 | exception_wrapper(std::move(ew)); |
287 | EXPECT_FALSE(bool(ew)); |
288 | EXPECT_EQ(exception_wrapper::none(), ew.type()); |
289 | EXPECT_EQ(nullptr, ew.get_exception()); |
290 | EXPECT_EQ(nullptr, ew.get_exception<std::exception>()); |
291 | EXPECT_EQ(nullptr, ew.get_exception<int>()); |
292 | EXPECT_EQ(nullptr, ew.to_exception_ptr()); |
293 | EXPECT_EQ("" , ew.class_name()); |
294 | EXPECT_EQ("" , ew.what()); |
295 | EXPECT_FALSE(ew.is_compatible_with<std::exception>()); |
296 | EXPECT_FALSE(ew.is_compatible_with<std::runtime_error>()); |
297 | EXPECT_FALSE(ew.is_compatible_with<int>()); |
298 | } |
299 | |
300 | TEST(ExceptionWrapper, with_exception_ptr_exn_test) { |
301 | auto ep = std::make_exception_ptr(std::runtime_error("foo" )); |
302 | auto ew = exception_wrapper(ep, from_eptr<std::runtime_error>(ep)); |
303 | EXPECT_TRUE(bool(ew)); |
304 | EXPECT_EQ(typeid(std::runtime_error), ew.type()); |
305 | EXPECT_NE(nullptr, ew.get_exception()); |
306 | EXPECT_NE(nullptr, ew.get_exception<std::exception>()); |
307 | EXPECT_STREQ("foo" , ew.get_exception<std::exception>()->what()); |
308 | EXPECT_EQ(nullptr, ew.get_exception<int>()); |
309 | EXPECT_TRUE(ew.has_exception_ptr()); |
310 | EXPECT_EQ(ep, ew.to_exception_ptr()); |
311 | EXPECT_TRUE(ew.has_exception_ptr()); |
312 | EXPECT_EQ(kRuntimeErrorClassName, ew.class_name()); |
313 | EXPECT_EQ(kRuntimeErrorClassName + ": foo" , ew.what()); |
314 | EXPECT_TRUE(ew.is_compatible_with<std::exception>()); |
315 | EXPECT_TRUE(ew.is_compatible_with<std::runtime_error>()); |
316 | EXPECT_FALSE(ew.is_compatible_with<int>()); |
317 | EXPECT_THROW(ew.throw_exception(), std::runtime_error); |
318 | |
319 | exception_wrapper(std::move(ew)); |
320 | EXPECT_FALSE(bool(ew)); |
321 | EXPECT_EQ(exception_wrapper::none(), ew.type()); |
322 | EXPECT_EQ(nullptr, ew.get_exception()); |
323 | EXPECT_EQ(nullptr, ew.get_exception<std::exception>()); |
324 | EXPECT_EQ(nullptr, ew.get_exception<int>()); |
325 | EXPECT_EQ(nullptr, ew.to_exception_ptr()); |
326 | EXPECT_EQ("" , ew.class_name()); |
327 | EXPECT_EQ("" , ew.what()); |
328 | EXPECT_FALSE(ew.is_compatible_with<std::exception>()); |
329 | EXPECT_FALSE(ew.is_compatible_with<std::runtime_error>()); |
330 | EXPECT_FALSE(ew.is_compatible_with<int>()); |
331 | } |
332 | |
333 | TEST(ExceptionWrapper, with_exception_ptr_any_test) { |
334 | auto ep = std::make_exception_ptr<int>(12); |
335 | auto ew = exception_wrapper(ep, from_eptr<int>(ep)); |
336 | EXPECT_TRUE(bool(ew)); |
337 | EXPECT_EQ(nullptr, ew.get_exception()); |
338 | EXPECT_EQ(nullptr, ew.get_exception<std::exception>()); |
339 | EXPECT_NE(nullptr, ew.get_exception<int>()); |
340 | EXPECT_EQ(12, *ew.get_exception<int>()); |
341 | EXPECT_TRUE(ew.has_exception_ptr()); |
342 | EXPECT_EQ(ep, ew.to_exception_ptr()); |
343 | EXPECT_TRUE(ew.has_exception_ptr()); |
344 | EXPECT_EQ(demangle(typeid(int)), ew.class_name()); |
345 | EXPECT_EQ(demangle(typeid(int)), ew.what()); |
346 | EXPECT_FALSE(ew.is_compatible_with<std::exception>()); |
347 | EXPECT_FALSE(ew.is_compatible_with<std::runtime_error>()); |
348 | EXPECT_TRUE(ew.is_compatible_with<int>()); |
349 | EXPECT_THROW(ew.throw_exception(), int); |
350 | |
351 | exception_wrapper(std::move(ew)); |
352 | EXPECT_FALSE(bool(ew)); |
353 | EXPECT_EQ(nullptr, ew.get_exception()); |
354 | EXPECT_EQ(nullptr, ew.get_exception<std::exception>()); |
355 | EXPECT_EQ(nullptr, ew.get_exception<int>()); |
356 | EXPECT_EQ(nullptr, ew.to_exception_ptr()); |
357 | EXPECT_FALSE(ew.has_exception_ptr()); |
358 | EXPECT_EQ("" , ew.class_name()); |
359 | EXPECT_EQ("" , ew.what()); |
360 | EXPECT_FALSE(ew.is_compatible_with<std::exception>()); |
361 | EXPECT_FALSE(ew.is_compatible_with<std::runtime_error>()); |
362 | EXPECT_FALSE(ew.is_compatible_with<int>()); |
363 | } |
364 | |
365 | TEST(ExceptionWrapper, with_non_std_exception_test) { |
366 | auto ew = exception_wrapper(folly::in_place, 42); |
367 | EXPECT_TRUE(bool(ew)); |
368 | EXPECT_EQ(nullptr, ew.get_exception()); |
369 | EXPECT_EQ(nullptr, ew.get_exception<std::exception>()); |
370 | EXPECT_NE(nullptr, ew.get_exception<int>()); |
371 | EXPECT_EQ(42, *ew.get_exception<int>()); |
372 | EXPECT_TRUE(ew.has_exception_ptr()); |
373 | EXPECT_EQ(demangle(typeid(int)), ew.class_name()); |
374 | EXPECT_EQ(demangle(typeid(int)), ew.what()); |
375 | EXPECT_NE(nullptr, ew.to_exception_ptr()); |
376 | EXPECT_TRUE(ew.has_exception_ptr()); |
377 | EXPECT_EQ(demangle(typeid(int)), ew.class_name()); |
378 | EXPECT_EQ(demangle(typeid(int)), ew.what()); |
379 | EXPECT_FALSE(ew.is_compatible_with<std::exception>()); |
380 | EXPECT_FALSE(ew.is_compatible_with<std::runtime_error>()); |
381 | EXPECT_TRUE(ew.is_compatible_with<int>()); |
382 | EXPECT_THROW(ew.throw_exception(), int); |
383 | |
384 | exception_wrapper(std::move(ew)); |
385 | EXPECT_FALSE(bool(ew)); |
386 | EXPECT_EQ(nullptr, ew.get_exception()); |
387 | EXPECT_EQ(nullptr, ew.get_exception<std::exception>()); |
388 | EXPECT_EQ(nullptr, ew.get_exception<int>()); |
389 | EXPECT_EQ(nullptr, ew.to_exception_ptr()); |
390 | EXPECT_FALSE(ew.has_exception_ptr()); |
391 | EXPECT_EQ("" , ew.class_name()); |
392 | EXPECT_EQ("" , ew.what()); |
393 | EXPECT_FALSE(ew.is_compatible_with<std::exception>()); |
394 | EXPECT_FALSE(ew.is_compatible_with<std::runtime_error>()); |
395 | EXPECT_FALSE(ew.is_compatible_with<int>()); |
396 | } |
397 | |
398 | TEST(ExceptionWrapper, with_exception_ptr_any_nil_test) { |
399 | auto ep = std::make_exception_ptr<int>(12); |
400 | auto ew = exception_wrapper(ep); // concrete type is erased |
401 | EXPECT_TRUE(bool(ew)); |
402 | EXPECT_EQ(nullptr, ew.get_exception()); |
403 | EXPECT_EQ(nullptr, ew.get_exception<std::exception>()); |
404 | EXPECT_NE(nullptr, ew.get_exception<int>()); |
405 | EXPECT_EQ(12, *ew.get_exception<int>()); |
406 | EXPECT_EQ(ep, ew.to_exception_ptr()); |
407 | EXPECT_EQ("<unknown exception>" , ew.class_name()); // because concrete type is |
408 | // erased |
409 | EXPECT_EQ("<unknown exception>" , ew.what()); |
410 | EXPECT_FALSE(ew.is_compatible_with<std::exception>()); |
411 | EXPECT_FALSE(ew.is_compatible_with<std::runtime_error>()); |
412 | EXPECT_TRUE(ew.is_compatible_with<int>()); |
413 | EXPECT_THROW(ew.throw_exception(), int); |
414 | |
415 | exception_wrapper(std::move(ew)); |
416 | EXPECT_FALSE(bool(ew)); |
417 | EXPECT_EQ(nullptr, ew.get_exception()); |
418 | EXPECT_EQ(nullptr, ew.get_exception<std::exception>()); |
419 | EXPECT_EQ(nullptr, ew.get_exception<int>()); |
420 | EXPECT_EQ(nullptr, ew.to_exception_ptr()); |
421 | EXPECT_EQ("" , ew.class_name()); |
422 | EXPECT_EQ("" , ew.what()); |
423 | EXPECT_FALSE(ew.is_compatible_with<std::exception>()); |
424 | EXPECT_FALSE(ew.is_compatible_with<std::runtime_error>()); |
425 | EXPECT_FALSE(ew.is_compatible_with<int>()); |
426 | } |
427 | |
428 | TEST(ExceptionWrapper, with_exception_deduction) { |
429 | auto ew = make_exception_wrapper<std::runtime_error>("hi" ); |
430 | EXPECT_TRUE(ew.with_exception([](std::runtime_error&) {})); |
431 | EXPECT_TRUE(ew.with_exception([](std::exception&) {})); |
432 | EXPECT_FALSE(ew.with_exception([](std::logic_error&) {})); |
433 | } |
434 | |
435 | TEST(ExceptionWrapper, with_exception_deduction_exn_const) { |
436 | auto ew = make_exception_wrapper<std::runtime_error>("hi" ); |
437 | EXPECT_TRUE(ew.with_exception([](const std::runtime_error&) {})); |
438 | EXPECT_TRUE(ew.with_exception([](const std::exception&) {})); |
439 | EXPECT_FALSE(ew.with_exception([](const std::logic_error&) {})); |
440 | } |
441 | |
442 | TEST(ExceptionWrapper, with_exception_deduction_wrap_const_exn_const) { |
443 | const auto cew = make_exception_wrapper<std::runtime_error>("hi" ); |
444 | EXPECT_TRUE(cew.with_exception([](const std::runtime_error&) {})); |
445 | EXPECT_TRUE(cew.with_exception([](const std::exception&) {})); |
446 | EXPECT_FALSE(cew.with_exception([](const std::logic_error&) {})); |
447 | } |
448 | |
449 | TEST(ExceptionWrapper, with_exception_deduction_returning) { |
450 | auto ew = make_exception_wrapper<std::runtime_error>("hi" ); |
451 | EXPECT_TRUE(ew.with_exception([](std::runtime_error&) { return 3; })); |
452 | EXPECT_TRUE(ew.with_exception([](std::exception&) { return "hello" ; })); |
453 | EXPECT_FALSE(ew.with_exception([](std::logic_error&) { return nullptr; })); |
454 | } |
455 | |
456 | namespace { |
457 | template <typename T> |
458 | T& r_to_l(T v) { |
459 | return std::ref(v); |
460 | } |
461 | } // namespace |
462 | |
463 | TEST(ExceptionWrapper, with_exception_deduction_functor_lvalue) { |
464 | auto ew = make_exception_wrapper<std::runtime_error>("hi" ); |
465 | EXPECT_TRUE(ew.with_exception(r_to_l([](std::runtime_error&) {}))); |
466 | EXPECT_TRUE(ew.with_exception(r_to_l([](std::exception&) {}))); |
467 | EXPECT_FALSE(ew.with_exception(r_to_l([](std::logic_error&) {}))); |
468 | } |
469 | |
470 | TEST(ExceptionWrapper, non_std_exception_test) { |
471 | int expected = 17; |
472 | |
473 | exception_wrapper ew = |
474 | try_and_catch<std::exception, int>([=]() { throw expected; }); |
475 | EXPECT_TRUE(bool(ew)); |
476 | EXPECT_FALSE(ew.is_compatible_with<std::exception>()); |
477 | EXPECT_TRUE(ew.is_compatible_with<int>()); |
478 | EXPECT_EQ(ew.what(), kIntClassName); |
479 | EXPECT_EQ(ew.class_name(), kIntClassName); |
480 | // non-std::exception types are supported, but the only way to |
481 | // access their value is to explicity rethrow and catch it. |
482 | try { |
483 | ew.throw_exception(); |
484 | } catch /* nolint */ (int& i) { |
485 | EXPECT_EQ(i, expected); |
486 | } |
487 | } |
488 | |
489 | TEST(ExceptionWrapper, exceptionStr) { |
490 | auto ew = make_exception_wrapper<std::runtime_error>("argh" ); |
491 | EXPECT_EQ(kRuntimeErrorClassName + ": argh" , exceptionStr(ew)); |
492 | } |
493 | |
494 | TEST(ExceptionWrapper, throwException_noException) { |
495 | exception_wrapper ew; |
496 | ASSERT_DEATH(ew.throw_exception(), "empty folly::exception_wrapper" ); |
497 | } |
498 | |
499 | namespace { |
500 | class TestException : public std::exception {}; |
501 | void testEW(const exception_wrapper& ew) { |
502 | EXPECT_THROW(ew.throw_exception(), TestException); |
503 | } |
504 | } // namespace |
505 | |
506 | TEST(ExceptionWrapper, implicitConstruction) { |
507 | // Try with both lvalue and rvalue references |
508 | TestException e; |
509 | testEW(e); |
510 | testEW(TestException()); |
511 | } |
512 | |
513 | namespace { |
514 | struct BaseException { |
515 | virtual ~BaseException() {} |
516 | }; |
517 | struct DerivedException : BaseException {}; |
518 | exception_wrapper testNonStdException() { |
519 | try { |
520 | throw DerivedException{}; |
521 | } catch (const BaseException& e) { |
522 | return exception_wrapper{std::current_exception(), e}; |
523 | } |
524 | } |
525 | } // namespace |
526 | |
527 | TEST(ExceptionWrapper, base_derived_non_std_exception_test) { |
528 | auto ew = testNonStdException(); |
529 | EXPECT_TRUE(ew.type() == typeid(DerivedException)); |
530 | EXPECT_TRUE(ew.with_exception([](const DerivedException&) {})); |
531 | } |
532 | |
533 | namespace { |
534 | // Cannot be stored within an exception_wrapper |
535 | struct BigRuntimeError : std::runtime_error { |
536 | using std::runtime_error::runtime_error; |
537 | char data_[sizeof(exception_wrapper) + 1]{}; |
538 | }; |
539 | |
540 | struct BigNonStdError { |
541 | char data_[sizeof(exception_wrapper) + 1]{}; |
542 | }; |
543 | } // namespace |
544 | |
545 | TEST(ExceptionWrapper, handle_std_exception) { |
546 | auto ep = std::make_exception_ptr(std::runtime_error{"hello world" }); |
547 | exception_wrapper const ew_eptr(ep, from_eptr<std::runtime_error>(ep)); |
548 | exception_wrapper const ew_small(std::runtime_error{"hello world" }); |
549 | exception_wrapper const ew_big(BigRuntimeError{"hello world" }); |
550 | |
551 | bool handled = false; |
552 | auto expect_runtime_error_yes_catch_all = [&](const exception_wrapper& ew) { |
553 | ew.handle( |
554 | [](const std::logic_error&) { ADD_FAILURE(); }, |
555 | [&](const std::runtime_error&) { handled = true; }, |
556 | [](const std::exception&) { ADD_FAILURE(); }, |
557 | [](...) { ADD_FAILURE(); }); |
558 | }; |
559 | |
560 | expect_runtime_error_yes_catch_all(ew_eptr); |
561 | EXPECT_TRUE(handled); |
562 | handled = false; |
563 | expect_runtime_error_yes_catch_all(ew_small); |
564 | EXPECT_TRUE(handled); |
565 | handled = false; |
566 | expect_runtime_error_yes_catch_all(ew_big); |
567 | EXPECT_TRUE(handled); |
568 | handled = false; |
569 | |
570 | auto expect_runtime_error_no_catch_all = [&](const exception_wrapper& ew) { |
571 | ew.handle( |
572 | [](const std::logic_error&) { ADD_FAILURE(); }, |
573 | [&](const std::runtime_error&) { handled = true; }, |
574 | [](const std::exception&) { ADD_FAILURE(); }); |
575 | }; |
576 | |
577 | expect_runtime_error_no_catch_all(ew_eptr); |
578 | EXPECT_TRUE(handled); |
579 | handled = false; |
580 | expect_runtime_error_no_catch_all(ew_small); |
581 | EXPECT_TRUE(handled); |
582 | handled = false; |
583 | expect_runtime_error_no_catch_all(ew_big); |
584 | EXPECT_TRUE(handled); |
585 | handled = false; |
586 | |
587 | auto expect_runtime_error_catch_non_std = [&](const exception_wrapper& ew) { |
588 | ew.handle( |
589 | [](const std::logic_error&) { ADD_FAILURE(); }, |
590 | [&](const std::runtime_error&) { handled = true; }, |
591 | [](const std::exception&) { ADD_FAILURE(); }, |
592 | [](const int&) { ADD_FAILURE(); }); |
593 | }; |
594 | |
595 | expect_runtime_error_catch_non_std(ew_eptr); |
596 | EXPECT_TRUE(handled); |
597 | handled = false; |
598 | expect_runtime_error_catch_non_std(ew_small); |
599 | EXPECT_TRUE(handled); |
600 | handled = false; |
601 | expect_runtime_error_catch_non_std(ew_big); |
602 | EXPECT_TRUE(handled); |
603 | handled = false; |
604 | |
605 | // Test that an exception thrown from one handler is not caught by an |
606 | // outer handler: |
607 | auto expect_runtime_error_rethrow = [&](const exception_wrapper& ew) { |
608 | ew.handle( |
609 | [](const std::logic_error&) { ADD_FAILURE(); }, |
610 | [&](const std::runtime_error& e) { |
611 | handled = true; |
612 | throw e; |
613 | }, |
614 | [](const std::exception&) { ADD_FAILURE(); }); |
615 | }; |
616 | |
617 | EXPECT_THROW(expect_runtime_error_rethrow(ew_eptr), std::runtime_error); |
618 | EXPECT_TRUE(handled); |
619 | handled = false; |
620 | EXPECT_THROW(expect_runtime_error_rethrow(ew_small), std::runtime_error); |
621 | EXPECT_TRUE(handled); |
622 | handled = false; |
623 | EXPECT_THROW(expect_runtime_error_rethrow(ew_big), std::runtime_error); |
624 | EXPECT_TRUE(handled); |
625 | } |
626 | |
627 | TEST(ExceptionWrapper, handle_std_exception_unhandled) { |
628 | auto ep = std::make_exception_ptr(std::exception{}); |
629 | exception_wrapper const ew_eptr(ep, from_eptr<std::exception>(ep)); |
630 | exception_wrapper const ew_small(std::exception{}); |
631 | |
632 | bool handled = false; |
633 | auto expect_runtime_error_yes_catch_all = [&](const exception_wrapper& ew) { |
634 | ew.handle( |
635 | [](const std::logic_error&) { ADD_FAILURE(); }, |
636 | [](const std::runtime_error&) { ADD_FAILURE(); }, |
637 | [&](...) { handled = true; }); |
638 | }; |
639 | |
640 | expect_runtime_error_yes_catch_all(ew_eptr); |
641 | EXPECT_TRUE(handled); |
642 | handled = false; |
643 | expect_runtime_error_yes_catch_all(ew_small); |
644 | EXPECT_TRUE(handled); |
645 | } |
646 | |
647 | TEST(ExceptionWrapper, handle_std_exception_propagated) { |
648 | auto ep = std::make_exception_ptr(std::runtime_error{"hello world" }); |
649 | exception_wrapper const ew_eptr(ep, from_eptr<std::runtime_error>(ep)); |
650 | exception_wrapper const ew_small(std::runtime_error{"hello world" }); |
651 | exception_wrapper const ew_big(BigRuntimeError{"hello world" }); |
652 | |
653 | try { |
654 | ew_eptr.handle(); |
655 | } catch (const std::runtime_error&) { |
656 | SUCCEED(); |
657 | } catch (const std::exception&) { |
658 | ADD_FAILURE(); |
659 | } |
660 | |
661 | try { |
662 | ew_small.handle(); |
663 | } catch (const std::runtime_error&) { |
664 | SUCCEED(); |
665 | } catch (const std::exception&) { |
666 | ADD_FAILURE(); |
667 | } |
668 | |
669 | try { |
670 | ew_big.handle(); |
671 | } catch (const std::runtime_error&) { |
672 | SUCCEED(); |
673 | } catch (const std::exception&) { |
674 | ADD_FAILURE(); |
675 | } |
676 | } |
677 | |
678 | TEST(ExceptionWrapper, handle_non_std_exception_small) { |
679 | auto ep = std::make_exception_ptr(42); |
680 | exception_wrapper const ew_eptr1(ep); |
681 | exception_wrapper const ew_eptr2(ep, from_eptr<int>(ep)); |
682 | exception_wrapper const ew_small(folly::in_place, 42); |
683 | bool handled = false; |
684 | |
685 | auto expect_int_yes_catch_all = [&](const exception_wrapper& ew) { |
686 | ew.handle( |
687 | [](const std::exception&) { ADD_FAILURE(); }, |
688 | [&](...) { handled = true; }); |
689 | }; |
690 | |
691 | expect_int_yes_catch_all(ew_eptr1); |
692 | EXPECT_TRUE(handled); |
693 | handled = false; |
694 | expect_int_yes_catch_all(ew_eptr2); |
695 | EXPECT_TRUE(handled); |
696 | handled = false; |
697 | expect_int_yes_catch_all(ew_small); |
698 | EXPECT_TRUE(handled); |
699 | handled = false; |
700 | |
701 | auto expect_int_no_catch_all = [&](const exception_wrapper& ew) { |
702 | ew.handle( |
703 | [](const std::exception&) { ADD_FAILURE(); }, |
704 | [&](const int&) { handled = true; }); |
705 | }; |
706 | |
707 | expect_int_no_catch_all(ew_eptr1); |
708 | EXPECT_TRUE(handled); |
709 | handled = false; |
710 | expect_int_no_catch_all(ew_eptr2); |
711 | EXPECT_TRUE(handled); |
712 | handled = false; |
713 | expect_int_no_catch_all(ew_small); |
714 | EXPECT_TRUE(handled); |
715 | handled = false; |
716 | |
717 | auto expect_int_no_catch_all_2 = [&](const exception_wrapper& ew) { |
718 | ew.handle( |
719 | [&](const int&) { handled = true; }, |
720 | [](const std::exception&) { ADD_FAILURE(); }); |
721 | }; |
722 | |
723 | expect_int_no_catch_all_2(ew_eptr1); |
724 | EXPECT_TRUE(handled); |
725 | handled = false; |
726 | expect_int_no_catch_all_2(ew_eptr2); |
727 | EXPECT_TRUE(handled); |
728 | handled = false; |
729 | expect_int_no_catch_all_2(ew_small); |
730 | EXPECT_TRUE(handled); |
731 | } |
732 | |
733 | TEST(ExceptionWrapper, handle_non_std_exception_big) { |
734 | auto ep = std::make_exception_ptr(BigNonStdError{}); |
735 | exception_wrapper const ew_eptr1(ep); |
736 | exception_wrapper const ew_eptr2(ep, from_eptr<BigNonStdError>(ep)); |
737 | exception_wrapper const ew_big(folly::in_place, BigNonStdError{}); |
738 | bool handled = false; |
739 | |
740 | auto expect_int_yes_catch_all = [&](const exception_wrapper& ew) { |
741 | ew.handle( |
742 | [](const std::exception&) { ADD_FAILURE(); }, |
743 | [&](...) { handled = true; }); |
744 | }; |
745 | |
746 | expect_int_yes_catch_all(ew_eptr1); |
747 | EXPECT_TRUE(handled); |
748 | handled = false; |
749 | expect_int_yes_catch_all(ew_eptr2); |
750 | EXPECT_TRUE(handled); |
751 | handled = false; |
752 | expect_int_yes_catch_all(ew_big); |
753 | EXPECT_TRUE(handled); |
754 | handled = false; |
755 | |
756 | auto expect_int_no_catch_all = [&](const exception_wrapper& ew) { |
757 | ew.handle( |
758 | [](const std::exception&) { ADD_FAILURE(); }, |
759 | [&](const BigNonStdError&) { handled = true; }); |
760 | }; |
761 | |
762 | expect_int_no_catch_all(ew_eptr1); |
763 | EXPECT_TRUE(handled); |
764 | handled = false; |
765 | expect_int_no_catch_all(ew_eptr2); |
766 | EXPECT_TRUE(handled); |
767 | handled = false; |
768 | expect_int_no_catch_all(ew_big); |
769 | EXPECT_TRUE(handled); |
770 | handled = false; |
771 | |
772 | auto expect_int_no_catch_all_2 = [&](const exception_wrapper& ew) { |
773 | ew.handle( |
774 | [&](const BigNonStdError&) { handled = true; }, |
775 | [](const std::exception&) { ADD_FAILURE(); }); |
776 | }; |
777 | |
778 | expect_int_no_catch_all_2(ew_eptr1); |
779 | EXPECT_TRUE(handled); |
780 | handled = false; |
781 | expect_int_no_catch_all_2(ew_eptr2); |
782 | EXPECT_TRUE(handled); |
783 | handled = false; |
784 | expect_int_no_catch_all_2(ew_big); |
785 | EXPECT_TRUE(handled); |
786 | handled = false; |
787 | |
788 | EXPECT_THROW( |
789 | expect_int_no_catch_all_2(exception_wrapper{folly::in_place, 42}), int); |
790 | } |
791 | |
792 | TEST(ExceptionWrapper, handle_non_std_exception_rethrow_base_derived) { |
793 | auto ew = testNonStdException(); |
794 | bool handled = false; |
795 | EXPECT_THROW( |
796 | ew.handle( |
797 | [&](const DerivedException& e) { |
798 | handled = true; |
799 | throw e; |
800 | }, |
801 | [](const BaseException&) { ADD_FAILURE(); }), |
802 | DerivedException); |
803 | EXPECT_TRUE(handled); |
804 | handled = false; |
805 | EXPECT_THROW( |
806 | ew.handle( |
807 | [&](const DerivedException& e) { |
808 | handled = true; |
809 | throw e; |
810 | }, |
811 | [](...) { ADD_FAILURE(); }), |
812 | DerivedException); |
813 | EXPECT_TRUE(handled); |
814 | } |
815 | |
816 | TEST(ExceptionWrapper, self_swap_test) { |
817 | exception_wrapper ew(std::runtime_error("hello world" )); |
818 | folly::swap(ew, ew); |
819 | EXPECT_EQ(kRuntimeErrorClassName + ": hello world" , ew.what()); |
820 | auto& ew2 = ew; |
821 | ew = std::move(ew2); // should not crash |
822 | } |
823 | |