2 # Copyright 2012 The Chromium Authors
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
14 from PRESUBMIT_test_mocks import MockFile, MockAffectedFile
15 from PRESUBMIT_test_mocks import MockInputApi, MockOutputApi
18 _TEST_DATA_DIR = 'base/test/data/presubmit'
21 class VersionControlConflictsTest(unittest.TestCase):
22 def testTypicalConflict(self):
23 lines = ['<<<<<<< HEAD',
24 ' base::ScopedTempDir temp_dir_;',
26 ' ScopedTempDir temp_dir_;',
28 errors = PRESUBMIT._CheckForVersionControlConflictsInFile(
29 MockInputApi(), MockFile('some/path/foo_platform.cc', lines))
30 self.assertEqual(3, len(errors))
31 self.assertTrue('1' in errors[0])
32 self.assertTrue('3' in errors[1])
33 self.assertTrue('5' in errors[2])
35 def testIgnoresReadmes(self):
36 lines = ['A First Level Header',
37 '====================',
39 'A Second Level Header',
40 '---------------------']
41 errors = PRESUBMIT._CheckForVersionControlConflictsInFile(
42 MockInputApi(), MockFile('some/polymer/README.md', lines))
43 self.assertEqual(0, len(errors))
46 class BadExtensionsTest(unittest.TestCase):
47 def testBadRejFile(self):
48 mock_input_api = MockInputApi()
49 mock_input_api.files = [
50 MockFile('some/path/foo.cc', ''),
51 MockFile('some/path/foo.cc.rej', ''),
52 MockFile('some/path2/bar.h.rej', ''),
55 results = PRESUBMIT.CheckPatchFiles(mock_input_api, MockOutputApi())
56 self.assertEqual(1, len(results))
57 self.assertEqual(2, len(results[0].items))
58 self.assertTrue('foo.cc.rej' in results[0].items[0])
59 self.assertTrue('bar.h.rej' in results[0].items[1])
61 def testBadOrigFile(self):
62 mock_input_api = MockInputApi()
63 mock_input_api.files = [
64 MockFile('other/path/qux.h.orig', ''),
65 MockFile('other/path/qux.h', ''),
66 MockFile('other/path/qux.cc', ''),
69 results = PRESUBMIT.CheckPatchFiles(mock_input_api, MockOutputApi())
70 self.assertEqual(1, len(results))
71 self.assertEqual(1, len(results[0].items))
72 self.assertTrue('qux.h.orig' in results[0].items[0])
74 def testGoodFiles(self):
75 mock_input_api = MockInputApi()
76 mock_input_api.files = [
77 MockFile('other/path/qux.h', ''),
78 MockFile('other/path/qux.cc', ''),
80 results = PRESUBMIT.CheckPatchFiles(mock_input_api, MockOutputApi())
81 self.assertEqual(0, len(results))
84 class CheckForSuperfluousStlIncludesInHeadersTest(unittest.TestCase):
85 def testGoodFiles(self):
86 mock_input_api = MockInputApi()
87 mock_input_api.files = [
88 # The check is not smart enough to figure out which definitions correspond
90 MockFile('other/path/foo.h',
93 # The check is not smart enough to do IWYU.
94 MockFile('other/path/bar.h',
95 ['#include "base/check.h"',
97 MockFile('other/path/qux.h',
98 ['#include "base/stl_util.h"',
100 MockFile('other/path/baz.h',
101 ['#include "set/vector.h"',
103 # The check is only for header files.
104 MockFile('other/path/not_checked.cc',
105 ['#include <vector>',
108 results = PRESUBMIT.CheckForSuperfluousStlIncludesInHeaders(
109 mock_input_api, MockOutputApi())
110 self.assertEqual(0, len(results))
112 def testBadFiles(self):
113 mock_input_api = MockInputApi()
114 mock_input_api.files = [
115 MockFile('other/path/foo.h',
116 ['#include <vector>',
118 MockFile('other/path/bar.h',
119 ['#include <limits>',
121 'no_std_namespace']),
123 results = PRESUBMIT.CheckForSuperfluousStlIncludesInHeaders(
124 mock_input_api, MockOutputApi())
125 self.assertEqual(1, len(results))
126 self.assertTrue('foo.h: Includes STL' in results[0].message)
127 self.assertTrue('bar.h: Includes STL' in results[0].message)
130 class CheckSingletonInHeadersTest(unittest.TestCase):
131 def testSingletonInArbitraryHeader(self):
132 diff_singleton_h = ['base::subtle::AtomicWord '
133 'base::Singleton<Type, Traits, DifferentiatingType>::']
134 diff_foo_h = ['// base::Singleton<Foo> in comment.',
135 'friend class base::Singleton<Foo>']
136 diff_foo2_h = [' //Foo* bar = base::Singleton<Foo>::get();']
137 diff_bad_h = ['Foo* foo = base::Singleton<Foo>::get();']
138 mock_input_api = MockInputApi()
139 mock_input_api.files = [MockAffectedFile('base/memory/singleton.h',
141 MockAffectedFile('foo.h', diff_foo_h),
142 MockAffectedFile('foo2.h', diff_foo2_h),
143 MockAffectedFile('bad.h', diff_bad_h)]
144 warnings = PRESUBMIT.CheckSingletonInHeaders(mock_input_api,
146 self.assertEqual(1, len(warnings))
147 self.assertEqual(1, len(warnings[0].items))
148 self.assertEqual('error', warnings[0].type)
149 self.assertTrue('Found base::Singleton<T>' in warnings[0].message)
151 def testSingletonInCC(self):
152 diff_cc = ['Foo* foo = base::Singleton<Foo>::get();']
153 mock_input_api = MockInputApi()
154 mock_input_api.files = [MockAffectedFile('some/path/foo.cc', diff_cc)]
155 warnings = PRESUBMIT.CheckSingletonInHeaders(mock_input_api,
157 self.assertEqual(0, len(warnings))
160 class DeprecatedOSMacroNamesTest(unittest.TestCase):
161 def testDeprecatedOSMacroNames(self):
162 lines = ['#if defined(OS_WIN)',
163 ' #elif defined(OS_WINDOW)',
164 ' # if defined(OS_MAC) || defined(OS_CHROME)']
165 errors = PRESUBMIT._CheckForDeprecatedOSMacrosInFile(
166 MockInputApi(), MockFile('some/path/foo_platform.cc', lines))
167 self.assertEqual(len(lines) + 1, len(errors))
168 self.assertTrue(':1: defined(OS_WIN) -> BUILDFLAG(IS_WIN)' in errors[0])
171 class InvalidIfDefinedMacroNamesTest(unittest.TestCase):
172 def testInvalidIfDefinedMacroNames(self):
173 lines = ['#if defined(TARGET_IPHONE_SIMULATOR)',
174 '#if !defined(TARGET_IPHONE_SIMULATOR)',
175 '#elif defined(TARGET_IPHONE_SIMULATOR)',
176 '#ifdef TARGET_IPHONE_SIMULATOR',
177 ' # ifdef TARGET_IPHONE_SIMULATOR',
178 '# if defined(VALID) || defined(TARGET_IPHONE_SIMULATOR)',
179 '# else // defined(TARGET_IPHONE_SIMULATOR)',
180 '#endif // defined(TARGET_IPHONE_SIMULATOR)']
181 errors = PRESUBMIT._CheckForInvalidIfDefinedMacrosInFile(
182 MockInputApi(), MockFile('some/path/source.mm', lines))
183 self.assertEqual(len(lines), len(errors))
185 def testValidIfDefinedMacroNames(self):
186 lines = ['#if defined(FOO)',
188 errors = PRESUBMIT._CheckForInvalidIfDefinedMacrosInFile(
189 MockInputApi(), MockFile('some/path/source.cc', lines))
190 self.assertEqual(0, len(errors))
193 class CheckAddedDepsHaveTestApprovalsTest(unittest.TestCase):
195 def calculate(self, old_include_rules, old_specific_include_rules,
196 new_include_rules, new_specific_include_rules):
197 return PRESUBMIT._CalculateAddedDeps(
198 os.path, 'include_rules = %r\nspecific_include_rules = %r' % (
199 old_include_rules, old_specific_include_rules),
200 'include_rules = %r\nspecific_include_rules = %r' % (
201 new_include_rules, new_specific_include_rules))
203 def testCalculateAddedDeps(self):
204 old_include_rules = [
213 old_specific_include_rules = {
219 new_include_rules = [
226 '+grit/generated_resources.h",',
230 '+' + os.path.join('third_party', 'WebKit'),
232 new_specific_include_rules = {
242 os.path.join('chrome', 'DEPS'),
243 os.path.join('gpu', 'DEPS'),
244 os.path.join('components', 'DEPS'),
245 os.path.join('policy', 'DEPS'),
246 os.path.join('third_party', 'WebKit', 'DEPS'),
250 self.calculate(old_include_rules, old_specific_include_rules,
251 new_include_rules, new_specific_include_rules))
253 def testCalculateAddedDepsIgnoresPermutations(self):
254 old_include_rules = [
258 new_include_rules = [
262 self.assertEqual(set(),
263 self.calculate(old_include_rules, {}, new_include_rules,
267 class JSONParsingTest(unittest.TestCase):
268 def testSuccess(self):
269 input_api = MockInputApi()
270 filename = 'valid_json.json'
271 contents = ['// This is a comment.',
273 ' "key1": ["value1", "value2"],',
274 ' "key2": 3 // This is an inline comment.',
277 input_api.files = [MockFile(filename, contents)]
278 self.assertEqual(None,
279 PRESUBMIT._GetJSONParseError(input_api, filename))
281 def testFailure(self):
282 input_api = MockInputApi()
284 ('invalid_json_1.json',
286 'Expecting property name'),
287 ('invalid_json_2.json',
289 '{ "hello": "world }'],
290 'Unterminated string starting at:'),
291 ('invalid_json_3.json',
292 ['{ "a": "b", "c": "d", }'],
293 'Expecting property name'),
294 ('invalid_json_4.json',
295 ['{ "a": "b" "c": "d" }'],
296 "Expecting ',' delimiter:"),
299 input_api.files = [MockFile(filename, contents)
300 for (filename, contents, _) in test_data]
302 for (filename, _, expected_error) in test_data:
303 actual_error = PRESUBMIT._GetJSONParseError(input_api, filename)
304 self.assertTrue(expected_error in str(actual_error),
305 "'%s' not found in '%s'" % (expected_error, actual_error))
307 def testNoEatComments(self):
308 input_api = MockInputApi()
309 file_with_comments = 'file_with_comments.json'
310 contents_with_comments = ['// This is a comment.',
312 ' "key1": ["value1", "value2"],',
313 ' "key2": 3 // This is an inline comment.',
316 file_without_comments = 'file_without_comments.json'
317 contents_without_comments = ['{',
318 ' "key1": ["value1", "value2"],',
322 input_api.files = [MockFile(file_with_comments, contents_with_comments),
323 MockFile(file_without_comments,
324 contents_without_comments)]
326 self.assertNotEqual(None,
327 str(PRESUBMIT._GetJSONParseError(input_api,
329 eat_comments=False)))
330 self.assertEqual(None,
331 PRESUBMIT._GetJSONParseError(input_api,
332 file_without_comments,
336 class IDLParsingTest(unittest.TestCase):
337 def testSuccess(self):
338 input_api = MockInputApi()
339 filename = 'valid_idl_basics.idl'
340 contents = ['// Tests a valid IDL file.',
341 'namespace idl_basics {',
347 ' dictionary MyType1 {',
351 ' callback Callback1 = void();',
352 ' callback Callback2 = void(long x);',
353 ' callback Callback3 = void(MyType1 arg);',
354 ' callback Callback4 = void(EnumType type);',
356 ' interface Functions {',
357 ' static void function1();',
358 ' static void function2(long x);',
359 ' static void function3(MyType1 arg);',
360 ' static void function4(Callback1 cb);',
361 ' static void function5(Callback2 cb);',
362 ' static void function6(Callback3 cb);',
363 ' static void function7(Callback4 cb);',
366 ' interface Events {',
367 ' static void onFoo1();',
368 ' static void onFoo2(long x);',
369 ' static void onFoo2(MyType1 arg);',
370 ' static void onFoo3(EnumType type);',
374 input_api.files = [MockFile(filename, contents)]
375 self.assertEqual(None,
376 PRESUBMIT._GetIDLParseError(input_api, filename))
378 def testFailure(self):
379 input_api = MockInputApi()
381 ('invalid_idl_1.idl',
388 'Unexpected "{" after keyword "dictionary".\n'),
389 # TODO(yoz): Disabled because it causes the IDL parser to hang.
390 # See crbug.com/363830.
391 # ('invalid_idl_2.idl',
392 # (['namespace test {',
393 # ' dictionary MissingSemicolon {',
398 # 'Unexpected symbol DOMString after symbol a.'),
399 ('invalid_idl_3.idl',
402 ' enum MissingComma {',
407 'Unexpected symbol name2 after symbol name1.'),
408 ('invalid_idl_4.idl',
411 ' enum TrailingComma {',
416 'Trailing comma in block.'),
417 ('invalid_idl_5.idl',
420 ' callback Callback1 = void(;',
422 'Unexpected ";" after "(".'),
423 ('invalid_idl_6.idl',
426 ' callback Callback1 = void(long );',
428 'Unexpected ")" after symbol long.'),
429 ('invalid_idl_7.idl',
432 ' interace Events {',
433 ' static void onFoo1();',
436 'Unexpected symbol Events after symbol interace.'),
437 ('invalid_idl_8.idl',
440 ' interface NotEvent {',
441 ' static void onFoo1();',
444 'Did not process Interface Interface(NotEvent)'),
445 ('invalid_idl_9.idl',
449 ' static void function1();',
452 'Interface missing name.'),
455 input_api.files = [MockFile(filename, contents)
456 for (filename, contents, _) in test_data]
458 for (filename, _, expected_error) in test_data:
459 actual_error = PRESUBMIT._GetIDLParseError(input_api, filename)
460 self.assertTrue(expected_error in str(actual_error),
461 "'%s' not found in '%s'" % (expected_error, actual_error))
464 class UserMetricsActionTest(unittest.TestCase):
465 def testUserMetricsActionInActions(self):
466 input_api = MockInputApi()
467 file_with_user_action = 'file_with_user_action.cc'
468 contents_with_user_action = [
469 'base::UserMetricsAction("AboutChrome")'
472 input_api.files = [MockFile(file_with_user_action,
473 contents_with_user_action)]
476 [], PRESUBMIT.CheckUserActionUpdate(input_api, MockOutputApi()))
478 def testUserMetricsActionNotAddedToActions(self):
479 input_api = MockInputApi()
480 file_with_user_action = 'file_with_user_action.cc'
481 contents_with_user_action = [
482 'base::UserMetricsAction("NotInActionsXml")'
485 input_api.files = [MockFile(file_with_user_action,
486 contents_with_user_action)]
488 output = PRESUBMIT.CheckUserActionUpdate(input_api, MockOutputApi())
490 ('File %s line %d: %s is missing in '
491 'tools/metrics/actions/actions.xml. Please run '
492 'tools/metrics/actions/extract_actions.py to update.'
493 % (file_with_user_action, 1, 'NotInActionsXml')),
496 def testUserMetricsActionInTestFile(self):
497 input_api = MockInputApi()
498 file_with_user_action = 'file_with_user_action_unittest.cc'
499 contents_with_user_action = [
500 'base::UserMetricsAction("NotInActionsXml")'
503 input_api.files = [MockFile(file_with_user_action,
504 contents_with_user_action)]
507 [], PRESUBMIT.CheckUserActionUpdate(input_api, MockOutputApi()))
510 class PydepsNeedsUpdatingTest(unittest.TestCase):
512 def __init__(self, stdout_func):
513 self._stdout_func = stdout_func
516 self.stdout = io.StringIO(self._stdout_func())
519 class MockSubprocess:
520 CalledProcessError = subprocess.CalledProcessError
524 self._popen_func = None
526 def SetPopenCallback(self, func):
527 self._popen_func = func
529 def Popen(self, cmd, *args, **kwargs):
530 return PydepsNeedsUpdatingTest.MockPopen(lambda: self._popen_func(cmd))
532 def _MockParseGclientArgs(self, is_android=True):
533 return lambda: {'checkout_android': 'true' if is_android else 'false' }
536 mock_all_pydeps = ['A.pydeps', 'B.pydeps', 'D.pydeps']
537 self.old_ALL_PYDEPS_FILES = PRESUBMIT._ALL_PYDEPS_FILES
538 PRESUBMIT._ALL_PYDEPS_FILES = mock_all_pydeps
539 mock_android_pydeps = ['D.pydeps']
540 self.old_ANDROID_SPECIFIC_PYDEPS_FILES = (
541 PRESUBMIT._ANDROID_SPECIFIC_PYDEPS_FILES)
542 PRESUBMIT._ANDROID_SPECIFIC_PYDEPS_FILES = mock_android_pydeps
543 self.old_ParseGclientArgs = PRESUBMIT._ParseGclientArgs
544 PRESUBMIT._ParseGclientArgs = self._MockParseGclientArgs()
545 self.mock_input_api = MockInputApi()
546 self.mock_output_api = MockOutputApi()
547 self.mock_input_api.subprocess = PydepsNeedsUpdatingTest.MockSubprocess()
548 self.checker = PRESUBMIT.PydepsChecker(self.mock_input_api, mock_all_pydeps)
549 self.checker._file_cache = {
550 'A.pydeps': '# Generated by:\n# CMD --output A.pydeps A\nA.py\nC.py\n',
551 'B.pydeps': '# Generated by:\n# CMD --output B.pydeps B\nB.py\nC.py\n',
552 'D.pydeps': '# Generated by:\n# CMD --output D.pydeps D\nD.py\n',
556 PRESUBMIT._ALL_PYDEPS_FILES = self.old_ALL_PYDEPS_FILES
557 PRESUBMIT._ANDROID_SPECIFIC_PYDEPS_FILES = (
558 self.old_ANDROID_SPECIFIC_PYDEPS_FILES)
559 PRESUBMIT._ParseGclientArgs = self.old_ParseGclientArgs
562 return PRESUBMIT.CheckPydepsNeedsUpdating(self.mock_input_api,
563 self.mock_output_api,
564 checker_for_tests=self.checker)
566 def testAddedPydep(self):
567 # PRESUBMIT.CheckPydepsNeedsUpdating is only implemented for Linux.
568 if not self.mock_input_api.platform.startswith('linux'):
571 self.mock_input_api.files = [
572 MockAffectedFile('new.pydeps', [], action='A'),
575 self.mock_input_api.CreateMockFileInPath(
576 [x.LocalPath() for x in self.mock_input_api.AffectedFiles(
577 include_deletes=True)])
578 results = self._RunCheck()
579 self.assertEqual(1, len(results))
580 self.assertIn('PYDEPS_FILES', str(results[0]))
582 def testPydepNotInSrc(self):
583 self.mock_input_api.files = [
584 MockAffectedFile('new.pydeps', [], action='A'),
586 self.mock_input_api.CreateMockFileInPath([])
587 results = self._RunCheck()
588 self.assertEqual(0, len(results))
590 def testRemovedPydep(self):
591 # PRESUBMIT.CheckPydepsNeedsUpdating is only implemented for Linux.
592 if not self.mock_input_api.platform.startswith('linux'):
595 self.mock_input_api.files = [
596 MockAffectedFile(PRESUBMIT._ALL_PYDEPS_FILES[0], [], action='D'),
598 self.mock_input_api.CreateMockFileInPath(
599 [x.LocalPath() for x in self.mock_input_api.AffectedFiles(
600 include_deletes=True)])
601 results = self._RunCheck()
602 self.assertEqual(1, len(results))
603 self.assertIn('PYDEPS_FILES', str(results[0]))
605 def testRandomPyIgnored(self):
606 # PRESUBMIT.CheckPydepsNeedsUpdating is only implemented for Linux.
607 if not self.mock_input_api.platform.startswith('linux'):
610 self.mock_input_api.files = [
611 MockAffectedFile('random.py', []),
614 results = self._RunCheck()
615 self.assertEqual(0, len(results), 'Unexpected results: %r' % results)
617 def testRelevantPyNoChange(self):
618 # PRESUBMIT.CheckPydepsNeedsUpdating is only implemented for Linux.
619 if not self.mock_input_api.platform.startswith('linux'):
622 self.mock_input_api.files = [
623 MockAffectedFile('A.py', []),
626 def popen_callback(cmd):
627 self.assertEqual('CMD --output A.pydeps A --output ""', cmd)
628 return self.checker._file_cache['A.pydeps']
630 self.mock_input_api.subprocess.SetPopenCallback(popen_callback)
632 results = self._RunCheck()
633 self.assertEqual(0, len(results), 'Unexpected results: %r' % results)
635 def testRelevantPyOneChange(self):
636 # PRESUBMIT.CheckPydepsNeedsUpdating is only implemented for Linux.
637 if not self.mock_input_api.platform.startswith('linux'):
640 self.mock_input_api.files = [
641 MockAffectedFile('A.py', []),
644 def popen_callback(cmd):
645 self.assertEqual('CMD --output A.pydeps A --output ""', cmd)
646 return 'changed data'
648 self.mock_input_api.subprocess.SetPopenCallback(popen_callback)
650 results = self._RunCheck()
651 self.assertEqual(1, len(results))
652 # Check that --output "" is not included.
653 self.assertNotIn('""', str(results[0]))
654 self.assertIn('File is stale', str(results[0]))
656 def testRelevantPyTwoChanges(self):
657 # PRESUBMIT.CheckPydepsNeedsUpdating is only implemented for Linux.
658 if not self.mock_input_api.platform.startswith('linux'):
661 self.mock_input_api.files = [
662 MockAffectedFile('C.py', []),
665 def popen_callback(cmd):
666 return 'changed data'
668 self.mock_input_api.subprocess.SetPopenCallback(popen_callback)
670 results = self._RunCheck()
671 self.assertEqual(2, len(results))
672 self.assertIn('File is stale', str(results[0]))
673 self.assertIn('File is stale', str(results[1]))
675 def testRelevantAndroidPyInNonAndroidCheckout(self):
676 # PRESUBMIT.CheckPydepsNeedsUpdating is only implemented for Linux.
677 if not self.mock_input_api.platform.startswith('linux'):
680 self.mock_input_api.files = [
681 MockAffectedFile('D.py', []),
684 def popen_callback(cmd):
685 self.assertEqual('CMD --output D.pydeps D --output ""', cmd)
686 return 'changed data'
688 self.mock_input_api.subprocess.SetPopenCallback(popen_callback)
689 PRESUBMIT._ParseGclientArgs = self._MockParseGclientArgs(is_android=False)
691 results = self._RunCheck()
692 self.assertEqual(1, len(results))
693 self.assertIn('Android', str(results[0]))
694 self.assertIn('D.pydeps', str(results[0]))
696 def testGnPathsAndMissingOutputFlag(self):
697 # PRESUBMIT.CheckPydepsNeedsUpdating is only implemented for Linux.
698 if not self.mock_input_api.platform.startswith('linux'):
701 self.checker._file_cache = {
702 'A.pydeps': '# Generated by:\n# CMD --gn-paths A\n//A.py\n//C.py\n',
703 'B.pydeps': '# Generated by:\n# CMD --gn-paths B\n//B.py\n//C.py\n',
704 'D.pydeps': '# Generated by:\n# CMD --gn-paths D\n//D.py\n',
707 self.mock_input_api.files = [
708 MockAffectedFile('A.py', []),
711 def popen_callback(cmd):
712 self.assertEqual('CMD --gn-paths A --output A.pydeps --output ""', cmd)
713 return 'changed data'
715 self.mock_input_api.subprocess.SetPopenCallback(popen_callback)
717 results = self._RunCheck()
718 self.assertEqual(1, len(results))
719 self.assertIn('File is stale', str(results[0]))
722 class IncludeGuardTest(unittest.TestCase):
723 def testIncludeGuardChecks(self):
724 mock_input_api = MockInputApi()
725 mock_output_api = MockOutputApi()
726 mock_input_api.files = [
727 MockAffectedFile('content/browser/thing/foo.h', [
729 '#ifndef CONTENT_BROWSER_THING_FOO_H_',
730 '#define CONTENT_BROWSER_THING_FOO_H_',
731 'struct McBoatFace;',
732 '#endif // CONTENT_BROWSER_THING_FOO_H_',
734 MockAffectedFile('content/browser/thing/bar.h', [
735 '#ifndef CONTENT_BROWSER_THING_BAR_H_',
736 '#define CONTENT_BROWSER_THING_BAR_H_',
737 'namespace content {',
738 '#endif // CONTENT_BROWSER_THING_BAR_H_',
739 '} // namespace content',
741 MockAffectedFile('content/browser/test1.h', [
742 'namespace content {',
743 '} // namespace content',
745 MockAffectedFile('content\\browser\\win.h', [
746 '#ifndef CONTENT_BROWSER_WIN_H_',
747 '#define CONTENT_BROWSER_WIN_H_',
748 'struct McBoatFace;',
749 '#endif // CONTENT_BROWSER_WIN_H_',
751 MockAffectedFile('content/browser/test2.h', [
753 '#ifndef CONTENT_BROWSER_TEST2_H_',
754 'struct McBoatFace;',
755 '#endif // CONTENT_BROWSER_TEST2_H_',
757 MockAffectedFile('content/browser/internal.h', [
759 '#ifndef CONTENT_BROWSER_INTERNAL_H_',
760 '#define CONTENT_BROWSER_INTERNAL_H_',
762 '#ifndef INTERNAL_CONTENT_BROWSER_INTERNAL_H_',
763 '#define INTERNAL_CONTENT_BROWSER_INTERNAL_H_',
764 'namespace internal {',
765 '} // namespace internal',
766 '#endif // INTERNAL_CONTENT_BROWSER_THING_BAR_H_',
767 'namespace content {',
768 '} // namespace content',
769 '#endif // CONTENT_BROWSER_THING_BAR_H_',
771 MockAffectedFile('content/browser/thing/foo.cc', [
772 '// This is a non-header.',
774 MockAffectedFile('content/browser/disabled.h', [
775 '// no-include-guard-because-multiply-included',
776 'struct McBoatFace;',
778 # New files don't allow misspelled include guards.
779 MockAffectedFile('content/browser/spleling.h', [
780 '#ifndef CONTENT_BROWSER_SPLLEING_H_',
781 '#define CONTENT_BROWSER_SPLLEING_H_',
782 'struct McBoatFace;',
783 '#endif // CONTENT_BROWSER_SPLLEING_H_',
785 # New files don't allow + in include guards.
786 MockAffectedFile('content/browser/foo+bar.h', [
787 '#ifndef CONTENT_BROWSER_FOO+BAR_H_',
788 '#define CONTENT_BROWSER_FOO+BAR_H_',
789 'struct McBoatFace;',
790 '#endif // CONTENT_BROWSER_FOO+BAR_H_',
792 # Old files allow misspelled include guards (for now).
793 MockAffectedFile('chrome/old.h', [
795 '#ifndef CHROME_ODL_H_',
796 '#define CHROME_ODL_H_',
797 '#endif // CHROME_ODL_H_',
800 '#ifndef CHROME_ODL_H_',
801 '#define CHROME_ODL_H_',
802 '#endif // CHROME_ODL_H_',
804 # Using a Blink style include guard outside Blink is wrong.
805 MockAffectedFile('content/NotInBlink.h', [
806 '#ifndef NotInBlink_h',
807 '#define NotInBlink_h',
808 'struct McBoatFace;',
809 '#endif // NotInBlink_h',
811 # Using a Blink style include guard in Blink is no longer ok.
812 MockAffectedFile('third_party/blink/InBlink.h', [
815 'struct McBoatFace;',
816 '#endif // InBlink_h',
818 # Using a bad include guard in Blink is not ok.
819 MockAffectedFile('third_party/blink/AlsoInBlink.h', [
820 '#ifndef WrongInBlink_h',
821 '#define WrongInBlink_h',
822 'struct McBoatFace;',
823 '#endif // WrongInBlink_h',
825 # Using a bad include guard in Blink is not supposed to be accepted even
826 # if it's an old file. However the current presubmit has accepted this
828 MockAffectedFile('third_party/blink/StillInBlink.h', [
830 '#ifndef AcceptedInBlink_h',
831 '#define AcceptedInBlink_h',
832 'struct McBoatFace;',
833 '#endif // AcceptedInBlink_h',
836 '#ifndef AcceptedInBlink_h',
837 '#define AcceptedInBlink_h',
838 'struct McBoatFace;',
839 '#endif // AcceptedInBlink_h',
841 # Using a non-Chromium include guard in third_party
842 # (outside blink) is accepted.
843 MockAffectedFile('third_party/foo/some_file.h', [
844 '#ifndef REQUIRED_RPCNDR_H_',
845 '#define REQUIRED_RPCNDR_H_',
846 'struct SomeFileFoo;',
847 '#endif // REQUIRED_RPCNDR_H_',
849 # Not having proper include guard in *_message_generator.h
850 # for old IPC messages is allowed.
851 MockAffectedFile('content/common/content_message_generator.h', [
852 '#undef CONTENT_COMMON_FOO_MESSAGES_H_',
853 '#include "content/common/foo_messages.h"',
854 '#ifndef CONTENT_COMMON_FOO_MESSAGES_H_',
855 '#error "Failed to include content/common/foo_messages.h"',
859 msgs = PRESUBMIT.CheckForIncludeGuards(
860 mock_input_api, mock_output_api)
861 expected_fail_count = 8
862 self.assertEqual(expected_fail_count, len(msgs),
863 'Expected %d items, found %d: %s'
864 % (expected_fail_count, len(msgs), msgs))
865 self.assertEqual(msgs[0].items, ['content/browser/thing/bar.h'])
866 self.assertEqual(msgs[0].message,
867 'Include guard CONTENT_BROWSER_THING_BAR_H_ '
868 'not covering the whole file')
870 self.assertIn('content/browser/test1.h', msgs[1].message)
871 self.assertIn('Recommended name: CONTENT_BROWSER_TEST1_H_',
874 self.assertEqual(msgs[2].items, ['content/browser/test2.h:3'])
875 self.assertEqual(msgs[2].message,
876 'Missing "#define CONTENT_BROWSER_TEST2_H_" for '
879 self.assertEqual(msgs[3].items, ['content/browser/spleling.h:1'])
880 self.assertEqual(msgs[3].message,
881 'Header using the wrong include guard name '
882 'CONTENT_BROWSER_SPLLEING_H_')
884 self.assertIn('content/browser/foo+bar.h', msgs[4].message)
885 self.assertIn('Recommended name: CONTENT_BROWSER_FOO_BAR_H_',
888 self.assertEqual(msgs[5].items, ['content/NotInBlink.h:1'])
889 self.assertEqual(msgs[5].message,
890 'Header using the wrong include guard name '
893 self.assertEqual(msgs[6].items, ['third_party/blink/InBlink.h:1'])
894 self.assertEqual(msgs[6].message,
895 'Header using the wrong include guard name '
898 self.assertEqual(msgs[7].items, ['third_party/blink/AlsoInBlink.h:1'])
899 self.assertEqual(msgs[7].message,
900 'Header using the wrong include guard name '
903 class AccessibilityRelnotesFieldTest(unittest.TestCase):
904 def testRelnotesPresent(self):
905 mock_input_api = MockInputApi()
906 mock_output_api = MockOutputApi()
908 mock_input_api.files = [MockAffectedFile('ui/accessibility/foo.bar', [''])]
909 mock_input_api.change.DescriptionText = lambda : 'Commit description'
910 mock_input_api.change.footers['AX-Relnotes'] = [
911 'Important user facing change']
913 msgs = PRESUBMIT.CheckAccessibilityRelnotesField(
914 mock_input_api, mock_output_api)
915 self.assertEqual(0, len(msgs),
916 'Expected %d messages, found %d: %s'
917 % (0, len(msgs), msgs))
919 def testRelnotesMissingFromAccessibilityChange(self):
920 mock_input_api = MockInputApi()
921 mock_output_api = MockOutputApi()
923 mock_input_api.files = [
924 MockAffectedFile('some/file', ['']),
925 MockAffectedFile('ui/accessibility/foo.bar', ['']),
926 MockAffectedFile('some/other/file', [''])
928 mock_input_api.change.DescriptionText = lambda : 'Commit description'
930 msgs = PRESUBMIT.CheckAccessibilityRelnotesField(
931 mock_input_api, mock_output_api)
932 self.assertEqual(1, len(msgs),
933 'Expected %d messages, found %d: %s'
934 % (1, len(msgs), msgs))
935 self.assertTrue("Missing 'AX-Relnotes:' field" in msgs[0].message,
936 'Missing AX-Relnotes field message not found in errors')
938 # The relnotes footer is not required for changes which do not touch any
939 # accessibility directories.
940 def testIgnoresNonAccessibilityCode(self):
941 mock_input_api = MockInputApi()
942 mock_output_api = MockOutputApi()
944 mock_input_api.files = [
945 MockAffectedFile('some/file', ['']),
946 MockAffectedFile('some/other/file', [''])
948 mock_input_api.change.DescriptionText = lambda : 'Commit description'
950 msgs = PRESUBMIT.CheckAccessibilityRelnotesField(
951 mock_input_api, mock_output_api)
952 self.assertEqual(0, len(msgs),
953 'Expected %d messages, found %d: %s'
954 % (0, len(msgs), msgs))
956 # Test that our presubmit correctly raises an error for a set of known paths.
957 def testExpectedPaths(self):
959 "chrome/browser/accessibility/foo.py",
960 "chrome/browser/ash/arc/accessibility/foo.cc",
961 "chrome/browser/ui/views/accessibility/foo.h",
962 "chrome/browser/extensions/api/automation/foo.h",
963 "chrome/browser/extensions/api/automation_internal/foo.cc",
964 "chrome/renderer/extensions/accessibility_foo.h",
965 "chrome/tests/data/accessibility/foo.html",
966 "content/browser/accessibility/foo.cc",
967 "content/renderer/accessibility/foo.h",
968 "content/tests/data/accessibility/foo.cc",
969 "extensions/renderer/api/automation/foo.h",
970 "ui/accessibility/foo/bar/baz.cc",
971 "ui/views/accessibility/foo/bar/baz.h",
974 for testFile in filesToTest:
975 mock_input_api = MockInputApi()
976 mock_output_api = MockOutputApi()
978 mock_input_api.files = [
979 MockAffectedFile(testFile, [''])
981 mock_input_api.change.DescriptionText = lambda : 'Commit description'
983 msgs = PRESUBMIT.CheckAccessibilityRelnotesField(
984 mock_input_api, mock_output_api)
985 self.assertEqual(1, len(msgs),
986 'Expected %d messages, found %d: %s, for file %s'
987 % (1, len(msgs), msgs, testFile))
988 self.assertTrue("Missing 'AX-Relnotes:' field" in msgs[0].message,
989 ('Missing AX-Relnotes field message not found in errors '
990 ' for file %s' % (testFile)))
992 # Test that AX-Relnotes field can appear in the commit description (as long
993 # as it appears at the beginning of a line).
994 def testRelnotesInCommitDescription(self):
995 mock_input_api = MockInputApi()
996 mock_output_api = MockOutputApi()
998 mock_input_api.files = [
999 MockAffectedFile('ui/accessibility/foo.bar', ['']),
1001 mock_input_api.change.DescriptionText = lambda : ('Description:\n' +
1002 'AX-Relnotes: solves all accessibility issues forever')
1004 msgs = PRESUBMIT.CheckAccessibilityRelnotesField(
1005 mock_input_api, mock_output_api)
1006 self.assertEqual(0, len(msgs),
1007 'Expected %d messages, found %d: %s'
1008 % (0, len(msgs), msgs))
1010 # Test that we don't match AX-Relnotes if it appears in the middle of a line.
1011 def testRelnotesMustAppearAtBeginningOfLine(self):
1012 mock_input_api = MockInputApi()
1013 mock_output_api = MockOutputApi()
1015 mock_input_api.files = [
1016 MockAffectedFile('ui/accessibility/foo.bar', ['']),
1018 mock_input_api.change.DescriptionText = lambda : ('Description:\n' +
1019 'This change has no AX-Relnotes: we should print a warning')
1021 msgs = PRESUBMIT.CheckAccessibilityRelnotesField(
1022 mock_input_api, mock_output_api)
1023 self.assertTrue("Missing 'AX-Relnotes:' field" in msgs[0].message,
1024 'Missing AX-Relnotes field message not found in errors')
1026 # Tests that the AX-Relnotes field can be lowercase and use a '=' in place
1028 def testRelnotesLowercaseWithEqualSign(self):
1029 mock_input_api = MockInputApi()
1030 mock_output_api = MockOutputApi()
1032 mock_input_api.files = [
1033 MockAffectedFile('ui/accessibility/foo.bar', ['']),
1035 mock_input_api.change.DescriptionText = lambda : ('Description:\n' +
1036 'ax-relnotes= this is a valid format for accessibility relnotes')
1038 msgs = PRESUBMIT.CheckAccessibilityRelnotesField(
1039 mock_input_api, mock_output_api)
1040 self.assertEqual(0, len(msgs),
1041 'Expected %d messages, found %d: %s'
1042 % (0, len(msgs), msgs))
1044 class AccessibilityEventsTestsAreIncludedForAndroidTest(unittest.TestCase):
1045 # Test that no warning is raised when the Android file is also modified.
1046 def testAndroidChangeIncluded(self):
1047 mock_input_api = MockInputApi()
1049 mock_input_api.files = [
1050 MockAffectedFile('content/test/data/accessibility/event/foo.html',
1053 'accessibility/WebContentsAccessibilityEventsTest.java',
1057 msgs = PRESUBMIT.CheckAccessibilityEventsTestsAreIncludedForAndroid(
1058 mock_input_api, MockOutputApi())
1059 self.assertEqual(0, len(msgs),
1060 'Expected %d messages, found %d: %s'
1061 % (0, len(msgs), msgs))
1063 # Test that a warning is raised when the Android file is not modified.
1064 def testAndroidChangeMissing(self):
1065 mock_input_api = MockInputApi()
1067 mock_input_api.files = [
1068 MockAffectedFile('content/test/data/accessibility/event/foo.html',
1072 msgs = PRESUBMIT.CheckAccessibilityEventsTestsAreIncludedForAndroid(
1073 mock_input_api, MockOutputApi())
1074 self.assertEqual(1, len(msgs),
1075 'Expected %d messages, found %d: %s'
1076 % (1, len(msgs), msgs))
1078 # Test that Android change is not required when no html file is added/removed.
1079 def testIgnoreNonHtmlFiles(self):
1080 mock_input_api = MockInputApi()
1082 mock_input_api.files = [
1083 MockAffectedFile('content/test/data/accessibility/event/foo.txt',
1085 MockAffectedFile('content/test/data/accessibility/event/foo.cc',
1087 MockAffectedFile('content/test/data/accessibility/event/foo.h',
1089 MockAffectedFile('content/test/data/accessibility/event/foo.py',
1093 msgs = PRESUBMIT.CheckAccessibilityEventsTestsAreIncludedForAndroid(
1094 mock_input_api, MockOutputApi())
1095 self.assertEqual(0, len(msgs),
1096 'Expected %d messages, found %d: %s'
1097 % (0, len(msgs), msgs))
1099 # Test that Android change is not required for unrelated html files.
1100 def testIgnoreNonRelatedHtmlFiles(self):
1101 mock_input_api = MockInputApi()
1103 mock_input_api.files = [
1104 MockAffectedFile('content/test/data/accessibility/aria/foo.html',
1106 MockAffectedFile('content/test/data/accessibility/html/foo.html',
1108 MockAffectedFile('chrome/tests/data/accessibility/foo.html',
1112 msgs = PRESUBMIT.CheckAccessibilityEventsTestsAreIncludedForAndroid(
1113 mock_input_api, MockOutputApi())
1114 self.assertEqual(0, len(msgs),
1115 'Expected %d messages, found %d: %s'
1116 % (0, len(msgs), msgs))
1118 # Test that only modifying an html file will not trigger the warning.
1119 def testIgnoreModifiedFiles(self):
1120 mock_input_api = MockInputApi()
1122 mock_input_api.files = [
1123 MockAffectedFile('content/test/data/accessibility/event/foo.html',
1127 msgs = PRESUBMIT.CheckAccessibilityEventsTestsAreIncludedForAndroid(
1128 mock_input_api, MockOutputApi())
1129 self.assertEqual(0, len(msgs),
1130 'Expected %d messages, found %d: %s'
1131 % (0, len(msgs), msgs))
1133 # Test that deleting an html file will trigger the warning.
1134 def testAndroidChangeMissingOnDeletedFile(self):
1135 mock_input_api = MockInputApi()
1137 mock_input_api.files = [
1138 MockAffectedFile('content/test/data/accessibility/event/foo.html',
1142 msgs = PRESUBMIT.CheckAccessibilityEventsTestsAreIncludedForAndroid(
1143 mock_input_api, MockOutputApi())
1144 self.assertEqual(1, len(msgs),
1145 'Expected %d messages, found %d: %s'
1146 % (1, len(msgs), msgs))
1148 class AccessibilityTreeTestsAreIncludedForAndroidTest(unittest.TestCase):
1149 # Test that no warning is raised when the Android file is also modified.
1150 def testAndroidChangeIncluded(self):
1151 mock_input_api = MockInputApi()
1153 mock_input_api.files = [
1154 MockAffectedFile('content/test/data/accessibility/aria/foo.html',
1157 'accessibility/WebContentsAccessibilityTreeTest.java',
1161 msgs = PRESUBMIT.CheckAccessibilityTreeTestsAreIncludedForAndroid(
1162 mock_input_api, MockOutputApi())
1163 self.assertEqual(0, len(msgs),
1164 'Expected %d messages, found %d: %s'
1165 % (0, len(msgs), msgs))
1167 # Test that no warning is raised when the Android file is also modified.
1168 def testAndroidChangeIncludedManyFiles(self):
1169 mock_input_api = MockInputApi()
1171 mock_input_api.files = [
1172 MockAffectedFile('content/test/data/accessibility/accname/foo.html',
1174 MockAffectedFile('content/test/data/accessibility/aria/foo.html',
1176 MockAffectedFile('content/test/data/accessibility/css/foo.html',
1178 MockAffectedFile('content/test/data/accessibility/html/foo.html',
1181 'accessibility/WebContentsAccessibilityTreeTest.java',
1185 msgs = PRESUBMIT.CheckAccessibilityTreeTestsAreIncludedForAndroid(
1186 mock_input_api, MockOutputApi())
1187 self.assertEqual(0, len(msgs),
1188 'Expected %d messages, found %d: %s'
1189 % (0, len(msgs), msgs))
1191 # Test that a warning is raised when the Android file is not modified.
1192 def testAndroidChangeMissing(self):
1193 mock_input_api = MockInputApi()
1195 mock_input_api.files = [
1196 MockAffectedFile('content/test/data/accessibility/aria/foo.html',
1200 msgs = PRESUBMIT.CheckAccessibilityTreeTestsAreIncludedForAndroid(
1201 mock_input_api, MockOutputApi())
1202 self.assertEqual(1, len(msgs),
1203 'Expected %d messages, found %d: %s'
1204 % (1, len(msgs), msgs))
1206 # Test that Android change is not required when no html file is added/removed.
1207 def testIgnoreNonHtmlFiles(self):
1208 mock_input_api = MockInputApi()
1210 mock_input_api.files = [
1211 MockAffectedFile('content/test/data/accessibility/accname/foo.txt',
1213 MockAffectedFile('content/test/data/accessibility/aria/foo.cc',
1215 MockAffectedFile('content/test/data/accessibility/css/foo.h',
1217 MockAffectedFile('content/test/data/accessibility/tree/foo.py',
1221 msgs = PRESUBMIT.CheckAccessibilityTreeTestsAreIncludedForAndroid(
1222 mock_input_api, MockOutputApi())
1223 self.assertEqual(0, len(msgs),
1224 'Expected %d messages, found %d: %s'
1225 % (0, len(msgs), msgs))
1227 # Test that Android change is not required for unrelated html files.
1228 def testIgnoreNonRelatedHtmlFiles(self):
1229 mock_input_api = MockInputApi()
1231 mock_input_api.files = [
1232 MockAffectedFile('content/test/data/accessibility/event/foo.html',
1236 msgs = PRESUBMIT.CheckAccessibilityTreeTestsAreIncludedForAndroid(
1237 mock_input_api, MockOutputApi())
1238 self.assertEqual(0, len(msgs),
1239 'Expected %d messages, found %d: %s'
1240 % (0, len(msgs), msgs))
1242 # Test that only modifying an html file will not trigger the warning.
1243 def testIgnoreModifiedFiles(self):
1244 mock_input_api = MockInputApi()
1246 mock_input_api.files = [
1247 MockAffectedFile('content/test/data/accessibility/aria/foo.html',
1251 msgs = PRESUBMIT.CheckAccessibilityTreeTestsAreIncludedForAndroid(
1252 mock_input_api, MockOutputApi())
1253 self.assertEqual(0, len(msgs),
1254 'Expected %d messages, found %d: %s'
1255 % (0, len(msgs), msgs))
1257 # Test that deleting an html file will trigger the warning.
1258 def testAndroidChangeMissingOnDeletedFile(self):
1259 mock_input_api = MockInputApi()
1261 mock_input_api.files = [
1262 MockAffectedFile('content/test/data/accessibility/accname/foo.html',
1266 msgs = PRESUBMIT.CheckAccessibilityTreeTestsAreIncludedForAndroid(
1267 mock_input_api, MockOutputApi())
1268 self.assertEqual(1, len(msgs),
1269 'Expected %d messages, found %d: %s'
1270 % (1, len(msgs), msgs))
1272 class AndroidDeprecatedTestAnnotationTest(unittest.TestCase):
1273 def testCheckAndroidTestAnnotationUsage(self):
1274 mock_input_api = MockInputApi()
1275 mock_output_api = MockOutputApi()
1277 mock_input_api.files = [
1278 MockAffectedFile('LalaLand.java', [
1281 MockAffectedFile('CorrectUsage.java', [
1282 'import androidx.test.filters.LargeTest;',
1283 'import androidx.test.filters.MediumTest;',
1284 'import androidx.test.filters.SmallTest;',
1286 MockAffectedFile('UsedDeprecatedLargeTestAnnotation.java', [
1287 'import android.test.suitebuilder.annotation.LargeTest;',
1289 MockAffectedFile('UsedDeprecatedMediumTestAnnotation.java', [
1290 'import android.test.suitebuilder.annotation.MediumTest;',
1292 MockAffectedFile('UsedDeprecatedSmallTestAnnotation.java', [
1293 'import android.test.suitebuilder.annotation.SmallTest;',
1295 MockAffectedFile('UsedDeprecatedSmokeAnnotation.java', [
1296 'import android.test.suitebuilder.annotation.Smoke;',
1299 msgs = PRESUBMIT._CheckAndroidTestAnnotationUsage(
1300 mock_input_api, mock_output_api)
1301 self.assertEqual(1, len(msgs),
1302 'Expected %d items, found %d: %s'
1303 % (1, len(msgs), msgs))
1304 self.assertEqual(4, len(msgs[0].items),
1305 'Expected %d items, found %d: %s'
1306 % (4, len(msgs[0].items), msgs[0].items))
1307 self.assertTrue('UsedDeprecatedLargeTestAnnotation.java:1' in msgs[0].items,
1308 'UsedDeprecatedLargeTestAnnotation not found in errors')
1309 self.assertTrue('UsedDeprecatedMediumTestAnnotation.java:1'
1311 'UsedDeprecatedMediumTestAnnotation not found in errors')
1312 self.assertTrue('UsedDeprecatedSmallTestAnnotation.java:1' in msgs[0].items,
1313 'UsedDeprecatedSmallTestAnnotation not found in errors')
1314 self.assertTrue('UsedDeprecatedSmokeAnnotation.java:1' in msgs[0].items,
1315 'UsedDeprecatedSmokeAnnotation not found in errors')
1317 class AndroidBannedImportTest(unittest.TestCase):
1318 def testCheckAndroidNoBannedImports(self):
1319 mock_input_api = MockInputApi()
1320 mock_output_api = MockOutputApi()
1323 MockAffectedFile('RandomStufff.java', [
1326 MockAffectedFile('NoBannedImports.java', [
1327 'import androidx.test.filters.LargeTest;',
1328 'import androidx.test.filters.MediumTest;',
1329 'import androidx.test.filters.SmallTest;',
1331 MockAffectedFile('BannedUri.java', [
1332 'import java.net.URI;',
1334 MockAffectedFile('BannedTargetApi.java', [
1335 'import android.annotation.TargetApi;',
1337 MockAffectedFile('BannedUiThreadTestRule.java', [
1338 'import androidx.test.rule.UiThreadTestRule;',
1340 MockAffectedFile('BannedUiThreadTest.java', [
1341 'import androidx.test.annotation.UiThreadTest;',
1343 MockAffectedFile('BannedActivityTestRule.java', [
1344 'import androidx.test.rule.ActivityTestRule;',
1346 MockAffectedFile('BannedVectorDrawableCompat.java', [
1347 'import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat;',
1351 for file in test_files:
1352 mock_input_api.files = [file]
1353 msgs.append(PRESUBMIT._CheckAndroidNoBannedImports(
1354 mock_input_api, mock_output_api))
1355 self.assertEqual(0, len(msgs[0]))
1356 self.assertEqual(0, len(msgs[1]))
1357 self.assertTrue(msgs[2][0].message.startswith(textwrap.dedent("""\
1358 Banned imports were used.
1359 BannedUri.java:1:"""
1361 self.assertTrue(msgs[3][0].message.startswith(textwrap.dedent("""\
1362 Banned imports were used.
1363 BannedTargetApi.java:1:"""
1365 self.assertTrue(msgs[4][0].message.startswith(textwrap.dedent("""\
1366 Banned imports were used.
1367 BannedUiThreadTestRule.java:1:"""
1369 self.assertTrue(msgs[5][0].message.startswith(textwrap.dedent("""\
1370 Banned imports were used.
1371 BannedUiThreadTest.java:1:"""
1373 self.assertTrue(msgs[6][0].message.startswith(textwrap.dedent("""\
1374 Banned imports were used.
1375 BannedActivityTestRule.java:1:"""
1377 self.assertTrue(msgs[7][0].message.startswith(textwrap.dedent("""\
1378 Banned imports were used.
1379 BannedVectorDrawableCompat.java:1:"""
1382 class CheckNoDownstreamDepsTest(unittest.TestCase):
1383 def testInvalidDepFromUpstream(self):
1384 mock_input_api = MockInputApi()
1385 mock_output_api = MockOutputApi()
1387 mock_input_api.files = [
1388 MockAffectedFile('BUILD.gn', [
1390 ' "//clank/target:test",',
1393 MockAffectedFile('chrome/android/BUILD.gn', [
1394 'deps = [ "//clank/target:test" ]'
1396 MockAffectedFile('chrome/chrome_java_deps.gni', [
1398 ' "//clank/target:test",',
1402 mock_input_api.change.RepositoryRoot = lambda: 'chromium/src'
1403 msgs = PRESUBMIT.CheckNoUpstreamDepsOnClank(
1404 mock_input_api, mock_output_api)
1405 self.assertEqual(1, len(msgs),
1406 'Expected %d items, found %d: %s'
1407 % (1, len(msgs), msgs))
1408 self.assertEqual(3, len(msgs[0].items),
1409 'Expected %d items, found %d: %s'
1410 % (3, len(msgs[0].items), msgs[0].items))
1411 self.assertTrue(any('BUILD.gn:2' in item for item in msgs[0].items),
1412 'BUILD.gn not found in errors')
1414 any('chrome/android/BUILD.gn:1' in item for item in msgs[0].items),
1415 'chrome/android/BUILD.gn:1 not found in errors')
1417 any('chrome/chrome_java_deps.gni:2' in item for item in msgs[0].items),
1418 'chrome/chrome_java_deps.gni:2 not found in errors')
1420 def testAllowsComments(self):
1421 mock_input_api = MockInputApi()
1422 mock_output_api = MockOutputApi()
1424 mock_input_api.files = [
1425 MockAffectedFile('BUILD.gn', [
1426 '# real implementation in //clank/target:test',
1429 mock_input_api.change.RepositoryRoot = lambda: 'chromium/src'
1430 msgs = PRESUBMIT.CheckNoUpstreamDepsOnClank(
1431 mock_input_api, mock_output_api)
1432 self.assertEqual(0, len(msgs),
1433 'Expected %d items, found %d: %s'
1434 % (0, len(msgs), msgs))
1436 def testOnlyChecksBuildFiles(self):
1437 mock_input_api = MockInputApi()
1438 mock_output_api = MockOutputApi()
1440 mock_input_api.files = [
1441 MockAffectedFile('README.md', [
1442 'DEPS = [ "//clank/target:test" ]'
1444 MockAffectedFile('chrome/android/java/file.java', [
1445 '//clank/ only function'
1448 mock_input_api.change.RepositoryRoot = lambda: 'chromium/src'
1449 msgs = PRESUBMIT.CheckNoUpstreamDepsOnClank(
1450 mock_input_api, mock_output_api)
1451 self.assertEqual(0, len(msgs),
1452 'Expected %d items, found %d: %s'
1453 % (0, len(msgs), msgs))
1455 def testValidDepFromDownstream(self):
1456 mock_input_api = MockInputApi()
1457 mock_output_api = MockOutputApi()
1459 mock_input_api.files = [
1460 MockAffectedFile('BUILD.gn', [
1462 ' "//clank/target:test",',
1465 MockAffectedFile('java/BUILD.gn', [
1466 'DEPS = [ "//clank/target:test" ]'
1469 mock_input_api.change.RepositoryRoot = lambda: 'chromium/src/clank'
1470 msgs = PRESUBMIT.CheckNoUpstreamDepsOnClank(
1471 mock_input_api, mock_output_api)
1472 self.assertEqual(0, len(msgs),
1473 'Expected %d items, found %d: %s'
1474 % (0, len(msgs), msgs))
1476 class AndroidDeprecatedJUnitFrameworkTest(unittest.TestCase):
1477 def testCheckAndroidTestJUnitFramework(self):
1478 mock_input_api = MockInputApi()
1479 mock_output_api = MockOutputApi()
1481 mock_input_api.files = [
1482 MockAffectedFile('LalaLand.java', [
1485 MockAffectedFile('CorrectUsage.java', [
1486 'import org.junit.ABC',
1487 'import org.junit.XYZ;',
1489 MockAffectedFile('UsedDeprecatedJUnit.java', [
1490 'import junit.framework.*;',
1492 MockAffectedFile('UsedDeprecatedJUnitAssert.java', [
1493 'import junit.framework.Assert;',
1496 msgs = PRESUBMIT._CheckAndroidTestJUnitFrameworkImport(
1497 mock_input_api, mock_output_api)
1498 self.assertEqual(1, len(msgs),
1499 'Expected %d items, found %d: %s'
1500 % (1, len(msgs), msgs))
1501 self.assertEqual(2, len(msgs[0].items),
1502 'Expected %d items, found %d: %s'
1503 % (2, len(msgs[0].items), msgs[0].items))
1504 self.assertTrue('UsedDeprecatedJUnit.java:1' in msgs[0].items,
1505 'UsedDeprecatedJUnit.java not found in errors')
1506 self.assertTrue('UsedDeprecatedJUnitAssert.java:1'
1508 'UsedDeprecatedJUnitAssert not found in errors')
1511 class AndroidJUnitBaseClassTest(unittest.TestCase):
1512 def testCheckAndroidTestJUnitBaseClass(self):
1513 mock_input_api = MockInputApi()
1514 mock_output_api = MockOutputApi()
1516 mock_input_api.files = [
1517 MockAffectedFile('LalaLand.java', [
1520 MockAffectedFile('CorrectTest.java', [
1521 '@RunWith(ABC.class);'
1522 'public class CorrectTest {',
1525 MockAffectedFile('HistoricallyIncorrectTest.java', [
1526 'public class Test extends BaseCaseA {',
1529 'public class Test extends BaseCaseB {',
1532 MockAffectedFile('CorrectTestWithInterface.java', [
1533 '@RunWith(ABC.class);'
1534 'public class CorrectTest implement Interface {',
1537 MockAffectedFile('IncorrectTest.java', [
1538 'public class IncorrectTest extends TestCase {',
1541 MockAffectedFile('IncorrectWithInterfaceTest.java', [
1542 'public class Test implements X extends BaseClass {',
1545 MockAffectedFile('IncorrectMultiLineTest.java', [
1546 'public class Test implements X, Y, Z',
1547 ' extends TestBase {',
1551 msgs = PRESUBMIT._CheckAndroidTestJUnitInheritance(
1552 mock_input_api, mock_output_api)
1553 self.assertEqual(1, len(msgs),
1554 'Expected %d items, found %d: %s'
1555 % (1, len(msgs), msgs))
1556 self.assertEqual(3, len(msgs[0].items),
1557 'Expected %d items, found %d: %s'
1558 % (3, len(msgs[0].items), msgs[0].items))
1559 self.assertTrue('IncorrectTest.java:1' in msgs[0].items,
1560 'IncorrectTest not found in errors')
1561 self.assertTrue('IncorrectWithInterfaceTest.java:1'
1563 'IncorrectWithInterfaceTest not found in errors')
1564 self.assertTrue('IncorrectMultiLineTest.java:2' in msgs[0].items,
1565 'IncorrectMultiLineTest not found in errors')
1567 class AndroidDebuggableBuildTest(unittest.TestCase):
1569 def testCheckAndroidDebuggableBuild(self):
1570 mock_input_api = MockInputApi()
1571 mock_output_api = MockOutputApi()
1573 mock_input_api.files = [
1574 MockAffectedFile('RandomStuff.java', [
1577 MockAffectedFile('CorrectUsage.java', [
1578 'import org.chromium.base.BuildInfo;',
1579 'some random stuff',
1580 'boolean isOsDebuggable = BuildInfo.isDebugAndroid();',
1582 MockAffectedFile('JustCheckUserdebugBuild.java', [
1583 'import android.os.Build;',
1584 'some random stuff',
1585 'boolean isOsDebuggable = Build.TYPE.equals("userdebug")',
1587 MockAffectedFile('JustCheckEngineeringBuild.java', [
1588 'import android.os.Build;',
1589 'some random stuff',
1590 'boolean isOsDebuggable = "eng".equals(Build.TYPE)',
1592 MockAffectedFile('UsedBuildType.java', [
1593 'import android.os.Build;',
1594 'some random stuff',
1595 'boolean isOsDebuggable = Build.TYPE.equals("userdebug")'
1596 '|| "eng".equals(Build.TYPE)',
1598 MockAffectedFile('UsedExplicitBuildType.java', [
1599 'some random stuff',
1600 'boolean isOsDebuggable = android.os.Build.TYPE.equals("userdebug")'
1601 '|| "eng".equals(android.os.Build.TYPE)',
1605 msgs = PRESUBMIT._CheckAndroidDebuggableBuild(
1606 mock_input_api, mock_output_api)
1607 self.assertEqual(1, len(msgs),
1608 'Expected %d items, found %d: %s'
1609 % (1, len(msgs), msgs))
1610 self.assertEqual(4, len(msgs[0].items),
1611 'Expected %d items, found %d: %s'
1612 % (4, len(msgs[0].items), msgs[0].items))
1613 self.assertTrue('JustCheckUserdebugBuild.java:3' in msgs[0].items)
1614 self.assertTrue('JustCheckEngineeringBuild.java:3' in msgs[0].items)
1615 self.assertTrue('UsedBuildType.java:3' in msgs[0].items)
1616 self.assertTrue('UsedExplicitBuildType.java:2' in msgs[0].items)
1619 class LogUsageTest(unittest.TestCase):
1621 def testCheckAndroidCrLogUsage(self):
1622 mock_input_api = MockInputApi()
1623 mock_output_api = MockOutputApi()
1625 mock_input_api.files = [
1626 MockAffectedFile('RandomStuff.java', [
1629 MockAffectedFile('HasAndroidLog.java', [
1630 'import android.util.Log;',
1631 'some random stuff',
1632 'Log.d("TAG", "foo");',
1634 MockAffectedFile('HasExplicitUtilLog.java', [
1635 'some random stuff',
1636 'android.util.Log.d("TAG", "foo");',
1638 MockAffectedFile('IsInBasePackage.java', [
1639 'package org.chromium.base;',
1640 'private static final String TAG = "cr_Foo";',
1641 'Log.d(TAG, "foo");',
1643 MockAffectedFile('IsInBasePackageButImportsLog.java', [
1644 'package org.chromium.base;',
1645 'import android.util.Log;',
1646 'private static final String TAG = "cr_Foo";',
1647 'Log.d(TAG, "foo");',
1649 MockAffectedFile('HasBothLog.java', [
1650 'import org.chromium.base.Log;',
1651 'some random stuff',
1652 'private static final String TAG = "cr_Foo";',
1653 'Log.d(TAG, "foo");',
1654 'android.util.Log.d("TAG", "foo");',
1656 MockAffectedFile('HasCorrectTag.java', [
1657 'import org.chromium.base.Log;',
1658 'some random stuff',
1659 'private static final String TAG = "cr_Foo";',
1660 'Log.d(TAG, "foo");',
1662 MockAffectedFile('HasOldTag.java', [
1663 'import org.chromium.base.Log;',
1664 'some random stuff',
1665 'private static final String TAG = "cr.Foo";',
1666 'Log.d(TAG, "foo");',
1668 MockAffectedFile('HasDottedTag.java', [
1669 'import org.chromium.base.Log;',
1670 'some random stuff',
1671 'private static final String TAG = "cr_foo.bar";',
1672 'Log.d(TAG, "foo");',
1674 MockAffectedFile('HasDottedTagPublic.java', [
1675 'import org.chromium.base.Log;',
1676 'some random stuff',
1677 'public static final String TAG = "cr_foo.bar";',
1678 'Log.d(TAG, "foo");',
1680 MockAffectedFile('HasNoTagDecl.java', [
1681 'import org.chromium.base.Log;',
1682 'some random stuff',
1683 'Log.d(TAG, "foo");',
1685 MockAffectedFile('HasIncorrectTagDecl.java', [
1686 'import org.chromium.base.Log;',
1687 'private static final String TAHG = "cr_Foo";',
1688 'some random stuff',
1689 'Log.d(TAG, "foo");',
1691 MockAffectedFile('HasInlineTag.java', [
1692 'import org.chromium.base.Log;',
1693 'some random stuff',
1694 'private static final String TAG = "cr_Foo";',
1695 'Log.d("TAG", "foo");',
1697 MockAffectedFile('HasInlineTagWithSpace.java', [
1698 'import org.chromium.base.Log;',
1699 'some random stuff',
1700 'private static final String TAG = "cr_Foo";',
1701 'Log.d("log message", "foo");',
1703 MockAffectedFile('HasUnprefixedTag.java', [
1704 'import org.chromium.base.Log;',
1705 'some random stuff',
1706 'private static final String TAG = "rubbish";',
1707 'Log.d(TAG, "foo");',
1709 MockAffectedFile('HasTooLongTag.java', [
1710 'import org.chromium.base.Log;',
1711 'some random stuff',
1712 'private static final String TAG = "21_characters_long___";',
1713 'Log.d(TAG, "foo");',
1715 MockAffectedFile('HasTooLongTagWithNoLogCallsInDiff.java', [
1716 'import org.chromium.base.Log;',
1717 'some random stuff',
1718 'private static final String TAG = "21_characters_long___";',
1722 msgs = PRESUBMIT._CheckAndroidCrLogUsage(
1723 mock_input_api, mock_output_api)
1725 self.assertEqual(5, len(msgs),
1726 'Expected %d items, found %d: %s' % (5, len(msgs), msgs))
1728 # Declaration format
1729 nb = len(msgs[0].items)
1730 self.assertEqual(2, nb,
1731 'Expected %d items, found %d: %s' % (2, nb, msgs[0].items))
1732 self.assertTrue('HasNoTagDecl.java' in msgs[0].items)
1733 self.assertTrue('HasIncorrectTagDecl.java' in msgs[0].items)
1736 nb = len(msgs[1].items)
1737 self.assertEqual(2, nb,
1738 'Expected %d items, found %d: %s' % (2, nb, msgs[1].items))
1739 self.assertTrue('HasTooLongTag.java' in msgs[1].items)
1740 self.assertTrue('HasTooLongTagWithNoLogCallsInDiff.java' in msgs[1].items)
1742 # Tag must be a variable named TAG
1743 nb = len(msgs[2].items)
1744 self.assertEqual(3, nb,
1745 'Expected %d items, found %d: %s' % (3, nb, msgs[2].items))
1746 self.assertTrue('HasBothLog.java:5' in msgs[2].items)
1747 self.assertTrue('HasInlineTag.java:4' in msgs[2].items)
1748 self.assertTrue('HasInlineTagWithSpace.java:4' in msgs[2].items)
1751 nb = len(msgs[3].items)
1752 self.assertEqual(3, nb,
1753 'Expected %d items, found %d: %s' % (3, nb, msgs[3].items))
1754 self.assertTrue('HasAndroidLog.java:3' in msgs[3].items)
1755 self.assertTrue('HasExplicitUtilLog.java:2' in msgs[3].items)
1756 self.assertTrue('IsInBasePackageButImportsLog.java:4' in msgs[3].items)
1758 # Tag must not contain
1759 nb = len(msgs[4].items)
1760 self.assertEqual(3, nb,
1761 'Expected %d items, found %d: %s' % (2, nb, msgs[4].items))
1762 self.assertTrue('HasDottedTag.java' in msgs[4].items)
1763 self.assertTrue('HasDottedTagPublic.java' in msgs[4].items)
1764 self.assertTrue('HasOldTag.java' in msgs[4].items)
1767 class GoogleAnswerUrlFormatTest(unittest.TestCase):
1769 def testCatchAnswerUrlId(self):
1770 input_api = MockInputApi()
1772 MockFile('somewhere/file.cc',
1774 ' "https://support.google.com/chrome/answer/123456";']),
1775 MockFile('somewhere_else/file.cc',
1777 ' "https://support.google.com/chrome/a/answer/123456";']),
1780 warnings = PRESUBMIT.CheckGoogleSupportAnswerUrlOnUpload(
1781 input_api, MockOutputApi())
1782 self.assertEqual(1, len(warnings))
1783 self.assertEqual(2, len(warnings[0].items))
1785 def testAllowAnswerUrlParam(self):
1786 input_api = MockInputApi()
1788 MockFile('somewhere/file.cc',
1790 ' "https://support.google.com/chrome/?p=cpn_crash_reports";']),
1793 warnings = PRESUBMIT.CheckGoogleSupportAnswerUrlOnUpload(
1794 input_api, MockOutputApi())
1795 self.assertEqual(0, len(warnings))
1798 class HardcodedGoogleHostsTest(unittest.TestCase):
1800 def testWarnOnAssignedLiterals(self):
1801 input_api = MockInputApi()
1803 MockFile('content/file.cc',
1804 ['char* host = "https://www.google.com";']),
1805 MockFile('content/file.cc',
1806 ['char* host = "https://www.googleapis.com";']),
1807 MockFile('content/file.cc',
1808 ['char* host = "https://clients1.google.com";']),
1811 warnings = PRESUBMIT.CheckHardcodedGoogleHostsInLowerLayers(
1812 input_api, MockOutputApi())
1813 self.assertEqual(1, len(warnings))
1814 self.assertEqual(3, len(warnings[0].items))
1816 def testAllowInComment(self):
1817 input_api = MockInputApi()
1819 MockFile('content/file.cc',
1820 ['char* host = "https://www.aol.com"; // google.com'])
1823 warnings = PRESUBMIT.CheckHardcodedGoogleHostsInLowerLayers(
1824 input_api, MockOutputApi())
1825 self.assertEqual(0, len(warnings))
1828 class ChromeOsSyncedPrefRegistrationTest(unittest.TestCase):
1830 def testWarnsOnChromeOsDirectories(self):
1832 MockFile('ash/file.cc',
1833 ['PrefRegistrySyncable::SYNCABLE_PREF']),
1834 MockFile('chrome/browser/chromeos/file.cc',
1835 ['PrefRegistrySyncable::SYNCABLE_PREF']),
1836 MockFile('chromeos/file.cc',
1837 ['PrefRegistrySyncable::SYNCABLE_PREF']),
1838 MockFile('components/arc/file.cc',
1839 ['PrefRegistrySyncable::SYNCABLE_PREF']),
1840 MockFile('components/exo/file.cc',
1841 ['PrefRegistrySyncable::SYNCABLE_PREF']),
1843 input_api = MockInputApi()
1845 input_api.files = [file]
1846 warnings = PRESUBMIT.CheckChromeOsSyncedPrefRegistration(
1847 input_api, MockOutputApi())
1848 self.assertEqual(1, len(warnings))
1850 def testDoesNotWarnOnSyncOsPref(self):
1851 input_api = MockInputApi()
1853 MockFile('chromeos/file.cc',
1854 ['PrefRegistrySyncable::SYNCABLE_OS_PREF']),
1856 warnings = PRESUBMIT.CheckChromeOsSyncedPrefRegistration(
1857 input_api, MockOutputApi())
1858 self.assertEqual(0, len(warnings))
1860 def testDoesNotWarnOnOtherDirectories(self):
1861 input_api = MockInputApi()
1863 MockFile('chrome/browser/ui/file.cc',
1864 ['PrefRegistrySyncable::SYNCABLE_PREF']),
1865 MockFile('components/sync/file.cc',
1866 ['PrefRegistrySyncable::SYNCABLE_PREF']),
1867 MockFile('content/browser/file.cc',
1868 ['PrefRegistrySyncable::SYNCABLE_PREF']),
1869 MockFile('a/notchromeos/file.cc',
1870 ['PrefRegistrySyncable::SYNCABLE_PREF']),
1872 warnings = PRESUBMIT.CheckChromeOsSyncedPrefRegistration(
1873 input_api, MockOutputApi())
1874 self.assertEqual(0, len(warnings))
1876 def testSeparateWarningForPriorityPrefs(self):
1877 input_api = MockInputApi()
1879 MockFile('chromeos/file.cc',
1880 ['PrefRegistrySyncable::SYNCABLE_PREF',
1881 'PrefRegistrySyncable::SYNCABLE_PRIORITY_PREF']),
1883 warnings = PRESUBMIT.CheckChromeOsSyncedPrefRegistration(
1884 input_api, MockOutputApi())
1885 self.assertEqual(2, len(warnings))
1888 class ForwardDeclarationTest(unittest.TestCase):
1889 def testCheckHeadersOnlyOutsideThirdParty(self):
1890 mock_input_api = MockInputApi()
1891 mock_input_api.files = [
1892 MockAffectedFile('somewhere/file.cc', [
1895 MockAffectedFile('third_party/header.h', [
1899 warnings = PRESUBMIT.CheckUselessForwardDeclarations(mock_input_api,
1901 self.assertEqual(0, len(warnings))
1903 def testNoNestedDeclaration(self):
1904 mock_input_api = MockInputApi()
1905 mock_input_api.files = [
1906 MockAffectedFile('somewhere/header.h', [
1907 'class SomeClass {',
1909 ' class NotAMatch;',
1913 warnings = PRESUBMIT.CheckUselessForwardDeclarations(mock_input_api,
1915 self.assertEqual(0, len(warnings))
1917 def testSubStrings(self):
1918 mock_input_api = MockInputApi()
1919 mock_input_api.files = [
1920 MockAffectedFile('somewhere/header.h', [
1921 'class NotUsefulClass;',
1922 'struct SomeStruct;',
1924 'SomeStructPtr *p2;'
1927 warnings = PRESUBMIT.CheckUselessForwardDeclarations(mock_input_api,
1929 self.assertEqual(2, len(warnings))
1931 def testUselessForwardDeclaration(self):
1932 mock_input_api = MockInputApi()
1933 mock_input_api.files = [
1934 MockAffectedFile('somewhere/header.h', [
1935 'class DummyClass;',
1936 'struct DummyStruct;',
1937 'class UsefulClass;',
1938 'std::unique_ptr<UsefulClass> p;'
1941 warnings = PRESUBMIT.CheckUselessForwardDeclarations(mock_input_api,
1943 self.assertEqual(2, len(warnings))
1945 def testBlinkHeaders(self):
1946 mock_input_api = MockInputApi()
1947 mock_input_api.files = [
1948 MockAffectedFile('third_party/blink/header.h', [
1949 'class DummyClass;',
1950 'struct DummyStruct;',
1952 MockAffectedFile('third_party\\blink\\header.h', [
1953 'class DummyClass;',
1954 'struct DummyStruct;',
1957 warnings = PRESUBMIT.CheckUselessForwardDeclarations(mock_input_api,
1959 self.assertEqual(4, len(warnings))
1962 class RelativeIncludesTest(unittest.TestCase):
1963 def testThirdPartyNotWebKitIgnored(self):
1964 mock_input_api = MockInputApi()
1965 mock_input_api.files = [
1966 MockAffectedFile('third_party/test.cpp', '#include "../header.h"'),
1967 MockAffectedFile('third_party/test/test.cpp', '#include "../header.h"'),
1970 mock_output_api = MockOutputApi()
1972 errors = PRESUBMIT.CheckForRelativeIncludes(
1973 mock_input_api, mock_output_api)
1974 self.assertEqual(0, len(errors))
1976 def testNonCppFileIgnored(self):
1977 mock_input_api = MockInputApi()
1978 mock_input_api.files = [
1979 MockAffectedFile('test.py', '#include "../header.h"'),
1982 mock_output_api = MockOutputApi()
1984 errors = PRESUBMIT.CheckForRelativeIncludes(
1985 mock_input_api, mock_output_api)
1986 self.assertEqual(0, len(errors))
1988 def testInnocuousChangesAllowed(self):
1989 mock_input_api = MockInputApi()
1990 mock_input_api.files = [
1991 MockAffectedFile('test.cpp', '#include "header.h"'),
1992 MockAffectedFile('test2.cpp', '../'),
1995 mock_output_api = MockOutputApi()
1997 errors = PRESUBMIT.CheckForRelativeIncludes(
1998 mock_input_api, mock_output_api)
1999 self.assertEqual(0, len(errors))
2001 def testRelativeIncludeNonWebKitProducesError(self):
2002 mock_input_api = MockInputApi()
2003 mock_input_api.files = [
2004 MockAffectedFile('test.cpp', ['#include "../header.h"']),
2007 mock_output_api = MockOutputApi()
2009 errors = PRESUBMIT.CheckForRelativeIncludes(
2010 mock_input_api, mock_output_api)
2011 self.assertEqual(1, len(errors))
2013 def testRelativeIncludeWebKitProducesError(self):
2014 mock_input_api = MockInputApi()
2015 mock_input_api.files = [
2016 MockAffectedFile('third_party/blink/test.cpp',
2017 ['#include "../header.h']),
2020 mock_output_api = MockOutputApi()
2022 errors = PRESUBMIT.CheckForRelativeIncludes(
2023 mock_input_api, mock_output_api)
2024 self.assertEqual(1, len(errors))
2027 class CCIncludeTest(unittest.TestCase):
2028 def testThirdPartyNotBlinkIgnored(self):
2029 mock_input_api = MockInputApi()
2030 mock_input_api.files = [
2031 MockAffectedFile('third_party/test.cpp', '#include "file.cc"'),
2034 mock_output_api = MockOutputApi()
2036 errors = PRESUBMIT.CheckForCcIncludes(
2037 mock_input_api, mock_output_api)
2038 self.assertEqual(0, len(errors))
2040 def testPythonFileIgnored(self):
2041 mock_input_api = MockInputApi()
2042 mock_input_api.files = [
2043 MockAffectedFile('test.py', '#include "file.cc"'),
2046 mock_output_api = MockOutputApi()
2048 errors = PRESUBMIT.CheckForCcIncludes(
2049 mock_input_api, mock_output_api)
2050 self.assertEqual(0, len(errors))
2052 def testIncFilesAccepted(self):
2053 mock_input_api = MockInputApi()
2054 mock_input_api.files = [
2055 MockAffectedFile('test.py', '#include "file.inc"'),
2058 mock_output_api = MockOutputApi()
2060 errors = PRESUBMIT.CheckForCcIncludes(
2061 mock_input_api, mock_output_api)
2062 self.assertEqual(0, len(errors))
2064 def testInnocuousChangesAllowed(self):
2065 mock_input_api = MockInputApi()
2066 mock_input_api.files = [
2067 MockAffectedFile('test.cpp', '#include "header.h"'),
2068 MockAffectedFile('test2.cpp', 'Something "file.cc"'),
2071 mock_output_api = MockOutputApi()
2073 errors = PRESUBMIT.CheckForCcIncludes(
2074 mock_input_api, mock_output_api)
2075 self.assertEqual(0, len(errors))
2077 def testCcIncludeNonBlinkProducesError(self):
2078 mock_input_api = MockInputApi()
2079 mock_input_api.files = [
2080 MockAffectedFile('test.cpp', ['#include "file.cc"']),
2083 mock_output_api = MockOutputApi()
2085 errors = PRESUBMIT.CheckForCcIncludes(
2086 mock_input_api, mock_output_api)
2087 self.assertEqual(1, len(errors))
2089 def testCppIncludeBlinkProducesError(self):
2090 mock_input_api = MockInputApi()
2091 mock_input_api.files = [
2092 MockAffectedFile('third_party/blink/test.cpp',
2093 ['#include "foo/file.cpp"']),
2096 mock_output_api = MockOutputApi()
2098 errors = PRESUBMIT.CheckForCcIncludes(
2099 mock_input_api, mock_output_api)
2100 self.assertEqual(1, len(errors))
2103 class GnGlobForwardTest(unittest.TestCase):
2104 def testAddBareGlobs(self):
2105 mock_input_api = MockInputApi()
2106 mock_input_api.files = [
2107 MockAffectedFile('base/stuff.gni', [
2108 'forward_variables_from(invoker, "*")']),
2109 MockAffectedFile('base/BUILD.gn', [
2110 'forward_variables_from(invoker, "*")']),
2112 warnings = PRESUBMIT.CheckGnGlobForward(mock_input_api, MockOutputApi())
2113 self.assertEqual(1, len(warnings))
2114 msg = '\n'.join(warnings[0].items)
2115 self.assertIn('base/stuff.gni', msg)
2116 # Should not check .gn files. Local templates don't need to care about
2117 # visibility / testonly.
2118 self.assertNotIn('base/BUILD.gn', msg)
2120 def testValidUses(self):
2121 mock_input_api = MockInputApi()
2122 mock_input_api.files = [
2123 MockAffectedFile('base/stuff.gni', [
2124 'forward_variables_from(invoker, "*", [])']),
2125 MockAffectedFile('base/stuff2.gni', [
2126 'forward_variables_from(invoker, "*", TESTONLY_AND_VISIBILITY)']),
2127 MockAffectedFile('base/stuff3.gni', [
2128 'forward_variables_from(invoker, [ "testonly" ])']),
2130 warnings = PRESUBMIT.CheckGnGlobForward(mock_input_api, MockOutputApi())
2131 self.assertEqual([], warnings)
2134 class GnRebasePathTest(unittest.TestCase):
2135 def testAddAbsolutePath(self):
2136 mock_input_api = MockInputApi()
2137 mock_input_api.files = [
2138 MockAffectedFile('base/BUILD.gn', ['rebase_path("$target_gen_dir", "//")']),
2139 MockAffectedFile('base/root/BUILD.gn', ['rebase_path("$target_gen_dir", "/")']),
2140 MockAffectedFile('base/variable/BUILD.gn', ['rebase_path(target_gen_dir, "/")']),
2142 warnings = PRESUBMIT.CheckGnRebasePath(mock_input_api, MockOutputApi())
2143 self.assertEqual(1, len(warnings))
2144 msg = '\n'.join(warnings[0].items)
2145 self.assertIn('base/BUILD.gn', msg)
2146 self.assertIn('base/root/BUILD.gn', msg)
2147 self.assertIn('base/variable/BUILD.gn', msg)
2148 self.assertEqual(3, len(warnings[0].items))
2150 def testValidUses(self):
2151 mock_input_api = MockInputApi()
2152 mock_input_api.files = [
2153 MockAffectedFile('base/foo/BUILD.gn', ['rebase_path("$target_gen_dir", root_build_dir)']),
2154 MockAffectedFile('base/bar/BUILD.gn', ['rebase_path("$target_gen_dir", root_build_dir, "/")']),
2155 MockAffectedFile('base/baz/BUILD.gn', ['rebase_path(target_gen_dir, root_build_dir)']),
2156 MockAffectedFile('base/baz/BUILD.gn', ['rebase_path(target_gen_dir, "//some/arbitrary/path")']),
2157 MockAffectedFile('base/okay_slash/BUILD.gn', ['rebase_path(".", "//")']),
2159 warnings = PRESUBMIT.CheckGnRebasePath(mock_input_api, MockOutputApi())
2160 self.assertEqual([], warnings)
2163 class NewHeaderWithoutGnChangeTest(unittest.TestCase):
2164 def testAddHeaderWithoutGn(self):
2165 mock_input_api = MockInputApi()
2166 mock_input_api.files = [
2167 MockAffectedFile('base/stuff.h', ''),
2169 warnings = PRESUBMIT.CheckNewHeaderWithoutGnChangeOnUpload(
2170 mock_input_api, MockOutputApi())
2171 self.assertEqual(1, len(warnings))
2172 self.assertTrue('base/stuff.h' in warnings[0].items)
2174 def testModifyHeader(self):
2175 mock_input_api = MockInputApi()
2176 mock_input_api.files = [
2177 MockAffectedFile('base/stuff.h', '', action='M'),
2179 warnings = PRESUBMIT.CheckNewHeaderWithoutGnChangeOnUpload(
2180 mock_input_api, MockOutputApi())
2181 self.assertEqual(0, len(warnings))
2183 def testDeleteHeader(self):
2184 mock_input_api = MockInputApi()
2185 mock_input_api.files = [
2186 MockAffectedFile('base/stuff.h', '', action='D'),
2188 warnings = PRESUBMIT.CheckNewHeaderWithoutGnChangeOnUpload(
2189 mock_input_api, MockOutputApi())
2190 self.assertEqual(0, len(warnings))
2192 def testAddHeaderWithGn(self):
2193 mock_input_api = MockInputApi()
2194 mock_input_api.files = [
2195 MockAffectedFile('base/stuff.h', ''),
2196 MockAffectedFile('base/BUILD.gn', 'stuff.h'),
2198 warnings = PRESUBMIT.CheckNewHeaderWithoutGnChangeOnUpload(
2199 mock_input_api, MockOutputApi())
2200 self.assertEqual(0, len(warnings))
2202 def testAddHeaderWithGni(self):
2203 mock_input_api = MockInputApi()
2204 mock_input_api.files = [
2205 MockAffectedFile('base/stuff.h', ''),
2206 MockAffectedFile('base/files.gni', 'stuff.h'),
2208 warnings = PRESUBMIT.CheckNewHeaderWithoutGnChangeOnUpload(
2209 mock_input_api, MockOutputApi())
2210 self.assertEqual(0, len(warnings))
2212 def testAddHeaderWithOther(self):
2213 mock_input_api = MockInputApi()
2214 mock_input_api.files = [
2215 MockAffectedFile('base/stuff.h', ''),
2216 MockAffectedFile('base/stuff.cc', 'stuff.h'),
2218 warnings = PRESUBMIT.CheckNewHeaderWithoutGnChangeOnUpload(
2219 mock_input_api, MockOutputApi())
2220 self.assertEqual(1, len(warnings))
2222 def testAddHeaderWithWrongGn(self):
2223 mock_input_api = MockInputApi()
2224 mock_input_api.files = [
2225 MockAffectedFile('base/stuff.h', ''),
2226 MockAffectedFile('base/BUILD.gn', 'stuff_h'),
2228 warnings = PRESUBMIT.CheckNewHeaderWithoutGnChangeOnUpload(
2229 mock_input_api, MockOutputApi())
2230 self.assertEqual(1, len(warnings))
2232 def testAddHeadersWithGn(self):
2233 mock_input_api = MockInputApi()
2234 mock_input_api.files = [
2235 MockAffectedFile('base/stuff.h', ''),
2236 MockAffectedFile('base/another.h', ''),
2237 MockAffectedFile('base/BUILD.gn', 'another.h\nstuff.h'),
2239 warnings = PRESUBMIT.CheckNewHeaderWithoutGnChangeOnUpload(
2240 mock_input_api, MockOutputApi())
2241 self.assertEqual(0, len(warnings))
2243 def testAddHeadersWithWrongGn(self):
2244 mock_input_api = MockInputApi()
2245 mock_input_api.files = [
2246 MockAffectedFile('base/stuff.h', ''),
2247 MockAffectedFile('base/another.h', ''),
2248 MockAffectedFile('base/BUILD.gn', 'another_h\nstuff.h'),
2250 warnings = PRESUBMIT.CheckNewHeaderWithoutGnChangeOnUpload(
2251 mock_input_api, MockOutputApi())
2252 self.assertEqual(1, len(warnings))
2253 self.assertFalse('base/stuff.h' in warnings[0].items)
2254 self.assertTrue('base/another.h' in warnings[0].items)
2256 def testAddHeadersWithWrongGn2(self):
2257 mock_input_api = MockInputApi()
2258 mock_input_api.files = [
2259 MockAffectedFile('base/stuff.h', ''),
2260 MockAffectedFile('base/another.h', ''),
2261 MockAffectedFile('base/BUILD.gn', 'another_h\nstuff_h'),
2263 warnings = PRESUBMIT.CheckNewHeaderWithoutGnChangeOnUpload(
2264 mock_input_api, MockOutputApi())
2265 self.assertEqual(1, len(warnings))
2266 self.assertTrue('base/stuff.h' in warnings[0].items)
2267 self.assertTrue('base/another.h' in warnings[0].items)
2270 class CorrectProductNameInMessagesTest(unittest.TestCase):
2271 def testProductNameInDesc(self):
2272 mock_input_api = MockInputApi()
2273 mock_input_api.files = [
2274 MockAffectedFile('chrome/app/google_chrome_strings.grd', [
2275 '<message name="Foo" desc="Welcome to Chrome">',
2276 ' Welcome to Chrome!',
2279 MockAffectedFile('chrome/app/chromium_strings.grd', [
2280 '<message name="Bar" desc="Welcome to Chrome">',
2281 ' Welcome to Chromium!',
2285 warnings = PRESUBMIT.CheckCorrectProductNameInMessages(
2286 mock_input_api, MockOutputApi())
2287 self.assertEqual(0, len(warnings))
2289 def testChromeInChromium(self):
2290 mock_input_api = MockInputApi()
2291 mock_input_api.files = [
2292 MockAffectedFile('chrome/app/google_chrome_strings.grd', [
2293 '<message name="Foo" desc="Welcome to Chrome">',
2294 ' Welcome to Chrome!',
2297 MockAffectedFile('chrome/app/chromium_strings.grd', [
2298 '<message name="Bar" desc="Welcome to Chrome">',
2299 ' Welcome to Chrome!',
2303 warnings = PRESUBMIT.CheckCorrectProductNameInMessages(
2304 mock_input_api, MockOutputApi())
2305 self.assertEqual(1, len(warnings))
2306 self.assertTrue('chrome/app/chromium_strings.grd' in warnings[0].items[0])
2308 def testChromiumInChrome(self):
2309 mock_input_api = MockInputApi()
2310 mock_input_api.files = [
2311 MockAffectedFile('chrome/app/google_chrome_strings.grd', [
2312 '<message name="Foo" desc="Welcome to Chrome">',
2313 ' Welcome to Chromium!',
2316 MockAffectedFile('chrome/app/chromium_strings.grd', [
2317 '<message name="Bar" desc="Welcome to Chrome">',
2318 ' Welcome to Chromium!',
2322 warnings = PRESUBMIT.CheckCorrectProductNameInMessages(
2323 mock_input_api, MockOutputApi())
2324 self.assertEqual(1, len(warnings))
2326 'chrome/app/google_chrome_strings.grd:2' in warnings[0].items[0])
2328 def testMultipleInstances(self):
2329 mock_input_api = MockInputApi()
2330 mock_input_api.files = [
2331 MockAffectedFile('chrome/app/chromium_strings.grd', [
2332 '<message name="Bar" desc="Welcome to Chrome">',
2333 ' Welcome to Chrome!',
2335 '<message name="Baz" desc="A correct message">',
2336 ' Chromium is the software you are using.',
2338 '<message name="Bat" desc="An incorrect message">',
2339 ' Google Chrome is the software you are using.',
2343 warnings = PRESUBMIT.CheckCorrectProductNameInMessages(
2344 mock_input_api, MockOutputApi())
2345 self.assertEqual(1, len(warnings))
2347 'chrome/app/chromium_strings.grd:2' in warnings[0].items[0])
2349 'chrome/app/chromium_strings.grd:8' in warnings[0].items[1])
2351 def testMultipleWarnings(self):
2352 mock_input_api = MockInputApi()
2353 mock_input_api.files = [
2354 MockAffectedFile('chrome/app/chromium_strings.grd', [
2355 '<message name="Bar" desc="Welcome to Chrome">',
2356 ' Welcome to Chrome!',
2358 '<message name="Baz" desc="A correct message">',
2359 ' Chromium is the software you are using.',
2361 '<message name="Bat" desc="An incorrect message">',
2362 ' Google Chrome is the software you are using.',
2365 MockAffectedFile('components/components_google_chrome_strings.grd', [
2366 '<message name="Bar" desc="Welcome to Chrome">',
2367 ' Welcome to Chrome!',
2369 '<message name="Baz" desc="A correct message">',
2370 ' Chromium is the software you are using.',
2372 '<message name="Bat" desc="An incorrect message">',
2373 ' Google Chrome is the software you are using.',
2377 warnings = PRESUBMIT.CheckCorrectProductNameInMessages(
2378 mock_input_api, MockOutputApi())
2379 self.assertEqual(2, len(warnings))
2381 'components/components_google_chrome_strings.grd:5'
2382 in warnings[0].items[0])
2384 'chrome/app/chromium_strings.grd:2' in warnings[1].items[0])
2386 'chrome/app/chromium_strings.grd:8' in warnings[1].items[1])
2389 class _SecurityOwnersTestCase(unittest.TestCase):
2390 def _createMockInputApi(self):
2391 mock_input_api = MockInputApi()
2392 def FakeRepositoryRoot():
2393 return mock_input_api.os_path.join('chromium', 'src')
2394 mock_input_api.change.RepositoryRoot = FakeRepositoryRoot
2395 self._injectFakeOwnersClient(
2397 ['apple@chromium.org', 'orange@chromium.org'])
2398 return mock_input_api
2400 def _setupFakeChange(self, input_api):
2401 class FakeGerrit(object):
2402 def IsOwnersOverrideApproved(self, issue):
2405 input_api.change.issue = 123
2406 input_api.gerrit = FakeGerrit()
2408 def _injectFakeOwnersClient(self, input_api, owners):
2409 class FakeOwnersClient(object):
2410 def ListOwners(self, f):
2413 input_api.owners_client = FakeOwnersClient()
2415 def _injectFakeChangeOwnerAndReviewers(self, input_api, owner, reviewers):
2416 def MockOwnerAndReviewers(input_api, email_regexp, approval_needed=False):
2417 return [owner, reviewers]
2418 input_api.canned_checks.GetCodereviewOwnerAndReviewers = \
2419 MockOwnerAndReviewers
2422 class IpcSecurityOwnerTest(_SecurityOwnersTestCase):
2424 ('*_messages.cc', 'scary_messages.cc'),
2425 ('*_messages*.h', 'scary_messages.h'),
2426 ('*_messages*.h', 'scary_messages_android.h'),
2427 ('*_param_traits*.*', 'scary_param_traits.h'),
2428 ('*_param_traits*.*', 'scary_param_traits_win.h'),
2429 ('*.mojom', 'scary.mojom'),
2430 ('*_mojom_traits*.*', 'scary_mojom_traits.h'),
2431 ('*_mojom_traits*.*', 'scary_mojom_traits_mac.h'),
2432 ('*_type_converter*.*', 'scary_type_converter.h'),
2433 ('*_type_converter*.*', 'scary_type_converter_nacl.h'),
2434 ('*.aidl', 'scary.aidl'),
2437 def testHasCorrectPerFileRulesAndSecurityReviewer(self):
2438 mock_input_api = self._createMockInputApi()
2439 new_owners_file_path = mock_input_api.os_path.join(
2440 'services', 'goat', 'public', 'OWNERS')
2442 'per-file *.mojom=set noparent',
2443 'per-file *.mojom=file://ipc/SECURITY_OWNERS'
2445 def FakeReadFile(filename):
2447 mock_input_api.os_path.join('chromium', 'src', new_owners_file_path),
2449 return '\n'.join(new_owners_file)
2450 mock_input_api.ReadFile = FakeReadFile
2451 mock_input_api.files = [
2453 new_owners_file_path, new_owners_file),
2455 mock_input_api.os_path.join(
2456 'services', 'goat', 'public', 'goat.mojom'),
2457 ['// Scary contents.'])]
2458 self._setupFakeChange(mock_input_api)
2459 self._injectFakeChangeOwnerAndReviewers(
2460 mock_input_api, 'owner@chromium.org', ['orange@chromium.org'])
2461 mock_input_api.is_committing = True
2462 mock_input_api.dry_run = False
2463 mock_output_api = MockOutputApi()
2464 results = PRESUBMIT.CheckSecurityOwners(
2465 mock_input_api, mock_output_api)
2466 self.assertEqual(0, len(results))
2468 def testMissingSecurityReviewerAtUpload(self):
2469 mock_input_api = self._createMockInputApi()
2470 new_owners_file_path = mock_input_api.os_path.join(
2471 'services', 'goat', 'public', 'OWNERS')
2473 'per-file *.mojom=set noparent',
2474 'per-file *.mojom=file://ipc/SECURITY_OWNERS'
2476 def FakeReadFile(filename):
2478 mock_input_api.os_path.join('chromium', 'src', new_owners_file_path),
2480 return '\n'.join(new_owners_file)
2481 mock_input_api.ReadFile = FakeReadFile
2482 mock_input_api.files = [
2484 new_owners_file_path, new_owners_file),
2486 mock_input_api.os_path.join(
2487 'services', 'goat', 'public', 'goat.mojom'),
2488 ['// Scary contents.'])]
2489 self._setupFakeChange(mock_input_api)
2490 self._injectFakeChangeOwnerAndReviewers(
2491 mock_input_api, 'owner@chromium.org', ['banana@chromium.org'])
2492 mock_input_api.is_committing = False
2493 mock_input_api.dry_run = False
2494 mock_output_api = MockOutputApi()
2495 results = PRESUBMIT.CheckSecurityOwners(
2496 mock_input_api, mock_output_api)
2497 self.assertEqual(1, len(results))
2498 self.assertEqual('notify', results[0].type)
2500 'Review from an owner in ipc/SECURITY_OWNERS is required for the '
2501 'following newly-added files:', results[0].message)
2503 def testMissingSecurityReviewerAtDryRunCommit(self):
2504 mock_input_api = self._createMockInputApi()
2505 new_owners_file_path = mock_input_api.os_path.join(
2506 'services', 'goat', 'public', 'OWNERS')
2508 'per-file *.mojom=set noparent',
2509 'per-file *.mojom=file://ipc/SECURITY_OWNERS'
2511 def FakeReadFile(filename):
2513 mock_input_api.os_path.join('chromium', 'src', new_owners_file_path),
2515 return '\n'.join(new_owners_file)
2516 mock_input_api.ReadFile = FakeReadFile
2517 mock_input_api.files = [
2519 new_owners_file_path, new_owners_file),
2521 mock_input_api.os_path.join(
2522 'services', 'goat', 'public', 'goat.mojom'),
2523 ['// Scary contents.'])]
2524 self._setupFakeChange(mock_input_api)
2525 self._injectFakeChangeOwnerAndReviewers(
2526 mock_input_api, 'owner@chromium.org', ['banana@chromium.org'])
2527 mock_input_api.is_committing = True
2528 mock_input_api.dry_run = True
2529 mock_output_api = MockOutputApi()
2530 results = PRESUBMIT.CheckSecurityOwners(
2531 mock_input_api, mock_output_api)
2532 self.assertEqual(1, len(results))
2533 self.assertEqual('error', results[0].type)
2535 'Review from an owner in ipc/SECURITY_OWNERS is required for the '
2536 'following newly-added files:', results[0].message)
2538 def testMissingSecurityApprovalAtRealCommit(self):
2539 mock_input_api = self._createMockInputApi()
2540 new_owners_file_path = mock_input_api.os_path.join(
2541 'services', 'goat', 'public', 'OWNERS')
2543 'per-file *.mojom=set noparent',
2544 'per-file *.mojom=file://ipc/SECURITY_OWNERS'
2546 def FakeReadFile(filename):
2548 mock_input_api.os_path.join('chromium', 'src', new_owners_file_path),
2550 return '\n'.join(new_owners_file)
2551 mock_input_api.ReadFile = FakeReadFile
2552 mock_input_api.files = [
2554 new_owners_file_path, new_owners_file),
2556 mock_input_api.os_path.join(
2557 'services', 'goat', 'public', 'goat.mojom'),
2558 ['// Scary contents.'])]
2559 self._setupFakeChange(mock_input_api)
2560 self._injectFakeChangeOwnerAndReviewers(
2561 mock_input_api, 'owner@chromium.org', ['banana@chromium.org'])
2562 mock_input_api.is_committing = True
2563 mock_input_api.dry_run = False
2564 mock_output_api = MockOutputApi()
2565 results = PRESUBMIT.CheckSecurityOwners(
2566 mock_input_api, mock_output_api)
2567 self.assertEqual('error', results[0].type)
2569 'Review from an owner in ipc/SECURITY_OWNERS is required for the '
2570 'following newly-added files:', results[0].message)
2572 def testIpcChangeNeedsSecurityOwner(self):
2573 for is_committing in [True, False]:
2574 for pattern, filename in self._test_cases:
2576 line=f'is_committing={is_committing}, filename={filename}'):
2577 mock_input_api = self._createMockInputApi()
2578 mock_input_api.files = [
2580 mock_input_api.os_path.join(
2581 'services', 'goat', 'public', filename),
2582 ['// Scary contents.'])]
2583 self._setupFakeChange(mock_input_api)
2584 self._injectFakeChangeOwnerAndReviewers(
2585 mock_input_api, 'owner@chromium.org', ['banana@chromium.org'])
2586 mock_input_api.is_committing = is_committing
2587 mock_input_api.dry_run = False
2588 mock_output_api = MockOutputApi()
2589 results = PRESUBMIT.CheckSecurityOwners(
2590 mock_input_api, mock_output_api)
2591 self.assertEqual(1, len(results))
2592 self.assertEqual('error', results[0].type)
2593 self.assertTrue(results[0].message.replace('\\', '/').startswith(
2594 'Found missing OWNERS lines for security-sensitive files. '
2595 'Please add the following lines to services/goat/public/OWNERS:'))
2596 self.assertEqual(['ipc-security-reviews@chromium.org'],
2597 mock_output_api.more_cc)
2600 def testServiceManifestChangeNeedsSecurityOwner(self):
2601 mock_input_api = self._createMockInputApi()
2602 mock_input_api.files = [
2604 mock_input_api.os_path.join(
2605 'services', 'goat', 'public', 'cpp', 'manifest.cc'),
2607 '#include "services/goat/public/cpp/manifest.h"',
2608 'const service_manager::Manifest& GetManifest() {}',
2610 self._setupFakeChange(mock_input_api)
2611 self._injectFakeChangeOwnerAndReviewers(
2612 mock_input_api, 'owner@chromium.org', ['banana@chromium.org'])
2613 mock_output_api = MockOutputApi()
2614 errors = PRESUBMIT.CheckSecurityOwners(
2615 mock_input_api, mock_output_api)
2616 self.assertEqual(1, len(errors))
2617 self.assertTrue(errors[0].message.replace('\\', '/').startswith(
2618 'Found missing OWNERS lines for security-sensitive files. '
2619 'Please add the following lines to services/goat/public/cpp/OWNERS:'))
2620 self.assertEqual(['ipc-security-reviews@chromium.org'], mock_output_api.more_cc)
2622 def testNonServiceManifestSourceChangesDoNotRequireSecurityOwner(self):
2623 mock_input_api = self._createMockInputApi()
2624 self._injectFakeChangeOwnerAndReviewers(
2625 mock_input_api, 'owner@chromium.org', ['banana@chromium.org'])
2626 mock_input_api.files = [
2627 MockAffectedFile('some/non/service/thing/foo_manifest.cc',
2629 'const char kNoEnforcement[] = "not a manifest!";',
2631 mock_output_api = MockOutputApi()
2632 errors = PRESUBMIT.CheckSecurityOwners(
2633 mock_input_api, mock_output_api)
2634 self.assertEqual([], errors)
2635 self.assertEqual([], mock_output_api.more_cc)
2638 class FuchsiaSecurityOwnerTest(_SecurityOwnersTestCase):
2639 def testFidlChangeNeedsSecurityOwner(self):
2640 mock_input_api = self._createMockInputApi()
2641 mock_input_api.files = [
2642 MockAffectedFile('potentially/scary/ipc.fidl',
2646 self._setupFakeChange(mock_input_api)
2647 self._injectFakeChangeOwnerAndReviewers(
2648 mock_input_api, 'owner@chromium.org', ['banana@chromium.org'])
2649 mock_output_api = MockOutputApi()
2650 errors = PRESUBMIT.CheckSecurityOwners(
2651 mock_input_api, mock_output_api)
2652 self.assertEqual(1, len(errors))
2653 self.assertTrue(errors[0].message.replace('\\', '/').startswith(
2654 'Found missing OWNERS lines for security-sensitive files. '
2655 'Please add the following lines to potentially/scary/OWNERS:'))
2657 def testComponentManifestV1ChangeNeedsSecurityOwner(self):
2658 mock_input_api = self._createMockInputApi()
2659 mock_input_api.files = [
2660 MockAffectedFile('potentially/scary/v2_manifest.cmx',
2662 '{ "that is no": "manifest!" }'
2664 self._setupFakeChange(mock_input_api)
2665 self._injectFakeChangeOwnerAndReviewers(
2666 mock_input_api, 'owner@chromium.org', ['banana@chromium.org'])
2667 mock_output_api = MockOutputApi()
2668 errors = PRESUBMIT.CheckSecurityOwners(
2669 mock_input_api, mock_output_api)
2670 self.assertEqual(1, len(errors))
2671 self.assertTrue(errors[0].message.replace('\\', '/').startswith(
2672 'Found missing OWNERS lines for security-sensitive files. '
2673 'Please add the following lines to potentially/scary/OWNERS:'))
2675 def testComponentManifestV2NeedsSecurityOwner(self):
2676 mock_input_api = self._createMockInputApi()
2677 mock_input_api.files = [
2678 MockAffectedFile('potentially/scary/v2_manifest.cml',
2680 '{ "that is no": "manifest!" }'
2682 self._setupFakeChange(mock_input_api)
2683 self._injectFakeChangeOwnerAndReviewers(
2684 mock_input_api, 'owner@chromium.org', ['banana@chromium.org'])
2685 mock_output_api = MockOutputApi()
2686 errors = PRESUBMIT.CheckSecurityOwners(
2687 mock_input_api, mock_output_api)
2688 self.assertEqual(1, len(errors))
2689 self.assertTrue(errors[0].message.replace('\\', '/').startswith(
2690 'Found missing OWNERS lines for security-sensitive files. '
2691 'Please add the following lines to potentially/scary/OWNERS:'))
2693 def testThirdPartyTestsDoNotRequireSecurityOwner(self):
2694 mock_input_api = MockInputApi()
2695 self._injectFakeChangeOwnerAndReviewers(
2696 mock_input_api, 'owner@chromium.org', ['banana@chromium.org'])
2697 mock_input_api.files = [
2698 MockAffectedFile('third_party/crashpad/test/tests.cmx',
2700 'const char kNoEnforcement[] = "Security?!? Pah!";',
2702 mock_output_api = MockOutputApi()
2703 errors = PRESUBMIT.CheckSecurityOwners(
2704 mock_input_api, mock_output_api)
2705 self.assertEqual([], errors)
2707 def testOtherFuchsiaChangesDoNotRequireSecurityOwner(self):
2708 mock_input_api = MockInputApi()
2709 self._injectFakeChangeOwnerAndReviewers(
2710 mock_input_api, 'owner@chromium.org', ['banana@chromium.org'])
2711 mock_input_api.files = [
2712 MockAffectedFile('some/non/service/thing/fuchsia_fidl_cml_cmx_magic.cc',
2714 'const char kNoEnforcement[] = "Security?!? Pah!";',
2716 mock_output_api = MockOutputApi()
2717 errors = PRESUBMIT.CheckSecurityOwners(
2718 mock_input_api, mock_output_api)
2719 self.assertEqual([], errors)
2722 class SecurityChangeTest(_SecurityOwnersTestCase):
2723 def testDiffGetServiceSandboxType(self):
2724 mock_input_api = MockInputApi()
2725 mock_input_api.files = [
2727 'services/goat/teleporter_host.cc',
2730 'inline content::SandboxType',
2731 'content::GetServiceSandboxType<chrome::mojom::GoatTeleporter>() {',
2732 '#if defined(OS_WIN)',
2733 ' return SandboxType::kGoaty;',
2735 ' return SandboxType::kNoSandbox;',
2736 '#endif // !defined(OS_WIN)',
2741 files_to_functions = PRESUBMIT._GetFilesUsingSecurityCriticalFunctions(
2744 'services/goat/teleporter_host.cc': set([
2745 'content::GetServiceSandboxType<>()'
2749 def testDiffRemovingLine(self):
2750 mock_input_api = MockInputApi()
2751 mock_file = MockAffectedFile('services/goat/teleporter_host.cc', '')
2752 mock_file._scm_diff = """--- old 2020-05-04 14:08:25.000000000 -0400
2753 +++ new 2020-05-04 14:08:32.000000000 -0400
2756 inline content::SandboxType
2757 -content::GetServiceSandboxType<chrome::mojom::GoatTeleporter>() {
2759 return SandboxType::kGoaty;
2761 mock_input_api.files = [mock_file]
2762 files_to_functions = PRESUBMIT._GetFilesUsingSecurityCriticalFunctions(
2765 'services/goat/teleporter_host.cc': set([
2766 'content::GetServiceSandboxType<>()'
2770 def testChangeOwnersMissing(self):
2771 mock_input_api = self._createMockInputApi()
2772 self._setupFakeChange(mock_input_api)
2773 self._injectFakeChangeOwnerAndReviewers(
2774 mock_input_api, 'owner@chromium.org', ['banana@chromium.org'])
2775 mock_input_api.is_committing = False
2776 mock_input_api.files = [
2777 MockAffectedFile('file.cc', ['GetServiceSandboxType<Goat>(Sandbox)'])
2779 mock_output_api = MockOutputApi()
2780 result = PRESUBMIT.CheckSecurityChanges(mock_input_api, mock_output_api)
2781 self.assertEqual(1, len(result))
2782 self.assertEqual(result[0].type, 'notify')
2783 self.assertEqual(result[0].message,
2784 'The following files change calls to security-sensitive functions\n' \
2785 'that need to be reviewed by ipc/SECURITY_OWNERS.\n'
2787 ' content::GetServiceSandboxType<>()\n\n')
2789 def testChangeOwnersMissingAtCommit(self):
2790 mock_input_api = self._createMockInputApi()
2791 self._setupFakeChange(mock_input_api)
2792 self._injectFakeChangeOwnerAndReviewers(
2793 mock_input_api, 'owner@chromium.org', ['banana@chromium.org'])
2794 mock_input_api.is_committing = True
2795 mock_input_api.dry_run = False
2796 mock_input_api.files = [
2797 MockAffectedFile('file.cc', ['GetServiceSandboxType<mojom::Goat>()'])
2799 mock_output_api = MockOutputApi()
2800 result = PRESUBMIT.CheckSecurityChanges(mock_input_api, mock_output_api)
2801 self.assertEqual(1, len(result))
2802 self.assertEqual(result[0].type, 'error')
2803 self.assertEqual(result[0].message,
2804 'The following files change calls to security-sensitive functions\n' \
2805 'that need to be reviewed by ipc/SECURITY_OWNERS.\n'
2807 ' content::GetServiceSandboxType<>()\n\n')
2809 def testChangeOwnersPresent(self):
2810 mock_input_api = self._createMockInputApi()
2811 self._injectFakeChangeOwnerAndReviewers(
2812 mock_input_api, 'owner@chromium.org',
2813 ['apple@chromium.org', 'banana@chromium.org'])
2814 mock_input_api.files = [
2815 MockAffectedFile('file.cc', ['WithSandboxType(Sandbox)'])
2817 mock_output_api = MockOutputApi()
2818 result = PRESUBMIT.CheckSecurityChanges(mock_input_api, mock_output_api)
2819 self.assertEqual(0, len(result))
2821 def testChangeOwnerIsSecurityOwner(self):
2822 mock_input_api = self._createMockInputApi()
2823 self._setupFakeChange(mock_input_api)
2824 self._injectFakeChangeOwnerAndReviewers(
2825 mock_input_api, 'orange@chromium.org', ['pear@chromium.org'])
2826 mock_input_api.files = [
2827 MockAffectedFile('file.cc', ['GetServiceSandboxType<T>(Sandbox)'])
2829 mock_output_api = MockOutputApi()
2830 result = PRESUBMIT.CheckSecurityChanges(mock_input_api, mock_output_api)
2831 self.assertEqual(1, len(result))
2834 class BannedTypeCheckTest(unittest.TestCase):
2835 def testBannedJsFunctions(self):
2836 input_api = MockInputApi()
2838 MockFile('ash/webui/file.js',
2839 ['chrome.send(something);']),
2840 MockFile('some/js/ok/file.js',
2841 ['chrome.send(something);']),
2844 results = PRESUBMIT.CheckNoBannedFunctions(input_api, MockOutputApi())
2846 self.assertEqual(1, len(results))
2847 self.assertTrue('ash/webui/file.js' in results[0].message)
2848 self.assertFalse('some/js/ok/file.js' in results[0].message)
2850 def testBannedJavaFunctions(self):
2851 input_api = MockInputApi()
2853 MockFile('some/java/problematic/diskread.java',
2854 ['StrictMode.allowThreadDiskReads();']),
2855 MockFile('some/java/problematic/diskwrite.java',
2856 ['StrictMode.allowThreadDiskWrites();']),
2857 MockFile('some/java/ok/diskwrite.java',
2858 ['StrictModeContext.allowDiskWrites();']),
2859 MockFile('some/java/problematic/waitidleforsync.java',
2860 ['instrumentation.waitForIdleSync();']),
2861 MockFile('some/java/problematic/registerreceiver.java',
2862 ['context.registerReceiver();']),
2863 MockFile('some/java/problematic/property.java',
2864 ['new Property<abc, Integer>;']),
2865 MockFile('some/java/problematic/requestlayout.java',
2866 ['requestLayout();']),
2867 MockFile('some/java/problematic/lastprofile.java',
2868 ['Profile.getLastUsedRegularProfile();']),
2869 MockFile('some/java/problematic/getdrawable1.java',
2870 ['ResourcesCompat.getDrawable();']),
2871 MockFile('some/java/problematic/getdrawable2.java',
2872 ['getResources().getDrawable();']),
2875 errors = PRESUBMIT.CheckNoBannedFunctions(input_api, MockOutputApi())
2876 self.assertEqual(2, len(errors))
2877 self.assertTrue('some/java/problematic/diskread.java' in errors[0].message)
2878 self.assertTrue('some/java/problematic/diskwrite.java' in errors[0].message)
2879 self.assertFalse('some/java/ok/diskwrite.java' in errors[0].message)
2880 self.assertFalse('some/java/ok/diskwrite.java' in errors[1].message)
2881 self.assertTrue('some/java/problematic/waitidleforsync.java' in errors[0].message)
2882 self.assertTrue('some/java/problematic/registerreceiver.java' in errors[1].message)
2883 self.assertTrue('some/java/problematic/property.java' in errors[0].message)
2884 self.assertTrue('some/java/problematic/requestlayout.java' in errors[0].message)
2885 self.assertTrue('some/java/problematic/lastprofile.java' in errors[0].message)
2886 self.assertTrue('some/java/problematic/getdrawable1.java' in errors[0].message)
2887 self.assertTrue('some/java/problematic/getdrawable2.java' in errors[0].message)
2889 def testBannedCppFunctions(self):
2890 input_api = MockInputApi()
2892 MockFile('some/cpp/problematic/file.cc',
2893 ['using namespace std;']),
2894 MockFile('third_party/blink/problematic/file.cc',
2895 ['GetInterfaceProvider()']),
2896 MockFile('some/cpp/ok/file.cc',
2897 ['using std::string;']),
2898 MockFile('some/cpp/problematic/file2.cc',
2899 ['set_owned_by_client()']),
2900 MockFile('some/cpp/nocheck/file.cc',
2901 ['using namespace std; // nocheck']),
2902 MockFile('some/cpp/comment/file.cc',
2903 [' // A comment about `using namespace std;`']),
2906 results = PRESUBMIT.CheckNoBannedFunctions(input_api, MockOutputApi())
2908 # warnings are results[0], errors are results[1]
2909 self.assertEqual(2, len(results))
2910 self.assertTrue('some/cpp/problematic/file.cc' in results[1].message)
2912 'third_party/blink/problematic/file.cc' in results[0].message)
2913 self.assertTrue('some/cpp/ok/file.cc' not in results[1].message)
2914 self.assertTrue('some/cpp/problematic/file2.cc' in results[0].message)
2915 self.assertFalse('some/cpp/nocheck/file.cc' in results[0].message)
2916 self.assertFalse('some/cpp/nocheck/file.cc' in results[1].message)
2917 self.assertFalse('some/cpp/comment/file.cc' in results[0].message)
2918 self.assertFalse('some/cpp/comment/file.cc' in results[1].message)
2920 def testBannedCppRandomFunctions(self):
2923 'absl::InsecureBitGen',
2924 'std::linear_congruential_engine',
2925 'std::mersenne_twister_engine',
2926 'std::subtract_with_carry_engine',
2927 'std::discard_block_engine',
2928 'std::independent_bits_engine',
2929 'std::shuffle_order_engine',
2930 'std::minstd_rand0',
2934 'std::ranlux24_base',
2935 'std::ranlux48_base',
2939 'std::default_random_engine',
2940 'std::random_device',
2942 for banned_rng in banned_rngs:
2943 input_api = MockInputApi()
2945 MockFile('some/cpp/problematic/file.cc',
2946 [f'{banned_rng} engine;']),
2947 MockFile('third_party/blink/problematic/file.cc',
2948 [f'{banned_rng} engine;']),
2949 MockFile('third_party/ok/file.cc',
2950 [f'{banned_rng} engine;']),
2952 results = PRESUBMIT.CheckNoBannedFunctions(input_api, MockOutputApi())
2953 self.assertEqual(1, len(results), banned_rng)
2954 self.assertTrue('some/cpp/problematic/file.cc' in results[0].message,
2957 'third_party/blink/problematic/file.cc' in results[0].message,
2960 'third_party/ok/file.cc' in results[0].message, banned_rng)
2962 def testBannedIosObjcFunctions(self):
2963 input_api = MockInputApi()
2965 MockFile('some/ios/file.mm',
2966 ['TEST(SomeClassTest, SomeInteraction) {',
2968 MockFile('some/mac/file.mm',
2969 ['TEST(SomeClassTest, SomeInteraction) {',
2971 MockFile('another/ios_file.mm',
2972 ['class SomeTest : public testing::Test {};']),
2973 MockFile('some/ios/file_egtest.mm',
2974 ['- (void)testSomething { EXPECT_OCMOCK_VERIFY(aMock); }']),
2975 MockFile('some/ios/file_unittest.mm',
2976 ['TEST_F(SomeTest, TestThis) { EXPECT_OCMOCK_VERIFY(aMock); }']),
2979 errors = PRESUBMIT.CheckNoBannedFunctions(input_api, MockOutputApi())
2980 self.assertEqual(1, len(errors))
2981 self.assertTrue('some/ios/file.mm' in errors[0].message)
2982 self.assertTrue('another/ios_file.mm' in errors[0].message)
2983 self.assertTrue('some/mac/file.mm' not in errors[0].message)
2984 self.assertTrue('some/ios/file_egtest.mm' in errors[0].message)
2985 self.assertTrue('some/ios/file_unittest.mm' not in errors[0].message)
2987 def testBannedMojoFunctions(self):
2988 input_api = MockInputApi()
2990 MockFile('some/cpp/problematic/file2.cc',
2991 ['mojo::ConvertTo<>']),
2992 MockFile('third_party/blink/ok/file3.cc',
2993 ['mojo::ConvertTo<>']),
2994 MockFile('content/renderer/ok/file3.cc',
2995 ['mojo::ConvertTo<>']),
2998 results = PRESUBMIT.CheckNoBannedFunctions(input_api, MockOutputApi())
3000 # warnings are results[0], errors are results[1]
3001 self.assertEqual(1, len(results))
3002 self.assertTrue('some/cpp/problematic/file2.cc' in results[0].message)
3003 self.assertTrue('third_party/blink/ok/file3.cc' not in results[0].message)
3004 self.assertTrue('content/renderer/ok/file3.cc' not in results[0].message)
3006 def testBannedMojomPatterns(self):
3007 input_api = MockInputApi()
3009 MockFile('bad.mojom',
3011 ' handle<shared_buffer> buffer;',
3013 MockFile('good.mojom',
3015 ' mojo_base.mojom.ReadOnlySharedMemoryRegion region1;',
3016 ' mojo_base.mojom.WritableSharedMemoryRegion region2;',
3017 ' mojo_base.mojom.UnsafeSharedMemoryRegion region3;',
3021 results = PRESUBMIT.CheckNoBannedFunctions(input_api, MockOutputApi())
3023 # warnings are results[0], errors are results[1]
3024 self.assertEqual(1, len(results))
3025 self.assertTrue('bad.mojom' in results[0].message)
3026 self.assertTrue('good.mojom' not in results[0].message)
3028 class NoProductionCodeUsingTestOnlyFunctionsTest(unittest.TestCase):
3029 def testTruePositives(self):
3030 mock_input_api = MockInputApi()
3031 mock_input_api.files = [
3032 MockFile('some/path/foo.cc', ['foo_for_testing();']),
3033 MockFile('some/path/foo.mm', ['FooForTesting();']),
3034 MockFile('some/path/foo.cxx', ['FooForTests();']),
3035 MockFile('some/path/foo.cpp', ['foo_for_test();']),
3038 results = PRESUBMIT.CheckNoProductionCodeUsingTestOnlyFunctions(
3039 mock_input_api, MockOutputApi())
3040 self.assertEqual(1, len(results))
3041 self.assertEqual(4, len(results[0].items))
3042 self.assertTrue('foo.cc' in results[0].items[0])
3043 self.assertTrue('foo.mm' in results[0].items[1])
3044 self.assertTrue('foo.cxx' in results[0].items[2])
3045 self.assertTrue('foo.cpp' in results[0].items[3])
3047 def testFalsePositives(self):
3048 mock_input_api = MockInputApi()
3049 mock_input_api.files = [
3050 MockFile('some/path/foo.h', ['foo_for_testing();']),
3051 MockFile('some/path/foo.mm', ['FooForTesting() {']),
3052 MockFile('some/path/foo.cc', ['::FooForTests();']),
3053 MockFile('some/path/foo.cpp', ['// foo_for_test();']),
3056 results = PRESUBMIT.CheckNoProductionCodeUsingTestOnlyFunctions(
3057 mock_input_api, MockOutputApi())
3058 self.assertEqual(0, len(results))
3060 def testAllowedFiles(self):
3061 mock_input_api = MockInputApi()
3062 mock_input_api.files = [
3063 MockFile('path/foo_unittest.cc', ['foo_for_testing();']),
3064 MockFile('path/bar_unittest_mac.cc', ['foo_for_testing();']),
3065 MockFile('path/baz_unittests.cc', ['foo_for_testing();']),
3068 results = PRESUBMIT.CheckNoProductionCodeUsingTestOnlyFunctions(
3069 mock_input_api, MockOutputApi())
3070 self.assertEqual(0, len(results))
3073 class NoProductionJavaCodeUsingTestOnlyFunctionsTest(unittest.TestCase):
3074 def testTruePositives(self):
3075 mock_input_api = MockInputApi()
3076 mock_input_api.files = [
3077 MockFile('dir/java/src/foo.java', ['FooForTesting();']),
3078 MockFile('dir/java/src/bar.java', ['FooForTests(x);']),
3079 MockFile('dir/java/src/baz.java', ['FooForTest(', 'y', ');']),
3080 MockFile('dir/java/src/mult.java', [
3081 'int x = SomethingLongHere()',
3082 ' * SomethingLongHereForTesting();'
3086 results = PRESUBMIT.CheckNoProductionCodeUsingTestOnlyFunctionsJava(
3087 mock_input_api, MockOutputApi())
3088 self.assertEqual(1, len(results))
3089 self.assertEqual(4, len(results[0].items))
3090 self.assertTrue('foo.java' in results[0].items[0])
3091 self.assertTrue('bar.java' in results[0].items[1])
3092 self.assertTrue('baz.java' in results[0].items[2])
3093 self.assertTrue('mult.java' in results[0].items[3])
3095 def testFalsePositives(self):
3096 mock_input_api = MockInputApi()
3097 mock_input_api.files = [
3098 MockFile('dir/java/src/foo.xml', ['FooForTesting();']),
3099 MockFile('dir/java/src/foo.java', ['FooForTests() {']),
3100 MockFile('dir/java/src/bar.java', ['// FooForTest();']),
3101 MockFile('dir/java/src/bar2.java', ['x = 1; // FooForTest();']),
3102 MockFile('dir/java/src/bar3.java', ['@VisibleForTesting']),
3103 MockFile('dir/java/src/bar4.java', ['@VisibleForTesting()']),
3104 MockFile('dir/java/src/bar5.java', [
3105 '@VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)'
3107 MockFile('dir/javatests/src/baz.java', ['FooForTest(', 'y', ');']),
3108 MockFile('dir/junit/src/baz.java', ['FooForTest(', 'y', ');']),
3109 MockFile('dir/junit/src/javadoc.java', [
3110 '/** Use FooForTest(); to obtain foo in tests.'
3113 MockFile('dir/junit/src/javadoc2.java', [
3115 ' * Use FooForTest(); to obtain foo in tests.'
3120 results = PRESUBMIT.CheckNoProductionCodeUsingTestOnlyFunctionsJava(
3121 mock_input_api, MockOutputApi())
3122 self.assertEqual(0, len(results))
3125 class NewImagesWarningTest(unittest.TestCase):
3126 def testTruePositives(self):
3127 mock_input_api = MockInputApi()
3128 mock_input_api.files = [
3129 MockFile('dir/android/res/drawable/foo.png', []),
3130 MockFile('dir/android/res/drawable-v21/bar.svg', []),
3131 MockFile('dir/android/res/mipmap-v21-en/baz.webp', []),
3132 MockFile('dir/android/res_gshoe/drawable-mdpi/foobar.png', []),
3135 results = PRESUBMIT._CheckNewImagesWarning(mock_input_api, MockOutputApi())
3136 self.assertEqual(1, len(results))
3137 self.assertEqual(4, len(results[0].items))
3138 self.assertTrue('foo.png' in results[0].items[0].LocalPath())
3139 self.assertTrue('bar.svg' in results[0].items[1].LocalPath())
3140 self.assertTrue('baz.webp' in results[0].items[2].LocalPath())
3141 self.assertTrue('foobar.png' in results[0].items[3].LocalPath())
3143 def testFalsePositives(self):
3144 mock_input_api = MockInputApi()
3145 mock_input_api.files = [
3146 MockFile('dir/pngs/README.md', []),
3147 MockFile('java/test/res/drawable/foo.png', []),
3148 MockFile('third_party/blink/foo.png', []),
3149 MockFile('dir/third_party/libpng/src/foo.cc', ['foobar']),
3150 MockFile('dir/resources.webp/.gitignore', ['foo.png']),
3153 results = PRESUBMIT._CheckNewImagesWarning(mock_input_api, MockOutputApi())
3154 self.assertEqual(0, len(results))
3156 class ProductIconsTest(unittest.TestCase):
3158 mock_input_api = MockInputApi()
3159 mock_input_api.files = [
3160 MockFile('components/vector_icons/google_jetpack.icon', []),
3161 MockFile('components/vector_icons/generic_jetpack.icon', []),
3164 results = PRESUBMIT.CheckNoProductIconsAddedToPublicRepo(mock_input_api, MockOutputApi())
3165 self.assertEqual(1, len(results))
3166 self.assertEqual(1, len(results[0].items))
3167 self.assertTrue('google_jetpack.icon' in results[0].items[0])
3169 class CheckUniquePtrTest(unittest.TestCase):
3170 def testTruePositivesNullptr(self):
3171 mock_input_api = MockInputApi()
3172 mock_input_api.files = [
3173 MockFile('dir/baz.cc', ['std::unique_ptr<T>()']),
3174 MockFile('dir/baz-p.cc', ['std::unique_ptr<T<P>>()']),
3177 results = PRESUBMIT.CheckUniquePtrOnUpload(mock_input_api, MockOutputApi())
3178 self.assertEqual(1, len(results))
3179 self.assertTrue('nullptr' in results[0].message)
3180 self.assertEqual(2, len(results[0].items))
3181 self.assertTrue('baz.cc' in results[0].items[0])
3182 self.assertTrue('baz-p.cc' in results[0].items[1])
3184 def testTruePositivesConstructor(self):
3185 mock_input_api = MockInputApi()
3186 mock_input_api.files = [
3187 MockFile('dir/foo.cc', ['return std::unique_ptr<T>(foo);']),
3188 MockFile('dir/bar.mm', ['bar = std::unique_ptr<T>(foo)']),
3189 MockFile('dir/mult.cc', [
3191 ' std::unique_ptr<T>(barVeryVeryLongFooSoThatItWouldNotFitAbove);'
3193 MockFile('dir/mult2.cc', [
3194 'barVeryVeryLongLongBaaaaaarSoThatTheLineLimitIsAlmostReached =',
3195 ' std::unique_ptr<T>(foo);'
3197 MockFile('dir/mult3.cc', [
3198 'bar = std::unique_ptr<T>(',
3199 ' fooVeryVeryVeryLongStillGoingWellThisWillTakeAWhileFinallyThere);'
3201 MockFile('dir/multi_arg.cc', [
3202 'auto p = std::unique_ptr<std::pair<T, D>>(new std::pair(T, D));']),
3205 results = PRESUBMIT.CheckUniquePtrOnUpload(mock_input_api, MockOutputApi())
3206 self.assertEqual(1, len(results))
3207 self.assertTrue('std::make_unique' in results[0].message)
3208 self.assertEqual(6, len(results[0].items))
3209 self.assertTrue('foo.cc' in results[0].items[0])
3210 self.assertTrue('bar.mm' in results[0].items[1])
3211 self.assertTrue('mult.cc' in results[0].items[2])
3212 self.assertTrue('mult2.cc' in results[0].items[3])
3213 self.assertTrue('mult3.cc' in results[0].items[4])
3214 self.assertTrue('multi_arg.cc' in results[0].items[5])
3216 def testFalsePositives(self):
3217 mock_input_api = MockInputApi()
3218 mock_input_api.files = [
3219 MockFile('dir/foo.cc', ['return std::unique_ptr<T[]>(foo);']),
3220 MockFile('dir/bar.mm', ['bar = std::unique_ptr<T[]>(foo)']),
3221 MockFile('dir/file.cc', ['std::unique_ptr<T> p = Foo();']),
3222 MockFile('dir/baz.cc', [
3223 'std::unique_ptr<T> result = std::make_unique<T>();'
3225 MockFile('dir/baz2.cc', [
3226 'std::unique_ptr<T> result = std::make_unique<T>('
3228 MockFile('dir/nested.cc', ['set<std::unique_ptr<T>>();']),
3229 MockFile('dir/nested2.cc', ['map<U, std::unique_ptr<T>>();']),
3231 # Two-argument invocation of std::unique_ptr is exempt because there is
3232 # no equivalent using std::make_unique.
3233 MockFile('dir/multi_arg.cc', [
3234 'auto p = std::unique_ptr<T, D>(new T(), D());']),
3237 results = PRESUBMIT.CheckUniquePtrOnUpload(mock_input_api, MockOutputApi())
3238 self.assertEqual(0, len(results))
3240 class CheckNoDirectIncludesHeadersWhichRedefineStrCat(unittest.TestCase):
3241 def testBlocksDirectIncludes(self):
3242 mock_input_api = MockInputApi()
3243 mock_input_api.files = [
3244 MockFile('dir/foo_win.cc', ['#include "shlwapi.h"']),
3245 MockFile('dir/bar.h', ['#include <propvarutil.h>']),
3246 MockFile('dir/baz.h', ['#include <atlbase.h>']),
3247 MockFile('dir/jumbo.h', ['#include "sphelper.h"']),
3249 results = PRESUBMIT.CheckNoStrCatRedefines(mock_input_api, MockOutputApi())
3250 self.assertEqual(1, len(results))
3251 self.assertEqual(4, len(results[0].items))
3252 self.assertTrue('StrCat' in results[0].message)
3253 self.assertTrue('foo_win.cc' in results[0].items[0])
3254 self.assertTrue('bar.h' in results[0].items[1])
3255 self.assertTrue('baz.h' in results[0].items[2])
3256 self.assertTrue('jumbo.h' in results[0].items[3])
3258 def testAllowsToIncludeWrapper(self):
3259 mock_input_api = MockInputApi()
3260 mock_input_api.files = [
3261 MockFile('dir/baz_win.cc', ['#include "base/win/shlwapi.h"']),
3262 MockFile('dir/baz-win.h', ['#include "base/win/atl.h"']),
3264 results = PRESUBMIT.CheckNoStrCatRedefines(mock_input_api, MockOutputApi())
3265 self.assertEqual(0, len(results))
3267 def testAllowsToCreateWrapper(self):
3268 mock_input_api = MockInputApi()
3269 mock_input_api.files = [
3270 MockFile('base/win/shlwapi.h', [
3271 '#include <shlwapi.h>',
3272 '#include "base/win/windows_defines.inc"']),
3274 results = PRESUBMIT.CheckNoStrCatRedefines(mock_input_api, MockOutputApi())
3275 self.assertEqual(0, len(results))
3277 def testIgnoresNonImplAndHeaders(self):
3278 mock_input_api = MockInputApi()
3279 mock_input_api.files = [
3280 MockFile('dir/foo_win.txt', ['#include "shlwapi.h"']),
3281 MockFile('dir/bar.asm', ['#include <propvarutil.h>']),
3283 results = PRESUBMIT.CheckNoStrCatRedefines(mock_input_api, MockOutputApi())
3284 self.assertEqual(0, len(results))
3287 class StringTest(unittest.TestCase):
3288 """Tests ICU syntax check and translation screenshots check."""
3290 # An empty grd file.
3291 OLD_GRD_CONTENTS = """<?xml version="1.0" encoding="UTF-8"?>
3292 <grit latest_public_release="1" current_release="1">
3294 <messages></messages>
3298 # A grd file with a single message.
3299 NEW_GRD_CONTENTS1 = """<?xml version="1.0" encoding="UTF-8"?>
3300 <grit latest_public_release="1" current_release="1">
3303 <message name="IDS_TEST1">
3306 <message name="IDS_TEST_STRING_NON_TRANSLATEABLE1"
3307 translateable="false">
3308 Non translateable message 1, should be ignored
3310 <message name="IDS_TEST_STRING_ACCESSIBILITY"
3311 is_accessibility_with_no_ui="true">
3312 Accessibility label 1, should be ignored
3318 # A grd file with two messages.
3319 NEW_GRD_CONTENTS2 = """<?xml version="1.0" encoding="UTF-8"?>
3320 <grit latest_public_release="1" current_release="1">
3323 <message name="IDS_TEST1">
3326 <message name="IDS_TEST2">
3329 <message name="IDS_TEST_STRING_NON_TRANSLATEABLE2"
3330 translateable="false">
3331 Non translateable message 2, should be ignored
3337 # A grd file with one ICU syntax message without syntax errors.
3338 NEW_GRD_CONTENTS_ICU_SYNTAX_OK1 = """<?xml version="1.0" encoding="UTF-8"?>
3339 <grit latest_public_release="1" current_release="1">
3342 <message name="IDS_TEST1">
3344 =1 {Test text for numeric one}
3345 other {Test text for plural with {NUM} as number}}
3351 # A grd file with one ICU syntax message without syntax errors.
3352 NEW_GRD_CONTENTS_ICU_SYNTAX_OK2 = """<?xml version="1.0" encoding="UTF-8"?>
3353 <grit latest_public_release="1" current_release="1">
3356 <message name="IDS_TEST1">
3358 =1 {Different test text for numeric one}
3359 other {Different test text for plural with {NUM} as number}}
3365 # A grd file with one ICU syntax message with syntax errors (misses a comma).
3366 NEW_GRD_CONTENTS_ICU_SYNTAX_ERROR = """<?xml version="1.0" encoding="UTF-8"?>
3367 <grit latest_public_release="1" current_release="1">
3370 <message name="IDS_TEST1">
3372 =1 {Test text for numeric one}
3373 other {Test text for plural with {NUM} as number}}
3380 OLD_GRDP_CONTENTS = (
3381 '<?xml version="1.0" encoding="utf-8"?>',
3386 NEW_GRDP_CONTENTS1 = (
3387 '<?xml version="1.0" encoding="utf-8"?>',
3389 '<message name="IDS_PART_TEST1">',
3394 NEW_GRDP_CONTENTS2 = (
3395 '<?xml version="1.0" encoding="utf-8"?>',
3397 '<message name="IDS_PART_TEST1">',
3400 '<message name="IDS_PART_TEST2">',
3405 NEW_GRDP_CONTENTS3 = (
3406 '<?xml version="1.0" encoding="utf-8"?>',
3408 '<message name="IDS_PART_TEST1" desc="Description with typo.">',
3413 NEW_GRDP_CONTENTS4 = (
3414 '<?xml version="1.0" encoding="utf-8"?>',
3416 '<message name="IDS_PART_TEST1" desc="Description with typo fixed.">',
3421 NEW_GRDP_CONTENTS5 = (
3422 '<?xml version="1.0" encoding="utf-8"?>',
3424 '<message name="IDS_PART_TEST1" meaning="Meaning with typo.">',
3429 NEW_GRDP_CONTENTS6 = (
3430 '<?xml version="1.0" encoding="utf-8"?>',
3432 '<message name="IDS_PART_TEST1" meaning="Meaning with typo fixed.">',
3437 # A grdp file with one ICU syntax message without syntax errors.
3438 NEW_GRDP_CONTENTS_ICU_SYNTAX_OK1 = (
3439 '<?xml version="1.0" encoding="utf-8"?>',
3441 '<message name="IDS_PART_TEST1">',
3443 '=1 {Test text for numeric one}',
3444 'other {Test text for plural with {NUM} as number}}',
3447 # A grdp file with one ICU syntax message without syntax errors.
3448 NEW_GRDP_CONTENTS_ICU_SYNTAX_OK2 = (
3449 '<?xml version="1.0" encoding="utf-8"?>',
3451 '<message name="IDS_PART_TEST1">',
3453 '=1 {Different test text for numeric one}',
3454 'other {Different test text for plural with {NUM} as number}}',
3458 # A grdp file with one ICU syntax message with syntax errors (superfluent
3460 NEW_GRDP_CONTENTS_ICU_SYNTAX_ERROR = (
3461 '<?xml version="1.0" encoding="utf-8"?>',
3463 '<message name="IDS_PART_TEST1">',
3465 '= 1 {Test text for numeric one}',
3466 'other {Test text for plural with {NUM} as number}}',
3470 DO_NOT_UPLOAD_PNG_MESSAGE = ('Do not include actual screenshots in the '
3472 'tools/translate/upload_screenshots.py to '
3473 'upload them instead:')
3474 ADD_SIGNATURES_MESSAGE = ('You are adding UI strings.\n'
3475 'To ensure the best translations, take '
3476 'screenshots of the relevant UI '
3477 '(https://g.co/chrome/translation) and add '
3478 'these files to your changelist:')
3479 REMOVE_SIGNATURES_MESSAGE = ('You removed strings associated with these '
3481 ICU_SYNTAX_ERROR_MESSAGE = ('ICU syntax errors were found in the following '
3482 'strings (problems or feedback? Contact '
3483 'rainhard@chromium.org):')
3485 def makeInputApi(self, files):
3486 input_api = MockInputApi()
3487 input_api.files = files
3488 # Override os_path.exists because the presubmit uses the actual
3490 input_api.CreateMockFileInPath(
3491 [x.LocalPath() for x in input_api.AffectedFiles(include_deletes=True)])
3494 """ CL modified and added messages, but didn't add any screenshots."""
3495 def testNoScreenshots(self):
3496 # No new strings (file contents same). Should not warn.
3497 input_api = self.makeInputApi([
3498 MockAffectedFile('test.grd', self.NEW_GRD_CONTENTS1,
3499 self.NEW_GRD_CONTENTS1, action='M'),
3500 MockAffectedFile('part.grdp', self.NEW_GRDP_CONTENTS1,
3501 self.NEW_GRDP_CONTENTS1, action='M')])
3502 warnings = PRESUBMIT.CheckStrings(input_api, MockOutputApi())
3503 self.assertEqual(0, len(warnings))
3505 # Add two new strings. Should have two warnings.
3506 input_api = self.makeInputApi([
3507 MockAffectedFile('test.grd', self.NEW_GRD_CONTENTS2,
3508 self.NEW_GRD_CONTENTS1, action='M'),
3509 MockAffectedFile('part.grdp', self.NEW_GRDP_CONTENTS2,
3510 self.NEW_GRDP_CONTENTS1, action='M')])
3511 warnings = PRESUBMIT.CheckStrings(input_api, MockOutputApi())
3512 self.assertEqual(1, len(warnings))
3513 self.assertEqual(self.ADD_SIGNATURES_MESSAGE, warnings[0].message)
3514 self.assertEqual('error', warnings[0].type)
3516 os.path.join('part_grdp', 'IDS_PART_TEST2.png.sha1'),
3517 os.path.join('test_grd', 'IDS_TEST2.png.sha1')],
3520 # Add four new strings. Should have four warnings.
3521 input_api = self.makeInputApi([
3522 MockAffectedFile('test.grd', self.NEW_GRD_CONTENTS2,
3523 self.OLD_GRD_CONTENTS, action='M'),
3524 MockAffectedFile('part.grdp', self.NEW_GRDP_CONTENTS2,
3525 self.OLD_GRDP_CONTENTS, action='M')])
3526 warnings = PRESUBMIT.CheckStrings(input_api, MockOutputApi())
3527 self.assertEqual(1, len(warnings))
3528 self.assertEqual('error', warnings[0].type)
3529 self.assertEqual(self.ADD_SIGNATURES_MESSAGE, warnings[0].message)
3531 os.path.join('part_grdp', 'IDS_PART_TEST1.png.sha1'),
3532 os.path.join('part_grdp', 'IDS_PART_TEST2.png.sha1'),
3533 os.path.join('test_grd', 'IDS_TEST1.png.sha1'),
3534 os.path.join('test_grd', 'IDS_TEST2.png.sha1'),
3535 ], warnings[0].items)
3537 def testModifiedMessageDescription(self):
3538 # CL modified a message description for a message that does not yet have a
3539 # screenshot. Should not warn.
3540 input_api = self.makeInputApi([
3541 MockAffectedFile('part.grdp', self.NEW_GRDP_CONTENTS3,
3542 self.NEW_GRDP_CONTENTS4, action='M')])
3543 warnings = PRESUBMIT.CheckStrings(input_api, MockOutputApi())
3544 self.assertEqual(0, len(warnings))
3546 # CL modified a message description for a message that already has a
3547 # screenshot. Should not warn.
3548 input_api = self.makeInputApi([
3549 MockAffectedFile('part.grdp', self.NEW_GRDP_CONTENTS3,
3550 self.NEW_GRDP_CONTENTS4, action='M'),
3551 MockFile(os.path.join('part_grdp', 'IDS_PART_TEST1.png.sha1'),
3552 'binary', action='A')])
3553 warnings = PRESUBMIT.CheckStrings(input_api, MockOutputApi())
3554 self.assertEqual(0, len(warnings))
3556 def testModifiedMessageMeaning(self):
3557 # CL modified a message meaning for a message that does not yet have a
3558 # screenshot. Should warn.
3559 input_api = self.makeInputApi([
3560 MockAffectedFile('part.grdp', self.NEW_GRDP_CONTENTS5,
3561 self.NEW_GRDP_CONTENTS6, action='M')])
3562 warnings = PRESUBMIT.CheckStrings(input_api, MockOutputApi())
3563 self.assertEqual(1, len(warnings))
3565 # CL modified a message meaning for a message that already has a
3566 # screenshot. Should not warn.
3567 input_api = self.makeInputApi([
3568 MockAffectedFile('part.grdp', self.NEW_GRDP_CONTENTS5,
3569 self.NEW_GRDP_CONTENTS6, action='M'),
3570 MockFile(os.path.join('part_grdp', 'IDS_PART_TEST1.png.sha1'),
3571 'binary', action='A')])
3572 warnings = PRESUBMIT.CheckStrings(input_api, MockOutputApi())
3573 self.assertEqual(0, len(warnings))
3575 def testPngAddedSha1NotAdded(self):
3576 # CL added one new message in a grd file and added the png file associated
3577 # with it, but did not add the corresponding sha1 file. This should warn
3579 # - Once for the added png file (because we don't want developers to upload
3581 # - Once for the missing .sha1 file
3582 input_api = self.makeInputApi([
3585 self.NEW_GRD_CONTENTS1,
3586 self.OLD_GRD_CONTENTS,
3589 os.path.join('test_grd', 'IDS_TEST1.png'), 'binary', action='A')
3591 warnings = PRESUBMIT.CheckStrings(input_api, MockOutputApi())
3592 self.assertEqual(2, len(warnings))
3593 self.assertEqual('error', warnings[0].type)
3594 self.assertEqual(self.DO_NOT_UPLOAD_PNG_MESSAGE, warnings[0].message)
3595 self.assertEqual([os.path.join('test_grd', 'IDS_TEST1.png')],
3597 self.assertEqual('error', warnings[1].type)
3598 self.assertEqual(self.ADD_SIGNATURES_MESSAGE, warnings[1].message)
3599 self.assertEqual([os.path.join('test_grd', 'IDS_TEST1.png.sha1')],
3602 # CL added two messages (one in grd, one in grdp) and added the png files
3603 # associated with the messages, but did not add the corresponding sha1
3604 # files. This should warn twice:
3605 # - Once for the added png files (because we don't want developers to upload
3607 # - Once for the missing .sha1 files
3608 input_api = self.makeInputApi([
3612 self.NEW_GRD_CONTENTS1,
3613 self.OLD_GRD_CONTENTS,
3617 self.NEW_GRDP_CONTENTS1,
3618 self.OLD_GRDP_CONTENTS,
3622 os.path.join('test_grd', 'IDS_TEST1.png'), 'binary', action='A'),
3624 os.path.join('part_grdp', 'IDS_PART_TEST1.png'), 'binary',
3627 warnings = PRESUBMIT.CheckStrings(input_api, MockOutputApi())
3628 self.assertEqual(2, len(warnings))
3629 self.assertEqual('error', warnings[0].type)
3630 self.assertEqual(self.DO_NOT_UPLOAD_PNG_MESSAGE, warnings[0].message)
3631 self.assertEqual([os.path.join('part_grdp', 'IDS_PART_TEST1.png'),
3632 os.path.join('test_grd', 'IDS_TEST1.png')],
3634 self.assertEqual('error', warnings[0].type)
3635 self.assertEqual(self.ADD_SIGNATURES_MESSAGE, warnings[1].message)
3636 self.assertEqual([os.path.join('part_grdp', 'IDS_PART_TEST1.png.sha1'),
3637 os.path.join('test_grd', 'IDS_TEST1.png.sha1')],
3640 def testScreenshotsWithSha1(self):
3641 # CL added four messages (two each in a grd and grdp) and their
3642 # corresponding .sha1 files. No warnings.
3643 input_api = self.makeInputApi([
3647 self.NEW_GRD_CONTENTS2,
3648 self.OLD_GRD_CONTENTS,
3652 self.NEW_GRDP_CONTENTS2,
3653 self.OLD_GRDP_CONTENTS,
3657 os.path.join('test_grd', 'IDS_TEST1.png.sha1'),
3661 os.path.join('test_grd', 'IDS_TEST2.png.sha1'),
3665 os.path.join('part_grdp', 'IDS_PART_TEST1.png.sha1'),
3669 os.path.join('part_grdp', 'IDS_PART_TEST2.png.sha1'),
3673 warnings = PRESUBMIT.CheckStrings(input_api, MockOutputApi())
3674 self.assertEqual([], warnings)
3676 def testScreenshotsRemovedWithSha1(self):
3677 # Replace new contents with old contents in grd and grp files, removing
3678 # IDS_TEST1, IDS_TEST2, IDS_PART_TEST1 and IDS_PART_TEST2.
3679 # Should warn to remove the sha1 files associated with these strings.
3680 input_api = self.makeInputApi([
3684 self.OLD_GRD_CONTENTS, # new_contents
3685 self.NEW_GRD_CONTENTS2, # old_contents
3689 self.OLD_GRDP_CONTENTS, # new_contents
3690 self.NEW_GRDP_CONTENTS2, # old_contents
3693 MockFile(os.path.join('test_grd', 'IDS_TEST1.png.sha1'), 'binary', ''),
3694 MockFile(os.path.join('test_grd', 'IDS_TEST2.png.sha1'), 'binary', ''),
3695 MockFile(os.path.join('part_grdp', 'IDS_PART_TEST1.png.sha1'),
3697 MockFile(os.path.join('part_grdp', 'IDS_PART_TEST2.png.sha1'),
3700 warnings = PRESUBMIT.CheckStrings(input_api, MockOutputApi())
3701 self.assertEqual(1, len(warnings))
3702 self.assertEqual('error', warnings[0].type)
3703 self.assertEqual(self.REMOVE_SIGNATURES_MESSAGE, warnings[0].message)
3705 os.path.join('part_grdp', 'IDS_PART_TEST1.png.sha1'),
3706 os.path.join('part_grdp', 'IDS_PART_TEST2.png.sha1'),
3707 os.path.join('test_grd', 'IDS_TEST1.png.sha1'),
3708 os.path.join('test_grd', 'IDS_TEST2.png.sha1')
3709 ], warnings[0].items)
3711 # Same as above, but this time one of the .sha1 files is also removed.
3712 input_api = self.makeInputApi([
3716 self.OLD_GRD_CONTENTS, # new_contents
3717 self.NEW_GRD_CONTENTS2, # old_contents
3721 self.OLD_GRDP_CONTENTS, # new_contents
3722 self.NEW_GRDP_CONTENTS2, # old_contents
3725 MockFile(os.path.join('test_grd', 'IDS_TEST1.png.sha1'), 'binary', ''),
3726 MockFile(os.path.join('part_grdp', 'IDS_PART_TEST1.png.sha1'),
3730 os.path.join('test_grd', 'IDS_TEST2.png.sha1'),
3735 os.path.join('part_grdp', 'IDS_PART_TEST2.png.sha1'),
3740 warnings = PRESUBMIT.CheckStrings(input_api, MockOutputApi())
3741 self.assertEqual(1, len(warnings))
3742 self.assertEqual('error', warnings[0].type)
3743 self.assertEqual(self.REMOVE_SIGNATURES_MESSAGE, warnings[0].message)
3744 self.assertEqual([os.path.join('part_grdp', 'IDS_PART_TEST1.png.sha1'),
3745 os.path.join('test_grd', 'IDS_TEST1.png.sha1')
3746 ], warnings[0].items)
3748 # Remove all sha1 files. There should be no warnings.
3749 input_api = self.makeInputApi([
3753 self.OLD_GRD_CONTENTS,
3754 self.NEW_GRD_CONTENTS2,
3758 self.OLD_GRDP_CONTENTS,
3759 self.NEW_GRDP_CONTENTS2,
3763 os.path.join('test_grd', 'IDS_TEST1.png.sha1'),
3767 os.path.join('test_grd', 'IDS_TEST2.png.sha1'),
3771 os.path.join('part_grdp', 'IDS_PART_TEST1.png.sha1'),
3775 os.path.join('part_grdp', 'IDS_PART_TEST2.png.sha1'),
3779 warnings = PRESUBMIT.CheckStrings(input_api, MockOutputApi())
3780 self.assertEqual([], warnings)
3782 def testIcuSyntax(self):
3783 # Add valid ICU syntax string. Should not raise an error.
3784 input_api = self.makeInputApi([
3785 MockAffectedFile('test.grd', self.NEW_GRD_CONTENTS_ICU_SYNTAX_OK2,
3786 self.NEW_GRD_CONTENTS1, action='M'),
3787 MockAffectedFile('part.grdp', self.NEW_GRDP_CONTENTS_ICU_SYNTAX_OK2,
3788 self.NEW_GRDP_CONTENTS1, action='M')])
3789 results = PRESUBMIT.CheckStrings(input_api, MockOutputApi())
3790 # We expect no ICU syntax errors.
3791 icu_errors = [e for e in results
3792 if e.message == self.ICU_SYNTAX_ERROR_MESSAGE]
3793 self.assertEqual(0, len(icu_errors))
3795 # Valid changes in ICU syntax. Should not raise an error.
3796 input_api = self.makeInputApi([
3797 MockAffectedFile('test.grd', self.NEW_GRD_CONTENTS_ICU_SYNTAX_OK2,
3798 self.NEW_GRD_CONTENTS_ICU_SYNTAX_OK1, action='M'),
3799 MockAffectedFile('part.grdp', self.NEW_GRDP_CONTENTS_ICU_SYNTAX_OK2,
3800 self.NEW_GRDP_CONTENTS_ICU_SYNTAX_OK1, action='M')])
3801 results = PRESUBMIT.CheckStrings(input_api, MockOutputApi())
3802 # We expect no ICU syntax errors.
3803 icu_errors = [e for e in results
3804 if e.message == self.ICU_SYNTAX_ERROR_MESSAGE]
3805 self.assertEqual(0, len(icu_errors))
3807 # Add invalid ICU syntax strings. Should raise two errors.
3808 input_api = self.makeInputApi([
3809 MockAffectedFile('test.grd', self.NEW_GRD_CONTENTS_ICU_SYNTAX_ERROR,
3810 self.NEW_GRD_CONTENTS1, action='M'),
3811 MockAffectedFile('part.grdp', self.NEW_GRDP_CONTENTS_ICU_SYNTAX_ERROR,
3812 self.NEW_GRD_CONTENTS1, action='M')])
3813 results = PRESUBMIT.CheckStrings(input_api, MockOutputApi())
3814 # We expect 2 ICU syntax errors.
3815 icu_errors = [e for e in results
3816 if e.message == self.ICU_SYNTAX_ERROR_MESSAGE]
3817 self.assertEqual(1, len(icu_errors))
3819 'IDS_TEST1: This message looks like an ICU plural, but does not follow '
3821 'IDS_PART_TEST1: Variant "= 1" is not valid for plural message'
3822 ], icu_errors[0].items)
3824 # Change two strings to have ICU syntax errors. Should raise two errors.
3825 input_api = self.makeInputApi([
3826 MockAffectedFile('test.grd', self.NEW_GRD_CONTENTS_ICU_SYNTAX_ERROR,
3827 self.NEW_GRD_CONTENTS_ICU_SYNTAX_OK1, action='M'),
3828 MockAffectedFile('part.grdp', self.NEW_GRDP_CONTENTS_ICU_SYNTAX_ERROR,
3829 self.NEW_GRDP_CONTENTS_ICU_SYNTAX_OK1, action='M')])
3830 results = PRESUBMIT.CheckStrings(input_api, MockOutputApi())
3831 # We expect 2 ICU syntax errors.
3832 icu_errors = [e for e in results
3833 if e.message == self.ICU_SYNTAX_ERROR_MESSAGE]
3834 self.assertEqual(1, len(icu_errors))
3836 'IDS_TEST1: This message looks like an ICU plural, but does not follow '
3838 'IDS_PART_TEST1: Variant "= 1" is not valid for plural message'
3839 ], icu_errors[0].items)
3842 class TranslationExpectationsTest(unittest.TestCase):
3843 ERROR_MESSAGE_FORMAT = (
3844 "Failed to get a list of translatable grd files. "
3845 "This happens when:\n"
3846 " - One of the modified grd or grdp files cannot be parsed or\n"
3847 " - %s is not updated.\n"
3850 REPO_ROOT = os.path.join('tools', 'translation', 'testdata')
3851 # This lists all .grd files under REPO_ROOT.
3852 EXPECTATIONS = os.path.join(REPO_ROOT,
3853 "translation_expectations.pyl")
3854 # This lists all .grd files under REPO_ROOT except unlisted.grd.
3855 EXPECTATIONS_WITHOUT_UNLISTED_FILE = os.path.join(
3856 REPO_ROOT, "translation_expectations_without_unlisted_file.pyl")
3858 # Tests that the presubmit doesn't return when no grd or grdp files are
3860 def testExpectationsNoModifiedGrd(self):
3861 input_api = MockInputApi()
3863 MockAffectedFile('not_used.txt', 'not used', 'not used', action='M')
3865 # Fake list of all grd files in the repo. This list is missing all grd/grdps
3866 # under tools/translation/testdata. This is OK because the presubmit won't
3867 # run in the first place since there are no modified grd/grps in input_api.
3868 grd_files = ['doesnt_exist_doesnt_matter.grd']
3869 warnings = PRESUBMIT.CheckTranslationExpectations(
3870 input_api, MockOutputApi(), self.REPO_ROOT, self.EXPECTATIONS,
3872 self.assertEqual(0, len(warnings))
3875 # Tests that the list of files passed to the presubmit matches the list of
3876 # files in the expectations.
3877 def testExpectationsSuccess(self):
3878 # Mock input file list needs a grd or grdp file in order to run the
3879 # presubmit. The file itself doesn't matter.
3880 input_api = MockInputApi()
3882 MockAffectedFile('dummy.grd', 'not used', 'not used', action='M')
3884 # List of all grd files in the repo.
3885 grd_files = ['test.grd', 'unlisted.grd', 'not_translated.grd',
3887 warnings = PRESUBMIT.CheckTranslationExpectations(
3888 input_api, MockOutputApi(), self.REPO_ROOT, self.EXPECTATIONS,
3890 self.assertEqual(0, len(warnings))
3892 # Tests that the presubmit warns when a file is listed in expectations, but
3893 # does not actually exist.
3894 def testExpectationsMissingFile(self):
3895 # Mock input file list needs a grd or grdp file in order to run the
3897 input_api = MockInputApi()
3899 MockAffectedFile('dummy.grd', 'not used', 'not used', action='M')
3901 # unlisted.grd is listed under tools/translation/testdata but is not
3902 # included in translation expectations.
3903 grd_files = ['unlisted.grd', 'not_translated.grd', 'internal.grd']
3904 warnings = PRESUBMIT.CheckTranslationExpectations(
3905 input_api, MockOutputApi(), self.REPO_ROOT, self.EXPECTATIONS,
3907 self.assertEqual(1, len(warnings))
3908 self.assertTrue(warnings[0].message.startswith(
3909 self.ERROR_MESSAGE_FORMAT % self.EXPECTATIONS))
3911 ("test.grd is listed in the translation expectations, "
3912 "but this grd file does not exist")
3913 in warnings[0].message)
3915 # Tests that the presubmit warns when a file is not listed in expectations but
3916 # does actually exist.
3917 def testExpectationsUnlistedFile(self):
3918 # Mock input file list needs a grd or grdp file in order to run the
3920 input_api = MockInputApi()
3922 MockAffectedFile('dummy.grd', 'not used', 'not used', action='M')
3924 # unlisted.grd is listed under tools/translation/testdata but is not
3925 # included in translation expectations.
3926 grd_files = ['test.grd', 'unlisted.grd', 'not_translated.grd',
3928 warnings = PRESUBMIT.CheckTranslationExpectations(
3929 input_api, MockOutputApi(), self.REPO_ROOT,
3930 self.EXPECTATIONS_WITHOUT_UNLISTED_FILE, grd_files)
3931 self.assertEqual(1, len(warnings))
3932 self.assertTrue(warnings[0].message.startswith(
3933 self.ERROR_MESSAGE_FORMAT % self.EXPECTATIONS_WITHOUT_UNLISTED_FILE))
3935 ("unlisted.grd appears to be translatable "
3936 "(because it contains <file> or <message> elements), "
3937 "but is not listed in the translation expectations.")
3938 in warnings[0].message)
3940 # Tests that the presubmit warns twice:
3941 # - for a non-existing file listed in expectations
3942 # - for an existing file not listed in expectations
3943 def testMultipleWarnings(self):
3944 # Mock input file list needs a grd or grdp file in order to run the
3946 input_api = MockInputApi()
3948 MockAffectedFile('dummy.grd', 'not used', 'not used', action='M')
3950 # unlisted.grd is listed under tools/translation/testdata but is not
3951 # included in translation expectations.
3952 # test.grd is not listed under tools/translation/testdata but is included
3953 # in translation expectations.
3954 grd_files = ['unlisted.grd', 'not_translated.grd', 'internal.grd']
3955 warnings = PRESUBMIT.CheckTranslationExpectations(
3956 input_api, MockOutputApi(), self.REPO_ROOT,
3957 self.EXPECTATIONS_WITHOUT_UNLISTED_FILE, grd_files)
3958 self.assertEqual(1, len(warnings))
3959 self.assertTrue(warnings[0].message.startswith(
3960 self.ERROR_MESSAGE_FORMAT % self.EXPECTATIONS_WITHOUT_UNLISTED_FILE))
3962 ("unlisted.grd appears to be translatable "
3963 "(because it contains <file> or <message> elements), "
3964 "but is not listed in the translation expectations.")
3965 in warnings[0].message)
3967 ("test.grd is listed in the translation expectations, "
3968 "but this grd file does not exist")
3969 in warnings[0].message)
3972 class DISABLETypoInTest(unittest.TestCase):
3974 def testPositive(self):
3975 # Verify the typo "DISABLE_" instead of "DISABLED_" in various contexts
3976 # where the desire is to disable a test.
3978 # Disabled on one platform:
3979 '#if defined(OS_WIN)\n'
3980 '#define MAYBE_FoobarTest DISABLE_FoobarTest\n'
3982 '#define MAYBE_FoobarTest FoobarTest\n'
3984 # Disabled on one platform spread cross lines:
3985 '#if defined(OS_WIN)\n'
3986 '#define MAYBE_FoobarTest \\\n'
3987 ' DISABLE_FoobarTest\n'
3989 '#define MAYBE_FoobarTest FoobarTest\n'
3991 # Disabled on all platforms:
3992 ' TEST_F(FoobarTest, DISABLE_Foo)\n{\n}',
3993 # Disabled on all platforms but multiple lines
3994 ' TEST_F(FoobarTest,\n DISABLE_foo){\n}\n',
3998 mock_input_api = MockInputApi()
3999 mock_input_api.files = [
4000 MockFile('some/path/foo_unittest.cc', test.splitlines()),
4003 results = PRESUBMIT.CheckNoDISABLETypoInTests(mock_input_api,
4008 msg=('expected len(results) == 1 but got %d in test: %s' %
4009 (len(results), test)))
4011 'foo_unittest.cc' in results[0].message,
4012 msg=('expected foo_unittest.cc in message but got %s in test %s' %
4013 (results[0].message, test)))
4015 def testIgnoreNotTestFiles(self):
4016 mock_input_api = MockInputApi()
4017 mock_input_api.files = [
4018 MockFile('some/path/foo.cc', 'TEST_F(FoobarTest, DISABLE_Foo)'),
4021 results = PRESUBMIT.CheckNoDISABLETypoInTests(mock_input_api,
4023 self.assertEqual(0, len(results))
4025 def testIgnoreDeletedFiles(self):
4026 mock_input_api = MockInputApi()
4027 mock_input_api.files = [
4028 MockFile('some/path/foo.cc', 'TEST_F(FoobarTest, Foo)', action='D'),
4031 results = PRESUBMIT.CheckNoDISABLETypoInTests(mock_input_api,
4033 self.assertEqual(0, len(results))
4035 class ForgettingMAYBEInTests(unittest.TestCase):
4036 def testPositive(self):
4038 '#if defined(HAS_ENERGY)\n'
4039 '#define MAYBE_CastExplosion DISABLED_CastExplosion\n'
4041 '#define MAYBE_CastExplosion CastExplosion\n'
4043 'TEST_F(ArchWizard, CastExplosion) {\n'
4044 '#if defined(ARCH_PRIEST_IN_PARTY)\n'
4045 '#define MAYBE_ArchPriest ArchPriest\n'
4047 '#define MAYBE_ArchPriest DISABLED_ArchPriest\n'
4049 'TEST_F(ArchPriest, CastNaturesBounty) {\n'
4050 '#if !defined(CRUSADER_IN_PARTY)\n'
4051 '#define MAYBE_Crusader \\\n'
4052 ' DISABLED_Crusader \n'
4054 '#define MAYBE_Crusader \\\n'
4060 '#if defined(LEARNED_BASIC_SKILLS)\n'
4061 '#define MAYBE_CastSteal \\\n'
4062 ' DISABLED_CastSteal \n'
4064 '#define MAYBE_CastSteal \\\n'
4071 mock_input_api = MockInputApi()
4072 mock_input_api.files = [
4073 MockFile('fantasyworld/classes_unittest.cc', test.splitlines()),
4075 results = PRESUBMIT.CheckForgettingMAYBEInTests(mock_input_api,
4077 self.assertEqual(4, len(results))
4078 self.assertTrue('CastExplosion' in results[0].message)
4079 self.assertTrue('fantasyworld/classes_unittest.cc:2' in results[0].message)
4080 self.assertTrue('ArchPriest' in results[1].message)
4081 self.assertTrue('fantasyworld/classes_unittest.cc:8' in results[1].message)
4082 self.assertTrue('Crusader' in results[2].message)
4083 self.assertTrue('fantasyworld/classes_unittest.cc:14' in results[2].message)
4084 self.assertTrue('CastSteal' in results[3].message)
4085 self.assertTrue('fantasyworld/classes_unittest.cc:24' in results[3].message)
4087 def testNegative(self):
4089 '#if defined(HAS_ENERGY)\n'
4090 '#define MAYBE_CastExplosion DISABLED_CastExplosion\n'
4092 '#define MAYBE_CastExplosion CastExplosion\n'
4094 'TEST_F(ArchWizard, MAYBE_CastExplosion) {\n'
4095 '#if defined(ARCH_PRIEST_IN_PARTY)\n'
4096 '#define MAYBE_ArchPriest ArchPriest\n'
4098 '#define MAYBE_ArchPriest DISABLED_ArchPriest\n'
4100 'TEST_F(MAYBE_ArchPriest, CastNaturesBounty) {\n'
4101 '#if !defined(CRUSADER_IN_PARTY)\n'
4102 '#define MAYBE_Crusader \\\n'
4103 ' DISABLED_Crusader \n'
4105 '#define MAYBE_Crusader \\\n'
4109 ' MAYBE_Crusader,\n'
4111 '#if defined(LEARNED_BASIC_SKILLS)\n'
4112 '#define MAYBE_CastSteal \\\n'
4113 ' DISABLED_CastSteal \n'
4115 '#define MAYBE_CastSteal \\\n'
4120 ' MAYBE_CastSteal) { }\n'
4123 mock_input_api = MockInputApi()
4124 mock_input_api.files = [
4125 MockFile('fantasyworld/classes_unittest.cc', test.splitlines()),
4127 results = PRESUBMIT.CheckForgettingMAYBEInTests(mock_input_api,
4129 self.assertEqual(0, len(results))
4131 class CheckFuzzTargetsTest(unittest.TestCase):
4133 def _check(self, files):
4134 mock_input_api = MockInputApi()
4135 mock_input_api.files = []
4136 for fname, contents in files.items():
4137 mock_input_api.files.append(MockFile(fname, contents.splitlines()))
4138 return PRESUBMIT.CheckFuzzTargetsOnUpload(mock_input_api, MockOutputApi())
4140 def testLibFuzzerSourcesIgnored(self):
4141 results = self._check({
4142 "third_party/lib/Fuzzer/FuzzerDriver.cpp": "LLVMFuzzerInitialize",
4144 self.assertEqual(results, [])
4146 def testNonCodeFilesIgnored(self):
4147 results = self._check({
4148 "README.md": "LLVMFuzzerInitialize",
4150 self.assertEqual(results, [])
4152 def testNoErrorHeaderPresent(self):
4153 results = self._check({
4155 "#include \"testing/libfuzzer/libfuzzer_exports.h\"\n" +
4156 "LLVMFuzzerInitialize"
4159 self.assertEqual(results, [])
4161 def testErrorMissingHeader(self):
4162 results = self._check({
4163 "fuzzer.cc": "LLVMFuzzerInitialize"
4165 self.assertEqual(len(results), 1)
4166 self.assertEqual(results[0].items, ['fuzzer.cc'])
4169 class SetNoParentTest(unittest.TestCase):
4170 def testSetNoParentTopLevelAllowed(self):
4171 mock_input_api = MockInputApi()
4172 mock_input_api.files = [
4173 MockAffectedFile('goat/OWNERS',
4176 'jochen@chromium.org',
4179 mock_output_api = MockOutputApi()
4180 errors = PRESUBMIT.CheckSetNoParent(mock_input_api, mock_output_api)
4181 self.assertEqual([], errors)
4183 def testSetNoParentMissing(self):
4184 mock_input_api = MockInputApi()
4185 mock_input_api.files = [
4186 MockAffectedFile('services/goat/OWNERS',
4189 'jochen@chromium.org',
4190 'per-file *.json=set noparent',
4191 'per-file *.json=jochen@chromium.org',
4194 mock_output_api = MockOutputApi()
4195 errors = PRESUBMIT.CheckSetNoParent(mock_input_api, mock_output_api)
4196 self.assertEqual(1, len(errors))
4197 self.assertTrue('goat/OWNERS:1' in errors[0].long_text)
4198 self.assertTrue('goat/OWNERS:3' in errors[0].long_text)
4200 def testSetNoParentWithCorrectRule(self):
4201 mock_input_api = MockInputApi()
4202 mock_input_api.files = [
4203 MockAffectedFile('services/goat/OWNERS',
4206 'file://ipc/SECURITY_OWNERS',
4207 'per-file *.json=set noparent',
4208 'per-file *.json=file://ipc/SECURITY_OWNERS',
4211 mock_output_api = MockOutputApi()
4212 errors = PRESUBMIT.CheckSetNoParent(mock_input_api, mock_output_api)
4213 self.assertEqual([], errors)
4216 class MojomStabilityCheckTest(unittest.TestCase):
4217 def runTestWithAffectedFiles(self, affected_files):
4218 mock_input_api = MockInputApi()
4219 mock_input_api.files = affected_files
4220 mock_output_api = MockOutputApi()
4221 return PRESUBMIT.CheckStableMojomChanges(
4222 mock_input_api, mock_output_api)
4224 def testSafeChangePasses(self):
4225 errors = self.runTestWithAffectedFiles([
4226 MockAffectedFile('foo/foo.mojom',
4227 ['[Stable] struct S { [MinVersion=1] int32 x; };'],
4228 old_contents=['[Stable] struct S {};'])
4230 self.assertEqual([], errors)
4232 def testBadChangeFails(self):
4233 errors = self.runTestWithAffectedFiles([
4234 MockAffectedFile('foo/foo.mojom',
4235 ['[Stable] struct S { int32 x; };'],
4236 old_contents=['[Stable] struct S {};'])
4238 self.assertEqual(1, len(errors))
4239 self.assertTrue('not backward-compatible' in errors[0].message)
4241 def testDeletedFile(self):
4242 """Regression test for https://crbug.com/1091407."""
4243 errors = self.runTestWithAffectedFiles([
4244 MockAffectedFile('a.mojom', [], old_contents=['struct S {};'],
4246 MockAffectedFile('b.mojom',
4247 ['struct S {}; struct T { S s; };'],
4248 old_contents=['import "a.mojom"; struct T { S s; };'])
4250 self.assertEqual([], errors)
4252 class CheckForUseOfChromeAppsDeprecationsTest(unittest.TestCase):
4254 ERROR_MSG_PIECE = 'technologies which will soon be deprecated'
4256 # Each positive test is also a naive negative test for the other cases.
4258 def testWarningNMF(self):
4259 mock_input_api = MockInputApi()
4260 mock_input_api.files = [
4263 ['"program"', '"Z":"content"', 'B'],
4265 scm_diff='\n'.join([
4266 '--- foo.NMF.old 2020-12-02 20:40:54.430676385 +0100',
4267 '+++ foo.NMF.new 2020-12-02 20:41:02.086700197 +0100',
4274 mock_output_api = MockOutputApi()
4275 errors = PRESUBMIT.CheckForUseOfChromeAppsDeprecations(mock_input_api,
4277 self.assertEqual(1, len(errors))
4278 self.assertTrue( self.ERROR_MSG_PIECE in errors[0].message)
4279 self.assertTrue( 'foo.NMF' in errors[0].message)
4281 def testWarningManifest(self):
4282 mock_input_api = MockInputApi()
4283 mock_input_api.files = [
4286 ['"app":', '"Z":"content"', 'B'],
4288 scm_diff='\n'.join([
4289 '--- manifest.json.old 2020-12-02 20:40:54.430676385 +0100',
4290 '+++ manifest.json.new 2020-12-02 20:41:02.086700197 +0100',
4297 mock_output_api = MockOutputApi()
4298 errors = PRESUBMIT.CheckForUseOfChromeAppsDeprecations(mock_input_api,
4300 self.assertEqual(1, len(errors))
4301 self.assertTrue( self.ERROR_MSG_PIECE in errors[0].message)
4302 self.assertTrue( 'manifest.json' in errors[0].message)
4304 def testOKWarningManifestWithoutApp(self):
4305 mock_input_api = MockInputApi()
4306 mock_input_api.files = [
4309 ['"name":', '"Z":"content"', 'B'],
4311 scm_diff='\n'.join([
4312 '--- manifest.json.old 2020-12-02 20:40:54.430676385 +0100',
4313 '+++ manifest.json.new 2020-12-02 20:41:02.086700197 +0100',
4320 mock_output_api = MockOutputApi()
4321 errors = PRESUBMIT.CheckForUseOfChromeAppsDeprecations(mock_input_api,
4323 self.assertEqual(0, len(errors))
4325 def testWarningPPAPI(self):
4326 mock_input_api = MockInputApi()
4327 mock_input_api.files = [
4330 ['A', '#include <ppapi.h>', 'B'],
4332 scm_diff='\n'.join([
4333 '--- foo.hpp.old 2020-12-02 20:40:54.430676385 +0100',
4334 '+++ foo.hpp.new 2020-12-02 20:41:02.086700197 +0100',
4337 '+#include <ppapi.h>',
4341 mock_output_api = MockOutputApi()
4342 errors = PRESUBMIT.CheckForUseOfChromeAppsDeprecations(mock_input_api,
4344 self.assertEqual(1, len(errors))
4345 self.assertTrue( self.ERROR_MSG_PIECE in errors[0].message)
4346 self.assertTrue( 'foo.hpp' in errors[0].message)
4348 def testNoWarningPPAPI(self):
4349 mock_input_api = MockInputApi()
4350 mock_input_api.files = [
4353 ['A', 'Peppapig', 'B'],
4355 scm_diff='\n'.join([
4356 '--- foo.txt.old 2020-12-02 20:40:54.430676385 +0100',
4357 '+++ foo.txt.new 2020-12-02 20:41:02.086700197 +0100',
4364 mock_output_api = MockOutputApi()
4365 errors = PRESUBMIT.CheckForUseOfChromeAppsDeprecations(mock_input_api,
4367 self.assertEqual(0, len(errors))
4369 class CheckDeprecationOfPreferencesTest(unittest.TestCase):
4370 # Test that a warning is generated if a preference registration is removed
4371 # from a random file.
4372 def testWarning(self):
4373 mock_input_api = MockInputApi()
4374 mock_input_api.files = [
4378 ['A', 'prefs->RegisterStringPref("foo", "default");', 'B'],
4379 scm_diff='\n'.join([
4380 '--- foo.cc.old 2020-12-02 20:40:54.430676385 +0100',
4381 '+++ foo.cc.new 2020-12-02 20:41:02.086700197 +0100',
4384 '-prefs->RegisterStringPref("foo", "default");',
4388 mock_output_api = MockOutputApi()
4389 errors = PRESUBMIT.CheckDeprecationOfPreferences(mock_input_api,
4391 self.assertEqual(1, len(errors))
4393 'Discovered possible removal of preference registrations' in
4396 # Test that a warning is inhibited if the preference registration was moved
4397 # to the deprecation functions in browser prefs.
4398 def testNoWarningForMigration(self):
4399 mock_input_api = MockInputApi()
4400 mock_input_api.files = [
4401 # RegisterStringPref was removed from foo.cc.
4405 ['A', 'prefs->RegisterStringPref("foo", "default");', 'B'],
4406 scm_diff='\n'.join([
4407 '--- foo.cc.old 2020-12-02 20:40:54.430676385 +0100',
4408 '+++ foo.cc.new 2020-12-02 20:41:02.086700197 +0100',
4411 '-prefs->RegisterStringPref("foo", "default");',
4414 # But the preference was properly migrated.
4416 'chrome/browser/prefs/browser_prefs.cc',
4418 '// BEGIN_MIGRATE_OBSOLETE_LOCAL_STATE_PREFS',
4419 '// END_MIGRATE_OBSOLETE_LOCAL_STATE_PREFS',
4420 '// BEGIN_MIGRATE_OBSOLETE_PROFILE_PREFS',
4421 'prefs->RegisterStringPref("foo", "default");',
4422 '// END_MIGRATE_OBSOLETE_PROFILE_PREFS',
4425 '// BEGIN_MIGRATE_OBSOLETE_LOCAL_STATE_PREFS',
4426 '// END_MIGRATE_OBSOLETE_LOCAL_STATE_PREFS',
4427 '// BEGIN_MIGRATE_OBSOLETE_PROFILE_PREFS',
4428 '// END_MIGRATE_OBSOLETE_PROFILE_PREFS',
4430 scm_diff='\n'.join([
4431 '--- browser_prefs.cc.old 2020-12-02 20:51:40.812686731 +0100',
4432 '+++ browser_prefs.cc.new 2020-12-02 20:52:02.936755539 +0100',
4434 ' // END_MIGRATE_OBSOLETE_LOCAL_STATE_PREFS',
4435 ' // BEGIN_MIGRATE_OBSOLETE_PROFILE_PREFS',
4436 '+prefs->RegisterStringPref("foo", "default");',
4437 ' // END_MIGRATE_OBSOLETE_PROFILE_PREFS']),
4440 mock_output_api = MockOutputApi()
4441 errors = PRESUBMIT.CheckDeprecationOfPreferences(mock_input_api,
4443 self.assertEqual(0, len(errors))
4445 # Test that a warning is NOT inhibited if the preference registration was
4446 # moved to a place outside of the migration functions in browser_prefs.cc
4447 def testWarningForImproperMigration(self):
4448 mock_input_api = MockInputApi()
4449 mock_input_api.files = [
4450 # RegisterStringPref was removed from foo.cc.
4454 ['A', 'prefs->RegisterStringPref("foo", "default");', 'B'],
4455 scm_diff='\n'.join([
4456 '--- foo.cc.old 2020-12-02 20:40:54.430676385 +0100',
4457 '+++ foo.cc.new 2020-12-02 20:41:02.086700197 +0100',
4460 '-prefs->RegisterStringPref("foo", "default");',
4463 # The registration call was moved to a place in browser_prefs.cc that
4464 # is outside the migration functions.
4466 'chrome/browser/prefs/browser_prefs.cc',
4468 'prefs->RegisterStringPref("foo", "default");',
4469 '// BEGIN_MIGRATE_OBSOLETE_LOCAL_STATE_PREFS',
4470 '// END_MIGRATE_OBSOLETE_LOCAL_STATE_PREFS',
4471 '// BEGIN_MIGRATE_OBSOLETE_PROFILE_PREFS',
4472 '// END_MIGRATE_OBSOLETE_PROFILE_PREFS',
4475 '// BEGIN_MIGRATE_OBSOLETE_LOCAL_STATE_PREFS',
4476 '// END_MIGRATE_OBSOLETE_LOCAL_STATE_PREFS',
4477 '// BEGIN_MIGRATE_OBSOLETE_PROFILE_PREFS',
4478 '// END_MIGRATE_OBSOLETE_PROFILE_PREFS',
4480 scm_diff='\n'.join([
4481 '--- browser_prefs.cc.old 2020-12-02 20:51:40.812686731 +0100',
4482 '+++ browser_prefs.cc.new 2020-12-02 20:52:02.936755539 +0100',
4484 '+prefs->RegisterStringPref("foo", "default");',
4485 ' // BEGIN_MIGRATE_OBSOLETE_LOCAL_STATE_PREFS',
4486 ' // END_MIGRATE_OBSOLETE_LOCAL_STATE_PREFS']),
4489 mock_output_api = MockOutputApi()
4490 errors = PRESUBMIT.CheckDeprecationOfPreferences(mock_input_api,
4492 self.assertEqual(1, len(errors))
4494 'Discovered possible removal of preference registrations' in
4497 # Check that the presubmit fails if a marker line in browser_prefs.cc is
4499 def testDeletedMarkerRaisesError(self):
4500 mock_input_api = MockInputApi()
4501 mock_input_api.files = [
4502 MockAffectedFile('chrome/browser/prefs/browser_prefs.cc',
4504 '// BEGIN_MIGRATE_OBSOLETE_LOCAL_STATE_PREFS',
4505 '// END_MIGRATE_OBSOLETE_LOCAL_STATE_PREFS',
4506 '// BEGIN_MIGRATE_OBSOLETE_PROFILE_PREFS',
4507 # The following line is deleted for this test
4508 # '// END_MIGRATE_OBSOLETE_PROFILE_PREFS',
4511 mock_output_api = MockOutputApi()
4512 errors = PRESUBMIT.CheckDeprecationOfPreferences(mock_input_api,
4514 self.assertEqual(1, len(errors))
4516 'Broken .*MIGRATE_OBSOLETE_.*_PREFS markers in browser_prefs.cc.',
4519 class CheckCrosApiNeedBrowserTestTest(unittest.TestCase):
4520 def testWarning(self):
4521 mock_input_api = MockInputApi()
4522 mock_output_api = MockOutputApi()
4523 mock_input_api.files = [
4524 MockAffectedFile('chromeos/crosapi/mojom/example.mojom', [], action='A'),
4526 result = PRESUBMIT.CheckCrosApiNeedBrowserTest(mock_input_api, mock_output_api)
4527 self.assertEqual(1, len(result))
4528 self.assertEqual(result[0].type, 'warning')
4530 def testNoWarningWithBrowserTest(self):
4531 mock_input_api = MockInputApi()
4532 mock_output_api = MockOutputApi()
4533 mock_input_api.files = [
4534 MockAffectedFile('chromeos/crosapi/mojom/example.mojom', [], action='A'),
4535 MockAffectedFile('chrome/example_browsertest.cc', [], action='A'),
4537 result = PRESUBMIT.CheckCrosApiNeedBrowserTest(mock_input_api, mock_output_api)
4538 self.assertEqual(0, len(result))
4540 def testNoWarningModifyCrosapi(self):
4541 mock_input_api = MockInputApi()
4542 mock_output_api = MockOutputApi()
4543 mock_input_api.files = [
4544 MockAffectedFile('chromeos/crosapi/mojom/example.mojom', [], action='M'),
4546 result = PRESUBMIT.CheckCrosApiNeedBrowserTest(mock_input_api, mock_output_api)
4547 self.assertEqual(0, len(result))
4549 def testNoWarningAddNonMojomFile(self):
4550 mock_input_api = MockInputApi()
4551 mock_output_api = MockOutputApi()
4552 mock_input_api.files = [
4553 MockAffectedFile('chromeos/crosapi/mojom/example.cc', [], action='A'),
4555 result = PRESUBMIT.CheckCrosApiNeedBrowserTest(mock_input_api, mock_output_api)
4556 self.assertEqual(0, len(result))
4558 def testNoWarningNoneRelatedMojom(self):
4559 mock_input_api = MockInputApi()
4560 mock_output_api = MockOutputApi()
4561 mock_input_api.files = [
4562 MockAffectedFile('random/folder/example.mojom', [], action='A'),
4564 result = PRESUBMIT.CheckCrosApiNeedBrowserTest(mock_input_api, mock_output_api)
4565 self.assertEqual(0, len(result))
4568 class AssertAshOnlyCodeTest(unittest.TestCase):
4569 def testErrorsOnlyOnAshDirectories(self):
4571 MockFile('ash/BUILD.gn', []),
4572 MockFile('chrome/browser/ash/BUILD.gn', []),
4575 MockFile('chrome/browser/BUILD.gn', []),
4576 MockFile('chrome/browser/BUILD.gn', ['assert(is_chromeos_ash)']),
4578 input_api = MockInputApi()
4579 input_api.files = files_in_ash
4580 errors = PRESUBMIT.CheckAssertAshOnlyCode(input_api, MockOutputApi())
4581 self.assertEqual(2, len(errors))
4583 input_api.files = other_files
4584 errors = PRESUBMIT.CheckAssertAshOnlyCode(input_api, MockOutputApi())
4585 self.assertEqual(0, len(errors))
4587 def testDoesNotErrorOnNonGNFiles(self):
4588 input_api = MockInputApi()
4590 MockFile('ash/test.h', ['assert(is_chromeos_ash)']),
4591 MockFile('chrome/browser/ash/test.cc',
4592 ['assert(is_chromeos_ash)']),
4594 errors = PRESUBMIT.CheckAssertAshOnlyCode(input_api, MockOutputApi())
4595 self.assertEqual(0, len(errors))
4597 def testDeletedFile(self):
4598 input_api = MockInputApi()
4600 MockFile('ash/BUILD.gn', []),
4601 MockFile('ash/foo/BUILD.gn', [], action='D'),
4603 errors = PRESUBMIT.CheckAssertAshOnlyCode(input_api, MockOutputApi())
4604 self.assertEqual(1, len(errors))
4606 def testDoesNotErrorWithAssertion(self):
4607 input_api = MockInputApi()
4609 MockFile('ash/BUILD.gn', ['assert(is_chromeos_ash)']),
4610 MockFile('chrome/browser/ash/BUILD.gn',
4611 ['assert(is_chromeos_ash)']),
4612 MockFile('chrome/browser/ash/BUILD.gn',
4613 ['assert(is_chromeos_ash, "test")']),
4615 errors = PRESUBMIT.CheckAssertAshOnlyCode(input_api, MockOutputApi())
4616 self.assertEqual(0, len(errors))
4619 class CheckRawPtrUsageTest(unittest.TestCase):
4620 def testAllowedCases(self):
4621 mock_input_api = MockInputApi()
4622 mock_input_api.files = [
4623 # Browser-side files are allowed.
4624 MockAffectedFile('test10/browser/foo.h', ['raw_ptr<int>']),
4625 MockAffectedFile('test11/browser/foo.cc', ['raw_ptr<int>']),
4626 MockAffectedFile('test12/blink/common/foo.cc', ['raw_ptr<int>']),
4627 MockAffectedFile('test13/blink/public/common/foo.cc', ['raw_ptr<int>']),
4628 MockAffectedFile('test14/blink/public/platform/foo.cc',
4631 # Non-C++ files are allowed.
4632 MockAffectedFile('test20/renderer/foo.md', ['raw_ptr<int>']),
4634 # Mentions in a comment are allowed.
4635 MockAffectedFile('test30/renderer/foo.cc', ['//raw_ptr<int>']),
4637 mock_output_api = MockOutputApi()
4638 errors = PRESUBMIT.CheckRawPtrUsage(mock_input_api, mock_output_api)
4639 self.assertFalse(errors)
4641 def testDisallowedCases(self):
4642 mock_input_api = MockInputApi()
4643 mock_input_api.files = [
4644 MockAffectedFile('test1/renderer/foo.h', ['raw_ptr<int>']),
4645 MockAffectedFile('test2/renderer/foo.cc', ['raw_ptr<int>']),
4646 MockAffectedFile('test3/blink/public/web/foo.cc', ['raw_ptr<int>']),
4648 mock_output_api = MockOutputApi()
4649 errors = PRESUBMIT.CheckRawPtrUsage(mock_input_api, mock_output_api)
4650 self.assertEqual(len(mock_input_api.files), len(errors))
4651 for error in errors:
4653 'raw_ptr<T> should not be used in Renderer-only code' in
4657 class AssertPythonShebangTest(unittest.TestCase):
4658 def testError(self):
4659 input_api = MockInputApi()
4661 MockFile('ash/test.py', ['#!/usr/bin/python']),
4662 MockFile('chrome/test.py', ['#!/usr/bin/python2']),
4663 MockFile('third_party/blink/test.py', ['#!/usr/bin/python3']),
4664 MockFile('empty.py', []),
4666 errors = PRESUBMIT.CheckPythonShebang(input_api, MockOutputApi())
4667 self.assertEqual(3, len(errors))
4669 def testNonError(self):
4670 input_api = MockInputApi()
4672 MockFile('chrome/browser/BUILD.gn', ['#!/usr/bin/python']),
4673 MockFile('third_party/blink/web_tests/external/test.py',
4674 ['#!/usr/bin/python2']),
4675 MockFile('third_party/test/test.py', ['#!/usr/bin/python3']),
4677 errors = PRESUBMIT.CheckPythonShebang(input_api, MockOutputApi())
4678 self.assertEqual(0, len(errors))
4680 class VerifyDcheckParentheses(unittest.TestCase):
4681 def testPermissibleUsage(self):
4682 input_api = MockInputApi()
4684 MockFile('okay1.cc', ['DCHECK_IS_ON()']),
4685 MockFile('okay2.cc', ['#if DCHECK_IS_ON()']),
4687 # Other constructs that aren't exactly `DCHECK_IS_ON()` do their
4688 # own thing at their own risk.
4689 MockFile('okay3.cc', ['PA_DCHECK_IS_ON']),
4690 MockFile('okay4.cc', ['#if PA_DCHECK_IS_ON']),
4691 MockFile('okay6.cc', ['BUILDFLAG(PA_DCHECK_IS_ON)']),
4693 errors = PRESUBMIT.CheckDCHECK_IS_ONHasBraces(input_api, MockOutputApi())
4694 self.assertEqual(0, len(errors))
4696 def testMissingParentheses(self):
4697 input_api = MockInputApi()
4699 MockFile('bad1.cc', ['DCHECK_IS_ON']),
4700 MockFile('bad2.cc', ['#if DCHECK_IS_ON']),
4701 MockFile('bad3.cc', ['DCHECK_IS_ON && foo']),
4703 errors = PRESUBMIT.CheckDCHECK_IS_ONHasBraces(input_api, MockOutputApi())
4704 self.assertEqual(3, len(errors))
4705 for error in errors:
4706 self.assertRegex(error.message, r'DCHECK_IS_ON().+parentheses')
4709 class CheckBatchAnnotation(unittest.TestCase):
4710 """Test the CheckBatchAnnotation presubmit check."""
4712 def testTruePositives(self):
4713 """Examples of when there is no @Batch or @DoNotBatch is correctly flagged.
4715 mock_input = MockInputApi()
4716 mock_input.files = [
4717 MockFile('path/OneTest.java', ['public class OneTest']),
4718 MockFile('path/TwoTest.java', ['public class TwoTest']),
4719 MockFile('path/ThreeTest.java',
4720 ['@Batch(Batch.PER_CLASS)',
4721 'import org.chromium.base.test.BaseRobolectricTestRunner;',
4722 'public class Three {']),
4723 MockFile('path/FourTest.java',
4724 ['@DoNotBatch(reason = "dummy reason 1")',
4725 'import org.chromium.base.test.BaseRobolectricTestRunner;',
4726 'public class Four {']),
4728 errors = PRESUBMIT.CheckBatchAnnotation(mock_input, MockOutputApi())
4729 self.assertEqual(2, len(errors))
4730 self.assertEqual(2, len(errors[0].items))
4731 self.assertIn('OneTest.java', errors[0].items[0])
4732 self.assertIn('TwoTest.java', errors[0].items[1])
4733 self.assertEqual(2, len(errors[1].items))
4734 self.assertIn('ThreeTest.java', errors[1].items[0])
4735 self.assertIn('FourTest.java', errors[1].items[1])
4738 def testAnnotationsPresent(self):
4739 """Examples of when there is @Batch or @DoNotBatch is correctly flagged."""
4740 mock_input = MockInputApi()
4741 mock_input.files = [
4742 MockFile('path/OneTest.java',
4743 ['@Batch(Batch.PER_CLASS)', 'public class One {']),
4744 MockFile('path/TwoTest.java',
4745 ['@DoNotBatch(reason = "dummy reasons.")', 'public class Two {'
4747 MockFile('path/ThreeTest.java',
4748 ['@Batch(Batch.PER_CLASS)',
4749 'public class Three extends BaseTestA {'],
4750 ['@Batch(Batch.PER_CLASS)',
4751 'public class Three extends BaseTestB {']),
4752 MockFile('path/FourTest.java',
4753 ['@DoNotBatch(reason = "dummy reason 1")',
4754 'public class Four extends BaseTestA {'],
4755 ['@DoNotBatch(reason = "dummy reason 2")',
4756 'public class Four extends BaseTestB {']),
4757 MockFile('path/FiveTest.java',
4758 ['import androidx.test.uiautomator.UiDevice;',
4759 'public class Five extends BaseTestA {'],
4760 ['import androidx.test.uiautomator.UiDevice;',
4761 'public class Five extends BaseTestB {']),
4762 MockFile('path/SixTest.java',
4763 ['import org.chromium.base.test.BaseRobolectricTestRunner;',
4764 'public class Six extends BaseTestA {'],
4765 ['import org.chromium.base.test.BaseRobolectricTestRunner;',
4766 'public class Six extends BaseTestB {']),
4767 MockFile('path/SevenTest.java',
4768 ['import org.robolectric.annotation.Config;',
4769 'public class Seven extends BaseTestA {'],
4770 ['import org.robolectric.annotation.Config;',
4771 'public class Seven extends BaseTestB {']),
4773 'path/OtherClass.java',
4774 ['public class OtherClass {'],
4776 MockFile('path/PRESUBMIT.py',
4777 ['@Batch(Batch.PER_CLASS)',
4778 '@DoNotBatch(reason = "dummy reason)']),
4780 errors = PRESUBMIT.CheckBatchAnnotation(mock_input, MockOutputApi())
4781 self.assertEqual(0, len(errors))
4784 class CheckMockAnnotation(unittest.TestCase):
4785 """Test the CheckMockAnnotation presubmit check."""
4787 def testTruePositives(self):
4788 """Examples of @Mock or @Spy being used and nothing should be flagged."""
4789 mock_input = MockInputApi()
4790 mock_input.files = [
4791 MockFile('path/OneTest.java', [
4792 'import a.b.c.Bar;',
4793 'import a.b.c.Foo;',
4795 'public static Foo f = new Foo();',
4796 'Mockito.mock(new Bar(a, b, c))'
4798 MockFile('path/TwoTest.java', [
4800 'import static org.mockito.Mockito.spy;',
4802 'public static FooBar<Baz> f;',
4803 'a = spy(Baz.class)'
4806 errors = PRESUBMIT.CheckMockAnnotation(mock_input, MockOutputApi())
4807 self.assertEqual(1, len(errors))
4808 self.assertEqual(2, len(errors[0].items))
4809 self.assertIn('a.b.c.Bar in path/OneTest.java', errors[0].items)
4810 self.assertIn('x.y.z.Baz in path/TwoTest.java', errors[0].items)
4812 def testTrueNegatives(self):
4813 """Examples of when we should not be flagging mock() or spy() calls."""
4814 mock_input = MockInputApi()
4815 mock_input.files = [
4816 MockFile('path/OneTest.java', [
4818 'import org.chromium.base.test.BaseRobolectricTestRunner;',
4819 'Mockito.mock(Abc.class)'
4821 MockFile('path/TwoTest.java', [
4823 'import androidx.test.uiautomator.UiDevice;',
4824 'Mockito.spy(new Def())'
4826 MockFile('path/ThreeTest.java', [
4828 'import static org.mockito.Mockito.spy;',
4830 'public static Foo f = new Abc();',
4831 'a = spy(Foo.class)'
4833 MockFile('path/FourTest.java', [
4835 'import static org.mockito.Mockito.mock;',
4837 'public static Bar b = new Abc(a, b, c, d);',
4838 ' mock(new Bar(a,b,c))'
4840 MockFile('path/FiveTest.java', [
4843 'public static Baz<abc> b;',
4844 'Mockito.mock(Baz.class)']),
4845 MockFile('path/SixTest.java', [
4847 'import android.view.View;',
4848 'import java.ArrayList;',
4849 'Mockito.spy(new View())',
4850 'Mockito.mock(ArrayList.class)'
4853 errors = PRESUBMIT.CheckMockAnnotation(mock_input, MockOutputApi())
4854 self.assertEqual(0, len(errors))
4857 class LayoutInTestsTest(unittest.TestCase):
4858 def testLayoutInTest(self):
4859 mock_input = MockInputApi()
4860 mock_input.files = [
4861 MockFile('path/to/foo_unittest.cc',
4862 [' foo->Layout();', ' bar.Layout();']),
4864 errors = PRESUBMIT.CheckNoLayoutCallsInTests(mock_input, MockOutputApi())
4865 self.assertNotEqual(0, len(errors))
4867 def testNoTriggerOnLayoutOverride(self):
4868 mock_input = MockInputApi();
4869 mock_input.files = [
4870 MockFile('path/to/foo_unittest.cc',
4871 ['class TestView: public views::View {',
4873 ' void Layout(); override {',
4874 ' views::View::Layout();',
4875 ' // perform bespoke layout',
4879 errors = PRESUBMIT.CheckNoLayoutCallsInTests(mock_input, MockOutputApi())
4880 self.assertEqual(0, len(errors))
4882 class AssertNoJsInIosTest(unittest.TestCase):
4883 def testErrorJs(self):
4884 input_api = MockInputApi()
4886 MockFile('components/feature/ios/resources/script.js', []),
4887 MockFile('ios/chrome/feature/resources/script.js', []),
4889 results = PRESUBMIT.CheckNoJsInIos(input_api, MockOutputApi())
4890 self.assertEqual(1, len(results))
4891 self.assertEqual('error', results[0].type)
4892 self.assertEqual(2, len(results[0].items))
4894 def testNonError(self):
4895 input_api = MockInputApi()
4897 MockFile('chrome/resources/script.js', []),
4898 MockFile('components/feature/ios/resources/script.ts', []),
4899 MockFile('ios/chrome/feature/resources/script.ts', []),
4900 MockFile('ios/web/feature/resources/script.ts', []),
4901 MockFile('ios/third_party/script.js', []),
4902 MockFile('third_party/ios/script.js', []),
4904 results = PRESUBMIT.CheckNoJsInIos(input_api, MockOutputApi())
4905 self.assertEqual(0, len(results))
4907 def testExistingFilesWarningOnly(self):
4908 input_api = MockInputApi()
4910 MockFile('ios/chrome/feature/resources/script.js', [], action='M'),
4911 MockFile('ios/chrome/feature/resources/script2.js', [], action='D'),
4913 results = PRESUBMIT.CheckNoJsInIos(input_api, MockOutputApi())
4914 self.assertEqual(1, len(results))
4915 self.assertEqual('warning', results[0].type)
4916 self.assertEqual(1, len(results[0].items))
4918 def testMovedScriptWarningOnly(self):
4919 input_api = MockInputApi()
4921 MockFile('ios/chrome/feature/resources/script.js', [], action='D'),
4922 MockFile('ios/chrome/renamed_feature/resources/script.js', [], action='A'),
4924 results = PRESUBMIT.CheckNoJsInIos(input_api, MockOutputApi())
4925 self.assertEqual(1, len(results))
4926 self.assertEqual('warning', results[0].type)
4927 self.assertEqual(1, len(results[0].items))
4929 class CheckNoAbbreviationInPngFileNameTest(unittest.TestCase):
4930 def testHasAbbreviation(self):
4931 """test png file names with abbreviation that fails the check"""
4932 input_api = MockInputApi()
4934 MockFile('image_a.png', [], action='A'),
4935 MockFile('image_a_.png', [], action='A'),
4936 MockFile('image_a_name.png', [], action='A'),
4937 MockFile('chrome/ui/feature_name/resources/image_a.png', [], action='A'),
4938 MockFile('chrome/ui/feature_name/resources/image_a_.png', [], action='A'),
4939 MockFile('chrome/ui/feature_name/resources/image_a_name.png', [], action='A'),
4941 results = PRESUBMIT.CheckNoAbbreviationInPngFileName(input_api, MockOutputApi())
4942 self.assertEqual(1, len(results))
4943 self.assertEqual('error', results[0].type)
4944 self.assertEqual(len(input_api.files), len(results[0].items))
4946 def testNoAbbreviation(self):
4947 """test png file names without abbreviation that passes the check"""
4948 input_api = MockInputApi()
4950 MockFile('a.png', [], action='A'),
4951 MockFile('_a.png', [], action='A'),
4952 MockFile('image.png', [], action='A'),
4953 MockFile('image_ab_.png', [], action='A'),
4954 MockFile('image_ab_name.png', [], action='A'),
4955 # These paths used to fail because `feature_a_name` matched the regex by mistake.
4956 # They should pass now because the path components ahead of the file name are ignored in the check.
4957 MockFile('chrome/ui/feature_a_name/resources/a.png', [], action='A'),
4958 MockFile('chrome/ui/feature_a_name/resources/_a.png', [], action='A'),
4959 MockFile('chrome/ui/feature_a_name/resources/image.png', [], action='A'),
4960 MockFile('chrome/ui/feature_a_name/resources/image_ab_.png', [], action='A'),
4961 MockFile('chrome/ui/feature_a_name/resources/image_ab_name.png', [], action='A'),
4963 results = PRESUBMIT.CheckNoAbbreviationInPngFileName(input_api, MockOutputApi())
4964 self.assertEqual(0, len(results))
4966 if __name__ == '__main__':