Commit 4ec4cd23 authored by Abseil Team's avatar Abseil Team Committed by Andy Soffer
Browse files

Googletest export

Implement 'Contains(e).Times(n)' matcher modifier which allows to test for arbitrary occurrences including absence with Times(0).

PiperOrigin-RevId: 382210276
parent 5f97ce4c
...@@ -116,6 +116,7 @@ messages, you can use: ...@@ -116,6 +116,7 @@ messages, you can use:
| `BeginEndDistanceIs(m)` | `argument` is a container whose `begin()` and `end()` iterators are separated by a number of increments matching `m`. E.g. `BeginEndDistanceIs(2)` or `BeginEndDistanceIs(Lt(2))`. For containers that define a `size()` method, `SizeIs(m)` may be more efficient. | | `BeginEndDistanceIs(m)` | `argument` is a container whose `begin()` and `end()` iterators are separated by a number of increments matching `m`. E.g. `BeginEndDistanceIs(2)` or `BeginEndDistanceIs(Lt(2))`. For containers that define a `size()` method, `SizeIs(m)` may be more efficient. |
| `ContainerEq(container)` | The same as `Eq(container)` except that the failure message also includes which elements are in one container but not the other. | | `ContainerEq(container)` | The same as `Eq(container)` except that the failure message also includes which elements are in one container but not the other. |
| `Contains(e)` | `argument` contains an element that matches `e`, which can be either a value or a matcher. | | `Contains(e)` | `argument` contains an element that matches `e`, which can be either a value or a matcher. |
| `Contains(e).Times(n)` | `argument` contains elements that match `e`, which can be either a value or a matcher, and the number of matches is `n`, which can be either a value or a matcher. Unlike the plain `Contains` and `Each` this allows to check for arbitrary occurrences including testing for absence with `Contains(e).Times(0)`. |
| `Each(e)` | `argument` is a container where *every* element matches `e`, which can be either a value or a matcher. | | `Each(e)` | `argument` is a container where *every* element matches `e`, which can be either a value or a matcher. |
| `ElementsAre(e0, e1, ..., en)` | `argument` has `n + 1` elements, where the *i*-th element matches `ei`, which can be a value or a matcher. | | `ElementsAre(e0, e1, ..., en)` | `argument` has `n + 1` elements, where the *i*-th element matches `ei`, which can be a value or a matcher. |
| `ElementsAreArray({e0, e1, ..., en})`, `ElementsAreArray(a_container)`, `ElementsAreArray(begin, end)`, `ElementsAreArray(array)`, or `ElementsAreArray(array, count)` | The same as `ElementsAre()` except that the expected element values/matchers come from an initializer list, STL-style container, iterator range, or C-style array. | | `ElementsAreArray({e0, e1, ..., en})`, `ElementsAreArray(a_container)`, `ElementsAreArray(begin, end)`, `ElementsAreArray(array)`, or `ElementsAreArray(array, count)` | The same as `ElementsAre()` except that the expected element values/matchers come from an initializer list, STL-style container, iterator range, or C-style array. |
......
...@@ -2581,7 +2581,7 @@ class PointwiseMatcher { ...@@ -2581,7 +2581,7 @@ class PointwiseMatcher {
StringMatchResultListener inner_listener; StringMatchResultListener inner_listener;
// Create InnerMatcherArg as a temporarily object to avoid it outlives // Create InnerMatcherArg as a temporarily object to avoid it outlives
// *left and *right. Dereference or the conversion to `const T&` may // *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( if (!mono_tuple_matcher_.MatchAndExplain(
InnerMatcherArg(ImplicitCast_<const LhsValue&>(*left), InnerMatcherArg(ImplicitCast_<const LhsValue&>(*left),
ImplicitCast_<const RhsValue&>(*right)), ImplicitCast_<const RhsValue&>(*right)),
...@@ -2653,6 +2653,54 @@ class QuantifierMatcherImpl : public MatcherInterface<Container> { ...@@ -2653,6 +2653,54 @@ class QuantifierMatcherImpl : public MatcherInterface<Container> {
return all_elements_should_match; 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: protected:
const Matcher<const Element&> inner_matcher_; const Matcher<const Element&> inner_matcher_;
}; };
...@@ -2709,6 +2757,58 @@ class EachMatcherImpl : public QuantifierMatcherImpl<Container> { ...@@ -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). // Implements polymorphic Contains(element_matcher).
template <typename M> template <typename M>
class ContainsMatcher { class ContainsMatcher {
...@@ -2716,11 +2816,15 @@ class ContainsMatcher { ...@@ -2716,11 +2816,15 @@ class ContainsMatcher {
explicit ContainsMatcher(M m) : inner_matcher_(m) {} explicit ContainsMatcher(M m) : inner_matcher_(m) {}
template <typename Container> template <typename Container>
operator Matcher<Container>() const { operator Matcher<Container>() const { // NOLINT
return Matcher<Container>( return Matcher<Container>(
new ContainsMatcherImpl<const Container&>(inner_matcher_)); 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: private:
const M inner_matcher_; const M inner_matcher_;
}; };
...@@ -2732,7 +2836,7 @@ class EachMatcher { ...@@ -2732,7 +2836,7 @@ class EachMatcher {
explicit EachMatcher(M m) : inner_matcher_(m) {} explicit EachMatcher(M m) : inner_matcher_(m) {}
template <typename Container> template <typename Container>
operator Matcher<Container>() const { operator Matcher<Container>() const { // NOLINT
return Matcher<Container>( return Matcher<Container>(
new EachMatcherImpl<const Container&>(inner_matcher_)); new EachMatcherImpl<const Container&>(inner_matcher_));
} }
...@@ -4615,7 +4719,6 @@ UnorderedPointwise(const Tuple2Matcher& tuple2_matcher, ...@@ -4615,7 +4719,6 @@ UnorderedPointwise(const Tuple2Matcher& tuple2_matcher,
return UnorderedPointwise(tuple2_matcher, std::vector<T>(rhs)); return UnorderedPointwise(tuple2_matcher, std::vector<T>(rhs));
} }
// Matches an STL-style container or a native array that contains at // Matches an STL-style container or a native array that contains at
// least one element matching the given value or matcher. // least one element matching the given value or matcher.
// //
...@@ -4625,7 +4728,7 @@ UnorderedPointwise(const Tuple2Matcher& tuple2_matcher, ...@@ -4625,7 +4728,7 @@ UnorderedPointwise(const Tuple2Matcher& tuple2_matcher,
// page_ids.insert(1); // page_ids.insert(1);
// EXPECT_THAT(page_ids, Contains(1)); // EXPECT_THAT(page_ids, Contains(1));
// EXPECT_THAT(page_ids, Contains(Gt(2))); // 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; // ::std::map<int, size_t> page_lengths;
// page_lengths[1] = 100; // page_lengths[1] = 100;
...@@ -4634,6 +4737,19 @@ UnorderedPointwise(const Tuple2Matcher& tuple2_matcher, ...@@ -4634,6 +4737,19 @@ UnorderedPointwise(const Tuple2Matcher& tuple2_matcher,
// //
// const char* user_ids[] = { "joe", "mike", "tom" }; // const char* user_ids[] = { "joe", "mike", "tom" };
// EXPECT_THAT(user_ids, Contains(Eq(::std::string("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> template <typename M>
inline internal::ContainsMatcher<M> Contains(M matcher) { inline internal::ContainsMatcher<M> Contains(M matcher) {
return internal::ContainsMatcher<M>(matcher); return internal::ContainsMatcher<M>(matcher);
...@@ -4760,7 +4876,7 @@ inline internal::UnorderedElementsAreArrayMatcher<T> IsSubsetOf( ...@@ -4760,7 +4876,7 @@ inline internal::UnorderedElementsAreArrayMatcher<T> IsSubsetOf(
// Matches an STL-style container or a native array that contains only // Matches an STL-style container or a native array that contains only
// elements matching the given value or matcher. // 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. // the messages are different.
// //
// Examples: // Examples:
......
...@@ -114,31 +114,32 @@ std::vector<std::unique_ptr<int>> MakeUniquePtrs(const std::vector<int>& ints) { ...@@ -114,31 +114,32 @@ std::vector<std::unique_ptr<int>> MakeUniquePtrs(const std::vector<int>& ints) {
} }
// For testing ExplainMatchResultTo(). // For testing ExplainMatchResultTo().
class GreaterThanMatcher : public MatcherInterface<int> { template <typename T = int>
class GreaterThanMatcher : public MatcherInterface<T> {
public: public:
explicit GreaterThanMatcher(int rhs) : rhs_(rhs) {} explicit GreaterThanMatcher(T rhs) : rhs_(rhs) {}
void DescribeTo(ostream* os) const override { *os << "is > " << rhs_; } void DescribeTo(ostream* os) const override { *os << "is > " << rhs_; }
bool MatchAndExplain(int lhs, MatchResultListener* listener) const override { bool MatchAndExplain(T lhs, MatchResultListener* listener) const override {
const int diff = lhs - rhs_; if (lhs > rhs_) {
if (diff > 0) { *listener << "which is " << (lhs - rhs_) << " more than " << rhs_;
*listener << "which is " << diff << " more than " << rhs_; } else if (lhs == rhs_) {
} else if (diff == 0) {
*listener << "which is the same as " << rhs_; *listener << "which is the same as " << rhs_;
} else { } else {
*listener << "which is " << -diff << " less than " << rhs_; *listener << "which is " << (rhs_ - lhs) << " less than " << rhs_;
} }
return lhs > rhs_; return lhs > rhs_;
} }
private: private:
int rhs_; const T rhs_;
}; };
Matcher<int> GreaterThan(int n) { template <typename T>
return MakeMatcher(new GreaterThanMatcher(n)); Matcher<T> GreaterThan(T n) {
return MakeMatcher(new GreaterThanMatcher<T>(n));
} }
std::string OfType(const std::string& type_name) { std::string OfType(const std::string& type_name) {
...@@ -8023,6 +8024,7 @@ TEST(ContainsTest, ListMatchesWhenElementIsInContainer) { ...@@ -8023,6 +8024,7 @@ TEST(ContainsTest, ListMatchesWhenElementIsInContainer) {
some_list.push_back(3); some_list.push_back(3);
some_list.push_back(1); some_list.push_back(1);
some_list.push_back(2); some_list.push_back(2);
some_list.push_back(3);
EXPECT_THAT(some_list, Contains(1)); EXPECT_THAT(some_list, Contains(1));
EXPECT_THAT(some_list, Contains(Gt(2.5))); EXPECT_THAT(some_list, Contains(Gt(2.5)));
EXPECT_THAT(some_list, Contains(Eq(2.0f))); EXPECT_THAT(some_list, Contains(Eq(2.0f)));
...@@ -8147,6 +8149,79 @@ TEST(ContainsTest, WorksForTwoDimensionalNativeArray) { ...@@ -8147,6 +8149,79 @@ TEST(ContainsTest, WorksForTwoDimensionalNativeArray) {
EXPECT_THAT(a, Contains(Not(Contains(5)))); 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) { TEST(AllOfArrayTest, BasicForms) {
// Iterator // Iterator
std::vector<int> v0{}; std::vector<int> v0{};
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment