diff options
Diffstat (limited to 'googlemock')
-rw-r--r-- | googlemock/docs/CheatSheet.md | 2 | ||||
-rw-r--r-- | googlemock/include/gmock/gmock-matchers.h | 302 | ||||
-rw-r--r-- | googlemock/src/gmock-matchers.cc | 196 | ||||
-rw-r--r-- | googlemock/test/gmock-matchers_test.cc | 64 | ||||
-rw-r--r-- | googlemock/test/gmock_link_test.h | 24 |
5 files changed, 465 insertions, 123 deletions
diff --git a/googlemock/docs/CheatSheet.md b/googlemock/docs/CheatSheet.md index c6367fd..f8bbbfe 100644 --- a/googlemock/docs/CheatSheet.md +++ b/googlemock/docs/CheatSheet.md @@ -178,6 +178,8 @@ divided into several categories: |`Ne(value)` |`argument != value`| |`IsNull()` |`argument` is a `NULL` pointer (raw or smart).| |`NotNull()` |`argument` is a non-null pointer (raw or smart).| +|`VariantWith<T>(m)` |`argument` is `variant<>` that holds the alternative of +type T with a value matching `m`.| |`Ref(variable)` |`argument` is a reference to `variable`.| |`TypedEq<type>(value)`|`argument` has type `type` and is equal to `value`. You may need to use this instead of `Eq(value)` when the mock function is overloaded.| diff --git a/googlemock/include/gmock/gmock-matchers.h b/googlemock/include/gmock/gmock-matchers.h index fc3fe3a..9522c85 100644 --- a/googlemock/include/gmock/gmock-matchers.h +++ b/googlemock/include/gmock/gmock-matchers.h @@ -514,7 +514,7 @@ template <typename T, typename M> class MatcherCastImpl { public: static Matcher<T> Cast(const M& polymorphic_matcher_or_value) { - // M can be a polymorhic matcher, in which case we want to use + // M can be a polymorphic matcher, in which case we want to use // its conversion operator to create Matcher<T>. Or it can be a value // that should be passed to the Matcher<T>'s constructor. // @@ -3303,14 +3303,23 @@ typedef ::std::vector<ElementMatcherPair> ElementMatcherPairs; GTEST_API_ ElementMatcherPairs FindMaxBipartiteMatching(const MatchMatrix& g); -GTEST_API_ bool FindPairing(const MatchMatrix& matrix, - MatchResultListener* listener); +struct UnorderedMatcherRequire { + enum Flags { + Superset = 1 << 0, + Subset = 1 << 1, + ExactMatch = Superset | Subset, + }; +}; // Untyped base class for implementing UnorderedElementsAre. By // putting logic that's not specific to the element type here, we // reduce binary bloat and increase compilation speed. class GTEST_API_ UnorderedElementsAreMatcherImplBase { protected: + explicit UnorderedElementsAreMatcherImplBase( + UnorderedMatcherRequire::Flags matcher_flags) + : match_flags_(matcher_flags) {} + // A vector of matcher describers, one for each element matcher. // Does not own the describers (and thus can be used only when the // element matchers are alive). @@ -3322,9 +3331,12 @@ class GTEST_API_ UnorderedElementsAreMatcherImplBase { // Describes the negation of this UnorderedElementsAre matcher. void DescribeNegationToImpl(::std::ostream* os) const; - bool VerifyAllElementsAndMatchersAreMatched( - const ::std::vector<std::string>& element_printouts, - const MatchMatrix& matrix, MatchResultListener* listener) const; + bool VerifyMatchMatrix(const ::std::vector<std::string>& element_printouts, + const MatchMatrix& matrix, + MatchResultListener* listener) const; + + bool FindPairing(const MatchMatrix& matrix, + MatchResultListener* listener) const; MatcherDescriberVec& matcher_describers() { return matcher_describers_; @@ -3334,13 +3346,17 @@ class GTEST_API_ UnorderedElementsAreMatcherImplBase { return Message() << n << " element" << (n == 1 ? "" : "s"); } + UnorderedMatcherRequire::Flags match_flags() const { return match_flags_; } + private: + UnorderedMatcherRequire::Flags match_flags_; MatcherDescriberVec matcher_describers_; GTEST_DISALLOW_ASSIGN_(UnorderedElementsAreMatcherImplBase); }; -// Implements unordered ElementsAre and unordered ElementsAreArray. +// Implements UnorderedElementsAre, UnorderedElementsAreArray, IsSubsetOf, and +// IsSupersetOf. template <typename Container> class UnorderedElementsAreMatcherImpl : public MatcherInterface<Container>, @@ -3353,10 +3369,10 @@ class UnorderedElementsAreMatcherImpl typedef typename StlContainer::const_iterator StlContainerConstIterator; typedef typename StlContainer::value_type Element; - // Constructs the matcher from a sequence of element values or - // element matchers. template <typename InputIter> - UnorderedElementsAreMatcherImpl(InputIter first, InputIter last) { + UnorderedElementsAreMatcherImpl(UnorderedMatcherRequire::Flags matcher_flags, + InputIter first, InputIter last) + : UnorderedElementsAreMatcherImplBase(matcher_flags) { for (; first != last; ++first) { matchers_.push_back(MatcherCast<const Element&>(*first)); matcher_describers().push_back(matchers_.back().GetDescriber()); @@ -3377,34 +3393,32 @@ class UnorderedElementsAreMatcherImpl MatchResultListener* listener) const { StlContainerReference stl_container = View::ConstReference(container); ::std::vector<std::string> element_printouts; - MatchMatrix matrix = AnalyzeElements(stl_container.begin(), - stl_container.end(), - &element_printouts, - listener); + MatchMatrix matrix = + AnalyzeElements(stl_container.begin(), stl_container.end(), + &element_printouts, listener); - const size_t actual_count = matrix.LhsSize(); - if (actual_count == 0 && matchers_.empty()) { + if (matrix.LhsSize() == 0 && matrix.RhsSize() == 0) { return true; } - if (actual_count != matchers_.size()) { - // The element count doesn't match. If the container is empty, - // there's no need to explain anything as Google Mock already - // prints the empty container. Otherwise we just need to show - // how many elements there actually are. - if (actual_count != 0 && listener->IsInterested()) { - *listener << "which has " << Elements(actual_count); + + if (match_flags() == UnorderedMatcherRequire::ExactMatch) { + if (matrix.LhsSize() != matrix.RhsSize()) { + // The element count doesn't match. If the container is empty, + // there's no need to explain anything as Google Mock already + // prints the empty container. Otherwise we just need to show + // how many elements there actually are. + if (matrix.LhsSize() != 0 && listener->IsInterested()) { + *listener << "which has " << Elements(matrix.LhsSize()); + } + return false; } - return false; } - return VerifyAllElementsAndMatchersAreMatched(element_printouts, - matrix, listener) && + return VerifyMatchMatrix(element_printouts, matrix, listener) && FindPairing(matrix, listener); } private: - typedef ::std::vector<Matcher<const Element&> > MatcherVec; - template <typename ElementIter> MatchMatrix AnalyzeElements(ElementIter elem_first, ElementIter elem_last, ::std::vector<std::string>* element_printouts, @@ -3431,7 +3445,7 @@ class UnorderedElementsAreMatcherImpl return matrix; } - MatcherVec matchers_; + ::std::vector<Matcher<const Element&> > matchers_; GTEST_DISALLOW_ASSIGN_(UnorderedElementsAreMatcherImpl); }; @@ -3464,7 +3478,7 @@ class UnorderedElementsAreMatcher { TransformTupleValues(CastAndAppendTransform<const Element&>(), matchers_, ::std::back_inserter(matchers)); return MakeMatcher(new UnorderedElementsAreMatcherImpl<Container>( - matchers.begin(), matchers.end())); + UnorderedMatcherRequire::ExactMatch, matchers.begin(), matchers.end())); } private: @@ -3497,24 +3511,23 @@ class ElementsAreMatcher { GTEST_DISALLOW_ASSIGN_(ElementsAreMatcher); }; -// Implements UnorderedElementsAreArray(). +// Implements UnorderedElementsAreArray(), IsSubsetOf(), and IsSupersetOf(). template <typename T> class UnorderedElementsAreArrayMatcher { public: - UnorderedElementsAreArrayMatcher() {} - template <typename Iter> - UnorderedElementsAreArrayMatcher(Iter first, Iter last) - : matchers_(first, last) {} + UnorderedElementsAreArrayMatcher(UnorderedMatcherRequire::Flags match_flags, + Iter first, Iter last) + : match_flags_(match_flags), matchers_(first, last) {} template <typename Container> operator Matcher<Container>() const { - return MakeMatcher( - new UnorderedElementsAreMatcherImpl<Container>(matchers_.begin(), - matchers_.end())); + return MakeMatcher(new UnorderedElementsAreMatcherImpl<Container>( + match_flags_, matchers_.begin(), matchers_.end())); } private: + UnorderedMatcherRequire::Flags match_flags_; ::std::vector<T> matchers_; GTEST_DISALLOW_ASSIGN_(UnorderedElementsAreArrayMatcher); @@ -3623,9 +3636,69 @@ GTEST_API_ std::string FormatMatcherDescription(bool negation, const char* matcher_name, const Strings& param_values); +namespace variant_matcher { +// Overloads to allow VariantMatcher to do proper ADL lookup. +template <typename T> +void holds_alternative() {} +template <typename T> +void get() {} + +// Implements a matcher that checks the value of a variant<> type variable. +template <typename T> +class VariantMatcher { + public: + explicit VariantMatcher(::testing::Matcher<const T&> matcher) + : matcher_(internal::move(matcher)) {} + + template <typename Variant> + bool MatchAndExplain(const Variant& value, + ::testing::MatchResultListener* listener) const { + if (!listener->IsInterested()) { + return holds_alternative<T>(value) && matcher_.Matches(get<T>(value)); + } + + if (!holds_alternative<T>(value)) { + *listener << "whose value is not of type '" << GetTypeName() << "'"; + return false; + } + + const T& elem = get<T>(value); + StringMatchResultListener elem_listener; + const bool match = matcher_.MatchAndExplain(elem, &elem_listener); + *listener << "whose value " << PrintToString(elem) + << (match ? " matches" : " doesn't match"); + PrintIfNotEmpty(elem_listener.str(), listener->stream()); + return match; + } + + void DescribeTo(std::ostream* os) const { + *os << "is a variant<> with value of type '" << GetTypeName() + << "' and the value "; + matcher_.DescribeTo(os); + } + + void DescribeNegationTo(std::ostream* os) const { + *os << "is a variant<> with value of type other than '" << GetTypeName() + << "' or the value "; + matcher_.DescribeNegationTo(os); + } + + private: + static string GetTypeName() { +#if GTEST_HAS_RTTI + return internal::GetTypeName<T>(); +#endif + return "the element type"; + } + + const ::testing::Matcher<const T&> matcher_; +}; + +} // namespace variant_matcher + } // namespace internal -// ElementsAreArray(first, last) +// ElementsAreArray(iterator_first, iterator_last) // ElementsAreArray(pointer, count) // ElementsAreArray(array) // ElementsAreArray(container) @@ -3674,20 +3747,26 @@ ElementsAreArray(::std::initializer_list<T> xs) { } #endif -// UnorderedElementsAreArray(first, last) +// UnorderedElementsAreArray(iterator_first, iterator_last) // UnorderedElementsAreArray(pointer, count) // UnorderedElementsAreArray(array) // UnorderedElementsAreArray(container) // UnorderedElementsAreArray({ e1, e2, ..., en }) // -// The UnorderedElementsAreArray() functions are like -// ElementsAreArray(...), but allow matching the elements in any order. +// UnorderedElementsAreArray() verifies that a bijective mapping onto a +// collection of matchers exists. +// +// The matchers can be specified as an array, a pointer and count, a container, +// an initializer list, or an STL iterator range. In each of these cases, the +// underlying matchers can be either values or matchers. + template <typename Iter> inline internal::UnorderedElementsAreArrayMatcher< typename ::std::iterator_traits<Iter>::value_type> UnorderedElementsAreArray(Iter first, Iter last) { typedef typename ::std::iterator_traits<Iter>::value_type T; - return internal::UnorderedElementsAreArrayMatcher<T>(first, last); + return internal::UnorderedElementsAreArrayMatcher<T>( + internal::UnorderedMatcherRequire::ExactMatch, first, last); } template <typename T> @@ -3729,7 +3808,9 @@ UnorderedElementsAreArray(::std::initializer_list<T> xs) { const internal::AnythingMatcher _ = {}; // Creates a matcher that matches any value of the given type T. template <typename T> -inline Matcher<T> A() { return MakeMatcher(new internal::AnyMatcherImpl<T>()); } +inline Matcher<T> A() { + return Matcher<T>(new internal::AnyMatcherImpl<T>()); +} // Creates a matcher that matches any value of the given type T. template <typename T> @@ -4299,6 +4380,128 @@ inline internal::ContainsMatcher<M> Contains(M matcher) { return internal::ContainsMatcher<M>(matcher); } +// IsSupersetOf(iterator_first, iterator_last) +// IsSupersetOf(pointer, count) +// IsSupersetOf(array) +// IsSupersetOf(container) +// IsSupersetOf({e1, e2, ..., en}) +// +// IsSupersetOf() verifies that a surjective partial mapping onto a collection +// of matchers exists. In other words, a container matches +// IsSupersetOf({e1, ..., en}) if and only if there is a permutation +// {y1, ..., yn} of some of the container's elements where y1 matches e1, +// ..., and yn matches en. Obviously, the size of the container must be >= n +// in order to have a match. Examples: +// +// - {1, 2, 3} matches IsSupersetOf({Ge(3), Ne(0)}), as 3 matches Ge(3) and +// 1 matches Ne(0). +// - {1, 2} doesn't match IsSupersetOf({Eq(1), Lt(2)}), even though 1 matches +// both Eq(1) and Lt(2). The reason is that different matchers must be used +// for elements in different slots of the container. +// - {1, 1, 2} matches IsSupersetOf({Eq(1), Lt(2)}), as (the first) 1 matches +// Eq(1) and (the second) 1 matches Lt(2). +// - {1, 2, 3} matches IsSupersetOf(Gt(1), Gt(1)), as 2 matches (the first) +// Gt(1) and 3 matches (the second) Gt(1). +// +// The matchers can be specified as an array, a pointer and count, a container, +// an initializer list, or an STL iterator range. In each of these cases, the +// underlying matchers can be either values or matchers. + +template <typename Iter> +inline internal::UnorderedElementsAreArrayMatcher< + typename ::std::iterator_traits<Iter>::value_type> +IsSupersetOf(Iter first, Iter last) { + typedef typename ::std::iterator_traits<Iter>::value_type T; + return internal::UnorderedElementsAreArrayMatcher<T>( + internal::UnorderedMatcherRequire::Superset, first, last); +} + +template <typename T> +inline internal::UnorderedElementsAreArrayMatcher<T> IsSupersetOf( + const T* pointer, size_t count) { + return IsSupersetOf(pointer, pointer + count); +} + +template <typename T, size_t N> +inline internal::UnorderedElementsAreArrayMatcher<T> IsSupersetOf( + const T (&array)[N]) { + return IsSupersetOf(array, N); +} + +template <typename Container> +inline internal::UnorderedElementsAreArrayMatcher< + typename Container::value_type> +IsSupersetOf(const Container& container) { + return IsSupersetOf(container.begin(), container.end()); +} + +#if GTEST_HAS_STD_INITIALIZER_LIST_ +template <typename T> +inline internal::UnorderedElementsAreArrayMatcher<T> IsSupersetOf( + ::std::initializer_list<T> xs) { + return IsSupersetOf(xs.begin(), xs.end()); +} +#endif + +// IsSubsetOf(iterator_first, iterator_last) +// IsSubsetOf(pointer, count) +// IsSubsetOf(array) +// IsSubsetOf(container) +// IsSubsetOf({e1, e2, ..., en}) +// +// IsSubsetOf() verifies that an injective mapping onto a collection of matchers +// exists. In other words, a container matches IsSubsetOf({e1, ..., en}) if and +// only if there is a subset of matchers {m1, ..., mk} which would match the +// container using UnorderedElementsAre. Obviously, the size of the container +// must be <= n in order to have a match. Examples: +// +// - {1} matches IsSubsetOf({Gt(0), Lt(0)}), as 1 matches Gt(0). +// - {1, -1} matches IsSubsetOf({Lt(0), Gt(0)}), as 1 matches Gt(0) and -1 +// matches Lt(0). +// - {1, 2} doesn't matches IsSubsetOf({Gt(0), Lt(0)}), even though 1 and 2 both +// match Gt(0). The reason is that different matchers must be used for +// elements in different slots of the container. +// +// The matchers can be specified as an array, a pointer and count, a container, +// an initializer list, or an STL iterator range. In each of these cases, the +// underlying matchers can be either values or matchers. + +template <typename Iter> +inline internal::UnorderedElementsAreArrayMatcher< + typename ::std::iterator_traits<Iter>::value_type> +IsSubsetOf(Iter first, Iter last) { + typedef typename ::std::iterator_traits<Iter>::value_type T; + return internal::UnorderedElementsAreArrayMatcher<T>( + internal::UnorderedMatcherRequire::Subset, first, last); +} + +template <typename T> +inline internal::UnorderedElementsAreArrayMatcher<T> IsSubsetOf( + const T* pointer, size_t count) { + return IsSubsetOf(pointer, pointer + count); +} + +template <typename T, size_t N> +inline internal::UnorderedElementsAreArrayMatcher<T> IsSubsetOf( + const T (&array)[N]) { + return IsSubsetOf(array, N); +} + +template <typename Container> +inline internal::UnorderedElementsAreArrayMatcher< + typename Container::value_type> +IsSubsetOf(const Container& container) { + return IsSubsetOf(container.begin(), container.end()); +} + +#if GTEST_HAS_STD_INITIALIZER_LIST_ +template <typename T> +inline internal::UnorderedElementsAreArrayMatcher<T> IsSubsetOf( + ::std::initializer_list<T> xs) { + return IsSubsetOf(xs.begin(), xs.end()); +} +#endif + // Matches an STL-style container or a native array that contains only // elements matching the given value or matcher. // @@ -4397,6 +4600,17 @@ inline internal::AnyOfMatcher<Args...> AnyOf(const Args&... matchers) { template <typename InnerMatcher> inline InnerMatcher AllArgs(const InnerMatcher& matcher) { return matcher; } +// Returns a matcher that matches the value of a variant<> type variable. +// The matcher implementation uses ADL to find the holds_alternative and get +// functions. +// It is compatible with std::variant. +template <typename T> +PolymorphicMatcher<internal::variant_matcher::VariantMatcher<T> > VariantWith( + const Matcher<const T&>& matcher) { + return MakePolymorphicMatcher( + internal::variant_matcher::VariantMatcher<T>(matcher)); +} + // 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/googlemock/src/gmock-matchers.cc b/googlemock/src/gmock-matchers.cc index f37d5c2..88e4008 100644 --- a/googlemock/src/gmock-matchers.cc +++ b/googlemock/src/gmock-matchers.cc @@ -38,6 +38,7 @@ #include "gmock/gmock-generated-matchers.h" #include <string.h> +#include <iostream> #include <sstream> #include <string> @@ -181,8 +182,7 @@ class MaxBipartiteMatchState { explicit MaxBipartiteMatchState(const MatchMatrix& graph) : graph_(&graph), left_(graph_->LhsSize(), kUnused), - right_(graph_->RhsSize(), kUnused) { - } + right_(graph_->RhsSize(), kUnused) {} // Returns the edges of a maximal match, each in the form {left, right}. ElementMatcherPairs Compute() { @@ -239,10 +239,8 @@ class MaxBipartiteMatchState { // bool TryAugment(size_t ilhs, ::std::vector<char>* seen) { for (size_t irhs = 0; irhs < graph_->RhsSize(); ++irhs) { - if ((*seen)[irhs]) - continue; - if (!graph_->HasEdge(ilhs, irhs)) - continue; + if ((*seen)[irhs]) continue; + if (!graph_->HasEdge(ilhs, irhs)) continue; // There's an available edge from ilhs to irhs. (*seen)[irhs] = 1; // Next a search is performed to determine whether @@ -285,8 +283,7 @@ class MaxBipartiteMatchState { const size_t MaxBipartiteMatchState::kUnused; -GTEST_API_ ElementMatcherPairs -FindMaxBipartiteMatching(const MatchMatrix& g) { +GTEST_API_ ElementMatcherPairs FindMaxBipartiteMatching(const MatchMatrix& g) { return MaxBipartiteMatchState(g).Compute(); } @@ -295,7 +292,7 @@ static void LogElementMatcherPairVec(const ElementMatcherPairs& pairs, typedef ElementMatcherPairs::const_iterator Iter; ::std::ostream& os = *stream; os << "{"; - const char *sep = ""; + const char* sep = ""; for (Iter it = pairs.begin(); it != pairs.end(); ++it) { os << sep << "\n (" << "element #" << it->first << ", " @@ -305,38 +302,6 @@ static void LogElementMatcherPairVec(const ElementMatcherPairs& pairs, os << "\n}"; } -// Tries to find a pairing, and explains the result. -GTEST_API_ bool FindPairing(const MatchMatrix& matrix, - MatchResultListener* listener) { - ElementMatcherPairs matches = FindMaxBipartiteMatching(matrix); - - size_t max_flow = matches.size(); - bool result = (max_flow == matrix.RhsSize()); - - if (!result) { - if (listener->IsInterested()) { - *listener << "where no permutation of the elements can " - "satisfy all matchers, and the closest match is " - << max_flow << " of " << matrix.RhsSize() - << " matchers with the pairings:\n"; - LogElementMatcherPairVec(matches, listener->stream()); - } - return false; - } - - if (matches.size() > 1) { - if (listener->IsInterested()) { - const char *sep = "where:\n"; - for (size_t mi = 0; mi < matches.size(); ++mi) { - *listener << sep << " - element #" << matches[mi].first - << " is matched by matcher #" << matches[mi].second; - sep = ",\n"; - } - } - } - return true; -} - bool MatchMatrix::NextGraph() { for (size_t ilhs = 0; ilhs < LhsSize(); ++ilhs) { for (size_t irhs = 0; irhs < RhsSize(); ++irhs) { @@ -362,7 +327,7 @@ void MatchMatrix::Randomize() { std::string MatchMatrix::DebugString() const { ::std::stringstream ss; - const char *sep = ""; + const char* sep = ""; for (size_t i = 0; i < LhsSize(); ++i) { ss << sep; for (size_t j = 0; j < RhsSize(); ++j) { @@ -375,44 +340,83 @@ std::string MatchMatrix::DebugString() const { void UnorderedElementsAreMatcherImplBase::DescribeToImpl( ::std::ostream* os) const { - if (matcher_describers_.empty()) { - *os << "is empty"; - return; - } - if (matcher_describers_.size() == 1) { - *os << "has " << Elements(1) << " and that element "; - matcher_describers_[0]->DescribeTo(os); - return; + switch (match_flags()) { + case UnorderedMatcherRequire::ExactMatch: + if (matcher_describers_.empty()) { + *os << "is empty"; + return; + } + if (matcher_describers_.size() == 1) { + *os << "has " << Elements(1) << " and that element "; + matcher_describers_[0]->DescribeTo(os); + return; + } + *os << "has " << Elements(matcher_describers_.size()) + << " and there exists some permutation of elements such that:\n"; + break; + case UnorderedMatcherRequire::Superset: + *os << "a surjection from elements to requirements exists such that:\n"; + break; + case UnorderedMatcherRequire::Subset: + *os << "an injection from elements to requirements exists such that:\n"; + break; } - *os << "has " << Elements(matcher_describers_.size()) - << " and there exists some permutation of elements such that:\n"; + const char* sep = ""; for (size_t i = 0; i != matcher_describers_.size(); ++i) { - *os << sep << " - element #" << i << " "; + *os << sep; + if (match_flags() == UnorderedMatcherRequire::ExactMatch) { + *os << " - element #" << i << " "; + } else { + *os << " - an element "; + } matcher_describers_[i]->DescribeTo(os); - sep = ", and\n"; + if (match_flags() == UnorderedMatcherRequire::ExactMatch) { + sep = ", and\n"; + } else { + sep = "\n"; + } } } void UnorderedElementsAreMatcherImplBase::DescribeNegationToImpl( ::std::ostream* os) const { - if (matcher_describers_.empty()) { - *os << "isn't empty"; - return; - } - if (matcher_describers_.size() == 1) { - *os << "doesn't have " << Elements(1) - << ", or has " << Elements(1) << " that "; - matcher_describers_[0]->DescribeNegationTo(os); - return; + switch (match_flags()) { + case UnorderedMatcherRequire::ExactMatch: + if (matcher_describers_.empty()) { + *os << "isn't empty"; + return; + } + if (matcher_describers_.size() == 1) { + *os << "doesn't have " << Elements(1) << ", or has " << Elements(1) + << " that "; + matcher_describers_[0]->DescribeNegationTo(os); + return; + } + *os << "doesn't have " << Elements(matcher_describers_.size()) + << ", or there exists no permutation of elements such that:\n"; + break; + case UnorderedMatcherRequire::Superset: + *os << "no surjection from elements to requirements exists such that:\n"; + break; + case UnorderedMatcherRequire::Subset: + *os << "no injection from elements to requirements exists such that:\n"; + break; } - *os << "doesn't have " << Elements(matcher_describers_.size()) - << ", or there exists no permutation of elements such that:\n"; const char* sep = ""; for (size_t i = 0; i != matcher_describers_.size(); ++i) { - *os << sep << " - element #" << i << " "; + *os << sep; + if (match_flags() == UnorderedMatcherRequire::ExactMatch) { + *os << " - element #" << i << " "; + } else { + *os << " - an element "; + } matcher_describers_[i]->DescribeTo(os); - sep = ", and\n"; + if (match_flags() == UnorderedMatcherRequire::ExactMatch) { + sep = ", and\n"; + } else { + sep = "\n"; + } } } @@ -421,10 +425,9 @@ void UnorderedElementsAreMatcherImplBase::DescribeNegationToImpl( // and better error reporting. // Returns false, writing an explanation to 'listener', if and only // if the success criteria are not met. -bool UnorderedElementsAreMatcherImplBase:: - VerifyAllElementsAndMatchersAreMatched( - const ::std::vector<std::string>& element_printouts, - const MatchMatrix& matrix, MatchResultListener* listener) const { +bool UnorderedElementsAreMatcherImplBase::VerifyMatchMatrix( + const ::std::vector<std::string>& element_printouts, + const MatchMatrix& matrix, MatchResultListener* listener) const { bool result = true; ::std::vector<char> element_matched(matrix.LhsSize(), 0); ::std::vector<char> matcher_matched(matrix.RhsSize(), 0); @@ -437,12 +440,11 @@ bool UnorderedElementsAreMatcherImplBase:: } } - { + if (match_flags() & UnorderedMatcherRequire::Superset) { const char* sep = "where the following matchers don't match any elements:\n"; for (size_t mi = 0; mi < matcher_matched.size(); ++mi) { - if (matcher_matched[mi]) - continue; + if (matcher_matched[mi]) continue; result = false; if (listener->IsInterested()) { *listener << sep << "matcher #" << mi << ": "; @@ -452,7 +454,7 @@ bool UnorderedElementsAreMatcherImplBase:: } } - { + if (match_flags() & UnorderedMatcherRequire::Subset) { const char* sep = "where the following elements don't match any matchers:\n"; const char* outer_sep = ""; @@ -460,8 +462,7 @@ bool UnorderedElementsAreMatcherImplBase:: outer_sep = "\nand "; } for (size_t ei = 0; ei < element_matched.size(); ++ei) { - if (element_matched[ei]) - continue; + if (element_matched[ei]) continue; result = false; if (listener->IsInterested()) { *listener << outer_sep << sep << "element #" << ei << ": " @@ -474,5 +475,46 @@ bool UnorderedElementsAreMatcherImplBase:: return result; } +bool UnorderedElementsAreMatcherImplBase::FindPairing( + const MatchMatrix& matrix, MatchResultListener* listener) const { + ElementMatcherPairs matches = FindMaxBipartiteMatching(matrix); + + size_t max_flow = matches.size(); + if ((match_flags() & UnorderedMatcherRequire::Superset) && + max_flow < matrix.RhsSize()) { + if (listener->IsInterested()) { + *listener << "where no permutation of the elements can satisfy all " + "matchers, and the closest match is " + << max_flow << " of " << matrix.RhsSize() + << " matchers with the pairings:\n"; + LogElementMatcherPairVec(matches, listener->stream()); + } + return false; + } + if ((match_flags() & UnorderedMatcherRequire::Subset) && + max_flow < matrix.LhsSize()) { + if (listener->IsInterested()) { + *listener + << "where not all elements can be matched, and the closest match is " + << max_flow << " of " << matrix.RhsSize() + << " matchers with the pairings:\n"; + LogElementMatcherPairVec(matches, listener->stream()); + } + return false; + } + + if (matches.size() > 1) { + if (listener->IsInterested()) { + const char* sep = "where:\n"; + for (size_t mi = 0; mi < matches.size(); ++mi) { + *listener << sep << " - element #" << matches[mi].first + << " is matched by matcher #" << matches[mi].second; + sep = ",\n"; + } + } + } + return true; +} + } // namespace internal } // namespace testing diff --git a/googlemock/test/gmock-matchers_test.cc b/googlemock/test/gmock-matchers_test.cc index 761c0c2..829935e 100644 --- a/googlemock/test/gmock-matchers_test.cc +++ b/googlemock/test/gmock-matchers_test.cc @@ -5655,5 +5655,69 @@ TEST(UnorderedPointwiseTest, AllowsMonomorphicInnerMatcher) { EXPECT_THAT(lhs, UnorderedPointwise(m2, rhs)); } +class SampleVariantIntString { + public: + SampleVariantIntString(int i) : i_(i), has_int_(true) {} + SampleVariantIntString(const std::string& s) : s_(s), has_int_(false) {} + + template <typename T> + friend bool holds_alternative(const SampleVariantIntString& value) { + return value.has_int_ == internal::IsSame<T, int>::value; + } + + template <typename T> + friend const T& get(const SampleVariantIntString& value) { + return value.get_impl(static_cast<T*>(NULL)); + } + + private: + const int& get_impl(int*) const { return i_; } + const std::string& get_impl(std::string*) const { return s_; } + + int i_; + std::string s_; + bool has_int_; +}; + +TEST(VariantTest, DescribesSelf) { + const Matcher<SampleVariantIntString> m = VariantWith<int>(Eq(1)); + EXPECT_THAT(Describe(m), ContainsRegex("is a variant<> with value of type " + "'.*' and the value is equal to 1")); +} + +TEST(VariantTest, ExplainsSelf) { + const Matcher<SampleVariantIntString> m = VariantWith<int>(Eq(1)); + EXPECT_THAT(Explain(m, SampleVariantIntString(1)), + ContainsRegex("whose value 1")); + EXPECT_THAT(Explain(m, SampleVariantIntString("A")), + HasSubstr("whose value is not of type '")); + EXPECT_THAT(Explain(m, SampleVariantIntString(2)), + "whose value 2 doesn't match"); +} + +TEST(VariantTest, FullMatch) { + Matcher<SampleVariantIntString> m = VariantWith<int>(Eq(1)); + EXPECT_TRUE(m.Matches(SampleVariantIntString(1))); + + m = VariantWith<std::string>(Eq("1")); + EXPECT_TRUE(m.Matches(SampleVariantIntString("1"))); +} + +TEST(VariantTest, TypeDoesNotMatch) { + Matcher<SampleVariantIntString> m = VariantWith<int>(Eq(1)); + EXPECT_FALSE(m.Matches(SampleVariantIntString("1"))); + + m = VariantWith<std::string>(Eq("1")); + EXPECT_FALSE(m.Matches(SampleVariantIntString(1))); +} + +TEST(VariantTest, InnerDoesNotMatch) { + Matcher<SampleVariantIntString> m = VariantWith<int>(Eq(1)); + EXPECT_FALSE(m.Matches(SampleVariantIntString(2))); + + m = VariantWith<std::string>(Eq("1")); + EXPECT_FALSE(m.Matches(SampleVariantIntString("2"))); +} + } // namespace gmock_matchers_test } // namespace testing diff --git a/googlemock/test/gmock_link_test.h b/googlemock/test/gmock_link_test.h index 1f55f5b..5f855d1 100644 --- a/googlemock/test/gmock_link_test.h +++ b/googlemock/test/gmock_link_test.h @@ -120,13 +120,15 @@ # include <errno.h> #endif -#include "gmock/internal/gmock-port.h" -#include "gtest/gtest.h" #include <iostream> #include <vector> +#include "gtest/gtest.h" +#include "gtest/internal/gtest-port.h" + using testing::_; using testing::A; +using testing::Action; using testing::AllOf; using testing::AnyOf; using testing::Assign; @@ -148,6 +150,8 @@ using testing::Invoke; using testing::InvokeArgument; using testing::InvokeWithoutArgs; using testing::IsNull; +using testing::IsSubsetOf; +using testing::IsSupersetOf; using testing::Le; using testing::Lt; using testing::Matcher; @@ -592,6 +596,22 @@ TEST(LinkTest, TestMatcherElementsAreArray) { ON_CALL(mock, VoidFromVector(ElementsAreArray(arr))).WillByDefault(Return()); } +// Tests the linkage of the IsSubsetOf matcher. +TEST(LinkTest, TestMatcherIsSubsetOf) { + Mock mock; + char arr[] = {'a', 'b'}; + + ON_CALL(mock, VoidFromVector(IsSubsetOf(arr))).WillByDefault(Return()); +} + +// Tests the linkage of the IsSupersetOf matcher. +TEST(LinkTest, TestMatcherIsSupersetOf) { + Mock mock; + char arr[] = {'a', 'b'}; + + ON_CALL(mock, VoidFromVector(IsSupersetOf(arr))).WillByDefault(Return()); +} + // Tests the linkage of the ContainerEq matcher. TEST(LinkTest, TestMatcherContainerEq) { Mock mock; |