e04b9f47d649e39cda355d2b0e083b2b37ca6d1a
[folly.git] / folly / test / ExceptionWrapperTest.cpp
1 /*
2  * Copyright 2014 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 <gtest/gtest.h>
18 #include <stdexcept>
19 #include <folly/ExceptionWrapper.h>
20 #include <folly/Conv.h>
21
22 using namespace folly;
23
24 // Tests that when we call throwException, the proper type is thrown (derived)
25 TEST(ExceptionWrapper, throw_test) {
26   std::runtime_error e("payload");
27   auto ew = make_exception_wrapper<std::runtime_error>(e);
28
29   std::vector<exception_wrapper> container;
30   container.push_back(ew);
31
32   try {
33     container[0].throwException();
34   } catch (std::runtime_error& e) {
35     std::string expected = "payload";
36     std::string actual = e.what();
37     EXPECT_EQ(expected, actual);
38   }
39 }
40
41 TEST(ExceptionWrapper, boolean) {
42   auto ew = exception_wrapper();
43   EXPECT_FALSE(bool(ew));
44   ew = make_exception_wrapper<std::runtime_error>("payload");
45   EXPECT_TRUE(bool(ew));
46 }
47
48 TEST(ExceptionWrapper, try_and_catch_test) {
49   std::string expected = "payload";
50
51   // Catch rightmost matching exception type
52   exception_wrapper ew = try_and_catch<std::exception, std::runtime_error>(
53     [=]() {
54       throw std::runtime_error(expected);
55     });
56   EXPECT_TRUE(bool(ew));
57   EXPECT_TRUE(ew.getCopied());
58   EXPECT_EQ(ew.what(), "std::runtime_error: payload");
59   auto rep = ew.is_compatible_with<std::runtime_error>();
60   EXPECT_TRUE(rep);
61
62   // Changing order is like catching in wrong order. Beware of this in your
63   // code.
64   auto ew2 = try_and_catch<std::runtime_error, std::exception>([=]() {
65     throw std::runtime_error(expected);
66   });
67   EXPECT_TRUE(bool(ew2));
68   // We are catching a std::exception, not std::runtime_error.
69   EXPECT_FALSE(ew2.getCopied());
70   // But, we can still get the actual type if we want it.
71   rep = ew2.is_compatible_with<std::runtime_error>();
72   EXPECT_TRUE(rep);
73
74   // Catches even if not rightmost.
75   auto ew3 = try_and_catch<std::exception, std::runtime_error>([]() {
76     throw std::exception();
77   });
78   EXPECT_TRUE(bool(ew3));
79   EXPECT_NE(ew3.what(), expected);
80   rep = ew3.is_compatible_with<std::runtime_error>();
81   EXPECT_FALSE(rep);
82
83   // If does not catch, throws.
84   EXPECT_THROW(
85     try_and_catch<std::runtime_error>([]() {
86       throw std::exception();
87     }),
88     std::exception);
89 }
90
91 class AbstractIntException : public std::exception {
92 public:
93   virtual int getInt() const = 0;
94 };
95
96 class IntException : public AbstractIntException {
97 public:
98   explicit IntException(int i)
99     : i_(i) {}
100
101   virtual int getInt() const { return i_; }
102   virtual const char* what() const noexcept override {
103     what_ = folly::to<std::string>("int == ", i_);
104     return what_.c_str();
105   }
106
107 private:
108   int i_;
109   mutable std::string what_;
110 };
111
112 TEST(ExceptionWrapper, with_exception_test) {
113   int expected = 23;
114
115   // This works, and doesn't slice.
116   exception_wrapper ew = try_and_catch<std::exception, std::runtime_error>(
117     [=]() {
118       throw IntException(expected);
119     });
120   EXPECT_TRUE(bool(ew));
121   EXPECT_EQ(ew.what(), "IntException: int == 23");
122   ew.with_exception<IntException>([&](const IntException& ie) {
123       EXPECT_EQ(ie.getInt(), expected);
124     });
125
126   // I can try_and_catch a non-copyable base class.  This will use
127   // std::exception_ptr internally.
128   exception_wrapper ew2 = try_and_catch<AbstractIntException>(
129     [=]() {
130       throw IntException(expected);
131     });
132   EXPECT_TRUE(bool(ew2));
133   EXPECT_EQ(ew2.what(), "IntException: int == 23");
134   ew2.with_exception<AbstractIntException>([&](AbstractIntException& ie) {
135       EXPECT_EQ(ie.getInt(), expected);
136       EXPECT_EQ(typeid(ie), typeid(IntException));
137     });
138 }
139
140 TEST(ExceptionWrapper, non_std_exception_test) {
141   int expected = 17;
142
143   exception_wrapper ew = try_and_catch<std::exception, int>(
144     [=]() {
145       throw expected;
146     });
147   EXPECT_TRUE(bool(ew));
148   EXPECT_FALSE(ew.is_compatible_with<std::exception>());
149   EXPECT_EQ(ew.what(), "int");
150   // non-std::exception types are supported, but the only way to
151   // access their value is to explicity rethrow and catch it.
152   try {
153     ew.throwException();
154   } catch (int& i) {
155     EXPECT_EQ(i, expected);
156   }
157 }