2 * Copyright 2016 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.h>
22 #include <folly/portability/GTest.h>
24 using namespace folly;
26 class AbstractIntException : public std::exception {
28 virtual int getInt() const = 0;
31 class IntException : public AbstractIntException {
33 explicit IntException(int i) : i_(i) {}
35 int getInt() const override { return i_; }
36 const char* what() const noexcept override {
37 what_ = folly::to<std::string>("int == ", i_);
43 mutable std::string what_;
46 const static std::string kExceptionClassName =
47 demangle(typeid(std::exception)).toStdString();
48 const static std::string kRuntimeErrorClassName =
49 demangle(typeid(std::runtime_error)).toStdString();
50 const static std::string kIntExceptionClassName =
51 demangle(typeid(IntException)).toStdString();
52 const static std::string kIntClassName = demangle(typeid(int)).toStdString();
54 // Tests that when we call throwException, the proper type is thrown (derived)
55 TEST(ExceptionWrapper, throw_test) {
56 std::runtime_error e("payload");
57 auto ew = make_exception_wrapper<std::runtime_error>(e);
59 std::vector<exception_wrapper> container;
60 container.push_back(ew);
63 container[0].throwException();
64 } catch (std::runtime_error& e) {
65 std::string expected = "payload";
66 std::string actual = e.what();
67 EXPECT_EQ(expected, actual);
71 TEST(ExceptionWrapper, members) {
72 auto ew = exception_wrapper();
73 EXPECT_FALSE(bool(ew));
74 EXPECT_EQ(ew.what(), "");
75 EXPECT_EQ(ew.class_name(), "");
76 ew = make_exception_wrapper<std::runtime_error>("payload");
77 EXPECT_TRUE(bool(ew));
78 EXPECT_EQ(ew.what(), kRuntimeErrorClassName + ": payload");
79 EXPECT_EQ(ew.class_name(), kRuntimeErrorClassName);
82 TEST(ExceptionWrapper, equals) {
83 std::runtime_error e("payload");
84 auto ew1 = make_exception_wrapper<std::runtime_error>(e);
88 auto ew3 = try_and_catch<std::exception>([&]() {
89 throw std::runtime_error("payload");
91 auto ew4 = try_and_catch<std::exception>([&]() {
97 TEST(ExceptionWrapper, not_equals) {
98 std::runtime_error e1("payload");
99 std::runtime_error e2("payload");
100 auto ew1 = make_exception_wrapper<std::runtime_error>(e1);
101 auto ew2 = make_exception_wrapper<std::runtime_error>(e2);
104 auto ew3 = make_exception_wrapper<std::runtime_error>(e1);
105 auto ew4 = make_exception_wrapper<std::runtime_error>(e1);
108 auto ew5 = try_and_catch<std::exception>([&]() {
111 auto ew6 = try_and_catch<std::exception>([&]() {
117 TEST(ExceptionWrapper, try_and_catch_test) {
118 std::string expected = "payload";
120 // Catch rightmost matching exception type
121 exception_wrapper ew = try_and_catch<std::exception, std::runtime_error>(
123 throw std::runtime_error(expected);
125 EXPECT_TRUE(bool(ew));
126 EXPECT_TRUE(ew.getCopied());
127 EXPECT_EQ(ew.what(), kRuntimeErrorClassName + ": payload");
128 EXPECT_EQ(ew.class_name(), kRuntimeErrorClassName);
129 auto rep = ew.is_compatible_with<std::runtime_error>();
132 // Changing order is like catching in wrong order. Beware of this in your
134 auto ew2 = try_and_catch<std::runtime_error, std::exception>([=]() {
135 throw std::runtime_error(expected);
137 EXPECT_TRUE(bool(ew2));
138 // We are catching a std::exception, not std::runtime_error.
139 EXPECT_FALSE(ew2.getCopied());
140 // But, we can still get the actual type if we want it.
141 rep = ew2.is_compatible_with<std::runtime_error>();
144 // Catches even if not rightmost.
145 auto ew3 = try_and_catch<std::exception, std::runtime_error>([]() {
146 throw std::exception();
148 EXPECT_TRUE(bool(ew3));
149 EXPECT_EQ(ew3.what(), kExceptionClassName + ": std::exception");
150 EXPECT_EQ(ew3.class_name(), kExceptionClassName);
151 rep = ew3.is_compatible_with<std::runtime_error>();
154 // If does not catch, throws.
156 try_and_catch<std::runtime_error>([]() {
157 throw std::exception();
162 TEST(ExceptionWrapper, with_exception_test) {
165 // This works, and doesn't slice.
166 exception_wrapper ew = try_and_catch<std::exception, std::runtime_error>(
168 throw IntException(expected);
170 EXPECT_TRUE(bool(ew));
171 EXPECT_EQ(ew.what(), kIntExceptionClassName + ": int == 23");
172 EXPECT_EQ(ew.class_name(), kIntExceptionClassName);
173 ew.with_exception([&](const IntException& ie) {
174 EXPECT_EQ(ie.getInt(), expected);
177 // I can try_and_catch a non-copyable base class. This will use
178 // std::exception_ptr internally.
179 exception_wrapper ew2 = try_and_catch<AbstractIntException>(
181 throw IntException(expected);
183 EXPECT_TRUE(bool(ew2));
184 EXPECT_EQ(ew2.what(), kIntExceptionClassName + ": int == 23");
185 EXPECT_EQ(ew2.class_name(), kIntExceptionClassName);
186 ew2.with_exception([&](AbstractIntException& ie) {
187 EXPECT_EQ(ie.getInt(), expected);
188 EXPECT_TRUE(dynamic_cast<IntException*>(&ie));
191 // Test with const this. If this compiles and does not crash due to
192 // infinite loop when it runs, it succeeds.
193 const exception_wrapper& cew = ew;
194 cew.with_exception([&](const IntException& /* ie */) { SUCCEED(); });
196 // This won't even compile. You can't use a function which takes a
197 // non-const reference with a const exception_wrapper.
199 cew.with_exception([&](IntException& ie) {
205 TEST(ExceptionWrapper, with_exception_deduction) {
206 auto ew = make_exception_wrapper<std::runtime_error>("hi");
207 EXPECT_TRUE(ew.with_exception([](std::runtime_error&) {}));
208 EXPECT_TRUE(ew.with_exception([](std::exception&) {}));
209 EXPECT_FALSE(ew.with_exception([](std::logic_error&) {}));
212 TEST(ExceptionWrapper, with_exception_deduction_exn_const) {
213 auto ew = make_exception_wrapper<std::runtime_error>("hi");
214 EXPECT_TRUE(ew.with_exception([](const std::runtime_error&) {}));
215 EXPECT_TRUE(ew.with_exception([](const std::exception&) {}));
216 EXPECT_FALSE(ew.with_exception([](const std::logic_error&) {}));
219 TEST(ExceptionWrapper, with_exception_deduction_wrap_const_exn_const) {
220 const auto cew = make_exception_wrapper<std::runtime_error>("hi");
221 EXPECT_TRUE(cew.with_exception([](const std::runtime_error&) {}));
222 EXPECT_TRUE(cew.with_exception([](const std::exception&) {}));
223 EXPECT_FALSE(cew.with_exception([](const std::logic_error&) {}));
226 TEST(ExceptionWrapper, with_exception_deduction_returning) {
227 auto ew = make_exception_wrapper<std::runtime_error>("hi");
228 EXPECT_TRUE(ew.with_exception([](std::runtime_error&) { return 3; }));
229 EXPECT_TRUE(ew.with_exception([](std::exception&) { return "hello"; }));
230 EXPECT_FALSE(ew.with_exception([](std::logic_error&) { return nullptr; }));
234 template <typename T>
235 T& r_to_l(T v) { return std::ref(v); }
238 TEST(ExceptionWrapper, with_exception_deduction_functor_lvalue) {
239 auto ew = make_exception_wrapper<std::runtime_error>("hi");
240 EXPECT_TRUE(ew.with_exception(r_to_l([](std::runtime_error&) {})));
241 EXPECT_TRUE(ew.with_exception(r_to_l([](std::exception&) {})));
242 EXPECT_FALSE(ew.with_exception(r_to_l([](std::logic_error&) {})));
245 TEST(ExceptionWrapper, non_std_exception_test) {
248 exception_wrapper ew = try_and_catch<std::exception, int>(
252 EXPECT_TRUE(bool(ew));
253 EXPECT_FALSE(ew.is_compatible_with<std::exception>());
254 EXPECT_EQ(ew.what(), kIntClassName);
255 EXPECT_EQ(ew.class_name(), kIntClassName);
256 // non-std::exception types are supported, but the only way to
257 // access their value is to explicity rethrow and catch it.
260 } catch /* nolint */ (int& i) {
261 EXPECT_EQ(i, expected);
266 TEST(ExceptionWrapper, exceptionStr) {
267 auto ew = make_exception_wrapper<std::runtime_error>("argh");
268 EXPECT_EQ(kRuntimeErrorClassName + ": argh", exceptionStr(ew));
272 class TestException : public std::exception { };
273 void testEW(const exception_wrapper& ew) {
274 EXPECT_THROW(ew.throwException(), TestException);
278 TEST(ExceptionWrapper, implicitConstruction) {
279 // Try with both lvalue and rvalue references
282 testEW(TestException());