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:
@@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user