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/include/gmock | |
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/include/gmock')
-rw-r--r-- | googlemock/include/gmock/gmock-matchers.h | 128 |
1 files changed, 122 insertions, 6 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: |