// Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #ifndef CARBON_COMMON_ERROR_TEST_HELPERS_H_ #define CARBON_COMMON_ERROR_TEST_HELPERS_H_ #include #include "common/error.h" namespace Carbon::Testing { // Matches the message for an error state of `ErrorOr`. For example: // EXPECT_THAT(my_result, IsError(StrEq("error message"))); class IsError { public: // NOLINTNEXTLINE(readability-identifier-naming) using is_gtest_matcher = void; explicit IsError(::testing::Matcher matcher) : matcher_(std::move(matcher)) {} template auto MatchAndExplain(const ErrorOr& result, ::testing::MatchResultListener* listener) const -> bool { if (result.ok()) { *listener << "is a success"; return false; } else { RawStringOstream os; os << result.error(); return matcher_.MatchAndExplain(os.TakeStr(), listener); } } auto DescribeTo(std::ostream* os) const -> void { *os << "is an error and matches "; matcher_.DescribeTo(os); } auto DescribeNegationTo(std::ostream* os) const -> void { *os << "is a success or does not match "; matcher_.DescribeTo(os); } private: ::testing::Matcher matcher_; }; // Matches the value for a non-error state of `ErrorOr`. For example: // EXPECT_THAT(my_result, IsSuccess(Eq(3))); template class IsSuccessMatcher { public: // NOLINTNEXTLINE(readability-identifier-naming) using is_gtest_matcher = void; explicit IsSuccessMatcher(InnerMatcher matcher) : matcher_(std::move(matcher)) {} template auto MatchAndExplain(const ErrorOr& result, ::testing::MatchResultListener* listener) const -> bool { if (result.ok()) { return ::testing::Matcher(matcher_).MatchAndExplain(*result, listener); } else { *listener << "is an error with `" << result.error() << "`"; return false; } } auto DescribeTo(std::ostream* os) const -> void { *os << "is a success and matches "; matcher_.DescribeTo(os); } auto DescribeNegationTo(std::ostream* os) const -> void { *os << "is an error or does not match "; matcher_.DescribeTo(os); } private: InnerMatcher matcher_; }; // Wraps `IsSuccessMatcher` for the inner matcher deduction. template auto IsSuccess(InnerMatcher matcher) -> IsSuccessMatcher { return IsSuccessMatcher(matcher); } } // namespace Carbon::Testing namespace Carbon { // Supports printing `ErrorOr` to `std::ostream` in tests. template auto operator<<(std::ostream& out, const ErrorOr& error_or) -> std::ostream& { if (error_or.ok()) { // Try and print the value, but only if we can find a viable `<<` overload // for the value type. This should ensure that the `formatv` below can // compile cleanly, and avoid erroring when using matchers on `ErrorOr` with // unprintable value types. if constexpr (requires(const T& value) { out << value; }) { out << llvm::formatv("ErrorOr{{.value = `{0}`}}", *error_or); } else { out << "ErrorOr{{.value = ``}}"; } } else { out << llvm::formatv("ErrorOr{{.error = \"{0}\"}}", error_or.error()); } return out; } } // namespace Carbon #endif // CARBON_COMMON_ERROR_TEST_HELPERS_H_