PRESUBMIT_test.py 7.2 KB

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