Lei Zhang | 5db76de | 2017-11-11 05:30:43 | [diff] [blame] | 1 | #!/usr/bin/env python |
| 2 | # Copyright 2016 The Chromium Authors. All rights reserved. |
pnoland | dfef663b | 2016-04-08 19:49:54 | [diff] [blame] | 3 | # Use of this source code is governed by a BSD-style license that can be |
| 4 | # found in the LICENSE file. |
| 5 | |
| 6 | import os |
| 7 | import re |
| 8 | import sys |
| 9 | import unittest |
Lei Zhang | 5db76de | 2017-11-11 05:30:43 | [diff] [blame] | 10 | |
pnoland | dfef663b | 2016-04-08 19:49:54 | [diff] [blame] | 11 | import PRESUBMIT |
| 12 | |
pnoland | 4feacb1e | 2016-10-21 21:50:03 | [diff] [blame] | 13 | sys.path.append( |
| 14 | os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) |
Lei Zhang | 5db76de | 2017-11-11 05:30:43 | [diff] [blame] | 15 | from PRESUBMIT_test_mocks import MockChange, MockOutputApi |
| 16 | |
| 17 | class MockCannedChecks(object): |
| 18 | def CheckChangeLintsClean(self, input_api, output_api, source_filter, |
| 19 | lint_filters, verbose_level): |
| 20 | return [] |
| 21 | |
pnoland | dfef663b | 2016-04-08 19:49:54 | [diff] [blame] | 22 | |
| 23 | class MockInputApi(object): |
| 24 | """ Mocked input api for unit testing of presubmit. |
| 25 | This lets us mock things like file system operations and changed files. |
| 26 | """ |
| 27 | def __init__(self): |
Lei Zhang | 5db76de | 2017-11-11 05:30:43 | [diff] [blame] | 28 | self.canned_checks = MockCannedChecks() |
pnoland | dfef663b | 2016-04-08 19:49:54 | [diff] [blame] | 29 | self.re = re |
| 30 | self.os_path = os.path |
| 31 | self.files = [] |
| 32 | self.is_committing = False |
| 33 | |
| 34 | def AffectedFiles(self): |
| 35 | return self.files |
| 36 | |
| 37 | def AffectedSourceFiles(self): |
| 38 | return self.files |
| 39 | |
| 40 | def ReadFile(self, f): |
| 41 | """ Returns the mock contents of f if they've been defined. |
| 42 | """ |
| 43 | for api_file in self.files: |
| 44 | if api_file.LocalPath() == f: |
| 45 | return api_file.NewContents() |
| 46 | |
| 47 | |
| 48 | class MockFile(object): |
| 49 | """Mock file object so that presubmit can act invoke file system operations. |
| 50 | """ |
| 51 | def __init__(self, local_path, new_contents): |
| 52 | self._local_path = local_path |
| 53 | self._new_contents = new_contents |
| 54 | self._changed_contents = ([(i + 1, l) for i, l in enumerate(new_contents)]) |
| 55 | |
| 56 | def ChangedContents(self): |
| 57 | return self._changed_contents |
| 58 | |
| 59 | def NewContents(self): |
| 60 | return self._new_contents |
| 61 | |
| 62 | def LocalPath(self): |
| 63 | return self._local_path |
| 64 | |
| 65 | def AbsoluteLocalPath(self): |
| 66 | return self._local_path |
Lei Zhang | 5db76de | 2017-11-11 05:30:43 | [diff] [blame] | 67 | |
| 68 | |
pnoland | dfef663b | 2016-04-08 19:49:54 | [diff] [blame] | 69 | # Format string used as the contents of a mock sync.proto in order to |
| 70 | # test presubmit parsing of EntitySpecifics definition in that file. |
| 71 | MOCK_PROTOFILE_CONTENTS = ('\n' |
| 72 | 'message EntitySpecifics {\n' |
Jan Krcal | a87a02de | 2018-07-24 16:57:33 | [diff] [blame] | 73 | ' //comment\n' |
pnoland | dfef663b | 2016-04-08 19:49:54 | [diff] [blame] | 74 | '\n' |
Jan Krcal | a87a02de | 2018-07-24 16:57:33 | [diff] [blame] | 75 | ' oneof specifics_variant {\n' |
| 76 | ' AutofillSpecifics autofill = 123;\n' |
| 77 | ' AppSpecifics app = 456;\n' |
| 78 | ' AppSettingSpecifics app_setting = 789;\n' |
| 79 | ' ExtensionSettingSpecifics extension_setting = 910;\n' |
Jan Krcal | a87a02de | 2018-07-24 16:57:33 | [diff] [blame] | 80 | ' //comment\n' |
| 81 | ' }\n' |
| 82 | '}\n' |
| 83 | ) |
pnoland | dfef663b | 2016-04-08 19:49:54 | [diff] [blame] | 84 | |
| 85 | |
| 86 | # Format string used as the contents of a mock model_type.cc |
| 87 | # in order to test presubmit parsing of the ModelTypeInfoMap in that file. |
| 88 | MOCK_MODELTYPE_CONTENTS =('\n' |
| 89 | 'const ModelTypeInfo kModelTypeInfoMap[] = {\n' |
vitaliii | 30e9dbf4 | 2018-05-11 13:27:04 | [diff] [blame] | 90 | '// Some comment \n' |
pnoland | dfef663b | 2016-04-08 19:49:54 | [diff] [blame] | 91 | '{APP_SETTINGS, "APP_SETTING", "app_settings", "App settings",\n' |
| 92 | 'sync_pb::EntitySpecifics::kAppSettingFieldNumber, 13},\n' |
| 93 | '%s\n' |
| 94 | '};\n') |
| 95 | |
| 96 | |
| 97 | class ModelTypeInfoChangeTest(unittest.TestCase): |
| 98 | """Unit testing class that contains tests for sync/PRESUBMIT.py. |
| 99 | """ |
| 100 | def test_ValidChangeMultiLine(self): |
| 101 | results = self._testChange('{APPS, "APP", "apps", "Apps",\n' |
| 102 | 'sync_pb::EntitySpecifics::kAppFieldNumber, 12},') |
| 103 | self.assertEqual(0, len(results)) |
| 104 | |
| 105 | def testValidChangeToleratesPluralization(self): |
| 106 | results = self._testChange('{APPS, "APP", "apps", "App",\n' |
| 107 | 'sync_pb::EntitySpecifics::kAppFieldNumber, 12},') |
| 108 | self.assertEqual(0, len(results)) |
| 109 | |
| 110 | def testValidChangeGrandfatheredEntry(self): |
| 111 | results = self._testChange('{PROXY_TABS, "", "", "Tabs", -1, 25},') |
| 112 | self.assertEqual(0, len(results)) |
| 113 | |
Victor Hugo Vianna Silva | 8d782af | 2021-01-27 19:18:09 | [diff] [blame] | 114 | # TODO(crbug.com/1170749): The only remaining deprecated type doesn't satisfy |
| 115 | # this test, revisit it. |
| 116 | def DISABLED_testValidChangeDeprecatedEntry(self): |
| 117 | results = self._testChange('{DEPRECATED_SUPERVISED_USER_ALLOWLISTS,\n' |
| 118 | '"MANAGED_USER_WHITELIST",\n' |
| 119 | '"managed_user_whitelists", "Managed User Whitelists",\n' |
| 120 | 'sync_pb::EntitySpecifics::kManagedUserWhitelistFieldNumber, 33},') |
Marc Treib | 39804b2 | 2020-02-04 17:12:20 | [diff] [blame] | 121 | self.assertEqual(0, len(results)) |
| 122 | |
pnoland | dfef663b | 2016-04-08 19:49:54 | [diff] [blame] | 123 | def testInvalidChangeMismatchedNotificationType(self): |
| 124 | results = self._testChange('{AUTOFILL, "AUTOFILL_WRONG", "autofill",\n' |
| 125 | '"Autofill",sync_pb::EntitySpecifics::kAutofillFieldNumber, 6},') |
| 126 | self.assertEqual(1, len(results)) |
| 127 | self.assertTrue('notification type' in results[0].message) |
| 128 | |
| 129 | def testInvalidChangeInconsistentModelType(self): |
| 130 | results = self._testChange('{AUTOFILL, "AUTOFILL", "autofill",\n' |
| 131 | '"Autofill Extra",sync_pb::EntitySpecifics::kAutofillFieldNumber, 6},') |
| 132 | self.assertEqual(1, len(results)) |
| 133 | self.assertTrue('model type string' in results[0].message) |
| 134 | |
| 135 | def testInvalidChangeNotTitleCased(self): |
| 136 | results = self._testChange('{AUTOFILL, "AUTOFILL", "autofill",\n' |
| 137 | '"autofill",sync_pb::EntitySpecifics::kAutofillFieldNumber, 6},') |
| 138 | self.assertEqual(1, len(results)) |
| 139 | self.assertTrue('title' in results[0].message) |
| 140 | |
| 141 | def testInvalidChangeInconsistentRootTag(self): |
| 142 | results = self._testChange('{AUTOFILL, "AUTOFILL", "autofill root",\n' |
| 143 | '"Autofill",sync_pb::EntitySpecifics::kAutofillFieldNumber, 6},') |
| 144 | self.assertEqual(1, len(results)) |
| 145 | self.assertTrue('root tag' in results[0].message) |
| 146 | |
| 147 | def testInvalidChangeDuplicatedValues(self): |
| 148 | results = self._testChange('{APP_SETTINGS, "APP_SETTING",\n' |
| 149 | '"app_settings", "App settings",\n' |
| 150 | 'sync_pb::EntitySpecifics::kAppSettingFieldNumber, 13},\n') |
| 151 | self.assertEqual(6, len(results)) |
| 152 | self.assertTrue('APP_SETTINGS' in results[0].message) |
| 153 | |
Toby Huang | a7246afa | 2021-04-13 15:48:45 | [diff] [blame] | 154 | def testBlocklistedRootTag(self): |
pnoland | 4feacb1e | 2016-10-21 21:50:03 | [diff] [blame] | 155 | results = self._testChange('{EXTENSION_SETTING, "EXTENSION_SETTING",\n' |
| 156 | '"_mts_schema_descriptor","Extension Setting",\n' |
| 157 | 'sync_pb::EntitySpecifics::kExtensionSettingFieldNumber, 6},') |
| 158 | self.assertEqual(2, len(results)) |
| 159 | self.assertTrue('_mts_schema_descriptor' in results[0].message) |
Toby Huang | a7246afa | 2021-04-13 15:48:45 | [diff] [blame] | 160 | self.assertTrue("blocklist" in results[0].message) |
pnoland | 4feacb1e | 2016-10-21 21:50:03 | [diff] [blame] | 161 | |
pnoland | dfef663b | 2016-04-08 19:49:54 | [diff] [blame] | 162 | def _testChange(self, modeltype_literal): |
| 163 | mock_input_api = MockInputApi() |
| 164 | mock_input_api.files = [ |
| 165 | MockFile(os.path.abspath('./protocol/sync.proto'), |
| 166 | MOCK_PROTOFILE_CONTENTS), |
Mikel Astiz | cb6827e | 2019-04-02 12:33:46 | [diff] [blame] | 167 | MockFile(os.path.abspath('./base/model_type.cc'), |
pnoland | dfef663b | 2016-04-08 19:49:54 | [diff] [blame] | 168 | MOCK_MODELTYPE_CONTENTS % (modeltype_literal)) |
| 169 | ] |
| 170 | |
| 171 | return PRESUBMIT.CheckChangeOnCommit(mock_input_api, MockOutputApi()) |
Lei Zhang | 5db76de | 2017-11-11 05:30:43 | [diff] [blame] | 172 | |
| 173 | |
pnoland | dfef663b | 2016-04-08 19:49:54 | [diff] [blame] | 174 | if __name__ == '__main__': |
| 175 | unittest.main() |