Index: extensions/browser/PRESUBMIT.py |
diff --git a/extensions/browser/PRESUBMIT.py b/extensions/browser/PRESUBMIT.py |
index 29a709b5c807ff63d08b90bba2efb5c61c888067..659c1761e264a12132b2d104f4ef0e357afe9872 100644 |
--- a/extensions/browser/PRESUBMIT.py |
+++ b/extensions/browser/PRESUBMIT.py |
@@ -8,6 +8,8 @@ See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts |
for more details on the presubmit API built into gcl. |
""" |
+import sys |
+ |
def GetPreferredTryMasters(project, change): |
return { |
'tryserver.chromium': { |
@@ -15,294 +17,26 @@ def GetPreferredTryMasters(project, change): |
} |
} |
-class HistogramValueChecker(object): |
- """Verify that changes to "extension_function_histogram_value.h" are valid. |
- |
- See comments at the top of the "extension_function_histogram_value.h" file |
- for what are considered valid changes. There are situations where this script |
- gives false positive warnings, i.e. it warns even though the edit is |
- legitimate. Since the script warns using prompt warnings, the user can always |
- choose to continue. The main point is to attract the attention to all |
- (potentially or not) invalid edits. |
- |
- """ |
- |
- # The name of the file we want to check against |
- LOCAL_PATH = "extensions/browser/extension_function_histogram_value.h" |
- |
- # The markers we look for in the above source file as delimiters of the enum |
- # definition. |
- ENUM_START_MARKER = "enum HistogramValue {" |
- ENUM_END_MARKER = " // Last entry:" |
- |
- def __init__(self, input_api, output_api): |
- self.input_api = input_api |
- self.output_api = output_api |
- self.results = [] |
- |
- class EnumRange(object): |
- """Represents a range of line numbers (1-based)""" |
- def __init__(self, first_line, last_line): |
- self.first_line = first_line |
- self.last_line = last_line |
- |
- def Count(self): |
- return self.last_line - self.first_line + 1 |
- |
- def Contains(self, line_num): |
- return self.first_line <= line_num and line_num <= self.last_line |
- |
- def LogInfo(self, message): |
- self.input_api.logging.info(message) |
- return |
- |
- def LogDebug(self, message): |
- self.input_api.logging.debug(message) |
- return |
- |
- def ComputeEnumRangeInContents(self, contents): |
- """Returns an |EnumRange| object representing the line extent of the |
- HistogramValue enum members in |contents|. The line numbers are 1-based, |
- compatible with line numbers returned by AffectedFile.ChangeContents(). |
- |contents| is a list of strings reprenting the lines of a text file. |
- |
- If either ENUM_START_MARKER or ENUM_END_MARKER cannot be found in |
- |contents|, returns None and emits detailed warnings about the problem. |
- |
- """ |
- first_enum_line = 0 |
- last_enum_line = 0 |
- line_num = 1 # Line numbers are 1-based |
- for line in contents: |
- if line.startswith(self.ENUM_START_MARKER): |
- first_enum_line = line_num + 1 |
- elif line.startswith(self.ENUM_END_MARKER): |
- last_enum_line = line_num |
- line_num += 1 |
- |
- if first_enum_line == 0: |
- self.EmitWarning("The presubmit script could not find the start of the " |
- "enum definition (\"%s\"). Did the enum definition " |
- "change?" % self.ENUM_START_MARKER) |
- return None |
- |
- if last_enum_line == 0: |
- self.EmitWarning("The presubmit script could not find the end of the " |
- "enum definition (\"%s\"). Did the enum definition " |
- "change?" % self.ENUM_END_MARKER) |
- return None |
- |
- if first_enum_line >= last_enum_line: |
- self.EmitWarning("The presubmit script located the start of the enum " |
- "definition (\"%s\" at line %d) *after* its end " |
- "(\"%s\" at line %d). Something is not quite right." |
- % (self.ENUM_START_MARKER, first_enum_line, |
- self.ENUM_END_MARKER, last_enum_line)) |
- return None |
- |
- self.LogInfo("Line extent of |HistogramValue| enum definition: " |
- "first_line=%d, last_line=%d." |
- % (first_enum_line, last_enum_line)) |
- return self.EnumRange(first_enum_line, last_enum_line) |
- |
- def ComputeEnumRangeInNewFile(self, affected_file): |
- return self.ComputeEnumRangeInContents(affected_file.NewContents()) |
- |
- def GetLongMessage(self): |
- return str("The file \"%s\" contains the definition of the " |
- "|HistogramValue| enum which should be edited in specific ways " |
- "only - *** read the comments at the top of the header file ***" |
- ". There are changes to the file that may be incorrect and " |
- "warrant manual confirmation after review. Note that this " |
- "presubmit script can not reliably report the nature of all " |
- "types of invalid changes, especially when the diffs are " |
- "complex. For example, an invalid deletion may be reported " |
- "whereas the change contains a valid rename." |
- % self.LOCAL_PATH) |
- |
- def EmitWarning(self, message, line_number=None, line_text=None): |
- """Emits a presubmit prompt warning containing the short message |
- |message|. |item| is |LOCAL_PATH| with optional |line_number| and |
- |line_text|. |
- |
- """ |
- if line_number is not None and line_text is not None: |
- item = "%s(%d): %s" % (self.LOCAL_PATH, line_number, line_text) |
- elif line_number is not None: |
- item = "%s(%d)" % (self.LOCAL_PATH, line_number) |
- else: |
- item = self.LOCAL_PATH |
- long_message = self.GetLongMessage() |
- self.LogInfo(message) |
- self.results.append( |
- self.output_api.PresubmitPromptWarning(message, [item], long_message)) |
- |
- def CollectRangesInsideEnumDefinition(self, affected_file, |
- first_line, last_line): |
- """Returns a list of triplet (line_start, line_end, line_text) of ranges of |
- edits changes. The |line_text| part is the text at line |line_start|. |
- Since it used only for reporting purposes, we do not need all the text |
- lines in the range. |
- |
- """ |
- results = [] |
- previous_line_number = 0 |
- previous_range_start_line_number = 0 |
- previous_range_start_text = "" |
- |
- def addRange(): |
- tuple = (previous_range_start_line_number, |
- previous_line_number, |
- previous_range_start_text) |
- results.append(tuple) |
- |
- for line_number, line_text in affected_file.ChangedContents(): |
- if first_line <= line_number and line_number <= last_line: |
- self.LogDebug("Line change at line number " + str(line_number) + ": " + |
- line_text) |
- # Start a new interval if none started |
- if previous_range_start_line_number == 0: |
- previous_range_start_line_number = line_number |
- previous_range_start_text = line_text |
- # Add new interval if we reached past the previous one |
- elif line_number != previous_line_number + 1: |
- addRange() |
- previous_range_start_line_number = line_number |
- previous_range_start_text = line_text |
- previous_line_number = line_number |
- |
- # Add a last interval if needed |
- if previous_range_start_line_number != 0: |
- addRange() |
- return results |
- |
- def CheckForFileDeletion(self, affected_file): |
- """Emits a warning notification if file has been deleted """ |
- if not affected_file.NewContents(): |
- self.EmitWarning("The file seems to be deleted in the changelist. If " |
- "your intent is to really delete the file, the code in " |
- "PRESUBMIT.py should be updated to remove the " |
- "|HistogramValueChecker| class."); |
- return False |
- return True |
- |
- def GetDeletedLinesFromScmDiff(self, affected_file): |
- """Return a list of of line numbers (1-based) corresponding to lines |
- deleted from the new source file (if they had been present in it). Note |
- that if multiple contiguous lines have been deleted, the returned list will |
- contain contiguous line number entries. To prevent false positives, we |
- return deleted line numbers *only* from diff chunks which decrease the size |
- of the new file. |
- |
- Note: We need this method because we have access to neither the old file |
- content nor the list of "delete" changes from the current presubmit script |
- API. |
- |
- """ |
- results = [] |
- line_num = 0 |
- deleting_lines = False |
- for line in affected_file.GenerateScmDiff().splitlines(): |
- # Parse the unified diff chunk optional section heading, which looks like |
- # @@ -l,s +l,s @@ optional section heading |
- m = self.input_api.re.match( |
- r'^@@ \-([0-9]+)\,([0-9]+) \+([0-9]+)\,([0-9]+) @@', line) |
- if m: |
- old_line_num = int(m.group(1)) |
- old_size = int(m.group(2)) |
- new_line_num = int(m.group(3)) |
- new_size = int(m.group(4)) |
- line_num = new_line_num |
- # Return line numbers only from diff chunks decreasing the size of the |
- # new file |
- deleting_lines = old_size > new_size |
- continue |
- if not line.startswith('-'): |
- line_num += 1 |
- if deleting_lines and line.startswith('-') and not line.startswith('--'): |
- results.append(line_num) |
- return results |
- |
- def CheckForEnumEntryDeletions(self, affected_file): |
- """Look for deletions inside the enum definition. We currently use a |
- simple heuristics (not 100% accurate): if there are deleted lines inside |
- the enum definition, this might be a deletion. |
- |
- """ |
- range_new = self.ComputeEnumRangeInNewFile(affected_file) |
- if not range_new: |
- return False |
- |
- is_ok = True |
- for line_num in self.GetDeletedLinesFromScmDiff(affected_file): |
- if range_new.Contains(line_num): |
- self.EmitWarning("It looks like you are deleting line(s) from the " |
- "enum definition. This should never happen.", |
- line_num) |
- is_ok = False |
- return is_ok |
- |
- def CheckForEnumEntryInsertions(self, affected_file): |
- range = self.ComputeEnumRangeInNewFile(affected_file) |
- if not range: |
- return False |
- |
- first_line = range.first_line |
- last_line = range.last_line |
- |
- # Collect the range of changes inside the enum definition range. |
- is_ok = True |
- for line_start, line_end, line_text in \ |
- self.CollectRangesInsideEnumDefinition(affected_file, |
- first_line, |
- last_line): |
- # The only edit we consider valid is adding 1 or more entries *exactly* |
- # at the end of the enum definition. Every other edit inside the enum |
- # definition will result in a "warning confirmation" message. |
- # |
- # TODO(rpaquay): We currently cannot detect "renames" of existing entries |
- # vs invalid insertions, so we sometimes will warn for valid edits. |
- is_valid_edit = (line_end == last_line - 1) |
- |
- self.LogDebug("Edit range in new file at starting at line number %d and " |
- "ending at line number %d: valid=%s" |
- % (line_start, line_end, is_valid_edit)) |
- |
- if not is_valid_edit: |
- self.EmitWarning("The change starting at line %d and ending at line " |
- "%d is *not* located *exactly* at the end of the " |
- "enum definition. Unless you are renaming an " |
- "existing entry, this is not a valid changes, as new " |
- "entries should *always* be added at the end of the " |
- "enum definition, right before the 'ENUM_BOUNDARY' " |
- "entry." % (line_start, line_end), |
- line_start, |
- line_text) |
- is_ok = False |
- return is_ok |
- |
- def PerformChecks(self, affected_file): |
- if not self.CheckForFileDeletion(affected_file): |
- return |
- if not self.CheckForEnumEntryDeletions(affected_file): |
- return |
- if not self.CheckForEnumEntryInsertions(affected_file): |
- return |
+def _CreateHistogramValueChecker(input_api, output_api): |
+ original_sys_path = sys.path |
- def ProcessHistogramValueFile(self, affected_file): |
- self.LogInfo("Start processing file \"%s\"" % affected_file.LocalPath()) |
- self.PerformChecks(affected_file) |
- self.LogInfo("Done processing file \"%s\"" % affected_file.LocalPath()) |
+ try: |
+ sys.path.append(input_api.os_path.join( |
+ input_api.PresubmitLocalPath(), '..', '..', 'tools', |
+ 'strict_enum_value_checker')) |
+ from strict_enum_value_checker import StrictEnumValueChecker |
+ finally: |
+ sys.path = original_sys_path |
- def Run(self): |
- for file in self.input_api.AffectedFiles(include_deletes=True): |
- if file.LocalPath() == self.LOCAL_PATH: |
- self.ProcessHistogramValueFile(file) |
- return self.results |
+ return StrictEnumValueChecker(input_api, output_api, |
+ start_marker='enum HistogramValue {', end_marker=' // Last entry:', |
+ path='extensions/browser/extension_function_histogram_value.h') |
def CheckChangeOnUpload(input_api, output_api): |
results = [] |
- results += HistogramValueChecker(input_api, output_api).Run() |
+ results += _CreateHistogramValueChecker(input_api, output_api).Run() |
results += input_api.canned_checks.CheckPatchFormatted(input_api, output_api) |
return results |
+def CheckChangeOnCommit(input_api, output_api): |
+ return _CreateHistogramValueChecker(input_api, output_api).Run() |