Skip to content

Commit 5c11ed8

Browse files
committed
fix #1 / Problem with generating mocks for templates.
1 parent bd9f1ed commit 5c11ed8

File tree

10 files changed

+110
-21
lines changed

10 files changed

+110
-21
lines changed

README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ find project -iname "*.h" -or -iname "*.hpp" | xargs "project/externals/gmock.py
5555
+ easy integration with the project build system -> generate mocks files for each interface from given files limited to the project (for example via project namespace)
5656
+ able to generate cpp files with default constructors (to speed up compilation times)
5757
+ generate pretty output (one mock per file)
58+
+ mocking class templates
5859
+ easy to extend (~300 lines of code)
5960
+ handle c++ operators
6061

@@ -73,6 +74,8 @@ find project -iname "*.h" -or -iname "*.hpp" | xargs "project/externals/gmock.py
7374
# file: interface file name
7475
# dir: interface directory
7576
# guard: header guard
77+
# template: template parameters
78+
# template_interface: template interface class
7679
# interface: interface class
7780
# mock_methods: generated gmock methods
7881
# generated_dir: generated directory
@@ -93,7 +96,7 @@ file_template_hpp = """\
9396
9497
%(namespaces_begin)s
9598
96-
class %(interface)sMock : public %(interface)s
99+
%(template)sclass %(interface)sMock : public %(template_interface)s
97100
{
98101
public:
99102
%(mock_methods)s

gmock.conf

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
# file: interface file name
33
# dir: interface directory
44
# guard: header guard
5+
# template: template parameters
6+
# template_interface: template interface class
57
# interface: interface class
68
# mock_methods: generated gmock methods
79
# generated_dir: generated directory
@@ -22,7 +24,7 @@ file_template_hpp = """\
2224

2325
%(namespaces_begin)s
2426

25-
class %(interface)sMock : public %(interface)s
27+
%(template)sclass %(interface)sMock : public %(template_interface)s
2628
{
2729
public:
2830
%(mock_methods)s

gmock.py

Lines changed: 61 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,11 @@ class mock_method:
5858
'operator~' : 'complement_operator'
5959
}
6060

61-
def __init__(self, result_type, name, is_const, args_size, args, args_prefix = 'arg'):
61+
def __init__(self, result_type, name, is_const, is_template, args_size, args, args_prefix = 'arg'):
6262
self.result_type = result_type
6363
self.name = name
6464
self.is_const = is_const
65+
self.is_template = is_template
6566
self.args_size = args_size
6667
self.args = args
6768
self.args_prefix = args_prefix
@@ -110,9 +111,10 @@ def to_string(self, gap = ' '):
110111

111112
mock.append(gap)
112113
mock.append(
113-
"MOCK_%(const)sMETHOD%(nr)s(%(name)s, %(result_type)s(%(args)s));" % {
114+
"MOCK_%(const)sMETHOD%(nr)s%(template)s(%(name)s, %(result_type)s(%(args)s));" % {
114115
'const' : self.is_const and 'CONST_' or '',
115116
'nr' : self.args_size,
117+
'template' : self.is_template and '_T' or '',
116118
'name' : name,
117119
'result_type' : self.result_type,
118120
'args' : self.args
@@ -121,6 +123,9 @@ def to_string(self, gap = ' '):
121123
return ''.join(mock)
122124

123125
class mock_generator:
126+
def __is_template_class(self, decl):
127+
return '<' in decl
128+
124129
def __is_const_function(self, tokens):
125130
for token in reversed(tokens):
126131
if token == 'const':
@@ -151,6 +156,36 @@ def __get_result_type(self, tokens, name):
151156
result_type.append(' ')
152157
return ''.join(result_type)
153158

159+
def __pretty_template(self, decl):
160+
first = False
161+
typename = []
162+
typenames = []
163+
for token in decl.split("::")[-1]:
164+
if token == '<':
165+
first = True
166+
elif token == ',':
167+
typenames.append(''.join(typename))
168+
typename = []
169+
elif token == '>':
170+
typenames.append(''.join(typename))
171+
typename = []
172+
elif token == ' ':
173+
continue
174+
elif first:
175+
typename.append(token)
176+
177+
result = []
178+
if len(typenames) > 0:
179+
result.append("template<")
180+
for i, t in enumerate(typenames):
181+
i != 0 and result.append(", ")
182+
result.append("typename ")
183+
result.append(t)
184+
result.append(">")
185+
result.append("\n")
186+
187+
return ''.join(result)
188+
154189
def __pretty_mock_methods(self, mock_methods):
155190
result = []
156191
for i, mock_method in enumerate(mock_methods):
@@ -173,31 +208,44 @@ def __pretty_namespaces_end(self, decl):
173208
result.append("} // namespace " + namespace)
174209
return ''.join(result)
175210

176-
def __get_mock_methods(self, node, mock_methods, class_decl = ""):
211+
def __get_interface(self, decl):
212+
result = []
213+
ignore = False
214+
for token in decl.split("::")[-1]:
215+
if token == '<':
216+
ignore = True
217+
if not ignore:
218+
result.append(token)
219+
if token == '>':
220+
ignore = False
221+
return ''.join(result)
222+
223+
def __get_mock_methods(self, node, mock_methods, decl = ""):
177224
name = str(node.displayname, self.encode)
178225
if node.kind == CursorKind.CXX_METHOD:
179-
tokens = [str(token.spelling, self.encode) for token in node.get_tokens()]
180226
spelling = str(node.spelling, self.encode)
227+
tokens = [str(token.spelling, self.encode) for token in node.get_tokens()]
181228
file = str(node.location.file.name, self.encode)
182229
if self.__is_pure_virtual_function(tokens):
183-
mock_methods.setdefault(class_decl, [file]).append(
230+
mock_methods.setdefault(decl, [file]).append(
184231
mock_method(
185232
self.__get_result_type(tokens, spelling),
186233
spelling,
187234
self.__is_const_function(tokens),
235+
self.__is_template_class(decl),
188236
len(list(node.get_arguments())),
189237
name[len(node.spelling) + 1 : -1]
190238
)
191239
)
192-
elif node.kind in [CursorKind.STRUCT_DECL, CursorKind.CLASS_DECL, CursorKind.NAMESPACE]:
193-
class_decl = class_decl == "" and name or class_decl + (name == "" and "" or "::") + name
194-
if class_decl.startswith(self.decl):
195-
[self.__get_mock_methods(c, mock_methods, class_decl) for c in node.get_children()]
240+
elif node.kind in [CursorKind.CLASS_TEMPLATE, CursorKind.STRUCT_DECL, CursorKind.CLASS_DECL, CursorKind.NAMESPACE]:
241+
decl = decl == "" and name or decl + (name == "" and "" or "::") + name
242+
if decl.startswith(self.decl):
243+
[self.__get_mock_methods(c, mock_methods, decl) for c in node.get_children()]
196244
else:
197-
[self.__get_mock_methods(c, mock_methods, class_decl) for c in node.get_children()]
245+
[self.__get_mock_methods(c, mock_methods, decl) for c in node.get_children()]
198246

199247
def __generate_file(self, decl, mock_methods, file_type, file_template_type):
200-
interface = decl.split("::")[-1]
248+
interface = self.__get_interface(decl)
201249
mock_file = {
202250
'hpp' : self.mock_file_hpp % { 'interface' : interface },
203251
'cpp' : self.mock_file_cpp % { 'interface' : interface },
@@ -214,6 +262,8 @@ def __generate_file(self, decl, mock_methods, file_type, file_template_type):
214262
'file' : os.path.basename(mock_methods[0]),
215263
'namespaces_begin' : self.__pretty_namespaces_begin(decl),
216264
'interface' : interface,
265+
'template_interface' : decl.split("::")[-1],
266+
'template' : self.__pretty_template(decl),
217267
'mock_methods' : self.__pretty_mock_methods(mock_methods[1:]),
218268
'namespaces_end' : self.__pretty_namespaces_end(decl)
219269
})

test/given/I3.hpp

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
#define I3_HPP
33

44
namespace n1 {
5-
namespace {
65

76
class I3
87
{
@@ -30,7 +29,6 @@ class I3
3029
virtual void f1() = 0;
3130
};
3231

33-
} // namespace
3432
} // namespace n1
3533

3634
#endif

test/given/I3I4.hpp

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
#define I3I4_HPP
33

44
namespace n1 {
5-
namespace {
65

76
struct I3
87
{
@@ -16,8 +15,6 @@ struct I3
1615
virtual void f1() = 0;
1716
};
1817

19-
} // namespace
20-
2118
class I4
2219
{
2320
public:

test/given/T.hpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
namespace n {
2+
3+
template <typename Elem>
4+
class T {
5+
virtual ~T();
6+
7+
virtual int GetSize() const = 0;
8+
virtual void Push(const Elem& x) = 0;
9+
};
10+
11+
} // namespace n
12+

test/gmock_test.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,12 @@ def test_gmock_long_args(self):
7676
self.assertTrue('I2_mock.cpp' in os.listdir(self.generated_dir))
7777
self.assertMocks();
7878

79+
def test_gmock_class_templates(self):
80+
self.assertEqual(0, gmock.main(['../gmock.py', '-d', self.generated_dir, 'given/T.hpp']))
81+
self.assertEqual(1, len([name for name in os.listdir(self.generated_dir)]))
82+
self.assertTrue('TMock.hpp' in os.listdir(self.generated_dir))
83+
self.assertMocks();
84+
7985
if __name__ == '__main__':
8086
unittest.main()
8187

test/test.conf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ file_template_hpp = """\
99

1010
%(namespaces_begin)s
1111

12-
class %(interface)s_mock : public %(interface)s
12+
%(template)sclass %(interface)s_mock : public %(template_interface)s
1313
{
1414
public:
1515
%(interface)s_mock();

test/then/I3Mock.hpp

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
#include "./given/I3I4.hpp"
99

1010
namespace n1 {
11-
namespace {
1211

1312
class I3Mock : public I3
1413
{
@@ -17,7 +16,6 @@ class I3Mock : public I3
1716
};
1817

1918
} // namespace n1
20-
} // namespace
2119

2220
#endif // I3MOCK_HPP
2321

test/then/TMock.hpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/*
2+
* file generated by gmock: TMock.hpp
3+
*/
4+
#ifndef TMOCK_HPP
5+
#define TMOCK_HPP
6+
7+
#include <gmock/gmock.h>
8+
#include "./given/T.hpp"
9+
10+
namespace n {
11+
12+
template<typename Elem>
13+
class TMock : public T<Elem>
14+
{
15+
public:
16+
MOCK_CONST_METHOD0_T(GetSize, int());
17+
MOCK_METHOD1_T(Push, void(const Elem &));
18+
};
19+
20+
} // namespace n
21+
22+
#endif // TMOCK_HPP
23+

0 commit comments

Comments
 (0)