#pragma once
+/*
+ * This file contains additional gtest-style check macros to use in unit tests.
+ *
+ * - SKIP()
+ * - EXPECT_THROW_RE(), ASSERT_THROW_RE()
+ * - EXPECT_THROW_ERRNO(), ASSERT_THROW_ERRNO()
+ * - AreWithinSecs()
+ *
+ * Additionally, it includes a PrintTo() function for StringPiece.
+ * Including this file in your tests will ensure that StringPiece is printed
+ * nicely when used in EXPECT_EQ() or EXPECT_NE() checks.
+ */
+
#include <chrono>
+#include <regex>
+#include <system_error>
+#include <type_traits>
+#include <folly/Conv.h>
+#include <folly/ExceptionString.h>
#include <folly/Range.h>
#include <folly/portability/GTest.h>
// interprets the message.
#define SKIP() GTEST_FATAL_FAILURE_("Test skipped by client")
+#define TEST_THROW_ERRNO_(statement, errnoValue, fail) \
+ GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
+ if (::folly::test::detail::CheckResult gtest_result = \
+ ::folly::test::detail::checkThrowErrno( \
+ [&] { statement; }, errnoValue, #statement)) { \
+ } else \
+ fail(gtest_result.what())
+
+/**
+ * Check that a statement throws a std::system_error with the expected errno
+ * value. This is useful for checking code that uses the functions in
+ * folly/Exception.h to throw exceptions.
+ *
+ * Like other EXPECT_* and ASSERT_* macros, additional message information
+ * can be included using the << stream operator.
+ *
+ * Example usage:
+ *
+ * EXPECT_THROW_ERRNO(readFile("notpresent.txt"), ENOENT)
+ * << "notpresent.txt should not exist";
+ */
+#define EXPECT_THROW_ERRNO(statement, errnoValue) \
+ TEST_THROW_ERRNO_(statement, errnoValue, GTEST_NONFATAL_FAILURE_)
+#define ASSERT_THROW_ERRNO(statement, errnoValue) \
+ TEST_THROW_ERRNO_(statement, errnoValue, GTEST_FATAL_FAILURE_)
+
+#define TEST_THROW_RE_(statement, exceptionType, pattern, fail) \
+ GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
+ if (::folly::test::detail::CheckResult gtest_result = \
+ ::folly::test::detail::checkThrowRegex<exceptionType>( \
+ [&] { statement; }, pattern, #statement, #exceptionType)) { \
+ } else \
+ fail(gtest_result.what())
+
+/**
+ * Check that a statement throws the expected exception type, and that the
+ * exception message matches the specified regular expression.
+ *
+ * Partial matches (against just a portion of the error message) are accepted
+ * if the regular expression does not explicitly start with "^" and end with
+ * "$". (The matching is performed using std::regex_search() rather than
+ * std::regex_match().)
+ *
+ * This uses ECMA-262 style regular expressions (the default behavior of
+ * std::regex).
+ *
+ * Like other EXPECT_* and ASSERT_* macros, additional message information
+ * can be included using the << stream operator.
+ *
+ * Example usage:
+ *
+ * EXPECT_THROW_RE(badFunction(), std::runtime_error, "oh noes")
+ * << "function did not throw the expected exception";
+ */
+#define EXPECT_THROW_RE(statement, exceptionType, pattern) \
+ TEST_THROW_RE_(statement, exceptionType, pattern, GTEST_NONFATAL_FAILURE_)
+#define ASSERT_THROW_RE(statement, exceptionType, pattern) \
+ TEST_THROW_RE_(statement, exceptionType, pattern, GTEST_FATAL_FAILURE_)
+
namespace folly {
namespace test {
<< acceptableDeltaSecs.count() << " secs of each other";
}
}
+
+namespace detail {
+
+/**
+ * Helper class for implementing test macros
+ */
+class CheckResult {
+ public:
+ explicit CheckResult(bool s) noexcept : success_(s) {}
+
+ explicit operator bool() const noexcept {
+ return success_;
+ }
+ const char* what() const noexcept {
+ return message_.c_str();
+ }
+
+ /**
+ * Support the << operator for building up the error message.
+ *
+ * The arguments are treated as with folly::to<string>(), and we do not
+ * support iomanip parameters. The main reason we use the << operator
+ * as opposed to a variadic function like folly::to is that clang-format
+ * formats long statements using << much nicer than function call arguments.
+ */
+ template <typename T>
+ CheckResult& operator<<(T&& t) {
+ toAppend(std::forward<T>(t), &message_);
+ return *this;
+ }
+
+ private:
+ bool success_;
+ std::string message_;
+};
+
+/**
+ * Helper function for implementing EXPECT_THROW
+ */
+template <typename Fn>
+CheckResult checkThrowErrno(Fn&& fn, int errnoValue, const char* statementStr) {
+ try {
+ fn();
+ } catch (const std::system_error& ex) {
+ // TODO: POSIX errno values should really use std::generic_category(),
+ // but folly/Exception.h throws them with std::system_category() at the
+ // moment.
+ if (ex.code().category() != std::system_category()) {
+ return CheckResult(false)
+ << "Expected: " << statementStr << " throws an exception with errno "
+ << errnoValue << " (" << std::generic_category().message(errnoValue)
+ << ")\nActual: it throws a system_error with category "
+ << ex.code().category().name() << ": " << ex.what();
+ }
+ if (ex.code().value() != errnoValue) {
+ return CheckResult(false)
+ << "Expected: " << statementStr << " throws an exception with errno "
+ << errnoValue << " (" << std::generic_category().message(errnoValue)
+ << ")\nActual: it throws errno " << ex.code().value() << ": "
+ << ex.what();
+ }
+ return CheckResult(true);
+ } catch (const std::exception& ex) {
+ return CheckResult(false)
+ << "Expected: " << statementStr << " throws an exception with errno "
+ << errnoValue << " (" << std::generic_category().message(errnoValue)
+ << ")\nActual: it throws a different exception: " << exceptionStr(ex);
+ } catch (...) {
+ return CheckResult(false)
+ << "Expected: " << statementStr << " throws an exception with errno "
+ << errnoValue << " (" << std::generic_category().message(errnoValue)
+ << ")\nActual: it throws a non-exception type";
+ }
+ return CheckResult(false)
+ << "Expected: " << statementStr << " throws an exception with errno "
+ << errnoValue << " (" << std::generic_category().message(errnoValue)
+ << ")\nActual: it throws nothing";
+}
+
+/**
+ * Helper function for implementing EXPECT_THROW_RE
+ */
+template <typename ExType, typename Fn>
+CheckResult checkThrowRegex(
+ Fn&& fn,
+ const char* pattern,
+ const char* statementStr,
+ const char* excTypeStr) {
+ static_assert(
+ std::is_base_of<std::exception, ExType>::value,
+ "EXPECT_THROW_RE() exception type must derive from std::exception");
+
+ try {
+ fn();
+ } catch (const std::exception& ex) {
+ const auto* derived = dynamic_cast<const ExType*>(&ex);
+ if (!derived) {
+ return CheckResult(false)
+ << "Expected: " << statementStr << "throws a " << excTypeStr
+ << ")\nActual: it throws a different exception type: "
+ << exceptionStr(ex);
+ }
+
+ std::regex re(pattern);
+ if (!std::regex_search(derived->what(), re)) {
+ return CheckResult(false)
+ << "Expected: " << statementStr << " throws a " << excTypeStr
+ << " with message matching \"" << pattern
+ << "\"\nActual: message is: " << derived->what();
+ }
+ return CheckResult(true);
+ } catch (...) {
+ return CheckResult(false)
+ << "Expected: " << statementStr << " throws a " << excTypeStr
+ << ")\nActual: it throws a non-exception type";
+ }
+ return CheckResult(false) << "Expected: " << statementStr << " throws a "
+ << excTypeStr << ")\nActual: it throws nothing";
+}
+
+} // namespace detail
} // namespace test
// Define a PrintTo() function for StringPiece, so that gtest checks
// standard string types.
*os << ::testing::PrintToString(sp.str());
}
+
} // namespace folly