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