vmcpherson / googlemock

Automatically exported from code.google.com/p/googlemock
BSD 3-Clause "New" or "Revised" License
0 stars 0 forks source link

need to allow annotating the print format of a mock function's arguments / return value #47

Closed GoogleCodeExporter closed 9 years ago

GoogleCodeExporter commented 9 years ago
= The problem =

To assist debugging, Google Mock sometimes prints the argument values
a mock function receives and its return value.  Some people run into
problems with that, as they mock APIs where char* is used to pass a
buffer (as opposed to a NUL-terminated string).  When Google Mock
tries to print such an argument or return value, the user sees trash;
worse, the program could crash for access violation.

We could avoid the problem by making Google Mock always print char* as
a pointer, but that doesn't work well for people using char* for
strings, which is a standard practice.

So far, we have been asking the users to change their buffer-passing
API to use something like unsigned char* instead of char*, such that
Google Mock won't mistaken it as a string.  Doing that also makes the
code more readable as the intent of the argument is less ambiguous.

Sometimes the user isn't free to make that change.  For that we
recommend this pattern:

 // The base class has virtual void Foo(const char* buffer, int len).
 MOCK_METHOD2(Foo, void(const unsigned char* buffer, int len));
 virtual void Foo(const char* buffer, int len) {
   // Forwards to the mock function.
   Foo(reinterpret_cast<const unsigned char*>(buffer), len);
 }

While this works, it's tedious.

Also, sometimes a user uses an array or container that has elements of
type char* inside.  Google Mock will print the container elements as C
strings.  The above wrapper pattern is harder to implement for
this case.

= Solution 1 =

We could introduce a global flag that makes all char* being printed as
pointers.  However, this is a hack as the decision often needs to be
made locally: char* may mean a C string in one API and a pointer in
another, and you may be using both APIs in the same test program; even
different arguments of the same function may use char* in different
ways.

Therefore this is not a good solution.

= Solution 2 (the Proposal) =

In MOCK_METHOD*(), we can let the user annotate types that need to be
printed specially.  If you want to leave it to gmock to decide how to
print an argument, you write MOCK_METHOD* the same as before.  Otherwise, you
change the argument type from T to PrintBy<Format, T>, where Format is
a class for selecting the print format.

For example, to mock:

 // 'buffer' points to some bytes, not necessarily NUL-terminated.
 virtual void Foo(const char* buffer, int len);

you could write:

 MOCK_METHOD2(Foo, void(PrintBy<CharStarAsPointer, const char*>
buffer, int len));

where CharStarAsPointer is defined by Google Mock.  Advanced users can
define their own print format classes, but we'll discuss the details
later.

If you need to do this repeatedly, you can give the annotated type a
name using typedef:

 typedef PrintBy<CharStarAsPointer, const char*> Buffer;
 MOCK_METHOD2(Foo, void(Buffer buffer, int len));
 MOCK_METHOD3(Compare, int(Buffer lhs, Buffer rhs, int len));
 MOCK_METHOD0(GetBuffer, Buffer());

You may wonder why we need to expose the PrintBy template and the
CharStarAsPointer class.  Can't Google Mock just define the Buffer
type and ask people to use it?

The answer is that there are unbounded number of container types out
there that can be used to contain char* values, and we cannot make
Google Mock aware of all of them.  Suppose you use a class (written by
someone else) BufferTable which contains char* values, and you want
the values printed as pointers, you can tell Google Mock by:

 MOCK_METHOD1(Blah, void(PrintBy<CharStarAsPointer, const
BufferTable&> table));

The T in PrintBy<CharStarAsPointer, T> doesn't have to be a char*.  If
it's an array or container, Google Mock will print the elements
recursively.  CharStarAsPointer tells Google Mock to treat elements of
char* type as pointers.

You may also wonder why we don't just ask the user to define a <<
operator or PrintTo() for the BufferTable class.  The problem is that
often a container type is general-purpose and the char* elements it
carries may be C strings in one API and pointers in another.  Since we
can only define one << or PrintTo() for each type, it's not
fine-grained enough.

Original issue reported on code.google.com by zhanyong...@gmail.com on 22 May 2009 at 4:31

GoogleCodeExporter commented 9 years ago
This design requires all users of the mock class to use the same
formatter for any given parameter/return value.  Consider an
alternative design that allows different instances of the same mock
class to use different formatters.

Original comment by zhanyong...@gmail.com on 5 Jun 2009 at 12:19

GoogleCodeExporter commented 9 years ago

Original comment by zhanyong...@gmail.com on 21 Sep 2009 at 11:35

GoogleCodeExporter commented 9 years ago
Too complex to be worth it.

Original comment by w...@google.com on 6 Mar 2010 at 5:42