Avoids unnecessary printing of call into to internal buffers;

Made the universal value printer safer when printing char[];
Removed duplicated code in InvokeWith;
Improved gmock_doctor.py.
This commit is contained in:
zhanyong.wan
2009-05-29 19:50:06 +00:00
parent 16cf473930
commit 9413f2ff61
10 changed files with 390 additions and 275 deletions

View File

@@ -580,6 +580,41 @@ class UniversalPrinter {
#endif // _MSC_VER
};
// UniversalPrintArray(begin, len, os) prints an array of 'len'
// elements, starting at address 'begin'.
template <typename T>
void UniversalPrintArray(const T* begin, size_t len, ::std::ostream* os) {
if (len == 0) {
*os << "{}";
} else {
*os << "{ ";
const size_t kThreshold = 18;
const size_t kChunkSize = 8;
// If the array has more than kThreshold elements, we'll have to
// omit some details by printing only the first and the last
// kChunkSize elements.
// TODO(wan@google.com): let the user control the threshold using a flag.
if (len <= kThreshold) {
PrintRawArrayTo(begin, len, os);
} else {
PrintRawArrayTo(begin, kChunkSize, os);
*os << ", ..., ";
PrintRawArrayTo(begin + len - kChunkSize, kChunkSize, os);
}
*os << " }";
}
}
// This overload prints a (const) char array compactly.
void UniversalPrintArray(const char* begin, size_t len, ::std::ostream* os);
// Prints an array of 'len' elements, starting at address 'begin', to a string.
template <typename T>
string UniversalPrintArrayToString(const T* begin, size_t len) {
::std::stringstream ss;
UniversalPrintArray(begin, len, &ss);
return ss.str();
}
// Implements printing an array type T[N].
template <typename T, size_t N>
class UniversalPrinter<T[N]> {
@@ -587,41 +622,13 @@ class UniversalPrinter<T[N]> {
// Prints the given array, omitting some elements when there are too
// many.
static void Print(const T (&a)[N], ::std::ostream* os) {
// Prints a char array as a C string. Note that we compare 'const
// T' with 'const char' instead of comparing T with char, in case
// that T is already a const type.
if (internal::type_equals<const T, const char>::value) {
UniversalPrinter<const T*>::Print(a, os);
return;
}
if (N == 0) {
*os << "{}";
} else {
*os << "{ ";
const size_t kThreshold = 18;
const size_t kChunkSize = 8;
// If the array has more than kThreshold elements, we'll have to
// omit some details by printing only the first and the last
// kChunkSize elements.
// TODO(wan): let the user control the threshold using a flag.
if (N <= kThreshold) {
PrintRawArrayTo(a, N, os);
} else {
PrintRawArrayTo(a, kChunkSize, os);
*os << ", ..., ";
PrintRawArrayTo(a + N - kChunkSize, kChunkSize, os);
}
*os << " }";
}
UniversalPrintArray(a, N, os);
}
// A convenient wrapper for Print() that returns the print-out as a
// string.
static string PrintToString(const T (&a)[N]) {
::std::stringstream ss;
Print(a, &ss);
return ss.str();
return UniversalPrintArrayToString(a, N);
}
};

View File

@@ -93,10 +93,6 @@ class ExpectationTester;
template <typename F>
class FunctionMockerBase;
// Helper class for implementing FunctionMockerBase<F>::InvokeWith().
template <typename Result, typename F>
class InvokeWithHelper;
// Protects the mock object registry (in class Mock), all function
// mockers, and all expectations.
//
@@ -269,9 +265,6 @@ class Mock {
template <typename F>
friend class internal::FunctionMockerBase;
template <typename R, typename Args>
friend class internal::InvokeWithHelper;
template <typename M>
friend class NiceMock;
@@ -763,9 +756,6 @@ class Expectation : public ExpectationBase {
template <typename Function>
friend class FunctionMockerBase;
template <typename R, typename Function>
friend class InvokeWithHelper;
// The following methods will be called only after the EXPECT_CALL()
// statement finishes and when the current thread holds
// g_gmock_mutex.
@@ -1042,6 +1032,78 @@ class MockSpec {
#pragma warning(disable:4355) // Temporarily disables warning 4355.
#endif // _MSV_VER
// C++ treats the void type specially. For example, you cannot define
// a void-typed variable or pass a void value to a function.
// ActionResultHolder<T> holds a value of type T, where T must be a
// copyable type or void (T doesn't need to be default-constructable).
// It hides the syntactic difference between void and other types, and
// is used to unify the code for invoking both void-returning and
// non-void-returning mock functions. This generic definition is used
// when T is not void.
template <typename T>
class ActionResultHolder {
public:
explicit ActionResultHolder(T value) : value_(value) {}
// The compiler-generated copy constructor and assignment operator
// are exactly what we need, so we don't need to define them.
T value() const { return value_; }
// Prints the held value as an action's result to os.
void PrintAsActionResult(::std::ostream* os) const {
*os << "\n Returns: ";
UniversalPrinter<T>::Print(value_, os);
}
// Performs the given mock function's default action and returns the
// result in a ActionResultHolder.
template <typename Function, typename Arguments>
static ActionResultHolder PerformDefaultAction(
const FunctionMockerBase<Function>* func_mocker,
const Arguments& args,
const string& call_description) {
return ActionResultHolder(
func_mocker->PerformDefaultAction(args, call_description));
}
// Performs the given action and returns the result in a
// ActionResultHolder.
template <typename Function, typename Arguments>
static ActionResultHolder PerformAction(const Action<Function>& action,
const Arguments& args) {
return ActionResultHolder(action.Perform(args));
}
private:
T value_;
};
// Specialization for T = void.
template <>
class ActionResultHolder<void> {
public:
ActionResultHolder() {}
void value() const {}
void PrintAsActionResult(::std::ostream* /* os */) const {}
template <typename Function, typename Arguments>
static ActionResultHolder PerformDefaultAction(
const FunctionMockerBase<Function>* func_mocker,
const Arguments& args,
const string& call_description) {
func_mocker->PerformDefaultAction(args, call_description);
return ActionResultHolder();
}
template <typename Function, typename Arguments>
static ActionResultHolder PerformAction(const Action<Function>& action,
const Arguments& args) {
action.Perform(args);
return ActionResultHolder();
}
};
// The base of the function mocker class for the given function type.
// We put the methods in this class instead of its child to avoid code
// bloat.
@@ -1167,16 +1229,11 @@ class FunctionMockerBase : public UntypedFunctionMockerBase {
template <typename Function>
friend class MockSpec;
template <typename R, typename Function>
friend class InvokeWithHelper;
// Returns the result of invoking this mock function with the given
// arguments. This function can be safely called from multiple
// threads concurrently.
// L < g_gmock_mutex
Result InvokeWith(const ArgumentTuple& args) {
return InvokeWithHelper<Result, F>::InvokeAndPrintResult(this, args);
}
Result InvokeWith(const ArgumentTuple& args);
// Adds and returns a default action spec for this mock function.
// L < g_gmock_mutex
@@ -1417,170 +1474,109 @@ bool FunctionMockerBase<F>::VerifyAndClearExpectationsLocked() {
// manner specified by 'reaction'.
void ReportUninterestingCall(CallReaction reaction, const string& msg);
// When an uninteresting or unexpected mock function is called, we
// want to print its return value to assist the user debugging. Since
// there's nothing to print when the function returns void, we need to
// specialize the logic of FunctionMockerBase<F>::InvokeWith() for
// void return values.
//
// C++ doesn't allow us to specialize a member function template
// unless we also specialize its enclosing class, so we had to let
// InvokeWith() delegate its work to a helper class InvokeWithHelper,
// which can then be specialized.
//
// Note that InvokeWithHelper must be a class template (as opposed to
// a function template), as only class templates can be partially
// specialized.
template <typename Result, typename F>
class InvokeWithHelper {
public:
typedef typename Function<F>::ArgumentTuple ArgumentTuple;
// Calculates the result of invoking the function mocked by mocker
// with the given arguments, prints it, and returns it.
// L < g_gmock_mutex
static Result InvokeAndPrintResult(
FunctionMockerBase<F>* mocker,
const ArgumentTuple& args) {
if (mocker->expectations_.size() == 0) {
// No expectation is set on this mock method - we have an
// uninteresting call.
// Warns about the uninteresting call.
::std::stringstream ss;
mocker->DescribeUninterestingCall(args, &ss);
// We must get Google Mock's reaction on uninteresting calls
// made on this mock object BEFORE performing the action,
// because the action may DELETE the mock object and make the
// following expression meaningless.
const CallReaction reaction =
Mock::GetReactionOnUninterestingCalls(mocker->MockObject());
// Calculates the function result.
Result result = mocker->PerformDefaultAction(args, ss.str());
// Prints the function result.
ss << "\n Returns: ";
UniversalPrinter<Result>::Print(result, &ss);
ReportUninterestingCall(reaction, ss.str());
return result;
}
bool is_excessive = false;
::std::stringstream ss;
::std::stringstream why;
::std::stringstream loc;
Action<F> action;
Expectation<F>* exp;
// The FindMatchingExpectationAndAction() function acquires and
// releases g_gmock_mutex.
const bool found = mocker->FindMatchingExpectationAndAction(
args, &exp, &action, &is_excessive, &ss, &why);
ss << " Function call: " << mocker->Name();
UniversalPrinter<ArgumentTuple>::Print(args, &ss);
// In case the action deletes a piece of the expectation, we
// generate the message beforehand.
if (found && !is_excessive) {
exp->DescribeLocationTo(&loc);
}
Result result = action.IsDoDefault() ?
mocker->PerformDefaultAction(args, ss.str())
: action.Perform(args);
ss << "\n Returns: ";
UniversalPrinter<Result>::Print(result, &ss);
ss << "\n" << why.str();
if (found) {
if (is_excessive) {
// We had an upper-bound violation and the failure message is in ss.
Expect(false, exp->file(), exp->line(), ss.str());
} else {
// We had an expected call and the matching expectation is
// described in ss.
Log(INFO, loc.str() + ss.str(), 3);
}
} else {
// No expectation matches this call - reports a failure.
Expect(false, NULL, -1, ss.str());
}
return result;
}
}; // class InvokeWithHelper
// This specialization helps to implement
// FunctionMockerBase<F>::InvokeWith() for void-returning functions.
// Calculates the result of invoking this mock function with the given
// arguments, prints it, and returns it.
// L < g_gmock_mutex
template <typename F>
class InvokeWithHelper<void, F> {
public:
typedef typename Function<F>::ArgumentTuple ArgumentTuple;
typename Function<F>::Result FunctionMockerBase<F>::InvokeWith(
const typename Function<F>::ArgumentTuple& args) {
typedef ActionResultHolder<Result> ResultHolder;
// Invokes the function mocked by mocker with the given arguments.
// L < g_gmock_mutex
static void InvokeAndPrintResult(FunctionMockerBase<F>* mocker,
const ArgumentTuple& args) {
const int count = static_cast<int>(mocker->expectations_.size());
if (count == 0) {
// No expectation is set on this mock method - we have an
// uninteresting call.
::std::stringstream ss;
mocker->DescribeUninterestingCall(args, &ss);
if (expectations_.size() == 0) {
// No expectation is set on this mock method - we have an
// uninteresting call.
// We must get Google Mock's reaction on uninteresting calls
// made on this mock object BEFORE performing the action,
// because the action may DELETE the mock object and make the
// following expression meaningless.
const CallReaction reaction =
Mock::GetReactionOnUninterestingCalls(mocker->MockObject());
// We must get Google Mock's reaction on uninteresting calls
// made on this mock object BEFORE performing the action,
// because the action may DELETE the mock object and make the
// following expression meaningless.
const CallReaction reaction =
Mock::GetReactionOnUninterestingCalls(MockObject());
mocker->PerformDefaultAction(args, ss.str());
ReportUninterestingCall(reaction, ss.str());
return;
// True iff we need to print this call's arguments and return
// value. This definition must be kept in sync with
// the behavior of ReportUninterestingCall().
const bool need_to_report_uninteresting_call =
// If the user allows this uninteresting call, we print it
// only when he wants informational messages.
reaction == ALLOW ? LogIsVisible(INFO) :
// If the user wants this to be a warning, we print it only
// when he wants to see warnings.
reaction == WARN ? LogIsVisible(WARNING) :
// Otherwise, the user wants this to be an error, and we
// should always print detailed information in the error.
true;
if (!need_to_report_uninteresting_call) {
// Perform the action without printing the call information.
return PerformDefaultAction(args, "");
}
bool is_excessive = false;
// Warns about the uninteresting call.
::std::stringstream ss;
::std::stringstream why;
::std::stringstream loc;
Action<F> action;
Expectation<F>* exp;
DescribeUninterestingCall(args, &ss);
// The FindMatchingExpectationAndAction() function acquires and
// releases g_gmock_mutex.
const bool found = mocker->FindMatchingExpectationAndAction(
args, &exp, &action, &is_excessive, &ss, &why);
ss << " Function call: " << mocker->Name();
UniversalPrinter<ArgumentTuple>::Print(args, &ss);
ss << "\n" << why.str();
// In case the action deletes a piece of the expectation, we
// generate the message beforehand.
if (found && !is_excessive) {
exp->DescribeLocationTo(&loc);
}
if (action.IsDoDefault()) {
mocker->PerformDefaultAction(args, ss.str());
} else {
action.Perform(args);
}
// Calculates the function result.
const ResultHolder result =
ResultHolder::PerformDefaultAction(this, args, ss.str());
if (found) {
// A matching expectation and corresponding action were found.
if (is_excessive) {
// We had an upper-bound violation and the failure message is in ss.
Expect(false, exp->file(), exp->line(), ss.str());
} else {
// We had an expected call and the matching expectation is
// described in ss.
Log(INFO, loc.str() + ss.str(), 3);
}
} else {
// No matching expectation was found - reports an error.
Expect(false, NULL, -1, ss.str());
}
// Prints the function result.
result.PrintAsActionResult(&ss);
ReportUninterestingCall(reaction, ss.str());
return result.value();
}
}; // class InvokeWithHelper<void, F>
bool is_excessive = false;
::std::stringstream ss;
::std::stringstream why;
::std::stringstream loc;
Action<F> action;
Expectation<F>* exp;
// The FindMatchingExpectationAndAction() function acquires and
// releases g_gmock_mutex.
const bool found = FindMatchingExpectationAndAction(
args, &exp, &action, &is_excessive, &ss, &why);
// True iff we need to print the call's arguments and return value.
// This definition must be kept in sync with the uses of Expect()
// and Log() in this function.
const bool need_to_report_call = !found || is_excessive || LogIsVisible(INFO);
if (!need_to_report_call) {
// Perform the action without printing the call information.
return action.IsDoDefault() ? PerformDefaultAction(args, "") :
action.Perform(args);
}
ss << " Function call: " << Name();
UniversalPrinter<ArgumentTuple>::Print(args, &ss);
// In case the action deletes a piece of the expectation, we
// generate the message beforehand.
if (found && !is_excessive) {
exp->DescribeLocationTo(&loc);
}
const ResultHolder result = action.IsDoDefault() ?
ResultHolder::PerformDefaultAction(this, args, ss.str()) :
ResultHolder::PerformAction(action, args);
result.PrintAsActionResult(&ss);
ss << "\n" << why.str();
if (!found) {
// No expectation matches this call - reports a failure.
Expect(false, NULL, -1, ss.str());
} else if (is_excessive) {
// We had an upper-bound violation and the failure message is in ss.
Expect(false, exp->file(), exp->line(), ss.str());
} else {
// We had an expected call and the matching expectation is
// described in ss.
Log(INFO, loc.str() + ss.str(), 2);
}
return result.value();
}
} // namespace internal

View File

@@ -438,6 +438,10 @@ const char kWarningVerbosity[] = "warning";
// No logs are printed.
const char kErrorVerbosity[] = "error";
// Returns true iff a log with the given severity is visible according
// to the --gmock_verbose flag.
bool LogIsVisible(LogSeverity severity);
// Prints the given message to stdout iff 'severity' >= the level
// specified by the --gmock_verbose flag. If stack_frames_to_skip >=
// 0, also prints the stack trace excluding the top