Merge branch 'variant_matcher' of https://github.com/zhangxy988/googletest into variant_matcher

This commit is contained in:
Xiaoyi Zhang 2018-02-27 16:06:07 -05:00
commit e1622337ae
5 changed files with 334 additions and 126 deletions

View File

@ -514,7 +514,7 @@ template <typename T, typename M>
class MatcherCastImpl { class MatcherCastImpl {
public: public:
static Matcher<T> Cast(const M& polymorphic_matcher_or_value) { 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 // its conversion operator to create Matcher<T>. Or it can be a value
// that should be passed to the Matcher<T>'s constructor. // that should be passed to the Matcher<T>'s constructor.
// //
@ -3303,14 +3303,23 @@ typedef ::std::vector<ElementMatcherPair> ElementMatcherPairs;
GTEST_API_ ElementMatcherPairs GTEST_API_ ElementMatcherPairs
FindMaxBipartiteMatching(const MatchMatrix& g); FindMaxBipartiteMatching(const MatchMatrix& g);
GTEST_API_ bool FindPairing(const MatchMatrix& matrix, struct UnorderedMatcherRequire {
MatchResultListener* listener); enum Flags {
Superset = 1 << 0,
Subset = 1 << 1,
ExactMatch = Superset | Subset,
};
};
// Untyped base class for implementing UnorderedElementsAre. By // Untyped base class for implementing UnorderedElementsAre. By
// putting logic that's not specific to the element type here, we // putting logic that's not specific to the element type here, we
// reduce binary bloat and increase compilation speed. // reduce binary bloat and increase compilation speed.
class GTEST_API_ UnorderedElementsAreMatcherImplBase { class GTEST_API_ UnorderedElementsAreMatcherImplBase {
protected: protected:
explicit UnorderedElementsAreMatcherImplBase(
UnorderedMatcherRequire::Flags matcher_flags)
: match_flags_(matcher_flags) {}
// A vector of matcher describers, one for each element matcher. // A vector of matcher describers, one for each element matcher.
// Does not own the describers (and thus can be used only when the // Does not own the describers (and thus can be used only when the
// element matchers are alive). // element matchers are alive).
@ -3322,9 +3331,12 @@ class GTEST_API_ UnorderedElementsAreMatcherImplBase {
// Describes the negation of this UnorderedElementsAre matcher. // Describes the negation of this UnorderedElementsAre matcher.
void DescribeNegationToImpl(::std::ostream* os) const; void DescribeNegationToImpl(::std::ostream* os) const;
bool VerifyAllElementsAndMatchersAreMatched( bool VerifyMatchMatrix(const ::std::vector<std::string>& element_printouts,
const ::std::vector<std::string>& element_printouts, const MatchMatrix& matrix,
const MatchMatrix& matrix, MatchResultListener* listener) const; MatchResultListener* listener) const;
bool FindPairing(const MatchMatrix& matrix,
MatchResultListener* listener) const;
MatcherDescriberVec& matcher_describers() { MatcherDescriberVec& matcher_describers() {
return matcher_describers_; return matcher_describers_;
@ -3334,13 +3346,17 @@ class GTEST_API_ UnorderedElementsAreMatcherImplBase {
return Message() << n << " element" << (n == 1 ? "" : "s"); return Message() << n << " element" << (n == 1 ? "" : "s");
} }
UnorderedMatcherRequire::Flags match_flags() const { return match_flags_; }
private: private:
UnorderedMatcherRequire::Flags match_flags_;
MatcherDescriberVec matcher_describers_; MatcherDescriberVec matcher_describers_;
GTEST_DISALLOW_ASSIGN_(UnorderedElementsAreMatcherImplBase); GTEST_DISALLOW_ASSIGN_(UnorderedElementsAreMatcherImplBase);
}; };
// Implements unordered ElementsAre and unordered ElementsAreArray. // Implements UnorderedElementsAre, UnorderedElementsAreArray, IsSubsetOf, and
// IsSupersetOf.
template <typename Container> template <typename Container>
class UnorderedElementsAreMatcherImpl class UnorderedElementsAreMatcherImpl
: public MatcherInterface<Container>, : public MatcherInterface<Container>,
@ -3353,10 +3369,10 @@ class UnorderedElementsAreMatcherImpl
typedef typename StlContainer::const_iterator StlContainerConstIterator; typedef typename StlContainer::const_iterator StlContainerConstIterator;
typedef typename StlContainer::value_type Element; typedef typename StlContainer::value_type Element;
// Constructs the matcher from a sequence of element values or
// element matchers.
template <typename InputIter> template <typename InputIter>
UnorderedElementsAreMatcherImpl(InputIter first, InputIter last) { UnorderedElementsAreMatcherImpl(UnorderedMatcherRequire::Flags matcher_flags,
InputIter first, InputIter last)
: UnorderedElementsAreMatcherImplBase(matcher_flags) {
for (; first != last; ++first) { for (; first != last; ++first) {
matchers_.push_back(MatcherCast<const Element&>(*first)); matchers_.push_back(MatcherCast<const Element&>(*first));
matcher_describers().push_back(matchers_.back().GetDescriber()); matcher_describers().push_back(matchers_.back().GetDescriber());
@ -3377,34 +3393,32 @@ class UnorderedElementsAreMatcherImpl
MatchResultListener* listener) const { MatchResultListener* listener) const {
StlContainerReference stl_container = View::ConstReference(container); StlContainerReference stl_container = View::ConstReference(container);
::std::vector<std::string> element_printouts; ::std::vector<std::string> element_printouts;
MatchMatrix matrix = AnalyzeElements(stl_container.begin(), MatchMatrix matrix =
stl_container.end(), AnalyzeElements(stl_container.begin(), stl_container.end(),
&element_printouts, &element_printouts, listener);
listener);
const size_t actual_count = matrix.LhsSize(); if (matrix.LhsSize() == 0 && matrix.RhsSize() == 0) {
if (actual_count == 0 && matchers_.empty()) {
return true; return true;
} }
if (actual_count != matchers_.size()) {
// The element count doesn't match. If the container is empty, if (match_flags() == UnorderedMatcherRequire::ExactMatch) {
// there's no need to explain anything as Google Mock already if (matrix.LhsSize() != matrix.RhsSize()) {
// prints the empty container. Otherwise we just need to show // The element count doesn't match. If the container is empty,
// how many elements there actually are. // there's no need to explain anything as Google Mock already
if (actual_count != 0 && listener->IsInterested()) { // prints the empty container. Otherwise we just need to show
*listener << "which has " << Elements(actual_count); // 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, return VerifyMatchMatrix(element_printouts, matrix, listener) &&
matrix, listener) &&
FindPairing(matrix, listener); FindPairing(matrix, listener);
} }
private: private:
typedef ::std::vector<Matcher<const Element&> > MatcherVec;
template <typename ElementIter> template <typename ElementIter>
MatchMatrix AnalyzeElements(ElementIter elem_first, ElementIter elem_last, MatchMatrix AnalyzeElements(ElementIter elem_first, ElementIter elem_last,
::std::vector<std::string>* element_printouts, ::std::vector<std::string>* element_printouts,
@ -3431,7 +3445,7 @@ class UnorderedElementsAreMatcherImpl
return matrix; return matrix;
} }
MatcherVec matchers_; ::std::vector<Matcher<const Element&> > matchers_;
GTEST_DISALLOW_ASSIGN_(UnorderedElementsAreMatcherImpl); GTEST_DISALLOW_ASSIGN_(UnorderedElementsAreMatcherImpl);
}; };
@ -3464,7 +3478,7 @@ class UnorderedElementsAreMatcher {
TransformTupleValues(CastAndAppendTransform<const Element&>(), matchers_, TransformTupleValues(CastAndAppendTransform<const Element&>(), matchers_,
::std::back_inserter(matchers)); ::std::back_inserter(matchers));
return MakeMatcher(new UnorderedElementsAreMatcherImpl<Container>( return MakeMatcher(new UnorderedElementsAreMatcherImpl<Container>(
matchers.begin(), matchers.end())); UnorderedMatcherRequire::ExactMatch, matchers.begin(), matchers.end()));
} }
private: private:
@ -3497,24 +3511,23 @@ class ElementsAreMatcher {
GTEST_DISALLOW_ASSIGN_(ElementsAreMatcher); GTEST_DISALLOW_ASSIGN_(ElementsAreMatcher);
}; };
// Implements UnorderedElementsAreArray(). // Implements UnorderedElementsAreArray(), IsSubsetOf(), and IsSupersetOf().
template <typename T> template <typename T>
class UnorderedElementsAreArrayMatcher { class UnorderedElementsAreArrayMatcher {
public: public:
UnorderedElementsAreArrayMatcher() {}
template <typename Iter> template <typename Iter>
UnorderedElementsAreArrayMatcher(Iter first, Iter last) UnorderedElementsAreArrayMatcher(UnorderedMatcherRequire::Flags match_flags,
: matchers_(first, last) {} Iter first, Iter last)
: match_flags_(match_flags), matchers_(first, last) {}
template <typename Container> template <typename Container>
operator Matcher<Container>() const { operator Matcher<Container>() const {
return MakeMatcher( return MakeMatcher(new UnorderedElementsAreMatcherImpl<Container>(
new UnorderedElementsAreMatcherImpl<Container>(matchers_.begin(), match_flags_, matchers_.begin(), matchers_.end()));
matchers_.end()));
} }
private: private:
UnorderedMatcherRequire::Flags match_flags_;
::std::vector<T> matchers_; ::std::vector<T> matchers_;
GTEST_DISALLOW_ASSIGN_(UnorderedElementsAreArrayMatcher); GTEST_DISALLOW_ASSIGN_(UnorderedElementsAreArrayMatcher);
@ -3685,7 +3698,7 @@ class VariantMatcher {
} // namespace internal } // namespace internal
// ElementsAreArray(first, last) // ElementsAreArray(iterator_first, iterator_last)
// ElementsAreArray(pointer, count) // ElementsAreArray(pointer, count)
// ElementsAreArray(array) // ElementsAreArray(array)
// ElementsAreArray(container) // ElementsAreArray(container)
@ -3734,20 +3747,26 @@ ElementsAreArray(::std::initializer_list<T> xs) {
} }
#endif #endif
// UnorderedElementsAreArray(first, last) // UnorderedElementsAreArray(iterator_first, iterator_last)
// UnorderedElementsAreArray(pointer, count) // UnorderedElementsAreArray(pointer, count)
// UnorderedElementsAreArray(array) // UnorderedElementsAreArray(array)
// UnorderedElementsAreArray(container) // UnorderedElementsAreArray(container)
// UnorderedElementsAreArray({ e1, e2, ..., en }) // UnorderedElementsAreArray({ e1, e2, ..., en })
// //
// The UnorderedElementsAreArray() functions are like // UnorderedElementsAreArray() verifies that a bijective mapping onto a
// ElementsAreArray(...), but allow matching the elements in any order. // 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> template <typename Iter>
inline internal::UnorderedElementsAreArrayMatcher< inline internal::UnorderedElementsAreArrayMatcher<
typename ::std::iterator_traits<Iter>::value_type> typename ::std::iterator_traits<Iter>::value_type>
UnorderedElementsAreArray(Iter first, Iter last) { UnorderedElementsAreArray(Iter first, Iter last) {
typedef typename ::std::iterator_traits<Iter>::value_type T; 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> template <typename T>
@ -3789,7 +3808,9 @@ UnorderedElementsAreArray(::std::initializer_list<T> xs) {
const internal::AnythingMatcher _ = {}; const internal::AnythingMatcher _ = {};
// Creates a matcher that matches any value of the given type T. // Creates a matcher that matches any value of the given type T.
template <typename 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. // Creates a matcher that matches any value of the given type T.
template <typename T> template <typename T>
@ -4359,6 +4380,128 @@ inline internal::ContainsMatcher<M> Contains(M matcher) {
return internal::ContainsMatcher<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 // Matches an STL-style container or a native array that contains only
// elements matching the given value or matcher. // elements matching the given value or matcher.
// //

View File

@ -38,6 +38,7 @@
#include "gmock/gmock-generated-matchers.h" #include "gmock/gmock-generated-matchers.h"
#include <string.h> #include <string.h>
#include <iostream>
#include <sstream> #include <sstream>
#include <string> #include <string>
@ -181,8 +182,7 @@ class MaxBipartiteMatchState {
explicit MaxBipartiteMatchState(const MatchMatrix& graph) explicit MaxBipartiteMatchState(const MatchMatrix& graph)
: graph_(&graph), : graph_(&graph),
left_(graph_->LhsSize(), kUnused), 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}. // Returns the edges of a maximal match, each in the form {left, right}.
ElementMatcherPairs Compute() { ElementMatcherPairs Compute() {
@ -239,10 +239,8 @@ class MaxBipartiteMatchState {
// //
bool TryAugment(size_t ilhs, ::std::vector<char>* seen) { bool TryAugment(size_t ilhs, ::std::vector<char>* seen) {
for (size_t irhs = 0; irhs < graph_->RhsSize(); ++irhs) { for (size_t irhs = 0; irhs < graph_->RhsSize(); ++irhs) {
if ((*seen)[irhs]) if ((*seen)[irhs]) continue;
continue; if (!graph_->HasEdge(ilhs, irhs)) continue;
if (!graph_->HasEdge(ilhs, irhs))
continue;
// There's an available edge from ilhs to irhs. // There's an available edge from ilhs to irhs.
(*seen)[irhs] = 1; (*seen)[irhs] = 1;
// Next a search is performed to determine whether // Next a search is performed to determine whether
@ -285,8 +283,7 @@ class MaxBipartiteMatchState {
const size_t MaxBipartiteMatchState::kUnused; const size_t MaxBipartiteMatchState::kUnused;
GTEST_API_ ElementMatcherPairs GTEST_API_ ElementMatcherPairs FindMaxBipartiteMatching(const MatchMatrix& g) {
FindMaxBipartiteMatching(const MatchMatrix& g) {
return MaxBipartiteMatchState(g).Compute(); return MaxBipartiteMatchState(g).Compute();
} }
@ -295,7 +292,7 @@ static void LogElementMatcherPairVec(const ElementMatcherPairs& pairs,
typedef ElementMatcherPairs::const_iterator Iter; typedef ElementMatcherPairs::const_iterator Iter;
::std::ostream& os = *stream; ::std::ostream& os = *stream;
os << "{"; os << "{";
const char *sep = ""; const char* sep = "";
for (Iter it = pairs.begin(); it != pairs.end(); ++it) { for (Iter it = pairs.begin(); it != pairs.end(); ++it) {
os << sep << "\n (" os << sep << "\n ("
<< "element #" << it->first << ", " << "element #" << it->first << ", "
@ -305,38 +302,6 @@ static void LogElementMatcherPairVec(const ElementMatcherPairs& pairs,
os << "\n}"; 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() { bool MatchMatrix::NextGraph() {
for (size_t ilhs = 0; ilhs < LhsSize(); ++ilhs) { for (size_t ilhs = 0; ilhs < LhsSize(); ++ilhs) {
for (size_t irhs = 0; irhs < RhsSize(); ++irhs) { for (size_t irhs = 0; irhs < RhsSize(); ++irhs) {
@ -362,7 +327,7 @@ void MatchMatrix::Randomize() {
std::string MatchMatrix::DebugString() const { std::string MatchMatrix::DebugString() const {
::std::stringstream ss; ::std::stringstream ss;
const char *sep = ""; const char* sep = "";
for (size_t i = 0; i < LhsSize(); ++i) { for (size_t i = 0; i < LhsSize(); ++i) {
ss << sep; ss << sep;
for (size_t j = 0; j < RhsSize(); ++j) { for (size_t j = 0; j < RhsSize(); ++j) {
@ -375,44 +340,83 @@ std::string MatchMatrix::DebugString() const {
void UnorderedElementsAreMatcherImplBase::DescribeToImpl( void UnorderedElementsAreMatcherImplBase::DescribeToImpl(
::std::ostream* os) const { ::std::ostream* os) const {
if (matcher_describers_.empty()) { switch (match_flags()) {
*os << "is empty"; case UnorderedMatcherRequire::ExactMatch:
return; 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;
} }
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";
const char* sep = ""; const char* sep = "";
for (size_t i = 0; i != matcher_describers_.size(); ++i) { 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); matcher_describers_[i]->DescribeTo(os);
sep = ", and\n"; if (match_flags() == UnorderedMatcherRequire::ExactMatch) {
sep = ", and\n";
} else {
sep = "\n";
}
} }
} }
void UnorderedElementsAreMatcherImplBase::DescribeNegationToImpl( void UnorderedElementsAreMatcherImplBase::DescribeNegationToImpl(
::std::ostream* os) const { ::std::ostream* os) const {
if (matcher_describers_.empty()) { switch (match_flags()) {
*os << "isn't empty"; case UnorderedMatcherRequire::ExactMatch:
return; 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;
} }
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";
const char* sep = ""; const char* sep = "";
for (size_t i = 0; i != matcher_describers_.size(); ++i) { 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); 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. // and better error reporting.
// Returns false, writing an explanation to 'listener', if and only // Returns false, writing an explanation to 'listener', if and only
// if the success criteria are not met. // if the success criteria are not met.
bool UnorderedElementsAreMatcherImplBase:: bool UnorderedElementsAreMatcherImplBase::VerifyMatchMatrix(
VerifyAllElementsAndMatchersAreMatched( const ::std::vector<std::string>& element_printouts,
const ::std::vector<std::string>& element_printouts, const MatchMatrix& matrix, MatchResultListener* listener) const {
const MatchMatrix& matrix, MatchResultListener* listener) const {
bool result = true; bool result = true;
::std::vector<char> element_matched(matrix.LhsSize(), 0); ::std::vector<char> element_matched(matrix.LhsSize(), 0);
::std::vector<char> matcher_matched(matrix.RhsSize(), 0); ::std::vector<char> matcher_matched(matrix.RhsSize(), 0);
@ -437,12 +440,11 @@ bool UnorderedElementsAreMatcherImplBase::
} }
} }
{ if (match_flags() & UnorderedMatcherRequire::Superset) {
const char* sep = const char* sep =
"where the following matchers don't match any elements:\n"; "where the following matchers don't match any elements:\n";
for (size_t mi = 0; mi < matcher_matched.size(); ++mi) { for (size_t mi = 0; mi < matcher_matched.size(); ++mi) {
if (matcher_matched[mi]) if (matcher_matched[mi]) continue;
continue;
result = false; result = false;
if (listener->IsInterested()) { if (listener->IsInterested()) {
*listener << sep << "matcher #" << mi << ": "; *listener << sep << "matcher #" << mi << ": ";
@ -452,7 +454,7 @@ bool UnorderedElementsAreMatcherImplBase::
} }
} }
{ if (match_flags() & UnorderedMatcherRequire::Subset) {
const char* sep = const char* sep =
"where the following elements don't match any matchers:\n"; "where the following elements don't match any matchers:\n";
const char* outer_sep = ""; const char* outer_sep = "";
@ -460,8 +462,7 @@ bool UnorderedElementsAreMatcherImplBase::
outer_sep = "\nand "; outer_sep = "\nand ";
} }
for (size_t ei = 0; ei < element_matched.size(); ++ei) { for (size_t ei = 0; ei < element_matched.size(); ++ei) {
if (element_matched[ei]) if (element_matched[ei]) continue;
continue;
result = false; result = false;
if (listener->IsInterested()) { if (listener->IsInterested()) {
*listener << outer_sep << sep << "element #" << ei << ": " *listener << outer_sep << sep << "element #" << ei << ": "
@ -474,5 +475,46 @@ bool UnorderedElementsAreMatcherImplBase::
return result; 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 internal
} // namespace testing } // namespace testing

View File

@ -120,13 +120,15 @@
# include <errno.h> # include <errno.h>
#endif #endif
#include "gmock/internal/gmock-port.h"
#include "gtest/gtest.h"
#include <iostream> #include <iostream>
#include <vector> #include <vector>
#include "gtest/gtest.h"
#include "gtest/internal/gtest-port.h"
using testing::_; using testing::_;
using testing::A; using testing::A;
using testing::Action;
using testing::AllOf; using testing::AllOf;
using testing::AnyOf; using testing::AnyOf;
using testing::Assign; using testing::Assign;
@ -148,6 +150,8 @@ using testing::Invoke;
using testing::InvokeArgument; using testing::InvokeArgument;
using testing::InvokeWithoutArgs; using testing::InvokeWithoutArgs;
using testing::IsNull; using testing::IsNull;
using testing::IsSubsetOf;
using testing::IsSupersetOf;
using testing::Le; using testing::Le;
using testing::Lt; using testing::Lt;
using testing::Matcher; using testing::Matcher;
@ -592,6 +596,22 @@ TEST(LinkTest, TestMatcherElementsAreArray) {
ON_CALL(mock, VoidFromVector(ElementsAreArray(arr))).WillByDefault(Return()); 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. // Tests the linkage of the ContainerEq matcher.
TEST(LinkTest, TestMatcherContainerEq) { TEST(LinkTest, TestMatcherContainerEq) {
Mock mock; Mock mock;

View File

@ -91,7 +91,7 @@ macro(config_compiler_and_linker)
set(cxx_base_flags "${cxx_base_flags} -D_UNICODE -DUNICODE -DWIN32 -D_WIN32") set(cxx_base_flags "${cxx_base_flags} -D_UNICODE -DUNICODE -DWIN32 -D_WIN32")
set(cxx_base_flags "${cxx_base_flags} -DSTRICT -DWIN32_LEAN_AND_MEAN") set(cxx_base_flags "${cxx_base_flags} -DSTRICT -DWIN32_LEAN_AND_MEAN")
set(cxx_exception_flags "-EHsc -D_HAS_EXCEPTIONS=1") set(cxx_exception_flags "-EHsc -D_HAS_EXCEPTIONS=1")
set(cxx_no_exception_flags "-D_HAS_EXCEPTIONS=0") set(cxx_no_exception_flags "-EHs-c- -D_HAS_EXCEPTIONS=0")
set(cxx_no_rtti_flags "-GR-") set(cxx_no_rtti_flags "-GR-")
elseif (CMAKE_COMPILER_IS_GNUCXX) elseif (CMAKE_COMPILER_IS_GNUCXX)
set(cxx_base_flags "-Wall -Wshadow -Werror") set(cxx_base_flags "-Wall -Wshadow -Werror")

View File

@ -471,8 +471,11 @@ typedef struct _RTL_CRITICAL_SECTION GTEST_CRITICAL_SECTION;
#ifndef GTEST_HAS_EXCEPTIONS #ifndef GTEST_HAS_EXCEPTIONS
// The user didn't tell us whether exceptions are enabled, so we need // The user didn't tell us whether exceptions are enabled, so we need
// to figure it out. // to figure it out.
# if defined(_MSC_VER) || defined(__BORLANDC__) # if defined(_MSC_VER) && defined(_CPPUNWIND)
// MSVC's and C++Builder's implementations of the STL use the _HAS_EXCEPTIONS // MSVC defines _CPPUNWIND to 1 iff exceptions are enabled.
# define GTEST_HAS_EXCEPTIONS 1
# elif defined(__BORLANDC__)
// C++Builder's implementation of the STL uses the _HAS_EXCEPTIONS
// macro to enable exceptions, so we'll do the same. // macro to enable exceptions, so we'll do the same.
// Assumes that exceptions are enabled by default. // Assumes that exceptions are enabled by default.
# ifndef _HAS_EXCEPTIONS # ifndef _HAS_EXCEPTIONS