X-Git-Url: http://plrg.eecs.uci.edu/git/?a=blobdiff_plain;f=folly%2Ftest%2FExceptionWrapperTest.cpp;h=fde0e8a97fb810403967925b3c7c064a9b14257a;hb=22d531a8fe503001a51672750dc09daae252fbf6;hp=8683e89f94798737eee4fc635156b7a2eebfe1dc;hpb=ce64f0f685111ac24c7a321ea56d0c3524621df1;p=folly.git diff --git a/folly/test/ExceptionWrapperTest.cpp b/folly/test/ExceptionWrapperTest.cpp index 8683e89f..fde0e8a9 100644 --- a/folly/test/ExceptionWrapperTest.cpp +++ b/folly/test/ExceptionWrapperTest.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2014 Facebook, Inc. + * Copyright 2017 Facebook, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,13 +14,54 @@ * limitations under the License. */ -#include #include + #include +#include +#include using namespace folly; -// Tests that when we call throwException, the proper type is thrown (derived) +class AbstractIntException : public std::exception { + public: + virtual int getInt() const = 0; +}; + +class IntException : public AbstractIntException { + public: + explicit IntException(int i) : i_(i) {} + + int getInt() const override { return i_; } + const char* what() const noexcept override { + what_ = folly::to("int == ", i_); + return what_.c_str(); + } + + private: + int i_; + mutable std::string what_; +}; + +const static std::string kExceptionClassName = + demangle(typeid(std::exception)).toStdString(); +const static std::string kRuntimeErrorClassName = + demangle(typeid(std::runtime_error)).toStdString(); +const static std::string kIntExceptionClassName = + demangle(typeid(IntException)).toStdString(); +const static std::string kIntClassName = demangle(typeid(int)).toStdString(); + +template +T& from_eptr(std::exception_ptr& eptr) { + try { + std::rethrow_exception(eptr); + } catch (T& e) { + return e; + } catch (...) { + throw std::logic_error("impossible"); + } +} + +// Tests that when we call throw_exception, the proper type is thrown (derived) TEST(ExceptionWrapper, throw_test) { std::runtime_error e("payload"); auto ew = make_exception_wrapper(e); @@ -29,19 +70,23 @@ TEST(ExceptionWrapper, throw_test) { container.push_back(ew); try { - container[0].throwException(); - } catch (std::runtime_error& e) { + container[0].throw_exception(); + } catch (std::runtime_error& err) { std::string expected = "payload"; - std::string actual = e.what(); + std::string actual = err.what(); EXPECT_EQ(expected, actual); } } -TEST(ExceptionWrapper, boolean) { +TEST(ExceptionWrapper, members) { auto ew = exception_wrapper(); EXPECT_FALSE(bool(ew)); + EXPECT_EQ(ew.what(), ""); + EXPECT_EQ(ew.class_name(), ""); ew = make_exception_wrapper("payload"); EXPECT_TRUE(bool(ew)); + EXPECT_EQ(ew.what(), kRuntimeErrorClassName + ": payload"); + EXPECT_EQ(ew.class_name(), kRuntimeErrorClassName); } TEST(ExceptionWrapper, try_and_catch_test) { @@ -52,9 +97,10 @@ TEST(ExceptionWrapper, try_and_catch_test) { [=]() { throw std::runtime_error(expected); }); - EXPECT_TRUE(ew.get()); - EXPECT_EQ(ew.get()->what(), expected); - auto rep = dynamic_cast(ew.get()); + EXPECT_TRUE(bool(ew)); + EXPECT_EQ(ew.what(), kRuntimeErrorClassName + ": payload"); + EXPECT_EQ(ew.class_name(), kRuntimeErrorClassName); + auto rep = ew.is_compatible_with(); EXPECT_TRUE(rep); // Changing order is like catching in wrong order. Beware of this in your @@ -62,19 +108,20 @@ TEST(ExceptionWrapper, try_and_catch_test) { auto ew2 = try_and_catch([=]() { throw std::runtime_error(expected); }); - EXPECT_TRUE(ew2.get()); + EXPECT_TRUE(bool(ew2)); // We are catching a std::exception, not std::runtime_error. - EXPECT_NE(ew2.get()->what(), expected); - rep = dynamic_cast(ew2.get()); - EXPECT_FALSE(rep); + // But, we can still get the actual type if we want it. + rep = ew2.is_compatible_with(); + EXPECT_TRUE(rep); // Catches even if not rightmost. auto ew3 = try_and_catch([]() { throw std::exception(); }); - EXPECT_TRUE(ew3.get()); - EXPECT_NE(ew3.get()->what(), expected); - rep = dynamic_cast(ew3.get()); + EXPECT_TRUE(bool(ew3)); + EXPECT_EQ(ew3.what(), kExceptionClassName + ": std::exception"); + EXPECT_EQ(ew3.class_name(), kExceptionClassName); + rep = ew3.is_compatible_with(); EXPECT_FALSE(rep); // If does not catch, throws. @@ -84,3 +131,619 @@ TEST(ExceptionWrapper, try_and_catch_test) { }), std::exception); } + +TEST(ExceptionWrapper, with_exception_test) { + int expected = 23; + + // This works, and doesn't slice. + exception_wrapper ew = try_and_catch( + [=]() { + throw IntException(expected); + }); + EXPECT_TRUE(bool(ew)); + EXPECT_EQ(ew.what(), kIntExceptionClassName + ": int == 23"); + EXPECT_EQ(ew.class_name(), kIntExceptionClassName); + EXPECT_TRUE(ew.with_exception( + [&](const IntException& ie) { EXPECT_EQ(ie.getInt(), expected); })); + + // I can try_and_catch a non-copyable base class. This will use + // std::exception_ptr internally. + exception_wrapper ew2 = try_and_catch( + [=]() { + throw IntException(expected); + }); + EXPECT_TRUE(bool(ew2)); + EXPECT_EQ(ew2.what(), kIntExceptionClassName + ": int == 23"); + EXPECT_EQ(ew2.class_name(), kIntExceptionClassName); + bool res = ew2.with_exception([&](AbstractIntException& ie) { + EXPECT_EQ(ie.getInt(), expected); + EXPECT_TRUE(dynamic_cast(&ie)); + }); + EXPECT_TRUE(res); + + // Test with const this. If this compiles and does not crash due to + // infinite loop when it runs, it succeeds. + const exception_wrapper& cew = ew; + EXPECT_TRUE( + cew.with_exception([&](const IntException& /* ie */) { SUCCEED(); })); + + // Test with empty ew. + exception_wrapper empty_ew; + EXPECT_FALSE( + empty_ew.with_exception([&](const std::exception& /* ie */) { FAIL(); })); + + // Testing with const exception_wrapper; sanity check first: + EXPECT_FALSE(cew.with_exception([&](const std::runtime_error&) {})); + EXPECT_FALSE(cew.with_exception([&](const int&) {})); + // This won't even compile. You can't use a function which takes a + // non-const reference with a const exception_wrapper. + /* + EXPECT_FALSE(cew.with_exception([&](std::runtime_error&) {})); + EXPECT_FALSE(cew.with_exception([&](int&) {})); + */ +} + +TEST(ExceptionWrapper, get_or_make_exception_ptr_test) { + int expected = 23; + + // This works, and doesn't slice. + exception_wrapper ew = try_and_catch( + [=]() { throw IntException(expected); }); + std::exception_ptr eptr = ew.to_exception_ptr(); + EXPECT_THROW(std::rethrow_exception(eptr), IntException); + + // I can try_and_catch a non-copyable base class. This will use + // std::exception_ptr internally. + exception_wrapper ew2 = try_and_catch( + [=]() { throw IntException(expected); }); + eptr = ew2.to_exception_ptr(); + EXPECT_THROW(std::rethrow_exception(eptr), IntException); + + // Test with const this. + const exception_wrapper& cew = ew; + eptr = cew.to_exception_ptr(); + EXPECT_THROW(std::rethrow_exception(eptr), IntException); + + // Test with empty ew. + exception_wrapper empty_ew; + eptr = empty_ew.to_exception_ptr(); + EXPECT_FALSE(eptr); +} + +TEST(ExceptionWrapper, with_exception_ptr_empty) { + auto ew = exception_wrapper(std::exception_ptr()); + EXPECT_EQ(exception_wrapper::none(), ew.type()); + EXPECT_FALSE(bool(ew)); + EXPECT_EQ(nullptr, ew.get_exception()); + EXPECT_EQ(nullptr, ew.get_object()); + EXPECT_EQ(nullptr, ew.get_object()); + EXPECT_FALSE(ew.has_exception_ptr()); + EXPECT_EQ(nullptr, ew.to_exception_ptr()); + EXPECT_FALSE(ew.has_exception_ptr()); + EXPECT_EQ("", ew.class_name()); + EXPECT_EQ("", ew.what()); + EXPECT_FALSE(ew.is_compatible_with()); + EXPECT_FALSE(ew.is_compatible_with()); + EXPECT_DEATH(ew.throw_exception(), "empty folly::exception_wrapper"); +} + +TEST(ExceptionWrapper, with_shared_ptr_test) { + auto ew = exception_wrapper(std::runtime_error("foo")); + EXPECT_TRUE(bool(ew)); + EXPECT_EQ(typeid(std::runtime_error), ew.type()); + EXPECT_NE(nullptr, ew.get_exception()); + EXPECT_NE(nullptr, ew.get_object()); + EXPECT_EQ(nullptr, ew.get_object()); + EXPECT_FALSE(ew.has_exception_ptr()); + EXPECT_NE(nullptr, ew.to_exception_ptr()); + EXPECT_TRUE(ew.has_exception_ptr()); + EXPECT_EQ("std::runtime_error", ew.class_name()); + EXPECT_EQ("std::runtime_error: foo", ew.what()); + EXPECT_TRUE(ew.is_compatible_with()); + EXPECT_TRUE(ew.is_compatible_with()); + EXPECT_FALSE(ew.is_compatible_with()); + EXPECT_THROW(ew.throw_exception(), std::runtime_error); + + exception_wrapper(std::move(ew)); + EXPECT_FALSE(bool(ew)); + EXPECT_EQ(exception_wrapper::none(), ew.type()); + EXPECT_EQ(nullptr, ew.get_exception()); + EXPECT_EQ(nullptr, ew.get_object()); + EXPECT_EQ(nullptr, ew.get_object()); + EXPECT_EQ(nullptr, ew.to_exception_ptr()); + EXPECT_EQ("", ew.class_name()); + EXPECT_EQ("", ew.what()); + EXPECT_FALSE(ew.is_compatible_with()); + EXPECT_FALSE(ew.is_compatible_with()); + EXPECT_FALSE(ew.is_compatible_with()); +} + +TEST(ExceptionWrapper, with_exception_ptr_exn_test) { + auto ep = std::make_exception_ptr(std::runtime_error("foo")); + auto ew = exception_wrapper(ep, from_eptr(ep)); + EXPECT_TRUE(bool(ew)); + EXPECT_EQ(typeid(std::runtime_error), ew.type()); + EXPECT_NE(nullptr, ew.get_exception()); + EXPECT_NE(nullptr, ew.get_object()); + EXPECT_EQ(nullptr, ew.get_object()); + EXPECT_TRUE(ew.has_exception_ptr()); + EXPECT_EQ(ep, ew.to_exception_ptr()); + EXPECT_TRUE(ew.has_exception_ptr()); + EXPECT_EQ("std::runtime_error", ew.class_name()); + EXPECT_EQ("std::runtime_error: foo", ew.what()); + EXPECT_TRUE(ew.is_compatible_with()); + EXPECT_TRUE(ew.is_compatible_with()); + EXPECT_FALSE(ew.is_compatible_with()); + EXPECT_THROW(ew.throw_exception(), std::runtime_error); + + exception_wrapper(std::move(ew)); + EXPECT_FALSE(bool(ew)); + EXPECT_EQ(exception_wrapper::none(), ew.type()); + EXPECT_EQ(nullptr, ew.get_exception()); + EXPECT_EQ(nullptr, ew.get_object()); + EXPECT_EQ(nullptr, ew.get_object()); + EXPECT_EQ(nullptr, ew.to_exception_ptr()); + EXPECT_EQ("", ew.class_name()); + EXPECT_EQ("", ew.what()); + EXPECT_FALSE(ew.is_compatible_with()); + EXPECT_FALSE(ew.is_compatible_with()); + EXPECT_FALSE(ew.is_compatible_with()); +} + +TEST(ExceptionWrapper, with_exception_ptr_any_test) { + auto ep = std::make_exception_ptr(12); + auto ew = exception_wrapper(ep, from_eptr(ep)); + EXPECT_TRUE(bool(ew)); + EXPECT_EQ(nullptr, ew.get_exception()); + EXPECT_EQ(nullptr, ew.get_object()); + EXPECT_NE(nullptr, ew.get_object()); + EXPECT_TRUE(ew.has_exception_ptr()); + EXPECT_EQ(ep, ew.to_exception_ptr()); + EXPECT_TRUE(ew.has_exception_ptr()); + EXPECT_EQ("int", ew.class_name()); + EXPECT_EQ("int", ew.what()); + EXPECT_FALSE(ew.is_compatible_with()); + EXPECT_FALSE(ew.is_compatible_with()); + EXPECT_TRUE(ew.is_compatible_with()); + EXPECT_THROW(ew.throw_exception(), int); + + exception_wrapper(std::move(ew)); + EXPECT_FALSE(bool(ew)); + EXPECT_EQ(nullptr, ew.get_exception()); + EXPECT_EQ(nullptr, ew.get_object()); + EXPECT_EQ(nullptr, ew.get_object()); + EXPECT_EQ(nullptr, ew.to_exception_ptr()); + EXPECT_FALSE(ew.has_exception_ptr()); + EXPECT_EQ("", ew.class_name()); + EXPECT_EQ("", ew.what()); + EXPECT_FALSE(ew.is_compatible_with()); + EXPECT_FALSE(ew.is_compatible_with()); + EXPECT_FALSE(ew.is_compatible_with()); +} + +TEST(ExceptionWrapper, with_non_std_exception_test) { + auto ew = exception_wrapper(folly::in_place, 42); + EXPECT_TRUE(bool(ew)); + EXPECT_EQ(nullptr, ew.get_exception()); + EXPECT_EQ(nullptr, ew.get_object()); + EXPECT_NE(nullptr, ew.get_object()); + EXPECT_FALSE(ew.has_exception_ptr()); + EXPECT_EQ("int", ew.class_name()); + EXPECT_EQ("int", ew.what()); + EXPECT_NE(nullptr, ew.to_exception_ptr()); + EXPECT_TRUE(ew.has_exception_ptr()); + EXPECT_EQ("int", ew.class_name()); + EXPECT_EQ("int", ew.what()); + EXPECT_FALSE(ew.is_compatible_with()); + EXPECT_FALSE(ew.is_compatible_with()); + EXPECT_TRUE(ew.is_compatible_with()); + EXPECT_THROW(ew.throw_exception(), int); + + exception_wrapper(std::move(ew)); + EXPECT_FALSE(bool(ew)); + EXPECT_EQ(nullptr, ew.get_exception()); + EXPECT_EQ(nullptr, ew.get_object()); + EXPECT_EQ(nullptr, ew.get_object()); + EXPECT_EQ(nullptr, ew.to_exception_ptr()); + EXPECT_FALSE(ew.has_exception_ptr()); + EXPECT_EQ("", ew.class_name()); + EXPECT_EQ("", ew.what()); + EXPECT_FALSE(ew.is_compatible_with()); + EXPECT_FALSE(ew.is_compatible_with()); + EXPECT_FALSE(ew.is_compatible_with()); +} + +TEST(ExceptionWrapper, with_exception_ptr_any_nil_test) { + auto ep = std::make_exception_ptr(12); + auto ew = exception_wrapper(ep); // concrete type is erased + EXPECT_TRUE(bool(ew)); + EXPECT_EQ(nullptr, ew.get_exception()); + EXPECT_EQ(nullptr, ew.get_object()); + EXPECT_NE(nullptr, ew.get_object()); + EXPECT_EQ(ep, ew.to_exception_ptr()); + EXPECT_EQ("", ew.class_name()); // because concrete type is + // erased + EXPECT_EQ("", ew.what()); + EXPECT_FALSE(ew.is_compatible_with()); + EXPECT_FALSE(ew.is_compatible_with()); + EXPECT_TRUE(ew.is_compatible_with()); + EXPECT_THROW(ew.throw_exception(), int); + + exception_wrapper(std::move(ew)); + EXPECT_FALSE(bool(ew)); + EXPECT_EQ(nullptr, ew.get_exception()); + EXPECT_EQ(nullptr, ew.get_object()); + EXPECT_EQ(nullptr, ew.get_object()); + EXPECT_EQ(nullptr, ew.to_exception_ptr()); + EXPECT_EQ("", ew.class_name()); + EXPECT_EQ("", ew.what()); + EXPECT_FALSE(ew.is_compatible_with()); + EXPECT_FALSE(ew.is_compatible_with()); + EXPECT_FALSE(ew.is_compatible_with()); +} + +TEST(ExceptionWrapper, with_exception_deduction) { + auto ew = make_exception_wrapper("hi"); + EXPECT_TRUE(ew.with_exception([](std::runtime_error&) {})); + EXPECT_TRUE(ew.with_exception([](std::exception&) {})); + EXPECT_FALSE(ew.with_exception([](std::logic_error&) {})); +} + +TEST(ExceptionWrapper, with_exception_deduction_exn_const) { + auto ew = make_exception_wrapper("hi"); + EXPECT_TRUE(ew.with_exception([](const std::runtime_error&) {})); + EXPECT_TRUE(ew.with_exception([](const std::exception&) {})); + EXPECT_FALSE(ew.with_exception([](const std::logic_error&) {})); +} + +TEST(ExceptionWrapper, with_exception_deduction_wrap_const_exn_const) { + const auto cew = make_exception_wrapper("hi"); + EXPECT_TRUE(cew.with_exception([](const std::runtime_error&) {})); + EXPECT_TRUE(cew.with_exception([](const std::exception&) {})); + EXPECT_FALSE(cew.with_exception([](const std::logic_error&) {})); +} + +TEST(ExceptionWrapper, with_exception_deduction_returning) { + auto ew = make_exception_wrapper("hi"); + EXPECT_TRUE(ew.with_exception([](std::runtime_error&) { return 3; })); + EXPECT_TRUE(ew.with_exception([](std::exception&) { return "hello"; })); + EXPECT_FALSE(ew.with_exception([](std::logic_error&) { return nullptr; })); +} + +namespace { +template +T& r_to_l(T v) { return std::ref(v); } +} + +TEST(ExceptionWrapper, with_exception_deduction_functor_lvalue) { + auto ew = make_exception_wrapper("hi"); + EXPECT_TRUE(ew.with_exception(r_to_l([](std::runtime_error&) {}))); + EXPECT_TRUE(ew.with_exception(r_to_l([](std::exception&) {}))); + EXPECT_FALSE(ew.with_exception(r_to_l([](std::logic_error&) {}))); +} + +TEST(ExceptionWrapper, non_std_exception_test) { + int expected = 17; + + exception_wrapper ew = try_and_catch( + [=]() { + throw expected; + }); + EXPECT_TRUE(bool(ew)); + EXPECT_FALSE(ew.is_compatible_with()); + EXPECT_TRUE(ew.is_compatible_with()); + EXPECT_EQ(ew.what(), kIntClassName); + EXPECT_EQ(ew.class_name(), kIntClassName); + // non-std::exception types are supported, but the only way to + // access their value is to explicity rethrow and catch it. + try { + ew.throw_exception(); + } catch /* nolint */ (int& i) { + EXPECT_EQ(i, expected); + } +} + + +TEST(ExceptionWrapper, exceptionStr) { + auto ew = make_exception_wrapper("argh"); + EXPECT_EQ(kRuntimeErrorClassName + ": argh", exceptionStr(ew)); +} + +TEST(ExceptionWrapper, throwException_noException) { + exception_wrapper ew; + ASSERT_DEATH(ew.throw_exception(), "empty folly::exception_wrapper"); +} + +namespace { +class TestException : public std::exception { }; +void testEW(const exception_wrapper& ew) { + EXPECT_THROW(ew.throw_exception(), TestException); +} +} // namespace + +TEST(ExceptionWrapper, implicitConstruction) { + // Try with both lvalue and rvalue references + TestException e; + testEW(e); + testEW(TestException()); +} + +namespace { +struct BaseException { + virtual ~BaseException() {} +}; +struct DerivedException : BaseException {}; +exception_wrapper testNonStdException() { + try { + throw DerivedException{}; + } catch (const BaseException& e) { + return exception_wrapper{std::current_exception(), e}; + } +} +} + +TEST(ExceptionWrapper, base_derived_non_std_exception_test) { + auto ew = testNonStdException(); + EXPECT_TRUE(ew.type() == typeid(DerivedException)); + EXPECT_TRUE(ew.with_exception([](const DerivedException&) {})); +} + +namespace { +// Cannot be stored within an exception_wrapper +struct BigRuntimeError : std::runtime_error { + using std::runtime_error::runtime_error; + char data_[sizeof(exception_wrapper) + 1]{}; +}; + +struct BigNonStdError { + char data_[sizeof(exception_wrapper) + 1]{}; +}; +} + +TEST(ExceptionWrapper, handle_std_exception) { + auto ep = std::make_exception_ptr(std::runtime_error{"hello world"}); + exception_wrapper const ew_eptr(ep, from_eptr(ep)); + exception_wrapper const ew_small(std::runtime_error{"hello world"}); + exception_wrapper const ew_big(BigRuntimeError{"hello world"}); + + bool handled = false; + auto expect_runtime_error_yes_catch_all = [&](const exception_wrapper& ew) { + ew.handle( + [](const std::logic_error&) { EXPECT_TRUE(false); }, + [&](const std::runtime_error&) { handled = true; }, + [](const std::exception&) { EXPECT_TRUE(false); }, + [](...) { EXPECT_TRUE(false); }); + }; + + expect_runtime_error_yes_catch_all(ew_eptr); + EXPECT_TRUE(handled); + handled = false; + expect_runtime_error_yes_catch_all(ew_small); + EXPECT_TRUE(handled); + handled = false; + expect_runtime_error_yes_catch_all(ew_big); + EXPECT_TRUE(handled); + handled = false; + + auto expect_runtime_error_no_catch_all = [&](const exception_wrapper& ew) { + ew.handle( + [](const std::logic_error&) { EXPECT_TRUE(false); }, + [&](const std::runtime_error&) { handled = true; }, + [](const std::exception&) { EXPECT_TRUE(false); }); + }; + + expect_runtime_error_no_catch_all(ew_eptr); + EXPECT_TRUE(handled); + handled = false; + expect_runtime_error_no_catch_all(ew_small); + EXPECT_TRUE(handled); + handled = false; + expect_runtime_error_no_catch_all(ew_big); + EXPECT_TRUE(handled); + handled = false; + + auto expect_runtime_error_catch_non_std = [&](const exception_wrapper& ew) { + ew.handle( + [](const std::logic_error&) { EXPECT_TRUE(false); }, + [&](const std::runtime_error&) { handled = true; }, + [](const std::exception&) { EXPECT_TRUE(false); }, + [](const int&) { EXPECT_TRUE(false); }); + }; + + expect_runtime_error_catch_non_std(ew_eptr); + EXPECT_TRUE(handled); + handled = false; + expect_runtime_error_catch_non_std(ew_small); + EXPECT_TRUE(handled); + handled = false; + expect_runtime_error_catch_non_std(ew_big); + EXPECT_TRUE(handled); + handled = false; + + // Test that an exception thrown from one handler is not caught by an + // outer handler: + auto expect_runtime_error_rethrow = [&](const exception_wrapper& ew) { + ew.handle( + [](const std::logic_error&) { EXPECT_TRUE(false); }, + [&](const std::runtime_error& e) { + handled = true; + throw e; + }, + [](const std::exception&) { EXPECT_TRUE(false); }); + }; + + EXPECT_THROW(expect_runtime_error_rethrow(ew_eptr), std::runtime_error); + EXPECT_TRUE(handled); + handled = false; + EXPECT_THROW(expect_runtime_error_rethrow(ew_small), std::runtime_error); + EXPECT_TRUE(handled); + handled = false; + EXPECT_THROW(expect_runtime_error_rethrow(ew_big), std::runtime_error); + EXPECT_TRUE(handled); +} + +TEST(ExceptionWrapper, handle_std_exception_unhandled) { + auto ep = std::make_exception_ptr(std::exception{}); + exception_wrapper const ew_eptr(ep, from_eptr(ep)); + exception_wrapper const ew_small(std::exception{}); + + bool handled = false; + auto expect_runtime_error_yes_catch_all = [&](const exception_wrapper& ew) { + ew.handle( + [](const std::logic_error&) { EXPECT_TRUE(false); }, + [](const std::runtime_error&) { EXPECT_TRUE(false); }, + [&](...) { handled = true; }); + }; + + expect_runtime_error_yes_catch_all(ew_eptr); + EXPECT_TRUE(handled); + handled = false; + expect_runtime_error_yes_catch_all(ew_small); + EXPECT_TRUE(handled); +} + +TEST(ExceptionWrapper, handle_non_std_exception_small) { + auto ep = std::make_exception_ptr(42); + exception_wrapper const ew_eptr1(ep); + exception_wrapper const ew_eptr2(ep, from_eptr(ep)); + exception_wrapper const ew_small(folly::in_place, 42); + bool handled = false; + + auto expect_int_yes_catch_all = [&](const exception_wrapper& ew) { + ew.handle( + [](const std::exception&) { EXPECT_TRUE(false); }, + [&](...) { handled = true; }); + }; + + expect_int_yes_catch_all(ew_eptr1); + EXPECT_TRUE(handled); + handled = false; + expect_int_yes_catch_all(ew_eptr2); + EXPECT_TRUE(handled); + handled = false; + expect_int_yes_catch_all(ew_small); + EXPECT_TRUE(handled); + handled = false; + + auto expect_int_no_catch_all = [&](const exception_wrapper& ew) { + ew.handle( + [](const std::exception&) { EXPECT_TRUE(false); }, + [&](const int&) { handled = true; }); + }; + + expect_int_no_catch_all(ew_eptr1); + EXPECT_TRUE(handled); + handled = false; + expect_int_no_catch_all(ew_eptr2); + EXPECT_TRUE(handled); + handled = false; + expect_int_no_catch_all(ew_small); + EXPECT_TRUE(handled); + handled = false; + + auto expect_int_no_catch_all_2 = [&](const exception_wrapper& ew) { + ew.handle( + [&](const int&) { handled = true; }, + [](const std::exception&) { EXPECT_TRUE(false); }); + }; + + expect_int_no_catch_all_2(ew_eptr1); + EXPECT_TRUE(handled); + handled = false; + expect_int_no_catch_all_2(ew_eptr2); + EXPECT_TRUE(handled); + handled = false; + expect_int_no_catch_all_2(ew_small); + EXPECT_TRUE(handled); +} + +TEST(ExceptionWrapper, handle_non_std_exception_big) { + auto ep = std::make_exception_ptr(BigNonStdError{}); + exception_wrapper const ew_eptr1(ep); + exception_wrapper const ew_eptr2(ep, from_eptr(ep)); + exception_wrapper const ew_big(folly::in_place, BigNonStdError{}); + bool handled = false; + + auto expect_int_yes_catch_all = [&](const exception_wrapper& ew) { + ew.handle( + [](const std::exception&) { EXPECT_TRUE(false); }, + [&](...) { handled = true; }); + }; + + expect_int_yes_catch_all(ew_eptr1); + EXPECT_TRUE(handled); + handled = false; + expect_int_yes_catch_all(ew_eptr2); + EXPECT_TRUE(handled); + handled = false; + expect_int_yes_catch_all(ew_big); + EXPECT_TRUE(handled); + handled = false; + + auto expect_int_no_catch_all = [&](const exception_wrapper& ew) { + ew.handle( + [](const std::exception&) { EXPECT_TRUE(false); }, + [&](const BigNonStdError&) { handled = true; }); + }; + + expect_int_no_catch_all(ew_eptr1); + EXPECT_TRUE(handled); + handled = false; + expect_int_no_catch_all(ew_eptr2); + EXPECT_TRUE(handled); + handled = false; + expect_int_no_catch_all(ew_big); + EXPECT_TRUE(handled); + handled = false; + + auto expect_int_no_catch_all_2 = [&](const exception_wrapper& ew) { + ew.handle( + [&](const BigNonStdError&) { handled = true; }, + [](const std::exception&) { EXPECT_TRUE(false); }); + }; + + expect_int_no_catch_all_2(ew_eptr1); + EXPECT_TRUE(handled); + handled = false; + expect_int_no_catch_all_2(ew_eptr2); + EXPECT_TRUE(handled); + handled = false; + expect_int_no_catch_all_2(ew_big); + EXPECT_TRUE(handled); + handled = false; + + EXPECT_THROW( + expect_int_no_catch_all_2(exception_wrapper{folly::in_place, 42}), int); +} + +TEST(ExceptionWrapper, handle_non_std_exception_rethrow_base_derived) { + auto ew = testNonStdException(); + bool handled = false; + EXPECT_THROW( + ew.handle( + [&](const DerivedException& e) { + handled = true; + throw e; + }, + [](const BaseException&) { EXPECT_TRUE(false); }), + DerivedException); + EXPECT_TRUE(handled); + handled = false; + EXPECT_THROW( + ew.handle( + [&](const DerivedException& e) { + handled = true; + throw e; + }, + [](...) { EXPECT_TRUE(false); }), + DerivedException); + EXPECT_TRUE(handled); +} + +TEST(ExceptionWrapper, self_swap_test) { + exception_wrapper ew(std::runtime_error("hello world")); + folly::swap(ew, ew); + EXPECT_STREQ("std::runtime_error: hello world", ew.what().c_str()); + auto& ew2 = ew; + ew = std::move(ew2); // should not crash +}