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 '#if TARGET_IPHONE_SIMULATOR']
189 errors = PRESUBMIT._CheckForInvalidIfDefinedMacrosInFile(
190 MockInputApi(), MockFile('some/path/source.cc', lines))
191 self.assertEqual(0, len(errors))
194 class CheckNoUNIT_TESTInSourceFilesTest(unittest.TestCase):
195 def testUnitTestMacros(self):
196 lines = ['#if defined(UNIT_TEST)',
197 '#if defined UNIT_TEST',
198 '#if !defined(UNIT_TEST)',
199 '#elif defined(UNIT_TEST)',
201 ' # ifdef UNIT_TEST',
203 '# if defined(VALID) || defined(UNIT_TEST)',
204 '# if defined(UNIT_TEST) && defined(VALID)',
205 '# else // defined(UNIT_TEST)',
206 '#endif // defined(UNIT_TEST)']
207 errors = PRESUBMIT._CheckNoUNIT_TESTInSourceFiles(
208 MockInputApi(), MockFile('some/path/source.cc', lines))
209 self.assertEqual(len(lines), len(errors))
211 def testNotUnitTestMacros(self):
212 lines = ['// Comment about "#if defined(UNIT_TEST)"',
213 '/* Comment about #if defined(UNIT_TEST)" */',
214 '#ifndef UNIT_TEST_H',
215 '#define UNIT_TEST_H',
216 '#ifndef TEST_UNIT_TEST',
217 '#define TEST_UNIT_TEST',
218 '#if defined(_UNIT_TEST)',
219 '#if defined(UNIT_TEST_)',
222 '#ifndef _UNIT_TEST',
223 '#ifndef UNIT_TEST_']
224 errors = PRESUBMIT._CheckNoUNIT_TESTInSourceFiles(
225 MockInputApi(), MockFile('some/path/source.cc', lines))
226 self.assertEqual(0, len(errors))
228 class CheckAddedDepsHaveTestApprovalsTest(unittest.TestCase):
230 def calculate(self, old_include_rules, old_specific_include_rules,
231 new_include_rules, new_specific_include_rules):
232 return PRESUBMIT._CalculateAddedDeps(
233 os.path, 'include_rules = %r\nspecific_include_rules = %r' % (
234 old_include_rules, old_specific_include_rules),
235 'include_rules = %r\nspecific_include_rules = %r' % (
236 new_include_rules, new_specific_include_rules))
238 def testCalculateAddedDeps(self):
239 old_include_rules = [
248 old_specific_include_rules = {
254 new_include_rules = [
261 '+grit/generated_resources.h",',
265 '+' + os.path.join('third_party', 'WebKit'),
267 new_specific_include_rules = {
277 os.path.join('chrome', 'DEPS'),
278 os.path.join('gpu', 'DEPS'),
279 os.path.join('components', 'DEPS'),
280 os.path.join('policy', 'DEPS'),
281 os.path.join('third_party', 'WebKit', 'DEPS'),
285 self.calculate(old_include_rules, old_specific_include_rules,
286 new_include_rules, new_specific_include_rules))
288 def testCalculateAddedDepsIgnoresPermutations(self):
289 old_include_rules = [
293 new_include_rules = [
297 self.assertEqual(set(),
298 self.calculate(old_include_rules, {}, new_include_rules,
302 class JSONParsingTest(unittest.TestCase):
303 def testSuccess(self):
304 input_api = MockInputApi()
305 filename = 'valid_json.json'
306 contents = ['// This is a comment.',
308 ' "key1": ["value1", "value2"],',
309 ' "key2": 3 // This is an inline comment.',
312 input_api.files = [MockFile(filename, contents)]
313 self.assertEqual(None,
314 PRESUBMIT._GetJSONParseError(input_api, filename))
316 def testFailure(self):
317 input_api = MockInputApi()
319 ('invalid_json_1.json',
321 'Expecting property name'),
322 ('invalid_json_2.json',
324 '{ "hello": "world }'],
325 'Unterminated string starting at:'),
326 ('invalid_json_3.json',
327 ['{ "a": "b", "c": "d", }'],
328 'Expecting property name'),
329 ('invalid_json_4.json',
330 ['{ "a": "b" "c": "d" }'],
331 "Expecting ',' delimiter:"),
334 input_api.files = [MockFile(filename, contents)
335 for (filename, contents, _) in test_data]
337 for (filename, _, expected_error) in test_data:
338 actual_error = PRESUBMIT._GetJSONParseError(input_api, filename)
339 self.assertTrue(expected_error in str(actual_error),
340 "'%s' not found in '%s'" % (expected_error, actual_error))
342 def testNoEatComments(self):
343 input_api = MockInputApi()
344 file_with_comments = 'file_with_comments.json'
345 contents_with_comments = ['// This is a comment.',
347 ' "key1": ["value1", "value2"],',
348 ' "key2": 3 // This is an inline comment.',
351 file_without_comments = 'file_without_comments.json'
352 contents_without_comments = ['{',
353 ' "key1": ["value1", "value2"],',
357 input_api.files = [MockFile(file_with_comments, contents_with_comments),
358 MockFile(file_without_comments,
359 contents_without_comments)]
361 self.assertNotEqual(None,
362 str(PRESUBMIT._GetJSONParseError(input_api,
364 eat_comments=False)))
365 self.assertEqual(None,
366 PRESUBMIT._GetJSONParseError(input_api,
367 file_without_comments,
371 class IDLParsingTest(unittest.TestCase):
372 def testSuccess(self):
373 input_api = MockInputApi()
374 filename = 'valid_idl_basics.idl'
375 contents = ['// Tests a valid IDL file.',
376 'namespace idl_basics {',
382 ' dictionary MyType1 {',
386 ' callback Callback1 = void();',
387 ' callback Callback2 = void(long x);',
388 ' callback Callback3 = void(MyType1 arg);',
389 ' callback Callback4 = void(EnumType type);',
391 ' interface Functions {',
392 ' static void function1();',
393 ' static void function2(long x);',
394 ' static void function3(MyType1 arg);',
395 ' static void function4(Callback1 cb);',
396 ' static void function5(Callback2 cb);',
397 ' static void function6(Callback3 cb);',
398 ' static void function7(Callback4 cb);',
401 ' interface Events {',
402 ' static void onFoo1();',
403 ' static void onFoo2(long x);',
404 ' static void onFoo2(MyType1 arg);',
405 ' static void onFoo3(EnumType type);',
409 input_api.files = [MockFile(filename, contents)]
410 self.assertEqual(None,
411 PRESUBMIT._GetIDLParseError(input_api, filename))
413 def testFailure(self):
414 input_api = MockInputApi()
416 ('invalid_idl_1.idl',
423 'Unexpected "{" after keyword "dictionary".\n'),
424 # TODO(yoz): Disabled because it causes the IDL parser to hang.
425 # See crbug.com/363830.
426 # ('invalid_idl_2.idl',
427 # (['namespace test {',
428 # ' dictionary MissingSemicolon {',
433 # 'Unexpected symbol DOMString after symbol a.'),
434 ('invalid_idl_3.idl',
437 ' enum MissingComma {',
442 'Unexpected symbol name2 after symbol name1.'),
443 ('invalid_idl_4.idl',
446 ' enum TrailingComma {',
451 'Trailing comma in block.'),
452 ('invalid_idl_5.idl',
455 ' callback Callback1 = void(;',
457 'Unexpected ";" after "(".'),
458 ('invalid_idl_6.idl',
461 ' callback Callback1 = void(long );',
463 'Unexpected ")" after symbol long.'),
464 ('invalid_idl_7.idl',
467 ' interace Events {',
468 ' static void onFoo1();',
471 'Unexpected symbol Events after symbol interace.'),
472 ('invalid_idl_8.idl',
475 ' interface NotEvent {',
476 ' static void onFoo1();',
479 'Did not process Interface Interface(NotEvent)'),
480 ('invalid_idl_9.idl',
484 ' static void function1();',
487 'Interface missing name.'),
490 input_api.files = [MockFile(filename, contents)
491 for (filename, contents, _) in test_data]
493 for (filename, _, expected_error) in test_data:
494 actual_error = PRESUBMIT._GetIDLParseError(input_api, filename)
495 self.assertTrue(expected_error in str(actual_error),
496 "'%s' not found in '%s'" % (expected_error, actual_error))
499 class UserMetricsActionTest(unittest.TestCase):
500 def testUserMetricsActionInActions(self):
501 input_api = MockInputApi()
502 file_with_user_action = 'file_with_user_action.cc'
503 contents_with_user_action = [
504 'base::UserMetricsAction("AboutChrome")'
507 input_api.files = [MockFile(file_with_user_action,
508 contents_with_user_action)]
511 [], PRESUBMIT.CheckUserActionUpdate(input_api, MockOutputApi()))
513 def testUserMetricsActionNotAddedToActions(self):
514 input_api = MockInputApi()
515 file_with_user_action = 'file_with_user_action.cc'
516 contents_with_user_action = [
517 'base::UserMetricsAction("NotInActionsXml")'
520 input_api.files = [MockFile(file_with_user_action,
521 contents_with_user_action)]
523 output = PRESUBMIT.CheckUserActionUpdate(input_api, MockOutputApi())
525 ('File %s line %d: %s is missing in '
526 'tools/metrics/actions/actions.xml. Please run '
527 'tools/metrics/actions/extract_actions.py to update.'
528 % (file_with_user_action, 1, 'NotInActionsXml')),
531 def testUserMetricsActionInTestFile(self):
532 input_api = MockInputApi()
533 file_with_user_action = 'file_with_user_action_unittest.cc'
534 contents_with_user_action = [
535 'base::UserMetricsAction("NotInActionsXml")'
538 input_api.files = [MockFile(file_with_user_action,
539 contents_with_user_action)]
542 [], PRESUBMIT.CheckUserActionUpdate(input_api, MockOutputApi()))
545 class PydepsNeedsUpdatingTest(unittest.TestCase):
547 def __init__(self, stdout):
548 self.stdout = io.StringIO(stdout)
553 class MockSubprocess:
554 CalledProcessError = subprocess.CalledProcessError
558 self._popen_func = None
560 def SetPopenCallback(self, func):
561 self._popen_func = func
563 def Popen(self, cmd, *args, **kwargs):
564 return PydepsNeedsUpdatingTest.MockPopen(self._popen_func(cmd))
566 def _MockParseGclientArgs(self, is_android=True):
567 return lambda: {'checkout_android': 'true' if is_android else 'false' }
570 mock_all_pydeps = ['A.pydeps', 'B.pydeps', 'D.pydeps']
571 self.old_ALL_PYDEPS_FILES = PRESUBMIT._ALL_PYDEPS_FILES
572 PRESUBMIT._ALL_PYDEPS_FILES = mock_all_pydeps
573 mock_android_pydeps = ['D.pydeps']
574 self.old_ANDROID_SPECIFIC_PYDEPS_FILES = (
575 PRESUBMIT._ANDROID_SPECIFIC_PYDEPS_FILES)
576 PRESUBMIT._ANDROID_SPECIFIC_PYDEPS_FILES = mock_android_pydeps
577 self.old_ParseGclientArgs = PRESUBMIT._ParseGclientArgs
578 PRESUBMIT._ParseGclientArgs = self._MockParseGclientArgs()
579 self.mock_input_api = MockInputApi()
580 self.mock_output_api = MockOutputApi()
581 self.mock_input_api.subprocess = PydepsNeedsUpdatingTest.MockSubprocess()
582 self.checker = PRESUBMIT.PydepsChecker(self.mock_input_api, mock_all_pydeps)
583 self.checker._file_cache = {
584 'A.pydeps': '# Generated by:\n# CMD --output A.pydeps A\nA.py\nC.py\n',
585 'B.pydeps': '# Generated by:\n# CMD --output B.pydeps B\nB.py\nC.py\n',
586 'D.pydeps': '# Generated by:\n# CMD --output D.pydeps D\nD.py\n',
590 PRESUBMIT._ALL_PYDEPS_FILES = self.old_ALL_PYDEPS_FILES
591 PRESUBMIT._ANDROID_SPECIFIC_PYDEPS_FILES = (
592 self.old_ANDROID_SPECIFIC_PYDEPS_FILES)
593 PRESUBMIT._ParseGclientArgs = self.old_ParseGclientArgs
596 return PRESUBMIT.CheckPydepsNeedsUpdating(self.mock_input_api,
597 self.mock_output_api,
598 checker_for_tests=self.checker)
600 def testAddedPydep(self):
601 # PRESUBMIT.CheckPydepsNeedsUpdating is only implemented for Linux.
602 if not self.mock_input_api.platform.startswith('linux'):
605 self.mock_input_api.files = [
606 MockAffectedFile('new.pydeps', [], action='A'),
609 self.mock_input_api.CreateMockFileInPath(
610 [x.LocalPath() for x in self.mock_input_api.AffectedFiles(
611 include_deletes=True)])
612 results = self._RunCheck()
613 self.assertEqual(1, len(results))
614 self.assertIn('PYDEPS_FILES', str(results[0]))
616 def testPydepNotInSrc(self):
617 self.mock_input_api.files = [
618 MockAffectedFile('new.pydeps', [], action='A'),
620 self.mock_input_api.CreateMockFileInPath([])
621 results = self._RunCheck()
622 self.assertEqual(0, len(results))
624 def testRemovedPydep(self):
625 # PRESUBMIT.CheckPydepsNeedsUpdating is only implemented for Linux.
626 if not self.mock_input_api.platform.startswith('linux'):
629 self.mock_input_api.files = [
630 MockAffectedFile(PRESUBMIT._ALL_PYDEPS_FILES[0], [], action='D'),
632 self.mock_input_api.CreateMockFileInPath(
633 [x.LocalPath() for x in self.mock_input_api.AffectedFiles(
634 include_deletes=True)])
635 results = self._RunCheck()
636 self.assertEqual(1, len(results))
637 self.assertIn('PYDEPS_FILES', str(results[0]))
639 def testRandomPyIgnored(self):
640 # PRESUBMIT.CheckPydepsNeedsUpdating is only implemented for Linux.
641 if not self.mock_input_api.platform.startswith('linux'):
644 self.mock_input_api.files = [
645 MockAffectedFile('random.py', []),
648 results = self._RunCheck()
649 self.assertEqual(0, len(results), 'Unexpected results: %r' % results)
651 def testRelevantPyNoChange(self):
652 # PRESUBMIT.CheckPydepsNeedsUpdating is only implemented for Linux.
653 if not self.mock_input_api.platform.startswith('linux'):
656 self.mock_input_api.files = [
657 MockAffectedFile('A.py', []),
660 def popen_callback(cmd):
661 self.assertEqual('CMD --output A.pydeps A --output ""', cmd)
662 return self.checker._file_cache['A.pydeps']
664 self.mock_input_api.subprocess.SetPopenCallback(popen_callback)
666 results = self._RunCheck()
667 self.assertEqual(0, len(results), 'Unexpected results: %r' % results)
669 def testRelevantPyOneChange(self):
670 # PRESUBMIT.CheckPydepsNeedsUpdating is only implemented for Linux.
671 if not self.mock_input_api.platform.startswith('linux'):
674 self.mock_input_api.files = [
675 MockAffectedFile('A.py', []),
678 def popen_callback(cmd):
679 self.assertEqual('CMD --output A.pydeps A --output ""', cmd)
680 return 'changed data'
682 self.mock_input_api.subprocess.SetPopenCallback(popen_callback)
684 results = self._RunCheck()
685 self.assertEqual(1, len(results))
686 # Check that --output "" is not included.
687 self.assertNotIn('""', str(results[0]))
688 self.assertIn('File is stale', str(results[0]))
690 def testRelevantPyTwoChanges(self):
691 # PRESUBMIT.CheckPydepsNeedsUpdating is only implemented for Linux.
692 if not self.mock_input_api.platform.startswith('linux'):
695 self.mock_input_api.files = [
696 MockAffectedFile('C.py', []),
699 def popen_callback(cmd):
700 return 'changed data'
702 self.mock_input_api.subprocess.SetPopenCallback(popen_callback)
704 results = self._RunCheck()
705 self.assertEqual(2, len(results))
706 self.assertIn('File is stale', str(results[0]))
707 self.assertIn('File is stale', str(results[1]))
709 def testRelevantAndroidPyInNonAndroidCheckout(self):
710 # PRESUBMIT.CheckPydepsNeedsUpdating is only implemented for Linux.
711 if not self.mock_input_api.platform.startswith('linux'):
714 self.mock_input_api.files = [
715 MockAffectedFile('D.py', []),
718 def popen_callback(cmd):
719 self.assertEqual('CMD --output D.pydeps D --output ""', cmd)
720 return 'changed data'
722 self.mock_input_api.subprocess.SetPopenCallback(popen_callback)
723 PRESUBMIT._ParseGclientArgs = self._MockParseGclientArgs(is_android=False)
725 results = self._RunCheck()
726 self.assertEqual(1, len(results))
727 self.assertIn('Android', str(results[0]))
728 self.assertIn('D.pydeps', str(results[0]))
730 def testGnPathsAndMissingOutputFlag(self):
731 # PRESUBMIT.CheckPydepsNeedsUpdating is only implemented for Linux.
732 if not self.mock_input_api.platform.startswith('linux'):
735 self.checker._file_cache = {
736 'A.pydeps': '# Generated by:\n# CMD --gn-paths A\n//A.py\n//C.py\n',
737 'B.pydeps': '# Generated by:\n# CMD --gn-paths B\n//B.py\n//C.py\n',
738 'D.pydeps': '# Generated by:\n# CMD --gn-paths D\n//D.py\n',
741 self.mock_input_api.files = [
742 MockAffectedFile('A.py', []),
745 def popen_callback(cmd):
746 self.assertEqual('CMD --gn-paths A --output A.pydeps --output ""', cmd)
747 return 'changed data'
749 self.mock_input_api.subprocess.SetPopenCallback(popen_callback)
751 results = self._RunCheck()
752 self.assertEqual(1, len(results))
753 self.assertIn('File is stale', str(results[0]))
756 class IncludeGuardTest(unittest.TestCase):
757 def testIncludeGuardChecks(self):
758 mock_input_api = MockInputApi()
759 mock_output_api = MockOutputApi()
760 mock_input_api.files = [
761 MockAffectedFile('content/browser/thing/foo.h', [
763 '#ifndef CONTENT_BROWSER_THING_FOO_H_',
764 '#define CONTENT_BROWSER_THING_FOO_H_',
765 'struct McBoatFace;',
766 '#endif // CONTENT_BROWSER_THING_FOO_H_',
768 MockAffectedFile('content/browser/thing/bar.h', [
769 '#ifndef CONTENT_BROWSER_THING_BAR_H_',
770 '#define CONTENT_BROWSER_THING_BAR_H_',
771 'namespace content {',
772 '#endif // CONTENT_BROWSER_THING_BAR_H_',
773 '} // namespace content',
775 MockAffectedFile('content/browser/test1.h', [
776 'namespace content {',
777 '} // namespace content',
779 MockAffectedFile('content\\browser\\win.h', [
780 '#ifndef CONTENT_BROWSER_WIN_H_',
781 '#define CONTENT_BROWSER_WIN_H_',
782 'struct McBoatFace;',
783 '#endif // CONTENT_BROWSER_WIN_H_',
785 MockAffectedFile('content/browser/test2.h', [
787 '#ifndef CONTENT_BROWSER_TEST2_H_',
788 'struct McBoatFace;',
789 '#endif // CONTENT_BROWSER_TEST2_H_',
791 MockAffectedFile('content/browser/internal.h', [
793 '#ifndef CONTENT_BROWSER_INTERNAL_H_',
794 '#define CONTENT_BROWSER_INTERNAL_H_',
796 '#ifndef INTERNAL_CONTENT_BROWSER_INTERNAL_H_',
797 '#define INTERNAL_CONTENT_BROWSER_INTERNAL_H_',
798 'namespace internal {',
799 '} // namespace internal',
800 '#endif // INTERNAL_CONTENT_BROWSER_THING_BAR_H_',
801 'namespace content {',
802 '} // namespace content',
803 '#endif // CONTENT_BROWSER_THING_BAR_H_',
805 MockAffectedFile('content/browser/thing/foo.cc', [
806 '// This is a non-header.',
808 MockAffectedFile('content/browser/disabled.h', [
809 '// no-include-guard-because-multiply-included',
810 'struct McBoatFace;',
812 # New files don't allow misspelled include guards.
813 MockAffectedFile('content/browser/spleling.h', [
814 '#ifndef CONTENT_BROWSER_SPLLEING_H_',
815 '#define CONTENT_BROWSER_SPLLEING_H_',
816 'struct McBoatFace;',
817 '#endif // CONTENT_BROWSER_SPLLEING_H_',
819 # New files don't allow + in include guards.
820 MockAffectedFile('content/browser/foo+bar.h', [
821 '#ifndef CONTENT_BROWSER_FOO+BAR_H_',
822 '#define CONTENT_BROWSER_FOO+BAR_H_',
823 'struct McBoatFace;',
824 '#endif // CONTENT_BROWSER_FOO+BAR_H_',
826 # Old files allow misspelled include guards (for now).
827 MockAffectedFile('chrome/old.h', [
829 '#ifndef CHROME_ODL_H_',
830 '#define CHROME_ODL_H_',
831 '#endif // CHROME_ODL_H_',
834 '#ifndef CHROME_ODL_H_',
835 '#define CHROME_ODL_H_',
836 '#endif // CHROME_ODL_H_',
838 # Using a Blink style include guard outside Blink is wrong.
839 MockAffectedFile('content/NotInBlink.h', [
840 '#ifndef NotInBlink_h',
841 '#define NotInBlink_h',
842 'struct McBoatFace;',
843 '#endif // NotInBlink_h',
845 # Using a Blink style include guard in Blink is no longer ok.
846 MockAffectedFile('third_party/blink/InBlink.h', [
849 'struct McBoatFace;',
850 '#endif // InBlink_h',
852 # Using a bad include guard in Blink is not ok.
853 MockAffectedFile('third_party/blink/AlsoInBlink.h', [
854 '#ifndef WrongInBlink_h',
855 '#define WrongInBlink_h',
856 'struct McBoatFace;',
857 '#endif // WrongInBlink_h',
859 # Using a bad include guard in Blink is not supposed to be accepted even
860 # if it's an old file. However the current presubmit has accepted this
862 MockAffectedFile('third_party/blink/StillInBlink.h', [
864 '#ifndef AcceptedInBlink_h',
865 '#define AcceptedInBlink_h',
866 'struct McBoatFace;',
867 '#endif // AcceptedInBlink_h',
870 '#ifndef AcceptedInBlink_h',
871 '#define AcceptedInBlink_h',
872 'struct McBoatFace;',
873 '#endif // AcceptedInBlink_h',
875 # Using a non-Chromium include guard in third_party
876 # (outside blink) is accepted.
877 MockAffectedFile('third_party/foo/some_file.h', [
878 '#ifndef REQUIRED_RPCNDR_H_',
879 '#define REQUIRED_RPCNDR_H_',
880 'struct SomeFileFoo;',
881 '#endif // REQUIRED_RPCNDR_H_',
883 # Not having proper include guard in *_message_generator.h
884 # for old IPC messages is allowed.
885 MockAffectedFile('content/common/content_message_generator.h', [
886 '#undef CONTENT_COMMON_FOO_MESSAGES_H_',
887 '#include "content/common/foo_messages.h"',
888 '#ifndef CONTENT_COMMON_FOO_MESSAGES_H_',
889 '#error "Failed to include content/common/foo_messages.h"',
893 msgs = PRESUBMIT.CheckForIncludeGuards(
894 mock_input_api, mock_output_api)
895 expected_fail_count = 8
896 self.assertEqual(expected_fail_count, len(msgs),
897 'Expected %d items, found %d: %s'
898 % (expected_fail_count, len(msgs), msgs))
899 self.assertEqual(msgs[0].items, ['content/browser/thing/bar.h'])
900 self.assertEqual(msgs[0].message,
901 'Include guard CONTENT_BROWSER_THING_BAR_H_ '
902 'not covering the whole file')
904 self.assertIn('content/browser/test1.h', msgs[1].message)
905 self.assertIn('Recommended name: CONTENT_BROWSER_TEST1_H_',
908 self.assertEqual(msgs[2].items, ['content/browser/test2.h:3'])
909 self.assertEqual(msgs[2].message,
910 'Missing "#define CONTENT_BROWSER_TEST2_H_" for '
913 self.assertEqual(msgs[3].items, ['content/browser/spleling.h:1'])
914 self.assertEqual(msgs[3].message,
915 'Header using the wrong include guard name '
916 'CONTENT_BROWSER_SPLLEING_H_')
918 self.assertIn('content/browser/foo+bar.h', msgs[4].message)
919 self.assertIn('Recommended name: CONTENT_BROWSER_FOO_BAR_H_',
922 self.assertEqual(msgs[5].items, ['content/NotInBlink.h:1'])
923 self.assertEqual(msgs[5].message,
924 'Header using the wrong include guard name '
927 self.assertEqual(msgs[6].items, ['third_party/blink/InBlink.h:1'])
928 self.assertEqual(msgs[6].message,
929 'Header using the wrong include guard name '
932 self.assertEqual(msgs[7].items, ['third_party/blink/AlsoInBlink.h:1'])
933 self.assertEqual(msgs[7].message,
934 'Header using the wrong include guard name '
937 class AccessibilityRelnotesFieldTest(unittest.TestCase):
938 def testRelnotesPresent(self):
939 mock_input_api = MockInputApi()
940 mock_output_api = MockOutputApi()
942 mock_input_api.files = [MockAffectedFile('ui/accessibility/foo.bar', [''])]
943 mock_input_api.change.DescriptionText = lambda : 'Commit description'
944 mock_input_api.change.footers['AX-Relnotes'] = [
945 'Important user facing change']
947 msgs = PRESUBMIT.CheckAccessibilityRelnotesField(
948 mock_input_api, mock_output_api)
949 self.assertEqual(0, len(msgs),
950 'Expected %d messages, found %d: %s'
951 % (0, len(msgs), msgs))
953 def testRelnotesMissingFromAccessibilityChange(self):
954 mock_input_api = MockInputApi()
955 mock_output_api = MockOutputApi()
957 mock_input_api.files = [
958 MockAffectedFile('some/file', ['']),
959 MockAffectedFile('ui/accessibility/foo.bar', ['']),
960 MockAffectedFile('some/other/file', [''])
962 mock_input_api.change.DescriptionText = lambda : 'Commit description'
964 msgs = PRESUBMIT.CheckAccessibilityRelnotesField(
965 mock_input_api, mock_output_api)
966 self.assertEqual(1, len(msgs),
967 'Expected %d messages, found %d: %s'
968 % (1, len(msgs), msgs))
969 self.assertTrue("Missing 'AX-Relnotes:' field" in msgs[0].message,
970 'Missing AX-Relnotes field message not found in errors')
972 # The relnotes footer is not required for changes which do not touch any
973 # accessibility directories.
974 def testIgnoresNonAccessibilityCode(self):
975 mock_input_api = MockInputApi()
976 mock_output_api = MockOutputApi()
978 mock_input_api.files = [
979 MockAffectedFile('some/file', ['']),
980 MockAffectedFile('some/other/file', [''])
982 mock_input_api.change.DescriptionText = lambda : 'Commit description'
984 msgs = PRESUBMIT.CheckAccessibilityRelnotesField(
985 mock_input_api, mock_output_api)
986 self.assertEqual(0, len(msgs),
987 'Expected %d messages, found %d: %s'
988 % (0, len(msgs), msgs))
990 # Test that our presubmit correctly raises an error for a set of known paths.
991 def testExpectedPaths(self):
993 "chrome/browser/accessibility/foo.py",
994 "chrome/browser/ash/arc/accessibility/foo.cc",
995 "chrome/browser/ui/views/accessibility/foo.h",
996 "chrome/browser/extensions/api/automation/foo.h",
997 "chrome/browser/extensions/api/automation_internal/foo.cc",
998 "chrome/renderer/extensions/accessibility_foo.h",
999 "chrome/tests/data/accessibility/foo.html",
1000 "content/browser/accessibility/foo.cc",
1001 "content/renderer/accessibility/foo.h",
1002 "content/tests/data/accessibility/foo.cc",
1003 "extensions/renderer/api/automation/foo.h",
1004 "ui/accessibility/foo/bar/baz.cc",
1005 "ui/views/accessibility/foo/bar/baz.h",
1008 for testFile in filesToTest:
1009 mock_input_api = MockInputApi()
1010 mock_output_api = MockOutputApi()
1012 mock_input_api.files = [
1013 MockAffectedFile(testFile, [''])
1015 mock_input_api.change.DescriptionText = lambda : 'Commit description'
1017 msgs = PRESUBMIT.CheckAccessibilityRelnotesField(
1018 mock_input_api, mock_output_api)
1019 self.assertEqual(1, len(msgs),
1020 'Expected %d messages, found %d: %s, for file %s'
1021 % (1, len(msgs), msgs, testFile))
1022 self.assertTrue("Missing 'AX-Relnotes:' field" in msgs[0].message,
1023 ('Missing AX-Relnotes field message not found in errors '
1024 ' for file %s' % (testFile)))
1026 # Test that AX-Relnotes field can appear in the commit description (as long
1027 # as it appears at the beginning of a line).
1028 def testRelnotesInCommitDescription(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: solves all accessibility issues forever')
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 # Test that we don't match AX-Relnotes if it appears in the middle of a line.
1045 def testRelnotesMustAppearAtBeginningOfLine(self):
1046 mock_input_api = MockInputApi()
1047 mock_output_api = MockOutputApi()
1049 mock_input_api.files = [
1050 MockAffectedFile('ui/accessibility/foo.bar', ['']),
1052 mock_input_api.change.DescriptionText = lambda : ('Description:\n' +
1053 'This change has no AX-Relnotes: we should print a warning')
1055 msgs = PRESUBMIT.CheckAccessibilityRelnotesField(
1056 mock_input_api, mock_output_api)
1057 self.assertTrue("Missing 'AX-Relnotes:' field" in msgs[0].message,
1058 'Missing AX-Relnotes field message not found in errors')
1060 # Tests that the AX-Relnotes field can be lowercase and use a '=' in place
1062 def testRelnotesLowercaseWithEqualSign(self):
1063 mock_input_api = MockInputApi()
1064 mock_output_api = MockOutputApi()
1066 mock_input_api.files = [
1067 MockAffectedFile('ui/accessibility/foo.bar', ['']),
1069 mock_input_api.change.DescriptionText = lambda : ('Description:\n' +
1070 'ax-relnotes= this is a valid format for accessibility relnotes')
1072 msgs = PRESUBMIT.CheckAccessibilityRelnotesField(
1073 mock_input_api, mock_output_api)
1074 self.assertEqual(0, len(msgs),
1075 'Expected %d messages, found %d: %s'
1076 % (0, len(msgs), msgs))
1078 class AccessibilityEventsTestsAreIncludedForAndroidTest(unittest.TestCase):
1079 # Test that no warning is raised when the Android file is also modified.
1080 def testAndroidChangeIncluded(self):
1081 mock_input_api = MockInputApi()
1083 mock_input_api.files = [
1085 'content/test/data/accessibility/event/foo-expected-mac.txt',
1088 'accessibility/WebContentsAccessibilityEventsTest.java',
1092 msgs = PRESUBMIT.CheckAccessibilityEventsTestsAreIncludedForAndroid(
1093 mock_input_api, MockOutputApi())
1094 self.assertEqual(0, len(msgs),
1095 'Expected %d messages, found %d: %s'
1096 % (0, len(msgs), msgs))
1098 # Test that Android change is not required when no html file is added/removed.
1099 def testIgnoreNonHtmlFiles(self):
1100 mock_input_api = MockInputApi()
1102 mock_input_api.files = [
1103 MockAffectedFile('content/test/data/accessibility/event/foo.txt',
1105 MockAffectedFile('content/test/data/accessibility/event/foo.cc',
1107 MockAffectedFile('content/test/data/accessibility/event/foo.h',
1109 MockAffectedFile('content/test/data/accessibility/event/foo.py',
1113 msgs = PRESUBMIT.CheckAccessibilityEventsTestsAreIncludedForAndroid(
1114 mock_input_api, MockOutputApi())
1115 self.assertEqual(0, len(msgs),
1116 'Expected %d messages, found %d: %s'
1117 % (0, len(msgs), msgs))
1119 # Test that Android change is not required for unrelated html files.
1120 def testIgnoreNonRelatedHtmlFiles(self):
1121 mock_input_api = MockInputApi()
1123 mock_input_api.files = [
1124 MockAffectedFile('content/test/data/accessibility/aria/foo.html',
1126 MockAffectedFile('content/test/data/accessibility/html/foo.html',
1128 MockAffectedFile('chrome/tests/data/accessibility/foo.html',
1132 msgs = PRESUBMIT.CheckAccessibilityEventsTestsAreIncludedForAndroid(
1133 mock_input_api, MockOutputApi())
1134 self.assertEqual(0, len(msgs),
1135 'Expected %d messages, found %d: %s'
1136 % (0, len(msgs), msgs))
1138 # Test that only modifying an html file will not trigger the warning.
1139 def testIgnoreModifiedFiles(self):
1140 mock_input_api = MockInputApi()
1142 mock_input_api.files = [
1144 'content/test/data/accessibility/event/foo-expected-win.txt',
1148 msgs = PRESUBMIT.CheckAccessibilityEventsTestsAreIncludedForAndroid(
1149 mock_input_api, MockOutputApi())
1150 self.assertEqual(0, len(msgs),
1151 'Expected %d messages, found %d: %s'
1152 % (0, len(msgs), msgs))
1154 class AccessibilityTreeTestsAreIncludedForAndroidTest(unittest.TestCase):
1155 # Test that no warning is raised when the Android file is also modified.
1156 def testAndroidChangeIncluded(self):
1157 mock_input_api = MockInputApi()
1159 mock_input_api.files = [
1160 MockAffectedFile('content/test/data/accessibility/aria/foo.html',
1163 'accessibility/WebContentsAccessibilityTreeTest.java',
1167 msgs = PRESUBMIT.CheckAccessibilityTreeTestsAreIncludedForAndroid(
1168 mock_input_api, MockOutputApi())
1169 self.assertEqual(0, len(msgs),
1170 'Expected %d messages, found %d: %s'
1171 % (0, len(msgs), msgs))
1173 # Test that no warning is raised when the Android file is also modified.
1174 def testAndroidChangeIncludedManyFiles(self):
1175 mock_input_api = MockInputApi()
1177 mock_input_api.files = [
1178 MockAffectedFile('content/test/data/accessibility/accname/foo.html',
1180 MockAffectedFile('content/test/data/accessibility/aria/foo.html',
1182 MockAffectedFile('content/test/data/accessibility/css/foo.html',
1184 MockAffectedFile('content/test/data/accessibility/html/foo.html',
1187 'accessibility/WebContentsAccessibilityTreeTest.java',
1191 msgs = PRESUBMIT.CheckAccessibilityTreeTestsAreIncludedForAndroid(
1192 mock_input_api, MockOutputApi())
1193 self.assertEqual(0, len(msgs),
1194 'Expected %d messages, found %d: %s'
1195 % (0, len(msgs), msgs))
1197 # Test that a warning is raised when the Android file is not modified.
1198 def testAndroidChangeMissing(self):
1199 mock_input_api = MockInputApi()
1201 mock_input_api.files = [
1203 'content/test/data/accessibility/aria/foo-expected-win.txt',
1207 msgs = PRESUBMIT.CheckAccessibilityTreeTestsAreIncludedForAndroid(
1208 mock_input_api, MockOutputApi())
1209 self.assertEqual(1, len(msgs),
1210 'Expected %d messages, found %d: %s'
1211 % (1, len(msgs), msgs))
1213 # Test that Android change is not required when no platform expectations files are changed.
1214 def testAndroidChangNotMissing(self):
1215 mock_input_api = MockInputApi()
1217 mock_input_api.files = [
1218 MockAffectedFile('content/test/data/accessibility/accname/foo.txt',
1221 'content/test/data/accessibility/html/foo-expected-blink.txt',
1223 MockAffectedFile('content/test/data/accessibility/html/foo.html',
1225 MockAffectedFile('content/test/data/accessibility/aria/foo.cc',
1227 MockAffectedFile('content/test/data/accessibility/css/foo.h',
1229 MockAffectedFile('content/test/data/accessibility/tree/foo.py',
1233 msgs = PRESUBMIT.CheckAccessibilityTreeTestsAreIncludedForAndroid(
1234 mock_input_api, MockOutputApi())
1235 self.assertEqual(0, len(msgs),
1236 'Expected %d messages, found %d: %s'
1237 % (0, len(msgs), msgs))
1239 # Test that Android change is not required for unrelated html files.
1240 def testIgnoreNonRelatedHtmlFiles(self):
1241 mock_input_api = MockInputApi()
1243 mock_input_api.files = [
1244 MockAffectedFile('content/test/data/accessibility/event/foo.html',
1248 msgs = PRESUBMIT.CheckAccessibilityTreeTestsAreIncludedForAndroid(
1249 mock_input_api, MockOutputApi())
1250 self.assertEqual(0, len(msgs),
1251 'Expected %d messages, found %d: %s'
1252 % (0, len(msgs), msgs))
1254 # Test that only modifying an html file will not trigger the warning.
1255 def testIgnoreModifiedFiles(self):
1256 mock_input_api = MockInputApi()
1258 mock_input_api.files = [
1259 MockAffectedFile('content/test/data/accessibility/aria/foo.html',
1263 msgs = PRESUBMIT.CheckAccessibilityTreeTestsAreIncludedForAndroid(
1264 mock_input_api, MockOutputApi())
1265 self.assertEqual(0, len(msgs),
1266 'Expected %d messages, found %d: %s'
1267 % (0, len(msgs), msgs))
1269 class AndroidDeprecatedTestAnnotationTest(unittest.TestCase):
1270 def testCheckAndroidTestAnnotationUsage(self):
1271 mock_input_api = MockInputApi()
1272 mock_output_api = MockOutputApi()
1274 mock_input_api.files = [
1275 MockAffectedFile('LalaLand.java', [
1278 MockAffectedFile('CorrectUsage.java', [
1279 'import androidx.test.filters.LargeTest;',
1280 'import androidx.test.filters.MediumTest;',
1281 'import androidx.test.filters.SmallTest;',
1283 MockAffectedFile('UsedDeprecatedLargeTestAnnotation.java', [
1284 'import android.test.suitebuilder.annotation.LargeTest;',
1286 MockAffectedFile('UsedDeprecatedMediumTestAnnotation.java', [
1287 'import android.test.suitebuilder.annotation.MediumTest;',
1289 MockAffectedFile('UsedDeprecatedSmallTestAnnotation.java', [
1290 'import android.test.suitebuilder.annotation.SmallTest;',
1292 MockAffectedFile('UsedDeprecatedSmokeAnnotation.java', [
1293 'import android.test.suitebuilder.annotation.Smoke;',
1296 msgs = PRESUBMIT._CheckAndroidTestAnnotationUsage(
1297 mock_input_api, mock_output_api)
1298 self.assertEqual(1, len(msgs),
1299 'Expected %d items, found %d: %s'
1300 % (1, len(msgs), msgs))
1301 self.assertEqual(4, len(msgs[0].items),
1302 'Expected %d items, found %d: %s'
1303 % (4, len(msgs[0].items), msgs[0].items))
1304 self.assertTrue('UsedDeprecatedLargeTestAnnotation.java:1' in msgs[0].items,
1305 'UsedDeprecatedLargeTestAnnotation not found in errors')
1306 self.assertTrue('UsedDeprecatedMediumTestAnnotation.java:1'
1308 'UsedDeprecatedMediumTestAnnotation not found in errors')
1309 self.assertTrue('UsedDeprecatedSmallTestAnnotation.java:1' in msgs[0].items,
1310 'UsedDeprecatedSmallTestAnnotation not found in errors')
1311 self.assertTrue('UsedDeprecatedSmokeAnnotation.java:1' in msgs[0].items,
1312 'UsedDeprecatedSmokeAnnotation not found in errors')
1314 class AndroidBannedImportTest(unittest.TestCase):
1315 def testCheckAndroidNoBannedImports(self):
1316 mock_input_api = MockInputApi()
1317 mock_output_api = MockOutputApi()
1320 MockAffectedFile('RandomStufff.java', [
1323 MockAffectedFile('NoBannedImports.java', [
1324 'import androidx.test.filters.LargeTest;',
1325 'import androidx.test.filters.MediumTest;',
1326 'import androidx.test.filters.SmallTest;',
1328 MockAffectedFile('BannedUri.java', [
1329 'import java.net.URI;',
1331 MockAffectedFile('BannedTargetApi.java', [
1332 'import android.annotation.TargetApi;',
1334 MockAffectedFile('BannedUiThreadTestRule.java', [
1335 'import androidx.test.rule.UiThreadTestRule;',
1337 MockAffectedFile('BannedUiThreadTest.java', [
1338 'import androidx.test.annotation.UiThreadTest;',
1340 MockAffectedFile('BannedActivityTestRule.java', [
1341 'import androidx.test.rule.ActivityTestRule;',
1343 MockAffectedFile('BannedVectorDrawableCompat.java', [
1344 'import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat;',
1348 for file in test_files:
1349 mock_input_api.files = [file]
1350 msgs.append(PRESUBMIT._CheckAndroidNoBannedImports(
1351 mock_input_api, mock_output_api))
1352 self.assertEqual(0, len(msgs[0]))
1353 self.assertEqual(0, len(msgs[1]))
1354 self.assertTrue(msgs[2][0].message.startswith(textwrap.dedent("""\
1355 Banned imports were used.
1356 BannedUri.java:1:"""
1358 self.assertTrue(msgs[3][0].message.startswith(textwrap.dedent("""\
1359 Banned imports were used.
1360 BannedTargetApi.java:1:"""
1362 self.assertTrue(msgs[4][0].message.startswith(textwrap.dedent("""\
1363 Banned imports were used.
1364 BannedUiThreadTestRule.java:1:"""
1366 self.assertTrue(msgs[5][0].message.startswith(textwrap.dedent("""\
1367 Banned imports were used.
1368 BannedUiThreadTest.java:1:"""
1370 self.assertTrue(msgs[6][0].message.startswith(textwrap.dedent("""\
1371 Banned imports were used.
1372 BannedActivityTestRule.java:1:"""
1374 self.assertTrue(msgs[7][0].message.startswith(textwrap.dedent("""\
1375 Banned imports were used.
1376 BannedVectorDrawableCompat.java:1:"""
1379 class CheckNoDownstreamDepsTest(unittest.TestCase):
1380 def testInvalidDepFromUpstream(self):
1381 mock_input_api = MockInputApi()
1382 mock_output_api = MockOutputApi()
1384 mock_input_api.files = [
1385 MockAffectedFile('BUILD.gn', [
1387 ' "//clank/target:test",',
1390 MockAffectedFile('chrome/android/BUILD.gn', [
1391 'deps = [ "//clank/target:test" ]'
1393 MockAffectedFile('chrome/chrome_java_deps.gni', [
1395 ' "//clank/target:test",',
1399 mock_input_api.change.RepositoryRoot = lambda: 'chromium/src'
1400 msgs = PRESUBMIT.CheckNoUpstreamDepsOnClank(
1401 mock_input_api, mock_output_api)
1402 self.assertEqual(1, len(msgs),
1403 'Expected %d items, found %d: %s'
1404 % (1, len(msgs), msgs))
1405 self.assertEqual(3, len(msgs[0].items),
1406 'Expected %d items, found %d: %s'
1407 % (3, len(msgs[0].items), msgs[0].items))
1408 self.assertTrue(any('BUILD.gn:2' in item for item in msgs[0].items),
1409 'BUILD.gn not found in errors')
1411 any('chrome/android/BUILD.gn:1' in item for item in msgs[0].items),
1412 'chrome/android/BUILD.gn:1 not found in errors')
1414 any('chrome/chrome_java_deps.gni:2' in item for item in msgs[0].items),
1415 'chrome/chrome_java_deps.gni:2 not found in errors')
1417 def testAllowsComments(self):
1418 mock_input_api = MockInputApi()
1419 mock_output_api = MockOutputApi()
1421 mock_input_api.files = [
1422 MockAffectedFile('BUILD.gn', [
1423 '# real implementation in //clank/target:test',
1426 mock_input_api.change.RepositoryRoot = lambda: 'chromium/src'
1427 msgs = PRESUBMIT.CheckNoUpstreamDepsOnClank(
1428 mock_input_api, mock_output_api)
1429 self.assertEqual(0, len(msgs),
1430 'Expected %d items, found %d: %s'
1431 % (0, len(msgs), msgs))
1433 def testOnlyChecksBuildFiles(self):
1434 mock_input_api = MockInputApi()
1435 mock_output_api = MockOutputApi()
1437 mock_input_api.files = [
1438 MockAffectedFile('README.md', [
1439 'DEPS = [ "//clank/target:test" ]'
1441 MockAffectedFile('chrome/android/java/file.java', [
1442 '//clank/ only function'
1445 mock_input_api.change.RepositoryRoot = lambda: 'chromium/src'
1446 msgs = PRESUBMIT.CheckNoUpstreamDepsOnClank(
1447 mock_input_api, mock_output_api)
1448 self.assertEqual(0, len(msgs),
1449 'Expected %d items, found %d: %s'
1450 % (0, len(msgs), msgs))
1452 def testValidDepFromDownstream(self):
1453 mock_input_api = MockInputApi()
1454 mock_output_api = MockOutputApi()
1456 mock_input_api.files = [
1457 MockAffectedFile('BUILD.gn', [
1459 ' "//clank/target:test",',
1462 MockAffectedFile('java/BUILD.gn', [
1463 'DEPS = [ "//clank/target:test" ]'
1466 mock_input_api.change.RepositoryRoot = lambda: 'chromium/src/clank'
1467 msgs = PRESUBMIT.CheckNoUpstreamDepsOnClank(
1468 mock_input_api, mock_output_api)
1469 self.assertEqual(0, len(msgs),
1470 'Expected %d items, found %d: %s'
1471 % (0, len(msgs), msgs))
1473 class AndroidDeprecatedJUnitFrameworkTest(unittest.TestCase):
1474 def testCheckAndroidTestJUnitFramework(self):
1475 mock_input_api = MockInputApi()
1476 mock_output_api = MockOutputApi()
1478 mock_input_api.files = [
1479 MockAffectedFile('LalaLand.java', [
1482 MockAffectedFile('CorrectUsage.java', [
1483 'import org.junit.ABC',
1484 'import org.junit.XYZ;',
1486 MockAffectedFile('UsedDeprecatedJUnit.java', [
1487 'import junit.framework.*;',
1489 MockAffectedFile('UsedDeprecatedJUnitAssert.java', [
1490 'import junit.framework.Assert;',
1493 msgs = PRESUBMIT._CheckAndroidTestJUnitFrameworkImport(
1494 mock_input_api, mock_output_api)
1495 self.assertEqual(1, len(msgs),
1496 'Expected %d items, found %d: %s'
1497 % (1, len(msgs), msgs))
1498 self.assertEqual(2, len(msgs[0].items),
1499 'Expected %d items, found %d: %s'
1500 % (2, len(msgs[0].items), msgs[0].items))
1501 self.assertTrue('UsedDeprecatedJUnit.java:1' in msgs[0].items,
1502 'UsedDeprecatedJUnit.java not found in errors')
1503 self.assertTrue('UsedDeprecatedJUnitAssert.java:1'
1505 'UsedDeprecatedJUnitAssert not found in errors')
1508 class AndroidJUnitBaseClassTest(unittest.TestCase):
1509 def testCheckAndroidTestJUnitBaseClass(self):
1510 mock_input_api = MockInputApi()
1511 mock_output_api = MockOutputApi()
1513 mock_input_api.files = [
1514 MockAffectedFile('LalaLand.java', [
1517 MockAffectedFile('CorrectTest.java', [
1518 '@RunWith(ABC.class);'
1519 'public class CorrectTest {',
1522 MockAffectedFile('HistoricallyIncorrectTest.java', [
1523 'public class Test extends BaseCaseA {',
1526 'public class Test extends BaseCaseB {',
1529 MockAffectedFile('CorrectTestWithInterface.java', [
1530 '@RunWith(ABC.class);'
1531 'public class CorrectTest implement Interface {',
1534 MockAffectedFile('IncorrectTest.java', [
1535 'public class IncorrectTest extends TestCase {',
1538 MockAffectedFile('IncorrectWithInterfaceTest.java', [
1539 'public class Test implements X extends BaseClass {',
1542 MockAffectedFile('IncorrectMultiLineTest.java', [
1543 'public class Test implements X, Y, Z',
1544 ' extends TestBase {',
1548 msgs = PRESUBMIT._CheckAndroidTestJUnitInheritance(
1549 mock_input_api, mock_output_api)
1550 self.assertEqual(1, len(msgs),
1551 'Expected %d items, found %d: %s'
1552 % (1, len(msgs), msgs))
1553 self.assertEqual(3, len(msgs[0].items),
1554 'Expected %d items, found %d: %s'
1555 % (3, len(msgs[0].items), msgs[0].items))
1556 self.assertTrue('IncorrectTest.java:1' in msgs[0].items,
1557 'IncorrectTest not found in errors')
1558 self.assertTrue('IncorrectWithInterfaceTest.java:1'
1560 'IncorrectWithInterfaceTest not found in errors')
1561 self.assertTrue('IncorrectMultiLineTest.java:2' in msgs[0].items,
1562 'IncorrectMultiLineTest not found in errors')
1564 class AndroidDebuggableBuildTest(unittest.TestCase):
1566 def testCheckAndroidDebuggableBuild(self):
1567 mock_input_api = MockInputApi()
1568 mock_output_api = MockOutputApi()
1570 mock_input_api.files = [
1571 MockAffectedFile('RandomStuff.java', [
1574 MockAffectedFile('CorrectUsage.java', [
1575 'import org.chromium.base.BuildInfo;',
1576 'some random stuff',
1577 'boolean isOsDebuggable = BuildInfo.isDebugAndroid();',
1579 MockAffectedFile('JustCheckUserdebugBuild.java', [
1580 'import android.os.Build;',
1581 'some random stuff',
1582 'boolean isOsDebuggable = Build.TYPE.equals("userdebug")',
1584 MockAffectedFile('JustCheckEngineeringBuild.java', [
1585 'import android.os.Build;',
1586 'some random stuff',
1587 'boolean isOsDebuggable = "eng".equals(Build.TYPE)',
1589 MockAffectedFile('UsedBuildType.java', [
1590 'import android.os.Build;',
1591 'some random stuff',
1592 'boolean isOsDebuggable = Build.TYPE.equals("userdebug")'
1593 '|| "eng".equals(Build.TYPE)',
1595 MockAffectedFile('UsedExplicitBuildType.java', [
1596 'some random stuff',
1597 'boolean isOsDebuggable = android.os.Build.TYPE.equals("userdebug")'
1598 '|| "eng".equals(android.os.Build.TYPE)',
1602 msgs = PRESUBMIT._CheckAndroidDebuggableBuild(
1603 mock_input_api, mock_output_api)
1604 self.assertEqual(1, len(msgs),
1605 'Expected %d items, found %d: %s'
1606 % (1, len(msgs), msgs))
1607 self.assertEqual(4, len(msgs[0].items),
1608 'Expected %d items, found %d: %s'
1609 % (4, len(msgs[0].items), msgs[0].items))
1610 self.assertTrue('JustCheckUserdebugBuild.java:3' in msgs[0].items)
1611 self.assertTrue('JustCheckEngineeringBuild.java:3' in msgs[0].items)
1612 self.assertTrue('UsedBuildType.java:3' in msgs[0].items)
1613 self.assertTrue('UsedExplicitBuildType.java:2' in msgs[0].items)
1616 class LogUsageTest(unittest.TestCase):
1618 def testCheckAndroidCrLogUsage(self):
1619 mock_input_api = MockInputApi()
1620 mock_output_api = MockOutputApi()
1622 mock_input_api.files = [
1623 MockAffectedFile('RandomStuff.java', [
1626 MockAffectedFile('HasAndroidLog.java', [
1627 'import android.util.Log;',
1628 'some random stuff',
1629 'Log.d("TAG", "foo");',
1631 MockAffectedFile('HasExplicitUtilLog.java', [
1632 'some random stuff',
1633 'android.util.Log.d("TAG", "foo");',
1635 MockAffectedFile('IsInBasePackage.java', [
1636 'package org.chromium.base;',
1637 'private static final String TAG = "cr_Foo";',
1638 'Log.d(TAG, "foo");',
1640 MockAffectedFile('IsInBasePackageButImportsLog.java', [
1641 'package org.chromium.base;',
1642 'import android.util.Log;',
1643 'private static final String TAG = "cr_Foo";',
1644 'Log.d(TAG, "foo");',
1646 MockAffectedFile('HasBothLog.java', [
1647 'import org.chromium.base.Log;',
1648 'some random stuff',
1649 'private static final String TAG = "cr_Foo";',
1650 'Log.d(TAG, "foo");',
1651 'android.util.Log.d("TAG", "foo");',
1653 MockAffectedFile('HasCorrectTag.java', [
1654 'import org.chromium.base.Log;',
1655 'some random stuff',
1656 'private static final String TAG = "cr_Foo";',
1657 'Log.d(TAG, "foo");',
1659 MockAffectedFile('HasOldTag.java', [
1660 'import org.chromium.base.Log;',
1661 'some random stuff',
1662 'private static final String TAG = "cr.Foo";',
1663 'Log.d(TAG, "foo");',
1665 MockAffectedFile('HasDottedTag.java', [
1666 'import org.chromium.base.Log;',
1667 'some random stuff',
1668 'private static final String TAG = "cr_foo.bar";',
1669 'Log.d(TAG, "foo");',
1671 MockAffectedFile('HasDottedTagPublic.java', [
1672 'import org.chromium.base.Log;',
1673 'some random stuff',
1674 'public static final String TAG = "cr_foo.bar";',
1675 'Log.d(TAG, "foo");',
1677 MockAffectedFile('HasNoTagDecl.java', [
1678 'import org.chromium.base.Log;',
1679 'some random stuff',
1680 'Log.d(TAG, "foo");',
1682 MockAffectedFile('HasIncorrectTagDecl.java', [
1683 'import org.chromium.base.Log;',
1684 'private static final String TAHG = "cr_Foo";',
1685 'some random stuff',
1686 'Log.d(TAG, "foo");',
1688 MockAffectedFile('HasInlineTag.java', [
1689 'import org.chromium.base.Log;',
1690 'some random stuff',
1691 'private static final String TAG = "cr_Foo";',
1692 'Log.d("TAG", "foo");',
1694 MockAffectedFile('HasInlineTagWithSpace.java', [
1695 'import org.chromium.base.Log;',
1696 'some random stuff',
1697 'private static final String TAG = "cr_Foo";',
1698 'Log.d("log message", "foo");',
1700 MockAffectedFile('HasUnprefixedTag.java', [
1701 'import org.chromium.base.Log;',
1702 'some random stuff',
1703 'private static final String TAG = "rubbish";',
1704 'Log.d(TAG, "foo");',
1706 MockAffectedFile('HasTooLongTag.java', [
1707 'import org.chromium.base.Log;',
1708 'some random stuff',
1709 'private static final String TAG = "21_characters_long___";',
1710 'Log.d(TAG, "foo");',
1712 MockAffectedFile('HasTooLongTagWithNoLogCallsInDiff.java', [
1713 'import org.chromium.base.Log;',
1714 'some random stuff',
1715 'private static final String TAG = "21_characters_long___";',
1719 msgs = PRESUBMIT._CheckAndroidCrLogUsage(
1720 mock_input_api, mock_output_api)
1722 self.assertEqual(5, len(msgs),
1723 'Expected %d items, found %d: %s' % (5, len(msgs), msgs))
1725 # Declaration format
1726 nb = len(msgs[0].items)
1727 self.assertEqual(2, nb,
1728 'Expected %d items, found %d: %s' % (2, nb, msgs[0].items))
1729 self.assertTrue('HasNoTagDecl.java' in msgs[0].items)
1730 self.assertTrue('HasIncorrectTagDecl.java' in msgs[0].items)
1733 nb = len(msgs[1].items)
1734 self.assertEqual(2, nb,
1735 'Expected %d items, found %d: %s' % (2, nb, msgs[1].items))
1736 self.assertTrue('HasTooLongTag.java' in msgs[1].items)
1737 self.assertTrue('HasTooLongTagWithNoLogCallsInDiff.java' in msgs[1].items)
1739 # Tag must be a variable named TAG
1740 nb = len(msgs[2].items)
1741 self.assertEqual(3, nb,
1742 'Expected %d items, found %d: %s' % (3, nb, msgs[2].items))
1743 self.assertTrue('HasBothLog.java:5' in msgs[2].items)
1744 self.assertTrue('HasInlineTag.java:4' in msgs[2].items)
1745 self.assertTrue('HasInlineTagWithSpace.java:4' in msgs[2].items)
1748 nb = len(msgs[3].items)
1749 self.assertEqual(3, nb,
1750 'Expected %d items, found %d: %s' % (3, nb, msgs[3].items))
1751 self.assertTrue('HasAndroidLog.java:3' in msgs[3].items)
1752 self.assertTrue('HasExplicitUtilLog.java:2' in msgs[3].items)
1753 self.assertTrue('IsInBasePackageButImportsLog.java:4' in msgs[3].items)
1755 # Tag must not contain
1756 nb = len(msgs[4].items)
1757 self.assertEqual(3, nb,
1758 'Expected %d items, found %d: %s' % (2, nb, msgs[4].items))
1759 self.assertTrue('HasDottedTag.java' in msgs[4].items)
1760 self.assertTrue('HasDottedTagPublic.java' in msgs[4].items)
1761 self.assertTrue('HasOldTag.java' in msgs[4].items)
1764 class GoogleAnswerUrlFormatTest(unittest.TestCase):
1766 def testCatchAnswerUrlId(self):
1767 input_api = MockInputApi()
1769 MockFile('somewhere/file.cc',
1771 ' "https://support.google.com/chrome/answer/123456";']),
1772 MockFile('somewhere_else/file.cc',
1774 ' "https://support.google.com/chrome/a/answer/123456";']),
1777 warnings = PRESUBMIT.CheckGoogleSupportAnswerUrlOnUpload(
1778 input_api, MockOutputApi())
1779 self.assertEqual(1, len(warnings))
1780 self.assertEqual(2, len(warnings[0].items))
1782 def testAllowAnswerUrlParam(self):
1783 input_api = MockInputApi()
1785 MockFile('somewhere/file.cc',
1787 ' "https://support.google.com/chrome/?p=cpn_crash_reports";']),
1790 warnings = PRESUBMIT.CheckGoogleSupportAnswerUrlOnUpload(
1791 input_api, MockOutputApi())
1792 self.assertEqual(0, len(warnings))
1795 class HardcodedGoogleHostsTest(unittest.TestCase):
1797 def testWarnOnAssignedLiterals(self):
1798 input_api = MockInputApi()
1800 MockFile('content/file.cc',
1801 ['char* host = "https://www.google.com";']),
1802 MockFile('content/file.cc',
1803 ['char* host = "https://www.googleapis.com";']),
1804 MockFile('content/file.cc',
1805 ['char* host = "https://clients1.google.com";']),
1808 warnings = PRESUBMIT.CheckHardcodedGoogleHostsInLowerLayers(
1809 input_api, MockOutputApi())
1810 self.assertEqual(1, len(warnings))
1811 self.assertEqual(3, len(warnings[0].items))
1813 def testAllowInComment(self):
1814 input_api = MockInputApi()
1816 MockFile('content/file.cc',
1817 ['char* host = "https://www.aol.com"; // google.com'])
1820 warnings = PRESUBMIT.CheckHardcodedGoogleHostsInLowerLayers(
1821 input_api, MockOutputApi())
1822 self.assertEqual(0, len(warnings))
1825 class ChromeOsSyncedPrefRegistrationTest(unittest.TestCase):
1827 def testWarnsOnChromeOsDirectories(self):
1829 MockFile('ash/file.cc',
1830 ['PrefRegistrySyncable::SYNCABLE_PREF']),
1831 MockFile('chrome/browser/chromeos/file.cc',
1832 ['PrefRegistrySyncable::SYNCABLE_PREF']),
1833 MockFile('chromeos/file.cc',
1834 ['PrefRegistrySyncable::SYNCABLE_PREF']),
1835 MockFile('components/arc/file.cc',
1836 ['PrefRegistrySyncable::SYNCABLE_PREF']),
1837 MockFile('components/exo/file.cc',
1838 ['PrefRegistrySyncable::SYNCABLE_PREF']),
1840 input_api = MockInputApi()
1842 input_api.files = [file]
1843 warnings = PRESUBMIT.CheckChromeOsSyncedPrefRegistration(
1844 input_api, MockOutputApi())
1845 self.assertEqual(1, len(warnings))
1847 def testDoesNotWarnOnSyncOsPref(self):
1848 input_api = MockInputApi()
1850 MockFile('chromeos/file.cc',
1851 ['PrefRegistrySyncable::SYNCABLE_OS_PREF']),
1853 warnings = PRESUBMIT.CheckChromeOsSyncedPrefRegistration(
1854 input_api, MockOutputApi())
1855 self.assertEqual(0, len(warnings))
1857 def testDoesNotWarnOnOtherDirectories(self):
1858 input_api = MockInputApi()
1860 MockFile('chrome/browser/ui/file.cc',
1861 ['PrefRegistrySyncable::SYNCABLE_PREF']),
1862 MockFile('components/sync/file.cc',
1863 ['PrefRegistrySyncable::SYNCABLE_PREF']),
1864 MockFile('content/browser/file.cc',
1865 ['PrefRegistrySyncable::SYNCABLE_PREF']),
1866 MockFile('a/notchromeos/file.cc',
1867 ['PrefRegistrySyncable::SYNCABLE_PREF']),
1869 warnings = PRESUBMIT.CheckChromeOsSyncedPrefRegistration(
1870 input_api, MockOutputApi())
1871 self.assertEqual(0, len(warnings))
1873 def testSeparateWarningForPriorityPrefs(self):
1874 input_api = MockInputApi()
1876 MockFile('chromeos/file.cc',
1877 ['PrefRegistrySyncable::SYNCABLE_PREF',
1878 'PrefRegistrySyncable::SYNCABLE_PRIORITY_PREF']),
1880 warnings = PRESUBMIT.CheckChromeOsSyncedPrefRegistration(
1881 input_api, MockOutputApi())
1882 self.assertEqual(2, len(warnings))
1885 class ForwardDeclarationTest(unittest.TestCase):
1886 def testCheckHeadersOnlyOutsideThirdParty(self):
1887 mock_input_api = MockInputApi()
1888 mock_input_api.files = [
1889 MockAffectedFile('somewhere/file.cc', [
1892 MockAffectedFile('third_party/header.h', [
1896 warnings = PRESUBMIT.CheckUselessForwardDeclarations(mock_input_api,
1898 self.assertEqual(0, len(warnings))
1900 def testNoNestedDeclaration(self):
1901 mock_input_api = MockInputApi()
1902 mock_input_api.files = [
1903 MockAffectedFile('somewhere/header.h', [
1904 'class SomeClass {',
1906 ' class NotAMatch;',
1910 warnings = PRESUBMIT.CheckUselessForwardDeclarations(mock_input_api,
1912 self.assertEqual(0, len(warnings))
1914 def testSubStrings(self):
1915 mock_input_api = MockInputApi()
1916 mock_input_api.files = [
1917 MockAffectedFile('somewhere/header.h', [
1918 'class NotUsefulClass;',
1919 'struct SomeStruct;',
1921 'SomeStructPtr *p2;'
1924 warnings = PRESUBMIT.CheckUselessForwardDeclarations(mock_input_api,
1926 self.assertEqual(2, len(warnings))
1928 def testUselessForwardDeclaration(self):
1929 mock_input_api = MockInputApi()
1930 mock_input_api.files = [
1931 MockAffectedFile('somewhere/header.h', [
1932 'class DummyClass;',
1933 'struct DummyStruct;',
1934 'class UsefulClass;',
1935 'std::unique_ptr<UsefulClass> p;'
1938 warnings = PRESUBMIT.CheckUselessForwardDeclarations(mock_input_api,
1940 self.assertEqual(2, len(warnings))
1942 def testBlinkHeaders(self):
1943 mock_input_api = MockInputApi()
1944 mock_input_api.files = [
1945 MockAffectedFile('third_party/blink/header.h', [
1946 'class DummyClass;',
1947 'struct DummyStruct;',
1949 MockAffectedFile('third_party\\blink\\header.h', [
1950 'class DummyClass;',
1951 'struct DummyStruct;',
1954 warnings = PRESUBMIT.CheckUselessForwardDeclarations(mock_input_api,
1956 self.assertEqual(4, len(warnings))
1959 class RelativeIncludesTest(unittest.TestCase):
1960 def testThirdPartyNotWebKitIgnored(self):
1961 mock_input_api = MockInputApi()
1962 mock_input_api.files = [
1963 MockAffectedFile('third_party/test.cpp', '#include "../header.h"'),
1964 MockAffectedFile('third_party/test/test.cpp', '#include "../header.h"'),
1967 mock_output_api = MockOutputApi()
1969 errors = PRESUBMIT.CheckForRelativeIncludes(
1970 mock_input_api, mock_output_api)
1971 self.assertEqual(0, len(errors))
1973 def testNonCppFileIgnored(self):
1974 mock_input_api = MockInputApi()
1975 mock_input_api.files = [
1976 MockAffectedFile('test.py', '#include "../header.h"'),
1979 mock_output_api = MockOutputApi()
1981 errors = PRESUBMIT.CheckForRelativeIncludes(
1982 mock_input_api, mock_output_api)
1983 self.assertEqual(0, len(errors))
1985 def testInnocuousChangesAllowed(self):
1986 mock_input_api = MockInputApi()
1987 mock_input_api.files = [
1988 MockAffectedFile('test.cpp', '#include "header.h"'),
1989 MockAffectedFile('test2.cpp', '../'),
1992 mock_output_api = MockOutputApi()
1994 errors = PRESUBMIT.CheckForRelativeIncludes(
1995 mock_input_api, mock_output_api)
1996 self.assertEqual(0, len(errors))
1998 def testRelativeIncludeNonWebKitProducesError(self):
1999 mock_input_api = MockInputApi()
2000 mock_input_api.files = [
2001 MockAffectedFile('test.cpp', ['#include "../header.h"']),
2004 mock_output_api = MockOutputApi()
2006 errors = PRESUBMIT.CheckForRelativeIncludes(
2007 mock_input_api, mock_output_api)
2008 self.assertEqual(1, len(errors))
2010 def testRelativeIncludeWebKitProducesError(self):
2011 mock_input_api = MockInputApi()
2012 mock_input_api.files = [
2013 MockAffectedFile('third_party/blink/test.cpp',
2014 ['#include "../header.h']),
2017 mock_output_api = MockOutputApi()
2019 errors = PRESUBMIT.CheckForRelativeIncludes(
2020 mock_input_api, mock_output_api)
2021 self.assertEqual(1, len(errors))
2024 class CCIncludeTest(unittest.TestCase):
2025 def testThirdPartyNotBlinkIgnored(self):
2026 mock_input_api = MockInputApi()
2027 mock_input_api.files = [
2028 MockAffectedFile('third_party/test.cpp', '#include "file.cc"'),
2031 mock_output_api = MockOutputApi()
2033 errors = PRESUBMIT.CheckForCcIncludes(
2034 mock_input_api, mock_output_api)
2035 self.assertEqual(0, len(errors))
2037 def testPythonFileIgnored(self):
2038 mock_input_api = MockInputApi()
2039 mock_input_api.files = [
2040 MockAffectedFile('test.py', '#include "file.cc"'),
2043 mock_output_api = MockOutputApi()
2045 errors = PRESUBMIT.CheckForCcIncludes(
2046 mock_input_api, mock_output_api)
2047 self.assertEqual(0, len(errors))
2049 def testIncFilesAccepted(self):
2050 mock_input_api = MockInputApi()
2051 mock_input_api.files = [
2052 MockAffectedFile('test.py', '#include "file.inc"'),
2055 mock_output_api = MockOutputApi()
2057 errors = PRESUBMIT.CheckForCcIncludes(
2058 mock_input_api, mock_output_api)
2059 self.assertEqual(0, len(errors))
2061 def testInnocuousChangesAllowed(self):
2062 mock_input_api = MockInputApi()
2063 mock_input_api.files = [
2064 MockAffectedFile('test.cpp', '#include "header.h"'),
2065 MockAffectedFile('test2.cpp', 'Something "file.cc"'),
2068 mock_output_api = MockOutputApi()
2070 errors = PRESUBMIT.CheckForCcIncludes(
2071 mock_input_api, mock_output_api)
2072 self.assertEqual(0, len(errors))
2074 def testCcIncludeNonBlinkProducesError(self):
2075 mock_input_api = MockInputApi()
2076 mock_input_api.files = [
2077 MockAffectedFile('test.cpp', ['#include "file.cc"']),
2080 mock_output_api = MockOutputApi()
2082 errors = PRESUBMIT.CheckForCcIncludes(
2083 mock_input_api, mock_output_api)
2084 self.assertEqual(1, len(errors))
2086 def testCppIncludeBlinkProducesError(self):
2087 mock_input_api = MockInputApi()
2088 mock_input_api.files = [
2089 MockAffectedFile('third_party/blink/test.cpp',
2090 ['#include "foo/file.cpp"']),
2093 mock_output_api = MockOutputApi()
2095 errors = PRESUBMIT.CheckForCcIncludes(
2096 mock_input_api, mock_output_api)
2097 self.assertEqual(1, len(errors))
2100 class GnGlobForwardTest(unittest.TestCase):
2101 def testAddBareGlobs(self):
2102 mock_input_api = MockInputApi()
2103 mock_input_api.files = [
2104 MockAffectedFile('base/stuff.gni', [
2105 'forward_variables_from(invoker, "*")']),
2106 MockAffectedFile('base/BUILD.gn', [
2107 'forward_variables_from(invoker, "*")']),
2109 warnings = PRESUBMIT.CheckGnGlobForward(mock_input_api, MockOutputApi())
2110 self.assertEqual(1, len(warnings))
2111 msg = '\n'.join(warnings[0].items)
2112 self.assertIn('base/stuff.gni', msg)
2113 # Should not check .gn files. Local templates don't need to care about
2114 # visibility / testonly.
2115 self.assertNotIn('base/BUILD.gn', msg)
2117 def testValidUses(self):
2118 mock_input_api = MockInputApi()
2119 mock_input_api.files = [
2120 MockAffectedFile('base/stuff.gni', [
2121 'forward_variables_from(invoker, "*", [])']),
2122 MockAffectedFile('base/stuff2.gni', [
2123 'forward_variables_from(invoker, "*", TESTONLY_AND_VISIBILITY)']),
2124 MockAffectedFile('base/stuff3.gni', [
2125 'forward_variables_from(invoker, [ "testonly" ])']),
2127 warnings = PRESUBMIT.CheckGnGlobForward(mock_input_api, MockOutputApi())
2128 self.assertEqual([], warnings)
2131 class GnRebasePathTest(unittest.TestCase):
2132 def testAddAbsolutePath(self):
2133 mock_input_api = MockInputApi()
2134 mock_input_api.files = [
2135 MockAffectedFile('base/BUILD.gn', ['rebase_path("$target_gen_dir", "//")']),
2136 MockAffectedFile('base/root/BUILD.gn', ['rebase_path("$target_gen_dir", "/")']),
2137 MockAffectedFile('base/variable/BUILD.gn', ['rebase_path(target_gen_dir, "/")']),
2139 warnings = PRESUBMIT.CheckGnRebasePath(mock_input_api, MockOutputApi())
2140 self.assertEqual(1, len(warnings))
2141 msg = '\n'.join(warnings[0].items)
2142 self.assertIn('base/BUILD.gn', msg)
2143 self.assertIn('base/root/BUILD.gn', msg)
2144 self.assertIn('base/variable/BUILD.gn', msg)
2145 self.assertEqual(3, len(warnings[0].items))
2147 def testValidUses(self):
2148 mock_input_api = MockInputApi()
2149 mock_input_api.files = [
2150 MockAffectedFile('base/foo/BUILD.gn', ['rebase_path("$target_gen_dir", root_build_dir)']),
2151 MockAffectedFile('base/bar/BUILD.gn', ['rebase_path("$target_gen_dir", root_build_dir, "/")']),
2152 MockAffectedFile('base/baz/BUILD.gn', ['rebase_path(target_gen_dir, root_build_dir)']),
2153 MockAffectedFile('base/baz/BUILD.gn', ['rebase_path(target_gen_dir, "//some/arbitrary/path")']),
2154 MockAffectedFile('base/okay_slash/BUILD.gn', ['rebase_path(".", "//")']),
2156 warnings = PRESUBMIT.CheckGnRebasePath(mock_input_api, MockOutputApi())
2157 self.assertEqual([], warnings)
2160 class NewHeaderWithoutGnChangeTest(unittest.TestCase):
2161 def testAddHeaderWithoutGn(self):
2162 mock_input_api = MockInputApi()
2163 mock_input_api.files = [
2164 MockAffectedFile('base/stuff.h', ''),
2166 warnings = PRESUBMIT.CheckNewHeaderWithoutGnChangeOnUpload(
2167 mock_input_api, MockOutputApi())
2168 self.assertEqual(1, len(warnings))
2169 self.assertTrue('base/stuff.h' in warnings[0].items)
2171 def testModifyHeader(self):
2172 mock_input_api = MockInputApi()
2173 mock_input_api.files = [
2174 MockAffectedFile('base/stuff.h', '', action='M'),
2176 warnings = PRESUBMIT.CheckNewHeaderWithoutGnChangeOnUpload(
2177 mock_input_api, MockOutputApi())
2178 self.assertEqual(0, len(warnings))
2180 def testDeleteHeader(self):
2181 mock_input_api = MockInputApi()
2182 mock_input_api.files = [
2183 MockAffectedFile('base/stuff.h', '', action='D'),
2185 warnings = PRESUBMIT.CheckNewHeaderWithoutGnChangeOnUpload(
2186 mock_input_api, MockOutputApi())
2187 self.assertEqual(0, len(warnings))
2189 def testAddHeaderWithGn(self):
2190 mock_input_api = MockInputApi()
2191 mock_input_api.files = [
2192 MockAffectedFile('base/stuff.h', ''),
2193 MockAffectedFile('base/BUILD.gn', 'stuff.h'),
2195 warnings = PRESUBMIT.CheckNewHeaderWithoutGnChangeOnUpload(
2196 mock_input_api, MockOutputApi())
2197 self.assertEqual(0, len(warnings))
2199 def testAddHeaderWithGni(self):
2200 mock_input_api = MockInputApi()
2201 mock_input_api.files = [
2202 MockAffectedFile('base/stuff.h', ''),
2203 MockAffectedFile('base/files.gni', 'stuff.h'),
2205 warnings = PRESUBMIT.CheckNewHeaderWithoutGnChangeOnUpload(
2206 mock_input_api, MockOutputApi())
2207 self.assertEqual(0, len(warnings))
2209 def testAddHeaderWithOther(self):
2210 mock_input_api = MockInputApi()
2211 mock_input_api.files = [
2212 MockAffectedFile('base/stuff.h', ''),
2213 MockAffectedFile('base/stuff.cc', 'stuff.h'),
2215 warnings = PRESUBMIT.CheckNewHeaderWithoutGnChangeOnUpload(
2216 mock_input_api, MockOutputApi())
2217 self.assertEqual(1, len(warnings))
2219 def testAddHeaderWithWrongGn(self):
2220 mock_input_api = MockInputApi()
2221 mock_input_api.files = [
2222 MockAffectedFile('base/stuff.h', ''),
2223 MockAffectedFile('base/BUILD.gn', 'stuff_h'),
2225 warnings = PRESUBMIT.CheckNewHeaderWithoutGnChangeOnUpload(
2226 mock_input_api, MockOutputApi())
2227 self.assertEqual(1, len(warnings))
2229 def testAddHeadersWithGn(self):
2230 mock_input_api = MockInputApi()
2231 mock_input_api.files = [
2232 MockAffectedFile('base/stuff.h', ''),
2233 MockAffectedFile('base/another.h', ''),
2234 MockAffectedFile('base/BUILD.gn', 'another.h\nstuff.h'),
2236 warnings = PRESUBMIT.CheckNewHeaderWithoutGnChangeOnUpload(
2237 mock_input_api, MockOutputApi())
2238 self.assertEqual(0, len(warnings))
2240 def testAddHeadersWithWrongGn(self):
2241 mock_input_api = MockInputApi()
2242 mock_input_api.files = [
2243 MockAffectedFile('base/stuff.h', ''),
2244 MockAffectedFile('base/another.h', ''),
2245 MockAffectedFile('base/BUILD.gn', 'another_h\nstuff.h'),
2247 warnings = PRESUBMIT.CheckNewHeaderWithoutGnChangeOnUpload(
2248 mock_input_api, MockOutputApi())
2249 self.assertEqual(1, len(warnings))
2250 self.assertFalse('base/stuff.h' in warnings[0].items)
2251 self.assertTrue('base/another.h' in warnings[0].items)
2253 def testAddHeadersWithWrongGn2(self):
2254 mock_input_api = MockInputApi()
2255 mock_input_api.files = [
2256 MockAffectedFile('base/stuff.h', ''),
2257 MockAffectedFile('base/another.h', ''),
2258 MockAffectedFile('base/BUILD.gn', 'another_h\nstuff_h'),
2260 warnings = PRESUBMIT.CheckNewHeaderWithoutGnChangeOnUpload(
2261 mock_input_api, MockOutputApi())
2262 self.assertEqual(1, len(warnings))
2263 self.assertTrue('base/stuff.h' in warnings[0].items)
2264 self.assertTrue('base/another.h' in warnings[0].items)
2267 class CorrectProductNameInMessagesTest(unittest.TestCase):
2268 def testProductNameInDesc(self):
2269 mock_input_api = MockInputApi()
2270 mock_input_api.files = [
2271 MockAffectedFile('chrome/app/google_chrome_strings.grd', [
2272 '<message name="Foo" desc="Welcome to Chrome">',
2273 ' Welcome to Chrome!',
2276 MockAffectedFile('chrome/app/chromium_strings.grd', [
2277 '<message name="Bar" desc="Welcome to Chrome">',
2278 ' Welcome to Chromium!',
2282 warnings = PRESUBMIT.CheckCorrectProductNameInMessages(
2283 mock_input_api, MockOutputApi())
2284 self.assertEqual(0, len(warnings))
2286 def testChromeInChromium(self):
2287 mock_input_api = MockInputApi()
2288 mock_input_api.files = [
2289 MockAffectedFile('chrome/app/google_chrome_strings.grd', [
2290 '<message name="Foo" desc="Welcome to Chrome">',
2291 ' Welcome to Chrome!',
2294 MockAffectedFile('chrome/app/chromium_strings.grd', [
2295 '<message name="Bar" desc="Welcome to Chrome">',
2296 ' Welcome to Chrome!',
2300 warnings = PRESUBMIT.CheckCorrectProductNameInMessages(
2301 mock_input_api, MockOutputApi())
2302 self.assertEqual(1, len(warnings))
2303 self.assertTrue('chrome/app/chromium_strings.grd' in warnings[0].items[0])
2305 def testChromiumInChrome(self):
2306 mock_input_api = MockInputApi()
2307 mock_input_api.files = [
2308 MockAffectedFile('chrome/app/google_chrome_strings.grd', [
2309 '<message name="Foo" desc="Welcome to Chrome">',
2310 ' Welcome to Chromium!',
2313 MockAffectedFile('chrome/app/chromium_strings.grd', [
2314 '<message name="Bar" desc="Welcome to Chrome">',
2315 ' Welcome to Chromium!',
2319 warnings = PRESUBMIT.CheckCorrectProductNameInMessages(
2320 mock_input_api, MockOutputApi())
2321 self.assertEqual(1, len(warnings))
2323 'chrome/app/google_chrome_strings.grd:2' in warnings[0].items[0])
2325 def testChromeForTestingInChromium(self):
2326 mock_input_api = MockInputApi()
2327 mock_input_api.files = [
2328 MockAffectedFile('chrome/app/chromium_strings.grd', [
2329 '<message name="Bar" desc="Welcome to Chrome">',
2330 ' Welcome to Chrome for Testing!',
2334 warnings = PRESUBMIT.CheckCorrectProductNameInMessages(
2335 mock_input_api, MockOutputApi())
2336 self.assertEqual(0, len(warnings))
2338 def testChromeForTestingInChrome(self):
2339 mock_input_api = MockInputApi()
2340 mock_input_api.files = [
2341 MockAffectedFile('chrome/app/google_chrome_strings.grd', [
2342 '<message name="Bar" desc="Welcome to Chrome">',
2343 ' Welcome to Chrome for Testing!',
2347 warnings = PRESUBMIT.CheckCorrectProductNameInMessages(
2348 mock_input_api, MockOutputApi())
2349 self.assertEqual(1, len(warnings))
2351 'chrome/app/google_chrome_strings.grd:2' in warnings[0].items[0])
2353 def testMultipleInstances(self):
2354 mock_input_api = MockInputApi()
2355 mock_input_api.files = [
2356 MockAffectedFile('chrome/app/chromium_strings.grd', [
2357 '<message name="Bar" desc="Welcome to Chrome">',
2358 ' Welcome to Chrome!',
2360 '<message name="Baz" desc="A correct message">',
2361 ' Chromium is the software you are using.',
2363 '<message name="Bat" desc="An incorrect message">',
2364 ' Google Chrome is the software you are using.',
2368 warnings = PRESUBMIT.CheckCorrectProductNameInMessages(
2369 mock_input_api, MockOutputApi())
2370 self.assertEqual(1, len(warnings))
2372 'chrome/app/chromium_strings.grd:2' in warnings[0].items[0])
2374 'chrome/app/chromium_strings.grd:8' in warnings[0].items[1])
2376 def testMultipleWarnings(self):
2377 mock_input_api = MockInputApi()
2378 mock_input_api.files = [
2379 MockAffectedFile('chrome/app/chromium_strings.grd', [
2380 '<message name="Bar" desc="Welcome to Chrome">',
2381 ' Welcome to Chrome!',
2383 '<message name="Baz" desc="A correct message">',
2384 ' Chromium is the software you are using.',
2386 '<message name="Bat" desc="An incorrect message">',
2387 ' Google Chrome is the software you are using.',
2390 MockAffectedFile('components/components_google_chrome_strings.grd', [
2391 '<message name="Bar" desc="Welcome to Chrome">',
2392 ' Welcome to Chrome!',
2394 '<message name="Baz" desc="A correct message">',
2395 ' Chromium is the software you are using.',
2397 '<message name="Bat" desc="An incorrect message">',
2398 ' Google Chrome is the software you are using.',
2402 warnings = PRESUBMIT.CheckCorrectProductNameInMessages(
2403 mock_input_api, MockOutputApi())
2404 self.assertEqual(2, len(warnings))
2406 'components/components_google_chrome_strings.grd:5'
2407 in warnings[0].items[0])
2409 'chrome/app/chromium_strings.grd:2' in warnings[1].items[0])
2411 'chrome/app/chromium_strings.grd:8' in warnings[1].items[1])
2414 class _SecurityOwnersTestCase(unittest.TestCase):
2415 def _createMockInputApi(self):
2416 mock_input_api = MockInputApi()
2417 def FakeRepositoryRoot():
2418 return mock_input_api.os_path.join('chromium', 'src')
2419 mock_input_api.change.RepositoryRoot = FakeRepositoryRoot
2420 self._injectFakeOwnersClient(
2422 ['apple@chromium.org', 'orange@chromium.org'])
2423 return mock_input_api
2425 def _setupFakeChange(self, input_api):
2426 class FakeGerrit(object):
2427 def IsOwnersOverrideApproved(self, issue):
2430 input_api.change.issue = 123
2431 input_api.gerrit = FakeGerrit()
2433 def _injectFakeOwnersClient(self, input_api, owners):
2434 class FakeOwnersClient(object):
2435 def ListOwners(self, f):
2438 input_api.owners_client = FakeOwnersClient()
2440 def _injectFakeChangeOwnerAndReviewers(self, input_api, owner, reviewers):
2441 def MockOwnerAndReviewers(input_api, email_regexp, approval_needed=False):
2442 return [owner, reviewers]
2443 input_api.canned_checks.GetCodereviewOwnerAndReviewers = \
2444 MockOwnerAndReviewers
2447 class IpcSecurityOwnerTest(_SecurityOwnersTestCase):
2449 ('*_messages.cc', 'scary_messages.cc'),
2450 ('*_messages*.h', 'scary_messages.h'),
2451 ('*_messages*.h', 'scary_messages_android.h'),
2452 ('*_param_traits*.*', 'scary_param_traits.h'),
2453 ('*_param_traits*.*', 'scary_param_traits_win.h'),
2454 ('*.mojom', 'scary.mojom'),
2455 ('*_mojom_traits*.*', 'scary_mojom_traits.h'),
2456 ('*_mojom_traits*.*', 'scary_mojom_traits_mac.h'),
2457 ('*_type_converter*.*', 'scary_type_converter.h'),
2458 ('*_type_converter*.*', 'scary_type_converter_nacl.h'),
2459 ('*.aidl', 'scary.aidl'),
2462 def testHasCorrectPerFileRulesAndSecurityReviewer(self):
2463 mock_input_api = self._createMockInputApi()
2464 new_owners_file_path = mock_input_api.os_path.join(
2465 'services', 'goat', 'public', 'OWNERS')
2467 'per-file *.mojom=set noparent',
2468 'per-file *.mojom=file://ipc/SECURITY_OWNERS'
2470 def FakeReadFile(filename):
2472 mock_input_api.os_path.join('chromium', 'src', new_owners_file_path),
2474 return '\n'.join(new_owners_file)
2475 mock_input_api.ReadFile = FakeReadFile
2476 mock_input_api.files = [
2478 new_owners_file_path, new_owners_file),
2480 mock_input_api.os_path.join(
2481 'services', 'goat', 'public', 'goat.mojom'),
2482 ['// Scary contents.'])]
2483 self._setupFakeChange(mock_input_api)
2484 self._injectFakeChangeOwnerAndReviewers(
2485 mock_input_api, 'owner@chromium.org', ['orange@chromium.org'])
2486 mock_input_api.is_committing = True
2487 mock_input_api.dry_run = False
2488 mock_output_api = MockOutputApi()
2489 results = PRESUBMIT.CheckSecurityOwners(
2490 mock_input_api, mock_output_api)
2491 self.assertEqual(0, len(results))
2493 def testMissingSecurityReviewerAtUpload(self):
2494 mock_input_api = self._createMockInputApi()
2495 new_owners_file_path = mock_input_api.os_path.join(
2496 'services', 'goat', 'public', 'OWNERS')
2498 'per-file *.mojom=set noparent',
2499 'per-file *.mojom=file://ipc/SECURITY_OWNERS'
2501 def FakeReadFile(filename):
2503 mock_input_api.os_path.join('chromium', 'src', new_owners_file_path),
2505 return '\n'.join(new_owners_file)
2506 mock_input_api.ReadFile = FakeReadFile
2507 mock_input_api.files = [
2509 new_owners_file_path, new_owners_file),
2511 mock_input_api.os_path.join(
2512 'services', 'goat', 'public', 'goat.mojom'),
2513 ['// Scary contents.'])]
2514 self._setupFakeChange(mock_input_api)
2515 self._injectFakeChangeOwnerAndReviewers(
2516 mock_input_api, 'owner@chromium.org', ['banana@chromium.org'])
2517 mock_input_api.is_committing = False
2518 mock_input_api.dry_run = False
2519 mock_output_api = MockOutputApi()
2520 results = PRESUBMIT.CheckSecurityOwners(
2521 mock_input_api, mock_output_api)
2522 self.assertEqual(1, len(results))
2523 self.assertEqual('notify', results[0].type)
2525 'Review from an owner in ipc/SECURITY_OWNERS is required for the '
2526 'following newly-added files:', results[0].message)
2528 def testMissingSecurityReviewerAtDryRunCommit(self):
2529 mock_input_api = self._createMockInputApi()
2530 new_owners_file_path = mock_input_api.os_path.join(
2531 'services', 'goat', 'public', 'OWNERS')
2533 'per-file *.mojom=set noparent',
2534 'per-file *.mojom=file://ipc/SECURITY_OWNERS'
2536 def FakeReadFile(filename):
2538 mock_input_api.os_path.join('chromium', 'src', new_owners_file_path),
2540 return '\n'.join(new_owners_file)
2541 mock_input_api.ReadFile = FakeReadFile
2542 mock_input_api.files = [
2544 new_owners_file_path, new_owners_file),
2546 mock_input_api.os_path.join(
2547 'services', 'goat', 'public', 'goat.mojom'),
2548 ['// Scary contents.'])]
2549 self._setupFakeChange(mock_input_api)
2550 self._injectFakeChangeOwnerAndReviewers(
2551 mock_input_api, 'owner@chromium.org', ['banana@chromium.org'])
2552 mock_input_api.is_committing = True
2553 mock_input_api.dry_run = True
2554 mock_output_api = MockOutputApi()
2555 results = PRESUBMIT.CheckSecurityOwners(
2556 mock_input_api, mock_output_api)
2557 self.assertEqual(1, len(results))
2558 self.assertEqual('error', results[0].type)
2560 'Review from an owner in ipc/SECURITY_OWNERS is required for the '
2561 'following newly-added files:', results[0].message)
2563 def testMissingSecurityApprovalAtRealCommit(self):
2564 mock_input_api = self._createMockInputApi()
2565 new_owners_file_path = mock_input_api.os_path.join(
2566 'services', 'goat', 'public', 'OWNERS')
2568 'per-file *.mojom=set noparent',
2569 'per-file *.mojom=file://ipc/SECURITY_OWNERS'
2571 def FakeReadFile(filename):
2573 mock_input_api.os_path.join('chromium', 'src', new_owners_file_path),
2575 return '\n'.join(new_owners_file)
2576 mock_input_api.ReadFile = FakeReadFile
2577 mock_input_api.files = [
2579 new_owners_file_path, new_owners_file),
2581 mock_input_api.os_path.join(
2582 'services', 'goat', 'public', 'goat.mojom'),
2583 ['// Scary contents.'])]
2584 self._setupFakeChange(mock_input_api)
2585 self._injectFakeChangeOwnerAndReviewers(
2586 mock_input_api, 'owner@chromium.org', ['banana@chromium.org'])
2587 mock_input_api.is_committing = True
2588 mock_input_api.dry_run = False
2589 mock_output_api = MockOutputApi()
2590 results = PRESUBMIT.CheckSecurityOwners(
2591 mock_input_api, mock_output_api)
2592 self.assertEqual('error', results[0].type)
2594 'Review from an owner in ipc/SECURITY_OWNERS is required for the '
2595 'following newly-added files:', results[0].message)
2597 def testIpcChangeNeedsSecurityOwner(self):
2598 for is_committing in [True, False]:
2599 for pattern, filename in self._test_cases:
2601 line=f'is_committing={is_committing}, filename={filename}'):
2602 mock_input_api = self._createMockInputApi()
2603 mock_input_api.files = [
2605 mock_input_api.os_path.join(
2606 'services', 'goat', 'public', filename),
2607 ['// Scary contents.'])]
2608 self._setupFakeChange(mock_input_api)
2609 self._injectFakeChangeOwnerAndReviewers(
2610 mock_input_api, 'owner@chromium.org', ['banana@chromium.org'])
2611 mock_input_api.is_committing = is_committing
2612 mock_input_api.dry_run = False
2613 mock_output_api = MockOutputApi()
2614 results = PRESUBMIT.CheckSecurityOwners(
2615 mock_input_api, mock_output_api)
2616 self.assertEqual(1, len(results))
2617 self.assertEqual('error', results[0].type)
2618 self.assertTrue(results[0].message.replace('\\', '/').startswith(
2619 'Found missing OWNERS lines for security-sensitive files. '
2620 'Please add the following lines to services/goat/public/OWNERS:'))
2621 self.assertEqual(['ipc-security-reviews@chromium.org'],
2622 mock_output_api.more_cc)
2625 def testServiceManifestChangeNeedsSecurityOwner(self):
2626 mock_input_api = self._createMockInputApi()
2627 mock_input_api.files = [
2629 mock_input_api.os_path.join(
2630 'services', 'goat', 'public', 'cpp', 'manifest.cc'),
2632 '#include "services/goat/public/cpp/manifest.h"',
2633 'const service_manager::Manifest& GetManifest() {}',
2635 self._setupFakeChange(mock_input_api)
2636 self._injectFakeChangeOwnerAndReviewers(
2637 mock_input_api, 'owner@chromium.org', ['banana@chromium.org'])
2638 mock_output_api = MockOutputApi()
2639 errors = PRESUBMIT.CheckSecurityOwners(
2640 mock_input_api, mock_output_api)
2641 self.assertEqual(1, len(errors))
2642 self.assertTrue(errors[0].message.replace('\\', '/').startswith(
2643 'Found missing OWNERS lines for security-sensitive files. '
2644 'Please add the following lines to services/goat/public/cpp/OWNERS:'))
2645 self.assertEqual(['ipc-security-reviews@chromium.org'], mock_output_api.more_cc)
2647 def testNonServiceManifestSourceChangesDoNotRequireSecurityOwner(self):
2648 mock_input_api = self._createMockInputApi()
2649 self._injectFakeChangeOwnerAndReviewers(
2650 mock_input_api, 'owner@chromium.org', ['banana@chromium.org'])
2651 mock_input_api.files = [
2652 MockAffectedFile('some/non/service/thing/foo_manifest.cc',
2654 'const char kNoEnforcement[] = "not a manifest!";',
2656 mock_output_api = MockOutputApi()
2657 errors = PRESUBMIT.CheckSecurityOwners(
2658 mock_input_api, mock_output_api)
2659 self.assertEqual([], errors)
2660 self.assertEqual([], mock_output_api.more_cc)
2663 class FuchsiaSecurityOwnerTest(_SecurityOwnersTestCase):
2664 def testFidlChangeNeedsSecurityOwner(self):
2665 mock_input_api = self._createMockInputApi()
2666 mock_input_api.files = [
2667 MockAffectedFile('potentially/scary/ipc.fidl',
2671 self._setupFakeChange(mock_input_api)
2672 self._injectFakeChangeOwnerAndReviewers(
2673 mock_input_api, 'owner@chromium.org', ['banana@chromium.org'])
2674 mock_output_api = MockOutputApi()
2675 errors = PRESUBMIT.CheckSecurityOwners(
2676 mock_input_api, mock_output_api)
2677 self.assertEqual(1, len(errors))
2678 self.assertTrue(errors[0].message.replace('\\', '/').startswith(
2679 'Found missing OWNERS lines for security-sensitive files. '
2680 'Please add the following lines to potentially/scary/OWNERS:'))
2682 def testComponentManifestV1ChangeNeedsSecurityOwner(self):
2683 mock_input_api = self._createMockInputApi()
2684 mock_input_api.files = [
2685 MockAffectedFile('potentially/scary/v2_manifest.cmx',
2687 '{ "that is no": "manifest!" }'
2689 self._setupFakeChange(mock_input_api)
2690 self._injectFakeChangeOwnerAndReviewers(
2691 mock_input_api, 'owner@chromium.org', ['banana@chromium.org'])
2692 mock_output_api = MockOutputApi()
2693 errors = PRESUBMIT.CheckSecurityOwners(
2694 mock_input_api, mock_output_api)
2695 self.assertEqual(1, len(errors))
2696 self.assertTrue(errors[0].message.replace('\\', '/').startswith(
2697 'Found missing OWNERS lines for security-sensitive files. '
2698 'Please add the following lines to potentially/scary/OWNERS:'))
2700 def testComponentManifestV2NeedsSecurityOwner(self):
2701 mock_input_api = self._createMockInputApi()
2702 mock_input_api.files = [
2703 MockAffectedFile('potentially/scary/v2_manifest.cml',
2705 '{ "that is no": "manifest!" }'
2707 self._setupFakeChange(mock_input_api)
2708 self._injectFakeChangeOwnerAndReviewers(
2709 mock_input_api, 'owner@chromium.org', ['banana@chromium.org'])
2710 mock_output_api = MockOutputApi()
2711 errors = PRESUBMIT.CheckSecurityOwners(
2712 mock_input_api, mock_output_api)
2713 self.assertEqual(1, len(errors))
2714 self.assertTrue(errors[0].message.replace('\\', '/').startswith(
2715 'Found missing OWNERS lines for security-sensitive files. '
2716 'Please add the following lines to potentially/scary/OWNERS:'))
2718 def testThirdPartyTestsDoNotRequireSecurityOwner(self):
2719 mock_input_api = MockInputApi()
2720 self._injectFakeChangeOwnerAndReviewers(
2721 mock_input_api, 'owner@chromium.org', ['banana@chromium.org'])
2722 mock_input_api.files = [
2723 MockAffectedFile('third_party/crashpad/test/tests.cmx',
2725 'const char kNoEnforcement[] = "Security?!? Pah!";',
2727 mock_output_api = MockOutputApi()
2728 errors = PRESUBMIT.CheckSecurityOwners(
2729 mock_input_api, mock_output_api)
2730 self.assertEqual([], errors)
2732 def testOtherFuchsiaChangesDoNotRequireSecurityOwner(self):
2733 mock_input_api = MockInputApi()
2734 self._injectFakeChangeOwnerAndReviewers(
2735 mock_input_api, 'owner@chromium.org', ['banana@chromium.org'])
2736 mock_input_api.files = [
2737 MockAffectedFile('some/non/service/thing/fuchsia_fidl_cml_cmx_magic.cc',
2739 'const char kNoEnforcement[] = "Security?!? Pah!";',
2741 mock_output_api = MockOutputApi()
2742 errors = PRESUBMIT.CheckSecurityOwners(
2743 mock_input_api, mock_output_api)
2744 self.assertEqual([], errors)
2747 class SecurityChangeTest(_SecurityOwnersTestCase):
2748 def testDiffGetServiceSandboxType(self):
2749 mock_input_api = MockInputApi()
2750 mock_input_api.files = [
2752 'services/goat/teleporter_host.cc',
2755 'inline content::SandboxType',
2756 'content::GetServiceSandboxType<chrome::mojom::GoatTeleporter>() {',
2757 '#if defined(OS_WIN)',
2758 ' return SandboxType::kGoaty;',
2760 ' return SandboxType::kNoSandbox;',
2761 '#endif // !defined(OS_WIN)',
2766 files_to_functions = PRESUBMIT._GetFilesUsingSecurityCriticalFunctions(
2769 'services/goat/teleporter_host.cc': set([
2770 'content::GetServiceSandboxType<>()'
2774 def testDiffRemovingLine(self):
2775 mock_input_api = MockInputApi()
2776 mock_file = MockAffectedFile('services/goat/teleporter_host.cc', '')
2777 mock_file._scm_diff = """--- old 2020-05-04 14:08:25.000000000 -0400
2778 +++ new 2020-05-04 14:08:32.000000000 -0400
2781 inline content::SandboxType
2782 -content::GetServiceSandboxType<chrome::mojom::GoatTeleporter>() {
2784 return SandboxType::kGoaty;
2786 mock_input_api.files = [mock_file]
2787 files_to_functions = PRESUBMIT._GetFilesUsingSecurityCriticalFunctions(
2790 'services/goat/teleporter_host.cc': set([
2791 'content::GetServiceSandboxType<>()'
2795 def testChangeOwnersMissing(self):
2796 mock_input_api = self._createMockInputApi()
2797 self._setupFakeChange(mock_input_api)
2798 self._injectFakeChangeOwnerAndReviewers(
2799 mock_input_api, 'owner@chromium.org', ['banana@chromium.org'])
2800 mock_input_api.is_committing = False
2801 mock_input_api.files = [
2802 MockAffectedFile('file.cc', ['GetServiceSandboxType<Goat>(Sandbox)'])
2804 mock_output_api = MockOutputApi()
2805 result = PRESUBMIT.CheckSecurityChanges(mock_input_api, mock_output_api)
2806 self.assertEqual(1, len(result))
2807 self.assertEqual(result[0].type, 'notify')
2808 self.assertEqual(result[0].message,
2809 'The following files change calls to security-sensitive functions\n' \
2810 'that need to be reviewed by ipc/SECURITY_OWNERS.\n'
2812 ' content::GetServiceSandboxType<>()\n\n')
2814 def testChangeOwnersMissingAtCommit(self):
2815 mock_input_api = self._createMockInputApi()
2816 self._setupFakeChange(mock_input_api)
2817 self._injectFakeChangeOwnerAndReviewers(
2818 mock_input_api, 'owner@chromium.org', ['banana@chromium.org'])
2819 mock_input_api.is_committing = True
2820 mock_input_api.dry_run = False
2821 mock_input_api.files = [
2822 MockAffectedFile('file.cc', ['GetServiceSandboxType<mojom::Goat>()'])
2824 mock_output_api = MockOutputApi()
2825 result = PRESUBMIT.CheckSecurityChanges(mock_input_api, mock_output_api)
2826 self.assertEqual(1, len(result))
2827 self.assertEqual(result[0].type, 'error')
2828 self.assertEqual(result[0].message,
2829 'The following files change calls to security-sensitive functions\n' \
2830 'that need to be reviewed by ipc/SECURITY_OWNERS.\n'
2832 ' content::GetServiceSandboxType<>()\n\n')
2834 def testChangeOwnersPresent(self):
2835 mock_input_api = self._createMockInputApi()
2836 self._injectFakeChangeOwnerAndReviewers(
2837 mock_input_api, 'owner@chromium.org',
2838 ['apple@chromium.org', 'banana@chromium.org'])
2839 mock_input_api.files = [
2840 MockAffectedFile('file.cc', ['WithSandboxType(Sandbox)'])
2842 mock_output_api = MockOutputApi()
2843 result = PRESUBMIT.CheckSecurityChanges(mock_input_api, mock_output_api)
2844 self.assertEqual(0, len(result))
2846 def testChangeOwnerIsSecurityOwner(self):
2847 mock_input_api = self._createMockInputApi()
2848 self._setupFakeChange(mock_input_api)
2849 self._injectFakeChangeOwnerAndReviewers(
2850 mock_input_api, 'orange@chromium.org', ['pear@chromium.org'])
2851 mock_input_api.files = [
2852 MockAffectedFile('file.cc', ['GetServiceSandboxType<T>(Sandbox)'])
2854 mock_output_api = MockOutputApi()
2855 result = PRESUBMIT.CheckSecurityChanges(mock_input_api, mock_output_api)
2856 self.assertEqual(1, len(result))
2859 class BannedTypeCheckTest(unittest.TestCase):
2860 def testBannedJsFunctions(self):
2861 input_api = MockInputApi()
2863 MockFile('ash/webui/file.js',
2864 ['chrome.send(something);']),
2865 MockFile('some/js/ok/file.js',
2866 ['chrome.send(something);']),
2869 results = PRESUBMIT.CheckNoBannedFunctions(input_api, MockOutputApi())
2871 self.assertEqual(1, len(results))
2872 self.assertTrue('ash/webui/file.js' in results[0].message)
2873 self.assertFalse('some/js/ok/file.js' in results[0].message)
2875 def testBannedJavaFunctions(self):
2876 input_api = MockInputApi()
2878 MockFile('some/java/problematic/diskread.java',
2879 ['StrictMode.allowThreadDiskReads();']),
2880 MockFile('some/java/problematic/diskwrite.java',
2881 ['StrictMode.allowThreadDiskWrites();']),
2882 MockFile('some/java/ok/diskwrite.java',
2883 ['StrictModeContext.allowDiskWrites();']),
2884 MockFile('some/java/problematic/waitidleforsync.java',
2885 ['instrumentation.waitForIdleSync();']),
2886 MockFile('some/java/problematic/registerreceiver.java',
2887 ['context.registerReceiver();']),
2888 MockFile('some/java/problematic/property.java',
2889 ['new Property<abc, Integer>;']),
2890 MockFile('some/java/problematic/requestlayout.java',
2891 ['requestLayout();']),
2892 MockFile('some/java/problematic/lastprofile.java',
2893 ['Profile.getLastUsedRegularProfile();']),
2894 MockFile('some/java/problematic/getdrawable1.java',
2895 ['ResourcesCompat.getDrawable();']),
2896 MockFile('some/java/problematic/getdrawable2.java',
2897 ['getResources().getDrawable();']),
2900 errors = PRESUBMIT.CheckNoBannedFunctions(input_api, MockOutputApi())
2901 self.assertEqual(2, len(errors))
2902 self.assertTrue('some/java/problematic/diskread.java' in errors[0].message)
2903 self.assertTrue('some/java/problematic/diskwrite.java' in errors[0].message)
2904 self.assertFalse('some/java/ok/diskwrite.java' in errors[0].message)
2905 self.assertFalse('some/java/ok/diskwrite.java' in errors[1].message)
2906 self.assertTrue('some/java/problematic/waitidleforsync.java' in errors[0].message)
2907 self.assertTrue('some/java/problematic/registerreceiver.java' in errors[1].message)
2908 self.assertTrue('some/java/problematic/property.java' in errors[0].message)
2909 self.assertTrue('some/java/problematic/requestlayout.java' in errors[0].message)
2910 self.assertTrue('some/java/problematic/lastprofile.java' in errors[0].message)
2911 self.assertTrue('some/java/problematic/getdrawable1.java' in errors[0].message)
2912 self.assertTrue('some/java/problematic/getdrawable2.java' in errors[0].message)
2914 def testBannedCppFunctions(self):
2915 input_api = MockInputApi()
2917 MockFile('some/cpp/problematic/file.cc',
2918 ['using namespace std;']),
2919 MockFile('third_party/blink/problematic/file.cc',
2920 ['GetInterfaceProvider()']),
2921 MockFile('some/cpp/ok/file.cc',
2922 ['using std::string;']),
2923 MockFile('some/cpp/problematic/file2.cc',
2924 ['set_owned_by_client()']),
2925 MockFile('some/cpp/nocheck/file.cc',
2926 ['using namespace std; // nocheck']),
2927 MockFile('some/cpp/comment/file.cc',
2928 [' // A comment about `using namespace std;`']),
2931 results = PRESUBMIT.CheckNoBannedFunctions(input_api, MockOutputApi())
2933 # warnings are results[0], errors are results[1]
2934 self.assertEqual(2, len(results))
2935 self.assertTrue('some/cpp/problematic/file.cc' in results[1].message)
2937 'third_party/blink/problematic/file.cc' in results[0].message)
2938 self.assertTrue('some/cpp/ok/file.cc' not in results[1].message)
2939 self.assertTrue('some/cpp/problematic/file2.cc' in results[0].message)
2940 self.assertFalse('some/cpp/nocheck/file.cc' in results[0].message)
2941 self.assertFalse('some/cpp/nocheck/file.cc' in results[1].message)
2942 self.assertFalse('some/cpp/comment/file.cc' in results[0].message)
2943 self.assertFalse('some/cpp/comment/file.cc' in results[1].message)
2945 def testBannedCppRandomFunctions(self):
2948 'absl::InsecureBitGen',
2949 'std::linear_congruential_engine',
2950 'std::mersenne_twister_engine',
2951 'std::subtract_with_carry_engine',
2952 'std::discard_block_engine',
2953 'std::independent_bits_engine',
2954 'std::shuffle_order_engine',
2955 'std::minstd_rand0',
2959 'std::ranlux24_base',
2960 'std::ranlux48_base',
2964 'std::default_random_engine',
2965 'std::random_device',
2967 for banned_rng in banned_rngs:
2968 input_api = MockInputApi()
2970 MockFile('some/cpp/problematic/file.cc',
2971 [f'{banned_rng} engine;']),
2972 MockFile('third_party/blink/problematic/file.cc',
2973 [f'{banned_rng} engine;']),
2974 MockFile('third_party/ok/file.cc',
2975 [f'{banned_rng} engine;']),
2977 results = PRESUBMIT.CheckNoBannedFunctions(input_api, MockOutputApi())
2978 self.assertEqual(1, len(results), banned_rng)
2979 self.assertTrue('some/cpp/problematic/file.cc' in results[0].message,
2982 'third_party/blink/problematic/file.cc' in results[0].message,
2985 'third_party/ok/file.cc' in results[0].message, banned_rng)
2987 def testBannedIosObjcFunctions(self):
2988 input_api = MockInputApi()
2990 MockFile('some/ios/file.mm',
2991 ['TEST(SomeClassTest, SomeInteraction) {',
2993 MockFile('some/mac/file.mm',
2994 ['TEST(SomeClassTest, SomeInteraction) {',
2996 MockFile('another/ios_file.mm',
2997 ['class SomeTest : public testing::Test {};']),
2998 MockFile('some/ios/file_egtest.mm',
2999 ['- (void)testSomething { EXPECT_OCMOCK_VERIFY(aMock); }']),
3000 MockFile('some/ios/file_unittest.mm',
3001 ['TEST_F(SomeTest, TestThis) { EXPECT_OCMOCK_VERIFY(aMock); }']),
3004 errors = PRESUBMIT.CheckNoBannedFunctions(input_api, MockOutputApi())
3005 self.assertEqual(1, len(errors))
3006 self.assertTrue('some/ios/file.mm' in errors[0].message)
3007 self.assertTrue('another/ios_file.mm' in errors[0].message)
3008 self.assertTrue('some/mac/file.mm' not in errors[0].message)
3009 self.assertTrue('some/ios/file_egtest.mm' in errors[0].message)
3010 self.assertTrue('some/ios/file_unittest.mm' not in errors[0].message)
3012 def testBannedMojoFunctions(self):
3013 input_api = MockInputApi()
3015 MockFile('some/cpp/problematic/file2.cc',
3016 ['mojo::ConvertTo<>']),
3017 MockFile('third_party/blink/ok/file3.cc',
3018 ['mojo::ConvertTo<>']),
3019 MockFile('content/renderer/ok/file3.cc',
3020 ['mojo::ConvertTo<>']),
3023 results = PRESUBMIT.CheckNoBannedFunctions(input_api, MockOutputApi())
3025 # warnings are results[0], errors are results[1]
3026 self.assertEqual(1, len(results))
3027 self.assertTrue('some/cpp/problematic/file2.cc' in results[0].message)
3028 self.assertTrue('third_party/blink/ok/file3.cc' not in results[0].message)
3029 self.assertTrue('content/renderer/ok/file3.cc' not in results[0].message)
3031 def testBannedMojomPatterns(self):
3032 input_api = MockInputApi()
3034 MockFile('bad.mojom',
3036 ' handle<shared_buffer> buffer;',
3038 MockFile('good.mojom',
3040 ' mojo_base.mojom.ReadOnlySharedMemoryRegion region1;',
3041 ' mojo_base.mojom.WritableSharedMemoryRegion region2;',
3042 ' mojo_base.mojom.UnsafeSharedMemoryRegion region3;',
3046 results = PRESUBMIT.CheckNoBannedFunctions(input_api, MockOutputApi())
3048 # warnings are results[0], errors are results[1]
3049 self.assertEqual(1, len(results))
3050 self.assertTrue('bad.mojom' in results[0].message)
3051 self.assertTrue('good.mojom' not in results[0].message)
3053 class NoProductionCodeUsingTestOnlyFunctionsTest(unittest.TestCase):
3054 def testTruePositives(self):
3055 mock_input_api = MockInputApi()
3056 mock_input_api.files = [
3057 MockFile('some/path/foo.cc', ['foo_for_testing();']),
3058 MockFile('some/path/foo.mm', ['FooForTesting();']),
3059 MockFile('some/path/foo.cxx', ['FooForTests();']),
3060 MockFile('some/path/foo.cpp', ['foo_for_test();']),
3063 results = PRESUBMIT.CheckNoProductionCodeUsingTestOnlyFunctions(
3064 mock_input_api, MockOutputApi())
3065 self.assertEqual(1, len(results))
3066 self.assertEqual(4, len(results[0].items))
3067 self.assertTrue('foo.cc' in results[0].items[0])
3068 self.assertTrue('foo.mm' in results[0].items[1])
3069 self.assertTrue('foo.cxx' in results[0].items[2])
3070 self.assertTrue('foo.cpp' in results[0].items[3])
3072 def testFalsePositives(self):
3073 mock_input_api = MockInputApi()
3074 mock_input_api.files = [
3075 MockFile('some/path/foo.h', ['foo_for_testing();']),
3076 MockFile('some/path/foo.mm', ['FooForTesting() {']),
3077 MockFile('some/path/foo.cc', ['::FooForTests();']),
3078 MockFile('some/path/foo.cpp', ['// foo_for_test();']),
3079 MockFile('some/path/foo.cxx', ['foo_for_test(); // IN-TEST']),
3082 results = PRESUBMIT.CheckNoProductionCodeUsingTestOnlyFunctions(
3083 mock_input_api, MockOutputApi())
3084 self.assertEqual(0, len(results))
3086 def testAllowedFiles(self):
3087 mock_input_api = MockInputApi()
3088 mock_input_api.files = [
3089 MockFile('path/foo_unittest.cc', ['foo_for_testing();']),
3090 MockFile('path/bar_unittest_mac.cc', ['foo_for_testing();']),
3091 MockFile('path/baz_unittests.cc', ['foo_for_testing();']),
3094 results = PRESUBMIT.CheckNoProductionCodeUsingTestOnlyFunctions(
3095 mock_input_api, MockOutputApi())
3096 self.assertEqual(0, len(results))
3099 class NoProductionJavaCodeUsingTestOnlyFunctionsTest(unittest.TestCase):
3100 def testTruePositives(self):
3101 mock_input_api = MockInputApi()
3102 mock_input_api.files = [
3103 MockFile('dir/java/src/foo.java', ['FooForTesting();']),
3104 MockFile('dir/java/src/bar.java', ['FooForTests(x);']),
3105 MockFile('dir/java/src/baz.java', ['FooForTest(', 'y', ');']),
3106 MockFile('dir/java/src/mult.java', [
3107 'int x = SomethingLongHere()',
3108 ' * SomethingLongHereForTesting();'
3112 results = PRESUBMIT.CheckNoProductionCodeUsingTestOnlyFunctionsJava(
3113 mock_input_api, MockOutputApi())
3114 self.assertEqual(1, len(results))
3115 self.assertEqual(4, len(results[0].items))
3116 self.assertTrue('foo.java' in results[0].items[0])
3117 self.assertTrue('bar.java' in results[0].items[1])
3118 self.assertTrue('baz.java' in results[0].items[2])
3119 self.assertTrue('mult.java' in results[0].items[3])
3121 def testFalsePositives(self):
3122 mock_input_api = MockInputApi()
3123 mock_input_api.files = [
3124 MockFile('dir/java/src/foo.xml', ['FooForTesting();']),
3125 MockFile('dir/java/src/foo.java', ['FooForTests() {']),
3126 MockFile('dir/java/src/bar.java', ['// FooForTest();']),
3127 MockFile('dir/java/src/bar2.java', ['x = 1; // FooForTest();']),
3128 MockFile('dir/java/src/bar3.java', ['@VisibleForTesting']),
3129 MockFile('dir/java/src/bar4.java', ['@VisibleForTesting()']),
3130 MockFile('dir/java/src/bar5.java', [
3131 '@VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)'
3133 MockFile('dir/javatests/src/baz.java', ['FooForTest(', 'y', ');']),
3134 MockFile('dir/junit/src/baz.java', ['FooForTest(', 'y', ');']),
3135 MockFile('dir/junit/src/javadoc.java', [
3136 '/** Use FooForTest(); to obtain foo in tests.'
3139 MockFile('dir/junit/src/javadoc2.java', [
3141 ' * Use FooForTest(); to obtain foo in tests.'
3144 MockFile('dir/java/src/bar6.java', ['FooForTesting(); // IN-TEST']),
3147 results = PRESUBMIT.CheckNoProductionCodeUsingTestOnlyFunctionsJava(
3148 mock_input_api, MockOutputApi())
3149 self.assertEqual(0, len(results))
3152 class NewImagesWarningTest(unittest.TestCase):
3153 def testTruePositives(self):
3154 mock_input_api = MockInputApi()
3155 mock_input_api.files = [
3156 MockFile('dir/android/res/drawable/foo.png', []),
3157 MockFile('dir/android/res/drawable-v21/bar.svg', []),
3158 MockFile('dir/android/res/mipmap-v21-en/baz.webp', []),
3159 MockFile('dir/android/res_gshoe/drawable-mdpi/foobar.png', []),
3162 results = PRESUBMIT._CheckNewImagesWarning(mock_input_api, MockOutputApi())
3163 self.assertEqual(1, len(results))
3164 self.assertEqual(4, len(results[0].items))
3165 self.assertTrue('foo.png' in results[0].items[0].LocalPath())
3166 self.assertTrue('bar.svg' in results[0].items[1].LocalPath())
3167 self.assertTrue('baz.webp' in results[0].items[2].LocalPath())
3168 self.assertTrue('foobar.png' in results[0].items[3].LocalPath())
3170 def testFalsePositives(self):
3171 mock_input_api = MockInputApi()
3172 mock_input_api.files = [
3173 MockFile('dir/pngs/README.md', []),
3174 MockFile('java/test/res/drawable/foo.png', []),
3175 MockFile('third_party/blink/foo.png', []),
3176 MockFile('dir/third_party/libpng/src/foo.cc', ['foobar']),
3177 MockFile('dir/resources.webp/.gitignore', ['foo.png']),
3180 results = PRESUBMIT._CheckNewImagesWarning(mock_input_api, MockOutputApi())
3181 self.assertEqual(0, len(results))
3183 class ProductIconsTest(unittest.TestCase):
3185 mock_input_api = MockInputApi()
3186 mock_input_api.files = [
3187 MockFile('components/vector_icons/google_jetpack.icon', []),
3188 MockFile('components/vector_icons/generic_jetpack.icon', []),
3191 results = PRESUBMIT.CheckNoProductIconsAddedToPublicRepo(mock_input_api, MockOutputApi())
3192 self.assertEqual(1, len(results))
3193 self.assertEqual(1, len(results[0].items))
3194 self.assertTrue('google_jetpack.icon' in results[0].items[0])
3196 class CheckUniquePtrTest(unittest.TestCase):
3197 def testTruePositivesNullptr(self):
3198 mock_input_api = MockInputApi()
3199 mock_input_api.files = [
3200 MockFile('dir/baz.cc', ['std::unique_ptr<T>()']),
3201 MockFile('dir/baz-p.cc', ['std::unique_ptr<T<P>>()']),
3204 results = PRESUBMIT.CheckUniquePtrOnUpload(mock_input_api, MockOutputApi())
3205 self.assertEqual(1, len(results))
3206 self.assertTrue('nullptr' in results[0].message)
3207 self.assertEqual(2, len(results[0].items))
3208 self.assertTrue('baz.cc' in results[0].items[0])
3209 self.assertTrue('baz-p.cc' in results[0].items[1])
3211 def testTruePositivesConstructor(self):
3212 mock_input_api = MockInputApi()
3213 mock_input_api.files = [
3214 MockFile('dir/foo.cc', ['return std::unique_ptr<T>(foo);']),
3215 MockFile('dir/bar.mm', ['bar = std::unique_ptr<T>(foo)']),
3216 MockFile('dir/mult.cc', [
3218 ' std::unique_ptr<T>(barVeryVeryLongFooSoThatItWouldNotFitAbove);'
3220 MockFile('dir/mult2.cc', [
3221 'barVeryVeryLongLongBaaaaaarSoThatTheLineLimitIsAlmostReached =',
3222 ' std::unique_ptr<T>(foo);'
3224 MockFile('dir/mult3.cc', [
3225 'bar = std::unique_ptr<T>(',
3226 ' fooVeryVeryVeryLongStillGoingWellThisWillTakeAWhileFinallyThere);'
3228 MockFile('dir/multi_arg.cc', [
3229 'auto p = std::unique_ptr<std::pair<T, D>>(new std::pair(T, D));']),
3232 results = PRESUBMIT.CheckUniquePtrOnUpload(mock_input_api, MockOutputApi())
3233 self.assertEqual(1, len(results))
3234 self.assertTrue('std::make_unique' in results[0].message)
3235 self.assertEqual(6, len(results[0].items))
3236 self.assertTrue('foo.cc' in results[0].items[0])
3237 self.assertTrue('bar.mm' in results[0].items[1])
3238 self.assertTrue('mult.cc' in results[0].items[2])
3239 self.assertTrue('mult2.cc' in results[0].items[3])
3240 self.assertTrue('mult3.cc' in results[0].items[4])
3241 self.assertTrue('multi_arg.cc' in results[0].items[5])
3243 def testFalsePositives(self):
3244 mock_input_api = MockInputApi()
3245 mock_input_api.files = [
3246 MockFile('dir/foo.cc', ['return std::unique_ptr<T[]>(foo);']),
3247 MockFile('dir/bar.mm', ['bar = std::unique_ptr<T[]>(foo)']),
3248 MockFile('dir/file.cc', ['std::unique_ptr<T> p = Foo();']),
3249 MockFile('dir/baz.cc', [
3250 'std::unique_ptr<T> result = std::make_unique<T>();'
3252 MockFile('dir/baz2.cc', [
3253 'std::unique_ptr<T> result = std::make_unique<T>('
3255 MockFile('dir/nested.cc', ['set<std::unique_ptr<T>>();']),
3256 MockFile('dir/nested2.cc', ['map<U, std::unique_ptr<T>>();']),
3258 # Two-argument invocation of std::unique_ptr is exempt because there is
3259 # no equivalent using std::make_unique.
3260 MockFile('dir/multi_arg.cc', [
3261 'auto p = std::unique_ptr<T, D>(new T(), D());']),
3264 results = PRESUBMIT.CheckUniquePtrOnUpload(mock_input_api, MockOutputApi())
3265 self.assertEqual(0, len(results))
3267 class CheckNoDirectIncludesHeadersWhichRedefineStrCat(unittest.TestCase):
3268 def testBlocksDirectIncludes(self):
3269 mock_input_api = MockInputApi()
3270 mock_input_api.files = [
3271 MockFile('dir/foo_win.cc', ['#include "shlwapi.h"']),
3272 MockFile('dir/bar.h', ['#include <propvarutil.h>']),
3273 MockFile('dir/baz.h', ['#include <atlbase.h>']),
3274 MockFile('dir/jumbo.h', ['#include "sphelper.h"']),
3276 results = PRESUBMIT.CheckNoStrCatRedefines(mock_input_api, MockOutputApi())
3277 self.assertEqual(1, len(results))
3278 self.assertEqual(4, len(results[0].items))
3279 self.assertTrue('StrCat' in results[0].message)
3280 self.assertTrue('foo_win.cc' in results[0].items[0])
3281 self.assertTrue('bar.h' in results[0].items[1])
3282 self.assertTrue('baz.h' in results[0].items[2])
3283 self.assertTrue('jumbo.h' in results[0].items[3])
3285 def testAllowsToIncludeWrapper(self):
3286 mock_input_api = MockInputApi()
3287 mock_input_api.files = [
3288 MockFile('dir/baz_win.cc', ['#include "base/win/shlwapi.h"']),
3289 MockFile('dir/baz-win.h', ['#include "base/win/atl.h"']),
3291 results = PRESUBMIT.CheckNoStrCatRedefines(mock_input_api, MockOutputApi())
3292 self.assertEqual(0, len(results))
3294 def testAllowsToCreateWrapper(self):
3295 mock_input_api = MockInputApi()
3296 mock_input_api.files = [
3297 MockFile('base/win/shlwapi.h', [
3298 '#include <shlwapi.h>',
3299 '#include "base/win/windows_defines.inc"']),
3301 results = PRESUBMIT.CheckNoStrCatRedefines(mock_input_api, MockOutputApi())
3302 self.assertEqual(0, len(results))
3304 def testIgnoresNonImplAndHeaders(self):
3305 mock_input_api = MockInputApi()
3306 mock_input_api.files = [
3307 MockFile('dir/foo_win.txt', ['#include "shlwapi.h"']),
3308 MockFile('dir/bar.asm', ['#include <propvarutil.h>']),
3310 results = PRESUBMIT.CheckNoStrCatRedefines(mock_input_api, MockOutputApi())
3311 self.assertEqual(0, len(results))
3314 class StringTest(unittest.TestCase):
3315 """Tests ICU syntax check and translation screenshots check."""
3317 # An empty grd file.
3318 OLD_GRD_CONTENTS = """<?xml version="1.0" encoding="UTF-8"?>
3319 <grit latest_public_release="1" current_release="1">
3321 <messages></messages>
3325 # A grd file with a single message.
3326 NEW_GRD_CONTENTS1 = """<?xml version="1.0" encoding="UTF-8"?>
3327 <grit latest_public_release="1" current_release="1">
3330 <message name="IDS_TEST1">
3333 <message name="IDS_TEST_STRING_NON_TRANSLATEABLE1"
3334 translateable="false">
3335 Non translateable message 1, should be ignored
3337 <message name="IDS_TEST_STRING_ACCESSIBILITY"
3338 is_accessibility_with_no_ui="true">
3339 Accessibility label 1, should be ignored
3345 # A grd file with two messages.
3346 NEW_GRD_CONTENTS2 = """<?xml version="1.0" encoding="UTF-8"?>
3347 <grit latest_public_release="1" current_release="1">
3350 <message name="IDS_TEST1">
3353 <message name="IDS_TEST2">
3356 <message name="IDS_TEST_STRING_NON_TRANSLATEABLE2"
3357 translateable="false">
3358 Non translateable message 2, should be ignored
3364 # A grd file with one ICU syntax message without syntax errors.
3365 NEW_GRD_CONTENTS_ICU_SYNTAX_OK1 = """<?xml version="1.0" encoding="UTF-8"?>
3366 <grit latest_public_release="1" current_release="1">
3369 <message name="IDS_TEST1">
3371 =1 {Test text for numeric one}
3372 other {Test text for plural with {NUM} as number}}
3378 # A grd file with one ICU syntax message without syntax errors.
3379 NEW_GRD_CONTENTS_ICU_SYNTAX_OK2 = """<?xml version="1.0" encoding="UTF-8"?>
3380 <grit latest_public_release="1" current_release="1">
3383 <message name="IDS_TEST1">
3385 =1 {Different test text for numeric one}
3386 other {Different test text for plural with {NUM} as number}}
3392 # A grd file with one ICU syntax message with syntax errors (misses a comma).
3393 NEW_GRD_CONTENTS_ICU_SYNTAX_ERROR = """<?xml version="1.0" encoding="UTF-8"?>
3394 <grit latest_public_release="1" current_release="1">
3397 <message name="IDS_TEST1">
3399 =1 {Test text for numeric one}
3400 other {Test text for plural with {NUM} as number}}
3407 OLD_GRDP_CONTENTS = (
3408 '<?xml version="1.0" encoding="utf-8"?>',
3413 NEW_GRDP_CONTENTS1 = (
3414 '<?xml version="1.0" encoding="utf-8"?>',
3416 '<message name="IDS_PART_TEST1">',
3421 NEW_GRDP_CONTENTS2 = (
3422 '<?xml version="1.0" encoding="utf-8"?>',
3424 '<message name="IDS_PART_TEST1">',
3427 '<message name="IDS_PART_TEST2">',
3432 NEW_GRDP_CONTENTS3 = (
3433 '<?xml version="1.0" encoding="utf-8"?>',
3435 '<message name="IDS_PART_TEST1" desc="Description with typo.">',
3440 NEW_GRDP_CONTENTS4 = (
3441 '<?xml version="1.0" encoding="utf-8"?>',
3443 '<message name="IDS_PART_TEST1" desc="Description with typo fixed.">',
3448 NEW_GRDP_CONTENTS5 = (
3449 '<?xml version="1.0" encoding="utf-8"?>',
3451 '<message name="IDS_PART_TEST1" meaning="Meaning with typo.">',
3456 NEW_GRDP_CONTENTS6 = (
3457 '<?xml version="1.0" encoding="utf-8"?>',
3459 '<message name="IDS_PART_TEST1" meaning="Meaning with typo fixed.">',
3464 # A grdp file with one ICU syntax message without syntax errors.
3465 NEW_GRDP_CONTENTS_ICU_SYNTAX_OK1 = (
3466 '<?xml version="1.0" encoding="utf-8"?>',
3468 '<message name="IDS_PART_TEST1">',
3470 '=1 {Test text for numeric one}',
3471 'other {Test text for plural with {NUM} as number}}',
3474 # A grdp file with one ICU syntax message without syntax errors.
3475 NEW_GRDP_CONTENTS_ICU_SYNTAX_OK2 = (
3476 '<?xml version="1.0" encoding="utf-8"?>',
3478 '<message name="IDS_PART_TEST1">',
3480 '=1 {Different test text for numeric one}',
3481 'other {Different test text for plural with {NUM} as number}}',
3485 # A grdp file with one ICU syntax message with syntax errors (superfluent
3487 NEW_GRDP_CONTENTS_ICU_SYNTAX_ERROR = (
3488 '<?xml version="1.0" encoding="utf-8"?>',
3490 '<message name="IDS_PART_TEST1">',
3492 '= 1 {Test text for numeric one}',
3493 'other {Test text for plural with {NUM} as number}}',
3497 VALID_SHA1 = ('0000000000000000000000000000000000000000',)
3498 DO_NOT_UPLOAD_PNG_MESSAGE = ('Do not include actual screenshots in the '
3500 'tools/translate/upload_screenshots.py to '
3501 'upload them instead:')
3502 ADD_SIGNATURES_MESSAGE = ('You are adding UI strings.\n'
3503 'To ensure the best translations, take '
3504 'screenshots of the relevant UI '
3505 '(https://g.co/chrome/translation) and add '
3506 'these files to your changelist:')
3507 REMOVE_SIGNATURES_MESSAGE = ('You removed strings associated with these '
3509 ICU_SYNTAX_ERROR_MESSAGE = ('ICU syntax errors were found in the following '
3510 'strings (problems or feedback? Contact '
3511 'rainhard@chromium.org):')
3512 SHA1_FORMAT_MESSAGE = ('The following files do not seem to contain valid sha1 '
3513 'hashes. Make sure they contain hashes created by '
3514 'tools/translate/upload_screenshots.py:')
3516 def makeInputApi(self, files):
3517 input_api = MockInputApi()
3518 input_api.files = files
3519 # Override os_path.exists because the presubmit uses the actual
3521 input_api.CreateMockFileInPath(
3522 [x.LocalPath() for x in input_api.AffectedFiles(include_deletes=True)])
3525 """ CL modified and added messages, but didn't add any screenshots."""
3526 def testNoScreenshots(self):
3527 # No new strings (file contents same). Should not warn.
3528 input_api = self.makeInputApi([
3529 MockAffectedFile('test.grd', self.NEW_GRD_CONTENTS1,
3530 self.NEW_GRD_CONTENTS1, action='M'),
3531 MockAffectedFile('part.grdp', self.NEW_GRDP_CONTENTS1,
3532 self.NEW_GRDP_CONTENTS1, action='M')])
3533 warnings = PRESUBMIT.CheckStrings(input_api, MockOutputApi())
3534 self.assertEqual(0, len(warnings))
3536 # Add two new strings. Should have two warnings.
3537 input_api = self.makeInputApi([
3538 MockAffectedFile('test.grd', self.NEW_GRD_CONTENTS2,
3539 self.NEW_GRD_CONTENTS1, action='M'),
3540 MockAffectedFile('part.grdp', self.NEW_GRDP_CONTENTS2,
3541 self.NEW_GRDP_CONTENTS1, action='M')])
3542 warnings = PRESUBMIT.CheckStrings(input_api, MockOutputApi())
3543 self.assertEqual(1, len(warnings))
3544 self.assertEqual(self.ADD_SIGNATURES_MESSAGE, warnings[0].message)
3545 self.assertEqual('error', warnings[0].type)
3547 os.path.join('part_grdp', 'IDS_PART_TEST2.png.sha1'),
3548 os.path.join('test_grd', 'IDS_TEST2.png.sha1')],
3551 # Add four new strings. Should have four warnings.
3552 input_api = self.makeInputApi([
3553 MockAffectedFile('test.grd', self.NEW_GRD_CONTENTS2,
3554 self.OLD_GRD_CONTENTS, action='M'),
3555 MockAffectedFile('part.grdp', self.NEW_GRDP_CONTENTS2,
3556 self.OLD_GRDP_CONTENTS, action='M')])
3557 warnings = PRESUBMIT.CheckStrings(input_api, MockOutputApi())
3558 self.assertEqual(1, len(warnings))
3559 self.assertEqual('error', warnings[0].type)
3560 self.assertEqual(self.ADD_SIGNATURES_MESSAGE, warnings[0].message)
3562 os.path.join('part_grdp', 'IDS_PART_TEST1.png.sha1'),
3563 os.path.join('part_grdp', 'IDS_PART_TEST2.png.sha1'),
3564 os.path.join('test_grd', 'IDS_TEST1.png.sha1'),
3565 os.path.join('test_grd', 'IDS_TEST2.png.sha1'),
3566 ], warnings[0].items)
3568 def testModifiedMessageDescription(self):
3569 # CL modified a message description for a message that does not yet have a
3570 # screenshot. Should not warn.
3571 input_api = self.makeInputApi([
3572 MockAffectedFile('part.grdp', self.NEW_GRDP_CONTENTS3,
3573 self.NEW_GRDP_CONTENTS4, action='M')])
3574 warnings = PRESUBMIT.CheckStrings(input_api, MockOutputApi())
3575 self.assertEqual(0, len(warnings))
3577 # CL modified a message description for a message that already has a
3578 # screenshot. Should not warn.
3579 input_api = self.makeInputApi([
3580 MockAffectedFile('part.grdp', self.NEW_GRDP_CONTENTS3,
3581 self.NEW_GRDP_CONTENTS4, action='M'),
3582 MockFile(os.path.join('part_grdp', 'IDS_PART_TEST1.png.sha1'),
3583 self.VALID_SHA1, action='A')])
3584 warnings = PRESUBMIT.CheckStrings(input_api, MockOutputApi())
3585 self.assertEqual(0, len(warnings))
3587 def testModifiedMessageMeaning(self):
3588 # CL modified a message meaning for a message that does not yet have a
3589 # screenshot. Should warn.
3590 input_api = self.makeInputApi([
3591 MockAffectedFile('part.grdp', self.NEW_GRDP_CONTENTS5,
3592 self.NEW_GRDP_CONTENTS6, action='M')])
3593 warnings = PRESUBMIT.CheckStrings(input_api, MockOutputApi())
3594 self.assertEqual(1, len(warnings))
3596 # CL modified a message meaning for a message that already has a
3597 # screenshot. Should not warn.
3598 input_api = self.makeInputApi([
3599 MockAffectedFile('part.grdp', self.NEW_GRDP_CONTENTS5,
3600 self.NEW_GRDP_CONTENTS6, action='M'),
3601 MockFile(os.path.join('part_grdp', 'IDS_PART_TEST1.png.sha1'),
3602 self.VALID_SHA1, action='A')])
3603 warnings = PRESUBMIT.CheckStrings(input_api, MockOutputApi())
3604 self.assertEqual(0, len(warnings))
3606 def testModifiedIntroducedInvalidSha1(self):
3607 # CL modified a message and the sha1 file changed to invalid
3608 input_api = self.makeInputApi([
3609 MockAffectedFile('part.grdp', self.NEW_GRDP_CONTENTS5,
3610 self.NEW_GRDP_CONTENTS6, action='M'),
3611 MockAffectedFile(os.path.join('part_grdp', 'IDS_PART_TEST1.png.sha1'),
3612 ('some invalid sha1',), self.VALID_SHA1, action='M')])
3613 warnings = PRESUBMIT.CheckStrings(input_api, MockOutputApi())
3614 self.assertEqual(1, len(warnings))
3616 def testPngAddedSha1NotAdded(self):
3617 # CL added one new message in a grd file and added the png file associated
3618 # with it, but did not add the corresponding sha1 file. This should warn
3620 # - Once for the added png file (because we don't want developers to upload
3622 # - Once for the missing .sha1 file
3623 input_api = self.makeInputApi([
3626 self.NEW_GRD_CONTENTS1,
3627 self.OLD_GRD_CONTENTS,
3630 os.path.join('test_grd', 'IDS_TEST1.png'), 'binary', action='A')
3632 warnings = PRESUBMIT.CheckStrings(input_api, MockOutputApi())
3633 self.assertEqual(2, len(warnings))
3634 self.assertEqual('error', warnings[0].type)
3635 self.assertEqual(self.DO_NOT_UPLOAD_PNG_MESSAGE, warnings[0].message)
3636 self.assertEqual([os.path.join('test_grd', 'IDS_TEST1.png')],
3638 self.assertEqual('error', warnings[1].type)
3639 self.assertEqual(self.ADD_SIGNATURES_MESSAGE, warnings[1].message)
3640 self.assertEqual([os.path.join('test_grd', 'IDS_TEST1.png.sha1')],
3643 # CL added two messages (one in grd, one in grdp) and added the png files
3644 # associated with the messages, but did not add the corresponding sha1
3645 # files. This should warn twice:
3646 # - Once for the added png files (because we don't want developers to upload
3648 # - Once for the missing .sha1 files
3649 input_api = self.makeInputApi([
3653 self.NEW_GRD_CONTENTS1,
3654 self.OLD_GRD_CONTENTS,
3658 self.NEW_GRDP_CONTENTS1,
3659 self.OLD_GRDP_CONTENTS,
3663 os.path.join('test_grd', 'IDS_TEST1.png'), 'binary', action='A'),
3665 os.path.join('part_grdp', 'IDS_PART_TEST1.png'), 'binary',
3668 warnings = PRESUBMIT.CheckStrings(input_api, MockOutputApi())
3669 self.assertEqual(2, len(warnings))
3670 self.assertEqual('error', warnings[0].type)
3671 self.assertEqual(self.DO_NOT_UPLOAD_PNG_MESSAGE, warnings[0].message)
3672 self.assertEqual([os.path.join('part_grdp', 'IDS_PART_TEST1.png'),
3673 os.path.join('test_grd', 'IDS_TEST1.png')],
3675 self.assertEqual('error', warnings[0].type)
3676 self.assertEqual(self.ADD_SIGNATURES_MESSAGE, warnings[1].message)
3677 self.assertEqual([os.path.join('part_grdp', 'IDS_PART_TEST1.png.sha1'),
3678 os.path.join('test_grd', 'IDS_TEST1.png.sha1')],
3682 def testScreenshotsWithSha1(self):
3683 # CL added four messages (two each in a grd and grdp) and their
3684 # corresponding .sha1 files. No warnings.
3685 input_api = self.makeInputApi([
3689 self.NEW_GRD_CONTENTS2,
3690 self.OLD_GRD_CONTENTS,
3694 self.NEW_GRDP_CONTENTS2,
3695 self.OLD_GRDP_CONTENTS,
3699 os.path.join('test_grd', 'IDS_TEST1.png.sha1'),
3703 os.path.join('test_grd', 'IDS_TEST2.png.sha1'),
3704 ('0000000000000000000000000000000000000000', ''),
3707 os.path.join('part_grdp', 'IDS_PART_TEST1.png.sha1'),
3711 os.path.join('part_grdp', 'IDS_PART_TEST2.png.sha1'),
3715 warnings = PRESUBMIT.CheckStrings(input_api, MockOutputApi())
3716 self.assertEqual([], warnings)
3719 def testScreenshotsWithInvalidSha1(self):
3720 input_api = self.makeInputApi([
3724 self.NEW_GRD_CONTENTS2,
3725 self.OLD_GRD_CONTENTS,
3729 self.NEW_GRDP_CONTENTS2,
3730 self.OLD_GRDP_CONTENTS,
3734 os.path.join('test_grd', 'IDS_TEST1.png.sha1'),
3738 os.path.join('test_grd', 'IDS_TEST2.png.sha1'),
3742 os.path.join('part_grdp', 'IDS_PART_TEST1.png.sha1'),
3746 os.path.join('part_grdp', 'IDS_PART_TEST2.png.sha1'),
3750 warnings = PRESUBMIT.CheckStrings(input_api, MockOutputApi())
3751 self.assertEqual(1, len(warnings))
3752 self.assertEqual('error', warnings[0].type)
3753 self.assertEqual(self.SHA1_FORMAT_MESSAGE, warnings[0].message)
3754 self.assertEqual([os.path.join('test_grd', 'IDS_TEST2.png.sha1')],
3758 def testScreenshotsRemovedWithSha1(self):
3759 # Replace new contents with old contents in grd and grp files, removing
3760 # IDS_TEST1, IDS_TEST2, IDS_PART_TEST1 and IDS_PART_TEST2.
3761 # Should warn to remove the sha1 files associated with these strings.
3762 input_api = self.makeInputApi([
3766 self.OLD_GRD_CONTENTS, # new_contents
3767 self.NEW_GRD_CONTENTS2, # old_contents
3771 self.OLD_GRDP_CONTENTS, # new_contents
3772 self.NEW_GRDP_CONTENTS2, # old_contents
3775 MockFile(os.path.join('test_grd', 'IDS_TEST1.png.sha1'),
3776 self.VALID_SHA1, ''),
3777 MockFile(os.path.join('test_grd', 'IDS_TEST2.png.sha1'),
3778 self.VALID_SHA1, ''),
3779 MockFile(os.path.join('part_grdp', 'IDS_PART_TEST1.png.sha1'),
3780 self.VALID_SHA1, ''),
3781 MockFile(os.path.join('part_grdp', 'IDS_PART_TEST2.png.sha1'),
3782 self.VALID_SHA1, '')
3784 warnings = PRESUBMIT.CheckStrings(input_api, MockOutputApi())
3785 self.assertEqual(1, len(warnings))
3786 self.assertEqual('error', warnings[0].type)
3787 self.assertEqual(self.REMOVE_SIGNATURES_MESSAGE, warnings[0].message)
3789 os.path.join('part_grdp', 'IDS_PART_TEST1.png.sha1'),
3790 os.path.join('part_grdp', 'IDS_PART_TEST2.png.sha1'),
3791 os.path.join('test_grd', 'IDS_TEST1.png.sha1'),
3792 os.path.join('test_grd', 'IDS_TEST2.png.sha1')
3793 ], warnings[0].items)
3795 # Same as above, but this time one of the .sha1 files is also removed.
3796 input_api = self.makeInputApi([
3800 self.OLD_GRD_CONTENTS, # new_contents
3801 self.NEW_GRD_CONTENTS2, # old_contents
3805 self.OLD_GRDP_CONTENTS, # new_contents
3806 self.NEW_GRDP_CONTENTS2, # old_contents
3809 MockFile(os.path.join('test_grd', 'IDS_TEST1.png.sha1'),
3810 self.VALID_SHA1, ''),
3811 MockFile(os.path.join('part_grdp', 'IDS_PART_TEST1.png.sha1'),
3812 self.VALID_SHA1, ''),
3815 os.path.join('test_grd', 'IDS_TEST2.png.sha1'),
3820 os.path.join('part_grdp', 'IDS_PART_TEST2.png.sha1'),
3825 warnings = PRESUBMIT.CheckStrings(input_api, MockOutputApi())
3826 self.assertEqual(1, len(warnings))
3827 self.assertEqual('error', warnings[0].type)
3828 self.assertEqual(self.REMOVE_SIGNATURES_MESSAGE, warnings[0].message)
3829 self.assertEqual([os.path.join('part_grdp', 'IDS_PART_TEST1.png.sha1'),
3830 os.path.join('test_grd', 'IDS_TEST1.png.sha1')
3831 ], warnings[0].items)
3833 # Remove all sha1 files. There should be no warnings.
3834 input_api = self.makeInputApi([
3838 self.OLD_GRD_CONTENTS,
3839 self.NEW_GRD_CONTENTS2,
3843 self.OLD_GRDP_CONTENTS,
3844 self.NEW_GRDP_CONTENTS2,
3848 os.path.join('test_grd', 'IDS_TEST1.png.sha1'),
3852 os.path.join('test_grd', 'IDS_TEST2.png.sha1'),
3856 os.path.join('part_grdp', 'IDS_PART_TEST1.png.sha1'),
3860 os.path.join('part_grdp', 'IDS_PART_TEST2.png.sha1'),
3864 warnings = PRESUBMIT.CheckStrings(input_api, MockOutputApi())
3865 self.assertEqual([], warnings)
3867 def testIcuSyntax(self):
3868 # Add valid ICU syntax string. Should not raise an error.
3869 input_api = self.makeInputApi([
3870 MockAffectedFile('test.grd', self.NEW_GRD_CONTENTS_ICU_SYNTAX_OK2,
3871 self.NEW_GRD_CONTENTS1, action='M'),
3872 MockAffectedFile('part.grdp', self.NEW_GRDP_CONTENTS_ICU_SYNTAX_OK2,
3873 self.NEW_GRDP_CONTENTS1, action='M')])
3874 results = PRESUBMIT.CheckStrings(input_api, MockOutputApi())
3875 # We expect no ICU syntax errors.
3876 icu_errors = [e for e in results
3877 if e.message == self.ICU_SYNTAX_ERROR_MESSAGE]
3878 self.assertEqual(0, len(icu_errors))
3880 # Valid changes in ICU syntax. Should not raise an error.
3881 input_api = self.makeInputApi([
3882 MockAffectedFile('test.grd', self.NEW_GRD_CONTENTS_ICU_SYNTAX_OK2,
3883 self.NEW_GRD_CONTENTS_ICU_SYNTAX_OK1, action='M'),
3884 MockAffectedFile('part.grdp', self.NEW_GRDP_CONTENTS_ICU_SYNTAX_OK2,
3885 self.NEW_GRDP_CONTENTS_ICU_SYNTAX_OK1, action='M')])
3886 results = PRESUBMIT.CheckStrings(input_api, MockOutputApi())
3887 # We expect no ICU syntax errors.
3888 icu_errors = [e for e in results
3889 if e.message == self.ICU_SYNTAX_ERROR_MESSAGE]
3890 self.assertEqual(0, len(icu_errors))
3892 # Add invalid ICU syntax strings. Should raise two errors.
3893 input_api = self.makeInputApi([
3894 MockAffectedFile('test.grd', self.NEW_GRD_CONTENTS_ICU_SYNTAX_ERROR,
3895 self.NEW_GRD_CONTENTS1, action='M'),
3896 MockAffectedFile('part.grdp', self.NEW_GRDP_CONTENTS_ICU_SYNTAX_ERROR,
3897 self.NEW_GRD_CONTENTS1, action='M')])
3898 results = PRESUBMIT.CheckStrings(input_api, MockOutputApi())
3899 # We expect 2 ICU syntax errors.
3900 icu_errors = [e for e in results
3901 if e.message == self.ICU_SYNTAX_ERROR_MESSAGE]
3902 self.assertEqual(1, len(icu_errors))
3904 'IDS_TEST1: This message looks like an ICU plural, but does not follow '
3906 'IDS_PART_TEST1: Variant "= 1" is not valid for plural message'
3907 ], icu_errors[0].items)
3909 # Change two strings to have ICU syntax errors. Should raise two errors.
3910 input_api = self.makeInputApi([
3911 MockAffectedFile('test.grd', self.NEW_GRD_CONTENTS_ICU_SYNTAX_ERROR,
3912 self.NEW_GRD_CONTENTS_ICU_SYNTAX_OK1, action='M'),
3913 MockAffectedFile('part.grdp', self.NEW_GRDP_CONTENTS_ICU_SYNTAX_ERROR,
3914 self.NEW_GRDP_CONTENTS_ICU_SYNTAX_OK1, action='M')])
3915 results = PRESUBMIT.CheckStrings(input_api, MockOutputApi())
3916 # We expect 2 ICU syntax errors.
3917 icu_errors = [e for e in results
3918 if e.message == self.ICU_SYNTAX_ERROR_MESSAGE]
3919 self.assertEqual(1, len(icu_errors))
3921 'IDS_TEST1: This message looks like an ICU plural, but does not follow '
3923 'IDS_PART_TEST1: Variant "= 1" is not valid for plural message'
3924 ], icu_errors[0].items)
3927 class TranslationExpectationsTest(unittest.TestCase):
3928 ERROR_MESSAGE_FORMAT = (
3929 "Failed to get a list of translatable grd files. "
3930 "This happens when:\n"
3931 " - One of the modified grd or grdp files cannot be parsed or\n"
3932 " - %s is not updated.\n"
3935 REPO_ROOT = os.path.join('tools', 'translation', 'testdata')
3936 # This lists all .grd files under REPO_ROOT.
3937 EXPECTATIONS = os.path.join(REPO_ROOT,
3938 "translation_expectations.pyl")
3939 # This lists all .grd files under REPO_ROOT except unlisted.grd.
3940 EXPECTATIONS_WITHOUT_UNLISTED_FILE = os.path.join(
3941 REPO_ROOT, "translation_expectations_without_unlisted_file.pyl")
3943 # Tests that the presubmit doesn't return when no grd or grdp files are
3945 def testExpectationsNoModifiedGrd(self):
3946 input_api = MockInputApi()
3948 MockAffectedFile('not_used.txt', 'not used', 'not used', action='M')
3950 # Fake list of all grd files in the repo. This list is missing all grd/grdps
3951 # under tools/translation/testdata. This is OK because the presubmit won't
3952 # run in the first place since there are no modified grd/grps in input_api.
3953 grd_files = ['doesnt_exist_doesnt_matter.grd']
3954 warnings = PRESUBMIT.CheckTranslationExpectations(
3955 input_api, MockOutputApi(), self.REPO_ROOT, self.EXPECTATIONS,
3957 self.assertEqual(0, len(warnings))
3960 # Tests that the list of files passed to the presubmit matches the list of
3961 # files in the expectations.
3962 def testExpectationsSuccess(self):
3963 # Mock input file list needs a grd or grdp file in order to run the
3964 # presubmit. The file itself doesn't matter.
3965 input_api = MockInputApi()
3967 MockAffectedFile('dummy.grd', 'not used', 'not used', action='M')
3969 # List of all grd files in the repo.
3970 grd_files = ['test.grd', 'unlisted.grd', 'not_translated.grd',
3972 warnings = PRESUBMIT.CheckTranslationExpectations(
3973 input_api, MockOutputApi(), self.REPO_ROOT, self.EXPECTATIONS,
3975 self.assertEqual(0, len(warnings))
3977 # Tests that the presubmit warns when a file is listed in expectations, but
3978 # does not actually exist.
3979 def testExpectationsMissingFile(self):
3980 # Mock input file list needs a grd or grdp file in order to run the
3982 input_api = MockInputApi()
3984 MockAffectedFile('dummy.grd', 'not used', 'not used', action='M')
3986 # unlisted.grd is listed under tools/translation/testdata but is not
3987 # included in translation expectations.
3988 grd_files = ['unlisted.grd', 'not_translated.grd', 'internal.grd']
3989 warnings = PRESUBMIT.CheckTranslationExpectations(
3990 input_api, MockOutputApi(), self.REPO_ROOT, self.EXPECTATIONS,
3992 self.assertEqual(1, len(warnings))
3993 self.assertTrue(warnings[0].message.startswith(
3994 self.ERROR_MESSAGE_FORMAT % self.EXPECTATIONS))
3996 ("test.grd is listed in the translation expectations, "
3997 "but this grd file does not exist")
3998 in warnings[0].message)
4000 # Tests that the presubmit warns when a file is not listed in expectations but
4001 # does actually exist.
4002 def testExpectationsUnlistedFile(self):
4003 # Mock input file list needs a grd or grdp file in order to run the
4005 input_api = MockInputApi()
4007 MockAffectedFile('dummy.grd', 'not used', 'not used', action='M')
4009 # unlisted.grd is listed under tools/translation/testdata but is not
4010 # included in translation expectations.
4011 grd_files = ['test.grd', 'unlisted.grd', 'not_translated.grd',
4013 warnings = PRESUBMIT.CheckTranslationExpectations(
4014 input_api, MockOutputApi(), self.REPO_ROOT,
4015 self.EXPECTATIONS_WITHOUT_UNLISTED_FILE, grd_files)
4016 self.assertEqual(1, len(warnings))
4017 self.assertTrue(warnings[0].message.startswith(
4018 self.ERROR_MESSAGE_FORMAT % self.EXPECTATIONS_WITHOUT_UNLISTED_FILE))
4020 ("unlisted.grd appears to be translatable "
4021 "(because it contains <file> or <message> elements), "
4022 "but is not listed in the translation expectations.")
4023 in warnings[0].message)
4025 # Tests that the presubmit warns twice:
4026 # - for a non-existing file listed in expectations
4027 # - for an existing file not listed in expectations
4028 def testMultipleWarnings(self):
4029 # Mock input file list needs a grd or grdp file in order to run the
4031 input_api = MockInputApi()
4033 MockAffectedFile('dummy.grd', 'not used', 'not used', action='M')
4035 # unlisted.grd is listed under tools/translation/testdata but is not
4036 # included in translation expectations.
4037 # test.grd is not listed under tools/translation/testdata but is included
4038 # in translation expectations.
4039 grd_files = ['unlisted.grd', 'not_translated.grd', 'internal.grd']
4040 warnings = PRESUBMIT.CheckTranslationExpectations(
4041 input_api, MockOutputApi(), self.REPO_ROOT,
4042 self.EXPECTATIONS_WITHOUT_UNLISTED_FILE, grd_files)
4043 self.assertEqual(1, len(warnings))
4044 self.assertTrue(warnings[0].message.startswith(
4045 self.ERROR_MESSAGE_FORMAT % self.EXPECTATIONS_WITHOUT_UNLISTED_FILE))
4047 ("unlisted.grd appears to be translatable "
4048 "(because it contains <file> or <message> elements), "
4049 "but is not listed in the translation expectations.")
4050 in warnings[0].message)
4052 ("test.grd is listed in the translation expectations, "
4053 "but this grd file does not exist")
4054 in warnings[0].message)
4057 class DISABLETypoInTest(unittest.TestCase):
4059 def testPositive(self):
4060 # Verify the typo "DISABLE_" instead of "DISABLED_" in various contexts
4061 # where the desire is to disable a test.
4063 # Disabled on one platform:
4064 '#if defined(OS_WIN)\n'
4065 '#define MAYBE_FoobarTest DISABLE_FoobarTest\n'
4067 '#define MAYBE_FoobarTest FoobarTest\n'
4069 # Disabled on one platform spread cross lines:
4070 '#if defined(OS_WIN)\n'
4071 '#define MAYBE_FoobarTest \\\n'
4072 ' DISABLE_FoobarTest\n'
4074 '#define MAYBE_FoobarTest FoobarTest\n'
4076 # Disabled on all platforms:
4077 ' TEST_F(FoobarTest, DISABLE_Foo)\n{\n}',
4078 # Disabled on all platforms but multiple lines
4079 ' TEST_F(FoobarTest,\n DISABLE_foo){\n}\n',
4083 mock_input_api = MockInputApi()
4084 mock_input_api.files = [
4085 MockFile('some/path/foo_unittest.cc', test.splitlines()),
4088 results = PRESUBMIT.CheckNoDISABLETypoInTests(mock_input_api,
4093 msg=('expected len(results) == 1 but got %d in test: %s' %
4094 (len(results), test)))
4096 'foo_unittest.cc' in results[0].message,
4097 msg=('expected foo_unittest.cc in message but got %s in test %s' %
4098 (results[0].message, test)))
4100 def testIgnoreNotTestFiles(self):
4101 mock_input_api = MockInputApi()
4102 mock_input_api.files = [
4103 MockFile('some/path/foo.cc', 'TEST_F(FoobarTest, DISABLE_Foo)'),
4106 results = PRESUBMIT.CheckNoDISABLETypoInTests(mock_input_api,
4108 self.assertEqual(0, len(results))
4110 def testIgnoreDeletedFiles(self):
4111 mock_input_api = MockInputApi()
4112 mock_input_api.files = [
4113 MockFile('some/path/foo.cc', 'TEST_F(FoobarTest, Foo)', action='D'),
4116 results = PRESUBMIT.CheckNoDISABLETypoInTests(mock_input_api,
4118 self.assertEqual(0, len(results))
4120 class ForgettingMAYBEInTests(unittest.TestCase):
4121 def testPositive(self):
4123 '#if defined(HAS_ENERGY)\n'
4124 '#define MAYBE_CastExplosion DISABLED_CastExplosion\n'
4126 '#define MAYBE_CastExplosion CastExplosion\n'
4128 'TEST_F(ArchWizard, CastExplosion) {\n'
4129 '#if defined(ARCH_PRIEST_IN_PARTY)\n'
4130 '#define MAYBE_ArchPriest ArchPriest\n'
4132 '#define MAYBE_ArchPriest DISABLED_ArchPriest\n'
4134 'TEST_F(ArchPriest, CastNaturesBounty) {\n'
4135 '#if !defined(CRUSADER_IN_PARTY)\n'
4136 '#define MAYBE_Crusader \\\n'
4137 ' DISABLED_Crusader \n'
4139 '#define MAYBE_Crusader \\\n'
4145 '#if defined(LEARNED_BASIC_SKILLS)\n'
4146 '#define MAYBE_CastSteal \\\n'
4147 ' DISABLED_CastSteal \n'
4149 '#define MAYBE_CastSteal \\\n'
4156 mock_input_api = MockInputApi()
4157 mock_input_api.files = [
4158 MockFile('fantasyworld/classes_unittest.cc', test.splitlines()),
4160 results = PRESUBMIT.CheckForgettingMAYBEInTests(mock_input_api,
4162 self.assertEqual(4, len(results))
4163 self.assertTrue('CastExplosion' in results[0].message)
4164 self.assertTrue('fantasyworld/classes_unittest.cc:2' in results[0].message)
4165 self.assertTrue('ArchPriest' in results[1].message)
4166 self.assertTrue('fantasyworld/classes_unittest.cc:8' in results[1].message)
4167 self.assertTrue('Crusader' in results[2].message)
4168 self.assertTrue('fantasyworld/classes_unittest.cc:14' in results[2].message)
4169 self.assertTrue('CastSteal' in results[3].message)
4170 self.assertTrue('fantasyworld/classes_unittest.cc:24' in results[3].message)
4172 def testNegative(self):
4174 '#if defined(HAS_ENERGY)\n'
4175 '#define MAYBE_CastExplosion DISABLED_CastExplosion\n'
4177 '#define MAYBE_CastExplosion CastExplosion\n'
4179 'TEST_F(ArchWizard, MAYBE_CastExplosion) {\n'
4180 '#if defined(ARCH_PRIEST_IN_PARTY)\n'
4181 '#define MAYBE_ArchPriest ArchPriest\n'
4183 '#define MAYBE_ArchPriest DISABLED_ArchPriest\n'
4185 'TEST_F(MAYBE_ArchPriest, CastNaturesBounty) {\n'
4186 '#if !defined(CRUSADER_IN_PARTY)\n'
4187 '#define MAYBE_Crusader \\\n'
4188 ' DISABLED_Crusader \n'
4190 '#define MAYBE_Crusader \\\n'
4194 ' MAYBE_Crusader,\n'
4196 '#if defined(LEARNED_BASIC_SKILLS)\n'
4197 '#define MAYBE_CastSteal \\\n'
4198 ' DISABLED_CastSteal \n'
4200 '#define MAYBE_CastSteal \\\n'
4205 ' MAYBE_CastSteal) { }\n'
4208 mock_input_api = MockInputApi()
4209 mock_input_api.files = [
4210 MockFile('fantasyworld/classes_unittest.cc', test.splitlines()),
4212 results = PRESUBMIT.CheckForgettingMAYBEInTests(mock_input_api,
4214 self.assertEqual(0, len(results))
4216 class CheckFuzzTargetsTest(unittest.TestCase):
4218 def _check(self, files):
4219 mock_input_api = MockInputApi()
4220 mock_input_api.files = []
4221 for fname, contents in files.items():
4222 mock_input_api.files.append(MockFile(fname, contents.splitlines()))
4223 return PRESUBMIT.CheckFuzzTargetsOnUpload(mock_input_api, MockOutputApi())
4225 def testLibFuzzerSourcesIgnored(self):
4226 results = self._check({
4227 "third_party/lib/Fuzzer/FuzzerDriver.cpp": "LLVMFuzzerInitialize",
4229 self.assertEqual(results, [])
4231 def testNonCodeFilesIgnored(self):
4232 results = self._check({
4233 "README.md": "LLVMFuzzerInitialize",
4235 self.assertEqual(results, [])
4237 def testNoErrorHeaderPresent(self):
4238 results = self._check({
4240 "#include \"testing/libfuzzer/libfuzzer_exports.h\"\n" +
4241 "LLVMFuzzerInitialize"
4244 self.assertEqual(results, [])
4246 def testErrorMissingHeader(self):
4247 results = self._check({
4248 "fuzzer.cc": "LLVMFuzzerInitialize"
4250 self.assertEqual(len(results), 1)
4251 self.assertEqual(results[0].items, ['fuzzer.cc'])
4254 class SetNoParentTest(unittest.TestCase):
4255 def testSetNoParentTopLevelAllowed(self):
4256 mock_input_api = MockInputApi()
4257 mock_input_api.files = [
4258 MockAffectedFile('goat/OWNERS',
4261 'jochen@chromium.org',
4264 mock_output_api = MockOutputApi()
4265 errors = PRESUBMIT.CheckSetNoParent(mock_input_api, mock_output_api)
4266 self.assertEqual([], errors)
4268 def testSetNoParentMissing(self):
4269 mock_input_api = MockInputApi()
4270 mock_input_api.files = [
4271 MockAffectedFile('services/goat/OWNERS',
4274 'jochen@chromium.org',
4275 'per-file *.json=set noparent',
4276 'per-file *.json=jochen@chromium.org',
4279 mock_output_api = MockOutputApi()
4280 errors = PRESUBMIT.CheckSetNoParent(mock_input_api, mock_output_api)
4281 self.assertEqual(1, len(errors))
4282 self.assertTrue('goat/OWNERS:1' in errors[0].long_text)
4283 self.assertTrue('goat/OWNERS:3' in errors[0].long_text)
4285 def testSetNoParentWithCorrectRule(self):
4286 mock_input_api = MockInputApi()
4287 mock_input_api.files = [
4288 MockAffectedFile('services/goat/OWNERS',
4291 'file://ipc/SECURITY_OWNERS',
4292 'per-file *.json=set noparent',
4293 'per-file *.json=file://ipc/SECURITY_OWNERS',
4296 mock_output_api = MockOutputApi()
4297 errors = PRESUBMIT.CheckSetNoParent(mock_input_api, mock_output_api)
4298 self.assertEqual([], errors)
4301 class MojomStabilityCheckTest(unittest.TestCase):
4302 def runTestWithAffectedFiles(self, affected_files):
4303 mock_input_api = MockInputApi()
4304 mock_input_api.files = affected_files
4305 mock_output_api = MockOutputApi()
4306 return PRESUBMIT.CheckStableMojomChanges(
4307 mock_input_api, mock_output_api)
4309 def testSafeChangePasses(self):
4310 errors = self.runTestWithAffectedFiles([
4311 MockAffectedFile('foo/foo.mojom',
4312 ['[Stable] struct S { [MinVersion=1] int32 x; };'],
4313 old_contents=['[Stable] struct S {};'])
4315 self.assertEqual([], errors)
4317 def testBadChangeFails(self):
4318 errors = self.runTestWithAffectedFiles([
4319 MockAffectedFile('foo/foo.mojom',
4320 ['[Stable] struct S { int32 x; };'],
4321 old_contents=['[Stable] struct S {};'])
4323 self.assertEqual(1, len(errors))
4324 self.assertTrue('not backward-compatible' in errors[0].message)
4326 def testDeletedFile(self):
4327 """Regression test for https://crbug.com/1091407."""
4328 errors = self.runTestWithAffectedFiles([
4329 MockAffectedFile('a.mojom', [], old_contents=['struct S {};'],
4331 MockAffectedFile('b.mojom',
4332 ['struct S {}; struct T { S s; };'],
4333 old_contents=['import "a.mojom"; struct T { S s; };'])
4335 self.assertEqual([], errors)
4337 class CheckForUseOfChromeAppsDeprecationsTest(unittest.TestCase):
4339 ERROR_MSG_PIECE = 'technologies which will soon be deprecated'
4341 # Each positive test is also a naive negative test for the other cases.
4343 def testWarningNMF(self):
4344 mock_input_api = MockInputApi()
4345 mock_input_api.files = [
4348 ['"program"', '"Z":"content"', 'B'],
4350 scm_diff='\n'.join([
4351 '--- foo.NMF.old 2020-12-02 20:40:54.430676385 +0100',
4352 '+++ foo.NMF.new 2020-12-02 20:41:02.086700197 +0100',
4359 mock_output_api = MockOutputApi()
4360 errors = PRESUBMIT.CheckForUseOfChromeAppsDeprecations(mock_input_api,
4362 self.assertEqual(1, len(errors))
4363 self.assertTrue( self.ERROR_MSG_PIECE in errors[0].message)
4364 self.assertTrue( 'foo.NMF' in errors[0].message)
4366 def testWarningManifest(self):
4367 mock_input_api = MockInputApi()
4368 mock_input_api.files = [
4371 ['"app":', '"Z":"content"', 'B'],
4373 scm_diff='\n'.join([
4374 '--- manifest.json.old 2020-12-02 20:40:54.430676385 +0100',
4375 '+++ manifest.json.new 2020-12-02 20:41:02.086700197 +0100',
4382 mock_output_api = MockOutputApi()
4383 errors = PRESUBMIT.CheckForUseOfChromeAppsDeprecations(mock_input_api,
4385 self.assertEqual(1, len(errors))
4386 self.assertTrue( self.ERROR_MSG_PIECE in errors[0].message)
4387 self.assertTrue( 'manifest.json' in errors[0].message)
4389 def testOKWarningManifestWithoutApp(self):
4390 mock_input_api = MockInputApi()
4391 mock_input_api.files = [
4394 ['"name":', '"Z":"content"', 'B'],
4396 scm_diff='\n'.join([
4397 '--- manifest.json.old 2020-12-02 20:40:54.430676385 +0100',
4398 '+++ manifest.json.new 2020-12-02 20:41:02.086700197 +0100',
4405 mock_output_api = MockOutputApi()
4406 errors = PRESUBMIT.CheckForUseOfChromeAppsDeprecations(mock_input_api,
4408 self.assertEqual(0, len(errors))
4410 def testWarningPPAPI(self):
4411 mock_input_api = MockInputApi()
4412 mock_input_api.files = [
4415 ['A', '#include <ppapi.h>', 'B'],
4417 scm_diff='\n'.join([
4418 '--- foo.hpp.old 2020-12-02 20:40:54.430676385 +0100',
4419 '+++ foo.hpp.new 2020-12-02 20:41:02.086700197 +0100',
4422 '+#include <ppapi.h>',
4426 mock_output_api = MockOutputApi()
4427 errors = PRESUBMIT.CheckForUseOfChromeAppsDeprecations(mock_input_api,
4429 self.assertEqual(1, len(errors))
4430 self.assertTrue( self.ERROR_MSG_PIECE in errors[0].message)
4431 self.assertTrue( 'foo.hpp' in errors[0].message)
4433 def testNoWarningPPAPI(self):
4434 mock_input_api = MockInputApi()
4435 mock_input_api.files = [
4438 ['A', 'Peppapig', 'B'],
4440 scm_diff='\n'.join([
4441 '--- foo.txt.old 2020-12-02 20:40:54.430676385 +0100',
4442 '+++ foo.txt.new 2020-12-02 20:41:02.086700197 +0100',
4449 mock_output_api = MockOutputApi()
4450 errors = PRESUBMIT.CheckForUseOfChromeAppsDeprecations(mock_input_api,
4452 self.assertEqual(0, len(errors))
4454 class CheckDeprecationOfPreferencesTest(unittest.TestCase):
4455 # Test that a warning is generated if a preference registration is removed
4456 # from a random file.
4457 def testWarning(self):
4458 mock_input_api = MockInputApi()
4459 mock_input_api.files = [
4463 ['A', 'prefs->RegisterStringPref("foo", "default");', 'B'],
4464 scm_diff='\n'.join([
4465 '--- foo.cc.old 2020-12-02 20:40:54.430676385 +0100',
4466 '+++ foo.cc.new 2020-12-02 20:41:02.086700197 +0100',
4469 '-prefs->RegisterStringPref("foo", "default");',
4473 mock_output_api = MockOutputApi()
4474 errors = PRESUBMIT.CheckDeprecationOfPreferences(mock_input_api,
4476 self.assertEqual(1, len(errors))
4478 'Discovered possible removal of preference registrations' in
4481 # Test that a warning is inhibited if the preference registration was moved
4482 # to the deprecation functions in browser prefs.
4483 def testNoWarningForMigration(self):
4484 mock_input_api = MockInputApi()
4485 mock_input_api.files = [
4486 # RegisterStringPref was removed from foo.cc.
4490 ['A', 'prefs->RegisterStringPref("foo", "default");', 'B'],
4491 scm_diff='\n'.join([
4492 '--- foo.cc.old 2020-12-02 20:40:54.430676385 +0100',
4493 '+++ foo.cc.new 2020-12-02 20:41:02.086700197 +0100',
4496 '-prefs->RegisterStringPref("foo", "default");',
4499 # But the preference was properly migrated.
4501 'chrome/browser/prefs/browser_prefs.cc',
4503 '// BEGIN_MIGRATE_OBSOLETE_LOCAL_STATE_PREFS',
4504 '// END_MIGRATE_OBSOLETE_LOCAL_STATE_PREFS',
4505 '// BEGIN_MIGRATE_OBSOLETE_PROFILE_PREFS',
4506 'prefs->RegisterStringPref("foo", "default");',
4507 '// END_MIGRATE_OBSOLETE_PROFILE_PREFS',
4510 '// BEGIN_MIGRATE_OBSOLETE_LOCAL_STATE_PREFS',
4511 '// END_MIGRATE_OBSOLETE_LOCAL_STATE_PREFS',
4512 '// BEGIN_MIGRATE_OBSOLETE_PROFILE_PREFS',
4513 '// END_MIGRATE_OBSOLETE_PROFILE_PREFS',
4515 scm_diff='\n'.join([
4516 '--- browser_prefs.cc.old 2020-12-02 20:51:40.812686731 +0100',
4517 '+++ browser_prefs.cc.new 2020-12-02 20:52:02.936755539 +0100',
4519 ' // END_MIGRATE_OBSOLETE_LOCAL_STATE_PREFS',
4520 ' // BEGIN_MIGRATE_OBSOLETE_PROFILE_PREFS',
4521 '+prefs->RegisterStringPref("foo", "default");',
4522 ' // END_MIGRATE_OBSOLETE_PROFILE_PREFS']),
4525 mock_output_api = MockOutputApi()
4526 errors = PRESUBMIT.CheckDeprecationOfPreferences(mock_input_api,
4528 self.assertEqual(0, len(errors))
4530 # Test that a warning is NOT inhibited if the preference registration was
4531 # moved to a place outside of the migration functions in browser_prefs.cc
4532 def testWarningForImproperMigration(self):
4533 mock_input_api = MockInputApi()
4534 mock_input_api.files = [
4535 # RegisterStringPref was removed from foo.cc.
4539 ['A', 'prefs->RegisterStringPref("foo", "default");', 'B'],
4540 scm_diff='\n'.join([
4541 '--- foo.cc.old 2020-12-02 20:40:54.430676385 +0100',
4542 '+++ foo.cc.new 2020-12-02 20:41:02.086700197 +0100',
4545 '-prefs->RegisterStringPref("foo", "default");',
4548 # The registration call was moved to a place in browser_prefs.cc that
4549 # is outside the migration functions.
4551 'chrome/browser/prefs/browser_prefs.cc',
4553 'prefs->RegisterStringPref("foo", "default");',
4554 '// BEGIN_MIGRATE_OBSOLETE_LOCAL_STATE_PREFS',
4555 '// END_MIGRATE_OBSOLETE_LOCAL_STATE_PREFS',
4556 '// BEGIN_MIGRATE_OBSOLETE_PROFILE_PREFS',
4557 '// END_MIGRATE_OBSOLETE_PROFILE_PREFS',
4560 '// BEGIN_MIGRATE_OBSOLETE_LOCAL_STATE_PREFS',
4561 '// END_MIGRATE_OBSOLETE_LOCAL_STATE_PREFS',
4562 '// BEGIN_MIGRATE_OBSOLETE_PROFILE_PREFS',
4563 '// END_MIGRATE_OBSOLETE_PROFILE_PREFS',
4565 scm_diff='\n'.join([
4566 '--- browser_prefs.cc.old 2020-12-02 20:51:40.812686731 +0100',
4567 '+++ browser_prefs.cc.new 2020-12-02 20:52:02.936755539 +0100',
4569 '+prefs->RegisterStringPref("foo", "default");',
4570 ' // BEGIN_MIGRATE_OBSOLETE_LOCAL_STATE_PREFS',
4571 ' // END_MIGRATE_OBSOLETE_LOCAL_STATE_PREFS']),
4574 mock_output_api = MockOutputApi()
4575 errors = PRESUBMIT.CheckDeprecationOfPreferences(mock_input_api,
4577 self.assertEqual(1, len(errors))
4579 'Discovered possible removal of preference registrations' in
4582 # Check that the presubmit fails if a marker line in browser_prefs.cc is
4584 def testDeletedMarkerRaisesError(self):
4585 mock_input_api = MockInputApi()
4586 mock_input_api.files = [
4587 MockAffectedFile('chrome/browser/prefs/browser_prefs.cc',
4589 '// BEGIN_MIGRATE_OBSOLETE_LOCAL_STATE_PREFS',
4590 '// END_MIGRATE_OBSOLETE_LOCAL_STATE_PREFS',
4591 '// BEGIN_MIGRATE_OBSOLETE_PROFILE_PREFS',
4592 # The following line is deleted for this test
4593 # '// END_MIGRATE_OBSOLETE_PROFILE_PREFS',
4596 mock_output_api = MockOutputApi()
4597 errors = PRESUBMIT.CheckDeprecationOfPreferences(mock_input_api,
4599 self.assertEqual(1, len(errors))
4601 'Broken .*MIGRATE_OBSOLETE_.*_PREFS markers in browser_prefs.cc.',
4604 class CheckCrosApiNeedBrowserTestTest(unittest.TestCase):
4605 def testWarning(self):
4606 mock_input_api = MockInputApi()
4607 mock_output_api = MockOutputApi()
4608 mock_input_api.files = [
4609 MockAffectedFile('chromeos/crosapi/mojom/example.mojom', [], action='A'),
4611 result = PRESUBMIT.CheckCrosApiNeedBrowserTest(mock_input_api, mock_output_api)
4612 self.assertEqual(1, len(result))
4613 self.assertEqual(result[0].type, 'warning')
4615 def testNoWarningWithBrowserTest(self):
4616 mock_input_api = MockInputApi()
4617 mock_output_api = MockOutputApi()
4618 mock_input_api.files = [
4619 MockAffectedFile('chromeos/crosapi/mojom/example.mojom', [], action='A'),
4620 MockAffectedFile('chrome/example_browsertest.cc', [], action='A'),
4622 result = PRESUBMIT.CheckCrosApiNeedBrowserTest(mock_input_api, mock_output_api)
4623 self.assertEqual(0, len(result))
4625 def testNoWarningModifyCrosapi(self):
4626 mock_input_api = MockInputApi()
4627 mock_output_api = MockOutputApi()
4628 mock_input_api.files = [
4629 MockAffectedFile('chromeos/crosapi/mojom/example.mojom', [], action='M'),
4631 result = PRESUBMIT.CheckCrosApiNeedBrowserTest(mock_input_api, mock_output_api)
4632 self.assertEqual(0, len(result))
4634 def testNoWarningAddNonMojomFile(self):
4635 mock_input_api = MockInputApi()
4636 mock_output_api = MockOutputApi()
4637 mock_input_api.files = [
4638 MockAffectedFile('chromeos/crosapi/mojom/example.cc', [], action='A'),
4640 result = PRESUBMIT.CheckCrosApiNeedBrowserTest(mock_input_api, mock_output_api)
4641 self.assertEqual(0, len(result))
4643 def testNoWarningNoneRelatedMojom(self):
4644 mock_input_api = MockInputApi()
4645 mock_output_api = MockOutputApi()
4646 mock_input_api.files = [
4647 MockAffectedFile('random/folder/example.mojom', [], action='A'),
4649 result = PRESUBMIT.CheckCrosApiNeedBrowserTest(mock_input_api, mock_output_api)
4650 self.assertEqual(0, len(result))
4653 class AssertAshOnlyCodeTest(unittest.TestCase):
4654 def testErrorsOnlyOnAshDirectories(self):
4656 MockFile('ash/BUILD.gn', []),
4657 MockFile('chrome/browser/ash/BUILD.gn', []),
4660 MockFile('chrome/browser/BUILD.gn', []),
4661 MockFile('chrome/browser/BUILD.gn', ['assert(is_chromeos_ash)']),
4663 input_api = MockInputApi()
4664 input_api.files = files_in_ash
4665 errors = PRESUBMIT.CheckAssertAshOnlyCode(input_api, MockOutputApi())
4666 self.assertEqual(2, len(errors))
4668 input_api.files = other_files
4669 errors = PRESUBMIT.CheckAssertAshOnlyCode(input_api, MockOutputApi())
4670 self.assertEqual(0, len(errors))
4672 def testDoesNotErrorOnNonGNFiles(self):
4673 input_api = MockInputApi()
4675 MockFile('ash/test.h', ['assert(is_chromeos_ash)']),
4676 MockFile('chrome/browser/ash/test.cc',
4677 ['assert(is_chromeos_ash)']),
4679 errors = PRESUBMIT.CheckAssertAshOnlyCode(input_api, MockOutputApi())
4680 self.assertEqual(0, len(errors))
4682 def testDeletedFile(self):
4683 input_api = MockInputApi()
4685 MockFile('ash/BUILD.gn', []),
4686 MockFile('ash/foo/BUILD.gn', [], action='D'),
4688 errors = PRESUBMIT.CheckAssertAshOnlyCode(input_api, MockOutputApi())
4689 self.assertEqual(1, len(errors))
4691 def testDoesNotErrorWithAssertion(self):
4692 input_api = MockInputApi()
4694 MockFile('ash/BUILD.gn', ['assert(is_chromeos_ash)']),
4695 MockFile('chrome/browser/ash/BUILD.gn',
4696 ['assert(is_chromeos_ash)']),
4697 MockFile('chrome/browser/ash/BUILD.gn',
4698 ['assert(is_chromeos_ash, "test")']),
4700 errors = PRESUBMIT.CheckAssertAshOnlyCode(input_api, MockOutputApi())
4701 self.assertEqual(0, len(errors))
4704 class CheckRawPtrUsageTest(unittest.TestCase):
4705 def testAllowedCases(self):
4706 mock_input_api = MockInputApi()
4707 mock_input_api.files = [
4708 # Browser-side files are allowed.
4709 MockAffectedFile('test10/browser/foo.h', ['raw_ptr<int>']),
4710 MockAffectedFile('test11/browser/foo.cc', ['raw_ptr<int>']),
4711 MockAffectedFile('test12/blink/common/foo.cc', ['raw_ptr<int>']),
4712 MockAffectedFile('test13/blink/public/common/foo.cc', ['raw_ptr<int>']),
4713 MockAffectedFile('test14/blink/public/platform/foo.cc',
4716 # Non-C++ files are allowed.
4717 MockAffectedFile('test20/renderer/foo.md', ['raw_ptr<int>']),
4719 # Renderer code is generally allowed (except specifically
4720 # disallowed directories).
4721 MockAffectedFile('test30/renderer/foo.cc', ['raw_ptr<int>']),
4723 mock_output_api = MockOutputApi()
4724 errors = PRESUBMIT.CheckRawPtrUsage(mock_input_api, mock_output_api)
4725 self.assertFalse(errors)
4727 def testDisallowedCases(self):
4728 mock_input_api = MockInputApi()
4729 mock_input_api.files = [
4730 MockAffectedFile('test1/third_party/blink/renderer/core/foo.h',
4733 'test2/third_party/blink/renderer/platform/heap/foo.cc',
4736 'test3/third_party/blink/renderer/platform/wtf/foo.cc',
4738 MockAffectedFile('test4/blink/public/web/foo.cc', ['raw_ptr<int>']),
4740 mock_output_api = MockOutputApi()
4741 errors = PRESUBMIT.CheckRawPtrUsage(mock_input_api, mock_output_api)
4742 self.assertEqual(len(mock_input_api.files), len(errors))
4743 for error in errors:
4745 'raw_ptr<T> should not be used in this renderer code' in
4748 class CheckAdvancedMemorySafetyChecksUsageTest(unittest.TestCase):
4749 def testAllowedCases(self):
4750 mock_input_api = MockInputApi()
4751 mock_input_api.files = [
4752 # Non-C++ files are allowed.
4754 'test20/renderer/foo.md', ['ADVANCED_MEMORY_SAFETY_CHECKS()']),
4756 # Mentions in a comment are allowed.
4758 'test30/renderer/foo.cc', ['//ADVANCED_MEMORY_SAFETY_CHECKS()']),
4760 mock_output_api = MockOutputApi()
4761 errors = PRESUBMIT.CheckAdvancedMemorySafetyChecksUsage(
4762 mock_input_api, mock_output_api)
4763 self.assertFalse(errors)
4765 def testDisallowedCases(self):
4766 mock_input_api = MockInputApi()
4767 mock_input_api.files = [
4768 MockAffectedFile('test1/foo.h', ['ADVANCED_MEMORY_SAFETY_CHECKS()']),
4769 MockAffectedFile('test2/foo.cc', ['ADVANCED_MEMORY_SAFETY_CHECKS()']),
4771 mock_output_api = MockOutputApi()
4772 errors = PRESUBMIT.CheckAdvancedMemorySafetyChecksUsage(mock_input_api, mock_output_api)
4773 self.assertEqual(1, len(errors))
4775 'ADVANCED_MEMORY_SAFETY_CHECKS() macro is managed by' in
4778 class AssertPythonShebangTest(unittest.TestCase):
4779 def testError(self):
4780 input_api = MockInputApi()
4782 MockFile('ash/test.py', ['#!/usr/bin/python']),
4783 MockFile('chrome/test.py', ['#!/usr/bin/python2']),
4784 MockFile('third_party/blink/test.py', ['#!/usr/bin/python3']),
4785 MockFile('empty.py', []),
4787 errors = PRESUBMIT.CheckPythonShebang(input_api, MockOutputApi())
4788 self.assertEqual(3, len(errors))
4790 def testNonError(self):
4791 input_api = MockInputApi()
4793 MockFile('chrome/browser/BUILD.gn', ['#!/usr/bin/python']),
4794 MockFile('third_party/blink/web_tests/external/test.py',
4795 ['#!/usr/bin/python2']),
4796 MockFile('third_party/test/test.py', ['#!/usr/bin/python3']),
4798 errors = PRESUBMIT.CheckPythonShebang(input_api, MockOutputApi())
4799 self.assertEqual(0, len(errors))
4801 class VerifyDcheckParentheses(unittest.TestCase):
4802 def testPermissibleUsage(self):
4803 input_api = MockInputApi()
4805 MockFile('okay1.cc', ['DCHECK_IS_ON()']),
4806 MockFile('okay2.cc', ['#if DCHECK_IS_ON()']),
4808 # Other constructs that aren't exactly `DCHECK_IS_ON()` do their
4809 # own thing at their own risk.
4810 MockFile('okay3.cc', ['PA_DCHECK_IS_ON']),
4811 MockFile('okay4.cc', ['#if PA_DCHECK_IS_ON']),
4812 MockFile('okay6.cc', ['BUILDFLAG(PA_DCHECK_IS_ON)']),
4814 errors = PRESUBMIT.CheckDCHECK_IS_ONHasBraces(input_api, MockOutputApi())
4815 self.assertEqual(0, len(errors))
4817 def testMissingParentheses(self):
4818 input_api = MockInputApi()
4820 MockFile('bad1.cc', ['DCHECK_IS_ON']),
4821 MockFile('bad2.cc', ['#if DCHECK_IS_ON']),
4822 MockFile('bad3.cc', ['DCHECK_IS_ON && foo']),
4824 errors = PRESUBMIT.CheckDCHECK_IS_ONHasBraces(input_api, MockOutputApi())
4825 self.assertEqual(3, len(errors))
4826 for error in errors:
4827 self.assertRegex(error.message, r'DCHECK_IS_ON().+parentheses')
4830 class CheckBatchAnnotation(unittest.TestCase):
4831 """Test the CheckBatchAnnotation presubmit check."""
4833 def testTruePositives(self):
4834 """Examples of when there is no @Batch or @DoNotBatch is correctly flagged.
4836 mock_input = MockInputApi()
4837 mock_input.files = [
4838 MockFile('path/OneTest.java', ['public class OneTest']),
4839 MockFile('path/TwoTest.java', ['public class TwoTest']),
4840 MockFile('path/ThreeTest.java',
4841 ['@Batch(Batch.PER_CLASS)',
4842 'import org.chromium.base.test.BaseRobolectricTestRunner;',
4843 'public class Three {']),
4844 MockFile('path/FourTest.java',
4845 ['@DoNotBatch(reason = "placeholder reason 1")',
4846 'import org.chromium.base.test.BaseRobolectricTestRunner;',
4847 'public class Four {']),
4849 errors = PRESUBMIT.CheckBatchAnnotation(mock_input, MockOutputApi())
4850 self.assertEqual(2, len(errors))
4851 self.assertEqual(2, len(errors[0].items))
4852 self.assertIn('OneTest.java', errors[0].items[0])
4853 self.assertIn('TwoTest.java', errors[0].items[1])
4854 self.assertEqual(2, len(errors[1].items))
4855 self.assertIn('ThreeTest.java', errors[1].items[0])
4856 self.assertIn('FourTest.java', errors[1].items[1])
4859 def testAnnotationsPresent(self):
4860 """Examples of when there is @Batch or @DoNotBatch is correctly flagged."""
4861 mock_input = MockInputApi()
4862 mock_input.files = [
4863 MockFile('path/OneTest.java',
4864 ['@Batch(Batch.PER_CLASS)', 'public class One {']),
4865 MockFile('path/TwoTest.java',
4866 ['@DoNotBatch(reason = "placeholder reasons.")', 'public class Two {'
4868 MockFile('path/ThreeTest.java',
4869 ['@Batch(Batch.PER_CLASS)',
4870 'public class Three extends BaseTestA {'],
4871 ['@Batch(Batch.PER_CLASS)',
4872 'public class Three extends BaseTestB {']),
4873 MockFile('path/FourTest.java',
4874 ['@DoNotBatch(reason = "placeholder reason 1")',
4875 'public class Four extends BaseTestA {'],
4876 ['@DoNotBatch(reason = "placeholder reason 2")',
4877 'public class Four extends BaseTestB {']),
4878 MockFile('path/FiveTest.java',
4879 ['import androidx.test.uiautomator.UiDevice;',
4880 'public class Five extends BaseTestA {'],
4881 ['import androidx.test.uiautomator.UiDevice;',
4882 'public class Five extends BaseTestB {']),
4883 MockFile('path/SixTest.java',
4884 ['import org.chromium.base.test.BaseRobolectricTestRunner;',
4885 'public class Six extends BaseTestA {'],
4886 ['import org.chromium.base.test.BaseRobolectricTestRunner;',
4887 'public class Six extends BaseTestB {']),
4888 MockFile('path/SevenTest.java',
4889 ['import org.robolectric.annotation.Config;',
4890 'public class Seven extends BaseTestA {'],
4891 ['import org.robolectric.annotation.Config;',
4892 'public class Seven extends BaseTestB {']),
4894 'path/OtherClass.java',
4895 ['public class OtherClass {'],
4897 MockFile('path/PRESUBMIT.py',
4898 ['@Batch(Batch.PER_CLASS)',
4899 '@DoNotBatch(reason = "placeholder reason)']),
4900 MockFile('path/AnnotationTest.java',
4901 ['public @interface SomeAnnotation {'],),
4903 errors = PRESUBMIT.CheckBatchAnnotation(mock_input, MockOutputApi())
4904 self.assertEqual(0, len(errors))
4907 class CheckMockAnnotation(unittest.TestCase):
4908 """Test the CheckMockAnnotation presubmit check."""
4910 def testTruePositives(self):
4911 """Examples of @Mock or @Spy being used and nothing should be flagged."""
4912 mock_input = MockInputApi()
4913 mock_input.files = [
4914 MockFile('path/OneTest.java', [
4915 'import a.b.c.Bar;',
4916 'import a.b.c.Foo;',
4917 '@Mock public static Foo f = new Foo();',
4918 'Mockito.mock(new Bar(a, b, c))'
4920 MockFile('path/TwoTest.java', [
4922 'import static org.mockito.Mockito.spy;',
4924 'public static FooBar<Baz> f;',
4925 'a = spy(Baz.class)'
4928 errors = PRESUBMIT.CheckMockAnnotation(mock_input, MockOutputApi())
4929 self.assertEqual(1, len(errors))
4930 self.assertEqual(2, len(errors[0].items))
4931 self.assertIn('a.b.c.Bar in path/OneTest.java', errors[0].items)
4932 self.assertIn('x.y.z.Baz in path/TwoTest.java', errors[0].items)
4934 def testTrueNegatives(self):
4935 """Examples of when we should not be flagging mock() or spy() calls."""
4936 mock_input = MockInputApi()
4937 mock_input.files = [
4938 MockFile('path/OneTest.java', [
4940 'import org.chromium.base.test.BaseRobolectricTestRunner;',
4941 'Mockito.mock(Abc.class)'
4943 MockFile('path/TwoTest.java', [
4945 'import androidx.test.uiautomator.UiDevice;',
4946 'Mockito.spy(new Def())'
4948 MockFile('path/ThreeTest.java', [
4950 'import static org.mockito.Mockito.spy;',
4952 'public static Foo f = new Abc();',
4953 'a = spy(Foo.class)'
4955 MockFile('path/FourTest.java', [
4957 'import static org.mockito.Mockito.mock;',
4959 'public static Bar b = new Abc(a, b, c, d);',
4960 ' mock(new Bar(a,b,c))'
4962 MockFile('path/FiveTest.java', [
4965 'public static Baz<abc> b;',
4966 'Mockito.mock(Baz.class)'
4968 MockFile('path/SixTest.java', [
4970 'import android.view.View;',
4971 'import java.ArrayList;',
4972 'Mockito.spy(new View())',
4973 'Mockito.mock(ArrayList.class)'
4975 MockFile('path/SevenTest.java', [
4977 '@Mock private static Seven s;',
4978 'Mockito.mock(Seven.class)'
4980 MockFile('path/EightTest.java', [
4982 '@Spy Eight e = new Eight2();',
4983 'Mockito.py(new Eight())'
4986 errors = PRESUBMIT.CheckMockAnnotation(mock_input, MockOutputApi())
4987 self.assertEqual(0, len(errors))
4990 class LayoutInTestsTest(unittest.TestCase):
4991 def testLayoutInTest(self):
4992 mock_input = MockInputApi()
4993 mock_input.files = [
4994 MockFile('path/to/foo_unittest.cc',
4995 [' foo->Layout();', ' bar.Layout();']),
4997 errors = PRESUBMIT.CheckNoLayoutCallsInTests(mock_input, MockOutputApi())
4998 self.assertNotEqual(0, len(errors))
5000 def testNoTriggerOnLayoutOverride(self):
5001 mock_input = MockInputApi();
5002 mock_input.files = [
5003 MockFile('path/to/foo_unittest.cc',
5004 ['class TestView: public views::View {',
5006 ' void Layout(); override {',
5007 ' views::View::Layout();',
5008 ' // perform bespoke layout',
5012 errors = PRESUBMIT.CheckNoLayoutCallsInTests(mock_input, MockOutputApi())
5013 self.assertEqual(0, len(errors))
5015 class AssertNoJsInIosTest(unittest.TestCase):
5016 def testErrorJs(self):
5017 input_api = MockInputApi()
5019 MockFile('components/feature/ios/resources/script.js', []),
5020 MockFile('ios/chrome/feature/resources/script.js', []),
5022 results = PRESUBMIT.CheckNoJsInIos(input_api, MockOutputApi())
5023 self.assertEqual(1, len(results))
5024 self.assertEqual('error', results[0].type)
5025 self.assertEqual(2, len(results[0].items))
5027 def testNonError(self):
5028 input_api = MockInputApi()
5030 MockFile('chrome/resources/script.js', []),
5031 MockFile('components/feature/ios/resources/script.ts', []),
5032 MockFile('ios/chrome/feature/resources/script.ts', []),
5033 MockFile('ios/web/feature/resources/script.ts', []),
5034 MockFile('ios/third_party/script.js', []),
5035 MockFile('third_party/ios/script.js', []),
5037 results = PRESUBMIT.CheckNoJsInIos(input_api, MockOutputApi())
5038 self.assertEqual(0, len(results))
5040 def testExistingFilesWarningOnly(self):
5041 input_api = MockInputApi()
5043 MockFile('ios/chrome/feature/resources/script.js', [], action='M'),
5044 MockFile('ios/chrome/feature/resources/script2.js', [], action='D'),
5046 results = PRESUBMIT.CheckNoJsInIos(input_api, MockOutputApi())
5047 self.assertEqual(1, len(results))
5048 self.assertEqual('warning', results[0].type)
5049 self.assertEqual(1, len(results[0].items))
5051 def testMovedScriptWarningOnly(self):
5052 input_api = MockInputApi()
5054 MockFile('ios/chrome/feature/resources/script.js', [], action='D'),
5055 MockFile('ios/chrome/renamed_feature/resources/script.js', [], action='A'),
5057 results = PRESUBMIT.CheckNoJsInIos(input_api, MockOutputApi())
5058 self.assertEqual(1, len(results))
5059 self.assertEqual('warning', results[0].type)
5060 self.assertEqual(1, len(results[0].items))
5062 class CheckNoAbbreviationInPngFileNameTest(unittest.TestCase):
5063 def testHasAbbreviation(self):
5064 """test png file names with abbreviation that fails the check"""
5065 input_api = MockInputApi()
5067 MockFile('image_a.png', [], action='A'),
5068 MockFile('image_a_.png', [], action='A'),
5069 MockFile('image_a_name.png', [], action='A'),
5070 MockFile('chrome/ui/feature_name/resources/image_a.png', [], action='A'),
5071 MockFile('chrome/ui/feature_name/resources/image_a_.png', [], action='A'),
5072 MockFile('chrome/ui/feature_name/resources/image_a_name.png', [], action='A'),
5074 results = PRESUBMIT.CheckNoAbbreviationInPngFileName(input_api, MockOutputApi())
5075 self.assertEqual(1, len(results))
5076 self.assertEqual('error', results[0].type)
5077 self.assertEqual(len(input_api.files), len(results[0].items))
5079 def testNoAbbreviation(self):
5080 """test png file names without abbreviation that passes the check"""
5081 input_api = MockInputApi()
5083 MockFile('a.png', [], action='A'),
5084 MockFile('_a.png', [], action='A'),
5085 MockFile('image.png', [], action='A'),
5086 MockFile('image_ab_.png', [], action='A'),
5087 MockFile('image_ab_name.png', [], action='A'),
5088 # These paths used to fail because `feature_a_name` matched the regex by mistake.
5089 # They should pass now because the path components ahead of the file name are ignored in the check.
5090 MockFile('chrome/ui/feature_a_name/resources/a.png', [], action='A'),
5091 MockFile('chrome/ui/feature_a_name/resources/_a.png', [], action='A'),
5092 MockFile('chrome/ui/feature_a_name/resources/image.png', [], action='A'),
5093 MockFile('chrome/ui/feature_a_name/resources/image_ab_.png', [], action='A'),
5094 MockFile('chrome/ui/feature_a_name/resources/image_ab_name.png', [], action='A'),
5096 results = PRESUBMIT.CheckNoAbbreviationInPngFileName(input_api, MockOutputApi())
5097 self.assertEqual(0, len(results))
5099 class CheckDanglingUntriagedTest(unittest.TestCase):
5100 def testError(self):
5101 """Test patch adding dangling pointers are reported"""
5102 mock_input_api = MockInputApi()
5103 mock_output_api = MockOutputApi()
5105 mock_input_api.change.DescriptionText = lambda: "description"
5106 mock_input_api.files = [
5108 local_path="foo/foo.cc",
5109 old_contents="raw_ptr<T>",
5110 new_contents="raw_ptr<T, DanglingUntriaged>",
5113 msgs = PRESUBMIT.CheckDanglingUntriaged(mock_input_api, mock_output_api)
5114 self.assertEqual(len(msgs), 1)
5115 self.assertEqual(len(msgs[0].message), 10)
5118 "Unexpected new occurrences of `DanglingUntriaged` detected. Please",
5121 class CheckDanglingUntriagedTest(unittest.TestCase):
5122 def testError(self):
5123 """Test patch adding dangling pointers are reported"""
5124 mock_input_api = MockInputApi()
5125 mock_output_api = MockOutputApi()
5127 mock_input_api.change.DescriptionText = lambda: "description"
5128 mock_input_api.files = [
5130 local_path="foo/foo.cc",
5131 old_contents="raw_ptr<T>",
5132 new_contents="raw_ptr<T, DanglingUntriaged>",
5135 msgs = PRESUBMIT.CheckDanglingUntriaged(mock_input_api,
5137 self.assertEqual(len(msgs), 1)
5138 self.assertEqual(len(msgs[0].message), 11)
5141 "Unexpected new occurrences of `DanglingUntriaged` detected. Please",
5144 def testNonCppFile(self):
5145 """Test patch adding dangling pointers are not reported in non C++ files"""
5146 mock_input_api = MockInputApi()
5147 mock_output_api = MockOutputApi()
5149 mock_input_api.change.DescriptionText = lambda: "description"
5150 mock_input_api.files = [
5152 local_path="foo/README.md",
5154 new_contents="The DanglingUntriaged annotation means",
5157 msgs = PRESUBMIT.CheckDanglingUntriaged(mock_input_api,
5159 self.assertEqual(len(msgs), 0)
5161 def testDeveloperAcknowledgeInCommitDescription(self):
5162 """Test patch adding dangling pointers, but acknowledged by the developers
5164 mock_input_api = MockInputApi()
5165 mock_output_api = MockOutputApi()
5167 mock_input_api.files = [
5169 local_path="foo/foo.cc",
5170 old_contents="raw_ptr<T>",
5171 new_contents="raw_ptr<T, DanglingUntriaged>",
5174 mock_input_api.change.DescriptionText = lambda: (
5175 "DanglingUntriaged-notes: Sorry about this!")
5176 msgs = PRESUBMIT.CheckDanglingUntriaged(mock_input_api,
5178 self.assertEqual(len(msgs), 0)
5180 def testDeveloperAcknowledgeInCommitFooter(self):
5181 """Test patch adding dangling pointers, but acknowledged by the developers
5183 mock_input_api = MockInputApi()
5184 mock_output_api = MockOutputApi()
5186 mock_input_api.files = [
5188 local_path="foo/foo.cc",
5189 old_contents="raw_ptr<T>",
5190 new_contents="raw_ptr<T, DanglingUntriaged>",
5193 mock_input_api.change.DescriptionText = lambda: "description"
5194 mock_input_api.change.footers["DanglingUntriaged-notes"] = ["Sorry!"]
5195 msgs = PRESUBMIT.CheckDanglingUntriaged(mock_input_api,
5197 self.assertEqual(len(msgs), 0)
5199 def testCongrats(self):
5200 """Test the presubmit congrats users removing dangling pointers"""
5201 mock_input_api = MockInputApi()
5202 mock_output_api = MockOutputApi()
5204 mock_input_api.files = [
5206 local_path="foo/foo.cc",
5207 old_contents="raw_ptr<T, DanglingUntriaged>",
5208 new_contents="raw_ptr<T>",
5211 mock_input_api.change.DescriptionText = lambda: (
5212 "This patch fixes some DanglingUntriaged pointers!")
5213 msgs = PRESUBMIT.CheckDanglingUntriaged(mock_input_api,
5215 self.assertEqual(len(msgs), 1)
5217 "DanglingUntriaged pointers removed: 1" in msgs[0].message)
5218 self.assertTrue("Thank you!" in msgs[0].message)
5220 def testRenameFile(self):
5221 """Patch that we do not warn about DanglingUntriaged when moving files"""
5222 mock_input_api = MockInputApi()
5223 mock_output_api = MockOutputApi()
5225 mock_input_api.files = [
5227 local_path="foo/foo.cc",
5228 old_contents="raw_ptr<T, DanglingUntriaged>",
5233 local_path="foo/foo.cc",
5235 new_contents="raw_ptr<T, DanglingUntriaged>",
5239 mock_input_api.change.DescriptionText = lambda: (
5240 "This patch moves files")
5241 msgs = PRESUBMIT.CheckDanglingUntriaged(mock_input_api,
5243 self.assertEqual(len(msgs), 0)
5245 class CheckInlineConstexprDefinitionsInHeadersTest(unittest.TestCase):
5246 def testNoInlineConstexprInHeaderFile(self):
5247 """Tests that non-inlined constexpr variables in headers fail the test."""
5248 input_api = MockInputApi()
5250 MockAffectedFile('src/constants.h', ['constexpr int kVersion = 5;'])
5252 warnings = PRESUBMIT.CheckInlineConstexprDefinitionsInHeaders(input_api, MockOutputApi())
5253 self.assertEqual(1, len(warnings))
5255 def testNoInlineConstexprInHeaderFileInitializedFromFunction(self):
5256 """Tests that non-inlined constexpr header variables that are initialized from a function fail."""
5257 input_api = MockInputApi()
5259 MockAffectedFile('src/constants.h', ['constexpr int kVersion = GetVersion();'])
5261 warnings = PRESUBMIT.CheckInlineConstexprDefinitionsInHeaders(input_api, MockOutputApi())
5262 self.assertEqual(1, len(warnings))
5264 def testNoInlineConstexprInHeaderFileInitializedWithExpression(self):
5265 """Tests that non-inlined constexpr header variables initialized with an expression fail."""
5266 input_api = MockInputApi()
5268 MockAffectedFile('src/constants.h', ['constexpr int kVersion = (4 + 5)*3;'])
5270 warnings = PRESUBMIT.CheckInlineConstexprDefinitionsInHeaders(input_api, MockOutputApi())
5271 self.assertEqual(1, len(warnings))
5273 def testNoInlineConstexprInHeaderFileBraceInitialized(self):
5274 """Tests that non-inlined constexpr header variables that are brace-initialized fail."""
5275 input_api = MockInputApi()
5277 MockAffectedFile('src/constants.h', ['constexpr int kVersion{5};'])
5279 warnings = PRESUBMIT.CheckInlineConstexprDefinitionsInHeaders(input_api, MockOutputApi())
5280 self.assertEqual(1, len(warnings))
5282 def testNoInlineConstexprInHeaderWithAttribute(self):
5283 """Tests that non-inlined constexpr header variables that have compiler attributes fail."""
5284 input_api = MockInputApi()
5286 MockAffectedFile('src/constants.h', ['constexpr [[maybe_unused]] int kVersion{5};'])
5288 warnings = PRESUBMIT.CheckInlineConstexprDefinitionsInHeaders(input_api, MockOutputApi())
5289 self.assertEqual(1, len(warnings))
5291 def testInlineConstexprInHeaderWithAttribute(self):
5292 """Tests that inlined constexpr header variables that have compiler attributes pass."""
5293 input_api = MockInputApi()
5295 MockAffectedFile('src/constants.h', ['inline constexpr [[maybe_unused]] int kVersion{5};']),
5296 MockAffectedFile('src/constants.h', ['constexpr inline [[maybe_unused]] int kVersion{5};']),
5297 MockAffectedFile('src/constants.h', ['inline constexpr [[maybe_unused]] inline int kVersion{5};'])
5299 warnings = PRESUBMIT.CheckInlineConstexprDefinitionsInHeaders(input_api, MockOutputApi())
5300 self.assertEqual(0, len(warnings))
5302 def testNoInlineConstexprInHeaderFileMultipleLines(self):
5303 """Tests that non-inlined constexpr header variable definitions spanning multiple lines fail."""
5304 input_api = MockInputApi()
5305 lines = ['constexpr char kLongName =',
5306 ' "This is a very long name of something.";'
5308 input_api.files = [MockAffectedFile('src/constants.h', lines)]
5309 warnings = PRESUBMIT.CheckInlineConstexprDefinitionsInHeaders(input_api, MockOutputApi())
5310 self.assertEqual(1, len(warnings))
5312 def testNoInlineConstexprInCCFile(self):
5313 """Tests that non-inlined constexpr variables in .cc files pass the test."""
5314 input_api = MockInputApi()
5316 MockAffectedFile('src/implementation.cc', ['constexpr int kVersion = 5;'])
5318 warnings = PRESUBMIT.CheckInlineConstexprDefinitionsInHeaders(input_api, MockOutputApi())
5319 self.assertEqual(0, len(warnings))
5321 def testInlineConstexprInHeaderFile(self):
5322 """Tests that inlined constexpr variables in header files pass the test."""
5323 input_api = MockInputApi()
5325 MockAffectedFile('src/constants.h', ['constexpr inline int kX = 5;']),
5326 MockAffectedFile('src/version.h', ['inline constexpr float kY = 5.0f;'])
5328 warnings = PRESUBMIT.CheckInlineConstexprDefinitionsInHeaders(input_api, MockOutputApi())
5329 self.assertEqual(0, len(warnings))
5331 def testConstexprStandaloneFunctionInHeaderFile(self):
5332 """Tests that non-inlined constexpr functions in headers pass the test."""
5333 input_api = MockInputApi()
5335 MockAffectedFile('src/helpers.h', ['constexpr int GetVersion();'])
5337 warnings = PRESUBMIT.CheckInlineConstexprDefinitionsInHeaders(input_api, MockOutputApi())
5338 self.assertEqual(0, len(warnings))
5340 def testConstexprWithAbseilAttributeInHeader(self):
5341 """Tests that non-inlined constexpr variables with Abseil-type prefixes in headers fail."""
5342 input_api = MockInputApi()
5344 MockAffectedFile('src/helpers.h', ['ABSL_FOOFOO constexpr int i = 5;'])
5346 warnings = PRESUBMIT.CheckInlineConstexprDefinitionsInHeaders(input_api, MockOutputApi())
5347 self.assertEqual(1, len(warnings))
5349 def testInlineConstexprWithAbseilAttributeInHeader(self):
5350 """Tests that inlined constexpr variables with Abseil-type prefixes in headers pass."""
5351 input_api = MockInputApi()
5353 MockAffectedFile('src/helpers.h', ['constexpr ABSL_FOO inline int i = 5;'])
5355 warnings = PRESUBMIT.CheckInlineConstexprDefinitionsInHeaders(input_api, MockOutputApi())
5356 self.assertEqual(0, len(warnings))
5358 def testConstexprWithClangAttributeInHeader(self):
5359 """Tests that non-inlined constexpr variables with attributes with colons in headers fail."""
5360 input_api = MockInputApi()
5362 MockAffectedFile('src/helpers.h', ['[[clang::someattribute]] constexpr int i = 5;'])
5364 warnings = PRESUBMIT.CheckInlineConstexprDefinitionsInHeaders(input_api, MockOutputApi())
5365 self.assertEqual(1, len(warnings))
5367 def testInlineConstexprWithClangAttributeInHeader(self):
5368 """Tests that inlined constexpr variables with attributes with colons in headers pass."""
5369 input_api = MockInputApi()
5371 MockAffectedFile('src/helpers.h', ['constexpr [[clang::someattribute]] inline int i = 5;'])
5373 warnings = PRESUBMIT.CheckInlineConstexprDefinitionsInHeaders(input_api, MockOutputApi())
5374 self.assertEqual(0, len(warnings))
5376 def testNoExplicitInlineConstexprInsideClassInHeaderFile(self):
5377 """Tests that non-inlined constexpr class members pass the test."""
5378 input_api = MockInputApi()
5379 lines = ['class SomeClass {',
5381 ' static constexpr kVersion = 5;',
5384 MockAffectedFile('src/class.h', lines)
5386 warnings = PRESUBMIT.CheckInlineConstexprDefinitionsInHeaders(input_api, MockOutputApi())
5387 self.assertEqual(0, len(warnings))
5389 if __name__ == '__main__':