fixup! [M67 Dev][EWK] Classify EWK APIs by public, internal, or product
[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 no longer ok.
910         MockAffectedFile('third_party/blink/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/blink/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 not accepted even if
924         # it's an old file.
925         MockAffectedFile('third_party/blink/StillInBlink.h', [
926           '// New contents',
927           '#ifndef AcceptedInBlink_h',
928           '#define AcceptedInBlink_h',
929           'struct McBoatFace;',
930           '#endif  // AcceptedInBlink_h',
931         ], [
932           '// Old contents',
933           '#ifndef AcceptedInBlink_h',
934           '#define AcceptedInBlink_h',
935           'struct McBoatFace;',
936           '#endif  // AcceptedInBlink_h',
937         ]),
938         # Using a non-Chromium include guard in third_party
939         # (outside blink) is accepted.
940         MockAffectedFile('third_party/foo/some_file.h', [
941           '#ifndef REQUIRED_RPCNDR_H_',
942           '#define REQUIRED_RPCNDR_H_',
943           'struct SomeFileFoo;',
944           '#endif  // REQUIRED_RPCNDR_H_',
945         ]),
946       ]
947     msgs = PRESUBMIT._CheckForIncludeGuards(
948         mock_input_api, mock_output_api)
949     expected_fail_count = 7
950     self.assertEqual(expected_fail_count, len(msgs),
951                      'Expected %d items, found %d: %s'
952                      % (expected_fail_count, len(msgs), msgs))
953     self.assertEqual(msgs[0].items, [ 'content/browser/thing/bar.h' ])
954     self.assertEqual(msgs[0].message,
955                      'Include guard CONTENT_BROWSER_THING_BAR_H_ '
956                      'not covering the whole file')
957
958     self.assertEqual(msgs[1].items, [ 'content/browser/test1.h' ])
959     self.assertEqual(msgs[1].message,
960                      'Missing include guard CONTENT_BROWSER_TEST1_H_')
961
962     self.assertEqual(msgs[2].items, [ 'content/browser/test2.h:3' ])
963     self.assertEqual(msgs[2].message,
964                      'Missing "#define CONTENT_BROWSER_TEST2_H_" for '
965                      'include guard')
966
967     self.assertEqual(msgs[3].items, [ 'content/browser/spleling.h:1' ])
968     self.assertEqual(msgs[3].message,
969                      'Header using the wrong include guard name '
970                      'CONTENT_BROWSER_SPLLEING_H_')
971
972     self.assertEqual(msgs[4].items, [ 'content/NotInBlink.h:1' ])
973     self.assertEqual(msgs[4].message,
974                      'Header using the wrong include guard name '
975                      'NotInBlink_h')
976
977     self.assertEqual(msgs[5].items, [ 'third_party/blink/InBlink.h:1' ])
978     self.assertEqual(msgs[5].message,
979                      'Header using the wrong include guard name '
980                      'InBlink_h')
981
982     self.assertEqual(msgs[6].items, [ 'third_party/blink/AlsoInBlink.h:1' ])
983     self.assertEqual(msgs[6].message,
984                      'Header using the wrong include guard name '
985                      'WrongInBlink_h')
986
987 class AndroidDeprecatedTestAnnotationTest(unittest.TestCase):
988   def testCheckAndroidTestAnnotationUsage(self):
989     mock_input_api = MockInputApi()
990     mock_output_api = MockOutputApi()
991
992     mock_input_api.files = [
993         MockAffectedFile('LalaLand.java', [
994           'random stuff'
995         ]),
996         MockAffectedFile('CorrectUsage.java', [
997           'import android.support.test.filters.LargeTest;',
998           'import android.support.test.filters.MediumTest;',
999           'import android.support.test.filters.SmallTest;',
1000         ]),
1001         MockAffectedFile('UsedDeprecatedLargeTestAnnotation.java', [
1002           'import android.test.suitebuilder.annotation.LargeTest;',
1003         ]),
1004         MockAffectedFile('UsedDeprecatedMediumTestAnnotation.java', [
1005           'import android.test.suitebuilder.annotation.MediumTest;',
1006         ]),
1007         MockAffectedFile('UsedDeprecatedSmallTestAnnotation.java', [
1008           'import android.test.suitebuilder.annotation.SmallTest;',
1009         ]),
1010         MockAffectedFile('UsedDeprecatedSmokeAnnotation.java', [
1011           'import android.test.suitebuilder.annotation.Smoke;',
1012         ])
1013     ]
1014     msgs = PRESUBMIT._CheckAndroidTestAnnotationUsage(
1015         mock_input_api, mock_output_api)
1016     self.assertEqual(1, len(msgs),
1017                      'Expected %d items, found %d: %s'
1018                      % (1, len(msgs), msgs))
1019     self.assertEqual(4, len(msgs[0].items),
1020                      'Expected %d items, found %d: %s'
1021                      % (4, len(msgs[0].items), msgs[0].items))
1022     self.assertTrue('UsedDeprecatedLargeTestAnnotation.java:1' in msgs[0].items,
1023                     'UsedDeprecatedLargeTestAnnotation not found in errors')
1024     self.assertTrue('UsedDeprecatedMediumTestAnnotation.java:1'
1025                     in msgs[0].items,
1026                     'UsedDeprecatedMediumTestAnnotation not found in errors')
1027     self.assertTrue('UsedDeprecatedSmallTestAnnotation.java:1' in msgs[0].items,
1028                     'UsedDeprecatedSmallTestAnnotation not found in errors')
1029     self.assertTrue('UsedDeprecatedSmokeAnnotation.java:1' in msgs[0].items,
1030                     'UsedDeprecatedSmokeAnnotation not found in errors')
1031
1032 class AndroidDeprecatedJUnitFrameworkTest(unittest.TestCase):
1033   def testCheckAndroidTestAnnotationUsage(self):
1034     mock_input_api = MockInputApi()
1035     mock_output_api = MockOutputApi()
1036
1037     mock_input_api.files = [
1038         MockAffectedFile('LalaLand.java', [
1039           'random stuff'
1040         ]),
1041         MockAffectedFile('CorrectUsage.java', [
1042           'import org.junit.ABC',
1043           'import org.junit.XYZ;',
1044         ]),
1045         MockAffectedFile('UsedDeprecatedJUnit.java', [
1046           'import junit.framework.*;',
1047         ]),
1048         MockAffectedFile('UsedDeprecatedJUnitAssert.java', [
1049           'import junit.framework.Assert;',
1050         ]),
1051     ]
1052     msgs = PRESUBMIT._CheckAndroidTestJUnitFrameworkImport(
1053         mock_input_api, mock_output_api)
1054     self.assertEqual(1, len(msgs),
1055                      'Expected %d items, found %d: %s'
1056                      % (1, len(msgs), msgs))
1057     self.assertEqual(2, len(msgs[0].items),
1058                      'Expected %d items, found %d: %s'
1059                      % (2, len(msgs[0].items), msgs[0].items))
1060     self.assertTrue('UsedDeprecatedJUnit.java:1' in msgs[0].items,
1061                     'UsedDeprecatedJUnit.java not found in errors')
1062     self.assertTrue('UsedDeprecatedJUnitAssert.java:1'
1063                     in msgs[0].items,
1064                     'UsedDeprecatedJUnitAssert not found in errors')
1065
1066 class AndroidJUnitBaseClass(unittest.TestCase):
1067   def testCheckAndroidTestAnnotationUsage(self):
1068     mock_input_api = MockInputApi()
1069     mock_output_api = MockOutputApi()
1070
1071     mock_input_api.files = [
1072         MockAffectedFile('LalaLand.java', [
1073           'random stuff'
1074         ]),
1075         MockAffectedFile('CorrectTest.java', [
1076           '@RunWith(ABC.class);'
1077           'public class CorrectTest {',
1078           '}',
1079         ]),
1080         MockAffectedFile('HistoricallyIncorrectTest.java', [
1081           'public class Test extends BaseCaseA {',
1082           '}',
1083           ], old_contents=[
1084           'public class Test extends BaseCaseB {',
1085           '}',
1086         ]),
1087         MockAffectedFile('CorrectTestWithInterface.java', [
1088           '@RunWith(ABC.class);'
1089           'public class CorrectTest implement Interface {',
1090           '}',
1091         ]),
1092         MockAffectedFile('IncorrectTest.java', [
1093           'public class IncorrectTest extends TestCase {',
1094           '}',
1095         ]),
1096         MockAffectedFile('IncorrectWithInterfaceTest.java', [
1097           'public class Test implements X extends BaseClass {',
1098           '}',
1099         ]),
1100         MockAffectedFile('IncorrectMultiLineTest.java', [
1101           'public class Test implements X, Y, Z',
1102           '        extends TestBase {',
1103           '}',
1104         ]),
1105     ]
1106     msgs = PRESUBMIT._CheckAndroidTestJUnitInheritance(
1107         mock_input_api, mock_output_api)
1108     self.assertEqual(1, len(msgs),
1109                      'Expected %d items, found %d: %s'
1110                      % (1, len(msgs), msgs))
1111     self.assertEqual(3, len(msgs[0].items),
1112                      'Expected %d items, found %d: %s'
1113                      % (3, len(msgs[0].items), msgs[0].items))
1114     self.assertTrue('IncorrectTest.java:1' in msgs[0].items,
1115                     'IncorrectTest not found in errors')
1116     self.assertTrue('IncorrectWithInterfaceTest.java:1'
1117                     in msgs[0].items,
1118                     'IncorrectWithInterfaceTest not found in errors')
1119     self.assertTrue('IncorrectMultiLineTest.java:2' in msgs[0].items,
1120                     'IncorrectMultiLineTest not found in errors')
1121
1122 class LogUsageTest(unittest.TestCase):
1123
1124   def testCheckAndroidCrLogUsage(self):
1125     mock_input_api = MockInputApi()
1126     mock_output_api = MockOutputApi()
1127
1128     mock_input_api.files = [
1129       MockAffectedFile('RandomStuff.java', [
1130         'random stuff'
1131       ]),
1132       MockAffectedFile('HasAndroidLog.java', [
1133         'import android.util.Log;',
1134         'some random stuff',
1135         'Log.d("TAG", "foo");',
1136       ]),
1137       MockAffectedFile('HasExplicitUtilLog.java', [
1138         'some random stuff',
1139         'android.util.Log.d("TAG", "foo");',
1140       ]),
1141       MockAffectedFile('IsInBasePackage.java', [
1142         'package org.chromium.base;',
1143         'private static final String TAG = "cr_Foo";',
1144         'Log.d(TAG, "foo");',
1145       ]),
1146       MockAffectedFile('IsInBasePackageButImportsLog.java', [
1147         'package org.chromium.base;',
1148         'import android.util.Log;',
1149         'private static final String TAG = "cr_Foo";',
1150         'Log.d(TAG, "foo");',
1151       ]),
1152       MockAffectedFile('HasBothLog.java', [
1153         'import org.chromium.base.Log;',
1154         'some random stuff',
1155         'private static final String TAG = "cr_Foo";',
1156         'Log.d(TAG, "foo");',
1157         'android.util.Log.d("TAG", "foo");',
1158       ]),
1159       MockAffectedFile('HasCorrectTag.java', [
1160         'import org.chromium.base.Log;',
1161         'some random stuff',
1162         'private static final String TAG = "cr_Foo";',
1163         'Log.d(TAG, "foo");',
1164       ]),
1165       MockAffectedFile('HasOldTag.java', [
1166         'import org.chromium.base.Log;',
1167         'some random stuff',
1168         'private static final String TAG = "cr.Foo";',
1169         'Log.d(TAG, "foo");',
1170       ]),
1171       MockAffectedFile('HasDottedTag.java', [
1172         'import org.chromium.base.Log;',
1173         'some random stuff',
1174         'private static final String TAG = "cr_foo.bar";',
1175         'Log.d(TAG, "foo");',
1176       ]),
1177       MockAffectedFile('HasNoTagDecl.java', [
1178         'import org.chromium.base.Log;',
1179         'some random stuff',
1180         'Log.d(TAG, "foo");',
1181       ]),
1182       MockAffectedFile('HasIncorrectTagDecl.java', [
1183         'import org.chromium.base.Log;',
1184         'private static final String TAHG = "cr_Foo";',
1185         'some random stuff',
1186         'Log.d(TAG, "foo");',
1187       ]),
1188       MockAffectedFile('HasInlineTag.java', [
1189         'import org.chromium.base.Log;',
1190         'some random stuff',
1191         'private static final String TAG = "cr_Foo";',
1192         'Log.d("TAG", "foo");',
1193       ]),
1194       MockAffectedFile('HasUnprefixedTag.java', [
1195         'import org.chromium.base.Log;',
1196         'some random stuff',
1197         'private static final String TAG = "rubbish";',
1198         'Log.d(TAG, "foo");',
1199       ]),
1200       MockAffectedFile('HasTooLongTag.java', [
1201         'import org.chromium.base.Log;',
1202         'some random stuff',
1203         'private static final String TAG = "21_charachers_long___";',
1204         'Log.d(TAG, "foo");',
1205       ]),
1206     ]
1207
1208     msgs = PRESUBMIT._CheckAndroidCrLogUsage(
1209         mock_input_api, mock_output_api)
1210
1211     self.assertEqual(5, len(msgs),
1212                      'Expected %d items, found %d: %s' % (5, len(msgs), msgs))
1213
1214     # Declaration format
1215     nb = len(msgs[0].items)
1216     self.assertEqual(2, nb,
1217                      'Expected %d items, found %d: %s' % (2, nb, msgs[0].items))
1218     self.assertTrue('HasNoTagDecl.java' in msgs[0].items)
1219     self.assertTrue('HasIncorrectTagDecl.java' in msgs[0].items)
1220
1221     # Tag length
1222     nb = len(msgs[1].items)
1223     self.assertEqual(1, nb,
1224                      'Expected %d items, found %d: %s' % (1, nb, msgs[1].items))
1225     self.assertTrue('HasTooLongTag.java' in msgs[1].items)
1226
1227     # Tag must be a variable named TAG
1228     nb = len(msgs[2].items)
1229     self.assertEqual(1, nb,
1230                      'Expected %d items, found %d: %s' % (1, nb, msgs[2].items))
1231     self.assertTrue('HasInlineTag.java:4' in msgs[2].items)
1232
1233     # Util Log usage
1234     nb = len(msgs[3].items)
1235     self.assertEqual(2, nb,
1236                      'Expected %d items, found %d: %s' % (2, nb, msgs[3].items))
1237     self.assertTrue('HasAndroidLog.java:3' in msgs[3].items)
1238     self.assertTrue('IsInBasePackageButImportsLog.java:4' in msgs[3].items)
1239
1240     # Tag must not contain
1241     nb = len(msgs[4].items)
1242     self.assertEqual(2, nb,
1243                      'Expected %d items, found %d: %s' % (2, nb, msgs[4].items))
1244     self.assertTrue('HasDottedTag.java' in msgs[4].items)
1245     self.assertTrue('HasOldTag.java' in msgs[4].items)
1246
1247 class GoogleAnswerUrlFormatTest(unittest.TestCase):
1248
1249   def testCatchAnswerUrlId(self):
1250     input_api = MockInputApi()
1251     input_api.files = [
1252       MockFile('somewhere/file.cc',
1253                ['char* host = '
1254                 '  "https://support.google.com/chrome/answer/123456";']),
1255       MockFile('somewhere_else/file.cc',
1256                ['char* host = '
1257                 '  "https://support.google.com/chrome/a/answer/123456";']),
1258     ]
1259
1260     warnings = PRESUBMIT._CheckGoogleSupportAnswerUrl(
1261       input_api, MockOutputApi())
1262     self.assertEqual(1, len(warnings))
1263     self.assertEqual(2, len(warnings[0].items))
1264
1265   def testAllowAnswerUrlParam(self):
1266     input_api = MockInputApi()
1267     input_api.files = [
1268       MockFile('somewhere/file.cc',
1269                ['char* host = '
1270                 '  "https://support.google.com/chrome/?p=cpn_crash_reports";']),
1271     ]
1272
1273     warnings = PRESUBMIT._CheckGoogleSupportAnswerUrl(
1274       input_api, MockOutputApi())
1275     self.assertEqual(0, len(warnings))
1276
1277 class HardcodedGoogleHostsTest(unittest.TestCase):
1278
1279   def testWarnOnAssignedLiterals(self):
1280     input_api = MockInputApi()
1281     input_api.files = [
1282       MockFile('content/file.cc',
1283                ['char* host = "https://www.google.com";']),
1284       MockFile('content/file.cc',
1285                ['char* host = "https://www.googleapis.com";']),
1286       MockFile('content/file.cc',
1287                ['char* host = "https://clients1.google.com";']),
1288     ]
1289
1290     warnings = PRESUBMIT._CheckHardcodedGoogleHostsInLowerLayers(
1291       input_api, MockOutputApi())
1292     self.assertEqual(1, len(warnings))
1293     self.assertEqual(3, len(warnings[0].items))
1294
1295   def testAllowInComment(self):
1296     input_api = MockInputApi()
1297     input_api.files = [
1298       MockFile('content/file.cc',
1299                ['char* host = "https://www.aol.com"; // google.com'])
1300     ]
1301
1302     warnings = PRESUBMIT._CheckHardcodedGoogleHostsInLowerLayers(
1303       input_api, MockOutputApi())
1304     self.assertEqual(0, len(warnings))
1305
1306
1307 class ForwardDeclarationTest(unittest.TestCase):
1308   def testCheckHeadersOnlyOutsideThirdParty(self):
1309     mock_input_api = MockInputApi()
1310     mock_input_api.files = [
1311       MockAffectedFile('somewhere/file.cc', [
1312         'class DummyClass;'
1313       ]),
1314       MockAffectedFile('third_party/header.h', [
1315         'class DummyClass;'
1316       ])
1317     ]
1318     warnings = PRESUBMIT._CheckUselessForwardDeclarations(mock_input_api,
1319       MockOutputApi())
1320     self.assertEqual(0, len(warnings))
1321
1322   def testNoNestedDeclaration(self):
1323     mock_input_api = MockInputApi()
1324     mock_input_api.files = [
1325       MockAffectedFile('somewhere/header.h', [
1326         'class SomeClass {',
1327         ' protected:',
1328         '  class NotAMatch;',
1329         '};'
1330       ])
1331     ]
1332     warnings = PRESUBMIT._CheckUselessForwardDeclarations(mock_input_api,
1333       MockOutputApi())
1334     self.assertEqual(0, len(warnings))
1335
1336   def testSubStrings(self):
1337     mock_input_api = MockInputApi()
1338     mock_input_api.files = [
1339       MockAffectedFile('somewhere/header.h', [
1340         'class NotUsefulClass;',
1341         'struct SomeStruct;',
1342         'UsefulClass *p1;',
1343         'SomeStructPtr *p2;'
1344       ])
1345     ]
1346     warnings = PRESUBMIT._CheckUselessForwardDeclarations(mock_input_api,
1347       MockOutputApi())
1348     self.assertEqual(2, len(warnings))
1349
1350   def testUselessForwardDeclaration(self):
1351     mock_input_api = MockInputApi()
1352     mock_input_api.files = [
1353       MockAffectedFile('somewhere/header.h', [
1354         'class DummyClass;',
1355         'struct DummyStruct;',
1356         'class UsefulClass;',
1357         'std::unique_ptr<UsefulClass> p;'
1358       ])
1359     ]
1360     warnings = PRESUBMIT._CheckUselessForwardDeclarations(mock_input_api,
1361       MockOutputApi())
1362     self.assertEqual(2, len(warnings))
1363
1364   def testBlinkHeaders(self):
1365     mock_input_api = MockInputApi()
1366     mock_input_api.files = [
1367       MockAffectedFile('third_party/WebKit/header.h', [
1368         'class DummyClass;',
1369         'struct DummyStruct;',
1370       ]),
1371       MockAffectedFile('third_party\\WebKit\\header.h', [
1372         'class DummyClass;',
1373         'struct DummyStruct;',
1374       ])
1375     ]
1376     warnings = PRESUBMIT._CheckUselessForwardDeclarations(mock_input_api,
1377       MockOutputApi())
1378     self.assertEqual(4, len(warnings))
1379
1380
1381 class RiskyJsTest(unittest.TestCase):
1382   def testArrowWarnInIos9Code(self):
1383     mock_input_api = MockInputApi()
1384     mock_output_api = MockOutputApi()
1385
1386     mock_input_api.files = [
1387       MockAffectedFile('components/blah.js', ["shouldn't use => here"]),
1388     ]
1389     warnings = PRESUBMIT._CheckForRiskyJsFeatures(
1390         mock_input_api, mock_output_api)
1391     self.assertEqual(1, len(warnings))
1392
1393     mock_input_api.files = [
1394       MockAffectedFile('ios/blee.js', ['might => break folks']),
1395     ]
1396     warnings = PRESUBMIT._CheckForRiskyJsFeatures(
1397         mock_input_api, mock_output_api)
1398     self.assertEqual(1, len(warnings))
1399
1400     mock_input_api.files = [
1401       MockAffectedFile('ui/webui/resources/blarg.js', ['on => iOS9']),
1402     ]
1403     warnings = PRESUBMIT._CheckForRiskyJsFeatures(
1404         mock_input_api, mock_output_api)
1405     self.assertEqual(1, len(warnings))
1406
1407   def testArrowsAllowedInChromeCode(self):
1408     mock_input_api = MockInputApi()
1409     mock_input_api.files = [
1410       MockAffectedFile('chrome/browser/resources/blah.js', 'arrow => OK here'),
1411     ]
1412     warnings = PRESUBMIT._CheckForRiskyJsFeatures(
1413         mock_input_api, MockOutputApi())
1414     self.assertEqual(0, len(warnings))
1415
1416   def testConstLetWarningIos9Code(self):
1417     mock_input_api = MockInputApi()
1418     mock_output_api = MockOutputApi()
1419
1420     mock_input_api.files = [
1421       MockAffectedFile('components/blah.js', [" const foo = 'bar';"]),
1422       MockAffectedFile('ui/webui/resources/blah.js', [" let foo = 3;"]),
1423     ]
1424     warnings = PRESUBMIT._CheckForRiskyJsFeatures(
1425         mock_input_api, mock_output_api)
1426     self.assertEqual(2, len(warnings))
1427
1428 class RelativeIncludesTest(unittest.TestCase):
1429   def testThirdPartyNotWebKitIgnored(self):
1430     mock_input_api = MockInputApi()
1431     mock_input_api.files = [
1432       MockAffectedFile('third_party/test.cpp', '#include "../header.h"'),
1433       MockAffectedFile('third_party/test/test.cpp', '#include "../header.h"'),
1434     ]
1435
1436     mock_output_api = MockOutputApi()
1437
1438     errors = PRESUBMIT._CheckForRelativeIncludes(
1439         mock_input_api, mock_output_api)
1440     self.assertEqual(0, len(errors))
1441
1442   def testNonCppFileIgnored(self):
1443     mock_input_api = MockInputApi()
1444     mock_input_api.files = [
1445       MockAffectedFile('test.py', '#include "../header.h"'),
1446     ]
1447
1448     mock_output_api = MockOutputApi()
1449
1450     errors = PRESUBMIT._CheckForRelativeIncludes(
1451         mock_input_api, mock_output_api)
1452     self.assertEqual(0, len(errors))
1453
1454   def testInnocuousChangesAllowed(self):
1455     mock_input_api = MockInputApi()
1456     mock_input_api.files = [
1457       MockAffectedFile('test.cpp', '#include "header.h"'),
1458       MockAffectedFile('test2.cpp', '../'),
1459     ]
1460
1461     mock_output_api = MockOutputApi()
1462
1463     errors = PRESUBMIT._CheckForRelativeIncludes(
1464         mock_input_api, mock_output_api)
1465     self.assertEqual(0, len(errors))
1466
1467   def testRelativeIncludeNonWebKitProducesError(self):
1468     mock_input_api = MockInputApi()
1469     mock_input_api.files = [
1470       MockAffectedFile('test.cpp', ['#include "../header.h"']),
1471     ]
1472
1473     mock_output_api = MockOutputApi()
1474
1475     errors = PRESUBMIT._CheckForRelativeIncludes(
1476         mock_input_api, mock_output_api)
1477     self.assertEqual(1, len(errors))
1478
1479   def testRelativeIncludeWebKitProducesError(self):
1480     mock_input_api = MockInputApi()
1481     mock_input_api.files = [
1482       MockAffectedFile('third_party/WebKit/test.cpp',
1483                        ['#include "../header.h']),
1484     ]
1485
1486     mock_output_api = MockOutputApi()
1487
1488     errors = PRESUBMIT._CheckForRelativeIncludes(
1489         mock_input_api, mock_output_api)
1490     self.assertEqual(1, len(errors))
1491
1492
1493 class MojoManifestOwnerTest(unittest.TestCase):
1494   def testMojoManifestChangeNeedsSecurityOwner(self):
1495     mock_input_api = MockInputApi()
1496     mock_input_api.files = [
1497       MockAffectedFile('services/goat/manifest.json',
1498                        [
1499                          '{',
1500                          '  "name": "teleporter",',
1501                          '  "display_name": "Goat Teleporter",'
1502                          '  "interface_provider_specs": {',
1503                          '  }',
1504                          '}',
1505                        ])
1506     ]
1507     mock_output_api = MockOutputApi()
1508     errors = PRESUBMIT._CheckIpcOwners(
1509         mock_input_api, mock_output_api)
1510     self.assertEqual(1, len(errors))
1511     self.assertEqual(
1512         'Found OWNERS files that need to be updated for IPC security review ' +
1513         'coverage.\nPlease update the OWNERS files below:', errors[0].message)
1514
1515     # No warning if already covered by an OWNERS rule.
1516
1517   def testNonManifestChangesDoNotRequireSecurityOwner(self):
1518     mock_input_api = MockInputApi()
1519     mock_input_api.files = [
1520       MockAffectedFile('services/goat/species.json',
1521                        [
1522                          '[',
1523                          '  "anglo-nubian",',
1524                          '  "angora"',
1525                          ']',
1526                        ])
1527     ]
1528     mock_output_api = MockOutputApi()
1529     errors = PRESUBMIT._CheckIpcOwners(
1530         mock_input_api, mock_output_api)
1531     self.assertEqual([], errors)
1532
1533
1534 class CrbugUrlFormatTest(unittest.TestCase):
1535
1536   def testCheckCrbugLinksHaveHttps(self):
1537     input_api = MockInputApi()
1538     input_api.files = [
1539       MockFile('somewhere/file.cc',
1540                ['// TODO(developer): crbug.com should be linkified',
1541                 '// TODO(developer): (crbug.com) should be linkified',
1542                 '// TODO(developer): crbug/123 should be well formed',
1543                 '// TODO(developer): http://crbug.com it\'s OK',
1544                 '// TODO(developer): https://crbug.com is just great',
1545                 '// TODO(crbug.com/123456): this pattern it\'s also OK']),
1546     ]
1547
1548     warnings = PRESUBMIT._CheckCrbugLinksHaveHttps(input_api, MockOutputApi())
1549     self.assertEqual(1, len(warnings))
1550     self.assertEqual(3, warnings[0].message.count('\n'));
1551
1552
1553 class BannedFunctionCheckTest(unittest.TestCase):
1554
1555   def testBannedIosObcjFunctions(self):
1556     input_api = MockInputApi()
1557     input_api.files = [
1558       MockFile('some/ios/file.mm',
1559                ['TEST(SomeClassTest, SomeInteraction) {',
1560                 '}']),
1561       MockFile('some/mac/file.mm',
1562                ['TEST(SomeClassTest, SomeInteraction) {',
1563                 '}']),
1564       MockFile('another/ios_file.mm',
1565                ['class SomeTest : public testing::Test {};']),
1566     ]
1567
1568     errors = PRESUBMIT._CheckNoBannedFunctions(input_api, MockOutputApi())
1569     self.assertEqual(1, len(errors))
1570     self.assertTrue('some/ios/file.mm' in errors[0].message)
1571     self.assertTrue('another/ios_file.mm' in errors[0].message)
1572     self.assertTrue('some/mac/file.mm' not in errors[0].message)
1573
1574
1575 class NoProductionCodeUsingTestOnlyFunctions(unittest.TestCase):
1576   def testTruePositives(self):
1577     mock_input_api = MockInputApi()
1578     mock_input_api.files = [
1579       MockFile('some/path/foo.cc', ['foo_for_testing();']),
1580       MockFile('some/path/foo.mm', ['FooForTesting();']),
1581       MockFile('some/path/foo.cxx', ['FooForTests();']),
1582       MockFile('some/path/foo.cpp', ['foo_for_test();']),
1583     ]
1584
1585     results = PRESUBMIT._CheckNoProductionCodeUsingTestOnlyFunctions(
1586         mock_input_api, MockOutputApi())
1587     self.assertEqual(1, len(results))
1588     self.assertEqual(4, len(results[0].items))
1589     self.assertTrue('foo.cc' in results[0].items[0])
1590     self.assertTrue('foo.mm' in results[0].items[1])
1591     self.assertTrue('foo.cxx' in results[0].items[2])
1592     self.assertTrue('foo.cpp' in results[0].items[3])
1593
1594   def testFalsePositives(self):
1595     mock_input_api = MockInputApi()
1596     mock_input_api.files = [
1597       MockFile('some/path/foo.h', ['foo_for_testing();']),
1598       MockFile('some/path/foo.mm', ['FooForTesting() {']),
1599       MockFile('some/path/foo.cc', ['::FooForTests();']),
1600       MockFile('some/path/foo.cpp', ['// foo_for_test();']),
1601     ]
1602
1603     results = PRESUBMIT._CheckNoProductionCodeUsingTestOnlyFunctions(
1604         mock_input_api, MockOutputApi())
1605     self.assertEqual(0, len(results))
1606
1607
1608 class NoProductionJavaCodeUsingTestOnlyFunctions(unittest.TestCase):
1609   def testTruePositives(self):
1610     mock_input_api = MockInputApi()
1611     mock_input_api.files = [
1612       MockFile('dir/java/src/foo.java', ['FooForTesting();']),
1613       MockFile('dir/java/src/bar.java', ['FooForTests(x);']),
1614       MockFile('dir/java/src/baz.java', ['FooForTest(','y',');']),
1615       MockFile('dir/java/src/mult.java', [
1616         'int x = SomethingLongHere()',
1617         '    * SomethingLongHereForTesting();'
1618       ]),
1619     ]
1620
1621     results = PRESUBMIT._CheckNoProductionCodeUsingTestOnlyFunctionsJava(
1622         mock_input_api, MockOutputApi())
1623     self.assertEqual(1, len(results))
1624     self.assertEqual(4, len(results[0].items))
1625     self.assertTrue('foo.java' in results[0].items[0])
1626     self.assertTrue('bar.java' in results[0].items[1])
1627     self.assertTrue('baz.java' in results[0].items[2])
1628     self.assertTrue('mult.java' in results[0].items[3])
1629
1630   def testFalsePositives(self):
1631     mock_input_api = MockInputApi()
1632     mock_input_api.files = [
1633       MockFile('dir/java/src/foo.xml', ['FooForTesting();']),
1634       MockFile('dir/java/src/foo.java', ['FooForTests() {']),
1635       MockFile('dir/java/src/bar.java', ['// FooForTest();']),
1636       MockFile('dir/java/src/bar2.java', ['x = 1; // FooForTest();']),
1637       MockFile('dir/javatests/src/baz.java', ['FooForTest(','y',');']),
1638       MockFile('dir/junit/src/baz.java', ['FooForTest(','y',');']),
1639       MockFile('dir/junit/src/javadoc.java', [
1640         '/** Use FooForTest(); to obtain foo in tests.'
1641         ' */'
1642       ]),
1643       MockFile('dir/junit/src/javadoc2.java', [
1644         '/** ',
1645         ' * Use FooForTest(); to obtain foo in tests.'
1646         ' */'
1647       ]),
1648     ]
1649
1650     results = PRESUBMIT._CheckNoProductionCodeUsingTestOnlyFunctionsJava(
1651         mock_input_api, MockOutputApi())
1652     self.assertEqual(0, len(results))
1653
1654
1655 class CheckUniquePtr(unittest.TestCase):
1656   def testTruePositivesNullptr(self):
1657     mock_input_api = MockInputApi()
1658     mock_input_api.files = [
1659       MockFile('dir/baz.cc', ['std::unique_ptr<T>()']),
1660       MockFile('dir/baz-p.cc', ['std::unique_ptr<T<P>>()']),
1661     ]
1662
1663     results = PRESUBMIT._CheckUniquePtr(mock_input_api, MockOutputApi())
1664     self.assertEqual(1, len(results))
1665     self.assertTrue('nullptr' in results[0].message)
1666     self.assertEqual(2, len(results[0].items))
1667     self.assertTrue('baz.cc' in results[0].items[0])
1668     self.assertTrue('baz-p.cc' in results[0].items[1])
1669
1670   def testTruePositivesConstructor(self):
1671     mock_input_api = MockInputApi()
1672     mock_input_api.files = [
1673       MockFile('dir/foo.cc', ['return std::unique_ptr<T>(foo);']),
1674       MockFile('dir/bar.mm', ['bar = std::unique_ptr<T>(foo)']),
1675       MockFile('dir/mult.cc', [
1676         'return',
1677         '    std::unique_ptr<T>(barVeryVeryLongFooSoThatItWouldNotFitAbove);'
1678       ]),
1679       MockFile('dir/mult2.cc', [
1680         'barVeryVeryLongLongBaaaaaarSoThatTheLineLimitIsAlmostReached =',
1681         '    std::unique_ptr<T>(foo);'
1682       ]),
1683       MockFile('dir/mult3.cc', [
1684         'bar = std::unique_ptr<T>(',
1685         '    fooVeryVeryVeryLongStillGoingWellThisWillTakeAWhileFinallyThere);'
1686       ]),
1687     ]
1688
1689     results = PRESUBMIT._CheckUniquePtr(mock_input_api, MockOutputApi())
1690     self.assertEqual(1, len(results))
1691     self.assertTrue('std::make_unique' in results[0].message)
1692     self.assertEqual(5, len(results[0].items))
1693     self.assertTrue('foo.cc' in results[0].items[0])
1694     self.assertTrue('bar.mm' in results[0].items[1])
1695     self.assertTrue('mult.cc' in results[0].items[2])
1696     self.assertTrue('mult2.cc' in results[0].items[3])
1697     self.assertTrue('mult3.cc' in results[0].items[4])
1698
1699   def testFalsePositives(self):
1700     mock_input_api = MockInputApi()
1701     mock_input_api.files = [
1702       MockFile('dir/foo.cc', ['return std::unique_ptr<T[]>(foo);']),
1703       MockFile('dir/bar.mm', ['bar = std::unique_ptr<T[]>(foo)']),
1704       MockFile('dir/file.cc', ['std::unique_ptr<T> p = Foo();']),
1705       MockFile('dir/baz.cc', [
1706         'std::unique_ptr<T> result = std::make_unique<T>();'
1707       ]),
1708       MockFile('dir/baz2.cc', [
1709         'std::unique_ptr<T> result = std::make_unique<T>('
1710       ]),
1711       MockFile('dir/nested.cc', ['set<std::unique_ptr<T>>();']),
1712       MockFile('dir/nested2.cc', ['map<U, std::unique_ptr<T>>();']),
1713     ]
1714
1715     results = PRESUBMIT._CheckUniquePtr(mock_input_api, MockOutputApi())
1716     self.assertEqual(0, len(results))
1717
1718
1719 if __name__ == '__main__':
1720   unittest.main()