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
2661c682
Commit
2661c682
authored
Jun 09, 2009
by
zhanyong.wan
Browse files
Implements the Args<k1, ..., kn>(m) matcher.
parent
240fe5a0
Changes
8
Show whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
597 additions
and
29 deletions
+597
-29
include/gmock/gmock-generated-matchers.h
include/gmock/gmock-generated-matchers.h
+323
-0
include/gmock/gmock-generated-matchers.h.pump
include/gmock/gmock-generated-matchers.h.pump
+141
-0
include/gmock/gmock-matchers.h
include/gmock/gmock-matchers.h
+9
-9
include/gmock/gmock-spec-builders.h
include/gmock/gmock-spec-builders.h
+2
-2
test/gmock-generated-matchers_test.cc
test/gmock-generated-matchers_test.cc
+107
-2
test/gmock-internal-utils_test.cc
test/gmock-internal-utils_test.cc
+1
-1
test/gmock-matchers_test.cc
test/gmock-matchers_test.cc
+10
-11
test/gmock_output_test_golden.txt
test/gmock_output_test_golden.txt
+4
-4
No files found.
include/gmock/gmock-generated-matchers.h
View file @
2661c682
...
...
@@ -45,6 +45,251 @@
namespace
testing
{
namespace
internal
{
// The type of the i-th (0-based) field of Tuple.
#define GMOCK_FIELD_TYPE_(Tuple, i) \
typename ::std::tr1::tuple_element<i, Tuple>::type
// TupleFields<Tuple, k0, ..., kn> is for selecting fields from a
// tuple of type Tuple. It has two members:
//
// type: a tuple type whose i-th field is the ki-th field of Tuple.
// GetSelectedFields(t): returns fields k0, ..., and kn of t as a tuple.
//
// For example, in class TupleFields<tuple<bool, char, int>, 2, 0>, we have:
//
// type is tuple<int, bool>, and
// GetSelectedFields(make_tuple(true, 'a', 42)) is (42, true).
template
<
class
Tuple
,
int
k0
=
-
1
,
int
k1
=
-
1
,
int
k2
=
-
1
,
int
k3
=
-
1
,
int
k4
=
-
1
,
int
k5
=
-
1
,
int
k6
=
-
1
,
int
k7
=
-
1
,
int
k8
=
-
1
,
int
k9
=
-
1
>
class
TupleFields
;
// This generic version is used when there are 10 selectors.
template
<
class
Tuple
,
int
k0
,
int
k1
,
int
k2
,
int
k3
,
int
k4
,
int
k5
,
int
k6
,
int
k7
,
int
k8
,
int
k9
>
class
TupleFields
{
public:
typedef
::
std
::
tr1
::
tuple
<
GMOCK_FIELD_TYPE_
(
Tuple
,
k0
),
GMOCK_FIELD_TYPE_
(
Tuple
,
k1
),
GMOCK_FIELD_TYPE_
(
Tuple
,
k2
),
GMOCK_FIELD_TYPE_
(
Tuple
,
k3
),
GMOCK_FIELD_TYPE_
(
Tuple
,
k4
),
GMOCK_FIELD_TYPE_
(
Tuple
,
k5
),
GMOCK_FIELD_TYPE_
(
Tuple
,
k6
),
GMOCK_FIELD_TYPE_
(
Tuple
,
k7
),
GMOCK_FIELD_TYPE_
(
Tuple
,
k8
),
GMOCK_FIELD_TYPE_
(
Tuple
,
k9
)
>
type
;
static
type
GetSelectedFields
(
const
Tuple
&
t
)
{
using
::
std
::
tr1
::
get
;
return
type
(
get
<
k0
>
(
t
),
get
<
k1
>
(
t
),
get
<
k2
>
(
t
),
get
<
k3
>
(
t
),
get
<
k4
>
(
t
),
get
<
k5
>
(
t
),
get
<
k6
>
(
t
),
get
<
k7
>
(
t
),
get
<
k8
>
(
t
),
get
<
k9
>
(
t
));
}
};
// The following specialization is used for 0 ~ 9 selectors.
template
<
class
Tuple
>
class
TupleFields
<
Tuple
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
>
{
public:
typedef
::
std
::
tr1
::
tuple
<>
type
;
static
type
GetSelectedFields
(
const
Tuple
&
t
)
{
using
::
std
::
tr1
::
get
;
return
type
();
}
};
template
<
class
Tuple
,
int
k0
>
class
TupleFields
<
Tuple
,
k0
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
>
{
public:
typedef
::
std
::
tr1
::
tuple
<
GMOCK_FIELD_TYPE_
(
Tuple
,
k0
)
>
type
;
static
type
GetSelectedFields
(
const
Tuple
&
t
)
{
using
::
std
::
tr1
::
get
;
return
type
(
get
<
k0
>
(
t
));
}
};
template
<
class
Tuple
,
int
k0
,
int
k1
>
class
TupleFields
<
Tuple
,
k0
,
k1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
>
{
public:
typedef
::
std
::
tr1
::
tuple
<
GMOCK_FIELD_TYPE_
(
Tuple
,
k0
),
GMOCK_FIELD_TYPE_
(
Tuple
,
k1
)
>
type
;
static
type
GetSelectedFields
(
const
Tuple
&
t
)
{
using
::
std
::
tr1
::
get
;
return
type
(
get
<
k0
>
(
t
),
get
<
k1
>
(
t
));
}
};
template
<
class
Tuple
,
int
k0
,
int
k1
,
int
k2
>
class
TupleFields
<
Tuple
,
k0
,
k1
,
k2
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
>
{
public:
typedef
::
std
::
tr1
::
tuple
<
GMOCK_FIELD_TYPE_
(
Tuple
,
k0
),
GMOCK_FIELD_TYPE_
(
Tuple
,
k1
),
GMOCK_FIELD_TYPE_
(
Tuple
,
k2
)
>
type
;
static
type
GetSelectedFields
(
const
Tuple
&
t
)
{
using
::
std
::
tr1
::
get
;
return
type
(
get
<
k0
>
(
t
),
get
<
k1
>
(
t
),
get
<
k2
>
(
t
));
}
};
template
<
class
Tuple
,
int
k0
,
int
k1
,
int
k2
,
int
k3
>
class
TupleFields
<
Tuple
,
k0
,
k1
,
k2
,
k3
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
>
{
public:
typedef
::
std
::
tr1
::
tuple
<
GMOCK_FIELD_TYPE_
(
Tuple
,
k0
),
GMOCK_FIELD_TYPE_
(
Tuple
,
k1
),
GMOCK_FIELD_TYPE_
(
Tuple
,
k2
),
GMOCK_FIELD_TYPE_
(
Tuple
,
k3
)
>
type
;
static
type
GetSelectedFields
(
const
Tuple
&
t
)
{
using
::
std
::
tr1
::
get
;
return
type
(
get
<
k0
>
(
t
),
get
<
k1
>
(
t
),
get
<
k2
>
(
t
),
get
<
k3
>
(
t
));
}
};
template
<
class
Tuple
,
int
k0
,
int
k1
,
int
k2
,
int
k3
,
int
k4
>
class
TupleFields
<
Tuple
,
k0
,
k1
,
k2
,
k3
,
k4
,
-
1
,
-
1
,
-
1
,
-
1
,
-
1
>
{
public:
typedef
::
std
::
tr1
::
tuple
<
GMOCK_FIELD_TYPE_
(
Tuple
,
k0
),
GMOCK_FIELD_TYPE_
(
Tuple
,
k1
),
GMOCK_FIELD_TYPE_
(
Tuple
,
k2
),
GMOCK_FIELD_TYPE_
(
Tuple
,
k3
),
GMOCK_FIELD_TYPE_
(
Tuple
,
k4
)
>
type
;
static
type
GetSelectedFields
(
const
Tuple
&
t
)
{
using
::
std
::
tr1
::
get
;
return
type
(
get
<
k0
>
(
t
),
get
<
k1
>
(
t
),
get
<
k2
>
(
t
),
get
<
k3
>
(
t
),
get
<
k4
>
(
t
));
}
};
template
<
class
Tuple
,
int
k0
,
int
k1
,
int
k2
,
int
k3
,
int
k4
,
int
k5
>
class
TupleFields
<
Tuple
,
k0
,
k1
,
k2
,
k3
,
k4
,
k5
,
-
1
,
-
1
,
-
1
,
-
1
>
{
public:
typedef
::
std
::
tr1
::
tuple
<
GMOCK_FIELD_TYPE_
(
Tuple
,
k0
),
GMOCK_FIELD_TYPE_
(
Tuple
,
k1
),
GMOCK_FIELD_TYPE_
(
Tuple
,
k2
),
GMOCK_FIELD_TYPE_
(
Tuple
,
k3
),
GMOCK_FIELD_TYPE_
(
Tuple
,
k4
),
GMOCK_FIELD_TYPE_
(
Tuple
,
k5
)
>
type
;
static
type
GetSelectedFields
(
const
Tuple
&
t
)
{
using
::
std
::
tr1
::
get
;
return
type
(
get
<
k0
>
(
t
),
get
<
k1
>
(
t
),
get
<
k2
>
(
t
),
get
<
k3
>
(
t
),
get
<
k4
>
(
t
),
get
<
k5
>
(
t
));
}
};
template
<
class
Tuple
,
int
k0
,
int
k1
,
int
k2
,
int
k3
,
int
k4
,
int
k5
,
int
k6
>
class
TupleFields
<
Tuple
,
k0
,
k1
,
k2
,
k3
,
k4
,
k5
,
k6
,
-
1
,
-
1
,
-
1
>
{
public:
typedef
::
std
::
tr1
::
tuple
<
GMOCK_FIELD_TYPE_
(
Tuple
,
k0
),
GMOCK_FIELD_TYPE_
(
Tuple
,
k1
),
GMOCK_FIELD_TYPE_
(
Tuple
,
k2
),
GMOCK_FIELD_TYPE_
(
Tuple
,
k3
),
GMOCK_FIELD_TYPE_
(
Tuple
,
k4
),
GMOCK_FIELD_TYPE_
(
Tuple
,
k5
),
GMOCK_FIELD_TYPE_
(
Tuple
,
k6
)
>
type
;
static
type
GetSelectedFields
(
const
Tuple
&
t
)
{
using
::
std
::
tr1
::
get
;
return
type
(
get
<
k0
>
(
t
),
get
<
k1
>
(
t
),
get
<
k2
>
(
t
),
get
<
k3
>
(
t
),
get
<
k4
>
(
t
),
get
<
k5
>
(
t
),
get
<
k6
>
(
t
));
}
};
template
<
class
Tuple
,
int
k0
,
int
k1
,
int
k2
,
int
k3
,
int
k4
,
int
k5
,
int
k6
,
int
k7
>
class
TupleFields
<
Tuple
,
k0
,
k1
,
k2
,
k3
,
k4
,
k5
,
k6
,
k7
,
-
1
,
-
1
>
{
public:
typedef
::
std
::
tr1
::
tuple
<
GMOCK_FIELD_TYPE_
(
Tuple
,
k0
),
GMOCK_FIELD_TYPE_
(
Tuple
,
k1
),
GMOCK_FIELD_TYPE_
(
Tuple
,
k2
),
GMOCK_FIELD_TYPE_
(
Tuple
,
k3
),
GMOCK_FIELD_TYPE_
(
Tuple
,
k4
),
GMOCK_FIELD_TYPE_
(
Tuple
,
k5
),
GMOCK_FIELD_TYPE_
(
Tuple
,
k6
),
GMOCK_FIELD_TYPE_
(
Tuple
,
k7
)
>
type
;
static
type
GetSelectedFields
(
const
Tuple
&
t
)
{
using
::
std
::
tr1
::
get
;
return
type
(
get
<
k0
>
(
t
),
get
<
k1
>
(
t
),
get
<
k2
>
(
t
),
get
<
k3
>
(
t
),
get
<
k4
>
(
t
),
get
<
k5
>
(
t
),
get
<
k6
>
(
t
),
get
<
k7
>
(
t
));
}
};
template
<
class
Tuple
,
int
k0
,
int
k1
,
int
k2
,
int
k3
,
int
k4
,
int
k5
,
int
k6
,
int
k7
,
int
k8
>
class
TupleFields
<
Tuple
,
k0
,
k1
,
k2
,
k3
,
k4
,
k5
,
k6
,
k7
,
k8
,
-
1
>
{
public:
typedef
::
std
::
tr1
::
tuple
<
GMOCK_FIELD_TYPE_
(
Tuple
,
k0
),
GMOCK_FIELD_TYPE_
(
Tuple
,
k1
),
GMOCK_FIELD_TYPE_
(
Tuple
,
k2
),
GMOCK_FIELD_TYPE_
(
Tuple
,
k3
),
GMOCK_FIELD_TYPE_
(
Tuple
,
k4
),
GMOCK_FIELD_TYPE_
(
Tuple
,
k5
),
GMOCK_FIELD_TYPE_
(
Tuple
,
k6
),
GMOCK_FIELD_TYPE_
(
Tuple
,
k7
),
GMOCK_FIELD_TYPE_
(
Tuple
,
k8
)
>
type
;
static
type
GetSelectedFields
(
const
Tuple
&
t
)
{
using
::
std
::
tr1
::
get
;
return
type
(
get
<
k0
>
(
t
),
get
<
k1
>
(
t
),
get
<
k2
>
(
t
),
get
<
k3
>
(
t
),
get
<
k4
>
(
t
),
get
<
k5
>
(
t
),
get
<
k6
>
(
t
),
get
<
k7
>
(
t
),
get
<
k8
>
(
t
));
}
};
#undef GMOCK_FIELD_TYPE_
// Implements the Args() matcher.
template
<
class
ArgsTuple
,
int
k0
=
-
1
,
int
k1
=
-
1
,
int
k2
=
-
1
,
int
k3
=
-
1
,
int
k4
=
-
1
,
int
k5
=
-
1
,
int
k6
=
-
1
,
int
k7
=
-
1
,
int
k8
=
-
1
,
int
k9
=
-
1
>
class
ArgsMatcherImpl
:
public
MatcherInterface
<
ArgsTuple
>
{
public:
// ArgsTuple may have top-level const or reference modifiers.
typedef
GMOCK_REMOVE_CONST_
(
GMOCK_REMOVE_REFERENCE_
(
ArgsTuple
))
RawArgsTuple
;
typedef
typename
internal
::
TupleFields
<
RawArgsTuple
,
k0
,
k1
,
k2
,
k3
,
k4
,
k5
,
k6
,
k7
,
k8
,
k9
>::
type
SelectedArgs
;
typedef
Matcher
<
const
SelectedArgs
&>
MonomorphicInnerMatcher
;
template
<
typename
InnerMatcher
>
explicit
ArgsMatcherImpl
(
const
InnerMatcher
&
inner_matcher
)
:
inner_matcher_
(
SafeMatcherCast
<
const
SelectedArgs
&>
(
inner_matcher
))
{}
virtual
bool
Matches
(
ArgsTuple
args
)
const
{
return
inner_matcher_
.
Matches
(
GetSelectedArgs
(
args
));
}
virtual
void
DescribeTo
(
::
std
::
ostream
*
os
)
const
{
PrintIndices
(
os
);
inner_matcher_
.
DescribeTo
(
os
);
}
virtual
void
DescribeNegationTo
(
::
std
::
ostream
*
os
)
const
{
PrintIndices
(
os
);
inner_matcher_
.
DescribeNegationTo
(
os
);
}
virtual
void
ExplainMatchResultTo
(
ArgsTuple
args
,
::
std
::
ostream
*
os
)
const
{
inner_matcher_
.
ExplainMatchResultTo
(
GetSelectedArgs
(
args
),
os
);
}
private:
static
SelectedArgs
GetSelectedArgs
(
ArgsTuple
args
)
{
return
TupleFields
<
RawArgsTuple
,
k0
,
k1
,
k2
,
k3
,
k4
,
k5
,
k6
,
k7
,
k8
,
k9
>::
GetSelectedFields
(
args
);
}
// Prints the indices of the selected fields.
static
void
PrintIndices
(
::
std
::
ostream
*
os
)
{
*
os
<<
"are a tuple whose fields ("
;
const
int
indices
[
10
]
=
{
k0
,
k1
,
k2
,
k3
,
k4
,
k5
,
k6
,
k7
,
k8
,
k9
};
for
(
int
i
=
0
;
i
<
10
;
i
++
)
{
if
(
indices
[
i
]
<
0
)
break
;
if
(
i
>=
1
)
*
os
<<
", "
;
*
os
<<
"#"
<<
indices
[
i
];
}
*
os
<<
") "
;
}
const
MonomorphicInnerMatcher
inner_matcher_
;
};
template
<
class
InnerMatcher
,
int
k0
=
-
1
,
int
k1
=
-
1
,
int
k2
=
-
1
,
int
k3
=
-
1
,
int
k4
=
-
1
,
int
k5
=
-
1
,
int
k6
=
-
1
,
int
k7
=
-
1
,
int
k8
=
-
1
,
int
k9
=
-
1
>
class
ArgsMatcher
{
public:
explicit
ArgsMatcher
(
const
InnerMatcher
&
inner_matcher
)
:
inner_matcher_
(
inner_matcher
)
{}
template
<
typename
ArgsTuple
>
operator
Matcher
<
ArgsTuple
>
()
const
{
return
MakeMatcher
(
new
ArgsMatcherImpl
<
ArgsTuple
,
k0
,
k1
,
k2
,
k3
,
k4
,
k5
,
k6
,
k7
,
k8
,
k9
>
(
inner_matcher_
));
}
const
InnerMatcher
inner_matcher_
;
};
// Implements ElementsAre() and ElementsAreArray().
template
<
typename
Container
>
class
ElementsAreMatcherImpl
:
public
MatcherInterface
<
Container
>
{
...
...
@@ -567,6 +812,84 @@ class ElementsAreArrayMatcher {
}
// namespace internal
// Args<N1, N2, ..., Nk>(a_matcher) matches a tuple if the selected
// fields of it matches a_matcher. C++ doesn't support default
// arguments for function templates, so we have to overload it.
template
<
typename
InnerMatcher
>
inline
internal
::
ArgsMatcher
<
InnerMatcher
>
Args
(
const
InnerMatcher
&
matcher
)
{
return
internal
::
ArgsMatcher
<
InnerMatcher
>
(
matcher
);
}
template
<
int
k1
,
typename
InnerMatcher
>
inline
internal
::
ArgsMatcher
<
InnerMatcher
,
k1
>
Args
(
const
InnerMatcher
&
matcher
)
{
return
internal
::
ArgsMatcher
<
InnerMatcher
,
k1
>
(
matcher
);
}
template
<
int
k1
,
int
k2
,
typename
InnerMatcher
>
inline
internal
::
ArgsMatcher
<
InnerMatcher
,
k1
,
k2
>
Args
(
const
InnerMatcher
&
matcher
)
{
return
internal
::
ArgsMatcher
<
InnerMatcher
,
k1
,
k2
>
(
matcher
);
}
template
<
int
k1
,
int
k2
,
int
k3
,
typename
InnerMatcher
>
inline
internal
::
ArgsMatcher
<
InnerMatcher
,
k1
,
k2
,
k3
>
Args
(
const
InnerMatcher
&
matcher
)
{
return
internal
::
ArgsMatcher
<
InnerMatcher
,
k1
,
k2
,
k3
>
(
matcher
);
}
template
<
int
k1
,
int
k2
,
int
k3
,
int
k4
,
typename
InnerMatcher
>
inline
internal
::
ArgsMatcher
<
InnerMatcher
,
k1
,
k2
,
k3
,
k4
>
Args
(
const
InnerMatcher
&
matcher
)
{
return
internal
::
ArgsMatcher
<
InnerMatcher
,
k1
,
k2
,
k3
,
k4
>
(
matcher
);
}
template
<
int
k1
,
int
k2
,
int
k3
,
int
k4
,
int
k5
,
typename
InnerMatcher
>
inline
internal
::
ArgsMatcher
<
InnerMatcher
,
k1
,
k2
,
k3
,
k4
,
k5
>
Args
(
const
InnerMatcher
&
matcher
)
{
return
internal
::
ArgsMatcher
<
InnerMatcher
,
k1
,
k2
,
k3
,
k4
,
k5
>
(
matcher
);
}
template
<
int
k1
,
int
k2
,
int
k3
,
int
k4
,
int
k5
,
int
k6
,
typename
InnerMatcher
>
inline
internal
::
ArgsMatcher
<
InnerMatcher
,
k1
,
k2
,
k3
,
k4
,
k5
,
k6
>
Args
(
const
InnerMatcher
&
matcher
)
{
return
internal
::
ArgsMatcher
<
InnerMatcher
,
k1
,
k2
,
k3
,
k4
,
k5
,
k6
>
(
matcher
);
}
template
<
int
k1
,
int
k2
,
int
k3
,
int
k4
,
int
k5
,
int
k6
,
int
k7
,
typename
InnerMatcher
>
inline
internal
::
ArgsMatcher
<
InnerMatcher
,
k1
,
k2
,
k3
,
k4
,
k5
,
k6
,
k7
>
Args
(
const
InnerMatcher
&
matcher
)
{
return
internal
::
ArgsMatcher
<
InnerMatcher
,
k1
,
k2
,
k3
,
k4
,
k5
,
k6
,
k7
>
(
matcher
);
}
template
<
int
k1
,
int
k2
,
int
k3
,
int
k4
,
int
k5
,
int
k6
,
int
k7
,
int
k8
,
typename
InnerMatcher
>
inline
internal
::
ArgsMatcher
<
InnerMatcher
,
k1
,
k2
,
k3
,
k4
,
k5
,
k6
,
k7
,
k8
>
Args
(
const
InnerMatcher
&
matcher
)
{
return
internal
::
ArgsMatcher
<
InnerMatcher
,
k1
,
k2
,
k3
,
k4
,
k5
,
k6
,
k7
,
k8
>
(
matcher
);
}
template
<
int
k1
,
int
k2
,
int
k3
,
int
k4
,
int
k5
,
int
k6
,
int
k7
,
int
k8
,
int
k9
,
typename
InnerMatcher
>
inline
internal
::
ArgsMatcher
<
InnerMatcher
,
k1
,
k2
,
k3
,
k4
,
k5
,
k6
,
k7
,
k8
,
k9
>
Args
(
const
InnerMatcher
&
matcher
)
{
return
internal
::
ArgsMatcher
<
InnerMatcher
,
k1
,
k2
,
k3
,
k4
,
k5
,
k6
,
k7
,
k8
,
k9
>
(
matcher
);
}
template
<
int
k1
,
int
k2
,
int
k3
,
int
k4
,
int
k5
,
int
k6
,
int
k7
,
int
k8
,
int
k9
,
int
k10
,
typename
InnerMatcher
>
inline
internal
::
ArgsMatcher
<
InnerMatcher
,
k1
,
k2
,
k3
,
k4
,
k5
,
k6
,
k7
,
k8
,
k9
,
k10
>
Args
(
const
InnerMatcher
&
matcher
)
{
return
internal
::
ArgsMatcher
<
InnerMatcher
,
k1
,
k2
,
k3
,
k4
,
k5
,
k6
,
k7
,
k8
,
k9
,
k10
>
(
matcher
);
}
// ElementsAre(e0, e1, ..., e_n) matches an STL-style container with
// (n + 1) elements, where the i-th element in the container must
// match the i-th argument in the list. Each argument of
...
...
include/gmock/gmock-generated-matchers.h.pump
View file @
2661c682
...
...
@@ -3,6 +3,7 @@ $$ This is a Pump source file. Please use Pump to convert it to
$$
gmock
-
generated
-
variadic
-
actions
.
h
.
$$
$
var
n
=
10
$$
The
maximum
arity
we
support
.
$$
}}
This
line
fixes
auto
-
indentation
of
the
following
code
in
Emacs
.
// Copyright 2008, Google Inc.
// All rights reserved.
//
...
...
@@ -48,6 +49,130 @@ $var n = 10 $$ The maximum arity we support.
namespace
testing
{
namespace
internal
{
$
range
i
0.
.
n
-
1
// The type of the i-th (0-based) field of Tuple.
#define GMOCK_FIELD_TYPE_(Tuple, i) \
typename ::std::tr1::tuple_element<i, Tuple>::type
// TupleFields<Tuple, k0, ..., kn> is for selecting fields from a
// tuple of type Tuple. It has two members:
//
// type: a tuple type whose i-th field is the ki-th field of Tuple.
// GetSelectedFields(t): returns fields k0, ..., and kn of t as a tuple.
//
// For example, in class TupleFields<tuple<bool, char, int>, 2, 0>, we have:
//
// type is tuple<int, bool>, and
// GetSelectedFields(make_tuple(true, 'a', 42)) is (42, true).
template
<
class
Tuple
$
for
i
[[,
int
k
$
i
=
-
1
]]>
class
TupleFields
;
// This generic version is used when there are $n selectors.
template
<
class
Tuple
$
for
i
[[,
int
k
$
i
]]>
class
TupleFields
{
public:
typedef
::
std
::
tr1
::
tuple
<
$
for
i
,
[[
GMOCK_FIELD_TYPE_
(
Tuple
,
k
$
i
)]]
>
type
;
static
type
GetSelectedFields
(
const
Tuple
&
t
)
{
using
::
std
::
tr1
::
get
;
return
type
(
$
for
i
,
[[
get
<
k
$
i
>
(
t
)]]);
}
};
// The following specialization is used for 0 ~ $(n-1) selectors.
$
for
i
[[
$$
}}
}
$
range
j
0.
.
i
-
1
$
range
k
0.
.
n
-
1
template
<
class
Tuple
$
for
j
[[,
int
k
$
j
]]>
class
TupleFields
<
Tuple
,
$
for
k
,
[[
$
if
k
<
i
[[
k
$
k
]]
$
else
[[
-
1
]]]]
>
{
public:
typedef
::
std
::
tr1
::
tuple
<
$
for
j
,
[[
GMOCK_FIELD_TYPE_
(
Tuple
,
k
$
j
)]]
>
type
;
static
type
GetSelectedFields
(
const
Tuple
&
t
)
{
using
::
std
::
tr1
::
get
;
return
type
(
$
for
j
,
[[
get
<
k
$
j
>
(
t
)]]);
}
};
]]
#undef GMOCK_FIELD_TYPE_
// Implements the Args() matcher.
$
var
ks
=
[[
$
for
i
,
[[
k
$
i
]]]]
template
<
class
ArgsTuple
$
for
i
[[,
int
k
$
i
=
-
1
]]>
class
ArgsMatcherImpl
:
public
MatcherInterface
<
ArgsTuple
>
{
public:
// ArgsTuple may have top-level const or reference modifiers.
typedef
GMOCK_REMOVE_CONST_
(
GMOCK_REMOVE_REFERENCE_
(
ArgsTuple
))
RawArgsTuple
;
typedef
typename
internal
::
TupleFields
<
RawArgsTuple
,
$
ks
>::
type
SelectedArgs
;
typedef
Matcher
<
const
SelectedArgs
&>
MonomorphicInnerMatcher
;
template
<
typename
InnerMatcher
>
explicit
ArgsMatcherImpl
(
const
InnerMatcher
&
inner_matcher
)
:
inner_matcher_
(
SafeMatcherCast
<
const
SelectedArgs
&>
(
inner_matcher
))
{}
virtual
bool
Matches
(
ArgsTuple
args
)
const
{
return
inner_matcher_
.
Matches
(
GetSelectedArgs
(
args
));
}
virtual
void
DescribeTo
(
::
std
::
ostream
*
os
)
const
{
PrintIndices
(
os
);
inner_matcher_
.
DescribeTo
(
os
);
}
virtual
void
DescribeNegationTo
(
::
std
::
ostream
*
os
)
const
{
PrintIndices
(
os
);
inner_matcher_
.
DescribeNegationTo
(
os
);
}
virtual
void
ExplainMatchResultTo
(
ArgsTuple
args
,
::
std
::
ostream
*
os
)
const
{
inner_matcher_
.
ExplainMatchResultTo
(
GetSelectedArgs
(
args
),
os
);
}
private:
static
SelectedArgs
GetSelectedArgs
(
ArgsTuple
args
)
{
return
TupleFields
<
RawArgsTuple
,
$
ks
>::
GetSelectedFields
(
args
);
}
// Prints the indices of the selected fields.
static
void
PrintIndices
(
::
std
::
ostream
*
os
)
{
*
os
<<
"are a tuple whose fields ("
;
const
int
indices
[
$
n
]
=
{
$
ks
};
for
(
int
i
=
0
;
i
<
$
n
;
i
++
)
{
if
(
indices
[
i
]
<
0
)
break
;
if
(
i
>=
1
)
*
os
<<
", "
;
*
os
<<
"#"
<<
indices
[
i
];
}
*
os
<<
") "
;
}
const
MonomorphicInnerMatcher
inner_matcher_
;
};
template
<
class
InnerMatcher
$
for
i
[[,
int
k
$
i
=
-
1
]]>
class
ArgsMatcher
{
public:
explicit
ArgsMatcher
(
const
InnerMatcher
&
inner_matcher
)
:
inner_matcher_
(
inner_matcher
)
{}
template
<
typename
ArgsTuple
>
operator
Matcher
<
ArgsTuple
>
()
const
{
return
MakeMatcher
(
new
ArgsMatcherImpl
<
ArgsTuple
,
$
ks
>
(
inner_matcher_
));
}
const
InnerMatcher
inner_matcher_
;
};
// Implements ElementsAre() and ElementsAreArray().
template
<
typename
Container
>
class
ElementsAreMatcherImpl
:
public
MatcherInterface
<
Container
>
{
...
...
@@ -268,6 +393,21 @@ class ElementsAreArrayMatcher {
}
// namespace internal
// Args<N1, N2, ..., Nk>(a_matcher) matches a tuple if the selected
// fields of it matches a_matcher. C++ doesn't support default
// arguments for function templates, so we have to overload it.
$
range
i
0.
.
n
$
for
i
[[
$
range
j
1.
.
i
template
<
$
for
j
[[
int
k
$
j
,
]]
typename
InnerMatcher
>
inline
internal
::
ArgsMatcher
<
InnerMatcher
$
for
j
[[,
k
$
j
]]
>
Args
(
const
InnerMatcher
&
matcher
)
{
return
internal
::
ArgsMatcher
<
InnerMatcher
$
for
j
[[,
k
$
j
]]
>
(
matcher
);
}
]]
// ElementsAre(e0, e1, ..., e_n) matches an STL-style container with
// (n + 1) elements, where the i-th element in the container must
// match the i-th argument in the list. Each argument of
...
...
@@ -282,6 +422,7 @@ inline internal::ElementsAreMatcher0 ElementsAre() {
return
internal
::
ElementsAreMatcher0
();
}
$
range
i
1.
.
n
$
for
i
[[
$
range
j
1.
.
i
...
...
include/gmock/gmock-matchers.h
View file @
2661c682
...
...
@@ -938,7 +938,7 @@ class MatchesRegexMatcher {
//
// We define this as a macro in order to eliminate duplicated source
// code.
#define GMOCK_IMPLEMENT_COMPARISON2_MATCHER_(name, op
, relation
) \
#define GMOCK_IMPLEMENT_COMPARISON2_MATCHER_(name, op) \
class name##2Matcher { \
public: \
template <typename T1, typename T2> \
...
...
@@ -953,21 +953,21 @@ class MatchesRegexMatcher {
return ::std::tr1::get<0>(args) op ::std::tr1::get<1>(args); \
} \
virtual void DescribeTo(::std::ostream* os) const { \
*os << "ar
gument #0 is " relation " argument #1
"; \
*os << "ar
e a pair (x, y) where x " #op " y
"; \
} \
virtual void DescribeNegationTo(::std::ostream* os) const { \
*os << "ar
gument #0 is not " relation " argument #1
"; \
*os << "ar
e a pair (x, y) where x " #op " y is false
"; \
} \
}; \
}
// Implements Eq(), Ge(), Gt(), Le(), Lt(), and Ne() respectively.
GMOCK_IMPLEMENT_COMPARISON2_MATCHER_
(
Eq
,
==
,
"equal to"
);
GMOCK_IMPLEMENT_COMPARISON2_MATCHER_
(
Ge
,
>=
,
"greater than or equal to"
);
GMOCK_IMPLEMENT_COMPARISON2_MATCHER_
(
Gt
,
>
,
"greater than"
);
GMOCK_IMPLEMENT_COMPARISON2_MATCHER_
(
Le
,
<=
,
"less than or equal to"
);
GMOCK_IMPLEMENT_COMPARISON2_MATCHER_
(
Lt
,
<
,
"less than"
);
GMOCK_IMPLEMENT_COMPARISON2_MATCHER_
(
Ne
,
!=
,
"not equal to"
);
GMOCK_IMPLEMENT_COMPARISON2_MATCHER_
(
Eq
,
==
);
GMOCK_IMPLEMENT_COMPARISON2_MATCHER_
(
Ge
,
>=
);
GMOCK_IMPLEMENT_COMPARISON2_MATCHER_
(
Gt
,
>
);
GMOCK_IMPLEMENT_COMPARISON2_MATCHER_
(
Le
,
<=
);
GMOCK_IMPLEMENT_COMPARISON2_MATCHER_
(
Lt
,
<
);
GMOCK_IMPLEMENT_COMPARISON2_MATCHER_
(
Ne
,
!=
);
#undef GMOCK_IMPLEMENT_COMPARISON2_MATCHER_
...
...
include/gmock/gmock-spec-builders.h
View file @
2661c682
...
...
@@ -795,9 +795,9 @@ class Expectation : public ExpectationBase {
DescribeMatchFailureTupleTo
(
matchers_
,
args
,
os
);
}
if
(
!
extra_matcher_
.
Matches
(
args
))
{
*
os
<<
"
Expected: "
;
*
os
<<
" Expected
args
: "
;
extra_matcher_
.
DescribeTo
(
os
);
*
os
<<
"
\n
Actual:
false
"
;
*
os
<<
"
\n
Actual:
don't match
"
;
internal
::
ExplainMatchResultAsNeededTo
<
const
ArgumentTuple
&>
(
extra_matcher_
,
args
,
os
);
...
...
test/gmock-generated-matchers_test.cc
View file @
2661c682
...
...
@@ -53,8 +53,11 @@ using std::pair;
using
std
::
set
;
using
std
::
stringstream
;
using
std
::
vector
;
using
std
::
tr1
::
get
;
using
std
::
tr1
::
make_tuple
;
using
std
::
tr1
::
tuple
;
using
testing
::
_
;
using
testing
::
Args
;
using
testing
::
Contains
;
using
testing
::
ElementsAre
;
using
testing
::
ElementsAreArray
;
...
...
@@ -98,6 +101,107 @@ string Explain(const MatcherType& m, const Value& x) {
return
ss
.
str
();
}
// Tests Args<k0, ..., kn>(m).
TEST
(
ArgsTest
,
AcceptsZeroTemplateArg
)
{
const
tuple
<
int
,
bool
>
t
(
5
,
true
);
EXPECT_THAT
(
t
,
Args
<>
(
Eq
(
tuple
<>
())));
EXPECT_THAT
(
t
,
Not
(
Args
<>
(
Ne
(
tuple
<>
()))));
}
TEST
(
ArgsTest
,
AcceptsOneTemplateArg
)
{
const
tuple
<
int
,
bool
>
t
(
5
,
true
);
EXPECT_THAT
(
t
,
Args
<
0
>
(
Eq
(
make_tuple
(
5
))));
EXPECT_THAT
(
t
,
Args
<
1
>
(
Eq
(
make_tuple
(
true
))));
EXPECT_THAT
(
t
,
Not
(
Args
<
1
>
(
Eq
(
make_tuple
(
false
)))));
}
TEST
(
ArgsTest
,
AcceptsTwoTemplateArgs
)
{
const
tuple
<
short
,
int
,
long
>
t
(
4
,
5
,
6L
);
// NOLINT
EXPECT_THAT
(
t
,
(
Args
<
0
,
1
>
(
Lt
())));
EXPECT_THAT
(
t
,
(
Args
<
1
,
2
>
(
Lt
())));
EXPECT_THAT
(
t
,
Not
(
Args
<
0
,
2
>
(
Gt
())));
}
TEST
(
ArgsTest
,
AcceptsRepeatedTemplateArgs
)
{
const
tuple
<
short
,
int
,
long
>
t
(
4
,
5
,
6L
);
// NOLINT
EXPECT_THAT
(
t
,
(
Args
<
0
,
0
>
(
Eq
())));
EXPECT_THAT
(
t
,
Not
(
Args
<
1
,
1
>
(
Ne
())));
}
TEST
(
ArgsTest
,
AcceptsDecreasingTemplateArgs
)
{
const
tuple
<
short
,
int
,
long
>
t
(
4
,
5
,
6L
);
// NOLINT
EXPECT_THAT
(
t
,
(
Args
<
2
,
0
>
(
Gt
())));
EXPECT_THAT
(
t
,
Not
(
Args
<
2
,
1
>
(
Lt
())));
}
MATCHER
(
SumIsZero
,
""
)
{
return
get
<
0
>
(
arg
)
+
get
<
1
>
(
arg
)
+
get
<
2
>
(
arg
)
==
0
;
}
TEST
(
ArgsTest
,
AcceptsMoreTemplateArgsThanArityOfOriginalTuple
)
{
EXPECT_THAT
(
make_tuple
(
-
1
,
2
),
(
Args
<
0
,
0
,
1
>
(
SumIsZero
())));
EXPECT_THAT
(
make_tuple
(
1
,
2
),
Not
(
Args
<
0
,
0
,
1
>
(
SumIsZero
())));
}
TEST
(
ArgsTest
,
CanBeNested
)
{
const
tuple
<
short
,
int
,
long
,
int
>
t
(
4
,
5
,
6L
,
6
);
// NOLINT
EXPECT_THAT
(
t
,
(
Args
<
1
,
2
,
3
>
(
Args
<
1
,
2
>
(
Eq
()))));
EXPECT_THAT
(
t
,
(
Args
<
0
,
1
,
3
>
(
Args
<
0
,
2
>
(
Lt
()))));
}
TEST
(
ArgsTest
,
CanMatchTupleByValue
)
{
typedef
tuple
<
char
,
int
,
int
>
Tuple3
;
const
Matcher
<
Tuple3
>
m
=
Args
<
1
,
2
>
(
Lt
());
EXPECT_TRUE
(
m
.
Matches
(
Tuple3
(
'a'
,
1
,
2
)));
EXPECT_FALSE
(
m
.
Matches
(
Tuple3
(
'b'
,
2
,
2
)));
}
TEST
(
ArgsTest
,
CanMatchTupleByReference
)
{
typedef
tuple
<
char
,
char
,
int
>
Tuple3
;
const
Matcher
<
const
Tuple3
&>
m
=
Args
<
0
,
1
>
(
Lt
());
EXPECT_TRUE
(
m
.
Matches
(
Tuple3
(
'a'
,
'b'
,
2
)));
EXPECT_FALSE
(
m
.
Matches
(
Tuple3
(
'b'
,
'b'
,
2
)));
}
// Validates that arg is printed as str.
MATCHER_P
(
PrintsAs
,
str
,
""
)
{
typedef
GMOCK_REMOVE_CONST_
(
GMOCK_REMOVE_REFERENCE_
(
arg_type
))
RawTuple
;
return
testing
::
internal
::
UniversalPrinter
<
RawTuple
>::
PrintToString
(
arg
)
==
str
;
}
TEST
(
ArgsTest
,
AcceptsTenTemplateArgs
)
{
EXPECT_THAT
(
make_tuple
(
0
,
1L
,
2
,
3L
,
4
,
5
,
6
,
7
,
8
,
9
),
(
Args
<
9
,
8
,
7
,
6
,
5
,
4
,
3
,
2
,
1
,
0
>
(
PrintsAs
(
"(9, 8, 7, 6, 5, 4, 3, 2, 1, 0)"
))));
EXPECT_THAT
(
make_tuple
(
0
,
1L
,
2
,
3L
,
4
,
5
,
6
,
7
,
8
,
9
),
Not
(
Args
<
9
,
8
,
7
,
6
,
5
,
4
,
3
,
2
,
1
,
0
>
(
PrintsAs
(
"(0, 8, 7, 6, 5, 4, 3, 2, 1, 0)"
))));
}
TEST
(
ArgsTest
,
DescirbesSelfCorrectly
)
{
const
Matcher
<
tuple
<
int
,
bool
,
char
>
>
m
=
Args
<
2
,
0
>
(
Lt
());
EXPECT_EQ
(
"are a tuple whose fields (#2, #0) are a pair (x, y) where x < y"
,
Describe
(
m
));
}
TEST
(
ArgsTest
,
DescirbesNestedArgsCorrectly
)
{
const
Matcher
<
const
tuple
<
int
,
bool
,
char
,
int
>&>
m
=
Args
<
0
,
2
,
3
>
(
Args
<
2
,
0
>
(
Lt
()));
EXPECT_EQ
(
"are a tuple whose fields (#0, #2, #3) are a tuple "
"whose fields (#2, #0) are a pair (x, y) where x < y"
,
Describe
(
m
));
}
TEST
(
ArgsTest
,
DescribesNegationCorrectly
)
{
const
Matcher
<
tuple
<
int
,
char
>
>
m
=
Args
<
1
,
0
>
(
Gt
());
EXPECT_EQ
(
"are a tuple whose fields (#1, #0) are a pair (x, y) "
"where x > y is false"
,
DescribeNegation
(
m
));
}
// For testing ExplainMatchResultTo().
class
GreaterThanMatcher
:
public
MatcherInterface
<
int
>
{
public:
...
...
@@ -926,8 +1030,9 @@ TEST(ContainsTest, AcceptsMatcher) {
TEST
(
ContainsTest
,
WorksForNativeArrayAsTuple
)
{
const
int
a
[]
=
{
1
,
2
};
EXPECT_THAT
(
make_tuple
(
a
,
2
),
Contains
(
1
));
EXPECT_THAT
(
make_tuple
(
a
,
2
),
Not
(
Contains
(
Gt
(
3
))));
const
int
*
const
pointer
=
a
;
EXPECT_THAT
(
make_tuple
(
pointer
,
2
),
Contains
(
1
));
EXPECT_THAT
(
make_tuple
(
pointer
,
2
),
Not
(
Contains
(
Gt
(
3
))));
}
TEST
(
ContainsTest
,
WorksForTwoDimensionalNativeArray
)
{
...
...
test/gmock-internal-utils_test.cc
View file @
2661c682
...
...
@@ -941,7 +941,7 @@ TEST(StlContainerViewTest, WorksForDynamicNativeArray) {
EXPECT_EQ
(
a1
,
a2
.
begin
());
const
NativeArray
<
int
>
a3
=
StlContainerView
<
tuple
<
int
*
,
size_t
>
>::
Copy
(
make_tuple
(
a1
,
3
));
Copy
(
make_tuple
(
static_cast
<
int
*>
(
a1
)
,
3
));
ASSERT_EQ
(
3
,
a3
.
size
());
EXPECT_EQ
(
0
,
a3
.
begin
()[
0
]);
EXPECT_EQ
(
1
,
a3
.
begin
()[
1
]);
...
...
test/gmock-matchers_test.cc
View file @
2661c682
...
...
@@ -1335,7 +1335,7 @@ TEST(Eq2Test, MatchesEqualArguments) {
// Tests that Eq() describes itself properly.
TEST
(
Eq2Test
,
CanDescribeSelf
)
{
Matcher
<
const
Tuple2
&>
m
=
Eq
();
EXPECT_EQ
(
"ar
gument #0 is equal to argument #1
"
,
Describe
(
m
));
EXPECT_EQ
(
"ar
e a pair (x, y) where x == y
"
,
Describe
(
m
));
}
// Tests that Ge() matches a 2-tuple where the first field >= the
...
...
@@ -1350,8 +1350,7 @@ TEST(Ge2Test, MatchesGreaterThanOrEqualArguments) {
// Tests that Ge() describes itself properly.
TEST
(
Ge2Test
,
CanDescribeSelf
)
{
Matcher
<
const
Tuple2
&>
m
=
Ge
();
EXPECT_EQ
(
"argument #0 is greater than or equal to argument #1"
,
Describe
(
m
));
EXPECT_EQ
(
"are a pair (x, y) where x >= y"
,
Describe
(
m
));
}
// Tests that Gt() matches a 2-tuple where the first field > the
...
...
@@ -1366,7 +1365,7 @@ TEST(Gt2Test, MatchesGreaterThanArguments) {
// Tests that Gt() describes itself properly.
TEST
(
Gt2Test
,
CanDescribeSelf
)
{
Matcher
<
const
Tuple2
&>
m
=
Gt
();
EXPECT_EQ
(
"ar
gument #0 is greater than argument #1
"
,
Describe
(
m
));
EXPECT_EQ
(
"ar
e a pair (x, y) where x > y
"
,
Describe
(
m
));
}
// Tests that Le() matches a 2-tuple where the first field <= the
...
...
@@ -1381,8 +1380,7 @@ TEST(Le2Test, MatchesLessThanOrEqualArguments) {
// Tests that Le() describes itself properly.
TEST
(
Le2Test
,
CanDescribeSelf
)
{
Matcher
<
const
Tuple2
&>
m
=
Le
();
EXPECT_EQ
(
"argument #0 is less than or equal to argument #1"
,
Describe
(
m
));
EXPECT_EQ
(
"are a pair (x, y) where x <= y"
,
Describe
(
m
));
}
// Tests that Lt() matches a 2-tuple where the first field < the
...
...
@@ -1397,7 +1395,7 @@ TEST(Lt2Test, MatchesLessThanArguments) {
// Tests that Lt() describes itself properly.
TEST
(
Lt2Test
,
CanDescribeSelf
)
{
Matcher
<
const
Tuple2
&>
m
=
Lt
();
EXPECT_EQ
(
"ar
gument #0 is less than argument #1
"
,
Describe
(
m
));
EXPECT_EQ
(
"ar
e a pair (x, y) where x < y
"
,
Describe
(
m
));
}
// Tests that Ne() matches a 2-tuple where the first field != the
...
...
@@ -1412,7 +1410,7 @@ TEST(Ne2Test, MatchesUnequalArguments) {
// Tests that Ne() describes itself properly.
TEST
(
Ne2Test
,
CanDescribeSelf
)
{
Matcher
<
const
Tuple2
&>
m
=
Ne
();
EXPECT_EQ
(
"ar
gument #0 is not equal to argument #1
"
,
Describe
(
m
));
EXPECT_EQ
(
"ar
e a pair (x, y) where x != y
"
,
Describe
(
m
));
}
// Tests that Not(m) matches any value that doesn't match m.
...
...
@@ -2948,11 +2946,12 @@ TEST(ContainerEqExtraTest, WorksForNativeArrayAsTuple) {
const
int
a2
[]
=
{
1
,
2
,
3
};
const
int
b
[]
=
{
1
,
2
,
3
,
4
};
EXPECT_THAT
(
make_tuple
(
a1
,
3
),
ContainerEq
(
a2
));
EXPECT_THAT
(
make_tuple
(
a1
,
3
),
Not
(
ContainerEq
(
b
)));
const
int
*
const
p1
=
a1
;
EXPECT_THAT
(
make_tuple
(
p1
,
3
),
ContainerEq
(
a2
));
EXPECT_THAT
(
make_tuple
(
p1
,
3
),
Not
(
ContainerEq
(
b
)));
const
int
c
[]
=
{
1
,
3
,
2
};
EXPECT_THAT
(
make_tuple
(
a
1
,
3
),
Not
(
ContainerEq
(
c
)));
EXPECT_THAT
(
make_tuple
(
p
1
,
3
),
Not
(
ContainerEq
(
c
)));
}
TEST
(
ContainerEqExtraTest
,
CopiesNativeArrayParameter
)
{
...
...
test/gmock_output_test_golden.txt
View file @
2661c682
...
...
@@ -183,8 +183,8 @@ Unexpected mock function call - returning default value.
Google Mock tried the following 1 expectation, but it didn't match:
FILE:#:
Expected
:
arg
ument #0 is greater than or equal to argument #1
Actual:
false
Expected arg
s: are a pair (x, y) where x >= y
Actual:
don't match
Expected: to be called once
Actual: never called - unsatisfied and active
[ FAILED ] GMockOutputTest.MismatchWithArguments
...
...
@@ -199,8 +199,8 @@ Google Mock tried the following 1 expectation, but it didn't match:
FILE:#:
Expected arg #0: is greater than or equal to 2
Actual: 1
Expected
:
arg
ument #0 is greater than or equal to argument #1
Actual:
false
Expected arg
s: are a pair (x, y) where x >= y
Actual:
don't match
Expected: to be called once
Actual: never called - unsatisfied and active
[ FAILED ] GMockOutputTest.MismatchArgumentsAndWithArguments
...
...
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