proxygen
TestUtils.h
Go to the documentation of this file.
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  * Additionally, it includes a PrintTo() function for StringPiece.
28  * Including this file in your tests will ensure that StringPiece is printed
29  * nicely when used in EXPECT_EQ() or EXPECT_NE() 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/Range.h>
41 
42 // SKIP() is used to mark a test skipped if we could not successfully execute
43 // the test due to runtime issues or behavior that do not necessarily indicate
44 // a problem with the code.
45 //
46 // googletest does not have a built-in mechanism to report tests as skipped a
47 // run time. We either report the test as successful or failure based on the
48 // FOLLY_SKIP_AS_FAILURE configuration setting. The default is to report the
49 // test as successful. Enabling FOLLY_SKIP_AS_FAILURE can be useful with a
50 // test harness that can identify the "Test skipped by client" in the failure
51 // message and convert this into a skipped test result.
52 #if FOLLY_SKIP_AS_FAILURE
53 #define SKIP() GTEST_FATAL_FAILURE_("Test skipped by client")
54 #else
55 #define SKIP() return GTEST_SUCCESS_("Test skipped by client")
56 #endif
57 
58 // Encapsulate conditional-skip, since it's nontrivial to get right.
59 #define SKIP_IF(expr) \
60  GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
61  if (!(expr)) { \
62  } else \
63  SKIP()
64 
65 #define TEST_THROW_ERRNO_(statement, errnoValue, fail) \
66  GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
67  if (::folly::test::detail::CheckResult gtest_result = \
68  ::folly::test::detail::checkThrowErrno( \
69  [&] { statement; }, errnoValue, #statement)) { \
70  } else \
71  fail(gtest_result.what())
72 
86 #define EXPECT_THROW_ERRNO(statement, errnoValue) \
87  TEST_THROW_ERRNO_(statement, errnoValue, GTEST_NONFATAL_FAILURE_)
88 #define ASSERT_THROW_ERRNO(statement, errnoValue) \
89  TEST_THROW_ERRNO_(statement, errnoValue, GTEST_FATAL_FAILURE_)
90 
91 #define TEST_THROW_RE_(statement, exceptionType, pattern, fail) \
92  GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
93  if (::folly::test::detail::CheckResult gtest_result = \
94  ::folly::test::detail::checkThrowRegex<exceptionType>( \
95  [&] { statement; }, pattern, #statement, #exceptionType)) { \
96  } else \
97  fail(gtest_result.what())
98 
119 #define EXPECT_THROW_RE(statement, exceptionType, pattern) \
120  TEST_THROW_RE_(statement, exceptionType, pattern, GTEST_NONFATAL_FAILURE_)
121 #define ASSERT_THROW_RE(statement, exceptionType, pattern) \
122  TEST_THROW_RE_(statement, exceptionType, pattern, GTEST_FATAL_FAILURE_)
123 
124 namespace folly {
125 namespace test {
126 
127 template <typename T1, typename T2>
129 AreWithinSecs(T1 val1, T2 val2, std::chrono::seconds acceptableDeltaSecs) {
130  auto deltaSecs =
131  std::chrono::duration_cast<std::chrono::seconds>(val1 - val2);
132  if (deltaSecs <= acceptableDeltaSecs &&
133  deltaSecs >= -1 * acceptableDeltaSecs) {
135  } else {
137  << val1.count() << " and " << val2.count() << " are not within "
138  << acceptableDeltaSecs.count() << " secs of each other";
139  }
140 }
141 
142 namespace detail {
143 
147 class CheckResult {
148  public:
149  explicit CheckResult(bool s) noexcept : success_(s) {}
150 
151  explicit operator bool() const noexcept {
152  return success_;
153  }
154  const char* what() const noexcept {
155  return message_.c_str();
156  }
157 
166  template <typename T>
168  toAppend(std::forward<T>(t), &message_);
169  return *this;
170  }
171 
172  private:
173  bool success_;
175 };
176 
180 template <typename Fn>
181 CheckResult checkThrowErrno(Fn&& fn, int errnoValue, const char* statementStr) {
182  try {
183  fn();
184  } catch (const std::system_error& ex) {
185  // TODO: POSIX errno values should use std::generic_category(), but
186  // folly/Exception.h incorrectly throws them using std::system_category()
187  // at the moment.
188  // For now we also accept std::system_category so that we will also handle
189  // exceptions from folly/Exception.h correctly.
190  if (ex.code().category() != std::generic_category() &&
191  ex.code().category() != std::system_category()) {
192  return CheckResult(false)
193  << "Expected: " << statementStr << " throws an exception with errno "
194  << errnoValue << " (" << std::generic_category().message(errnoValue)
195  << ")\nActual: it throws a system_error with category "
196  << ex.code().category().name() << ": " << ex.what();
197  }
198  if (ex.code().value() != errnoValue) {
199  return CheckResult(false)
200  << "Expected: " << statementStr << " throws an exception with errno "
201  << errnoValue << " (" << std::generic_category().message(errnoValue)
202  << ")\nActual: it throws errno " << ex.code().value() << ": "
203  << ex.what();
204  }
205  return CheckResult(true);
206  } catch (const std::exception& ex) {
207  return CheckResult(false)
208  << "Expected: " << statementStr << " throws an exception with errno "
209  << errnoValue << " (" << std::generic_category().message(errnoValue)
210  << ")\nActual: it throws a different exception: " << exceptionStr(ex);
211  } catch (...) {
212  return CheckResult(false)
213  << "Expected: " << statementStr << " throws an exception with errno "
214  << errnoValue << " (" << std::generic_category().message(errnoValue)
215  << ")\nActual: it throws a non-exception type";
216  }
217  return CheckResult(false)
218  << "Expected: " << statementStr << " throws an exception with errno "
219  << errnoValue << " (" << std::generic_category().message(errnoValue)
220  << ")\nActual: it throws nothing";
221 }
222 
226 template <typename ExType, typename Fn>
228  Fn&& fn,
229  const char* pattern,
230  const char* statementStr,
231  const char* excTypeStr) {
232  static_assert(
234  "EXPECT_THROW_RE() exception type must derive from std::exception");
235 
236  try {
237  fn();
238  } catch (const std::exception& ex) {
239  const auto* derived = dynamic_cast<const ExType*>(&ex);
240  if (!derived) {
241  return CheckResult(false)
242  << "Expected: " << statementStr << "throws a " << excTypeStr
243  << ")\nActual: it throws a different exception type: "
244  << exceptionStr(ex);
245  }
246 
247  std::regex re(pattern);
248  if (!std::regex_search(derived->what(), re)) {
249  return CheckResult(false)
250  << "Expected: " << statementStr << " throws a " << excTypeStr
251  << " with message matching \"" << pattern
252  << "\"\nActual: message is: " << derived->what();
253  }
254  return CheckResult(true);
255  } catch (...) {
256  return CheckResult(false)
257  << "Expected: " << statementStr << " throws a " << excTypeStr
258  << ")\nActual: it throws a non-exception type";
259  }
260  return CheckResult(false) << "Expected: " << statementStr << " throws a "
261  << excTypeStr << ")\nActual: it throws nothing";
262 }
263 
264 } // namespace detail
265 } // namespace test
266 
267 // Define a PrintTo() function for StringPiece, so that gtest checks
268 // will print it as a string. Without this gtest identifies StringPiece as a
269 // container type, and therefore tries printing its elements individually,
270 // despite the fact that there is an ostream operator<<() defined for
271 // StringPiece.
272 inline void PrintTo(StringPiece sp, ::std::ostream* os) {
273  // gtest's PrintToString() function will quote the string and escape internal
274  // quotes and non-printable characters, the same way gtest does for the
275  // standard string types.
276  *os << ::testing::PrintToString(sp.str());
277 }
278 
279 } // namespace folly
::testing::AssertionResult AreWithinSecs(T1 val1, T2 val2, std::chrono::seconds acceptableDeltaSecs)
Definition: TestUtils.h:129
CheckResult & operator<<(T &&t)
Definition: TestUtils.h:167
GTEST_API_ AssertionResult AssertionFailure()
Definition: gtest.cc:1015
std::string str() const
Definition: Range.h:591
regex
Definition: CMakeCache.txt:563
::std::string PrintToString(const T &value)
fbstring exceptionStr(const std::exception &e)
CheckResult checkThrowRegex(Fn &&fn, const char *pattern, const char *statementStr, const char *excTypeStr)
Definition: TestUtils.h:227
void PrintTo(const dynamic &dyn, std::ostream *os)
Definition: json.cpp:937
folly::std T
—— Concurrent Priority Queue Implementation ——
Definition: AtomicBitSet.h:29
CheckResult(bool s) noexcept
Definition: TestUtils.h:149
requires E e noexcept(noexcept(s.error(std::move(e))))
GTEST_API_ AssertionResult AssertionSuccess()
Definition: gtest.cc:1010
const char * what() const noexcept
Definition: TestUtils.h:154
static const char *const value
Definition: Conv.cpp:50
void toAppend(char value, Tgt *result)
Definition: Conv.h:406
CheckResult checkThrowErrno(Fn &&fn, int errnoValue, const char *statementStr)
Definition: TestUtils.h:181
const char * string
Definition: Conv.cpp:212
static set< string > s
const
Definition: upload.py:398
int errnoValue
Definition: Subprocess.cpp:225