• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2015 The Chromium Authors
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""Top-level presubmit script for src/components/cronet.
6
7See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
8for more details about the presubmit API built into depot_tools.
9"""
10
11import os
12
13def _PyLintChecks(input_api, output_api):
14  pylint_checks = input_api.canned_checks.GetPylint(input_api, output_api,
15          extra_paths_list=_GetPathsToPrepend(input_api), pylintrc='pylintrc',
16          version='2.7')
17  return input_api.RunTests(pylint_checks)
18
19
20def _GetPathsToPrepend(input_api):
21  current_dir = input_api.PresubmitLocalPath()
22  chromium_src_dir = input_api.os_path.join(current_dir, '..', '..')
23  return [
24    input_api.os_path.join(chromium_src_dir, 'components'),
25    input_api.os_path.join(chromium_src_dir, 'tools', 'perf'),
26    input_api.os_path.join(chromium_src_dir, 'build', 'android'),
27    input_api.os_path.join(chromium_src_dir, 'build', 'android', 'gyp'),
28    input_api.os_path.join(chromium_src_dir,
29        'mojo', 'public', 'tools', 'bindings', 'pylib'),
30    input_api.os_path.join(chromium_src_dir, 'net', 'tools', 'net_docs'),
31    input_api.os_path.join(chromium_src_dir, 'tools'),
32    input_api.os_path.join(chromium_src_dir, 'third_party'),
33    input_api.os_path.join(chromium_src_dir,
34        'third_party', 'catapult', 'telemetry'),
35    input_api.os_path.join(chromium_src_dir,
36        'third_party', 'catapult', 'devil'),
37    input_api.os_path.join(chromium_src_dir,
38        'third_party', 'catapult', 'common', 'py_utils'),
39  ]
40
41
42def _PackageChecks(input_api, output_api):
43  """Verify API classes are in org.chromium.net package, and implementation
44  classes are not in org.chromium.net package."""
45  api_packages = ['org.chromium.net', 'org.chromium.net.apihelpers']
46  api_packages_regex = '(' + '|'.join(api_packages) + ')'
47  api_file_pattern = input_api.re.compile(
48      r'^components/cronet/android/api/.*\.(java|template)$')
49  impl_file_pattern = input_api.re.compile(
50      r'^components/cronet/android/java/.*\.(java|template)$')
51  invalid_api_package_pattern = input_api.re.compile(
52    r'^package (?!' + api_packages_regex + ';)')
53  invalid_impl_package_pattern = input_api.re.compile(
54    r'^package ' + api_packages_regex + ';')
55
56  source_filter = lambda path: input_api.FilterSourceFile(path,
57      files_to_check=[r'^components/cronet/android/.*\.(java|template)$'])
58
59  problems = []
60  for f in input_api.AffectedSourceFiles(source_filter):
61    local_path = f.LocalPath()
62    for line_number, line in f.ChangedContents():
63      if (api_file_pattern.search(local_path)):
64        if (invalid_api_package_pattern.search(line)):
65          problems.append(
66            '%s:%d\n    %s' % (local_path, line_number, line.strip()))
67      elif (impl_file_pattern.search(local_path)):
68        if (invalid_impl_package_pattern.search(line)):
69          problems.append(
70            '%s:%d\n    %s' % (local_path, line_number, line.strip()))
71
72  if problems:
73    return [output_api.PresubmitError(
74        'API classes must be in org.chromium.net package, and implementation\n'
75        'classes must not be in org.chromium.net package.',
76        problems)]
77  return []
78
79
80def _RunToolsUnittests(input_api, output_api):
81  return input_api.canned_checks.RunUnitTestsInDirectory(
82      input_api, output_api,
83      '.',
84      [ r'^tools_unittest\.py$'])
85
86
87def _ChangeAffectsCronetTools(change):
88  """ Returns |true| if the change may affect Cronet tools. """
89
90  for path in change.LocalPaths():
91    if path.startswith(os.path.join('components', 'cronet', 'tools')):
92      return True
93  return False
94
95GOOD_CHANGE_ID_TXT = 'good_change_id'
96BAD_CHANGE_ID_TXT = 'bad_change_id'
97BUG_TXT = 'bugs'
98COMMENT_TXT = 'comment'
99
100def _GetBreakagesFilePathIfChanged(change):
101  """ Returns |true| if the change may affect the breakages file. """
102
103  for file in change.AffectedFiles(include_deletes=False):
104    if file.LocalPath().endswith('breakages.json'):
105      return file
106  return None
107
108def _IsValidChangeId(input_api, change_id):
109  """ Returns |true| if the change_id is not valid.
110
111  Validity means starting with the letter I followed by 40 hex chars.
112  """
113  if (input_api.re.fullmatch(r'^I[0-9a-fA-F]{40}$', change_id)
114      and not input_api.re.fullmatch(r'^I00*$', change_id)):
115    return True
116  return False
117
118def _GetInvalidChangeIdText(input_api, breakage, key):
119  if key not in breakage:
120    return ''
121  if not _IsValidChangeId(input_api, breakage[key]):
122    return '\t - entry has invalid %s: %s\n' % (key, breakage[key])
123  return ''
124
125def _GetMissingKeyText(breakage, key):
126  if key in breakage:
127    return ''
128  return '\t - entry is missing the "%s" key\n' % key
129
130def _GetGoodWithoutBadChangeIdText(breakage):
131  if GOOD_CHANGE_ID_TXT in breakage and BAD_CHANGE_ID_TXT not in breakage:
132    return '\t - entry cannot have %s without %s\n' % \
133      (GOOD_CHANGE_ID_TXT, BAD_CHANGE_ID_TXT)
134  return ''
135
136def _GetUnknownKeyText(breakage):
137  unknown_keys = []
138  for key in breakage:
139    if (key.startswith('_') or # ignore comments
140        key == BAD_CHANGE_ID_TXT or
141        key == GOOD_CHANGE_ID_TXT or
142        key == BUG_TXT or
143        key == COMMENT_TXT):
144      continue
145    unknown_keys.append(key)
146
147  if unknown_keys:
148    return '\t - entry contains unknown key(s): %s. Expected either %s, %s, ' \
149      '%s or %s.\n' % \
150      (unknown_keys, BAD_CHANGE_ID_TXT, GOOD_CHANGE_ID_TXT, BUG_TXT,
151       COMMENT_TXT)
152  return ''
153
154def _BreakageFileChecks(input_api, output_api, file):
155  """Verify that the change_ids listed in the breakages file are valid."""
156  breakages = input_api.json.loads(input_api.ReadFile(file))["breakages"]
157  problems = []
158  for i, breakage in enumerate(breakages):
159    problem = ""
160    # ensures that the entries, where existing are valid and that there are no
161    # unknown keys.
162    problem += _GetInvalidChangeIdText(input_api, breakage, BAD_CHANGE_ID_TXT)
163    problem += _GetInvalidChangeIdText(input_api, breakage, GOOD_CHANGE_ID_TXT)
164    problem += _GetGoodWithoutBadChangeIdText(breakage)
165    problem += _GetMissingKeyText(breakage, BUG_TXT)
166    problem += _GetUnknownKeyText(breakage)
167
168    if problem:
169      problems.append('Breakage Entry %d: \n%s' % (i, problem))
170
171  if problems:
172    return [output_api.PresubmitError(
173        'The breakages.json file contains invalid entries.\n'
174        'Please cross-check the entries.',
175        problems)]
176  return []
177
178def CheckChangeOnUpload(input_api, output_api):
179  results = []
180  results.extend(_PyLintChecks(input_api, output_api))
181  results.extend(_PackageChecks(input_api, output_api))
182  if _ChangeAffectsCronetTools(input_api.change):
183    results.extend(_RunToolsUnittests(input_api, output_api))
184  breakages_file = _GetBreakagesFilePathIfChanged(input_api.change)
185  if breakages_file:
186    results.extend(_BreakageFileChecks(input_api, output_api, breakages_file))
187  return results
188
189
190def CheckChangeOnCommit(input_api, output_api):
191  return _RunToolsUnittests(input_api, output_api)
192