1#!/usr/bin/env python
2#
3# Copyright 2009 Neal Norwitz All Rights Reserved.
4# Portions Copyright 2009 Google Inc. All Rights Reserved.
5#
6# Licensed under the Apache License, Version 2.0 (the "License");
7# you may not use this file except in compliance with the License.
8# You may obtain a copy of the License at
9#
10#      http://www.apache.org/licenses/LICENSE-2.0
11#
12# Unless required by applicable law or agreed to in writing, software
13# distributed under the License is distributed on an "AS IS" BASIS,
14# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15# See the License for the specific language governing permissions and
16# limitations under the License.
17
18"""Tests for gmock.scripts.generator.cpp.gmock_class."""
19
20__author__ = 'nnorwitz@google.com (Neal Norwitz)'
21
22
23import os
24import sys
25import unittest
26
27# Allow the cpp imports below to work when run as a standalone script.
28sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
29
30from cpp import ast
31from cpp import gmock_class
32
33
34class TestCase(unittest.TestCase):
35  """Helper class that adds assert methods."""
36
37  def StripLeadingWhitespace(self, lines):
38    """Strip leading whitespace in each line in 'lines'."""
39    return '\n'.join([s.lstrip() for s in lines.split('\n')])
40
41  def assertEqualIgnoreLeadingWhitespace(self, expected_lines, lines):
42    """Specialized assert that ignores the indent level."""
43    self.assertEqual(expected_lines, self.StripLeadingWhitespace(lines))
44
45
46class GenerateMethodsTest(TestCase):
47
48  def GenerateMethodSource(self, cpp_source):
49    """Convert C++ source to Google Mock output source lines."""
50    method_source_lines = []
51    # <test> is a pseudo-filename, it is not read or written.
52    builder = ast.BuilderFromSource(cpp_source, '<test>')
53    ast_list = list(builder.Generate())
54    gmock_class._GenerateMethods(method_source_lines, cpp_source, ast_list[0])
55    return '\n'.join(method_source_lines)
56
57  def testSimpleMethod(self):
58    source = """
59class Foo {
60 public:
61  virtual int Bar();
62};
63"""
64    self.assertEqualIgnoreLeadingWhitespace(
65        'MOCK_METHOD0(Bar,\nint());',
66        self.GenerateMethodSource(source))
67
68  def testSimpleConstMethod(self):
69    source = """
70class Foo {
71 public:
72  virtual void Bar(bool flag) const;
73};
74"""
75    self.assertEqualIgnoreLeadingWhitespace(
76        'MOCK_CONST_METHOD1(Bar,\nvoid(bool flag));',
77        self.GenerateMethodSource(source))
78
79  def testExplicitVoid(self):
80    source = """
81class Foo {
82 public:
83  virtual int Bar(void);
84};
85"""
86    self.assertEqualIgnoreLeadingWhitespace(
87        'MOCK_METHOD0(Bar,\nint(void));',
88        self.GenerateMethodSource(source))
89
90  def testStrangeNewlineInParameter(self):
91    source = """
92class Foo {
93 public:
94  virtual void Bar(int
95a) = 0;
96};
97"""
98    self.assertEqualIgnoreLeadingWhitespace(
99        'MOCK_METHOD1(Bar,\nvoid(int a));',
100        self.GenerateMethodSource(source))
101
102  def testDefaultParameters(self):
103    source = """
104class Foo {
105 public:
106  virtual void Bar(int a, char c = 'x') = 0;
107};
108"""
109    self.assertEqualIgnoreLeadingWhitespace(
110        'MOCK_METHOD2(Bar,\nvoid(int, char));',
111        self.GenerateMethodSource(source))
112
113  def testMultipleDefaultParameters(self):
114    source = """
115class Foo {
116 public:
117  virtual void Bar(int a = 42, char c = 'x') = 0;
118};
119"""
120    self.assertEqualIgnoreLeadingWhitespace(
121        'MOCK_METHOD2(Bar,\nvoid(int, char));',
122        self.GenerateMethodSource(source))
123
124  def testRemovesCommentsWhenDefaultsArePresent(self):
125    source = """
126class Foo {
127 public:
128  virtual void Bar(int a = 42 /* a comment */,
129                   char /* other comment */ c= 'x') = 0;
130};
131"""
132    self.assertEqualIgnoreLeadingWhitespace(
133        'MOCK_METHOD2(Bar,\nvoid(int, char));',
134        self.GenerateMethodSource(source))
135
136  def testDoubleSlashCommentsInParameterListAreRemoved(self):
137    source = """
138class Foo {
139 public:
140  virtual void Bar(int a,  // inline comments should be elided.
141                   int b   // inline comments should be elided.
142                   ) const = 0;
143};
144"""
145    self.assertEqualIgnoreLeadingWhitespace(
146        'MOCK_CONST_METHOD2(Bar,\nvoid(int a, int b));',
147        self.GenerateMethodSource(source))
148
149  def testCStyleCommentsInParameterListAreNotRemoved(self):
150    # NOTE(nnorwitz): I'm not sure if it's the best behavior to keep these
151    # comments.  Also note that C style comments after the last parameter
152    # are still elided.
153    source = """
154class Foo {
155 public:
156  virtual const string& Bar(int /* keeper */, int b);
157};
158"""
159    self.assertEqualIgnoreLeadingWhitespace(
160        'MOCK_METHOD2(Bar,\nconst string&(int /* keeper */, int b));',
161        self.GenerateMethodSource(source))
162
163  def testArgsOfTemplateTypes(self):
164    source = """
165class Foo {
166 public:
167  virtual int Bar(const vector<int>& v, map<int, string>* output);
168};"""
169    self.assertEqualIgnoreLeadingWhitespace(
170        'MOCK_METHOD2(Bar,\n'
171        'int(const vector<int>& v, map<int, string>* output));',
172        self.GenerateMethodSource(source))
173
174  def testReturnTypeWithOneTemplateArg(self):
175    source = """
176class Foo {
177 public:
178  virtual vector<int>* Bar(int n);
179};"""
180    self.assertEqualIgnoreLeadingWhitespace(
181        'MOCK_METHOD1(Bar,\nvector<int>*(int n));',
182        self.GenerateMethodSource(source))
183
184  def testReturnTypeWithManyTemplateArgs(self):
185    source = """
186class Foo {
187 public:
188  virtual map<int, string> Bar();
189};"""
190    # Comparing the comment text is brittle - we'll think of something
191    # better in case this gets annoying, but for now let's keep it simple.
192    self.assertEqualIgnoreLeadingWhitespace(
193        '// The following line won\'t really compile, as the return\n'
194        '// type has multiple template arguments.  To fix it, use a\n'
195        '// typedef for the return type.\n'
196        'MOCK_METHOD0(Bar,\nmap<int, string>());',
197        self.GenerateMethodSource(source))
198
199
200class GenerateMocksTest(TestCase):
201
202  def GenerateMocks(self, cpp_source):
203    """Convert C++ source to complete Google Mock output source."""
204    # <test> is a pseudo-filename, it is not read or written.
205    filename = '<test>'
206    builder = ast.BuilderFromSource(cpp_source, filename)
207    ast_list = list(builder.Generate())
208    lines = gmock_class._GenerateMocks(filename, cpp_source, ast_list, None)
209    return '\n'.join(lines)
210
211  def testNamespaces(self):
212    source = """
213namespace Foo {
214namespace Bar { class Forward; }
215namespace Baz {
216
217class Test {
218 public:
219  virtual void Foo();
220};
221
222}  // namespace Baz
223}  // namespace Foo
224"""
225    expected = """\
226namespace Foo {
227namespace Baz {
228
229class MockTest : public Test {
230public:
231MOCK_METHOD0(Foo,
232void());
233};
234
235}  // namespace Baz
236}  // namespace Foo
237"""
238    self.assertEqualIgnoreLeadingWhitespace(
239        expected, self.GenerateMocks(source))
240
241  def testClassWithStorageSpecifierMacro(self):
242    source = """
243class STORAGE_SPECIFIER Test {
244 public:
245  virtual void Foo();
246};
247"""
248    expected = """\
249class MockTest : public Test {
250public:
251MOCK_METHOD0(Foo,
252void());
253};
254"""
255    self.assertEqualIgnoreLeadingWhitespace(
256        expected, self.GenerateMocks(source))
257
258if __name__ == '__main__':
259  unittest.main()
260