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 +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 +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 class UniversalPrinter { @@ -587,41 +622,13 @@ class UniversalPrinter { // 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::value) { - UniversalPrinter::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 class FunctionMockerBase; -// Helper class for implementing FunctionMockerBase::InvokeWith(). -template -class InvokeWithHelper; - // Protects the mock object registry (in class Mock), all function // mockers, and all expectations. // @@ -269,9 +265,6 @@ class Mock { template friend class internal::FunctionMockerBase; - template - friend class internal::InvokeWithHelper; - template friend class NiceMock; @@ -763,9 +756,6 @@ class Expectation : public ExpectationBase { template friend class FunctionMockerBase; - template - 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 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 +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::Print(value_, os); + } + + // Performs the given mock function's default action and returns the + // result in a ActionResultHolder. + template + static ActionResultHolder PerformDefaultAction( + const FunctionMockerBase* 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 + static ActionResultHolder PerformAction(const Action& action, + const Arguments& args) { + return ActionResultHolder(action.Perform(args)); + } + + private: + T value_; +}; + +// Specialization for T = void. +template <> +class ActionResultHolder { + public: + ActionResultHolder() {} + void value() const {} + void PrintAsActionResult(::std::ostream* /* os */) const {} + + template + static ActionResultHolder PerformDefaultAction( + const FunctionMockerBase* func_mocker, + const Arguments& args, + const string& call_description) { + func_mocker->PerformDefaultAction(args, call_description); + return ActionResultHolder(); + } + + template + static ActionResultHolder PerformAction(const Action& 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 friend class MockSpec; - template - 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::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::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::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 -class InvokeWithHelper { - public: - typedef typename Function::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* 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::Print(result, &ss); - ReportUninterestingCall(reaction, ss.str()); - - return result; - } - - bool is_excessive = false; - ::std::stringstream ss; - ::std::stringstream why; - ::std::stringstream loc; - Action action; - Expectation* 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::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::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::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 -class InvokeWithHelper { - public: - typedef typename Function::ArgumentTuple ArgumentTuple; +typename Function::Result FunctionMockerBase::InvokeWith( + const typename Function::ArgumentTuple& args) { + typedef ActionResultHolder ResultHolder; - // Invokes the function mocked by mocker with the given arguments. - // L < g_gmock_mutex - static void InvokeAndPrintResult(FunctionMockerBase* mocker, - const ArgumentTuple& args) { - const int count = static_cast(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 action; - Expectation* 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::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 + + bool is_excessive = false; + ::std::stringstream ss; + ::std::stringstream why; + ::std::stringstream loc; + Action action; + Expectation* 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::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 diff --git a/scripts/gmock_doctor.py b/scripts/gmock_doctor.py index 907089e9..1980977c 100755 --- a/scripts/gmock_doctor.py +++ b/scripts/gmock_doctor.py @@ -36,7 +36,7 @@ __author__ = 'wan@google.com (Zhanyong Wan)' import re import sys -_VERSION = '1.0.0' +_VERSION = '1.0.1' _COMMON_GMOCK_SYMBOLS = [ # Matchers @@ -46,8 +46,12 @@ _COMMON_GMOCK_SYMBOLS = [ 'AllOf', 'An', 'AnyOf', + 'ContainerEq', + 'Contains', 'ContainsRegex', 'DoubleEq', + 'ElementsAre', + 'ElementsAreArray', 'EndsWith', 'Eq', 'Field', @@ -60,6 +64,8 @@ _COMMON_GMOCK_SYMBOLS = [ 'Lt', 'MatcherCast', 'MatchesRegex', + 'NanSensitiveDoubleEq', + 'NanSensitiveFloatEq', 'Ne', 'Not', 'NotNull', @@ -67,6 +73,8 @@ _COMMON_GMOCK_SYMBOLS = [ 'PointeeIsInitializedProto', 'Property', 'Ref', + 'ResultOf', + 'SafeMatcherCast', 'StartsWith', 'StrCaseEq', 'StrCaseNe', @@ -76,7 +84,9 @@ _COMMON_GMOCK_SYMBOLS = [ 'TypedEq', # Actions + 'Assign', 'ByRef', + 'DeleteArg', 'DoAll', 'DoDefault', 'IgnoreResult', @@ -84,11 +94,18 @@ _COMMON_GMOCK_SYMBOLS = [ 'InvokeArgument', 'InvokeWithoutArgs', 'Return', + 'ReturnNew', 'ReturnNull', 'ReturnRef', + 'SaveArg', + 'SetArgReferee', 'SetArgumentPointee', 'SetArrayArgument', + 'SetErrnoAndReturn', + 'Throw', + 'WithArg', 'WithArgs', + 'WithoutArgs', # Cardinalities 'AnyNumber', @@ -106,6 +123,9 @@ _COMMON_GMOCK_SYMBOLS = [ 'Mock', ] +# Regex for matching source file path and line number in gcc's errors. +_FILE_LINE_RE = r'(?P.*):(?P\d+):\s+' + def _FindAllMatches(regex, s): """Generates all matches of regex in string s.""" @@ -128,6 +148,7 @@ def _GenericDiagnoser(short_name, long_name, regex, diagnosis, msg): (short name of disease, long name of disease, diagnosis). """ + diagnosis = '%(file)s:%(line)s:' + diagnosis for m in _FindAllMatches(regex, msg): yield (short_name, long_name, diagnosis % m.groupdict()) @@ -136,9 +157,9 @@ def _NeedToReturnReferenceDiagnoser(msg): """Diagnoses the NRR disease, given the error messages by gcc.""" regex = (r'In member function \'testing::internal::ReturnAction.*\n' - r'(?P.*):(?P\d+):\s+instantiated from here\n' + + _FILE_LINE_RE + r'instantiated from here\n' r'.*gmock-actions\.h.*error: creating array with negative size') - diagnosis = """%(file)s:%(line)s: + diagnosis = """ You are using an Return() action in a function that returns a reference. Please use ReturnRef() instead.""" return _GenericDiagnoser('NRR', 'Need to Return Reference', @@ -148,11 +169,11 @@ Please use ReturnRef() instead.""" def _NeedToReturnSomethingDiagnoser(msg): """Diagnoses the NRS disease, given the error messages by gcc.""" - regex = (r'(?P.*):(?P\d+):\s+' + regex = (_FILE_LINE_RE + r'(instantiated from here\n.' r'*gmock-actions\.h.*error: void value not ignored)' r'|(error: control reaches end of non-void function)') - diagnosis = """%(file)s:%(line)s: + diagnosis = """ You are using an action that returns void, but it needs to return *something*. Please tell it *what* to return. Perhaps you can use the pattern DoAll(some_action, Return(some_value))?""" @@ -163,10 +184,10 @@ the pattern DoAll(some_action, Return(some_value))?""" def _NeedToReturnNothingDiagnoser(msg): """Diagnoses the NRN disease, given the error messages by gcc.""" - regex = (r'(?P.*):(?P\d+):\s+instantiated from here\n' + regex = (_FILE_LINE_RE + r'instantiated from here\n' r'.*gmock-actions\.h.*error: return-statement with a value, ' r'in function returning \'void\'') - diagnosis = """%(file)s:%(line)s: + diagnosis = """ You are using an action that returns *something*, but it needs to return void. Please use a void-returning action instead. @@ -179,10 +200,10 @@ to re-arrange the order of actions in a DoAll(), if you are using one?""" def _IncompleteByReferenceArgumentDiagnoser(msg): """Diagnoses the IBRA disease, given the error messages by gcc.""" - regex = (r'(?P.*):(?P\d+):\s+instantiated from here\n' + regex = (_FILE_LINE_RE + r'instantiated from here\n' r'.*gmock-printers\.h.*error: invalid application of ' r'\'sizeof\' to incomplete type \'(?P.*)\'') - diagnosis = """%(file)s:%(line)s: + diagnosis = """ In order to mock this function, Google Mock needs to see the definition of type "%(type)s" - declaration alone is not enough. Either #include the header that defines it, or change the argument to be passed @@ -194,9 +215,9 @@ by pointer.""" def _OverloadedFunctionMatcherDiagnoser(msg): """Diagnoses the OFM disease, given the error messages by gcc.""" - regex = (r'(?P.*):(?P\d+): error: no matching function for ' + regex = (_FILE_LINE_RE + r'error: no matching function for ' r'call to \'Truly\(\)') - diagnosis = """%(file)s:%(line)s: + diagnosis = """ The argument you gave to Truly() is an overloaded function. Please tell gcc which overloaded version you want to use. @@ -211,10 +232,9 @@ you should write def _OverloadedFunctionActionDiagnoser(msg): """Diagnoses the OFA disease, given the error messages by gcc.""" - regex = (r'(?P.*):(?P\d+): error: ' - r'no matching function for call to \'Invoke\(' + regex = (_FILE_LINE_RE + r'error: no matching function for call to \'Invoke\(' r'') - diagnosis = """%(file)s:%(line)s: + diagnosis = """ You are passing an overloaded function to Invoke(). Please tell gcc which overloaded version you want to use. @@ -229,10 +249,10 @@ you should write something like def _OverloadedMethodActionDiagnoser1(msg): """Diagnoses the OMA disease, given the error messages by gcc.""" - regex = (r'(?P.*):(?P\d+): error: ' + regex = (_FILE_LINE_RE + r'error: ' r'.*no matching function for call to \'Invoke\(.*, ' r'unresolved overloaded function type>') - diagnosis = """%(file)s:%(line)s: + diagnosis = """ The second argument you gave to Invoke() is an overloaded method. Please tell gcc which overloaded version you want to use. @@ -250,10 +270,10 @@ you should write something like def _MockObjectPointerDiagnoser(msg): """Diagnoses the MOP disease, given the error messages by gcc.""" - regex = (r'(?P.*):(?P\d+): error: request for member ' + regex = (_FILE_LINE_RE + r'error: request for member ' r'\'gmock_(?P.+)\' in \'(?P.+)\', ' r'which is of non-class type \'(.*::)*(?P.+)\*\'') - diagnosis = """%(file)s:%(line)s: + diagnosis = """ The first argument to ON_CALL() and EXPECT_CALL() must be a mock *object*, not a *pointer* to it. Please write '*(%(mock_object)s)' instead of '%(mock_object)s' as your first argument. @@ -279,9 +299,9 @@ you should use the EXPECT_CALL like this: def _OverloadedMethodActionDiagnoser2(msg): """Diagnoses the OMA disease, given the error messages by gcc.""" - regex = (r'(?P.*):(?P\d+): error: no matching function for ' + regex = (_FILE_LINE_RE + r'error: no matching function for ' r'call to \'Invoke\(.+, \)') - diagnosis = """%(file)s:%(line)s: + diagnosis = """ The second argument you gave to Invoke() is an overloaded method. Please tell gcc which overloaded version you want to use. @@ -299,9 +319,9 @@ you should write something like def _NeedToUseSymbolDiagnoser(msg): """Diagnoses the NUS disease, given the error messages by gcc.""" - regex = (r'(?P.*):(?P\d+): error: \'(?P.+)\' ' + regex = (_FILE_LINE_RE + r'error: \'(?P.+)\' ' r'(was not declared in this scope|has not been declared)') - diagnosis = """%(file)s:%(line)s: + diagnosis = """ '%(symbol)s' is defined by Google Mock in the testing namespace. Did you forget to write using testing::%(symbol)s; @@ -315,11 +335,10 @@ Did you forget to write def _NeedToUseReturnNullDiagnoser(msg): """Diagnoses the NRNULL disease, given the error messages by gcc.""" - regex = (r'(?P.*):(?P\d+):\s+instantiated from here\n' + regex = (_FILE_LINE_RE + r'instantiated from here\n' r'.*gmock-actions\.h.*error: invalid conversion from ' r'\'long int\' to \'(?P.+\*)') - - diagnosis = """%(file)s:%(line)s: + diagnosis = """ You are probably calling Return(NULL) and the compiler isn't sure how to turn NULL into a %(type)s*. Use ReturnNull() instead. Note: the line number may be off; please fix all instances of Return(NULL).""" @@ -330,13 +349,11 @@ Note: the line number may be off; please fix all instances of Return(NULL).""" def _WrongMockMethodMacroDiagnoser(msg): """Diagnoses the WMM disease, given the error messages by gcc.""" - regex = (r'(?P.*):(?P\d+):\s+' + regex = (_FILE_LINE_RE + r'.*this_method_does_not_take_(?P\d+)_argument.*\n' r'.*\n' - r'.*candidates are.*FunctionMocker<[^>]+A(?P\d+)\)>' - ) - - diagnosis = """%(file)s:%(line)s: + r'.*candidates are.*FunctionMocker<[^>]+A(?P\d+)\)>') + diagnosis = """ You are using MOCK_METHOD%(wrong_args)s to define a mock method that has %(args)s arguments. Use MOCK_METHOD%(args)s (or MOCK_CONST_METHOD%(args)s, MOCK_METHOD%(args)s_T, MOCK_CONST_METHOD%(args)s_T as appropriate) instead.""" @@ -344,6 +361,21 @@ MOCK_METHOD%(args)s_T, MOCK_CONST_METHOD%(args)s_T as appropriate) instead.""" regex, diagnosis, msg) +def _WrongParenPositionDiagnoser(msg): + """Diagnoses the WPP disease, given the error messages by gcc.""" + + regex = (_FILE_LINE_RE + + r'error:.*testing::internal::MockSpec<.* has no member named \'' + r'(?P\w+)\'') + diagnosis = """ +The closing parenthesis of ON_CALL or EXPECT_CALL should be *before* +".%(method)s". For example, you should write: + EXPECT_CALL(my_mock, Foo(_)).%(method)s(...); +instead of: + EXPECT_CALL(my_mock, Foo(_).%(method)s(...));""" + return _GenericDiagnoser('WPP', 'Wrong parenthesis position', + regex, diagnosis, msg) + _DIAGNOSERS = [ _IncompleteByReferenceArgumentDiagnoser, @@ -358,6 +390,7 @@ _DIAGNOSERS = [ _OverloadedMethodActionDiagnoser1, _OverloadedMethodActionDiagnoser2, _WrongMockMethodMacroDiagnoser, + _WrongParenPositionDiagnoser, ] diff --git a/src/gmock-internal-utils.cc b/src/gmock-internal-utils.cc index 735abce5..ce17d715 100644 --- a/src/gmock-internal-utils.cc +++ b/src/gmock-internal-utils.cc @@ -101,6 +101,22 @@ FailureReporterInterface* GetFailureReporter() { // Protects global resources (stdout in particular) used by Log(). static Mutex g_log_mutex(Mutex::NO_CONSTRUCTOR_NEEDED_FOR_STATIC_MUTEX); +// Returns true iff a log with the given severity is visible according +// to the --gmock_verbose flag. +bool LogIsVisible(LogSeverity severity) { + if (GMOCK_FLAG(verbose) == kInfoVerbosity) { + // Always show the log if --gmock_verbose=info. + return true; + } else if (GMOCK_FLAG(verbose) == kErrorVerbosity) { + // Always hide it if --gmock_verbose=error. + return false; + } else { + // If --gmock_verbose is neither "info" nor "error", we treat it + // as "warning" (its default value). + return severity == WARNING; + } +} + // 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 @@ -110,17 +126,8 @@ static Mutex g_log_mutex(Mutex::NO_CONSTRUCTOR_NEEDED_FOR_STATIC_MUTEX); // conservative. void Log(LogSeverity severity, const string& message, int stack_frames_to_skip) { - if (GMOCK_FLAG(verbose) == kErrorVerbosity) { - // The user is not interested in logs. + if (!LogIsVisible(severity)) return; - } else if (GMOCK_FLAG(verbose) != kInfoVerbosity) { - // The user is interested in warnings but not informational logs. - // Note that invalid values of GMOCK_FLAG(verbose) are treated as - // "warning", which is the default value of the flag. - if (severity == INFO) { - return; - } - } // Ensures that logs from different threads don't interleave. MutexLock l(&g_log_mutex); diff --git a/src/gmock-printers.cc b/src/gmock-printers.cc index e6d4001a..922a7b2d 100644 --- a/src/gmock-printers.cc +++ b/src/gmock-printers.cc @@ -242,6 +242,11 @@ static void PrintCharsAsStringTo(const char* begin, size_t len, ostream* os) { *os << "\""; } +// Prints a (const) char array of 'len' elements, starting at address 'begin'. +void UniversalPrintArray(const char* begin, size_t len, ostream* os) { + PrintCharsAsStringTo(begin, len, os); +} + // Prints the given array of wide characters to the ostream. // The array starts at *begin, the length is len, it may include L'\0' // characters and may not be null-terminated. diff --git a/src/gmock-spec-builders.cc b/src/gmock-spec-builders.cc index 65a74b81..465e4d63 100644 --- a/src/gmock-spec-builders.cc +++ b/src/gmock-spec-builders.cc @@ -139,10 +139,10 @@ ThreadLocal g_gmock_implicit_sequence; void ReportUninterestingCall(CallReaction reaction, const string& msg) { switch (reaction) { case ALLOW: - Log(INFO, msg, 4); + Log(INFO, msg, 3); break; case WARN: - Log(WARNING, msg, 4); + Log(WARNING, msg, 3); break; default: // FAIL Expect(false, NULL, -1, msg); diff --git a/test/gmock-internal-utils_test.cc b/test/gmock-internal-utils_test.cc index 5e4dc030..7886f6d3 100644 --- a/test/gmock-internal-utils_test.cc +++ b/test/gmock-internal-utils_test.cc @@ -494,6 +494,34 @@ TEST(ExpectTest, FailsNonfatallyOnFalse) { }, "Expectation failed"); } +// Tests LogIsVisible(). + +class LogIsVisibleTest : public ::testing::Test { + protected: + virtual void SetUp() { original_verbose_ = GMOCK_FLAG(verbose); } + virtual void TearDown() { GMOCK_FLAG(verbose) = original_verbose_; } + + string original_verbose_; +}; + +TEST_F(LogIsVisibleTest, AlwaysReturnsTrueIfVerbosityIsInfo) { + GMOCK_FLAG(verbose) = kInfoVerbosity; + EXPECT_TRUE(LogIsVisible(INFO)); + EXPECT_TRUE(LogIsVisible(WARNING)); +} + +TEST_F(LogIsVisibleTest, AlwaysReturnsFalseIfVerbosityIsError) { + GMOCK_FLAG(verbose) = kErrorVerbosity; + EXPECT_FALSE(LogIsVisible(INFO)); + EXPECT_FALSE(LogIsVisible(WARNING)); +} + +TEST_F(LogIsVisibleTest, WorksWhenVerbosityIsWarning) { + GMOCK_FLAG(verbose) = kWarningVerbosity; + EXPECT_FALSE(LogIsVisible(INFO)); + EXPECT_TRUE(LogIsVisible(WARNING)); +} + // TODO(wan@google.com): find a way to re-enable these tests. #if 0 diff --git a/test/gmock-printers_test.cc b/test/gmock-printers_test.cc index 29a0db84..f03be292 100644 --- a/test/gmock-printers_test.cc +++ b/test/gmock-printers_test.cc @@ -485,75 +485,58 @@ TEST(PrintPointerTest, MemberFunctionPointer) { // Tests printing C arrays. -// One-dimensional array. - -void ArrayHelper1(int (&a)[5]) { // NOLINT - EXPECT_EQ("{ 1, 2, 3, 4, 5 }", Print(a)); +// The difference between this and Print() is that it ensures that the +// argument is a reference to an array. +template +string PrintArrayHelper(T (&a)[N]) { + return Print(a); } +// One-dimensional array. TEST(PrintArrayTest, OneDimensionalArray) { int a[5] = { 1, 2, 3, 4, 5 }; - ArrayHelper1(a); + EXPECT_EQ("{ 1, 2, 3, 4, 5 }", PrintArrayHelper(a)); } // Two-dimensional array. - -void ArrayHelper2(int (&a)[2][5]) { // NOLINT - EXPECT_EQ("{ { 1, 2, 3, 4, 5 }, { 6, 7, 8, 9, 0 } }", Print(a)); -} - TEST(PrintArrayTest, TwoDimensionalArray) { int a[2][5] = { { 1, 2, 3, 4, 5 }, { 6, 7, 8, 9, 0 } }; - ArrayHelper2(a); + EXPECT_EQ("{ { 1, 2, 3, 4, 5 }, { 6, 7, 8, 9, 0 } }", PrintArrayHelper(a)); } // Array of const elements. - -void ArrayHelper3(const bool (&a)[1]) { // NOLINT - EXPECT_EQ("{ false }", Print(a)); -} - TEST(PrintArrayTest, ConstArray) { const bool a[1] = { false }; - ArrayHelper3(a); + EXPECT_EQ("{ false }", PrintArrayHelper(a)); } // Char array. - -void ArrayHelper4(char (&a)[3]) { // NOLINT - EXPECT_EQ(PrintPointer(a) + " pointing to \"Hi\"", Print(a)); -} - TEST(PrintArrayTest, CharArray) { - char a[3] = "Hi"; - ArrayHelper4(a); + // Array a contains '\0' in the middle and doesn't end with '\0'. + char a[3] = { 'H', '\0', 'i' }; + EXPECT_EQ("\"H\\0i\"", PrintArrayHelper(a)); } // Const char array. - -void ArrayHelper5(const char (&a)[3]) { // NOLINT - EXPECT_EQ(Print(a), PrintPointer(a) + " pointing to \"Hi\""); -} - TEST(PrintArrayTest, ConstCharArray) { - const char a[3] = "Hi"; - ArrayHelper5(a); + const char a[4] = "\0Hi"; + EXPECT_EQ("\"\\0Hi\\0\"", PrintArrayHelper(a)); } // Array of objects. TEST(PrintArrayTest, ObjectArray) { string a[3] = { "Hi", "Hello", "Ni hao" }; - EXPECT_EQ("{ \"Hi\", \"Hello\", \"Ni hao\" }", Print(a)); + EXPECT_EQ("{ \"Hi\", \"Hello\", \"Ni hao\" }", PrintArrayHelper(a)); } // Array with many elements. TEST(PrintArrayTest, BigArray) { int a[100] = { 1, 2, 3 }; EXPECT_EQ("{ 1, 2, 3, 0, 0, 0, 0, 0, ..., 0, 0, 0, 0, 0, 0, 0, 0 }", - Print(a)); + PrintArrayHelper(a)); } // Tests printing ::string and ::std::string. @@ -995,6 +978,11 @@ TEST(PrintToStringTest, WorksForReference) { UniversalPrinter::PrintToString(n)); } +TEST(PrintToStringTest, WorksForArray) { + int n[3] = { 1, 2, 3 }; + EXPECT_EQ("{ 1, 2, 3 }", UniversalPrinter::PrintToString(n)); +} + TEST(UniversalTersePrintTest, WorksForNonReference) { ::std::stringstream ss; UniversalTersePrint(123, &ss); diff --git a/test/gmock-spec-builders_test.cc b/test/gmock-spec-builders_test.cc index e8c39028..4711899d 100644 --- a/test/gmock-spec-builders_test.cc +++ b/test/gmock-spec-builders_test.cc @@ -1612,6 +1612,53 @@ TEST_F(GMockVerboseFlagTest, InvalidFlagIsTreatedAsWarning) { #endif // 0 +// A helper class that generates a failure when printed. We use it to +// ensure that Google Mock doesn't print a value (even to an internal +// buffer) when it is not supposed to do so. +class PrintMeNot {}; + +void PrintTo(PrintMeNot /* dummy */, ::std::ostream* /* os */) { + ADD_FAILURE() << "Google Mock is printing a value that shouldn't be " + << "printed even to an internal buffer."; +} + +class LogTestHelper { + public: + MOCK_METHOD1(Foo, PrintMeNot(PrintMeNot)); +}; + +class GMockLogTest : public ::testing::Test { + protected: + virtual void SetUp() { original_verbose_ = GMOCK_FLAG(verbose); } + virtual void TearDown() { GMOCK_FLAG(verbose) = original_verbose_; } + + LogTestHelper helper_; + string original_verbose_; +}; + +TEST_F(GMockLogTest, DoesNotPrintGoodCallInternallyIfVerbosityIsWarning) { + GMOCK_FLAG(verbose) = kWarningVerbosity; + EXPECT_CALL(helper_, Foo(_)) + .WillOnce(Return(PrintMeNot())); + helper_.Foo(PrintMeNot()); // This is an expected call. +} + +TEST_F(GMockLogTest, DoesNotPrintGoodCallInternallyIfVerbosityIsError) { + GMOCK_FLAG(verbose) = kErrorVerbosity; + EXPECT_CALL(helper_, Foo(_)) + .WillOnce(Return(PrintMeNot())); + helper_.Foo(PrintMeNot()); // This is an expected call. +} + +TEST_F(GMockLogTest, DoesNotPrintWarningInternallyIfVerbosityIsError) { + GMOCK_FLAG(verbose) = kErrorVerbosity; + ON_CALL(helper_, Foo(_)) + .WillByDefault(Return(PrintMeNot())); + helper_.Foo(PrintMeNot()); // This should generate a warning. +} + +// Tests Mock::AllowLeak(). + TEST(AllowLeakTest, AllowsLeakingUnusedMockObject) { MockA* a = new MockA; Mock::AllowLeak(a);