diff --git a/googlemock/include/gmock/gmock-matchers.h b/googlemock/include/gmock/gmock-matchers.h index b1c0dc04..40e0452f 100644 --- a/googlemock/include/gmock/gmock-matchers.h +++ b/googlemock/include/gmock/gmock-matchers.h @@ -3239,10 +3239,16 @@ class OptionalMatcher { : value_matcher_(value_matcher) {} template - operator Matcher() const { + operator Matcher() const { // NOLINT return Matcher(new Impl(value_matcher_)); } + template + operator Matcher>() const { // NOLINT + return MakeMatcher( + new PairImpl(value_matcher_)); + } + template class Impl : public MatcherInterface { public: @@ -3281,6 +3287,49 @@ class OptionalMatcher { GTEST_DISALLOW_ASSIGN_(Impl); }; + template + class PairImpl : public MatcherInterface> { + public: + typedef GTEST_REMOVE_REFERENCE_AND_CONST_(Optional1) Optional1View; + typedef typename Optional1View::value_type ValueType1; + typedef std::tuple OptionalTuple; + typedef std::tuple ValuePair; + + explicit PairImpl(const ValueMatcher& value_matcher) + : value_matcher_(MatcherCast(value_matcher)) {} + + void DescribeTo(::std::ostream* os) const override { + *os << "are optionals where the values "; + value_matcher_.DescribeTo(os); + } + + void DescribeNegationTo(::std::ostream* os) const override { + *os << "are optionals where the values "; + value_matcher_.DescribeNegationTo(os); + } + + bool MatchAndExplain(OptionalTuple optional_tuple, + MatchResultListener* listener) const override { + const auto& optional1 = std::get<0>(optional_tuple); + const auto& value2 = std::get<1>(optional_tuple); + if (!optional1) { + *listener << "left is nullopt"; + return false; + } + const ValueType1& value1 = *optional1; + StringMatchResultListener value_listener; + const bool match = value_matcher_.MatchAndExplain( + std::make_tuple(value1, value2), &value_listener); + *listener << (match ? "which match" : "whose values don't match"); + PrintIfNotEmpty(value_listener.str(), listener->stream()); + return match; + } + + private: + const Matcher value_matcher_; + GTEST_DISALLOW_ASSIGN_(PairImpl); + }; + private: const ValueMatcher value_matcher_; GTEST_DISALLOW_ASSIGN_(OptionalMatcher); diff --git a/googlemock/test/gmock-matchers_test.cc b/googlemock/test/gmock-matchers_test.cc index a61d040b..f5e25e07 100644 --- a/googlemock/test/gmock-matchers_test.cc +++ b/googlemock/test/gmock-matchers_test.cc @@ -6387,7 +6387,7 @@ class SampleOptional { explicit SampleOptional(T value) : value_(std::move(value)), has_value_(true) {} SampleOptional() : value_(), has_value_(false) {} - operator bool() const { return has_value_; } + explicit operator bool() const { return has_value_; } const T& operator*() const { return value_; } private: @@ -6427,6 +6427,39 @@ TEST(OptionalTest, WorksWithMoveOnly) { EXPECT_TRUE(m.Matches(SampleOptional>(nullptr))); } +TEST(OptionalTest, TupleDescribesSelf) { + const Matcher, int>> m = Optional(Eq()); + EXPECT_EQ("are optionals where the values are an equal pair", Describe(m)); +} + +TEST(OptionalTest, TupleExplainsSelf) { + const Matcher, int>> m = Optional(Eq()); + EXPECT_EQ("which match", + Explain(m, std::make_tuple(SampleOptional(1), 1))); + EXPECT_EQ("whose values don't match", + Explain(m, std::make_tuple(SampleOptional(1), 2))); +} + +TEST(OptionalTest, TupleMatchesNonEmpty) { + const Matcher, int>> m1 = Optional(Eq()); + const Matcher, int>> m2 = Optional(Lt()); + EXPECT_TRUE(m1.Matches(std::make_tuple(SampleOptional(1), 1))); + EXPECT_FALSE(m1.Matches(std::make_tuple(SampleOptional(1), 2))); + EXPECT_FALSE(m2.Matches(std::make_tuple(SampleOptional(1), 1))); + EXPECT_TRUE(m2.Matches(std::make_tuple(SampleOptional(1), 2))); +} + +TEST(OptionalTest, TupleDoesNotMatchNullopt) { + const Matcher, int>> m1 = Optional(Eq()); + EXPECT_FALSE(m1.Matches(std::make_tuple(SampleOptional(), 1))); +} + +TEST(OptionalTest, TupleWorksInPointwise) { + std::vector> v = { + SampleOptional(1), SampleOptional(2), SampleOptional(3)}; + EXPECT_THAT(v, Pointwise(Optional(Eq()), {1, 2, 3})); +} + class SampleVariantIntString { public: SampleVariantIntString(int i) : i_(i), has_int_(true) {}