Fix copyright lines
[folly.git] / folly / test / UncaughtExceptionsTest.cpp
1 /*
2  * Copyright 2016-present 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 <folly/Conv.h>
18 #include <folly/UncaughtExceptions.h>
19 #include <folly/portability/GTest.h>
20 #include <glog/logging.h>
21
22 /*
23  * Test helper class, when goes out of scope it validaes that
24  * folly::uncaught_exceptions() returns the specified
25  * value.
26  */
27 class Validator {
28  public:
29   Validator(int expectedCount, const std::string& msg)
30       : expectedCount_(expectedCount), msg_(msg) {}
31
32   // Automatic validation during destruction.
33   ~Validator() {
34     validate();
35   }
36
37   // Invoke to validate explicitly.
38   void validate() {
39     LOG(INFO) << msg_ << ", expected " << expectedCount_ << ", is "
40               << folly::uncaught_exceptions();
41     EXPECT_EQ(expectedCount_, folly::uncaught_exceptions()) << msg_;
42   }
43
44  private:
45   const int32_t expectedCount_;
46   const std::string msg_;
47 };
48
49 TEST(UncaughtExceptions, no_exception) {
50   Validator validator(0, "no_exception");
51 }
52
53 TEST(UncaughtExceptions, no_uncaught_exception) {
54   Validator validator(0, "no_uncaught_exception");
55   try {
56     throw std::runtime_error("exception");
57   } catch (const std::runtime_error& e) {
58     validator.validate();
59   }
60 }
61
62 TEST(UncaughtExceptions, one_uncaught_exception) {
63   try {
64     Validator validator(1, "one_uncaught_exception");
65     throw std::runtime_error("exception");
66   } catch (const std::runtime_error& e) {
67   }
68 }
69
70 TEST(UncaughtExceptions, catch_rethrow) {
71   try {
72     Validator validatorOuter(1, "catch_rethrow_outer");
73     try {
74       Validator validatorInner(1, "catch_rethrow_inner");
75       throw std::runtime_error("exception");
76     } catch (const std::runtime_error& e) {
77       EXPECT_EQ(0, folly::uncaught_exceptions());
78       Validator validatorRethrow(1, "catch_rethrow");
79       throw;
80     }
81   } catch (const std::runtime_error& e) {
82     EXPECT_EQ(0, folly::uncaught_exceptions());
83   }
84 }
85
86 [[noreturn]] void throwingFunction() {
87   Validator validator(1, "one_uncaught_exception_in_function");
88   throw std::runtime_error("exception");
89 }
90
91 TEST(UncaughtExceptions, one_uncaught_exception_in_function) {
92   EXPECT_THROW({ throwingFunction(); }, std::runtime_error);
93 }
94
95 /*
96  * Test helper class. Generates N wrapped classes/objects.
97  * The destructor N of the most outer class creates the N-1
98  * object, and N - 1 object destructor creating the N-2 object,
99  * and so on. Each destructor throws an exception after creating
100  * the inner object on the stack, thus the number of exceptions
101  * accumulates while the stack is unwinding. It's used to test
102  * the folly::uncaught_exceptions() with value >= 2.
103  */
104 template <size_t N, size_t I = N>
105 struct ThrowInDestructor {
106   using InnerThrowInDestructor = ThrowInDestructor<N, I - 1>;
107   ThrowInDestructor() {}
108
109   ~ThrowInDestructor() {
110     try {
111       InnerThrowInDestructor stackObjectThrowingOnUnwind;
112       (void)stackObjectThrowingOnUnwind;
113       Validator validator(
114           N - I + 1, "validating in " + folly::to<std::string>(I));
115       LOG(INFO) << "throwing in ~ThrowInDestructor " << I;
116       throw std::logic_error("inner");
117     } catch (const std::logic_error& e) {
118       LOG(INFO) << "catching in ~ThrowInDestructor " << I << " expecting "
119                 << N - I << ", it is " << folly::uncaught_exceptions();
120       EXPECT_EQ(N - I, folly::uncaught_exceptions());
121     }
122   }
123 };
124
125 /*
126  * Terminate recursion
127  */
128 template <size_t N>
129 struct ThrowInDestructor<N, 0> {
130   ThrowInDestructor() = default;
131   ~ThrowInDestructor() = default;
132 };
133
134 TEST(UncaughtExceptions, two_uncaught_exceptions) {
135   ThrowInDestructor<2> twoUncaughtExceptions;
136 }
137
138 TEST(UncaughtExceptions, ten_uncaught_exceptions) {
139   ThrowInDestructor<10> twoUncaughtExceptions;
140 }
141
142 struct ThrowingConstructor {
143   [[noreturn]] ThrowingConstructor() noexcept(false) {
144     throw std::runtime_error("exception");
145   }
146 };
147
148 struct InheritsThrowingConstructor : public Validator,
149                                      public ThrowingConstructor {
150   InheritsThrowingConstructor() try
151       : Validator(1, "one_exception_in_ctor_initializer_expression"),
152         ThrowingConstructor() {
153   } catch (...) {
154     // This is being re-thrown once the catch block ends, so I guess
155     // it's similar to a catch/throw; (re-throw) behavior and thus the value
156     // is 0.
157     EXPECT_EQ(0, folly::uncaught_exceptions());
158   }
159 };
160
161 TEST(UncaughtExceptions, one_exception_in_ctor_initializer_expression) {
162   EXPECT_THROW(
163       { InheritsThrowingConstructor inheritsThrowingConstructor; },
164       std::runtime_error);
165 }