diff --git a/include/gmock/gmock-matchers.h b/include/gmock/gmock-matchers.h index 5e2a8d5c..de966a7a 100644 --- a/include/gmock/gmock-matchers.h +++ b/include/gmock/gmock-matchers.h @@ -2267,6 +2267,67 @@ class SizeIsMatcher { GTEST_DISALLOW_ASSIGN_(SizeIsMatcher); }; +// Implements a matcher that checks the begin()..end() distance of an STL-style +// container. +template +class BeginEndDistanceIsMatcher { + public: + explicit BeginEndDistanceIsMatcher(const DistanceMatcher& distance_matcher) + : distance_matcher_(distance_matcher) {} + + template + operator Matcher() const { + return MakeMatcher(new Impl(distance_matcher_)); + } + + template + class Impl : public MatcherInterface { + public: + typedef internal::StlContainerView< + GTEST_REMOVE_REFERENCE_AND_CONST_(Container)> ContainerView; + typedef typename std::iterator_traits< + typename ContainerView::type::const_iterator>::difference_type + DistanceType; + explicit Impl(const DistanceMatcher& distance_matcher) + : distance_matcher_(MatcherCast(distance_matcher)) {} + + virtual void DescribeTo(::std::ostream* os) const { + *os << "distance between begin() and end() "; + distance_matcher_.DescribeTo(os); + } + virtual void DescribeNegationTo(::std::ostream* os) const { + *os << "distance between begin() and end() "; + distance_matcher_.DescribeNegationTo(os); + } + + virtual bool MatchAndExplain(Container container, + MatchResultListener* listener) const { +#if GTEST_LANG_CXX11 + using std::begin; + using std::end; + DistanceType distance = std::distance(begin(container), end(container)); +#else + DistanceType distance = std::distance(container.begin(), container.end()); +#endif + StringMatchResultListener distance_listener; + const bool result = + distance_matcher_.MatchAndExplain(distance, &distance_listener); + *listener << "whose distance between begin() and end() " << distance + << (result ? " matches" : " doesn't match"); + PrintIfNotEmpty(distance_listener.str(), listener->stream()); + return result; + } + + private: + const Matcher distance_matcher_; + GTEST_DISALLOW_ASSIGN_(Impl); + }; + + private: + const DistanceMatcher distance_matcher_; + GTEST_DISALLOW_ASSIGN_(BeginEndDistanceIsMatcher); +}; + // 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. @@ -3799,6 +3860,17 @@ SizeIs(const SizeMatcher& size_matcher) { return internal::SizeIsMatcher(size_matcher); } +// Returns a matcher that matches the distance between the container's begin() +// iterator and its end() iterator, i.e. the size of the container. This matcher +// can be used instead of SizeIs with containers such as std::forward_list which +// do not implement size(). The container must provide const_iterator (with +// valid iterator_traits), begin() and end(). +template +inline internal::BeginEndDistanceIsMatcher +BeginEndDistanceIs(const DistanceMatcher& distance_matcher) { + return internal::BeginEndDistanceIsMatcher(distance_matcher); +} + // 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 diff --git a/test/gmock-matchers_test.cc b/test/gmock-matchers_test.cc index 2a5cf908..bd20add2 100644 --- a/test/gmock-matchers_test.cc +++ b/test/gmock-matchers_test.cc @@ -54,6 +54,10 @@ #include "gtest/gtest.h" #include "gtest/gtest-spi.h" +#if GTEST_LANG_CXX11 +# include // NOLINT +#endif + namespace testing { namespace internal { @@ -4419,6 +4423,63 @@ TEST(StreamlikeTest, Iteration) { } } +#if GTEST_LANG_CXX11 +TEST(BeginEndDistanceIsTest, WorksWithForwardList) { + std::forward_list container; + EXPECT_THAT(container, BeginEndDistanceIs(0)); + EXPECT_THAT(container, Not(BeginEndDistanceIs(1))); + container.push_front(0); + EXPECT_THAT(container, Not(BeginEndDistanceIs(0))); + EXPECT_THAT(container, BeginEndDistanceIs(1)); + container.push_front(0); + EXPECT_THAT(container, Not(BeginEndDistanceIs(0))); + EXPECT_THAT(container, BeginEndDistanceIs(2)); +} +#endif // GTEST_LANG_CXX11 + +TEST(BeginEndDistanceIsTest, WorksWithNonStdList) { + const int a[5] = { 1, 2, 3, 4, 5 }; + Streamlike s(a, a + 5); + EXPECT_THAT(s, BeginEndDistanceIs(5)); +} + +TEST(BeginEndDistanceIsTest, CanDescribeSelf) { + Matcher > m = BeginEndDistanceIs(2); + EXPECT_EQ("distance between begin() and end() is equal to 2", Describe(m)); + EXPECT_EQ("distance between begin() and end() isn't equal to 2", + DescribeNegation(m)); +} + +TEST(BeginEndDistanceIsTest, ExplainsResult) { + Matcher > m1 = BeginEndDistanceIs(2); + Matcher > m2 = BeginEndDistanceIs(Lt(2)); + Matcher > m3 = BeginEndDistanceIs(AnyOf(0, 3)); + Matcher > m4 = BeginEndDistanceIs(GreaterThan(1)); + vector container; + EXPECT_EQ("whose distance between begin() and end() 0 doesn't match", + Explain(m1, container)); + EXPECT_EQ("whose distance between begin() and end() 0 matches", + Explain(m2, container)); + EXPECT_EQ("whose distance between begin() and end() 0 matches", + Explain(m3, container)); + EXPECT_EQ( + "whose distance between begin() and end() 0 doesn't match, which is 1 " + "less than 1", + Explain(m4, container)); + container.push_back(0); + container.push_back(0); + EXPECT_EQ("whose distance between begin() and end() 2 matches", + Explain(m1, container)); + EXPECT_EQ("whose distance between begin() and end() 2 doesn't match", + Explain(m2, container)); + EXPECT_EQ("whose distance between begin() and end() 2 doesn't match", + Explain(m3, container)); + EXPECT_EQ( + "whose distance between begin() and end() 2 matches, which is 1 more " + "than 1", + Explain(m4, container)); +} + TEST(WhenSortedTest, WorksForStreamlike) { // Streamlike 'container' provides only minimal iterator support. // Its iterators are tagged with input_iterator_tag.