#!/usr/bin/env python # # Copyright 2009 Neal Norwitz All Rights Reserved. # Portions Copyright 2009 Google Inc. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Tests for gmock.scripts.generator.cpp.gmock_class.""" __author__ = 'nnorwitz@google.com (Neal Norwitz)' import os import sys import unittest # Allow the cpp imports below to work when run as a standalone script. sys.path.append(os.path.join(os.path.dirname(__file__), '..')) from cpp import ast from cpp import gmock_class class TestCase(unittest.TestCase): """Helper class that adds assert methods.""" def StripLeadingWhitespace(self, lines): """Strip leading whitespace in each line in 'lines'.""" return '\n'.join([s.lstrip() for s in lines.split('\n')]) def assertEqualIgnoreLeadingWhitespace(self, expected_lines, lines): """Specialized assert that ignores the indent level.""" self.assertEqual(expected_lines, self.StripLeadingWhitespace(lines)) class GenerateMethodsTest(TestCase): def GenerateMethodSource(self, cpp_source): """Convert C++ source to Google Mock output source lines.""" method_source_lines = [] # is a pseudo-filename, it is not read or written. builder = ast.BuilderFromSource(cpp_source, '') ast_list = list(builder.Generate()) gmock_class._GenerateMethods(method_source_lines, cpp_source, ast_list[0]) return '\n'.join(method_source_lines) def testSimpleMethod(self): source = """ class Foo { public: virtual int Bar(); }; """ self.assertEqualIgnoreLeadingWhitespace( 'MOCK_METHOD0(Bar,\nint());', self.GenerateMethodSource(source)) def testSimpleConstructorsAndDestructor(self): source = """ class Foo { public: Foo(); Foo(int x); Foo(const Foo& f); Foo(Foo&& f); ~Foo(); virtual int Bar() = 0; }; """ # The constructors and destructor should be ignored. self.assertEqualIgnoreLeadingWhitespace( 'MOCK_METHOD0(Bar,\nint());', self.GenerateMethodSource(source)) def testVirtualDestructor(self): source = """ class Foo { public: virtual ~Foo(); virtual int Bar() = 0; }; """ # The destructor should be ignored. self.assertEqualIgnoreLeadingWhitespace( 'MOCK_METHOD0(Bar,\nint());', self.GenerateMethodSource(source)) def testExplicitlyDefaultedConstructorsAndDestructor(self): source = """ class Foo { public: Foo() = default; Foo(const Foo& f) = default; Foo(Foo&& f) = default; ~Foo() = default; virtual int Bar() = 0; }; """ # The constructors and destructor should be ignored. self.assertEqualIgnoreLeadingWhitespace( 'MOCK_METHOD0(Bar,\nint());', self.GenerateMethodSource(source)) def testExplicitlyDeletedConstructorsAndDestructor(self): source = """ class Foo { public: Foo() = delete; Foo(const Foo& f) = delete; Foo(Foo&& f) = delete; ~Foo() = delete; virtual int Bar() = 0; }; """ # The constructors and destructor should be ignored. self.assertEqualIgnoreLeadingWhitespace( 'MOCK_METHOD0(Bar,\nint());', self.GenerateMethodSource(source)) def testSimpleOverrideMethod(self): source = """ class Foo { public: int Bar() override; }; """ self.assertEqualIgnoreLeadingWhitespace( 'MOCK_METHOD0(Bar,\nint());', self.GenerateMethodSource(source)) def testSimpleConstMethod(self): source = """ class Foo { public: virtual void Bar(bool flag) const; }; """ self.assertEqualIgnoreLeadingWhitespace( 'MOCK_CONST_METHOD1(Bar,\nvoid(bool flag));', self.GenerateMethodSource(source)) def testExplicitVoid(self): source = """ class Foo { public: virtual int Bar(void); }; """ self.assertEqualIgnoreLeadingWhitespace( 'MOCK_METHOD0(Bar,\nint(void));', self.GenerateMethodSource(source)) def testStrangeNewlineInParameter(self): source = """ class Foo { public: virtual void Bar(int a) = 0; }; """ self.assertEqualIgnoreLeadingWhitespace( 'MOCK_METHOD1(Bar,\nvoid(int a));', self.GenerateMethodSource(source)) def testDefaultParameters(self): source = """ class Foo { public: virtual void Bar(int a, char c = 'x') = 0; }; """ self.assertEqualIgnoreLeadingWhitespace( 'MOCK_METHOD2(Bar,\nvoid(int, char));', self.GenerateMethodSource(source)) def testMultipleDefaultParameters(self): source = """ class Foo { public: virtual void Bar(int a = 42, char c = 'x') = 0; }; """ self.assertEqualIgnoreLeadingWhitespace( 'MOCK_METHOD2(Bar,\nvoid(int, char));', self.GenerateMethodSource(source)) def testRemovesCommentsWhenDefaultsArePresent(self): source = """ class Foo { public: virtual void Bar(int a = 42 /* a comment */, char /* other comment */ c= 'x') = 0; }; """ self.assertEqualIgnoreLeadingWhitespace( 'MOCK_METHOD2(Bar,\nvoid(int, char));', self.GenerateMethodSource(source)) def testDoubleSlashCommentsInParameterListAreRemoved(self): source = """ class Foo { public: virtual void Bar(int a, // inline comments should be elided. int b // inline comments should be elided. ) const = 0; }; """ self.assertEqualIgnoreLeadingWhitespace( 'MOCK_CONST_METHOD2(Bar,\nvoid(int a, int b));', self.GenerateMethodSource(source)) def testCStyleCommentsInParameterListAreNotRemoved(self): # NOTE(nnorwitz): I'm not sure if it's the best behavior to keep these # comments. Also note that C style comments after the last parameter # are still elided. source = """ class Foo { public: virtual const string& Bar(int /* keeper */, int b); }; """ self.assertEqualIgnoreLeadingWhitespace( 'MOCK_METHOD2(Bar,\nconst string&(int /* keeper */, int b));', self.GenerateMethodSource(source)) def testArgsOfTemplateTypes(self): source = """ class Foo { public: virtual int Bar(const vector& v, map* output); };""" self.assertEqualIgnoreLeadingWhitespace( 'MOCK_METHOD2(Bar,\n' 'int(const vector& v, map* output));', self.GenerateMethodSource(source)) def testReturnTypeWithOneTemplateArg(self): source = """ class Foo { public: virtual vector* Bar(int n); };""" self.assertEqualIgnoreLeadingWhitespace( 'MOCK_METHOD1(Bar,\nvector*(int n));', self.GenerateMethodSource(source)) def testReturnTypeWithManyTemplateArgs(self): source = """ class Foo { public: virtual map Bar(); };""" # Comparing the comment text is brittle - we'll think of something # better in case this gets annoying, but for now let's keep it simple. self.assertEqualIgnoreLeadingWhitespace( '// The following line won\'t really compile, as the return\n' '// type has multiple template arguments. To fix it, use a\n' '// typedef for the return type.\n' 'MOCK_METHOD0(Bar,\nmap());', self.GenerateMethodSource(source)) def testSimpleMethodInTemplatedClass(self): source = """ template class Foo { public: virtual int Bar(); }; """ self.assertEqualIgnoreLeadingWhitespace( 'MOCK_METHOD0_T(Bar,\nint());', self.GenerateMethodSource(source)) def testPointerArgWithoutNames(self): source = """ class Foo { virtual int Bar(C*); }; """ self.assertEqualIgnoreLeadingWhitespace( 'MOCK_METHOD1(Bar,\nint(C*));', self.GenerateMethodSource(source)) def testReferenceArgWithoutNames(self): source = """ class Foo { virtual int Bar(C&); }; """ self.assertEqualIgnoreLeadingWhitespace( 'MOCK_METHOD1(Bar,\nint(C&));', self.GenerateMethodSource(source)) def testArrayArgWithoutNames(self): source = """ class Foo { virtual int Bar(C[]); }; """ self.assertEqualIgnoreLeadingWhitespace( 'MOCK_METHOD1(Bar,\nint(C[]));', self.GenerateMethodSource(source)) class GenerateMocksTest(TestCase): def GenerateMocks(self, cpp_source): """Convert C++ source to complete Google Mock output source.""" # is a pseudo-filename, it is not read or written. filename = '' builder = ast.BuilderFromSource(cpp_source, filename) ast_list = list(builder.Generate()) lines = gmock_class._GenerateMocks(filename, cpp_source, ast_list, None) return '\n'.join(lines) def testNamespaces(self): source = """ namespace Foo { namespace Bar { class Forward; } namespace Baz { class Test { public: virtual void Foo(); }; } // namespace Baz } // namespace Foo """ expected = """\ namespace Foo { namespace Baz { class MockTest : public Test { public: MOCK_METHOD0(Foo, void()); }; } // namespace Baz } // namespace Foo """ self.assertEqualIgnoreLeadingWhitespace( expected, self.GenerateMocks(source)) def testClassWithStorageSpecifierMacro(self): source = """ class STORAGE_SPECIFIER Test { public: virtual void Foo(); }; """ expected = """\ class MockTest : public Test { public: MOCK_METHOD0(Foo, void()); }; """ self.assertEqualIgnoreLeadingWhitespace( expected, self.GenerateMocks(source)) def testTemplatedForwardDeclaration(self): source = """ template class Forward; // Forward declaration should be ignored. class Test { public: virtual void Foo(); }; """ expected = """\ class MockTest : public Test { public: MOCK_METHOD0(Foo, void()); }; """ self.assertEqualIgnoreLeadingWhitespace( expected, self.GenerateMocks(source)) def testTemplatedClass(self): source = """ template class Test { public: virtual void Foo(); }; """ expected = """\ template class MockTest : public Test { public: MOCK_METHOD0_T(Foo, void()); }; """ self.assertEqualIgnoreLeadingWhitespace( expected, self.GenerateMocks(source)) def testTemplateInATemplateTypedef(self): source = """ class Test { public: typedef std::vector> FooType; virtual void Bar(const FooType& test_arg); }; """ expected = """\ class MockTest : public Test { public: MOCK_METHOD1(Bar, void(const FooType& test_arg)); }; """ self.assertEqualIgnoreLeadingWhitespace( expected, self.GenerateMocks(source)) def testTemplateInATemplateTypedefWithComma(self): source = """ class Test { public: typedef std::function>&, int> FooType; virtual void Bar(const FooType& test_arg); }; """ expected = """\ class MockTest : public Test { public: MOCK_METHOD1(Bar, void(const FooType& test_arg)); }; """ self.assertEqualIgnoreLeadingWhitespace( expected, self.GenerateMocks(source)) if __name__ == '__main__': unittest.main()