ui/events/event_switches.h
[chromium-dfly.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 subprocess
8 import unittest
9
10 import PRESUBMIT
11 from PRESUBMIT_test_mocks import MockFile, MockAffectedFile
12 from PRESUBMIT_test_mocks import MockInputApi, MockOutputApi
13
14
15 _TEST_DATA_DIR = 'base/test/data/presubmit'
16
17
18 class VersionControlConflictsTest(unittest.TestCase):
19   def testTypicalConflict(self):
20     lines = ['<<<<<<< HEAD',
21              '  base::ScopedTempDir temp_dir_;',
22              '=======',
23              '  ScopedTempDir temp_dir_;',
24              '>>>>>>> master']
25     errors = PRESUBMIT._CheckForVersionControlConflictsInFile(
26         MockInputApi(), MockFile('some/path/foo_platform.cc', lines))
27     self.assertEqual(3, len(errors))
28     self.assertTrue('1' in errors[0])
29     self.assertTrue('3' in errors[1])
30     self.assertTrue('5' in errors[2])
31
32   def testIgnoresReadmes(self):
33     lines = ['A First Level Header',
34              '====================',
35              '',
36              'A Second Level Header',
37              '---------------------']
38     errors = PRESUBMIT._CheckForVersionControlConflictsInFile(
39         MockInputApi(), MockFile('some/polymer/README.md', lines))
40     self.assertEqual(0, len(errors))
41
42
43 class UmaHistogramChangeMatchedOrNotTest(unittest.TestCase):
44   def testTypicalCorrectlyMatchedChange(self):
45     diff_cc = ['UMA_HISTOGRAM_BOOL("Bla.Foo.Dummy", true)']
46     diff_java = [
47       'RecordHistogram.recordBooleanHistogram("Bla.Foo.Dummy", true)']
48     diff_xml = ['<histogram name="Bla.Foo.Dummy"> </histogram>']
49     mock_input_api = MockInputApi()
50     mock_input_api.files = [
51       MockFile('some/path/foo.cc', diff_cc),
52       MockFile('some/path/foo.java', diff_java),
53       MockFile('tools/metrics/histograms/histograms.xml', diff_xml),
54     ]
55     warnings = PRESUBMIT._CheckUmaHistogramChanges(mock_input_api,
56                                                    MockOutputApi())
57     self.assertEqual(0, len(warnings))
58
59   def testTypicalNotMatchedChange(self):
60     diff_cc = ['UMA_HISTOGRAM_BOOL("Bla.Foo.Dummy", true)']
61     diff_java = [
62       'RecordHistogram.recordBooleanHistogram("Bla.Foo.Dummy", true)']
63     mock_input_api = MockInputApi()
64     mock_input_api.files = [
65       MockFile('some/path/foo.cc', diff_cc),
66       MockFile('some/path/foo.java', diff_java),
67     ]
68     warnings = PRESUBMIT._CheckUmaHistogramChanges(mock_input_api,
69                                                    MockOutputApi())
70     self.assertEqual(1, len(warnings))
71     self.assertEqual('warning', warnings[0].type)
72     self.assertTrue('foo.cc' in warnings[0].items[0])
73     self.assertTrue('foo.java' in warnings[0].items[1])
74
75   def testTypicalNotMatchedChangeViaSuffixes(self):
76     diff_cc = ['UMA_HISTOGRAM_BOOL("Bla.Foo.Dummy", true)']
77     diff_java = [
78       'RecordHistogram.recordBooleanHistogram("Bla.Foo.Dummy", true)']
79     diff_xml = ['<histogram_suffixes name="SuperHistogram">',
80                 '  <suffix name="Dummy"/>',
81                 '  <affected-histogram name="Snafu.Dummy"/>',
82                 '</histogram>']
83     mock_input_api = MockInputApi()
84     mock_input_api.files = [
85       MockFile('some/path/foo.cc', diff_cc),
86       MockFile('some/path/foo.java', diff_java),
87       MockFile('tools/metrics/histograms/histograms.xml', diff_xml),
88     ]
89     warnings = PRESUBMIT._CheckUmaHistogramChanges(mock_input_api,
90                                                    MockOutputApi())
91     self.assertEqual(1, len(warnings))
92     self.assertEqual('warning', warnings[0].type)
93     self.assertTrue('foo.cc' in warnings[0].items[0])
94     self.assertTrue('foo.java' in warnings[0].items[1])
95
96   def testTypicalCorrectlyMatchedChangeViaSuffixes(self):
97     diff_cc = ['UMA_HISTOGRAM_BOOL("Bla.Foo.Dummy", true)']
98     diff_java = [
99       'RecordHistogram.recordBooleanHistogram("Bla.Foo.Dummy", true)']
100     diff_xml = ['<histogram_suffixes name="SuperHistogram">',
101                 '  <suffix name="Dummy"/>',
102                 '  <affected-histogram name="Bla.Foo"/>',
103                 '</histogram>']
104     mock_input_api = MockInputApi()
105     mock_input_api.files = [
106       MockFile('some/path/foo.cc', diff_cc),
107       MockFile('some/path/foo.java', diff_java),
108       MockFile('tools/metrics/histograms/histograms.xml', diff_xml),
109     ]
110     warnings = PRESUBMIT._CheckUmaHistogramChanges(mock_input_api,
111                                                    MockOutputApi())
112     self.assertEqual(0, len(warnings))
113
114   def testTypicalCorrectlyMatchedChangeViaSuffixesWithSeparator(self):
115     diff_cc = ['UMA_HISTOGRAM_BOOL("Snafu_Dummy", true)']
116     diff_java = ['RecordHistogram.recordBooleanHistogram("Snafu_Dummy", true)']
117     diff_xml = ['<histogram_suffixes name="SuperHistogram" separator="_">',
118                 '  <suffix name="Dummy"/>',
119                 '  <affected-histogram name="Snafu"/>',
120                 '</histogram>']
121     mock_input_api = MockInputApi()
122     mock_input_api.files = [
123       MockFile('some/path/foo.cc', diff_cc),
124       MockFile('some/path/foo.java', diff_java),
125       MockFile('tools/metrics/histograms/histograms.xml', diff_xml),
126     ]
127     warnings = PRESUBMIT._CheckUmaHistogramChanges(mock_input_api,
128                                                    MockOutputApi())
129     self.assertEqual(0, len(warnings))
130
131   def testCorrectlyMatchedChangeViaSuffixesWithLineWrapping(self):
132     diff_cc = [
133         'UMA_HISTOGRAM_BOOL("LongHistogramNameNeedsLineWrapping.Dummy", true)']
134     diff_java = ['RecordHistogram.recordBooleanHistogram(' +
135                  '"LongHistogramNameNeedsLineWrapping.Dummy", true)']
136     diff_xml = ['<histogram_suffixes',
137                 '    name="LongHistogramNameNeedsLineWrapping"',
138                 '    separator=".">',
139                 '  <suffix name="Dummy"/>',
140                 '  <affected-histogram',
141                 '      name="LongHistogramNameNeedsLineWrapping"/>',
142                 '</histogram>']
143     mock_input_api = MockInputApi()
144     mock_input_api.files = [
145       MockFile('some/path/foo.cc', diff_cc),
146       MockFile('some/path/foo.java', diff_java),
147       MockFile('tools/metrics/histograms/histograms.xml', diff_xml),
148     ]
149     warnings = PRESUBMIT._CheckUmaHistogramChanges(mock_input_api,
150                                                    MockOutputApi())
151     self.assertEqual(0, len(warnings))
152
153   def testNameMatch(self):
154     # Check that the detected histogram name is "Dummy" and not, e.g.,
155     # "Dummy\", true);  // The \"correct"
156     diff_cc = ['UMA_HISTOGRAM_BOOL("Dummy", true);  // The "correct" histogram']
157     diff_java = [
158       'RecordHistogram.recordBooleanHistogram("Dummy", true);' +
159       '  // The "correct" histogram']
160     diff_xml = ['<histogram name="Dummy"> </histogram>']
161     mock_input_api = MockInputApi()
162     mock_input_api.files = [
163       MockFile('some/path/foo.cc', diff_cc),
164       MockFile('some/path/foo.java', diff_java),
165       MockFile('tools/metrics/histograms/histograms.xml', diff_xml),
166     ]
167     warnings = PRESUBMIT._CheckUmaHistogramChanges(mock_input_api,
168                                                    MockOutputApi())
169     self.assertEqual(0, len(warnings))
170
171   def testSimilarMacroNames(self):
172     diff_cc = ['PUMA_HISTOGRAM_COOL("Mountain Lion", 42)']
173     diff_java = [
174       'FakeRecordHistogram.recordFakeHistogram("Mountain Lion", 42)']
175     mock_input_api = MockInputApi()
176     mock_input_api.files = [
177       MockFile('some/path/foo.cc', diff_cc),
178       MockFile('some/path/foo.java', diff_java),
179     ]
180     warnings = PRESUBMIT._CheckUmaHistogramChanges(mock_input_api,
181                                                    MockOutputApi())
182     self.assertEqual(0, len(warnings))
183
184   def testMultiLine(self):
185     diff_cc = ['UMA_HISTOGRAM_BOOLEAN(', '    "Multi.Line", true)']
186     diff_cc2 = ['UMA_HISTOGRAM_BOOLEAN(', '    "Multi.Line"', '    , true)']
187     diff_java = [
188       'RecordHistogram.recordBooleanHistogram(',
189       '    "Multi.Line", true);',
190     ]
191     mock_input_api = MockInputApi()
192     mock_input_api.files = [
193       MockFile('some/path/foo.cc', diff_cc),
194       MockFile('some/path/foo2.cc', diff_cc2),
195       MockFile('some/path/foo.java', diff_java),
196     ]
197     warnings = PRESUBMIT._CheckUmaHistogramChanges(mock_input_api,
198                                                    MockOutputApi())
199     self.assertEqual(1, len(warnings))
200     self.assertEqual('warning', warnings[0].type)
201     self.assertTrue('foo.cc' in warnings[0].items[0])
202     self.assertTrue('foo2.cc' in warnings[0].items[1])
203
204
205 class BadExtensionsTest(unittest.TestCase):
206   def testBadRejFile(self):
207     mock_input_api = MockInputApi()
208     mock_input_api.files = [
209       MockFile('some/path/foo.cc', ''),
210       MockFile('some/path/foo.cc.rej', ''),
211       MockFile('some/path2/bar.h.rej', ''),
212     ]
213
214     results = PRESUBMIT._CheckPatchFiles(mock_input_api, MockOutputApi())
215     self.assertEqual(1, len(results))
216     self.assertEqual(2, len(results[0].items))
217     self.assertTrue('foo.cc.rej' in results[0].items[0])
218     self.assertTrue('bar.h.rej' in results[0].items[1])
219
220   def testBadOrigFile(self):
221     mock_input_api = MockInputApi()
222     mock_input_api.files = [
223       MockFile('other/path/qux.h.orig', ''),
224       MockFile('other/path/qux.h', ''),
225       MockFile('other/path/qux.cc', ''),
226     ]
227
228     results = PRESUBMIT._CheckPatchFiles(mock_input_api, MockOutputApi())
229     self.assertEqual(1, len(results))
230     self.assertEqual(1, len(results[0].items))
231     self.assertTrue('qux.h.orig' in results[0].items[0])
232
233   def testGoodFiles(self):
234     mock_input_api = MockInputApi()
235     mock_input_api.files = [
236       MockFile('other/path/qux.h', ''),
237       MockFile('other/path/qux.cc', ''),
238     ]
239     results = PRESUBMIT._CheckPatchFiles(mock_input_api, MockOutputApi())
240     self.assertEqual(0, len(results))
241
242
243 class CheckSingletonInHeadersTest(unittest.TestCase):
244   def testSingletonInArbitraryHeader(self):
245     diff_singleton_h = ['base::subtle::AtomicWord '
246                         'base::Singleton<Type, Traits, DifferentiatingType>::']
247     diff_foo_h = ['// base::Singleton<Foo> in comment.',
248                   'friend class base::Singleton<Foo>']
249     diff_foo2_h = ['  //Foo* bar = base::Singleton<Foo>::get();']
250     diff_bad_h = ['Foo* foo = base::Singleton<Foo>::get();']
251     mock_input_api = MockInputApi()
252     mock_input_api.files = [MockAffectedFile('base/memory/singleton.h',
253                                              diff_singleton_h),
254                             MockAffectedFile('foo.h', diff_foo_h),
255                             MockAffectedFile('foo2.h', diff_foo2_h),
256                             MockAffectedFile('bad.h', diff_bad_h)]
257     warnings = PRESUBMIT._CheckSingletonInHeaders(mock_input_api,
258                                                   MockOutputApi())
259     self.assertEqual(1, len(warnings))
260     self.assertEqual(1, len(warnings[0].items))
261     self.assertEqual('error', warnings[0].type)
262     self.assertTrue('Found base::Singleton<T>' in warnings[0].message)
263
264   def testSingletonInCC(self):
265     diff_cc = ['Foo* foo = base::Singleton<Foo>::get();']
266     mock_input_api = MockInputApi()
267     mock_input_api.files = [MockAffectedFile('some/path/foo.cc', diff_cc)]
268     warnings = PRESUBMIT._CheckSingletonInHeaders(mock_input_api,
269                                                   MockOutputApi())
270     self.assertEqual(0, len(warnings))
271
272
273 class InvalidOSMacroNamesTest(unittest.TestCase):
274   def testInvalidOSMacroNames(self):
275     lines = ['#if defined(OS_WINDOWS)',
276              ' #elif defined(OS_WINDOW)',
277              ' # if defined(OS_MACOSX) || defined(OS_CHROME)',
278              '# else  // defined(OS_MAC)',
279              '#endif  // defined(OS_MACOS)']
280     errors = PRESUBMIT._CheckForInvalidOSMacrosInFile(
281         MockInputApi(), MockFile('some/path/foo_platform.cc', lines))
282     self.assertEqual(len(lines), len(errors))
283     self.assertTrue(':1 OS_WINDOWS' in errors[0])
284     self.assertTrue('(did you mean OS_WIN?)' in errors[0])
285
286   def testValidOSMacroNames(self):
287     lines = ['#if defined(%s)' % m for m in PRESUBMIT._VALID_OS_MACROS]
288     errors = PRESUBMIT._CheckForInvalidOSMacrosInFile(
289         MockInputApi(), MockFile('some/path/foo_platform.cc', lines))
290     self.assertEqual(0, len(errors))
291
292
293 class InvalidIfDefinedMacroNamesTest(unittest.TestCase):
294   def testInvalidIfDefinedMacroNames(self):
295     lines = ['#if defined(TARGET_IPHONE_SIMULATOR)',
296              '#if !defined(TARGET_IPHONE_SIMULATOR)',
297              '#elif defined(TARGET_IPHONE_SIMULATOR)',
298              '#ifdef TARGET_IPHONE_SIMULATOR',
299              ' # ifdef TARGET_IPHONE_SIMULATOR',
300              '# if defined(VALID) || defined(TARGET_IPHONE_SIMULATOR)',
301              '# else  // defined(TARGET_IPHONE_SIMULATOR)',
302              '#endif  // defined(TARGET_IPHONE_SIMULATOR)']
303     errors = PRESUBMIT._CheckForInvalidIfDefinedMacrosInFile(
304         MockInputApi(), MockFile('some/path/source.mm', lines))
305     self.assertEqual(len(lines), len(errors))
306
307   def testValidIfDefinedMacroNames(self):
308     lines = ['#if defined(FOO)',
309              '#ifdef BAR']
310     errors = PRESUBMIT._CheckForInvalidIfDefinedMacrosInFile(
311         MockInputApi(), MockFile('some/path/source.cc', lines))
312     self.assertEqual(0, len(errors))
313
314
315 class CheckAddedDepsHaveTetsApprovalsTest(unittest.TestCase):
316
317   def calculate(self, old_include_rules, old_specific_include_rules,
318                 new_include_rules, new_specific_include_rules):
319     return PRESUBMIT._CalculateAddedDeps(
320         os.path, 'include_rules = %r\nspecific_include_rules = %r' % (
321             old_include_rules, old_specific_include_rules),
322         'include_rules = %r\nspecific_include_rules = %r' % (
323             new_include_rules, new_specific_include_rules))
324
325   def testCalculateAddedDeps(self):
326     old_include_rules = [
327         '+base',
328         '-chrome',
329         '+content',
330         '-grit',
331         '-grit/",',
332         '+jni/fooblat.h',
333         '!sandbox',
334     ]
335     old_specific_include_rules = {
336         'compositor\.*': {
337             '+cc',
338         },
339     }
340
341     new_include_rules = [
342         '-ash',
343         '+base',
344         '+chrome',
345         '+components',
346         '+content',
347         '+grit',
348         '+grit/generated_resources.h",',
349         '+grit/",',
350         '+jni/fooblat.h',
351         '+policy',
352         '+' + os.path.join('third_party', 'WebKit'),
353     ]
354     new_specific_include_rules = {
355         'compositor\.*': {
356             '+cc',
357         },
358         'widget\.*': {
359             '+gpu',
360         },
361     }
362
363     expected = set([
364         os.path.join('chrome', 'DEPS'),
365         os.path.join('gpu', 'DEPS'),
366         os.path.join('components', 'DEPS'),
367         os.path.join('policy', 'DEPS'),
368         os.path.join('third_party', 'WebKit', 'DEPS'),
369     ])
370     self.assertEqual(
371         expected,
372         self.calculate(old_include_rules, old_specific_include_rules,
373                        new_include_rules, new_specific_include_rules))
374
375   def testCalculateAddedDepsIgnoresPermutations(self):
376     old_include_rules = [
377         '+base',
378         '+chrome',
379     ]
380     new_include_rules = [
381         '+chrome',
382         '+base',
383     ]
384     self.assertEqual(set(),
385                      self.calculate(old_include_rules, {}, new_include_rules,
386                                     {}))
387
388
389 class JSONParsingTest(unittest.TestCase):
390   def testSuccess(self):
391     input_api = MockInputApi()
392     filename = 'valid_json.json'
393     contents = ['// This is a comment.',
394                 '{',
395                 '  "key1": ["value1", "value2"],',
396                 '  "key2": 3  // This is an inline comment.',
397                 '}'
398                 ]
399     input_api.files = [MockFile(filename, contents)]
400     self.assertEqual(None,
401                      PRESUBMIT._GetJSONParseError(input_api, filename))
402
403   def testFailure(self):
404     input_api = MockInputApi()
405     test_data = [
406       ('invalid_json_1.json',
407        ['{ x }'],
408        'Expecting property name:'),
409       ('invalid_json_2.json',
410        ['// Hello world!',
411         '{ "hello": "world }'],
412        'Unterminated string starting at:'),
413       ('invalid_json_3.json',
414        ['{ "a": "b", "c": "d", }'],
415        'Expecting property name:'),
416       ('invalid_json_4.json',
417        ['{ "a": "b" "c": "d" }'],
418        'Expecting , delimiter:'),
419     ]
420
421     input_api.files = [MockFile(filename, contents)
422                        for (filename, contents, _) in test_data]
423
424     for (filename, _, expected_error) in test_data:
425       actual_error = PRESUBMIT._GetJSONParseError(input_api, filename)
426       self.assertTrue(expected_error in str(actual_error),
427                       "'%s' not found in '%s'" % (expected_error, actual_error))
428
429   def testNoEatComments(self):
430     input_api = MockInputApi()
431     file_with_comments = 'file_with_comments.json'
432     contents_with_comments = ['// This is a comment.',
433                               '{',
434                               '  "key1": ["value1", "value2"],',
435                               '  "key2": 3  // This is an inline comment.',
436                               '}'
437                               ]
438     file_without_comments = 'file_without_comments.json'
439     contents_without_comments = ['{',
440                                  '  "key1": ["value1", "value2"],',
441                                  '  "key2": 3',
442                                  '}'
443                                  ]
444     input_api.files = [MockFile(file_with_comments, contents_with_comments),
445                        MockFile(file_without_comments,
446                                 contents_without_comments)]
447
448     self.assertEqual('No JSON object could be decoded',
449                      str(PRESUBMIT._GetJSONParseError(input_api,
450                                                       file_with_comments,
451                                                       eat_comments=False)))
452     self.assertEqual(None,
453                      PRESUBMIT._GetJSONParseError(input_api,
454                                                   file_without_comments,
455                                                   eat_comments=False))
456
457
458 class IDLParsingTest(unittest.TestCase):
459   def testSuccess(self):
460     input_api = MockInputApi()
461     filename = 'valid_idl_basics.idl'
462     contents = ['// Tests a valid IDL file.',
463                 'namespace idl_basics {',
464                 '  enum EnumType {',
465                 '    name1,',
466                 '    name2',
467                 '  };',
468                 '',
469                 '  dictionary MyType1 {',
470                 '    DOMString a;',
471                 '  };',
472                 '',
473                 '  callback Callback1 = void();',
474                 '  callback Callback2 = void(long x);',
475                 '  callback Callback3 = void(MyType1 arg);',
476                 '  callback Callback4 = void(EnumType type);',
477                 '',
478                 '  interface Functions {',
479                 '    static void function1();',
480                 '    static void function2(long x);',
481                 '    static void function3(MyType1 arg);',
482                 '    static void function4(Callback1 cb);',
483                 '    static void function5(Callback2 cb);',
484                 '    static void function6(Callback3 cb);',
485                 '    static void function7(Callback4 cb);',
486                 '  };',
487                 '',
488                 '  interface Events {',
489                 '    static void onFoo1();',
490                 '    static void onFoo2(long x);',
491                 '    static void onFoo2(MyType1 arg);',
492                 '    static void onFoo3(EnumType type);',
493                 '  };',
494                 '};'
495                 ]
496     input_api.files = [MockFile(filename, contents)]
497     self.assertEqual(None,
498                      PRESUBMIT._GetIDLParseError(input_api, filename))
499
500   def testFailure(self):
501     input_api = MockInputApi()
502     test_data = [
503       ('invalid_idl_1.idl',
504        ['//',
505         'namespace test {',
506         '  dictionary {',
507         '    DOMString s;',
508         '  };',
509         '};'],
510        'Unexpected "{" after keyword "dictionary".\n'),
511       # TODO(yoz): Disabled because it causes the IDL parser to hang.
512       # See crbug.com/363830.
513       # ('invalid_idl_2.idl',
514       #  (['namespace test {',
515       #    '  dictionary MissingSemicolon {',
516       #    '    DOMString a',
517       #    '    DOMString b;',
518       #    '  };',
519       #    '};'],
520       #   'Unexpected symbol DOMString after symbol a.'),
521       ('invalid_idl_3.idl',
522        ['//',
523         'namespace test {',
524         '  enum MissingComma {',
525         '    name1',
526         '    name2',
527         '  };',
528         '};'],
529        'Unexpected symbol name2 after symbol name1.'),
530       ('invalid_idl_4.idl',
531        ['//',
532         'namespace test {',
533         '  enum TrailingComma {',
534         '    name1,',
535         '    name2,',
536         '  };',
537         '};'],
538        'Trailing comma in block.'),
539       ('invalid_idl_5.idl',
540        ['//',
541         'namespace test {',
542         '  callback Callback1 = void(;',
543         '};'],
544        'Unexpected ";" after "(".'),
545       ('invalid_idl_6.idl',
546        ['//',
547         'namespace test {',
548         '  callback Callback1 = void(long );',
549         '};'],
550        'Unexpected ")" after symbol long.'),
551       ('invalid_idl_7.idl',
552        ['//',
553         'namespace test {',
554         '  interace Events {',
555         '    static void onFoo1();',
556         '  };',
557         '};'],
558        'Unexpected symbol Events after symbol interace.'),
559       ('invalid_idl_8.idl',
560        ['//',
561         'namespace test {',
562         '  interface NotEvent {',
563         '    static void onFoo1();',
564         '  };',
565         '};'],
566        'Did not process Interface Interface(NotEvent)'),
567       ('invalid_idl_9.idl',
568        ['//',
569         'namespace test {',
570         '  interface {',
571         '    static void function1();',
572         '  };',
573         '};'],
574        'Interface missing name.'),
575     ]
576
577     input_api.files = [MockFile(filename, contents)
578                        for (filename, contents, _) in test_data]
579
580     for (filename, _, expected_error) in test_data:
581       actual_error = PRESUBMIT._GetIDLParseError(input_api, filename)
582       self.assertTrue(expected_error in str(actual_error),
583                       "'%s' not found in '%s'" % (expected_error, actual_error))
584
585
586 class TryServerMasterTest(unittest.TestCase):
587   def testTryServerMasters(self):
588     bots = {
589         'master.tryserver.chromium.android': [
590             'android_archive_rel_ng',
591             'android_arm64_dbg_recipe',
592             'android_blink_rel',
593             'android_clang_dbg_recipe',
594             'android_compile_dbg',
595             'android_compile_x64_dbg',
596             'android_compile_x86_dbg',
597             'android_coverage',
598             'android_cronet_tester'
599             'android_swarming_rel',
600             'cast_shell_android',
601             'linux_android_dbg_ng',
602             'linux_android_rel_ng',
603         ],
604         'master.tryserver.chromium.mac': [
605             'ios_dbg_simulator',
606             'ios_rel_device',
607             'ios_rel_device_ninja',
608             'mac_asan',
609             'mac_asan_64',
610             'mac_chromium_compile_dbg',
611             'mac_chromium_compile_rel',
612             'mac_chromium_dbg',
613             'mac_chromium_rel',
614             'mac_nacl_sdk',
615             'mac_nacl_sdk_build',
616             'mac_rel_naclmore',
617             'mac_x64_rel',
618             'mac_xcodebuild',
619         ],
620         'master.tryserver.chromium.linux': [
621             'chromium_presubmit',
622             'linux_arm_cross_compile',
623             'linux_arm_tester',
624             'linux_chromeos_asan',
625             'linux_chromeos_browser_asan',
626             'linux_chromeos_valgrind',
627             'linux_chromium_chromeos_dbg',
628             'linux_chromium_chromeos_rel',
629             'linux_chromium_compile_dbg',
630             'linux_chromium_compile_rel',
631             'linux_chromium_dbg',
632             'linux_chromium_gn_dbg',
633             'linux_chromium_gn_rel',
634             'linux_chromium_rel',
635             'linux_chromium_trusty32_dbg',
636             'linux_chromium_trusty32_rel',
637             'linux_chromium_trusty_dbg',
638             'linux_chromium_trusty_rel',
639             'linux_clang_tsan',
640             'linux_ecs_ozone',
641             'linux_layout',
642             'linux_layout_asan',
643             'linux_layout_rel',
644             'linux_layout_rel_32',
645             'linux_nacl_sdk',
646             'linux_nacl_sdk_bionic',
647             'linux_nacl_sdk_bionic_build',
648             'linux_nacl_sdk_build',
649             'linux_redux',
650             'linux_rel_naclmore',
651             'linux_rel_precise32',
652             'linux_valgrind',
653             'tools_build_presubmit',
654         ],
655         'master.tryserver.chromium.win': [
656             'win8_aura',
657             'win8_chromium_dbg',
658             'win8_chromium_rel',
659             'win_chromium_compile_dbg',
660             'win_chromium_compile_rel',
661             'win_chromium_dbg',
662             'win_chromium_rel',
663             'win_chromium_rel',
664             'win_chromium_x64_dbg',
665             'win_chromium_x64_rel',
666             'win_nacl_sdk',
667             'win_nacl_sdk_build',
668             'win_rel_naclmore',
669          ],
670     }
671     for master, bots in bots.iteritems():
672       for bot in bots:
673         self.assertEqual(master, PRESUBMIT.GetTryServerMasterForBot(bot),
674                          'bot=%s: expected %s, computed %s' % (
675             bot, master, PRESUBMIT.GetTryServerMasterForBot(bot)))
676
677
678 class UserMetricsActionTest(unittest.TestCase):
679   def testUserMetricsActionInActions(self):
680     input_api = MockInputApi()
681     file_with_user_action = 'file_with_user_action.cc'
682     contents_with_user_action = [
683       'base::UserMetricsAction("AboutChrome")'
684     ]
685
686     input_api.files = [MockFile(file_with_user_action,
687                                 contents_with_user_action)]
688
689     self.assertEqual(
690       [], PRESUBMIT._CheckUserActionUpdate(input_api, MockOutputApi()))
691
692   def testUserMetricsActionNotAddedToActions(self):
693     input_api = MockInputApi()
694     file_with_user_action = 'file_with_user_action.cc'
695     contents_with_user_action = [
696       'base::UserMetricsAction("NotInActionsXml")'
697     ]
698
699     input_api.files = [MockFile(file_with_user_action,
700                                 contents_with_user_action)]
701
702     output = PRESUBMIT._CheckUserActionUpdate(input_api, MockOutputApi())
703     self.assertEqual(
704       ('File %s line %d: %s is missing in '
705        'tools/metrics/actions/actions.xml. Please run '
706        'tools/metrics/actions/extract_actions.py to update.'
707        % (file_with_user_action, 1, 'NotInActionsXml')),
708       output[0].message)
709
710
711 class PydepsNeedsUpdatingTest(unittest.TestCase):
712
713   class MockSubprocess(object):
714     CalledProcessError = subprocess.CalledProcessError
715
716   def setUp(self):
717     mock_all_pydeps = ['A.pydeps', 'B.pydeps']
718     self.old_ALL_PYDEPS_FILES = PRESUBMIT._ALL_PYDEPS_FILES
719     PRESUBMIT._ALL_PYDEPS_FILES = mock_all_pydeps
720     self.mock_input_api = MockInputApi()
721     self.mock_output_api = MockOutputApi()
722     self.mock_input_api.subprocess = PydepsNeedsUpdatingTest.MockSubprocess()
723     self.checker = PRESUBMIT.PydepsChecker(self.mock_input_api, mock_all_pydeps)
724     self.checker._file_cache = {
725         'A.pydeps': '# Generated by:\n# CMD A\nA.py\nC.py\n',
726         'B.pydeps': '# Generated by:\n# CMD B\nB.py\nC.py\n',
727     }
728
729   def tearDown(self):
730     PRESUBMIT._ALL_PYDEPS_FILES = self.old_ALL_PYDEPS_FILES
731
732   def _RunCheck(self):
733     return PRESUBMIT._CheckPydepsNeedsUpdating(self.mock_input_api,
734                                                self.mock_output_api,
735                                                checker_for_tests=self.checker)
736
737   def testAddedPydep(self):
738     # PRESUBMIT._CheckPydepsNeedsUpdating is only implemented for Android.
739     if self.mock_input_api.platform != 'linux2':
740       return []
741
742     self.mock_input_api.files = [
743       MockAffectedFile('new.pydeps', [], action='A'),
744     ]
745
746     self.mock_input_api.CreateMockFileInPath(
747         [x.LocalPath() for x in self.mock_input_api.AffectedFiles(
748             include_deletes=True)])
749     results = self._RunCheck()
750     self.assertEqual(1, len(results))
751     self.assertTrue('PYDEPS_FILES' in str(results[0]))
752
753   def testPydepNotInSrc(self):
754     self.mock_input_api.files = [
755       MockAffectedFile('new.pydeps', [], action='A'),
756     ]
757     self.mock_input_api.CreateMockFileInPath([])
758     results = self._RunCheck()
759     self.assertEqual(0, len(results))
760
761   def testRemovedPydep(self):
762     # PRESUBMIT._CheckPydepsNeedsUpdating is only implemented for Android.
763     if self.mock_input_api.platform != 'linux2':
764       return []
765
766     self.mock_input_api.files = [
767       MockAffectedFile(PRESUBMIT._ALL_PYDEPS_FILES[0], [], action='D'),
768     ]
769     self.mock_input_api.CreateMockFileInPath(
770         [x.LocalPath() for x in self.mock_input_api.AffectedFiles(
771             include_deletes=True)])
772     results = self._RunCheck()
773     self.assertEqual(1, len(results))
774     self.assertTrue('PYDEPS_FILES' in str(results[0]))
775
776   def testRandomPyIgnored(self):
777     # PRESUBMIT._CheckPydepsNeedsUpdating is only implemented for Android.
778     if self.mock_input_api.platform != 'linux2':
779       return []
780
781     self.mock_input_api.files = [
782       MockAffectedFile('random.py', []),
783     ]
784
785     results = self._RunCheck()
786     self.assertEqual(0, len(results), 'Unexpected results: %r' % results)
787
788   def testRelevantPyNoChange(self):
789     # PRESUBMIT._CheckPydepsNeedsUpdating is only implemented for Android.
790     if self.mock_input_api.platform != 'linux2':
791       return []
792
793     self.mock_input_api.files = [
794       MockAffectedFile('A.py', []),
795     ]
796
797     def mock_check_output(cmd, shell=False, env=None):
798       self.assertEqual('CMD A --output ""', cmd)
799       return self.checker._file_cache['A.pydeps']
800
801     self.mock_input_api.subprocess.check_output = mock_check_output
802
803     results = self._RunCheck()
804     self.assertEqual(0, len(results), 'Unexpected results: %r' % results)
805
806   def testRelevantPyOneChange(self):
807     # PRESUBMIT._CheckPydepsNeedsUpdating is only implemented for Android.
808     if self.mock_input_api.platform != 'linux2':
809       return []
810
811     self.mock_input_api.files = [
812       MockAffectedFile('A.py', []),
813     ]
814
815     def mock_check_output(cmd, shell=False, env=None):
816       self.assertEqual('CMD A --output ""', cmd)
817       return 'changed data'
818
819     self.mock_input_api.subprocess.check_output = mock_check_output
820
821     results = self._RunCheck()
822     self.assertEqual(1, len(results))
823     self.assertTrue('File is stale' in str(results[0]))
824
825   def testRelevantPyTwoChanges(self):
826     # PRESUBMIT._CheckPydepsNeedsUpdating is only implemented for Android.
827     if self.mock_input_api.platform != 'linux2':
828       return []
829
830     self.mock_input_api.files = [
831       MockAffectedFile('C.py', []),
832     ]
833
834     def mock_check_output(cmd, shell=False, env=None):
835       return 'changed data'
836
837     self.mock_input_api.subprocess.check_output = mock_check_output
838
839     results = self._RunCheck()
840     self.assertEqual(2, len(results))
841     self.assertTrue('File is stale' in str(results[0]))
842     self.assertTrue('File is stale' in str(results[1]))
843
844
845 class IncludeGuardTest(unittest.TestCase):
846   def testIncludeGuardChecks(self):
847     mock_input_api = MockInputApi()
848     mock_output_api = MockOutputApi()
849     mock_input_api.files = [
850         MockAffectedFile('content/browser/thing/foo.h', [
851           '// Comment',
852           '#ifndef CONTENT_BROWSER_THING_FOO_H_',
853           '#define CONTENT_BROWSER_THING_FOO_H_',
854           'struct McBoatFace;',
855           '#endif  // CONTENT_BROWSER_THING_FOO_H_',
856         ]),
857         MockAffectedFile('content/browser/thing/bar.h', [
858           '#ifndef CONTENT_BROWSER_THING_BAR_H_',
859           '#define CONTENT_BROWSER_THING_BAR_H_',
860           'namespace content {',
861           '#endif  // CONTENT_BROWSER_THING_BAR_H_',
862           '}  // namespace content',
863         ]),
864         MockAffectedFile('content/browser/test1.h', [
865           'namespace content {',
866           '}  // namespace content',
867         ]),
868         MockAffectedFile('content\\browser\\win.h', [
869           '#ifndef CONTENT_BROWSER_WIN_H_',
870           '#define CONTENT_BROWSER_WIN_H_',
871           'struct McBoatFace;',
872           '#endif  // CONTENT_BROWSER_WIN_H_',
873         ]),
874         MockAffectedFile('content/browser/test2.h', [
875           '// Comment',
876           '#ifndef CONTENT_BROWSER_TEST2_H_',
877           'struct McBoatFace;',
878           '#endif  // CONTENT_BROWSER_TEST2_H_',
879         ]),
880         MockAffectedFile('content/browser/internal.h', [
881           '// Comment',
882           '#ifndef CONTENT_BROWSER_INTERNAL_H_',
883           '#define CONTENT_BROWSER_INTERNAL_H_',
884           '// Comment',
885           '#ifndef INTERNAL_CONTENT_BROWSER_INTERNAL_H_',
886           '#define INTERNAL_CONTENT_BROWSER_INTERNAL_H_',
887           'namespace internal {',
888           '}  // namespace internal',
889           '#endif  // INTERNAL_CONTENT_BROWSER_THING_BAR_H_',
890           'namespace content {',
891           '}  // namespace content',
892           '#endif  // CONTENT_BROWSER_THING_BAR_H_',
893         ]),
894         MockAffectedFile('content/browser/thing/foo.cc', [
895           '// This is a non-header.',
896         ]),
897         MockAffectedFile('content/browser/disabled.h', [
898           '// no-include-guard-because-multiply-included',
899           'struct McBoatFace;',
900         ]),
901         # New files don't allow misspelled include guards.
902         MockAffectedFile('content/browser/spleling.h', [
903           '#ifndef CONTENT_BROWSER_SPLLEING_H_',
904           '#define CONTENT_BROWSER_SPLLEING_H_',
905           'struct McBoatFace;',
906           '#endif  // CONTENT_BROWSER_SPLLEING_H_',
907         ]),
908         # New files don't allow + in include guards.
909         MockAffectedFile('content/browser/foo+bar.h', [
910           '#ifndef CONTENT_BROWSER_FOO+BAR_H_',
911           '#define CONTENT_BROWSER_FOO+BAR_H_',
912           'struct McBoatFace;',
913           '#endif  // CONTENT_BROWSER_FOO+BAR_H_',
914         ]),
915         # Old files allow misspelled include guards (for now).
916         MockAffectedFile('chrome/old.h', [
917           '// New contents',
918           '#ifndef CHROME_ODL_H_',
919           '#define CHROME_ODL_H_',
920           '#endif  // CHROME_ODL_H_',
921         ], [
922           '// Old contents',
923           '#ifndef CHROME_ODL_H_',
924           '#define CHROME_ODL_H_',
925           '#endif  // CHROME_ODL_H_',
926         ]),
927         # Using a Blink style include guard outside Blink is wrong.
928         MockAffectedFile('content/NotInBlink.h', [
929           '#ifndef NotInBlink_h',
930           '#define NotInBlink_h',
931           'struct McBoatFace;',
932           '#endif  // NotInBlink_h',
933         ]),
934         # Using a Blink style include guard in Blink is no longer ok.
935         MockAffectedFile('third_party/blink/InBlink.h', [
936           '#ifndef InBlink_h',
937           '#define InBlink_h',
938           'struct McBoatFace;',
939           '#endif  // InBlink_h',
940         ]),
941         # Using a bad include guard in Blink is not ok.
942         MockAffectedFile('third_party/blink/AlsoInBlink.h', [
943           '#ifndef WrongInBlink_h',
944           '#define WrongInBlink_h',
945           'struct McBoatFace;',
946           '#endif  // WrongInBlink_h',
947         ]),
948         # Using a bad include guard in Blink is not accepted even if
949         # it's an old file.
950         MockAffectedFile('third_party/blink/StillInBlink.h', [
951           '// New contents',
952           '#ifndef AcceptedInBlink_h',
953           '#define AcceptedInBlink_h',
954           'struct McBoatFace;',
955           '#endif  // AcceptedInBlink_h',
956         ], [
957           '// Old contents',
958           '#ifndef AcceptedInBlink_h',
959           '#define AcceptedInBlink_h',
960           'struct McBoatFace;',
961           '#endif  // AcceptedInBlink_h',
962         ]),
963         # Using a non-Chromium include guard in third_party
964         # (outside blink) is accepted.
965         MockAffectedFile('third_party/foo/some_file.h', [
966           '#ifndef REQUIRED_RPCNDR_H_',
967           '#define REQUIRED_RPCNDR_H_',
968           'struct SomeFileFoo;',
969           '#endif  // REQUIRED_RPCNDR_H_',
970         ]),
971         # Not having proper include guard in *_message_generator.h
972         # for old IPC messages is allowed.
973         MockAffectedFile('content/common/content_message_generator.h', [
974           '#undef CONTENT_COMMON_FOO_MESSAGES_H_',
975           '#include "content/common/foo_messages.h"',
976           '#ifndef CONTENT_COMMON_FOO_MESSAGES_H_',
977           '#error "Failed to include content/common/foo_messages.h"',
978           '#endif',
979         ]),
980       ]
981     msgs = PRESUBMIT._CheckForIncludeGuards(
982         mock_input_api, mock_output_api)
983     expected_fail_count = 8
984     self.assertEqual(expected_fail_count, len(msgs),
985                      'Expected %d items, found %d: %s'
986                      % (expected_fail_count, len(msgs), msgs))
987     self.assertEqual(msgs[0].items, ['content/browser/thing/bar.h'])
988     self.assertEqual(msgs[0].message,
989                      'Include guard CONTENT_BROWSER_THING_BAR_H_ '
990                      'not covering the whole file')
991
992     self.assertEqual(msgs[1].items, ['content/browser/test1.h'])
993     self.assertEqual(msgs[1].message,
994                      'Missing include guard CONTENT_BROWSER_TEST1_H_')
995
996     self.assertEqual(msgs[2].items, ['content/browser/test2.h:3'])
997     self.assertEqual(msgs[2].message,
998                      'Missing "#define CONTENT_BROWSER_TEST2_H_" for '
999                      'include guard')
1000
1001     self.assertEqual(msgs[3].items, ['content/browser/spleling.h:1'])
1002     self.assertEqual(msgs[3].message,
1003                      'Header using the wrong include guard name '
1004                      'CONTENT_BROWSER_SPLLEING_H_')
1005
1006     self.assertEqual(msgs[4].items, ['content/browser/foo+bar.h'])
1007     self.assertEqual(msgs[4].message,
1008                      'Missing include guard CONTENT_BROWSER_FOO_BAR_H_')
1009
1010     self.assertEqual(msgs[5].items, ['content/NotInBlink.h:1'])
1011     self.assertEqual(msgs[5].message,
1012                      'Header using the wrong include guard name '
1013                      'NotInBlink_h')
1014
1015     self.assertEqual(msgs[6].items, ['third_party/blink/InBlink.h:1'])
1016     self.assertEqual(msgs[6].message,
1017                      'Header using the wrong include guard name '
1018                      'InBlink_h')
1019
1020     self.assertEqual(msgs[7].items, ['third_party/blink/AlsoInBlink.h:1'])
1021     self.assertEqual(msgs[7].message,
1022                      'Header using the wrong include guard name '
1023                      'WrongInBlink_h')
1024
1025
1026 class AndroidDeprecatedTestAnnotationTest(unittest.TestCase):
1027   def testCheckAndroidTestAnnotationUsage(self):
1028     mock_input_api = MockInputApi()
1029     mock_output_api = MockOutputApi()
1030
1031     mock_input_api.files = [
1032         MockAffectedFile('LalaLand.java', [
1033           'random stuff'
1034         ]),
1035         MockAffectedFile('CorrectUsage.java', [
1036           'import android.support.test.filters.LargeTest;',
1037           'import android.support.test.filters.MediumTest;',
1038           'import android.support.test.filters.SmallTest;',
1039         ]),
1040         MockAffectedFile('UsedDeprecatedLargeTestAnnotation.java', [
1041           'import android.test.suitebuilder.annotation.LargeTest;',
1042         ]),
1043         MockAffectedFile('UsedDeprecatedMediumTestAnnotation.java', [
1044           'import android.test.suitebuilder.annotation.MediumTest;',
1045         ]),
1046         MockAffectedFile('UsedDeprecatedSmallTestAnnotation.java', [
1047           'import android.test.suitebuilder.annotation.SmallTest;',
1048         ]),
1049         MockAffectedFile('UsedDeprecatedSmokeAnnotation.java', [
1050           'import android.test.suitebuilder.annotation.Smoke;',
1051         ])
1052     ]
1053     msgs = PRESUBMIT._CheckAndroidTestAnnotationUsage(
1054         mock_input_api, mock_output_api)
1055     self.assertEqual(1, len(msgs),
1056                      'Expected %d items, found %d: %s'
1057                      % (1, len(msgs), msgs))
1058     self.assertEqual(4, len(msgs[0].items),
1059                      'Expected %d items, found %d: %s'
1060                      % (4, len(msgs[0].items), msgs[0].items))
1061     self.assertTrue('UsedDeprecatedLargeTestAnnotation.java:1' in msgs[0].items,
1062                     'UsedDeprecatedLargeTestAnnotation not found in errors')
1063     self.assertTrue('UsedDeprecatedMediumTestAnnotation.java:1'
1064                     in msgs[0].items,
1065                     'UsedDeprecatedMediumTestAnnotation not found in errors')
1066     self.assertTrue('UsedDeprecatedSmallTestAnnotation.java:1' in msgs[0].items,
1067                     'UsedDeprecatedSmallTestAnnotation not found in errors')
1068     self.assertTrue('UsedDeprecatedSmokeAnnotation.java:1' in msgs[0].items,
1069                     'UsedDeprecatedSmokeAnnotation not found in errors')
1070
1071
1072 class AndroidDeprecatedJUnitFrameworkTest(unittest.TestCase):
1073   def testCheckAndroidTestJUnitFramework(self):
1074     mock_input_api = MockInputApi()
1075     mock_output_api = MockOutputApi()
1076
1077     mock_input_api.files = [
1078         MockAffectedFile('LalaLand.java', [
1079           'random stuff'
1080         ]),
1081         MockAffectedFile('CorrectUsage.java', [
1082           'import org.junit.ABC',
1083           'import org.junit.XYZ;',
1084         ]),
1085         MockAffectedFile('UsedDeprecatedJUnit.java', [
1086           'import junit.framework.*;',
1087         ]),
1088         MockAffectedFile('UsedDeprecatedJUnitAssert.java', [
1089           'import junit.framework.Assert;',
1090         ]),
1091     ]
1092     msgs = PRESUBMIT._CheckAndroidTestJUnitFrameworkImport(
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(2, len(msgs[0].items),
1098                      'Expected %d items, found %d: %s'
1099                      % (2, len(msgs[0].items), msgs[0].items))
1100     self.assertTrue('UsedDeprecatedJUnit.java:1' in msgs[0].items,
1101                     'UsedDeprecatedJUnit.java not found in errors')
1102     self.assertTrue('UsedDeprecatedJUnitAssert.java:1'
1103                     in msgs[0].items,
1104                     'UsedDeprecatedJUnitAssert not found in errors')
1105
1106
1107 class AndroidJUnitBaseClassTest(unittest.TestCase):
1108   def testCheckAndroidTestJUnitBaseClass(self):
1109     mock_input_api = MockInputApi()
1110     mock_output_api = MockOutputApi()
1111
1112     mock_input_api.files = [
1113         MockAffectedFile('LalaLand.java', [
1114           'random stuff'
1115         ]),
1116         MockAffectedFile('CorrectTest.java', [
1117           '@RunWith(ABC.class);'
1118           'public class CorrectTest {',
1119           '}',
1120         ]),
1121         MockAffectedFile('HistoricallyIncorrectTest.java', [
1122           'public class Test extends BaseCaseA {',
1123           '}',
1124         ], old_contents=[
1125           'public class Test extends BaseCaseB {',
1126           '}',
1127         ]),
1128         MockAffectedFile('CorrectTestWithInterface.java', [
1129           '@RunWith(ABC.class);'
1130           'public class CorrectTest implement Interface {',
1131           '}',
1132         ]),
1133         MockAffectedFile('IncorrectTest.java', [
1134           'public class IncorrectTest extends TestCase {',
1135           '}',
1136         ]),
1137         MockAffectedFile('IncorrectWithInterfaceTest.java', [
1138           'public class Test implements X extends BaseClass {',
1139           '}',
1140         ]),
1141         MockAffectedFile('IncorrectMultiLineTest.java', [
1142           'public class Test implements X, Y, Z',
1143           '        extends TestBase {',
1144           '}',
1145         ]),
1146     ]
1147     msgs = PRESUBMIT._CheckAndroidTestJUnitInheritance(
1148         mock_input_api, mock_output_api)
1149     self.assertEqual(1, len(msgs),
1150                      'Expected %d items, found %d: %s'
1151                      % (1, len(msgs), msgs))
1152     self.assertEqual(3, len(msgs[0].items),
1153                      'Expected %d items, found %d: %s'
1154                      % (3, len(msgs[0].items), msgs[0].items))
1155     self.assertTrue('IncorrectTest.java:1' in msgs[0].items,
1156                     'IncorrectTest not found in errors')
1157     self.assertTrue('IncorrectWithInterfaceTest.java:1'
1158                     in msgs[0].items,
1159                     'IncorrectWithInterfaceTest not found in errors')
1160     self.assertTrue('IncorrectMultiLineTest.java:2' in msgs[0].items,
1161                     'IncorrectMultiLineTest not found in errors')
1162
1163 class AndroidDebuggableBuildTest(unittest.TestCase):
1164
1165   def testCheckAndroidDebuggableBuild(self):
1166     mock_input_api = MockInputApi()
1167     mock_output_api = MockOutputApi()
1168
1169     mock_input_api.files = [
1170       MockAffectedFile('RandomStuff.java', [
1171         'random stuff'
1172       ]),
1173       MockAffectedFile('CorrectUsage.java', [
1174         'import org.chromium.base.BuildInfo;',
1175         'some random stuff',
1176         'boolean isOsDebuggable = BuildInfo.isDebugAndroid();',
1177       ]),
1178       MockAffectedFile('JustCheckUserdebugBuild.java', [
1179         'import android.os.Build;',
1180         'some random stuff',
1181         'boolean isOsDebuggable = Build.TYPE.equals("userdebug")',
1182       ]),
1183       MockAffectedFile('JustCheckEngineeringBuild.java', [
1184         'import android.os.Build;',
1185         'some random stuff',
1186         'boolean isOsDebuggable = "eng".equals(Build.TYPE)',
1187       ]),
1188       MockAffectedFile('UsedBuildType.java', [
1189         'import android.os.Build;',
1190         'some random stuff',
1191         'boolean isOsDebuggable = Build.TYPE.equals("userdebug")'
1192             '|| "eng".equals(Build.TYPE)',
1193       ]),
1194       MockAffectedFile('UsedExplicitBuildType.java', [
1195         'some random stuff',
1196         'boolean isOsDebuggable = android.os.Build.TYPE.equals("userdebug")'
1197             '|| "eng".equals(android.os.Build.TYPE)',
1198       ]),
1199     ]
1200
1201     msgs = PRESUBMIT._CheckAndroidDebuggableBuild(
1202         mock_input_api, mock_output_api)
1203     self.assertEqual(1, len(msgs),
1204                      'Expected %d items, found %d: %s'
1205                      % (1, len(msgs), msgs))
1206     self.assertEqual(4, len(msgs[0].items),
1207                      'Expected %d items, found %d: %s'
1208                      % (4, len(msgs[0].items), msgs[0].items))
1209     self.assertTrue('JustCheckUserdebugBuild.java:3' in msgs[0].items)
1210     self.assertTrue('JustCheckEngineeringBuild.java:3' in msgs[0].items)
1211     self.assertTrue('UsedBuildType.java:3' in msgs[0].items)
1212     self.assertTrue('UsedExplicitBuildType.java:2' in msgs[0].items)
1213
1214
1215 class LogUsageTest(unittest.TestCase):
1216
1217   def testCheckAndroidCrLogUsage(self):
1218     mock_input_api = MockInputApi()
1219     mock_output_api = MockOutputApi()
1220
1221     mock_input_api.files = [
1222       MockAffectedFile('RandomStuff.java', [
1223         'random stuff'
1224       ]),
1225       MockAffectedFile('HasAndroidLog.java', [
1226         'import android.util.Log;',
1227         'some random stuff',
1228         'Log.d("TAG", "foo");',
1229       ]),
1230       MockAffectedFile('HasExplicitUtilLog.java', [
1231         'some random stuff',
1232         'android.util.Log.d("TAG", "foo");',
1233       ]),
1234       MockAffectedFile('IsInBasePackage.java', [
1235         'package org.chromium.base;',
1236         'private static final String TAG = "cr_Foo";',
1237         'Log.d(TAG, "foo");',
1238       ]),
1239       MockAffectedFile('IsInBasePackageButImportsLog.java', [
1240         'package org.chromium.base;',
1241         'import android.util.Log;',
1242         'private static final String TAG = "cr_Foo";',
1243         'Log.d(TAG, "foo");',
1244       ]),
1245       MockAffectedFile('HasBothLog.java', [
1246         'import org.chromium.base.Log;',
1247         'some random stuff',
1248         'private static final String TAG = "cr_Foo";',
1249         'Log.d(TAG, "foo");',
1250         'android.util.Log.d("TAG", "foo");',
1251       ]),
1252       MockAffectedFile('HasCorrectTag.java', [
1253         'import org.chromium.base.Log;',
1254         'some random stuff',
1255         'private static final String TAG = "cr_Foo";',
1256         'Log.d(TAG, "foo");',
1257       ]),
1258       MockAffectedFile('HasOldTag.java', [
1259         'import org.chromium.base.Log;',
1260         'some random stuff',
1261         'private static final String TAG = "cr.Foo";',
1262         'Log.d(TAG, "foo");',
1263       ]),
1264       MockAffectedFile('HasDottedTag.java', [
1265         'import org.chromium.base.Log;',
1266         'some random stuff',
1267         'private static final String TAG = "cr_foo.bar";',
1268         'Log.d(TAG, "foo");',
1269       ]),
1270       MockAffectedFile('HasNoTagDecl.java', [
1271         'import org.chromium.base.Log;',
1272         'some random stuff',
1273         'Log.d(TAG, "foo");',
1274       ]),
1275       MockAffectedFile('HasIncorrectTagDecl.java', [
1276         'import org.chromium.base.Log;',
1277         'private static final String TAHG = "cr_Foo";',
1278         'some random stuff',
1279         'Log.d(TAG, "foo");',
1280       ]),
1281       MockAffectedFile('HasInlineTag.java', [
1282         'import org.chromium.base.Log;',
1283         'some random stuff',
1284         'private static final String TAG = "cr_Foo";',
1285         'Log.d("TAG", "foo");',
1286       ]),
1287       MockAffectedFile('HasUnprefixedTag.java', [
1288         'import org.chromium.base.Log;',
1289         'some random stuff',
1290         'private static final String TAG = "rubbish";',
1291         'Log.d(TAG, "foo");',
1292       ]),
1293       MockAffectedFile('HasTooLongTag.java', [
1294         'import org.chromium.base.Log;',
1295         'some random stuff',
1296         'private static final String TAG = "21_charachers_long___";',
1297         'Log.d(TAG, "foo");',
1298       ]),
1299     ]
1300
1301     msgs = PRESUBMIT._CheckAndroidCrLogUsage(
1302         mock_input_api, mock_output_api)
1303
1304     self.assertEqual(5, len(msgs),
1305                      'Expected %d items, found %d: %s' % (5, len(msgs), msgs))
1306
1307     # Declaration format
1308     nb = len(msgs[0].items)
1309     self.assertEqual(2, nb,
1310                      'Expected %d items, found %d: %s' % (2, nb, msgs[0].items))
1311     self.assertTrue('HasNoTagDecl.java' in msgs[0].items)
1312     self.assertTrue('HasIncorrectTagDecl.java' in msgs[0].items)
1313
1314     # Tag length
1315     nb = len(msgs[1].items)
1316     self.assertEqual(1, nb,
1317                      'Expected %d items, found %d: %s' % (1, nb, msgs[1].items))
1318     self.assertTrue('HasTooLongTag.java' in msgs[1].items)
1319
1320     # Tag must be a variable named TAG
1321     nb = len(msgs[2].items)
1322     self.assertEqual(1, nb,
1323                      'Expected %d items, found %d: %s' % (1, nb, msgs[2].items))
1324     self.assertTrue('HasInlineTag.java:4' in msgs[2].items)
1325
1326     # Util Log usage
1327     nb = len(msgs[3].items)
1328     self.assertEqual(2, nb,
1329                      'Expected %d items, found %d: %s' % (2, nb, msgs[3].items))
1330     self.assertTrue('HasAndroidLog.java:3' in msgs[3].items)
1331     self.assertTrue('IsInBasePackageButImportsLog.java:4' in msgs[3].items)
1332
1333     # Tag must not contain
1334     nb = len(msgs[4].items)
1335     self.assertEqual(2, nb,
1336                      'Expected %d items, found %d: %s' % (2, nb, msgs[4].items))
1337     self.assertTrue('HasDottedTag.java' in msgs[4].items)
1338     self.assertTrue('HasOldTag.java' in msgs[4].items)
1339
1340
1341 class GoogleAnswerUrlFormatTest(unittest.TestCase):
1342
1343   def testCatchAnswerUrlId(self):
1344     input_api = MockInputApi()
1345     input_api.files = [
1346       MockFile('somewhere/file.cc',
1347                ['char* host = '
1348                 '  "https://support.google.com/chrome/answer/123456";']),
1349       MockFile('somewhere_else/file.cc',
1350                ['char* host = '
1351                 '  "https://support.google.com/chrome/a/answer/123456";']),
1352     ]
1353
1354     warnings = PRESUBMIT._CheckGoogleSupportAnswerUrl(
1355       input_api, MockOutputApi())
1356     self.assertEqual(1, len(warnings))
1357     self.assertEqual(2, len(warnings[0].items))
1358
1359   def testAllowAnswerUrlParam(self):
1360     input_api = MockInputApi()
1361     input_api.files = [
1362       MockFile('somewhere/file.cc',
1363                ['char* host = '
1364                 '  "https://support.google.com/chrome/?p=cpn_crash_reports";']),
1365     ]
1366
1367     warnings = PRESUBMIT._CheckGoogleSupportAnswerUrl(
1368       input_api, MockOutputApi())
1369     self.assertEqual(0, len(warnings))
1370
1371
1372 class HardcodedGoogleHostsTest(unittest.TestCase):
1373
1374   def testWarnOnAssignedLiterals(self):
1375     input_api = MockInputApi()
1376     input_api.files = [
1377       MockFile('content/file.cc',
1378                ['char* host = "https://www.google.com";']),
1379       MockFile('content/file.cc',
1380                ['char* host = "https://www.googleapis.com";']),
1381       MockFile('content/file.cc',
1382                ['char* host = "https://clients1.google.com";']),
1383     ]
1384
1385     warnings = PRESUBMIT._CheckHardcodedGoogleHostsInLowerLayers(
1386       input_api, MockOutputApi())
1387     self.assertEqual(1, len(warnings))
1388     self.assertEqual(3, len(warnings[0].items))
1389
1390   def testAllowInComment(self):
1391     input_api = MockInputApi()
1392     input_api.files = [
1393       MockFile('content/file.cc',
1394                ['char* host = "https://www.aol.com"; // google.com'])
1395     ]
1396
1397     warnings = PRESUBMIT._CheckHardcodedGoogleHostsInLowerLayers(
1398       input_api, MockOutputApi())
1399     self.assertEqual(0, len(warnings))
1400
1401
1402 class ForwardDeclarationTest(unittest.TestCase):
1403   def testCheckHeadersOnlyOutsideThirdParty(self):
1404     mock_input_api = MockInputApi()
1405     mock_input_api.files = [
1406       MockAffectedFile('somewhere/file.cc', [
1407         'class DummyClass;'
1408       ]),
1409       MockAffectedFile('third_party/header.h', [
1410         'class DummyClass;'
1411       ])
1412     ]
1413     warnings = PRESUBMIT._CheckUselessForwardDeclarations(mock_input_api,
1414                                                           MockOutputApi())
1415     self.assertEqual(0, len(warnings))
1416
1417   def testNoNestedDeclaration(self):
1418     mock_input_api = MockInputApi()
1419     mock_input_api.files = [
1420       MockAffectedFile('somewhere/header.h', [
1421         'class SomeClass {',
1422         ' protected:',
1423         '  class NotAMatch;',
1424         '};'
1425       ])
1426     ]
1427     warnings = PRESUBMIT._CheckUselessForwardDeclarations(mock_input_api,
1428                                                           MockOutputApi())
1429     self.assertEqual(0, len(warnings))
1430
1431   def testSubStrings(self):
1432     mock_input_api = MockInputApi()
1433     mock_input_api.files = [
1434       MockAffectedFile('somewhere/header.h', [
1435         'class NotUsefulClass;',
1436         'struct SomeStruct;',
1437         'UsefulClass *p1;',
1438         'SomeStructPtr *p2;'
1439       ])
1440     ]
1441     warnings = PRESUBMIT._CheckUselessForwardDeclarations(mock_input_api,
1442                                                           MockOutputApi())
1443     self.assertEqual(2, len(warnings))
1444
1445   def testUselessForwardDeclaration(self):
1446     mock_input_api = MockInputApi()
1447     mock_input_api.files = [
1448       MockAffectedFile('somewhere/header.h', [
1449         'class DummyClass;',
1450         'struct DummyStruct;',
1451         'class UsefulClass;',
1452         'std::unique_ptr<UsefulClass> p;'
1453       ])
1454     ]
1455     warnings = PRESUBMIT._CheckUselessForwardDeclarations(mock_input_api,
1456                                                           MockOutputApi())
1457     self.assertEqual(2, len(warnings))
1458
1459   def testBlinkHeaders(self):
1460     mock_input_api = MockInputApi()
1461     mock_input_api.files = [
1462       MockAffectedFile('third_party/blink/header.h', [
1463         'class DummyClass;',
1464         'struct DummyStruct;',
1465       ]),
1466       MockAffectedFile('third_party\\blink\\header.h', [
1467         'class DummyClass;',
1468         'struct DummyStruct;',
1469       ])
1470     ]
1471     warnings = PRESUBMIT._CheckUselessForwardDeclarations(mock_input_api,
1472                                                           MockOutputApi())
1473     self.assertEqual(4, len(warnings))
1474
1475
1476 class RelativeIncludesTest(unittest.TestCase):
1477   def testThirdPartyNotWebKitIgnored(self):
1478     mock_input_api = MockInputApi()
1479     mock_input_api.files = [
1480       MockAffectedFile('third_party/test.cpp', '#include "../header.h"'),
1481       MockAffectedFile('third_party/test/test.cpp', '#include "../header.h"'),
1482     ]
1483
1484     mock_output_api = MockOutputApi()
1485
1486     errors = PRESUBMIT._CheckForRelativeIncludes(
1487         mock_input_api, mock_output_api)
1488     self.assertEqual(0, len(errors))
1489
1490   def testNonCppFileIgnored(self):
1491     mock_input_api = MockInputApi()
1492     mock_input_api.files = [
1493       MockAffectedFile('test.py', '#include "../header.h"'),
1494     ]
1495
1496     mock_output_api = MockOutputApi()
1497
1498     errors = PRESUBMIT._CheckForRelativeIncludes(
1499         mock_input_api, mock_output_api)
1500     self.assertEqual(0, len(errors))
1501
1502   def testInnocuousChangesAllowed(self):
1503     mock_input_api = MockInputApi()
1504     mock_input_api.files = [
1505       MockAffectedFile('test.cpp', '#include "header.h"'),
1506       MockAffectedFile('test2.cpp', '../'),
1507     ]
1508
1509     mock_output_api = MockOutputApi()
1510
1511     errors = PRESUBMIT._CheckForRelativeIncludes(
1512         mock_input_api, mock_output_api)
1513     self.assertEqual(0, len(errors))
1514
1515   def testRelativeIncludeNonWebKitProducesError(self):
1516     mock_input_api = MockInputApi()
1517     mock_input_api.files = [
1518       MockAffectedFile('test.cpp', ['#include "../header.h"']),
1519     ]
1520
1521     mock_output_api = MockOutputApi()
1522
1523     errors = PRESUBMIT._CheckForRelativeIncludes(
1524         mock_input_api, mock_output_api)
1525     self.assertEqual(1, len(errors))
1526
1527   def testRelativeIncludeWebKitProducesError(self):
1528     mock_input_api = MockInputApi()
1529     mock_input_api.files = [
1530       MockAffectedFile('third_party/blink/test.cpp',
1531                        ['#include "../header.h']),
1532     ]
1533
1534     mock_output_api = MockOutputApi()
1535
1536     errors = PRESUBMIT._CheckForRelativeIncludes(
1537         mock_input_api, mock_output_api)
1538     self.assertEqual(1, len(errors))
1539
1540
1541 class CCIncludeTest(unittest.TestCase):
1542   def testThirdPartyNotBlinkIgnored(self):
1543     mock_input_api = MockInputApi()
1544     mock_input_api.files = [
1545       MockAffectedFile('third_party/test.cpp', '#include "file.cc"'),
1546     ]
1547
1548     mock_output_api = MockOutputApi()
1549
1550     errors = PRESUBMIT._CheckForCcIncludes(
1551         mock_input_api, mock_output_api)
1552     self.assertEqual(0, len(errors))
1553
1554   def testPythonFileIgnored(self):
1555     mock_input_api = MockInputApi()
1556     mock_input_api.files = [
1557       MockAffectedFile('test.py', '#include "file.cc"'),
1558     ]
1559
1560     mock_output_api = MockOutputApi()
1561
1562     errors = PRESUBMIT._CheckForCcIncludes(
1563         mock_input_api, mock_output_api)
1564     self.assertEqual(0, len(errors))
1565
1566   def testIncFilesAccepted(self):
1567     mock_input_api = MockInputApi()
1568     mock_input_api.files = [
1569       MockAffectedFile('test.py', '#include "file.inc"'),
1570     ]
1571
1572     mock_output_api = MockOutputApi()
1573
1574     errors = PRESUBMIT._CheckForCcIncludes(
1575         mock_input_api, mock_output_api)
1576     self.assertEqual(0, len(errors))
1577
1578   def testInnocuousChangesAllowed(self):
1579     mock_input_api = MockInputApi()
1580     mock_input_api.files = [
1581       MockAffectedFile('test.cpp', '#include "header.h"'),
1582       MockAffectedFile('test2.cpp', 'Something "file.cc"'),
1583     ]
1584
1585     mock_output_api = MockOutputApi()
1586
1587     errors = PRESUBMIT._CheckForCcIncludes(
1588         mock_input_api, mock_output_api)
1589     self.assertEqual(0, len(errors))
1590
1591   def testCcIncludeNonBlinkProducesError(self):
1592     mock_input_api = MockInputApi()
1593     mock_input_api.files = [
1594       MockAffectedFile('test.cpp', ['#include "file.cc"']),
1595     ]
1596
1597     mock_output_api = MockOutputApi()
1598
1599     errors = PRESUBMIT._CheckForCcIncludes(
1600         mock_input_api, mock_output_api)
1601     self.assertEqual(1, len(errors))
1602
1603   def testCppIncludeBlinkProducesError(self):
1604     mock_input_api = MockInputApi()
1605     mock_input_api.files = [
1606       MockAffectedFile('third_party/blink/test.cpp',
1607                        ['#include "foo/file.cpp"']),
1608     ]
1609
1610     mock_output_api = MockOutputApi()
1611
1612     errors = PRESUBMIT._CheckForCcIncludes(
1613         mock_input_api, mock_output_api)
1614     self.assertEqual(1, len(errors))
1615
1616
1617 class NewHeaderWithoutGnChangeTest(unittest.TestCase):
1618   def testAddHeaderWithoutGn(self):
1619     mock_input_api = MockInputApi()
1620     mock_input_api.files = [
1621       MockAffectedFile('base/stuff.h', ''),
1622     ]
1623     warnings = PRESUBMIT._CheckNewHeaderWithoutGnChange(
1624         mock_input_api, MockOutputApi())
1625     self.assertEqual(1, len(warnings))
1626     self.assertTrue('base/stuff.h' in warnings[0].items)
1627
1628   def testModifyHeader(self):
1629     mock_input_api = MockInputApi()
1630     mock_input_api.files = [
1631       MockAffectedFile('base/stuff.h', '', action='M'),
1632     ]
1633     warnings = PRESUBMIT._CheckNewHeaderWithoutGnChange(
1634         mock_input_api, MockOutputApi())
1635     self.assertEqual(0, len(warnings))
1636
1637   def testDeleteHeader(self):
1638     mock_input_api = MockInputApi()
1639     mock_input_api.files = [
1640       MockAffectedFile('base/stuff.h', '', action='D'),
1641     ]
1642     warnings = PRESUBMIT._CheckNewHeaderWithoutGnChange(
1643         mock_input_api, MockOutputApi())
1644     self.assertEqual(0, len(warnings))
1645
1646   def testAddHeaderWithGn(self):
1647     mock_input_api = MockInputApi()
1648     mock_input_api.files = [
1649       MockAffectedFile('base/stuff.h', ''),
1650       MockAffectedFile('base/BUILD.gn', 'stuff.h'),
1651     ]
1652     warnings = PRESUBMIT._CheckNewHeaderWithoutGnChange(
1653         mock_input_api, MockOutputApi())
1654     self.assertEqual(0, len(warnings))
1655
1656   def testAddHeaderWithGni(self):
1657     mock_input_api = MockInputApi()
1658     mock_input_api.files = [
1659       MockAffectedFile('base/stuff.h', ''),
1660       MockAffectedFile('base/files.gni', 'stuff.h'),
1661     ]
1662     warnings = PRESUBMIT._CheckNewHeaderWithoutGnChange(
1663         mock_input_api, MockOutputApi())
1664     self.assertEqual(0, len(warnings))
1665
1666   def testAddHeaderWithOther(self):
1667     mock_input_api = MockInputApi()
1668     mock_input_api.files = [
1669       MockAffectedFile('base/stuff.h', ''),
1670       MockAffectedFile('base/stuff.cc', 'stuff.h'),
1671     ]
1672     warnings = PRESUBMIT._CheckNewHeaderWithoutGnChange(
1673         mock_input_api, MockOutputApi())
1674     self.assertEqual(1, len(warnings))
1675
1676   def testAddHeaderWithWrongGn(self):
1677     mock_input_api = MockInputApi()
1678     mock_input_api.files = [
1679       MockAffectedFile('base/stuff.h', ''),
1680       MockAffectedFile('base/BUILD.gn', 'stuff_h'),
1681     ]
1682     warnings = PRESUBMIT._CheckNewHeaderWithoutGnChange(
1683         mock_input_api, MockOutputApi())
1684     self.assertEqual(1, len(warnings))
1685
1686   def testAddHeadersWithGn(self):
1687     mock_input_api = MockInputApi()
1688     mock_input_api.files = [
1689       MockAffectedFile('base/stuff.h', ''),
1690       MockAffectedFile('base/another.h', ''),
1691       MockAffectedFile('base/BUILD.gn', 'another.h\nstuff.h'),
1692     ]
1693     warnings = PRESUBMIT._CheckNewHeaderWithoutGnChange(
1694         mock_input_api, MockOutputApi())
1695     self.assertEqual(0, len(warnings))
1696
1697   def testAddHeadersWithWrongGn(self):
1698     mock_input_api = MockInputApi()
1699     mock_input_api.files = [
1700       MockAffectedFile('base/stuff.h', ''),
1701       MockAffectedFile('base/another.h', ''),
1702       MockAffectedFile('base/BUILD.gn', 'another_h\nstuff.h'),
1703     ]
1704     warnings = PRESUBMIT._CheckNewHeaderWithoutGnChange(
1705         mock_input_api, MockOutputApi())
1706     self.assertEqual(1, len(warnings))
1707     self.assertFalse('base/stuff.h' in warnings[0].items)
1708     self.assertTrue('base/another.h' in warnings[0].items)
1709
1710   def testAddHeadersWithWrongGn2(self):
1711     mock_input_api = MockInputApi()
1712     mock_input_api.files = [
1713       MockAffectedFile('base/stuff.h', ''),
1714       MockAffectedFile('base/another.h', ''),
1715       MockAffectedFile('base/BUILD.gn', 'another_h\nstuff_h'),
1716     ]
1717     warnings = PRESUBMIT._CheckNewHeaderWithoutGnChange(
1718         mock_input_api, MockOutputApi())
1719     self.assertEqual(1, len(warnings))
1720     self.assertTrue('base/stuff.h' in warnings[0].items)
1721     self.assertTrue('base/another.h' in warnings[0].items)
1722
1723
1724 class CorrectProductNameInMessagesTest(unittest.TestCase):
1725   def testProductNameInDesc(self):
1726     mock_input_api = MockInputApi()
1727     mock_input_api.files = [
1728       MockAffectedFile('chrome/app/google_chrome_strings.grd', [
1729         '<message name="Foo" desc="Welcome to Chrome">',
1730         '  Welcome to Chrome!',
1731         '</message>',
1732       ]),
1733       MockAffectedFile('chrome/app/chromium_strings.grd', [
1734         '<message name="Bar" desc="Welcome to Chrome">',
1735         '  Welcome to Chromium!',
1736         '</message>',
1737       ]),
1738     ]
1739     warnings = PRESUBMIT._CheckCorrectProductNameInMessages(
1740         mock_input_api, MockOutputApi())
1741     self.assertEqual(0, len(warnings))
1742
1743   def testChromeInChromium(self):
1744     mock_input_api = MockInputApi()
1745     mock_input_api.files = [
1746       MockAffectedFile('chrome/app/google_chrome_strings.grd', [
1747         '<message name="Foo" desc="Welcome to Chrome">',
1748         '  Welcome to Chrome!',
1749         '</message>',
1750       ]),
1751       MockAffectedFile('chrome/app/chromium_strings.grd', [
1752         '<message name="Bar" desc="Welcome to Chrome">',
1753         '  Welcome to Chrome!',
1754         '</message>',
1755       ]),
1756     ]
1757     warnings = PRESUBMIT._CheckCorrectProductNameInMessages(
1758         mock_input_api, MockOutputApi())
1759     self.assertEqual(1, len(warnings))
1760     self.assertTrue('chrome/app/chromium_strings.grd' in warnings[0].items[0])
1761
1762   def testChromiumInChrome(self):
1763     mock_input_api = MockInputApi()
1764     mock_input_api.files = [
1765       MockAffectedFile('chrome/app/google_chrome_strings.grd', [
1766         '<message name="Foo" desc="Welcome to Chrome">',
1767         '  Welcome to Chromium!',
1768         '</message>',
1769       ]),
1770       MockAffectedFile('chrome/app/chromium_strings.grd', [
1771         '<message name="Bar" desc="Welcome to Chrome">',
1772         '  Welcome to Chromium!',
1773         '</message>',
1774       ]),
1775     ]
1776     warnings = PRESUBMIT._CheckCorrectProductNameInMessages(
1777         mock_input_api, MockOutputApi())
1778     self.assertEqual(1, len(warnings))
1779     self.assertTrue(
1780         'chrome/app/google_chrome_strings.grd:2' in warnings[0].items[0])
1781
1782   def testMultipleInstances(self):
1783     mock_input_api = MockInputApi()
1784     mock_input_api.files = [
1785       MockAffectedFile('chrome/app/chromium_strings.grd', [
1786         '<message name="Bar" desc="Welcome to Chrome">',
1787         '  Welcome to Chrome!',
1788         '</message>',
1789         '<message name="Baz" desc="A correct message">',
1790         '  Chromium is the software you are using.',
1791         '</message>',
1792         '<message name="Bat" desc="An incorrect message">',
1793         '  Google Chrome is the software you are using.',
1794         '</message>',
1795       ]),
1796     ]
1797     warnings = PRESUBMIT._CheckCorrectProductNameInMessages(
1798         mock_input_api, MockOutputApi())
1799     self.assertEqual(1, len(warnings))
1800     self.assertTrue(
1801         'chrome/app/chromium_strings.grd:2' in warnings[0].items[0])
1802     self.assertTrue(
1803         'chrome/app/chromium_strings.grd:8' in warnings[0].items[1])
1804
1805   def testMultipleWarnings(self):
1806     mock_input_api = MockInputApi()
1807     mock_input_api.files = [
1808       MockAffectedFile('chrome/app/chromium_strings.grd', [
1809         '<message name="Bar" desc="Welcome to Chrome">',
1810         '  Welcome to Chrome!',
1811         '</message>',
1812         '<message name="Baz" desc="A correct message">',
1813         '  Chromium is the software you are using.',
1814         '</message>',
1815         '<message name="Bat" desc="An incorrect message">',
1816         '  Google Chrome is the software you are using.',
1817         '</message>',
1818       ]),
1819       MockAffectedFile('components/components_google_chrome_strings.grd', [
1820         '<message name="Bar" desc="Welcome to Chrome">',
1821         '  Welcome to Chrome!',
1822         '</message>',
1823         '<message name="Baz" desc="A correct message">',
1824         '  Chromium is the software you are using.',
1825         '</message>',
1826         '<message name="Bat" desc="An incorrect message">',
1827         '  Google Chrome is the software you are using.',
1828         '</message>',
1829       ]),
1830     ]
1831     warnings = PRESUBMIT._CheckCorrectProductNameInMessages(
1832         mock_input_api, MockOutputApi())
1833     self.assertEqual(2, len(warnings))
1834     self.assertTrue(
1835         'components/components_google_chrome_strings.grd:5'
1836              in warnings[0].items[0])
1837     self.assertTrue(
1838         'chrome/app/chromium_strings.grd:2' in warnings[1].items[0])
1839     self.assertTrue(
1840         'chrome/app/chromium_strings.grd:8' in warnings[1].items[1])
1841
1842
1843 class ServiceManifestOwnerTest(unittest.TestCase):
1844   def testServiceManifestJsonChangeNeedsSecurityOwner(self):
1845     mock_input_api = MockInputApi()
1846     mock_input_api.files = [
1847       MockAffectedFile('services/goat/manifest.json',
1848                        [
1849                          '{',
1850                          '  "name": "teleporter",',
1851                          '  "display_name": "Goat Teleporter",'
1852                          '  "interface_provider_specs": {',
1853                          '  }',
1854                          '}',
1855                        ])
1856     ]
1857     mock_output_api = MockOutputApi()
1858     errors = PRESUBMIT._CheckIpcOwners(
1859         mock_input_api, mock_output_api)
1860     self.assertEqual(1, len(errors))
1861     self.assertEqual(
1862         'Found OWNERS files that need to be updated for IPC security review ' +
1863         'coverage.\nPlease update the OWNERS files below:', errors[0].message)
1864
1865     # No warning if already covered by an OWNERS rule.
1866
1867   def testNonManifestJsonChangesDoNotRequireSecurityOwner(self):
1868     mock_input_api = MockInputApi()
1869     mock_input_api.files = [
1870       MockAffectedFile('services/goat/species.json',
1871                        [
1872                          '[',
1873                          '  "anglo-nubian",',
1874                          '  "angora"',
1875                          ']',
1876                        ])
1877     ]
1878     mock_output_api = MockOutputApi()
1879     errors = PRESUBMIT._CheckIpcOwners(
1880         mock_input_api, mock_output_api)
1881     self.assertEqual([], errors)
1882
1883   def testServiceManifestChangeNeedsSecurityOwner(self):
1884     mock_input_api = MockInputApi()
1885     mock_input_api.files = [
1886       MockAffectedFile('services/goat/public/cpp/manifest.cc',
1887                        [
1888                          '#include "services/goat/public/cpp/manifest.h"',
1889                          'const service_manager::Manifest& GetManifest() {}',
1890                        ])]
1891     mock_output_api = MockOutputApi()
1892     errors = PRESUBMIT._CheckIpcOwners(
1893         mock_input_api, mock_output_api)
1894     self.assertEqual(1, len(errors))
1895     self.assertEqual(
1896         'Found OWNERS files that need to be updated for IPC security review ' +
1897         'coverage.\nPlease update the OWNERS files below:', errors[0].message)
1898
1899   def testNonServiceManifestSourceChangesDoNotRequireSecurityOwner(self):
1900     mock_input_api = MockInputApi()
1901     mock_input_api.files = [
1902       MockAffectedFile('some/non/service/thing/foo_manifest.cc',
1903                        [
1904                          'const char kNoEnforcement[] = "not a manifest!";',
1905                        ])]
1906     mock_output_api = MockOutputApi()
1907     errors = PRESUBMIT._CheckIpcOwners(
1908         mock_input_api, mock_output_api)
1909     self.assertEqual([], errors)
1910
1911
1912 class BannedFunctionCheckTest(unittest.TestCase):
1913
1914   def testBannedIosObjcFunctions(self):
1915     input_api = MockInputApi()
1916     input_api.files = [
1917       MockFile('some/ios/file.mm',
1918                ['TEST(SomeClassTest, SomeInteraction) {',
1919                 '}']),
1920       MockFile('some/mac/file.mm',
1921                ['TEST(SomeClassTest, SomeInteraction) {',
1922                 '}']),
1923       MockFile('another/ios_file.mm',
1924                ['class SomeTest : public testing::Test {};']),
1925       MockFile('some/ios/file_egtest.mm',
1926                ['- (void)testSomething { EXPECT_OCMOCK_VERIFY(aMock); }']),
1927       MockFile('some/ios/file_unittest.mm',
1928                ['TEST_F(SomeTest, TestThis) { EXPECT_OCMOCK_VERIFY(aMock); }']),
1929     ]
1930
1931     errors = PRESUBMIT._CheckNoBannedFunctions(input_api, MockOutputApi())
1932     self.assertEqual(1, len(errors))
1933     self.assertTrue('some/ios/file.mm' in errors[0].message)
1934     self.assertTrue('another/ios_file.mm' in errors[0].message)
1935     self.assertTrue('some/mac/file.mm' not in errors[0].message)
1936     self.assertTrue('some/ios/file_egtest.mm' in errors[0].message)
1937     self.assertTrue('some/ios/file_unittest.mm' not in errors[0].message)
1938
1939   def testBannedMojoFunctions(self):
1940     input_api = MockInputApi()
1941     input_api.files = [
1942       MockFile('some/cpp/problematic/file.cc',
1943                ['mojo::DataPipe();']),
1944       MockFile('some/cpp/problematic/file2.cc',
1945                ['mojo::ConvertTo<>']),
1946       MockFile('some/cpp/ok/file.cc',
1947                ['CreateDataPipe();']),
1948       MockFile('some/cpp/ok/file2.cc',
1949                ['mojo::DataPipeDrainer();']),
1950       MockFile('third_party/blink/ok/file3.cc',
1951                ['mojo::ConvertTo<>']),
1952       MockFile('content/renderer/ok/file3.cc',
1953                ['mojo::ConvertTo<>']),
1954     ]
1955
1956     results = PRESUBMIT._CheckNoBannedFunctions(input_api, MockOutputApi())
1957
1958     # warnings are results[0], errors are results[1]
1959     self.assertEqual(2, len(results))
1960     self.assertTrue('some/cpp/problematic/file.cc' in results[1].message)
1961     self.assertTrue('some/cpp/problematic/file2.cc' in results[0].message)
1962     self.assertTrue('some/cpp/ok/file.cc' not in results[1].message)
1963     self.assertTrue('some/cpp/ok/file2.cc' not in results[1].message)
1964     self.assertTrue('third_party/blink/ok/file3.cc' not in results[0].message)
1965     self.assertTrue('content/renderer/ok/file3.cc' not in results[0].message)
1966
1967
1968 class NoProductionCodeUsingTestOnlyFunctionsTest(unittest.TestCase):
1969   def testTruePositives(self):
1970     mock_input_api = MockInputApi()
1971     mock_input_api.files = [
1972       MockFile('some/path/foo.cc', ['foo_for_testing();']),
1973       MockFile('some/path/foo.mm', ['FooForTesting();']),
1974       MockFile('some/path/foo.cxx', ['FooForTests();']),
1975       MockFile('some/path/foo.cpp', ['foo_for_test();']),
1976     ]
1977
1978     results = PRESUBMIT._CheckNoProductionCodeUsingTestOnlyFunctions(
1979         mock_input_api, MockOutputApi())
1980     self.assertEqual(1, len(results))
1981     self.assertEqual(4, len(results[0].items))
1982     self.assertTrue('foo.cc' in results[0].items[0])
1983     self.assertTrue('foo.mm' in results[0].items[1])
1984     self.assertTrue('foo.cxx' in results[0].items[2])
1985     self.assertTrue('foo.cpp' in results[0].items[3])
1986
1987   def testFalsePositives(self):
1988     mock_input_api = MockInputApi()
1989     mock_input_api.files = [
1990       MockFile('some/path/foo.h', ['foo_for_testing();']),
1991       MockFile('some/path/foo.mm', ['FooForTesting() {']),
1992       MockFile('some/path/foo.cc', ['::FooForTests();']),
1993       MockFile('some/path/foo.cpp', ['// foo_for_test();']),
1994     ]
1995
1996     results = PRESUBMIT._CheckNoProductionCodeUsingTestOnlyFunctions(
1997         mock_input_api, MockOutputApi())
1998     self.assertEqual(0, len(results))
1999
2000
2001 class NoProductionJavaCodeUsingTestOnlyFunctionsTest(unittest.TestCase):
2002   def testTruePositives(self):
2003     mock_input_api = MockInputApi()
2004     mock_input_api.files = [
2005       MockFile('dir/java/src/foo.java', ['FooForTesting();']),
2006       MockFile('dir/java/src/bar.java', ['FooForTests(x);']),
2007       MockFile('dir/java/src/baz.java', ['FooForTest(', 'y', ');']),
2008       MockFile('dir/java/src/mult.java', [
2009         'int x = SomethingLongHere()',
2010         '    * SomethingLongHereForTesting();'
2011       ])
2012     ]
2013
2014     results = PRESUBMIT._CheckNoProductionCodeUsingTestOnlyFunctionsJava(
2015         mock_input_api, MockOutputApi())
2016     self.assertEqual(1, len(results))
2017     self.assertEqual(4, len(results[0].items))
2018     self.assertTrue('foo.java' in results[0].items[0])
2019     self.assertTrue('bar.java' in results[0].items[1])
2020     self.assertTrue('baz.java' in results[0].items[2])
2021     self.assertTrue('mult.java' in results[0].items[3])
2022
2023   def testFalsePositives(self):
2024     mock_input_api = MockInputApi()
2025     mock_input_api.files = [
2026       MockFile('dir/java/src/foo.xml', ['FooForTesting();']),
2027       MockFile('dir/java/src/foo.java', ['FooForTests() {']),
2028       MockFile('dir/java/src/bar.java', ['// FooForTest();']),
2029       MockFile('dir/java/src/bar2.java', ['x = 1; // FooForTest();']),
2030       MockFile('dir/javatests/src/baz.java', ['FooForTest(', 'y', ');']),
2031       MockFile('dir/junit/src/baz.java', ['FooForTest(', 'y', ');']),
2032       MockFile('dir/junit/src/javadoc.java', [
2033         '/** Use FooForTest(); to obtain foo in tests.'
2034         ' */'
2035       ]),
2036       MockFile('dir/junit/src/javadoc2.java', [
2037         '/** ',
2038         ' * Use FooForTest(); to obtain foo in tests.'
2039         ' */'
2040       ]),
2041     ]
2042
2043     results = PRESUBMIT._CheckNoProductionCodeUsingTestOnlyFunctionsJava(
2044         mock_input_api, MockOutputApi())
2045     self.assertEqual(0, len(results))
2046
2047
2048 class CheckUniquePtrTest(unittest.TestCase):
2049   def testTruePositivesNullptr(self):
2050     mock_input_api = MockInputApi()
2051     mock_input_api.files = [
2052       MockFile('dir/baz.cc', ['std::unique_ptr<T>()']),
2053       MockFile('dir/baz-p.cc', ['std::unique_ptr<T<P>>()']),
2054     ]
2055
2056     results = PRESUBMIT._CheckUniquePtr(mock_input_api, MockOutputApi())
2057     self.assertEqual(1, len(results))
2058     self.assertTrue('nullptr' in results[0].message)
2059     self.assertEqual(2, len(results[0].items))
2060     self.assertTrue('baz.cc' in results[0].items[0])
2061     self.assertTrue('baz-p.cc' in results[0].items[1])
2062
2063   def testTruePositivesConstructor(self):
2064     mock_input_api = MockInputApi()
2065     mock_input_api.files = [
2066       MockFile('dir/foo.cc', ['return std::unique_ptr<T>(foo);']),
2067       MockFile('dir/bar.mm', ['bar = std::unique_ptr<T>(foo)']),
2068       MockFile('dir/mult.cc', [
2069         'return',
2070         '    std::unique_ptr<T>(barVeryVeryLongFooSoThatItWouldNotFitAbove);'
2071       ]),
2072       MockFile('dir/mult2.cc', [
2073         'barVeryVeryLongLongBaaaaaarSoThatTheLineLimitIsAlmostReached =',
2074         '    std::unique_ptr<T>(foo);'
2075       ]),
2076       MockFile('dir/mult3.cc', [
2077         'bar = std::unique_ptr<T>(',
2078         '    fooVeryVeryVeryLongStillGoingWellThisWillTakeAWhileFinallyThere);'
2079       ]),
2080       MockFile('dir/multi_arg.cc', [
2081           'auto p = std::unique_ptr<std::pair<T, D>>(new std::pair(T, D));']),
2082     ]
2083
2084     results = PRESUBMIT._CheckUniquePtr(mock_input_api, MockOutputApi())
2085     self.assertEqual(1, len(results))
2086     self.assertTrue('std::make_unique' in results[0].message)
2087     self.assertEqual(6, len(results[0].items))
2088     self.assertTrue('foo.cc' in results[0].items[0])
2089     self.assertTrue('bar.mm' in results[0].items[1])
2090     self.assertTrue('mult.cc' in results[0].items[2])
2091     self.assertTrue('mult2.cc' in results[0].items[3])
2092     self.assertTrue('mult3.cc' in results[0].items[4])
2093     self.assertTrue('multi_arg.cc' in results[0].items[5])
2094
2095   def testFalsePositives(self):
2096     mock_input_api = MockInputApi()
2097     mock_input_api.files = [
2098       MockFile('dir/foo.cc', ['return std::unique_ptr<T[]>(foo);']),
2099       MockFile('dir/bar.mm', ['bar = std::unique_ptr<T[]>(foo)']),
2100       MockFile('dir/file.cc', ['std::unique_ptr<T> p = Foo();']),
2101       MockFile('dir/baz.cc', [
2102         'std::unique_ptr<T> result = std::make_unique<T>();'
2103       ]),
2104       MockFile('dir/baz2.cc', [
2105         'std::unique_ptr<T> result = std::make_unique<T>('
2106       ]),
2107       MockFile('dir/nested.cc', ['set<std::unique_ptr<T>>();']),
2108       MockFile('dir/nested2.cc', ['map<U, std::unique_ptr<T>>();']),
2109
2110       # Two-argument invocation of std::unique_ptr is exempt because there is
2111       # no equivalent using std::make_unique.
2112       MockFile('dir/multi_arg.cc', [
2113         'auto p = std::unique_ptr<T, D>(new T(), D());']),
2114     ]
2115
2116     results = PRESUBMIT._CheckUniquePtr(mock_input_api, MockOutputApi())
2117     self.assertEqual(0, len(results))
2118
2119 class CheckNoDirectIncludesHeadersWhichRedefineStrCat(unittest.TestCase):
2120   def testBlocksDirectIncludes(self):
2121     mock_input_api = MockInputApi()
2122     mock_input_api.files = [
2123       MockFile('dir/foo_win.cc', ['#include "shlwapi.h"']),
2124       MockFile('dir/bar.h', ['#include <propvarutil.h>']),
2125       MockFile('dir/baz.h', ['#include <atlbase.h>']),
2126       MockFile('dir/jumbo.h', ['#include "sphelper.h"']),
2127     ]
2128     results = PRESUBMIT._CheckNoStrCatRedefines(mock_input_api, MockOutputApi())
2129     self.assertEquals(1, len(results))
2130     self.assertEquals(4, len(results[0].items))
2131     self.assertTrue('StrCat' in results[0].message)
2132     self.assertTrue('foo_win.cc' in results[0].items[0])
2133     self.assertTrue('bar.h' in results[0].items[1])
2134     self.assertTrue('baz.h' in results[0].items[2])
2135     self.assertTrue('jumbo.h' in results[0].items[3])
2136
2137   def testAllowsToIncludeWrapper(self):
2138     mock_input_api = MockInputApi()
2139     mock_input_api.files = [
2140       MockFile('dir/baz_win.cc', ['#include "base/win/shlwapi.h"']),
2141       MockFile('dir/baz-win.h', ['#include "base/win/atl.h"']),
2142     ]
2143     results = PRESUBMIT._CheckNoStrCatRedefines(mock_input_api, MockOutputApi())
2144     self.assertEquals(0, len(results))
2145
2146   def testAllowsToCreateWrapper(self):
2147     mock_input_api = MockInputApi()
2148     mock_input_api.files = [
2149       MockFile('base/win/shlwapi.h', [
2150         '#include <shlwapi.h>',
2151         '#include "base/win/windows_defines.inc"']),
2152     ]
2153     results = PRESUBMIT._CheckNoStrCatRedefines(mock_input_api, MockOutputApi())
2154     self.assertEquals(0, len(results))
2155
2156 class TranslationScreenshotsTest(unittest.TestCase):
2157   # An empty grd file.
2158   OLD_GRD_CONTENTS = """<?xml version="1.0" encoding="UTF-8"?>
2159            <grit latest_public_release="1" current_release="1">
2160              <release seq="1">
2161                <messages></messages>
2162              </release>
2163            </grit>
2164         """.splitlines()
2165   # A grd file with a single message.
2166   NEW_GRD_CONTENTS1 = """<?xml version="1.0" encoding="UTF-8"?>
2167            <grit latest_public_release="1" current_release="1">
2168              <release seq="1">
2169                <messages>
2170                  <message name="IDS_TEST1">
2171                    Test string 1
2172                  </message>
2173                </messages>
2174              </release>
2175            </grit>
2176         """.splitlines()
2177   # A grd file with two messages.
2178   NEW_GRD_CONTENTS2 = """<?xml version="1.0" encoding="UTF-8"?>
2179            <grit latest_public_release="1" current_release="1">
2180              <release seq="1">
2181                <messages>
2182                  <message name="IDS_TEST1">
2183                    Test string 1
2184                  </message>
2185                  <message name="IDS_TEST2">
2186                    Test string 2
2187                  </message>
2188                </messages>
2189              </release>
2190            </grit>
2191         """.splitlines()
2192
2193   DO_NOT_UPLOAD_PNG_MESSAGE = ('Do not include actual screenshots in the '
2194                                'changelist. Run '
2195                                'tools/translate/upload_screenshots.py to '
2196                                'upload them instead:')
2197   GENERATE_SIGNATURES_MESSAGE = ('You are adding or modifying UI strings.\n'
2198                                  'To ensure the best translations, take '
2199                                  'screenshots of the relevant UI '
2200                                  '(https://g.co/chrome/translation) and add '
2201                                  'these files to your changelist:')
2202   REMOVE_SIGNATURES_MESSAGE = ('You removed strings associated with these '
2203                                'files. Remove:')
2204
2205   def makeInputApi(self, files):
2206     input_api = MockInputApi()
2207     input_api.files = files
2208     return input_api
2209
2210   def testNoScreenshots(self):
2211     # CL modified and added messages, but didn't add any screenshots.
2212     input_api = self.makeInputApi([
2213       MockAffectedFile('test.grd', self.NEW_GRD_CONTENTS2,
2214                        self.OLD_GRD_CONTENTS, action='M')])
2215     warnings = PRESUBMIT._CheckTranslationScreenshots(input_api,
2216                                                       MockOutputApi())
2217     self.assertEqual(1, len(warnings))
2218     self.assertEqual(self.GENERATE_SIGNATURES_MESSAGE, warnings[0].message)
2219     self.assertEqual([
2220         os.path.join('test_grd', 'IDS_TEST1.png.sha1'),
2221         os.path.join('test_grd', 'IDS_TEST2.png.sha1')
2222     ], warnings[0].items)
2223
2224     input_api = self.makeInputApi([
2225       MockAffectedFile('test.grd', self.NEW_GRD_CONTENTS2,
2226                        self.NEW_GRD_CONTENTS1, action='M')])
2227     warnings = PRESUBMIT._CheckTranslationScreenshots(input_api,
2228                                                       MockOutputApi())
2229     self.assertEqual(1, len(warnings))
2230     self.assertEqual(self.GENERATE_SIGNATURES_MESSAGE, warnings[0].message)
2231     self.assertEqual([os.path.join('test_grd', 'IDS_TEST2.png.sha1')],
2232                      warnings[0].items)
2233
2234
2235   def testUnnecessaryScreenshots(self):
2236     # CL added a single message and added the png file, but not the sha1 file.
2237     input_api = self.makeInputApi([
2238         MockAffectedFile(
2239             'test.grd',
2240             self.NEW_GRD_CONTENTS1,
2241             self.OLD_GRD_CONTENTS,
2242             action='M'),
2243         MockAffectedFile(
2244             os.path.join('test_grd', 'IDS_TEST1.png'), 'binary', action='A')
2245     ])
2246     warnings = PRESUBMIT._CheckTranslationScreenshots(input_api,
2247                                                       MockOutputApi())
2248     self.assertEqual(2, len(warnings))
2249     self.assertEqual(self.DO_NOT_UPLOAD_PNG_MESSAGE, warnings[0].message)
2250     self.assertEqual([os.path.join('test_grd', 'IDS_TEST1.png')],
2251                      warnings[0].items)
2252     self.assertEqual(self.GENERATE_SIGNATURES_MESSAGE, warnings[1].message)
2253     self.assertEqual([os.path.join('test_grd', 'IDS_TEST1.png.sha1')],
2254                      warnings[1].items)
2255
2256     # CL added two messages, one has a png. Expect two messages:
2257     # - One for the unnecessary png.
2258     # - Another one for missing .sha1 files.
2259     input_api = self.makeInputApi([
2260         MockAffectedFile(
2261             'test.grd',
2262             self.NEW_GRD_CONTENTS2,
2263             self.OLD_GRD_CONTENTS,
2264             action='M'),
2265         MockAffectedFile(
2266             os.path.join('test_grd', 'IDS_TEST1.png'), 'binary', action='A')
2267     ])
2268     warnings = PRESUBMIT._CheckTranslationScreenshots(input_api,
2269                                                       MockOutputApi())
2270     self.assertEqual(2, len(warnings))
2271     self.assertEqual(self.DO_NOT_UPLOAD_PNG_MESSAGE, warnings[0].message)
2272     self.assertEqual([os.path.join('test_grd', 'IDS_TEST1.png')],
2273                      warnings[0].items)
2274     self.assertEqual(self.GENERATE_SIGNATURES_MESSAGE, warnings[1].message)
2275     self.assertEqual([
2276         os.path.join('test_grd', 'IDS_TEST1.png.sha1'),
2277         os.path.join('test_grd', 'IDS_TEST2.png.sha1')
2278     ], warnings[1].items)
2279
2280   def testScreenshotsWithSha1(self):
2281     # CL added two messages and their corresponding .sha1 files. No warnings.
2282     input_api = self.makeInputApi([
2283         MockAffectedFile(
2284             'test.grd',
2285             self.NEW_GRD_CONTENTS2,
2286             self.OLD_GRD_CONTENTS,
2287             action='M'),
2288         MockFile(
2289             os.path.join('test_grd', 'IDS_TEST1.png.sha1'),
2290             'binary',
2291             action='A'),
2292         MockFile(
2293             os.path.join('test_grd', 'IDS_TEST2.png.sha1'),
2294             'binary',
2295             action='A')
2296     ])
2297     warnings = PRESUBMIT._CheckTranslationScreenshots(input_api,
2298                                                       MockOutputApi())
2299     self.assertEqual([], warnings)
2300
2301   def testScreenshotsRemovedWithSha1(self):
2302     # Swap old contents with new contents, remove IDS_TEST1 and IDS_TEST2. The
2303     # sha1 files associated with the messages should also be removed by the CL.
2304     input_api = self.makeInputApi([
2305         MockAffectedFile(
2306             'test.grd',
2307             self.OLD_GRD_CONTENTS,
2308             self.NEW_GRD_CONTENTS2,
2309             action='M'),
2310         MockFile(os.path.join('test_grd', 'IDS_TEST1.png.sha1'), 'binary', ""),
2311         MockFile(os.path.join('test_grd', 'IDS_TEST2.png.sha1'), 'binary', "")
2312     ])
2313     warnings = PRESUBMIT._CheckTranslationScreenshots(input_api,
2314                                                       MockOutputApi())
2315     self.assertEqual(1, len(warnings))
2316     self.assertEqual(self.REMOVE_SIGNATURES_MESSAGE, warnings[0].message)
2317     self.assertEqual([
2318         os.path.join('test_grd', 'IDS_TEST1.png.sha1'),
2319         os.path.join('test_grd', 'IDS_TEST2.png.sha1')
2320     ], warnings[0].items)
2321
2322     # Same as above, but this time one of the .sha1 files is removed.
2323     input_api = self.makeInputApi([
2324         MockAffectedFile(
2325             'test.grd',
2326             self.OLD_GRD_CONTENTS,
2327             self.NEW_GRD_CONTENTS2,
2328             action='M'),
2329         MockFile(os.path.join('test_grd', 'IDS_TEST1.png.sha1'), 'binary', ''),
2330         MockAffectedFile(
2331             os.path.join('test_grd', 'IDS_TEST2.png.sha1'),
2332             '',
2333             'old_contents',
2334             action='D')
2335     ])
2336     warnings = PRESUBMIT._CheckTranslationScreenshots(input_api,
2337                                                       MockOutputApi())
2338     self.assertEqual(1, len(warnings))
2339     self.assertEqual(self.REMOVE_SIGNATURES_MESSAGE, warnings[0].message)
2340     self.assertEqual([os.path.join('test_grd', 'IDS_TEST1.png.sha1')],
2341                      warnings[0].items)
2342
2343     # Remove both sha1 files. No presubmit warnings.
2344     input_api = self.makeInputApi([
2345         MockAffectedFile(
2346             'test.grd',
2347             self.OLD_GRD_CONTENTS,
2348             self.NEW_GRD_CONTENTS2,
2349             action='M'),
2350         MockFile(
2351             os.path.join('test_grd', 'IDS_TEST1.png.sha1'),
2352             'binary',
2353             action='D'),
2354         MockFile(
2355             os.path.join('test_grd', 'IDS_TEST2.png.sha1'),
2356             'binary',
2357             action='D')
2358     ])
2359     warnings = PRESUBMIT._CheckTranslationScreenshots(input_api,
2360                                                       MockOutputApi())
2361     self.assertEqual([], warnings)
2362
2363
2364 class DISABLETypoInTest(unittest.TestCase):
2365
2366   def testPositive(self):
2367     # Verify the typo "DISABLE_" instead of "DISABLED_" in various contexts
2368     # where the desire is to disable a test.
2369     tests = [
2370         # Disabled on one platform:
2371         '#if defined(OS_WIN)\n'
2372         '#define MAYBE_FoobarTest DISABLE_FoobarTest\n'
2373         '#else\n'
2374         '#define MAYBE_FoobarTest FoobarTest\n'
2375         '#endif\n',
2376         # Disabled on one platform spread cross lines:
2377         '#if defined(OS_WIN)\n'
2378         '#define MAYBE_FoobarTest \\\n'
2379         '    DISABLE_FoobarTest\n'
2380         '#else\n'
2381         '#define MAYBE_FoobarTest FoobarTest\n'
2382         '#endif\n',
2383         # Disabled on all platforms:
2384         '  TEST_F(FoobarTest, DISABLE_Foo)\n{\n}',
2385         # Disabled on all platforms but multiple lines
2386         '  TEST_F(FoobarTest,\n   DISABLE_foo){\n}\n',
2387     ]
2388
2389     for test in tests:
2390       mock_input_api = MockInputApi()
2391       mock_input_api.files = [
2392           MockFile('some/path/foo_unittest.cc', test.splitlines()),
2393       ]
2394
2395       results = PRESUBMIT._CheckNoDISABLETypoInTests(mock_input_api,
2396                                                      MockOutputApi())
2397       self.assertEqual(
2398           1,
2399           len(results),
2400           msg=('expected len(results) == 1 but got %d in test: %s' %
2401                (len(results), test)))
2402       self.assertTrue(
2403           'foo_unittest.cc' in results[0].message,
2404           msg=('expected foo_unittest.cc in message but got %s in test %s' %
2405                (results[0].message, test)))
2406
2407   def testIngoreNotTestFiles(self):
2408     mock_input_api = MockInputApi()
2409     mock_input_api.files = [
2410         MockFile('some/path/foo.cc', 'TEST_F(FoobarTest, DISABLE_Foo)'),
2411     ]
2412
2413     results = PRESUBMIT._CheckNoDISABLETypoInTests(mock_input_api,
2414                                                    MockOutputApi())
2415     self.assertEqual(0, len(results))
2416
2417   def testIngoreDeletedFiles(self):
2418     mock_input_api = MockInputApi()
2419     mock_input_api.files = [
2420         MockFile('some/path/foo.cc', 'TEST_F(FoobarTest, Foo)', action='D'),
2421     ]
2422
2423     results = PRESUBMIT._CheckNoDISABLETypoInTests(mock_input_api,
2424                                                    MockOutputApi())
2425     self.assertEqual(0, len(results))
2426
2427
2428 class BuildtoolsRevisionsAreInSyncTest(unittest.TestCase):
2429   # TODO(crbug.com/941824): We need to make sure the entries in
2430   # //buildtools/DEPS are kept in sync with the entries in //DEPS
2431   # so that users of //buildtools in other projects get the same tooling
2432   # Chromium gets. If we ever fix the referenced bug and add 'includedeps'
2433   # support to gclient, we can eliminate the duplication and delete
2434   # these tests for the corresponding presubmit check.
2435
2436   def _check(self, files):
2437     mock_input_api = MockInputApi()
2438     mock_input_api.files = []
2439     for fname, contents in files.items():
2440       mock_input_api.files.append(MockFile(fname, contents.splitlines()))
2441     return PRESUBMIT._CheckBuildtoolsRevisionsAreInSync(mock_input_api,
2442                                                         MockOutputApi())
2443
2444   def testOneFileChangedButNotTheOther(self):
2445     results = self._check({
2446         "DEPS": "'libunwind_revision': 'onerev'",
2447     })
2448     self.assertNotEqual(results, [])
2449
2450   def testNeitherFileChanged(self):
2451     results = self._check({
2452         "OWNERS": "foobar@example.com",
2453     })
2454     self.assertEqual(results, [])
2455
2456   def testBothFilesChangedAndMatch(self):
2457     results = self._check({
2458         "DEPS": "'libunwind_revision': 'onerev'",
2459         "buildtools/DEPS": "'libunwind_revision': 'onerev'",
2460     })
2461     self.assertEqual(results, [])
2462
2463   def testBothFilesWereChangedAndDontMatch(self):
2464     results = self._check({
2465         "DEPS": "'libunwind_revision': 'onerev'",
2466         "buildtools/DEPS": "'libunwind_revision': 'anotherrev'",
2467     })
2468     self.assertNotEqual(results, [])
2469
2470
2471 class CheckFuzzTargetsTest(unittest.TestCase):
2472
2473   def _check(self, files):
2474     mock_input_api = MockInputApi()
2475     mock_input_api.files = []
2476     for fname, contents in files.items():
2477       mock_input_api.files.append(MockFile(fname, contents.splitlines()))
2478     return PRESUBMIT._CheckFuzzTargets(mock_input_api, MockOutputApi())
2479
2480   def testLibFuzzerSourcesIgnored(self):
2481     results = self._check({
2482         "third_party/lib/Fuzzer/FuzzerDriver.cpp": "LLVMFuzzerInitialize",
2483     })
2484     self.assertEqual(results, [])
2485
2486   def testNonCodeFilesIgnored(self):
2487     results = self._check({
2488         "README.md": "LLVMFuzzerInitialize",
2489     })
2490     self.assertEqual(results, [])
2491
2492   def testNoErrorHeaderPresent(self):
2493     results = self._check({
2494         "fuzzer.cc": (
2495             "#include \"testing/libfuzzer/libfuzzer_exports.h\"\n" +
2496             "LLVMFuzzerInitialize"
2497         )
2498     })
2499     self.assertEqual(results, [])
2500
2501   def testErrorMissingHeader(self):
2502     results = self._check({
2503         "fuzzer.cc": "LLVMFuzzerInitialize"
2504     })
2505     self.assertEqual(len(results), 1)
2506     self.assertEqual(results[0].items, ['fuzzer.cc'])
2507
2508
2509 if __name__ == '__main__':
2510   unittest.main()