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