mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-25 20:23:11 +01:00
70970300a3
Since I had some fun understanding how to properly use llvm::Expected<T> I added some code examples that I would have liked to see when learning to use it. Reviewed By: sammccall Differential Revision: https://reviews.llvm.org/D105014
228 lines
6.6 KiB
C++
228 lines
6.6 KiB
C++
//===- llvm/Testing/Support/Error.h ---------------------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#ifndef LLVM_TESTING_SUPPORT_ERROR_H
|
|
#define LLVM_TESTING_SUPPORT_ERROR_H
|
|
|
|
#include "llvm/ADT/Optional.h"
|
|
#include "llvm/Support/Error.h"
|
|
#include "llvm/Testing/Support/SupportHelpers.h"
|
|
|
|
#include "gmock/gmock.h"
|
|
#include <ostream>
|
|
|
|
namespace llvm {
|
|
namespace detail {
|
|
ErrorHolder TakeError(Error Err);
|
|
|
|
template <typename T> ExpectedHolder<T> TakeExpected(Expected<T> &Exp) {
|
|
return {TakeError(Exp.takeError()), Exp};
|
|
}
|
|
|
|
template <typename T> ExpectedHolder<T> TakeExpected(Expected<T> &&Exp) {
|
|
return TakeExpected(Exp);
|
|
}
|
|
|
|
template <typename T>
|
|
class ValueMatchesMono
|
|
: public testing::MatcherInterface<const ExpectedHolder<T> &> {
|
|
public:
|
|
explicit ValueMatchesMono(const testing::Matcher<T> &Matcher)
|
|
: Matcher(Matcher) {}
|
|
|
|
bool MatchAndExplain(const ExpectedHolder<T> &Holder,
|
|
testing::MatchResultListener *listener) const override {
|
|
if (!Holder.Success())
|
|
return false;
|
|
|
|
bool result = Matcher.MatchAndExplain(*Holder.Exp, listener);
|
|
|
|
if (result || !listener->IsInterested())
|
|
return result;
|
|
*listener << "(";
|
|
Matcher.DescribeNegationTo(listener->stream());
|
|
*listener << ")";
|
|
return result;
|
|
}
|
|
|
|
void DescribeTo(std::ostream *OS) const override {
|
|
*OS << "succeeded with value (";
|
|
Matcher.DescribeTo(OS);
|
|
*OS << ")";
|
|
}
|
|
|
|
void DescribeNegationTo(std::ostream *OS) const override {
|
|
*OS << "did not succeed or value (";
|
|
Matcher.DescribeNegationTo(OS);
|
|
*OS << ")";
|
|
}
|
|
|
|
private:
|
|
testing::Matcher<T> Matcher;
|
|
};
|
|
|
|
template<typename M>
|
|
class ValueMatchesPoly {
|
|
public:
|
|
explicit ValueMatchesPoly(const M &Matcher) : Matcher(Matcher) {}
|
|
|
|
template <typename T>
|
|
operator testing::Matcher<const ExpectedHolder<T> &>() const {
|
|
return MakeMatcher(
|
|
new ValueMatchesMono<T>(testing::SafeMatcherCast<T>(Matcher)));
|
|
}
|
|
|
|
private:
|
|
M Matcher;
|
|
};
|
|
|
|
template <typename InfoT>
|
|
class ErrorMatchesMono : public testing::MatcherInterface<const ErrorHolder &> {
|
|
public:
|
|
explicit ErrorMatchesMono(Optional<testing::Matcher<InfoT &>> 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<InfoT>()) {
|
|
*listener << "Error was not of given type";
|
|
return false;
|
|
}
|
|
|
|
if (!Matcher)
|
|
return true;
|
|
|
|
return Matcher->MatchAndExplain(static_cast<InfoT &>(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<testing::Matcher<InfoT &>> Matcher;
|
|
};
|
|
|
|
class ErrorMessageMatches
|
|
: public testing::MatcherInterface<const ErrorHolder &> {
|
|
public:
|
|
explicit ErrorMessageMatches(
|
|
testing::Matcher<std::vector<std::string>> Matcher)
|
|
: Matcher(std::move(Matcher)) {}
|
|
|
|
bool MatchAndExplain(const ErrorHolder &Holder,
|
|
testing::MatchResultListener *listener) const override {
|
|
std::vector<std::string> Messages;
|
|
for (const std::shared_ptr<ErrorInfoBase> &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<std::vector<std::string>> Matcher;
|
|
};
|
|
} // namespace detail
|
|
|
|
#define EXPECT_THAT_ERROR(Err, Matcher) \
|
|
EXPECT_THAT(llvm::detail::TakeError(Err), Matcher)
|
|
#define ASSERT_THAT_ERROR(Err, Matcher) \
|
|
ASSERT_THAT(llvm::detail::TakeError(Err), Matcher)
|
|
|
|
/// Helper macro for checking the result of an 'Expected<T>'
|
|
///
|
|
/// @code{.cpp}
|
|
/// // function to be tested
|
|
/// Expected<int> myDivide(int A, int B);
|
|
///
|
|
/// TEST(myDivideTests, GoodAndBad) {
|
|
/// // test good case
|
|
/// // if you only care about success or failure:
|
|
/// EXPECT_THAT_EXPECTED(myDivide(10, 5), Succeeded());
|
|
/// // if you also care about the value:
|
|
/// EXPECT_THAT_EXPECTED(myDivide(10, 5), HasValue(2));
|
|
///
|
|
/// // test the error case
|
|
/// EXPECT_THAT_EXPECTED(myDivide(10, 0), Failed());
|
|
/// // also check the error message
|
|
/// EXPECT_THAT_EXPECTED(myDivide(10, 0),
|
|
/// FailedWithMessage("B must not be zero!"));
|
|
/// }
|
|
/// @endcode
|
|
|
|
#define EXPECT_THAT_EXPECTED(Err, Matcher) \
|
|
EXPECT_THAT(llvm::detail::TakeExpected(Err), Matcher)
|
|
#define ASSERT_THAT_EXPECTED(Err, Matcher) \
|
|
ASSERT_THAT(llvm::detail::TakeExpected(Err), Matcher)
|
|
|
|
MATCHER(Succeeded, "") { return arg.Success(); }
|
|
MATCHER(Failed, "") { return !arg.Success(); }
|
|
|
|
template <typename InfoT>
|
|
testing::Matcher<const detail::ErrorHolder &> Failed() {
|
|
return MakeMatcher(new detail::ErrorMatchesMono<InfoT>(None));
|
|
}
|
|
|
|
template <typename InfoT, typename M>
|
|
testing::Matcher<const detail::ErrorHolder &> Failed(M Matcher) {
|
|
return MakeMatcher(new detail::ErrorMatchesMono<InfoT>(
|
|
testing::SafeMatcherCast<InfoT &>(Matcher)));
|
|
}
|
|
|
|
template <typename... M>
|
|
testing::Matcher<const detail::ErrorHolder &> FailedWithMessage(M... Matcher) {
|
|
static_assert(sizeof...(M) > 0, "");
|
|
return MakeMatcher(
|
|
new detail::ErrorMessageMatches(testing::ElementsAre(Matcher...)));
|
|
}
|
|
|
|
template <typename M>
|
|
testing::Matcher<const detail::ErrorHolder &> FailedWithMessageArray(M Matcher) {
|
|
return MakeMatcher(new detail::ErrorMessageMatches(Matcher));
|
|
}
|
|
|
|
template <typename M>
|
|
detail::ValueMatchesPoly<M> HasValue(M Matcher) {
|
|
return detail::ValueMatchesPoly<M>(Matcher);
|
|
}
|
|
|
|
} // namespace llvm
|
|
|
|
#endif
|