[PDNCF] Python 3.12 compatibility
[platform/framework/web/chromium-efl.git] / tools / add_header_test.py
1 #!/usr/bin/env python3
2 # Copyright 2021 The Chromium Authors
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
5
6 import random
7 import unittest
8
9 import add_header
10
11
12 class 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
39 class 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
102 class 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
136 class 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
232 class 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
297   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
308   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
314 class 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
374   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)
379     includes.append(add_header.Include('<winsock2.h>', 'include', [], None))
380     includes.append(add_header.Include('<windows.h>', 'include', [], None))
381     includes.append(add_header.Include('<ws2tcpip.h>', 'include', [], None))
382     includes.append(add_header.Include('<shobjidl.h>', 'include', [], None))
383     includes.append(add_header.Include('<atlbase.h>', 'include', [], None))
384     includes.append(add_header.Include('<ole2.h>', 'include', [], None))
385     includes.append(add_header.Include('<unknwn.h>', 'include', [], None))
386     includes.append(add_header.Include('<objbase.h>', 'include', [], None))
387     includes.append(add_header.Include('<tchar.h>', 'include', [], None))
388     includes.append(add_header.Include('<string.h>', 'include', [], None))
389     includes.append(add_header.Include('<stddef.h>', 'include', [], None))
390     includes.append(add_header.Include('<stdio.h>', 'include', [], None))
391     includes.append(add_header.Include('"moo.h"', 'include', [], None))
392     random.shuffle(includes)
393     source = add_header.SerializeIncludes(includes)
394     self.assertEqual(source, [
395         '#include "cow.h"', '', '#include <winsock2.h>', '#include <windows.h>',
396         '#include <ws2tcpip.h>', '#include <shobjidl.h>',
397         '#include <atlbase.h>', '#include <ole2.h>', '#include <unknwn.h>',
398         '#include <objbase.h>', '#include <tchar.h>', '#include <stddef.h>',
399         '#include <stdio.h>', '#include <string.h>', '', '#include "moo.h"'
400     ])
401
402
403 class AddHeaderToSourceTest(unittest.TestCase):
404   def testAddInclude(self):
405     source = add_header.AddHeaderToSource(
406         '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     ])
431     self.assertEqual(add_header.AddHeaderToSource('cow.cc', source, '<memory>'),
432                      None)
433
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.
437     source = add_header.AddHeaderToSource(
438         '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
455   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
477
478 if __name__ == '__main__':
479   unittest.main()