summaryrefslogtreecommitdiff
path: root/chromium/chrome/browser/resources/PRESUBMIT.py
blob: e7a3e430986f0a2d2062a15497b6b5e3e4784501 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
# Copyright 2014 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 files in chrome/browser/resources.

See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
for more details about the presubmit API built into depot_tools.
"""

import re


ACTION_XML_PATH = '../../../tools/metrics/actions/actions.xml'


def CheckUserActionUpdate(input_api, output_api, action_xml_path):
  """Checks if any new user action has been added."""
  if any('actions.xml' == input_api.os_path.basename(f) for f in
         input_api.change.LocalPaths()):
    # If actions.xml is already included in the changelist, the PRESUBMIT
    # for actions.xml will do a more complete presubmit check.
    return []

  file_filter = lambda f: f.LocalPath().endswith('.html')
  action_re = r'(^|\s+)metric\s*=\s*"([^ ]*)"'
  current_actions = None
  for f in input_api.AffectedFiles(file_filter=file_filter):
    for line_num, line in f.ChangedContents():
      match = input_api.re.search(action_re, line)
      if match:
        # Loads contents in tools/metrics/actions/actions.xml to memory. It's
        # loaded only once.
        if not current_actions:
          with open(action_xml_path) as actions_f:
            current_actions = actions_f.read()

        metric_name = match.group(2)
        is_boolean = IsBoolean(f.NewContents(), metric_name, input_api)

        # Search for the matched user action name in |current_actions|.
        if not IsActionPresent(current_actions, metric_name, is_boolean):
          return [output_api.PresubmitPromptWarning(
            'File %s line %d: %s is missing in '
            'tools/metrics/actions/actions.xml. Please run '
            'tools/metrics/actions/extract_actions.py to update.'
            % (f.LocalPath(), line_num, metric_name), [])]
  return []


def IsActionPresent(current_actions, metric_name, is_boolean):
  """Checks if metric_name is defined in the actions file.

  Checks whether there's matching entries in an actions.xml file for the given
  |metric_name|, depending on whether it is a boolean action.

  Args:
    current_actions: The content of the actions.xml file.
    metric_name: The name for which the check should be done.
    is_boolean: Whether the action comes from a boolean control.
  """
  if not is_boolean:
    action = 'name="{0}"'.format(metric_name)
    return action in current_actions

  action_disabled = 'name="{0}_Disable"'.format(metric_name)
  action_enabled = 'name="{0}_Enable"'.format(metric_name)

  return (action_disabled in current_actions and
      action_enabled in current_actions)


def IsBoolean(new_content_lines, metric_name, input_api):
  """Check whether action defined in the changed code is boolean or not.

  Checks whether the action comes from boolean control based on the HTML
  elements attributes.

  Args:
    new_content_lines: List of changed lines.
    metric_name: The  name for which the check should be done.
  """
  new_content = '\n'.join(new_content_lines)

  html_element_re = r'<(.*?)(^|\s+)metric\s*=\s*"%s"(.*?)>' % (metric_name)
  type_re = (r'datatype\s*=\s*"boolean"|type\s*=\s*"checkbox"|'
      'type\s*=\s*"radio".*?value\s*=\s*("true"|"false")')

  match = input_api.re.search(html_element_re, new_content, input_api.re.DOTALL)
  return (match and
      any(input_api.re.search(type_re, match.group(i)) for i in (1, 3)))


def CheckHtml(input_api, output_api):
  return input_api.canned_checks.CheckLongLines(
      input_api, output_api, 80, lambda x: x.LocalPath().endswith('.html'))


def RunOptimizeWebUiTests(input_api, output_api):
  presubmit_path = input_api.PresubmitLocalPath()
  tests = [input_api.os_path.join(presubmit_path, 'optimize_webui_test.py')]
  return input_api.canned_checks.RunUnitTests(input_api, output_api, tests)


def _CheckWebDevStyle(input_api, output_api):
  results = []

  try:
    import sys
    old_sys_path = sys.path[:]
    cwd = input_api.PresubmitLocalPath()
    sys.path += [input_api.os_path.join(cwd, '..', '..', '..', 'tools')]
    import web_dev_style.presubmit_support
    results += web_dev_style.presubmit_support.CheckStyle(input_api, output_api)
  finally:
    sys.path = old_sys_path

  return results


def _CheckChangeOnUploadOrCommit(input_api, output_api):
  results = CheckUserActionUpdate(input_api, output_api, ACTION_XML_PATH)
  affected = input_api.AffectedFiles()
  if any(f for f in affected if f.LocalPath().endswith('.html')):
    results += CheckHtml(input_api, output_api)
  if any(f for f in affected if f.LocalPath().endswith('optimize_webui.py')):
    results += RunOptimizeWebUiTests(input_api, output_api)
  results += _CheckWebDevStyle(input_api, output_api)
  results += input_api.canned_checks.CheckPatchFormatted(input_api, output_api,
                                                         check_js=True)
  return results


def CheckChangeOnUpload(input_api, output_api):
  return _CheckChangeOnUploadOrCommit(input_api, output_api)


def CheckChangeOnCommit(input_api, output_api):
  return _CheckChangeOnUploadOrCommit(input_api, output_api)


def PostUploadHook(cl, change, output_api):
  return output_api.EnsureCQIncludeTrybotsAreAdded(
    cl,
    [
      'master.tryserver.chromium.linux:closure_compilation',
    ],
    'Automatically added optional Closure bots to run on CQ.')