2 * Copyright 2015 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.
17 #include <gtest/gtest.h>
19 #include <folly/ExceptionWrapper.h>
20 #include <folly/Conv.h>
21 #include <folly/Portability.h>
23 using namespace folly;
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);
30 std::vector<exception_wrapper> container;
31 container.push_back(ew);
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);
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");
53 TEST(ExceptionWrapper, equals) {
54 std::runtime_error e("payload");
55 auto ew1 = make_exception_wrapper<std::runtime_error>(e);
59 auto ew3 = try_and_catch<std::exception>([&]() {
60 throw std::runtime_error("payload");
62 auto ew4 = try_and_catch<std::exception>([&]() {
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);
75 auto ew3 = make_exception_wrapper<std::runtime_error>(e1);
76 auto ew4 = make_exception_wrapper<std::runtime_error>(e1);
79 auto ew5 = try_and_catch<std::exception>([&]() {
82 auto ew6 = try_and_catch<std::exception>([&]() {
88 TEST(ExceptionWrapper, try_and_catch_test) {
89 std::string expected = "payload";
91 // Catch rightmost matching exception type
92 exception_wrapper ew = try_and_catch<std::exception, std::runtime_error>(
94 throw std::runtime_error(expected);
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>();
103 // Changing order is like catching in wrong order. Beware of this in your
105 auto ew2 = try_and_catch<std::runtime_error, std::exception>([=]() {
106 throw std::runtime_error(expected);
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>();
115 // Catches even if not rightmost.
116 auto ew3 = try_and_catch<std::exception, std::runtime_error>([]() {
117 throw std::exception();
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>();
125 // If does not catch, throws.
127 try_and_catch<std::runtime_error>([]() {
128 throw std::exception();
133 class AbstractIntException : public std::exception {
135 virtual int getInt() const = 0;
138 class IntException : public AbstractIntException {
140 explicit IntException(int i)
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();
151 mutable std::string what_;
154 TEST(ExceptionWrapper, with_exception_test) {
157 // This works, and doesn't slice.
158 exception_wrapper ew = try_and_catch<std::exception, std::runtime_error>(
160 throw IntException(expected);
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);
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>(
173 throw IntException(expected);
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 #if __CLANG_PREREQ(3, 6)
181 # pragma clang diagnostic push
182 # pragma clang diagnostic ignored "-Wunevaluated-expression"
184 EXPECT_EQ(typeid(ie), typeid(IntException));
185 #if __CLANG_PREREQ(3, 6)
186 # pragma clang diagnostic pop
190 // Test with const this. If this compiles and does not crash due to
191 // infinite loop when it runs, it succeeds.
192 const exception_wrapper& cew = ew;
193 cew.with_exception([&](const IntException& ie) {
197 // This won't even compile. You can't use a function which takes a
198 // non-const reference with a const exception_wrapper.
200 cew.with_exception([&](IntException& ie) {
206 TEST(ExceptionWrapper, with_exception_deduction) {
207 auto ew = make_exception_wrapper<std::runtime_error>("hi");
208 EXPECT_TRUE(ew.with_exception([](std::runtime_error&) {}));
209 EXPECT_TRUE(ew.with_exception([](std::exception&) {}));
210 EXPECT_FALSE(ew.with_exception([](std::logic_error&) {}));
213 TEST(ExceptionWrapper, with_exception_deduction_exn_const) {
214 auto ew = make_exception_wrapper<std::runtime_error>("hi");
215 EXPECT_TRUE(ew.with_exception([](const std::runtime_error&) {}));
216 EXPECT_TRUE(ew.with_exception([](const std::exception&) {}));
217 EXPECT_FALSE(ew.with_exception([](const std::logic_error&) {}));
220 TEST(ExceptionWrapper, with_exception_deduction_wrap_const_exn_const) {
221 const auto cew = make_exception_wrapper<std::runtime_error>("hi");
222 EXPECT_TRUE(cew.with_exception([](const std::runtime_error&) {}));
223 EXPECT_TRUE(cew.with_exception([](const std::exception&) {}));
224 EXPECT_FALSE(cew.with_exception([](const std::logic_error&) {}));
227 TEST(ExceptionWrapper, with_exception_deduction_returning) {
228 auto ew = make_exception_wrapper<std::runtime_error>("hi");
229 EXPECT_TRUE(ew.with_exception([](std::runtime_error&) { return 3; }));
230 EXPECT_TRUE(ew.with_exception([](std::exception&) { return "hello"; }));
231 EXPECT_FALSE(ew.with_exception([](std::logic_error&) { return nullptr; }));
235 template <typename T>
236 T& r_to_l(T v) { return std::ref(v); }
239 TEST(ExceptionWrapper, with_exception_deduction_functor_lvalue) {
240 auto ew = make_exception_wrapper<std::runtime_error>("hi");
241 EXPECT_TRUE(ew.with_exception(r_to_l([](std::runtime_error&) {})));
242 EXPECT_TRUE(ew.with_exception(r_to_l([](std::exception&) {})));
243 EXPECT_FALSE(ew.with_exception(r_to_l([](std::logic_error&) {})));
246 TEST(ExceptionWrapper, non_std_exception_test) {
249 exception_wrapper ew = try_and_catch<std::exception, int>(
253 EXPECT_TRUE(bool(ew));
254 EXPECT_FALSE(ew.is_compatible_with<std::exception>());
255 EXPECT_EQ(ew.what(), "int");
256 EXPECT_EQ(ew.class_name(), "int");
257 // non-std::exception types are supported, but the only way to
258 // access their value is to explicity rethrow and catch it.
261 } catch /* nolint */ (int& i) {
262 EXPECT_EQ(i, expected);
267 TEST(ExceptionWrapper, exceptionStr) {
268 auto ew = make_exception_wrapper<std::runtime_error>("argh");
269 EXPECT_EQ("std::runtime_error: argh", exceptionStr(ew));
273 class TestException : public std::exception { };
274 void testEW(const exception_wrapper& ew) {
275 EXPECT_THROW(ew.throwException(), TestException);
279 TEST(ExceptionWrapper, implicitConstruction) {
280 // Try with both lvalue and rvalue references
283 testEW(TestException());