diff --git a/llvm/include/llvm/Testing/Support/Error.h b/llvm/include/llvm/Testing/Support/Error.h index 85328f26440ba..cd5b79cd6bfb0 100644 --- a/llvm/include/llvm/Testing/Support/Error.h +++ b/llvm/include/llvm/Testing/Support/Error.h @@ -128,6 +128,36 @@ class ErrorMatchesMono : public testing::MatcherInterface { private: Optional> Matcher; }; + +class ErrorMessageMatches + : public testing::MatcherInterface { +public: + explicit ErrorMessageMatches( + testing::Matcher> Matcher) + : Matcher(std::move(Matcher)) {} + + bool MatchAndExplain(const ErrorHolder &Holder, + testing::MatchResultListener *listener) const override { + std::vector Messages; + for (const std::shared_ptr &Info: Holder.Infos) + Messages.push_back(Info->message()); + + return Matcher.MatchAndExplain(Messages, listener); + } + + void DescribeTo(std::ostream *OS) const override { + *OS << "failed with Error whose message "; + Matcher.DescribeTo(OS); + } + + void DescribeNegationTo(std::ostream *OS) const override { + *OS << "failed with an Error whose message "; + Matcher.DescribeNegationTo(OS); + } + +private: + testing::Matcher> Matcher; +}; } // namespace detail #define EXPECT_THAT_ERROR(Err, Matcher) \ @@ -154,6 +184,18 @@ testing::Matcher Failed(M Matcher) { testing::SafeMatcherCast(Matcher))); } +template +testing::Matcher FailedWithMessage(M... Matcher) { + static_assert(sizeof...(M) > 0, ""); + return MakeMatcher( + new detail::ErrorMessageMatches(testing::ElementsAre(Matcher...))); +} + +template +testing::Matcher FailedWithMessageArray(M Matcher) { + return MakeMatcher(new detail::ErrorMessageMatches(Matcher)); +} + template detail::ValueMatchesPoly HasValue(M Matcher) { return detail::ValueMatchesPoly(Matcher); diff --git a/llvm/unittests/Support/ErrorTest.cpp b/llvm/unittests/Support/ErrorTest.cpp index 8b8a621ab50db..37289e1b23562 100644 --- a/llvm/unittests/Support/ErrorTest.cpp +++ b/llvm/unittests/Support/ErrorTest.cpp @@ -840,6 +840,46 @@ TEST(Error, HasValueMatcher) { " Actual: failed (CustomError {0})"); } +TEST(Error, FailedWithMessageMatcher) { + EXPECT_THAT_EXPECTED(Expected(make_error(0)), + FailedWithMessage("CustomError {0}")); + + EXPECT_NONFATAL_FAILURE( + EXPECT_THAT_EXPECTED(Expected(make_error(1)), + FailedWithMessage("CustomError {0}")), + "Expected: failed with Error whose message has 1 element that is equal " + "to \"CustomError {0}\"\n" + " Actual: failed (CustomError {1})"); + + EXPECT_NONFATAL_FAILURE( + EXPECT_THAT_EXPECTED(Expected(0), + FailedWithMessage("CustomError {0}")), + "Expected: failed with Error whose message has 1 element that is equal " + "to \"CustomError {0}\"\n" + " Actual: succeeded with value 0"); + + EXPECT_NONFATAL_FAILURE( + EXPECT_THAT_EXPECTED(Expected(make_error(0)), + FailedWithMessage("CustomError {0}", "CustomError {0}")), + "Expected: failed with Error whose message has 2 elements where\n" + "element #0 is equal to \"CustomError {0}\",\n" + "element #1 is equal to \"CustomError {0}\"\n" + " Actual: failed (CustomError {0}), which has 1 element"); + + EXPECT_NONFATAL_FAILURE( + EXPECT_THAT_EXPECTED( + Expected(joinErrors(make_error(0), + make_error(0))), + FailedWithMessage("CustomError {0}")), + "Expected: failed with Error whose message has 1 element that is equal " + "to \"CustomError {0}\"\n" + " Actual: failed (CustomError {0}; CustomError {0}), which has 2 elements"); + + EXPECT_THAT_ERROR( + joinErrors(make_error(0), make_error(0)), + FailedWithMessageArray(testing::SizeIs(2))); +} + TEST(Error, C_API) { EXPECT_THAT_ERROR(unwrap(wrap(Error::success())), Succeeded()) << "Failed to round-trip Error success value via C API";