equality for exception_wrapper
[folly.git] / folly / test / ExceptionWrapperTest.cpp
1 /*
2  * Copyright 2014 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, boolean) {
42   auto ew = exception_wrapper();
43   EXPECT_FALSE(bool(ew));
44   ew = make_exception_wrapper<std::runtime_error>("payload");
45   EXPECT_TRUE(bool(ew));
46 }
47
48 TEST(ExceptionWrapper, equals) {
49   std::runtime_error e("payload");
50   auto ew1 = make_exception_wrapper<std::runtime_error>(e);
51   auto ew2 = ew1;
52   EXPECT_EQ(ew1, ew2);
53
54   auto ew3 = try_and_catch<std::exception>([&]() {
55     throw std::runtime_error("payload");
56   });
57   auto ew4 = try_and_catch<std::exception>([&]() {
58     ew3.throwException();
59   });
60   EXPECT_EQ(ew3, ew4);
61 }
62
63 TEST(ExceptionWrapper, not_equals) {
64   std::runtime_error e1("payload");
65   std::runtime_error e2("payload");
66   auto ew1 = make_exception_wrapper<std::runtime_error>(e1);
67   auto ew2 = make_exception_wrapper<std::runtime_error>(e2);
68   EXPECT_NE(ew1, ew2);
69
70   auto ew3 = make_exception_wrapper<std::runtime_error>(e1);
71   auto ew4 = make_exception_wrapper<std::runtime_error>(e1);
72   EXPECT_NE(ew3, ew4);
73
74   auto ew5 = try_and_catch<std::exception>([&]() {
75     throw e1;
76   });
77   auto ew6 = try_and_catch<std::exception>([&]() {
78     throw e1;
79   });
80   EXPECT_NE(ew5, ew6);
81 }
82
83 TEST(ExceptionWrapper, try_and_catch_test) {
84   std::string expected = "payload";
85
86   // Catch rightmost matching exception type
87   exception_wrapper ew = try_and_catch<std::exception, std::runtime_error>(
88     [=]() {
89       throw std::runtime_error(expected);
90     });
91   EXPECT_TRUE(bool(ew));
92   EXPECT_TRUE(ew.getCopied());
93   EXPECT_EQ(ew.what(), "std::runtime_error: payload");
94   auto rep = ew.is_compatible_with<std::runtime_error>();
95   EXPECT_TRUE(rep);
96
97   // Changing order is like catching in wrong order. Beware of this in your
98   // code.
99   auto ew2 = try_and_catch<std::runtime_error, std::exception>([=]() {
100     throw std::runtime_error(expected);
101   });
102   EXPECT_TRUE(bool(ew2));
103   // We are catching a std::exception, not std::runtime_error.
104   EXPECT_FALSE(ew2.getCopied());
105   // But, we can still get the actual type if we want it.
106   rep = ew2.is_compatible_with<std::runtime_error>();
107   EXPECT_TRUE(rep);
108
109   // Catches even if not rightmost.
110   auto ew3 = try_and_catch<std::exception, std::runtime_error>([]() {
111     throw std::exception();
112   });
113   EXPECT_TRUE(bool(ew3));
114   EXPECT_NE(ew3.what(), expected);
115   rep = ew3.is_compatible_with<std::runtime_error>();
116   EXPECT_FALSE(rep);
117
118   // If does not catch, throws.
119   EXPECT_THROW(
120     try_and_catch<std::runtime_error>([]() {
121       throw std::exception();
122     }),
123     std::exception);
124 }
125
126 class AbstractIntException : public std::exception {
127 public:
128   virtual int getInt() const = 0;
129 };
130
131 class IntException : public AbstractIntException {
132 public:
133   explicit IntException(int i)
134     : i_(i) {}
135
136   virtual int getInt() const { return i_; }
137   virtual const char* what() const noexcept override {
138     what_ = folly::to<std::string>("int == ", i_);
139     return what_.c_str();
140   }
141
142 private:
143   int i_;
144   mutable std::string what_;
145 };
146
147 TEST(ExceptionWrapper, with_exception_test) {
148   int expected = 23;
149
150   // This works, and doesn't slice.
151   exception_wrapper ew = try_and_catch<std::exception, std::runtime_error>(
152     [=]() {
153       throw IntException(expected);
154     });
155   EXPECT_TRUE(bool(ew));
156   EXPECT_EQ(ew.what(), "IntException: int == 23");
157   ew.with_exception<IntException>([&](const IntException& ie) {
158       EXPECT_EQ(ie.getInt(), expected);
159     });
160
161   // I can try_and_catch a non-copyable base class.  This will use
162   // std::exception_ptr internally.
163   exception_wrapper ew2 = try_and_catch<AbstractIntException>(
164     [=]() {
165       throw IntException(expected);
166     });
167   EXPECT_TRUE(bool(ew2));
168   EXPECT_EQ(ew2.what(), "IntException: int == 23");
169   ew2.with_exception<AbstractIntException>([&](AbstractIntException& ie) {
170       EXPECT_EQ(ie.getInt(), expected);
171       EXPECT_EQ(typeid(ie), typeid(IntException));
172     });
173 }
174
175 TEST(ExceptionWrapper, non_std_exception_test) {
176   int expected = 17;
177
178   exception_wrapper ew = try_and_catch<std::exception, int>(
179     [=]() {
180       throw expected;
181     });
182   EXPECT_TRUE(bool(ew));
183   EXPECT_FALSE(ew.is_compatible_with<std::exception>());
184   EXPECT_EQ(ew.what(), "int");
185   // non-std::exception types are supported, but the only way to
186   // access their value is to explicity rethrow and catch it.
187   try {
188     ew.throwException();
189   } catch (int& i) {
190     EXPECT_EQ(i, expected);
191   }
192 }