2 * Copyright 2017 Facebook, Inc.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include <folly/ExceptionWrapper.h>
20 #include <folly/Conv.h>
21 #include <folly/portability/GTest.h>
23 using namespace folly;
25 class AbstractIntException : public std::exception {
27 virtual int getInt() const = 0;
30 class IntException : public AbstractIntException {
32 explicit IntException(int i) : i_(i) {}
34 int getInt() const override { return i_; }
35 const char* what() const noexcept override {
36 what_ = folly::to<std::string>("int == ", i_);
42 mutable std::string what_;
45 const static std::string kExceptionClassName =
46 demangle(typeid(std::exception)).toStdString();
47 const static std::string kRuntimeErrorClassName =
48 demangle(typeid(std::runtime_error)).toStdString();
49 const static std::string kIntExceptionClassName =
50 demangle(typeid(IntException)).toStdString();
51 const static std::string kIntClassName = demangle(typeid(int)).toStdString();
53 // Tests that when we call throwException, the proper type is thrown (derived)
54 TEST(ExceptionWrapper, throw_test) {
55 std::runtime_error e("payload");
56 auto ew = make_exception_wrapper<std::runtime_error>(e);
58 std::vector<exception_wrapper> container;
59 container.push_back(ew);
62 container[0].throwException();
63 } catch (std::runtime_error& err) {
64 std::string expected = "payload";
65 std::string actual = err.what();
66 EXPECT_EQ(expected, actual);
70 TEST(ExceptionWrapper, members) {
71 auto ew = exception_wrapper();
72 EXPECT_FALSE(bool(ew));
73 EXPECT_EQ(ew.what(), "");
74 EXPECT_EQ(ew.class_name(), "");
75 ew = make_exception_wrapper<std::runtime_error>("payload");
76 EXPECT_TRUE(bool(ew));
77 EXPECT_EQ(ew.what(), kRuntimeErrorClassName + ": payload");
78 EXPECT_EQ(ew.class_name(), kRuntimeErrorClassName);
81 TEST(ExceptionWrapper, equals) {
82 std::runtime_error e("payload");
83 auto ew1 = make_exception_wrapper<std::runtime_error>(e);
87 auto ew3 = try_and_catch<std::exception>([&]() {
88 throw std::runtime_error("payload");
90 auto ew4 = try_and_catch<std::exception>([&]() {
96 TEST(ExceptionWrapper, not_equals) {
97 std::runtime_error e1("payload");
98 std::runtime_error e2("payload");
99 auto ew1 = make_exception_wrapper<std::runtime_error>(e1);
100 auto ew2 = make_exception_wrapper<std::runtime_error>(e2);
103 auto ew3 = make_exception_wrapper<std::runtime_error>(e1);
104 auto ew4 = make_exception_wrapper<std::runtime_error>(e1);
107 auto ew5 = try_and_catch<std::exception>([&]() {
110 auto ew6 = try_and_catch<std::exception>([&]() {
116 TEST(ExceptionWrapper, try_and_catch_test) {
117 std::string expected = "payload";
119 // Catch rightmost matching exception type
120 exception_wrapper ew = try_and_catch<std::exception, std::runtime_error>(
122 throw std::runtime_error(expected);
124 EXPECT_TRUE(bool(ew));
125 EXPECT_TRUE(ew.getCopied());
126 EXPECT_EQ(ew.what(), kRuntimeErrorClassName + ": payload");
127 EXPECT_EQ(ew.class_name(), kRuntimeErrorClassName);
128 auto rep = ew.is_compatible_with<std::runtime_error>();
131 // Changing order is like catching in wrong order. Beware of this in your
133 auto ew2 = try_and_catch<std::runtime_error, std::exception>([=]() {
134 throw std::runtime_error(expected);
136 EXPECT_TRUE(bool(ew2));
137 // We are catching a std::exception, not std::runtime_error.
138 EXPECT_FALSE(ew2.getCopied());
139 // But, we can still get the actual type if we want it.
140 rep = ew2.is_compatible_with<std::runtime_error>();
143 // Catches even if not rightmost.
144 auto ew3 = try_and_catch<std::exception, std::runtime_error>([]() {
145 throw std::exception();
147 EXPECT_TRUE(bool(ew3));
148 EXPECT_EQ(ew3.what(), kExceptionClassName + ": std::exception");
149 EXPECT_EQ(ew3.class_name(), kExceptionClassName);
150 rep = ew3.is_compatible_with<std::runtime_error>();
153 // If does not catch, throws.
155 try_and_catch<std::runtime_error>([]() {
156 throw std::exception();
161 TEST(ExceptionWrapper, with_exception_test) {
164 // This works, and doesn't slice.
165 exception_wrapper ew = try_and_catch<std::exception, std::runtime_error>(
167 throw IntException(expected);
169 EXPECT_TRUE(bool(ew));
170 EXPECT_EQ(ew.what(), kIntExceptionClassName + ": int == 23");
171 EXPECT_EQ(ew.class_name(), kIntExceptionClassName);
172 EXPECT_TRUE(ew.with_exception(
173 [&](const IntException& ie) { EXPECT_EQ(ie.getInt(), expected); }));
175 // I can try_and_catch a non-copyable base class. This will use
176 // std::exception_ptr internally.
177 exception_wrapper ew2 = try_and_catch<AbstractIntException>(
179 throw IntException(expected);
181 EXPECT_TRUE(bool(ew2));
182 EXPECT_EQ(ew2.what(), kIntExceptionClassName + ": int == 23");
183 EXPECT_EQ(ew2.class_name(), kIntExceptionClassName);
184 EXPECT_TRUE(ew2.with_exception([&](AbstractIntException& ie) {
185 EXPECT_EQ(ie.getInt(), expected);
186 EXPECT_TRUE(dynamic_cast<IntException*>(&ie));
189 // Test with const this. If this compiles and does not crash due to
190 // infinite loop when it runs, it succeeds.
191 const exception_wrapper& cew = ew;
193 cew.with_exception([&](const IntException& /* ie */) { SUCCEED(); }));
195 // Test with empty ew.
196 exception_wrapper empty_ew;
198 empty_ew.with_exception([&](const std::exception& /* ie */) { FAIL(); }));
200 // Testing with const exception_wrapper; sanity check first:
201 EXPECT_FALSE(cew.with_exception([&](const std::runtime_error&) {}));
202 EXPECT_FALSE(cew.with_exception([&](const int&) {}));
203 // This won't even compile. You can't use a function which takes a
204 // non-const reference with a const exception_wrapper.
206 EXPECT_FALSE(cew.with_exception([&](std::runtime_error&) {}));
207 EXPECT_FALSE(cew.with_exception([&](int&) {}));
211 TEST(ExceptionWrapper, get_or_make_exception_ptr_test) {
214 // This works, and doesn't slice.
215 exception_wrapper ew = try_and_catch<std::exception, std::runtime_error>(
216 [=]() { throw IntException(expected); });
217 std::exception_ptr eptr = ew.to_exception_ptr();
218 EXPECT_THROW(std::rethrow_exception(eptr), IntException);
220 // I can try_and_catch a non-copyable base class. This will use
221 // std::exception_ptr internally.
222 exception_wrapper ew2 = try_and_catch<AbstractIntException>(
223 [=]() { throw IntException(expected); });
224 eptr = ew2.to_exception_ptr();
225 EXPECT_THROW(std::rethrow_exception(eptr), IntException);
227 // Test with const this.
228 const exception_wrapper& cew = ew;
229 eptr = cew.to_exception_ptr();
230 EXPECT_THROW(std::rethrow_exception(eptr), IntException);
232 // Test with empty ew.
233 exception_wrapper empty_ew;
234 eptr = empty_ew.to_exception_ptr();
238 TEST(ExceptionWrapper, with_exception_deduction) {
239 auto ew = make_exception_wrapper<std::runtime_error>("hi");
240 EXPECT_TRUE(ew.with_exception([](std::runtime_error&) {}));
241 EXPECT_TRUE(ew.with_exception([](std::exception&) {}));
242 EXPECT_FALSE(ew.with_exception([](std::logic_error&) {}));
245 TEST(ExceptionWrapper, with_exception_deduction_exn_const) {
246 auto ew = make_exception_wrapper<std::runtime_error>("hi");
247 EXPECT_TRUE(ew.with_exception([](const std::runtime_error&) {}));
248 EXPECT_TRUE(ew.with_exception([](const std::exception&) {}));
249 EXPECT_FALSE(ew.with_exception([](const std::logic_error&) {}));
252 TEST(ExceptionWrapper, with_exception_deduction_wrap_const_exn_const) {
253 const auto cew = make_exception_wrapper<std::runtime_error>("hi");
254 EXPECT_TRUE(cew.with_exception([](const std::runtime_error&) {}));
255 EXPECT_TRUE(cew.with_exception([](const std::exception&) {}));
256 EXPECT_FALSE(cew.with_exception([](const std::logic_error&) {}));
259 TEST(ExceptionWrapper, with_exception_deduction_returning) {
260 auto ew = make_exception_wrapper<std::runtime_error>("hi");
261 EXPECT_TRUE(ew.with_exception([](std::runtime_error&) { return 3; }));
262 EXPECT_TRUE(ew.with_exception([](std::exception&) { return "hello"; }));
263 EXPECT_FALSE(ew.with_exception([](std::logic_error&) { return nullptr; }));
267 template <typename T>
268 T& r_to_l(T v) { return std::ref(v); }
271 TEST(ExceptionWrapper, with_exception_deduction_functor_lvalue) {
272 auto ew = make_exception_wrapper<std::runtime_error>("hi");
273 EXPECT_TRUE(ew.with_exception(r_to_l([](std::runtime_error&) {})));
274 EXPECT_TRUE(ew.with_exception(r_to_l([](std::exception&) {})));
275 EXPECT_FALSE(ew.with_exception(r_to_l([](std::logic_error&) {})));
278 TEST(ExceptionWrapper, non_std_exception_test) {
281 exception_wrapper ew = try_and_catch<std::exception, int>(
285 EXPECT_TRUE(bool(ew));
286 EXPECT_FALSE(ew.is_compatible_with<std::exception>());
287 EXPECT_TRUE(ew.is_compatible_with<int>());
288 EXPECT_EQ(ew.what(), kIntClassName);
289 EXPECT_EQ(ew.class_name(), kIntClassName);
290 // non-std::exception types are supported, but the only way to
291 // access their value is to explicity rethrow and catch it.
294 } catch /* nolint */ (int& i) {
295 EXPECT_EQ(i, expected);
300 TEST(ExceptionWrapper, exceptionStr) {
301 auto ew = make_exception_wrapper<std::runtime_error>("argh");
302 EXPECT_EQ(kRuntimeErrorClassName + ": argh", exceptionStr(ew));
305 TEST(ExceptionWrapper, throwException_noException) {
306 exception_wrapper ew;
307 ASSERT_DEATH(ew.throwException(), "empty folly::exception_wrapper");
311 class TestException : public std::exception { };
312 void testEW(const exception_wrapper& ew) {
313 EXPECT_THROW(ew.throwException(), TestException);
317 TEST(ExceptionWrapper, implicitConstruction) {
318 // Try with both lvalue and rvalue references
321 testEW(TestException());