diff options
Diffstat (limited to 'include')
-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 |
5 files changed, 495 insertions, 145 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 |