Enable dev build with the latest repo
[platform/framework/web/chromium-efl.git] / PRESUBMIT_test.py
1 #!/usr/bin/env python
2 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
5
6 import os.path
7 import 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       ]
972     msgs = PRESUBMIT._CheckForIncludeGuards(
973         mock_input_api, mock_output_api)
974     expected_fail_count = 8
975     self.assertEqual(expected_fail_count, len(msgs),
976                      'Expected %d items, found %d: %s'
977                      % (expected_fail_count, len(msgs), msgs))
978     self.assertEqual(msgs[0].items, ['content/browser/thing/bar.h'])
979     self.assertEqual(msgs[0].message,
980                      'Include guard CONTENT_BROWSER_THING_BAR_H_ '
981                      'not covering the whole file')
982
983     self.assertEqual(msgs[1].items, ['content/browser/test1.h'])
984     self.assertEqual(msgs[1].message,
985                      'Missing include guard CONTENT_BROWSER_TEST1_H_')
986
987     self.assertEqual(msgs[2].items, ['content/browser/test2.h:3'])
988     self.assertEqual(msgs[2].message,
989                      'Missing "#define CONTENT_BROWSER_TEST2_H_" for '
990                      'include guard')
991
992     self.assertEqual(msgs[3].items, ['content/browser/spleling.h:1'])
993     self.assertEqual(msgs[3].message,
994                      'Header using the wrong include guard name '
995                      'CONTENT_BROWSER_SPLLEING_H_')
996
997     self.assertEqual(msgs[4].items, ['content/browser/foo+bar.h'])
998     self.assertEqual(msgs[4].message,
999                      'Missing include guard CONTENT_BROWSER_FOO_BAR_H_')
1000
1001     self.assertEqual(msgs[5].items, ['content/NotInBlink.h:1'])
1002     self.assertEqual(msgs[5].message,
1003                      'Header using the wrong include guard name '
1004                      'NotInBlink_h')
1005
1006     self.assertEqual(msgs[6].items, ['third_party/blink/InBlink.h:1'])
1007     self.assertEqual(msgs[6].message,
1008                      'Header using the wrong include guard name '
1009                      'InBlink_h')
1010
1011     self.assertEqual(msgs[7].items, ['third_party/blink/AlsoInBlink.h:1'])
1012     self.assertEqual(msgs[7].message,
1013                      'Header using the wrong include guard name '
1014                      'WrongInBlink_h')
1015
1016
1017 class AndroidDeprecatedTestAnnotationTest(unittest.TestCase):
1018   def testCheckAndroidTestAnnotationUsage(self):
1019     mock_input_api = MockInputApi()
1020     mock_output_api = MockOutputApi()
1021
1022     mock_input_api.files = [
1023         MockAffectedFile('LalaLand.java', [
1024           'random stuff'
1025         ]),
1026         MockAffectedFile('CorrectUsage.java', [
1027           'import android.support.test.filters.LargeTest;',
1028           'import android.support.test.filters.MediumTest;',
1029           'import android.support.test.filters.SmallTest;',
1030         ]),
1031         MockAffectedFile('UsedDeprecatedLargeTestAnnotation.java', [
1032           'import android.test.suitebuilder.annotation.LargeTest;',
1033         ]),
1034         MockAffectedFile('UsedDeprecatedMediumTestAnnotation.java', [
1035           'import android.test.suitebuilder.annotation.MediumTest;',
1036         ]),
1037         MockAffectedFile('UsedDeprecatedSmallTestAnnotation.java', [
1038           'import android.test.suitebuilder.annotation.SmallTest;',
1039         ]),
1040         MockAffectedFile('UsedDeprecatedSmokeAnnotation.java', [
1041           'import android.test.suitebuilder.annotation.Smoke;',
1042         ])
1043     ]
1044     msgs = PRESUBMIT._CheckAndroidTestAnnotationUsage(
1045         mock_input_api, mock_output_api)
1046     self.assertEqual(1, len(msgs),
1047                      'Expected %d items, found %d: %s'
1048                      % (1, len(msgs), msgs))
1049     self.assertEqual(4, len(msgs[0].items),
1050                      'Expected %d items, found %d: %s'
1051                      % (4, len(msgs[0].items), msgs[0].items))
1052     self.assertTrue('UsedDeprecatedLargeTestAnnotation.java:1' in msgs[0].items,
1053                     'UsedDeprecatedLargeTestAnnotation not found in errors')
1054     self.assertTrue('UsedDeprecatedMediumTestAnnotation.java:1'
1055                     in msgs[0].items,
1056                     'UsedDeprecatedMediumTestAnnotation not found in errors')
1057     self.assertTrue('UsedDeprecatedSmallTestAnnotation.java:1' in msgs[0].items,
1058                     'UsedDeprecatedSmallTestAnnotation not found in errors')
1059     self.assertTrue('UsedDeprecatedSmokeAnnotation.java:1' in msgs[0].items,
1060                     'UsedDeprecatedSmokeAnnotation not found in errors')
1061
1062
1063 class AndroidDeprecatedJUnitFrameworkTest(unittest.TestCase):
1064   def testCheckAndroidTestJUnitFramework(self):
1065     mock_input_api = MockInputApi()
1066     mock_output_api = MockOutputApi()
1067
1068     mock_input_api.files = [
1069         MockAffectedFile('LalaLand.java', [
1070           'random stuff'
1071         ]),
1072         MockAffectedFile('CorrectUsage.java', [
1073           'import org.junit.ABC',
1074           'import org.junit.XYZ;',
1075         ]),
1076         MockAffectedFile('UsedDeprecatedJUnit.java', [
1077           'import junit.framework.*;',
1078         ]),
1079         MockAffectedFile('UsedDeprecatedJUnitAssert.java', [
1080           'import junit.framework.Assert;',
1081         ]),
1082     ]
1083     msgs = PRESUBMIT._CheckAndroidTestJUnitFrameworkImport(
1084         mock_input_api, mock_output_api)
1085     self.assertEqual(1, len(msgs),
1086                      'Expected %d items, found %d: %s'
1087                      % (1, len(msgs), msgs))
1088     self.assertEqual(2, len(msgs[0].items),
1089                      'Expected %d items, found %d: %s'
1090                      % (2, len(msgs[0].items), msgs[0].items))
1091     self.assertTrue('UsedDeprecatedJUnit.java:1' in msgs[0].items,
1092                     'UsedDeprecatedJUnit.java not found in errors')
1093     self.assertTrue('UsedDeprecatedJUnitAssert.java:1'
1094                     in msgs[0].items,
1095                     'UsedDeprecatedJUnitAssert not found in errors')
1096
1097
1098 class AndroidJUnitBaseClassTest(unittest.TestCase):
1099   def testCheckAndroidTestJUnitBaseClass(self):
1100     mock_input_api = MockInputApi()
1101     mock_output_api = MockOutputApi()
1102
1103     mock_input_api.files = [
1104         MockAffectedFile('LalaLand.java', [
1105           'random stuff'
1106         ]),
1107         MockAffectedFile('CorrectTest.java', [
1108           '@RunWith(ABC.class);'
1109           'public class CorrectTest {',
1110           '}',
1111         ]),
1112         MockAffectedFile('HistoricallyIncorrectTest.java', [
1113           'public class Test extends BaseCaseA {',
1114           '}',
1115         ], old_contents=[
1116           'public class Test extends BaseCaseB {',
1117           '}',
1118         ]),
1119         MockAffectedFile('CorrectTestWithInterface.java', [
1120           '@RunWith(ABC.class);'
1121           'public class CorrectTest implement Interface {',
1122           '}',
1123         ]),
1124         MockAffectedFile('IncorrectTest.java', [
1125           'public class IncorrectTest extends TestCase {',
1126           '}',
1127         ]),
1128         MockAffectedFile('IncorrectWithInterfaceTest.java', [
1129           'public class Test implements X extends BaseClass {',
1130           '}',
1131         ]),
1132         MockAffectedFile('IncorrectMultiLineTest.java', [
1133           'public class Test implements X, Y, Z',
1134           '        extends TestBase {',
1135           '}',
1136         ]),
1137     ]
1138     msgs = PRESUBMIT._CheckAndroidTestJUnitInheritance(
1139         mock_input_api, mock_output_api)
1140     self.assertEqual(1, len(msgs),
1141                      'Expected %d items, found %d: %s'
1142                      % (1, len(msgs), msgs))
1143     self.assertEqual(3, len(msgs[0].items),
1144                      'Expected %d items, found %d: %s'
1145                      % (3, len(msgs[0].items), msgs[0].items))
1146     self.assertTrue('IncorrectTest.java:1' in msgs[0].items,
1147                     'IncorrectTest not found in errors')
1148     self.assertTrue('IncorrectWithInterfaceTest.java:1'
1149                     in msgs[0].items,
1150                     'IncorrectWithInterfaceTest not found in errors')
1151     self.assertTrue('IncorrectMultiLineTest.java:2' in msgs[0].items,
1152                     'IncorrectMultiLineTest not found in errors')
1153
1154 class AndroidDebuggableBuildTest(unittest.TestCase):
1155
1156   def testCheckAndroidDebuggableBuild(self):
1157     mock_input_api = MockInputApi()
1158     mock_output_api = MockOutputApi()
1159
1160     mock_input_api.files = [
1161       MockAffectedFile('RandomStuff.java', [
1162         'random stuff'
1163       ]),
1164       MockAffectedFile('CorrectUsage.java', [
1165         'import org.chromium.base.BuildInfo;',
1166         'some random stuff',
1167         'boolean isOsDebuggable = BuildInfo.isDebugAndroid();',
1168       ]),
1169       MockAffectedFile('JustCheckUserdebugBuild.java', [
1170         'import android.os.Build;',
1171         'some random stuff',
1172         'boolean isOsDebuggable = Build.TYPE.equals("userdebug")',
1173       ]),
1174       MockAffectedFile('JustCheckEngineeringBuild.java', [
1175         'import android.os.Build;',
1176         'some random stuff',
1177         'boolean isOsDebuggable = "eng".equals(Build.TYPE)',
1178       ]),
1179       MockAffectedFile('UsedBuildType.java', [
1180         'import android.os.Build;',
1181         'some random stuff',
1182         'boolean isOsDebuggable = Build.TYPE.equals("userdebug")'
1183             '|| "eng".equals(Build.TYPE)',
1184       ]),
1185       MockAffectedFile('UsedExplicitBuildType.java', [
1186         'some random stuff',
1187         'boolean isOsDebuggable = android.os.Build.TYPE.equals("userdebug")'
1188             '|| "eng".equals(android.os.Build.TYPE)',
1189       ]),
1190     ]
1191
1192     msgs = PRESUBMIT._CheckAndroidDebuggableBuild(
1193         mock_input_api, mock_output_api)
1194     self.assertEqual(1, len(msgs),
1195                      'Expected %d items, found %d: %s'
1196                      % (1, len(msgs), msgs))
1197     self.assertEqual(4, len(msgs[0].items),
1198                      'Expected %d items, found %d: %s'
1199                      % (4, len(msgs[0].items), msgs[0].items))
1200     self.assertTrue('JustCheckUserdebugBuild.java:3' in msgs[0].items)
1201     self.assertTrue('JustCheckEngineeringBuild.java:3' in msgs[0].items)
1202     self.assertTrue('UsedBuildType.java:3' in msgs[0].items)
1203     self.assertTrue('UsedExplicitBuildType.java:2' in msgs[0].items)
1204
1205
1206 class LogUsageTest(unittest.TestCase):
1207
1208   def testCheckAndroidCrLogUsage(self):
1209     mock_input_api = MockInputApi()
1210     mock_output_api = MockOutputApi()
1211
1212     mock_input_api.files = [
1213       MockAffectedFile('RandomStuff.java', [
1214         'random stuff'
1215       ]),
1216       MockAffectedFile('HasAndroidLog.java', [
1217         'import android.util.Log;',
1218         'some random stuff',
1219         'Log.d("TAG", "foo");',
1220       ]),
1221       MockAffectedFile('HasExplicitUtilLog.java', [
1222         'some random stuff',
1223         'android.util.Log.d("TAG", "foo");',
1224       ]),
1225       MockAffectedFile('IsInBasePackage.java', [
1226         'package org.chromium.base;',
1227         'private static final String TAG = "cr_Foo";',
1228         'Log.d(TAG, "foo");',
1229       ]),
1230       MockAffectedFile('IsInBasePackageButImportsLog.java', [
1231         'package org.chromium.base;',
1232         'import android.util.Log;',
1233         'private static final String TAG = "cr_Foo";',
1234         'Log.d(TAG, "foo");',
1235       ]),
1236       MockAffectedFile('HasBothLog.java', [
1237         'import org.chromium.base.Log;',
1238         'some random stuff',
1239         'private static final String TAG = "cr_Foo";',
1240         'Log.d(TAG, "foo");',
1241         'android.util.Log.d("TAG", "foo");',
1242       ]),
1243       MockAffectedFile('HasCorrectTag.java', [
1244         'import org.chromium.base.Log;',
1245         'some random stuff',
1246         'private static final String TAG = "cr_Foo";',
1247         'Log.d(TAG, "foo");',
1248       ]),
1249       MockAffectedFile('HasOldTag.java', [
1250         'import org.chromium.base.Log;',
1251         'some random stuff',
1252         'private static final String TAG = "cr.Foo";',
1253         'Log.d(TAG, "foo");',
1254       ]),
1255       MockAffectedFile('HasDottedTag.java', [
1256         'import org.chromium.base.Log;',
1257         'some random stuff',
1258         'private static final String TAG = "cr_foo.bar";',
1259         'Log.d(TAG, "foo");',
1260       ]),
1261       MockAffectedFile('HasNoTagDecl.java', [
1262         'import org.chromium.base.Log;',
1263         'some random stuff',
1264         'Log.d(TAG, "foo");',
1265       ]),
1266       MockAffectedFile('HasIncorrectTagDecl.java', [
1267         'import org.chromium.base.Log;',
1268         'private static final String TAHG = "cr_Foo";',
1269         'some random stuff',
1270         'Log.d(TAG, "foo");',
1271       ]),
1272       MockAffectedFile('HasInlineTag.java', [
1273         'import org.chromium.base.Log;',
1274         'some random stuff',
1275         'private static final String TAG = "cr_Foo";',
1276         'Log.d("TAG", "foo");',
1277       ]),
1278       MockAffectedFile('HasUnprefixedTag.java', [
1279         'import org.chromium.base.Log;',
1280         'some random stuff',
1281         'private static final String TAG = "rubbish";',
1282         'Log.d(TAG, "foo");',
1283       ]),
1284       MockAffectedFile('HasTooLongTag.java', [
1285         'import org.chromium.base.Log;',
1286         'some random stuff',
1287         'private static final String TAG = "21_charachers_long___";',
1288         'Log.d(TAG, "foo");',
1289       ]),
1290     ]
1291
1292     msgs = PRESUBMIT._CheckAndroidCrLogUsage(
1293         mock_input_api, mock_output_api)
1294
1295     self.assertEqual(5, len(msgs),
1296                      'Expected %d items, found %d: %s' % (5, len(msgs), msgs))
1297
1298     # Declaration format
1299     nb = len(msgs[0].items)
1300     self.assertEqual(2, nb,
1301                      'Expected %d items, found %d: %s' % (2, nb, msgs[0].items))
1302     self.assertTrue('HasNoTagDecl.java' in msgs[0].items)
1303     self.assertTrue('HasIncorrectTagDecl.java' in msgs[0].items)
1304
1305     # Tag length
1306     nb = len(msgs[1].items)
1307     self.assertEqual(1, nb,
1308                      'Expected %d items, found %d: %s' % (1, nb, msgs[1].items))
1309     self.assertTrue('HasTooLongTag.java' in msgs[1].items)
1310
1311     # Tag must be a variable named TAG
1312     nb = len(msgs[2].items)
1313     self.assertEqual(1, nb,
1314                      'Expected %d items, found %d: %s' % (1, nb, msgs[2].items))
1315     self.assertTrue('HasInlineTag.java:4' in msgs[2].items)
1316
1317     # Util Log usage
1318     nb = len(msgs[3].items)
1319     self.assertEqual(2, nb,
1320                      'Expected %d items, found %d: %s' % (2, nb, msgs[3].items))
1321     self.assertTrue('HasAndroidLog.java:3' in msgs[3].items)
1322     self.assertTrue('IsInBasePackageButImportsLog.java:4' in msgs[3].items)
1323
1324     # Tag must not contain
1325     nb = len(msgs[4].items)
1326     self.assertEqual(2, nb,
1327                      'Expected %d items, found %d: %s' % (2, nb, msgs[4].items))
1328     self.assertTrue('HasDottedTag.java' in msgs[4].items)
1329     self.assertTrue('HasOldTag.java' in msgs[4].items)
1330
1331
1332 class GoogleAnswerUrlFormatTest(unittest.TestCase):
1333
1334   def testCatchAnswerUrlId(self):
1335     input_api = MockInputApi()
1336     input_api.files = [
1337       MockFile('somewhere/file.cc',
1338                ['char* host = '
1339                 '  "https://support.google.com/chrome/answer/123456";']),
1340       MockFile('somewhere_else/file.cc',
1341                ['char* host = '
1342                 '  "https://support.google.com/chrome/a/answer/123456";']),
1343     ]
1344
1345     warnings = PRESUBMIT._CheckGoogleSupportAnswerUrl(
1346       input_api, MockOutputApi())
1347     self.assertEqual(1, len(warnings))
1348     self.assertEqual(2, len(warnings[0].items))
1349
1350   def testAllowAnswerUrlParam(self):
1351     input_api = MockInputApi()
1352     input_api.files = [
1353       MockFile('somewhere/file.cc',
1354                ['char* host = '
1355                 '  "https://support.google.com/chrome/?p=cpn_crash_reports";']),
1356     ]
1357
1358     warnings = PRESUBMIT._CheckGoogleSupportAnswerUrl(
1359       input_api, MockOutputApi())
1360     self.assertEqual(0, len(warnings))
1361
1362
1363 class HardcodedGoogleHostsTest(unittest.TestCase):
1364
1365   def testWarnOnAssignedLiterals(self):
1366     input_api = MockInputApi()
1367     input_api.files = [
1368       MockFile('content/file.cc',
1369                ['char* host = "https://www.google.com";']),
1370       MockFile('content/file.cc',
1371                ['char* host = "https://www.googleapis.com";']),
1372       MockFile('content/file.cc',
1373                ['char* host = "https://clients1.google.com";']),
1374     ]
1375
1376     warnings = PRESUBMIT._CheckHardcodedGoogleHostsInLowerLayers(
1377       input_api, MockOutputApi())
1378     self.assertEqual(1, len(warnings))
1379     self.assertEqual(3, len(warnings[0].items))
1380
1381   def testAllowInComment(self):
1382     input_api = MockInputApi()
1383     input_api.files = [
1384       MockFile('content/file.cc',
1385                ['char* host = "https://www.aol.com"; // google.com'])
1386     ]
1387
1388     warnings = PRESUBMIT._CheckHardcodedGoogleHostsInLowerLayers(
1389       input_api, MockOutputApi())
1390     self.assertEqual(0, len(warnings))
1391
1392
1393 class ForwardDeclarationTest(unittest.TestCase):
1394   def testCheckHeadersOnlyOutsideThirdParty(self):
1395     mock_input_api = MockInputApi()
1396     mock_input_api.files = [
1397       MockAffectedFile('somewhere/file.cc', [
1398         'class DummyClass;'
1399       ]),
1400       MockAffectedFile('third_party/header.h', [
1401         'class DummyClass;'
1402       ])
1403     ]
1404     warnings = PRESUBMIT._CheckUselessForwardDeclarations(mock_input_api,
1405                                                           MockOutputApi())
1406     self.assertEqual(0, len(warnings))
1407
1408   def testNoNestedDeclaration(self):
1409     mock_input_api = MockInputApi()
1410     mock_input_api.files = [
1411       MockAffectedFile('somewhere/header.h', [
1412         'class SomeClass {',
1413         ' protected:',
1414         '  class NotAMatch;',
1415         '};'
1416       ])
1417     ]
1418     warnings = PRESUBMIT._CheckUselessForwardDeclarations(mock_input_api,
1419                                                           MockOutputApi())
1420     self.assertEqual(0, len(warnings))
1421
1422   def testSubStrings(self):
1423     mock_input_api = MockInputApi()
1424     mock_input_api.files = [
1425       MockAffectedFile('somewhere/header.h', [
1426         'class NotUsefulClass;',
1427         'struct SomeStruct;',
1428         'UsefulClass *p1;',
1429         'SomeStructPtr *p2;'
1430       ])
1431     ]
1432     warnings = PRESUBMIT._CheckUselessForwardDeclarations(mock_input_api,
1433                                                           MockOutputApi())
1434     self.assertEqual(2, len(warnings))
1435
1436   def testUselessForwardDeclaration(self):
1437     mock_input_api = MockInputApi()
1438     mock_input_api.files = [
1439       MockAffectedFile('somewhere/header.h', [
1440         'class DummyClass;',
1441         'struct DummyStruct;',
1442         'class UsefulClass;',
1443         'std::unique_ptr<UsefulClass> p;'
1444       ])
1445     ]
1446     warnings = PRESUBMIT._CheckUselessForwardDeclarations(mock_input_api,
1447                                                           MockOutputApi())
1448     self.assertEqual(2, len(warnings))
1449
1450   def testBlinkHeaders(self):
1451     mock_input_api = MockInputApi()
1452     mock_input_api.files = [
1453       MockAffectedFile('third_party/blink/header.h', [
1454         'class DummyClass;',
1455         'struct DummyStruct;',
1456       ]),
1457       MockAffectedFile('third_party\\blink\\header.h', [
1458         'class DummyClass;',
1459         'struct DummyStruct;',
1460       ])
1461     ]
1462     warnings = PRESUBMIT._CheckUselessForwardDeclarations(mock_input_api,
1463                                                           MockOutputApi())
1464     self.assertEqual(4, len(warnings))
1465
1466
1467 class RelativeIncludesTest(unittest.TestCase):
1468   def testThirdPartyNotWebKitIgnored(self):
1469     mock_input_api = MockInputApi()
1470     mock_input_api.files = [
1471       MockAffectedFile('third_party/test.cpp', '#include "../header.h"'),
1472       MockAffectedFile('third_party/test/test.cpp', '#include "../header.h"'),
1473     ]
1474
1475     mock_output_api = MockOutputApi()
1476
1477     errors = PRESUBMIT._CheckForRelativeIncludes(
1478         mock_input_api, mock_output_api)
1479     self.assertEqual(0, len(errors))
1480
1481   def testNonCppFileIgnored(self):
1482     mock_input_api = MockInputApi()
1483     mock_input_api.files = [
1484       MockAffectedFile('test.py', '#include "../header.h"'),
1485     ]
1486
1487     mock_output_api = MockOutputApi()
1488
1489     errors = PRESUBMIT._CheckForRelativeIncludes(
1490         mock_input_api, mock_output_api)
1491     self.assertEqual(0, len(errors))
1492
1493   def testInnocuousChangesAllowed(self):
1494     mock_input_api = MockInputApi()
1495     mock_input_api.files = [
1496       MockAffectedFile('test.cpp', '#include "header.h"'),
1497       MockAffectedFile('test2.cpp', '../'),
1498     ]
1499
1500     mock_output_api = MockOutputApi()
1501
1502     errors = PRESUBMIT._CheckForRelativeIncludes(
1503         mock_input_api, mock_output_api)
1504     self.assertEqual(0, len(errors))
1505
1506   def testRelativeIncludeNonWebKitProducesError(self):
1507     mock_input_api = MockInputApi()
1508     mock_input_api.files = [
1509       MockAffectedFile('test.cpp', ['#include "../header.h"']),
1510     ]
1511
1512     mock_output_api = MockOutputApi()
1513
1514     errors = PRESUBMIT._CheckForRelativeIncludes(
1515         mock_input_api, mock_output_api)
1516     self.assertEqual(1, len(errors))
1517
1518   def testRelativeIncludeWebKitProducesError(self):
1519     mock_input_api = MockInputApi()
1520     mock_input_api.files = [
1521       MockAffectedFile('third_party/blink/test.cpp',
1522                        ['#include "../header.h']),
1523     ]
1524
1525     mock_output_api = MockOutputApi()
1526
1527     errors = PRESUBMIT._CheckForRelativeIncludes(
1528         mock_input_api, mock_output_api)
1529     self.assertEqual(1, len(errors))
1530
1531
1532 class CCIncludeTest(unittest.TestCase):
1533   def testThirdPartyNotBlinkIgnored(self):
1534     mock_input_api = MockInputApi()
1535     mock_input_api.files = [
1536       MockAffectedFile('third_party/test.cpp', '#include "file.cc"'),
1537     ]
1538
1539     mock_output_api = MockOutputApi()
1540
1541     errors = PRESUBMIT._CheckForCcIncludes(
1542         mock_input_api, mock_output_api)
1543     self.assertEqual(0, len(errors))
1544
1545   def testPythonFileIgnored(self):
1546     mock_input_api = MockInputApi()
1547     mock_input_api.files = [
1548       MockAffectedFile('test.py', '#include "file.cc"'),
1549     ]
1550
1551     mock_output_api = MockOutputApi()
1552
1553     errors = PRESUBMIT._CheckForCcIncludes(
1554         mock_input_api, mock_output_api)
1555     self.assertEqual(0, len(errors))
1556
1557   def testIncFilesAccepted(self):
1558     mock_input_api = MockInputApi()
1559     mock_input_api.files = [
1560       MockAffectedFile('test.py', '#include "file.inc"'),
1561     ]
1562
1563     mock_output_api = MockOutputApi()
1564
1565     errors = PRESUBMIT._CheckForCcIncludes(
1566         mock_input_api, mock_output_api)
1567     self.assertEqual(0, len(errors))
1568
1569   def testInnocuousChangesAllowed(self):
1570     mock_input_api = MockInputApi()
1571     mock_input_api.files = [
1572       MockAffectedFile('test.cpp', '#include "header.h"'),
1573       MockAffectedFile('test2.cpp', 'Something "file.cc"'),
1574     ]
1575
1576     mock_output_api = MockOutputApi()
1577
1578     errors = PRESUBMIT._CheckForCcIncludes(
1579         mock_input_api, mock_output_api)
1580     self.assertEqual(0, len(errors))
1581
1582   def testCcIncludeNonBlinkProducesError(self):
1583     mock_input_api = MockInputApi()
1584     mock_input_api.files = [
1585       MockAffectedFile('test.cpp', ['#include "file.cc"']),
1586     ]
1587
1588     mock_output_api = MockOutputApi()
1589
1590     errors = PRESUBMIT._CheckForCcIncludes(
1591         mock_input_api, mock_output_api)
1592     self.assertEqual(1, len(errors))
1593
1594   def testCppIncludeBlinkProducesError(self):
1595     mock_input_api = MockInputApi()
1596     mock_input_api.files = [
1597       MockAffectedFile('third_party/blink/test.cpp',
1598                        ['#include "foo/file.cpp"']),
1599     ]
1600
1601     mock_output_api = MockOutputApi()
1602
1603     errors = PRESUBMIT._CheckForCcIncludes(
1604         mock_input_api, mock_output_api)
1605     self.assertEqual(1, len(errors))
1606
1607
1608 class NewHeaderWithoutGnChangeTest(unittest.TestCase):
1609   def testAddHeaderWithoutGn(self):
1610     mock_input_api = MockInputApi()
1611     mock_input_api.files = [
1612       MockAffectedFile('base/stuff.h', ''),
1613     ]
1614     warnings = PRESUBMIT._CheckNewHeaderWithoutGnChange(
1615         mock_input_api, MockOutputApi())
1616     self.assertEqual(1, len(warnings))
1617     self.assertTrue('base/stuff.h' in warnings[0].items)
1618
1619   def testModifyHeader(self):
1620     mock_input_api = MockInputApi()
1621     mock_input_api.files = [
1622       MockAffectedFile('base/stuff.h', '', action='M'),
1623     ]
1624     warnings = PRESUBMIT._CheckNewHeaderWithoutGnChange(
1625         mock_input_api, MockOutputApi())
1626     self.assertEqual(0, len(warnings))
1627
1628   def testDeleteHeader(self):
1629     mock_input_api = MockInputApi()
1630     mock_input_api.files = [
1631       MockAffectedFile('base/stuff.h', '', action='D'),
1632     ]
1633     warnings = PRESUBMIT._CheckNewHeaderWithoutGnChange(
1634         mock_input_api, MockOutputApi())
1635     self.assertEqual(0, len(warnings))
1636
1637   def testAddHeaderWithGn(self):
1638     mock_input_api = MockInputApi()
1639     mock_input_api.files = [
1640       MockAffectedFile('base/stuff.h', ''),
1641       MockAffectedFile('base/BUILD.gn', 'stuff.h'),
1642     ]
1643     warnings = PRESUBMIT._CheckNewHeaderWithoutGnChange(
1644         mock_input_api, MockOutputApi())
1645     self.assertEqual(0, len(warnings))
1646
1647   def testAddHeaderWithGni(self):
1648     mock_input_api = MockInputApi()
1649     mock_input_api.files = [
1650       MockAffectedFile('base/stuff.h', ''),
1651       MockAffectedFile('base/files.gni', 'stuff.h'),
1652     ]
1653     warnings = PRESUBMIT._CheckNewHeaderWithoutGnChange(
1654         mock_input_api, MockOutputApi())
1655     self.assertEqual(0, len(warnings))
1656
1657   def testAddHeaderWithOther(self):
1658     mock_input_api = MockInputApi()
1659     mock_input_api.files = [
1660       MockAffectedFile('base/stuff.h', ''),
1661       MockAffectedFile('base/stuff.cc', 'stuff.h'),
1662     ]
1663     warnings = PRESUBMIT._CheckNewHeaderWithoutGnChange(
1664         mock_input_api, MockOutputApi())
1665     self.assertEqual(1, len(warnings))
1666
1667   def testAddHeaderWithWrongGn(self):
1668     mock_input_api = MockInputApi()
1669     mock_input_api.files = [
1670       MockAffectedFile('base/stuff.h', ''),
1671       MockAffectedFile('base/BUILD.gn', 'stuff_h'),
1672     ]
1673     warnings = PRESUBMIT._CheckNewHeaderWithoutGnChange(
1674         mock_input_api, MockOutputApi())
1675     self.assertEqual(1, len(warnings))
1676
1677   def testAddHeadersWithGn(self):
1678     mock_input_api = MockInputApi()
1679     mock_input_api.files = [
1680       MockAffectedFile('base/stuff.h', ''),
1681       MockAffectedFile('base/another.h', ''),
1682       MockAffectedFile('base/BUILD.gn', 'another.h\nstuff.h'),
1683     ]
1684     warnings = PRESUBMIT._CheckNewHeaderWithoutGnChange(
1685         mock_input_api, MockOutputApi())
1686     self.assertEqual(0, len(warnings))
1687
1688   def testAddHeadersWithWrongGn(self):
1689     mock_input_api = MockInputApi()
1690     mock_input_api.files = [
1691       MockAffectedFile('base/stuff.h', ''),
1692       MockAffectedFile('base/another.h', ''),
1693       MockAffectedFile('base/BUILD.gn', 'another_h\nstuff.h'),
1694     ]
1695     warnings = PRESUBMIT._CheckNewHeaderWithoutGnChange(
1696         mock_input_api, MockOutputApi())
1697     self.assertEqual(1, len(warnings))
1698     self.assertFalse('base/stuff.h' in warnings[0].items)
1699     self.assertTrue('base/another.h' in warnings[0].items)
1700
1701   def testAddHeadersWithWrongGn2(self):
1702     mock_input_api = MockInputApi()
1703     mock_input_api.files = [
1704       MockAffectedFile('base/stuff.h', ''),
1705       MockAffectedFile('base/another.h', ''),
1706       MockAffectedFile('base/BUILD.gn', 'another_h\nstuff_h'),
1707     ]
1708     warnings = PRESUBMIT._CheckNewHeaderWithoutGnChange(
1709         mock_input_api, MockOutputApi())
1710     self.assertEqual(1, len(warnings))
1711     self.assertTrue('base/stuff.h' in warnings[0].items)
1712     self.assertTrue('base/another.h' in warnings[0].items)
1713
1714
1715 class CorrectProductNameInMessagesTest(unittest.TestCase):
1716   def testProductNameInDesc(self):
1717     mock_input_api = MockInputApi()
1718     mock_input_api.files = [
1719       MockAffectedFile('chrome/app/google_chrome_strings.grd', [
1720         '<message name="Foo" desc="Welcome to Chrome">',
1721         '  Welcome to Chrome!',
1722         '</message>',
1723       ]),
1724       MockAffectedFile('chrome/app/chromium_strings.grd', [
1725         '<message name="Bar" desc="Welcome to Chrome">',
1726         '  Welcome to Chromium!',
1727         '</message>',
1728       ]),
1729     ]
1730     warnings = PRESUBMIT._CheckCorrectProductNameInMessages(
1731         mock_input_api, MockOutputApi())
1732     self.assertEqual(0, len(warnings))
1733
1734   def testChromeInChromium(self):
1735     mock_input_api = MockInputApi()
1736     mock_input_api.files = [
1737       MockAffectedFile('chrome/app/google_chrome_strings.grd', [
1738         '<message name="Foo" desc="Welcome to Chrome">',
1739         '  Welcome to Chrome!',
1740         '</message>',
1741       ]),
1742       MockAffectedFile('chrome/app/chromium_strings.grd', [
1743         '<message name="Bar" desc="Welcome to Chrome">',
1744         '  Welcome to Chrome!',
1745         '</message>',
1746       ]),
1747     ]
1748     warnings = PRESUBMIT._CheckCorrectProductNameInMessages(
1749         mock_input_api, MockOutputApi())
1750     self.assertEqual(1, len(warnings))
1751     self.assertTrue('chrome/app/chromium_strings.grd' in warnings[0].items[0])
1752
1753   def testChromiumInChrome(self):
1754     mock_input_api = MockInputApi()
1755     mock_input_api.files = [
1756       MockAffectedFile('chrome/app/google_chrome_strings.grd', [
1757         '<message name="Foo" desc="Welcome to Chrome">',
1758         '  Welcome to Chromium!',
1759         '</message>',
1760       ]),
1761       MockAffectedFile('chrome/app/chromium_strings.grd', [
1762         '<message name="Bar" desc="Welcome to Chrome">',
1763         '  Welcome to Chromium!',
1764         '</message>',
1765       ]),
1766     ]
1767     warnings = PRESUBMIT._CheckCorrectProductNameInMessages(
1768         mock_input_api, MockOutputApi())
1769     self.assertEqual(1, len(warnings))
1770     self.assertTrue(
1771         'chrome/app/google_chrome_strings.grd:2' in warnings[0].items[0])
1772
1773   def testMultipleInstances(self):
1774     mock_input_api = MockInputApi()
1775     mock_input_api.files = [
1776       MockAffectedFile('chrome/app/chromium_strings.grd', [
1777         '<message name="Bar" desc="Welcome to Chrome">',
1778         '  Welcome to Chrome!',
1779         '</message>',
1780         '<message name="Baz" desc="A correct message">',
1781         '  Chromium is the software you are using.',
1782         '</message>',
1783         '<message name="Bat" desc="An incorrect message">',
1784         '  Google Chrome is the software you are using.',
1785         '</message>',
1786       ]),
1787     ]
1788     warnings = PRESUBMIT._CheckCorrectProductNameInMessages(
1789         mock_input_api, MockOutputApi())
1790     self.assertEqual(1, len(warnings))
1791     self.assertTrue(
1792         'chrome/app/chromium_strings.grd:2' in warnings[0].items[0])
1793     self.assertTrue(
1794         'chrome/app/chromium_strings.grd:8' in warnings[0].items[1])
1795
1796   def testMultipleWarnings(self):
1797     mock_input_api = MockInputApi()
1798     mock_input_api.files = [
1799       MockAffectedFile('chrome/app/chromium_strings.grd', [
1800         '<message name="Bar" desc="Welcome to Chrome">',
1801         '  Welcome to Chrome!',
1802         '</message>',
1803         '<message name="Baz" desc="A correct message">',
1804         '  Chromium is the software you are using.',
1805         '</message>',
1806         '<message name="Bat" desc="An incorrect message">',
1807         '  Google Chrome is the software you are using.',
1808         '</message>',
1809       ]),
1810       MockAffectedFile('components/components_google_chrome_strings.grd', [
1811         '<message name="Bar" desc="Welcome to Chrome">',
1812         '  Welcome to Chrome!',
1813         '</message>',
1814         '<message name="Baz" desc="A correct message">',
1815         '  Chromium is the software you are using.',
1816         '</message>',
1817         '<message name="Bat" desc="An incorrect message">',
1818         '  Google Chrome is the software you are using.',
1819         '</message>',
1820       ]),
1821     ]
1822     warnings = PRESUBMIT._CheckCorrectProductNameInMessages(
1823         mock_input_api, MockOutputApi())
1824     self.assertEqual(2, len(warnings))
1825     self.assertTrue(
1826         'components/components_google_chrome_strings.grd:5'
1827              in warnings[0].items[0])
1828     self.assertTrue(
1829         'chrome/app/chromium_strings.grd:2' in warnings[1].items[0])
1830     self.assertTrue(
1831         'chrome/app/chromium_strings.grd:8' in warnings[1].items[1])
1832
1833
1834 class ServiceManifestOwnerTest(unittest.TestCase):
1835   def testServiceManifestJsonChangeNeedsSecurityOwner(self):
1836     mock_input_api = MockInputApi()
1837     mock_input_api.files = [
1838       MockAffectedFile('services/goat/manifest.json',
1839                        [
1840                          '{',
1841                          '  "name": "teleporter",',
1842                          '  "display_name": "Goat Teleporter",'
1843                          '  "interface_provider_specs": {',
1844                          '  }',
1845                          '}',
1846                        ])
1847     ]
1848     mock_output_api = MockOutputApi()
1849     errors = PRESUBMIT._CheckIpcOwners(
1850         mock_input_api, mock_output_api)
1851     self.assertEqual(1, len(errors))
1852     self.assertEqual(
1853         'Found OWNERS files that need to be updated for IPC security review ' +
1854         'coverage.\nPlease update the OWNERS files below:', errors[0].message)
1855
1856     # No warning if already covered by an OWNERS rule.
1857
1858   def testNonManifestJsonChangesDoNotRequireSecurityOwner(self):
1859     mock_input_api = MockInputApi()
1860     mock_input_api.files = [
1861       MockAffectedFile('services/goat/species.json',
1862                        [
1863                          '[',
1864                          '  "anglo-nubian",',
1865                          '  "angora"',
1866                          ']',
1867                        ])
1868     ]
1869     mock_output_api = MockOutputApi()
1870     errors = PRESUBMIT._CheckIpcOwners(
1871         mock_input_api, mock_output_api)
1872     self.assertEqual([], errors)
1873
1874   def testServiceManifestChangeNeedsSecurityOwner(self):
1875     mock_input_api = MockInputApi()
1876     mock_input_api.files = [
1877       MockAffectedFile('services/goat/public/cpp/manifest.cc',
1878                        [
1879                          '#include "services/goat/public/cpp/manifest.h"',
1880                          'const service_manager::Manifest& GetManifest() {}',
1881                        ])]
1882     mock_output_api = MockOutputApi()
1883     errors = PRESUBMIT._CheckIpcOwners(
1884         mock_input_api, mock_output_api)
1885     self.assertEqual(1, len(errors))
1886     self.assertEqual(
1887         'Found OWNERS files that need to be updated for IPC security review ' +
1888         'coverage.\nPlease update the OWNERS files below:', errors[0].message)
1889
1890   def testNonServiceManifestSourceChangesDoNotRequireSecurityOwner(self):
1891     mock_input_api = MockInputApi()
1892     mock_input_api.files = [
1893       MockAffectedFile('some/non/service/thing/foo_manifest.cc',
1894                        [
1895                          'const char kNoEnforcement[] = "not a manifest!";',
1896                        ])]
1897     mock_output_api = MockOutputApi()
1898     errors = PRESUBMIT._CheckIpcOwners(
1899         mock_input_api, mock_output_api)
1900     self.assertEqual([], errors)
1901
1902
1903 class BannedFunctionCheckTest(unittest.TestCase):
1904
1905   def testBannedIosObcjFunctions(self):
1906     input_api = MockInputApi()
1907     input_api.files = [
1908       MockFile('some/ios/file.mm',
1909                ['TEST(SomeClassTest, SomeInteraction) {',
1910                 '}']),
1911       MockFile('some/mac/file.mm',
1912                ['TEST(SomeClassTest, SomeInteraction) {',
1913                 '}']),
1914       MockFile('another/ios_file.mm',
1915                ['class SomeTest : public testing::Test {};']),
1916     ]
1917
1918     errors = PRESUBMIT._CheckNoBannedFunctions(input_api, MockOutputApi())
1919     self.assertEqual(1, len(errors))
1920     self.assertTrue('some/ios/file.mm' in errors[0].message)
1921     self.assertTrue('another/ios_file.mm' in errors[0].message)
1922     self.assertTrue('some/mac/file.mm' not in errors[0].message)
1923
1924   def testBannedMojoFunctions(self):
1925     input_api = MockInputApi()
1926     input_api.files = [
1927       MockFile('some/cpp/problematic/file.cc',
1928                ['mojo::DataPipe();']),
1929       MockFile('some/cpp/problematic/file2.cc',
1930                ['mojo::ConvertTo<>']),
1931       MockFile('some/cpp/ok/file.cc',
1932                ['CreateDataPipe();']),
1933       MockFile('some/cpp/ok/file2.cc',
1934                ['mojo::DataPipeDrainer();']),
1935       MockFile('third_party/blink/ok/file3.cc',
1936                ['mojo::ConvertTo<>']),
1937       MockFile('content/renderer/ok/file3.cc',
1938                ['mojo::ConvertTo<>']),
1939     ]
1940
1941     results = PRESUBMIT._CheckNoBannedFunctions(input_api, MockOutputApi())
1942
1943     # warnings are results[0], errors are results[1]
1944     self.assertEqual(2, len(results))
1945     self.assertTrue('some/cpp/problematic/file.cc' in results[1].message)
1946     self.assertTrue('some/cpp/problematic/file2.cc' in results[0].message)
1947     self.assertTrue('some/cpp/ok/file.cc' not in results[1].message)
1948     self.assertTrue('some/cpp/ok/file2.cc' not in results[1].message)
1949     self.assertTrue('third_party/blink/ok/file3.cc' not in results[0].message)
1950     self.assertTrue('content/renderer/ok/file3.cc' not in results[0].message)
1951
1952
1953 class NoProductionCodeUsingTestOnlyFunctionsTest(unittest.TestCase):
1954   def testTruePositives(self):
1955     mock_input_api = MockInputApi()
1956     mock_input_api.files = [
1957       MockFile('some/path/foo.cc', ['foo_for_testing();']),
1958       MockFile('some/path/foo.mm', ['FooForTesting();']),
1959       MockFile('some/path/foo.cxx', ['FooForTests();']),
1960       MockFile('some/path/foo.cpp', ['foo_for_test();']),
1961     ]
1962
1963     results = PRESUBMIT._CheckNoProductionCodeUsingTestOnlyFunctions(
1964         mock_input_api, MockOutputApi())
1965     self.assertEqual(1, len(results))
1966     self.assertEqual(4, len(results[0].items))
1967     self.assertTrue('foo.cc' in results[0].items[0])
1968     self.assertTrue('foo.mm' in results[0].items[1])
1969     self.assertTrue('foo.cxx' in results[0].items[2])
1970     self.assertTrue('foo.cpp' in results[0].items[3])
1971
1972   def testFalsePositives(self):
1973     mock_input_api = MockInputApi()
1974     mock_input_api.files = [
1975       MockFile('some/path/foo.h', ['foo_for_testing();']),
1976       MockFile('some/path/foo.mm', ['FooForTesting() {']),
1977       MockFile('some/path/foo.cc', ['::FooForTests();']),
1978       MockFile('some/path/foo.cpp', ['// foo_for_test();']),
1979     ]
1980
1981     results = PRESUBMIT._CheckNoProductionCodeUsingTestOnlyFunctions(
1982         mock_input_api, MockOutputApi())
1983     self.assertEqual(0, len(results))
1984
1985
1986 class NoProductionJavaCodeUsingTestOnlyFunctionsTest(unittest.TestCase):
1987   def testTruePositives(self):
1988     mock_input_api = MockInputApi()
1989     mock_input_api.files = [
1990       MockFile('dir/java/src/foo.java', ['FooForTesting();']),
1991       MockFile('dir/java/src/bar.java', ['FooForTests(x);']),
1992       MockFile('dir/java/src/baz.java', ['FooForTest(', 'y', ');']),
1993       MockFile('dir/java/src/mult.java', [
1994         'int x = SomethingLongHere()',
1995         '    * SomethingLongHereForTesting();'
1996       ])
1997     ]
1998
1999     results = PRESUBMIT._CheckNoProductionCodeUsingTestOnlyFunctionsJava(
2000         mock_input_api, MockOutputApi())
2001     self.assertEqual(1, len(results))
2002     self.assertEqual(4, len(results[0].items))
2003     self.assertTrue('foo.java' in results[0].items[0])
2004     self.assertTrue('bar.java' in results[0].items[1])
2005     self.assertTrue('baz.java' in results[0].items[2])
2006     self.assertTrue('mult.java' in results[0].items[3])
2007
2008   def testFalsePositives(self):
2009     mock_input_api = MockInputApi()
2010     mock_input_api.files = [
2011       MockFile('dir/java/src/foo.xml', ['FooForTesting();']),
2012       MockFile('dir/java/src/foo.java', ['FooForTests() {']),
2013       MockFile('dir/java/src/bar.java', ['// FooForTest();']),
2014       MockFile('dir/java/src/bar2.java', ['x = 1; // FooForTest();']),
2015       MockFile('dir/javatests/src/baz.java', ['FooForTest(', 'y', ');']),
2016       MockFile('dir/junit/src/baz.java', ['FooForTest(', 'y', ');']),
2017       MockFile('dir/junit/src/javadoc.java', [
2018         '/** Use FooForTest(); to obtain foo in tests.'
2019         ' */'
2020       ]),
2021       MockFile('dir/junit/src/javadoc2.java', [
2022         '/** ',
2023         ' * Use FooForTest(); to obtain foo in tests.'
2024         ' */'
2025       ]),
2026     ]
2027
2028     results = PRESUBMIT._CheckNoProductionCodeUsingTestOnlyFunctionsJava(
2029         mock_input_api, MockOutputApi())
2030     self.assertEqual(0, len(results))
2031
2032
2033 class CheckUniquePtrTest(unittest.TestCase):
2034   def testTruePositivesNullptr(self):
2035     mock_input_api = MockInputApi()
2036     mock_input_api.files = [
2037       MockFile('dir/baz.cc', ['std::unique_ptr<T>()']),
2038       MockFile('dir/baz-p.cc', ['std::unique_ptr<T<P>>()']),
2039     ]
2040
2041     results = PRESUBMIT._CheckUniquePtr(mock_input_api, MockOutputApi())
2042     self.assertEqual(1, len(results))
2043     self.assertTrue('nullptr' in results[0].message)
2044     self.assertEqual(2, len(results[0].items))
2045     self.assertTrue('baz.cc' in results[0].items[0])
2046     self.assertTrue('baz-p.cc' in results[0].items[1])
2047
2048   def testTruePositivesConstructor(self):
2049     mock_input_api = MockInputApi()
2050     mock_input_api.files = [
2051       MockFile('dir/foo.cc', ['return std::unique_ptr<T>(foo);']),
2052       MockFile('dir/bar.mm', ['bar = std::unique_ptr<T>(foo)']),
2053       MockFile('dir/mult.cc', [
2054         'return',
2055         '    std::unique_ptr<T>(barVeryVeryLongFooSoThatItWouldNotFitAbove);'
2056       ]),
2057       MockFile('dir/mult2.cc', [
2058         'barVeryVeryLongLongBaaaaaarSoThatTheLineLimitIsAlmostReached =',
2059         '    std::unique_ptr<T>(foo);'
2060       ]),
2061       MockFile('dir/mult3.cc', [
2062         'bar = std::unique_ptr<T>(',
2063         '    fooVeryVeryVeryLongStillGoingWellThisWillTakeAWhileFinallyThere);'
2064       ]),
2065       MockFile('dir/multi_arg.cc', [
2066           'auto p = std::unique_ptr<std::pair<T, D>>(new std::pair(T, D));']),
2067     ]
2068
2069     results = PRESUBMIT._CheckUniquePtr(mock_input_api, MockOutputApi())
2070     self.assertEqual(1, len(results))
2071     self.assertTrue('std::make_unique' in results[0].message)
2072     self.assertEqual(6, len(results[0].items))
2073     self.assertTrue('foo.cc' in results[0].items[0])
2074     self.assertTrue('bar.mm' in results[0].items[1])
2075     self.assertTrue('mult.cc' in results[0].items[2])
2076     self.assertTrue('mult2.cc' in results[0].items[3])
2077     self.assertTrue('mult3.cc' in results[0].items[4])
2078     self.assertTrue('multi_arg.cc' in results[0].items[5])
2079
2080   def testFalsePositives(self):
2081     mock_input_api = MockInputApi()
2082     mock_input_api.files = [
2083       MockFile('dir/foo.cc', ['return std::unique_ptr<T[]>(foo);']),
2084       MockFile('dir/bar.mm', ['bar = std::unique_ptr<T[]>(foo)']),
2085       MockFile('dir/file.cc', ['std::unique_ptr<T> p = Foo();']),
2086       MockFile('dir/baz.cc', [
2087         'std::unique_ptr<T> result = std::make_unique<T>();'
2088       ]),
2089       MockFile('dir/baz2.cc', [
2090         'std::unique_ptr<T> result = std::make_unique<T>('
2091       ]),
2092       MockFile('dir/nested.cc', ['set<std::unique_ptr<T>>();']),
2093       MockFile('dir/nested2.cc', ['map<U, std::unique_ptr<T>>();']),
2094
2095       # Two-argument invocation of std::unique_ptr is exempt because there is
2096       # no equivalent using std::make_unique.
2097       MockFile('dir/multi_arg.cc', [
2098         'auto p = std::unique_ptr<T, D>(new T(), D());']),
2099     ]
2100
2101     results = PRESUBMIT._CheckUniquePtr(mock_input_api, MockOutputApi())
2102     self.assertEqual(0, len(results))
2103
2104 class CheckNoDirectIncludesHeadersWhichRedefineStrCat(unittest.TestCase):
2105   def testBlocksDirectIncludes(self):
2106     mock_input_api = MockInputApi()
2107     mock_input_api.files = [
2108       MockFile('dir/foo_win.cc', ['#include "shlwapi.h"']),
2109       MockFile('dir/bar.h', ['#include <propvarutil.h>']),
2110       MockFile('dir/baz.h', ['#include <atlbase.h>']),
2111       MockFile('dir/jumbo.h', ['#include "sphelper.h"']),
2112     ]
2113     results = PRESUBMIT._CheckNoStrCatRedefines(mock_input_api, MockOutputApi())
2114     self.assertEquals(1, len(results))
2115     self.assertEquals(4, len(results[0].items))
2116     self.assertTrue('StrCat' in results[0].message)
2117     self.assertTrue('foo_win.cc' in results[0].items[0])
2118     self.assertTrue('bar.h' in results[0].items[1])
2119     self.assertTrue('baz.h' in results[0].items[2])
2120     self.assertTrue('jumbo.h' in results[0].items[3])
2121
2122   def testAllowsToIncludeWrapper(self):
2123     mock_input_api = MockInputApi()
2124     mock_input_api.files = [
2125       MockFile('dir/baz_win.cc', ['#include "base/win/shlwapi.h"']),
2126       MockFile('dir/baz-win.h', ['#include "base/win/atl.h"']),
2127     ]
2128     results = PRESUBMIT._CheckNoStrCatRedefines(mock_input_api, MockOutputApi())
2129     self.assertEquals(0, len(results))
2130
2131   def testAllowsToCreateWrapper(self):
2132     mock_input_api = MockInputApi()
2133     mock_input_api.files = [
2134       MockFile('base/win/shlwapi.h', [
2135         '#include <shlwapi.h>',
2136         '#include "base/win/windows_defines.inc"']),
2137     ]
2138     results = PRESUBMIT._CheckNoStrCatRedefines(mock_input_api, MockOutputApi())
2139     self.assertEquals(0, len(results))
2140
2141 class TranslationScreenshotsTest(unittest.TestCase):
2142   # An empty grd file.
2143   OLD_GRD_CONTENTS = """<?xml version="1.0" encoding="UTF-8"?>
2144            <grit latest_public_release="1" current_release="1">
2145              <release seq="1">
2146                <messages></messages>
2147              </release>
2148            </grit>
2149         """.splitlines()
2150   # A grd file with a single message.
2151   NEW_GRD_CONTENTS1 = """<?xml version="1.0" encoding="UTF-8"?>
2152            <grit latest_public_release="1" current_release="1">
2153              <release seq="1">
2154                <messages>
2155                  <message name="IDS_TEST1">
2156                    Test string 1
2157                  </message>
2158                </messages>
2159              </release>
2160            </grit>
2161         """.splitlines()
2162   # A grd file with two messages.
2163   NEW_GRD_CONTENTS2 = """<?xml version="1.0" encoding="UTF-8"?>
2164            <grit latest_public_release="1" current_release="1">
2165              <release seq="1">
2166                <messages>
2167                  <message name="IDS_TEST1">
2168                    Test string 1
2169                  </message>
2170                  <message name="IDS_TEST2">
2171                    Test string 2
2172                  </message>
2173                </messages>
2174              </release>
2175            </grit>
2176         """.splitlines()
2177
2178   DO_NOT_UPLOAD_PNG_MESSAGE = ('Do not include actual screenshots in the '
2179                                'changelist. Run '
2180                                'tools/translate/upload_screenshots.py to '
2181                                'upload them instead:')
2182   GENERATE_SIGNATURES_MESSAGE = ('You are adding or modifying UI strings.\n'
2183                                  'To ensure the best translations, take '
2184                                  'screenshots of the relevant UI '
2185                                  '(https://g.co/chrome/translation) and add '
2186                                  'these files to your changelist:')
2187   REMOVE_SIGNATURES_MESSAGE = ('You removed strings associated with these '
2188                                'files. Remove:')
2189
2190   def makeInputApi(self, files):
2191     input_api = MockInputApi()
2192     input_api.files = files
2193     return input_api
2194
2195   def testNoScreenshots(self):
2196     # CL modified and added messages, but didn't add any screenshots.
2197     input_api = self.makeInputApi([
2198       MockAffectedFile('test.grd', self.NEW_GRD_CONTENTS2,
2199                        self.OLD_GRD_CONTENTS, action='M')])
2200     warnings = PRESUBMIT._CheckTranslationScreenshots(input_api,
2201                                                       MockOutputApi())
2202     self.assertEqual(1, len(warnings))
2203     self.assertEqual(self.GENERATE_SIGNATURES_MESSAGE, warnings[0].message)
2204     self.assertEqual([
2205         os.path.join('test_grd', 'IDS_TEST1.png.sha1'),
2206         os.path.join('test_grd', 'IDS_TEST2.png.sha1')
2207     ], warnings[0].items)
2208
2209     input_api = self.makeInputApi([
2210       MockAffectedFile('test.grd', self.NEW_GRD_CONTENTS2,
2211                        self.NEW_GRD_CONTENTS1, action='M')])
2212     warnings = PRESUBMIT._CheckTranslationScreenshots(input_api,
2213                                                       MockOutputApi())
2214     self.assertEqual(1, len(warnings))
2215     self.assertEqual(self.GENERATE_SIGNATURES_MESSAGE, warnings[0].message)
2216     self.assertEqual([os.path.join('test_grd', 'IDS_TEST2.png.sha1')],
2217                      warnings[0].items)
2218
2219
2220   def testUnnecessaryScreenshots(self):
2221     # CL added a single message and added the png file, but not the sha1 file.
2222     input_api = self.makeInputApi([
2223         MockAffectedFile(
2224             'test.grd',
2225             self.NEW_GRD_CONTENTS1,
2226             self.OLD_GRD_CONTENTS,
2227             action='M'),
2228         MockAffectedFile(
2229             os.path.join('test_grd', 'IDS_TEST1.png'), 'binary', action='A')
2230     ])
2231     warnings = PRESUBMIT._CheckTranslationScreenshots(input_api,
2232                                                       MockOutputApi())
2233     self.assertEqual(2, len(warnings))
2234     self.assertEqual(self.DO_NOT_UPLOAD_PNG_MESSAGE, warnings[0].message)
2235     self.assertEqual([os.path.join('test_grd', 'IDS_TEST1.png')],
2236                      warnings[0].items)
2237     self.assertEqual(self.GENERATE_SIGNATURES_MESSAGE, warnings[1].message)
2238     self.assertEqual([os.path.join('test_grd', 'IDS_TEST1.png.sha1')],
2239                      warnings[1].items)
2240
2241     # CL added two messages, one has a png. Expect two messages:
2242     # - One for the unnecessary png.
2243     # - Another one for missing .sha1 files.
2244     input_api = self.makeInputApi([
2245         MockAffectedFile(
2246             'test.grd',
2247             self.NEW_GRD_CONTENTS2,
2248             self.OLD_GRD_CONTENTS,
2249             action='M'),
2250         MockAffectedFile(
2251             os.path.join('test_grd', 'IDS_TEST1.png'), 'binary', action='A')
2252     ])
2253     warnings = PRESUBMIT._CheckTranslationScreenshots(input_api,
2254                                                       MockOutputApi())
2255     self.assertEqual(2, len(warnings))
2256     self.assertEqual(self.DO_NOT_UPLOAD_PNG_MESSAGE, warnings[0].message)
2257     self.assertEqual([os.path.join('test_grd', 'IDS_TEST1.png')],
2258                      warnings[0].items)
2259     self.assertEqual(self.GENERATE_SIGNATURES_MESSAGE, warnings[1].message)
2260     self.assertEqual([
2261         os.path.join('test_grd', 'IDS_TEST1.png.sha1'),
2262         os.path.join('test_grd', 'IDS_TEST2.png.sha1')
2263     ], warnings[1].items)
2264
2265   def testScreenshotsWithSha1(self):
2266     # CL added two messages and their corresponding .sha1 files. No warnings.
2267     input_api = self.makeInputApi([
2268         MockAffectedFile(
2269             'test.grd',
2270             self.NEW_GRD_CONTENTS2,
2271             self.OLD_GRD_CONTENTS,
2272             action='M'),
2273         MockFile(
2274             os.path.join('test_grd', 'IDS_TEST1.png.sha1'),
2275             'binary',
2276             action='A'),
2277         MockFile(
2278             os.path.join('test_grd', 'IDS_TEST2.png.sha1'),
2279             'binary',
2280             action='A')
2281     ])
2282     warnings = PRESUBMIT._CheckTranslationScreenshots(input_api,
2283                                                       MockOutputApi())
2284     self.assertEqual([], warnings)
2285
2286   def testScreenshotsRemovedWithSha1(self):
2287     # Swap old contents with new contents, remove IDS_TEST1 and IDS_TEST2. The
2288     # sha1 files associated with the messages should also be removed by the CL.
2289     input_api = self.makeInputApi([
2290         MockAffectedFile(
2291             'test.grd',
2292             self.OLD_GRD_CONTENTS,
2293             self.NEW_GRD_CONTENTS2,
2294             action='M'),
2295         MockFile(os.path.join('test_grd', 'IDS_TEST1.png.sha1'), 'binary', ""),
2296         MockFile(os.path.join('test_grd', 'IDS_TEST2.png.sha1'), 'binary', "")
2297     ])
2298     warnings = PRESUBMIT._CheckTranslationScreenshots(input_api,
2299                                                       MockOutputApi())
2300     self.assertEqual(1, len(warnings))
2301     self.assertEqual(self.REMOVE_SIGNATURES_MESSAGE, warnings[0].message)
2302     self.assertEqual([
2303         os.path.join('test_grd', 'IDS_TEST1.png.sha1'),
2304         os.path.join('test_grd', 'IDS_TEST2.png.sha1')
2305     ], warnings[0].items)
2306
2307     # Same as above, but this time one of the .sha1 files is removed.
2308     input_api = self.makeInputApi([
2309         MockAffectedFile(
2310             'test.grd',
2311             self.OLD_GRD_CONTENTS,
2312             self.NEW_GRD_CONTENTS2,
2313             action='M'),
2314         MockFile(os.path.join('test_grd', 'IDS_TEST1.png.sha1'), 'binary', ''),
2315         MockAffectedFile(
2316             os.path.join('test_grd', 'IDS_TEST2.png.sha1'),
2317             '',
2318             'old_contents',
2319             action='D')
2320     ])
2321     warnings = PRESUBMIT._CheckTranslationScreenshots(input_api,
2322                                                       MockOutputApi())
2323     self.assertEqual(1, len(warnings))
2324     self.assertEqual(self.REMOVE_SIGNATURES_MESSAGE, warnings[0].message)
2325     self.assertEqual([os.path.join('test_grd', 'IDS_TEST1.png.sha1')],
2326                      warnings[0].items)
2327
2328     # Remove both sha1 files. No presubmit warnings.
2329     input_api = self.makeInputApi([
2330         MockAffectedFile(
2331             'test.grd',
2332             self.OLD_GRD_CONTENTS,
2333             self.NEW_GRD_CONTENTS2,
2334             action='M'),
2335         MockFile(
2336             os.path.join('test_grd', 'IDS_TEST1.png.sha1'),
2337             'binary',
2338             action='D'),
2339         MockFile(
2340             os.path.join('test_grd', 'IDS_TEST2.png.sha1'),
2341             'binary',
2342             action='D')
2343     ])
2344     warnings = PRESUBMIT._CheckTranslationScreenshots(input_api,
2345                                                       MockOutputApi())
2346     self.assertEqual([], warnings)
2347
2348
2349 class DISABLETypoInTest(unittest.TestCase):
2350
2351   def testPositive(self):
2352     # Verify the typo "DISABLE_" instead of "DISABLED_" in various contexts
2353     # where the desire is to disable a test.
2354     tests = [
2355         # Disabled on one platform:
2356         '#if defined(OS_WIN)\n'
2357         '#define MAYBE_FoobarTest DISABLE_FoobarTest\n'
2358         '#else\n'
2359         '#define MAYBE_FoobarTest FoobarTest\n'
2360         '#endif\n',
2361         # Disabled on one platform spread cross lines:
2362         '#if defined(OS_WIN)\n'
2363         '#define MAYBE_FoobarTest \\\n'
2364         '    DISABLE_FoobarTest\n'
2365         '#else\n'
2366         '#define MAYBE_FoobarTest FoobarTest\n'
2367         '#endif\n',
2368         # Disabled on all platforms:
2369         '  TEST_F(FoobarTest, DISABLE_Foo)\n{\n}',
2370         # Disabled on all platforms but multiple lines
2371         '  TEST_F(FoobarTest,\n   DISABLE_foo){\n}\n',
2372     ]
2373
2374     for test in tests:
2375       mock_input_api = MockInputApi()
2376       mock_input_api.files = [
2377           MockFile('some/path/foo_unittest.cc', test.splitlines()),
2378       ]
2379
2380       results = PRESUBMIT._CheckNoDISABLETypoInTests(mock_input_api,
2381                                                      MockOutputApi())
2382       self.assertEqual(
2383           1,
2384           len(results),
2385           msg=('expected len(results) == 1 but got %d in test: %s' %
2386                (len(results), test)))
2387       self.assertTrue(
2388           'foo_unittest.cc' in results[0].message,
2389           msg=('expected foo_unittest.cc in message but got %s in test %s' %
2390                (results[0].message, test)))
2391
2392   def testIngoreNotTestFiles(self):
2393     mock_input_api = MockInputApi()
2394     mock_input_api.files = [
2395         MockFile('some/path/foo.cc', 'TEST_F(FoobarTest, DISABLE_Foo)'),
2396     ]
2397
2398     results = PRESUBMIT._CheckNoDISABLETypoInTests(mock_input_api,
2399                                                    MockOutputApi())
2400     self.assertEqual(0, len(results))
2401
2402   def testIngoreDeletedFiles(self):
2403     mock_input_api = MockInputApi()
2404     mock_input_api.files = [
2405         MockFile('some/path/foo.cc', 'TEST_F(FoobarTest, Foo)', action='D'),
2406     ]
2407
2408     results = PRESUBMIT._CheckNoDISABLETypoInTests(mock_input_api,
2409                                                    MockOutputApi())
2410     self.assertEqual(0, len(results))
2411
2412
2413 class BuildtoolsRevisionsAreInSyncTest(unittest.TestCase):
2414   # TODO(crbug.com/941824): We need to make sure the entries in
2415   # //buildtools/DEPS are kept in sync with the entries in //DEPS
2416   # so that users of //buildtools in other projects get the same tooling
2417   # Chromium gets. If we ever fix the referenced bug and add 'includedeps'
2418   # support to gclient, we can eliminate the duplication and delete
2419   # these tests for the corresponding presubmit check.
2420
2421   def _check(self, files):
2422     mock_input_api = MockInputApi()
2423     mock_input_api.files = []
2424     for fname, contents in files.items():
2425       mock_input_api.files.append(MockFile(fname, contents.splitlines()))
2426     return PRESUBMIT._CheckBuildtoolsRevisionsAreInSync(mock_input_api,
2427                                                         MockOutputApi())
2428
2429   def testOneFileChangedButNotTheOther(self):
2430     results = self._check({
2431         "DEPS": "'libunwind_revision': 'onerev'",
2432     })
2433     self.assertNotEqual(results, [])
2434
2435   def testNeitherFileChanged(self):
2436     results = self._check({
2437         "OWNERS": "foobar@example.com",
2438     })
2439     self.assertEqual(results, [])
2440
2441   def testBothFilesChangedAndMatch(self):
2442     results = self._check({
2443         "DEPS": "'libunwind_revision': 'onerev'",
2444         "buildtools/DEPS": "'libunwind_revision': 'onerev'",
2445     })
2446     self.assertEqual(results, [])
2447
2448   def testBothFilesWereChangedAndDontMatch(self):
2449     results = self._check({
2450         "DEPS": "'libunwind_revision': 'onerev'",
2451         "buildtools/DEPS": "'libunwind_revision': 'anotherrev'",
2452     })
2453     self.assertNotEqual(results, [])
2454
2455
2456 if __name__ == '__main__':
2457   unittest.main()