| /* |
| * 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. |
| * 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 <gtest/gtest.h> |
| #include <stdexcept> |
| #include <folly/ExceptionWrapper.h> |
| #include <folly/Conv.h> |
| |
| using namespace folly; |
| |
| // Tests that when we call throwException, the proper type is thrown (derived) |
| TEST(ExceptionWrapper, throw_test) { |
| std::runtime_error e("payload"); |
| auto ew = make_exception_wrapper<std::runtime_error>(e); |
| |
| std::vector<exception_wrapper> container; |
| container.push_back(ew); |
| |
| try { |
| container[0].throwException(); |
| } catch (std::runtime_error& e) { |
| std::string expected = "payload"; |
| std::string actual = e.what(); |
| 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<IntException>([&](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>([&](AbstractIntException& ie) { |
| EXPECT_EQ(ie.getInt(), expected); |
| #if defined __clang__ && (__clang_major__ > 3 || __clang_minor__ >= 6) |
| # pragma clang diagnostic push |
| # pragma clang diagnostic ignored "-Wunevaluated-expression" |
| #endif |
| EXPECT_EQ(typeid(ie), typeid(IntException)); |
| #if defined __clang__ && (__clang_major__ > 3 || __clang_minor__ >= 6) |
| # pragma clang diagnostic pop |
| #endif |
| }); |
| |
| // 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<IntException>([&](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>([&](IntException& ie) { |
| SUCCEED(); |
| }); |
| */ |
| } |
| |
| 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()); |
| } |