summaryrefslogtreecommitdiff
path: root/chromium/PRESUBMIT_test.py
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/PRESUBMIT_test.py')
-rwxr-xr-xchromium/PRESUBMIT_test.py348
1 files changed, 313 insertions, 35 deletions
diff --git a/chromium/PRESUBMIT_test.py b/chromium/PRESUBMIT_test.py
index a15c15fa029..e2e8971de90 100755
--- a/chromium/PRESUBMIT_test.py
+++ b/chromium/PRESUBMIT_test.py
@@ -713,10 +713,19 @@ class PydepsNeedsUpdatingTest(unittest.TestCase):
class MockSubprocess(object):
CalledProcessError = subprocess.CalledProcessError
+ def _MockParseGclientArgs(self, is_android=True):
+ return lambda: {'checkout_android': 'true' if is_android else 'false' }
+
def setUp(self):
- mock_all_pydeps = ['A.pydeps', 'B.pydeps']
+ mock_all_pydeps = ['A.pydeps', 'B.pydeps', 'D.pydeps']
self.old_ALL_PYDEPS_FILES = PRESUBMIT._ALL_PYDEPS_FILES
PRESUBMIT._ALL_PYDEPS_FILES = mock_all_pydeps
+ mock_android_pydeps = ['D.pydeps']
+ self.old_ANDROID_SPECIFIC_PYDEPS_FILES = (
+ PRESUBMIT._ANDROID_SPECIFIC_PYDEPS_FILES)
+ PRESUBMIT._ANDROID_SPECIFIC_PYDEPS_FILES = mock_android_pydeps
+ self.old_ParseGclientArgs = PRESUBMIT._ParseGclientArgs
+ PRESUBMIT._ParseGclientArgs = self._MockParseGclientArgs()
self.mock_input_api = MockInputApi()
self.mock_output_api = MockOutputApi()
self.mock_input_api.subprocess = PydepsNeedsUpdatingTest.MockSubprocess()
@@ -724,10 +733,14 @@ class PydepsNeedsUpdatingTest(unittest.TestCase):
self.checker._file_cache = {
'A.pydeps': '# Generated by:\n# CMD A\nA.py\nC.py\n',
'B.pydeps': '# Generated by:\n# CMD B\nB.py\nC.py\n',
+ 'D.pydeps': '# Generated by:\n# CMD D\nD.py\n',
}
def tearDown(self):
PRESUBMIT._ALL_PYDEPS_FILES = self.old_ALL_PYDEPS_FILES
+ PRESUBMIT._ANDROID_SPECIFIC_PYDEPS_FILES = (
+ self.old_ANDROID_SPECIFIC_PYDEPS_FILES)
+ PRESUBMIT._ParseGclientArgs = self.old_ParseGclientArgs
def _RunCheck(self):
return PRESUBMIT._CheckPydepsNeedsUpdating(self.mock_input_api,
@@ -735,7 +748,7 @@ class PydepsNeedsUpdatingTest(unittest.TestCase):
checker_for_tests=self.checker)
def testAddedPydep(self):
- # PRESUBMIT._CheckPydepsNeedsUpdating is only implemented for Android.
+ # PRESUBMIT._CheckPydepsNeedsUpdating is only implemented for Linux.
if self.mock_input_api.platform != 'linux2':
return []
@@ -759,7 +772,7 @@ class PydepsNeedsUpdatingTest(unittest.TestCase):
self.assertEqual(0, len(results))
def testRemovedPydep(self):
- # PRESUBMIT._CheckPydepsNeedsUpdating is only implemented for Android.
+ # PRESUBMIT._CheckPydepsNeedsUpdating is only implemented for Linux.
if self.mock_input_api.platform != 'linux2':
return []
@@ -774,7 +787,7 @@ class PydepsNeedsUpdatingTest(unittest.TestCase):
self.assertTrue('PYDEPS_FILES' in str(results[0]))
def testRandomPyIgnored(self):
- # PRESUBMIT._CheckPydepsNeedsUpdating is only implemented for Android.
+ # PRESUBMIT._CheckPydepsNeedsUpdating is only implemented for Linux.
if self.mock_input_api.platform != 'linux2':
return []
@@ -786,7 +799,7 @@ class PydepsNeedsUpdatingTest(unittest.TestCase):
self.assertEqual(0, len(results), 'Unexpected results: %r' % results)
def testRelevantPyNoChange(self):
- # PRESUBMIT._CheckPydepsNeedsUpdating is only implemented for Android.
+ # PRESUBMIT._CheckPydepsNeedsUpdating is only implemented for Linux.
if self.mock_input_api.platform != 'linux2':
return []
@@ -804,7 +817,7 @@ class PydepsNeedsUpdatingTest(unittest.TestCase):
self.assertEqual(0, len(results), 'Unexpected results: %r' % results)
def testRelevantPyOneChange(self):
- # PRESUBMIT._CheckPydepsNeedsUpdating is only implemented for Android.
+ # PRESUBMIT._CheckPydepsNeedsUpdating is only implemented for Linux.
if self.mock_input_api.platform != 'linux2':
return []
@@ -823,7 +836,7 @@ class PydepsNeedsUpdatingTest(unittest.TestCase):
self.assertTrue('File is stale' in str(results[0]))
def testRelevantPyTwoChanges(self):
- # PRESUBMIT._CheckPydepsNeedsUpdating is only implemented for Android.
+ # PRESUBMIT._CheckPydepsNeedsUpdating is only implemented for Linux.
if self.mock_input_api.platform != 'linux2':
return []
@@ -841,6 +854,27 @@ class PydepsNeedsUpdatingTest(unittest.TestCase):
self.assertTrue('File is stale' in str(results[0]))
self.assertTrue('File is stale' in str(results[1]))
+ def testRelevantAndroidPyInNonAndroidCheckout(self):
+ # PRESUBMIT._CheckPydepsNeedsUpdating is only implemented for Linux.
+ if self.mock_input_api.platform != 'linux2':
+ return []
+
+ self.mock_input_api.files = [
+ MockAffectedFile('D.py', []),
+ ]
+
+ def mock_check_output(cmd, shell=False, env=None):
+ self.assertEqual('CMD D --output ""', cmd)
+ return 'changed data'
+
+ self.mock_input_api.subprocess.check_output = mock_check_output
+ PRESUBMIT._ParseGclientArgs = self._MockParseGclientArgs(is_android=False)
+
+ results = self._RunCheck()
+ self.assertEqual(1, len(results))
+ self.assertTrue('Android' in str(results[0]))
+ self.assertTrue('D.pydeps' in str(results[0]))
+
class IncludeGuardTest(unittest.TestCase):
def testIncludeGuardChecks(self):
@@ -1028,6 +1062,7 @@ class AccessibilityRelnotesFieldTest(unittest.TestCase):
mock_output_api = MockOutputApi()
mock_input_api.files = [MockAffectedFile('ui/accessibility/foo.bar', [''])]
+ mock_input_api.change.DescriptionText = lambda : 'Commit description'
mock_input_api.change.footers['AX-Relnotes'] = [
'Important user facing change']
@@ -1046,6 +1081,7 @@ class AccessibilityRelnotesFieldTest(unittest.TestCase):
MockAffectedFile('ui/accessibility/foo.bar', ['']),
MockAffectedFile('some/other/file', [''])
]
+ mock_input_api.change.DescriptionText = lambda : 'Commit description'
msgs = PRESUBMIT._CheckAccessibilityRelnotesField(
mock_input_api, mock_output_api)
@@ -1065,6 +1101,7 @@ class AccessibilityRelnotesFieldTest(unittest.TestCase):
MockAffectedFile('some/file', ['']),
MockAffectedFile('some/other/file', [''])
]
+ mock_input_api.change.DescriptionText = lambda : 'Commit description'
msgs = PRESUBMIT._CheckAccessibilityRelnotesField(
mock_input_api, mock_output_api)
@@ -1097,6 +1134,7 @@ class AccessibilityRelnotesFieldTest(unittest.TestCase):
mock_input_api.files = [
MockAffectedFile(testFile, [''])
]
+ mock_input_api.change.DescriptionText = lambda : 'Commit description'
msgs = PRESUBMIT._CheckAccessibilityRelnotesField(
mock_input_api, mock_output_api)
@@ -1107,6 +1145,58 @@ class AccessibilityRelnotesFieldTest(unittest.TestCase):
('Missing AX-Relnotes field message not found in errors '
' for file %s' % (testFile)))
+ # Test that AX-Relnotes field can appear in the commit description (as long
+ # as it appears at the beginning of a line).
+ def testRelnotesInCommitDescription(self):
+ mock_input_api = MockInputApi()
+ mock_output_api = MockOutputApi()
+
+ mock_input_api.files = [
+ MockAffectedFile('ui/accessibility/foo.bar', ['']),
+ ]
+ mock_input_api.change.DescriptionText = lambda : ('Description:\n' +
+ 'AX-Relnotes: solves all accessibility issues forever')
+
+ msgs = PRESUBMIT._CheckAccessibilityRelnotesField(
+ mock_input_api, mock_output_api)
+ self.assertEqual(0, len(msgs),
+ 'Expected %d messages, found %d: %s'
+ % (0, len(msgs), msgs))
+
+ # Test that we don't match AX-Relnotes if it appears in the middle of a line.
+ def testRelnotesMustAppearAtBeginningOfLine(self):
+ mock_input_api = MockInputApi()
+ mock_output_api = MockOutputApi()
+
+ mock_input_api.files = [
+ MockAffectedFile('ui/accessibility/foo.bar', ['']),
+ ]
+ mock_input_api.change.DescriptionText = lambda : ('Description:\n' +
+ 'This change has no AX-Relnotes: we should print a warning')
+
+ msgs = PRESUBMIT._CheckAccessibilityRelnotesField(
+ mock_input_api, mock_output_api)
+ self.assertTrue("Missing 'AX-Relnotes:' field" in msgs[0].message,
+ 'Missing AX-Relnotes field message not found in errors')
+
+ # Tests that the AX-Relnotes field can be lowercase and use a '=' in place
+ # of a ':'.
+ def testRelnotesLowercaseWithEqualSign(self):
+ mock_input_api = MockInputApi()
+ mock_output_api = MockOutputApi()
+
+ mock_input_api.files = [
+ MockAffectedFile('ui/accessibility/foo.bar', ['']),
+ ]
+ mock_input_api.change.DescriptionText = lambda : ('Description:\n' +
+ 'ax-relnotes= this is a valid format for accessibiliy relnotes')
+
+ msgs = PRESUBMIT._CheckAccessibilityRelnotesField(
+ mock_input_api, mock_output_api)
+ self.assertEqual(0, len(msgs),
+ 'Expected %d messages, found %d: %s'
+ % (0, len(msgs), msgs))
+
class AndroidDeprecatedTestAnnotationTest(unittest.TestCase):
def testCheckAndroidTestAnnotationUsage(self):
mock_input_api = MockInputApi()
@@ -2105,17 +2195,21 @@ class SecurityChangeTest(unittest.TestCase):
input_api.canned_checks.GetCodereviewOwnerAndReviewers = \
__MockOwnerAndReviewers
- def testDiffWithSandboxType(self):
+ def testDiffGetServiceSandboxType(self):
mock_input_api = MockInputApi()
mock_input_api.files = [
MockAffectedFile(
'services/goat/teleporter_host.cc',
[
- 'content::ServiceProcessHost::Launch<mojom::GoatTeleporter>(',
- ' content::ServiceProcessHost::LaunchOptions()',
- ' .WithSandboxType(content::SandboxType::kGoaty)',
- ' .WithDisplayName("goat_teleporter")',
- ' .Build())'
+ 'template <>',
+ 'inline content::SandboxType',
+ 'content::GetServiceSandboxType<chrome::mojom::GoatTeleporter>() {',
+ '#if defined(OS_WIN)',
+ ' return SandboxType::kGoaty;',
+ '#else',
+ ' return SandboxType::kNoSandbox;',
+ '#endif // !defined(OS_WIN)',
+ '}'
]
),
]
@@ -2123,7 +2217,7 @@ class SecurityChangeTest(unittest.TestCase):
mock_input_api)
self.assertEqual({
'services/goat/teleporter_host.cc': set([
- 'content::ServiceProcessHost::LaunchOptions::WithSandboxType'
+ 'content::GetServiceSandboxType<>()'
])},
files_to_functions)
@@ -2133,18 +2227,18 @@ class SecurityChangeTest(unittest.TestCase):
mock_file._scm_diff = """--- old 2020-05-04 14:08:25.000000000 -0400
+++ new 2020-05-04 14:08:32.000000000 -0400
@@ -1,5 +1,4 @@
- content::ServiceProcessHost::Launch<mojom::GoatTeleporter>(
- content::ServiceProcessHost::LaunchOptions()
-- .WithSandboxType(content::SandboxType::kGoaty)
- .WithDisplayName("goat_teleporter")
- .Build())
+ template <>
+ inline content::SandboxType
+-content::GetServiceSandboxType<chrome::mojom::GoatTeleporter>() {
+ #if defined(OS_WIN)
+ return SandboxType::kGoaty;
"""
mock_input_api.files = [mock_file]
files_to_functions = PRESUBMIT._GetFilesUsingSecurityCriticalFunctions(
mock_input_api)
self.assertEqual({
'services/goat/teleporter_host.cc': set([
- 'content::ServiceProcessHost::LaunchOptions::WithSandboxType'
+ 'content::GetServiceSandboxType<>()'
])},
files_to_functions)
@@ -2153,7 +2247,7 @@ class SecurityChangeTest(unittest.TestCase):
mock_input_api.owners_db = self._MockOwnersDB()
mock_input_api.is_committing = False
mock_input_api.files = [
- MockAffectedFile('file.cc', ['WithSandboxType(Sandbox)'])
+ MockAffectedFile('file.cc', ['GetServiceSandboxType<Goat>(Sandbox)'])
]
mock_output_api = MockOutputApi()
self._mockChangeOwnerAndReviewers(
@@ -2165,14 +2259,14 @@ class SecurityChangeTest(unittest.TestCase):
'The following files change calls to security-sensive functions\n' \
'that need to be reviewed by ipc/SECURITY_OWNERS.\n'
' file.cc\n'
- ' content::ServiceProcessHost::LaunchOptions::WithSandboxType\n\n')
+ ' content::GetServiceSandboxType<>()\n\n')
def testChangeOwnersMissingAtCommit(self):
mock_input_api = MockInputApi()
mock_input_api.owners_db = self._MockOwnersDB()
mock_input_api.is_committing = True
mock_input_api.files = [
- MockAffectedFile('file.cc', ['WithSandboxType(Sandbox)'])
+ MockAffectedFile('file.cc', ['GetServiceSandboxType<mojom::Goat>()'])
]
mock_output_api = MockOutputApi()
self._mockChangeOwnerAndReviewers(
@@ -2184,7 +2278,7 @@ class SecurityChangeTest(unittest.TestCase):
'The following files change calls to security-sensive functions\n' \
'that need to be reviewed by ipc/SECURITY_OWNERS.\n'
' file.cc\n'
- ' content::ServiceProcessHost::LaunchOptions::WithSandboxType\n\n')
+ ' content::GetServiceSandboxType<>()\n\n')
def testChangeOwnersPresent(self):
mock_input_api = MockInputApi()
@@ -2203,7 +2297,7 @@ class SecurityChangeTest(unittest.TestCase):
mock_input_api = MockInputApi()
mock_input_api.owners_db = self._MockOwnersDB()
mock_input_api.files = [
- MockAffectedFile('file.cc', ['WithSandboxType(Sandbox)'])
+ MockAffectedFile('file.cc', ['GetServiceSandboxType<T>(Sandbox)'])
]
mock_output_api = MockOutputApi()
self._mockChangeOwnerAndReviewers(
@@ -2651,7 +2745,9 @@ class CheckNoDirectIncludesHeadersWhichRedefineStrCat(unittest.TestCase):
self.assertEquals(0, len(results))
-class TranslationScreenshotsTest(unittest.TestCase):
+class StringTest(unittest.TestCase):
+ """Tests ICU syntax check and translation screenshots check."""
+
# An empty grd file.
OLD_GRD_CONTENTS = """<?xml version="1.0" encoding="UTF-8"?>
<grit latest_public_release="1" current_release="1">
@@ -2668,6 +2764,10 @@ class TranslationScreenshotsTest(unittest.TestCase):
<message name="IDS_TEST1">
Test string 1
</message>
+ <message name="IDS_TEST_STRING_NON_TRANSLATEABLE1"
+ translateable="false">
+ Non translateable message 1, should be ignored
+ </message>
</messages>
</release>
</grit>
@@ -2683,6 +2783,52 @@ class TranslationScreenshotsTest(unittest.TestCase):
<message name="IDS_TEST2">
Test string 2
</message>
+ <message name="IDS_TEST_STRING_NON_TRANSLATEABLE2"
+ translateable="false">
+ Non translateable message 2, should be ignored
+ </message>
+ </messages>
+ </release>
+ </grit>
+ """.splitlines()
+ # A grd file with one ICU syntax message without syntax errors.
+ NEW_GRD_CONTENTS_ICU_SYNTAX_OK1 = """<?xml version="1.0" encoding="UTF-8"?>
+ <grit latest_public_release="1" current_release="1">
+ <release seq="1">
+ <messages>
+ <message name="IDS_TEST1">
+ {NUM, plural,
+ =1 {Test text for numeric one}
+ other {Test text for plural with {NUM} as number}}
+ </message>
+ </messages>
+ </release>
+ </grit>
+ """.splitlines()
+ # A grd file with one ICU syntax message without syntax errors.
+ NEW_GRD_CONTENTS_ICU_SYNTAX_OK2 = """<?xml version="1.0" encoding="UTF-8"?>
+ <grit latest_public_release="1" current_release="1">
+ <release seq="1">
+ <messages>
+ <message name="IDS_TEST1">
+ {NUM, plural,
+ =1 {Different test text for numeric one}
+ other {Different test text for plural with {NUM} as number}}
+ </message>
+ </messages>
+ </release>
+ </grit>
+ """.splitlines()
+ # A grd file with one ICU syntax message with syntax errors (misses a comma).
+ NEW_GRD_CONTENTS_ICU_SYNTAX_ERROR = """<?xml version="1.0" encoding="UTF-8"?>
+ <grit latest_public_release="1" current_release="1">
+ <release seq="1">
+ <messages>
+ <message name="IDS_TEST1">
+ {NUM, plural
+ =1 {Test text for numeric one}
+ other {Test text for plural with {NUM} as number}}
+ </message>
</messages>
</release>
</grit>
@@ -2713,6 +2859,39 @@ class TranslationScreenshotsTest(unittest.TestCase):
'</message>',
'</grit-part>')
+ # A grdp file with one ICU syntax message without syntax errors.
+ NEW_GRDP_CONTENTS_ICU_SYNTAX_OK1 = (
+ '<?xml version="1.0" encoding="utf-8"?>',
+ '<grit-part>',
+ '<message name="IDS_PART_TEST1">',
+ '{NUM, plural,',
+ '=1 {Test text for numeric one}',
+ 'other {Test text for plural with {NUM} as number}}',
+ '</message>',
+ '</grit-part>')
+ # A grdp file with one ICU syntax message without syntax errors.
+ NEW_GRDP_CONTENTS_ICU_SYNTAX_OK2 = (
+ '<?xml version="1.0" encoding="utf-8"?>',
+ '<grit-part>',
+ '<message name="IDS_PART_TEST1">',
+ '{NUM, plural,',
+ '=1 {Different test text for numeric one}',
+ 'other {Different test text for plural with {NUM} as number}}',
+ '</message>',
+ '</grit-part>')
+
+ # A grdp file with one ICU syntax message with syntax errors (superfluent
+ # whitespace).
+ NEW_GRDP_CONTENTS_ICU_SYNTAX_ERROR = (
+ '<?xml version="1.0" encoding="utf-8"?>',
+ '<grit-part>',
+ '<message name="IDS_PART_TEST1">',
+ '{NUM, plural,',
+ '= 1 {Test text for numeric one}',
+ 'other {Test text for plural with {NUM} as number}}',
+ '</message>',
+ '</grit-part>')
+
DO_NOT_UPLOAD_PNG_MESSAGE = ('Do not include actual screenshots in the '
'changelist. Run '
'tools/translate/upload_screenshots.py to '
@@ -2724,6 +2903,9 @@ class TranslationScreenshotsTest(unittest.TestCase):
'these files to your changelist:')
REMOVE_SIGNATURES_MESSAGE = ('You removed strings associated with these '
'files. Remove:')
+ ICU_SYNTAX_ERROR_MESSAGE = ('ICU syntax errors were found in the following '
+ 'strings (problems or feedback? Contact '
+ 'rainhard@chromium.org):')
def makeInputApi(self, files):
input_api = MockInputApi()
@@ -2742,7 +2924,7 @@ class TranslationScreenshotsTest(unittest.TestCase):
self.NEW_GRD_CONTENTS1, action='M'),
MockAffectedFile('part.grdp', self.NEW_GRDP_CONTENTS1,
self.NEW_GRDP_CONTENTS1, action='M')])
- warnings = PRESUBMIT._CheckTranslationScreenshots(input_api,
+ warnings = PRESUBMIT._CheckStrings(input_api,
MockOutputApi())
self.assertEqual(0, len(warnings))
@@ -2752,7 +2934,7 @@ class TranslationScreenshotsTest(unittest.TestCase):
self.NEW_GRD_CONTENTS1, action='M'),
MockAffectedFile('part.grdp', self.NEW_GRDP_CONTENTS2,
self.NEW_GRDP_CONTENTS1, action='M')])
- warnings = PRESUBMIT._CheckTranslationScreenshots(input_api,
+ warnings = PRESUBMIT._CheckStrings(input_api,
MockOutputApi())
self.assertEqual(1, len(warnings))
self.assertEqual(self.GENERATE_SIGNATURES_MESSAGE, warnings[0].message)
@@ -2767,7 +2949,7 @@ class TranslationScreenshotsTest(unittest.TestCase):
self.OLD_GRD_CONTENTS, action='M'),
MockAffectedFile('part.grdp', self.NEW_GRDP_CONTENTS2,
self.OLD_GRDP_CONTENTS, action='M')])
- warnings = PRESUBMIT._CheckTranslationScreenshots(input_api,
+ warnings = PRESUBMIT._CheckStrings(input_api,
MockOutputApi())
self.assertEqual(1, len(warnings))
self.assertEqual(self.GENERATE_SIGNATURES_MESSAGE, warnings[0].message)
@@ -2794,7 +2976,7 @@ class TranslationScreenshotsTest(unittest.TestCase):
MockAffectedFile(
os.path.join('test_grd', 'IDS_TEST1.png'), 'binary', action='A')
])
- warnings = PRESUBMIT._CheckTranslationScreenshots(input_api,
+ warnings = PRESUBMIT._CheckStrings(input_api,
MockOutputApi())
self.assertEqual(2, len(warnings))
self.assertEqual(self.DO_NOT_UPLOAD_PNG_MESSAGE, warnings[0].message)
@@ -2829,7 +3011,7 @@ class TranslationScreenshotsTest(unittest.TestCase):
os.path.join('part_grdp', 'IDS_PART_TEST1.png'), 'binary',
action='A')
])
- warnings = PRESUBMIT._CheckTranslationScreenshots(input_api,
+ warnings = PRESUBMIT._CheckStrings(input_api,
MockOutputApi())
self.assertEqual(2, len(warnings))
self.assertEqual(self.DO_NOT_UPLOAD_PNG_MESSAGE, warnings[0].message)
@@ -2874,7 +3056,7 @@ class TranslationScreenshotsTest(unittest.TestCase):
'binary',
action='A'),
])
- warnings = PRESUBMIT._CheckTranslationScreenshots(input_api,
+ warnings = PRESUBMIT._CheckStrings(input_api,
MockOutputApi())
self.assertEqual([], warnings)
@@ -2902,7 +3084,7 @@ class TranslationScreenshotsTest(unittest.TestCase):
MockFile(os.path.join('part_grdp', 'IDS_PART_TEST2.png.sha1'),
'binary', '')
])
- warnings = PRESUBMIT._CheckTranslationScreenshots(input_api,
+ warnings = PRESUBMIT._CheckStrings(input_api,
MockOutputApi())
self.assertEqual(1, len(warnings))
self.assertEqual(self.REMOVE_SIGNATURES_MESSAGE, warnings[0].message)
@@ -2942,7 +3124,7 @@ class TranslationScreenshotsTest(unittest.TestCase):
'old_contents',
action='D')
])
- warnings = PRESUBMIT._CheckTranslationScreenshots(input_api,
+ warnings = PRESUBMIT._CheckStrings(input_api,
MockOutputApi())
self.assertEqual(1, len(warnings))
self.assertEqual(self.REMOVE_SIGNATURES_MESSAGE, warnings[0].message)
@@ -2981,10 +3163,69 @@ class TranslationScreenshotsTest(unittest.TestCase):
'binary',
action='D')
])
- warnings = PRESUBMIT._CheckTranslationScreenshots(input_api,
+ warnings = PRESUBMIT._CheckStrings(input_api,
MockOutputApi())
self.assertEqual([], warnings)
+ def testIcuSyntax(self):
+ # Add valid ICU syntax string. Should not raise an error.
+ input_api = self.makeInputApi([
+ MockAffectedFile('test.grd', self.NEW_GRD_CONTENTS_ICU_SYNTAX_OK2,
+ self.NEW_GRD_CONTENTS1, action='M'),
+ MockAffectedFile('part.grdp', self.NEW_GRDP_CONTENTS_ICU_SYNTAX_OK2,
+ self.NEW_GRDP_CONTENTS1, action='M')])
+ results = PRESUBMIT._CheckStrings(input_api, MockOutputApi())
+ # We expect no ICU syntax errors.
+ icu_errors = [e for e in results
+ if e.message == self.ICU_SYNTAX_ERROR_MESSAGE]
+ self.assertEqual(0, len(icu_errors))
+
+ # Valid changes in ICU syntax. Should not raise an error.
+ input_api = self.makeInputApi([
+ MockAffectedFile('test.grd', self.NEW_GRD_CONTENTS_ICU_SYNTAX_OK2,
+ self.NEW_GRD_CONTENTS_ICU_SYNTAX_OK1, action='M'),
+ MockAffectedFile('part.grdp', self.NEW_GRDP_CONTENTS_ICU_SYNTAX_OK2,
+ self.NEW_GRDP_CONTENTS_ICU_SYNTAX_OK1, action='M')])
+ results = PRESUBMIT._CheckStrings(input_api, MockOutputApi())
+ # We expect no ICU syntax errors.
+ icu_errors = [e for e in results
+ if e.message == self.ICU_SYNTAX_ERROR_MESSAGE]
+ self.assertEqual(0, len(icu_errors))
+
+ # Add invalid ICU syntax strings. Should raise two errors.
+ input_api = self.makeInputApi([
+ MockAffectedFile('test.grd', self.NEW_GRD_CONTENTS_ICU_SYNTAX_ERROR,
+ self.NEW_GRD_CONTENTS1, action='M'),
+ MockAffectedFile('part.grdp', self.NEW_GRDP_CONTENTS_ICU_SYNTAX_ERROR,
+ self.NEW_GRD_CONTENTS1, action='M')])
+ results = PRESUBMIT._CheckStrings(input_api, MockOutputApi())
+ # We expect 2 ICU syntax errors.
+ icu_errors = [e for e in results
+ if e.message == self.ICU_SYNTAX_ERROR_MESSAGE]
+ self.assertEqual(1, len(icu_errors))
+ self.assertEqual([
+ 'IDS_TEST1: This message looks like an ICU plural, but does not follow '
+ 'ICU syntax.',
+ 'IDS_PART_TEST1: Variant "= 1" is not valid for plural message'
+ ], icu_errors[0].items)
+
+ # Change two strings to have ICU syntax errors. Should raise two errors.
+ input_api = self.makeInputApi([
+ MockAffectedFile('test.grd', self.NEW_GRD_CONTENTS_ICU_SYNTAX_ERROR,
+ self.NEW_GRD_CONTENTS_ICU_SYNTAX_OK1, action='M'),
+ MockAffectedFile('part.grdp', self.NEW_GRDP_CONTENTS_ICU_SYNTAX_ERROR,
+ self.NEW_GRDP_CONTENTS_ICU_SYNTAX_OK1, action='M')])
+ results = PRESUBMIT._CheckStrings(input_api, MockOutputApi())
+ # We expect 2 ICU syntax errors.
+ icu_errors = [e for e in results
+ if e.message == self.ICU_SYNTAX_ERROR_MESSAGE]
+ self.assertEqual(1, len(icu_errors))
+ self.assertEqual([
+ 'IDS_TEST1: This message looks like an ICU plural, but does not follow '
+ 'ICU syntax.',
+ 'IDS_PART_TEST1: Variant "= 1" is not valid for plural message'
+ ], icu_errors[0].items)
+
class TranslationExpectationsTest(unittest.TestCase):
ERROR_MESSAGE_FORMAT = (
@@ -3296,5 +3537,42 @@ class SetNoParentTest(unittest.TestCase):
self.assertEqual([], errors)
+class MojomStabilityCheckTest(unittest.TestCase):
+ def runTestWithAffectedFiles(self, affected_files):
+ mock_input_api = MockInputApi()
+ mock_input_api.files = affected_files
+ mock_output_api = MockOutputApi()
+ return PRESUBMIT._CheckStableMojomChanges(
+ mock_input_api, mock_output_api)
+
+ def testSafeChangePasses(self):
+ errors = self.runTestWithAffectedFiles([
+ MockAffectedFile('foo/foo.mojom',
+ ['[Stable] struct S { [MinVersion=1] int32 x; };'],
+ old_contents=['[Stable] struct S {};'])
+ ])
+ self.assertEqual([], errors)
+
+ def testBadChangeFails(self):
+ errors = self.runTestWithAffectedFiles([
+ MockAffectedFile('foo/foo.mojom',
+ ['[Stable] struct S { int32 x; };'],
+ old_contents=['[Stable] struct S {};'])
+ ])
+ self.assertEqual(1, len(errors))
+ self.assertTrue('not backward-compatible' in errors[0].message)
+
+ def testDeletedFile(self):
+ """Regression test for https://crbug.com/1091407."""
+ errors = self.runTestWithAffectedFiles([
+ MockAffectedFile('a.mojom', [], old_contents=['struct S {};'],
+ action='D'),
+ MockAffectedFile('b.mojom',
+ ['struct S {}; struct T { S s; };'],
+ old_contents=['import "a.mojom"; struct T { S s; };'])
+ ])
+ self.assertEqual([], errors)
+
+
if __name__ == '__main__':
unittest.main()