diff options
Diffstat (limited to 'include')
-rw-r--r-- | include/gmock/gmock-printers.h | 67 | ||||
-rw-r--r-- | include/gmock/gmock-spec-builders.h | 348 | ||||
-rw-r--r-- | include/gmock/internal/gmock-internal-utils.h | 4 |
3 files changed, 213 insertions, 206 deletions
diff --git a/include/gmock/gmock-printers.h b/include/gmock/gmock-printers.h index 99002434..e233ef3e 100644 --- a/include/gmock/gmock-printers.h +++ b/include/gmock/gmock-printers.h @@ -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); } }; diff --git a/include/gmock/gmock-spec-builders.h b/include/gmock/gmock-spec-builders.h index cc48bc0b..d4578ac7 100644 --- a/include/gmock/gmock-spec-builders.h +++ b/include/gmock/gmock-spec-builders.h @@ -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; - - // 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); - - // 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()); - - mocker->PerformDefaultAction(args, ss.str()); - ReportUninterestingCall(reaction, ss.str()); - return; +typename Function<F>::Result FunctionMockerBase<F>::InvokeWith( + const typename Function<F>::ArgumentTuple& args) { + typedef ActionResultHolder<Result> ResultHolder; + + 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(MockObject()); + + // 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; - - // 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); - } - - 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()); - } - } -}; // class InvokeWithHelper<void, F> + DescribeUninterestingCall(args, &ss); + + // Calculates the function result. + const ResultHolder result = + ResultHolder::PerformDefaultAction(this, args, ss.str()); + + // Prints the function result. + result.PrintAsActionResult(&ss); + + ReportUninterestingCall(reaction, ss.str()); + return result.value(); + } + + 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 diff --git a/include/gmock/internal/gmock-internal-utils.h b/include/gmock/internal/gmock-internal-utils.h index b02682f8..b5e38db3 100644 --- a/include/gmock/internal/gmock-internal-utils.h +++ b/include/gmock/internal/gmock-internal-utils.h @@ -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 |