|
Packit |
bd1cd8 |
This page discusses the design of new Google Mock features.
|
|
Packit |
bd1cd8 |
|
|
Packit |
bd1cd8 |
|
|
Packit |
bd1cd8 |
|
|
Packit |
bd1cd8 |
# Macros for Defining Actions #
|
|
Packit |
bd1cd8 |
|
|
Packit |
bd1cd8 |
## Problem ##
|
|
Packit |
bd1cd8 |
|
|
Packit |
bd1cd8 |
Due to the lack of closures in C++, it currently requires some
|
|
Packit |
bd1cd8 |
non-trivial effort to define a custom action in Google Mock. For
|
|
Packit |
bd1cd8 |
example, suppose you want to "increment the value pointed to by the
|
|
Packit |
bd1cd8 |
second argument of the mock function and return it", you could write:
|
|
Packit |
bd1cd8 |
|
|
Packit |
bd1cd8 |
```
|
|
Packit |
bd1cd8 |
int IncrementArg1(Unused, int* p, Unused) {
|
|
Packit |
bd1cd8 |
return ++(*p);
|
|
Packit |
bd1cd8 |
}
|
|
Packit |
bd1cd8 |
|
|
Packit |
bd1cd8 |
... WillOnce(Invoke(IncrementArg1));
|
|
Packit |
bd1cd8 |
```
|
|
Packit |
bd1cd8 |
|
|
Packit |
bd1cd8 |
There are several things unsatisfactory about this approach:
|
|
Packit |
bd1cd8 |
|
|
Packit |
bd1cd8 |
* Even though the action only cares about the second argument of the mock function, its definition needs to list other arguments as dummies. This is tedious.
|
|
Packit |
bd1cd8 |
* The defined action is usable only in mock functions that takes exactly 3 arguments - an unnecessary restriction.
|
|
Packit |
bd1cd8 |
* To use the action, one has to say `Invoke(IncrementArg1)`, which isn't as nice as `IncrementArg1()`.
|
|
Packit |
bd1cd8 |
|
|
Packit |
bd1cd8 |
The latter two problems can be overcome using `MakePolymorphicAction()`,
|
|
Packit |
bd1cd8 |
but it requires much more boilerplate code:
|
|
Packit |
bd1cd8 |
|
|
Packit |
bd1cd8 |
```
|
|
Packit |
bd1cd8 |
class IncrementArg1Action {
|
|
Packit |
bd1cd8 |
public:
|
|
Packit |
bd1cd8 |
template <typename Result, typename ArgumentTuple>
|
|
Packit |
bd1cd8 |
Result Perform(const ArgumentTuple& args) const {
|
|
Packit |
bd1cd8 |
return ++(*tr1::get<1>(args));
|
|
Packit |
bd1cd8 |
}
|
|
Packit |
bd1cd8 |
};
|
|
Packit |
bd1cd8 |
|
|
Packit |
bd1cd8 |
PolymorphicAction<IncrementArg1Action> IncrementArg1() {
|
|
Packit |
bd1cd8 |
return MakePolymorphicAction(IncrementArg1Action());
|
|
Packit |
bd1cd8 |
}
|
|
Packit |
bd1cd8 |
|
|
Packit |
bd1cd8 |
... WillOnce(IncrementArg1());
|
|
Packit |
bd1cd8 |
```
|
|
Packit |
bd1cd8 |
|
|
Packit |
bd1cd8 |
Our goal is to allow defining custom actions with the least amount of
|
|
Packit |
bd1cd8 |
boiler-plate C++ requires.
|
|
Packit |
bd1cd8 |
|
|
Packit |
bd1cd8 |
## Solution ##
|
|
Packit |
bd1cd8 |
|
|
Packit |
bd1cd8 |
We propose to introduce a new macro:
|
|
Packit |
bd1cd8 |
```
|
|
Packit |
bd1cd8 |
ACTION(name) { statements; }
|
|
Packit |
bd1cd8 |
```
|
|
Packit |
bd1cd8 |
|
|
Packit |
bd1cd8 |
Using this in a namespace scope will define an action with the given
|
|
Packit |
bd1cd8 |
name that executes the statements. Inside the statements, you can
|
|
Packit |
bd1cd8 |
refer to the K-th (0-based) argument of the mock function as `argK`.
|
|
Packit |
bd1cd8 |
For example:
|
|
Packit |
bd1cd8 |
```
|
|
Packit |
bd1cd8 |
ACTION(IncrementArg1) { return ++(*arg1); }
|
|
Packit |
bd1cd8 |
```
|
|
Packit |
bd1cd8 |
allows you to write
|
|
Packit |
bd1cd8 |
```
|
|
Packit |
bd1cd8 |
... WillOnce(IncrementArg1());
|
|
Packit |
bd1cd8 |
```
|
|
Packit |
bd1cd8 |
|
|
Packit |
bd1cd8 |
Note that you don't need to specify the types of the mock function
|
|
Packit |
bd1cd8 |
arguments, as brevity is a top design goal here. Rest assured that
|
|
Packit |
bd1cd8 |
your code is still type-safe though: you'll get a compiler error if
|
|
Packit |
bd1cd8 |
`*arg1` doesn't support the `++` operator, or if the type of
|
|
Packit |
bd1cd8 |
`++(*arg1)` isn't compatible with the mock function's return type.
|
|
Packit |
bd1cd8 |
|
|
Packit |
bd1cd8 |
Another example:
|
|
Packit |
bd1cd8 |
```
|
|
Packit |
bd1cd8 |
ACTION(Foo) {
|
|
Packit |
bd1cd8 |
(*arg2)(5);
|
|
Packit |
bd1cd8 |
Blah();
|
|
Packit |
bd1cd8 |
*arg1 = 0;
|
|
Packit |
bd1cd8 |
return arg0;
|
|
Packit |
bd1cd8 |
}
|
|
Packit |
bd1cd8 |
```
|
|
Packit |
bd1cd8 |
defines an action `Foo()` that invokes argument #2 (a function pointer)
|
|
Packit |
bd1cd8 |
with 5, calls function `Blah()`, sets the value pointed to by argument
|
|
Packit |
bd1cd8 |
#1 to 0, and returns argument #0.
|
|
Packit |
bd1cd8 |
|
|
Packit |
bd1cd8 |
For more convenience and flexibility, you can also use the following
|
|
Packit |
bd1cd8 |
pre-defined symbols in the body of `ACTION`:
|
|
Packit |
bd1cd8 |
|
|
Packit |
bd1cd8 |
| `argK_type` | The type of the K-th (0-based) argument of the mock function |
|
|
Packit |
bd1cd8 |
|:------------|:-------------------------------------------------------------|
|
|
Packit |
bd1cd8 |
| `args` | All arguments of the mock function as a tuple |
|
|
Packit |
bd1cd8 |
| `args_type` | The type of all arguments of the mock function as a tuple |
|
|
Packit |
bd1cd8 |
| `return_type` | The return type of the mock function |
|
|
Packit |
bd1cd8 |
| `function_type` | The type of the mock function |
|
|
Packit |
bd1cd8 |
|
|
Packit |
bd1cd8 |
For example, when using an `ACTION` as a stub action for mock function:
|
|
Packit |
bd1cd8 |
```
|
|
Packit |
bd1cd8 |
int DoSomething(bool flag, int* ptr);
|
|
Packit |
bd1cd8 |
```
|
|
Packit |
bd1cd8 |
we have:
|
|
Packit |
bd1cd8 |
| **Pre-defined Symbol** | **Is Bound To** |
|
|
Packit |
bd1cd8 |
|:-----------------------|:----------------|
|
|
Packit |
bd1cd8 |
| `arg0` | the value of `flag` |
|
|
Packit |
bd1cd8 |
| `arg0_type` | the type `bool` |
|
|
Packit |
bd1cd8 |
| `arg1` | the value of `ptr` |
|
|
Packit |
bd1cd8 |
| `arg1_type` | the type `int*` |
|
|
Packit |
bd1cd8 |
| `args` | the tuple `(flag, ptr)` |
|
|
Packit |
bd1cd8 |
| `args_type` | the type `std::tr1::tuple<bool, int*>` |
|
|
Packit |
bd1cd8 |
| `return_type` | the type `int` |
|
|
Packit |
bd1cd8 |
| `function_type` | the type `int(bool, int*)` |
|
|
Packit |
bd1cd8 |
|
|
Packit |
bd1cd8 |
## Parameterized actions ##
|
|
Packit |
bd1cd8 |
|
|
Packit |
bd1cd8 |
Sometimes you'll want to parameterize the action. For that we propose
|
|
Packit |
bd1cd8 |
another macro
|
|
Packit |
bd1cd8 |
```
|
|
Packit |
bd1cd8 |
ACTION_P(name, param) { statements; }
|
|
Packit |
bd1cd8 |
```
|
|
Packit |
bd1cd8 |
|
|
Packit |
bd1cd8 |
For example,
|
|
Packit |
bd1cd8 |
```
|
|
Packit |
bd1cd8 |
ACTION_P(Add, n) { return arg0 + n; }
|
|
Packit |
bd1cd8 |
```
|
|
Packit |
bd1cd8 |
will allow you to write
|
|
Packit |
bd1cd8 |
```
|
|
Packit |
bd1cd8 |
// Returns argument #0 + 5.
|
|
Packit |
bd1cd8 |
... WillOnce(Add(5));
|
|
Packit |
bd1cd8 |
```
|
|
Packit |
bd1cd8 |
|
|
Packit |
bd1cd8 |
For convenience, we use the term _arguments_ for the values used to
|
|
Packit |
bd1cd8 |
invoke the mock function, and the term _parameters_ for the values
|
|
Packit |
bd1cd8 |
used to instantiate an action.
|
|
Packit |
bd1cd8 |
|
|
Packit |
bd1cd8 |
Note that you don't need to provide the type of the parameter either.
|
|
Packit |
bd1cd8 |
Suppose the parameter is named `param`, you can also use the
|
|
Packit |
bd1cd8 |
Google-Mock-defined symbol `param_type` to refer to the type of the
|
|
Packit |
bd1cd8 |
parameter as inferred by the compiler.
|
|
Packit |
bd1cd8 |
|
|
Packit |
bd1cd8 |
We will also provide `ACTION_P2`, `ACTION_P3`, and etc to support
|
|
Packit |
bd1cd8 |
multi-parameter actions. For example,
|
|
Packit |
bd1cd8 |
```
|
|
Packit |
bd1cd8 |
ACTION_P2(ReturnDistanceTo, x, y) {
|
|
Packit |
bd1cd8 |
double dx = arg0 - x;
|
|
Packit |
bd1cd8 |
double dy = arg1 - y;
|
|
Packit |
bd1cd8 |
return sqrt(dx*dx + dy*dy);
|
|
Packit |
bd1cd8 |
}
|
|
Packit |
bd1cd8 |
```
|
|
Packit |
bd1cd8 |
lets you write
|
|
Packit |
bd1cd8 |
```
|
|
Packit |
bd1cd8 |
... WillOnce(ReturnDistanceTo(5.0, 26.5));
|
|
Packit |
bd1cd8 |
```
|
|
Packit |
bd1cd8 |
|
|
Packit |
bd1cd8 |
You can view `ACTION` as a degenerated parameterized action where the
|
|
Packit |
bd1cd8 |
number of parameters is 0.
|
|
Packit |
bd1cd8 |
|
|
Packit |
bd1cd8 |
## Advanced Usages ##
|
|
Packit |
bd1cd8 |
|
|
Packit |
bd1cd8 |
### Overloading Actions ###
|
|
Packit |
bd1cd8 |
|
|
Packit |
bd1cd8 |
You can easily define actions overloaded on the number of parameters:
|
|
Packit |
bd1cd8 |
```
|
|
Packit |
bd1cd8 |
ACTION_P(Plus, a) { ... }
|
|
Packit |
bd1cd8 |
ACTION_P2(Plus, a, b) { ... }
|
|
Packit |
bd1cd8 |
```
|
|
Packit |
bd1cd8 |
|
|
Packit |
bd1cd8 |
### Restricting the Type of an Argument or Parameter ###
|
|
Packit |
bd1cd8 |
|
|
Packit |
bd1cd8 |
For maximum brevity and reusability, the `ACTION*` macros don't let
|
|
Packit |
bd1cd8 |
you specify the types of the mock function arguments and the action
|
|
Packit |
bd1cd8 |
parameters. Instead, we let the compiler infer the types for us.
|
|
Packit |
bd1cd8 |
|
|
Packit |
bd1cd8 |
Sometimes, however, we may want to be more explicit about the types.
|
|
Packit |
bd1cd8 |
There are several tricks to do that. For example:
|
|
Packit |
bd1cd8 |
```
|
|
Packit |
bd1cd8 |
ACTION(Foo) {
|
|
Packit |
bd1cd8 |
// Makes sure arg0 can be converted to int.
|
|
Packit |
bd1cd8 |
int n = arg0;
|
|
Packit |
bd1cd8 |
... use n instead of arg0 here ...
|
|
Packit |
bd1cd8 |
}
|
|
Packit |
bd1cd8 |
|
|
Packit |
bd1cd8 |
ACTION_P(Bar, param) {
|
|
Packit |
bd1cd8 |
// Makes sure the type of arg1 is const char*.
|
|
Packit |
bd1cd8 |
::testing::StaticAssertTypeEq<const char*, arg1_type>();
|
|
Packit |
bd1cd8 |
|
|
Packit |
bd1cd8 |
// Makes sure param can be converted to bool.
|
|
Packit |
bd1cd8 |
bool flag = param;
|
|
Packit |
bd1cd8 |
}
|
|
Packit |
bd1cd8 |
```
|
|
Packit |
bd1cd8 |
where `StaticAssertTypeEq` is a compile-time assertion we plan to add to
|
|
Packit |
bd1cd8 |
Google Test (the name is chosen to match `static_assert` in C++0x).
|
|
Packit |
bd1cd8 |
|
|
Packit |
bd1cd8 |
### Using the ACTION Object's Type ###
|
|
Packit |
bd1cd8 |
|
|
Packit |
bd1cd8 |
If you are writing a function that returns an `ACTION` object, you'll
|
|
Packit |
bd1cd8 |
need to know its type. The type depends on the macro used to define
|
|
Packit |
bd1cd8 |
the action and the parameter types. The rule is relatively simple:
|
|
Packit |
bd1cd8 |
| **Given Definition** | **Expression** | **Has Type** |
|
|
Packit |
bd1cd8 |
|:---------------------|:---------------|:-------------|
|
|
Packit |
bd1cd8 |
| `ACTION(Foo)` | `Foo()` | `FooAction` |
|
|
Packit |
bd1cd8 |
| `ACTION_P(Bar, param)` | `Bar(int_value)` | `BarActionP<int>` |
|
|
Packit |
bd1cd8 |
| `ACTION_P2(Baz, p1, p2)` | `Baz(bool_value, int_value)` | `BazActionP2<bool, int>` |
|
|
Packit |
bd1cd8 |
| ... | ... | ... |
|
|
Packit |
bd1cd8 |
|
|
Packit |
bd1cd8 |
Note that we have to pick different suffixes (`Action`, `ActionP`,
|
|
Packit |
bd1cd8 |
`ActionP2`, and etc) for actions with different numbers of parameters,
|
|
Packit |
bd1cd8 |
or the action definitions cannot be overloaded on the number of
|
|
Packit |
bd1cd8 |
parameters.
|
|
Packit |
bd1cd8 |
|
|
Packit |
bd1cd8 |
## When to Use ##
|
|
Packit |
bd1cd8 |
|
|
Packit |
bd1cd8 |
While the new macros are very convenient, please also consider other
|
|
Packit |
bd1cd8 |
means of implementing actions (e.g. via `ActionInterface` or
|
|
Packit |
bd1cd8 |
`MakePolymorphicAction()`), especially if you need to use the defined
|
|
Packit |
bd1cd8 |
action a lot. While the other approaches require more work, they give
|
|
Packit |
bd1cd8 |
you more control on the types of the mock function arguments and the
|
|
Packit |
bd1cd8 |
action parameters, which in general leads to better compiler error
|
|
Packit |
bd1cd8 |
messages that pay off in the long run. They also allow overloading
|
|
Packit |
bd1cd8 |
actions based on parameter types, as opposed to just the number of
|
|
Packit |
bd1cd8 |
parameters.
|
|
Packit |
bd1cd8 |
|
|
Packit |
bd1cd8 |
## Related Work ##
|
|
Packit |
bd1cd8 |
|
|
Packit |
bd1cd8 |
As you may have realized, the `ACTION*` macros resemble closures (also
|
|
Packit |
bd1cd8 |
known as lambda expressions or anonymous functions). Indeed, both of
|
|
Packit |
bd1cd8 |
them seek to lower the syntactic overhead for defining a function.
|
|
Packit |
bd1cd8 |
|
|
Packit |
bd1cd8 |
C++0x will support lambdas, but they are not part of C++ right now.
|
|
Packit |
bd1cd8 |
Some non-standard libraries (most notably BLL or Boost Lambda Library)
|
|
Packit |
bd1cd8 |
try to alleviate this problem. However, they are not a good choice
|
|
Packit |
bd1cd8 |
for defining actions as:
|
|
Packit |
bd1cd8 |
|
|
Packit |
bd1cd8 |
* They are non-standard and not widely installed. Google Mock only depends on standard libraries and `tr1::tuple`, which is part of the new C++ standard and comes with gcc 4+. We want to keep it that way.
|
|
Packit |
bd1cd8 |
* They are not trivial to learn.
|
|
Packit |
bd1cd8 |
* They will become obsolete when C++0x's lambda feature is widely supported. We don't want to make our users use a dying library.
|
|
Packit |
bd1cd8 |
* Since they are based on operators, they are rather ad hoc: you cannot use statements, and you cannot pass the lambda arguments to a function, for example.
|
|
Packit |
bd1cd8 |
* They have subtle semantics that easily confuses new users. For example, in expression `_1++ + foo++`, `foo` will be incremented only once where the expression is evaluated, while `_1` will be incremented every time the unnamed function is invoked. This is far from intuitive.
|
|
Packit |
bd1cd8 |
|
|
Packit |
bd1cd8 |
`ACTION*` avoid all these problems.
|
|
Packit |
bd1cd8 |
|
|
Packit |
bd1cd8 |
## Future Improvements ##
|
|
Packit |
bd1cd8 |
|
|
Packit |
bd1cd8 |
There may be a need for composing `ACTION*` definitions (i.e. invoking
|
|
Packit |
bd1cd8 |
another `ACTION` inside the definition of one `ACTION*`). We are not
|
|
Packit |
bd1cd8 |
sure we want it yet, as one can get a similar effect by putting
|
|
Packit |
bd1cd8 |
`ACTION` definitions in function templates and composing the function
|
|
Packit |
bd1cd8 |
templates. We'll revisit this based on user feedback.
|
|
Packit |
bd1cd8 |
|
|
Packit |
bd1cd8 |
The reason we don't allow `ACTION*()` inside a function body is that
|
|
Packit |
bd1cd8 |
the current C++ standard doesn't allow function-local types to be used
|
|
Packit |
bd1cd8 |
to instantiate templates. The upcoming C++0x standard will lift this
|
|
Packit |
bd1cd8 |
restriction. Once this feature is widely supported by compilers, we
|
|
Packit |
bd1cd8 |
can revisit the implementation and add support for using `ACTION*()`
|
|
Packit |
bd1cd8 |
inside a function.
|
|
Packit |
bd1cd8 |
|
|
Packit |
bd1cd8 |
C++0x will also support lambda expressions. When they become
|
|
Packit |
bd1cd8 |
available, we may want to support using lambdas as actions.
|
|
Packit |
bd1cd8 |
|
|
Packit |
bd1cd8 |
# Macros for Defining Matchers #
|
|
Packit |
bd1cd8 |
|
|
Packit |
bd1cd8 |
Once the macros for defining actions are implemented, we plan to do
|
|
Packit |
bd1cd8 |
the same for matchers:
|
|
Packit |
bd1cd8 |
|
|
Packit |
bd1cd8 |
```
|
|
Packit |
bd1cd8 |
MATCHER(name) { statements; }
|
|
Packit |
bd1cd8 |
```
|
|
Packit |
bd1cd8 |
|
|
Packit |
bd1cd8 |
where you can refer to the value being matched as `arg`. For example,
|
|
Packit |
bd1cd8 |
given:
|
|
Packit |
bd1cd8 |
|
|
Packit |
bd1cd8 |
```
|
|
Packit |
bd1cd8 |
MATCHER(IsPositive) { return arg > 0; }
|
|
Packit |
bd1cd8 |
```
|
|
Packit |
bd1cd8 |
|
|
Packit |
bd1cd8 |
you can use `IsPositive()` as a matcher that matches a value iff it is
|
|
Packit |
bd1cd8 |
greater than 0.
|
|
Packit |
bd1cd8 |
|
|
Packit |
bd1cd8 |
We will also add `MATCHER_P`, `MATCHER_P2`, and etc for parameterized
|
|
Packit |
bd1cd8 |
matchers.
|