Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(162)

Side by Side Diff: testing/gmock/scripts/gmock_doctor.py

Issue 6241018: Pull in gmock through DEPS (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/testing
Patch Set: don't use svn mirror for now Created 9 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 #!/usr/bin/env python
2 #
3 # Copyright 2008, Google Inc.
4 # All rights reserved.
5 #
6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions are
8 # met:
9 #
10 # * Redistributions of source code must retain the above copyright
11 # notice, this list of conditions and the following disclaimer.
12 # * Redistributions in binary form must reproduce the above
13 # copyright notice, this list of conditions and the following disclaimer
14 # in the documentation and/or other materials provided with the
15 # distribution.
16 # * Neither the name of Google Inc. nor the names of its
17 # contributors may be used to endorse or promote products derived from
18 # this software without specific prior written permission.
19 #
20 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31
32 """Converts gcc errors in code using Google Mock to plain English."""
33
34 __author__ = 'wan@google.com (Zhanyong Wan)'
35
36 import re
37 import sys
38
39 _VERSION = '1.0.3'
40
41 _COMMON_GMOCK_SYMBOLS = [
42 # Matchers
43 '_',
44 'A',
45 'AddressSatisfies',
46 'AllOf',
47 'An',
48 'AnyOf',
49 'ContainerEq',
50 'Contains',
51 'ContainsRegex',
52 'DoubleEq',
53 'ElementsAre',
54 'ElementsAreArray',
55 'EndsWith',
56 'Eq',
57 'Field',
58 'FloatEq',
59 'Ge',
60 'Gt',
61 'HasSubstr',
62 'IsInitializedProto',
63 'Le',
64 'Lt',
65 'MatcherCast',
66 'Matches',
67 'MatchesRegex',
68 'NanSensitiveDoubleEq',
69 'NanSensitiveFloatEq',
70 'Ne',
71 'Not',
72 'NotNull',
73 'Pointee',
74 'Property',
75 'Ref',
76 'ResultOf',
77 'SafeMatcherCast',
78 'StartsWith',
79 'StrCaseEq',
80 'StrCaseNe',
81 'StrEq',
82 'StrNe',
83 'Truly',
84 'TypedEq',
85 'Value',
86
87 # Actions
88 'Assign',
89 'ByRef',
90 'DeleteArg',
91 'DoAll',
92 'DoDefault',
93 'IgnoreResult',
94 'Invoke',
95 'InvokeArgument',
96 'InvokeWithoutArgs',
97 'Return',
98 'ReturnNew',
99 'ReturnNull',
100 'ReturnRef',
101 'SaveArg',
102 'SetArgReferee',
103 'SetArgumentPointee',
104 'SetArrayArgument',
105 'SetErrnoAndReturn',
106 'Throw',
107 'WithArg',
108 'WithArgs',
109 'WithoutArgs',
110
111 # Cardinalities
112 'AnyNumber',
113 'AtLeast',
114 'AtMost',
115 'Between',
116 'Exactly',
117
118 # Sequences
119 'InSequence',
120 'Sequence',
121
122 # Misc
123 'DefaultValue',
124 'Mock',
125 ]
126
127 # Regex for matching source file path and line number in gcc's errors.
128 _FILE_LINE_RE = r'(?P<file>.*):(?P<line>\d+):\s+'
129
130
131 def _FindAllMatches(regex, s):
132 """Generates all matches of regex in string s."""
133
134 r = re.compile(regex)
135 return r.finditer(s)
136
137
138 def _GenericDiagnoser(short_name, long_name, regex, diagnosis, msg):
139 """Diagnoses the given disease by pattern matching.
140
141 Args:
142 short_name: Short name of the disease.
143 long_name: Long name of the disease.
144 regex: Regex for matching the symptoms.
145 diagnosis: Pattern for formatting the diagnosis.
146 msg: Gcc's error messages.
147 Yields:
148 Tuples of the form
149 (short name of disease, long name of disease, diagnosis).
150 """
151
152 diagnosis = '%(file)s:%(line)s:' + diagnosis
153 for m in _FindAllMatches(regex, msg):
154 yield (short_name, long_name, diagnosis % m.groupdict())
155
156
157 def _NeedToReturnReferenceDiagnoser(msg):
158 """Diagnoses the NRR disease, given the error messages by gcc."""
159
160 regex = (r'In member function \'testing::internal::ReturnAction<R>.*\n'
161 + _FILE_LINE_RE + r'instantiated from here\n'
162 r'.*gmock-actions\.h.*error: creating array with negative size')
163 diagnosis = """
164 You are using an Return() action in a function that returns a reference.
165 Please use ReturnRef() instead."""
166 return _GenericDiagnoser('NRR', 'Need to Return Reference',
167 regex, diagnosis, msg)
168
169
170 def _NeedToReturnSomethingDiagnoser(msg):
171 """Diagnoses the NRS disease, given the error messages by gcc."""
172
173 regex = (_FILE_LINE_RE +
174 r'(instantiated from here\n.'
175 r'*gmock.*actions\.h.*error: void value not ignored)'
176 r'|(error: control reaches end of non-void function)')
177 diagnosis = """
178 You are using an action that returns void, but it needs to return
179 *something*. Please tell it *what* to return. Perhaps you can use
180 the pattern DoAll(some_action, Return(some_value))?"""
181 return _GenericDiagnoser('NRS', 'Need to Return Something',
182 regex, diagnosis, msg)
183
184
185 def _NeedToReturnNothingDiagnoser(msg):
186 """Diagnoses the NRN disease, given the error messages by gcc."""
187
188 regex = (_FILE_LINE_RE + r'instantiated from here\n'
189 r'.*gmock-actions\.h.*error: instantiation of '
190 r'\'testing::internal::ReturnAction<R>::Impl<F>::value_\' '
191 r'as type \'void\'')
192 diagnosis = """
193 You are using an action that returns *something*, but it needs to return
194 void. Please use a void-returning action instead.
195
196 All actions but the last in DoAll(...) must return void. Perhaps you need
197 to re-arrange the order of actions in a DoAll(), if you are using one?"""
198 return _GenericDiagnoser('NRN', 'Need to Return Nothing',
199 regex, diagnosis, msg)
200
201
202 def _IncompleteByReferenceArgumentDiagnoser(msg):
203 """Diagnoses the IBRA disease, given the error messages by gcc."""
204
205 regex = (_FILE_LINE_RE + r'instantiated from here\n'
206 r'.*gtest-printers\.h.*error: invalid application of '
207 r'\'sizeof\' to incomplete type \'(?P<type>.*)\'')
208 diagnosis = """
209 In order to mock this function, Google Mock needs to see the definition
210 of type "%(type)s" - declaration alone is not enough. Either #include
211 the header that defines it, or change the argument to be passed
212 by pointer."""
213 return _GenericDiagnoser('IBRA', 'Incomplete By-Reference Argument Type',
214 regex, diagnosis, msg)
215
216
217 def _OverloadedFunctionMatcherDiagnoser(msg):
218 """Diagnoses the OFM disease, given the error messages by gcc."""
219
220 regex = (_FILE_LINE_RE + r'error: no matching function for '
221 r'call to \'Truly\(<unresolved overloaded function type>\)')
222 diagnosis = """
223 The argument you gave to Truly() is an overloaded function. Please tell
224 gcc which overloaded version you want to use.
225
226 For example, if you want to use the version whose signature is
227 bool Foo(int n);
228 you should write
229 Truly(static_cast<bool (*)(int n)>(Foo))"""
230 return _GenericDiagnoser('OFM', 'Overloaded Function Matcher',
231 regex, diagnosis, msg)
232
233
234 def _OverloadedFunctionActionDiagnoser(msg):
235 """Diagnoses the OFA disease, given the error messages by gcc."""
236
237 regex = (_FILE_LINE_RE + r'error: no matching function for call to \'Invoke\('
238 r'<unresolved overloaded function type>')
239 diagnosis = """
240 You are passing an overloaded function to Invoke(). Please tell gcc
241 which overloaded version you want to use.
242
243 For example, if you want to use the version whose signature is
244 bool MyFunction(int n, double x);
245 you should write something like
246 Invoke(static_cast<bool (*)(int n, double x)>(MyFunction))"""
247 return _GenericDiagnoser('OFA', 'Overloaded Function Action',
248 regex, diagnosis, msg)
249
250
251 def _OverloadedMethodActionDiagnoser1(msg):
252 """Diagnoses the OMA disease, given the error messages by gcc."""
253
254 regex = (_FILE_LINE_RE + r'error: '
255 r'.*no matching function for call to \'Invoke\(.*, '
256 r'unresolved overloaded function type>')
257 diagnosis = """
258 The second argument you gave to Invoke() is an overloaded method. Please
259 tell gcc which overloaded version you want to use.
260
261 For example, if you want to use the version whose signature is
262 class Foo {
263 ...
264 bool Bar(int n, double x);
265 };
266 you should write something like
267 Invoke(foo, static_cast<bool (Foo::*)(int n, double x)>(&Foo::Bar))"""
268 return _GenericDiagnoser('OMA', 'Overloaded Method Action',
269 regex, diagnosis, msg)
270
271
272 def _MockObjectPointerDiagnoser(msg):
273 """Diagnoses the MOP disease, given the error messages by gcc."""
274
275 regex = (_FILE_LINE_RE + r'error: request for member '
276 r'\'gmock_(?P<method>.+)\' in \'(?P<mock_object>.+)\', '
277 r'which is of non-class type \'(.*::)*(?P<class_name>.+)\*\'')
278 diagnosis = """
279 The first argument to ON_CALL() and EXPECT_CALL() must be a mock *object*,
280 not a *pointer* to it. Please write '*(%(mock_object)s)' instead of
281 '%(mock_object)s' as your first argument.
282
283 For example, given the mock class:
284
285 class %(class_name)s : public ... {
286 ...
287 MOCK_METHOD0(%(method)s, ...);
288 };
289
290 and the following mock instance:
291
292 %(class_name)s* mock_ptr = ...
293
294 you should use the EXPECT_CALL like this:
295
296 EXPECT_CALL(*mock_ptr, %(method)s(...));"""
297 return _GenericDiagnoser('MOP', 'Mock Object Pointer',
298 regex, diagnosis, msg)
299
300
301 def _OverloadedMethodActionDiagnoser2(msg):
302 """Diagnoses the OMA disease, given the error messages by gcc."""
303
304 regex = (_FILE_LINE_RE + r'error: no matching function for '
305 r'call to \'Invoke\(.+, <unresolved overloaded function type>\)')
306 diagnosis = """
307 The second argument you gave to Invoke() is an overloaded method. Please
308 tell gcc which overloaded version you want to use.
309
310 For example, if you want to use the version whose signature is
311 class Foo {
312 ...
313 bool Bar(int n, double x);
314 };
315 you should write something like
316 Invoke(foo, static_cast<bool (Foo::*)(int n, double x)>(&Foo::Bar))"""
317 return _GenericDiagnoser('OMA', 'Overloaded Method Action',
318 regex, diagnosis, msg)
319
320
321 def _NeedToUseSymbolDiagnoser(msg):
322 """Diagnoses the NUS disease, given the error messages by gcc."""
323
324 regex = (_FILE_LINE_RE + r'error: \'(?P<symbol>.+)\' '
325 r'(was not declared in this scope|has not been declared)')
326 diagnosis = """
327 '%(symbol)s' is defined by Google Mock in the testing namespace.
328 Did you forget to write
329 using testing::%(symbol)s;
330 ?"""
331 for m in _FindAllMatches(regex, msg):
332 symbol = m.groupdict()['symbol']
333 if symbol in _COMMON_GMOCK_SYMBOLS:
334 yield ('NUS', 'Need to Use Symbol', diagnosis % m.groupdict())
335
336
337 def _NeedToUseReturnNullDiagnoser(msg):
338 """Diagnoses the NRNULL disease, given the error messages by gcc."""
339
340 regex = ('instantiated from \'testing::internal::ReturnAction<R>'
341 '::operator testing::Action<Func>\(\) const.*\n' +
342 _FILE_LINE_RE + r'instantiated from here\n'
343 r'.*error: no matching function for call to \'implicit_cast\('
344 r'long int&\)')
345 diagnosis = """
346 You are probably calling Return(NULL) and the compiler isn't sure how to turn
347 NULL into the right type. Use ReturnNull() instead.
348 Note: the line number may be off; please fix all instances of Return(NULL)."""
349 return _GenericDiagnoser('NRNULL', 'Need to use ReturnNull',
350 regex, diagnosis, msg)
351
352
353 _TTB_DIAGNOSIS = """
354 In a mock class template, types or typedefs defined in the base class
355 template are *not* automatically visible. This is how C++ works. Before
356 you can use a type or typedef named %(type)s defined in base class Base<T>, you
357 need to make it visible. One way to do it is:
358
359 typedef typename Base<T>::%(type)s %(type)s;"""
360
361
362 def _TypeInTemplatedBaseDiagnoser1(msg):
363 """Diagnoses the TTB disease, given the error messages by gcc.
364
365 This version works when the type is used as the mock function's return
366 type.
367 """
368
369 gcc_4_3_1_regex = (
370 r'In member function \'int .*\n' + _FILE_LINE_RE +
371 r'error: a function call cannot appear in a constant-expression')
372 gcc_4_4_0_regex = (
373 r'error: a function call cannot appear in a constant-expression'
374 + _FILE_LINE_RE + r'error: template argument 1 is invalid\n')
375 diagnosis = _TTB_DIAGNOSIS % {'type': 'Foo'}
376 return (list(_GenericDiagnoser('TTB', 'Type in Template Base',
377 gcc_4_3_1_regex, diagnosis, msg)) +
378 list(_GenericDiagnoser('TTB', 'Type in Template Base',
379 gcc_4_4_0_regex, diagnosis, msg)))
380
381
382 def _TypeInTemplatedBaseDiagnoser2(msg):
383 """Diagnoses the TTB disease, given the error messages by gcc.
384
385 This version works when the type is used as the mock function's sole
386 parameter type.
387 """
388
389 regex = (_FILE_LINE_RE +
390 r'error: \'(?P<type>.+)\' was not declared in this scope\n'
391 r'.*error: template argument 1 is invalid\n')
392 return _GenericDiagnoser('TTB', 'Type in Template Base',
393 regex, _TTB_DIAGNOSIS, msg)
394
395
396 def _TypeInTemplatedBaseDiagnoser3(msg):
397 """Diagnoses the TTB disease, given the error messages by gcc.
398
399 This version works when the type is used as a parameter of a mock
400 function that has multiple parameters.
401 """
402
403 regex = (r'error: expected `;\' before \'::\' token\n'
404 + _FILE_LINE_RE +
405 r'error: \'(?P<type>.+)\' was not declared in this scope\n'
406 r'.*error: template argument 1 is invalid\n'
407 r'.*error: \'.+\' was not declared in this scope')
408 return _GenericDiagnoser('TTB', 'Type in Template Base',
409 regex, _TTB_DIAGNOSIS, msg)
410
411
412 def _WrongMockMethodMacroDiagnoser(msg):
413 """Diagnoses the WMM disease, given the error messages by gcc."""
414
415 regex = (_FILE_LINE_RE +
416 r'.*this_method_does_not_take_(?P<wrong_args>\d+)_argument.*\n'
417 r'.*\n'
418 r'.*candidates are.*FunctionMocker<[^>]+A(?P<args>\d+)\)>')
419 diagnosis = """
420 You are using MOCK_METHOD%(wrong_args)s to define a mock method that has
421 %(args)s arguments. Use MOCK_METHOD%(args)s (or MOCK_CONST_METHOD%(args)s,
422 MOCK_METHOD%(args)s_T, MOCK_CONST_METHOD%(args)s_T as appropriate) instead."""
423 return _GenericDiagnoser('WMM', 'Wrong MOCK_METHODn Macro',
424 regex, diagnosis, msg)
425
426
427 def _WrongParenPositionDiagnoser(msg):
428 """Diagnoses the WPP disease, given the error messages by gcc."""
429
430 regex = (_FILE_LINE_RE +
431 r'error:.*testing::internal::MockSpec<.* has no member named \''
432 r'(?P<method>\w+)\'')
433 diagnosis = """
434 The closing parenthesis of ON_CALL or EXPECT_CALL should be *before*
435 ".%(method)s". For example, you should write:
436 EXPECT_CALL(my_mock, Foo(_)).%(method)s(...);
437 instead of:
438 EXPECT_CALL(my_mock, Foo(_).%(method)s(...));"""
439 return _GenericDiagnoser('WPP', 'Wrong Parenthesis Position',
440 regex, diagnosis, msg)
441
442
443 _DIAGNOSERS = [
444 _IncompleteByReferenceArgumentDiagnoser,
445 _MockObjectPointerDiagnoser,
446 _NeedToReturnNothingDiagnoser,
447 _NeedToReturnReferenceDiagnoser,
448 _NeedToReturnSomethingDiagnoser,
449 _NeedToUseReturnNullDiagnoser,
450 _NeedToUseSymbolDiagnoser,
451 _OverloadedFunctionActionDiagnoser,
452 _OverloadedFunctionMatcherDiagnoser,
453 _OverloadedMethodActionDiagnoser1,
454 _OverloadedMethodActionDiagnoser2,
455 _TypeInTemplatedBaseDiagnoser1,
456 _TypeInTemplatedBaseDiagnoser2,
457 _TypeInTemplatedBaseDiagnoser3,
458 _WrongMockMethodMacroDiagnoser,
459 _WrongParenPositionDiagnoser,
460 ]
461
462
463 def Diagnose(msg):
464 """Generates all possible diagnoses given the gcc error message."""
465
466 diagnoses = []
467 for diagnoser in _DIAGNOSERS:
468 for diag in diagnoser(msg):
469 diagnosis = '[%s - %s]\n%s' % diag
470 if not diagnosis in diagnoses:
471 diagnoses.append(diagnosis)
472 return diagnoses
473
474
475 def main():
476 print ('Google Mock Doctor v%s - '
477 'diagnoses problems in code using Google Mock.' % _VERSION)
478
479 if sys.stdin.isatty():
480 print ('Please copy and paste the compiler errors here. Press c-D when '
481 'you are done:')
482 else:
483 print 'Waiting for compiler errors on stdin . . .'
484
485 msg = sys.stdin.read().strip()
486 diagnoses = Diagnose(msg)
487 count = len(diagnoses)
488 if not count:
489 print '\nGcc complained:'
490 print '8<------------------------------------------------------------'
491 print msg
492 print '------------------------------------------------------------>8'
493 print """
494 Uh-oh, I'm not smart enough to figure out what the problem is. :-(
495 However...
496 If you send your source code and gcc's error messages to
497 googlemock@googlegroups.com, you can be helped and I can get smarter --
498 win-win for us!"""
499 else:
500 print '------------------------------------------------------------'
501 print 'Your code appears to have the following',
502 if count > 1:
503 print '%s diseases:' % (count,)
504 else:
505 print 'disease:'
506 i = 0
507 for d in diagnoses:
508 i += 1
509 if count > 1:
510 print '\n#%s:' % (i,)
511 print d
512 print """
513 How did I do? If you think I'm wrong or unhelpful, please send your
514 source code and gcc's error messages to googlemock@googlegroups.com. Then
515 you can be helped and I can get smarter -- I promise I won't be upset!"""
516
517
518 if __name__ == '__main__':
519 main()
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698