From 6a896b5ec607a54d86bbd2efdbc0248754b042e1 Mon Sep 17 00:00:00 2001 From: "zhanyong.wan" Date: Fri, 16 Jan 2009 01:13:50 +0000 Subject: [PATCH] Implements ContainerEq. --- include/gmock/gmock-matchers.h | 78 ++++++++++++++++++ test/gmock-matchers_test.cc | 146 ++++++++++++++++++++++++++++++++- 2 files changed, 223 insertions(+), 1 deletion(-) diff --git a/include/gmock/gmock-matchers.h b/include/gmock/gmock-matchers.h index 69879a48..9113d178 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 #include // NOLINT #include #include @@ -1635,6 +1636,73 @@ void ExplainMatchResultTo(const ResultOfMatcher& 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 +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::Print(rhs_, os); + } + void DescribeNegationTo(::std::ostream* os) const { + *os << "does not equal "; + UniversalPrinter::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::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::Print(*it, os); + } + } + } + private: + const Container rhs_; +}; + +template +void ExplainMatchResultTo(const ContainerEqMatcher& 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(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 +inline PolymorphicMatcher > + ContainerEq(const Container& rhs) { + return MakePolymorphicMatcher(internal::ContainerEqMatcher(rhs)); +} + // Returns a predicate that is satisfied by anything that matches the // given matcher. template diff --git a/test/gmock-matchers_test.cc b/test/gmock-matchers_test.cc index 29b038e4..e9d12b29 100644 --- a/test/gmock-matchers_test.cc +++ b/test/gmock-matchers_test.cc @@ -37,8 +37,12 @@ #include #include -#include +#include +#include +#include #include +#include +#include #include #include #include @@ -2625,5 +2629,145 @@ TEST(ByRefTest, AllowsNotCopyableValueInMatchers) { EXPECT_TRUE(m.Matches(n2)); } +// Tests ContainerEq with different container types, and +// different element types. + +template +class ContainerEqTest : public testing::Test { + public: +}; + +typedef testing::Types< + std::set, + std::vector, + std::multiset, + std::list > + 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 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 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 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 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 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 my_set(vals, vals + 6); + std::vector test_set(test_vals, test_vals + 3); + const Matcher > 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 my_set(vals, vals + 6); + std::list test_set(test_vals, test_vals + 7); + const Matcher&> 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 my_set(vals, vals + 6); + std::list test_set(test_vals, test_vals + 5); + const Matcher > 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 my_set(vals, vals + 6); + std::vector test_set(test_vals, test_vals + 5); + const Matcher > 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 my_map; + my_map[0] = "a"; + my_map[1] = "b"; + + std::map test_map; + test_map[0] = "aa"; + test_map[1] = "b"; + + const Matcher&> 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