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 testStrangeNewlineInParameter(self):
80    source = """
81class Foo {
82 public:
83  virtual void Bar(int
84a) = 0;
85};
86"""
87    self.assertEqualIgnoreLeadingWhitespace(
88        'MOCK_METHOD1(Bar,\nvoid(int a));',
89        self.GenerateMethodSource(source))
90
91  def testDoubleSlashCommentsInParameterListAreRemoved(self):
92    source = """
93class Foo {
94 public:
95  virtual void Bar(int a,  // inline comments should be elided.
96                   int b   // inline comments should be elided.
97                   ) const = 0;
98};
99"""
100    self.assertEqualIgnoreLeadingWhitespace(
101        'MOCK_CONST_METHOD2(Bar,\nvoid(int a, int b));',
102        self.GenerateMethodSource(source))
103
104  def testCStyleCommentsInParameterListAreNotRemoved(self):
105    # NOTE(nnorwitz): I'm not sure if it's the best behavior to keep these
106    # comments.  Also note that C style comments after the last parameter
107    # are still elided.
108    source = """
109class Foo {
110 public:
111  virtual const string& Bar(int /* keeper */, int b);
112};
113"""
114    self.assertEqualIgnoreLeadingWhitespace(
115        'MOCK_METHOD2(Bar,\nconst string&(int /* keeper */, int b));',
116        self.GenerateMethodSource(source))
117
118  def testArgsOfTemplateTypes(self):
119    source = """
120class Foo {
121 public:
122  virtual int Bar(const vector<int>& v, map<int, string>* output);
123};"""
124    self.assertEqualIgnoreLeadingWhitespace(
125        'MOCK_METHOD2(Bar,\n'
126        'int(const vector<int>& v, map<int, string>* output));',
127        self.GenerateMethodSource(source))
128
129  def testReturnTypeWithOneTemplateArg(self):
130    source = """
131class Foo {
132 public:
133  virtual vector<int>* Bar(int n);
134};"""
135    self.assertEqualIgnoreLeadingWhitespace(
136        'MOCK_METHOD1(Bar,\nvector<int>*(int n));',
137        self.GenerateMethodSource(source))
138
139  def testReturnTypeWithManyTemplateArgs(self):
140    source = """
141class Foo {
142 public:
143  virtual map<int, string> Bar();
144};"""
145    # Comparing the comment text is brittle - we'll think of something
146    # better in case this gets annoying, but for now let's keep it simple.
147    self.assertEqualIgnoreLeadingWhitespace(
148        '// The following line won\'t really compile, as the return\n'
149        '// type has multiple template arguments.  To fix it, use a\n'
150        '// typedef for the return type.\n'
151        'MOCK_METHOD0(Bar,\nmap<int, string>());',
152        self.GenerateMethodSource(source))
153
154
155class GenerateMocksTest(TestCase):
156
157  def GenerateMocks(self, cpp_source):
158    """Convert C++ source to complete Google Mock output source."""
159    # <test> is a pseudo-filename, it is not read or written.
160    filename = '<test>'
161    builder = ast.BuilderFromSource(cpp_source, filename)
162    ast_list = list(builder.Generate())
163    lines = gmock_class._GenerateMocks(filename, cpp_source, ast_list, None)
164    return '\n'.join(lines)
165
166  def testNamespaces(self):
167    source = """
168namespace Foo {
169namespace Bar { class Forward; }
170namespace Baz {
171
172class Test {
173 public:
174  virtual void Foo();
175};
176
177}  // namespace Baz
178}  // namespace Foo
179"""
180    expected = """\
181namespace Foo {
182namespace Baz {
183
184class MockTest : public Test {
185public:
186MOCK_METHOD0(Foo,
187void());
188};
189
190}  // namespace Baz
191}  // namespace Foo
192"""
193    self.assertEqualIgnoreLeadingWhitespace(
194        expected, self.GenerateMocks(source))
195
196  def testClassWithStorageSpecifierMacro(self):
197    source = """
198class STORAGE_SPECIFIER Test {
199 public:
200  virtual void Foo();
201};
202"""
203    expected = """\
204class MockTest : public Test {
205public:
206MOCK_METHOD0(Foo,
207void());
208};
209"""
210    self.assertEqualIgnoreLeadingWhitespace(
211        expected, self.GenerateMocks(source))
212
213if __name__ == '__main__':
214  unittest.main()
215