| #!/usr/bin/env python3 |
| # Copyright 2021 The Chromium Authors |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| import random |
| import unittest |
| |
| import add_header |
| |
| |
| class DecoratedFilenameTest(unittest.TestCase): |
| def testCHeaderClassification(self): |
| self.assertTrue(add_header.IsCSystemHeader('<stdlib.h>')) |
| self.assertFalse(add_header.IsCSystemHeader('<type_traits>')) |
| self.assertFalse(add_header.IsCSystemHeader('"moo.h"')) |
| |
| def testCXXHeaderClassification(self): |
| self.assertFalse(add_header.IsCXXSystemHeader('<stdlib.h>')) |
| self.assertTrue(add_header.IsCXXSystemHeader('<type_traits>')) |
| self.assertFalse(add_header.IsCXXSystemHeader('"moo.h"')) |
| |
| def testUserHeaderClassification(self): |
| self.assertFalse(add_header.IsUserHeader('<stdlib.h>')) |
| self.assertFalse(add_header.IsUserHeader('<type_traits>')) |
| self.assertTrue(add_header.IsUserHeader('"moo.h"')) |
| |
| def testClassifyHeader(self): |
| self.assertEqual(add_header.ClassifyHeader('<stdlib.h>'), |
| add_header._HEADER_TYPE_C_SYSTEM) |
| self.assertEqual(add_header.ClassifyHeader('<type_traits>'), |
| add_header._HEADER_TYPE_CXX_SYSTEM) |
| self.assertEqual(add_header.ClassifyHeader('"moo.h"'), |
| add_header._HEADER_TYPE_USER) |
| self.assertEqual(add_header.ClassifyHeader('invalid'), |
| add_header._HEADER_TYPE_INVALID) |
| |
| |
| class FindIncludesTest(unittest.TestCase): |
| def testEmpty(self): |
| begin, end = add_header.FindIncludes([]) |
| self.assertEqual(begin, -1) |
| self.assertEqual(end, -1) |
| |
| def testNoIncludes(self): |
| begin, end = add_header.FindIncludes(['a']) |
| self.assertEqual(begin, -1) |
| self.assertEqual(end, -1) |
| |
| def testOneInclude(self): |
| begin, end = add_header.FindIncludes(['#include <algorithm>']) |
| self.assertEqual(begin, 0) |
| self.assertEqual(end, 1) |
| |
| def testIncludeWithInlineComment(self): |
| begin, end = add_header.FindIncludes( |
| ['#include "moo.h" // TODO: Add more sounds.']) |
| self.assertEqual(begin, 0) |
| self.assertEqual(end, 1) |
| |
| def testNewlinesBetweenIncludes(self): |
| begin, end = add_header.FindIncludes( |
| ['#include <utility>', '', '#include "moo.h"']) |
| self.assertEqual(begin, 0) |
| self.assertEqual(end, 3) |
| |
| def testCommentsBetweenIncludes(self): |
| begin, end = add_header.FindIncludes([ |
| '#include <utility>', '// TODO: Add goat support.', '#include "moo.h"' |
| ]) |
| self.assertEqual(begin, 0) |
| self.assertEqual(end, 3) |
| |
| def testEmptyLinesNotIncluded(self): |
| begin, end = add_header.FindIncludes( |
| ['', '#include <utility>', '', '#include "moo.h"', '']) |
| self.assertEqual(begin, 1) |
| self.assertEqual(end, 4) |
| |
| def testCommentsNotIncluded(self): |
| begin, end = add_header.FindIncludes([ |
| '// Cow module.', '#include <utility>', '// For cow speech synthesis.', |
| '#include "moo.h"', '// TODO: Add Linux audio support.' |
| ]) |
| self.assertEqual(begin, 1) |
| self.assertEqual(end, 4) |
| |
| def testNonIncludesLinesBeforeIncludesIgnored(self): |
| begin, end = add_header.FindIncludes( |
| ['#ifndef COW_H_', '#define COW_H_', '#include "moo.h"']) |
| self.assertEqual(begin, 2) |
| self.assertEqual(end, 3) |
| |
| def testNonIncludesLinesAfterIncludesTerminates(self): |
| begin, end = add_header.FindIncludes([ |
| '#include "moo.h"', '#ifndef COW_MESSAGES_H_', '#define COW_MESSAGE_H_' |
| ]) |
| self.assertEqual(begin, 0) |
| self.assertEqual(end, 1) |
| |
| |
| class IncludeTest(unittest.TestCase): |
| def testToSource(self): |
| self.assertEqual( |
| add_header.Include('<moo.h>', 'include', [], None).ToSource(), |
| ['#include <moo.h>']) |
| |
| def testIncludeWithPreambleToSource(self): |
| self.assertEqual( |
| add_header.Include('"moo.h"', 'include', ['// preamble'], |
| None).ToSource(), |
| ['// preamble', '#include "moo.h"']) |
| |
| def testIncludeWithInlineCommentToSource(self): |
| self.assertEqual( |
| add_header.Include('"moo.h"', 'include', [], |
| ' inline comment').ToSource(), |
| ['#include "moo.h" // inline comment']) |
| |
| def testIncludeWithPreambleAndInlineCommentToSource(self): |
| # Make sure whitespace is vaguely normalized too. |
| self.assertEqual( |
| add_header.Include('"moo.h"', 'include', [ |
| '// preamble with trailing space ', |
| ], ' inline comment with trailing space ').ToSource(), [ |
| '// preamble with trailing space', |
| '#include "moo.h" // inline comment with trailing space' |
| ]) |
| |
| def testImportToSource(self): |
| self.assertEqual( |
| add_header.Include('"moo.h"', 'import', [], None).ToSource(), |
| ['#import "moo.h"']) |
| |
| |
| class ParseIncludesTest(unittest.TestCase): |
| def testInvalid(self): |
| self.assertIsNone(add_header.ParseIncludes(['invalid'])) |
| |
| def testInclude(self): |
| includes = add_header.ParseIncludes(['#include "moo.h"']) |
| self.assertEqual(len(includes), 1) |
| self.assertEqual(includes[0].decorated_name, '"moo.h"') |
| self.assertEqual(includes[0].directive, 'include') |
| self.assertEqual(includes[0].preamble, []) |
| self.assertIsNone(includes[0].inline_comment) |
| self.assertEqual(includes[0].header_type, add_header._HEADER_TYPE_USER) |
| self.assertFalse(includes[0].is_primary_header) |
| |
| def testIncludeSurroundedByWhitespace(self): |
| includes = add_header.ParseIncludes([' #include "moo.h" ']) |
| self.assertEqual(len(includes), 1) |
| self.assertEqual(includes[0].decorated_name, '"moo.h"') |
| self.assertEqual(includes[0].directive, 'include') |
| self.assertEqual(includes[0].preamble, []) |
| self.assertIsNone(includes[0].inline_comment) |
| self.assertEqual(includes[0].header_type, add_header._HEADER_TYPE_USER) |
| self.assertFalse(includes[0].is_primary_header) |
| |
| def testImport(self): |
| includes = add_header.ParseIncludes(['#import "moo.h"']) |
| self.assertEqual(len(includes), 1) |
| self.assertEqual(includes[0].decorated_name, '"moo.h"') |
| self.assertEqual(includes[0].directive, 'import') |
| self.assertEqual(includes[0].preamble, []) |
| self.assertIsNone(includes[0].inline_comment) |
| self.assertEqual(includes[0].header_type, add_header._HEADER_TYPE_USER) |
| self.assertFalse(includes[0].is_primary_header) |
| |
| def testIncludeWithPreamble(self): |
| includes = add_header.ParseIncludes( |
| ['// preamble comment ', '#include "moo.h"']) |
| self.assertEqual(len(includes), 1) |
| self.assertEqual(includes[0].decorated_name, '"moo.h"') |
| self.assertEqual(includes[0].directive, 'include') |
| self.assertEqual(includes[0].preamble, ['// preamble comment ']) |
| self.assertIsNone(includes[0].inline_comment) |
| self.assertEqual(includes[0].header_type, add_header._HEADER_TYPE_USER) |
| self.assertFalse(includes[0].is_primary_header) |
| |
| def testIncludeWithInvalidPreamble(self): |
| self.assertIsNone( |
| add_header.ParseIncludes(['// orphan comment', '', '#include "moo.h"'])) |
| |
| def testIncludeWIthInlineComment(self): |
| includes = add_header.ParseIncludes(['#include "moo.h"// For SFX ']) |
| self.assertEqual(len(includes), 1) |
| self.assertEqual(includes[0].decorated_name, '"moo.h"') |
| self.assertEqual(includes[0].directive, 'include') |
| self.assertEqual(includes[0].preamble, []) |
| self.assertEqual(includes[0].inline_comment, ' For SFX ') |
| self.assertEqual(includes[0].header_type, add_header._HEADER_TYPE_USER) |
| self.assertFalse(includes[0].is_primary_header) |
| |
| def testIncludeWithInlineCommentAndPreamble(self): |
| includes = add_header.ParseIncludes( |
| ['// preamble comment ', '#include "moo.h" // For SFX ']) |
| self.assertEqual(len(includes), 1) |
| self.assertEqual(includes[0].decorated_name, '"moo.h"') |
| self.assertEqual(includes[0].directive, 'include') |
| self.assertEqual(includes[0].preamble, ['// preamble comment ']) |
| self.assertEqual(includes[0].inline_comment, ' For SFX ') |
| self.assertEqual(includes[0].header_type, add_header._HEADER_TYPE_USER) |
| self.assertFalse(includes[0].is_primary_header) |
| |
| def testMultipleIncludes(self): |
| includes = add_header.ParseIncludes([ |
| '#include <time.h>', '', '#include "moo.h" // For SFX ', |
| '// TODO: Implement death ray.', '#import "goat.h"' |
| ]) |
| self.assertEqual(len(includes), 3) |
| self.assertEqual(includes[0].decorated_name, '<time.h>') |
| self.assertEqual(includes[0].directive, 'include') |
| self.assertEqual(includes[0].preamble, []) |
| self.assertIsNone(includes[0].inline_comment) |
| self.assertEqual(includes[0].header_type, add_header._HEADER_TYPE_C_SYSTEM) |
| self.assertFalse(includes[0].is_primary_header) |
| self.assertEqual(includes[1].decorated_name, '"moo.h"') |
| self.assertEqual(includes[1].directive, 'include') |
| self.assertEqual(includes[1].preamble, []) |
| self.assertEqual(includes[1].inline_comment, ' For SFX ') |
| self.assertEqual(includes[1].header_type, add_header._HEADER_TYPE_USER) |
| self.assertFalse(includes[1].is_primary_header) |
| self.assertEqual(includes[2].decorated_name, '"goat.h"') |
| self.assertEqual(includes[2].directive, 'import') |
| self.assertEqual(includes[2].preamble, ['// TODO: Implement death ray.']) |
| self.assertIsNone(includes[2].inline_comment) |
| self.assertEqual(includes[2].header_type, add_header._HEADER_TYPE_USER) |
| self.assertFalse(includes[2].is_primary_header) |
| |
| |
| class MarkPrimaryIncludeTest(unittest.TestCase): |
| def _extract_primary_name(self, includes): |
| for include in includes: |
| if include.is_primary_header: |
| return include.decorated_name |
| |
| def testNoOpOnHeader(self): |
| includes = [add_header.Include('"cow.h"', 'include', [], None)] |
| add_header.MarkPrimaryInclude(includes, 'cow.h') |
| self.assertIsNone(self._extract_primary_name(includes)) |
| |
| def testSystemHeaderNotMatched(self): |
| includes = [add_header.Include('<cow.h>', 'include', [], None)] |
| add_header.MarkPrimaryInclude(includes, 'cow.cc') |
| self.assertIsNone(self._extract_primary_name(includes)) |
| |
| def testExactMatch(self): |
| includes = [ |
| add_header.Include('"cow.h"', 'include', [], None), |
| add_header.Include('"cow_posix.h"', 'include', [], None), |
| ] |
| add_header.MarkPrimaryInclude(includes, 'cow.cc') |
| self.assertEqual(self._extract_primary_name(includes), '"cow.h"') |
| |
| def testFuzzyMatch(self): |
| includes = [add_header.Include('"cow.h"', 'include', [], None)] |
| add_header.MarkPrimaryInclude(includes, 'cow_linux_unittest.cc') |
| self.assertEqual(self._extract_primary_name(includes), '"cow.h"') |
| |
| def testFuzzymatchInReverse(self): |
| includes = [add_header.Include('"cow.h"', 'include', [], None)] |
| add_header.MarkPrimaryInclude(includes, 'cow_uitest_aura.cc') |
| self.assertEqual(self._extract_primary_name(includes), '"cow.h"') |
| |
| def testFuzzyMatchDoesntMatchDifferentSuffixes(self): |
| includes = [add_header.Include('"cow_posix.h"', 'include', [], None)] |
| add_header.MarkPrimaryInclude(includes, 'cow_windows.cc') |
| self.assertIsNone(self._extract_primary_name(includes)) |
| |
| def testMarksMostSpecific(self): |
| includes = [ |
| add_header.Include('"cow.h"', 'include', [], None), |
| add_header.Include('"cow_posix.h"', 'include', [], None), |
| ] |
| add_header.MarkPrimaryInclude(includes, 'cow_posix.cc') |
| self.assertEqual(self._extract_primary_name(includes), '"cow_posix.h"') |
| |
| def testFullPathMatch(self): |
| includes = [add_header.Include('"zfs/impl/cow.h"', 'include', [], None)] |
| add_header.MarkPrimaryInclude(includes, 'zfs/impl/cow.cc') |
| self.assertEqual(self._extract_primary_name(includes), '"zfs/impl/cow.h"') |
| |
| def testTopmostDirectoryDoesNotMatch(self): |
| includes = [add_header.Include('"animal/impl/cow.h"', 'include', [], None)] |
| add_header.MarkPrimaryInclude(includes, 'zfs/impl/cow.cc') |
| self.assertIsNone(self._extract_primary_name(includes)) |
| |
| def testSubstantiallySimilarPaths(self): |
| includes = [ |
| add_header.Include('"farm/public/animal/cow.h"', 'include', [], None) |
| ] |
| add_header.MarkPrimaryInclude(includes, 'farm/animal/cow.cc') |
| self.assertEqual(self._extract_primary_name(includes), |
| '"farm/public/animal/cow.h"') |
| |
| def testSubstantiallySimilarPathsAndExactMatch(self): |
| includes = [ |
| add_header.Include('"ui/gfx/ipc/geometry/gfx_param_traits.h"', |
| 'include', [], None), |
| add_header.Include('"ui/gfx/ipc/gfx_param_traits.h"', 'include', [], |
| None), |
| ] |
| add_header.MarkPrimaryInclude(includes, 'ui/gfx/ipc/gfx_param_traits.cc') |
| self.assertEqual(self._extract_primary_name(includes), |
| '"ui/gfx/ipc/gfx_param_traits.h"') |
| |
| def testNoMatchingSubdirectories(self): |
| includes = [add_header.Include('"base/zfs/cow.h"', 'include', [], None)] |
| add_header.MarkPrimaryInclude(includes, 'base/animal/cow.cc') |
| self.assertIsNone(self._extract_primary_name(includes)) |
| |
| |
| class SerializeIncludesTest(unittest.TestCase): |
| def testSystemHeaders(self): |
| source = add_header.SerializeIncludes([ |
| add_header.Include('<stdlib.h>', 'include', [], None), |
| add_header.Include('<map>', 'include', [], None), |
| ]) |
| self.assertEqual(source, ['#include <stdlib.h>', '', '#include <map>']) |
| |
| def testUserHeaders(self): |
| source = add_header.SerializeIncludes([ |
| add_header.Include('"goat.h"', 'include', [], None), |
| add_header.Include('"moo.h"', 'include', [], None), |
| ]) |
| self.assertEqual(source, ['#include "goat.h"', '#include "moo.h"']) |
| |
| def testSystemAndUserHeaders(self): |
| source = add_header.SerializeIncludes([ |
| add_header.Include('<stdlib.h>', 'include', [], None), |
| add_header.Include('<map>', 'include', [], None), |
| add_header.Include('"moo.h"', 'include', [], None), |
| ]) |
| self.assertEqual( |
| source, |
| ['#include <stdlib.h>', '', '#include <map>', '', '#include "moo.h"']) |
| |
| def testPrimaryAndSystemHeaders(self): |
| primary_header = add_header.Include('"cow.h"', 'include', [], None) |
| primary_header.is_primary_header = True |
| source = add_header.SerializeIncludes([ |
| primary_header, |
| add_header.Include('<stdlib.h>', 'include', [], None), |
| add_header.Include('<map>', 'include', [], None), |
| ]) |
| self.assertEqual( |
| source, |
| ['#include "cow.h"', '', '#include <stdlib.h>', '', '#include <map>']) |
| |
| def testPrimaryAndUserHeaders(self): |
| primary_header = add_header.Include('"cow.h"', 'include', [], None) |
| primary_header.is_primary_header = True |
| source = add_header.SerializeIncludes([ |
| primary_header, |
| add_header.Include('"moo.h"', 'include', [], None), |
| ]) |
| self.assertEqual(source, ['#include "cow.h"', '', '#include "moo.h"']) |
| |
| def testPrimarySystemAndUserHeaders(self): |
| primary_header = add_header.Include('"cow.h"', 'include', [], None) |
| primary_header.is_primary_header = True |
| source = add_header.SerializeIncludes([ |
| primary_header, |
| add_header.Include('<stdlib.h>', 'include', [], None), |
| add_header.Include('<map>', 'include', [], None), |
| add_header.Include('"moo.h"', 'include', [], None), |
| ]) |
| self.assertEqual(source, [ |
| '#include "cow.h"', '', '#include <stdlib.h>', '', '#include <map>', '', |
| '#include "moo.h"' |
| ]) |
| |
| def testSpecialHeaders(self): |
| includes = [] |
| primary_header = add_header.Include('"cow.h"', 'include', [], None) |
| primary_header.is_primary_header = True |
| includes.append(primary_header) |
| includes.append(add_header.Include('<winsock2.h>', 'include', [], None)) |
| includes.append(add_header.Include('<windows.h>', 'include', [], None)) |
| includes.append(add_header.Include('<ws2tcpip.h>', 'include', [], None)) |
| includes.append(add_header.Include('<shobjidl.h>', 'include', [], None)) |
| includes.append(add_header.Include('<atlbase.h>', 'include', [], None)) |
| includes.append(add_header.Include('<ole2.h>', 'include', [], None)) |
| includes.append(add_header.Include('<unknwn.h>', 'include', [], None)) |
| includes.append(add_header.Include('<objbase.h>', 'include', [], None)) |
| includes.append(add_header.Include('<tchar.h>', 'include', [], None)) |
| includes.append(add_header.Include('<string.h>', 'include', [], None)) |
| includes.append(add_header.Include('<stddef.h>', 'include', [], None)) |
| includes.append(add_header.Include('<stdio.h>', 'include', [], None)) |
| includes.append(add_header.Include('"moo.h"', 'include', [], None)) |
| random.shuffle(includes) |
| source = add_header.SerializeIncludes(includes) |
| self.assertEqual(source, [ |
| '#include "cow.h"', '', '#include <winsock2.h>', '#include <windows.h>', |
| '#include <ws2tcpip.h>', '#include <shobjidl.h>', |
| '#include <atlbase.h>', '#include <ole2.h>', '#include <unknwn.h>', |
| '#include <objbase.h>', '#include <tchar.h>', '#include <stddef.h>', |
| '#include <stdio.h>', '#include <string.h>', '', '#include "moo.h"' |
| ]) |
| |
| |
| class AddHeaderToSourceTest(unittest.TestCase): |
| def testAddInclude(self): |
| source = add_header.AddHeaderToSource( |
| 'cow.cc', '\n'.join([ |
| '// Copyright info here.', '', '#include <utility>', |
| '// For cow speech synthesis.', |
| '#include "moo.h" // TODO: Add Linux audio support.', |
| '#include <time.h>', '#include "cow.h"', 'namespace bovine {', '', |
| '// TODO: Implement.', '} // namespace bovine' |
| ]), '<memory>') |
| self.assertEqual( |
| source, '\n'.join([ |
| '// Copyright info here.', '', '#include "cow.h"', '', |
| '#include <time.h>', '', '#include <memory>', '#include <utility>', |
| '', '// For cow speech synthesis.', |
| '#include "moo.h" // TODO: Add Linux audio support.', |
| 'namespace bovine {', '', '// TODO: Implement.', |
| '} // namespace bovine', '' |
| ])) |
| |
| def testAlreadyIncluded(self): |
| # To make sure the original source is returned unmodified, the input source |
| # intentionally scrambles the #include order. |
| source = '\n'.join([ |
| '// Copyright info here.', '', '#include "moo.h"', '#include <utility>', |
| '#include <memory>', '#include "cow.h"', 'namespace bovine {', '', |
| '// TODO: Implement.', '} // namespace bovine' |
| ]) |
| self.assertEqual(add_header.AddHeaderToSource('cow.cc', source, '<memory>'), |
| None) |
| |
| def testConditionalIncludesLeftALone(self): |
| # TODO(dcheng): Conditional header handling could probably be more clever. |
| # But for the moment, this is probably Good Enough. |
| source = add_header.AddHeaderToSource( |
| 'cow.cc', '\n'.join([ |
| '// Copyright info here.', '', '#include "cow.h"', |
| '#include <utility>', '// For cow speech synthesis.', |
| '#include "moo.h" // TODO: Add Linux audio support.', |
| '#if defined(USE_AURA)', '#include <memory>', |
| '#endif // defined(USE_AURA)' |
| ]), '<memory>') |
| self.assertEqual( |
| source, '\n'.join([ |
| '// Copyright info here.', '', '#include "cow.h"', '', |
| '#include <memory>', '#include <utility>', '', |
| '// For cow speech synthesis.', |
| '#include "moo.h" // TODO: Add Linux audio support.', |
| '#if defined(USE_AURA)', '#include <memory>', |
| '#endif // defined(USE_AURA)', '' |
| ])) |
| |
| def testRemoveInclude(self): |
| source = add_header.AddHeaderToSource( |
| 'cow.cc', |
| '\n'.join([ |
| '// Copyright info here.', '', '#include <memory>', |
| '#include <utility>', '// For cow speech synthesis.', |
| '#include "moo.h" // TODO: Add Linux audio support.', |
| '#include <time.h>', '#include "cow.h"', 'namespace bovine {', '', |
| '// TODO: Implement.', '} // namespace bovine' |
| ]), |
| '<utility>', |
| remove=True) |
| self.assertEqual( |
| source, '\n'.join([ |
| '// Copyright info here.', '', '#include "cow.h"', '', |
| '#include <time.h>', '', '#include <memory>', '', |
| '// For cow speech synthesis.', |
| '#include "moo.h" // TODO: Add Linux audio support.', |
| 'namespace bovine {', '', '// TODO: Implement.', |
| '} // namespace bovine', '' |
| ])) |
| |
| |
| if __name__ == '__main__': |
| unittest.main() |