Commit fb25d539 authored by zhanyong.wan's avatar zhanyong.wan
Browse files

Adds matchers UnorderedElementsAre[Array]() (by Billy Donahue); pulls in

gtest r660.
parent 2989703e
...@@ -2,7 +2,8 @@ Changes for 1.7.0: ...@@ -2,7 +2,8 @@ Changes for 1.7.0:
* All new improvements in Google Test 1.7.0. * All new improvements in Google Test 1.7.0.
* New feature: matchers DoubleNear(), FloatNear(), * New feature: matchers DoubleNear(), FloatNear(),
NanSensitiveDoubleNear(), NanSensitiveFloatNear(), WhenSorted(), NanSensitiveDoubleNear(), NanSensitiveFloatNear(),
UnorderedElementsAre(), UnorderedElementsAreArray(), WhenSorted(),
WhenSortedBy(), IsEmpty(), and SizeIs(). WhenSortedBy(), IsEmpty(), and SizeIs().
* Improvement: Google Mock can now be built as a DLL. * Improvement: Google Mock can now be built as a DLL.
* Improvement: when compiled by a C++11 compiler, matchers AllOf() * Improvement: when compiled by a C++11 compiler, matchers AllOf()
......
This diff is collapsed.
...@@ -187,66 +187,6 @@ class ArgsMatcher { ...@@ -187,66 +187,6 @@ class ArgsMatcher {
GTEST_DISALLOW_ASSIGN_(ArgsMatcher); GTEST_DISALLOW_ASSIGN_(ArgsMatcher);
}; };
// Implements ElementsAre() of 1-$n arguments. The use of DecayArray in
// the implementation allows ElementsAre() to accept string literals, whose
// inferred type is const char[N] while we want to treat them as const char*.
$range i 1..n
$for i [[
$range j 1..i
template <$for j, [[typename T$j]]>
class ElementsAreMatcher$i {
public:
$if i==1 [[explicit ]]ElementsAreMatcher$i($for j, [[const T$j& e$j]])$if i > 0 [[ : ]]
$for j, [[e$j[[]]_(e$j)]] {}
template <typename Container>
operator Matcher<Container>() const {
typedef GTEST_REMOVE_REFERENCE_AND_CONST_(Container) RawContainer;
typedef typename internal::StlContainerView<RawContainer>::type::value_type
Element;
$if i==1 [[
// Nokia's Symbian Compiler has a nasty bug where the object put
// in a one-element local array is not destructed when the array
// goes out of scope. This leads to obvious badness as we've
// added the linked_ptr in it to our other linked_ptrs list.
// Hence we implement ElementsAreMatcher1 specially to avoid using
// a local array.
const Matcher<const Element&> matcher =
MatcherCast<const Element&>(e1_);
return MakeMatcher(new ElementsAreMatcherImpl<Container>(&matcher,
&matcher + 1));
]] $else [[
const Matcher<const Element&> matchers[] = {
$for j [[
MatcherCast<const Element&>(e$j[[]]_),
]]
};
return MakeMatcher(new ElementsAreMatcherImpl<Container>(matchers,
matchers + $i));
]]
}
private:
$for j [[
const typename DecayArray<T$j>::type e$j[[]]_;
]]
GTEST_DISALLOW_ASSIGN_(ElementsAreMatcher$i);
};
]]
// A set of metafunctions for computing the result type of AllOf. // A set of metafunctions for computing the result type of AllOf.
// AllOf(m1, ..., mN) returns // AllOf(m1, ..., mN) returns
// AllOfResultN<decltype(m1), ..., decltype(mN)>::type. // AllOfResultN<decltype(m1), ..., decltype(mN)>::type.
...@@ -324,79 +264,72 @@ Args(const InnerMatcher& matcher) { ...@@ -324,79 +264,72 @@ Args(const InnerMatcher& matcher) {
]] ]]
// ElementsAre(e0, e1, ..., e_n) matches an STL-style container with // ElementsAre(e_1, e_2, ... e_n) matches an STL-style container with
// (n + 1) elements, where the i-th element in the container must // n elements, where the i-th element in the container must
// match the i-th argument in the list. Each argument of // match the i-th argument in the list. Each argument of
// ElementsAre() can be either a value or a matcher. We support up to // ElementsAre() can be either a value or a matcher. We support up to
// $n arguments. // $n arguments.
// //
// The use of DecayArray in the implementation allows ElementsAre()
// to accept string literals, whose type is const char[N], but we
// want to treat them as const char*.
//
// NOTE: Since ElementsAre() cares about the order of the elements, it // NOTE: Since ElementsAre() cares about the order of the elements, it
// must not be used with containers whose elements's order is // must not be used with containers whose elements's order is
// undefined (e.g. hash_map). // undefined (e.g. hash_map).
inline internal::ElementsAreMatcher0 ElementsAre() { $range i 0..n
return internal::ElementsAreMatcher0();
}
$range i 1..n
$for i [[ $for i [[
$range j 1..i $range j 1..i
$if i>0 [[
template <$for j, [[typename T$j]]> template <$for j, [[typename T$j]]>
inline internal::ElementsAreMatcher$i<$for j, [[T$j]]> ElementsAre($for j, [[const T$j& e$j]]) { ]]
return internal::ElementsAreMatcher$i<$for j, [[T$j]]>($for j, [[e$j]]);
inline internal::ElementsAreMatcher<
std::tr1::tuple<
$for j, [[
typename internal::DecayArray<T$j[[]]>::type]]> >
ElementsAre($for j, [[const T$j& e$j]]) {
typedef std::tr1::tuple<
$for j, [[
typename internal::DecayArray<T$j[[]]>::type]]> Args;
return internal::ElementsAreMatcher<Args>(Args($for j, [[e$j]]));
} }
]] ]]
// ElementsAreArray(array) // UnorderedElementsAre(e_1, e_2, ..., e_n) is an ElementsAre extension
// ElementsAreArray(pointer, count) // that matches n elements in any order. We support up to n=$n arguments.
// ElementsAreArray(vector)
// ElementsAreArray(first, last)
//
// The ElementsAreArray() functions are like ElementsAre(...), except that
// they are given a sequence of matchers or values rather than taking each
// element as a function argument. The sequence can be specified as a
// C-style array, a pointer and count, a vector, or an STL iterator range.
//
// * The array form infers the size of 'array', which must be of a
// statically-sized C-style array type.
//
// * The (pointer, count) form can take either a statically-sized C-style
// array or a pointer to a dynamically created array. It does not take
// ownership of the pointer.
//
// * The vector form can take a std::vector either of values or of matchers.
//
// * The (first, last) form can take any STL iterator range.
//
// All forms of ElementsAreArray() make a copy of the input sequence.
template <typename T>
inline internal::ElementsAreArrayMatcher<T> ElementsAreArray(
const T* first, size_t count) {
return internal::ElementsAreArrayMatcher<T>(first, first + count);
}
template <typename T, size_t N> $range i 0..n
inline internal::ElementsAreArrayMatcher<T> ElementsAreArray( $for i [[
const T (&array)[N]) {
return internal::ElementsAreArrayMatcher<T>(array, array + N);
}
template <typename T, typename A> $range j 1..i
inline internal::ElementsAreArrayMatcher<T> ElementsAreArray(
const std::vector<T, A>& vec) { $if i>0 [[
return internal::ElementsAreArrayMatcher<T>(vec.begin(), vec.end());
}
template <typename Iter> template <$for j, [[typename T$j]]>
inline internal::ElementsAreArrayMatcher< ]]
typename std::iterator_traits<Iter>::value_type>
ElementsAreArray(Iter first, Iter last) { inline internal::UnorderedElementsAreMatcher<
typedef typename std::iterator_traits<Iter>::value_type T; std::tr1::tuple<
return internal::ElementsAreArrayMatcher<T>(first, last); $for j, [[
typename internal::DecayArray<T$j[[]]>::type]]> >
UnorderedElementsAre($for j, [[const T$j& e$j]]) {
typedef std::tr1::tuple<
$for j, [[
typename internal::DecayArray<T$j[[]]>::type]]> Args;
return internal::UnorderedElementsAreMatcher<Args>(Args($for j, [[e$j]]));
} }
]]
// AllOf(m1, m2, ..., mk) matches any value that matches all of the given // AllOf(m1, m2, ..., mk) matches any value that matches all of the given
// sub-matchers. AllOf is called fully qualified to prevent ADL from firing. // sub-matchers. AllOf is called fully qualified to prevent ADL from firing.
......
This diff is collapsed.
...@@ -133,5 +133,366 @@ GTEST_API_ string FormatMatcherDescription(bool negation, ...@@ -133,5 +133,366 @@ GTEST_API_ string FormatMatcherDescription(bool negation,
return negation ? "not (" + result + ")" : result; return negation ? "not (" + result + ")" : result;
} }
// FindMaxBipartiteMatching and its helper class.
//
// Uses the well-known Ford-Fulkerson max flow method to find a maximum
// bipartite matching. Flow is considered to be from left to right.
// There is an implicit source node that is connected to all of the left
// nodes, and an implicit sink node that is connected to all of the
// right nodes. All edges have unit capacity.
//
// Neither the flow graph nor the residual flow graph are represented
// explicitly. Instead, they are implied by the information in 'graph' and
// a vector<int> called 'left_' whose elements are initialized to the
// value kUnused. This represents the initial state of the algorithm,
// where the flow graph is empty, and the residual flow graph has the
// following edges:
// - An edge from source to each left_ node
// - An edge from each right_ node to sink
// - An edge from each left_ node to each right_ node, if the
// corresponding edge exists in 'graph'.
//
// When the TryAugment() method adds a flow, it sets left_[l] = r for some
// nodes l and r. This induces the following changes:
// - The edges (source, l), (l, r), and (r, sink) are added to the
// flow graph.
// - The same three edges are removed from the residual flow graph.
// - The reverse edges (l, source), (r, l), and (sink, r) are added
// to the residual flow graph, which is a directional graph
// representing unused flow capacity.
//
// When the method augments a flow (moving left_[l] from some r1 to some
// other r2), this can be thought of as "undoing" the above steps with
// respect to r1 and "redoing" them with respect to r2.
//
// It bears repeating that the flow graph and residual flow graph are
// never represented explicitly, but can be derived by looking at the
// information in 'graph' and in left_.
//
// As an optimization, there is a second vector<int> called right_ which
// does not provide any new information. Instead, it enables more
// efficient queries about edges entering or leaving the right-side nodes
// of the flow or residual flow graphs. The following invariants are
// maintained:
//
// left[l] == kUnused or right[left[l]] == l
// right[r] == kUnused or left[right[r]] == r
//
// . [ source ] .
// . ||| .
// . ||| .
// . ||\--> left[0]=1 ---\ right[0]=-1 ----\ .
// . || | | .
// . |\---> left[1]=-1 \--> right[1]=0 ---\| .
// . | || .
// . \----> left[2]=2 ------> right[2]=2 --\|| .
// . ||| .
// . elements matchers vvv .
// . [ sink ] .
//
// See Also:
// [1] Cormen, et al (2001). "Section 26.2: The Ford–Fulkerson method".
// "Introduction to Algorithms (Second ed.)", pp. 651–664.
// [2] "Ford–Fulkerson algorithm", Wikipedia,
// 'http://en.wikipedia.org/wiki/Ford%E2%80%93Fulkerson_algorithm'
class MaxBipartiteMatchState {
public:
explicit MaxBipartiteMatchState(const MatchMatrix& graph)
: graph_(&graph),
left_(graph_->LhsSize(), kUnused),
right_(graph_->RhsSize(), kUnused) {
}
// Returns the edges of a maximal match, each in the form {left, right}.
ElementMatcherPairs Compute() {
// 'seen' is used for path finding { 0: unseen, 1: seen }.
::std::vector<char> seen;
// Searches the residual flow graph for a path from each left node to
// the sink in the residual flow graph, and if one is found, add flow
// to the graph. It's okay to search through the left nodes once. The
// edge from the implicit source node to each previously-visited left
// node will have flow if that left node has any path to the sink
// whatsoever. Subsequent augmentations can only add flow to the
// network, and cannot take away that previous flow unit from the source.
// Since the source-to-left edge can only carry one flow unit (or,
// each element can be matched to only one matcher), there is no need
// to visit the left nodes more than once looking for augmented paths.
// The flow is known to be possible or impossible by looking at the
// node once.
for (size_t ilhs = 0; ilhs < graph_->LhsSize(); ++ilhs) {
// Reset the path-marking vector and try to find a path from
// source to sink starting at the left_[ilhs] node.
GTEST_CHECK_(left_[ilhs] == kUnused)
<< "ilhs: " << ilhs << ", left_[ilhs]: " << left_[ilhs];
// 'seen' initialized to 'graph_->RhsSize()' copies of 0.
seen.assign(graph_->RhsSize(), 0);
TryAugment(ilhs, &seen);
}
ElementMatcherPairs result;
for (size_t ilhs = 0; ilhs < left_.size(); ++ilhs) {
size_t irhs = left_[ilhs];
if (irhs == kUnused) continue;
result.push_back(ElementMatcherPair(ilhs, irhs));
}
return result;
}
private:
static const size_t kUnused = static_cast<size_t>(-1);
// Perform a depth-first search from left node ilhs to the sink. If a
// path is found, flow is added to the network by linking the left and
// right vector elements corresponding each segment of the path.
// Returns true if a path to sink was found, which means that a unit of
// flow was added to the network. The 'seen' vector elements correspond
// to right nodes and are marked to eliminate cycles from the search.
//
// Left nodes will only be explored at most once because they
// are accessible from at most one right node in the residual flow
// graph.
//
// Note that left_[ilhs] is the only element of left_ that TryAugment will
// potentially transition from kUnused to another value. Any other
// left_ element holding kUnused before TryAugment will be holding it
// when TryAugment returns.
//
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;
// There's an available edge from ilhs to irhs.
(*seen)[irhs] = 1;
// Next a search is performed to determine whether
// this edge is a dead end or leads to the sink.
//
// right_[irhs] == kUnused means that there is residual flow from
// right node irhs to the sink, so we can use that to finish this
// flow path and return success.
//
// Otherwise there is residual flow to some ilhs. We push flow
// along that path and call ourselves recursively to see if this
// ultimately leads to sink.
if (right_[irhs] == kUnused || TryAugment(right_[irhs], seen)) {
// Add flow from left_[ilhs] to right_[irhs].
left_[ilhs] = irhs;
right_[irhs] = ilhs;
return true;
}
}
return false;
}
const MatchMatrix* graph_; // not owned
// Each element of the left_ vector represents a left hand side node
// (i.e. an element) and each element of right_ is a right hand side
// node (i.e. a matcher). The values in the left_ vector indicate
// outflow from that node to a node on the the right_ side. The values
// in the right_ indicate inflow, and specify which left_ node is
// feeding that right_ node, if any. For example, left_[3] == 1 means
// there's a flow from element #3 to matcher #1. Such a flow would also
// be redundantly represented in the right_ vector as right_[1] == 3.
// Elements of left_ and right_ are either kUnused or mutually
// referent. Mutually referent means that left_[right_[i]] = i and
// right_[left_[i]] = i.
::std::vector<size_t> left_;
::std::vector<size_t> right_;
GTEST_DISALLOW_ASSIGN_(MaxBipartiteMatchState);
};
const size_t MaxBipartiteMatchState::kUnused;
GTEST_API_ ElementMatcherPairs
FindMaxBipartiteMatching(const MatchMatrix& g) {
return MaxBipartiteMatchState(g).Compute();
}
static void LogElementMatcherPairVec(const ElementMatcherPairs& pairs,
::std::ostream* stream) {
typedef ElementMatcherPairs::const_iterator Iter;
::std::ostream& os = *stream;
os << "{";
const char *sep = "";
for (Iter it = pairs.begin(); it != pairs.end(); ++it) {
os << sep << "\n ("
<< "element #" << it->first << ", "
<< "matcher #" << it->second << ")";
sep = ",";
}
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) {
char& b = matched_[SpaceIndex(ilhs, irhs)];
if (!b) {
b = 1;
return true;
}
b = 0;
}
}
return false;
}
void MatchMatrix::Randomize() {
for (size_t ilhs = 0; ilhs < LhsSize(); ++ilhs) {
for (size_t irhs = 0; irhs < RhsSize(); ++irhs) {
char& b = matched_[SpaceIndex(ilhs, irhs)];
b = static_cast<char>(rand() & 1); // NOLINT
}
}
}
string MatchMatrix::DebugString() const {
::std::stringstream ss;
const char *sep = "";
for (size_t i = 0; i < LhsSize(); ++i) {
ss << sep;
for (size_t j = 0; j < RhsSize(); ++j) {
ss << HasEdge(i, j);
}
sep = ";";
}
return ss.str();
}
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;
}
*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 << " ";
matcher_describers_[i]->DescribeTo(os);
sep = ", and\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;
}
*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 << " ";
matcher_describers_[i]->DescribeTo(os);
sep = ", and\n";
}
}
// Checks that all matchers match at least one element, and that all
// elements match at least one matcher. This enables faster matching
// 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<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);
for (size_t ilhs = 0; ilhs < matrix.LhsSize(); ilhs++) {
for (size_t irhs = 0; irhs < matrix.RhsSize(); irhs++) {
char matched = matrix.HasEdge(ilhs, irhs);
element_matched[ilhs] |= matched;
matcher_matched[irhs] |= matched;
}
}
{
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;
result = false;
if (listener->IsInterested()) {
*listener << sep << "matcher #" << mi << ": ";
matcher_describers_[mi]->DescribeTo(listener->stream());
sep = ",\n";
}
}
}
{
const char* sep =
"where the following elements don't match any matchers:\n";
const char* outer_sep = "";
if (!result) {
outer_sep = "\nand ";
}
for (size_t ei = 0; ei < element_matched.size(); ++ei) {
if (element_matched[ei])
continue;
result = false;
if (listener->IsInterested()) {
*listener << outer_sep << sep << "element #" << ei << ": "
<< element_printouts[ei];
sep = ",\n";
outer_sep = "";
}
}
}
return result;
}
} // namespace internal } // namespace internal
} // namespace testing } // namespace testing
...@@ -80,6 +80,9 @@ using testing::Value; ...@@ -80,6 +80,9 @@ using testing::Value;
using testing::internal::ElementsAreArrayMatcher; using testing::internal::ElementsAreArrayMatcher;
using testing::internal::string; using testing::internal::string;
// Evaluates to the number of elements in 'array'.
#define GMOCK_ARRAY_SIZE_(a) (sizeof(a) / sizeof(a[0]))
// Returns the description of the given matcher. // Returns the description of the given matcher.
template <typename T> template <typename T>
string Describe(const Matcher<T>& m) { string Describe(const Matcher<T>& m) {
...@@ -284,9 +287,6 @@ Matcher<int> GreaterThan(int n) { ...@@ -284,9 +287,6 @@ Matcher<int> GreaterThan(int n) {
// Tests for ElementsAre(). // Tests for ElementsAre().
// Evaluates to the number of elements in 'array'.
#define GMOCK_ARRAY_SIZE_(array) (sizeof(array)/sizeof(array[0]))
TEST(ElementsAreTest, CanDescribeExpectingNoElement) { TEST(ElementsAreTest, CanDescribeExpectingNoElement) {
Matcher<const vector<int>&> m = ElementsAre(); Matcher<const vector<int>&> m = ElementsAre();
EXPECT_EQ("is empty", Describe(m)); EXPECT_EQ("is empty", Describe(m));
...@@ -563,8 +563,8 @@ TEST(ElementsAreTest, MakesCopyOfArguments) { ...@@ -563,8 +563,8 @@ TEST(ElementsAreTest, MakesCopyOfArguments) {
int x = 1; int x = 1;
int y = 2; int y = 2;
// This should make a copy of x and y. // This should make a copy of x and y.
::testing::internal::ElementsAreMatcher2<int, int> polymorphic_matcher = ::testing::internal::ElementsAreMatcher<std::tr1::tuple<int, int> >
ElementsAre(x, y); polymorphic_matcher = ElementsAre(x, y);
// Changing x and y now shouldn't affect the meaning of the above matcher. // Changing x and y now shouldn't affect the meaning of the above matcher.
x = y = 0; x = y = 0;
const int array1[] = { 1, 2 }; const int array1[] = { 1, 2 };
...@@ -573,6 +573,7 @@ TEST(ElementsAreTest, MakesCopyOfArguments) { ...@@ -573,6 +573,7 @@ TEST(ElementsAreTest, MakesCopyOfArguments) {
EXPECT_THAT(array2, Not(polymorphic_matcher)); EXPECT_THAT(array2, Not(polymorphic_matcher));
} }
// Tests for ElementsAreArray(). Since ElementsAreArray() shares most // Tests for ElementsAreArray(). Since ElementsAreArray() shares most
// of the implementation with ElementsAre(), we don't test it as // of the implementation with ElementsAre(), we don't test it as
// thoroughly here. // thoroughly here.
......
...@@ -37,6 +37,7 @@ ...@@ -37,6 +37,7 @@
#include "gmock/gmock-more-matchers.h" #include "gmock/gmock-more-matchers.h"
#include <string.h> #include <string.h>
#include <time.h>
#include <deque> #include <deque>
#include <functional> #include <functional>
#include <iostream> #include <iostream>
...@@ -134,11 +135,14 @@ using testing::WhenSorted; ...@@ -134,11 +135,14 @@ using testing::WhenSorted;
using testing::WhenSortedBy; using testing::WhenSortedBy;
using testing::_; using testing::_;
using testing::internal::DummyMatchResultListener; using testing::internal::DummyMatchResultListener;
using testing::internal::ElementMatcherPair;
using testing::internal::ElementMatcherPairs;
using testing::internal::ExplainMatchFailureTupleTo; using testing::internal::ExplainMatchFailureTupleTo;
using testing::internal::FloatingEqMatcher; using testing::internal::FloatingEqMatcher;
using testing::internal::FormatMatcherDescription; using testing::internal::FormatMatcherDescription;
using testing::internal::IsReadableTypeName; using testing::internal::IsReadableTypeName;
using testing::internal::JoinAsTuple; using testing::internal::JoinAsTuple;
using testing::internal::MatchMatrix;
using testing::internal::RE; using testing::internal::RE;
using testing::internal::StreamMatchResultListener; using testing::internal::StreamMatchResultListener;
using testing::internal::StringMatchResultListener; using testing::internal::StringMatchResultListener;
...@@ -147,6 +151,9 @@ using testing::internal::linked_ptr; ...@@ -147,6 +151,9 @@ using testing::internal::linked_ptr;
using testing::internal::scoped_ptr; using testing::internal::scoped_ptr;
using testing::internal::string; using testing::internal::string;
// Evaluates to the number of elements in 'array'.
#define GMOCK_ARRAY_SIZE_(array) (sizeof(array) / sizeof(array[0]))
// For testing ExplainMatchResultTo(). // For testing ExplainMatchResultTo().
class GreaterThanMatcher : public MatcherInterface<int> { class GreaterThanMatcher : public MatcherInterface<int> {
public: public:
...@@ -4429,13 +4436,439 @@ TEST(WhenSortedTest, WorksForStreamlike) { ...@@ -4429,13 +4436,439 @@ TEST(WhenSortedTest, WorksForStreamlike) {
} }
TEST(WhenSortedTest, WorksForVectorConstRefMatcherOnStreamlike) { TEST(WhenSortedTest, WorksForVectorConstRefMatcherOnStreamlike) {
const int a[5] = { 2, 1, 4, 5, 3 }; const int a[] = { 2, 1, 4, 5, 3 };
Streamlike<int> s(a, a + 5); Streamlike<int> s(a, a + GMOCK_ARRAY_SIZE_(a));
Matcher<const std::vector<int>&> vector_match = ElementsAre(1, 2, 3, 4, 5); Matcher<const std::vector<int>&> vector_match = ElementsAre(1, 2, 3, 4, 5);
EXPECT_THAT(s, WhenSorted(vector_match)); EXPECT_THAT(s, WhenSorted(vector_match));
EXPECT_THAT(s, Not(WhenSorted(ElementsAre(2, 1, 4, 5, 3)))); EXPECT_THAT(s, Not(WhenSorted(ElementsAre(2, 1, 4, 5, 3))));
} }
// Tests for UnorderedElementsAreArray()
TEST(UnorderedElementsAreArrayTest, SucceedsWhenExpected) {
const int a[] = { 0, 1, 2, 3, 4 };
std::vector<int> s(a, a + GMOCK_ARRAY_SIZE_(a));
do {
StringMatchResultListener listener;
EXPECT_TRUE(ExplainMatchResult(UnorderedElementsAreArray(a),
s, &listener)) << listener.str();
} while (std::next_permutation(s.begin(), s.end()));
}
TEST(UnorderedElementsAreArrayTest, VectorBool) {
const bool a[] = { 0, 1, 0, 1, 1 };
const bool b[] = { 1, 0, 1, 1, 0 };
std::vector<bool> expected(a, a + GMOCK_ARRAY_SIZE_(a));
std::vector<bool> actual(b, b + GMOCK_ARRAY_SIZE_(b));
StringMatchResultListener listener;
EXPECT_TRUE(ExplainMatchResult(UnorderedElementsAreArray(expected),
actual, &listener)) << listener.str();
}
class UnorderedElementsAreTest : public testing::Test {
protected:
typedef std::vector<int> IntVec;
};
TEST_F(UnorderedElementsAreTest, SucceedsWhenExpected) {
const int a[] = { 1, 2, 3 };
std::vector<int> s(a, a + GMOCK_ARRAY_SIZE_(a));
do {
StringMatchResultListener listener;
EXPECT_TRUE(ExplainMatchResult(UnorderedElementsAre(1, 2, 3),
s, &listener)) << listener.str();
} while (std::next_permutation(s.begin(), s.end()));
}
TEST_F(UnorderedElementsAreTest, FailsWhenAnElementMatchesNoMatcher) {
const int a[] = { 1, 2, 3 };
std::vector<int> s(a, a + GMOCK_ARRAY_SIZE_(a));
std::vector<Matcher<int> > mv;
mv.push_back(1);
mv.push_back(2);
mv.push_back(2);
// The element with value '3' matches nothing: fail fast.
StringMatchResultListener listener;
EXPECT_FALSE(ExplainMatchResult(UnorderedElementsAreArray(mv),
s, &listener)) << listener.str();
}
// One naive implementation of the matcher runs in O(N!) time, which is too
// slow for many real-world inputs. This test shows that our matcher can match
// 100 inputs very quickly (a few milliseconds). An O(100!) is 10^158
// iterations and obviously effectively incomputable.
// [ RUN ] UnorderedElementsAreTest.Performance
// [ OK ] UnorderedElementsAreTest.Performance (4 ms)
TEST_F(UnorderedElementsAreTest, Performance) {
std::vector<int> s;
std::vector<Matcher<int> > mv;
for (int i = 0; i < 100; ++i) {
s.push_back(i);
mv.push_back(_);
}
mv[50] = Eq(0);
StringMatchResultListener listener;
EXPECT_TRUE(ExplainMatchResult(UnorderedElementsAreArray(mv),
s, &listener)) << listener.str();
}
// Another variant of 'Performance' with similar expectations.
// [ RUN ] UnorderedElementsAreTest.PerformanceHalfStrict
// [ OK ] UnorderedElementsAreTest.PerformanceHalfStrict (4 ms)
TEST_F(UnorderedElementsAreTest, PerformanceHalfStrict) {
std::vector<int> s;
std::vector<Matcher<int> > mv;
for (int i = 0; i < 100; ++i) {
s.push_back(i);
if (i & 1) {
mv.push_back(_);
} else {
mv.push_back(i);
}
}
StringMatchResultListener listener;
EXPECT_TRUE(ExplainMatchResult(UnorderedElementsAreArray(mv),
s, &listener)) << listener.str();
}
TEST_F(UnorderedElementsAreTest, FailMessageCountWrong) {
std::vector<int> v;
v.push_back(4);
StringMatchResultListener listener;
EXPECT_FALSE(ExplainMatchResult(UnorderedElementsAre(1, 2, 3),
v, &listener)) << listener.str();
EXPECT_THAT(listener.str(), Eq("which has 1 element"));
}
TEST_F(UnorderedElementsAreTest, FailMessageCountWrongZero) {
std::vector<int> v;
StringMatchResultListener listener;
EXPECT_FALSE(ExplainMatchResult(UnorderedElementsAre(1, 2, 3),
v, &listener)) << listener.str();
EXPECT_THAT(listener.str(), Eq(""));
}
TEST_F(UnorderedElementsAreTest, FailMessageUnmatchedMatchers) {
std::vector<int> v;
v.push_back(1);
v.push_back(1);
StringMatchResultListener listener;
EXPECT_FALSE(ExplainMatchResult(UnorderedElementsAre(1, 2),
v, &listener)) << listener.str();
EXPECT_THAT(
listener.str(),
Eq("where the following matchers don't match any elements:\n"
"matcher #1: is equal to 2"));
}
TEST_F(UnorderedElementsAreTest, FailMessageUnmatchedElements) {
std::vector<int> v;
v.push_back(1);
v.push_back(2);
StringMatchResultListener listener;
EXPECT_FALSE(ExplainMatchResult(UnorderedElementsAre(1, 1),
v, &listener)) << listener.str();
EXPECT_THAT(
listener.str(),
Eq("where the following elements don't match any matchers:\n"
"element #1: 2"));
}
TEST_F(UnorderedElementsAreTest, FailMessageUnmatchedMatcherAndElement) {
std::vector<int> v;
v.push_back(2);
v.push_back(3);
StringMatchResultListener listener;
EXPECT_FALSE(ExplainMatchResult(UnorderedElementsAre(1, 2),
v, &listener)) << listener.str();
EXPECT_THAT(
listener.str(),
Eq("where"
" the following matchers don't match any elements:\n"
"matcher #0: is equal to 1\n"
"and"
" where"
" the following elements don't match any matchers:\n"
"element #1: 3"));
}
// Test helper for formatting element, matcher index pairs in expectations.
static string EMString(int element, int matcher) {
stringstream ss;
ss << "(element #" << element << ", matcher #" << matcher << ")";
return ss.str();
}
TEST_F(UnorderedElementsAreTest, FailMessageImperfectMatchOnly) {
// A situation where all elements and matchers have a match
// associated with them, but the max matching is not perfect.
std::vector<string> v;
v.push_back("a");
v.push_back("b");
v.push_back("c");
StringMatchResultListener listener;
EXPECT_FALSE(ExplainMatchResult(
UnorderedElementsAre("a", "a", AnyOf("b", "c")), v, &listener))
<< listener.str();
string prefix =
"where no permutation of the elements can satisfy all matchers, "
"and the closest match is 2 of 3 matchers with the "
"pairings:\n";
// We have to be a bit loose here, because there are 4 valid max matches.
EXPECT_THAT(
listener.str(),
AnyOf(prefix + "{\n " + EMString(0, 0) +
",\n " + EMString(1, 2) + "\n}",
prefix + "{\n " + EMString(0, 1) +
",\n " + EMString(1, 2) + "\n}",
prefix + "{\n " + EMString(0, 0) +
",\n " + EMString(2, 2) + "\n}",
prefix + "{\n " + EMString(0, 1) +
",\n " + EMString(2, 2) + "\n}"));
}
TEST_F(UnorderedElementsAreTest, Describe) {
EXPECT_THAT(Describe<IntVec>(UnorderedElementsAre()),
Eq("is empty"));
EXPECT_THAT(
Describe<IntVec>(UnorderedElementsAre(345)),
Eq("has 1 element and that element is equal to 345"));
EXPECT_THAT(
Describe<IntVec>(UnorderedElementsAre(111, 222, 333)),
Eq("has 3 elements and there exists some permutation "
"of elements such that:\n"
" - element #0 is equal to 111, and\n"
" - element #1 is equal to 222, and\n"
" - element #2 is equal to 333"));
}
TEST_F(UnorderedElementsAreTest, DescribeNegation) {
EXPECT_THAT(DescribeNegation<IntVec>(UnorderedElementsAre()),
Eq("isn't empty"));
EXPECT_THAT(
DescribeNegation<IntVec>(UnorderedElementsAre(345)),
Eq("doesn't have 1 element, or has 1 element that isn't equal to 345"));
EXPECT_THAT(
DescribeNegation<IntVec>(UnorderedElementsAre(123, 234, 345)),
Eq("doesn't have 3 elements, or there exists no permutation "
"of elements such that:\n"
" - element #0 is equal to 123, and\n"
" - element #1 is equal to 234, and\n"
" - element #2 is equal to 345"));
}
namespace {
// Used as a check on the more complex max flow method used in the
// real testing::internal::FindMaxBipartiteMatching. This method is
// compatible but runs in worst-case factorial time, so we only
// use it in testing for small problem sizes.
template <typename Graph>
class BacktrackingMaxBPMState {
public:
// Does not take ownership of 'g'.
explicit BacktrackingMaxBPMState(const Graph* g) : graph_(g) { }
ElementMatcherPairs Compute() {
if (graph_->LhsSize() == 0 || graph_->RhsSize() == 0) {
return best_so_far_;
}
lhs_used_.assign(graph_->LhsSize(), kUnused);
rhs_used_.assign(graph_->RhsSize(), kUnused);
for (size_t irhs = 0; irhs < graph_->RhsSize(); ++irhs) {
matches_.clear();
RecurseInto(irhs);
if (best_so_far_.size() == graph_->RhsSize())
break;
}
return best_so_far_;
}
private:
static const size_t kUnused = static_cast<size_t>(-1);
void PushMatch(size_t lhs, size_t rhs) {
matches_.push_back(ElementMatcherPair(lhs, rhs));
lhs_used_[lhs] = rhs;
rhs_used_[rhs] = lhs;
if (matches_.size() > best_so_far_.size()) {
best_so_far_ = matches_;
}
}
void PopMatch() {
const ElementMatcherPair& back = matches_.back();
lhs_used_[back.first] = kUnused;
rhs_used_[back.second] = kUnused;
matches_.pop_back();
}
bool RecurseInto(size_t irhs) {
if (rhs_used_[irhs] != kUnused) {
return true;
}
for (size_t ilhs = 0; ilhs < graph_->LhsSize(); ++ilhs) {
if (lhs_used_[ilhs] != kUnused) {
continue;
}
if (!graph_->HasEdge(ilhs, irhs)) {
continue;
}
PushMatch(ilhs, irhs);
if (best_so_far_.size() == graph_->RhsSize()) {
return false;
}
for (size_t mi = irhs + 1; mi < graph_->RhsSize(); ++mi) {
if (!RecurseInto(mi)) return false;
}
PopMatch();
}
return true;
}
const Graph* graph_; // not owned
std::vector<size_t> lhs_used_;
std::vector<size_t> rhs_used_;
ElementMatcherPairs matches_;
ElementMatcherPairs best_so_far_;
};
template <typename Graph>
const size_t BacktrackingMaxBPMState<Graph>::kUnused;
} // namespace
// Implement a simple backtracking algorithm to determine if it is possible
// to find one element per matcher, without reusing elements.
template <typename Graph>
ElementMatcherPairs
FindBacktrackingMaxBPM(const Graph& g) {
return BacktrackingMaxBPMState<Graph>(&g).Compute();
}
class BacktrackingBPMTest : public ::testing::Test { };
// Tests the MaxBipartiteMatching algorithm with square matrices.
// The single int param is the # of nodes on each of the left and right sides.
class BipartiteTest : public ::testing::TestWithParam<int> { };
// Verify all match graphs up to some moderate number of edges.
TEST_P(BipartiteTest, Exhaustive) {
int nodes = GetParam();
MatchMatrix graph(nodes, nodes);
do {
ElementMatcherPairs matches =
internal::FindMaxBipartiteMatching(graph);
EXPECT_EQ(FindBacktrackingMaxBPM(graph).size(), matches.size())
<< "graph: " << graph.DebugString();
// Check that all elements of matches are in the graph.
// Check that elements of first and second are unique.
std::vector<bool> seen_element(graph.LhsSize());
std::vector<bool> seen_matcher(graph.RhsSize());
SCOPED_TRACE(PrintToString(matches));
for (size_t i = 0; i < matches.size(); ++i) {
size_t ilhs = matches[i].first;
size_t irhs = matches[i].second;
EXPECT_TRUE(graph.HasEdge(ilhs, irhs));
EXPECT_FALSE(seen_element[ilhs]);
EXPECT_FALSE(seen_matcher[irhs]);
seen_element[ilhs] = true;
seen_matcher[irhs] = true;
}
} while (graph.NextGraph());
}
INSTANTIATE_TEST_CASE_P(AllGraphs, BipartiteTest,
::testing::Range(0, 5));
// Parameterized by a pair interpreted as (LhsSize, RhsSize).
class BipartiteNonSquareTest
: public ::testing::TestWithParam<std::pair<size_t, size_t> > {
};
TEST_F(BipartiteNonSquareTest, SimpleBacktracking) {
// .......
// 0:-----\ :
// 1:---\ | :
// 2:---\ | :
// 3:-\ | | :
// :.......:
// 0 1 2
MatchMatrix g(4, 3);
static const int kEdges[][2] = { {0, 2}, {1, 1}, {2, 1}, {3, 0} };
for (size_t i = 0; i < GMOCK_ARRAY_SIZE_(kEdges); ++i) {
g.SetEdge(kEdges[i][0], kEdges[i][1], true);
}
EXPECT_THAT(FindBacktrackingMaxBPM(g),
ElementsAre(Pair(3, 0),
Pair(AnyOf(1, 2), 1),
Pair(0, 2))) << g.DebugString();
}
// Verify a few nonsquare matrices.
TEST_P(BipartiteNonSquareTest, Exhaustive) {
size_t nlhs = GetParam().first;
size_t nrhs = GetParam().second;
MatchMatrix graph(nlhs, nrhs);
do {
EXPECT_EQ(FindBacktrackingMaxBPM(graph).size(),
internal::FindMaxBipartiteMatching(graph).size())
<< "graph: " << graph.DebugString()
<< "\nbacktracking: "
<< PrintToString(FindBacktrackingMaxBPM(graph))
<< "\nmax flow: "
<< PrintToString(internal::FindMaxBipartiteMatching(graph));
} while (graph.NextGraph());
}
INSTANTIATE_TEST_CASE_P(AllGraphs, BipartiteNonSquareTest,
testing::Values(
std::make_pair(1, 2),
std::make_pair(2, 1),
std::make_pair(3, 2),
std::make_pair(2, 3),
std::make_pair(4, 1),
std::make_pair(1, 4),
std::make_pair(4, 3),
std::make_pair(3, 4)));
class BipartiteRandomTest
: public ::testing::TestWithParam<std::pair<int, int> > {
};
// Verifies a large sample of larger graphs.
TEST_P(BipartiteRandomTest, LargerNets) {
int nodes = GetParam().first;
int iters = GetParam().second;
MatchMatrix graph(nodes, nodes);
testing::internal::Int32 seed = GTEST_FLAG(random_seed);
if (seed == 0) {
seed = static_cast<testing::internal::Int32>(time(NULL));
}
for (; iters > 0; --iters, ++seed) {
srand(static_cast<int>(seed));
graph.Randomize();
EXPECT_EQ(FindBacktrackingMaxBPM(graph).size(),
internal::FindMaxBipartiteMatching(graph).size())
<< " graph: " << graph.DebugString()
<< "\nTo reproduce the failure, rerun the test with the flag"
" --" << GTEST_FLAG_PREFIX_ << "random_seed=" << seed;
}
}
// Test argument is a std::pair<int, int> representing (nodes, iters).
INSTANTIATE_TEST_CASE_P(Samples, BipartiteRandomTest,
testing::Values(
std::make_pair(5, 10000),
std::make_pair(6, 5000),
std::make_pair(7, 2000),
std::make_pair(8, 500),
std::make_pair(9, 100)));
// Tests IsReadableTypeName(). // Tests IsReadableTypeName().
TEST(IsReadableTypeNameTest, ReturnsTrueForShortNames) { TEST(IsReadableTypeNameTest, ReturnsTrueForShortNames) {
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment