diff options
author | zhanyong.wan <zhanyong.wan@8415998a-534a-0410-bf83-d39667b30386> | 2009-06-04 05:48:20 +0000 |
---|---|---|
committer | zhanyong.wan <zhanyong.wan@8415998a-534a-0410-bf83-d39667b30386> | 2009-06-04 05:48:20 +0000 |
commit | b82431625d1842d1498f3c0e6f1923ce81837c6e (patch) | |
tree | 3d89cbedefd32bf7b2fc76fdf4e0312c206bb0a0 | |
parent | c2ad46a5df4414fc2b804c53525f4578f01a3dfe (diff) | |
download | googletest-b82431625d1842d1498f3c0e6f1923ce81837c6e.tar.gz googletest-b82431625d1842d1498f3c0e6f1923ce81837c6e.tar.bz2 googletest-b82431625d1842d1498f3c0e6f1923ce81837c6e.zip |
Makes all container matchers work with (possibly multi-dimensional) native arrays; makes Contains() accept a matcher; adds Value(x, m); improves gmock doctor to diagnose the Type in Template Base disease.
-rw-r--r-- | include/gmock/gmock-generated-matchers.h | 108 | ||||
-rw-r--r-- | include/gmock/gmock-generated-matchers.h.pump | 81 | ||||
-rw-r--r-- | include/gmock/gmock-matchers.h | 170 | ||||
-rw-r--r-- | include/gmock/gmock-printers.h | 34 | ||||
-rw-r--r-- | include/gmock/internal/gmock-internal-utils.h | 247 | ||||
-rw-r--r-- | scripts/generator/README | 2 | ||||
-rwxr-xr-x | scripts/generator/cpp/gmock_class_test.py | 2 | ||||
-rwxr-xr-x | scripts/gmock_doctor.py | 67 | ||||
-rw-r--r-- | test/gmock-generated-matchers_test.cc | 123 | ||||
-rw-r--r-- | test/gmock-internal-utils_test.cc | 231 | ||||
-rw-r--r-- | test/gmock-matchers_test.cc | 78 | ||||
-rw-r--r-- | test/gmock-printers_test.cc | 45 |
12 files changed, 1026 insertions, 162 deletions
diff --git a/include/gmock/gmock-generated-matchers.h b/include/gmock/gmock-generated-matchers.h index afe1bd48..f3484cb4 100644 --- a/include/gmock/gmock-generated-matchers.h +++ b/include/gmock/gmock-generated-matchers.h @@ -50,7 +50,10 @@ template <typename Container> class ElementsAreMatcherImpl : public MatcherInterface<Container> { public: typedef GMOCK_REMOVE_CONST_(GMOCK_REMOVE_REFERENCE_(Container)) RawContainer; - typedef typename RawContainer::value_type Element; + typedef internal::StlContainerView<RawContainer> View; + typedef typename View::type StlContainer; + typedef typename View::const_reference StlContainerReference; + typedef typename StlContainer::value_type Element; // Constructs the matcher from a sequence of element values or // element matchers. @@ -65,12 +68,13 @@ class ElementsAreMatcherImpl : public MatcherInterface<Container> { // Returns true iff 'container' matches. virtual bool Matches(Container container) const { - if (container.size() != count()) + StlContainerReference stl_container = View::ConstReference(container); + if (stl_container.size() != count()) return false; - typename RawContainer::const_iterator container_iter = container.begin(); - for (size_t i = 0; i != count(); ++container_iter, ++i) { - if (!matchers_[i].Matches(*container_iter)) + typename StlContainer::const_iterator it = stl_container.begin(); + for (size_t i = 0; i != count(); ++it, ++i) { + if (!matchers_[i].Matches(*it)) return false; } @@ -116,15 +120,16 @@ class ElementsAreMatcherImpl : public MatcherInterface<Container> { // Explains why 'container' matches, or doesn't match, this matcher. virtual void ExplainMatchResultTo(Container container, ::std::ostream* os) const { + StlContainerReference stl_container = View::ConstReference(container); if (Matches(container)) { // We need to explain why *each* element matches (the obvious // ones can be skipped). bool reason_printed = false; - typename RawContainer::const_iterator container_iter = container.begin(); - for (size_t i = 0; i != count(); ++container_iter, ++i) { + typename StlContainer::const_iterator it = stl_container.begin(); + for (size_t i = 0; i != count(); ++it, ++i) { ::std::stringstream ss; - matchers_[i].ExplainMatchResultTo(*container_iter, &ss); + matchers_[i].ExplainMatchResultTo(*it, &ss); const string s = ss.str(); if (!s.empty()) { @@ -137,7 +142,7 @@ class ElementsAreMatcherImpl : public MatcherInterface<Container> { } } else { // We need to explain why the container doesn't match. - const size_t actual_count = container.size(); + const size_t actual_count = stl_container.size(); if (actual_count != count()) { // The element count doesn't match. If the container is // empty, there's no need to explain anything as Google Mock @@ -152,16 +157,16 @@ class ElementsAreMatcherImpl : public MatcherInterface<Container> { // The container has the right size but at least one element // doesn't match expectation. We need to find this element and // explain why it doesn't match. - typename RawContainer::const_iterator container_iter = container.begin(); - for (size_t i = 0; i != count(); ++container_iter, ++i) { - if (matchers_[i].Matches(*container_iter)) { + typename StlContainer::const_iterator it = stl_container.begin(); + for (size_t i = 0; i != count(); ++it, ++i) { + if (matchers_[i].Matches(*it)) { continue; } *os << "element " << i << " doesn't match"; ::std::stringstream ss; - matchers_[i].ExplainMatchResultTo(*container_iter, &ss); + matchers_[i].ExplainMatchResultTo(*it, &ss); const string s = ss.str(); if (!s.empty()) { *os << " (" << s << ")"; @@ -190,7 +195,8 @@ class ElementsAreMatcher0 { operator Matcher<Container>() const { typedef GMOCK_REMOVE_CONST_(GMOCK_REMOVE_REFERENCE_(Container)) RawContainer; - typedef typename RawContainer::value_type Element; + typedef typename internal::StlContainerView<RawContainer>::type::value_type + Element; const Matcher<const Element&>* const matchers = NULL; return MakeMatcher(new ElementsAreMatcherImpl<Container>(matchers, 0)); @@ -206,7 +212,8 @@ class ElementsAreMatcher1 { operator Matcher<Container>() const { typedef GMOCK_REMOVE_CONST_(GMOCK_REMOVE_REFERENCE_(Container)) RawContainer; - typedef typename RawContainer::value_type Element; + typedef typename internal::StlContainerView<RawContainer>::type::value_type + Element; const Matcher<const Element&> matchers[] = { MatcherCast<const Element&>(e1_), @@ -228,7 +235,8 @@ class ElementsAreMatcher2 { operator Matcher<Container>() const { typedef GMOCK_REMOVE_CONST_(GMOCK_REMOVE_REFERENCE_(Container)) RawContainer; - typedef typename RawContainer::value_type Element; + typedef typename internal::StlContainerView<RawContainer>::type::value_type + Element; const Matcher<const Element&> matchers[] = { MatcherCast<const Element&>(e1_), @@ -253,7 +261,8 @@ class ElementsAreMatcher3 { operator Matcher<Container>() const { typedef GMOCK_REMOVE_CONST_(GMOCK_REMOVE_REFERENCE_(Container)) RawContainer; - typedef typename RawContainer::value_type Element; + typedef typename internal::StlContainerView<RawContainer>::type::value_type + Element; const Matcher<const Element&> matchers[] = { MatcherCast<const Element&>(e1_), @@ -280,7 +289,8 @@ class ElementsAreMatcher4 { operator Matcher<Container>() const { typedef GMOCK_REMOVE_CONST_(GMOCK_REMOVE_REFERENCE_(Container)) RawContainer; - typedef typename RawContainer::value_type Element; + typedef typename internal::StlContainerView<RawContainer>::type::value_type + Element; const Matcher<const Element&> matchers[] = { MatcherCast<const Element&>(e1_), @@ -309,7 +319,8 @@ class ElementsAreMatcher5 { operator Matcher<Container>() const { typedef GMOCK_REMOVE_CONST_(GMOCK_REMOVE_REFERENCE_(Container)) RawContainer; - typedef typename RawContainer::value_type Element; + typedef typename internal::StlContainerView<RawContainer>::type::value_type + Element; const Matcher<const Element&> matchers[] = { MatcherCast<const Element&>(e1_), @@ -342,7 +353,8 @@ class ElementsAreMatcher6 { operator Matcher<Container>() const { typedef GMOCK_REMOVE_CONST_(GMOCK_REMOVE_REFERENCE_(Container)) RawContainer; - typedef typename RawContainer::value_type Element; + typedef typename internal::StlContainerView<RawContainer>::type::value_type + Element; const Matcher<const Element&> matchers[] = { MatcherCast<const Element&>(e1_), @@ -377,7 +389,8 @@ class ElementsAreMatcher7 { operator Matcher<Container>() const { typedef GMOCK_REMOVE_CONST_(GMOCK_REMOVE_REFERENCE_(Container)) RawContainer; - typedef typename RawContainer::value_type Element; + typedef typename internal::StlContainerView<RawContainer>::type::value_type + Element; const Matcher<const Element&> matchers[] = { MatcherCast<const Element&>(e1_), @@ -414,7 +427,8 @@ class ElementsAreMatcher8 { operator Matcher<Container>() const { typedef GMOCK_REMOVE_CONST_(GMOCK_REMOVE_REFERENCE_(Container)) RawContainer; - typedef typename RawContainer::value_type Element; + typedef typename internal::StlContainerView<RawContainer>::type::value_type + Element; const Matcher<const Element&> matchers[] = { MatcherCast<const Element&>(e1_), @@ -454,7 +468,8 @@ class ElementsAreMatcher9 { operator Matcher<Container>() const { typedef GMOCK_REMOVE_CONST_(GMOCK_REMOVE_REFERENCE_(Container)) RawContainer; - typedef typename RawContainer::value_type Element; + typedef typename internal::StlContainerView<RawContainer>::type::value_type + Element; const Matcher<const Element&> matchers[] = { MatcherCast<const Element&>(e1_), @@ -496,7 +511,8 @@ class ElementsAreMatcher10 { operator Matcher<Container>() const { typedef GMOCK_REMOVE_CONST_(GMOCK_REMOVE_REFERENCE_(Container)) RawContainer; - typedef typename RawContainer::value_type Element; + typedef typename internal::StlContainerView<RawContainer>::type::value_type + Element; const Matcher<const Element&> matchers[] = { MatcherCast<const Element&>(e1_), @@ -538,7 +554,8 @@ class ElementsAreArrayMatcher { operator Matcher<Container>() const { typedef GMOCK_REMOVE_CONST_(GMOCK_REMOVE_REFERENCE_(Container)) RawContainer; - typedef typename RawContainer::value_type Element; + typedef typename internal::StlContainerView<RawContainer>::type::value_type + Element; return MakeMatcher(new ElementsAreMatcherImpl<Container>(first_, count_)); } @@ -1573,45 +1590,4 @@ string FormatMatcherDescription( p4##_type, p5##_type, p6##_type, p7##_type, p8##_type, p9##_type>::\ gmock_Impl<arg_type>::Matches(arg_type arg) const -namespace testing { -namespace internal { - -// Returns true iff element is in the STL-style container. -template <typename Container, typename Element> -inline bool Contains(const Container& container, const Element& element) { - return ::std::find(container.begin(), container.end(), element) != - container.end(); -} - -// Returns true iff element is in the C-style array. -template <typename ArrayElement, size_t N, typename Element> -inline bool Contains(const ArrayElement (&array)[N], const Element& element) { - return ::std::find(array, array + N, element) != array + N; -} - -} // namespace internal - -// Matches an STL-style container or a C-style array that contains the given -// element. -// -// Examples: -// ::std::set<int> page_ids; -// page_ids.insert(3); -// page_ids.insert(1); -// EXPECT_THAT(page_ids, Contains(1)); -// EXPECT_THAT(page_ids, Contains(3.0)); -// EXPECT_THAT(page_ids, Not(Contains(4))); -// -// ::std::map<int, size_t> page_lengths; -// page_lengths[1] = 100; -// EXPECT_THAT(map_int, Contains(::std::pair<const int, size_t>(1, 100))); -// -// const char* user_ids[] = { "joe", "mike", "tom" }; -// EXPECT_THAT(user_ids, Contains(::std::string("tom"))); -MATCHER_P(Contains, element, "") { - return internal::Contains(arg, element); -} - -} // namespace testing - #endif // GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_MATCHERS_H_ diff --git a/include/gmock/gmock-generated-matchers.h.pump b/include/gmock/gmock-generated-matchers.h.pump index 09dfedfc..4495547d 100644 --- a/include/gmock/gmock-generated-matchers.h.pump +++ b/include/gmock/gmock-generated-matchers.h.pump @@ -53,7 +53,10 @@ template <typename Container> class ElementsAreMatcherImpl : public MatcherInterface<Container> { public: typedef GMOCK_REMOVE_CONST_(GMOCK_REMOVE_REFERENCE_(Container)) RawContainer; - typedef typename RawContainer::value_type Element; + typedef internal::StlContainerView<RawContainer> View; + typedef typename View::type StlContainer; + typedef typename View::const_reference StlContainerReference; + typedef typename StlContainer::value_type Element; // Constructs the matcher from a sequence of element values or // element matchers. @@ -68,12 +71,13 @@ class ElementsAreMatcherImpl : public MatcherInterface<Container> { // Returns true iff 'container' matches. virtual bool Matches(Container container) const { - if (container.size() != count()) + StlContainerReference stl_container = View::ConstReference(container); + if (stl_container.size() != count()) return false; - typename RawContainer::const_iterator container_iter = container.begin(); - for (size_t i = 0; i != count(); ++container_iter, ++i) { - if (!matchers_[i].Matches(*container_iter)) + typename StlContainer::const_iterator it = stl_container.begin(); + for (size_t i = 0; i != count(); ++it, ++i) { + if (!matchers_[i].Matches(*it)) return false; } @@ -119,15 +123,16 @@ class ElementsAreMatcherImpl : public MatcherInterface<Container> { // Explains why 'container' matches, or doesn't match, this matcher. virtual void ExplainMatchResultTo(Container container, ::std::ostream* os) const { + StlContainerReference stl_container = View::ConstReference(container); if (Matches(container)) { // We need to explain why *each* element matches (the obvious // ones can be skipped). bool reason_printed = false; - typename RawContainer::const_iterator container_iter = container.begin(); - for (size_t i = 0; i != count(); ++container_iter, ++i) { + typename StlContainer::const_iterator it = stl_container.begin(); + for (size_t i = 0; i != count(); ++it, ++i) { ::std::stringstream ss; - matchers_[i].ExplainMatchResultTo(*container_iter, &ss); + matchers_[i].ExplainMatchResultTo(*it, &ss); const string s = ss.str(); if (!s.empty()) { @@ -140,7 +145,7 @@ class ElementsAreMatcherImpl : public MatcherInterface<Container> { } } else { // We need to explain why the container doesn't match. - const size_t actual_count = container.size(); + const size_t actual_count = stl_container.size(); if (actual_count != count()) { // The element count doesn't match. If the container is // empty, there's no need to explain anything as Google Mock @@ -155,16 +160,16 @@ class ElementsAreMatcherImpl : public MatcherInterface<Container> { // The container has the right size but at least one element // doesn't match expectation. We need to find this element and // explain why it doesn't match. - typename RawContainer::const_iterator container_iter = container.begin(); - for (size_t i = 0; i != count(); ++container_iter, ++i) { - if (matchers_[i].Matches(*container_iter)) { + typename StlContainer::const_iterator it = stl_container.begin(); + for (size_t i = 0; i != count(); ++it, ++i) { + if (matchers_[i].Matches(*it)) { continue; } *os << "element " << i << " doesn't match"; ::std::stringstream ss; - matchers_[i].ExplainMatchResultTo(*container_iter, &ss); + matchers_[i].ExplainMatchResultTo(*it, &ss); const string s = ss.str(); if (!s.empty()) { *os << " (" << s << ")"; @@ -193,7 +198,8 @@ class ElementsAreMatcher0 { operator Matcher<Container>() const { typedef GMOCK_REMOVE_CONST_(GMOCK_REMOVE_REFERENCE_(Container)) RawContainer; - typedef typename RawContainer::value_type Element; + typedef typename internal::StlContainerView<RawContainer>::type::value_type + Element; const Matcher<const Element&>* const matchers = NULL; return MakeMatcher(new ElementsAreMatcherImpl<Container>(matchers, 0)); @@ -214,7 +220,8 @@ class ElementsAreMatcher$i { operator Matcher<Container>() const { typedef GMOCK_REMOVE_CONST_(GMOCK_REMOVE_REFERENCE_(Container)) RawContainer; - typedef typename RawContainer::value_type Element; + typedef typename internal::StlContainerView<RawContainer>::type::value_type + Element; const Matcher<const Element&> matchers[] = { @@ -248,7 +255,8 @@ class ElementsAreArrayMatcher { operator Matcher<Container>() const { typedef GMOCK_REMOVE_CONST_(GMOCK_REMOVE_REFERENCE_(Container)) RawContainer; - typedef typename RawContainer::value_type Element; + typedef typename internal::StlContainerView<RawContainer>::type::value_type + Element; return MakeMatcher(new ElementsAreMatcherImpl<Container>(first_, count_)); } @@ -590,45 +598,4 @@ $var param_field_decls2 = [[$for j ]] -namespace testing { -namespace internal { - -// Returns true iff element is in the STL-style container. -template <typename Container, typename Element> -inline bool Contains(const Container& container, const Element& element) { - return ::std::find(container.begin(), container.end(), element) != - container.end(); -} - -// Returns true iff element is in the C-style array. -template <typename ArrayElement, size_t N, typename Element> -inline bool Contains(const ArrayElement (&array)[N], const Element& element) { - return ::std::find(array, array + N, element) != array + N; -} - -} // namespace internal - -// Matches an STL-style container or a C-style array that contains the given -// element. -// -// Examples: -// ::std::set<int> page_ids; -// page_ids.insert(3); -// page_ids.insert(1); -// EXPECT_THAT(page_ids, Contains(1)); -// EXPECT_THAT(page_ids, Contains(3.0)); -// EXPECT_THAT(page_ids, Not(Contains(4))); -// -// ::std::map<int, size_t> page_lengths; -// page_lengths[1] = 100; -// EXPECT_THAT(map_int, Contains(::std::pair<const int, size_t>(1, 100))); -// -// const char* user_ids[] = { "joe", "mike", "tom" }; -// EXPECT_THAT(user_ids, Contains(::std::string("tom"))); -MATCHER_P(Contains, element, "") { - return internal::Contains(arg, element); -} - -} // namespace testing - #endif // GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_MATCHERS_H_ diff --git a/include/gmock/gmock-matchers.h b/include/gmock/gmock-matchers.h index 0497be27..ce7a2fe9 100644 --- a/include/gmock/gmock-matchers.h +++ b/include/gmock/gmock-matchers.h @@ -1709,60 +1709,164 @@ void ExplainMatchResultTo(const ResultOfMatcher<Callable>& matcher, template <typename Container> class ContainerEqMatcher { public: - explicit ContainerEqMatcher(const Container& rhs) : rhs_(rhs) {} - bool Matches(const Container& lhs) const { return lhs == rhs_; } + typedef internal::StlContainerView<Container> View; + typedef typename View::type StlContainer; + typedef typename View::const_reference StlContainerReference; + + // We make a copy of rhs in case the elements in it are modified + // after this matcher is created. + explicit ContainerEqMatcher(const Container& rhs) : rhs_(View::Copy(rhs)) { + // Makes sure the user doesn't instantiate this class template + // with a const or reference type. + testing::StaticAssertTypeEq<Container, + GMOCK_REMOVE_CONST_(GMOCK_REMOVE_REFERENCE_(Container))>(); + } + + template <typename LhsContainer> + bool Matches(const LhsContainer& lhs) const { + // GMOCK_REMOVE_CONST_() is needed to work around an MSVC 8.0 bug + // that causes LhsContainer to be a const type sometimes. + typedef internal::StlContainerView<GMOCK_REMOVE_CONST_(LhsContainer)> + LhsView; + StlContainerReference lhs_stl_container = LhsView::ConstReference(lhs); + return lhs_stl_container == rhs_; + } void DescribeTo(::std::ostream* os) const { *os << "equals "; - UniversalPrinter<Container>::Print(rhs_, os); + UniversalPrinter<StlContainer>::Print(rhs_, os); } void DescribeNegationTo(::std::ostream* os) const { *os << "does not equal "; - UniversalPrinter<Container>::Print(rhs_, os); + UniversalPrinter<StlContainer>::Print(rhs_, os); } - void ExplainMatchResultTo(const Container& lhs, + template <typename LhsContainer> + void ExplainMatchResultTo(const LhsContainer& lhs, ::std::ostream* os) const { + // GMOCK_REMOVE_CONST_() is needed to work around an MSVC 8.0 bug + // that causes LhsContainer to be a const type sometimes. + typedef internal::StlContainerView<GMOCK_REMOVE_CONST_(LhsContainer)> + LhsView; + typedef typename LhsView::type LhsStlContainer; + StlContainerReference lhs_stl_container = LhsView::ConstReference(lhs); + // 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()) { + for (typename LhsStlContainer::const_iterator it = + lhs_stl_container.begin(); + it != lhs_stl_container.end(); ++it) { + if (internal::ArrayAwareFind(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); + UniversalPrinter<typename LhsStlContainer::value_type>::Print(*it, os); } } // Now check for extra values. bool printed_header2 = false; - for (typename Container::const_iterator it = rhs_.begin(); + for (typename StlContainer::const_iterator it = rhs_.begin(); it != rhs_.end(); ++it) { - if (std::find(lhs.begin(), lhs.end(), *it) == lhs.end()) { + if (internal::ArrayAwareFind( + lhs_stl_container.begin(), lhs_stl_container.end(), *it) == + lhs_stl_container.end()) { if (printed_header2) { *os << ", "; } else { *os << (printed_header ? "; not" : "Not") << " in actual: "; printed_header2 = true; } - UniversalPrinter<typename Container::value_type>::Print(*it, os); + UniversalPrinter<typename StlContainer::value_type>::Print(*it, os); } } } private: - const Container rhs_; + const StlContainer rhs_; }; -template <typename Container> +template <typename LhsContainer, typename Container> void ExplainMatchResultTo(const ContainerEqMatcher<Container>& matcher, - const Container& lhs, + const LhsContainer& lhs, ::std::ostream* os) { matcher.ExplainMatchResultTo(lhs, os); } +// Implements Contains(element_matcher) for the given argument type Container. +template <typename Container> +class ContainsMatcherImpl : public MatcherInterface<Container> { + public: + typedef GMOCK_REMOVE_CONST_(GMOCK_REMOVE_REFERENCE_(Container)) RawContainer; + typedef StlContainerView<RawContainer> View; + typedef typename View::type StlContainer; + typedef typename View::const_reference StlContainerReference; + typedef typename StlContainer::value_type Element; + + template <typename InnerMatcher> + explicit ContainsMatcherImpl(InnerMatcher inner_matcher) + : inner_matcher_( + testing::SafeMatcherCast<const Element&>(inner_matcher)) {} + + // Returns true iff 'container' matches. + virtual bool Matches(Container container) const { + StlContainerReference stl_container = View::ConstReference(container); + for (typename StlContainer::const_iterator it = stl_container.begin(); + it != stl_container.end(); ++it) { + if (inner_matcher_.Matches(*it)) + return true; + } + return false; + } + + // Describes what this matcher does. + virtual void DescribeTo(::std::ostream* os) const { + *os << "contains at least one element that "; + inner_matcher_.DescribeTo(os); + } + + // Describes what the negation of this matcher does. + virtual void DescribeNegationTo(::std::ostream* os) const { + *os << "doesn't contain any element that "; + inner_matcher_.DescribeTo(os); + } + + // Explains why 'container' matches, or doesn't match, this matcher. + virtual void ExplainMatchResultTo(Container container, + ::std::ostream* os) const { + StlContainerReference stl_container = View::ConstReference(container); + + // We need to explain which (if any) element matches inner_matcher_. + typename StlContainer::const_iterator it = stl_container.begin(); + for (size_t i = 0; it != stl_container.end(); ++it, ++i) { + if (inner_matcher_.Matches(*it)) { + *os << "element " << i << " matches"; + return; + } + } + } + + private: + const Matcher<const Element&> inner_matcher_; +}; + +// Implements polymorphic Contains(element_matcher). +template <typename M> +class ContainsMatcher { + public: + explicit ContainsMatcher(M m) : inner_matcher_(m) {} + + template <typename Container> + operator Matcher<Container>() const { + return MakeMatcher(new ContainsMatcherImpl<Container>(inner_matcher_)); + } + + private: + const M inner_matcher_; +}; + } // namespace internal // Implements MatcherCast(). @@ -2206,9 +2310,35 @@ Truly(Predicate pred) { // 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> > +inline PolymorphicMatcher<internal::ContainerEqMatcher< + GMOCK_REMOVE_CONST_(Container)> > ContainerEq(const Container& rhs) { - return MakePolymorphicMatcher(internal::ContainerEqMatcher<Container>(rhs)); + // This following line is for working around a bug in MSVC 8.0, + // which causes Container to be a const type sometimes. + typedef GMOCK_REMOVE_CONST_(Container) RawContainer; + return MakePolymorphicMatcher(internal::ContainerEqMatcher<RawContainer>(rhs)); +} + +// Matches an STL-style container or a native array that contains at +// least one element matching the given value or matcher. +// +// Examples: +// ::std::set<int> page_ids; +// page_ids.insert(3); +// page_ids.insert(1); +// EXPECT_THAT(page_ids, Contains(1)); +// EXPECT_THAT(page_ids, Contains(Gt(2))); +// EXPECT_THAT(page_ids, Not(Contains(4))); +// +// ::std::map<int, size_t> page_lengths; +// page_lengths[1] = 100; +// EXPECT_THAT(map_int, Contains(::std::pair<const int, size_t>(1, 100))); +// +// const char* user_ids[] = { "joe", "mike", "tom" }; +// EXPECT_THAT(user_ids, Contains(Eq(::std::string("tom")))); +template <typename M> +inline internal::ContainsMatcher<M> Contains(M matcher) { + return internal::ContainsMatcher<M>(matcher); } // Returns a predicate that is satisfied by anything that matches the @@ -2218,6 +2348,12 @@ inline internal::MatcherAsPredicate<M> Matches(M matcher) { return internal::MatcherAsPredicate<M>(matcher); } +// Returns true iff the value matches the matcher. +template <typename T, typename M> +inline bool Value(const T& value, M matcher) { + return testing::Matches(matcher)(value); +} + // These macros allow using matchers to check values in Google Test // tests. ASSERT_THAT(value, matcher) and EXPECT_THAT(value, matcher) // succeed iff the value matches the matcher. If the assertion fails, diff --git a/include/gmock/gmock-printers.h b/include/gmock/gmock-printers.h index e233ef3e..561de3d9 100644 --- a/include/gmock/gmock-printers.h +++ b/include/gmock/gmock-printers.h @@ -66,10 +66,28 @@ // // printed. // void ::testing::internal::UniversalTersePrint(const T& value, ostream*); // +// // Prints value using the type inferred by the compiler. The difference +// // from UniversalTersePrint() is that this function prints both the +// // pointer and the NUL-terminated string for a (const) char pointer. +// void ::testing::internal::UniversalPrint(const T& value, ostream*); +// // // Prints the fields of a tuple tersely to a string vector, one // // element for each field. // std::vector<string> UniversalTersePrintTupleFieldsToStrings( // const Tuple& value); +// +// Known limitation: +// +// The print primitives print the elements of an STL-style container +// using the compiler-inferred type of *iter where iter is a +// const_iterator of the container. When const_iterator is an input +// iterator but not a forward iterator, this inferred type may not +// match value_type, and the print output may be incorrect. In +// practice, this is rarely a problem as for most containers +// const_iterator is a forward iterator. We'll fix this if there's an +// actual need for it. Note that this fix cannot rely on value_type +// being defined as many user-defined container types don't have +// value_type. #ifndef GMOCK_INCLUDE_GMOCK_GMOCK_PRINTERS_H_ #define GMOCK_INCLUDE_GMOCK_GMOCK_PRINTERS_H_ @@ -208,6 +226,9 @@ namespace internal { template <typename T> class UniversalPrinter; +template <typename T> +void UniversalPrint(const T& value, ::std::ostream* os); + // Used to print an STL-style container when the user doesn't define // a PrintTo() for it. template <typename C> @@ -227,7 +248,9 @@ void DefaultPrintTo(IsContainer /* dummy */, } } *os << ' '; - PrintTo(*it, os); + // We cannot call PrintTo(*it, os) here as PrintTo() doesn't + // handle *it being a native array. + internal::UniversalPrint(*it, os); } if (count > 0) { @@ -683,6 +706,15 @@ inline void UniversalTersePrint(char* str, ::std::ostream* os) { UniversalTersePrint(static_cast<const char*>(str), os); } +// Prints a value using the type inferred by the compiler. The +// difference between this and UniversalTersePrint() is that for a +// (const) char pointer, this prints both the pointer and the +// NUL-terminated string. +template <typename T> +void UniversalPrint(const T& value, ::std::ostream* os) { + UniversalPrinter<T>::Print(value, os); +} + // Prints the fields of a tuple tersely to a string vector, one // element for each field. See the comment before // UniversalTersePrint() for how we define "tersely". diff --git a/include/gmock/internal/gmock-internal-utils.h b/include/gmock/internal/gmock-internal-utils.h index b5e38db3..ee6aa1e2 100644 --- a/include/gmock/internal/gmock-internal-utils.h +++ b/include/gmock/internal/gmock-internal-utils.h @@ -99,6 +99,17 @@ struct RemoveConst { typedef T type; }; // NOLINT template <typename T> struct RemoveConst<const T> { typedef T type; }; // NOLINT +// MSVC 8.0 has a bug which causes the above definition to fail to +// remove the const in 'const int[3]'. The following specialization +// works around the bug. However, it causes trouble with gcc and thus +// needs to be conditionally compiled. +#ifdef _MSC_VER +template <typename T, size_t N> +struct RemoveConst<T[N]> { + typedef typename RemoveConst<T>::type type[N]; +}; +#endif // _MSC_VER + // A handy wrapper around RemoveConst that works when the argument // T depends on template parameters. #define GMOCK_REMOVE_CONST_(T) \ @@ -451,10 +462,6 @@ bool LogIsVisible(LogSeverity severity); // conservative. void Log(LogSeverity severity, const string& message, int stack_frames_to_skip); -// The universal value printer (public/gmock-printers.h) needs this -// to declare an unused << operator in the global namespace. -struct Unused {}; - // TODO(wan@google.com): group all type utilities together. // Type traits. @@ -482,6 +489,238 @@ inline T Invalid() { template <> inline void Invalid<void>() {} +// Utilities for native arrays. + +// ArrayEq() compares two k-dimensional native arrays using the +// elements' operator==, where k can be any integer >= 0. When k is +// 0, ArrayEq() degenerates into comparing a single pair of values. + +template <typename T, typename U> +bool ArrayEq(const T* lhs, size_t size, const U* rhs); + +// This generic version is used when k is 0. +template <typename T, typename U> +inline bool ArrayEq(const T& lhs, const U& rhs) { return lhs == rhs; } + +// This overload is used when k >= 1. +template <typename T, typename U, size_t N> +inline bool ArrayEq(const T(&lhs)[N], const U(&rhs)[N]) { + return internal::ArrayEq(lhs, N, rhs); +} + +// This helper reduces code bloat. If we instead put its logic inside +// the previous ArrayEq() function, arrays with different sizes would +// lead to different copies of the template code. +template <typename T, typename U> +bool ArrayEq(const T* lhs, size_t size, const U* rhs) { + for (size_t i = 0; i != size; i++) { + if (!internal::ArrayEq(lhs[i], rhs[i])) + return false; + } + return true; +} + +// Finds the first element in the iterator range [begin, end) that +// equals elem. Element may be a native array type itself. +template <typename Iter, typename Element> +Iter ArrayAwareFind(Iter begin, Iter end, const Element& elem) { + for (Iter it = begin; it != end; ++it) { + if (internal::ArrayEq(*it, elem)) + return it; + } + return end; +} + +// CopyArray() copies a k-dimensional native array using the elements' +// operator=, where k can be any integer >= 0. When k is 0, +// CopyArray() degenerates into copying a single value. + +template <typename T, typename U> +void CopyArray(const T* from, size_t size, U* to); + +// This generic version is used when k is 0. +template <typename T, typename U> +inline void CopyArray(const T& from, U* to) { *to = from; } + +// This overload is used when k >= 1. +template <typename T, typename U, size_t N> +inline void CopyArray(const T(&from)[N], U(*to)[N]) { + internal::CopyArray(from, N, *to); +} + +// This helper reduces code bloat. If we instead put its logic inside +// the previous CopyArray() function, arrays with different sizes +// would lead to different copies of the template code. +template <typename T, typename U> +void CopyArray(const T* from, size_t size, U* to) { + for (size_t i = 0; i != size; i++) { + internal::CopyArray(from[i], to + i); + } +} + +// The relation between an NativeArray object (see below) and the +// native array it represents. +enum RelationToSource { + kReference, // The NativeArray references the native array. + kCopy // The NativeArray makes a copy of the native array and + // owns the copy. +}; + +// Adapts a native array to a read-only STL-style container. Instead +// of the complete STL container concept, this adaptor only implements +// members useful for Google Mock's container matchers. New members +// should be added as needed. To simplify the implementation, we only +// support Element being a raw type (i.e. having no top-level const or +// reference modifier). It's the client's responsibility to satisfy +// this requirement. Element can be an array type itself (hence +// multi-dimensional arrays are supported). +template <typename Element> +class NativeArray { + public: + // STL-style container typedefs. + typedef Element value_type; + typedef const Element* const_iterator; + + // Constructs from a native array passed by reference. + template <size_t N> + NativeArray(const Element (&array)[N], RelationToSource relation) { + Init(array, N, relation); + } + + // Constructs from a native array passed by a pointer and a size. + // For generality we don't artificially restrict the types of the + // pointer and the size. + template <typename Pointer, typename Size> + NativeArray(const ::std::tr1::tuple<Pointer, Size>& array, + RelationToSource relation) { + Init(internal::GetRawPointer(::std::tr1::get<0>(array)), + ::std::tr1::get<1>(array), + relation); + } + + // Copy constructor. + NativeArray(const NativeArray& rhs) { + Init(rhs.array_, rhs.size_, rhs.relation_to_source_); + } + + ~NativeArray() { + // Ensures that the user doesn't instantiate NativeArray with a + // const or reference type. + testing::StaticAssertTypeEq<Element, + GMOCK_REMOVE_CONST_(GMOCK_REMOVE_REFERENCE_(Element))>(); + if (relation_to_source_ == kCopy) + delete[] array_; + } + + // STL-style container methods. + size_t size() const { return size_; } + const_iterator begin() const { return array_; } + const_iterator end() const { return array_ + size_; } + bool operator==(const NativeArray& rhs) const { + return size() == rhs.size() && + ArrayEq(begin(), size(), rhs.begin()); + } + + private: + // Not implemented as we don't want to support assignment. + void operator=(const NativeArray& rhs); + + // Initializes this object; makes a copy of the input array if + // 'relation' is kCopy. + void Init(const Element* array, size_t size, RelationToSource relation) { + if (relation == kReference) { + array_ = array; + } else { + Element* const copy = new Element[size]; + CopyArray(array, size, copy); + array_ = copy; + } + size_ = size; + relation_to_source_ = relation; + } + + const Element* array_; + size_t size_; + RelationToSource relation_to_source_; +}; + +// Given a raw type (i.e. having no top-level reference or const +// modifier) RawContainer that's either an STL-style container or a +// native array, class StlContainerView<RawContainer> has the +// following members: +// +// - type is a type that provides an STL-style container view to +// (i.e. implements the STL container concept for) RawContainer; +// - const_reference is a type that provides a reference to a const +// RawContainer; +// - ConstReference(raw_container) returns a const reference to an STL-style +// container view to raw_container, which is a RawContainer. +// - Copy(raw_container) returns an STL-style container view of a +// copy of raw_container, which is a RawContainer. +// +// This generic version is used when RawContainer itself is already an +// STL-style container. +template <class RawContainer> +class StlContainerView { + public: + typedef RawContainer type; + typedef const type& const_reference; + + static const_reference ConstReference(const RawContainer& container) { + // Ensures that RawContainer is not a const type. + testing::StaticAssertTypeEq<RawContainer, + GMOCK_REMOVE_CONST_(RawContainer)>(); + return container; + } + static type Copy(const RawContainer& container) { return container; } +}; + +// This specialization is used when RawContainer is a native array type. +template <typename Element, size_t N> +class StlContainerView<Element[N]> { + public: + typedef GMOCK_REMOVE_CONST_(Element) RawElement; + typedef internal::NativeArray<RawElement> type; + // NativeArray<T> can represent a native array either by value or by + // reference (selected by a constructor argument), so 'const type' + // can be used to reference a const native array. We cannot + // 'typedef const type& const_reference' here, as that would mean + // ConstReference() has to return a reference to a local variable. + typedef const type const_reference; + + static const_reference ConstReference(const Element (&array)[N]) { + // Ensures that Element is not a const type. + testing::StaticAssertTypeEq<Element, RawElement>(); + return type(array, kReference); + } + static type Copy(const Element (&array)[N]) { + return type(array, kCopy); + } +}; + +// This specialization is used when RawContainer is a native array +// represented as a (pointer, size) tuple. +template <typename ElementPointer, typename Size> +class StlContainerView< ::std::tr1::tuple<ElementPointer, Size> > { + public: + typedef GMOCK_REMOVE_CONST_( + typename internal::PointeeOf<ElementPointer>::type) RawElement; + typedef internal::NativeArray<RawElement> type; + typedef const type const_reference; + + static const_reference ConstReference( + const ::std::tr1::tuple<ElementPointer, Size>& array) { + return type(array, kReference); + } + static type Copy(const ::std::tr1::tuple<ElementPointer, Size>& array) { + return type(array, kCopy); + } +}; + +// The following specialization prevents the user from instantiating +// StlContainer with a reference type. +template <typename T> class StlContainerView<T&>; + } // namespace internal } // namespace testing diff --git a/scripts/generator/README b/scripts/generator/README index 071bf0fb..d6f95974 100644 --- a/scripts/generator/README +++ b/scripts/generator/README @@ -23,7 +23,7 @@ the environment. For example to use an indent of 4 spaces: INDENT=4 gmock_gen.py header-file.h ClassName -This version was made from SVN revision 279 in the cppclean repository. +This version was made from SVN revision 281 in the cppclean repository. Known Limitations ----------------- diff --git a/scripts/generator/cpp/gmock_class_test.py b/scripts/generator/cpp/gmock_class_test.py index 0132eef4..ae00800f 100755 --- a/scripts/generator/cpp/gmock_class_test.py +++ b/scripts/generator/cpp/gmock_class_test.py @@ -25,7 +25,7 @@ import sys import unittest # Allow the cpp imports below to work when run as a standalone script. -sys.path.append(os.path.dirname(os.path.dirname(__file__))) +sys.path.append(os.path.join(os.path.dirname(__file__), '..')) from cpp import ast from cpp import gmock_class diff --git a/scripts/gmock_doctor.py b/scripts/gmock_doctor.py index 1980977c..05e42585 100755 --- a/scripts/gmock_doctor.py +++ b/scripts/gmock_doctor.py @@ -36,7 +36,7 @@ __author__ = 'wan@google.com (Zhanyong Wan)' import re import sys -_VERSION = '1.0.1' +_VERSION = '1.0.3' _COMMON_GMOCK_SYMBOLS = [ # Matchers @@ -63,6 +63,7 @@ _COMMON_GMOCK_SYMBOLS = [ 'Le', 'Lt', 'MatcherCast', + 'Matches', 'MatchesRegex', 'NanSensitiveDoubleEq', 'NanSensitiveFloatEq', @@ -82,6 +83,7 @@ _COMMON_GMOCK_SYMBOLS = [ 'StrNe', 'Truly', 'TypedEq', + 'Value', # Actions 'Assign', @@ -171,7 +173,7 @@ def _NeedToReturnSomethingDiagnoser(msg): regex = (_FILE_LINE_RE + r'(instantiated from here\n.' - r'*gmock-actions\.h.*error: void value not ignored)' + r'*gmock.*actions\.h.*error: void value not ignored)' r'|(error: control reaches end of non-void function)') diagnosis = """ You are using an action that returns void, but it needs to return @@ -346,6 +348,60 @@ Note: the line number may be off; please fix all instances of Return(NULL).""" regex, diagnosis, msg) +_TTB_DIAGNOSIS = """ +In a mock class template, types or typedefs defined in the base class +template are *not* automatically visible. This is how C++ works. Before +you can use a type or typedef named %(type)s defined in base class Base<T>, you +need to make it visible. One way to do it is: + + typedef typename Base<T>::%(type)s %(type)s;""" + + +def _TypeInTemplatedBaseDiagnoser1(msg): + """Diagnoses the TTB disease, given the error messages by gcc. + + This version works when the type is used as the mock function's return + type. + """ + + regex = (r'In member function \'int .*\n' + _FILE_LINE_RE + + r'error: a function call cannot appear in a constant-expression') + diagnosis = _TTB_DIAGNOSIS % {'type': 'Foo'} + return _GenericDiagnoser('TTB', 'Type in Template Base', + regex, diagnosis, msg) + + +def _TypeInTemplatedBaseDiagnoser2(msg): + """Diagnoses the TTB disease, given the error messages by gcc. + + This version works when the type is used as the mock function's sole + parameter type. + """ + + regex = (r'In member function \'int .*\n' + + _FILE_LINE_RE + + r'error: \'(?P<type>.+)\' was not declared in this scope\n' + r'.*error: template argument 1 is invalid\n') + return _GenericDiagnoser('TTB', 'Type in Template Base', + regex, _TTB_DIAGNOSIS, msg) + + +def _TypeInTemplatedBaseDiagnoser3(msg): + """Diagnoses the TTB disease, given the error messages by gcc. + + This version works when the type is used as a parameter of a mock + function that has multiple parameters. + """ + + regex = (r'error: expected `;\' before \'::\' token\n' + + _FILE_LINE_RE + + r'error: \'(?P<type>.+)\' was not declared in this scope\n' + r'.*error: template argument 1 is invalid\n' + r'.*error: \'.+\' was not declared in this scope') + return _GenericDiagnoser('TTB', 'Type in Template Base', + regex, _TTB_DIAGNOSIS, msg) + + def _WrongMockMethodMacroDiagnoser(msg): """Diagnoses the WMM disease, given the error messages by gcc.""" @@ -357,7 +413,7 @@ def _WrongMockMethodMacroDiagnoser(msg): You are using MOCK_METHOD%(wrong_args)s to define a mock method that has %(args)s arguments. Use MOCK_METHOD%(args)s (or MOCK_CONST_METHOD%(args)s, MOCK_METHOD%(args)s_T, MOCK_CONST_METHOD%(args)s_T as appropriate) instead.""" - return _GenericDiagnoser('WMM', 'Wrong MOCK_METHODn macro', + return _GenericDiagnoser('WMM', 'Wrong MOCK_METHODn Macro', regex, diagnosis, msg) @@ -373,7 +429,7 @@ The closing parenthesis of ON_CALL or EXPECT_CALL should be *before* EXPECT_CALL(my_mock, Foo(_)).%(method)s(...); instead of: EXPECT_CALL(my_mock, Foo(_).%(method)s(...));""" - return _GenericDiagnoser('WPP', 'Wrong parenthesis position', + return _GenericDiagnoser('WPP', 'Wrong Parenthesis Position', regex, diagnosis, msg) @@ -389,6 +445,9 @@ _DIAGNOSERS = [ _OverloadedFunctionMatcherDiagnoser, _OverloadedMethodActionDiagnoser1, _OverloadedMethodActionDiagnoser2, + _TypeInTemplatedBaseDiagnoser1, + _TypeInTemplatedBaseDiagnoser2, + _TypeInTemplatedBaseDiagnoser3, _WrongMockMethodMacroDiagnoser, _WrongParenPositionDiagnoser, ] diff --git a/test/gmock-generated-matchers_test.cc b/test/gmock-generated-matchers_test.cc index 669652b9..26814463 100644 --- a/test/gmock-generated-matchers_test.cc +++ b/test/gmock-generated-matchers_test.cc @@ -53,6 +53,7 @@ using std::pair; using std::set; using std::stringstream; using std::vector; +using std::tr1::make_tuple; using testing::_; using testing::Contains; using testing::ElementsAre; @@ -60,6 +61,7 @@ using testing::ElementsAreArray; using testing::Eq; using testing::Ge; using testing::Gt; +using testing::Lt; using testing::MakeMatcher; using testing::Matcher; using testing::MatcherInterface; @@ -69,6 +71,7 @@ using testing::Pointee; using testing::Ref; using testing::StaticAssertTypeEq; using testing::StrEq; +using testing::Value; using testing::internal::string; // Returns the description of the given matcher. @@ -330,6 +333,39 @@ TEST(ElementsAreTest, WorksWithContainerPointerUsingPointee) { EXPECT_THAT(&v, Not(Pointee(ElementsAre(0, _, 3)))); } +TEST(ElementsAreTest, WorksWithNativeArrayPassedByReference) { + int array[] = { 0, 1, 2 }; + EXPECT_THAT(array, ElementsAre(0, 1, _)); + EXPECT_THAT(array, Not(ElementsAre(1, _, _))); + EXPECT_THAT(array, Not(ElementsAre(0, _))); +} + +class NativeArrayPassedAsPointerAndSize { + public: + MOCK_METHOD2(Helper, void(int* array, int size)); +}; + +TEST(ElementsAreTest, WorksWithNativeArrayPassedAsPointerAndSize) { + int array[] = { 0, 1 }; + ::std::tr1::tuple<int*, size_t> array_as_tuple(array, 2); + EXPECT_THAT(array_as_tuple, ElementsAre(0, 1)); + EXPECT_THAT(array_as_tuple, Not(ElementsAre(0))); + + NativeArrayPassedAsPointerAndSize helper; + EXPECT_CALL(helper, Helper(_, _)) + .WithArguments(ElementsAre(0, 1)); + helper.Helper(array, 2); +} + +TEST(ElementsAreTest, WorksWithTwoDimensionalNativeArray) { + const char a2[][3] = { "hi", "lo" }; + EXPECT_THAT(a2, ElementsAre(ElementsAre('h', 'i', '\0'), + ElementsAre('l', 'o', '\0'))); + EXPECT_THAT(a2, ElementsAre(StrEq("hi"), StrEq("lo"))); + EXPECT_THAT(a2, ElementsAre(Not(ElementsAre('h', 'o', '\0')), + ElementsAre('l', 'o', '\0'))); +} + // Tests for ElementsAreArray(). Since ElementsAreArray() shares most // of the implementation with ElementsAre(), we don't test it as // thoroughly here. @@ -379,6 +415,17 @@ TEST(ElementsAreArrayTest, CanBeCreatedWithMatcherArray) { EXPECT_THAT(test_vector, Not(ElementsAreArray(kMatcherArray))); } +// Since ElementsAre() and ElementsAreArray() share much of the +// implementation, we only do a sanity test for native arrays here. +TEST(ElementsAreArrayTest, WorksWithNativeArray) { + ::std::string a[] = { "hi", "ho" }; + ::std::string b[] = { "hi", "ho" }; + + EXPECT_THAT(a, ElementsAreArray(b)); + EXPECT_THAT(a, ElementsAreArray(b, 2)); + EXPECT_THAT(a, Not(ElementsAreArray(b, 1))); +} + // Tests for the MATCHER*() macro family. // Tests that a simple MATCHER() definition works. @@ -443,12 +490,23 @@ namespace matcher_test { MATCHER(IsOdd, "") { return (arg % 2) != 0; } } // namespace matcher_test -TEST(MatcherTest, WorksInNamespace) { +TEST(MatcherMacroTest, WorksInNamespace) { Matcher<int> m = matcher_test::IsOdd(); EXPECT_FALSE(m.Matches(4)); EXPECT_TRUE(m.Matches(5)); } +// Tests that Value() can be used to compose matchers. +MATCHER(IsPositiveOdd, "") { + return Value(arg, matcher_test::IsOdd()) && arg > 0; +} + +TEST(MatcherMacroTest, CanBeComposedUsingValue) { + EXPECT_THAT(3, IsPositiveOdd()); + EXPECT_THAT(4, Not(IsPositiveOdd())); + EXPECT_THAT(-1, Not(IsPositiveOdd())); +} + // Tests that a simple MATCHER_P() definition works. MATCHER_P(IsGreaterThan32And, n, "") { return arg > 32 && arg > n; } @@ -742,14 +800,31 @@ TEST(MatcherPnMacroTest, TypesAreCorrect) { EqualsSumOf(1, 2, 3, 4, 5, 6, 7, 8, 9, '0'); } +// Tests that matcher-typed parameters can be used in Value() inside a +// MATCHER_Pn definition. + +// Succeeds if arg matches exactly 2 of the 3 matchers. +MATCHER_P3(TwoOf, m1, m2, m3, "") { + const int count = static_cast<int>(Value(arg, m1)) + + static_cast<int>(Value(arg, m2)) + static_cast<int>(Value(arg, m3)); + return count == 2; +} + +TEST(MatcherPnMacroTest, CanUseMatcherTypedParameterInValue) { + EXPECT_THAT(42, TwoOf(Gt(0), Lt(50), Eq(10))); + EXPECT_THAT(0, Not(TwoOf(Gt(-1), Lt(1), Eq(0)))); +} + +// Tests Contains(). + TEST(ContainsTest, ListMatchesWhenElementIsInContainer) { list<int> some_list; some_list.push_back(3); some_list.push_back(1); some_list.push_back(2); EXPECT_THAT(some_list, Contains(1)); - EXPECT_THAT(some_list, Contains(3.0)); - EXPECT_THAT(some_list, Contains(2.0f)); + EXPECT_THAT(some_list, Contains(Gt(2.5))); + EXPECT_THAT(some_list, Contains(Eq(2.0f))); list<string> another_list; another_list.push_back("fee"); @@ -771,8 +846,8 @@ TEST(ContainsTest, SetMatchesWhenElementIsInContainer) { some_set.insert(3); some_set.insert(1); some_set.insert(2); - EXPECT_THAT(some_set, Contains(1.0)); - EXPECT_THAT(some_set, Contains(3.0f)); + EXPECT_THAT(some_set, Contains(Eq(1.0))); + EXPECT_THAT(some_set, Contains(Eq(3.0f))); EXPECT_THAT(some_set, Contains(2)); set<const char*> another_set; @@ -780,7 +855,7 @@ TEST(ContainsTest, SetMatchesWhenElementIsInContainer) { another_set.insert("fie"); another_set.insert("foe"); another_set.insert("fum"); - EXPECT_THAT(another_set, Contains(string("fum"))); + EXPECT_THAT(another_set, Contains(Eq(string("fum")))); } TEST(ContainsTest, SetDoesNotMatchWhenElementIsNotInContainer) { @@ -795,8 +870,20 @@ TEST(ContainsTest, SetDoesNotMatchWhenElementIsNotInContainer) { } TEST(ContainsTest, DescribesItselfCorrectly) { + const int a[2] = { 1, 2 }; + Matcher<const int(&)[2]> m = Contains(2); + EXPECT_EQ("element 1 matches", Explain(m, a)); + + m = Contains(3); + EXPECT_EQ("", Explain(m, a)); +} + +TEST(ContainsTest, ExplainsMatchResultCorrectly) { Matcher<vector<int> > m = Contains(1); - EXPECT_EQ("contains 1", Describe(m)); + EXPECT_EQ("contains at least one element that is equal to 1", Describe(m)); + + Matcher<vector<int> > m2 = Not(m); + EXPECT_EQ("doesn't contain any element that is equal to 1", Describe(m2)); } TEST(ContainsTest, MapMatchesWhenElementIsInContainer) { @@ -823,7 +910,7 @@ TEST(ContainsTest, MapDoesNotMatchWhenElementIsNotInContainer) { TEST(ContainsTest, ArrayMatchesWhenElementIsInContainer) { const char* string_array[] = { "fee", "fie", "foe", "fum" }; - EXPECT_THAT(string_array, Contains(string("fum"))); + EXPECT_THAT(string_array, Contains(Eq(string("fum")))); } TEST(ContainsTest, ArrayDoesNotMatchWhenElementIsNotInContainer) { @@ -831,4 +918,24 @@ TEST(ContainsTest, ArrayDoesNotMatchWhenElementIsNotInContainer) { EXPECT_THAT(int_array, Not(Contains(5))); } +TEST(ContainsTest, AcceptsMatcher) { + const int a[] = { 1, 2, 3 }; + EXPECT_THAT(a, Contains(Gt(2))); + EXPECT_THAT(a, Not(Contains(Gt(4)))); +} + +TEST(ContainsTest, WorksForNativeArrayAsTuple) { + const int a[] = { 1, 2 }; + EXPECT_THAT(make_tuple(a, 2), Contains(1)); + EXPECT_THAT(make_tuple(a, 2), Not(Contains(Gt(3)))); +} + +TEST(ContainsTest, WorksForTwoDimensionalNativeArray) { + int a[][3] = { { 1, 2, 3 }, { 4, 5, 6 } }; + EXPECT_THAT(a, Contains(ElementsAre(4, 5, 6))); + EXPECT_THAT(a, Contains(Contains(5))); + EXPECT_THAT(a, Not(Contains(ElementsAre(3, 4, 5)))); + EXPECT_THAT(a, Contains(Not(Contains(5)))); +} + } // namespace diff --git a/test/gmock-internal-utils_test.cc b/test/gmock-internal-utils_test.cc index 7886f6d3..73960df5 100644 --- a/test/gmock-internal-utils_test.cc +++ b/test/gmock-internal-utils_test.cc @@ -53,6 +53,7 @@ namespace internal { namespace { +using ::std::tr1::make_tuple; using ::std::tr1::tuple; TEST(ConvertIdentifierNameToWordsTest, WorksWhenNameContainsNoWord) { @@ -130,6 +131,8 @@ TEST(RemoveConstTest, DoesNotAffectNonConstType) { // Tests that RemoveConst removes const from const types. TEST(RemoveConstTest, RemovesConst) { CompileAssertTypesEqual<int, RemoveConst<const int>::type>(); + CompileAssertTypesEqual<char[2], RemoveConst<const char[2]>::type>(); + CompileAssertTypesEqual<char[2][3], RemoveConst<const char[2][3]>::type>(); } // Tests GMOCK_REMOVE_CONST_. @@ -721,6 +724,234 @@ TEST(OnCallTest, LogsAnythingArgument) { #endif // 0 +// Tests ArrayEq(). + +TEST(ArrayEqTest, WorksForDegeneratedArrays) { + EXPECT_TRUE(ArrayEq(5, 5L)); + EXPECT_FALSE(ArrayEq('a', 0)); +} + +TEST(ArrayEqTest, WorksForOneDimensionalArrays) { + const int a[] = { 0, 1 }; + long b[] = { 0, 1 }; + EXPECT_TRUE(ArrayEq(a, b)); + EXPECT_TRUE(ArrayEq(a, 2, b)); + + b[0] = 2; + EXPECT_FALSE(ArrayEq(a, b)); + EXPECT_FALSE(ArrayEq(a, 1, b)); +} + +TEST(ArrayEqTest, WorksForTwoDimensionalArrays) { + const char a[][3] = { "hi", "lo" }; + const char b[][3] = { "hi", "lo" }; + const char c[][3] = { "hi", "li" }; + + EXPECT_TRUE(ArrayEq(a, b)); + EXPECT_TRUE(ArrayEq(a, 2, b)); + + EXPECT_FALSE(ArrayEq(a, c)); + EXPECT_FALSE(ArrayEq(a, 2, c)); +} + +// Tests ArrayAwareFind(). + +TEST(ArrayAwareFindTest, WorksForOneDimensionalArray) { + const char a[] = "hello"; + EXPECT_EQ(a + 4, ArrayAwareFind(a, a + 5, 'o')); + EXPECT_EQ(a + 5, ArrayAwareFind(a, a + 5, 'x')); +} + +TEST(ArrayAwareFindTest, WorksForTwoDimensionalArray) { + int a[][2] = { { 0, 1 }, { 2, 3 }, { 4, 5 } }; + const int b[2] = { 2, 3 }; + EXPECT_EQ(a + 1, ArrayAwareFind(a, a + 3, b)); + + const int c[2] = { 6, 7 }; + EXPECT_EQ(a + 3, ArrayAwareFind(a, a + 3, c)); +} + +// Tests CopyArray(). + +TEST(CopyArrayTest, WorksForDegeneratedArrays) { + int n = 0; + CopyArray('a', &n); + EXPECT_EQ('a', n); +} + +TEST(CopyArrayTest, WorksForOneDimensionalArrays) { + const char a[3] = "hi"; + int b[3]; + CopyArray(a, &b); + EXPECT_TRUE(ArrayEq(a, b)); + + int c[3]; + CopyArray(a, 3, c); + EXPECT_TRUE(ArrayEq(a, c)); +} + +TEST(CopyArrayTest, WorksForTwoDimensionalArrays) { + const int a[2][3] = { { 0, 1, 2 }, { 3, 4, 5 } }; + int b[2][3]; + CopyArray(a, &b); + EXPECT_TRUE(ArrayEq(a, b)); + + int c[2][3]; + CopyArray(a, 2, c); + EXPECT_TRUE(ArrayEq(a, c)); +} + +// Tests NativeArray. + +TEST(NativeArrayTest, ConstructorFromArrayReferenceWorks) { + const int a[3] = { 0, 1, 2 }; + NativeArray<int> na(a, kReference); + EXPECT_EQ(3, na.size()); + EXPECT_EQ(a, na.begin()); +} + +TEST(NativeArrayTest, ConstructorFromTupleWorks) { + int a[3] = { 0, 1, 2 }; + // Tests with a plain pointer. + NativeArray<int> na(make_tuple(a, 3U), kReference); + EXPECT_EQ(a, na.begin()); + + const linked_ptr<char> b(new char); + *b = 'a'; + // Tests with a smart pointer. + NativeArray<char> nb(make_tuple(b, 1), kCopy); + EXPECT_NE(b.get(), nb.begin()); + EXPECT_EQ('a', nb.begin()[0]); +} + +TEST(NativeArrayTest, CreatesAndDeletesCopyOfArrayWhenAskedTo) { + typedef int Array[2]; + Array* a = new Array[1]; + (*a)[0] = 0; + (*a)[1] = 1; + NativeArray<int> na(*a, kCopy); + EXPECT_NE(*a, na.begin()); + delete[] a; + EXPECT_EQ(0, na.begin()[0]); + EXPECT_EQ(1, na.begin()[1]); + + // We rely on the heap checker to verify that na deletes the copy of + // array. +} + +TEST(NativeArrayTest, TypeMembersAreCorrect) { + StaticAssertTypeEq<char, NativeArray<char>::value_type>(); + StaticAssertTypeEq<int[2], NativeArray<int[2]>::value_type>(); + + StaticAssertTypeEq<const char*, NativeArray<char>::const_iterator>(); + StaticAssertTypeEq<const bool(*)[2], NativeArray<bool[2]>::const_iterator>(); +} + +TEST(NativeArrayTest, MethodsWork) { + const int a[] = { 0, 1, 2 }; + NativeArray<int> na(a, kCopy); + ASSERT_EQ(3, na.size()); + EXPECT_EQ(3, na.end() - na.begin()); + + NativeArray<int>::const_iterator it = na.begin(); + EXPECT_EQ(0, *it); + ++it; + EXPECT_EQ(1, *it); + it++; + EXPECT_EQ(2, *it); + ++it; + EXPECT_EQ(na.end(), it); + + EXPECT_THAT(na, Eq(na)); + + NativeArray<int> na2(a, kReference); + EXPECT_THAT(na, Eq(na2)); + + const int b1[] = { 0, 1, 1 }; + const int b2[] = { 0, 1, 2, 3 }; + EXPECT_THAT(na, Not(Eq(NativeArray<int>(b1, kReference)))); + EXPECT_THAT(na, Not(Eq(NativeArray<int>(b2, kCopy)))); +} + +TEST(NativeArrayTest, WorksForTwoDimensionalArray) { + const char a[2][3] = { "hi", "lo" }; + NativeArray<char[3]> na(a, kReference); + ASSERT_EQ(2, na.size()); + EXPECT_EQ(a, na.begin()); +} + +// Tests StlContainerView. + +TEST(StlContainerViewTest, WorksForStlContainer) { + StaticAssertTypeEq<std::vector<int>, + StlContainerView<std::vector<int> >::type>(); + StaticAssertTypeEq<const std::vector<double>&, + StlContainerView<std::vector<double> >::const_reference>(); + + typedef std::vector<char> Chars; + Chars v1; + const Chars& v2(StlContainerView<Chars>::ConstReference(v1)); + EXPECT_EQ(&v1, &v2); + + v1.push_back('a'); + Chars v3 = StlContainerView<Chars>::Copy(v1); + EXPECT_THAT(v3, Eq(v3)); +} + +TEST(StlContainerViewTest, WorksForStaticNativeArray) { + StaticAssertTypeEq<NativeArray<int>, + StlContainerView<int[3]>::type>(); + StaticAssertTypeEq<NativeArray<double>, + StlContainerView<const double[4]>::type>(); + StaticAssertTypeEq<NativeArray<char[3]>, + StlContainerView<const char[2][3]>::type>(); + + StaticAssertTypeEq<const NativeArray<int>, + StlContainerView<int[2]>::const_reference>(); + + int a1[3] = { 0, 1, 2 }; + NativeArray<int> a2 = StlContainerView<int[3]>::ConstReference(a1); + EXPECT_EQ(3, a2.size()); + EXPECT_EQ(a1, a2.begin()); + + const NativeArray<int> a3 = StlContainerView<int[3]>::Copy(a1); + ASSERT_EQ(3, a3.size()); + EXPECT_EQ(0, a3.begin()[0]); + EXPECT_EQ(1, a3.begin()[1]); + EXPECT_EQ(2, a3.begin()[2]); + + // Makes sure a1 and a3 aren't aliases. + a1[0] = 3; + EXPECT_EQ(0, a3.begin()[0]); +} + +TEST(StlContainerViewTest, WorksForDynamicNativeArray) { + StaticAssertTypeEq<NativeArray<int>, + StlContainerView<tuple<const int*, size_t> >::type>(); + StaticAssertTypeEq<NativeArray<double>, + StlContainerView<tuple<linked_ptr<double>, int> >::type>(); + + StaticAssertTypeEq<const NativeArray<int>, + StlContainerView<tuple<const int*, int> >::const_reference>(); + + int a1[3] = { 0, 1, 2 }; + NativeArray<int> a2 = StlContainerView<tuple<const int*, int> >:: + ConstReference(make_tuple(a1, 3)); + EXPECT_EQ(3, a2.size()); + EXPECT_EQ(a1, a2.begin()); + + const NativeArray<int> a3 = StlContainerView<tuple<int*, size_t> >:: + Copy(make_tuple(a1, 3)); + ASSERT_EQ(3, a3.size()); + EXPECT_EQ(0, a3.begin()[0]); + EXPECT_EQ(1, a3.begin()[1]); + EXPECT_EQ(2, a3.begin()[2]); + + // Makes sure a1 and a3 aren't aliases. + a1[0] = 3; + EXPECT_EQ(0, a3.begin()[0]); +} + } // namespace } // namespace internal } // namespace testing diff --git a/test/gmock-matchers_test.cc b/test/gmock-matchers_test.cc index e7709018..1226a1d1 100644 --- a/test/gmock-matchers_test.cc +++ b/test/gmock-matchers_test.cc @@ -60,6 +60,7 @@ bool SkipPrefix(const char* prefix, const char** pstr); namespace gmock_matchers_test { using std::stringstream; +using std::tr1::make_tuple; using testing::A; using testing::AllOf; using testing::An; @@ -98,6 +99,7 @@ using testing::StrEq; using testing::StrNe; using testing::Truly; using testing::TypedEq; +using testing::Value; using testing::_; using testing::internal::FloatingEqMatcher; using testing::internal::FormatMatcherDescriptionSyntaxError; @@ -1670,6 +1672,25 @@ TEST(MatchesTest, WorksWithMatcherOnNonRefType) { EXPECT_FALSE(Matches(eq5)(2)); } +// Tests Value(value, matcher). Since Value() is a simple wrapper for +// Matches(), which has been tested already, we don't spend a lot of +// effort on testing Value(). +TEST(ValueTest, WorksWithPolymorphicMatcher) { + EXPECT_TRUE(Value("hi", StartsWith("h"))); + EXPECT_FALSE(Value(5, Gt(10))); +} + +TEST(ValueTest, WorksWithMonomorphicMatcher) { + const Matcher<int> is_zero = Eq(0); + EXPECT_TRUE(Value(0, is_zero)); + EXPECT_FALSE(Value('a', is_zero)); + + int n = 0; + const Matcher<const int&> ref_n = Ref(n); + EXPECT_TRUE(Value(n, ref_n)); + EXPECT_FALSE(Value(1, ref_n)); +} + // Tests that ASSERT_THAT() and EXPECT_THAT() work when the value // matches the matcher. TEST(MatcherAssertionTest, WorksWhenMatcherIsSatisfied) { @@ -2765,9 +2786,7 @@ TEST(ByRefTest, AllowsNotCopyableValueInMatchers) { // different element types. template <typename T> -class ContainerEqTest : public testing::Test { - public: -}; +class ContainerEqTest : public testing::Test {}; typedef testing::Types< std::set<int>, @@ -2901,6 +2920,59 @@ TEST(ContainerEqExtraTest, WorksForMaps) { Explain(m, test_map)); } +TEST(ContainerEqExtraTest, WorksForNativeArray) { + int a1[] = { 1, 2, 3 }; + int a2[] = { 1, 2, 3 }; + int b[] = { 1, 2, 4 }; + + EXPECT_THAT(a1, ContainerEq(a2)); + EXPECT_THAT(a1, Not(ContainerEq(b))); +} + +TEST(ContainerEqExtraTest, WorksForTwoDimensionalNativeArray) { + const char a1[][3] = { "hi", "lo" }; + const char a2[][3] = { "hi", "lo" }; + const char b[][3] = { "lo", "hi" }; + + // Tests using ContainerEq() in the first dimension. + EXPECT_THAT(a1, ContainerEq(a2)); + EXPECT_THAT(a1, Not(ContainerEq(b))); + + // Tests using ContainerEq() in the second dimension. + EXPECT_THAT(a1, ElementsAre(ContainerEq(a2[0]), ContainerEq(a2[1]))); + EXPECT_THAT(a1, ElementsAre(Not(ContainerEq(b[0])), ContainerEq(a2[1]))); +} + +TEST(ContainerEqExtraTest, WorksForNativeArrayAsTuple) { + const int a1[] = { 1, 2, 3 }; + const int a2[] = { 1, 2, 3 }; + const int b[] = { 1, 2, 3, 4 }; + + EXPECT_THAT(make_tuple(a1, 3), ContainerEq(a2)); + EXPECT_THAT(make_tuple(a1, 3), Not(ContainerEq(b))); + + const int c[] = { 1, 3, 2 }; + EXPECT_THAT(make_tuple(a1, 3), Not(ContainerEq(c))); +} + +TEST(ContainerEqExtraTest, CopiesNativeArrayParameter) { + std::string a1[][3] = { + { "hi", "hello", "ciao" }, + { "bye", "see you", "ciao" } + }; + + std::string a2[][3] = { + { "hi", "hello", "ciao" }, + { "bye", "see you", "ciao" } + }; + + const Matcher<const std::string(&)[2][3]> m = ContainerEq(a2); + EXPECT_THAT(a1, m); + + a2[0][0] = "ha"; + EXPECT_THAT(a1, m); +} + // Tests GetParamIndex(). TEST(GetParamIndexTest, WorksForEmptyParamList) { diff --git a/test/gmock-printers_test.cc b/test/gmock-printers_test.cc index f03be292..8c03ec46 100644 --- a/test/gmock-printers_test.cc +++ b/test/gmock-printers_test.cc @@ -154,10 +154,13 @@ using ::std::tr1::tuple; using ::std::vector; using ::testing::ElementsAre; using ::testing::StartsWith; +using ::testing::internal::NativeArray; using ::testing::internal::Strings; using ::testing::internal::UniversalTersePrint; +using ::testing::internal::UniversalPrint; using ::testing::internal::UniversalTersePrintTupleFieldsToStrings; using ::testing::internal::UniversalPrinter; +using ::testing::internal::kReference; using ::testing::internal::string; #if GTEST_OS_WINDOWS @@ -786,6 +789,17 @@ TEST(PrintStlContainerTest, NestedContainer) { EXPECT_EQ("{ { 1, 2 }, { 3, 4, 5 } }", Print(v)); } +TEST(PrintStlContainerTest, OneDimensionalNativeArray) { + const int a[] = { 1, 2, 3 }; + NativeArray<int> b(a, kReference); + EXPECT_EQ("{ 1, 2, 3 }", Print(b)); +} + +TEST(PrintStlContainerTest, TwoDimensionalNativeArray) { + const int a[][3] = { { 1, 2, 3 }, { 4, 5, 6 } }; + NativeArray<int[3]> b(a, kReference); + EXPECT_EQ("{ { 1, 2, 3 }, { 4, 5, 6 } }", Print(b)); +} // Tests printing tuples. @@ -1013,6 +1027,37 @@ TEST(UniversalTersePrintTest, WorksForCString) { EXPECT_EQ("NULL", ss3.str()); } +TEST(UniversalPrintTest, WorksForNonReference) { + ::std::stringstream ss; + UniversalPrint(123, &ss); + EXPECT_EQ("123", ss.str()); +} + +TEST(UniversalPrintTest, WorksForReference) { + const int& n = 123; + ::std::stringstream ss; + UniversalPrint(n, &ss); + EXPECT_EQ("123", ss.str()); +} + +TEST(UniversalPrintTest, WorksForCString) { + const char* s1 = "abc"; + ::std::stringstream ss1; + UniversalPrint(s1, &ss1); + EXPECT_EQ(PrintPointer(s1) + " pointing to \"abc\"", string(ss1.str())); + + char* s2 = const_cast<char*>(s1); + ::std::stringstream ss2; + UniversalPrint(s2, &ss2); + EXPECT_EQ(PrintPointer(s2) + " pointing to \"abc\"", string(ss2.str())); + + const char* s3 = NULL; + ::std::stringstream ss3; + UniversalPrint(s3, &ss3); + EXPECT_EQ("NULL", ss3.str()); +} + + TEST(UniversalTersePrintTupleFieldsToStringsTest, PrintsEmptyTuple) { EXPECT_THAT(UniversalTersePrintTupleFieldsToStrings(make_tuple()), ElementsAre()); |