[Crosswalk] Move away from custom_hooks needs in .gclient.
[platform/framework/web/chromium-efl.git] / PRESUBMIT_test.py
1 #!/usr/bin/env python
2 # Copyright (c) 2012 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
6 import os.path
7 import re
8 import subprocess
9 import unittest
10
11 import PRESUBMIT
12 from PRESUBMIT_test_mocks import MockChange, MockFile, MockAffectedFile
13 from PRESUBMIT_test_mocks import MockInputApi, MockOutputApi
14
15 _TEST_DATA_DIR = 'base/test/data/presubmit'
16
17 class VersionControlConflictsTest(unittest.TestCase):
18   def testTypicalConflict(self):
19     lines = ['<<<<<<< HEAD',
20              '  base::ScopedTempDir temp_dir_;',
21              '=======',
22              '  ScopedTempDir temp_dir_;',
23              '>>>>>>> master']
24     errors = PRESUBMIT._CheckForVersionControlConflictsInFile(
25         MockInputApi(), MockFile('some/path/foo_platform.cc', lines))
26     self.assertEqual(3, len(errors))
27     self.assertTrue('1' in errors[0])
28     self.assertTrue('3' in errors[1])
29     self.assertTrue('5' in errors[2])
30
31   def testIgnoresReadmes(self):
32     lines = ['A First Level Header',
33              '====================',
34              '',
35              'A Second Level Header',
36              '---------------------']
37     errors = PRESUBMIT._CheckForVersionControlConflictsInFile(
38         MockInputApi(), MockFile('some/polymer/README.md', lines))
39     self.assertEqual(0, len(errors))
40
41 class UmaHistogramChangeMatchedOrNotTest(unittest.TestCase):
42   def testTypicalCorrectlyMatchedChange(self):
43     diff_cc = ['UMA_HISTOGRAM_BOOL("Bla.Foo.Dummy", true)']
44     diff_java = [
45       'RecordHistogram.recordBooleanHistogram("Bla.Foo.Dummy", true)']
46     diff_xml = ['<histogram name="Bla.Foo.Dummy"> </histogram>']
47     mock_input_api = MockInputApi()
48     mock_input_api.files = [
49       MockFile('some/path/foo.cc', diff_cc),
50       MockFile('some/path/foo.java', diff_java),
51       MockFile('tools/metrics/histograms/histograms.xml', diff_xml),
52     ]
53     warnings = PRESUBMIT._CheckUmaHistogramChanges(mock_input_api,
54                                                    MockOutputApi())
55     self.assertEqual(0, len(warnings))
56
57   def testTypicalNotMatchedChange(self):
58     diff_cc = ['UMA_HISTOGRAM_BOOL("Bla.Foo.Dummy", true)']
59     diff_java = [
60       'RecordHistogram.recordBooleanHistogram("Bla.Foo.Dummy", true)']
61     mock_input_api = MockInputApi()
62     mock_input_api.files = [
63       MockFile('some/path/foo.cc', diff_cc),
64       MockFile('some/path/foo.java', diff_java),
65     ]
66     warnings = PRESUBMIT._CheckUmaHistogramChanges(mock_input_api,
67                                                    MockOutputApi())
68     self.assertEqual(1, len(warnings))
69     self.assertEqual('warning', warnings[0].type)
70     self.assertTrue('foo.cc' in warnings[0].items[0])
71     self.assertTrue('foo.java' in warnings[0].items[1])
72
73   def testTypicalNotMatchedChangeViaSuffixes(self):
74     diff_cc = ['UMA_HISTOGRAM_BOOL("Bla.Foo.Dummy", true)']
75     diff_java = [
76       'RecordHistogram.recordBooleanHistogram("Bla.Foo.Dummy", true)']
77     diff_xml = ['<histogram_suffixes name="SuperHistogram">',
78                 '  <suffix name="Dummy"/>',
79                 '  <affected-histogram name="Snafu.Dummy"/>',
80                 '</histogram>']
81     mock_input_api = MockInputApi()
82     mock_input_api.files = [
83       MockFile('some/path/foo.cc', diff_cc),
84       MockFile('some/path/foo.java', diff_java),
85       MockFile('tools/metrics/histograms/histograms.xml', diff_xml),
86     ]
87     warnings = PRESUBMIT._CheckUmaHistogramChanges(mock_input_api,
88                                                    MockOutputApi())
89     self.assertEqual(1, len(warnings))
90     self.assertEqual('warning', warnings[0].type)
91     self.assertTrue('foo.cc' in warnings[0].items[0])
92     self.assertTrue('foo.java' in warnings[0].items[1])
93
94   def testTypicalCorrectlyMatchedChangeViaSuffixes(self):
95     diff_cc = ['UMA_HISTOGRAM_BOOL("Bla.Foo.Dummy", true)']
96     diff_java = [
97       'RecordHistogram.recordBooleanHistogram("Bla.Foo.Dummy", true)']
98     diff_xml = ['<histogram_suffixes name="SuperHistogram">',
99                 '  <suffix name="Dummy"/>',
100                 '  <affected-histogram name="Bla.Foo"/>',
101                 '</histogram>']
102     mock_input_api = MockInputApi()
103     mock_input_api.files = [
104       MockFile('some/path/foo.cc', diff_cc),
105       MockFile('some/path/foo.java', diff_java),
106       MockFile('tools/metrics/histograms/histograms.xml', diff_xml),
107     ]
108     warnings = PRESUBMIT._CheckUmaHistogramChanges(mock_input_api,
109                                                    MockOutputApi())
110     self.assertEqual(0, len(warnings))
111
112   def testTypicalCorrectlyMatchedChangeViaSuffixesWithSeparator(self):
113     diff_cc = ['UMA_HISTOGRAM_BOOL("Snafu_Dummy", true)']
114     diff_java = ['RecordHistogram.recordBooleanHistogram("Snafu_Dummy", true)']
115     diff_xml = ['<histogram_suffixes name="SuperHistogram" separator="_">',
116                 '  <suffix name="Dummy"/>',
117                 '  <affected-histogram name="Snafu"/>',
118                 '</histogram>']
119     mock_input_api = MockInputApi()
120     mock_input_api.files = [
121       MockFile('some/path/foo.cc', diff_cc),
122       MockFile('some/path/foo.java', diff_java),
123       MockFile('tools/metrics/histograms/histograms.xml', diff_xml),
124     ]
125     warnings = PRESUBMIT._CheckUmaHistogramChanges(mock_input_api,
126                                                    MockOutputApi())
127     self.assertEqual(0, len(warnings))
128
129   def testNameMatch(self):
130     # Check that the detected histogram name is "Dummy" and not, e.g.,
131     # "Dummy\", true);  // The \"correct"
132     diff_cc = ['UMA_HISTOGRAM_BOOL("Dummy", true);  // The "correct" histogram']
133     diff_java = [
134       'RecordHistogram.recordBooleanHistogram("Dummy", true);'
135       + '  // The "correct" histogram']
136     diff_xml = ['<histogram name="Dummy"> </histogram>']
137     mock_input_api = MockInputApi()
138     mock_input_api.files = [
139       MockFile('some/path/foo.cc', diff_cc),
140       MockFile('some/path/foo.java', diff_java),
141       MockFile('tools/metrics/histograms/histograms.xml', diff_xml),
142     ]
143     warnings = PRESUBMIT._CheckUmaHistogramChanges(mock_input_api,
144                                                    MockOutputApi())
145     self.assertEqual(0, len(warnings))
146
147   def testSimilarMacroNames(self):
148     diff_cc = ['PUMA_HISTOGRAM_COOL("Mountain Lion", 42)']
149     diff_java = [
150       'FakeRecordHistogram.recordFakeHistogram("Mountain Lion", 42)']
151     mock_input_api = MockInputApi()
152     mock_input_api.files = [
153       MockFile('some/path/foo.cc', diff_cc),
154       MockFile('some/path/foo.java', diff_java),
155     ]
156     warnings = PRESUBMIT._CheckUmaHistogramChanges(mock_input_api,
157                                                    MockOutputApi())
158     self.assertEqual(0, len(warnings))
159
160   def testMultiLine(self):
161     diff_cc = ['UMA_HISTOGRAM_BOOLEAN(', '    "Multi.Line", true)']
162     diff_cc2 = ['UMA_HISTOGRAM_BOOLEAN(', '    "Multi.Line"', '    , true)']
163     diff_java = [
164       'RecordHistogram.recordBooleanHistogram(',
165       '    "Multi.Line", true);',
166     ]
167     mock_input_api = MockInputApi()
168     mock_input_api.files = [
169       MockFile('some/path/foo.cc', diff_cc),
170       MockFile('some/path/foo2.cc', diff_cc2),
171       MockFile('some/path/foo.java', diff_java),
172     ]
173     warnings = PRESUBMIT._CheckUmaHistogramChanges(mock_input_api,
174                                                    MockOutputApi())
175     self.assertEqual(1, len(warnings))
176     self.assertEqual('warning', warnings[0].type)
177     self.assertTrue('foo.cc' in warnings[0].items[0])
178     self.assertTrue('foo2.cc' in warnings[0].items[1])
179
180 class BadExtensionsTest(unittest.TestCase):
181   def testBadRejFile(self):
182     mock_input_api = MockInputApi()
183     mock_input_api.files = [
184       MockFile('some/path/foo.cc', ''),
185       MockFile('some/path/foo.cc.rej', ''),
186       MockFile('some/path2/bar.h.rej', ''),
187     ]
188
189     results = PRESUBMIT._CheckPatchFiles(mock_input_api, MockOutputApi())
190     self.assertEqual(1, len(results))
191     self.assertEqual(2, len(results[0].items))
192     self.assertTrue('foo.cc.rej' in results[0].items[0])
193     self.assertTrue('bar.h.rej' in results[0].items[1])
194
195   def testBadOrigFile(self):
196     mock_input_api = MockInputApi()
197     mock_input_api.files = [
198       MockFile('other/path/qux.h.orig', ''),
199       MockFile('other/path/qux.h', ''),
200       MockFile('other/path/qux.cc', ''),
201     ]
202
203     results = PRESUBMIT._CheckPatchFiles(mock_input_api, MockOutputApi())
204     self.assertEqual(1, len(results))
205     self.assertEqual(1, len(results[0].items))
206     self.assertTrue('qux.h.orig' in results[0].items[0])
207
208   def testGoodFiles(self):
209     mock_input_api = MockInputApi()
210     mock_input_api.files = [
211       MockFile('other/path/qux.h', ''),
212       MockFile('other/path/qux.cc', ''),
213     ]
214     results = PRESUBMIT._CheckPatchFiles(mock_input_api, MockOutputApi())
215     self.assertEqual(0, len(results))
216
217
218 class CheckSingletonInHeadersTest(unittest.TestCase):
219   def testSingletonInArbitraryHeader(self):
220     diff_singleton_h = ['base::subtle::AtomicWord '
221                         'base::Singleton<Type, Traits, DifferentiatingType>::']
222     diff_foo_h = ['// base::Singleton<Foo> in comment.',
223                   'friend class base::Singleton<Foo>']
224     diff_foo2_h = ['  //Foo* bar = base::Singleton<Foo>::get();']
225     diff_bad_h = ['Foo* foo = base::Singleton<Foo>::get();']
226     mock_input_api = MockInputApi()
227     mock_input_api.files = [MockAffectedFile('base/memory/singleton.h',
228                                      diff_singleton_h),
229                             MockAffectedFile('foo.h', diff_foo_h),
230                             MockAffectedFile('foo2.h', diff_foo2_h),
231                             MockAffectedFile('bad.h', diff_bad_h)]
232     warnings = PRESUBMIT._CheckSingletonInHeaders(mock_input_api,
233                                                   MockOutputApi())
234     self.assertEqual(1, len(warnings))
235     self.assertEqual(1, len(warnings[0].items))
236     self.assertEqual('error', warnings[0].type)
237     self.assertTrue('Found base::Singleton<T>' in warnings[0].message)
238
239   def testSingletonInCC(self):
240     diff_cc = ['Foo* foo = base::Singleton<Foo>::get();']
241     mock_input_api = MockInputApi()
242     mock_input_api.files = [MockAffectedFile('some/path/foo.cc', diff_cc)]
243     warnings = PRESUBMIT._CheckSingletonInHeaders(mock_input_api,
244                                                   MockOutputApi())
245     self.assertEqual(0, len(warnings))
246
247
248 class InvalidOSMacroNamesTest(unittest.TestCase):
249   def testInvalidOSMacroNames(self):
250     lines = ['#if defined(OS_WINDOWS)',
251              ' #elif defined(OS_WINDOW)',
252              ' # if defined(OS_MACOSX) || defined(OS_CHROME)',
253              '# else  // defined(OS_MAC)',
254              '#endif  // defined(OS_MACOS)']
255     errors = PRESUBMIT._CheckForInvalidOSMacrosInFile(
256         MockInputApi(), MockFile('some/path/foo_platform.cc', lines))
257     self.assertEqual(len(lines), len(errors))
258     self.assertTrue(':1 OS_WINDOWS' in errors[0])
259     self.assertTrue('(did you mean OS_WIN?)' in errors[0])
260
261   def testValidOSMacroNames(self):
262     lines = ['#if defined(%s)' % m for m in PRESUBMIT._VALID_OS_MACROS]
263     errors = PRESUBMIT._CheckForInvalidOSMacrosInFile(
264         MockInputApi(), MockFile('some/path/foo_platform.cc', lines))
265     self.assertEqual(0, len(errors))
266
267
268 class InvalidIfDefinedMacroNamesTest(unittest.TestCase):
269   def testInvalidIfDefinedMacroNames(self):
270     lines = ['#if defined(TARGET_IPHONE_SIMULATOR)',
271              '#if !defined(TARGET_IPHONE_SIMULATOR)',
272              '#elif defined(TARGET_IPHONE_SIMULATOR)',
273              '#ifdef TARGET_IPHONE_SIMULATOR',
274              ' # ifdef TARGET_IPHONE_SIMULATOR',
275              '# if defined(VALID) || defined(TARGET_IPHONE_SIMULATOR)',
276              '# else  // defined(TARGET_IPHONE_SIMULATOR)',
277              '#endif  // defined(TARGET_IPHONE_SIMULATOR)',]
278     errors = PRESUBMIT._CheckForInvalidIfDefinedMacrosInFile(
279         MockInputApi(), MockFile('some/path/source.mm', lines))
280     self.assertEqual(len(lines), len(errors))
281
282   def testValidIfDefinedMacroNames(self):
283     lines = ['#if defined(FOO)',
284              '#ifdef BAR',]
285     errors = PRESUBMIT._CheckForInvalidIfDefinedMacrosInFile(
286         MockInputApi(), MockFile('some/path/source.cc', lines))
287     self.assertEqual(0, len(errors))
288
289
290 class CheckAddedDepsHaveTetsApprovalsTest(unittest.TestCase):
291
292   def calculate(self, old_include_rules, old_specific_include_rules,
293                 new_include_rules, new_specific_include_rules):
294     return PRESUBMIT._CalculateAddedDeps(
295         os.path, 'include_rules = %r\nspecific_include_rules = %r' % (
296             old_include_rules, old_specific_include_rules),
297         'include_rules = %r\nspecific_include_rules = %r' % (
298             new_include_rules, new_specific_include_rules))
299
300   def testCalculateAddedDeps(self):
301     old_include_rules = [
302         '+base',
303         '-chrome',
304         '+content',
305         '-grit',
306         '-grit/",',
307         '+jni/fooblat.h',
308         '!sandbox',
309     ]
310     old_specific_include_rules = {
311         'compositor\.*': {
312             '+cc',
313         },
314     }
315
316     new_include_rules = [
317         '-ash',
318         '+base',
319         '+chrome',
320         '+components',
321         '+content',
322         '+grit',
323         '+grit/generated_resources.h",',
324         '+grit/",',
325         '+jni/fooblat.h',
326         '+policy',
327         '+' + os.path.join('third_party', 'WebKit'),
328     ]
329     new_specific_include_rules = {
330         'compositor\.*': {
331             '+cc',
332         },
333         'widget\.*': {
334             '+gpu',
335         },
336     }
337
338     expected = set([
339         os.path.join('chrome', 'DEPS'),
340         os.path.join('gpu', 'DEPS'),
341         os.path.join('components', 'DEPS'),
342         os.path.join('policy', 'DEPS'),
343         os.path.join('third_party', 'WebKit', 'DEPS'),
344     ])
345     self.assertEqual(
346         expected,
347         self.calculate(old_include_rules, old_specific_include_rules,
348                        new_include_rules, new_specific_include_rules))
349
350   def testCalculateAddedDepsIgnoresPermutations(self):
351     old_include_rules = [
352         '+base',
353         '+chrome',
354     ]
355     new_include_rules = [
356         '+chrome',
357         '+base',
358     ]
359     self.assertEqual(set(),
360                      self.calculate(old_include_rules, {}, new_include_rules,
361                                     {}))
362
363
364 class JSONParsingTest(unittest.TestCase):
365   def testSuccess(self):
366     input_api = MockInputApi()
367     filename = 'valid_json.json'
368     contents = ['// This is a comment.',
369                 '{',
370                 '  "key1": ["value1", "value2"],',
371                 '  "key2": 3  // This is an inline comment.',
372                 '}'
373                 ]
374     input_api.files = [MockFile(filename, contents)]
375     self.assertEqual(None,
376                      PRESUBMIT._GetJSONParseError(input_api, filename))
377
378   def testFailure(self):
379     input_api = MockInputApi()
380     test_data = [
381       ('invalid_json_1.json',
382        ['{ x }'],
383        'Expecting property name:'),
384       ('invalid_json_2.json',
385        ['// Hello world!',
386         '{ "hello": "world }'],
387        'Unterminated string starting at:'),
388       ('invalid_json_3.json',
389        ['{ "a": "b", "c": "d", }'],
390        'Expecting property name:'),
391       ('invalid_json_4.json',
392        ['{ "a": "b" "c": "d" }'],
393        'Expecting , delimiter:'),
394       ]
395
396     input_api.files = [MockFile(filename, contents)
397                        for (filename, contents, _) in test_data]
398
399     for (filename, _, expected_error) in test_data:
400       actual_error = PRESUBMIT._GetJSONParseError(input_api, filename)
401       self.assertTrue(expected_error in str(actual_error),
402                       "'%s' not found in '%s'" % (expected_error, actual_error))
403
404   def testNoEatComments(self):
405     input_api = MockInputApi()
406     file_with_comments = 'file_with_comments.json'
407     contents_with_comments = ['// This is a comment.',
408                               '{',
409                               '  "key1": ["value1", "value2"],',
410                               '  "key2": 3  // This is an inline comment.',
411                               '}'
412                               ]
413     file_without_comments = 'file_without_comments.json'
414     contents_without_comments = ['{',
415                                  '  "key1": ["value1", "value2"],',
416                                  '  "key2": 3',
417                                  '}'
418                                  ]
419     input_api.files = [MockFile(file_with_comments, contents_with_comments),
420                        MockFile(file_without_comments,
421                                 contents_without_comments)]
422
423     self.assertEqual('No JSON object could be decoded',
424                      str(PRESUBMIT._GetJSONParseError(input_api,
425                                                       file_with_comments,
426                                                       eat_comments=False)))
427     self.assertEqual(None,
428                      PRESUBMIT._GetJSONParseError(input_api,
429                                                   file_without_comments,
430                                                   eat_comments=False))
431
432
433 class IDLParsingTest(unittest.TestCase):
434   def testSuccess(self):
435     input_api = MockInputApi()
436     filename = 'valid_idl_basics.idl'
437     contents = ['// Tests a valid IDL file.',
438                 'namespace idl_basics {',
439                 '  enum EnumType {',
440                 '    name1,',
441                 '    name2',
442                 '  };',
443                 '',
444                 '  dictionary MyType1 {',
445                 '    DOMString a;',
446                 '  };',
447                 '',
448                 '  callback Callback1 = void();',
449                 '  callback Callback2 = void(long x);',
450                 '  callback Callback3 = void(MyType1 arg);',
451                 '  callback Callback4 = void(EnumType type);',
452                 '',
453                 '  interface Functions {',
454                 '    static void function1();',
455                 '    static void function2(long x);',
456                 '    static void function3(MyType1 arg);',
457                 '    static void function4(Callback1 cb);',
458                 '    static void function5(Callback2 cb);',
459                 '    static void function6(Callback3 cb);',
460                 '    static void function7(Callback4 cb);',
461                 '  };',
462                 '',
463                 '  interface Events {',
464                 '    static void onFoo1();',
465                 '    static void onFoo2(long x);',
466                 '    static void onFoo2(MyType1 arg);',
467                 '    static void onFoo3(EnumType type);',
468                 '  };',
469                 '};'
470                 ]
471     input_api.files = [MockFile(filename, contents)]
472     self.assertEqual(None,
473                      PRESUBMIT._GetIDLParseError(input_api, filename))
474
475   def testFailure(self):
476     input_api = MockInputApi()
477     test_data = [
478       ('invalid_idl_1.idl',
479        ['//',
480         'namespace test {',
481         '  dictionary {',
482         '    DOMString s;',
483         '  };',
484         '};'],
485        'Unexpected "{" after keyword "dictionary".\n'),
486       # TODO(yoz): Disabled because it causes the IDL parser to hang.
487       # See crbug.com/363830.
488       # ('invalid_idl_2.idl',
489       #  (['namespace test {',
490       #    '  dictionary MissingSemicolon {',
491       #    '    DOMString a',
492       #    '    DOMString b;',
493       #    '  };',
494       #    '};'],
495       #   'Unexpected symbol DOMString after symbol a.'),
496       ('invalid_idl_3.idl',
497        ['//',
498         'namespace test {',
499         '  enum MissingComma {',
500         '    name1',
501         '    name2',
502         '  };',
503         '};'],
504        'Unexpected symbol name2 after symbol name1.'),
505       ('invalid_idl_4.idl',
506        ['//',
507         'namespace test {',
508         '  enum TrailingComma {',
509         '    name1,',
510         '    name2,',
511         '  };',
512         '};'],
513        'Trailing comma in block.'),
514       ('invalid_idl_5.idl',
515        ['//',
516         'namespace test {',
517         '  callback Callback1 = void(;',
518         '};'],
519        'Unexpected ";" after "(".'),
520       ('invalid_idl_6.idl',
521        ['//',
522         'namespace test {',
523         '  callback Callback1 = void(long );',
524         '};'],
525        'Unexpected ")" after symbol long.'),
526       ('invalid_idl_7.idl',
527        ['//',
528         'namespace test {',
529         '  interace Events {',
530         '    static void onFoo1();',
531         '  };',
532         '};'],
533        'Unexpected symbol Events after symbol interace.'),
534       ('invalid_idl_8.idl',
535        ['//',
536         'namespace test {',
537         '  interface NotEvent {',
538         '    static void onFoo1();',
539         '  };',
540         '};'],
541        'Did not process Interface Interface(NotEvent)'),
542       ('invalid_idl_9.idl',
543        ['//',
544         'namespace test {',
545         '  interface {',
546         '    static void function1();',
547         '  };',
548         '};'],
549        'Interface missing name.'),
550       ]
551
552     input_api.files = [MockFile(filename, contents)
553                        for (filename, contents, _) in test_data]
554
555     for (filename, _, expected_error) in test_data:
556       actual_error = PRESUBMIT._GetIDLParseError(input_api, filename)
557       self.assertTrue(expected_error in str(actual_error),
558                       "'%s' not found in '%s'" % (expected_error, actual_error))
559
560
561 class TryServerMasterTest(unittest.TestCase):
562   def testTryServerMasters(self):
563     bots = {
564         'master.tryserver.chromium.android': [
565             'android_archive_rel_ng',
566             'android_arm64_dbg_recipe',
567             'android_blink_rel',
568             'android_chromium_variable',
569             'android_chromium_variable_archive',
570             'android_chromium_variable_arm64',
571             'android_chromium_variable_cast_shell',
572             'android_chromium_variable_clang',
573             'android_chromium_variable_gn',
574             'android_chromium_variable_nexus4',
575             'android_clang_dbg_recipe',
576             'android_compile_dbg',
577             'android_compile_x64_dbg',
578             'android_compile_x86_dbg',
579             'android_coverage',
580             'android_cronet_tester'
581             'android_swarming_rel',
582             'cast_shell_android',
583             'linux_android_dbg_ng',
584             'linux_android_rel_ng',
585         ],
586         'master.tryserver.chromium.mac': [
587             'ios_dbg_simulator',
588             'ios_rel_device',
589             'ios_rel_device_ninja',
590             'mac_asan',
591             'mac_asan_64',
592             'mac_chromium_compile_dbg',
593             'mac_chromium_compile_rel',
594             'mac_chromium_dbg',
595             'mac_chromium_rel',
596             'mac_nacl_sdk',
597             'mac_nacl_sdk_build',
598             'mac_rel_naclmore',
599             'mac_x64_rel',
600             'mac_xcodebuild',
601         ],
602         'master.tryserver.chromium.linux': [
603             'chromium_presubmit',
604             'linux_arm_cross_compile',
605             'linux_arm_tester',
606             'linux_chromeos_asan',
607             'linux_chromeos_browser_asan',
608             'linux_chromeos_valgrind',
609             'linux_chromium_chromeos_dbg',
610             'linux_chromium_chromeos_rel',
611             'linux_chromium_compile_dbg',
612             'linux_chromium_compile_rel',
613             'linux_chromium_dbg',
614             'linux_chromium_gn_dbg',
615             'linux_chromium_gn_rel',
616             'linux_chromium_rel',
617             'linux_chromium_trusty32_dbg',
618             'linux_chromium_trusty32_rel',
619             'linux_chromium_trusty_dbg',
620             'linux_chromium_trusty_rel',
621             'linux_clang_tsan',
622             'linux_ecs_ozone',
623             'linux_layout',
624             'linux_layout_asan',
625             'linux_layout_rel',
626             'linux_layout_rel_32',
627             'linux_nacl_sdk',
628             'linux_nacl_sdk_bionic',
629             'linux_nacl_sdk_bionic_build',
630             'linux_nacl_sdk_build',
631             'linux_redux',
632             'linux_rel_naclmore',
633             'linux_rel_precise32',
634             'linux_valgrind',
635             'tools_build_presubmit',
636         ],
637         'master.tryserver.chromium.win': [
638             'win8_aura',
639             'win8_chromium_dbg',
640             'win8_chromium_rel',
641             'win_chromium_compile_dbg',
642             'win_chromium_compile_rel',
643             'win_chromium_dbg',
644             'win_chromium_rel',
645             'win_chromium_rel',
646             'win_chromium_x64_dbg',
647             'win_chromium_x64_rel',
648             'win_nacl_sdk',
649             'win_nacl_sdk_build',
650             'win_rel_naclmore',
651          ],
652     }
653     for master, bots in bots.iteritems():
654       for bot in bots:
655         self.assertEqual(master, PRESUBMIT.GetTryServerMasterForBot(bot),
656                          'bot=%s: expected %s, computed %s' % (
657             bot, master, PRESUBMIT.GetTryServerMasterForBot(bot)))
658
659
660 class UserMetricsActionTest(unittest.TestCase):
661   def testUserMetricsActionInActions(self):
662     input_api = MockInputApi()
663     file_with_user_action = 'file_with_user_action.cc'
664     contents_with_user_action = [
665       'base::UserMetricsAction("AboutChrome")'
666     ]
667
668     input_api.files = [MockFile(file_with_user_action,
669                                 contents_with_user_action)]
670
671     self.assertEqual(
672       [], PRESUBMIT._CheckUserActionUpdate(input_api, MockOutputApi()))
673
674
675   def testUserMetricsActionNotAddedToActions(self):
676     input_api = MockInputApi()
677     file_with_user_action = 'file_with_user_action.cc'
678     contents_with_user_action = [
679       'base::UserMetricsAction("NotInActionsXml")'
680     ]
681
682     input_api.files = [MockFile(file_with_user_action,
683                                 contents_with_user_action)]
684
685     output = PRESUBMIT._CheckUserActionUpdate(input_api, MockOutputApi())
686     self.assertEqual(
687       ('File %s line %d: %s is missing in '
688        'tools/metrics/actions/actions.xml. Please run '
689        'tools/metrics/actions/extract_actions.py to update.'
690        % (file_with_user_action, 1, 'NotInActionsXml')),
691       output[0].message)
692
693
694 class PydepsNeedsUpdatingTest(unittest.TestCase):
695
696   class MockSubprocess(object):
697     CalledProcessError = subprocess.CalledProcessError
698
699   def setUp(self):
700     mock_all_pydeps = ['A.pydeps', 'B.pydeps']
701     self.old_ALL_PYDEPS_FILES = PRESUBMIT._ALL_PYDEPS_FILES
702     PRESUBMIT._ALL_PYDEPS_FILES = mock_all_pydeps
703     self.mock_input_api = MockInputApi()
704     self.mock_output_api = MockOutputApi()
705     self.mock_input_api.subprocess = PydepsNeedsUpdatingTest.MockSubprocess()
706     self.checker = PRESUBMIT.PydepsChecker(self.mock_input_api, mock_all_pydeps)
707     self.checker._file_cache = {
708         'A.pydeps': '# Generated by:\n# CMD A\nA.py\nC.py\n',
709         'B.pydeps': '# Generated by:\n# CMD B\nB.py\nC.py\n',
710     }
711
712   def tearDown(self):
713     PRESUBMIT._ALL_PYDEPS_FILES = self.old_ALL_PYDEPS_FILES
714
715   def _RunCheck(self):
716     return PRESUBMIT._CheckPydepsNeedsUpdating(self.mock_input_api,
717                                                self.mock_output_api,
718                                                checker_for_tests=self.checker)
719
720   def testAddedPydep(self):
721     # PRESUBMIT._CheckPydepsNeedsUpdating is only implemented for Android.
722     if self.mock_input_api.platform != 'linux2':
723       return []
724
725     self.mock_input_api.files = [
726       MockAffectedFile('new.pydeps', [], action='A'),
727     ]
728
729     self.mock_input_api.CreateMockFileInPath(
730         [x.LocalPath() for x in self.mock_input_api.AffectedFiles(
731             include_deletes=True)])
732     results = self._RunCheck()
733     self.assertEqual(1, len(results))
734     self.assertTrue('PYDEPS_FILES' in str(results[0]))
735
736   def testPydepNotInSrc(self):
737     self.mock_input_api.files = [
738       MockAffectedFile('new.pydeps', [], action='A'),
739     ]
740     self.mock_input_api.CreateMockFileInPath([])
741     results = self._RunCheck()
742     self.assertEqual(0, len(results))
743
744   def testRemovedPydep(self):
745     # PRESUBMIT._CheckPydepsNeedsUpdating is only implemented for Android.
746     if self.mock_input_api.platform != 'linux2':
747       return []
748
749     self.mock_input_api.files = [
750       MockAffectedFile(PRESUBMIT._ALL_PYDEPS_FILES[0], [], action='D'),
751     ]
752     self.mock_input_api.CreateMockFileInPath(
753         [x.LocalPath() for x in self.mock_input_api.AffectedFiles(
754             include_deletes=True)])
755     results = self._RunCheck()
756     self.assertEqual(1, len(results))
757     self.assertTrue('PYDEPS_FILES' in str(results[0]))
758
759   def testRandomPyIgnored(self):
760     # PRESUBMIT._CheckPydepsNeedsUpdating is only implemented for Android.
761     if self.mock_input_api.platform != 'linux2':
762       return []
763
764     self.mock_input_api.files = [
765       MockAffectedFile('random.py', []),
766     ]
767
768     results = self._RunCheck()
769     self.assertEqual(0, len(results), 'Unexpected results: %r' % results)
770
771   def testRelevantPyNoChange(self):
772     # PRESUBMIT._CheckPydepsNeedsUpdating is only implemented for Android.
773     if self.mock_input_api.platform != 'linux2':
774       return []
775
776     self.mock_input_api.files = [
777       MockAffectedFile('A.py', []),
778     ]
779
780     def mock_check_output(cmd, shell=False, env=None):
781       self.assertEqual('CMD A --output ""', cmd)
782       return self.checker._file_cache['A.pydeps']
783
784     self.mock_input_api.subprocess.check_output = mock_check_output
785
786     results = self._RunCheck()
787     self.assertEqual(0, len(results), 'Unexpected results: %r' % results)
788
789   def testRelevantPyOneChange(self):
790     # PRESUBMIT._CheckPydepsNeedsUpdating is only implemented for Android.
791     if self.mock_input_api.platform != 'linux2':
792       return []
793
794     self.mock_input_api.files = [
795       MockAffectedFile('A.py', []),
796     ]
797
798     def mock_check_output(cmd, shell=False, env=None):
799       self.assertEqual('CMD A --output ""', cmd)
800       return 'changed data'
801
802     self.mock_input_api.subprocess.check_output = mock_check_output
803
804     results = self._RunCheck()
805     self.assertEqual(1, len(results))
806     self.assertTrue('File is stale' in str(results[0]))
807
808   def testRelevantPyTwoChanges(self):
809     # PRESUBMIT._CheckPydepsNeedsUpdating is only implemented for Android.
810     if self.mock_input_api.platform != 'linux2':
811       return []
812
813     self.mock_input_api.files = [
814       MockAffectedFile('C.py', []),
815     ]
816
817     def mock_check_output(cmd, shell=False, env=None):
818       return 'changed data'
819
820     self.mock_input_api.subprocess.check_output = mock_check_output
821
822     results = self._RunCheck()
823     self.assertEqual(2, len(results))
824     self.assertTrue('File is stale' in str(results[0]))
825     self.assertTrue('File is stale' in str(results[1]))
826
827 class IncludeGuardTest(unittest.TestCase):
828   def testIncludeGuardChecks(self):
829     mock_input_api = MockInputApi()
830     mock_output_api = MockOutputApi()
831     mock_input_api.files = [
832         MockAffectedFile('content/browser/thing/foo.h', [
833           '// Comment',
834           '#ifndef CONTENT_BROWSER_THING_FOO_H_',
835           '#define CONTENT_BROWSER_THING_FOO_H_',
836           'struct McBoatFace;',
837           '#endif  // CONTENT_BROWSER_THING_FOO_H_',
838         ]),
839         MockAffectedFile('content/browser/thing/bar.h', [
840           '#ifndef CONTENT_BROWSER_THING_BAR_H_',
841           '#define CONTENT_BROWSER_THING_BAR_H_',
842           'namespace content {',
843           '#endif  // CONTENT_BROWSER_THING_BAR_H_',
844           '}  // namespace content',
845         ]),
846         MockAffectedFile('content/browser/test1.h', [
847           'namespace content {',
848           '}  // namespace content',
849         ]),
850         MockAffectedFile('content\\browser\\win.h', [
851           '#ifndef CONTENT_BROWSER_WIN_H_',
852           '#define CONTENT_BROWSER_WIN_H_',
853           'struct McBoatFace;',
854           '#endif  // CONTENT_BROWSER_WIN_H_',
855         ]),
856         MockAffectedFile('content/browser/test2.h', [
857           '// Comment',
858           '#ifndef CONTENT_BROWSER_TEST2_H_',
859           'struct McBoatFace;',
860           '#endif  // CONTENT_BROWSER_TEST2_H_',
861         ]),
862         MockAffectedFile('content/browser/internal.h', [
863           '// Comment',
864           '#ifndef CONTENT_BROWSER_INTERNAL_H_',
865           '#define CONTENT_BROWSER_INTERNAL_H_',
866           '// Comment',
867           '#ifndef INTERNAL_CONTENT_BROWSER_INTERNAL_H_',
868           '#define INTERNAL_CONTENT_BROWSER_INTERNAL_H_',
869           'namespace internal {',
870           '}  // namespace internal',
871           '#endif  // INTERNAL_CONTENT_BROWSER_THING_BAR_H_',
872           'namespace content {',
873           '}  // namespace content',
874           '#endif  // CONTENT_BROWSER_THING_BAR_H_',
875         ]),
876         MockAffectedFile('content/browser/thing/foo.cc', [
877           '// This is a non-header.',
878         ]),
879         MockAffectedFile('content/browser/disabled.h', [
880           '// no-include-guard-because-multiply-included',
881           'struct McBoatFace;',
882         ]),
883         # New files don't allow misspelled include guards.
884         MockAffectedFile('content/browser/spleling.h', [
885           '#ifndef CONTENT_BROWSER_SPLLEING_H_',
886           '#define CONTENT_BROWSER_SPLLEING_H_',
887           'struct McBoatFace;',
888           '#endif  // CONTENT_BROWSER_SPLLEING_H_',
889         ]),
890         # Old files allow misspelled include guards (for now).
891         MockAffectedFile('chrome/old.h', [
892           '// New contents',
893           '#ifndef CHROME_ODL_H_',
894           '#define CHROME_ODL_H_',
895           '#endif  // CHROME_ODL_H_',
896         ], [
897           '// Old contents',
898           '#ifndef CHROME_ODL_H_',
899           '#define CHROME_ODL_H_',
900           '#endif  // CHROME_ODL_H_',
901         ]),
902         # Using a Blink style include guard outside Blink is wrong.
903         MockAffectedFile('content/NotInBlink.h', [
904           '#ifndef NotInBlink_h',
905           '#define NotInBlink_h',
906           'struct McBoatFace;',
907           '#endif  // NotInBlink_h',
908         ]),
909         # Using a Blink style include guard in Blink is ok for now.
910         MockAffectedFile('third_party/WebKit/InBlink.h', [
911           '#ifndef InBlink_h',
912           '#define InBlink_h',
913           'struct McBoatFace;',
914           '#endif  // InBlink_h',
915         ]),
916         # Using a bad include guard in Blink is not ok.
917         MockAffectedFile('third_party/WebKit/AlsoInBlink.h', [
918           '#ifndef WrongInBlink_h',
919           '#define WrongInBlink_h',
920           'struct McBoatFace;',
921           '#endif  // WrongInBlink_h',
922         ]),
923         # Using a bad include guard in Blink is accepted if it's an old file.
924         MockAffectedFile('third_party/WebKit/StillInBlink.h', [
925           '// New contents',
926           '#ifndef AcceptedInBlink_h',
927           '#define AcceptedInBlink_h',
928           'struct McBoatFace;',
929           '#endif  // AcceptedInBlink_h',
930         ], [
931           '// Old contents',
932           '#ifndef AcceptedInBlink_h',
933           '#define AcceptedInBlink_h',
934           'struct McBoatFace;',
935           '#endif  // AcceptedInBlink_h',
936         ]),
937       ]
938     msgs = PRESUBMIT._CheckForIncludeGuards(
939         mock_input_api, mock_output_api)
940     expected_fail_count = 6
941     self.assertEqual(expected_fail_count, len(msgs),
942                      'Expected %d items, found %d: %s'
943                      % (expected_fail_count, len(msgs), msgs))
944     self.assertEqual(msgs[0].items, [ 'content/browser/thing/bar.h' ])
945     self.assertEqual(msgs[0].message,
946                      'Include guard CONTENT_BROWSER_THING_BAR_H_ '
947                      'not covering the whole file')
948
949     self.assertEqual(msgs[1].items, [ 'content/browser/test1.h' ])
950     self.assertEqual(msgs[1].message,
951                      'Missing include guard CONTENT_BROWSER_TEST1_H_')
952
953     self.assertEqual(msgs[2].items, [ 'content/browser/test2.h:3' ])
954     self.assertEqual(msgs[2].message,
955                      'Missing "#define CONTENT_BROWSER_TEST2_H_" for '
956                      'include guard')
957
958     self.assertEqual(msgs[3].items, [ 'content/browser/spleling.h:1' ])
959     self.assertEqual(msgs[3].message,
960                      'Header using the wrong include guard name '
961                      'CONTENT_BROWSER_SPLLEING_H_')
962
963     self.assertEqual(msgs[4].items, [ 'content/NotInBlink.h:1' ])
964     self.assertEqual(msgs[4].message,
965                      'Header using the wrong include guard name '
966                      'NotInBlink_h')
967
968     self.assertEqual(msgs[5].items, [ 'third_party/WebKit/AlsoInBlink.h:1' ])
969     self.assertEqual(msgs[5].message,
970                      'Header using the wrong include guard name '
971                      'WrongInBlink_h')
972
973 class AndroidDeprecatedTestAnnotationTest(unittest.TestCase):
974   def testCheckAndroidTestAnnotationUsage(self):
975     mock_input_api = MockInputApi()
976     mock_output_api = MockOutputApi()
977
978     mock_input_api.files = [
979         MockAffectedFile('LalaLand.java', [
980           'random stuff'
981         ]),
982         MockAffectedFile('CorrectUsage.java', [
983           'import android.support.test.filters.LargeTest;',
984           'import android.support.test.filters.MediumTest;',
985           'import android.support.test.filters.SmallTest;',
986         ]),
987         MockAffectedFile('UsedDeprecatedLargeTestAnnotation.java', [
988           'import android.test.suitebuilder.annotation.LargeTest;',
989         ]),
990         MockAffectedFile('UsedDeprecatedMediumTestAnnotation.java', [
991           'import android.test.suitebuilder.annotation.MediumTest;',
992         ]),
993         MockAffectedFile('UsedDeprecatedSmallTestAnnotation.java', [
994           'import android.test.suitebuilder.annotation.SmallTest;',
995         ]),
996         MockAffectedFile('UsedDeprecatedSmokeAnnotation.java', [
997           'import android.test.suitebuilder.annotation.Smoke;',
998         ])
999     ]
1000     msgs = PRESUBMIT._CheckAndroidTestAnnotationUsage(
1001         mock_input_api, mock_output_api)
1002     self.assertEqual(1, len(msgs),
1003                      'Expected %d items, found %d: %s'
1004                      % (1, len(msgs), msgs))
1005     self.assertEqual(4, len(msgs[0].items),
1006                      'Expected %d items, found %d: %s'
1007                      % (4, len(msgs[0].items), msgs[0].items))
1008     self.assertTrue('UsedDeprecatedLargeTestAnnotation.java:1' in msgs[0].items,
1009                     'UsedDeprecatedLargeTestAnnotation not found in errors')
1010     self.assertTrue('UsedDeprecatedMediumTestAnnotation.java:1'
1011                     in msgs[0].items,
1012                     'UsedDeprecatedMediumTestAnnotation not found in errors')
1013     self.assertTrue('UsedDeprecatedSmallTestAnnotation.java:1' in msgs[0].items,
1014                     'UsedDeprecatedSmallTestAnnotation not found in errors')
1015     self.assertTrue('UsedDeprecatedSmokeAnnotation.java:1' in msgs[0].items,
1016                     'UsedDeprecatedSmokeAnnotation not found in errors')
1017
1018 class AndroidDeprecatedJUnitFrameworkTest(unittest.TestCase):
1019   def testCheckAndroidTestAnnotationUsage(self):
1020     mock_input_api = MockInputApi()
1021     mock_output_api = MockOutputApi()
1022
1023     mock_input_api.files = [
1024         MockAffectedFile('LalaLand.java', [
1025           'random stuff'
1026         ]),
1027         MockAffectedFile('CorrectUsage.java', [
1028           'import org.junit.ABC',
1029           'import org.junit.XYZ;',
1030         ]),
1031         MockAffectedFile('UsedDeprecatedJUnit.java', [
1032           'import junit.framework.*;',
1033         ]),
1034         MockAffectedFile('UsedDeprecatedJUnitAssert.java', [
1035           'import junit.framework.Assert;',
1036         ]),
1037     ]
1038     msgs = PRESUBMIT._CheckAndroidTestJUnitFrameworkImport(
1039         mock_input_api, mock_output_api)
1040     self.assertEqual(1, len(msgs),
1041                      'Expected %d items, found %d: %s'
1042                      % (1, len(msgs), msgs))
1043     self.assertEqual(2, len(msgs[0].items),
1044                      'Expected %d items, found %d: %s'
1045                      % (2, len(msgs[0].items), msgs[0].items))
1046     self.assertTrue('UsedDeprecatedJUnit.java:1' in msgs[0].items,
1047                     'UsedDeprecatedJUnit.java not found in errors')
1048     self.assertTrue('UsedDeprecatedJUnitAssert.java:1'
1049                     in msgs[0].items,
1050                     'UsedDeprecatedJUnitAssert not found in errors')
1051
1052 class AndroidJUnitBaseClass(unittest.TestCase):
1053   def testCheckAndroidTestAnnotationUsage(self):
1054     mock_input_api = MockInputApi()
1055     mock_output_api = MockOutputApi()
1056
1057     mock_input_api.files = [
1058         MockAffectedFile('LalaLand.java', [
1059           'random stuff'
1060         ]),
1061         MockAffectedFile('CorrectTest.java', [
1062           '@RunWith(ABC.class);'
1063           'public class CorrectTest {',
1064           '}',
1065         ]),
1066         MockAffectedFile('HistoricallyIncorrectTest.java', [
1067           'public class Test extends BaseCaseA {',
1068           '}',
1069           ], old_contents=[
1070           'public class Test extends BaseCaseB {',
1071           '}',
1072         ]),
1073         MockAffectedFile('CorrectTestWithInterface.java', [
1074           '@RunWith(ABC.class);'
1075           'public class CorrectTest implement Interface {',
1076           '}',
1077         ]),
1078         MockAffectedFile('IncorrectTest.java', [
1079           'public class IncorrectTest extends TestCase {',
1080           '}',
1081         ]),
1082         MockAffectedFile('IncorrectWithInterfaceTest.java', [
1083           'public class Test implements X extends BaseClass {',
1084           '}',
1085         ]),
1086         MockAffectedFile('IncorrectMultiLineTest.java', [
1087           'public class Test implements X, Y, Z',
1088           '        extends TestBase {',
1089           '}',
1090         ]),
1091     ]
1092     msgs = PRESUBMIT._CheckAndroidTestJUnitInheritance(
1093         mock_input_api, mock_output_api)
1094     self.assertEqual(1, len(msgs),
1095                      'Expected %d items, found %d: %s'
1096                      % (1, len(msgs), msgs))
1097     self.assertEqual(3, len(msgs[0].items),
1098                      'Expected %d items, found %d: %s'
1099                      % (3, len(msgs[0].items), msgs[0].items))
1100     self.assertTrue('IncorrectTest.java:1' in msgs[0].items,
1101                     'IncorrectTest not found in errors')
1102     self.assertTrue('IncorrectWithInterfaceTest.java:1'
1103                     in msgs[0].items,
1104                     'IncorrectWithInterfaceTest not found in errors')
1105     self.assertTrue('IncorrectMultiLineTest.java:2' in msgs[0].items,
1106                     'IncorrectMultiLineTest not found in errors')
1107
1108 class LogUsageTest(unittest.TestCase):
1109
1110   def testCheckAndroidCrLogUsage(self):
1111     mock_input_api = MockInputApi()
1112     mock_output_api = MockOutputApi()
1113
1114     mock_input_api.files = [
1115       MockAffectedFile('RandomStuff.java', [
1116         'random stuff'
1117       ]),
1118       MockAffectedFile('HasAndroidLog.java', [
1119         'import android.util.Log;',
1120         'some random stuff',
1121         'Log.d("TAG", "foo");',
1122       ]),
1123       MockAffectedFile('HasExplicitUtilLog.java', [
1124         'some random stuff',
1125         'android.util.Log.d("TAG", "foo");',
1126       ]),
1127       MockAffectedFile('IsInBasePackage.java', [
1128         'package org.chromium.base;',
1129         'private static final String TAG = "cr_Foo";',
1130         'Log.d(TAG, "foo");',
1131       ]),
1132       MockAffectedFile('IsInBasePackageButImportsLog.java', [
1133         'package org.chromium.base;',
1134         'import android.util.Log;',
1135         'private static final String TAG = "cr_Foo";',
1136         'Log.d(TAG, "foo");',
1137       ]),
1138       MockAffectedFile('HasBothLog.java', [
1139         'import org.chromium.base.Log;',
1140         'some random stuff',
1141         'private static final String TAG = "cr_Foo";',
1142         'Log.d(TAG, "foo");',
1143         'android.util.Log.d("TAG", "foo");',
1144       ]),
1145       MockAffectedFile('HasCorrectTag.java', [
1146         'import org.chromium.base.Log;',
1147         'some random stuff',
1148         'private static final String TAG = "cr_Foo";',
1149         'Log.d(TAG, "foo");',
1150       ]),
1151       MockAffectedFile('HasOldTag.java', [
1152         'import org.chromium.base.Log;',
1153         'some random stuff',
1154         'private static final String TAG = "cr.Foo";',
1155         'Log.d(TAG, "foo");',
1156       ]),
1157       MockAffectedFile('HasDottedTag.java', [
1158         'import org.chromium.base.Log;',
1159         'some random stuff',
1160         'private static final String TAG = "cr_foo.bar";',
1161         'Log.d(TAG, "foo");',
1162       ]),
1163       MockAffectedFile('HasNoTagDecl.java', [
1164         'import org.chromium.base.Log;',
1165         'some random stuff',
1166         'Log.d(TAG, "foo");',
1167       ]),
1168       MockAffectedFile('HasIncorrectTagDecl.java', [
1169         'import org.chromium.base.Log;',
1170         'private static final String TAHG = "cr_Foo";',
1171         'some random stuff',
1172         'Log.d(TAG, "foo");',
1173       ]),
1174       MockAffectedFile('HasInlineTag.java', [
1175         'import org.chromium.base.Log;',
1176         'some random stuff',
1177         'private static final String TAG = "cr_Foo";',
1178         'Log.d("TAG", "foo");',
1179       ]),
1180       MockAffectedFile('HasUnprefixedTag.java', [
1181         'import org.chromium.base.Log;',
1182         'some random stuff',
1183         'private static final String TAG = "rubbish";',
1184         'Log.d(TAG, "foo");',
1185       ]),
1186       MockAffectedFile('HasTooLongTag.java', [
1187         'import org.chromium.base.Log;',
1188         'some random stuff',
1189         'private static final String TAG = "21_charachers_long___";',
1190         'Log.d(TAG, "foo");',
1191       ]),
1192     ]
1193
1194     msgs = PRESUBMIT._CheckAndroidCrLogUsage(
1195         mock_input_api, mock_output_api)
1196
1197     self.assertEqual(5, len(msgs),
1198                      'Expected %d items, found %d: %s' % (5, len(msgs), msgs))
1199
1200     # Declaration format
1201     nb = len(msgs[0].items)
1202     self.assertEqual(2, nb,
1203                      'Expected %d items, found %d: %s' % (2, nb, msgs[0].items))
1204     self.assertTrue('HasNoTagDecl.java' in msgs[0].items)
1205     self.assertTrue('HasIncorrectTagDecl.java' in msgs[0].items)
1206
1207     # Tag length
1208     nb = len(msgs[1].items)
1209     self.assertEqual(1, nb,
1210                      'Expected %d items, found %d: %s' % (1, nb, msgs[1].items))
1211     self.assertTrue('HasTooLongTag.java' in msgs[1].items)
1212
1213     # Tag must be a variable named TAG
1214     nb = len(msgs[2].items)
1215     self.assertEqual(1, nb,
1216                      'Expected %d items, found %d: %s' % (1, nb, msgs[2].items))
1217     self.assertTrue('HasInlineTag.java:4' in msgs[2].items)
1218
1219     # Util Log usage
1220     nb = len(msgs[3].items)
1221     self.assertEqual(2, nb,
1222                      'Expected %d items, found %d: %s' % (2, nb, msgs[3].items))
1223     self.assertTrue('HasAndroidLog.java:3' in msgs[3].items)
1224     self.assertTrue('IsInBasePackageButImportsLog.java:4' in msgs[3].items)
1225
1226     # Tag must not contain
1227     nb = len(msgs[4].items)
1228     self.assertEqual(2, nb,
1229                      'Expected %d items, found %d: %s' % (2, nb, msgs[4].items))
1230     self.assertTrue('HasDottedTag.java' in msgs[4].items)
1231     self.assertTrue('HasOldTag.java' in msgs[4].items)
1232
1233 class GoogleAnswerUrlFormatTest(unittest.TestCase):
1234
1235   def testCatchAnswerUrlId(self):
1236     input_api = MockInputApi()
1237     input_api.files = [
1238       MockFile('somewhere/file.cc',
1239                ['char* host = '
1240                 '  "https://support.google.com/chrome/answer/123456";']),
1241       MockFile('somewhere_else/file.cc',
1242                ['char* host = '
1243                 '  "https://support.google.com/chrome/a/answer/123456";']),
1244     ]
1245
1246     warnings = PRESUBMIT._CheckGoogleSupportAnswerUrl(
1247       input_api, MockOutputApi())
1248     self.assertEqual(1, len(warnings))
1249     self.assertEqual(2, len(warnings[0].items))
1250
1251   def testAllowAnswerUrlParam(self):
1252     input_api = MockInputApi()
1253     input_api.files = [
1254       MockFile('somewhere/file.cc',
1255                ['char* host = '
1256                 '  "https://support.google.com/chrome/?p=cpn_crash_reports";']),
1257     ]
1258
1259     warnings = PRESUBMIT._CheckGoogleSupportAnswerUrl(
1260       input_api, MockOutputApi())
1261     self.assertEqual(0, len(warnings))
1262
1263 class HardcodedGoogleHostsTest(unittest.TestCase):
1264
1265   def testWarnOnAssignedLiterals(self):
1266     input_api = MockInputApi()
1267     input_api.files = [
1268       MockFile('content/file.cc',
1269                ['char* host = "https://www.google.com";']),
1270       MockFile('content/file.cc',
1271                ['char* host = "https://www.googleapis.com";']),
1272       MockFile('content/file.cc',
1273                ['char* host = "https://clients1.google.com";']),
1274     ]
1275
1276     warnings = PRESUBMIT._CheckHardcodedGoogleHostsInLowerLayers(
1277       input_api, MockOutputApi())
1278     self.assertEqual(1, len(warnings))
1279     self.assertEqual(3, len(warnings[0].items))
1280
1281   def testAllowInComment(self):
1282     input_api = MockInputApi()
1283     input_api.files = [
1284       MockFile('content/file.cc',
1285                ['char* host = "https://www.aol.com"; // google.com'])
1286     ]
1287
1288     warnings = PRESUBMIT._CheckHardcodedGoogleHostsInLowerLayers(
1289       input_api, MockOutputApi())
1290     self.assertEqual(0, len(warnings))
1291
1292
1293 class ForwardDeclarationTest(unittest.TestCase):
1294   def testCheckHeadersOnlyOutsideThirdParty(self):
1295     mock_input_api = MockInputApi()
1296     mock_input_api.files = [
1297       MockAffectedFile('somewhere/file.cc', [
1298         'class DummyClass;'
1299       ]),
1300       MockAffectedFile('third_party/header.h', [
1301         'class DummyClass;'
1302       ])
1303     ]
1304     warnings = PRESUBMIT._CheckUselessForwardDeclarations(mock_input_api,
1305       MockOutputApi())
1306     self.assertEqual(0, len(warnings))
1307
1308   def testNoNestedDeclaration(self):
1309     mock_input_api = MockInputApi()
1310     mock_input_api.files = [
1311       MockAffectedFile('somewhere/header.h', [
1312         'class SomeClass {',
1313         ' protected:',
1314         '  class NotAMatch;',
1315         '};'
1316       ])
1317     ]
1318     warnings = PRESUBMIT._CheckUselessForwardDeclarations(mock_input_api,
1319       MockOutputApi())
1320     self.assertEqual(0, len(warnings))
1321
1322   def testSubStrings(self):
1323     mock_input_api = MockInputApi()
1324     mock_input_api.files = [
1325       MockAffectedFile('somewhere/header.h', [
1326         'class NotUsefulClass;',
1327         'struct SomeStruct;',
1328         'UsefulClass *p1;',
1329         'SomeStructPtr *p2;'
1330       ])
1331     ]
1332     warnings = PRESUBMIT._CheckUselessForwardDeclarations(mock_input_api,
1333       MockOutputApi())
1334     self.assertEqual(2, len(warnings))
1335
1336   def testUselessForwardDeclaration(self):
1337     mock_input_api = MockInputApi()
1338     mock_input_api.files = [
1339       MockAffectedFile('somewhere/header.h', [
1340         'class DummyClass;',
1341         'struct DummyStruct;',
1342         'class UsefulClass;',
1343         'std::unique_ptr<UsefulClass> p;'
1344       ])
1345     ]
1346     warnings = PRESUBMIT._CheckUselessForwardDeclarations(mock_input_api,
1347       MockOutputApi())
1348     self.assertEqual(2, len(warnings))
1349
1350   def testBlinkHeaders(self):
1351     mock_input_api = MockInputApi()
1352     mock_input_api.files = [
1353       MockAffectedFile('third_party/WebKit/header.h', [
1354         'class DummyClass;',
1355         'struct DummyStruct;',
1356       ]),
1357       MockAffectedFile('third_party\\WebKit\\header.h', [
1358         'class DummyClass;',
1359         'struct DummyStruct;',
1360       ])
1361     ]
1362     warnings = PRESUBMIT._CheckUselessForwardDeclarations(mock_input_api,
1363       MockOutputApi())
1364     self.assertEqual(4, len(warnings))
1365
1366
1367 class RiskyJsTest(unittest.TestCase):
1368   def testArrowWarnInIos9Code(self):
1369     mock_input_api = MockInputApi()
1370     mock_output_api = MockOutputApi()
1371
1372     mock_input_api.files = [
1373       MockAffectedFile('components/blah.js', ["shouldn't use => here"]),
1374     ]
1375     warnings = PRESUBMIT._CheckForRiskyJsFeatures(
1376         mock_input_api, mock_output_api)
1377     self.assertEqual(1, len(warnings))
1378
1379     mock_input_api.files = [
1380       MockAffectedFile('ios/blee.js', ['might => break folks']),
1381     ]
1382     warnings = PRESUBMIT._CheckForRiskyJsFeatures(
1383         mock_input_api, mock_output_api)
1384     self.assertEqual(1, len(warnings))
1385
1386     mock_input_api.files = [
1387       MockAffectedFile('ui/webui/resources/blarg.js', ['on => iOS9']),
1388     ]
1389     warnings = PRESUBMIT._CheckForRiskyJsFeatures(
1390         mock_input_api, mock_output_api)
1391     self.assertEqual(1, len(warnings))
1392
1393   def testArrowsAllowedInChromeCode(self):
1394     mock_input_api = MockInputApi()
1395     mock_input_api.files = [
1396       MockAffectedFile('chrome/browser/resources/blah.js', 'arrow => OK here'),
1397     ]
1398     warnings = PRESUBMIT._CheckForRiskyJsFeatures(
1399         mock_input_api, MockOutputApi())
1400     self.assertEqual(0, len(warnings))
1401
1402   def testConstLetWarningIos9Code(self):
1403     mock_input_api = MockInputApi()
1404     mock_output_api = MockOutputApi()
1405
1406     mock_input_api.files = [
1407       MockAffectedFile('components/blah.js', [" const foo = 'bar';"]),
1408       MockAffectedFile('ui/webui/resources/blah.js', [" let foo = 3;"]),
1409     ]
1410     warnings = PRESUBMIT._CheckForRiskyJsFeatures(
1411         mock_input_api, mock_output_api)
1412     self.assertEqual(2, len(warnings))
1413
1414 class RelativeIncludesTest(unittest.TestCase):
1415   def testThirdPartyNotWebKitIgnored(self):
1416     mock_input_api = MockInputApi()
1417     mock_input_api.files = [
1418       MockAffectedFile('third_party/test.cpp', '#include "../header.h"'),
1419       MockAffectedFile('third_party/test/test.cpp', '#include "../header.h"'),
1420     ]
1421
1422     mock_output_api = MockOutputApi()
1423
1424     errors = PRESUBMIT._CheckForRelativeIncludes(
1425         mock_input_api, mock_output_api)
1426     self.assertEqual(0, len(errors))
1427
1428   def testNonCppFileIgnored(self):
1429     mock_input_api = MockInputApi()
1430     mock_input_api.files = [
1431       MockAffectedFile('test.py', '#include "../header.h"'),
1432     ]
1433
1434     mock_output_api = MockOutputApi()
1435
1436     errors = PRESUBMIT._CheckForRelativeIncludes(
1437         mock_input_api, mock_output_api)
1438     self.assertEqual(0, len(errors))
1439
1440   def testInnocuousChangesAllowed(self):
1441     mock_input_api = MockInputApi()
1442     mock_input_api.files = [
1443       MockAffectedFile('test.cpp', '#include "header.h"'),
1444       MockAffectedFile('test2.cpp', '../'),
1445     ]
1446
1447     mock_output_api = MockOutputApi()
1448
1449     errors = PRESUBMIT._CheckForRelativeIncludes(
1450         mock_input_api, mock_output_api)
1451     self.assertEqual(0, len(errors))
1452
1453   def testRelativeIncludeNonWebKitProducesError(self):
1454     mock_input_api = MockInputApi()
1455     mock_input_api.files = [
1456       MockAffectedFile('test.cpp', ['#include "../header.h"']),
1457     ]
1458
1459     mock_output_api = MockOutputApi()
1460
1461     errors = PRESUBMIT._CheckForRelativeIncludes(
1462         mock_input_api, mock_output_api)
1463     self.assertEqual(1, len(errors))
1464
1465   def testRelativeIncludeWebKitProducesError(self):
1466     mock_input_api = MockInputApi()
1467     mock_input_api.files = [
1468       MockAffectedFile('third_party/WebKit/test.cpp',
1469                        ['#include "../header.h']),
1470     ]
1471
1472     mock_output_api = MockOutputApi()
1473
1474     errors = PRESUBMIT._CheckForRelativeIncludes(
1475         mock_input_api, mock_output_api)
1476     self.assertEqual(1, len(errors))
1477
1478
1479 class MojoManifestOwnerTest(unittest.TestCase):
1480   def testMojoManifestChangeNeedsSecurityOwner(self):
1481     mock_input_api = MockInputApi()
1482     mock_input_api.files = [
1483       MockAffectedFile('services/goat/manifest.json',
1484                        [
1485                          '{',
1486                          '  "name": "teleporter",',
1487                          '  "display_name": "Goat Teleporter",'
1488                          '  "interface_provider_specs": {',
1489                          '  }',
1490                          '}',
1491                        ])
1492     ]
1493     mock_output_api = MockOutputApi()
1494     errors = PRESUBMIT._CheckIpcOwners(
1495         mock_input_api, mock_output_api)
1496     self.assertEqual(1, len(errors))
1497     self.assertEqual(
1498         'Found OWNERS files that need to be updated for IPC security review ' +
1499         'coverage.\nPlease update the OWNERS files below:', errors[0].message)
1500
1501     # No warning if already covered by an OWNERS rule.
1502
1503   def testNonManifestChangesDoNotRequireSecurityOwner(self):
1504     mock_input_api = MockInputApi()
1505     mock_input_api.files = [
1506       MockAffectedFile('services/goat/species.json',
1507                        [
1508                          '[',
1509                          '  "anglo-nubian",',
1510                          '  "angora"',
1511                          ']',
1512                        ])
1513     ]
1514     mock_output_api = MockOutputApi()
1515     errors = PRESUBMIT._CheckIpcOwners(
1516         mock_input_api, mock_output_api)
1517     self.assertEqual([], errors)
1518
1519
1520 class CrbugUrlFormatTest(unittest.TestCase):
1521
1522   def testCheckCrbugLinksHaveHttps(self):
1523     input_api = MockInputApi()
1524     input_api.files = [
1525       MockFile('somewhere/file.cc',
1526                ['// TODO(developer): crbug.com should be linkified',
1527                 '// TODO(developer): (crbug.com) should be linkified',
1528                 '// TODO(developer): crbug/123 should be well formed',
1529                 '// TODO(developer): http://crbug.com it\'s OK',
1530                 '// TODO(developer): https://crbug.com is just great',
1531                 '// TODO(crbug.com/123456): this pattern it\'s also OK']),
1532     ]
1533
1534     warnings = PRESUBMIT._CheckCrbugLinksHaveHttps(input_api, MockOutputApi())
1535     self.assertEqual(1, len(warnings))
1536     self.assertEqual(3, warnings[0].message.count('\n'));
1537
1538
1539 class BannedFunctionCheckTest(unittest.TestCase):
1540
1541   def testBannedIosObcjFunctions(self):
1542     input_api = MockInputApi()
1543     input_api.files = [
1544       MockFile('some/ios/file.mm',
1545                ['TEST(SomeClassTest, SomeInteraction) {',
1546                 '}']),
1547       MockFile('some/mac/file.mm',
1548                ['TEST(SomeClassTest, SomeInteraction) {',
1549                 '}']),
1550       MockFile('another/ios_file.mm',
1551                ['class SomeTest : public testing::Test {};']),
1552     ]
1553
1554     errors = PRESUBMIT._CheckNoBannedFunctions(input_api, MockOutputApi())
1555     self.assertEqual(1, len(errors))
1556     self.assertTrue('some/ios/file.mm' in errors[0].message)
1557     self.assertTrue('another/ios_file.mm' in errors[0].message)
1558     self.assertTrue('some/mac/file.mm' not in errors[0].message)
1559
1560
1561 class NoProductionCodeUsingTestOnlyFunctions(unittest.TestCase):
1562   def testTruePositives(self):
1563     mock_input_api = MockInputApi()
1564     mock_input_api.files = [
1565       MockFile('some/path/foo.cc', ['foo_for_testing();']),
1566       MockFile('some/path/foo.mm', ['FooForTesting();']),
1567       MockFile('some/path/foo.cxx', ['FooForTests();']),
1568       MockFile('some/path/foo.cpp', ['foo_for_test();']),
1569     ]
1570
1571     results = PRESUBMIT._CheckNoProductionCodeUsingTestOnlyFunctions(
1572         mock_input_api, MockOutputApi())
1573     self.assertEqual(1, len(results))
1574     self.assertEqual(4, len(results[0].items))
1575     self.assertTrue('foo.cc' in results[0].items[0])
1576     self.assertTrue('foo.mm' in results[0].items[1])
1577     self.assertTrue('foo.cxx' in results[0].items[2])
1578     self.assertTrue('foo.cpp' in results[0].items[3])
1579
1580   def testFalsePositives(self):
1581     mock_input_api = MockInputApi()
1582     mock_input_api.files = [
1583       MockFile('some/path/foo.h', ['foo_for_testing();']),
1584       MockFile('some/path/foo.mm', ['FooForTesting() {']),
1585       MockFile('some/path/foo.cc', ['::FooForTests();']),
1586       MockFile('some/path/foo.cpp', ['// foo_for_test();']),
1587     ]
1588
1589     results = PRESUBMIT._CheckNoProductionCodeUsingTestOnlyFunctions(
1590         mock_input_api, MockOutputApi())
1591     self.assertEqual(0, len(results))
1592
1593
1594 class NoProductionJavaCodeUsingTestOnlyFunctions(unittest.TestCase):
1595   def testTruePositives(self):
1596     mock_input_api = MockInputApi()
1597     mock_input_api.files = [
1598       MockFile('dir/java/src/foo.java', ['FooForTesting();']),
1599       MockFile('dir/java/src/bar.java', ['FooForTests(x);']),
1600       MockFile('dir/java/src/baz.java', ['FooForTest(','y',');']),
1601       MockFile('dir/java/src/mult.java', [
1602         'int x = SomethingLongHere()',
1603         '    * SomethingLongHereForTesting();'
1604       ]),
1605     ]
1606
1607     results = PRESUBMIT._CheckNoProductionCodeUsingTestOnlyFunctionsJava(
1608         mock_input_api, MockOutputApi())
1609     self.assertEqual(1, len(results))
1610     self.assertEqual(4, len(results[0].items))
1611     self.assertTrue('foo.java' in results[0].items[0])
1612     self.assertTrue('bar.java' in results[0].items[1])
1613     self.assertTrue('baz.java' in results[0].items[2])
1614     self.assertTrue('mult.java' in results[0].items[3])
1615
1616   def testFalsePositives(self):
1617     mock_input_api = MockInputApi()
1618     mock_input_api.files = [
1619       MockFile('dir/java/src/foo.xml', ['FooForTesting();']),
1620       MockFile('dir/java/src/foo.java', ['FooForTests() {']),
1621       MockFile('dir/java/src/bar.java', ['// FooForTest();']),
1622       MockFile('dir/java/src/bar2.java', ['x = 1; // FooForTest();']),
1623       MockFile('dir/javatests/src/baz.java', ['FooForTest(','y',');']),
1624       MockFile('dir/junit/src/baz.java', ['FooForTest(','y',');']),
1625       MockFile('dir/junit/src/javadoc.java', [
1626         '/** Use FooForTest(); to obtain foo in tests.'
1627         ' */'
1628       ]),
1629       MockFile('dir/junit/src/javadoc2.java', [
1630         '/** ',
1631         ' * Use FooForTest(); to obtain foo in tests.'
1632         ' */'
1633       ]),
1634     ]
1635
1636     results = PRESUBMIT._CheckNoProductionCodeUsingTestOnlyFunctionsJava(
1637         mock_input_api, MockOutputApi())
1638     self.assertEqual(0, len(results))
1639
1640
1641 class CheckUniquePtr(unittest.TestCase):
1642   def testTruePositivesNullptr(self):
1643     mock_input_api = MockInputApi()
1644     mock_input_api.files = [
1645       MockFile('dir/baz.cc', ['std::unique_ptr<T>()']),
1646       MockFile('dir/baz-p.cc', ['std::unique_ptr<T<P>>()']),
1647     ]
1648
1649     results = PRESUBMIT._CheckUniquePtr(mock_input_api, MockOutputApi())
1650     self.assertEqual(1, len(results))
1651     self.assertTrue('nullptr' in results[0].message)
1652     self.assertEqual(2, len(results[0].items))
1653     self.assertTrue('baz.cc' in results[0].items[0])
1654     self.assertTrue('baz-p.cc' in results[0].items[1])
1655
1656   def testTruePositivesConstructor(self):
1657     mock_input_api = MockInputApi()
1658     mock_input_api.files = [
1659       MockFile('dir/foo.cc', ['return std::unique_ptr<T>(foo);']),
1660       MockFile('dir/bar.mm', ['bar = std::unique_ptr<T>(foo)']),
1661       MockFile('dir/mult.cc', [
1662         'return',
1663         '    std::unique_ptr<T>(barVeryVeryLongFooSoThatItWouldNotFitAbove);'
1664       ]),
1665       MockFile('dir/mult2.cc', [
1666         'barVeryVeryLongLongBaaaaaarSoThatTheLineLimitIsAlmostReached =',
1667         '    std::unique_ptr<T>(foo);'
1668       ]),
1669       MockFile('dir/mult3.cc', [
1670         'bar = std::unique_ptr<T>(',
1671         '    fooVeryVeryVeryLongStillGoingWellThisWillTakeAWhileFinallyThere);'
1672       ]),
1673     ]
1674
1675     results = PRESUBMIT._CheckUniquePtr(mock_input_api, MockOutputApi())
1676     self.assertEqual(1, len(results))
1677     self.assertTrue('std::make_unique' in results[0].message)
1678     self.assertEqual(5, len(results[0].items))
1679     self.assertTrue('foo.cc' in results[0].items[0])
1680     self.assertTrue('bar.mm' in results[0].items[1])
1681     self.assertTrue('mult.cc' in results[0].items[2])
1682     self.assertTrue('mult2.cc' in results[0].items[3])
1683     self.assertTrue('mult3.cc' in results[0].items[4])
1684
1685   def testFalsePositives(self):
1686     mock_input_api = MockInputApi()
1687     mock_input_api.files = [
1688       MockFile('dir/foo.cc', ['return std::unique_ptr<T[]>(foo);']),
1689       MockFile('dir/bar.mm', ['bar = std::unique_ptr<T[]>(foo)']),
1690       MockFile('dir/file.cc', ['std::unique_ptr<T> p = Foo();']),
1691       MockFile('dir/baz.cc', [
1692         'std::unique_ptr<T> result = std::make_unique<T>();'
1693       ]),
1694       MockFile('dir/baz2.cc', [
1695         'std::unique_ptr<T> result = std::make_unique<T>('
1696       ]),
1697       MockFile('dir/nested.cc', ['set<std::unique_ptr<T>>();']),
1698       MockFile('dir/nested2.cc', ['map<U, std::unique_ptr<T>>();']),
1699     ]
1700
1701     results = PRESUBMIT._CheckUniquePtr(mock_input_api, MockOutputApi())
1702     self.assertEqual(0, len(results))
1703
1704
1705 if __name__ == '__main__':
1706   unittest.main()