blob: f4060bc957606504bb5cc21a8501f80a026e9d4f [file] [log] [blame]
Daniel Cheng11c06e02021-09-01 23:51:291#!/usr/bin/env python3
2# Copyright 2021 The Chromium Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6import unittest
7
8import add_header
9
10
11class DecoratedFilenameTest(unittest.TestCase):
12 def testCHeaderClassification(self):
13 self.assertTrue(add_header.IsCSystemHeader('<stdlib.h>'))
14 self.assertFalse(add_header.IsCSystemHeader('<type_traits>'))
15 self.assertFalse(add_header.IsCSystemHeader('"moo.h"'))
16
17 def testCXXHeaderClassification(self):
18 self.assertFalse(add_header.IsCXXSystemHeader('<stdlib.h>'))
19 self.assertTrue(add_header.IsCXXSystemHeader('<type_traits>'))
20 self.assertFalse(add_header.IsCXXSystemHeader('"moo.h"'))
21
22 def testUserHeaderClassification(self):
23 self.assertFalse(add_header.IsUserHeader('<stdlib.h>'))
24 self.assertFalse(add_header.IsUserHeader('<type_traits>'))
25 self.assertTrue(add_header.IsUserHeader('"moo.h"'))
26
27 def testClassifyHeader(self):
28 self.assertEqual(add_header.ClassifyHeader('<stdlib.h>'),
29 add_header._HEADER_TYPE_C_SYSTEM)
30 self.assertEqual(add_header.ClassifyHeader('<type_traits>'),
31 add_header._HEADER_TYPE_CXX_SYSTEM)
32 self.assertEqual(add_header.ClassifyHeader('"moo.h"'),
33 add_header._HEADER_TYPE_USER)
34 self.assertEqual(add_header.ClassifyHeader('invalid'),
35 add_header._HEADER_TYPE_INVALID)
36
37
38class FindIncludesTest(unittest.TestCase):
39 def testEmpty(self):
40 begin, end = add_header.FindIncludes([])
41 self.assertEqual(begin, -1)
42 self.assertEqual(end, -1)
43
44 def testNoIncludes(self):
45 begin, end = add_header.FindIncludes(['a'])
46 self.assertEqual(begin, -1)
47 self.assertEqual(end, -1)
48
49 def testOneInclude(self):
50 begin, end = add_header.FindIncludes(['#include <algorithm>'])
51 self.assertEqual(begin, 0)
52 self.assertEqual(end, 1)
53
54 def testIncludeWithInlineComment(self):
55 begin, end = add_header.FindIncludes(
56 ['#include "moo.h" // TODO: Add more sounds.'])
57 self.assertEqual(begin, 0)
58 self.assertEqual(end, 1)
59
60 def testNewlinesBetweenIncludes(self):
61 begin, end = add_header.FindIncludes(
62 ['#include <utility>', '', '#include "moo.h"'])
63 self.assertEqual(begin, 0)
64 self.assertEqual(end, 3)
65
66 def testCommentsBetweenIncludes(self):
67 begin, end = add_header.FindIncludes([
68 '#include <utility>', '// TODO: Add goat support.', '#include "moo.h"'
69 ])
70 self.assertEqual(begin, 0)
71 self.assertEqual(end, 3)
72
73 def testEmptyLinesNotIncluded(self):
74 begin, end = add_header.FindIncludes(
75 ['', '#include <utility>', '', '#include "moo.h"', ''])
76 self.assertEqual(begin, 1)
77 self.assertEqual(end, 4)
78
79 def testCommentsNotIncluded(self):
80 begin, end = add_header.FindIncludes([
81 '// Cow module.', '#include <utility>', '// For cow speech synthesis.',
82 '#include "moo.h"', '// TODO: Add Linux audio support.'
83 ])
84 self.assertEqual(begin, 1)
85 self.assertEqual(end, 4)
86
87 def testNonIncludesLinesBeforeIncludesIgnored(self):
88 begin, end = add_header.FindIncludes(
89 ['#ifndef COW_H_', '#define COW_H_', '#include "moo.h"'])
90 self.assertEqual(begin, 2)
91 self.assertEqual(end, 3)
92
93 def testNonIncludesLinesAfterIncludesTerminates(self):
94 begin, end = add_header.FindIncludes([
95 '#include "moo.h"', '#ifndef COW_MESSAGES_H_', '#define COW_MESSAGE_H_'
96 ])
97 self.assertEqual(begin, 0)
98 self.assertEqual(end, 1)
99
100
101class IncludeTest(unittest.TestCase):
102 def testToSource(self):
103 self.assertEqual(
104 add_header.Include('<moo.h>', 'include', [], None).ToSource(),
105 ['#include <moo.h>'])
106
107 def testIncludeWithPreambleToSource(self):
108 self.assertEqual(
109 add_header.Include('"moo.h"', 'include', ['// preamble'],
110 None).ToSource(),
111 ['// preamble', '#include "moo.h"'])
112
113 def testIncludeWithInlineCommentToSource(self):
114 self.assertEqual(
115 add_header.Include('"moo.h"', 'include', [],
116 ' inline comment').ToSource(),
117 ['#include "moo.h" // inline comment'])
118
119 def testIncludeWithPreambleAndInlineCommentToSource(self):
120 # Make sure whitespace is vaguely normalized too.
121 self.assertEqual(
122 add_header.Include('"moo.h"', 'include', [
123 '// preamble with trailing space ',
124 ], ' inline comment with trailing space ').ToSource(), [
125 '// preamble with trailing space',
126 '#include "moo.h" // inline comment with trailing space'
127 ])
128
129 def testImportToSource(self):
130 self.assertEqual(
131 add_header.Include('"moo.h"', 'import', [], None).ToSource(),
132 ['#import "moo.h"'])
133
134
135class ParseIncludesTest(unittest.TestCase):
136 def testInvalid(self):
137 self.assertIsNone(add_header.ParseIncludes(['invalid']))
138
139 def testInclude(self):
140 includes = add_header.ParseIncludes(['#include "moo.h"'])
141 self.assertEqual(len(includes), 1)
142 self.assertEqual(includes[0].decorated_name, '"moo.h"')
143 self.assertEqual(includes[0].directive, 'include')
144 self.assertEqual(includes[0].preamble, [])
145 self.assertIsNone(includes[0].inline_comment)
146 self.assertEqual(includes[0].header_type, add_header._HEADER_TYPE_USER)
147 self.assertFalse(includes[0].is_primary_header)
148
149 def testIncludeSurroundedByWhitespace(self):
150 includes = add_header.ParseIncludes([' #include "moo.h" '])
151 self.assertEqual(len(includes), 1)
152 self.assertEqual(includes[0].decorated_name, '"moo.h"')
153 self.assertEqual(includes[0].directive, 'include')
154 self.assertEqual(includes[0].preamble, [])
155 self.assertIsNone(includes[0].inline_comment)
156 self.assertEqual(includes[0].header_type, add_header._HEADER_TYPE_USER)
157 self.assertFalse(includes[0].is_primary_header)
158
159 def testImport(self):
160 includes = add_header.ParseIncludes(['#import "moo.h"'])
161 self.assertEqual(len(includes), 1)
162 self.assertEqual(includes[0].decorated_name, '"moo.h"')
163 self.assertEqual(includes[0].directive, 'import')
164 self.assertEqual(includes[0].preamble, [])
165 self.assertIsNone(includes[0].inline_comment)
166 self.assertEqual(includes[0].header_type, add_header._HEADER_TYPE_USER)
167 self.assertFalse(includes[0].is_primary_header)
168
169 def testIncludeWithPreamble(self):
170 includes = add_header.ParseIncludes(
171 ['// preamble comment ', '#include "moo.h"'])
172 self.assertEqual(len(includes), 1)
173 self.assertEqual(includes[0].decorated_name, '"moo.h"')
174 self.assertEqual(includes[0].directive, 'include')
175 self.assertEqual(includes[0].preamble, ['// preamble comment '])
176 self.assertIsNone(includes[0].inline_comment)
177 self.assertEqual(includes[0].header_type, add_header._HEADER_TYPE_USER)
178 self.assertFalse(includes[0].is_primary_header)
179
180 def testIncludeWithInvalidPreamble(self):
181 self.assertIsNone(
182 add_header.ParseIncludes(['// orphan comment', '', '#include "moo.h"']))
183
184 def testIncludeWIthInlineComment(self):
185 includes = add_header.ParseIncludes(['#include "moo.h"// For SFX '])
186 self.assertEqual(len(includes), 1)
187 self.assertEqual(includes[0].decorated_name, '"moo.h"')
188 self.assertEqual(includes[0].directive, 'include')
189 self.assertEqual(includes[0].preamble, [])
190 self.assertEqual(includes[0].inline_comment, ' For SFX ')
191 self.assertEqual(includes[0].header_type, add_header._HEADER_TYPE_USER)
192 self.assertFalse(includes[0].is_primary_header)
193
194 def testIncludeWithInlineCommentAndPreamble(self):
195 includes = add_header.ParseIncludes(
196 ['// preamble comment ', '#include "moo.h" // For SFX '])
197 self.assertEqual(len(includes), 1)
198 self.assertEqual(includes[0].decorated_name, '"moo.h"')
199 self.assertEqual(includes[0].directive, 'include')
200 self.assertEqual(includes[0].preamble, ['// preamble comment '])
201 self.assertEqual(includes[0].inline_comment, ' For SFX ')
202 self.assertEqual(includes[0].header_type, add_header._HEADER_TYPE_USER)
203 self.assertFalse(includes[0].is_primary_header)
204
205 def testMultipleIncludes(self):
206 includes = add_header.ParseIncludes([
207 '#include <time.h>', '', '#include "moo.h" // For SFX ',
208 '// TODO: Implement death ray.', '#import "goat.h"'
209 ])
210 self.assertEqual(len(includes), 3)
211 self.assertEqual(includes[0].decorated_name, '<time.h>')
212 self.assertEqual(includes[0].directive, 'include')
213 self.assertEqual(includes[0].preamble, [])
214 self.assertIsNone(includes[0].inline_comment)
215 self.assertEqual(includes[0].header_type, add_header._HEADER_TYPE_C_SYSTEM)
216 self.assertFalse(includes[0].is_primary_header)
217 self.assertEqual(includes[1].decorated_name, '"moo.h"')
218 self.assertEqual(includes[1].directive, 'include')
219 self.assertEqual(includes[1].preamble, [])
220 self.assertEqual(includes[1].inline_comment, ' For SFX ')
221 self.assertEqual(includes[1].header_type, add_header._HEADER_TYPE_USER)
222 self.assertFalse(includes[1].is_primary_header)
223 self.assertEqual(includes[2].decorated_name, '"goat.h"')
224 self.assertEqual(includes[2].directive, 'import')
225 self.assertEqual(includes[2].preamble, ['// TODO: Implement death ray.'])
226 self.assertIsNone(includes[2].inline_comment)
227 self.assertEqual(includes[2].header_type, add_header._HEADER_TYPE_USER)
228 self.assertFalse(includes[2].is_primary_header)
229
230
231class MarkPrimaryIncludeTest(unittest.TestCase):
232 def _extract_primary_name(self, includes):
233 for include in includes:
234 if include.is_primary_header:
235 return include.decorated_name
236
237 def testNoOpOnHeader(self):
238 includes = [add_header.Include('"cow.h"', 'include', [], None)]
239 add_header.MarkPrimaryInclude(includes, 'cow.h')
240 self.assertIsNone(self._extract_primary_name(includes))
241
242 def testSystemHeaderNotMatched(self):
243 includes = [add_header.Include('<cow.h>', 'include', [], None)]
244 add_header.MarkPrimaryInclude(includes, 'cow.cc')
245 self.assertIsNone(self._extract_primary_name(includes))
246
247 def testExactMatch(self):
248 includes = [
249 add_header.Include('"cow.h"', 'include', [], None),
250 add_header.Include('"cow_posix.h"', 'include', [], None),
251 ]
252 add_header.MarkPrimaryInclude(includes, 'cow.cc')
253 self.assertEqual(self._extract_primary_name(includes), '"cow.h"')
254
255 def testFuzzyMatch(self):
256 includes = [add_header.Include('"cow.h"', 'include', [], None)]
257 add_header.MarkPrimaryInclude(includes, 'cow_linux_unittest.cc')
258 self.assertEqual(self._extract_primary_name(includes), '"cow.h"')
259
260 def testFuzzymatchInReverse(self):
261 includes = [add_header.Include('"cow.h"', 'include', [], None)]
262 add_header.MarkPrimaryInclude(includes, 'cow_uitest_aura.cc')
263 self.assertEqual(self._extract_primary_name(includes), '"cow.h"')
264
265 def testFuzzyMatchDoesntMatchDifferentSuffixes(self):
266 includes = [add_header.Include('"cow_posix.h"', 'include', [], None)]
267 add_header.MarkPrimaryInclude(includes, 'cow_windows.cc')
268 self.assertIsNone(self._extract_primary_name(includes))
269
270 def testMarksMostSpecific(self):
271 includes = [
272 add_header.Include('"cow.h"', 'include', [], None),
273 add_header.Include('"cow_posix.h"', 'include', [], None),
274 ]
275 add_header.MarkPrimaryInclude(includes, 'cow_posix.cc')
276 self.assertEqual(self._extract_primary_name(includes), '"cow_posix.h"')
277
278 def testFullPathMatch(self):
279 includes = [add_header.Include('"zfs/impl/cow.h"', 'include', [], None)]
280 add_header.MarkPrimaryInclude(includes, 'zfs/impl/cow.cc')
281 self.assertEqual(self._extract_primary_name(includes), '"zfs/impl/cow.h"')
282
283 def testTopmostDirectoryDoesNotMatch(self):
284 includes = [add_header.Include('"animal/impl/cow.h"', 'include', [], None)]
285 add_header.MarkPrimaryInclude(includes, 'zfs/impl/cow.cc')
286 self.assertIsNone(self._extract_primary_name(includes))
287
288 def testSubstantiallySimilarPaths(self):
289 includes = [
290 add_header.Include('"farm/public/animal/cow.h"', 'include', [], None)
291 ]
292 add_header.MarkPrimaryInclude(includes, 'farm/animal/cow.cc')
293 self.assertEqual(self._extract_primary_name(includes),
294 '"farm/public/animal/cow.h"')
295
Xiaohan Wang3b5e94fb2022-01-21 00:05:31296 def testSubstantiallySimilarPathsAndExactMatch(self):
297 includes = [
298 add_header.Include('"ui/gfx/ipc/geometry/gfx_param_traits.h"',
299 'include', [], None),
300 add_header.Include('"ui/gfx/ipc/gfx_param_traits.h"', 'include', [],
301 None),
302 ]
303 add_header.MarkPrimaryInclude(includes, 'ui/gfx/ipc/gfx_param_traits.cc')
304 self.assertEqual(self._extract_primary_name(includes),
305 '"ui/gfx/ipc/gfx_param_traits.h"')
306
Daniel Cheng11c06e02021-09-01 23:51:29307 def testNoMatchingSubdirectories(self):
308 includes = [add_header.Include('"base/zfs/cow.h"', 'include', [], None)]
309 add_header.MarkPrimaryInclude(includes, 'base/animal/cow.cc')
310 self.assertIsNone(self._extract_primary_name(includes))
311
312
313class SerializeIncludesTest(unittest.TestCase):
314 def testSystemHeaders(self):
315 source = add_header.SerializeIncludes([
316 add_header.Include('<stdlib.h>', 'include', [], None),
317 add_header.Include('<map>', 'include', [], None),
318 ])
319 self.assertEqual(source, ['#include <stdlib.h>', '', '#include <map>'])
320
321 def testUserHeaders(self):
322 source = add_header.SerializeIncludes([
323 add_header.Include('"goat.h"', 'include', [], None),
324 add_header.Include('"moo.h"', 'include', [], None),
325 ])
326 self.assertEqual(source, ['#include "goat.h"', '#include "moo.h"'])
327
328 def testSystemAndUserHeaders(self):
329 source = add_header.SerializeIncludes([
330 add_header.Include('<stdlib.h>', 'include', [], None),
331 add_header.Include('<map>', 'include', [], None),
332 add_header.Include('"moo.h"', 'include', [], None),
333 ])
334 self.assertEqual(
335 source,
336 ['#include <stdlib.h>', '', '#include <map>', '', '#include "moo.h"'])
337
338 def testPrimaryAndSystemHeaders(self):
339 primary_header = add_header.Include('"cow.h"', 'include', [], None)
340 primary_header.is_primary_header = True
341 source = add_header.SerializeIncludes([
342 primary_header,
343 add_header.Include('<stdlib.h>', 'include', [], None),
344 add_header.Include('<map>', 'include', [], None),
345 ])
346 self.assertEqual(
347 source,
348 ['#include "cow.h"', '', '#include <stdlib.h>', '', '#include <map>'])
349
350 def testPrimaryAndUserHeaders(self):
351 primary_header = add_header.Include('"cow.h"', 'include', [], None)
352 primary_header.is_primary_header = True
353 source = add_header.SerializeIncludes([
354 primary_header,
355 add_header.Include('"moo.h"', 'include', [], None),
356 ])
357 self.assertEqual(source, ['#include "cow.h"', '', '#include "moo.h"'])
358
359 def testPrimarySystemAndUserHeaders(self):
360 primary_header = add_header.Include('"cow.h"', 'include', [], None)
361 primary_header.is_primary_header = True
362 source = add_header.SerializeIncludes([
363 primary_header,
364 add_header.Include('<stdlib.h>', 'include', [], None),
365 add_header.Include('<map>', 'include', [], None),
366 add_header.Include('"moo.h"', 'include', [], None),
367 ])
368 self.assertEqual(source, [
369 '#include "cow.h"', '', '#include <stdlib.h>', '', '#include <map>', '',
370 '#include "moo.h"'
371 ])
372
373
Xiaohan Wang6080a062021-12-07 21:34:34374class AddHeaderToSourceTest(unittest.TestCase):
Daniel Cheng11c06e02021-09-01 23:51:29375 def testAddInclude(self):
Xiaohan Wang6080a062021-12-07 21:34:34376 source = add_header.AddHeaderToSource(
Daniel Cheng11c06e02021-09-01 23:51:29377 'cow.cc', '\n'.join([
378 '// Copyright info here.', '', '#include <utility>',
379 '// For cow speech synthesis.',
380 '#include "moo.h" // TODO: Add Linux audio support.',
381 '#include <time.h>', '#include "cow.h"', 'namespace bovine {', '',
382 '// TODO: Implement.', '} // namespace bovine'
383 ]), '<memory>')
384 self.assertEqual(
385 source, '\n'.join([
386 '// Copyright info here.', '', '#include "cow.h"', '',
387 '#include <time.h>', '', '#include <memory>', '#include <utility>',
388 '', '// For cow speech synthesis.',
389 '#include "moo.h" // TODO: Add Linux audio support.',
390 'namespace bovine {', '', '// TODO: Implement.',
391 '} // namespace bovine', ''
392 ]))
393
394 def testAlreadyIncluded(self):
395 # To make sure the original source is returned unmodified, the input source
396 # intentionally scrambles the #include order.
397 source = '\n'.join([
398 '// Copyright info here.', '', '#include "moo.h"', '#include <utility>',
399 '#include <memory>', '#include "cow.h"', 'namespace bovine {', '',
400 '// TODO: Implement.', '} // namespace bovine'
401 ])
Xiaohan Wang6080a062021-12-07 21:34:34402 self.assertEqual(add_header.AddHeaderToSource('cow.cc', source, '<memory>'),
403 None)
Daniel Cheng11c06e02021-09-01 23:51:29404
405 def testConditionalIncludesLeftALone(self):
406 # TODO(dcheng): Conditional header handling could probably be more clever.
407 # But for the moment, this is probably Good Enough.
Xiaohan Wang6080a062021-12-07 21:34:34408 source = add_header.AddHeaderToSource(
Daniel Cheng11c06e02021-09-01 23:51:29409 'cow.cc', '\n'.join([
410 '// Copyright info here.', '', '#include "cow.h"',
411 '#include <utility>', '// For cow speech synthesis.',
412 '#include "moo.h" // TODO: Add Linux audio support.',
413 '#if defined(USE_AURA)', '#include <memory>',
414 '#endif // defined(USE_AURA)'
415 ]), '<memory>')
416 self.assertEqual(
417 source, '\n'.join([
418 '// Copyright info here.', '', '#include "cow.h"', '',
419 '#include <memory>', '#include <utility>', '',
420 '// For cow speech synthesis.',
421 '#include "moo.h" // TODO: Add Linux audio support.',
422 '#if defined(USE_AURA)', '#include <memory>',
423 '#endif // defined(USE_AURA)', ''
424 ]))
425
Xiaohan Wang6080a062021-12-07 21:34:34426 def testRemoveInclude(self):
427 source = add_header.AddHeaderToSource(
428 'cow.cc',
429 '\n'.join([
430 '// Copyright info here.', '', '#include <memory>',
431 '#include <utility>', '// For cow speech synthesis.',
432 '#include "moo.h" // TODO: Add Linux audio support.',
433 '#include <time.h>', '#include "cow.h"', 'namespace bovine {', '',
434 '// TODO: Implement.', '} // namespace bovine'
435 ]),
436 '<utility>',
437 remove=True)
438 self.assertEqual(
439 source, '\n'.join([
440 '// Copyright info here.', '', '#include "cow.h"', '',
441 '#include <time.h>', '', '#include <memory>', '',
442 '// For cow speech synthesis.',
443 '#include "moo.h" // TODO: Add Linux audio support.',
444 'namespace bovine {', '', '// TODO: Implement.',
445 '} // namespace bovine', ''
446 ]))
447
Daniel Cheng11c06e02021-09-01 23:51:29448
449if __name__ == '__main__':
450 unittest.main()