# Copyright 2015 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Top-level presubmit script for src/components/cronet.
See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
for more details about the presubmit API built into depot_tools.
"""
import os
def _PyLintChecks(input_api, output_api):
pylint_checks = input_api.canned_checks.GetPylint(input_api, output_api,
extra_paths_list=_GetPathsToPrepend(input_api), pylintrc='pylintrc',
version='2.7')
return input_api.RunTests(pylint_checks)
def _GetPathsToPrepend(input_api):
current_dir = input_api.PresubmitLocalPath()
chromium_src_dir = input_api.os_path.join(current_dir, '..', '..')
return [
input_api.os_path.join(chromium_src_dir, 'components'),
input_api.os_path.join(chromium_src_dir, 'tools', 'perf'),
input_api.os_path.join(chromium_src_dir, 'build', 'android'),
input_api.os_path.join(chromium_src_dir, 'build', 'android', 'gyp'),
input_api.os_path.join(chromium_src_dir,
'mojo', 'public', 'tools', 'bindings', 'pylib'),
input_api.os_path.join(chromium_src_dir, 'net', 'tools', 'net_docs'),
input_api.os_path.join(chromium_src_dir, 'tools'),
input_api.os_path.join(chromium_src_dir, 'third_party'),
input_api.os_path.join(chromium_src_dir,
'third_party', 'catapult', 'telemetry'),
input_api.os_path.join(chromium_src_dir,
'third_party', 'catapult', 'devil'),
input_api.os_path.join(chromium_src_dir,
'third_party', 'catapult', 'common', 'py_utils'),
]
def _PackageChecks(input_api, output_api):
"""Verify API classes are in org.chromium.net package, and implementation
classes are not in org.chromium.net package."""
api_packages = ['org.chromium.net', 'org.chromium.net.apihelpers']
api_packages_regex = '(' + '|'.join(api_packages) + ')'
api_file_pattern = input_api.re.compile(
r'^components/cronet/android/api/.*\.(java|template)$')
impl_file_pattern = input_api.re.compile(
r'^components/cronet/android/java/.*\.(java|template)$')
invalid_api_package_pattern = input_api.re.compile(
r'^package (?!' + api_packages_regex + ';)')
invalid_impl_package_pattern = input_api.re.compile(
r'^package ' + api_packages_regex + ';')
source_filter = lambda path: input_api.FilterSourceFile(path,
files_to_check=[r'^components/cronet/android/.*\.(java|template)$'])
problems = []
for f in input_api.AffectedSourceFiles(source_filter):
local_path = f.LocalPath()
for line_number, line in f.ChangedContents():
if (api_file_pattern.search(local_path)):
if (invalid_api_package_pattern.search(line)):
problems.append(
'%s:%d\n %s' % (local_path, line_number, line.strip()))
elif (impl_file_pattern.search(local_path)):
if (invalid_impl_package_pattern.search(line)):
problems.append(
'%s:%d\n %s' % (local_path, line_number, line.strip()))
if problems:
return [output_api.PresubmitError(
'API classes must be in org.chromium.net package, and implementation\n'
'classes must not be in org.chromium.net package.',
problems)]
return []
def _RunToolsUnittests(input_api, output_api):
return input_api.canned_checks.RunUnitTestsInDirectory(
input_api, output_api,
'.',
[ r'^tools_unittest\.py$'])
def _ChangeAffectsCronetTools(change):
""" Returns |true| if the change may affect Cronet tools. """
for path in change.LocalPaths():
if path.startswith(os.path.join('components', 'cronet', 'tools')):
return True
return False
GOOD_CHANGE_ID_TXT = 'good_change_id'
BAD_CHANGE_ID_TXT = 'bad_change_id'
BUG_TXT = 'bugs'
COMMENT_TXT = 'comment'
def _GetBreakagesFilePathIfChanged(change):
""" Returns |true| if the change may affect the breakages file. """
for file in change.AffectedFiles(include_deletes=False):
if file.LocalPath().endswith('breakages.json'):
return file
return None
def _IsValidChangeId(input_api, change_id):
""" Returns |true| if the change_id is not valid.
Validity means starting with the letter I followed by 40 hex chars.
"""
if (input_api.re.fullmatch(r'^I[0-9a-fA-F]{40}$', change_id)
and not input_api.re.fullmatch(r'^I00*$', change_id)):
return True
return False
def _GetInvalidChangeIdText(input_api, breakage, key):
if key not in breakage:
return ''
if not _IsValidChangeId(input_api, breakage[key]):
return '\t - entry has invalid %s: %s\n' % (key, breakage[key])
return ''
def _GetMissingKeyText(breakage, key):
if key in breakage:
return ''
return '\t - entry is missing the "%s" key\n' % key
def _GetGoodWithoutBadChangeIdText(breakage):
if GOOD_CHANGE_ID_TXT in breakage and BAD_CHANGE_ID_TXT not in breakage:
return '\t - entry cannot have %s without %s\n' % \
(GOOD_CHANGE_ID_TXT, BAD_CHANGE_ID_TXT)
return ''
def _GetUnknownKeyText(breakage):
unknown_keys = []
for key in breakage:
if (key.startswith('_') or # ignore comments
key == BAD_CHANGE_ID_TXT or
key == GOOD_CHANGE_ID_TXT or
key == BUG_TXT or
key == COMMENT_TXT):
continue
unknown_keys.append(key)
if unknown_keys:
return '\t - entry contains unknown key(s): %s. Expected either %s, %s, ' \
'%s or %s.\n' % \
(unknown_keys, BAD_CHANGE_ID_TXT, GOOD_CHANGE_ID_TXT, BUG_TXT,
COMMENT_TXT)
return ''
def _BreakageFileChecks(input_api, output_api, file):
"""Verify that the change_ids listed in the breakages file are valid."""
breakages = input_api.json.loads(input_api.ReadFile(file))["breakages"]
problems = []
for i, breakage in enumerate(breakages):
problem = ""
# ensures that the entries, where existing are valid and that there are no
# unknown keys.
problem += _GetInvalidChangeIdText(input_api, breakage, BAD_CHANGE_ID_TXT)
problem += _GetInvalidChangeIdText(input_api, breakage, GOOD_CHANGE_ID_TXT)
problem += _GetGoodWithoutBadChangeIdText(breakage)
problem += _GetMissingKeyText(breakage, BUG_TXT)
problem += _GetUnknownKeyText(breakage)
if problem:
problems.append('Breakage Entry %d: \n%s' % (i, problem))
if problems:
return [output_api.PresubmitError(
'The breakages.json file contains invalid entries.\n'
'Please cross-check the entries.',
problems)]
return []
def CheckChangeOnUpload(input_api, output_api):
results = []
results.extend(_PyLintChecks(input_api, output_api))
results.extend(_PackageChecks(input_api, output_api))
if _ChangeAffectsCronetTools(input_api.change):
results.extend(_RunToolsUnittests(input_api, output_api))
breakages_file = _GetBreakagesFilePathIfChanged(input_api.change)
if breakages_file:
results.extend(_BreakageFileChecks(input_api, output_api, breakages_file))
return results
def CheckChangeOnCommit(input_api, output_api):
return _RunToolsUnittests(input_api, output_api)