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
126namespace folly {
127namespace test {
128
129template <typename T1, typename T2>
130::testing::AssertionResult
131AreWithinSecs(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
144namespace detail {
145
146/**
147 * Helper class for implementing test macros
148 */
149class 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 */
182template <typename Fn>
183CheckResult 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 */
228template <typename ExType, typename Fn>
229CheckResult 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.
278inline void PrintTo(StringPiece const& stringPiece, std::ostream* out) {
279 *out << ::testing::PrintToString(stringPiece.str());
280}
281
282inline 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
289template <typename CharT, size_t N>
290void PrintTo(
291 BasicFixedString<CharT, N> const& someFixedString,
292 std::ostream* out) {
293 *out << ::testing::PrintToString(someFixedString.toStdString());
294}
295
296template <typename CharT, class Storage>
297void 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