/*
- * Copyright 2014 Facebook, Inc.
+ * Copyright 2015 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include <gtest/gtest.h>
#include <stdexcept>
-#include "folly/ExceptionWrapper.h"
+#include <folly/ExceptionWrapper.h>
+#include <folly/Conv.h>
+#include <folly/Portability.h>
using namespace folly;
EXPECT_EQ(expected, actual);
}
}
+
+TEST(ExceptionWrapper, members) {
+ auto ew = exception_wrapper();
+ EXPECT_FALSE(bool(ew));
+ EXPECT_EQ(ew.what(), "");
+ EXPECT_EQ(ew.class_name(), "");
+ ew = make_exception_wrapper<std::runtime_error>("payload");
+ EXPECT_TRUE(bool(ew));
+ EXPECT_EQ(ew.what(), "std::runtime_error: payload");
+ EXPECT_EQ(ew.class_name(), "std::runtime_error");
+}
+
+TEST(ExceptionWrapper, equals) {
+ std::runtime_error e("payload");
+ auto ew1 = make_exception_wrapper<std::runtime_error>(e);
+ auto ew2 = ew1;
+ EXPECT_EQ(ew1, ew2);
+
+ auto ew3 = try_and_catch<std::exception>([&]() {
+ throw std::runtime_error("payload");
+ });
+ auto ew4 = try_and_catch<std::exception>([&]() {
+ ew3.throwException();
+ });
+ EXPECT_EQ(ew3, ew4);
+}
+
+TEST(ExceptionWrapper, not_equals) {
+ std::runtime_error e1("payload");
+ std::runtime_error e2("payload");
+ auto ew1 = make_exception_wrapper<std::runtime_error>(e1);
+ auto ew2 = make_exception_wrapper<std::runtime_error>(e2);
+ EXPECT_NE(ew1, ew2);
+
+ auto ew3 = make_exception_wrapper<std::runtime_error>(e1);
+ auto ew4 = make_exception_wrapper<std::runtime_error>(e1);
+ EXPECT_NE(ew3, ew4);
+
+ auto ew5 = try_and_catch<std::exception>([&]() {
+ throw e1;
+ });
+ auto ew6 = try_and_catch<std::exception>([&]() {
+ throw e1;
+ });
+ EXPECT_NE(ew5, ew6);
+}
+
+TEST(ExceptionWrapper, try_and_catch_test) {
+ std::string expected = "payload";
+
+ // Catch rightmost matching exception type
+ exception_wrapper ew = try_and_catch<std::exception, std::runtime_error>(
+ [=]() {
+ throw std::runtime_error(expected);
+ });
+ EXPECT_TRUE(bool(ew));
+ EXPECT_TRUE(ew.getCopied());
+ EXPECT_EQ(ew.what(), "std::runtime_error: payload");
+ EXPECT_EQ(ew.class_name(), "std::runtime_error");
+ auto rep = ew.is_compatible_with<std::runtime_error>();
+ EXPECT_TRUE(rep);
+
+ // Changing order is like catching in wrong order. Beware of this in your
+ // code.
+ auto ew2 = try_and_catch<std::runtime_error, std::exception>([=]() {
+ throw std::runtime_error(expected);
+ });
+ EXPECT_TRUE(bool(ew2));
+ // We are catching a std::exception, not std::runtime_error.
+ EXPECT_FALSE(ew2.getCopied());
+ // But, we can still get the actual type if we want it.
+ rep = ew2.is_compatible_with<std::runtime_error>();
+ EXPECT_TRUE(rep);
+
+ // Catches even if not rightmost.
+ auto ew3 = try_and_catch<std::exception, std::runtime_error>([]() {
+ throw std::exception();
+ });
+ EXPECT_TRUE(bool(ew3));
+ EXPECT_EQ(ew3.what(), "std::exception: std::exception");
+ EXPECT_EQ(ew3.class_name(), "std::exception");
+ rep = ew3.is_compatible_with<std::runtime_error>();
+ EXPECT_FALSE(rep);
+
+ // If does not catch, throws.
+ EXPECT_THROW(
+ try_and_catch<std::runtime_error>([]() {
+ throw std::exception();
+ }),
+ std::exception);
+}
+
+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<std::string>("int == ", i_);
+ return what_.c_str();
+ }
+
+private:
+ int i_;
+ mutable std::string what_;
+};
+
+TEST(ExceptionWrapper, with_exception_test) {
+ int expected = 23;
+
+ // This works, and doesn't slice.
+ exception_wrapper ew = try_and_catch<std::exception, std::runtime_error>(
+ [=]() {
+ throw IntException(expected);
+ });
+ EXPECT_TRUE(bool(ew));
+ EXPECT_EQ(ew.what(), "IntException: int == 23");
+ EXPECT_EQ(ew.class_name(), "IntException");
+ 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<AbstractIntException>(
+ [=]() {
+ throw IntException(expected);
+ });
+ EXPECT_TRUE(bool(ew2));
+ EXPECT_EQ(ew2.what(), "IntException: int == 23");
+ EXPECT_EQ(ew2.class_name(), "IntException");
+ ew2.with_exception([&](AbstractIntException& ie) {
+ EXPECT_EQ(ie.getInt(), expected);
+ EXPECT_TRUE(dynamic_cast<IntException*>(&ie));
+ });
+
+ // 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;
+ cew.with_exception([&](const IntException& ie) {
+ SUCCEED();
+ });
+
+ // This won't even compile. You can't use a function which takes a
+ // non-const reference with a const exception_wrapper.
+/*
+ cew.with_exception([&](IntException& ie) {
+ SUCCEED();
+ });
+*/
+}
+
+TEST(ExceptionWrapper, with_exception_deduction) {
+ 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, with_exception_deduction_exn_const) {
+ 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, with_exception_deduction_wrap_const_exn_const) {
+ 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, with_exception_deduction_returning) {
+ 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); }
+}
+
+TEST(ExceptionWrapper, with_exception_deduction_functor_lvalue) {
+ 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, non_std_exception_test) {
+ int expected = 17;
+
+ exception_wrapper ew = try_and_catch<std::exception, int>(
+ [=]() {
+ throw expected;
+ });
+ EXPECT_TRUE(bool(ew));
+ EXPECT_FALSE(ew.is_compatible_with<std::exception>());
+ EXPECT_EQ(ew.what(), "int");
+ EXPECT_EQ(ew.class_name(), "int");
+ // non-std::exception types are supported, but the only way to
+ // access their value is to explicity rethrow and catch it.
+ try {
+ ew.throwException();
+ } catch /* nolint */ (int& i) {
+ EXPECT_EQ(i, expected);
+ }
+}
+
+
+TEST(ExceptionWrapper, exceptionStr) {
+ auto ew = make_exception_wrapper<std::runtime_error>("argh");
+ EXPECT_EQ("std::runtime_error: argh", exceptionStr(ew));
+}
+
+namespace {
+class TestException : public std::exception { };
+void testEW(const exception_wrapper& ew) {
+ EXPECT_THROW(ew.throwException(), TestException);
+}
+} // namespace
+
+TEST(ExceptionWrapper, implicitConstruction) {
+ // Try with both lvalue and rvalue references
+ TestException e;
+ testEW(e);
+ testEW(TestException());
+}