Googletest export
Implement 'Contains(e).Times(n)' matcher modifier which allows to test for arbitrary occurrences including absence with Times(0). PiperOrigin-RevId: 382210276
This commit is contained in:
parent
5f97ce4c70
commit
4ec4cd23f4
@ -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. |
|
||||
| `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).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. |
|
||||
| `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. |
|
||||
|
@ -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:
|
||||
|
@ -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{};
|
||||
|
Loading…
Reference in New Issue
Block a user