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
23using namespace folly;
24
25class AbstractIntException : public std::exception {
26 public:
27 virtual int getInt() const = 0;
28};
29
30class 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
46const static std::string kExceptionClassName =
47 demangle(typeid(std::exception)).toStdString();
48const static std::string kRuntimeErrorClassName =
49 demangle(typeid(std::runtime_error)).toStdString();
50const static std::string kIntExceptionClassName =
51 demangle(typeid(IntException)).toStdString();
52const static std::string kIntClassName = demangle(typeid(int)).toStdString();
53
54template <typename T>
55T& 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
65TEST(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)
74TEST(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.
91TEST(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
107TEST(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
118TEST(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
155TEST(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
202TEST(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
229TEST(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
235TEST(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
243TEST(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
251TEST(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
268TEST(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
300TEST(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
333TEST(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
365TEST(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
398TEST(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
428TEST(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
435TEST(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
442TEST(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
449TEST(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
456namespace {
457template <typename T>
458T& r_to_l(T v) {
459 return std::ref(v);
460}
461} // namespace
462
463TEST(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
470TEST(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
489TEST(ExceptionWrapper, exceptionStr) {
490 auto ew = make_exception_wrapper<std::runtime_error>("argh");
491 EXPECT_EQ(kRuntimeErrorClassName + ": argh", exceptionStr(ew));
492}
493
494TEST(ExceptionWrapper, throwException_noException) {
495 exception_wrapper ew;
496 ASSERT_DEATH(ew.throw_exception(), "empty folly::exception_wrapper");
497}
498
499namespace {
500class TestException : public std::exception {};
501void testEW(const exception_wrapper& ew) {
502 EXPECT_THROW(ew.throw_exception(), TestException);
503}
504} // namespace
505
506TEST(ExceptionWrapper, implicitConstruction) {
507 // Try with both lvalue and rvalue references
508 TestException e;
509 testEW(e);
510 testEW(TestException());
511}
512
513namespace {
514struct BaseException {
515 virtual ~BaseException() {}
516};
517struct DerivedException : BaseException {};
518exception_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
527TEST(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
533namespace {
534// Cannot be stored within an exception_wrapper
535struct BigRuntimeError : std::runtime_error {
536 using std::runtime_error::runtime_error;
537 char data_[sizeof(exception_wrapper) + 1]{};
538};
539
540struct BigNonStdError {
541 char data_[sizeof(exception_wrapper) + 1]{};
542};
543} // namespace
544
545TEST(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
627TEST(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
647TEST(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
678TEST(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
733TEST(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
792TEST(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
816TEST(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