/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <folly/ExceptionWrapper.h>
#include <memory>
#include <stdexcept>
#include <folly/Conv.h>
#include <folly/portability/GMock.h>
#include <folly/portability/GTest.h>
using namespace folly;
using namespace ::testing;
class AbstractIntException : public std::exception {
public:
virtual int getInt() const = 0;
};
class IntException : public AbstractIntException {
public:
explicit IntException(int i) : i_(i), what_(to<std::string>("int == ", i_)) {}
int getInt() const override { return i_; }
const char* what() const noexcept override { return what_.c_str(); }
private:
int i_;
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();
TEST(ExceptionWrapper, nothrow) {
EXPECT_TRUE(std::is_nothrow_default_constructible<exception_wrapper>::value);
EXPECT_TRUE(std::is_nothrow_move_constructible<exception_wrapper>::value);
EXPECT_TRUE(std::is_nothrow_move_assignable<exception_wrapper>::value);
EXPECT_TRUE(std::is_nothrow_copy_constructible<exception_wrapper>::value);
EXPECT_TRUE(std::is_nothrow_copy_assignable<exception_wrapper>::value);
}
// Tests that when we call throw_exception, the proper type is thrown (derived)
TEST(ExceptionWrapper, throwTest) {
std::runtime_error e("payload");
auto ew = make_exception_wrapper<std::runtime_error>(e);
std::vector<exception_wrapper> container;
container.push_back(ew);
EXPECT_THAT(
[&]() { container[0].throw_exception(); },
ThrowsMessage<std::runtime_error>(StrEq("payload")));
}
// Tests that when we call throw_with_nested, we can unnest it later.
TEST(ExceptionWrapper, throwWithNested) {
auto ew = make_exception_wrapper<std::runtime_error>("inner");
try {
ew.throw_with_nested(std::runtime_error("outer"));
// throw_with_nested is marked [[noreturn]] so an exception is guaranteed.
} catch (std::runtime_error& outer) {
EXPECT_STREQ(outer.what(), "outer");
EXPECT_THAT(
[&]() { std::rethrow_if_nested(outer); },
ThrowsMessage<std::runtime_error>(StrEq("inner")));
}
}
TEST(ExceptionWrapper, members) {
auto ew = exception_wrapper();
EXPECT_FALSE(bool(ew));
EXPECT_EQ(ew.what(), "");
EXPECT_EQ(ew.class_name(), "");
EXPECT_EQ(nullptr, std::as_const(ew).to_exception_ptr());
EXPECT_EQ(nullptr, ew.to_exception_ptr());
ew = make_exception_wrapper<std::runtime_error>("payload");
EXPECT_TRUE(bool(ew));
EXPECT_EQ(ew.what(), kRuntimeErrorClassName + ": payload");
EXPECT_EQ(ew.class_name(), kRuntimeErrorClassName);
}
TEST(ExceptionWrapper, tryAndCatchTest) {
auto ew4 = try_and_catch([] { throw 17; });
EXPECT_TRUE(bool(ew4));
EXPECT_TRUE(ew4.is_compatible_with<int>());
}
TEST(ExceptionWrapper, withExceptionTest) {
int expected = 23;
auto ew = exception_wrapper{std::make_exception_ptr(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); }));
// 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&) {}));
*/
// a move-only callback type:
cew.with_exception([v = std::make_unique<int>(7)](const std::exception&) {});
}
TEST(ExceptionWrapper, getOrMakeExceptionPtrTest) {
int expected = 23;
auto ew = exception_wrapper{std::make_exception_ptr(IntException(expected))};
std::exception_ptr eptr = ew.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, fromExceptionPtrEmpty) {
auto ep = std::exception_ptr();
auto ew = exception_wrapper{ep};
EXPECT_FALSE(bool(ew));
}
TEST(ExceptionWrapper, fromExceptionPtrExn) {
auto ep = std::make_exception_ptr(std::runtime_error("foo"));
auto ew = exception_wrapper{ep};
EXPECT_TRUE(bool(ew));
EXPECT_EQ(ep, std::as_const(ew).to_exception_ptr());
EXPECT_EQ(ep, ew.to_exception_ptr());
EXPECT_TRUE(ew.is_compatible_with<std::runtime_error>());
}
TEST(ExceptionWrapper, fromExceptionPtrAny) {
auto ep = std::make_exception_ptr<int>(12);
auto ew = exception_wrapper{ep};
EXPECT_TRUE(bool(ew));
EXPECT_EQ(ep, std::as_const(ew).to_exception_ptr());
EXPECT_EQ(ep, ew.to_exception_ptr());
EXPECT_TRUE(ew.is_compatible_with<int>());
}
TEST(ExceptionWrapper, withExceptionPtrEmpty) {
auto ew = exception_wrapper(std::exception_ptr());
EXPECT_EQ(nullptr, ew.type());
EXPECT_FALSE(bool(ew));
EXPECT_EQ(nullptr, ew.get_exception());
EXPECT_EQ(nullptr, ew.get_exception<std::exception>());
EXPECT_EQ(nullptr, ew.get_exception<int>());
EXPECT_FALSE(ew.has_exception_ptr());
EXPECT_EQ(nullptr, std::as_const(ew).to_exception_ptr());
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<std::exception>());
EXPECT_FALSE(ew.is_compatible_with<int>());
EXPECT_DEATH(ew.throw_exception(), "empty folly::exception_wrapper");
}
TEST(ExceptionWrapper, withSharedPtrTest) {
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_exception<std::exception>());
EXPECT_STREQ("foo", ew.get_exception<std::exception>()->what());
EXPECT_EQ(nullptr, ew.get_exception<int>());
EXPECT_TRUE(ew.has_exception_ptr());
EXPECT_NE(nullptr, std::as_const(ew).to_exception_ptr());
EXPECT_TRUE(ew.has_exception_ptr());
EXPECT_NE(nullptr, ew.to_exception_ptr());
EXPECT_TRUE(ew.has_exception_ptr());
EXPECT_EQ(kRuntimeErrorClassName, ew.class_name());
EXPECT_EQ(kRuntimeErrorClassName + ": foo", ew.what());
EXPECT_TRUE(ew.is_compatible_with<std::exception>());
EXPECT_TRUE(ew.is_compatible_with<std::runtime_error>());
EXPECT_FALSE(ew.is_compatible_with<int>());
EXPECT_THROW(ew.throw_exception(), std::runtime_error);
exception_wrapper(std::move(ew));
EXPECT_FALSE(bool(ew));
EXPECT_EQ(nullptr, ew.type());
EXPECT_EQ(nullptr, ew.get_exception());
EXPECT_EQ(nullptr, ew.get_exception<std::exception>());
EXPECT_EQ(nullptr, ew.get_exception<int>());
EXPECT_EQ(nullptr, std::as_const(ew).to_exception_ptr());
EXPECT_EQ(nullptr, ew.to_exception_ptr());
EXPECT_EQ("", ew.class_name());
EXPECT_EQ("", ew.what());
EXPECT_FALSE(ew.is_compatible_with<std::exception>());
EXPECT_FALSE(ew.is_compatible_with<std::runtime_error>());
EXPECT_FALSE(ew.is_compatible_with<int>());
}
TEST(ExceptionWrapper, withExceptionPtrExnTest) {
auto ep = std::make_exception_ptr(std::runtime_error("foo"));
auto ew = exception_wrapper(ep);
EXPECT_TRUE(bool(ew));
EXPECT_EQ(&typeid(std::runtime_error), ew.type());
EXPECT_NE(nullptr, ew.get_exception());
EXPECT_NE(nullptr, ew.get_exception<std::exception>());
EXPECT_STREQ("foo", ew.get_exception<std::exception>()->what());
EXPECT_EQ(nullptr, ew.get_exception<int>());
EXPECT_TRUE(ew.has_exception_ptr());
EXPECT_EQ(ep, ew.to_exception_ptr());
EXPECT_TRUE(ew.has_exception_ptr());
EXPECT_EQ(kRuntimeErrorClassName, ew.class_name());
EXPECT_EQ(kRuntimeErrorClassName + ": foo", ew.what());
EXPECT_TRUE(ew.is_compatible_with<std::exception>());
EXPECT_TRUE(ew.is_compatible_with<std::runtime_error>());
EXPECT_FALSE(ew.is_compatible_with<int>());
EXPECT_THROW(ew.throw_exception(), std::runtime_error);
exception_wrapper(std::move(ew));
EXPECT_FALSE(bool(ew));
EXPECT_EQ(nullptr, ew.type());
EXPECT_EQ(nullptr, ew.get_exception());
EXPECT_EQ(nullptr, ew.get_exception<std::exception>());
EXPECT_EQ(nullptr, ew.get_exception<int>());
EXPECT_EQ(nullptr, std::as_const(ew).to_exception_ptr());
EXPECT_EQ(nullptr, ew.to_exception_ptr());
EXPECT_EQ("", ew.class_name());
EXPECT_EQ("", ew.what());
EXPECT_FALSE(ew.is_compatible_with<std::exception>());
EXPECT_FALSE(ew.is_compatible_with<std::runtime_error>());
EXPECT_FALSE(ew.is_compatible_with<int>());
}
TEST(ExceptionWrapper, withExceptionPtrAnyTest) {
auto ep = std::make_exception_ptr<int>(12);
auto ew = exception_wrapper(ep);
EXPECT_TRUE(bool(ew));
EXPECT_EQ(nullptr, ew.get_exception());
EXPECT_EQ(nullptr, ew.get_exception<std::exception>());
EXPECT_NE(nullptr, ew.get_exception<int>());
EXPECT_EQ(12, *ew.get_exception<int>());
EXPECT_TRUE(ew.has_exception_ptr());
EXPECT_EQ(ep, std::as_const(ew).to_exception_ptr());
EXPECT_EQ(ep, ew.to_exception_ptr());
EXPECT_TRUE(ew.has_exception_ptr());
EXPECT_EQ(demangle(typeid(int)), ew.class_name());
EXPECT_EQ(demangle(typeid(int)), ew.what());
EXPECT_FALSE(ew.is_compatible_with<std::exception>());
EXPECT_FALSE(ew.is_compatible_with<std::runtime_error>());
EXPECT_TRUE(ew.is_compatible_with<int>());
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_exception<std::exception>());
EXPECT_EQ(nullptr, ew.get_exception<int>());
EXPECT_EQ(nullptr, std::as_const(ew).to_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<std::exception>());
EXPECT_FALSE(ew.is_compatible_with<std::runtime_error>());
EXPECT_FALSE(ew.is_compatible_with<int>());
}
TEST(ExceptionWrapper, withNonStdExceptionTest) {
auto ew = exception_wrapper(std::in_place, 42);
EXPECT_TRUE(bool(ew));
EXPECT_EQ(nullptr, ew.get_exception());
EXPECT_EQ(nullptr, ew.get_exception<std::exception>());
EXPECT_NE(nullptr, ew.get_exception<int>());
EXPECT_EQ(42, *ew.get_exception<int>());
EXPECT_TRUE(ew.has_exception_ptr());
EXPECT_EQ(demangle(typeid(int)), ew.class_name());
EXPECT_EQ(demangle(typeid(int)), ew.what());
EXPECT_NE(nullptr, std::as_const(ew).to_exception_ptr());
EXPECT_NE(nullptr, ew.to_exception_ptr());
EXPECT_TRUE(ew.has_exception_ptr());
EXPECT_EQ(demangle(typeid(int)), ew.class_name());
EXPECT_EQ(demangle(typeid(int)), ew.what());
EXPECT_FALSE(ew.is_compatible_with<std::exception>());
EXPECT_FALSE(ew.is_compatible_with<std::runtime_error>());
EXPECT_TRUE(ew.is_compatible_with<int>());
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_exception<std::exception>());
EXPECT_EQ(nullptr, ew.get_exception<int>());
EXPECT_EQ(nullptr, std::as_const(ew).to_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<std::exception>());
EXPECT_FALSE(ew.is_compatible_with<std::runtime_error>());
EXPECT_FALSE(ew.is_compatible_with<int>());
}
TEST(ExceptionWrapper, withExceptionPtrAnyNilTest) {
auto ep = std::make_exception_ptr<int>(12);
auto ew = exception_wrapper(ep);
EXPECT_TRUE(bool(ew));
EXPECT_EQ(nullptr, ew.get_exception());
EXPECT_EQ(nullptr, ew.get_exception<std::exception>());
EXPECT_NE(nullptr, ew.get_exception<int>());
EXPECT_EQ(12, *ew.get_exception<int>());
EXPECT_EQ(ep, std::as_const(ew).to_exception_ptr());
EXPECT_EQ(ep, ew.to_exception_ptr());
EXPECT_EQ("int", ew.class_name());
EXPECT_EQ("int", ew.what());
EXPECT_FALSE(ew.is_compatible_with<std::exception>());
EXPECT_FALSE(ew.is_compatible_with<std::runtime_error>());
EXPECT_TRUE(ew.is_compatible_with<int>());
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_exception<std::exception>());
EXPECT_EQ(nullptr, ew.get_exception<int>());
EXPECT_EQ(nullptr, std::as_const(ew).to_exception_ptr());
EXPECT_EQ(nullptr, ew.to_exception_ptr());
EXPECT_EQ("", ew.class_name());
EXPECT_EQ("", ew.what());
EXPECT_FALSE(ew.is_compatible_with<std::exception>());
EXPECT_FALSE(ew.is_compatible_with<std::runtime_error>());
EXPECT_FALSE(ew.is_compatible_with<int>());
}
TEST(ExceptionWrapper, withExceptionDeduction) {
auto ew = make_exception_wrapper<std::runtime_error>("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, withExceptionDeductionExnConst) {
auto ew = make_exception_wrapper<std::runtime_error>("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, withExceptionDeductionWrapConstExnConst) {
const auto cew = make_exception_wrapper<std::runtime_error>("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, withExceptionDeductionReturning) {
auto ew = make_exception_wrapper<std::runtime_error>("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 <typename T>
T& r_to_l(T v) {
return std::ref(v);
}
} // namespace
TEST(ExceptionWrapper, withExceptionDeductionFunctorLvalue) {
auto ew = make_exception_wrapper<std::runtime_error>("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, nonStdExceptionTest) {
int expected = 17;
auto ew = exception_wrapper{std::make_exception_ptr(expected)};
EXPECT_TRUE(bool(ew));
EXPECT_FALSE(ew.is_compatible_with<std::exception>());
EXPECT_TRUE(ew.is_compatible_with<int>());
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();
// throw_exception is marked [[noreturn]] so an exception is guaranteed.
} catch /* nolint */ (int& i) {
EXPECT_EQ(i, expected);
}
}
TEST(ExceptionWrapper, exceptionStr) {
auto ew = make_exception_wrapper<std::runtime_error>("argh");
EXPECT_EQ(kRuntimeErrorClassName + ": argh", exceptionStr(ew));
}
TEST(ExceptionWrapper, throwExceptionNoexception) {
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 BaseNonStdException {
virtual ~BaseNonStdException() {}
};
struct DerivedNonStdException : BaseNonStdException {};
} // namespace
TEST(ExceptionWrapper, baseDerivedNonStdExceptionTest) {
exception_wrapper ew{std::make_exception_ptr(DerivedNonStdException{})};
EXPECT_EQ(ew.type(), &typeid(DerivedNonStdException));
EXPECT_TRUE(ew.with_exception([](const DerivedNonStdException&) {}));
}
namespace {
struct ThrownException {};
struct InSituException : std::exception {
InSituException() = default;
InSituException(const InSituException&) noexcept {}
};
struct OnHeapException : std::exception {
OnHeapException() = default;
OnHeapException(const OnHeapException&) {}
};
} // namespace
TEST(ExceptionWrapper, makeWrapperNoArgs) {
EXPECT_THAT(
folly::make_exception_wrapper<ThrownException>().class_name(),
testing::Eq(demangle(typeid(ThrownException))));
EXPECT_THAT(
folly::make_exception_wrapper<InSituException>().class_name(),
testing::Eq(demangle(typeid(InSituException))));
EXPECT_THAT(
folly::make_exception_wrapper<OnHeapException>().class_name(),
testing::Eq(demangle(typeid(OnHeapException))));
}
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]{};
};
} // namespace
TEST(ExceptionWrapper, handleStdException) {
auto ep = std::make_exception_ptr(std::runtime_error{"hello world"});
exception_wrapper const ew_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&) { ADD_FAILURE(); },
[&](const std::runtime_error&) noexcept { handled = true; },
[](const std::exception&) { ADD_FAILURE(); },
[](...) { ADD_FAILURE(); });
};
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&) noexcept { ADD_FAILURE(); },
[&](const std::runtime_error&) { handled = true; },
[](const std::exception&) { ADD_FAILURE(); });
};
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&) { ADD_FAILURE(); },
[&](const std::runtime_error&) { handled = true; },
[](const std::exception&) { ADD_FAILURE(); },
[](const int&) { ADD_FAILURE(); });
};
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&) { ADD_FAILURE(); },
[&](const std::runtime_error& e) {
handled = true;
throw e;
},
[](const std::exception&) { ADD_FAILURE(); });
};
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, handleStdExceptionUnhandled) {
auto ep = std::make_exception_ptr(std::exception{});
exception_wrapper const ew_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&) { ADD_FAILURE(); },
[](const std::runtime_error&) { ADD_FAILURE(); },
[&](...) { 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, handleStdExceptionPropagated) {
auto ep = std::make_exception_ptr(std::runtime_error{"hello world"});
exception_wrapper const ew_eptr(ep);
exception_wrapper const ew_small(std::runtime_error{"hello world"});
exception_wrapper const ew_big(BigRuntimeError{"hello world"});
EXPECT_THROW(ew_eptr.handle(), std::runtime_error);
EXPECT_THROW(ew_small.handle(), std::runtime_error);
EXPECT_THROW(ew_big.handle(), std::runtime_error);
}
TEST(ExceptionWrapper, handleNonStdExceptionSmall) {
auto ep = std::make_exception_ptr(42);
exception_wrapper const ew_eptr1(ep);
exception_wrapper const ew_eptr2(ep);
exception_wrapper const ew_small(std::in_place, 42);
bool handled = false;
auto expect_int_yes_catch_all = [&](const exception_wrapper& ew) {
ew.handle(
[](const std::exception&) { ADD_FAILURE(); },
[&](...) { 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&) { ADD_FAILURE(); },
[&](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&) { ADD_FAILURE(); });
};
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, handleNonStdExceptionBig) {
auto ep = std::make_exception_ptr(BigNonStdError{});
exception_wrapper const ew_eptr1(ep);
exception_wrapper const ew_eptr2(ep);
exception_wrapper const ew_big(std::in_place, BigNonStdError{});
bool handled = false;
auto expect_int_yes_catch_all = [&](const exception_wrapper& ew) {
ew.handle(
[](const std::exception&) { ADD_FAILURE(); },
[&](...) { 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&) { ADD_FAILURE(); },
[&](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&) { ADD_FAILURE(); });
};
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{std::in_place, 42}), int);
}
TEST(ExceptionWrapper, handleNonStdExceptionRethrowBaseDerived) {
exception_wrapper ew{std::make_exception_ptr(DerivedNonStdException{})};
bool handled = false;
EXPECT_THROW(
ew.handle(
[&](const DerivedNonStdException& e) {
handled = true;
throw e;
},
[](const BaseNonStdException&) { ADD_FAILURE(); }),
DerivedNonStdException);
EXPECT_TRUE(handled);
handled = false;
EXPECT_THROW(
ew.handle(
[&](const DerivedNonStdException& e) {
handled = true;
throw e;
},
[](...) { ADD_FAILURE(); }),
DerivedNonStdException);
EXPECT_TRUE(handled);
}
TEST(ExceptionWrapper, selfSwapTest) {
exception_wrapper ew(std::runtime_error("hello world"));
folly::swap(ew, ew);
EXPECT_EQ(kRuntimeErrorClassName + ": hello world", ew.what());
auto& ew2 = ew;
ew = std::move(ew2); // should not crash
}
TEST(ExceptionWrapper, terminateWithTest) {
auto ew = make_exception_wrapper<int>(42);
EXPECT_DEATH(try { ew.terminate_with(); } catch (...){}, "int");
}