Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
yangql
googletest
Commits
fb25d539
Commit
fb25d539
authored
Jul 28, 2013
by
zhanyong.wan
Browse files
Adds matchers UnorderedElementsAre[Array]() (by Billy Donahue); pulls in
gtest r660.
parent
2989703e
Changes
7
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
1664 additions
and
605 deletions
+1664
-605
CHANGES
CHANGES
+2
-1
include/gmock/gmock-generated-matchers.h
include/gmock/gmock-generated-matchers.h
+382
-446
include/gmock/gmock-generated-matchers.h.pump
include/gmock/gmock-generated-matchers.h.pump
+45
-112
include/gmock/gmock-matchers.h
include/gmock/gmock-matchers.h
+433
-39
src/gmock-matchers.cc
src/gmock-matchers.cc
+361
-0
test/gmock-generated-matchers_test.cc
test/gmock-generated-matchers_test.cc
+6
-5
test/gmock-matchers_test.cc
test/gmock-matchers_test.cc
+435
-2
No files found.
CHANGES
View file @
fb25d539
...
@@ -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()
...
...
include/gmock/gmock-generated-matchers.h
View file @
fb25d539
This diff is collapsed.
Click to expand it.
include/gmock/gmock-generated-matchers.h.pump
View file @
fb25d539
...
@@ -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(e
0
, e
1
, ...
,
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.
...
...
include/gmock/gmock-matchers.h
View file @
fb25d539
This diff is collapsed.
Click to expand it.
src/gmock-matchers.cc
View file @
fb25d539
...
@@ -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
=
"
\n
and "
;
}
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
test/gmock-generated-matchers_test.cc
View file @
fb25d539
...
@@ -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
::
ElementsAreMatcher
2
<
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.
...
...
test/gmock-matchers_test.cc
View file @
fb25d539
...
@@ -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
()
<<
"
\n
backtracking: "
<<
PrintToString
(
FindBacktrackingMaxBPM
(
graph
))
<<
"
\n
max 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
()
<<
"
\n
To 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
)
{
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment