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.
12 from PRESUBMIT_test_mocks import MockFile, MockAffectedFile
13 from PRESUBMIT_test_mocks import MockInputApi, MockOutputApi
16 _TEST_DATA_DIR = 'base/test/data/presubmit'
19 class VersionControlConflictsTest(unittest.TestCase):
20 def testTypicalConflict(self):
21 lines = ['<<<<<<< HEAD',
22 ' base::ScopedTempDir temp_dir_;',
24 ' ScopedTempDir temp_dir_;',
26 errors = PRESUBMIT._CheckForVersionControlConflictsInFile(
27 MockInputApi(), MockFile('some/path/foo_platform.cc', lines))
28 self.assertEqual(3, len(errors))
29 self.assertTrue('1' in errors[0])
30 self.assertTrue('3' in errors[1])
31 self.assertTrue('5' in errors[2])
33 def testIgnoresReadmes(self):
34 lines = ['A First Level Header',
35 '====================',
37 'A Second Level Header',
38 '---------------------']
39 errors = PRESUBMIT._CheckForVersionControlConflictsInFile(
40 MockInputApi(), MockFile('some/polymer/README.md', lines))
41 self.assertEqual(0, len(errors))
44 class BadExtensionsTest(unittest.TestCase):
45 def testBadRejFile(self):
46 mock_input_api = MockInputApi()
47 mock_input_api.files = [
48 MockFile('some/path/foo.cc', ''),
49 MockFile('some/path/foo.cc.rej', ''),
50 MockFile('some/path2/bar.h.rej', ''),
53 results = PRESUBMIT.CheckPatchFiles(mock_input_api, MockOutputApi())
54 self.assertEqual(1, len(results))
55 self.assertEqual(2, len(results[0].items))
56 self.assertTrue('foo.cc.rej' in results[0].items[0])
57 self.assertTrue('bar.h.rej' in results[0].items[1])
59 def testBadOrigFile(self):
60 mock_input_api = MockInputApi()
61 mock_input_api.files = [
62 MockFile('other/path/qux.h.orig', ''),
63 MockFile('other/path/qux.h', ''),
64 MockFile('other/path/qux.cc', ''),
67 results = PRESUBMIT.CheckPatchFiles(mock_input_api, MockOutputApi())
68 self.assertEqual(1, len(results))
69 self.assertEqual(1, len(results[0].items))
70 self.assertTrue('qux.h.orig' in results[0].items[0])
72 def testGoodFiles(self):
73 mock_input_api = MockInputApi()
74 mock_input_api.files = [
75 MockFile('other/path/qux.h', ''),
76 MockFile('other/path/qux.cc', ''),
78 results = PRESUBMIT.CheckPatchFiles(mock_input_api, MockOutputApi())
79 self.assertEqual(0, len(results))
82 class CheckForSuperfluousStlIncludesInHeadersTest(unittest.TestCase):
83 def testGoodFiles(self):
84 mock_input_api = MockInputApi()
85 mock_input_api.files = [
86 # The check is not smart enough to figure out which definitions correspond
88 MockFile('other/path/foo.h',
91 # The check is not smart enough to do IWYU.
92 MockFile('other/path/bar.h',
93 ['#include "base/check.h"',
95 MockFile('other/path/qux.h',
96 ['#include "base/stl_util.h"',
98 MockFile('other/path/baz.h',
99 ['#include "set/vector.h"',
101 # The check is only for header files.
102 MockFile('other/path/not_checked.cc',
103 ['#include <vector>',
106 results = PRESUBMIT.CheckForSuperfluousStlIncludesInHeaders(
107 mock_input_api, MockOutputApi())
108 self.assertEqual(0, len(results))
110 def testBadFiles(self):
111 mock_input_api = MockInputApi()
112 mock_input_api.files = [
113 MockFile('other/path/foo.h',
114 ['#include <vector>',
116 MockFile('other/path/bar.h',
117 ['#include <limits>',
119 'no_std_namespace']),
121 results = PRESUBMIT.CheckForSuperfluousStlIncludesInHeaders(
122 mock_input_api, MockOutputApi())
123 self.assertEqual(1, len(results))
124 self.assertTrue('foo.h: Includes STL' in results[0].message)
125 self.assertTrue('bar.h: Includes STL' in results[0].message)
128 class CheckSingletonInHeadersTest(unittest.TestCase):
129 def testSingletonInArbitraryHeader(self):
130 diff_singleton_h = ['base::subtle::AtomicWord '
131 'base::Singleton<Type, Traits, DifferentiatingType>::']
132 diff_foo_h = ['// base::Singleton<Foo> in comment.',
133 'friend class base::Singleton<Foo>']
134 diff_foo2_h = [' //Foo* bar = base::Singleton<Foo>::get();']
135 diff_bad_h = ['Foo* foo = base::Singleton<Foo>::get();']
136 mock_input_api = MockInputApi()
137 mock_input_api.files = [MockAffectedFile('base/memory/singleton.h',
139 MockAffectedFile('foo.h', diff_foo_h),
140 MockAffectedFile('foo2.h', diff_foo2_h),
141 MockAffectedFile('bad.h', diff_bad_h)]
142 warnings = PRESUBMIT.CheckSingletonInHeaders(mock_input_api,
144 self.assertEqual(1, len(warnings))
145 self.assertEqual(1, len(warnings[0].items))
146 self.assertEqual('error', warnings[0].type)
147 self.assertTrue('Found base::Singleton<T>' in warnings[0].message)
149 def testSingletonInCC(self):
150 diff_cc = ['Foo* foo = base::Singleton<Foo>::get();']
151 mock_input_api = MockInputApi()
152 mock_input_api.files = [MockAffectedFile('some/path/foo.cc', diff_cc)]
153 warnings = PRESUBMIT.CheckSingletonInHeaders(mock_input_api,
155 self.assertEqual(0, len(warnings))
158 class InvalidOSMacroNamesTest(unittest.TestCase):
159 def testInvalidOSMacroNames(self):
160 lines = ['#if defined(OS_WINDOWS)',
161 ' #elif defined(OS_WINDOW)',
162 ' # if defined(OS_MAC) || defined(OS_CHROME)',
163 '# else // defined(OS_MACOSX)',
164 '#endif // defined(OS_MACOS)']
165 errors = PRESUBMIT._CheckForInvalidOSMacrosInFile(
166 MockInputApi(), MockFile('some/path/foo_platform.cc', lines))
167 self.assertEqual(len(lines), len(errors))
168 self.assertTrue(':1 OS_WINDOWS' in errors[0])
169 self.assertTrue('(did you mean OS_WIN?)' in errors[0])
171 def testValidOSMacroNames(self):
172 lines = ['#if defined(%s)' % m for m in PRESUBMIT._VALID_OS_MACROS]
173 errors = PRESUBMIT._CheckForInvalidOSMacrosInFile(
174 MockInputApi(), MockFile('some/path/foo_platform.cc', lines))
175 self.assertEqual(0, len(errors))
178 class InvalidIfDefinedMacroNamesTest(unittest.TestCase):
179 def testInvalidIfDefinedMacroNames(self):
180 lines = ['#if defined(TARGET_IPHONE_SIMULATOR)',
181 '#if !defined(TARGET_IPHONE_SIMULATOR)',
182 '#elif defined(TARGET_IPHONE_SIMULATOR)',
183 '#ifdef TARGET_IPHONE_SIMULATOR',
184 ' # ifdef TARGET_IPHONE_SIMULATOR',
185 '# if defined(VALID) || defined(TARGET_IPHONE_SIMULATOR)',
186 '# else // defined(TARGET_IPHONE_SIMULATOR)',
187 '#endif // defined(TARGET_IPHONE_SIMULATOR)']
188 errors = PRESUBMIT._CheckForInvalidIfDefinedMacrosInFile(
189 MockInputApi(), MockFile('some/path/source.mm', lines))
190 self.assertEqual(len(lines), len(errors))
192 def testValidIfDefinedMacroNames(self):
193 lines = ['#if defined(FOO)',
195 errors = PRESUBMIT._CheckForInvalidIfDefinedMacrosInFile(
196 MockInputApi(), MockFile('some/path/source.cc', lines))
197 self.assertEqual(0, len(errors))
200 class CheckAddedDepsHaveTestApprovalsTest(unittest.TestCase):
202 def calculate(self, old_include_rules, old_specific_include_rules,
203 new_include_rules, new_specific_include_rules):
204 return PRESUBMIT._CalculateAddedDeps(
205 os.path, 'include_rules = %r\nspecific_include_rules = %r' % (
206 old_include_rules, old_specific_include_rules),
207 'include_rules = %r\nspecific_include_rules = %r' % (
208 new_include_rules, new_specific_include_rules))
210 def testCalculateAddedDeps(self):
211 old_include_rules = [
220 old_specific_include_rules = {
226 new_include_rules = [
233 '+grit/generated_resources.h",',
237 '+' + os.path.join('third_party', 'WebKit'),
239 new_specific_include_rules = {
249 os.path.join('chrome', 'DEPS'),
250 os.path.join('gpu', 'DEPS'),
251 os.path.join('components', 'DEPS'),
252 os.path.join('policy', 'DEPS'),
253 os.path.join('third_party', 'WebKit', 'DEPS'),
257 self.calculate(old_include_rules, old_specific_include_rules,
258 new_include_rules, new_specific_include_rules))
260 def testCalculateAddedDepsIgnoresPermutations(self):
261 old_include_rules = [
265 new_include_rules = [
269 self.assertEqual(set(),
270 self.calculate(old_include_rules, {}, new_include_rules,
274 class JSONParsingTest(unittest.TestCase):
275 def testSuccess(self):
276 input_api = MockInputApi()
277 filename = 'valid_json.json'
278 contents = ['// This is a comment.',
280 ' "key1": ["value1", "value2"],',
281 ' "key2": 3 // This is an inline comment.',
284 input_api.files = [MockFile(filename, contents)]
285 self.assertEqual(None,
286 PRESUBMIT._GetJSONParseError(input_api, filename))
288 def testFailure(self):
289 input_api = MockInputApi()
291 ('invalid_json_1.json',
293 'Expecting property name'),
294 ('invalid_json_2.json',
296 '{ "hello": "world }'],
297 'Unterminated string starting at:'),
298 ('invalid_json_3.json',
299 ['{ "a": "b", "c": "d", }'],
300 'Expecting property name'),
301 ('invalid_json_4.json',
302 ['{ "a": "b" "c": "d" }'],
303 "Expecting ',' delimiter:"),
306 input_api.files = [MockFile(filename, contents)
307 for (filename, contents, _) in test_data]
309 for (filename, _, expected_error) in test_data:
310 actual_error = PRESUBMIT._GetJSONParseError(input_api, filename)
311 self.assertTrue(expected_error in str(actual_error),
312 "'%s' not found in '%s'" % (expected_error, actual_error))
314 def testNoEatComments(self):
315 input_api = MockInputApi()
316 file_with_comments = 'file_with_comments.json'
317 contents_with_comments = ['// This is a comment.',
319 ' "key1": ["value1", "value2"],',
320 ' "key2": 3 // This is an inline comment.',
323 file_without_comments = 'file_without_comments.json'
324 contents_without_comments = ['{',
325 ' "key1": ["value1", "value2"],',
329 input_api.files = [MockFile(file_with_comments, contents_with_comments),
330 MockFile(file_without_comments,
331 contents_without_comments)]
333 self.assertNotEqual(None,
334 str(PRESUBMIT._GetJSONParseError(input_api,
336 eat_comments=False)))
337 self.assertEqual(None,
338 PRESUBMIT._GetJSONParseError(input_api,
339 file_without_comments,
343 class IDLParsingTest(unittest.TestCase):
344 def testSuccess(self):
345 input_api = MockInputApi()
346 filename = 'valid_idl_basics.idl'
347 contents = ['// Tests a valid IDL file.',
348 'namespace idl_basics {',
354 ' dictionary MyType1 {',
358 ' callback Callback1 = void();',
359 ' callback Callback2 = void(long x);',
360 ' callback Callback3 = void(MyType1 arg);',
361 ' callback Callback4 = void(EnumType type);',
363 ' interface Functions {',
364 ' static void function1();',
365 ' static void function2(long x);',
366 ' static void function3(MyType1 arg);',
367 ' static void function4(Callback1 cb);',
368 ' static void function5(Callback2 cb);',
369 ' static void function6(Callback3 cb);',
370 ' static void function7(Callback4 cb);',
373 ' interface Events {',
374 ' static void onFoo1();',
375 ' static void onFoo2(long x);',
376 ' static void onFoo2(MyType1 arg);',
377 ' static void onFoo3(EnumType type);',
381 input_api.files = [MockFile(filename, contents)]
382 self.assertEqual(None,
383 PRESUBMIT._GetIDLParseError(input_api, filename))
385 def testFailure(self):
386 input_api = MockInputApi()
388 ('invalid_idl_1.idl',
395 'Unexpected "{" after keyword "dictionary".\n'),
396 # TODO(yoz): Disabled because it causes the IDL parser to hang.
397 # See crbug.com/363830.
398 # ('invalid_idl_2.idl',
399 # (['namespace test {',
400 # ' dictionary MissingSemicolon {',
405 # 'Unexpected symbol DOMString after symbol a.'),
406 ('invalid_idl_3.idl',
409 ' enum MissingComma {',
414 'Unexpected symbol name2 after symbol name1.'),
415 ('invalid_idl_4.idl',
418 ' enum TrailingComma {',
423 'Trailing comma in block.'),
424 ('invalid_idl_5.idl',
427 ' callback Callback1 = void(;',
429 'Unexpected ";" after "(".'),
430 ('invalid_idl_6.idl',
433 ' callback Callback1 = void(long );',
435 'Unexpected ")" after symbol long.'),
436 ('invalid_idl_7.idl',
439 ' interace Events {',
440 ' static void onFoo1();',
443 'Unexpected symbol Events after symbol interace.'),
444 ('invalid_idl_8.idl',
447 ' interface NotEvent {',
448 ' static void onFoo1();',
451 'Did not process Interface Interface(NotEvent)'),
452 ('invalid_idl_9.idl',
456 ' static void function1();',
459 'Interface missing name.'),
462 input_api.files = [MockFile(filename, contents)
463 for (filename, contents, _) in test_data]
465 for (filename, _, expected_error) in test_data:
466 actual_error = PRESUBMIT._GetIDLParseError(input_api, filename)
467 self.assertTrue(expected_error in str(actual_error),
468 "'%s' not found in '%s'" % (expected_error, actual_error))
471 class UserMetricsActionTest(unittest.TestCase):
472 def testUserMetricsActionInActions(self):
473 input_api = MockInputApi()
474 file_with_user_action = 'file_with_user_action.cc'
475 contents_with_user_action = [
476 'base::UserMetricsAction("AboutChrome")'
479 input_api.files = [MockFile(file_with_user_action,
480 contents_with_user_action)]
483 [], PRESUBMIT.CheckUserActionUpdate(input_api, MockOutputApi()))
485 def testUserMetricsActionNotAddedToActions(self):
486 input_api = MockInputApi()
487 file_with_user_action = 'file_with_user_action.cc'
488 contents_with_user_action = [
489 'base::UserMetricsAction("NotInActionsXml")'
492 input_api.files = [MockFile(file_with_user_action,
493 contents_with_user_action)]
495 output = PRESUBMIT.CheckUserActionUpdate(input_api, MockOutputApi())
497 ('File %s line %d: %s is missing in '
498 'tools/metrics/actions/actions.xml. Please run '
499 'tools/metrics/actions/extract_actions.py to update.'
500 % (file_with_user_action, 1, 'NotInActionsXml')),
503 def testUserMetricsActionInTestFile(self):
504 input_api = MockInputApi()
505 file_with_user_action = 'file_with_user_action_unittest.cc'
506 contents_with_user_action = [
507 'base::UserMetricsAction("NotInActionsXml")'
510 input_api.files = [MockFile(file_with_user_action,
511 contents_with_user_action)]
514 [], PRESUBMIT.CheckUserActionUpdate(input_api, MockOutputApi()))
517 class PydepsNeedsUpdatingTest(unittest.TestCase):
519 class MockSubprocess(object):
520 CalledProcessError = subprocess.CalledProcessError
522 def _MockParseGclientArgs(self, is_android=True):
523 return lambda: {'checkout_android': 'true' if is_android else 'false' }
526 mock_all_pydeps = ['A.pydeps', 'B.pydeps', 'D.pydeps']
527 self.old_ALL_PYDEPS_FILES = PRESUBMIT._ALL_PYDEPS_FILES
528 PRESUBMIT._ALL_PYDEPS_FILES = mock_all_pydeps
529 mock_android_pydeps = ['D.pydeps']
530 self.old_ANDROID_SPECIFIC_PYDEPS_FILES = (
531 PRESUBMIT._ANDROID_SPECIFIC_PYDEPS_FILES)
532 PRESUBMIT._ANDROID_SPECIFIC_PYDEPS_FILES = mock_android_pydeps
533 self.old_ParseGclientArgs = PRESUBMIT._ParseGclientArgs
534 PRESUBMIT._ParseGclientArgs = self._MockParseGclientArgs()
535 self.mock_input_api = MockInputApi()
536 self.mock_output_api = MockOutputApi()
537 self.mock_input_api.subprocess = PydepsNeedsUpdatingTest.MockSubprocess()
538 self.checker = PRESUBMIT.PydepsChecker(self.mock_input_api, mock_all_pydeps)
539 self.checker._file_cache = {
540 'A.pydeps': '# Generated by:\n# CMD --output A.pydeps A\nA.py\nC.py\n',
541 'B.pydeps': '# Generated by:\n# CMD --output B.pydeps B\nB.py\nC.py\n',
542 'D.pydeps': '# Generated by:\n# CMD --output D.pydeps D\nD.py\n',
546 PRESUBMIT._ALL_PYDEPS_FILES = self.old_ALL_PYDEPS_FILES
547 PRESUBMIT._ANDROID_SPECIFIC_PYDEPS_FILES = (
548 self.old_ANDROID_SPECIFIC_PYDEPS_FILES)
549 PRESUBMIT._ParseGclientArgs = self.old_ParseGclientArgs
552 return PRESUBMIT.CheckPydepsNeedsUpdating(self.mock_input_api,
553 self.mock_output_api,
554 checker_for_tests=self.checker)
556 def testAddedPydep(self):
557 # PRESUBMIT.CheckPydepsNeedsUpdating is only implemented for Linux.
558 if self.mock_input_api.platform != 'linux2':
561 self.mock_input_api.files = [
562 MockAffectedFile('new.pydeps', [], action='A'),
565 self.mock_input_api.CreateMockFileInPath(
566 [x.LocalPath() for x in self.mock_input_api.AffectedFiles(
567 include_deletes=True)])
568 results = self._RunCheck()
569 self.assertEqual(1, len(results))
570 self.assertIn('PYDEPS_FILES', str(results[0]))
572 def testPydepNotInSrc(self):
573 self.mock_input_api.files = [
574 MockAffectedFile('new.pydeps', [], action='A'),
576 self.mock_input_api.CreateMockFileInPath([])
577 results = self._RunCheck()
578 self.assertEqual(0, len(results))
580 def testRemovedPydep(self):
581 # PRESUBMIT.CheckPydepsNeedsUpdating is only implemented for Linux.
582 if self.mock_input_api.platform != 'linux2':
585 self.mock_input_api.files = [
586 MockAffectedFile(PRESUBMIT._ALL_PYDEPS_FILES[0], [], action='D'),
588 self.mock_input_api.CreateMockFileInPath(
589 [x.LocalPath() for x in self.mock_input_api.AffectedFiles(
590 include_deletes=True)])
591 results = self._RunCheck()
592 self.assertEqual(1, len(results))
593 self.assertIn('PYDEPS_FILES', str(results[0]))
595 def testRandomPyIgnored(self):
596 # PRESUBMIT.CheckPydepsNeedsUpdating is only implemented for Linux.
597 if self.mock_input_api.platform != 'linux2':
600 self.mock_input_api.files = [
601 MockAffectedFile('random.py', []),
604 results = self._RunCheck()
605 self.assertEqual(0, len(results), 'Unexpected results: %r' % results)
607 def testRelevantPyNoChange(self):
608 # PRESUBMIT.CheckPydepsNeedsUpdating is only implemented for Linux.
609 if self.mock_input_api.platform != 'linux2':
612 self.mock_input_api.files = [
613 MockAffectedFile('A.py', []),
616 def mock_check_output(cmd, shell=False, env=None):
617 self.assertEqual('CMD --output A.pydeps A --output ""', cmd)
618 return self.checker._file_cache['A.pydeps']
620 self.mock_input_api.subprocess.check_output = mock_check_output
622 results = self._RunCheck()
623 self.assertEqual(0, len(results), 'Unexpected results: %r' % results)
625 def testRelevantPyOneChange(self):
626 # PRESUBMIT.CheckPydepsNeedsUpdating is only implemented for Linux.
627 if self.mock_input_api.platform != 'linux2':
630 self.mock_input_api.files = [
631 MockAffectedFile('A.py', []),
634 def mock_check_output(cmd, shell=False, env=None):
635 self.assertEqual('CMD --output A.pydeps A --output ""', cmd)
636 return 'changed data'
638 self.mock_input_api.subprocess.check_output = mock_check_output
640 results = self._RunCheck()
641 self.assertEqual(1, len(results))
642 self.assertIn('File is stale', str(results[0]))
644 def testRelevantPyTwoChanges(self):
645 # PRESUBMIT.CheckPydepsNeedsUpdating is only implemented for Linux.
646 if self.mock_input_api.platform != 'linux2':
649 self.mock_input_api.files = [
650 MockAffectedFile('C.py', []),
653 def mock_check_output(cmd, shell=False, env=None):
654 return 'changed data'
656 self.mock_input_api.subprocess.check_output = mock_check_output
658 results = self._RunCheck()
659 self.assertEqual(2, len(results))
660 self.assertIn('File is stale', str(results[0]))
661 self.assertIn('File is stale', str(results[1]))
663 def testRelevantAndroidPyInNonAndroidCheckout(self):
664 # PRESUBMIT.CheckPydepsNeedsUpdating is only implemented for Linux.
665 if self.mock_input_api.platform != 'linux2':
668 self.mock_input_api.files = [
669 MockAffectedFile('D.py', []),
672 def mock_check_output(cmd, shell=False, env=None):
673 self.assertEqual('CMD --output D.pydeps D --output ""', cmd)
674 return 'changed data'
676 self.mock_input_api.subprocess.check_output = mock_check_output
677 PRESUBMIT._ParseGclientArgs = self._MockParseGclientArgs(is_android=False)
679 results = self._RunCheck()
680 self.assertEqual(1, len(results))
681 self.assertIn('Android', str(results[0]))
682 self.assertIn('D.pydeps', str(results[0]))
684 def testGnPathsAndMissingOutputFlag(self):
685 # PRESUBMIT.CheckPydepsNeedsUpdating is only implemented for Linux.
686 if self.mock_input_api.platform != 'linux2':
689 self.checker._file_cache = {
690 'A.pydeps': '# Generated by:\n# CMD --gn-paths A\n//A.py\n//C.py\n',
691 'B.pydeps': '# Generated by:\n# CMD --gn-paths B\n//B.py\n//C.py\n',
692 'D.pydeps': '# Generated by:\n# CMD --gn-paths D\n//D.py\n',
695 self.mock_input_api.files = [
696 MockAffectedFile('A.py', []),
699 def mock_check_output(cmd, shell=False, env=None):
700 self.assertEqual('CMD --gn-paths A --output A.pydeps --output ""', cmd)
701 return 'changed data'
703 self.mock_input_api.subprocess.check_output = mock_check_output
705 results = self._RunCheck()
706 self.assertEqual(1, len(results))
707 self.assertIn('File is stale', str(results[0]))
710 class IncludeGuardTest(unittest.TestCase):
711 def testIncludeGuardChecks(self):
712 mock_input_api = MockInputApi()
713 mock_output_api = MockOutputApi()
714 mock_input_api.files = [
715 MockAffectedFile('content/browser/thing/foo.h', [
717 '#ifndef CONTENT_BROWSER_THING_FOO_H_',
718 '#define CONTENT_BROWSER_THING_FOO_H_',
719 'struct McBoatFace;',
720 '#endif // CONTENT_BROWSER_THING_FOO_H_',
722 MockAffectedFile('content/browser/thing/bar.h', [
723 '#ifndef CONTENT_BROWSER_THING_BAR_H_',
724 '#define CONTENT_BROWSER_THING_BAR_H_',
725 'namespace content {',
726 '#endif // CONTENT_BROWSER_THING_BAR_H_',
727 '} // namespace content',
729 MockAffectedFile('content/browser/test1.h', [
730 'namespace content {',
731 '} // namespace content',
733 MockAffectedFile('content\\browser\\win.h', [
734 '#ifndef CONTENT_BROWSER_WIN_H_',
735 '#define CONTENT_BROWSER_WIN_H_',
736 'struct McBoatFace;',
737 '#endif // CONTENT_BROWSER_WIN_H_',
739 MockAffectedFile('content/browser/test2.h', [
741 '#ifndef CONTENT_BROWSER_TEST2_H_',
742 'struct McBoatFace;',
743 '#endif // CONTENT_BROWSER_TEST2_H_',
745 MockAffectedFile('content/browser/internal.h', [
747 '#ifndef CONTENT_BROWSER_INTERNAL_H_',
748 '#define CONTENT_BROWSER_INTERNAL_H_',
750 '#ifndef INTERNAL_CONTENT_BROWSER_INTERNAL_H_',
751 '#define INTERNAL_CONTENT_BROWSER_INTERNAL_H_',
752 'namespace internal {',
753 '} // namespace internal',
754 '#endif // INTERNAL_CONTENT_BROWSER_THING_BAR_H_',
755 'namespace content {',
756 '} // namespace content',
757 '#endif // CONTENT_BROWSER_THING_BAR_H_',
759 MockAffectedFile('content/browser/thing/foo.cc', [
760 '// This is a non-header.',
762 MockAffectedFile('content/browser/disabled.h', [
763 '// no-include-guard-because-multiply-included',
764 'struct McBoatFace;',
766 # New files don't allow misspelled include guards.
767 MockAffectedFile('content/browser/spleling.h', [
768 '#ifndef CONTENT_BROWSER_SPLLEING_H_',
769 '#define CONTENT_BROWSER_SPLLEING_H_',
770 'struct McBoatFace;',
771 '#endif // CONTENT_BROWSER_SPLLEING_H_',
773 # New files don't allow + in include guards.
774 MockAffectedFile('content/browser/foo+bar.h', [
775 '#ifndef CONTENT_BROWSER_FOO+BAR_H_',
776 '#define CONTENT_BROWSER_FOO+BAR_H_',
777 'struct McBoatFace;',
778 '#endif // CONTENT_BROWSER_FOO+BAR_H_',
780 # Old files allow misspelled include guards (for now).
781 MockAffectedFile('chrome/old.h', [
783 '#ifndef CHROME_ODL_H_',
784 '#define CHROME_ODL_H_',
785 '#endif // CHROME_ODL_H_',
788 '#ifndef CHROME_ODL_H_',
789 '#define CHROME_ODL_H_',
790 '#endif // CHROME_ODL_H_',
792 # Using a Blink style include guard outside Blink is wrong.
793 MockAffectedFile('content/NotInBlink.h', [
794 '#ifndef NotInBlink_h',
795 '#define NotInBlink_h',
796 'struct McBoatFace;',
797 '#endif // NotInBlink_h',
799 # Using a Blink style include guard in Blink is no longer ok.
800 MockAffectedFile('third_party/blink/InBlink.h', [
803 'struct McBoatFace;',
804 '#endif // InBlink_h',
806 # Using a bad include guard in Blink is not ok.
807 MockAffectedFile('third_party/blink/AlsoInBlink.h', [
808 '#ifndef WrongInBlink_h',
809 '#define WrongInBlink_h',
810 'struct McBoatFace;',
811 '#endif // WrongInBlink_h',
813 # Using a bad include guard in Blink is not accepted even if
815 MockAffectedFile('third_party/blink/StillInBlink.h', [
817 '#ifndef AcceptedInBlink_h',
818 '#define AcceptedInBlink_h',
819 'struct McBoatFace;',
820 '#endif // AcceptedInBlink_h',
823 '#ifndef AcceptedInBlink_h',
824 '#define AcceptedInBlink_h',
825 'struct McBoatFace;',
826 '#endif // AcceptedInBlink_h',
828 # Using a non-Chromium include guard in third_party
829 # (outside blink) is accepted.
830 MockAffectedFile('third_party/foo/some_file.h', [
831 '#ifndef REQUIRED_RPCNDR_H_',
832 '#define REQUIRED_RPCNDR_H_',
833 'struct SomeFileFoo;',
834 '#endif // REQUIRED_RPCNDR_H_',
836 # Not having proper include guard in *_message_generator.h
837 # for old IPC messages is allowed.
838 MockAffectedFile('content/common/content_message_generator.h', [
839 '#undef CONTENT_COMMON_FOO_MESSAGES_H_',
840 '#include "content/common/foo_messages.h"',
841 '#ifndef CONTENT_COMMON_FOO_MESSAGES_H_',
842 '#error "Failed to include content/common/foo_messages.h"',
846 msgs = PRESUBMIT.CheckForIncludeGuards(
847 mock_input_api, mock_output_api)
848 expected_fail_count = 8
849 self.assertEqual(expected_fail_count, len(msgs),
850 'Expected %d items, found %d: %s'
851 % (expected_fail_count, len(msgs), msgs))
852 self.assertEqual(msgs[0].items, ['content/browser/thing/bar.h'])
853 self.assertEqual(msgs[0].message,
854 'Include guard CONTENT_BROWSER_THING_BAR_H_ '
855 'not covering the whole file')
857 self.assertEqual(msgs[1].items, ['content/browser/test1.h'])
858 self.assertEqual(msgs[1].message,
859 'Missing include guard CONTENT_BROWSER_TEST1_H_')
861 self.assertEqual(msgs[2].items, ['content/browser/test2.h:3'])
862 self.assertEqual(msgs[2].message,
863 'Missing "#define CONTENT_BROWSER_TEST2_H_" for '
866 self.assertEqual(msgs[3].items, ['content/browser/spleling.h:1'])
867 self.assertEqual(msgs[3].message,
868 'Header using the wrong include guard name '
869 'CONTENT_BROWSER_SPLLEING_H_')
871 self.assertEqual(msgs[4].items, ['content/browser/foo+bar.h'])
872 self.assertEqual(msgs[4].message,
873 'Missing include guard CONTENT_BROWSER_FOO_BAR_H_')
875 self.assertEqual(msgs[5].items, ['content/NotInBlink.h:1'])
876 self.assertEqual(msgs[5].message,
877 'Header using the wrong include guard name '
880 self.assertEqual(msgs[6].items, ['third_party/blink/InBlink.h:1'])
881 self.assertEqual(msgs[6].message,
882 'Header using the wrong include guard name '
885 self.assertEqual(msgs[7].items, ['third_party/blink/AlsoInBlink.h:1'])
886 self.assertEqual(msgs[7].message,
887 'Header using the wrong include guard name '
890 class AccessibilityRelnotesFieldTest(unittest.TestCase):
891 def testRelnotesPresent(self):
892 mock_input_api = MockInputApi()
893 mock_output_api = MockOutputApi()
895 mock_input_api.files = [MockAffectedFile('ui/accessibility/foo.bar', [''])]
896 mock_input_api.change.DescriptionText = lambda : 'Commit description'
897 mock_input_api.change.footers['AX-Relnotes'] = [
898 'Important user facing change']
900 msgs = PRESUBMIT.CheckAccessibilityRelnotesField(
901 mock_input_api, mock_output_api)
902 self.assertEqual(0, len(msgs),
903 'Expected %d messages, found %d: %s'
904 % (0, len(msgs), msgs))
906 def testRelnotesMissingFromAccessibilityChange(self):
907 mock_input_api = MockInputApi()
908 mock_output_api = MockOutputApi()
910 mock_input_api.files = [
911 MockAffectedFile('some/file', ['']),
912 MockAffectedFile('ui/accessibility/foo.bar', ['']),
913 MockAffectedFile('some/other/file', [''])
915 mock_input_api.change.DescriptionText = lambda : 'Commit description'
917 msgs = PRESUBMIT.CheckAccessibilityRelnotesField(
918 mock_input_api, mock_output_api)
919 self.assertEqual(1, len(msgs),
920 'Expected %d messages, found %d: %s'
921 % (1, len(msgs), msgs))
922 self.assertTrue("Missing 'AX-Relnotes:' field" in msgs[0].message,
923 'Missing AX-Relnotes field message not found in errors')
925 # The relnotes footer is not required for changes which do not touch any
926 # accessibility directories.
927 def testIgnoresNonAccesssibilityCode(self):
928 mock_input_api = MockInputApi()
929 mock_output_api = MockOutputApi()
931 mock_input_api.files = [
932 MockAffectedFile('some/file', ['']),
933 MockAffectedFile('some/other/file', [''])
935 mock_input_api.change.DescriptionText = lambda : 'Commit description'
937 msgs = PRESUBMIT.CheckAccessibilityRelnotesField(
938 mock_input_api, mock_output_api)
939 self.assertEqual(0, len(msgs),
940 'Expected %d messages, found %d: %s'
941 % (0, len(msgs), msgs))
943 # Test that our presubmit correctly raises an error for a set of known paths.
944 def testExpectedPaths(self):
946 "chrome/browser/accessibility/foo.py",
947 "chrome/browser/ash/arc/accessibility/foo.cc",
948 "chrome/browser/ui/views/accessibility/foo.h",
949 "chrome/browser/extensions/api/automation/foo.h",
950 "chrome/browser/extensions/api/automation_internal/foo.cc",
951 "chrome/renderer/extensions/accessibility_foo.h",
952 "chrome/tests/data/accessibility/foo.html",
953 "content/browser/accessibility/foo.cc",
954 "content/renderer/accessibility/foo.h",
955 "content/tests/data/accessibility/foo.cc",
956 "extensions/renderer/api/automation/foo.h",
957 "ui/accessibility/foo/bar/baz.cc",
958 "ui/views/accessibility/foo/bar/baz.h",
961 for testFile in filesToTest:
962 mock_input_api = MockInputApi()
963 mock_output_api = MockOutputApi()
965 mock_input_api.files = [
966 MockAffectedFile(testFile, [''])
968 mock_input_api.change.DescriptionText = lambda : 'Commit description'
970 msgs = PRESUBMIT.CheckAccessibilityRelnotesField(
971 mock_input_api, mock_output_api)
972 self.assertEqual(1, len(msgs),
973 'Expected %d messages, found %d: %s, for file %s'
974 % (1, len(msgs), msgs, testFile))
975 self.assertTrue("Missing 'AX-Relnotes:' field" in msgs[0].message,
976 ('Missing AX-Relnotes field message not found in errors '
977 ' for file %s' % (testFile)))
979 # Test that AX-Relnotes field can appear in the commit description (as long
980 # as it appears at the beginning of a line).
981 def testRelnotesInCommitDescription(self):
982 mock_input_api = MockInputApi()
983 mock_output_api = MockOutputApi()
985 mock_input_api.files = [
986 MockAffectedFile('ui/accessibility/foo.bar', ['']),
988 mock_input_api.change.DescriptionText = lambda : ('Description:\n' +
989 'AX-Relnotes: solves all accessibility issues forever')
991 msgs = PRESUBMIT.CheckAccessibilityRelnotesField(
992 mock_input_api, mock_output_api)
993 self.assertEqual(0, len(msgs),
994 'Expected %d messages, found %d: %s'
995 % (0, len(msgs), msgs))
997 # Test that we don't match AX-Relnotes if it appears in the middle of a line.
998 def testRelnotesMustAppearAtBeginningOfLine(self):
999 mock_input_api = MockInputApi()
1000 mock_output_api = MockOutputApi()
1002 mock_input_api.files = [
1003 MockAffectedFile('ui/accessibility/foo.bar', ['']),
1005 mock_input_api.change.DescriptionText = lambda : ('Description:\n' +
1006 'This change has no AX-Relnotes: we should print a warning')
1008 msgs = PRESUBMIT.CheckAccessibilityRelnotesField(
1009 mock_input_api, mock_output_api)
1010 self.assertTrue("Missing 'AX-Relnotes:' field" in msgs[0].message,
1011 'Missing AX-Relnotes field message not found in errors')
1013 # Tests that the AX-Relnotes field can be lowercase and use a '=' in place
1015 def testRelnotesLowercaseWithEqualSign(self):
1016 mock_input_api = MockInputApi()
1017 mock_output_api = MockOutputApi()
1019 mock_input_api.files = [
1020 MockAffectedFile('ui/accessibility/foo.bar', ['']),
1022 mock_input_api.change.DescriptionText = lambda : ('Description:\n' +
1023 'ax-relnotes= this is a valid format for accessibiliy relnotes')
1025 msgs = PRESUBMIT.CheckAccessibilityRelnotesField(
1026 mock_input_api, mock_output_api)
1027 self.assertEqual(0, len(msgs),
1028 'Expected %d messages, found %d: %s'
1029 % (0, len(msgs), msgs))
1031 class AndroidDeprecatedTestAnnotationTest(unittest.TestCase):
1032 def testCheckAndroidTestAnnotationUsage(self):
1033 mock_input_api = MockInputApi()
1034 mock_output_api = MockOutputApi()
1036 mock_input_api.files = [
1037 MockAffectedFile('LalaLand.java', [
1040 MockAffectedFile('CorrectUsage.java', [
1041 'import android.support.test.filters.LargeTest;',
1042 'import android.support.test.filters.MediumTest;',
1043 'import android.support.test.filters.SmallTest;',
1045 MockAffectedFile('UsedDeprecatedLargeTestAnnotation.java', [
1046 'import android.test.suitebuilder.annotation.LargeTest;',
1048 MockAffectedFile('UsedDeprecatedMediumTestAnnotation.java', [
1049 'import android.test.suitebuilder.annotation.MediumTest;',
1051 MockAffectedFile('UsedDeprecatedSmallTestAnnotation.java', [
1052 'import android.test.suitebuilder.annotation.SmallTest;',
1054 MockAffectedFile('UsedDeprecatedSmokeAnnotation.java', [
1055 'import android.test.suitebuilder.annotation.Smoke;',
1058 msgs = PRESUBMIT._CheckAndroidTestAnnotationUsage(
1059 mock_input_api, mock_output_api)
1060 self.assertEqual(1, len(msgs),
1061 'Expected %d items, found %d: %s'
1062 % (1, len(msgs), msgs))
1063 self.assertEqual(4, len(msgs[0].items),
1064 'Expected %d items, found %d: %s'
1065 % (4, len(msgs[0].items), msgs[0].items))
1066 self.assertTrue('UsedDeprecatedLargeTestAnnotation.java:1' in msgs[0].items,
1067 'UsedDeprecatedLargeTestAnnotation not found in errors')
1068 self.assertTrue('UsedDeprecatedMediumTestAnnotation.java:1'
1070 'UsedDeprecatedMediumTestAnnotation not found in errors')
1071 self.assertTrue('UsedDeprecatedSmallTestAnnotation.java:1' in msgs[0].items,
1072 'UsedDeprecatedSmallTestAnnotation not found in errors')
1073 self.assertTrue('UsedDeprecatedSmokeAnnotation.java:1' in msgs[0].items,
1074 'UsedDeprecatedSmokeAnnotation not found in errors')
1077 class CheckNoDownstreamDepsTest(unittest.TestCase):
1078 def testInvalidDepFromUpstream(self):
1079 mock_input_api = MockInputApi()
1080 mock_output_api = MockOutputApi()
1082 mock_input_api.files = [
1083 MockAffectedFile('BUILD.gn', [
1085 ' "//clank/target:test",',
1088 MockAffectedFile('chrome/android/BUILD.gn', [
1089 'deps = [ "//clank/target:test" ]'
1091 MockAffectedFile('chrome/chrome_java_deps.gni', [
1093 ' "//clank/target:test",',
1097 mock_input_api.change.RepositoryRoot = lambda: 'chromium/src'
1098 msgs = PRESUBMIT.CheckNoUpstreamDepsOnClank(
1099 mock_input_api, mock_output_api)
1100 self.assertEqual(1, len(msgs),
1101 'Expected %d items, found %d: %s'
1102 % (1, len(msgs), msgs))
1103 self.assertEqual(3, len(msgs[0].items),
1104 'Expected %d items, found %d: %s'
1105 % (3, len(msgs[0].items), msgs[0].items))
1106 self.assertTrue(any('BUILD.gn:2' in item for item in msgs[0].items),
1107 'BUILD.gn not found in errors')
1109 any('chrome/android/BUILD.gn:1' in item for item in msgs[0].items),
1110 'chrome/android/BUILD.gn:1 not found in errors')
1112 any('chrome/chrome_java_deps.gni:2' in item for item in msgs[0].items),
1113 'chrome/chrome_java_deps.gni:2 not found in errors')
1115 def testAllowsComments(self):
1116 mock_input_api = MockInputApi()
1117 mock_output_api = MockOutputApi()
1119 mock_input_api.files = [
1120 MockAffectedFile('BUILD.gn', [
1121 '# real implementation in //clank/target:test',
1124 mock_input_api.change.RepositoryRoot = lambda: 'chromium/src'
1125 msgs = PRESUBMIT.CheckNoUpstreamDepsOnClank(
1126 mock_input_api, mock_output_api)
1127 self.assertEqual(0, len(msgs),
1128 'Expected %d items, found %d: %s'
1129 % (0, len(msgs), msgs))
1131 def testOnlyChecksBuildFiles(self):
1132 mock_input_api = MockInputApi()
1133 mock_output_api = MockOutputApi()
1135 mock_input_api.files = [
1136 MockAffectedFile('README.md', [
1137 'DEPS = [ "//clank/target:test" ]'
1139 MockAffectedFile('chrome/android/java/file.java', [
1140 '//clank/ only function'
1143 mock_input_api.change.RepositoryRoot = lambda: 'chromium/src'
1144 msgs = PRESUBMIT.CheckNoUpstreamDepsOnClank(
1145 mock_input_api, mock_output_api)
1146 self.assertEqual(0, len(msgs),
1147 'Expected %d items, found %d: %s'
1148 % (0, len(msgs), msgs))
1150 def testValidDepFromDownstream(self):
1151 mock_input_api = MockInputApi()
1152 mock_output_api = MockOutputApi()
1154 mock_input_api.files = [
1155 MockAffectedFile('BUILD.gn', [
1157 ' "//clank/target:test",',
1160 MockAffectedFile('java/BUILD.gn', [
1161 'DEPS = [ "//clank/target:test" ]'
1164 mock_input_api.change.RepositoryRoot = lambda: 'chromium/src/clank'
1165 msgs = PRESUBMIT.CheckNoUpstreamDepsOnClank(
1166 mock_input_api, mock_output_api)
1167 self.assertEqual(0, len(msgs),
1168 'Expected %d items, found %d: %s'
1169 % (0, len(msgs), msgs))
1171 class AndroidDeprecatedJUnitFrameworkTest(unittest.TestCase):
1172 def testCheckAndroidTestJUnitFramework(self):
1173 mock_input_api = MockInputApi()
1174 mock_output_api = MockOutputApi()
1176 mock_input_api.files = [
1177 MockAffectedFile('LalaLand.java', [
1180 MockAffectedFile('CorrectUsage.java', [
1181 'import org.junit.ABC',
1182 'import org.junit.XYZ;',
1184 MockAffectedFile('UsedDeprecatedJUnit.java', [
1185 'import junit.framework.*;',
1187 MockAffectedFile('UsedDeprecatedJUnitAssert.java', [
1188 'import junit.framework.Assert;',
1191 msgs = PRESUBMIT._CheckAndroidTestJUnitFrameworkImport(
1192 mock_input_api, mock_output_api)
1193 self.assertEqual(1, len(msgs),
1194 'Expected %d items, found %d: %s'
1195 % (1, len(msgs), msgs))
1196 self.assertEqual(2, len(msgs[0].items),
1197 'Expected %d items, found %d: %s'
1198 % (2, len(msgs[0].items), msgs[0].items))
1199 self.assertTrue('UsedDeprecatedJUnit.java:1' in msgs[0].items,
1200 'UsedDeprecatedJUnit.java not found in errors')
1201 self.assertTrue('UsedDeprecatedJUnitAssert.java:1'
1203 'UsedDeprecatedJUnitAssert not found in errors')
1206 class AndroidJUnitBaseClassTest(unittest.TestCase):
1207 def testCheckAndroidTestJUnitBaseClass(self):
1208 mock_input_api = MockInputApi()
1209 mock_output_api = MockOutputApi()
1211 mock_input_api.files = [
1212 MockAffectedFile('LalaLand.java', [
1215 MockAffectedFile('CorrectTest.java', [
1216 '@RunWith(ABC.class);'
1217 'public class CorrectTest {',
1220 MockAffectedFile('HistoricallyIncorrectTest.java', [
1221 'public class Test extends BaseCaseA {',
1224 'public class Test extends BaseCaseB {',
1227 MockAffectedFile('CorrectTestWithInterface.java', [
1228 '@RunWith(ABC.class);'
1229 'public class CorrectTest implement Interface {',
1232 MockAffectedFile('IncorrectTest.java', [
1233 'public class IncorrectTest extends TestCase {',
1236 MockAffectedFile('IncorrectWithInterfaceTest.java', [
1237 'public class Test implements X extends BaseClass {',
1240 MockAffectedFile('IncorrectMultiLineTest.java', [
1241 'public class Test implements X, Y, Z',
1242 ' extends TestBase {',
1246 msgs = PRESUBMIT._CheckAndroidTestJUnitInheritance(
1247 mock_input_api, mock_output_api)
1248 self.assertEqual(1, len(msgs),
1249 'Expected %d items, found %d: %s'
1250 % (1, len(msgs), msgs))
1251 self.assertEqual(3, len(msgs[0].items),
1252 'Expected %d items, found %d: %s'
1253 % (3, len(msgs[0].items), msgs[0].items))
1254 self.assertTrue('IncorrectTest.java:1' in msgs[0].items,
1255 'IncorrectTest not found in errors')
1256 self.assertTrue('IncorrectWithInterfaceTest.java:1'
1258 'IncorrectWithInterfaceTest not found in errors')
1259 self.assertTrue('IncorrectMultiLineTest.java:2' in msgs[0].items,
1260 'IncorrectMultiLineTest not found in errors')
1262 class AndroidDebuggableBuildTest(unittest.TestCase):
1264 def testCheckAndroidDebuggableBuild(self):
1265 mock_input_api = MockInputApi()
1266 mock_output_api = MockOutputApi()
1268 mock_input_api.files = [
1269 MockAffectedFile('RandomStuff.java', [
1272 MockAffectedFile('CorrectUsage.java', [
1273 'import org.chromium.base.BuildInfo;',
1274 'some random stuff',
1275 'boolean isOsDebuggable = BuildInfo.isDebugAndroid();',
1277 MockAffectedFile('JustCheckUserdebugBuild.java', [
1278 'import android.os.Build;',
1279 'some random stuff',
1280 'boolean isOsDebuggable = Build.TYPE.equals("userdebug")',
1282 MockAffectedFile('JustCheckEngineeringBuild.java', [
1283 'import android.os.Build;',
1284 'some random stuff',
1285 'boolean isOsDebuggable = "eng".equals(Build.TYPE)',
1287 MockAffectedFile('UsedBuildType.java', [
1288 'import android.os.Build;',
1289 'some random stuff',
1290 'boolean isOsDebuggable = Build.TYPE.equals("userdebug")'
1291 '|| "eng".equals(Build.TYPE)',
1293 MockAffectedFile('UsedExplicitBuildType.java', [
1294 'some random stuff',
1295 'boolean isOsDebuggable = android.os.Build.TYPE.equals("userdebug")'
1296 '|| "eng".equals(android.os.Build.TYPE)',
1300 msgs = PRESUBMIT._CheckAndroidDebuggableBuild(
1301 mock_input_api, mock_output_api)
1302 self.assertEqual(1, len(msgs),
1303 'Expected %d items, found %d: %s'
1304 % (1, len(msgs), msgs))
1305 self.assertEqual(4, len(msgs[0].items),
1306 'Expected %d items, found %d: %s'
1307 % (4, len(msgs[0].items), msgs[0].items))
1308 self.assertTrue('JustCheckUserdebugBuild.java:3' in msgs[0].items)
1309 self.assertTrue('JustCheckEngineeringBuild.java:3' in msgs[0].items)
1310 self.assertTrue('UsedBuildType.java:3' in msgs[0].items)
1311 self.assertTrue('UsedExplicitBuildType.java:2' in msgs[0].items)
1314 class LogUsageTest(unittest.TestCase):
1316 def testCheckAndroidCrLogUsage(self):
1317 mock_input_api = MockInputApi()
1318 mock_output_api = MockOutputApi()
1320 mock_input_api.files = [
1321 MockAffectedFile('RandomStuff.java', [
1324 MockAffectedFile('HasAndroidLog.java', [
1325 'import android.util.Log;',
1326 'some random stuff',
1327 'Log.d("TAG", "foo");',
1329 MockAffectedFile('HasExplicitUtilLog.java', [
1330 'some random stuff',
1331 'android.util.Log.d("TAG", "foo");',
1333 MockAffectedFile('IsInBasePackage.java', [
1334 'package org.chromium.base;',
1335 'private static final String TAG = "cr_Foo";',
1336 'Log.d(TAG, "foo");',
1338 MockAffectedFile('IsInBasePackageButImportsLog.java', [
1339 'package org.chromium.base;',
1340 'import android.util.Log;',
1341 'private static final String TAG = "cr_Foo";',
1342 'Log.d(TAG, "foo");',
1344 MockAffectedFile('HasBothLog.java', [
1345 'import org.chromium.base.Log;',
1346 'some random stuff',
1347 'private static final String TAG = "cr_Foo";',
1348 'Log.d(TAG, "foo");',
1349 'android.util.Log.d("TAG", "foo");',
1351 MockAffectedFile('HasCorrectTag.java', [
1352 'import org.chromium.base.Log;',
1353 'some random stuff',
1354 'private static final String TAG = "cr_Foo";',
1355 'Log.d(TAG, "foo");',
1357 MockAffectedFile('HasOldTag.java', [
1358 'import org.chromium.base.Log;',
1359 'some random stuff',
1360 'private static final String TAG = "cr.Foo";',
1361 'Log.d(TAG, "foo");',
1363 MockAffectedFile('HasDottedTag.java', [
1364 'import org.chromium.base.Log;',
1365 'some random stuff',
1366 'private static final String TAG = "cr_foo.bar";',
1367 'Log.d(TAG, "foo");',
1369 MockAffectedFile('HasDottedTagPublic.java', [
1370 'import org.chromium.base.Log;',
1371 'some random stuff',
1372 'public static final String TAG = "cr_foo.bar";',
1373 'Log.d(TAG, "foo");',
1375 MockAffectedFile('HasNoTagDecl.java', [
1376 'import org.chromium.base.Log;',
1377 'some random stuff',
1378 'Log.d(TAG, "foo");',
1380 MockAffectedFile('HasIncorrectTagDecl.java', [
1381 'import org.chromium.base.Log;',
1382 'private static final String TAHG = "cr_Foo";',
1383 'some random stuff',
1384 'Log.d(TAG, "foo");',
1386 MockAffectedFile('HasInlineTag.java', [
1387 'import org.chromium.base.Log;',
1388 'some random stuff',
1389 'private static final String TAG = "cr_Foo";',
1390 'Log.d("TAG", "foo");',
1392 MockAffectedFile('HasInlineTagWithSpace.java', [
1393 'import org.chromium.base.Log;',
1394 'some random stuff',
1395 'private static final String TAG = "cr_Foo";',
1396 'Log.d("log message", "foo");',
1398 MockAffectedFile('HasUnprefixedTag.java', [
1399 'import org.chromium.base.Log;',
1400 'some random stuff',
1401 'private static final String TAG = "rubbish";',
1402 'Log.d(TAG, "foo");',
1404 MockAffectedFile('HasTooLongTag.java', [
1405 'import org.chromium.base.Log;',
1406 'some random stuff',
1407 'private static final String TAG = "21_charachers_long___";',
1408 'Log.d(TAG, "foo");',
1410 MockAffectedFile('HasTooLongTagWithNoLogCallsInDiff.java', [
1411 'import org.chromium.base.Log;',
1412 'some random stuff',
1413 'private static final String TAG = "21_charachers_long___";',
1417 msgs = PRESUBMIT._CheckAndroidCrLogUsage(
1418 mock_input_api, mock_output_api)
1420 self.assertEqual(5, len(msgs),
1421 'Expected %d items, found %d: %s' % (5, len(msgs), msgs))
1423 # Declaration format
1424 nb = len(msgs[0].items)
1425 self.assertEqual(2, nb,
1426 'Expected %d items, found %d: %s' % (2, nb, msgs[0].items))
1427 self.assertTrue('HasNoTagDecl.java' in msgs[0].items)
1428 self.assertTrue('HasIncorrectTagDecl.java' in msgs[0].items)
1431 nb = len(msgs[1].items)
1432 self.assertEqual(2, nb,
1433 'Expected %d items, found %d: %s' % (2, nb, msgs[1].items))
1434 self.assertTrue('HasTooLongTag.java' in msgs[1].items)
1435 self.assertTrue('HasTooLongTagWithNoLogCallsInDiff.java' in msgs[1].items)
1437 # Tag must be a variable named TAG
1438 nb = len(msgs[2].items)
1439 self.assertEqual(3, nb,
1440 'Expected %d items, found %d: %s' % (3, nb, msgs[2].items))
1441 self.assertTrue('HasBothLog.java:5' in msgs[2].items)
1442 self.assertTrue('HasInlineTag.java:4' in msgs[2].items)
1443 self.assertTrue('HasInlineTagWithSpace.java:4' in msgs[2].items)
1446 nb = len(msgs[3].items)
1447 self.assertEqual(3, nb,
1448 'Expected %d items, found %d: %s' % (3, nb, msgs[3].items))
1449 self.assertTrue('HasAndroidLog.java:3' in msgs[3].items)
1450 self.assertTrue('HasExplicitUtilLog.java:2' in msgs[3].items)
1451 self.assertTrue('IsInBasePackageButImportsLog.java:4' in msgs[3].items)
1453 # Tag must not contain
1454 nb = len(msgs[4].items)
1455 self.assertEqual(3, nb,
1456 'Expected %d items, found %d: %s' % (2, nb, msgs[4].items))
1457 self.assertTrue('HasDottedTag.java' in msgs[4].items)
1458 self.assertTrue('HasDottedTagPublic.java' in msgs[4].items)
1459 self.assertTrue('HasOldTag.java' in msgs[4].items)
1462 class GoogleAnswerUrlFormatTest(unittest.TestCase):
1464 def testCatchAnswerUrlId(self):
1465 input_api = MockInputApi()
1467 MockFile('somewhere/file.cc',
1469 ' "https://support.google.com/chrome/answer/123456";']),
1470 MockFile('somewhere_else/file.cc',
1472 ' "https://support.google.com/chrome/a/answer/123456";']),
1475 warnings = PRESUBMIT.CheckGoogleSupportAnswerUrlOnUpload(
1476 input_api, MockOutputApi())
1477 self.assertEqual(1, len(warnings))
1478 self.assertEqual(2, len(warnings[0].items))
1480 def testAllowAnswerUrlParam(self):
1481 input_api = MockInputApi()
1483 MockFile('somewhere/file.cc',
1485 ' "https://support.google.com/chrome/?p=cpn_crash_reports";']),
1488 warnings = PRESUBMIT.CheckGoogleSupportAnswerUrlOnUpload(
1489 input_api, MockOutputApi())
1490 self.assertEqual(0, len(warnings))
1493 class HardcodedGoogleHostsTest(unittest.TestCase):
1495 def testWarnOnAssignedLiterals(self):
1496 input_api = MockInputApi()
1498 MockFile('content/file.cc',
1499 ['char* host = "https://www.google.com";']),
1500 MockFile('content/file.cc',
1501 ['char* host = "https://www.googleapis.com";']),
1502 MockFile('content/file.cc',
1503 ['char* host = "https://clients1.google.com";']),
1506 warnings = PRESUBMIT.CheckHardcodedGoogleHostsInLowerLayers(
1507 input_api, MockOutputApi())
1508 self.assertEqual(1, len(warnings))
1509 self.assertEqual(3, len(warnings[0].items))
1511 def testAllowInComment(self):
1512 input_api = MockInputApi()
1514 MockFile('content/file.cc',
1515 ['char* host = "https://www.aol.com"; // google.com'])
1518 warnings = PRESUBMIT.CheckHardcodedGoogleHostsInLowerLayers(
1519 input_api, MockOutputApi())
1520 self.assertEqual(0, len(warnings))
1523 class ChromeOsSyncedPrefRegistrationTest(unittest.TestCase):
1525 def testWarnsOnChromeOsDirectories(self):
1526 input_api = MockInputApi()
1528 MockFile('ash/file.cc',
1529 ['PrefRegistrySyncable::SYNCABLE_PREF']),
1530 MockFile('chrome/browser/chromeos/file.cc',
1531 ['PrefRegistrySyncable::SYNCABLE_PREF']),
1532 MockFile('chromeos/file.cc',
1533 ['PrefRegistrySyncable::SYNCABLE_PREF']),
1534 MockFile('components/arc/file.cc',
1535 ['PrefRegistrySyncable::SYNCABLE_PREF']),
1536 MockFile('components/exo/file.cc',
1537 ['PrefRegistrySyncable::SYNCABLE_PREF']),
1539 warnings = PRESUBMIT.CheckChromeOsSyncedPrefRegistration(
1540 input_api, MockOutputApi())
1541 self.assertEqual(1, len(warnings))
1543 def testDoesNotWarnOnSyncOsPref(self):
1544 input_api = MockInputApi()
1546 MockFile('chromeos/file.cc',
1547 ['PrefRegistrySyncable::SYNCABLE_OS_PREF']),
1549 warnings = PRESUBMIT.CheckChromeOsSyncedPrefRegistration(
1550 input_api, MockOutputApi())
1551 self.assertEqual(0, len(warnings))
1553 def testDoesNotWarnOnCrossPlatformDirectories(self):
1554 input_api = MockInputApi()
1556 MockFile('chrome/browser/ui/file.cc',
1557 ['PrefRegistrySyncable::SYNCABLE_PREF']),
1558 MockFile('components/sync/file.cc',
1559 ['PrefRegistrySyncable::SYNCABLE_PREF']),
1560 MockFile('content/browser/file.cc',
1561 ['PrefRegistrySyncable::SYNCABLE_PREF']),
1563 warnings = PRESUBMIT.CheckChromeOsSyncedPrefRegistration(
1564 input_api, MockOutputApi())
1565 self.assertEqual(0, len(warnings))
1567 def testSeparateWarningForPriorityPrefs(self):
1568 input_api = MockInputApi()
1570 MockFile('chromeos/file.cc',
1571 ['PrefRegistrySyncable::SYNCABLE_PREF',
1572 'PrefRegistrySyncable::SYNCABLE_PRIORITY_PREF']),
1574 warnings = PRESUBMIT.CheckChromeOsSyncedPrefRegistration(
1575 input_api, MockOutputApi())
1576 self.assertEqual(2, len(warnings))
1579 class ForwardDeclarationTest(unittest.TestCase):
1580 def testCheckHeadersOnlyOutsideThirdParty(self):
1581 mock_input_api = MockInputApi()
1582 mock_input_api.files = [
1583 MockAffectedFile('somewhere/file.cc', [
1586 MockAffectedFile('third_party/header.h', [
1590 warnings = PRESUBMIT.CheckUselessForwardDeclarations(mock_input_api,
1592 self.assertEqual(0, len(warnings))
1594 def testNoNestedDeclaration(self):
1595 mock_input_api = MockInputApi()
1596 mock_input_api.files = [
1597 MockAffectedFile('somewhere/header.h', [
1598 'class SomeClass {',
1600 ' class NotAMatch;',
1604 warnings = PRESUBMIT.CheckUselessForwardDeclarations(mock_input_api,
1606 self.assertEqual(0, len(warnings))
1608 def testSubStrings(self):
1609 mock_input_api = MockInputApi()
1610 mock_input_api.files = [
1611 MockAffectedFile('somewhere/header.h', [
1612 'class NotUsefulClass;',
1613 'struct SomeStruct;',
1615 'SomeStructPtr *p2;'
1618 warnings = PRESUBMIT.CheckUselessForwardDeclarations(mock_input_api,
1620 self.assertEqual(2, len(warnings))
1622 def testUselessForwardDeclaration(self):
1623 mock_input_api = MockInputApi()
1624 mock_input_api.files = [
1625 MockAffectedFile('somewhere/header.h', [
1626 'class DummyClass;',
1627 'struct DummyStruct;',
1628 'class UsefulClass;',
1629 'std::unique_ptr<UsefulClass> p;'
1632 warnings = PRESUBMIT.CheckUselessForwardDeclarations(mock_input_api,
1634 self.assertEqual(2, len(warnings))
1636 def testBlinkHeaders(self):
1637 mock_input_api = MockInputApi()
1638 mock_input_api.files = [
1639 MockAffectedFile('third_party/blink/header.h', [
1640 'class DummyClass;',
1641 'struct DummyStruct;',
1643 MockAffectedFile('third_party\\blink\\header.h', [
1644 'class DummyClass;',
1645 'struct DummyStruct;',
1648 warnings = PRESUBMIT.CheckUselessForwardDeclarations(mock_input_api,
1650 self.assertEqual(4, len(warnings))
1653 class RelativeIncludesTest(unittest.TestCase):
1654 def testThirdPartyNotWebKitIgnored(self):
1655 mock_input_api = MockInputApi()
1656 mock_input_api.files = [
1657 MockAffectedFile('third_party/test.cpp', '#include "../header.h"'),
1658 MockAffectedFile('third_party/test/test.cpp', '#include "../header.h"'),
1661 mock_output_api = MockOutputApi()
1663 errors = PRESUBMIT.CheckForRelativeIncludes(
1664 mock_input_api, mock_output_api)
1665 self.assertEqual(0, len(errors))
1667 def testNonCppFileIgnored(self):
1668 mock_input_api = MockInputApi()
1669 mock_input_api.files = [
1670 MockAffectedFile('test.py', '#include "../header.h"'),
1673 mock_output_api = MockOutputApi()
1675 errors = PRESUBMIT.CheckForRelativeIncludes(
1676 mock_input_api, mock_output_api)
1677 self.assertEqual(0, len(errors))
1679 def testInnocuousChangesAllowed(self):
1680 mock_input_api = MockInputApi()
1681 mock_input_api.files = [
1682 MockAffectedFile('test.cpp', '#include "header.h"'),
1683 MockAffectedFile('test2.cpp', '../'),
1686 mock_output_api = MockOutputApi()
1688 errors = PRESUBMIT.CheckForRelativeIncludes(
1689 mock_input_api, mock_output_api)
1690 self.assertEqual(0, len(errors))
1692 def testRelativeIncludeNonWebKitProducesError(self):
1693 mock_input_api = MockInputApi()
1694 mock_input_api.files = [
1695 MockAffectedFile('test.cpp', ['#include "../header.h"']),
1698 mock_output_api = MockOutputApi()
1700 errors = PRESUBMIT.CheckForRelativeIncludes(
1701 mock_input_api, mock_output_api)
1702 self.assertEqual(1, len(errors))
1704 def testRelativeIncludeWebKitProducesError(self):
1705 mock_input_api = MockInputApi()
1706 mock_input_api.files = [
1707 MockAffectedFile('third_party/blink/test.cpp',
1708 ['#include "../header.h']),
1711 mock_output_api = MockOutputApi()
1713 errors = PRESUBMIT.CheckForRelativeIncludes(
1714 mock_input_api, mock_output_api)
1715 self.assertEqual(1, len(errors))
1718 class CCIncludeTest(unittest.TestCase):
1719 def testThirdPartyNotBlinkIgnored(self):
1720 mock_input_api = MockInputApi()
1721 mock_input_api.files = [
1722 MockAffectedFile('third_party/test.cpp', '#include "file.cc"'),
1725 mock_output_api = MockOutputApi()
1727 errors = PRESUBMIT.CheckForCcIncludes(
1728 mock_input_api, mock_output_api)
1729 self.assertEqual(0, len(errors))
1731 def testPythonFileIgnored(self):
1732 mock_input_api = MockInputApi()
1733 mock_input_api.files = [
1734 MockAffectedFile('test.py', '#include "file.cc"'),
1737 mock_output_api = MockOutputApi()
1739 errors = PRESUBMIT.CheckForCcIncludes(
1740 mock_input_api, mock_output_api)
1741 self.assertEqual(0, len(errors))
1743 def testIncFilesAccepted(self):
1744 mock_input_api = MockInputApi()
1745 mock_input_api.files = [
1746 MockAffectedFile('test.py', '#include "file.inc"'),
1749 mock_output_api = MockOutputApi()
1751 errors = PRESUBMIT.CheckForCcIncludes(
1752 mock_input_api, mock_output_api)
1753 self.assertEqual(0, len(errors))
1755 def testInnocuousChangesAllowed(self):
1756 mock_input_api = MockInputApi()
1757 mock_input_api.files = [
1758 MockAffectedFile('test.cpp', '#include "header.h"'),
1759 MockAffectedFile('test2.cpp', 'Something "file.cc"'),
1762 mock_output_api = MockOutputApi()
1764 errors = PRESUBMIT.CheckForCcIncludes(
1765 mock_input_api, mock_output_api)
1766 self.assertEqual(0, len(errors))
1768 def testCcIncludeNonBlinkProducesError(self):
1769 mock_input_api = MockInputApi()
1770 mock_input_api.files = [
1771 MockAffectedFile('test.cpp', ['#include "file.cc"']),
1774 mock_output_api = MockOutputApi()
1776 errors = PRESUBMIT.CheckForCcIncludes(
1777 mock_input_api, mock_output_api)
1778 self.assertEqual(1, len(errors))
1780 def testCppIncludeBlinkProducesError(self):
1781 mock_input_api = MockInputApi()
1782 mock_input_api.files = [
1783 MockAffectedFile('third_party/blink/test.cpp',
1784 ['#include "foo/file.cpp"']),
1787 mock_output_api = MockOutputApi()
1789 errors = PRESUBMIT.CheckForCcIncludes(
1790 mock_input_api, mock_output_api)
1791 self.assertEqual(1, len(errors))
1794 class GnGlobForwardTest(unittest.TestCase):
1795 def testAddBareGlobs(self):
1796 mock_input_api = MockInputApi()
1797 mock_input_api.files = [
1798 MockAffectedFile('base/stuff.gni', [
1799 'forward_variables_from(invoker, "*")']),
1800 MockAffectedFile('base/BUILD.gn', [
1801 'forward_variables_from(invoker, "*")']),
1803 warnings = PRESUBMIT.CheckGnGlobForward(mock_input_api, MockOutputApi())
1804 self.assertEqual(1, len(warnings))
1805 msg = '\n'.join(warnings[0].items)
1806 self.assertIn('base/stuff.gni', msg)
1807 # Should not check .gn files. Local templates don't need to care about
1808 # visibility / testonly.
1809 self.assertNotIn('base/BUILD.gn', msg)
1811 def testValidUses(self):
1812 mock_input_api = MockInputApi()
1813 mock_input_api.files = [
1814 MockAffectedFile('base/stuff.gni', [
1815 'forward_variables_from(invoker, "*", [])']),
1816 MockAffectedFile('base/stuff2.gni', [
1817 'forward_variables_from(invoker, "*", TESTONLY_AND_VISIBILITY)']),
1818 MockAffectedFile('base/stuff3.gni', [
1819 'forward_variables_from(invoker, [ "testonly" ])']),
1821 warnings = PRESUBMIT.CheckGnGlobForward(mock_input_api, MockOutputApi())
1822 self.assertEqual([], warnings)
1825 class NewHeaderWithoutGnChangeTest(unittest.TestCase):
1826 def testAddHeaderWithoutGn(self):
1827 mock_input_api = MockInputApi()
1828 mock_input_api.files = [
1829 MockAffectedFile('base/stuff.h', ''),
1831 warnings = PRESUBMIT.CheckNewHeaderWithoutGnChangeOnUpload(
1832 mock_input_api, MockOutputApi())
1833 self.assertEqual(1, len(warnings))
1834 self.assertTrue('base/stuff.h' in warnings[0].items)
1836 def testModifyHeader(self):
1837 mock_input_api = MockInputApi()
1838 mock_input_api.files = [
1839 MockAffectedFile('base/stuff.h', '', action='M'),
1841 warnings = PRESUBMIT.CheckNewHeaderWithoutGnChangeOnUpload(
1842 mock_input_api, MockOutputApi())
1843 self.assertEqual(0, len(warnings))
1845 def testDeleteHeader(self):
1846 mock_input_api = MockInputApi()
1847 mock_input_api.files = [
1848 MockAffectedFile('base/stuff.h', '', action='D'),
1850 warnings = PRESUBMIT.CheckNewHeaderWithoutGnChangeOnUpload(
1851 mock_input_api, MockOutputApi())
1852 self.assertEqual(0, len(warnings))
1854 def testAddHeaderWithGn(self):
1855 mock_input_api = MockInputApi()
1856 mock_input_api.files = [
1857 MockAffectedFile('base/stuff.h', ''),
1858 MockAffectedFile('base/BUILD.gn', 'stuff.h'),
1860 warnings = PRESUBMIT.CheckNewHeaderWithoutGnChangeOnUpload(
1861 mock_input_api, MockOutputApi())
1862 self.assertEqual(0, len(warnings))
1864 def testAddHeaderWithGni(self):
1865 mock_input_api = MockInputApi()
1866 mock_input_api.files = [
1867 MockAffectedFile('base/stuff.h', ''),
1868 MockAffectedFile('base/files.gni', 'stuff.h'),
1870 warnings = PRESUBMIT.CheckNewHeaderWithoutGnChangeOnUpload(
1871 mock_input_api, MockOutputApi())
1872 self.assertEqual(0, len(warnings))
1874 def testAddHeaderWithOther(self):
1875 mock_input_api = MockInputApi()
1876 mock_input_api.files = [
1877 MockAffectedFile('base/stuff.h', ''),
1878 MockAffectedFile('base/stuff.cc', 'stuff.h'),
1880 warnings = PRESUBMIT.CheckNewHeaderWithoutGnChangeOnUpload(
1881 mock_input_api, MockOutputApi())
1882 self.assertEqual(1, len(warnings))
1884 def testAddHeaderWithWrongGn(self):
1885 mock_input_api = MockInputApi()
1886 mock_input_api.files = [
1887 MockAffectedFile('base/stuff.h', ''),
1888 MockAffectedFile('base/BUILD.gn', 'stuff_h'),
1890 warnings = PRESUBMIT.CheckNewHeaderWithoutGnChangeOnUpload(
1891 mock_input_api, MockOutputApi())
1892 self.assertEqual(1, len(warnings))
1894 def testAddHeadersWithGn(self):
1895 mock_input_api = MockInputApi()
1896 mock_input_api.files = [
1897 MockAffectedFile('base/stuff.h', ''),
1898 MockAffectedFile('base/another.h', ''),
1899 MockAffectedFile('base/BUILD.gn', 'another.h\nstuff.h'),
1901 warnings = PRESUBMIT.CheckNewHeaderWithoutGnChangeOnUpload(
1902 mock_input_api, MockOutputApi())
1903 self.assertEqual(0, len(warnings))
1905 def testAddHeadersWithWrongGn(self):
1906 mock_input_api = MockInputApi()
1907 mock_input_api.files = [
1908 MockAffectedFile('base/stuff.h', ''),
1909 MockAffectedFile('base/another.h', ''),
1910 MockAffectedFile('base/BUILD.gn', 'another_h\nstuff.h'),
1912 warnings = PRESUBMIT.CheckNewHeaderWithoutGnChangeOnUpload(
1913 mock_input_api, MockOutputApi())
1914 self.assertEqual(1, len(warnings))
1915 self.assertFalse('base/stuff.h' in warnings[0].items)
1916 self.assertTrue('base/another.h' in warnings[0].items)
1918 def testAddHeadersWithWrongGn2(self):
1919 mock_input_api = MockInputApi()
1920 mock_input_api.files = [
1921 MockAffectedFile('base/stuff.h', ''),
1922 MockAffectedFile('base/another.h', ''),
1923 MockAffectedFile('base/BUILD.gn', 'another_h\nstuff_h'),
1925 warnings = PRESUBMIT.CheckNewHeaderWithoutGnChangeOnUpload(
1926 mock_input_api, MockOutputApi())
1927 self.assertEqual(1, len(warnings))
1928 self.assertTrue('base/stuff.h' in warnings[0].items)
1929 self.assertTrue('base/another.h' in warnings[0].items)
1932 class CorrectProductNameInMessagesTest(unittest.TestCase):
1933 def testProductNameInDesc(self):
1934 mock_input_api = MockInputApi()
1935 mock_input_api.files = [
1936 MockAffectedFile('chrome/app/google_chrome_strings.grd', [
1937 '<message name="Foo" desc="Welcome to Chrome">',
1938 ' Welcome to Chrome!',
1941 MockAffectedFile('chrome/app/chromium_strings.grd', [
1942 '<message name="Bar" desc="Welcome to Chrome">',
1943 ' Welcome to Chromium!',
1947 warnings = PRESUBMIT.CheckCorrectProductNameInMessages(
1948 mock_input_api, MockOutputApi())
1949 self.assertEqual(0, len(warnings))
1951 def testChromeInChromium(self):
1952 mock_input_api = MockInputApi()
1953 mock_input_api.files = [
1954 MockAffectedFile('chrome/app/google_chrome_strings.grd', [
1955 '<message name="Foo" desc="Welcome to Chrome">',
1956 ' Welcome to Chrome!',
1959 MockAffectedFile('chrome/app/chromium_strings.grd', [
1960 '<message name="Bar" desc="Welcome to Chrome">',
1961 ' Welcome to Chrome!',
1965 warnings = PRESUBMIT.CheckCorrectProductNameInMessages(
1966 mock_input_api, MockOutputApi())
1967 self.assertEqual(1, len(warnings))
1968 self.assertTrue('chrome/app/chromium_strings.grd' in warnings[0].items[0])
1970 def testChromiumInChrome(self):
1971 mock_input_api = MockInputApi()
1972 mock_input_api.files = [
1973 MockAffectedFile('chrome/app/google_chrome_strings.grd', [
1974 '<message name="Foo" desc="Welcome to Chrome">',
1975 ' Welcome to Chromium!',
1978 MockAffectedFile('chrome/app/chromium_strings.grd', [
1979 '<message name="Bar" desc="Welcome to Chrome">',
1980 ' Welcome to Chromium!',
1984 warnings = PRESUBMIT.CheckCorrectProductNameInMessages(
1985 mock_input_api, MockOutputApi())
1986 self.assertEqual(1, len(warnings))
1988 'chrome/app/google_chrome_strings.grd:2' in warnings[0].items[0])
1990 def testMultipleInstances(self):
1991 mock_input_api = MockInputApi()
1992 mock_input_api.files = [
1993 MockAffectedFile('chrome/app/chromium_strings.grd', [
1994 '<message name="Bar" desc="Welcome to Chrome">',
1995 ' Welcome to Chrome!',
1997 '<message name="Baz" desc="A correct message">',
1998 ' Chromium is the software you are using.',
2000 '<message name="Bat" desc="An incorrect message">',
2001 ' Google Chrome is the software you are using.',
2005 warnings = PRESUBMIT.CheckCorrectProductNameInMessages(
2006 mock_input_api, MockOutputApi())
2007 self.assertEqual(1, len(warnings))
2009 'chrome/app/chromium_strings.grd:2' in warnings[0].items[0])
2011 'chrome/app/chromium_strings.grd:8' in warnings[0].items[1])
2013 def testMultipleWarnings(self):
2014 mock_input_api = MockInputApi()
2015 mock_input_api.files = [
2016 MockAffectedFile('chrome/app/chromium_strings.grd', [
2017 '<message name="Bar" desc="Welcome to Chrome">',
2018 ' Welcome to Chrome!',
2020 '<message name="Baz" desc="A correct message">',
2021 ' Chromium is the software you are using.',
2023 '<message name="Bat" desc="An incorrect message">',
2024 ' Google Chrome is the software you are using.',
2027 MockAffectedFile('components/components_google_chrome_strings.grd', [
2028 '<message name="Bar" desc="Welcome to Chrome">',
2029 ' Welcome to Chrome!',
2031 '<message name="Baz" desc="A correct message">',
2032 ' Chromium is the software you are using.',
2034 '<message name="Bat" desc="An incorrect message">',
2035 ' Google Chrome is the software you are using.',
2039 warnings = PRESUBMIT.CheckCorrectProductNameInMessages(
2040 mock_input_api, MockOutputApi())
2041 self.assertEqual(2, len(warnings))
2043 'components/components_google_chrome_strings.grd:5'
2044 in warnings[0].items[0])
2046 'chrome/app/chromium_strings.grd:2' in warnings[1].items[0])
2048 'chrome/app/chromium_strings.grd:8' in warnings[1].items[1])
2051 class ServiceManifestOwnerTest(unittest.TestCase):
2052 def testServiceManifestChangeNeedsSecurityOwner(self):
2053 mock_input_api = MockInputApi()
2054 mock_input_api.files = [
2055 MockAffectedFile('services/goat/public/cpp/manifest.cc',
2057 '#include "services/goat/public/cpp/manifest.h"',
2058 'const service_manager::Manifest& GetManifest() {}',
2060 mock_output_api = MockOutputApi()
2061 errors = PRESUBMIT.CheckSecurityOwners(
2062 mock_input_api, mock_output_api)
2063 self.assertEqual(1, len(errors))
2065 'Found OWNERS files that need to be updated for IPC security review ' +
2066 'coverage.\nPlease update the OWNERS files below:', errors[0].message)
2068 def testNonServiceManifestSourceChangesDoNotRequireSecurityOwner(self):
2069 mock_input_api = MockInputApi()
2070 mock_input_api.files = [
2071 MockAffectedFile('some/non/service/thing/foo_manifest.cc',
2073 'const char kNoEnforcement[] = "not a manifest!";',
2075 mock_output_api = MockOutputApi()
2076 errors = PRESUBMIT.CheckSecurityOwners(
2077 mock_input_api, mock_output_api)
2078 self.assertEqual([], errors)
2081 class FuchsiaSecurityOwnerTest(unittest.TestCase):
2082 def testFidlChangeNeedsSecurityOwner(self):
2083 mock_input_api = MockInputApi()
2084 mock_input_api.files = [
2085 MockAffectedFile('potentially/scary/ipc.fidl',
2089 mock_output_api = MockOutputApi()
2090 errors = PRESUBMIT.CheckSecurityOwners(
2091 mock_input_api, mock_output_api)
2092 self.assertEqual(1, len(errors))
2094 'Found OWNERS files that need to be updated for IPC security review ' +
2095 'coverage.\nPlease update the OWNERS files below:', errors[0].message)
2097 def testComponentManifestV1ChangeNeedsSecurityOwner(self):
2098 mock_input_api = MockInputApi()
2099 mock_input_api.files = [
2100 MockAffectedFile('potentially/scary/v2_manifest.cmx',
2102 '{ "that is no": "manifest!" }'
2104 mock_output_api = MockOutputApi()
2105 errors = PRESUBMIT.CheckSecurityOwners(
2106 mock_input_api, mock_output_api)
2107 self.assertEqual(1, len(errors))
2109 'Found OWNERS files that need to be updated for IPC security review ' +
2110 'coverage.\nPlease update the OWNERS files below:', errors[0].message)
2112 def testComponentManifestV2NeedsSecurityOwner(self):
2113 mock_input_api = MockInputApi()
2114 mock_input_api.files = [
2115 MockAffectedFile('potentially/scary/v2_manifest.cml',
2117 '{ "that is no": "manifest!" }'
2119 mock_output_api = MockOutputApi()
2120 errors = PRESUBMIT.CheckSecurityOwners(
2121 mock_input_api, mock_output_api)
2122 self.assertEqual(1, len(errors))
2124 'Found OWNERS files that need to be updated for IPC security review ' +
2125 'coverage.\nPlease update the OWNERS files below:', errors[0].message)
2127 def testThirdPartyTestsDoNotRequireSecurityOwner(self):
2128 mock_input_api = MockInputApi()
2129 mock_input_api.files = [
2130 MockAffectedFile('third_party/crashpad/test/tests.cmx',
2132 'const char kNoEnforcement[] = "Security?!? Pah!";',
2134 mock_output_api = MockOutputApi()
2135 errors = PRESUBMIT.CheckSecurityOwners(
2136 mock_input_api, mock_output_api)
2137 self.assertEqual([], errors)
2139 def testOtherFuchsiaChangesDoNotRequireSecurityOwner(self):
2140 mock_input_api = MockInputApi()
2141 mock_input_api.files = [
2142 MockAffectedFile('some/non/service/thing/fuchsia_fidl_cml_cmx_magic.cc',
2144 'const char kNoEnforcement[] = "Security?!? Pah!";',
2146 mock_output_api = MockOutputApi()
2147 errors = PRESUBMIT.CheckSecurityOwners(
2148 mock_input_api, mock_output_api)
2149 self.assertEqual([], errors)
2152 class SecurityChangeTest(unittest.TestCase):
2153 class _MockOwnersClient(object):
2154 def ListOwners(self, f):
2155 return ['apple@chromium.org', 'orange@chromium.org']
2157 def _mockChangeOwnerAndReviewers(self, input_api, owner, reviewers):
2158 def __MockOwnerAndReviewers(input_api, email_regexp, approval_needed=False):
2159 return [owner, reviewers]
2160 input_api.canned_checks.GetCodereviewOwnerAndReviewers = \
2161 __MockOwnerAndReviewers
2163 def testDiffGetServiceSandboxType(self):
2164 mock_input_api = MockInputApi()
2165 mock_input_api.files = [
2167 'services/goat/teleporter_host.cc',
2170 'inline content::SandboxType',
2171 'content::GetServiceSandboxType<chrome::mojom::GoatTeleporter>() {',
2172 '#if defined(OS_WIN)',
2173 ' return SandboxType::kGoaty;',
2175 ' return SandboxType::kNoSandbox;',
2176 '#endif // !defined(OS_WIN)',
2181 files_to_functions = PRESUBMIT._GetFilesUsingSecurityCriticalFunctions(
2184 'services/goat/teleporter_host.cc': set([
2185 'content::GetServiceSandboxType<>()'
2189 def testDiffRemovingLine(self):
2190 mock_input_api = MockInputApi()
2191 mock_file = MockAffectedFile('services/goat/teleporter_host.cc', '')
2192 mock_file._scm_diff = """--- old 2020-05-04 14:08:25.000000000 -0400
2193 +++ new 2020-05-04 14:08:32.000000000 -0400
2196 inline content::SandboxType
2197 -content::GetServiceSandboxType<chrome::mojom::GoatTeleporter>() {
2199 return SandboxType::kGoaty;
2201 mock_input_api.files = [mock_file]
2202 files_to_functions = PRESUBMIT._GetFilesUsingSecurityCriticalFunctions(
2205 'services/goat/teleporter_host.cc': set([
2206 'content::GetServiceSandboxType<>()'
2210 def testChangeOwnersMissing(self):
2211 mock_input_api = MockInputApi()
2212 mock_input_api.owners_client = self._MockOwnersClient()
2213 mock_input_api.is_committing = False
2214 mock_input_api.files = [
2215 MockAffectedFile('file.cc', ['GetServiceSandboxType<Goat>(Sandbox)'])
2217 mock_output_api = MockOutputApi()
2218 self._mockChangeOwnerAndReviewers(
2219 mock_input_api, 'owner@chromium.org', ['banana@chromium.org'])
2220 result = PRESUBMIT.CheckSecurityChanges(mock_input_api, mock_output_api)
2221 self.assertEqual(1, len(result))
2222 self.assertEqual(result[0].type, 'notify')
2223 self.assertEqual(result[0].message,
2224 'The following files change calls to security-sensive functions\n' \
2225 'that need to be reviewed by ipc/SECURITY_OWNERS.\n'
2227 ' content::GetServiceSandboxType<>()\n\n')
2229 def testChangeOwnersMissingAtCommit(self):
2230 mock_input_api = MockInputApi()
2231 mock_input_api.owners_client = self._MockOwnersClient()
2232 mock_input_api.is_committing = True
2233 mock_input_api.files = [
2234 MockAffectedFile('file.cc', ['GetServiceSandboxType<mojom::Goat>()'])
2236 mock_output_api = MockOutputApi()
2237 self._mockChangeOwnerAndReviewers(
2238 mock_input_api, 'owner@chromium.org', ['banana@chromium.org'])
2239 result = PRESUBMIT.CheckSecurityChanges(mock_input_api, mock_output_api)
2240 self.assertEqual(1, len(result))
2241 self.assertEqual(result[0].type, 'error')
2242 self.assertEqual(result[0].message,
2243 'The following files change calls to security-sensive functions\n' \
2244 'that need to be reviewed by ipc/SECURITY_OWNERS.\n'
2246 ' content::GetServiceSandboxType<>()\n\n')
2248 def testChangeOwnersPresent(self):
2249 mock_input_api = MockInputApi()
2250 mock_input_api.owners_client = self._MockOwnersClient()
2251 mock_input_api.files = [
2252 MockAffectedFile('file.cc', ['WithSandboxType(Sandbox)'])
2254 mock_output_api = MockOutputApi()
2255 self._mockChangeOwnerAndReviewers(
2256 mock_input_api, 'owner@chromium.org',
2257 ['apple@chromium.org', 'banana@chromium.org'])
2258 result = PRESUBMIT.CheckSecurityChanges(mock_input_api, mock_output_api)
2259 self.assertEqual(0, len(result))
2261 def testChangeOwnerIsSecurityOwner(self):
2262 mock_input_api = MockInputApi()
2263 mock_input_api.owners_client = self._MockOwnersClient()
2264 mock_input_api.files = [
2265 MockAffectedFile('file.cc', ['GetServiceSandboxType<T>(Sandbox)'])
2267 mock_output_api = MockOutputApi()
2268 self._mockChangeOwnerAndReviewers(
2269 mock_input_api, 'orange@chromium.org', ['pear@chromium.org'])
2270 result = PRESUBMIT.CheckSecurityChanges(mock_input_api, mock_output_api)
2271 self.assertEqual(1, len(result))
2274 class BannedTypeCheckTest(unittest.TestCase):
2276 def testBannedCppFunctions(self):
2277 input_api = MockInputApi()
2279 MockFile('some/cpp/problematic/file.cc',
2280 ['using namespace std;']),
2281 MockFile('third_party/blink/problematic/file.cc',
2282 ['GetInterfaceProvider()']),
2283 MockFile('some/cpp/ok/file.cc',
2284 ['using std::string;']),
2285 MockFile('some/cpp/problematic/file2.cc',
2286 ['set_owned_by_client()']),
2287 MockFile('some/cpp/nocheck/file.cc',
2288 ['using namespace std; // nocheck']),
2289 MockFile('some/cpp/comment/file.cc',
2290 [' // A comment about `using namespace std;`']),
2291 MockFile('some/cpp/macro/file.h',
2292 ['DISALLOW_COPY_AND_ASSIGN(foo)']),
2295 results = PRESUBMIT.CheckNoBannedFunctions(input_api, MockOutputApi())
2297 # warnings are results[0], errors are results[1]
2298 self.assertEqual(2, len(results))
2299 self.assertTrue('some/cpp/problematic/file.cc' in results[1].message)
2301 'third_party/blink/problematic/file.cc' in results[0].message)
2302 self.assertTrue('some/cpp/ok/file.cc' not in results[1].message)
2303 self.assertTrue('some/cpp/problematic/file2.cc' in results[0].message)
2304 self.assertFalse('some/cpp/nocheck/file.cc' in results[0].message)
2305 self.assertFalse('some/cpp/nocheck/file.cc' in results[1].message)
2306 self.assertFalse('some/cpp/comment/file.cc' in results[0].message)
2307 self.assertFalse('some/cpp/comment/file.cc' in results[1].message)
2308 self.assertTrue('some/cpp/macro/file.h' in results[0].message)
2309 self.assertFalse('some/cpp/macro/file.h' in results[1].message)
2311 def testBannedIosObjcFunctions(self):
2312 input_api = MockInputApi()
2314 MockFile('some/ios/file.mm',
2315 ['TEST(SomeClassTest, SomeInteraction) {',
2317 MockFile('some/mac/file.mm',
2318 ['TEST(SomeClassTest, SomeInteraction) {',
2320 MockFile('another/ios_file.mm',
2321 ['class SomeTest : public testing::Test {};']),
2322 MockFile('some/ios/file_egtest.mm',
2323 ['- (void)testSomething { EXPECT_OCMOCK_VERIFY(aMock); }']),
2324 MockFile('some/ios/file_unittest.mm',
2325 ['TEST_F(SomeTest, TestThis) { EXPECT_OCMOCK_VERIFY(aMock); }']),
2328 errors = PRESUBMIT.CheckNoBannedFunctions(input_api, MockOutputApi())
2329 self.assertEqual(1, len(errors))
2330 self.assertTrue('some/ios/file.mm' in errors[0].message)
2331 self.assertTrue('another/ios_file.mm' in errors[0].message)
2332 self.assertTrue('some/mac/file.mm' not in errors[0].message)
2333 self.assertTrue('some/ios/file_egtest.mm' in errors[0].message)
2334 self.assertTrue('some/ios/file_unittest.mm' not in errors[0].message)
2336 def testBannedMojoFunctions(self):
2337 input_api = MockInputApi()
2339 MockFile('some/cpp/problematic/file2.cc',
2340 ['mojo::ConvertTo<>']),
2341 MockFile('third_party/blink/ok/file3.cc',
2342 ['mojo::ConvertTo<>']),
2343 MockFile('content/renderer/ok/file3.cc',
2344 ['mojo::ConvertTo<>']),
2347 results = PRESUBMIT.CheckNoBannedFunctions(input_api, MockOutputApi())
2349 # warnings are results[0], errors are results[1]
2350 self.assertEqual(1, len(results))
2351 self.assertTrue('some/cpp/problematic/file2.cc' in results[0].message)
2352 self.assertTrue('third_party/blink/ok/file3.cc' not in results[0].message)
2353 self.assertTrue('content/renderer/ok/file3.cc' not in results[0].message)
2355 def testDeprecatedMojoTypes(self):
2356 ok_paths = ['components/arc']
2357 warning_paths = ['some/cpp']
2358 error_paths = ['third_party/blink', 'content']
2361 'type': 'mojo::AssociatedInterfacePtrInfo<>',
2365 'type': 'mojo::AssociatedInterfaceRequest<>',
2369 'type': 'mojo::InterfacePtr<>',
2373 'type': 'mojo::InterfacePtrInfo<>',
2377 'type': 'mojo::InterfaceRequest<>',
2381 'type': 'mojo::MakeRequest()',
2386 # Build the list of MockFiles considering paths that should trigger warnings
2387 # as well as paths that should trigger errors.
2388 input_api = MockInputApi()
2389 input_api.files = []
2390 for test_case in test_cases:
2391 for path in ok_paths:
2392 input_api.files.append(MockFile(os.path.join(path, test_case['file']),
2393 [test_case['type']]))
2394 for path in warning_paths:
2395 input_api.files.append(MockFile(os.path.join(path, test_case['file']),
2396 [test_case['type']]))
2397 for path in error_paths:
2398 input_api.files.append(MockFile(os.path.join(path, test_case['file']),
2399 [test_case['type']]))
2401 results = PRESUBMIT.CheckNoDeprecatedMojoTypes(input_api, MockOutputApi())
2403 # warnings are results[0], errors are results[1]
2404 self.assertEqual(2, len(results))
2406 for test_case in test_cases:
2407 # Check that no warnings nor errors have been triggered for these paths.
2408 for path in ok_paths:
2409 self.assertFalse(path in results[0].message)
2410 self.assertFalse(path in results[1].message)
2412 # Check warnings have been triggered for these paths.
2413 for path in warning_paths:
2414 self.assertTrue(path in results[0].message)
2415 self.assertFalse(path in results[1].message)
2417 # Check errors have been triggered for these paths.
2418 for path in error_paths:
2419 self.assertFalse(path in results[0].message)
2420 self.assertTrue(path in results[1].message)
2423 class NoProductionCodeUsingTestOnlyFunctionsTest(unittest.TestCase):
2424 def testTruePositives(self):
2425 mock_input_api = MockInputApi()
2426 mock_input_api.files = [
2427 MockFile('some/path/foo.cc', ['foo_for_testing();']),
2428 MockFile('some/path/foo.mm', ['FooForTesting();']),
2429 MockFile('some/path/foo.cxx', ['FooForTests();']),
2430 MockFile('some/path/foo.cpp', ['foo_for_test();']),
2433 results = PRESUBMIT.CheckNoProductionCodeUsingTestOnlyFunctions(
2434 mock_input_api, MockOutputApi())
2435 self.assertEqual(1, len(results))
2436 self.assertEqual(4, len(results[0].items))
2437 self.assertTrue('foo.cc' in results[0].items[0])
2438 self.assertTrue('foo.mm' in results[0].items[1])
2439 self.assertTrue('foo.cxx' in results[0].items[2])
2440 self.assertTrue('foo.cpp' in results[0].items[3])
2442 def testFalsePositives(self):
2443 mock_input_api = MockInputApi()
2444 mock_input_api.files = [
2445 MockFile('some/path/foo.h', ['foo_for_testing();']),
2446 MockFile('some/path/foo.mm', ['FooForTesting() {']),
2447 MockFile('some/path/foo.cc', ['::FooForTests();']),
2448 MockFile('some/path/foo.cpp', ['// foo_for_test();']),
2451 results = PRESUBMIT.CheckNoProductionCodeUsingTestOnlyFunctions(
2452 mock_input_api, MockOutputApi())
2453 self.assertEqual(0, len(results))
2455 def testAllowedFiles(self):
2456 mock_input_api = MockInputApi()
2457 mock_input_api.files = [
2458 MockFile('path/foo_unittest.cc', ['foo_for_testing();']),
2459 MockFile('path/bar_unittest_mac.cc', ['foo_for_testing();']),
2460 MockFile('path/baz_unittests.cc', ['foo_for_testing();']),
2463 results = PRESUBMIT.CheckNoProductionCodeUsingTestOnlyFunctions(
2464 mock_input_api, MockOutputApi())
2465 self.assertEqual(0, len(results))
2468 class NoProductionJavaCodeUsingTestOnlyFunctionsTest(unittest.TestCase):
2469 def testTruePositives(self):
2470 mock_input_api = MockInputApi()
2471 mock_input_api.files = [
2472 MockFile('dir/java/src/foo.java', ['FooForTesting();']),
2473 MockFile('dir/java/src/bar.java', ['FooForTests(x);']),
2474 MockFile('dir/java/src/baz.java', ['FooForTest(', 'y', ');']),
2475 MockFile('dir/java/src/mult.java', [
2476 'int x = SomethingLongHere()',
2477 ' * SomethingLongHereForTesting();'
2481 results = PRESUBMIT.CheckNoProductionCodeUsingTestOnlyFunctionsJava(
2482 mock_input_api, MockOutputApi())
2483 self.assertEqual(1, len(results))
2484 self.assertEqual(4, len(results[0].items))
2485 self.assertTrue('foo.java' in results[0].items[0])
2486 self.assertTrue('bar.java' in results[0].items[1])
2487 self.assertTrue('baz.java' in results[0].items[2])
2488 self.assertTrue('mult.java' in results[0].items[3])
2490 def testFalsePositives(self):
2491 mock_input_api = MockInputApi()
2492 mock_input_api.files = [
2493 MockFile('dir/java/src/foo.xml', ['FooForTesting();']),
2494 MockFile('dir/java/src/foo.java', ['FooForTests() {']),
2495 MockFile('dir/java/src/bar.java', ['// FooForTest();']),
2496 MockFile('dir/java/src/bar2.java', ['x = 1; // FooForTest();']),
2497 MockFile('dir/java/src/bar3.java', ['@VisibleForTesting']),
2498 MockFile('dir/java/src/bar4.java', ['@VisibleForTesting()']),
2499 MockFile('dir/java/src/bar5.java', [
2500 '@VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)'
2502 MockFile('dir/javatests/src/baz.java', ['FooForTest(', 'y', ');']),
2503 MockFile('dir/junit/src/baz.java', ['FooForTest(', 'y', ');']),
2504 MockFile('dir/junit/src/javadoc.java', [
2505 '/** Use FooForTest(); to obtain foo in tests.'
2508 MockFile('dir/junit/src/javadoc2.java', [
2510 ' * Use FooForTest(); to obtain foo in tests.'
2515 results = PRESUBMIT.CheckNoProductionCodeUsingTestOnlyFunctionsJava(
2516 mock_input_api, MockOutputApi())
2517 self.assertEqual(0, len(results))
2520 class NewImagesWarningTest(unittest.TestCase):
2521 def testTruePositives(self):
2522 mock_input_api = MockInputApi()
2523 mock_input_api.files = [
2524 MockFile('dir/android/res/drawable/foo.png', []),
2525 MockFile('dir/android/res/drawable-v21/bar.svg', []),
2526 MockFile('dir/android/res/mipmap-v21-en/baz.webp', []),
2527 MockFile('dir/android/res_gshoe/drawable-mdpi/foobar.png', []),
2530 results = PRESUBMIT._CheckNewImagesWarning(mock_input_api, MockOutputApi())
2531 self.assertEqual(1, len(results))
2532 self.assertEqual(4, len(results[0].items))
2533 self.assertTrue('foo.png' in results[0].items[0].LocalPath())
2534 self.assertTrue('bar.svg' in results[0].items[1].LocalPath())
2535 self.assertTrue('baz.webp' in results[0].items[2].LocalPath())
2536 self.assertTrue('foobar.png' in results[0].items[3].LocalPath())
2538 def testFalsePositives(self):
2539 mock_input_api = MockInputApi()
2540 mock_input_api.files = [
2541 MockFile('dir/pngs/README.md', []),
2542 MockFile('java/test/res/drawable/foo.png', []),
2543 MockFile('third_party/blink/foo.png', []),
2544 MockFile('dir/third_party/libpng/src/foo.cc', ['foobar']),
2545 MockFile('dir/resources.webp/.gitignore', ['foo.png']),
2548 results = PRESUBMIT._CheckNewImagesWarning(mock_input_api, MockOutputApi())
2549 self.assertEqual(0, len(results))
2552 class CheckUniquePtrTest(unittest.TestCase):
2553 def testTruePositivesNullptr(self):
2554 mock_input_api = MockInputApi()
2555 mock_input_api.files = [
2556 MockFile('dir/baz.cc', ['std::unique_ptr<T>()']),
2557 MockFile('dir/baz-p.cc', ['std::unique_ptr<T<P>>()']),
2560 results = PRESUBMIT.CheckUniquePtrOnUpload(mock_input_api, MockOutputApi())
2561 self.assertEqual(1, len(results))
2562 self.assertTrue('nullptr' in results[0].message)
2563 self.assertEqual(2, len(results[0].items))
2564 self.assertTrue('baz.cc' in results[0].items[0])
2565 self.assertTrue('baz-p.cc' in results[0].items[1])
2567 def testTruePositivesConstructor(self):
2568 mock_input_api = MockInputApi()
2569 mock_input_api.files = [
2570 MockFile('dir/foo.cc', ['return std::unique_ptr<T>(foo);']),
2571 MockFile('dir/bar.mm', ['bar = std::unique_ptr<T>(foo)']),
2572 MockFile('dir/mult.cc', [
2574 ' std::unique_ptr<T>(barVeryVeryLongFooSoThatItWouldNotFitAbove);'
2576 MockFile('dir/mult2.cc', [
2577 'barVeryVeryLongLongBaaaaaarSoThatTheLineLimitIsAlmostReached =',
2578 ' std::unique_ptr<T>(foo);'
2580 MockFile('dir/mult3.cc', [
2581 'bar = std::unique_ptr<T>(',
2582 ' fooVeryVeryVeryLongStillGoingWellThisWillTakeAWhileFinallyThere);'
2584 MockFile('dir/multi_arg.cc', [
2585 'auto p = std::unique_ptr<std::pair<T, D>>(new std::pair(T, D));']),
2588 results = PRESUBMIT.CheckUniquePtrOnUpload(mock_input_api, MockOutputApi())
2589 self.assertEqual(1, len(results))
2590 self.assertTrue('std::make_unique' in results[0].message)
2591 self.assertEqual(6, len(results[0].items))
2592 self.assertTrue('foo.cc' in results[0].items[0])
2593 self.assertTrue('bar.mm' in results[0].items[1])
2594 self.assertTrue('mult.cc' in results[0].items[2])
2595 self.assertTrue('mult2.cc' in results[0].items[3])
2596 self.assertTrue('mult3.cc' in results[0].items[4])
2597 self.assertTrue('multi_arg.cc' in results[0].items[5])
2599 def testFalsePositives(self):
2600 mock_input_api = MockInputApi()
2601 mock_input_api.files = [
2602 MockFile('dir/foo.cc', ['return std::unique_ptr<T[]>(foo);']),
2603 MockFile('dir/bar.mm', ['bar = std::unique_ptr<T[]>(foo)']),
2604 MockFile('dir/file.cc', ['std::unique_ptr<T> p = Foo();']),
2605 MockFile('dir/baz.cc', [
2606 'std::unique_ptr<T> result = std::make_unique<T>();'
2608 MockFile('dir/baz2.cc', [
2609 'std::unique_ptr<T> result = std::make_unique<T>('
2611 MockFile('dir/nested.cc', ['set<std::unique_ptr<T>>();']),
2612 MockFile('dir/nested2.cc', ['map<U, std::unique_ptr<T>>();']),
2614 # Two-argument invocation of std::unique_ptr is exempt because there is
2615 # no equivalent using std::make_unique.
2616 MockFile('dir/multi_arg.cc', [
2617 'auto p = std::unique_ptr<T, D>(new T(), D());']),
2620 results = PRESUBMIT.CheckUniquePtrOnUpload(mock_input_api, MockOutputApi())
2621 self.assertEqual(0, len(results))
2623 class CheckNoDirectIncludesHeadersWhichRedefineStrCat(unittest.TestCase):
2624 def testBlocksDirectIncludes(self):
2625 mock_input_api = MockInputApi()
2626 mock_input_api.files = [
2627 MockFile('dir/foo_win.cc', ['#include "shlwapi.h"']),
2628 MockFile('dir/bar.h', ['#include <propvarutil.h>']),
2629 MockFile('dir/baz.h', ['#include <atlbase.h>']),
2630 MockFile('dir/jumbo.h', ['#include "sphelper.h"']),
2632 results = PRESUBMIT._CheckNoStrCatRedefines(mock_input_api, MockOutputApi())
2633 self.assertEqual(1, len(results))
2634 self.assertEqual(4, len(results[0].items))
2635 self.assertTrue('StrCat' in results[0].message)
2636 self.assertTrue('foo_win.cc' in results[0].items[0])
2637 self.assertTrue('bar.h' in results[0].items[1])
2638 self.assertTrue('baz.h' in results[0].items[2])
2639 self.assertTrue('jumbo.h' in results[0].items[3])
2641 def testAllowsToIncludeWrapper(self):
2642 mock_input_api = MockInputApi()
2643 mock_input_api.files = [
2644 MockFile('dir/baz_win.cc', ['#include "base/win/shlwapi.h"']),
2645 MockFile('dir/baz-win.h', ['#include "base/win/atl.h"']),
2647 results = PRESUBMIT._CheckNoStrCatRedefines(mock_input_api, MockOutputApi())
2648 self.assertEqual(0, len(results))
2650 def testAllowsToCreateWrapper(self):
2651 mock_input_api = MockInputApi()
2652 mock_input_api.files = [
2653 MockFile('base/win/shlwapi.h', [
2654 '#include <shlwapi.h>',
2655 '#include "base/win/windows_defines.inc"']),
2657 results = PRESUBMIT._CheckNoStrCatRedefines(mock_input_api, MockOutputApi())
2658 self.assertEqual(0, len(results))
2661 class StringTest(unittest.TestCase):
2662 """Tests ICU syntax check and translation screenshots check."""
2664 # An empty grd file.
2665 OLD_GRD_CONTENTS = """<?xml version="1.0" encoding="UTF-8"?>
2666 <grit latest_public_release="1" current_release="1">
2668 <messages></messages>
2672 # A grd file with a single message.
2673 NEW_GRD_CONTENTS1 = """<?xml version="1.0" encoding="UTF-8"?>
2674 <grit latest_public_release="1" current_release="1">
2677 <message name="IDS_TEST1">
2680 <message name="IDS_TEST_STRING_NON_TRANSLATEABLE1"
2681 translateable="false">
2682 Non translateable message 1, should be ignored
2684 <message name="IDS_TEST_STRING_ACCESSIBILITY"
2685 is_accessibility_with_no_ui="true">
2686 Accessibility label 1, should be ignored
2692 # A grd file with two messages.
2693 NEW_GRD_CONTENTS2 = """<?xml version="1.0" encoding="UTF-8"?>
2694 <grit latest_public_release="1" current_release="1">
2697 <message name="IDS_TEST1">
2700 <message name="IDS_TEST2">
2703 <message name="IDS_TEST_STRING_NON_TRANSLATEABLE2"
2704 translateable="false">
2705 Non translateable message 2, should be ignored
2711 # A grd file with one ICU syntax message without syntax errors.
2712 NEW_GRD_CONTENTS_ICU_SYNTAX_OK1 = """<?xml version="1.0" encoding="UTF-8"?>
2713 <grit latest_public_release="1" current_release="1">
2716 <message name="IDS_TEST1">
2718 =1 {Test text for numeric one}
2719 other {Test text for plural with {NUM} as number}}
2725 # A grd file with one ICU syntax message without syntax errors.
2726 NEW_GRD_CONTENTS_ICU_SYNTAX_OK2 = """<?xml version="1.0" encoding="UTF-8"?>
2727 <grit latest_public_release="1" current_release="1">
2730 <message name="IDS_TEST1">
2732 =1 {Different test text for numeric one}
2733 other {Different test text for plural with {NUM} as number}}
2739 # A grd file with one ICU syntax message with syntax errors (misses a comma).
2740 NEW_GRD_CONTENTS_ICU_SYNTAX_ERROR = """<?xml version="1.0" encoding="UTF-8"?>
2741 <grit latest_public_release="1" current_release="1">
2744 <message name="IDS_TEST1">
2746 =1 {Test text for numeric one}
2747 other {Test text for plural with {NUM} as number}}
2754 OLD_GRDP_CONTENTS = (
2755 '<?xml version="1.0" encoding="utf-8"?>',
2760 NEW_GRDP_CONTENTS1 = (
2761 '<?xml version="1.0" encoding="utf-8"?>',
2763 '<message name="IDS_PART_TEST1">',
2768 NEW_GRDP_CONTENTS2 = (
2769 '<?xml version="1.0" encoding="utf-8"?>',
2771 '<message name="IDS_PART_TEST1">',
2774 '<message name="IDS_PART_TEST2">',
2779 NEW_GRDP_CONTENTS3 = (
2780 '<?xml version="1.0" encoding="utf-8"?>',
2782 '<message name="IDS_PART_TEST1" desc="Description with typo.">',
2787 NEW_GRDP_CONTENTS4 = (
2788 '<?xml version="1.0" encoding="utf-8"?>',
2790 '<message name="IDS_PART_TEST1" desc="Description with typo fixed.">',
2795 NEW_GRDP_CONTENTS5 = (
2796 '<?xml version="1.0" encoding="utf-8"?>',
2798 '<message name="IDS_PART_TEST1" meaning="Meaning with typo.">',
2803 NEW_GRDP_CONTENTS6 = (
2804 '<?xml version="1.0" encoding="utf-8"?>',
2806 '<message name="IDS_PART_TEST1" meaning="Meaning with typo fixed.">',
2811 # A grdp file with one ICU syntax message without syntax errors.
2812 NEW_GRDP_CONTENTS_ICU_SYNTAX_OK1 = (
2813 '<?xml version="1.0" encoding="utf-8"?>',
2815 '<message name="IDS_PART_TEST1">',
2817 '=1 {Test text for numeric one}',
2818 'other {Test text for plural with {NUM} as number}}',
2821 # A grdp file with one ICU syntax message without syntax errors.
2822 NEW_GRDP_CONTENTS_ICU_SYNTAX_OK2 = (
2823 '<?xml version="1.0" encoding="utf-8"?>',
2825 '<message name="IDS_PART_TEST1">',
2827 '=1 {Different test text for numeric one}',
2828 'other {Different test text for plural with {NUM} as number}}',
2832 # A grdp file with one ICU syntax message with syntax errors (superfluent
2834 NEW_GRDP_CONTENTS_ICU_SYNTAX_ERROR = (
2835 '<?xml version="1.0" encoding="utf-8"?>',
2837 '<message name="IDS_PART_TEST1">',
2839 '= 1 {Test text for numeric one}',
2840 'other {Test text for plural with {NUM} as number}}',
2844 DO_NOT_UPLOAD_PNG_MESSAGE = ('Do not include actual screenshots in the '
2846 'tools/translate/upload_screenshots.py to '
2847 'upload them instead:')
2848 GENERATE_SIGNATURES_MESSAGE = ('You are adding or modifying UI strings.\n'
2849 'To ensure the best translations, take '
2850 'screenshots of the relevant UI '
2851 '(https://g.co/chrome/translation) and add '
2852 'these files to your changelist:')
2853 REMOVE_SIGNATURES_MESSAGE = ('You removed strings associated with these '
2855 ICU_SYNTAX_ERROR_MESSAGE = ('ICU syntax errors were found in the following '
2856 'strings (problems or feedback? Contact '
2857 'rainhard@chromium.org):')
2859 def makeInputApi(self, files):
2860 input_api = MockInputApi()
2861 input_api.files = files
2862 # Override os_path.exists because the presubmit uses the actual
2864 input_api.CreateMockFileInPath(
2865 [x.LocalPath() for x in input_api.AffectedFiles(include_deletes=True)])
2868 """ CL modified and added messages, but didn't add any screenshots."""
2869 def testNoScreenshots(self):
2870 # No new strings (file contents same). Should not warn.
2871 input_api = self.makeInputApi([
2872 MockAffectedFile('test.grd', self.NEW_GRD_CONTENTS1,
2873 self.NEW_GRD_CONTENTS1, action='M'),
2874 MockAffectedFile('part.grdp', self.NEW_GRDP_CONTENTS1,
2875 self.NEW_GRDP_CONTENTS1, action='M')])
2876 warnings = PRESUBMIT.CheckStrings(input_api,
2878 self.assertEqual(0, len(warnings))
2880 # Add two new strings. Should have two warnings.
2881 input_api = self.makeInputApi([
2882 MockAffectedFile('test.grd', self.NEW_GRD_CONTENTS2,
2883 self.NEW_GRD_CONTENTS1, action='M'),
2884 MockAffectedFile('part.grdp', self.NEW_GRDP_CONTENTS2,
2885 self.NEW_GRDP_CONTENTS1, action='M')])
2886 warnings = PRESUBMIT.CheckStrings(input_api,
2888 self.assertEqual(1, len(warnings))
2889 self.assertEqual(self.GENERATE_SIGNATURES_MESSAGE, warnings[0].message)
2890 self.assertEqual('error', warnings[0].type)
2892 os.path.join('part_grdp', 'IDS_PART_TEST2.png.sha1'),
2893 os.path.join('test_grd', 'IDS_TEST2.png.sha1')],
2896 # Add four new strings. Should have four warnings.
2897 input_api = self.makeInputApi([
2898 MockAffectedFile('test.grd', self.NEW_GRD_CONTENTS2,
2899 self.OLD_GRD_CONTENTS, action='M'),
2900 MockAffectedFile('part.grdp', self.NEW_GRDP_CONTENTS2,
2901 self.OLD_GRDP_CONTENTS, action='M')])
2902 warnings = PRESUBMIT.CheckStrings(input_api,
2904 self.assertEqual(1, len(warnings))
2905 self.assertEqual('error', warnings[0].type)
2906 self.assertEqual(self.GENERATE_SIGNATURES_MESSAGE, warnings[0].message)
2908 os.path.join('part_grdp', 'IDS_PART_TEST1.png.sha1'),
2909 os.path.join('part_grdp', 'IDS_PART_TEST2.png.sha1'),
2910 os.path.join('test_grd', 'IDS_TEST1.png.sha1'),
2911 os.path.join('test_grd', 'IDS_TEST2.png.sha1'),
2912 ], warnings[0].items)
2914 def testModifiedMessageDescription(self):
2915 # CL modified a message description for a message that does not yet have a
2916 # screenshot. Should not warn.
2917 input_api = self.makeInputApi([
2918 MockAffectedFile('part.grdp', self.NEW_GRDP_CONTENTS3,
2919 self.NEW_GRDP_CONTENTS4, action='M')])
2920 warnings = PRESUBMIT.CheckStrings(input_api, MockOutputApi())
2921 self.assertEqual(0, len(warnings))
2923 # CL modified a message description for a message that already has a
2924 # screenshot. Should not warn.
2925 input_api = self.makeInputApi([
2926 MockAffectedFile('part.grdp', self.NEW_GRDP_CONTENTS3,
2927 self.NEW_GRDP_CONTENTS4, action='M'),
2928 MockFile(os.path.join('part_grdp', 'IDS_PART_TEST1.png.sha1'),
2929 'binary', action='A')])
2930 warnings = PRESUBMIT.CheckStrings(input_api, MockOutputApi())
2931 self.assertEqual(0, len(warnings))
2933 def testModifiedMessageMeaning(self):
2934 # CL modified a message meaning for a message that does not yet have a
2935 # screenshot. Should warn.
2936 input_api = self.makeInputApi([
2937 MockAffectedFile('part.grdp', self.NEW_GRDP_CONTENTS5,
2938 self.NEW_GRDP_CONTENTS6, action='M')])
2939 warnings = PRESUBMIT.CheckStrings(input_api, MockOutputApi())
2940 self.assertEqual(1, len(warnings))
2942 # CL modified a message meaning for a message that already has a
2943 # screenshot. Should not warn.
2944 input_api = self.makeInputApi([
2945 MockAffectedFile('part.grdp', self.NEW_GRDP_CONTENTS5,
2946 self.NEW_GRDP_CONTENTS6, action='M'),
2947 MockFile(os.path.join('part_grdp', 'IDS_PART_TEST1.png.sha1'),
2948 'binary', action='A')])
2949 warnings = PRESUBMIT.CheckStrings(input_api, MockOutputApi())
2950 self.assertEqual(0, len(warnings))
2952 def testPngAddedSha1NotAdded(self):
2953 # CL added one new message in a grd file and added the png file associated
2954 # with it, but did not add the corresponding sha1 file. This should warn
2956 # - Once for the added png file (because we don't want developers to upload
2958 # - Once for the missing .sha1 file
2959 input_api = self.makeInputApi([
2962 self.NEW_GRD_CONTENTS1,
2963 self.OLD_GRD_CONTENTS,
2966 os.path.join('test_grd', 'IDS_TEST1.png'), 'binary', action='A')
2968 warnings = PRESUBMIT.CheckStrings(input_api,
2970 self.assertEqual(2, len(warnings))
2971 self.assertEqual('error', warnings[0].type)
2972 self.assertEqual(self.DO_NOT_UPLOAD_PNG_MESSAGE, warnings[0].message)
2973 self.assertEqual([os.path.join('test_grd', 'IDS_TEST1.png')],
2975 self.assertEqual('error', warnings[1].type)
2976 self.assertEqual(self.GENERATE_SIGNATURES_MESSAGE, warnings[1].message)
2977 self.assertEqual([os.path.join('test_grd', 'IDS_TEST1.png.sha1')],
2980 # CL added two messages (one in grd, one in grdp) and added the png files
2981 # associated with the messages, but did not add the corresponding sha1
2982 # files. This should warn twice:
2983 # - Once for the added png files (because we don't want developers to upload
2985 # - Once for the missing .sha1 files
2986 input_api = self.makeInputApi([
2990 self.NEW_GRD_CONTENTS1,
2991 self.OLD_GRD_CONTENTS,
2995 self.NEW_GRDP_CONTENTS1,
2996 self.OLD_GRDP_CONTENTS,
3000 os.path.join('test_grd', 'IDS_TEST1.png'), 'binary', action='A'),
3002 os.path.join('part_grdp', 'IDS_PART_TEST1.png'), 'binary',
3005 warnings = PRESUBMIT.CheckStrings(input_api,
3007 self.assertEqual(2, len(warnings))
3008 self.assertEqual('error', warnings[0].type)
3009 self.assertEqual(self.DO_NOT_UPLOAD_PNG_MESSAGE, warnings[0].message)
3010 self.assertEqual([os.path.join('part_grdp', 'IDS_PART_TEST1.png'),
3011 os.path.join('test_grd', 'IDS_TEST1.png')],
3013 self.assertEqual('error', warnings[0].type)
3014 self.assertEqual(self.GENERATE_SIGNATURES_MESSAGE, warnings[1].message)
3015 self.assertEqual([os.path.join('part_grdp', 'IDS_PART_TEST1.png.sha1'),
3016 os.path.join('test_grd', 'IDS_TEST1.png.sha1')],
3019 def testScreenshotsWithSha1(self):
3020 # CL added four messages (two each in a grd and grdp) and their
3021 # corresponding .sha1 files. No warnings.
3022 input_api = self.makeInputApi([
3026 self.NEW_GRD_CONTENTS2,
3027 self.OLD_GRD_CONTENTS,
3031 self.NEW_GRDP_CONTENTS2,
3032 self.OLD_GRDP_CONTENTS,
3036 os.path.join('test_grd', 'IDS_TEST1.png.sha1'),
3040 os.path.join('test_grd', 'IDS_TEST2.png.sha1'),
3044 os.path.join('part_grdp', 'IDS_PART_TEST1.png.sha1'),
3048 os.path.join('part_grdp', 'IDS_PART_TEST2.png.sha1'),
3052 warnings = PRESUBMIT.CheckStrings(input_api,
3054 self.assertEqual([], warnings)
3056 def testScreenshotsRemovedWithSha1(self):
3057 # Replace new contents with old contents in grd and grp files, removing
3058 # IDS_TEST1, IDS_TEST2, IDS_PART_TEST1 and IDS_PART_TEST2.
3059 # Should warn to remove the sha1 files associated with these strings.
3060 input_api = self.makeInputApi([
3064 self.OLD_GRD_CONTENTS, # new_contents
3065 self.NEW_GRD_CONTENTS2, # old_contents
3069 self.OLD_GRDP_CONTENTS, # new_contents
3070 self.NEW_GRDP_CONTENTS2, # old_contents
3073 MockFile(os.path.join('test_grd', 'IDS_TEST1.png.sha1'), 'binary', ''),
3074 MockFile(os.path.join('test_grd', 'IDS_TEST2.png.sha1'), 'binary', ''),
3075 MockFile(os.path.join('part_grdp', 'IDS_PART_TEST1.png.sha1'),
3077 MockFile(os.path.join('part_grdp', 'IDS_PART_TEST2.png.sha1'),
3080 warnings = PRESUBMIT.CheckStrings(input_api,
3082 self.assertEqual(1, len(warnings))
3083 self.assertEqual('error', warnings[0].type)
3084 self.assertEqual(self.REMOVE_SIGNATURES_MESSAGE, warnings[0].message)
3086 os.path.join('part_grdp', 'IDS_PART_TEST1.png.sha1'),
3087 os.path.join('part_grdp', 'IDS_PART_TEST2.png.sha1'),
3088 os.path.join('test_grd', 'IDS_TEST1.png.sha1'),
3089 os.path.join('test_grd', 'IDS_TEST2.png.sha1')
3090 ], warnings[0].items)
3092 # Same as above, but this time one of the .sha1 files is also removed.
3093 input_api = self.makeInputApi([
3097 self.OLD_GRD_CONTENTS, # new_contents
3098 self.NEW_GRD_CONTENTS2, # old_contents
3102 self.OLD_GRDP_CONTENTS, # new_contents
3103 self.NEW_GRDP_CONTENTS2, # old_contents
3106 MockFile(os.path.join('test_grd', 'IDS_TEST1.png.sha1'), 'binary', ''),
3107 MockFile(os.path.join('part_grdp', 'IDS_PART_TEST1.png.sha1'),
3111 os.path.join('test_grd', 'IDS_TEST2.png.sha1'),
3116 os.path.join('part_grdp', 'IDS_PART_TEST2.png.sha1'),
3121 warnings = PRESUBMIT.CheckStrings(input_api,
3123 self.assertEqual(1, len(warnings))
3124 self.assertEqual('error', warnings[0].type)
3125 self.assertEqual(self.REMOVE_SIGNATURES_MESSAGE, warnings[0].message)
3126 self.assertEqual([os.path.join('part_grdp', 'IDS_PART_TEST1.png.sha1'),
3127 os.path.join('test_grd', 'IDS_TEST1.png.sha1')
3128 ], warnings[0].items)
3130 # Remove all sha1 files. There should be no warnings.
3131 input_api = self.makeInputApi([
3135 self.OLD_GRD_CONTENTS,
3136 self.NEW_GRD_CONTENTS2,
3140 self.OLD_GRDP_CONTENTS,
3141 self.NEW_GRDP_CONTENTS2,
3145 os.path.join('test_grd', 'IDS_TEST1.png.sha1'),
3149 os.path.join('test_grd', 'IDS_TEST2.png.sha1'),
3153 os.path.join('part_grdp', 'IDS_PART_TEST1.png.sha1'),
3157 os.path.join('part_grdp', 'IDS_PART_TEST2.png.sha1'),
3161 warnings = PRESUBMIT.CheckStrings(input_api,
3163 self.assertEqual([], warnings)
3165 def testIcuSyntax(self):
3166 # Add valid ICU syntax string. Should not raise an error.
3167 input_api = self.makeInputApi([
3168 MockAffectedFile('test.grd', self.NEW_GRD_CONTENTS_ICU_SYNTAX_OK2,
3169 self.NEW_GRD_CONTENTS1, action='M'),
3170 MockAffectedFile('part.grdp', self.NEW_GRDP_CONTENTS_ICU_SYNTAX_OK2,
3171 self.NEW_GRDP_CONTENTS1, action='M')])
3172 results = PRESUBMIT.CheckStrings(input_api, MockOutputApi())
3173 # We expect no ICU syntax errors.
3174 icu_errors = [e for e in results
3175 if e.message == self.ICU_SYNTAX_ERROR_MESSAGE]
3176 self.assertEqual(0, len(icu_errors))
3178 # Valid changes in ICU syntax. Should not raise an error.
3179 input_api = self.makeInputApi([
3180 MockAffectedFile('test.grd', self.NEW_GRD_CONTENTS_ICU_SYNTAX_OK2,
3181 self.NEW_GRD_CONTENTS_ICU_SYNTAX_OK1, action='M'),
3182 MockAffectedFile('part.grdp', self.NEW_GRDP_CONTENTS_ICU_SYNTAX_OK2,
3183 self.NEW_GRDP_CONTENTS_ICU_SYNTAX_OK1, action='M')])
3184 results = PRESUBMIT.CheckStrings(input_api, MockOutputApi())
3185 # We expect no ICU syntax errors.
3186 icu_errors = [e for e in results
3187 if e.message == self.ICU_SYNTAX_ERROR_MESSAGE]
3188 self.assertEqual(0, len(icu_errors))
3190 # Add invalid ICU syntax strings. Should raise two errors.
3191 input_api = self.makeInputApi([
3192 MockAffectedFile('test.grd', self.NEW_GRD_CONTENTS_ICU_SYNTAX_ERROR,
3193 self.NEW_GRD_CONTENTS1, action='M'),
3194 MockAffectedFile('part.grdp', self.NEW_GRDP_CONTENTS_ICU_SYNTAX_ERROR,
3195 self.NEW_GRD_CONTENTS1, action='M')])
3196 results = PRESUBMIT.CheckStrings(input_api, MockOutputApi())
3197 # We expect 2 ICU syntax errors.
3198 icu_errors = [e for e in results
3199 if e.message == self.ICU_SYNTAX_ERROR_MESSAGE]
3200 self.assertEqual(1, len(icu_errors))
3202 'IDS_TEST1: This message looks like an ICU plural, but does not follow '
3204 'IDS_PART_TEST1: Variant "= 1" is not valid for plural message'
3205 ], icu_errors[0].items)
3207 # Change two strings to have ICU syntax errors. Should raise two errors.
3208 input_api = self.makeInputApi([
3209 MockAffectedFile('test.grd', self.NEW_GRD_CONTENTS_ICU_SYNTAX_ERROR,
3210 self.NEW_GRD_CONTENTS_ICU_SYNTAX_OK1, action='M'),
3211 MockAffectedFile('part.grdp', self.NEW_GRDP_CONTENTS_ICU_SYNTAX_ERROR,
3212 self.NEW_GRDP_CONTENTS_ICU_SYNTAX_OK1, action='M')])
3213 results = PRESUBMIT.CheckStrings(input_api, MockOutputApi())
3214 # We expect 2 ICU syntax errors.
3215 icu_errors = [e for e in results
3216 if e.message == self.ICU_SYNTAX_ERROR_MESSAGE]
3217 self.assertEqual(1, len(icu_errors))
3219 'IDS_TEST1: This message looks like an ICU plural, but does not follow '
3221 'IDS_PART_TEST1: Variant "= 1" is not valid for plural message'
3222 ], icu_errors[0].items)
3225 class TranslationExpectationsTest(unittest.TestCase):
3226 ERROR_MESSAGE_FORMAT = (
3227 "Failed to get a list of translatable grd files. "
3228 "This happens when:\n"
3229 " - One of the modified grd or grdp files cannot be parsed or\n"
3230 " - %s is not updated.\n"
3233 REPO_ROOT = os.path.join('tools', 'translation', 'testdata')
3234 # This lists all .grd files under REPO_ROOT.
3235 EXPECTATIONS = os.path.join(REPO_ROOT,
3236 "translation_expectations.pyl")
3237 # This lists all .grd files under REPO_ROOT except unlisted.grd.
3238 EXPECTATIONS_WITHOUT_UNLISTED_FILE = os.path.join(
3239 REPO_ROOT, "translation_expectations_without_unlisted_file.pyl")
3241 # Tests that the presubmit doesn't return when no grd or grdp files are
3243 def testExpectationsNoModifiedGrd(self):
3244 input_api = MockInputApi()
3246 MockAffectedFile('not_used.txt', 'not used', 'not used', action='M')
3248 # Fake list of all grd files in the repo. This list is missing all grd/grdps
3249 # under tools/translation/testdata. This is OK because the presubmit won't
3250 # run in the first place since there are no modified grd/grps in input_api.
3251 grd_files = ['doesnt_exist_doesnt_matter.grd']
3252 warnings = PRESUBMIT.CheckTranslationExpectations(
3253 input_api, MockOutputApi(), self.REPO_ROOT, self.EXPECTATIONS,
3255 self.assertEqual(0, len(warnings))
3258 # Tests that the list of files passed to the presubmit matches the list of
3259 # files in the expectations.
3260 def testExpectationsSuccess(self):
3261 # Mock input file list needs a grd or grdp file in order to run the
3262 # presubmit. The file itself doesn't matter.
3263 input_api = MockInputApi()
3265 MockAffectedFile('dummy.grd', 'not used', 'not used', action='M')
3267 # List of all grd files in the repo.
3268 grd_files = ['test.grd', 'unlisted.grd', 'not_translated.grd',
3270 warnings = PRESUBMIT.CheckTranslationExpectations(
3271 input_api, MockOutputApi(), self.REPO_ROOT, self.EXPECTATIONS,
3273 self.assertEqual(0, len(warnings))
3275 # Tests that the presubmit warns when a file is listed in expectations, but
3276 # does not actually exist.
3277 def testExpectationsMissingFile(self):
3278 # Mock input file list needs a grd or grdp file in order to run the
3280 input_api = MockInputApi()
3282 MockAffectedFile('dummy.grd', 'not used', 'not used', action='M')
3284 # unlisted.grd is listed under tools/translation/testdata but is not
3285 # included in translation expectations.
3286 grd_files = ['unlisted.grd', 'not_translated.grd', 'internal.grd']
3287 warnings = PRESUBMIT.CheckTranslationExpectations(
3288 input_api, MockOutputApi(), self.REPO_ROOT, self.EXPECTATIONS,
3290 self.assertEqual(1, len(warnings))
3291 self.assertTrue(warnings[0].message.startswith(
3292 self.ERROR_MESSAGE_FORMAT % self.EXPECTATIONS))
3294 ("test.grd is listed in the translation expectations, "
3295 "but this grd file does not exist")
3296 in warnings[0].message)
3298 # Tests that the presubmit warns when a file is not listed in expectations but
3299 # does actually exist.
3300 def testExpectationsUnlistedFile(self):
3301 # Mock input file list needs a grd or grdp file in order to run the
3303 input_api = MockInputApi()
3305 MockAffectedFile('dummy.grd', 'not used', 'not used', action='M')
3307 # unlisted.grd is listed under tools/translation/testdata but is not
3308 # included in translation expectations.
3309 grd_files = ['test.grd', 'unlisted.grd', 'not_translated.grd',
3311 warnings = PRESUBMIT.CheckTranslationExpectations(
3312 input_api, MockOutputApi(), self.REPO_ROOT,
3313 self.EXPECTATIONS_WITHOUT_UNLISTED_FILE, grd_files)
3314 self.assertEqual(1, len(warnings))
3315 self.assertTrue(warnings[0].message.startswith(
3316 self.ERROR_MESSAGE_FORMAT % self.EXPECTATIONS_WITHOUT_UNLISTED_FILE))
3318 ("unlisted.grd appears to be translatable "
3319 "(because it contains <file> or <message> elements), "
3320 "but is not listed in the translation expectations.")
3321 in warnings[0].message)
3323 # Tests that the presubmit warns twice:
3324 # - for a non-existing file listed in expectations
3325 # - for an existing file not listed in expectations
3326 def testMultipleWarnings(self):
3327 # Mock input file list needs a grd or grdp file in order to run the
3329 input_api = MockInputApi()
3331 MockAffectedFile('dummy.grd', 'not used', 'not used', action='M')
3333 # unlisted.grd is listed under tools/translation/testdata but is not
3334 # included in translation expectations.
3335 # test.grd is not listed under tools/translation/testdata but is included
3336 # in translation expectations.
3337 grd_files = ['unlisted.grd', 'not_translated.grd', 'internal.grd']
3338 warnings = PRESUBMIT.CheckTranslationExpectations(
3339 input_api, MockOutputApi(), self.REPO_ROOT,
3340 self.EXPECTATIONS_WITHOUT_UNLISTED_FILE, grd_files)
3341 self.assertEqual(1, len(warnings))
3342 self.assertTrue(warnings[0].message.startswith(
3343 self.ERROR_MESSAGE_FORMAT % self.EXPECTATIONS_WITHOUT_UNLISTED_FILE))
3345 ("unlisted.grd appears to be translatable "
3346 "(because it contains <file> or <message> elements), "
3347 "but is not listed in the translation expectations.")
3348 in warnings[0].message)
3350 ("test.grd is listed in the translation expectations, "
3351 "but this grd file does not exist")
3352 in warnings[0].message)
3355 class DISABLETypoInTest(unittest.TestCase):
3357 def testPositive(self):
3358 # Verify the typo "DISABLE_" instead of "DISABLED_" in various contexts
3359 # where the desire is to disable a test.
3361 # Disabled on one platform:
3362 '#if defined(OS_WIN)\n'
3363 '#define MAYBE_FoobarTest DISABLE_FoobarTest\n'
3365 '#define MAYBE_FoobarTest FoobarTest\n'
3367 # Disabled on one platform spread cross lines:
3368 '#if defined(OS_WIN)\n'
3369 '#define MAYBE_FoobarTest \\\n'
3370 ' DISABLE_FoobarTest\n'
3372 '#define MAYBE_FoobarTest FoobarTest\n'
3374 # Disabled on all platforms:
3375 ' TEST_F(FoobarTest, DISABLE_Foo)\n{\n}',
3376 # Disabled on all platforms but multiple lines
3377 ' TEST_F(FoobarTest,\n DISABLE_foo){\n}\n',
3381 mock_input_api = MockInputApi()
3382 mock_input_api.files = [
3383 MockFile('some/path/foo_unittest.cc', test.splitlines()),
3386 results = PRESUBMIT.CheckNoDISABLETypoInTests(mock_input_api,
3391 msg=('expected len(results) == 1 but got %d in test: %s' %
3392 (len(results), test)))
3394 'foo_unittest.cc' in results[0].message,
3395 msg=('expected foo_unittest.cc in message but got %s in test %s' %
3396 (results[0].message, test)))
3398 def testIngoreNotTestFiles(self):
3399 mock_input_api = MockInputApi()
3400 mock_input_api.files = [
3401 MockFile('some/path/foo.cc', 'TEST_F(FoobarTest, DISABLE_Foo)'),
3404 results = PRESUBMIT.CheckNoDISABLETypoInTests(mock_input_api,
3406 self.assertEqual(0, len(results))
3408 def testIngoreDeletedFiles(self):
3409 mock_input_api = MockInputApi()
3410 mock_input_api.files = [
3411 MockFile('some/path/foo.cc', 'TEST_F(FoobarTest, Foo)', action='D'),
3414 results = PRESUBMIT.CheckNoDISABLETypoInTests(mock_input_api,
3416 self.assertEqual(0, len(results))
3419 class CheckFuzzTargetsTest(unittest.TestCase):
3421 def _check(self, files):
3422 mock_input_api = MockInputApi()
3423 mock_input_api.files = []
3424 for fname, contents in files.items():
3425 mock_input_api.files.append(MockFile(fname, contents.splitlines()))
3426 return PRESUBMIT.CheckFuzzTargetsOnUpload(mock_input_api, MockOutputApi())
3428 def testLibFuzzerSourcesIgnored(self):
3429 results = self._check({
3430 "third_party/lib/Fuzzer/FuzzerDriver.cpp": "LLVMFuzzerInitialize",
3432 self.assertEqual(results, [])
3434 def testNonCodeFilesIgnored(self):
3435 results = self._check({
3436 "README.md": "LLVMFuzzerInitialize",
3438 self.assertEqual(results, [])
3440 def testNoErrorHeaderPresent(self):
3441 results = self._check({
3443 "#include \"testing/libfuzzer/libfuzzer_exports.h\"\n" +
3444 "LLVMFuzzerInitialize"
3447 self.assertEqual(results, [])
3449 def testErrorMissingHeader(self):
3450 results = self._check({
3451 "fuzzer.cc": "LLVMFuzzerInitialize"
3453 self.assertEqual(len(results), 1)
3454 self.assertEqual(results[0].items, ['fuzzer.cc'])
3457 class SetNoParentTest(unittest.TestCase):
3458 def testSetNoParentTopLevelAllowed(self):
3459 mock_input_api = MockInputApi()
3460 mock_input_api.files = [
3461 MockAffectedFile('goat/OWNERS',
3464 'jochen@chromium.org',
3467 mock_output_api = MockOutputApi()
3468 errors = PRESUBMIT.CheckSetNoParent(mock_input_api, mock_output_api)
3469 self.assertEqual([], errors)
3471 def testSetNoParentMissing(self):
3472 mock_input_api = MockInputApi()
3473 mock_input_api.files = [
3474 MockAffectedFile('services/goat/OWNERS',
3477 'jochen@chromium.org',
3478 'per-file *.json=set noparent',
3479 'per-file *.json=jochen@chromium.org',
3482 mock_output_api = MockOutputApi()
3483 errors = PRESUBMIT.CheckSetNoParent(mock_input_api, mock_output_api)
3484 self.assertEqual(1, len(errors))
3485 self.assertTrue('goat/OWNERS:1' in errors[0].long_text)
3486 self.assertTrue('goat/OWNERS:3' in errors[0].long_text)
3488 def testSetNoParentWithCorrectRule(self):
3489 mock_input_api = MockInputApi()
3490 mock_input_api.files = [
3491 MockAffectedFile('services/goat/OWNERS',
3494 'file://ipc/SECURITY_OWNERS',
3495 'per-file *.json=set noparent',
3496 'per-file *.json=file://ipc/SECURITY_OWNERS',
3499 mock_output_api = MockOutputApi()
3500 errors = PRESUBMIT.CheckSetNoParent(mock_input_api, mock_output_api)
3501 self.assertEqual([], errors)
3504 class MojomStabilityCheckTest(unittest.TestCase):
3505 def runTestWithAffectedFiles(self, affected_files):
3506 mock_input_api = MockInputApi()
3507 mock_input_api.files = affected_files
3508 mock_output_api = MockOutputApi()
3509 return PRESUBMIT.CheckStableMojomChanges(
3510 mock_input_api, mock_output_api)
3512 def testSafeChangePasses(self):
3513 errors = self.runTestWithAffectedFiles([
3514 MockAffectedFile('foo/foo.mojom',
3515 ['[Stable] struct S { [MinVersion=1] int32 x; };'],
3516 old_contents=['[Stable] struct S {};'])
3518 self.assertEqual([], errors)
3520 def testBadChangeFails(self):
3521 errors = self.runTestWithAffectedFiles([
3522 MockAffectedFile('foo/foo.mojom',
3523 ['[Stable] struct S { int32 x; };'],
3524 old_contents=['[Stable] struct S {};'])
3526 self.assertEqual(1, len(errors))
3527 self.assertTrue('not backward-compatible' in errors[0].message)
3529 def testDeletedFile(self):
3530 """Regression test for https://crbug.com/1091407."""
3531 errors = self.runTestWithAffectedFiles([
3532 MockAffectedFile('a.mojom', [], old_contents=['struct S {};'],
3534 MockAffectedFile('b.mojom',
3535 ['struct S {}; struct T { S s; };'],
3536 old_contents=['import "a.mojom"; struct T { S s; };'])
3538 self.assertEqual([], errors)
3540 class CheckForUseOfChromeAppsDeprecationsTest(unittest.TestCase):
3542 ERROR_MSG_PIECE = 'technologies which will soon be deprecated'
3544 # Each positive test is also a naive negative test for the other cases.
3546 def testWarningNMF(self):
3547 mock_input_api = MockInputApi()
3548 mock_input_api.files = [
3551 ['"program"', '"Z":"content"', 'B'],
3553 scm_diff='\n'.join([
3554 '--- foo.NMF.old 2020-12-02 20:40:54.430676385 +0100',
3555 '+++ foo.NMF.new 2020-12-02 20:41:02.086700197 +0100',
3562 mock_output_api = MockOutputApi()
3563 errors = PRESUBMIT.CheckForUseOfChromeAppsDeprecations(mock_input_api,
3565 self.assertEqual(1, len(errors))
3566 self.assertTrue( self.ERROR_MSG_PIECE in errors[0].message)
3567 self.assertTrue( 'foo.NMF' in errors[0].message)
3569 def testWarningManifest(self):
3570 mock_input_api = MockInputApi()
3571 mock_input_api.files = [
3574 ['"app":', '"Z":"content"', 'B'],
3576 scm_diff='\n'.join([
3577 '--- manifest.json.old 2020-12-02 20:40:54.430676385 +0100',
3578 '+++ manifest.json.new 2020-12-02 20:41:02.086700197 +0100',
3585 mock_output_api = MockOutputApi()
3586 errors = PRESUBMIT.CheckForUseOfChromeAppsDeprecations(mock_input_api,
3588 self.assertEqual(1, len(errors))
3589 self.assertTrue( self.ERROR_MSG_PIECE in errors[0].message)
3590 self.assertTrue( 'manifest.json' in errors[0].message)
3592 def testOKWarningManifestWithoutApp(self):
3593 mock_input_api = MockInputApi()
3594 mock_input_api.files = [
3597 ['"name":', '"Z":"content"', 'B'],
3599 scm_diff='\n'.join([
3600 '--- manifest.json.old 2020-12-02 20:40:54.430676385 +0100',
3601 '+++ manifest.json.new 2020-12-02 20:41:02.086700197 +0100',
3608 mock_output_api = MockOutputApi()
3609 errors = PRESUBMIT.CheckForUseOfChromeAppsDeprecations(mock_input_api,
3611 self.assertEqual(0, len(errors))
3613 def testWarningPPAPI(self):
3614 mock_input_api = MockInputApi()
3615 mock_input_api.files = [
3618 ['A', '#include <ppapi.h>', 'B'],
3620 scm_diff='\n'.join([
3621 '--- foo.hpp.old 2020-12-02 20:40:54.430676385 +0100',
3622 '+++ foo.hpp.new 2020-12-02 20:41:02.086700197 +0100',
3625 '+#include <ppapi.h>',
3629 mock_output_api = MockOutputApi()
3630 errors = PRESUBMIT.CheckForUseOfChromeAppsDeprecations(mock_input_api,
3632 self.assertEqual(1, len(errors))
3633 self.assertTrue( self.ERROR_MSG_PIECE in errors[0].message)
3634 self.assertTrue( 'foo.hpp' in errors[0].message)
3636 def testNoWarningPPAPI(self):
3637 mock_input_api = MockInputApi()
3638 mock_input_api.files = [
3641 ['A', 'Peppapig', 'B'],
3643 scm_diff='\n'.join([
3644 '--- foo.txt.old 2020-12-02 20:40:54.430676385 +0100',
3645 '+++ foo.txt.new 2020-12-02 20:41:02.086700197 +0100',
3652 mock_output_api = MockOutputApi()
3653 errors = PRESUBMIT.CheckForUseOfChromeAppsDeprecations(mock_input_api,
3655 self.assertEqual(0, len(errors))
3657 class CheckDeprecationOfPreferencesTest(unittest.TestCase):
3658 # Test that a warning is generated if a preference registration is removed
3659 # from a random file.
3660 def testWarning(self):
3661 mock_input_api = MockInputApi()
3662 mock_input_api.files = [
3666 ['A', 'prefs->RegisterStringPref("foo", "default");', 'B'],
3667 scm_diff='\n'.join([
3668 '--- foo.cc.old 2020-12-02 20:40:54.430676385 +0100',
3669 '+++ foo.cc.new 2020-12-02 20:41:02.086700197 +0100',
3672 '-prefs->RegisterStringPref("foo", "default");',
3676 mock_output_api = MockOutputApi()
3677 errors = PRESUBMIT.CheckDeprecationOfPreferences(mock_input_api,
3679 self.assertEqual(1, len(errors))
3681 'Discovered possible removal of preference registrations' in
3684 # Test that a warning is inhibited if the preference registration was moved
3685 # to the deprecation functions in browser prefs.
3686 def testNoWarningForMigration(self):
3687 mock_input_api = MockInputApi()
3688 mock_input_api.files = [
3689 # RegisterStringPref was removed from foo.cc.
3693 ['A', 'prefs->RegisterStringPref("foo", "default");', 'B'],
3694 scm_diff='\n'.join([
3695 '--- foo.cc.old 2020-12-02 20:40:54.430676385 +0100',
3696 '+++ foo.cc.new 2020-12-02 20:41:02.086700197 +0100',
3699 '-prefs->RegisterStringPref("foo", "default");',
3702 # But the preference was properly migrated.
3704 'chrome/browser/prefs/browser_prefs.cc',
3706 '// BEGIN_MIGRATE_OBSOLETE_LOCAL_STATE_PREFS',
3707 '// END_MIGRATE_OBSOLETE_LOCAL_STATE_PREFS',
3708 '// BEGIN_MIGRATE_OBSOLETE_PROFILE_PREFS',
3709 'prefs->RegisterStringPref("foo", "default");',
3710 '// END_MIGRATE_OBSOLETE_PROFILE_PREFS',
3713 '// BEGIN_MIGRATE_OBSOLETE_LOCAL_STATE_PREFS',
3714 '// END_MIGRATE_OBSOLETE_LOCAL_STATE_PREFS',
3715 '// BEGIN_MIGRATE_OBSOLETE_PROFILE_PREFS',
3716 '// END_MIGRATE_OBSOLETE_PROFILE_PREFS',
3718 scm_diff='\n'.join([
3719 '--- browser_prefs.cc.old 2020-12-02 20:51:40.812686731 +0100',
3720 '+++ browser_prefs.cc.new 2020-12-02 20:52:02.936755539 +0100',
3722 ' // END_MIGRATE_OBSOLETE_LOCAL_STATE_PREFS',
3723 ' // BEGIN_MIGRATE_OBSOLETE_PROFILE_PREFS',
3724 '+prefs->RegisterStringPref("foo", "default");',
3725 ' // END_MIGRATE_OBSOLETE_PROFILE_PREFS']),
3728 mock_output_api = MockOutputApi()
3729 errors = PRESUBMIT.CheckDeprecationOfPreferences(mock_input_api,
3731 self.assertEqual(0, len(errors))
3733 # Test that a warning is NOT inhibited if the preference registration was
3734 # moved to a place outside of the migration functions in browser_prefs.cc
3735 def testWarningForImproperMigration(self):
3736 mock_input_api = MockInputApi()
3737 mock_input_api.files = [
3738 # RegisterStringPref was removed from foo.cc.
3742 ['A', 'prefs->RegisterStringPref("foo", "default");', 'B'],
3743 scm_diff='\n'.join([
3744 '--- foo.cc.old 2020-12-02 20:40:54.430676385 +0100',
3745 '+++ foo.cc.new 2020-12-02 20:41:02.086700197 +0100',
3748 '-prefs->RegisterStringPref("foo", "default");',
3751 # The registration call was moved to a place in browser_prefs.cc that
3752 # is outside the migration functions.
3754 'chrome/browser/prefs/browser_prefs.cc',
3756 'prefs->RegisterStringPref("foo", "default");',
3757 '// BEGIN_MIGRATE_OBSOLETE_LOCAL_STATE_PREFS',
3758 '// END_MIGRATE_OBSOLETE_LOCAL_STATE_PREFS',
3759 '// BEGIN_MIGRATE_OBSOLETE_PROFILE_PREFS',
3760 '// END_MIGRATE_OBSOLETE_PROFILE_PREFS',
3763 '// BEGIN_MIGRATE_OBSOLETE_LOCAL_STATE_PREFS',
3764 '// END_MIGRATE_OBSOLETE_LOCAL_STATE_PREFS',
3765 '// BEGIN_MIGRATE_OBSOLETE_PROFILE_PREFS',
3766 '// END_MIGRATE_OBSOLETE_PROFILE_PREFS',
3768 scm_diff='\n'.join([
3769 '--- browser_prefs.cc.old 2020-12-02 20:51:40.812686731 +0100',
3770 '+++ browser_prefs.cc.new 2020-12-02 20:52:02.936755539 +0100',
3772 '+prefs->RegisterStringPref("foo", "default");',
3773 ' // BEGIN_MIGRATE_OBSOLETE_LOCAL_STATE_PREFS',
3774 ' // END_MIGRATE_OBSOLETE_LOCAL_STATE_PREFS']),
3777 mock_output_api = MockOutputApi()
3778 errors = PRESUBMIT.CheckDeprecationOfPreferences(mock_input_api,
3780 self.assertEqual(1, len(errors))
3782 'Discovered possible removal of preference registrations' in
3785 # Check that the presubmit fails if a marker line in brower_prefs.cc is
3787 def testDeletedMarkerRaisesError(self):
3788 mock_input_api = MockInputApi()
3789 mock_input_api.files = [
3790 MockAffectedFile('chrome/browser/prefs/browser_prefs.cc',
3792 '// BEGIN_MIGRATE_OBSOLETE_LOCAL_STATE_PREFS',
3793 '// END_MIGRATE_OBSOLETE_LOCAL_STATE_PREFS',
3794 '// BEGIN_MIGRATE_OBSOLETE_PROFILE_PREFS',
3795 # The following line is deleted for this test
3796 # '// END_MIGRATE_OBSOLETE_PROFILE_PREFS',
3799 mock_output_api = MockOutputApi()
3800 errors = PRESUBMIT.CheckDeprecationOfPreferences(mock_input_api,
3802 self.assertEqual(1, len(errors))
3804 'Broken .*MIGRATE_OBSOLETE_.*_PREFS markers in browser_prefs.cc.',
3808 if __name__ == '__main__':