+
+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