summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorzhanyong.wan <zhanyong.wan@8415998a-534a-0410-bf83-d39667b30386>2009-01-16 01:13:50 +0000
committerzhanyong.wan <zhanyong.wan@8415998a-534a-0410-bf83-d39667b30386>2009-01-16 01:13:50 +0000
commitfcc494cff0ae711df8c7209ef1fadda243ed81e8 (patch)
tree683abab39c6075a33d25b46b37702adfff3108fd
parent35b75ba071d905fb1fb91836d3e1ba36a60901dc (diff)
downloadgooglemock-fcc494cff0ae711df8c7209ef1fadda243ed81e8.tar.gz
Implements ContainerEq.
git-svn-id: http://googlemock.googlecode.com/svn/trunk@87 8415998a-534a-0410-bf83-d39667b30386
-rw-r--r--include/gmock/gmock-matchers.h78
-rw-r--r--test/gmock-matchers_test.cc146
2 files changed, 223 insertions, 1 deletions
diff --git a/include/gmock/gmock-matchers.h b/include/gmock/gmock-matchers.h
index 69879a4..9113d17 100644
--- a/include/gmock/gmock-matchers.h
+++ b/include/gmock/gmock-matchers.h
@@ -38,6 +38,7 @@
#ifndef GMOCK_INCLUDE_GMOCK_GMOCK_MATCHERS_H_
#define GMOCK_INCLUDE_GMOCK_GMOCK_MATCHERS_H_
+#include <algorithm>
#include <ostream> // NOLINT
#include <sstream>
#include <string>
@@ -1635,6 +1636,73 @@ void ExplainMatchResultTo(const ResultOfMatcher<Callable>& matcher,
matcher.ExplainMatchResultTo(obj, os);
}
+// Implements an equality matcher for any STL-style container whose elements
+// support ==. This matcher is like Eq(), but its failure explanations provide
+// more detailed information that is useful when the container is used as a set.
+// The failure message reports elements that are in one of the operands but not
+// the other. The failure messages do not report duplicate or out-of-order
+// elements in the containers (which don't properly matter to sets, but can
+// occur if the containers are vectors or lists, for example).
+//
+// Uses the container's const_iterator, value_type, operator ==,
+// begin(), and end().
+template <typename Container>
+class ContainerEqMatcher {
+ public:
+ explicit ContainerEqMatcher(const Container& rhs) : rhs_(rhs) {}
+ bool Matches(const Container& lhs) const { return lhs == rhs_; }
+ void DescribeTo(::std::ostream* os) const {
+ *os << "equals ";
+ UniversalPrinter<Container>::Print(rhs_, os);
+ }
+ void DescribeNegationTo(::std::ostream* os) const {
+ *os << "does not equal ";
+ UniversalPrinter<Container>::Print(rhs_, os);
+ }
+
+ void ExplainMatchResultTo(const Container& lhs,
+ ::std::ostream* os) const {
+ // Something is different. Check for missing values first.
+ bool printed_header = false;
+ for (typename Container::const_iterator it = lhs.begin();
+ it != lhs.end(); ++it) {
+ if (std::find(rhs_.begin(), rhs_.end(), *it) == rhs_.end()) {
+ if (printed_header) {
+ *os << ", ";
+ } else {
+ *os << "Only in actual: ";
+ printed_header = true;
+ }
+ UniversalPrinter<typename Container::value_type>::Print(*it, os);
+ }
+ }
+
+ // Now check for extra values.
+ bool printed_header2 = false;
+ for (typename Container::const_iterator it = rhs_.begin();
+ it != rhs_.end(); ++it) {
+ if (std::find(lhs.begin(), lhs.end(), *it) == lhs.end()) {
+ if (printed_header2) {
+ *os << ", ";
+ } else {
+ *os << (printed_header ? "; not" : "Not") << " in actual: ";
+ printed_header2 = true;
+ }
+ UniversalPrinter<typename Container::value_type>::Print(*it, os);
+ }
+ }
+ }
+ private:
+ const Container rhs_;
+};
+
+template <typename Container>
+void ExplainMatchResultTo(const ContainerEqMatcher<Container>& matcher,
+ const Container& lhs,
+ ::std::ostream* os) {
+ matcher.ExplainMatchResultTo(lhs, os);
+}
+
} // namespace internal
// Implements MatcherCast().
@@ -2073,6 +2141,16 @@ Truly(Predicate pred) {
return MakePolymorphicMatcher(internal::TrulyMatcher<Predicate>(pred));
}
+// Returns a matcher that matches an equal container.
+// This matcher behaves like Eq(), but in the event of mismatch lists the
+// values that are included in one container but not the other. (Duplicate
+// values and order differences are not explained.)
+template <typename Container>
+inline PolymorphicMatcher<internal::ContainerEqMatcher<Container> >
+ ContainerEq(const Container& rhs) {
+ return MakePolymorphicMatcher(internal::ContainerEqMatcher<Container>(rhs));
+}
+
// Returns a predicate that is satisfied by anything that matches the
// given matcher.
template <typename M>
diff --git a/test/gmock-matchers_test.cc b/test/gmock-matchers_test.cc
index 29b038e..e9d12b2 100644
--- a/test/gmock-matchers_test.cc
+++ b/test/gmock-matchers_test.cc
@@ -37,8 +37,12 @@
#include <string.h>
#include <functional>
-#include <string>
+#include <list>
+#include <map>
+#include <set>
#include <sstream>
+#include <string>
+#include <vector>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <gtest/gtest-spi.h>
@@ -2625,5 +2629,145 @@ TEST(ByRefTest, AllowsNotCopyableValueInMatchers) {
EXPECT_TRUE(m.Matches(n2));
}
+// Tests ContainerEq with different container types, and
+// different element types.
+
+template <typename T>
+class ContainerEqTest : public testing::Test {
+ public:
+};
+
+typedef testing::Types<
+ std::set<int>,
+ std::vector<size_t>,
+ std::multiset<size_t>,
+ std::list<int> >
+ ContainerEqTestTypes;
+
+TYPED_TEST_CASE(ContainerEqTest, ContainerEqTestTypes);
+
+// Tests that the filled container is equal to itself.
+TYPED_TEST(ContainerEqTest, EqualsSelf) {
+ static const int vals[] = {1, 1, 2, 3, 5, 8};
+ TypeParam my_set(vals, vals + 6);
+ const Matcher<TypeParam> m = ContainerEq(my_set);
+ EXPECT_TRUE(m.Matches(my_set));
+ EXPECT_EQ("", Explain(m, my_set));
+}
+
+// Tests that missing values are reported.
+TYPED_TEST(ContainerEqTest, ValueMissing) {
+ static const int vals[] = {1, 1, 2, 3, 5, 8};
+ static const int test_vals[] = {2, 1, 8, 5};
+ TypeParam my_set(vals, vals + 6);
+ TypeParam test_set(test_vals, test_vals + 4);
+ const Matcher<TypeParam> m = ContainerEq(my_set);
+ EXPECT_FALSE(m.Matches(test_set));
+ EXPECT_EQ("Not in actual: 3", Explain(m, test_set));
+}
+
+// Tests that added values are reported.
+TYPED_TEST(ContainerEqTest, ValueAdded) {
+ static const int vals[] = {1, 1, 2, 3, 5, 8};
+ static const int test_vals[] = {1, 2, 3, 5, 8, 46};
+ TypeParam my_set(vals, vals + 6);
+ TypeParam test_set(test_vals, test_vals + 6);
+ const Matcher<const TypeParam&> m = ContainerEq(my_set);
+ EXPECT_FALSE(m.Matches(test_set));
+ EXPECT_EQ("Only in actual: 46", Explain(m, test_set));
+}
+
+// Tests that added and missing values are reported together.
+TYPED_TEST(ContainerEqTest, ValueAddedAndRemoved) {
+ static const int vals[] = {1, 1, 2, 3, 5, 8};
+ static const int test_vals[] = {1, 2, 3, 8, 46};
+ TypeParam my_set(vals, vals + 6);
+ TypeParam test_set(test_vals, test_vals + 5);
+ const Matcher<TypeParam> m = ContainerEq(my_set);
+ EXPECT_FALSE(m.Matches(test_set));
+ EXPECT_EQ("Only in actual: 46; not in actual: 5", Explain(m, test_set));
+}
+
+// Tests duplicated value -- expect no explanation.
+TYPED_TEST(ContainerEqTest, DuplicateDifference) {
+ static const int vals[] = {1, 1, 2, 3, 5, 8};
+ static const int test_vals[] = {1, 2, 3, 5, 8};
+ TypeParam my_set(vals, vals + 6);
+ TypeParam test_set(test_vals, test_vals + 5);
+ const Matcher<const TypeParam&> m = ContainerEq(my_set);
+ // Depending on the container, match may be true or false
+ // But in any case there should be no explanation.
+ EXPECT_EQ("", Explain(m, test_set));
+}
+
+// Tests that mutliple missing values are reported.
+// Using just vector here, so order is predicatble.
+TEST(ContainerEqExtraTest, MultipleValuesMissing) {
+ static const int vals[] = {1, 1, 2, 3, 5, 8};
+ static const int test_vals[] = {2, 1, 5};
+ std::vector<int> my_set(vals, vals + 6);
+ std::vector<int> test_set(test_vals, test_vals + 3);
+ const Matcher<std::vector<int> > m = ContainerEq(my_set);
+ EXPECT_FALSE(m.Matches(test_set));
+ EXPECT_EQ("Not in actual: 3, 8", Explain(m, test_set));
+}
+
+// Tests that added values are reported.
+// Using just vector here, so order is predicatble.
+TEST(ContainerEqExtraTest, MultipleValuesAdded) {
+ static const int vals[] = {1, 1, 2, 3, 5, 8};
+ static const int test_vals[] = {1, 2, 92, 3, 5, 8, 46};
+ std::list<size_t> my_set(vals, vals + 6);
+ std::list<size_t> test_set(test_vals, test_vals + 7);
+ const Matcher<const std::list<size_t>&> m = ContainerEq(my_set);
+ EXPECT_FALSE(m.Matches(test_set));
+ EXPECT_EQ("Only in actual: 92, 46", Explain(m, test_set));
+}
+
+// Tests that added and missing values are reported together.
+TEST(ContainerEqExtraTest, MultipleValuesAddedAndRemoved) {
+ static const int vals[] = {1, 1, 2, 3, 5, 8};
+ static const int test_vals[] = {1, 2, 3, 92, 46};
+ std::list<size_t> my_set(vals, vals + 6);
+ std::list<size_t> test_set(test_vals, test_vals + 5);
+ const Matcher<const std::list<size_t> > m = ContainerEq(my_set);
+ EXPECT_FALSE(m.Matches(test_set));
+ EXPECT_EQ("Only in actual: 92, 46; not in actual: 5, 8",
+ Explain(m, test_set));
+}
+
+// Tests to see that duplicate elements are detected,
+// but (as above) not reported in the explanation.
+TEST(ContainerEqExtraTest, MultiSetOfIntDuplicateDifference) {
+ static const int vals[] = {1, 1, 2, 3, 5, 8};
+ static const int test_vals[] = {1, 2, 3, 5, 8};
+ std::vector<int> my_set(vals, vals + 6);
+ std::vector<int> test_set(test_vals, test_vals + 5);
+ const Matcher<std::vector<int> > m = ContainerEq(my_set);
+ EXPECT_TRUE(m.Matches(my_set));
+ EXPECT_FALSE(m.Matches(test_set));
+ // There is nothing to report when both sets contain all the same values.
+ EXPECT_EQ("", Explain(m, test_set));
+}
+
+// Tests that ContainerEq works for non-trivial associative containers,
+// like maps.
+TEST(ContainerEqExtraTest, WorksForMaps) {
+ std::map<int, std::string> my_map;
+ my_map[0] = "a";
+ my_map[1] = "b";
+
+ std::map<int, std::string> test_map;
+ test_map[0] = "aa";
+ test_map[1] = "b";
+
+ const Matcher<const std::map<int, std::string>&> m = ContainerEq(my_map);
+ EXPECT_TRUE(m.Matches(my_map));
+ EXPECT_FALSE(m.Matches(test_map));
+
+ EXPECT_EQ("Only in actual: (0, \"aa\"); not in actual: (0, \"a\")",
+ Explain(m, test_map));
+}
+
} // namespace gmock_matchers_test
} // namespace testing