diff options
author | Abseil Team <absl-team@google.com> | 2021-06-30 01:55:02 (GMT) |
---|---|---|
committer | Andy Soffer <asoffer@google.com> | 2021-06-30 17:33:57 (GMT) |
commit | 4ec4cd23f486bf70efcc5d2caa40f24368f752e3 (patch) | |
tree | c074bfc531a1e6763297cb11891af2f3931b7179 /googlemock | |
parent | 5f97ce4c70b888a5a338a93c6f642d8ee6ddcded (diff) | |
download | googletest-4ec4cd23f486bf70efcc5d2caa40f24368f752e3.zip googletest-4ec4cd23f486bf70efcc5d2caa40f24368f752e3.tar.gz googletest-4ec4cd23f486bf70efcc5d2caa40f24368f752e3.tar.bz2 |
Googletest export
Implement 'Contains(e).Times(n)' matcher modifier which allows to test for arbitrary occurrences including absence with Times(0).
PiperOrigin-RevId: 382210276
Diffstat (limited to 'googlemock')
-rw-r--r-- | googlemock/include/gmock/gmock-matchers.h | 128 | ||||
-rw-r--r-- | googlemock/test/gmock-matchers_test.cc | 97 |
2 files changed, 208 insertions, 17 deletions
diff --git a/googlemock/include/gmock/gmock-matchers.h b/googlemock/include/gmock/gmock-matchers.h index be174b7..e1a7606 100644 --- a/googlemock/include/gmock/gmock-matchers.h +++ b/googlemock/include/gmock/gmock-matchers.h @@ -2581,7 +2581,7 @@ class PointwiseMatcher { StringMatchResultListener inner_listener; // Create InnerMatcherArg as a temporarily object to avoid it outlives // *left and *right. Dereference or the conversion to `const T&` may - // return temp objects, e.g for vector<bool>. + // return temp objects, e.g. for vector<bool>. if (!mono_tuple_matcher_.MatchAndExplain( InnerMatcherArg(ImplicitCast_<const LhsValue&>(*left), ImplicitCast_<const RhsValue&>(*right)), @@ -2653,6 +2653,54 @@ class QuantifierMatcherImpl : public MatcherInterface<Container> { return all_elements_should_match; } + bool MatchAndExplainImpl(const Matcher<size_t>& count_matcher, + Container container, + MatchResultListener* listener) const { + StlContainerReference stl_container = View::ConstReference(container); + size_t i = 0; + std::vector<size_t> match_elements; + for (auto it = stl_container.begin(); it != stl_container.end(); + ++it, ++i) { + StringMatchResultListener inner_listener; + const bool matches = inner_matcher_.MatchAndExplain(*it, &inner_listener); + if (matches) { + match_elements.push_back(i); + } + } + if (listener->IsInterested()) { + if (match_elements.empty()) { + *listener << "has no element that matches"; + } else if (match_elements.size() == 1) { + *listener << "whose element #" << match_elements[0] << " matches"; + } else { + *listener << "whose elements ("; + std::string sep = ""; + for (size_t e : match_elements) { + *listener << sep << e; + sep = ", "; + } + *listener << ") match"; + } + } + StringMatchResultListener count_listener; + if (count_matcher.MatchAndExplain(match_elements.size(), &count_listener)) { + *listener << " and whose match quantity of " << match_elements.size() + << " matches"; + PrintIfNotEmpty(count_listener.str(), listener->stream()); + return true; + } else { + if (match_elements.empty()) { + *listener << " and"; + } else { + *listener << " but"; + } + *listener << " whose match quantity of " << match_elements.size() + << " does not match"; + PrintIfNotEmpty(count_listener.str(), listener->stream()); + return false; + } + } + protected: const Matcher<const Element&> inner_matcher_; }; @@ -2709,6 +2757,58 @@ class EachMatcherImpl : public QuantifierMatcherImpl<Container> { } }; +// Implements Contains(element_matcher).Times(n) for the given argument type +// Container. +template <typename Container> +class ContainsTimesMatcherImpl : public QuantifierMatcherImpl<Container> { + public: + template <typename InnerMatcher> + explicit ContainsTimesMatcherImpl(InnerMatcher inner_matcher, + Matcher<size_t> count_matcher) + : QuantifierMatcherImpl<Container>(inner_matcher), + count_matcher_(std::move(count_matcher)) {} + + void DescribeTo(::std::ostream* os) const override { + *os << "quantity of elements that match "; + this->inner_matcher_.DescribeTo(os); + *os << " "; + count_matcher_.DescribeTo(os); + } + + void DescribeNegationTo(::std::ostream* os) const override { + *os << "quantity of elements that match "; + this->inner_matcher_.DescribeTo(os); + *os << " "; + count_matcher_.DescribeNegationTo(os); + } + + bool MatchAndExplain(Container container, + MatchResultListener* listener) const override { + return this->MatchAndExplainImpl(count_matcher_, container, listener); + } + + private: + const Matcher<size_t> count_matcher_; +}; + +// Implements polymorphic Contains(element_matcher).Times(n). +template <typename M> +class ContainsTimesMatcher { + public: + explicit ContainsTimesMatcher(M m, Matcher<size_t> count_matcher) + : inner_matcher_(m), count_matcher_(std::move(count_matcher)) {} + + template <typename Container> + operator Matcher<Container>() const { // NOLINT + return Matcher<Container>(new ContainsTimesMatcherImpl<const Container&>( + inner_matcher_, count_matcher_)); + } + + private: + const M inner_matcher_; + const Matcher<size_t> count_matcher_; +}; + // Implements polymorphic Contains(element_matcher). template <typename M> class ContainsMatcher { @@ -2716,11 +2816,15 @@ class ContainsMatcher { explicit ContainsMatcher(M m) : inner_matcher_(m) {} template <typename Container> - operator Matcher<Container>() const { + operator Matcher<Container>() const { // NOLINT return Matcher<Container>( new ContainsMatcherImpl<const Container&>(inner_matcher_)); } + ContainsTimesMatcher<M> Times(Matcher<size_t> count_matcher) const { + return ContainsTimesMatcher<M>(inner_matcher_, std::move(count_matcher)); + } + private: const M inner_matcher_; }; @@ -2732,7 +2836,7 @@ class EachMatcher { explicit EachMatcher(M m) : inner_matcher_(m) {} template <typename Container> - operator Matcher<Container>() const { + operator Matcher<Container>() const { // NOLINT return Matcher<Container>( new EachMatcherImpl<const Container&>(inner_matcher_)); } @@ -4615,7 +4719,6 @@ UnorderedPointwise(const Tuple2Matcher& tuple2_matcher, return UnorderedPointwise(tuple2_matcher, std::vector<T>(rhs)); } - // Matches an STL-style container or a native array that contains at // least one element matching the given value or matcher. // @@ -4625,7 +4728,7 @@ UnorderedPointwise(const Tuple2Matcher& tuple2_matcher, // page_ids.insert(1); // EXPECT_THAT(page_ids, Contains(1)); // EXPECT_THAT(page_ids, Contains(Gt(2))); -// EXPECT_THAT(page_ids, Not(Contains(4))); +// EXPECT_THAT(page_ids, Not(Contains(4))); // See below for Times(0) // // ::std::map<int, size_t> page_lengths; // page_lengths[1] = 100; @@ -4634,6 +4737,19 @@ UnorderedPointwise(const Tuple2Matcher& tuple2_matcher, // // const char* user_ids[] = { "joe", "mike", "tom" }; // EXPECT_THAT(user_ids, Contains(Eq(::std::string("tom")))); +// +// The matcher supports a modifier `Times` that allows to check for arbitrary +// occurrences including testing for absence with Times(0). +// +// Examples: +// ::std::vector<int> ids; +// ids.insert(1); +// ids.insert(1); +// ids.insert(3); +// EXPECT_THAT(ids, Contains(1).Times(2)); // 1 occurs 2 times +// EXPECT_THAT(ids, Contains(2).Times(0)); // 2 is not present +// EXPECT_THAT(ids, Contains(3).Times(Ge(1))); // 3 occurs at least once + template <typename M> inline internal::ContainsMatcher<M> Contains(M matcher) { return internal::ContainsMatcher<M>(matcher); @@ -4760,7 +4876,7 @@ inline internal::UnorderedElementsAreArrayMatcher<T> IsSubsetOf( // Matches an STL-style container or a native array that contains only // elements matching the given value or matcher. // -// Each(m) is semantically equivalent to Not(Contains(Not(m))). Only +// Each(m) is semantically equivalent to `Not(Contains(Not(m)))`. Only // the messages are different. // // Examples: diff --git a/googlemock/test/gmock-matchers_test.cc b/googlemock/test/gmock-matchers_test.cc index 1f48a76..e7ce97c 100644 --- a/googlemock/test/gmock-matchers_test.cc +++ b/googlemock/test/gmock-matchers_test.cc @@ -114,31 +114,32 @@ std::vector<std::unique_ptr<int>> MakeUniquePtrs(const std::vector<int>& ints) { } // For testing ExplainMatchResultTo(). -class GreaterThanMatcher : public MatcherInterface<int> { +template <typename T = int> +class GreaterThanMatcher : public MatcherInterface<T> { public: - explicit GreaterThanMatcher(int rhs) : rhs_(rhs) {} + explicit GreaterThanMatcher(T rhs) : rhs_(rhs) {} void DescribeTo(ostream* os) const override { *os << "is > " << rhs_; } - bool MatchAndExplain(int lhs, MatchResultListener* listener) const override { - const int diff = lhs - rhs_; - if (diff > 0) { - *listener << "which is " << diff << " more than " << rhs_; - } else if (diff == 0) { + bool MatchAndExplain(T lhs, MatchResultListener* listener) const override { + if (lhs > rhs_) { + *listener << "which is " << (lhs - rhs_) << " more than " << rhs_; + } else if (lhs == rhs_) { *listener << "which is the same as " << rhs_; } else { - *listener << "which is " << -diff << " less than " << rhs_; + *listener << "which is " << (rhs_ - lhs) << " less than " << rhs_; } return lhs > rhs_; } private: - int rhs_; + const T rhs_; }; -Matcher<int> GreaterThan(int n) { - return MakeMatcher(new GreaterThanMatcher(n)); +template <typename T> +Matcher<T> GreaterThan(T n) { + return MakeMatcher(new GreaterThanMatcher<T>(n)); } std::string OfType(const std::string& type_name) { @@ -8023,6 +8024,7 @@ TEST(ContainsTest, ListMatchesWhenElementIsInContainer) { some_list.push_back(3); some_list.push_back(1); some_list.push_back(2); + some_list.push_back(3); EXPECT_THAT(some_list, Contains(1)); EXPECT_THAT(some_list, Contains(Gt(2.5))); EXPECT_THAT(some_list, Contains(Eq(2.0f))); @@ -8147,6 +8149,79 @@ TEST(ContainsTest, WorksForTwoDimensionalNativeArray) { EXPECT_THAT(a, Contains(Not(Contains(5)))); } +// Tests Contains().Times(). + +TEST(ContainsTimes, ListMatchesWhenElementQuantityMatches) { + list<int> some_list; + some_list.push_back(3); + some_list.push_back(1); + some_list.push_back(2); + some_list.push_back(3); + EXPECT_THAT(some_list, Contains(3).Times(2)); + EXPECT_THAT(some_list, Contains(2).Times(1)); + EXPECT_THAT(some_list, Contains(Ge(2)).Times(3)); + EXPECT_THAT(some_list, Contains(Ge(2)).Times(Gt(2))); + EXPECT_THAT(some_list, Contains(4).Times(0)); + EXPECT_THAT(some_list, Contains(_).Times(4)); + EXPECT_THAT(some_list, Not(Contains(5).Times(1))); + EXPECT_THAT(some_list, Contains(5).Times(_)); // Times(_) always matches + EXPECT_THAT(some_list, Not(Contains(3).Times(1))); + EXPECT_THAT(some_list, Contains(3).Times(Not(1))); + EXPECT_THAT(list<int>{}, Not(Contains(_))); +} + +TEST(ContainsTimes, ExplainsMatchResultCorrectly) { + const int a[2] = {1, 2}; + Matcher<const int(&)[2]> m = Contains(2).Times(3); + EXPECT_EQ( + "whose element #1 matches but whose match quantity of 1 does not match", + Explain(m, a)); + + m = Contains(3).Times(0); + EXPECT_EQ("has no element that matches and whose match quantity of 0 matches", + Explain(m, a)); + + m = Contains(3).Times(4); + EXPECT_EQ( + "has no element that matches and whose match quantity of 0 does not " + "match", + Explain(m, a)); + + m = Contains(2).Times(4); + EXPECT_EQ( + "whose element #1 matches but whose match quantity of 1 does not " + "match", + Explain(m, a)); + + m = Contains(GreaterThan(0)).Times(2); + EXPECT_EQ("whose elements (0, 1) match and whose match quantity of 2 matches", + Explain(m, a)); + + m = Contains(GreaterThan(10)).Times(Gt(1)); + EXPECT_EQ( + "has no element that matches and whose match quantity of 0 does not " + "match", + Explain(m, a)); + + m = Contains(GreaterThan(0)).Times(GreaterThan<size_t>(5)); + EXPECT_EQ( + "whose elements (0, 1) match but whose match quantity of 2 does not " + "match, which is 3 less than 5", + Explain(m, a)); +} + +TEST(ContainsTimes, DescribesItselfCorrectly) { + Matcher<vector<int>> m = Contains(1).Times(2); + EXPECT_EQ("quantity of elements that match is equal to 1 is equal to 2", + Describe(m)); + + Matcher<vector<int>> m2 = Not(m); + EXPECT_EQ("quantity of elements that match is equal to 1 isn't equal to 2", + Describe(m2)); +} + +// Tests AllOfArray() + TEST(AllOfArrayTest, BasicForms) { // Iterator std::vector<int> v0{}; |