Go to the documentation of this file.00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017 """Generate Google Mock classes from base classes.
00018
00019 This program will read in a C++ source file and output the Google Mock
00020 classes for the specified classes. If no class is specified, all
00021 classes in the source file are emitted.
00022
00023 Usage:
00024 gmock_class.py header-file.h [ClassName]...
00025
00026 Output is sent to stdout.
00027 """
00028
00029 __author__ = 'nnorwitz@google.com (Neal Norwitz)'
00030
00031
00032 import os
00033 import re
00034 import sys
00035
00036 from cpp import ast
00037 from cpp import utils
00038
00039
00040 try:
00041 _dummy = set
00042 except NameError:
00043 import sets
00044 set = sets.Set
00045
00046 _VERSION = (1, 0, 1)
00047
00048 _INDENT = 2
00049
00050
00051 def _GenerateMethods(output_lines, source, class_node):
00052 function_type = ast.FUNCTION_VIRTUAL | ast.FUNCTION_PURE_VIRTUAL
00053 ctor_or_dtor = ast.FUNCTION_CTOR | ast.FUNCTION_DTOR
00054 indent = ' ' * _INDENT
00055
00056 for node in class_node.body:
00057
00058 if (isinstance(node, ast.Function) and
00059 node.modifiers & function_type and
00060 not node.modifiers & ctor_or_dtor):
00061
00062 const = ''
00063 if node.modifiers & ast.FUNCTION_CONST:
00064 const = 'CONST_'
00065 return_type = 'void'
00066 if node.return_type:
00067
00068 modifiers = ''
00069 if node.return_type.modifiers:
00070 modifiers = ' '.join(node.return_type.modifiers) + ' '
00071 return_type = modifiers + node.return_type.name
00072 template_args = [arg.name for arg in node.return_type.templated_types]
00073 if template_args:
00074 return_type += '<' + ', '.join(template_args) + '>'
00075 if len(template_args) > 1:
00076 for line in [
00077 '// The following line won\'t really compile, as the return',
00078 '// type has multiple template arguments. To fix it, use a',
00079 '// typedef for the return type.']:
00080 output_lines.append(indent + line)
00081 if node.return_type.pointer:
00082 return_type += '*'
00083 if node.return_type.reference:
00084 return_type += '&'
00085 num_parameters = len(node.parameters)
00086 if len(node.parameters) == 1:
00087 first_param = node.parameters[0]
00088 if source[first_param.start:first_param.end].strip() == 'void':
00089
00090 num_parameters = 0
00091 tmpl = ''
00092 if class_node.templated_types:
00093 tmpl = '_T'
00094 mock_method_macro = 'MOCK_%sMETHOD%d%s' % (const, num_parameters, tmpl)
00095
00096 args = ''
00097 if node.parameters:
00098
00099
00100
00101
00102
00103
00104
00105 if len([param for param in node.parameters if param.default]) > 0:
00106 args = ', '.join(param.type.name for param in node.parameters)
00107 else:
00108
00109
00110 start = node.parameters[0].start
00111 end = node.parameters[-1].end
00112
00113 args_strings = re.sub(r'//.*', '', source[start:end])
00114
00115
00116
00117
00118 args = re.sub(' +', ' ', args_strings.replace('\n', ' '))
00119
00120
00121 output_lines.extend(['%s%s(%s,' % (indent, mock_method_macro, node.name),
00122 '%s%s(%s));' % (indent*3, return_type, args)])
00123
00124
00125 def _GenerateMocks(filename, source, ast_list, desired_class_names):
00126 processed_class_names = set()
00127 lines = []
00128 for node in ast_list:
00129 if (isinstance(node, ast.Class) and node.body and
00130
00131 (not desired_class_names or node.name in desired_class_names)):
00132 class_name = node.name
00133 parent_name = class_name
00134 processed_class_names.add(class_name)
00135 class_node = node
00136
00137 if class_node.namespace:
00138 lines.extend(['namespace %s {' % n for n in class_node.namespace])
00139 lines.append('')
00140
00141
00142 if class_node.templated_types:
00143
00144
00145
00146
00147 template_arg_count = len(class_node.templated_types.keys())
00148 template_args = ['T%d' % n for n in range(template_arg_count)]
00149 template_decls = ['typename ' + arg for arg in template_args]
00150 lines.append('template <' + ', '.join(template_decls) + '>')
00151 parent_name += '<' + ', '.join(template_args) + '>'
00152
00153
00154 lines.append('class Mock%s : public %s {'
00155 % (class_name, parent_name))
00156 lines.append('%spublic:' % (' ' * (_INDENT // 2)))
00157
00158
00159 _GenerateMethods(lines, source, class_node)
00160
00161
00162 if lines:
00163
00164 if len(lines) == 2:
00165 del lines[-1]
00166
00167
00168 lines.append('};')
00169 lines.append('')
00170
00171
00172 if class_node.namespace:
00173 for i in range(len(class_node.namespace)-1, -1, -1):
00174 lines.append('} // namespace %s' % class_node.namespace[i])
00175 lines.append('')
00176
00177 if desired_class_names:
00178 missing_class_name_list = list(desired_class_names - processed_class_names)
00179 if missing_class_name_list:
00180 missing_class_name_list.sort()
00181 sys.stderr.write('Class(es) not found in %s: %s\n' %
00182 (filename, ', '.join(missing_class_name_list)))
00183 elif not processed_class_names:
00184 sys.stderr.write('No class found in %s\n' % filename)
00185
00186 return lines
00187
00188
00189 def main(argv=sys.argv):
00190 if len(argv) < 2:
00191 sys.stderr.write('Google Mock Class Generator v%s\n\n' %
00192 '.'.join(map(str, _VERSION)))
00193 sys.stderr.write(__doc__)
00194 return 1
00195
00196 global _INDENT
00197 try:
00198 _INDENT = int(os.environ['INDENT'])
00199 except KeyError:
00200 pass
00201 except:
00202 sys.stderr.write('Unable to use indent of %s\n' % os.environ.get('INDENT'))
00203
00204 filename = argv[1]
00205 desired_class_names = None
00206 if len(argv) >= 3:
00207 desired_class_names = set(argv[2:])
00208 source = utils.ReadFile(filename)
00209 if source is None:
00210 return 1
00211
00212 builder = ast.BuilderFromSource(source, filename)
00213 try:
00214 entire_ast = filter(None, builder.Generate())
00215 except KeyboardInterrupt:
00216 return
00217 except:
00218
00219 pass
00220 else:
00221 lines = _GenerateMocks(filename, source, entire_ast, desired_class_names)
00222 sys.stdout.write('\n'.join(lines))
00223
00224
00225 if __name__ == '__main__':
00226 main(sys.argv)