Add missing override and remove redundant virtual in folly
[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
22 using namespace folly;
23
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);
28
29   std::vector<exception_wrapper> container;
30   container.push_back(ew);
31
32   try {
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);
38   }
39 }
40
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");
50 }
51
52 TEST(ExceptionWrapper, equals) {
53   std::runtime_error e("payload");
54   auto ew1 = make_exception_wrapper<std::runtime_error>(e);
55   auto ew2 = ew1;
56   EXPECT_EQ(ew1, ew2);
57
58   auto ew3 = try_and_catch<std::exception>([&]() {
59     throw std::runtime_error("payload");
60   });
61   auto ew4 = try_and_catch<std::exception>([&]() {
62     ew3.throwException();
63   });
64   EXPECT_EQ(ew3, ew4);
65 }
66
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);
72   EXPECT_NE(ew1, ew2);
73
74   auto ew3 = make_exception_wrapper<std::runtime_error>(e1);
75   auto ew4 = make_exception_wrapper<std::runtime_error>(e1);
76   EXPECT_NE(ew3, ew4);
77
78   auto ew5 = try_and_catch<std::exception>([&]() {
79     throw e1;
80   });
81   auto ew6 = try_and_catch<std::exception>([&]() {
82     throw e1;
83   });
84   EXPECT_NE(ew5, ew6);
85 }
86
87 TEST(ExceptionWrapper, try_and_catch_test) {
88   std::string expected = "payload";
89
90   // Catch rightmost matching exception type
91   exception_wrapper ew = try_and_catch<std::exception, std::runtime_error>(
92     [=]() {
93       throw std::runtime_error(expected);
94     });
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>();
100   EXPECT_TRUE(rep);
101
102   // Changing order is like catching in wrong order. Beware of this in your
103   // code.
104   auto ew2 = try_and_catch<std::runtime_error, std::exception>([=]() {
105     throw std::runtime_error(expected);
106   });
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>();
112   EXPECT_TRUE(rep);
113
114   // Catches even if not rightmost.
115   auto ew3 = try_and_catch<std::exception, std::runtime_error>([]() {
116     throw std::exception();
117   });
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>();
122   EXPECT_FALSE(rep);
123
124   // If does not catch, throws.
125   EXPECT_THROW(
126     try_and_catch<std::runtime_error>([]() {
127       throw std::exception();
128     }),
129     std::exception);
130 }
131
132 class AbstractIntException : public std::exception {
133 public:
134   virtual int getInt() const = 0;
135 };
136
137 class IntException : public AbstractIntException {
138 public:
139   explicit IntException(int i)
140     : i_(i) {}
141
142   int getInt() const override { return i_; }
143   const char* what() const noexcept override {
144     what_ = folly::to<std::string>("int == ", i_);
145     return what_.c_str();
146   }
147
148 private:
149   int i_;
150   mutable std::string what_;
151 };
152
153 TEST(ExceptionWrapper, with_exception_test) {
154   int expected = 23;
155
156   // This works, and doesn't slice.
157   exception_wrapper ew = try_and_catch<std::exception, std::runtime_error>(
158     [=]() {
159       throw IntException(expected);
160     });
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);
166     });
167
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>(
171     [=]() {
172       throw IntException(expected);
173     });
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"
182 #endif
183       EXPECT_EQ(typeid(ie), typeid(IntException));
184 #if defined __clang__ && (__clang_major__ > 3 || __clang_minor__ >= 6)
185 # pragma clang diagnostic pop
186 #endif
187     });
188
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) {
193       SUCCEED();
194     });
195
196   // This won't even compile.  You can't use a function which takes a
197   // non-const reference with a const exception_wrapper.
198 /*
199   cew.with_exception<IntException>([&](IntException& ie) {
200       SUCCEED();
201     });
202 */
203 }
204
205 TEST(ExceptionWrapper, non_std_exception_test) {
206   int expected = 17;
207
208   exception_wrapper ew = try_and_catch<std::exception, int>(
209     [=]() {
210       throw expected;
211     });
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.
218   try {
219     ew.throwException();
220   } catch /* nolint */ (int& i) {
221     EXPECT_EQ(i, expected);
222   }
223 }
224
225
226 TEST(ExceptionWrapper, exceptionStr) {
227   auto ew = make_exception_wrapper<std::runtime_error>("argh");
228   EXPECT_EQ("std::runtime_error: argh", exceptionStr(ew));
229 }
230
231 namespace {
232 class TestException : public std::exception { };
233 void testEW(const exception_wrapper& ew) {
234   EXPECT_THROW(ew.throwException(), TestException);
235 }
236 }  // namespace
237
238 TEST(ExceptionWrapper, implicitConstruction) {
239   // Try with both lvalue and rvalue references
240   TestException e;
241   testEW(e);
242   testEW(TestException());
243 }