Merge pull request #1482 from zhangxy988/variant_matcher

Add matcher for std::variant.
This commit is contained in:
Gennadiy Civil 2018-02-27 16:58:28 -05:00 committed by GitHub
commit fe1144246e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 137 additions and 0 deletions

View File

@ -178,6 +178,8 @@ divided into several categories:
|`Ne(value)` |`argument != value`|
|`IsNull()` |`argument` is a `NULL` pointer (raw or smart).|
|`NotNull()` |`argument` is a non-null pointer (raw or smart).|
|`VariantWith<T>(m)` |`argument` is `variant<>` that holds the alternative of
type T with a value matching `m`.|
|`Ref(variable)` |`argument` is a reference to `variable`.|
|`TypedEq<type>(value)`|`argument` has type `type` and is equal to `value`. You may need to use this instead of `Eq(value)` when the mock function is overloaded.|

View File

@ -3636,6 +3636,66 @@ GTEST_API_ std::string FormatMatcherDescription(bool negation,
const char* matcher_name,
const Strings& param_values);
namespace variant_matcher {
// Overloads to allow VariantMatcher to do proper ADL lookup.
template <typename T>
void holds_alternative() {}
template <typename T>
void get() {}
// Implements a matcher that checks the value of a variant<> type variable.
template <typename T>
class VariantMatcher {
public:
explicit VariantMatcher(::testing::Matcher<const T&> matcher)
: matcher_(internal::move(matcher)) {}
template <typename Variant>
bool MatchAndExplain(const Variant& value,
::testing::MatchResultListener* listener) const {
if (!listener->IsInterested()) {
return holds_alternative<T>(value) && matcher_.Matches(get<T>(value));
}
if (!holds_alternative<T>(value)) {
*listener << "whose value is not of type '" << GetTypeName() << "'";
return false;
}
const T& elem = get<T>(value);
StringMatchResultListener elem_listener;
const bool match = matcher_.MatchAndExplain(elem, &elem_listener);
*listener << "whose value " << PrintToString(elem)
<< (match ? " matches" : " doesn't match");
PrintIfNotEmpty(elem_listener.str(), listener->stream());
return match;
}
void DescribeTo(std::ostream* os) const {
*os << "is a variant<> with value of type '" << GetTypeName()
<< "' and the value ";
matcher_.DescribeTo(os);
}
void DescribeNegationTo(std::ostream* os) const {
*os << "is a variant<> with value of type other than '" << GetTypeName()
<< "' or the value ";
matcher_.DescribeNegationTo(os);
}
private:
static string GetTypeName() {
#if GTEST_HAS_RTTI
return internal::GetTypeName<T>();
#endif
return "the element type";
}
const ::testing::Matcher<const T&> matcher_;
};
} // namespace variant_matcher
} // namespace internal
// ElementsAreArray(iterator_first, iterator_last)
@ -4540,6 +4600,17 @@ inline internal::AnyOfMatcher<Args...> AnyOf(const Args&... matchers) {
template <typename InnerMatcher>
inline InnerMatcher AllArgs(const InnerMatcher& matcher) { return matcher; }
// Returns a matcher that matches the value of a variant<> type variable.
// The matcher implementation uses ADL to find the holds_alternative and get
// functions.
// It is compatible with std::variant.
template <typename T>
PolymorphicMatcher<internal::variant_matcher::VariantMatcher<T> > VariantWith(
const Matcher<const T&>& matcher) {
return MakePolymorphicMatcher(
internal::variant_matcher::VariantMatcher<T>(matcher));
}
// These macros allow using matchers to check values in Google Test
// tests. ASSERT_THAT(value, matcher) and EXPECT_THAT(value, matcher)
// succeed iff the value matches the matcher. If the assertion fails,

View File

@ -5655,5 +5655,69 @@ TEST(UnorderedPointwiseTest, AllowsMonomorphicInnerMatcher) {
EXPECT_THAT(lhs, UnorderedPointwise(m2, rhs));
}
class SampleVariantIntString {
public:
SampleVariantIntString(int i) : i_(i), has_int_(true) {}
SampleVariantIntString(const std::string& s) : s_(s), has_int_(false) {}
template <typename T>
friend bool holds_alternative(const SampleVariantIntString& value) {
return value.has_int_ == internal::IsSame<T, int>::value;
}
template <typename T>
friend const T& get(const SampleVariantIntString& value) {
return value.get_impl(static_cast<T*>(NULL));
}
private:
const int& get_impl(int*) const { return i_; }
const std::string& get_impl(std::string*) const { return s_; }
int i_;
std::string s_;
bool has_int_;
};
TEST(VariantTest, DescribesSelf) {
const Matcher<SampleVariantIntString> m = VariantWith<int>(Eq(1));
EXPECT_THAT(Describe(m), ContainsRegex("is a variant<> with value of type "
"'.*' and the value is equal to 1"));
}
TEST(VariantTest, ExplainsSelf) {
const Matcher<SampleVariantIntString> m = VariantWith<int>(Eq(1));
EXPECT_THAT(Explain(m, SampleVariantIntString(1)),
ContainsRegex("whose value 1"));
EXPECT_THAT(Explain(m, SampleVariantIntString("A")),
HasSubstr("whose value is not of type '"));
EXPECT_THAT(Explain(m, SampleVariantIntString(2)),
"whose value 2 doesn't match");
}
TEST(VariantTest, FullMatch) {
Matcher<SampleVariantIntString> m = VariantWith<int>(Eq(1));
EXPECT_TRUE(m.Matches(SampleVariantIntString(1)));
m = VariantWith<std::string>(Eq("1"));
EXPECT_TRUE(m.Matches(SampleVariantIntString("1")));
}
TEST(VariantTest, TypeDoesNotMatch) {
Matcher<SampleVariantIntString> m = VariantWith<int>(Eq(1));
EXPECT_FALSE(m.Matches(SampleVariantIntString("1")));
m = VariantWith<std::string>(Eq("1"));
EXPECT_FALSE(m.Matches(SampleVariantIntString(1)));
}
TEST(VariantTest, InnerDoesNotMatch) {
Matcher<SampleVariantIntString> m = VariantWith<int>(Eq(1));
EXPECT_FALSE(m.Matches(SampleVariantIntString(2)));
m = VariantWith<std::string>(Eq("1"));
EXPECT_FALSE(m.Matches(SampleVariantIntString("2")));
}
} // namespace gmock_matchers_test
} // namespace testing