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>
22 using namespace folly;
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);
29 std::vector<exception_wrapper> container;
30 container.push_back(ew);
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);
41 TEST(ExceptionWrapper, members) {
42 auto ew = exception_wrapper();
43 EXPECT_FALSE(bool(ew));
44 EXPECT_EQ(ew.what(), "");
45 EXPECT_EQ(ew.class_name(), "");
46 ew = make_exception_wrapper<std::runtime_error>("payload");
47 EXPECT_TRUE(bool(ew));
48 EXPECT_EQ(ew.what(), "std::runtime_error: payload");
49 EXPECT_EQ(ew.class_name(), "std::runtime_error");
52 TEST(ExceptionWrapper, equals) {
53 std::runtime_error e("payload");
54 auto ew1 = make_exception_wrapper<std::runtime_error>(e);
58 auto ew3 = try_and_catch<std::exception>([&]() {
59 throw std::runtime_error("payload");
61 auto ew4 = try_and_catch<std::exception>([&]() {
67 TEST(ExceptionWrapper, not_equals) {
68 std::runtime_error e1("payload");
69 std::runtime_error e2("payload");
70 auto ew1 = make_exception_wrapper<std::runtime_error>(e1);
71 auto ew2 = make_exception_wrapper<std::runtime_error>(e2);
74 auto ew3 = make_exception_wrapper<std::runtime_error>(e1);
75 auto ew4 = make_exception_wrapper<std::runtime_error>(e1);
78 auto ew5 = try_and_catch<std::exception>([&]() {
81 auto ew6 = try_and_catch<std::exception>([&]() {
87 TEST(ExceptionWrapper, try_and_catch_test) {
88 std::string expected = "payload";
90 // Catch rightmost matching exception type
91 exception_wrapper ew = try_and_catch<std::exception, std::runtime_error>(
93 throw std::runtime_error(expected);
95 EXPECT_TRUE(bool(ew));
96 EXPECT_TRUE(ew.getCopied());
97 EXPECT_EQ(ew.what(), "std::runtime_error: payload");
98 EXPECT_EQ(ew.class_name(), "std::runtime_error");
99 auto rep = ew.is_compatible_with<std::runtime_error>();
102 // Changing order is like catching in wrong order. Beware of this in your
104 auto ew2 = try_and_catch<std::runtime_error, std::exception>([=]() {
105 throw std::runtime_error(expected);
107 EXPECT_TRUE(bool(ew2));
108 // We are catching a std::exception, not std::runtime_error.
109 EXPECT_FALSE(ew2.getCopied());
110 // But, we can still get the actual type if we want it.
111 rep = ew2.is_compatible_with<std::runtime_error>();
114 // Catches even if not rightmost.
115 auto ew3 = try_and_catch<std::exception, std::runtime_error>([]() {
116 throw std::exception();
118 EXPECT_TRUE(bool(ew3));
119 EXPECT_EQ(ew3.what(), "std::exception: std::exception");
120 EXPECT_EQ(ew3.class_name(), "std::exception");
121 rep = ew3.is_compatible_with<std::runtime_error>();
124 // If does not catch, throws.
126 try_and_catch<std::runtime_error>([]() {
127 throw std::exception();
132 class AbstractIntException : public std::exception {
134 virtual int getInt() const = 0;
137 class IntException : public AbstractIntException {
139 explicit IntException(int i)
142 virtual int getInt() const override { return i_; }
143 virtual const char* what() const noexcept override {
144 what_ = folly::to<std::string>("int == ", i_);
145 return what_.c_str();
150 mutable std::string what_;
153 TEST(ExceptionWrapper, with_exception_test) {
156 // This works, and doesn't slice.
157 exception_wrapper ew = try_and_catch<std::exception, std::runtime_error>(
159 throw IntException(expected);
161 EXPECT_TRUE(bool(ew));
162 EXPECT_EQ(ew.what(), "IntException: int == 23");
163 EXPECT_EQ(ew.class_name(), "IntException");
164 ew.with_exception<IntException>([&](const IntException& ie) {
165 EXPECT_EQ(ie.getInt(), expected);
168 // I can try_and_catch a non-copyable base class. This will use
169 // std::exception_ptr internally.
170 exception_wrapper ew2 = try_and_catch<AbstractIntException>(
172 throw IntException(expected);
174 EXPECT_TRUE(bool(ew2));
175 EXPECT_EQ(ew2.what(), "IntException: int == 23");
176 EXPECT_EQ(ew2.class_name(), "IntException");
177 ew2.with_exception<AbstractIntException>([&](AbstractIntException& ie) {
178 EXPECT_EQ(ie.getInt(), expected);
179 #if defined __clang__ && (__clang_major__ > 3 || __clang_minor__ >= 6)
180 # pragma clang diagnostic push
181 # pragma clang diagnostic ignored "-Wunevaluated-expression"
183 EXPECT_EQ(typeid(ie), typeid(IntException));
184 #if defined __clang__ && (__clang_major__ > 3 || __clang_minor__ >= 6)
185 # pragma clang diagnostic pop
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;
192 cew.with_exception<IntException>([&](const IntException& ie) {
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>([&](IntException& ie) {
205 TEST(ExceptionWrapper, non_std_exception_test) {
208 exception_wrapper ew = try_and_catch<std::exception, int>(
212 EXPECT_TRUE(bool(ew));
213 EXPECT_FALSE(ew.is_compatible_with<std::exception>());
214 EXPECT_EQ(ew.what(), "int");
215 EXPECT_EQ(ew.class_name(), "int");
216 // non-std::exception types are supported, but the only way to
217 // access their value is to explicity rethrow and catch it.
220 } catch /* nolint */ (int& i) {
221 EXPECT_EQ(i, expected);