Index: sync/PRESUBMIT.py |
diff --git a/sync/PRESUBMIT.py b/sync/PRESUBMIT.py |
deleted file mode 100644 |
index 9173777c327cf11b6369c8e18d23a434c7bdfc19..0000000000000000000000000000000000000000 |
--- a/sync/PRESUBMIT.py |
+++ /dev/null |
@@ -1,359 +0,0 @@ |
-# Copyright (c) 2016 The Chromium Authors. All rights reserved. |
-# Use of this source code is governed by a BSD-style license that can be |
-# found in the LICENSE file. |
- |
-"""Presubmit script for sync |
-This checks that ModelTypeInfo entries in model_type.cc follow conventions. |
-See CheckModelTypeInfoMap or model_type.cc for more detail on the rules. |
-""" |
- |
-import os |
- |
-# Some definitions don't follow all the conventions we want to enforce. |
-# It's either difficult or impossible to fix this, so we ignore the problem(s). |
-GRANDFATHERED_MODEL_TYPES = [ |
- 'UNSPECIFIED', # Doesn't have a root tag or notification type. |
- 'TOP_LEVEL_FOLDER', # Doesn't have a root tag or notification type. |
- 'AUTOFILL_WALLET_DATA', # Root tag and model type string lack DATA suffix. |
- 'APP_SETTINGS', # Model type string has inconsistent capitalization. |
- 'EXTENSION_SETTINGS', # Model type string has inconsistent capitalization. |
- 'SUPERVISED_USER_SETTINGS', # Root tag and model type string replace |
- # 'Supervised' with 'Managed' |
- 'SUPERVISED_USERS', # See previous. |
- 'SUPERVISED_USER_WHITELISTS', # See previous. |
- 'SUPERVISED_USER_SHARED_SETTINGS', # See previous. |
- 'PROXY_TABS', # Doesn't have a root tag or notification type. |
- 'NIGORI'] # Model type string is 'encryption keys'. |
- |
-# Number of distinct fields in a map entry; used to create |
-# sets that check for uniqueness. |
-MAP_ENTRY_FIELD_COUNT = 6 |
- |
-# String that precedes the ModelType when referencing the |
-# proto field number enum e.g. |
-# sync_pb::EntitySpecifics::kManagedUserFieldNumber. |
-# Used to map from enum references to the ModelType. |
-FIELD_NUMBER_PREFIX = 'sync_pb::EntitySpecifics::k' |
- |
-# Start and end regexes for finding the EntitySpecifics definition in |
-# sync.proto. |
-PROTO_DEFINITION_START_PATTERN = '^message EntitySpecifics' |
-PROTO_DEFINITION_END_PATTERN = '^\}' |
- |
-# Start and end regexes for finding the ModelTypeInfoMap definition |
-# in model_type.cc. |
-MODEL_TYPE_START_PATTERN = '^const ModelTypeInfo kModelTypeInfoMap' |
-MODEL_TYPE_END_PATTERN = '^\};' |
- |
-# Strings relating to files we'll need to read. |
-# model_type.cc is where the ModelTypeInfoMap is |
-# sync.proto is where the proto definitions for ModelTypes are. |
-PROTO_FILE_PATH = './protocol/sync.proto' |
-MODEL_TYPE_FILE_NAME = 'model_type.cc' |
- |
- |
-def CheckChangeOnUpload(input_api, output_api): |
- """Preupload check function required by presubmit convention. |
- See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts |
- """ |
- for f in input_api.AffectedFiles(): |
- if(f.LocalPath().endswith(MODEL_TYPE_FILE_NAME)): |
- return CheckModelTypeInfoMap(input_api, output_api, f) |
- return [] |
- |
- |
-def CheckChangeOnCommit(input_api, output_api): |
- """Precommit check function required by presubmit convention. |
- See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts |
- """ |
- for f in input_api.AffectedFiles(): |
- if f.LocalPath().endswith(MODEL_TYPE_FILE_NAME): |
- return CheckModelTypeInfoMap(input_api, output_api, f) |
- return [] |
- |
- |
-def CheckModelTypeInfoMap(input_api, output_api, model_type_file): |
- """Checks the kModelTypeInfoMap in model_type.cc follows conventions. |
- Checks that the kModelTypeInfoMap follows the below rules: |
- 1) The model type string should match the model type name, but with |
- only the first letter capitalized and spaces instead of underscores. |
- 2) The root tag should be the same as the model type but all lowercase. |
- 3) The notification type should match the proto message name. |
- 4) No duplicate data across model types. |
- Args: |
- input_api: presubmit_support InputApi instance |
- output_api: presubmit_support OutputApi instance |
- model_type_file: AffectedFile object where the ModelTypeInfoMap is |
- Returns: |
- A (potentially empty) list PresubmitError objects corresponding to |
- violations of the above rules. |
- """ |
- accumulated_problems = [] |
- map_entries = ParseModelTypeEntries( |
- input_api, model_type_file.AbsoluteLocalPath()) |
- # If any line of the map changed, we check the whole thing since |
- # definitions span multiple lines and there are rules that apply across |
- # all definitions e.g. no duplicated field values. |
- check_map = False |
- for line_num, _ in model_type_file.ChangedContents(): |
- for map_entry in map_entries: |
- if line_num in map_entry.affected_lines: |
- check_map = True |
- break |
- |
- if not check_map: |
- return [] |
- proto_field_definitions = ParseSyncProtoFieldIdentifiers( |
- input_api, os.path.abspath(PROTO_FILE_PATH)) |
- accumulated_problems.extend( |
- CheckNoDuplicatedFieldValues(output_api, map_entries)) |
- |
- for map_entry in map_entries: |
- entry_problems = [] |
- entry_problems.extend( |
- CheckNotificationTypeMatchesProtoMessageName( |
- output_api, map_entry, proto_field_definitions)) |
- |
- if map_entry.model_type not in GRANDFATHERED_MODEL_TYPES: |
- entry_problems.extend( |
- CheckModelTypeStringMatchesModelType(output_api, map_entry)) |
- entry_problems.extend( |
- CheckRootTagMatchesModelType(output_api, map_entry)) |
- |
- if len(entry_problems) > 0: |
- accumulated_problems.extend(entry_problems) |
- |
- return accumulated_problems |
- |
- |
-class ModelTypeEnumEntry(object): |
- """Class that encapsulates a ModelTypeInfo definition in model_type.cc. |
- Allows access to each of the named fields in the definition and also |
- which lines the definition spans. |
- Attributes: |
- model_type: entry's ModelType enum value |
- notification_type: model type's notification string |
- root_tag: model type's root tag |
- model_type_string: string corresponding to the ModelType |
- field_number: proto field number |
- histogram_val: value identifying ModelType in histogram |
- affected_lines: lines in model_type.cc that the definition spans |
- """ |
- def __init__(self, entry_strings, affected_lines): |
- (model_type, notification_type, root_tag, model_type_string, |
- field_number, histogram_val) = entry_strings |
- self.model_type = model_type |
- self.notification_type = notification_type |
- self.root_tag = root_tag |
- self.model_type_string = model_type_string |
- self.field_number = field_number |
- self.histogram_val = histogram_val |
- self.affected_lines = affected_lines |
- |
- |
-def ParseModelTypeEntries(input_api, model_type_cc_path): |
- """Parses model_type_cc_path for ModelTypeEnumEntries |
- Args: |
- input_api: presubmit_support InputAPI instance |
- model_type_cc_path: path to file containing the ModelTypeInfo entries |
- Returns: |
- A list of ModelTypeEnumEntry objects read from model_type.cc. |
- e.g. ('AUTOFILL_WALLET_METADATA', 'WALLET_METADATA', |
- 'autofill_wallet_metadata', 'Autofill Wallet Metadata', |
- 'sync_pb::EntitySpecifics::kWalletMetadataFieldNumber', '35', |
- [63, 64, 65]) |
- """ |
- file_contents = input_api.ReadFile(model_type_cc_path) |
- start_pattern = input_api.re.compile(MODEL_TYPE_START_PATTERN) |
- end_pattern = input_api.re.compile(MODEL_TYPE_END_PATTERN) |
- results, definition_strings, definition_lines = [], [], [] |
- inside_enum = False |
- current_line_number = 1 |
- for line in file_contents.splitlines(): |
- if start_pattern.match(line): |
- inside_enum = True |
- continue |
- if inside_enum: |
- if end_pattern.match(line): |
- break |
- line_entries = line.strip().strip('{},').split(',') |
- definition_strings.extend([entry.strip('" ') for entry in line_entries]) |
- definition_lines.append(current_line_number) |
- if line.endswith('},'): |
- results.append(ModelTypeEnumEntry(definition_strings, definition_lines)) |
- definition_strings = [] |
- definition_lines = [] |
- current_line_number += 1 |
- return results |
- |
- |
-def ParseSyncProtoFieldIdentifiers(input_api, sync_proto_path): |
- """Parses proto field identifiers from the EntitySpecifics definition. |
- Args: |
- input_api: presubmit_support InputAPI instance |
- proto_path: path to the file containing the proto field definitions |
- Returns: |
- A dictionary of the format {'SyncDataType': 'field_identifier'} |
- e.g. {'AutofillSpecifics': 'autofill'} |
- """ |
- proto_field_definitions = {} |
- proto_file_contents = input_api.ReadFile(sync_proto_path).splitlines() |
- start_pattern = input_api.re.compile(PROTO_DEFINITION_START_PATTERN) |
- end_pattern = input_api.re.compile(PROTO_DEFINITION_END_PATTERN) |
- in_proto_def = False |
- for line in proto_file_contents: |
- if start_pattern.match(line): |
- in_proto_def = True |
- continue |
- if in_proto_def: |
- if end_pattern.match(line): |
- break |
- line = line.strip() |
- split_proto_line = line.split(' ') |
- # ignore comments and lines that don't contain definitions. |
- if '//' in line or len(split_proto_line) < 3: |
- continue |
- |
- field_typename = split_proto_line[1] |
- field_identifier = split_proto_line[2] |
- proto_field_definitions[field_typename] = field_identifier |
- return proto_field_definitions |
- |
- |
-def StripTrailingS(string): |
- return string.rstrip('sS') |
- |
- |
-def IsTitleCased(string): |
- return reduce(lambda bool1, bool2: bool1 and bool2, |
- [s[0].isupper() for s in string.split(' ')]) |
- |
- |
-def FormatPresubmitError(output_api, message, affected_lines): |
- """ Outputs a formatted error message with filename and line number(s). |
- """ |
- if len(affected_lines) > 1: |
- message_including_lines = 'Error at lines %d-%d in model_type.cc: %s' %( |
- affected_lines[0], affected_lines[-1], message) |
- else: |
- message_including_lines = 'Error at line %d in model_type.cc: %s' %( |
- affected_lines[0], message) |
- return output_api.PresubmitError(message_including_lines) |
- |
- |
-def CheckNotificationTypeMatchesProtoMessageName( |
- output_api, map_entry, proto_field_definitions): |
- """Check that map_entry's notification type matches sync.proto. |
- Verifies that the notification_type matches the name of the field defined |
- in the sync.proto by looking it up in the proto_field_definitions map. |
- Args: |
- output_api: presubmit_support OutputApi instance |
- map_entry: ModelTypeEnumEntry instance |
- proto_field_definitions: dict of proto field types and field names |
- Returns: |
- A potentially empty list of PresubmitError objects corresponding to |
- violations of the above rule |
- """ |
- if map_entry.field_number == '-1': |
- return [] |
- proto_message_name = proto_field_definitions[ |
- FieldNumberToPrototypeString(map_entry.field_number)] |
- if map_entry.notification_type.lower() != proto_message_name: |
- return [ |
- FormatPresubmitError( |
- output_api,'notification type "%s" does not match proto message' |
- ' name defined in sync.proto: ' '"%s"' % |
- (map_entry.notification_type, proto_message_name), |
- map_entry.affected_lines)] |
- return [] |
- |
- |
-def CheckNoDuplicatedFieldValues(output_api, map_entries): |
- """Check that map_entries has no duplicated field values. |
- Verifies that every map_entry in map_entries doesn't have a field value |
- used elsewhere in map_entries, ignoring special values ("" and -1). |
- Args: |
- output_api: presubmit_support OutputApi instance |
- map_entries: list of ModelTypeEnumEntry objects to check |
- Returns: |
- A list PresubmitError objects for each duplicated field value |
- """ |
- problem_list = [] |
- field_value_sets = [set() for i in range(MAP_ENTRY_FIELD_COUNT)] |
- for map_entry in map_entries: |
- field_values = [ |
- map_entry.model_type, map_entry.notification_type, |
- map_entry.root_tag, map_entry.model_type_string, |
- map_entry.field_number, map_entry.histogram_val] |
- for i in range(MAP_ENTRY_FIELD_COUNT): |
- field_value = field_values[i] |
- field_value_set = field_value_sets[i] |
- if field_value in field_value_set: |
- problem_list.append( |
- FormatPresubmitError( |
- output_api, 'Duplicated field value "%s"' % field_value, |
- map_entry.affected_lines)) |
- elif len(field_value) > 0 and field_value != '-1': |
- field_value_set.add(field_value) |
- return problem_list |
- |
- |
-def CheckModelTypeStringMatchesModelType(output_api, map_entry): |
- """Check that map_entry's model_type_string matches ModelType. |
- Args: |
- output_api: presubmit_support OutputApi instance |
- map_entry: ModelTypeEnumEntry object to check |
- Returns: |
- A list of PresubmitError objects for each violation |
- """ |
- problem_list = [] |
- expected_model_type_string = map_entry.model_type.lower().replace('_', ' ') |
- if (StripTrailingS(expected_model_type_string) != |
- StripTrailingS(map_entry.model_type_string.lower())): |
- problem_list.append( |
- FormatPresubmitError( |
- output_api,'model type string "%s" does not match model type.' |
- ' It should be "%s"' % ( |
- map_entry.model_type_string, expected_model_type_string.title()), |
- map_entry.affected_lines)) |
- if not IsTitleCased(map_entry.model_type_string): |
- problem_list.append( |
- FormatPresubmitError( |
- output_api,'model type string "%s" should be title cased' % |
- (map_entry.model_type_string), map_entry.affected_lines)) |
- return problem_list |
- |
- |
-def CheckRootTagMatchesModelType(output_api, map_entry): |
- """Check that map_entry's root tag matches ModelType. |
- Args: |
- output_api: presubmit_support OutputAPI instance |
- map_entry: ModelTypeEnumEntry object to check |
- Returns: |
- A list of PresubmitError objects for each violation |
- """ |
- expected_root_tag = map_entry.model_type.lower() |
- if (StripTrailingS(expected_root_tag) != |
- StripTrailingS(map_entry.root_tag)): |
- return [ |
- FormatPresubmitError( |
- output_api,'root tag "%s" does not match model type. It should' |
- 'be "%s"' % (map_entry.root_tag, expected_root_tag), |
- map_entry.affected_lines)] |
- return [] |
- |
- |
-def FieldNumberToPrototypeString(field_number): |
- """Converts a field number enum reference to an EntitySpecifics string. |
- Converts a reference to the field number enum to the corresponding |
- proto data type string. |
- Args: |
- field_number: string representation of a field number enum reference |
- Returns: |
- A string that is the corresponding proto field data type. e.g. |
- FieldNumberToPrototypeString('EntitySpecifics::kAppFieldNumber') |
- => 'AppSpecifics' |
- """ |
- return field_number.replace(FIELD_NUMBER_PREFIX, '').replace( |
- 'FieldNumber', 'Specifics').replace( |
- 'AppNotificationSpecifics', 'AppNotification') |