blob: d6f8a34fc58398e4a3fd6c8c8933d8796e17f172 [file] [log] [blame]
[email protected]fb2b8eb2009-04-23 21:03:421#!/usr/bin/env python
2# Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6"""Generic presubmit checks that can be reused by other presubmit checks."""
7
8
[email protected]3410d912009-06-09 20:56:169### Description checks
10
[email protected]00c41e42009-05-12 21:43:1311def CheckChangeHasTestField(input_api, output_api):
12 """Requires that the changelist have a TEST= field."""
[email protected]e1a524f2009-05-27 14:43:4613 if input_api.change.TEST:
[email protected]00c41e42009-05-12 21:43:1314 return []
15 else:
16 return [output_api.PresubmitNotifyResult(
17 "Changelist should have a TEST= field. TEST=none is allowed.")]
18
19
20def CheckChangeHasBugField(input_api, output_api):
21 """Requires that the changelist have a BUG= field."""
[email protected]e1a524f2009-05-27 14:43:4622 if input_api.change.BUG:
[email protected]00c41e42009-05-12 21:43:1323 return []
24 else:
25 return [output_api.PresubmitNotifyResult(
26 "Changelist should have a BUG= field. BUG=none is allowed.")]
27
28
[email protected]fb2b8eb2009-04-23 21:03:4229def CheckChangeHasTestedField(input_api, output_api):
30 """Requires that the changelist have a TESTED= field."""
[email protected]e1a524f2009-05-27 14:43:4631 if input_api.change.TESTED:
[email protected]fb2b8eb2009-04-23 21:03:4232 return []
33 else:
34 return [output_api.PresubmitError("Changelist must have a TESTED= field.")]
35
36
37def CheckChangeHasQaField(input_api, output_api):
38 """Requires that the changelist have a QA= field."""
39 if input_api.change.QA:
40 return []
41 else:
42 return [output_api.PresubmitError("Changelist must have a QA= field.")]
43
44
45def CheckDoNotSubmitInDescription(input_api, output_api):
46 """Checks that the user didn't add 'DO NOT ' + 'SUBMIT' to the CL description.
47 """
48 keyword = 'DO NOT ' + 'SUBMIT'
49 if keyword in input_api.change.DescriptionText():
50 return [output_api.PresubmitError(
51 keyword + " is present in the changelist description.")]
52 else:
53 return []
54
55
[email protected]3410d912009-06-09 20:56:1656### Content checks
57
[email protected]fb2b8eb2009-04-23 21:03:4258def CheckDoNotSubmitInFiles(input_api, output_api):
59 """Checks that the user didn't add 'DO NOT ' + 'SUBMIT' to any files."""
60 keyword = 'DO NOT ' + 'SUBMIT'
[email protected]3410d912009-06-09 20:56:1661 # We want to check every text files, not just source files.
62 for f, line_num, line in input_api.RightHandSideLines(lambda x: x):
[email protected]fb2b8eb2009-04-23 21:03:4263 if keyword in line:
64 text = 'Found ' + keyword + ' in %s, line %s' % (f.LocalPath(), line_num)
65 return [output_api.PresubmitError(text)]
66 return []
67
68
[email protected]3410d912009-06-09 20:56:1669def CheckChangeHasNoCR(input_api, output_api, source_file_filter=None):
[email protected]e9b71c92009-06-10 18:10:0170 """Checks no '\r' (CR) character is in any source files."""
71 cr_files = []
[email protected]3410d912009-06-09 20:56:1672 for f in input_api.AffectedSourceFiles(source_file_filter):
[email protected]44a17ad2009-06-08 14:14:3573 if '\r' in input_api.ReadFile(f, 'rb'):
[email protected]e9b71c92009-06-10 18:10:0174 cr_files.append(f.LocalPath())
75 if cr_files:
76 return [output_api.PresubmitPromptWarning(
77 "Found a CR character in these files:", items=cr_files)]
78 return []
79
80
81def CheckChangeHasOnlyOneEol(input_api, output_api, source_file_filter=None):
82 """Checks the files ends with one and only one \n (LF)."""
83 eof_files = []
84 for f in input_api.AffectedSourceFiles(source_file_filter):
85 contents = input_api.ReadFile(f, 'rb')
86 # Check that the file ends in one and only one newline character.
87 if len(contents) > 1 and (contents[-1:] != "\n" or contents[-2:-1] == "\n"):
88 eof_files.append(f.LocalPath())
89
90 if eof_files:
91 return [output_api.PresubmitPromptWarning(
92 'These files should end in one (and only one) newline character:',
93 items=eof_files)]
94 return []
95
96
97def CheckChangeHasNoCrAndHasOnlyOneEol(input_api, output_api,
98 source_file_filter=None):
99 """Runs both CheckChangeHasNoCR and CheckChangeHasOnlyOneEOL in one pass.
100
101 It is faster because it is reading the file only once.
102 """
103 cr_files = []
104 eof_files = []
105 for f in input_api.AffectedSourceFiles(source_file_filter):
106 contents = input_api.ReadFile(f, 'rb')
107 if '\r' in contents:
108 cr_files.append(f.LocalPath())
109 # Check that the file ends in one and only one newline character.
110 if len(contents) > 1 and (contents[-1:] != "\n" or contents[-2:-1] == "\n"):
111 eof_files.append(f.LocalPath())
112 outputs = []
113 if cr_files:
114 outputs.append(output_api.PresubmitPromptWarning(
115 "Found a CR character in these files:", items=cr_files))
116 if eof_files:
117 outputs.append(output_api.PresubmitPromptWarning(
118 'These files should end in one (and only one) newline character:',
119 items=eof_files))
[email protected]44a17ad2009-06-08 14:14:35120 return outputs
121
122
[email protected]3410d912009-06-09 20:56:16123def CheckChangeHasNoTabs(input_api, output_api, source_file_filter=None):
[email protected]fb2b8eb2009-04-23 21:03:42124 """Checks that there are no tab characters in any of the text files to be
125 submitted.
126 """
[email protected]e9b71c92009-06-10 18:10:01127 tabs = []
[email protected]3410d912009-06-09 20:56:16128 for f, line_num, line in input_api.RightHandSideLines(source_file_filter):
[email protected]fb2b8eb2009-04-23 21:03:42129 if '\t' in line:
[email protected]e9b71c92009-06-10 18:10:01130 tabs.append("%s, line %s" % (f.LocalPath(), line_num))
131 if tabs:
132 return [output_api.PresubmitPromptWarning("Found a tab character in:",
133 long_text="\n".join(tabs))]
[email protected]fb2b8eb2009-04-23 21:03:42134 return []
135
136
[email protected]3410d912009-06-09 20:56:16137def CheckLongLines(input_api, output_api, maxlen=80, source_file_filter=None):
[email protected]fb2b8eb2009-04-23 21:03:42138 """Checks that there aren't any lines longer than maxlen characters in any of
139 the text files to be submitted.
140 """
[email protected]fb2b8eb2009-04-23 21:03:42141 bad = []
[email protected]3410d912009-06-09 20:56:16142 for f, line_num, line in input_api.RightHandSideLines(source_file_filter):
[email protected]5c2720e2009-06-09 14:04:08143 # Allow lines with http://, https:// and #define/#pragma/#include/#if/#endif
144 # to exceed the maxlen rule.
145 if (len(line) > maxlen and
146 not 'http://' in line and
147 not 'https://' in line and
148 not line.startswith('#define') and
149 not line.startswith('#include') and
150 not line.startswith('#pragma') and
151 not line.startswith('#if') and
152 not line.startswith('#endif')):
[email protected]fb2b8eb2009-04-23 21:03:42153 bad.append(
154 '%s, line %s, %s chars' %
[email protected]1487d532009-06-06 00:22:57155 (f.LocalPath(), line_num, len(line)))
[email protected]fb2b8eb2009-04-23 21:03:42156 if len(bad) == 5: # Just show the first 5 errors.
157 break
158
159 if bad:
160 msg = "Found lines longer than %s characters (first 5 shown)." % maxlen
161 return [output_api.PresubmitPromptWarning(msg, items=bad)]
162 else:
163 return []
164
165
[email protected]1a0e3cb2009-06-10 18:03:04166def CheckChangeSvnEolStyle(input_api, output_api, source_file_filter=None):
[email protected]b7d46902009-06-10 14:12:10167 """Checks that the source files have svn:eol-style=LF."""
168 bad = filter(lambda f: f.scm == 'svn' and f.Property('svn:eol-style') != 'LF',
169 input_api.AffectedSourceFiles(source_file_filter))
170 if bad:
171 return [output_api.PresubmitError(
172 "Fix these files with svn svn:eol-style=LF", items=bad)]
173 return []
174
175
[email protected]3410d912009-06-09 20:56:16176### Other checks
177
178def CheckDoNotSubmit(input_api, output_api):
179 return (
180 CheckDoNotSubmitInDescription(input_api, output_api) +
181 CheckDoNotSubmitInFiles(input_api, output_api)
182 )
183
184
[email protected]fb2b8eb2009-04-23 21:03:42185def CheckTreeIsOpen(input_api, output_api, url, closed):
186 """Checks that an url's content doesn't match a regexp that would mean that
187 the tree is closed."""
[email protected]89491382009-06-06 18:58:39188 assert(input_api.is_committing)
[email protected]fb2b8eb2009-04-23 21:03:42189 try:
190 connection = input_api.urllib2.urlopen(url)
191 status = connection.read()
192 connection.close()
193 if input_api.re.match(closed, status):
194 long_text = status + '\n' + url
[email protected]89491382009-06-06 18:58:39195 return [output_api.PresubmitPromptWarning("The tree is closed.",
196 long_text=long_text)]
[email protected]fb2b8eb2009-04-23 21:03:42197 except IOError:
198 pass
199 return []
[email protected]7b305e82009-05-19 18:24:20200
201
[email protected]1487d532009-06-06 00:22:57202def _RunPythonUnitTests_LoadTests(input_api, module_name):
203 """Meant to be stubbed out during unit testing."""
204 module = __import__(module_name)
205 for part in module_name.split('.')[1:]:
206 module = getattr(module, part)
207 return input_api.unittest.TestLoader().loadTestsFromModule(module)._tests
208
[email protected]3410d912009-06-09 20:56:16209
[email protected]7b305e82009-05-19 18:24:20210def RunPythonUnitTests(input_api, output_api, unit_tests):
211 """Imports the unit_tests modules and run them."""
[email protected]d7dccf52009-06-06 18:51:58212 # We don't want to hinder users from uploading incomplete patches.
213 if input_api.is_committing:
214 message_type = output_api.PresubmitError
215 else:
216 message_type = output_api.PresubmitNotifyResult
[email protected]7b305e82009-05-19 18:24:20217 tests_suite = []
[email protected]7b305e82009-05-19 18:24:20218 outputs = []
219 for unit_test in unit_tests:
220 try:
[email protected]de0ba292009-06-06 19:43:27221 tests_suite.extend(_RunPythonUnitTests_LoadTests(input_api, unit_test))
[email protected]7b305e82009-05-19 18:24:20222 except ImportError:
[email protected]d7dccf52009-06-06 18:51:58223 outputs.append(message_type("Failed to load %s" % unit_test,
224 long_text=input_api.traceback.format_exc()))
[email protected]7b305e82009-05-19 18:24:20225
[email protected]c809ec22009-06-10 14:25:42226 buffer = input_api.cStringIO.StringIO()
227 results = input_api.unittest.TextTestRunner(stream=buffer, verbosity=0).run(
[email protected]1487d532009-06-06 00:22:57228 input_api.unittest.TestSuite(tests_suite))
[email protected]7b305e82009-05-19 18:24:20229 if not results.wasSuccessful():
[email protected]de0ba292009-06-06 19:43:27230 outputs.append(message_type("%d unit tests failed." %
[email protected]c809ec22009-06-10 14:25:42231 (len(results.failures) + len(results.errors)),
232 long_text=buffer.getvalue()))
[email protected]7b305e82009-05-19 18:24:20233 return outputs