ThreadLocalDetail: fix bug just introduced w/ recent change to constexpr-ctor style...
[folly.git] / folly / test / ExceptionWrapperTest.cpp
1 /*
2  * Copyright 2015 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 #include <folly/Portability.h>
22
23 using namespace folly;
24
25 // Tests that when we call throwException, the proper type is thrown (derived)
26 TEST(ExceptionWrapper, throw_test) {
27   std::runtime_error e("payload");
28   auto ew = make_exception_wrapper<std::runtime_error>(e);
29
30   std::vector<exception_wrapper> container;
31   container.push_back(ew);
32
33   try {
34     container[0].throwException();
35   } catch (std::runtime_error& e) {
36     std::string expected = "payload";
37     std::string actual = e.what();
38     EXPECT_EQ(expected, actual);
39   }
40 }
41
42 TEST(ExceptionWrapper, members) {
43   auto ew = exception_wrapper();
44   EXPECT_FALSE(bool(ew));
45   EXPECT_EQ(ew.what(), "");
46   EXPECT_EQ(ew.class_name(), "");
47   ew = make_exception_wrapper<std::runtime_error>("payload");
48   EXPECT_TRUE(bool(ew));
49   EXPECT_EQ(ew.what(), "std::runtime_error: payload");
50   EXPECT_EQ(ew.class_name(), "std::runtime_error");
51 }
52
53 TEST(ExceptionWrapper, equals) {
54   std::runtime_error e("payload");
55   auto ew1 = make_exception_wrapper<std::runtime_error>(e);
56   auto ew2 = ew1;
57   EXPECT_EQ(ew1, ew2);
58
59   auto ew3 = try_and_catch<std::exception>([&]() {
60     throw std::runtime_error("payload");
61   });
62   auto ew4 = try_and_catch<std::exception>([&]() {
63     ew3.throwException();
64   });
65   EXPECT_EQ(ew3, ew4);
66 }
67
68 TEST(ExceptionWrapper, not_equals) {
69   std::runtime_error e1("payload");
70   std::runtime_error e2("payload");
71   auto ew1 = make_exception_wrapper<std::runtime_error>(e1);
72   auto ew2 = make_exception_wrapper<std::runtime_error>(e2);
73   EXPECT_NE(ew1, ew2);
74
75   auto ew3 = make_exception_wrapper<std::runtime_error>(e1);
76   auto ew4 = make_exception_wrapper<std::runtime_error>(e1);
77   EXPECT_NE(ew3, ew4);
78
79   auto ew5 = try_and_catch<std::exception>([&]() {
80     throw e1;
81   });
82   auto ew6 = try_and_catch<std::exception>([&]() {
83     throw e1;
84   });
85   EXPECT_NE(ew5, ew6);
86 }
87
88 TEST(ExceptionWrapper, try_and_catch_test) {
89   std::string expected = "payload";
90
91   // Catch rightmost matching exception type
92   exception_wrapper ew = try_and_catch<std::exception, std::runtime_error>(
93     [=]() {
94       throw std::runtime_error(expected);
95     });
96   EXPECT_TRUE(bool(ew));
97   EXPECT_TRUE(ew.getCopied());
98   EXPECT_EQ(ew.what(), "std::runtime_error: payload");
99   EXPECT_EQ(ew.class_name(), "std::runtime_error");
100   auto rep = ew.is_compatible_with<std::runtime_error>();
101   EXPECT_TRUE(rep);
102
103   // Changing order is like catching in wrong order. Beware of this in your
104   // code.
105   auto ew2 = try_and_catch<std::runtime_error, std::exception>([=]() {
106     throw std::runtime_error(expected);
107   });
108   EXPECT_TRUE(bool(ew2));
109   // We are catching a std::exception, not std::runtime_error.
110   EXPECT_FALSE(ew2.getCopied());
111   // But, we can still get the actual type if we want it.
112   rep = ew2.is_compatible_with<std::runtime_error>();
113   EXPECT_TRUE(rep);
114
115   // Catches even if not rightmost.
116   auto ew3 = try_and_catch<std::exception, std::runtime_error>([]() {
117     throw std::exception();
118   });
119   EXPECT_TRUE(bool(ew3));
120   EXPECT_EQ(ew3.what(), "std::exception: std::exception");
121   EXPECT_EQ(ew3.class_name(), "std::exception");
122   rep = ew3.is_compatible_with<std::runtime_error>();
123   EXPECT_FALSE(rep);
124
125   // If does not catch, throws.
126   EXPECT_THROW(
127     try_and_catch<std::runtime_error>([]() {
128       throw std::exception();
129     }),
130     std::exception);
131 }
132
133 class AbstractIntException : public std::exception {
134 public:
135   virtual int getInt() const = 0;
136 };
137
138 class IntException : public AbstractIntException {
139 public:
140   explicit IntException(int i)
141     : i_(i) {}
142
143   int getInt() const override { return i_; }
144   const char* what() const noexcept override {
145     what_ = folly::to<std::string>("int == ", i_);
146     return what_.c_str();
147   }
148
149 private:
150   int i_;
151   mutable std::string what_;
152 };
153
154 TEST(ExceptionWrapper, with_exception_test) {
155   int expected = 23;
156
157   // This works, and doesn't slice.
158   exception_wrapper ew = try_and_catch<std::exception, std::runtime_error>(
159     [=]() {
160       throw IntException(expected);
161     });
162   EXPECT_TRUE(bool(ew));
163   EXPECT_EQ(ew.what(), "IntException: int == 23");
164   EXPECT_EQ(ew.class_name(), "IntException");
165   ew.with_exception([&](const IntException& ie) {
166       EXPECT_EQ(ie.getInt(), expected);
167     });
168
169   // I can try_and_catch a non-copyable base class.  This will use
170   // std::exception_ptr internally.
171   exception_wrapper ew2 = try_and_catch<AbstractIntException>(
172     [=]() {
173       throw IntException(expected);
174     });
175   EXPECT_TRUE(bool(ew2));
176   EXPECT_EQ(ew2.what(), "IntException: int == 23");
177   EXPECT_EQ(ew2.class_name(), "IntException");
178   ew2.with_exception([&](AbstractIntException& ie) {
179       EXPECT_EQ(ie.getInt(), expected);
180       EXPECT_TRUE(dynamic_cast<IntException*>(&ie));
181     });
182
183   // Test with const this.  If this compiles and does not crash due to
184   // infinite loop when it runs, it succeeds.
185   const exception_wrapper& cew = ew;
186   cew.with_exception([&](const IntException& ie) {
187       SUCCEED();
188     });
189
190   // This won't even compile.  You can't use a function which takes a
191   // non-const reference with a const exception_wrapper.
192 /*
193   cew.with_exception([&](IntException& ie) {
194       SUCCEED();
195     });
196 */
197 }
198
199 TEST(ExceptionWrapper, with_exception_deduction) {
200   auto ew = make_exception_wrapper<std::runtime_error>("hi");
201   EXPECT_TRUE(ew.with_exception([](std::runtime_error&) {}));
202   EXPECT_TRUE(ew.with_exception([](std::exception&) {}));
203   EXPECT_FALSE(ew.with_exception([](std::logic_error&) {}));
204 }
205
206 TEST(ExceptionWrapper, with_exception_deduction_exn_const) {
207   auto ew = make_exception_wrapper<std::runtime_error>("hi");
208   EXPECT_TRUE(ew.with_exception([](const std::runtime_error&) {}));
209   EXPECT_TRUE(ew.with_exception([](const std::exception&) {}));
210   EXPECT_FALSE(ew.with_exception([](const std::logic_error&) {}));
211 }
212
213 TEST(ExceptionWrapper, with_exception_deduction_wrap_const_exn_const) {
214   const auto cew = make_exception_wrapper<std::runtime_error>("hi");
215   EXPECT_TRUE(cew.with_exception([](const std::runtime_error&) {}));
216   EXPECT_TRUE(cew.with_exception([](const std::exception&) {}));
217   EXPECT_FALSE(cew.with_exception([](const std::logic_error&) {}));
218 }
219
220 TEST(ExceptionWrapper, with_exception_deduction_returning) {
221   auto ew = make_exception_wrapper<std::runtime_error>("hi");
222   EXPECT_TRUE(ew.with_exception([](std::runtime_error&) { return 3; }));
223   EXPECT_TRUE(ew.with_exception([](std::exception&) { return "hello"; }));
224   EXPECT_FALSE(ew.with_exception([](std::logic_error&) { return nullptr; }));
225 }
226
227 namespace {
228 template <typename T>
229 T& r_to_l(T v) { return std::ref(v); }
230 }
231
232 TEST(ExceptionWrapper, with_exception_deduction_functor_lvalue) {
233   auto ew = make_exception_wrapper<std::runtime_error>("hi");
234   EXPECT_TRUE(ew.with_exception(r_to_l([](std::runtime_error&) {})));
235   EXPECT_TRUE(ew.with_exception(r_to_l([](std::exception&) {})));
236   EXPECT_FALSE(ew.with_exception(r_to_l([](std::logic_error&) {})));
237 }
238
239 TEST(ExceptionWrapper, non_std_exception_test) {
240   int expected = 17;
241
242   exception_wrapper ew = try_and_catch<std::exception, int>(
243     [=]() {
244       throw expected;
245     });
246   EXPECT_TRUE(bool(ew));
247   EXPECT_FALSE(ew.is_compatible_with<std::exception>());
248   EXPECT_EQ(ew.what(), "int");
249   EXPECT_EQ(ew.class_name(), "int");
250   // non-std::exception types are supported, but the only way to
251   // access their value is to explicity rethrow and catch it.
252   try {
253     ew.throwException();
254   } catch /* nolint */ (int& i) {
255     EXPECT_EQ(i, expected);
256   }
257 }
258
259
260 TEST(ExceptionWrapper, exceptionStr) {
261   auto ew = make_exception_wrapper<std::runtime_error>("argh");
262   EXPECT_EQ("std::runtime_error: argh", exceptionStr(ew));
263 }
264
265 namespace {
266 class TestException : public std::exception { };
267 void testEW(const exception_wrapper& ew) {
268   EXPECT_THROW(ew.throwException(), TestException);
269 }
270 }  // namespace
271
272 TEST(ExceptionWrapper, implicitConstruction) {
273   // Try with both lvalue and rvalue references
274   TestException e;
275   testEW(e);
276   testEW(TestException());
277 }