1 | /* |
2 | * Copyright 2015-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 | #pragma once |
18 | |
19 | /* |
20 | * This file contains additional gtest-style check macros to use in unit tests. |
21 | * |
22 | * - SKIP(), SKIP_IF(EXPR) |
23 | * - EXPECT_THROW_RE(), ASSERT_THROW_RE() |
24 | * - EXPECT_THROW_ERRNO(), ASSERT_THROW_ERRNO() |
25 | * - AreWithinSecs() |
26 | * |
27 | * It also imports PrintTo() functions for StringPiece, FixedString and |
28 | * FBString. Including this file in your tests will ensure that they are printed |
29 | * as strings by googletest - for example in failing EXPECT_EQ() checks. |
30 | */ |
31 | |
32 | #include <chrono> |
33 | #include <regex> |
34 | #include <system_error> |
35 | #include <type_traits> |
36 | |
37 | #include <folly/Conv.h> |
38 | #include <folly/ExceptionString.h> |
39 | #include <folly/FBString.h> |
40 | #include <folly/FixedString.h> |
41 | #include <folly/Range.h> |
42 | #include <folly/portability/GTest.h> |
43 | |
44 | // SKIP() is used to mark a test skipped if we could not successfully execute |
45 | // the test due to runtime issues or behavior that do not necessarily indicate |
46 | // a problem with the code. |
47 | // |
48 | // googletest does not have a built-in mechanism to report tests as skipped a |
49 | // run time. We either report the test as successful or failure based on the |
50 | // FOLLY_SKIP_AS_FAILURE configuration setting. The default is to report the |
51 | // test as successful. Enabling FOLLY_SKIP_AS_FAILURE can be useful with a |
52 | // test harness that can identify the "Test skipped by client" in the failure |
53 | // message and convert this into a skipped test result. |
54 | #if FOLLY_SKIP_AS_FAILURE |
55 | #define SKIP() GTEST_FATAL_FAILURE_("Test skipped by client") |
56 | #else |
57 | #define SKIP() return GTEST_SUCCESS_("Test skipped by client") |
58 | #endif |
59 | |
60 | // Encapsulate conditional-skip, since it's nontrivial to get right. |
61 | #define SKIP_IF(expr) \ |
62 | GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ |
63 | if (!(expr)) { \ |
64 | } else \ |
65 | SKIP() |
66 | |
67 | #define TEST_THROW_ERRNO_(statement, errnoValue, fail) \ |
68 | GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ |
69 | if (::folly::test::detail::CheckResult gtest_result = \ |
70 | ::folly::test::detail::checkThrowErrno( \ |
71 | [&] { statement; }, errnoValue, #statement)) { \ |
72 | } else \ |
73 | fail(gtest_result.what()) |
74 | |
75 | /** |
76 | * Check that a statement throws a std::system_error with the expected errno |
77 | * value. This is useful for checking code that uses the functions in |
78 | * folly/Exception.h to throw exceptions. |
79 | * |
80 | * Like other EXPECT_* and ASSERT_* macros, additional message information |
81 | * can be included using the << stream operator. |
82 | * |
83 | * Example usage: |
84 | * |
85 | * EXPECT_THROW_ERRNO(readFile("notpresent.txt"), ENOENT) |
86 | * << "notpresent.txt should not exist"; |
87 | */ |
88 | #define EXPECT_THROW_ERRNO(statement, errnoValue) \ |
89 | TEST_THROW_ERRNO_(statement, errnoValue, GTEST_NONFATAL_FAILURE_) |
90 | #define ASSERT_THROW_ERRNO(statement, errnoValue) \ |
91 | TEST_THROW_ERRNO_(statement, errnoValue, GTEST_FATAL_FAILURE_) |
92 | |
93 | #define TEST_THROW_RE_(statement, exceptionType, pattern, fail) \ |
94 | GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ |
95 | if (::folly::test::detail::CheckResult gtest_result = \ |
96 | ::folly::test::detail::checkThrowRegex<exceptionType>( \ |
97 | [&] { statement; }, pattern, #statement, #exceptionType)) { \ |
98 | } else \ |
99 | fail(gtest_result.what()) |
100 | |
101 | /** |
102 | * Check that a statement throws the expected exception type, and that the |
103 | * exception message matches the specified regular expression. |
104 | * |
105 | * Partial matches (against just a portion of the error message) are accepted |
106 | * if the regular expression does not explicitly start with "^" and end with |
107 | * "$". (The matching is performed using std::regex_search() rather than |
108 | * std::regex_match().) |
109 | * |
110 | * This uses ECMA-262 style regular expressions (the default behavior of |
111 | * std::regex). |
112 | * |
113 | * Like other EXPECT_* and ASSERT_* macros, additional message information |
114 | * can be included using the << stream operator. |
115 | * |
116 | * Example usage: |
117 | * |
118 | * EXPECT_THROW_RE(badFunction(), std::runtime_error, "oh noes") |
119 | * << "function did not throw the expected exception"; |
120 | */ |
121 | #define EXPECT_THROW_RE(statement, exceptionType, pattern) \ |
122 | TEST_THROW_RE_(statement, exceptionType, pattern, GTEST_NONFATAL_FAILURE_) |
123 | #define ASSERT_THROW_RE(statement, exceptionType, pattern) \ |
124 | TEST_THROW_RE_(statement, exceptionType, pattern, GTEST_FATAL_FAILURE_) |
125 | |
126 | namespace folly { |
127 | namespace test { |
128 | |
129 | template <typename T1, typename T2> |
130 | ::testing::AssertionResult |
131 | AreWithinSecs(T1 val1, T2 val2, std::chrono::seconds acceptableDeltaSecs) { |
132 | auto deltaSecs = |
133 | std::chrono::duration_cast<std::chrono::seconds>(val1 - val2); |
134 | if (deltaSecs <= acceptableDeltaSecs && |
135 | deltaSecs >= -1 * acceptableDeltaSecs) { |
136 | return ::testing::AssertionSuccess(); |
137 | } else { |
138 | return ::testing::AssertionFailure() |
139 | << val1.count() << " and " << val2.count() << " are not within " |
140 | << acceptableDeltaSecs.count() << " secs of each other" ; |
141 | } |
142 | } |
143 | |
144 | namespace detail { |
145 | |
146 | /** |
147 | * Helper class for implementing test macros |
148 | */ |
149 | class CheckResult { |
150 | public: |
151 | explicit CheckResult(bool s) noexcept : success_(s) {} |
152 | |
153 | explicit operator bool() const noexcept { |
154 | return success_; |
155 | } |
156 | const char* what() const noexcept { |
157 | return message_.c_str(); |
158 | } |
159 | |
160 | /** |
161 | * Support the << operator for building up the error message. |
162 | * |
163 | * The arguments are treated as with folly::to<string>(), and we do not |
164 | * support iomanip parameters. The main reason we use the << operator |
165 | * as opposed to a variadic function like folly::to is that clang-format |
166 | * formats long statements using << much nicer than function call arguments. |
167 | */ |
168 | template <typename T> |
169 | CheckResult& operator<<(T&& t) { |
170 | toAppend(std::forward<T>(t), &message_); |
171 | return *this; |
172 | } |
173 | |
174 | private: |
175 | bool success_; |
176 | std::string message_; |
177 | }; |
178 | |
179 | /** |
180 | * Helper function for implementing EXPECT_THROW |
181 | */ |
182 | template <typename Fn> |
183 | CheckResult checkThrowErrno(Fn&& fn, int errnoValue, const char* statementStr) { |
184 | try { |
185 | fn(); |
186 | } catch (const std::system_error& ex) { |
187 | // TODO: POSIX errno values should use std::generic_category(), but |
188 | // folly/Exception.h incorrectly throws them using std::system_category() |
189 | // at the moment. |
190 | // For now we also accept std::system_category so that we will also handle |
191 | // exceptions from folly/Exception.h correctly. |
192 | if (ex.code().category() != std::generic_category() && |
193 | ex.code().category() != std::system_category()) { |
194 | return CheckResult(false) |
195 | << "Expected: " << statementStr << " throws an exception with errno " |
196 | << errnoValue << " (" << std::generic_category().message(errnoValue) |
197 | << ")\nActual: it throws a system_error with category " |
198 | << ex.code().category().name() << ": " << ex.what(); |
199 | } |
200 | if (ex.code().value() != errnoValue) { |
201 | return CheckResult(false) |
202 | << "Expected: " << statementStr << " throws an exception with errno " |
203 | << errnoValue << " (" << std::generic_category().message(errnoValue) |
204 | << ")\nActual: it throws errno " << ex.code().value() << ": " |
205 | << ex.what(); |
206 | } |
207 | return CheckResult(true); |
208 | } catch (const std::exception& ex) { |
209 | return CheckResult(false) |
210 | << "Expected: " << statementStr << " throws an exception with errno " |
211 | << errnoValue << " (" << std::generic_category().message(errnoValue) |
212 | << ")\nActual: it throws a different exception: " << exceptionStr(ex); |
213 | } catch (...) { |
214 | return CheckResult(false) |
215 | << "Expected: " << statementStr << " throws an exception with errno " |
216 | << errnoValue << " (" << std::generic_category().message(errnoValue) |
217 | << ")\nActual: it throws a non-exception type" ; |
218 | } |
219 | return CheckResult(false) |
220 | << "Expected: " << statementStr << " throws an exception with errno " |
221 | << errnoValue << " (" << std::generic_category().message(errnoValue) |
222 | << ")\nActual: it throws nothing" ; |
223 | } |
224 | |
225 | /** |
226 | * Helper function for implementing EXPECT_THROW_RE |
227 | */ |
228 | template <typename ExType, typename Fn> |
229 | CheckResult checkThrowRegex( |
230 | Fn&& fn, |
231 | const char* pattern, |
232 | const char* statementStr, |
233 | const char* excTypeStr) { |
234 | static_assert( |
235 | std::is_base_of<std::exception, ExType>::value, |
236 | "EXPECT_THROW_RE() exception type must derive from std::exception" ); |
237 | |
238 | try { |
239 | fn(); |
240 | } catch (const std::exception& ex) { |
241 | const auto* derived = dynamic_cast<const ExType*>(&ex); |
242 | if (!derived) { |
243 | return CheckResult(false) |
244 | << "Expected: " << statementStr << "throws a " << excTypeStr |
245 | << ")\nActual: it throws a different exception type: " |
246 | << exceptionStr(ex); |
247 | } |
248 | |
249 | std::regex re(pattern); |
250 | if (!std::regex_search(derived->what(), re)) { |
251 | return CheckResult(false) |
252 | << "Expected: " << statementStr << " throws a " << excTypeStr |
253 | << " with message matching \"" << pattern |
254 | << "\"\nActual: message is: " << derived->what(); |
255 | } |
256 | return CheckResult(true); |
257 | } catch (...) { |
258 | return CheckResult(false) |
259 | << "Expected: " << statementStr << " throws a " << excTypeStr |
260 | << ")\nActual: it throws a non-exception type" ; |
261 | } |
262 | return CheckResult(false) << "Expected: " << statementStr << " throws a " |
263 | << excTypeStr << ")\nActual: it throws nothing" ; |
264 | } |
265 | |
266 | } // namespace detail |
267 | } // namespace test |
268 | |
269 | // Define PrintTo() functions for StringPiece/FixedString/fbstring, so that |
270 | // gtest checks will print them as strings. Without these gtest identifies them |
271 | // as container types, and therefore tries printing the elements individually, |
272 | // despite the fact that there is an ostream operator<<() defined for each of |
273 | // them. |
274 | // |
275 | // gtest's PrintToString() function is used to quote the string and escape |
276 | // internal quotes and non-printable characters, the same way gtest does for the |
277 | // string types it directly supports. |
278 | inline void PrintTo(StringPiece const& stringPiece, std::ostream* out) { |
279 | *out << ::testing::PrintToString(stringPiece.str()); |
280 | } |
281 | |
282 | inline void PrintTo( |
283 | Range<wchar_t const*> const& stringPiece, |
284 | std::ostream* out) { |
285 | *out << ::testing::PrintToString( |
286 | std::wstring(stringPiece.begin(), stringPiece.size())); |
287 | } |
288 | |
289 | template <typename CharT, size_t N> |
290 | void PrintTo( |
291 | BasicFixedString<CharT, N> const& someFixedString, |
292 | std::ostream* out) { |
293 | *out << ::testing::PrintToString(someFixedString.toStdString()); |
294 | } |
295 | |
296 | template <typename CharT, class Storage> |
297 | void PrintTo( |
298 | basic_fbstring< |
299 | CharT, |
300 | std::char_traits<CharT>, |
301 | std::allocator<CharT>, |
302 | Storage> const& someFbString, |
303 | std::ostream* out) { |
304 | *out << ::testing::PrintToString(someFbString.toStdString()); |
305 | } |
306 | } // namespace folly |
307 | |