Index: llvm/trunk/include/llvm/Testing/Support/Error.h =================================================================== --- llvm/trunk/include/llvm/Testing/Support/Error.h +++ llvm/trunk/include/llvm/Testing/Support/Error.h @@ -38,7 +38,7 @@ bool MatchAndExplain(const ExpectedHolder &Holder, testing::MatchResultListener *listener) const override { - if (!Holder.Success) + if (!Holder.Success()) return false; bool result = Matcher.MatchAndExplain(*Holder.Exp, listener); @@ -82,6 +82,53 @@ M Matcher; }; +template +class ErrorMatchesMono : public testing::MatcherInterface { +public: + explicit ErrorMatchesMono(Optional> Matcher) + : Matcher(std::move(Matcher)) {} + + bool MatchAndExplain(const ErrorHolder &Holder, + testing::MatchResultListener *listener) const override { + if (Holder.Success()) + return false; + + if (Holder.Infos.size() > 1) { + *listener << "multiple errors"; + return false; + } + + auto &Info = *Holder.Infos[0]; + if (!Info.isA()) { + *listener << "Error was not of given type"; + return false; + } + + if (!Matcher) + return true; + + return Matcher->MatchAndExplain(static_cast(Info), listener); + } + + void DescribeTo(std::ostream *OS) const override { + *OS << "failed with Error of given type"; + if (Matcher) { + *OS << " and the error "; + Matcher->DescribeTo(OS); + } + } + + void DescribeNegationTo(std::ostream *OS) const override { + *OS << "succeeded or did not fail with the error of given type"; + if (Matcher) { + *OS << " or the error "; + Matcher->DescribeNegationTo(OS); + } + } + +private: + Optional> Matcher; +}; } // namespace detail #define EXPECT_THAT_ERROR(Err, Matcher) \ @@ -94,8 +141,19 @@ #define ASSERT_THAT_EXPECTED(Err, Matcher) \ ASSERT_THAT(llvm::detail::TakeExpected(Err), Matcher) -MATCHER(Succeeded, "") { return arg.Success; } -MATCHER(Failed, "") { return !arg.Success; } +MATCHER(Succeeded, "") { return arg.Success(); } +MATCHER(Failed, "") { return !arg.Success(); } + +template +testing::Matcher Failed() { + return MakeMatcher(new detail::ErrorMatchesMono(None)); +} + +template +testing::Matcher Failed(M Matcher) { + return MakeMatcher(new detail::ErrorMatchesMono( + testing::SafeMatcherCast(Matcher))); +} template detail::ValueMatchesPoly HasValue(M Matcher) { Index: llvm/trunk/include/llvm/Testing/Support/SupportHelpers.h =================================================================== --- llvm/trunk/include/llvm/Testing/Support/SupportHelpers.h +++ llvm/trunk/include/llvm/Testing/Support/SupportHelpers.h @@ -17,8 +17,9 @@ namespace llvm { namespace detail { struct ErrorHolder { - bool Success; - std::string Message; + std::vector> Infos; + + bool Success() const { return Infos.empty(); } }; template struct ExpectedHolder : public ErrorHolder { @@ -29,15 +30,22 @@ }; inline void PrintTo(const ErrorHolder &Err, std::ostream *Out) { - *Out << (Err.Success ? "succeeded" : "failed"); - if (!Err.Success) { - *Out << " (" << StringRef(Err.Message).trim().str() << ")"; + raw_os_ostream OS(*Out); + OS << (Err.Success() ? "succeeded" : "failed"); + if (!Err.Success()) { + const char *Delim = " ("; + for (const auto &Info : Err.Infos) { + OS << Delim; + Delim = "; "; + Info->log(OS); + } + OS << ")"; } } template void PrintTo(const ExpectedHolder &Item, std::ostream *Out) { - if (Item.Success) { + if (Item.Success()) { *Out << "succeeded with value " << ::testing::PrintToString(*Item.Exp); } else { PrintTo(static_cast(Item), Out); Index: llvm/trunk/lib/Testing/Support/Error.cpp =================================================================== --- llvm/trunk/lib/Testing/Support/Error.cpp +++ llvm/trunk/lib/Testing/Support/Error.cpp @@ -14,9 +14,10 @@ using namespace llvm; llvm::detail::ErrorHolder llvm::detail::TakeError(llvm::Error Err) { - bool Succeeded = !static_cast(Err); - std::string Message; - if (!Succeeded) - Message = toString(std::move(Err)); - return {Succeeded, Message}; + std::vector> Infos; + handleAllErrors(std::move(Err), + [&Infos](std::unique_ptr Info) { + Infos.emplace_back(std::move(Info)); + }); + return {std::move(Infos)}; } Index: llvm/trunk/unittests/Support/ErrorTest.cpp =================================================================== --- llvm/trunk/unittests/Support/ErrorTest.cpp +++ llvm/trunk/unittests/Support/ErrorTest.cpp @@ -726,6 +726,30 @@ EXPECT_NONFATAL_FAILURE(EXPECT_THAT_ERROR(Error::success(), Failed()), "Expected: failed\n Actual: succeeded"); + EXPECT_THAT_ERROR(make_error(0), Failed()); + EXPECT_NONFATAL_FAILURE( + EXPECT_THAT_ERROR(Error::success(), Failed()), + "Expected: failed with Error of given type\n Actual: succeeded"); + EXPECT_NONFATAL_FAILURE( + EXPECT_THAT_ERROR(make_error(0), Failed()), + "Error was not of given type"); + EXPECT_NONFATAL_FAILURE( + EXPECT_THAT_ERROR( + joinErrors(make_error(0), make_error(1)), + Failed()), + "multiple errors"); + + EXPECT_THAT_ERROR( + make_error(0), + Failed(testing::Property(&CustomError::getInfo, 0))); + EXPECT_NONFATAL_FAILURE( + EXPECT_THAT_ERROR( + make_error(0), + Failed(testing::Property(&CustomError::getInfo, 1))), + "Expected: failed with Error of given type and the error is an object " + "whose given property is equal to 1\n" + " Actual: failed (CustomError { 0})"); + EXPECT_THAT_EXPECTED(Expected(0), Succeeded()); EXPECT_NONFATAL_FAILURE( EXPECT_THAT_EXPECTED(Expected(make_error(0)),