Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(98)

Side by Side Diff: presubmit_canned_checks.py

Issue 1119003: Add new canned checks: CheckRietveldTryJobExecution and CheckBuildbotPendingBuilds (Closed)
Patch Set: Standardize strings Created 10 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | tests/presubmit_unittest.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 # Copyright (c) 2010 The Chromium Authors. All rights reserved. 1 # Copyright (c) 2010 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be 2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file. 3 # found in the LICENSE file.
4 4
5 """Generic presubmit checks that can be reused by other presubmit checks.""" 5 """Generic presubmit checks that can be reused by other presubmit checks."""
6 6
7 ### Description checks 7 ### Description checks
8 8
9 def CheckChangeHasTestField(input_api, output_api): 9 def CheckChangeHasTestField(input_api, output_api):
10 """Requires that the changelist have a TEST= field.""" 10 """Requires that the changelist have a TEST= field."""
11 if input_api.change.TEST: 11 if input_api.change.TEST:
12 return [] 12 return []
13 else: 13 else:
14 return [output_api.PresubmitNotifyResult( 14 return [output_api.PresubmitNotifyResult(
15 "Changelist should have a TEST= field. TEST=none is allowed.")] 15 'Changelist should have a TEST= field. TEST=none is allowed.')]
16 16
17 17
18 def CheckChangeHasBugField(input_api, output_api): 18 def CheckChangeHasBugField(input_api, output_api):
19 """Requires that the changelist have a BUG= field.""" 19 """Requires that the changelist have a BUG= field."""
20 if input_api.change.BUG: 20 if input_api.change.BUG:
21 return [] 21 return []
22 else: 22 else:
23 return [output_api.PresubmitNotifyResult( 23 return [output_api.PresubmitNotifyResult(
24 "Changelist should have a BUG= field. BUG=none is allowed.")] 24 'Changelist should have a BUG= field. BUG=none is allowed.')]
25 25
26 26
27 def CheckChangeHasTestedField(input_api, output_api): 27 def CheckChangeHasTestedField(input_api, output_api):
28 """Requires that the changelist have a TESTED= field.""" 28 """Requires that the changelist have a TESTED= field."""
29 if input_api.change.TESTED: 29 if input_api.change.TESTED:
30 return [] 30 return []
31 else: 31 else:
32 return [output_api.PresubmitError("Changelist must have a TESTED= field.")] 32 return [output_api.PresubmitError('Changelist must have a TESTED= field.')]
33 33
34 34
35 def CheckChangeHasQaField(input_api, output_api): 35 def CheckChangeHasQaField(input_api, output_api):
36 """Requires that the changelist have a QA= field.""" 36 """Requires that the changelist have a QA= field."""
37 if input_api.change.QA: 37 if input_api.change.QA:
38 return [] 38 return []
39 else: 39 else:
40 return [output_api.PresubmitError("Changelist must have a QA= field.")] 40 return [output_api.PresubmitError('Changelist must have a QA= field.')]
41 41
42 42
43 def CheckDoNotSubmitInDescription(input_api, output_api): 43 def CheckDoNotSubmitInDescription(input_api, output_api):
44 """Checks that the user didn't add 'DO NOT ' + 'SUBMIT' to the CL description. 44 """Checks that the user didn't add 'DO NOT ' + 'SUBMIT' to the CL description.
45 """ 45 """
46 keyword = 'DO NOT ' + 'SUBMIT' 46 keyword = 'DO NOT ' + 'SUBMIT'
47 if keyword in input_api.change.DescriptionText(): 47 if keyword in input_api.change.DescriptionText():
48 return [output_api.PresubmitError( 48 return [output_api.PresubmitError(
49 keyword + " is present in the changelist description.")] 49 keyword + ' is present in the changelist description.')]
50 else: 50 else:
51 return [] 51 return []
52 52
53 53
54 def CheckChangeHasDescription(input_api, output_api): 54 def CheckChangeHasDescription(input_api, output_api):
55 """Checks the CL description is not empty.""" 55 """Checks the CL description is not empty."""
56 text = input_api.change.DescriptionText() 56 text = input_api.change.DescriptionText()
57 if text.strip() == '': 57 if text.strip() == '':
58 if input_api.is_committing: 58 if input_api.is_committing:
59 return [output_api.PresubmitError("Add a description.")] 59 return [output_api.PresubmitError('Add a description.')]
60 else: 60 else:
61 return [output_api.PresubmitNotifyResult("Add a description.")] 61 return [output_api.PresubmitNotifyResult('Add a description.')]
62 return [] 62 return []
63 63
64 ### Content checks 64 ### Content checks
65 65
66 def CheckDoNotSubmitInFiles(input_api, output_api): 66 def CheckDoNotSubmitInFiles(input_api, output_api):
67 """Checks that the user didn't add 'DO NOT ' + 'SUBMIT' to any files.""" 67 """Checks that the user didn't add 'DO NOT ' + 'SUBMIT' to any files."""
68 keyword = 'DO NOT ' + 'SUBMIT' 68 keyword = 'DO NOT ' + 'SUBMIT'
69 # We want to check every text files, not just source files. 69 # We want to check every text files, not just source files.
70 for f, line_num, line in input_api.RightHandSideLines(lambda x: x): 70 for f, line_num, line in input_api.RightHandSideLines(lambda x: x):
71 if keyword in line: 71 if keyword in line:
72 text = 'Found ' + keyword + ' in %s, line %s' % (f.LocalPath(), line_num) 72 text = 'Found ' + keyword + ' in %s, line %s' % (f.LocalPath(), line_num)
73 return [output_api.PresubmitError(text)] 73 return [output_api.PresubmitError(text)]
74 return [] 74 return []
75 75
76 76
77 def CheckChangeLintsClean(input_api, output_api, source_file_filter=None): 77 def CheckChangeLintsClean(input_api, output_api, source_file_filter=None):
78 """Checks that all ".cc" and ".h" files pass cpplint.py.""" 78 """Checks that all '.cc' and '.h' files pass cpplint.py."""
79 _RE_IS_TEST = input_api.re.compile(r'.*tests?.(cc|h)$') 79 _RE_IS_TEST = input_api.re.compile(r'.*tests?.(cc|h)$')
80 result = [] 80 result = []
81 81
82 # Initialize cpplint. 82 # Initialize cpplint.
83 import cpplint 83 import cpplint
84 cpplint._cpplint_state.ResetErrorCounts() 84 cpplint._cpplint_state.ResetErrorCounts()
85 85
86 # Justifications for each filter: 86 # Justifications for each filter:
87 # 87 #
88 # - build/include : Too many; fix in the future. 88 # - build/include : Too many; fix in the future.
89 # - build/include_order : Not happening; #ifdefed includes. 89 # - build/include_order : Not happening; #ifdefed includes.
90 # - build/namespace : I'm surprised by how often we violate this rule. 90 # - build/namespace : I'm surprised by how often we violate this rule.
91 # - readability/casting : Mistakes a whole bunch of function pointer. 91 # - readability/casting : Mistakes a whole bunch of function pointer.
92 # - runtime/int : Can be fixed long term; volume of errors too high 92 # - runtime/int : Can be fixed long term; volume of errors too high
93 # - runtime/virtual : Broken now, but can be fixed in the future? 93 # - runtime/virtual : Broken now, but can be fixed in the future?
94 # - whitespace/braces : We have a lot of explicit scoping in chrome code. 94 # - whitespace/braces : We have a lot of explicit scoping in chrome code.
95 cpplint._SetFilters("-build/include,-build/include_order,-build/namespace," 95 cpplint._SetFilters('-build/include,-build/include_order,-build/namespace,'
96 "-readability/casting,-runtime/int,-runtime/virtual," 96 '-readability/casting,-runtime/int,-runtime/virtual,'
97 "-whitespace/braces") 97 '-whitespace/braces')
98 98
99 # We currently are more strict with normal code than unit tests; 4 and 5 are 99 # We currently are more strict with normal code than unit tests; 4 and 5 are
100 # the verbosity level that would normally be passed to cpplint.py through 100 # the verbosity level that would normally be passed to cpplint.py through
101 # --verbose=#. Hopefully, in the future, we can be more verbose. 101 # --verbose=#. Hopefully, in the future, we can be more verbose.
102 files = [f.AbsoluteLocalPath() for f in 102 files = [f.AbsoluteLocalPath() for f in
103 input_api.AffectedSourceFiles(source_file_filter)] 103 input_api.AffectedSourceFiles(source_file_filter)]
104 for file_name in files: 104 for file_name in files:
105 if _RE_IS_TEST.match(file_name): 105 if _RE_IS_TEST.match(file_name):
106 level = 5 106 level = 5
107 else: 107 else:
108 level = 4 108 level = 4
109 109
110 cpplint.ProcessFile(file_name, level) 110 cpplint.ProcessFile(file_name, level)
111 111
112 if cpplint._cpplint_state.error_count > 0: 112 if cpplint._cpplint_state.error_count > 0:
113 if input_api.is_committing: 113 if input_api.is_committing:
114 res_type = output_api.PresubmitError 114 res_type = output_api.PresubmitError
115 else: 115 else:
116 res_type = output_api.PresubmitPromptWarning 116 res_type = output_api.PresubmitPromptWarning
117 result = [res_type("Changelist failed cpplint.py check.")] 117 result = [res_type('Changelist failed cpplint.py check.')]
118 118
119 return result 119 return result
120 120
121 121
122 def CheckChangeHasNoCR(input_api, output_api, source_file_filter=None): 122 def CheckChangeHasNoCR(input_api, output_api, source_file_filter=None):
123 """Checks no '\r' (CR) character is in any source files.""" 123 """Checks no '\r' (CR) character is in any source files."""
124 cr_files = [] 124 cr_files = []
125 for f in input_api.AffectedSourceFiles(source_file_filter): 125 for f in input_api.AffectedSourceFiles(source_file_filter):
126 if '\r' in input_api.ReadFile(f, 'rb'): 126 if '\r' in input_api.ReadFile(f, 'rb'):
127 cr_files.append(f.LocalPath()) 127 cr_files.append(f.LocalPath())
128 if cr_files: 128 if cr_files:
129 return [output_api.PresubmitPromptWarning( 129 return [output_api.PresubmitPromptWarning(
130 "Found a CR character in these files:", items=cr_files)] 130 'Found a CR character in these files:', items=cr_files)]
131 return [] 131 return []
132 132
133 133
134 def CheckSvnModifiedDirectories(input_api, output_api, source_file_filter=None): 134 def CheckSvnModifiedDirectories(input_api, output_api, source_file_filter=None):
135 """Checks for files in svn modified directories. 135 """Checks for files in svn modified directories.
136 136
137 They will get submitted on accident because svn commits recursively by 137 They will get submitted on accident because svn commits recursively by
138 default, and that's very dangerous. 138 default, and that's very dangerous.
139 """ 139 """
140 if input_api.change.scm != 'svn': 140 if input_api.change.scm != 'svn':
(...skipping 14 matching lines...) Expand all
155 for i in xrange(len(modified_files)): 155 for i in xrange(len(modified_files)):
156 abspath = modified_abspaths[i] 156 abspath = modified_abspaths[i]
157 if input_api.os_path.commonprefix([curpath, abspath]) == curpath: 157 if input_api.os_path.commonprefix([curpath, abspath]) == curpath:
158 bad_files.append(modified_files[i]) 158 bad_files.append(modified_files[i])
159 if bad_files: 159 if bad_files:
160 if input_api.is_committing: 160 if input_api.is_committing:
161 error_type = output_api.PresubmitPromptWarning 161 error_type = output_api.PresubmitPromptWarning
162 else: 162 else:
163 error_type = output_api.PresubmitNotifyResult 163 error_type = output_api.PresubmitNotifyResult
164 errors.append(error_type( 164 errors.append(error_type(
165 "Potential accidental commits in changelist %s:" % f.LocalPath(), 165 'Potential accidental commits in changelist %s:' % f.LocalPath(),
166 items=bad_files)) 166 items=bad_files))
167 return errors 167 return errors
168 168
169 169
170 def CheckChangeHasOnlyOneEol(input_api, output_api, source_file_filter=None): 170 def CheckChangeHasOnlyOneEol(input_api, output_api, source_file_filter=None):
171 """Checks the files ends with one and only one \n (LF).""" 171 """Checks the files ends with one and only one \n (LF)."""
172 eof_files = [] 172 eof_files = []
173 for f in input_api.AffectedSourceFiles(source_file_filter): 173 for f in input_api.AffectedSourceFiles(source_file_filter):
174 contents = input_api.ReadFile(f, 'rb') 174 contents = input_api.ReadFile(f, 'rb')
175 # Check that the file ends in one and only one newline character. 175 # Check that the file ends in one and only one newline character.
176 if len(contents) > 1 and (contents[-1:] != "\n" or contents[-2:-1] == "\n"): 176 if len(contents) > 1 and (contents[-1:] != '\n' or contents[-2:-1] == '\n'):
177 eof_files.append(f.LocalPath()) 177 eof_files.append(f.LocalPath())
178 178
179 if eof_files: 179 if eof_files:
180 return [output_api.PresubmitPromptWarning( 180 return [output_api.PresubmitPromptWarning(
181 'These files should end in one (and only one) newline character:', 181 'These files should end in one (and only one) newline character:',
182 items=eof_files)] 182 items=eof_files)]
183 return [] 183 return []
184 184
185 185
186 def CheckChangeHasNoCrAndHasOnlyOneEol(input_api, output_api, 186 def CheckChangeHasNoCrAndHasOnlyOneEol(input_api, output_api,
187 source_file_filter=None): 187 source_file_filter=None):
188 """Runs both CheckChangeHasNoCR and CheckChangeHasOnlyOneEOL in one pass. 188 """Runs both CheckChangeHasNoCR and CheckChangeHasOnlyOneEOL in one pass.
189 189
190 It is faster because it is reading the file only once. 190 It is faster because it is reading the file only once.
191 """ 191 """
192 cr_files = [] 192 cr_files = []
193 eof_files = [] 193 eof_files = []
194 for f in input_api.AffectedSourceFiles(source_file_filter): 194 for f in input_api.AffectedSourceFiles(source_file_filter):
195 contents = input_api.ReadFile(f, 'rb') 195 contents = input_api.ReadFile(f, 'rb')
196 if '\r' in contents: 196 if '\r' in contents:
197 cr_files.append(f.LocalPath()) 197 cr_files.append(f.LocalPath())
198 # Check that the file ends in one and only one newline character. 198 # Check that the file ends in one and only one newline character.
199 if len(contents) > 1 and (contents[-1:] != "\n" or contents[-2:-1] == "\n"): 199 if len(contents) > 1 and (contents[-1:] != '\n' or contents[-2:-1] == '\n'):
200 eof_files.append(f.LocalPath()) 200 eof_files.append(f.LocalPath())
201 outputs = [] 201 outputs = []
202 if cr_files: 202 if cr_files:
203 outputs.append(output_api.PresubmitPromptWarning( 203 outputs.append(output_api.PresubmitPromptWarning(
204 "Found a CR character in these files:", items=cr_files)) 204 'Found a CR character in these files:', items=cr_files))
205 if eof_files: 205 if eof_files:
206 outputs.append(output_api.PresubmitPromptWarning( 206 outputs.append(output_api.PresubmitPromptWarning(
207 'These files should end in one (and only one) newline character:', 207 'These files should end in one (and only one) newline character:',
208 items=eof_files)) 208 items=eof_files))
209 return outputs 209 return outputs
210 210
211 211
212 def CheckChangeHasNoTabs(input_api, output_api, source_file_filter=None): 212 def CheckChangeHasNoTabs(input_api, output_api, source_file_filter=None):
213 """Checks that there are no tab characters in any of the text files to be 213 """Checks that there are no tab characters in any of the text files to be
214 submitted. 214 submitted.
215 """ 215 """
216 tabs = [] 216 tabs = []
217 for f, line_num, line in input_api.RightHandSideLines(source_file_filter): 217 for f, line_num, line in input_api.RightHandSideLines(source_file_filter):
218 if '\t' in line: 218 if '\t' in line:
219 tabs.append("%s, line %s" % (f.LocalPath(), line_num)) 219 tabs.append('%s, line %s' % (f.LocalPath(), line_num))
220 if tabs: 220 if tabs:
221 return [output_api.PresubmitPromptWarning("Found a tab character in:", 221 return [output_api.PresubmitPromptWarning('Found a tab character in:',
222 long_text="\n".join(tabs))] 222 long_text='\n'.join(tabs))]
223 return [] 223 return []
224 224
225 225
226 def CheckChangeHasNoStrayWhitespace(input_api, output_api, 226 def CheckChangeHasNoStrayWhitespace(input_api, output_api,
227 source_file_filter=None): 227 source_file_filter=None):
228 """Checks that there is no stray whitespace at source lines end.""" 228 """Checks that there is no stray whitespace at source lines end."""
229 errors = [] 229 errors = []
230 for f, line_num, line in input_api.RightHandSideLines(source_file_filter): 230 for f, line_num, line in input_api.RightHandSideLines(source_file_filter):
231 if line.rstrip() != line: 231 if line.rstrip() != line:
232 errors.append("%s, line %s" % (f.LocalPath(), line_num)) 232 errors.append('%s, line %s' % (f.LocalPath(), line_num))
233 if errors: 233 if errors:
234 return [output_api.PresubmitPromptWarning( 234 return [output_api.PresubmitPromptWarning(
235 "Found line ending with white spaces in:", 235 'Found line ending with white spaces in:',
236 long_text="\n".join(errors))] 236 long_text='\n'.join(errors))]
237 return [] 237 return []
238 238
239 239
240 def CheckLongLines(input_api, output_api, maxlen=80, source_file_filter=None): 240 def CheckLongLines(input_api, output_api, maxlen=80, source_file_filter=None):
241 """Checks that there aren't any lines longer than maxlen characters in any of 241 """Checks that there aren't any lines longer than maxlen characters in any of
242 the text files to be submitted. 242 the text files to be submitted.
243 """ 243 """
244 bad = [] 244 bad = []
245 for f, line_num, line in input_api.RightHandSideLines(source_file_filter): 245 for f, line_num, line in input_api.RightHandSideLines(source_file_filter):
246 # Allow lines with http://, https:// and #define/#pragma/#include/#if/#endif 246 # Allow lines with http://, https:// and #define/#pragma/#include/#if/#endif
247 # to exceed the maxlen rule. 247 # to exceed the maxlen rule.
248 if (len(line) > maxlen and 248 if (len(line) > maxlen and
249 not 'http://' in line and 249 not 'http://' in line and
250 not 'https://' in line and 250 not 'https://' in line and
251 not line.startswith('#define') and 251 not line.startswith('#define') and
252 not line.startswith('#include') and 252 not line.startswith('#include') and
253 not line.startswith('#pragma') and 253 not line.startswith('#pragma') and
254 not line.startswith('#if') and 254 not line.startswith('#if') and
255 not line.startswith('#endif')): 255 not line.startswith('#endif')):
256 bad.append( 256 bad.append(
257 '%s, line %s, %s chars' % 257 '%s, line %s, %s chars' %
258 (f.LocalPath(), line_num, len(line))) 258 (f.LocalPath(), line_num, len(line)))
259 if len(bad) == 5: # Just show the first 5 errors. 259 if len(bad) == 5: # Just show the first 5 errors.
260 break 260 break
261 261
262 if bad: 262 if bad:
263 msg = "Found lines longer than %s characters (first 5 shown)." % maxlen 263 msg = 'Found lines longer than %s characters (first 5 shown).' % maxlen
264 return [output_api.PresubmitPromptWarning(msg, items=bad)] 264 return [output_api.PresubmitPromptWarning(msg, items=bad)]
265 else: 265 else:
266 return [] 266 return []
267 267
268 268
269 def CheckLicense(input_api, output_api, license, source_file_filter=None): 269 def CheckLicense(input_api, output_api, license, source_file_filter=None):
270 """Verifies the license header. 270 """Verifies the license header.
271 """ 271 """
272 license_re = input_api.re.compile(license, input_api.re.MULTILINE) 272 license_re = input_api.re.compile(license, input_api.re.MULTILINE)
273 bad_files = [] 273 bad_files = []
274 for f in input_api.AffectedSourceFiles(source_file_filter): 274 for f in input_api.AffectedSourceFiles(source_file_filter):
275 contents = input_api.ReadFile(f, 'rb') 275 contents = input_api.ReadFile(f, 'rb')
276 if not license_re.search(contents): 276 if not license_re.search(contents):
277 bad_files.append(f.LocalPath()) 277 bad_files.append(f.LocalPath())
278 if bad_files: 278 if bad_files:
279 if input_api.is_committing: 279 if input_api.is_committing:
280 res_type = output_api.PresubmitPromptWarning 280 res_type = output_api.PresubmitPromptWarning
281 else: 281 else:
282 res_type = output_api.PresubmitNotifyResult 282 res_type = output_api.PresubmitNotifyResult
283 return [res_type( 283 return [res_type(
284 "Found a bad license header in these files:", items=bad_files)] 284 'Found a bad license header in these files:', items=bad_files)]
285 return [] 285 return []
286 286
287 287
288 def CheckChangeSvnEolStyle(input_api, output_api, source_file_filter=None): 288 def CheckChangeSvnEolStyle(input_api, output_api, source_file_filter=None):
289 """Checks that the source files have svn:eol-style=LF.""" 289 """Checks that the source files have svn:eol-style=LF."""
290 return CheckSvnProperty(input_api, output_api, 290 return CheckSvnProperty(input_api, output_api,
291 'svn:eol-style', 'LF', 291 'svn:eol-style', 'LF',
292 input_api.AffectedSourceFiles(source_file_filter)) 292 input_api.AffectedSourceFiles(source_file_filter))
293 293
294 294
(...skipping 25 matching lines...) Expand all
320 """Checks that affected_files files have prop=expected.""" 320 """Checks that affected_files files have prop=expected."""
321 if input_api.change.scm != 'svn': 321 if input_api.change.scm != 'svn':
322 return [] 322 return []
323 323
324 bad = filter(lambda f: f.Property(prop) != expected, affected_files) 324 bad = filter(lambda f: f.Property(prop) != expected, affected_files)
325 if bad: 325 if bad:
326 if input_api.is_committing: 326 if input_api.is_committing:
327 res_type = output_api.PresubmitError 327 res_type = output_api.PresubmitError
328 else: 328 else:
329 res_type = output_api.PresubmitNotifyResult 329 res_type = output_api.PresubmitNotifyResult
330 message = "Run the command: svn pset %s %s \\" % (prop, expected) 330 message = 'Run the command: svn pset %s %s \\' % (prop, expected)
331 return [res_type(message, items=bad)] 331 return [res_type(message, items=bad)]
332 return [] 332 return []
333 333
334 334
335 ### Other checks 335 ### Other checks
336 336
337 def CheckDoNotSubmit(input_api, output_api): 337 def CheckDoNotSubmit(input_api, output_api):
338 return ( 338 return (
339 CheckDoNotSubmitInDescription(input_api, output_api) + 339 CheckDoNotSubmitInDescription(input_api, output_api) +
340 CheckDoNotSubmitInFiles(input_api, output_api) 340 CheckDoNotSubmitInFiles(input_api, output_api)
341 ) 341 )
342 342
343 343
344 def CheckTreeIsOpen(input_api, output_api, url, closed): 344 def CheckTreeIsOpen(input_api, output_api, url, closed):
345 """Checks that an url's content doesn't match a regexp that would mean that 345 """Checks that an url's content doesn't match a regexp that would mean that
346 the tree is closed.""" 346 the tree is closed."""
347 assert(input_api.is_committing) 347 if not input_api.is_committing:
348 return []
348 try: 349 try:
349 connection = input_api.urllib2.urlopen(url) 350 connection = input_api.urllib2.urlopen(url)
350 status = connection.read() 351 status = connection.read()
351 connection.close() 352 connection.close()
352 if input_api.re.match(closed, status): 353 if input_api.re.match(closed, status):
353 long_text = status + '\n' + url 354 long_text = status + '\n' + url
354 return [output_api.PresubmitPromptWarning("The tree is closed.", 355 return [output_api.PresubmitPromptWarning('The tree is closed.',
355 long_text=long_text)] 356 long_text=long_text)]
356 except IOError: 357 except IOError:
357 pass 358 pass
358 return [] 359 return []
359 360
360 361
361 def RunPythonUnitTests(input_api, output_api, unit_tests): 362 def RunPythonUnitTests(input_api, output_api, unit_tests):
362 """Run the unit tests out of process, capture the output and use the result 363 """Run the unit tests out of process, capture the output and use the result
363 code to determine success. 364 code to determine success.
364 """ 365 """
365 # We don't want to hinder users from uploading incomplete patches. 366 # We don't want to hinder users from uploading incomplete patches.
366 if input_api.is_committing: 367 if input_api.is_committing:
367 message_type = output_api.PresubmitError 368 message_type = output_api.PresubmitError
368 else: 369 else:
369 message_type = output_api.PresubmitNotifyResult 370 message_type = output_api.PresubmitNotifyResult
370 outputs = [] 371 outputs = []
371 for unit_test in unit_tests: 372 for unit_test in unit_tests:
372 # Run the unit tests out of process. This is because some unit tests 373 # Run the unit tests out of process. This is because some unit tests
373 # stub out base libraries and don't clean up their mess. It's too easy to 374 # stub out base libraries and don't clean up their mess. It's too easy to
374 # get subtle bugs. 375 # get subtle bugs.
375 cwd = None 376 cwd = None
376 env = None 377 env = None
377 unit_test_name = unit_test 378 unit_test_name = unit_test
378 # "python -m test.unit_test" doesn't work. We need to change to the right 379 # 'python -m test.unit_test' doesn't work. We need to change to the right
379 # directory instead. 380 # directory instead.
380 if '.' in unit_test: 381 if '.' in unit_test:
381 # Tests imported in submodules (subdirectories) assume that the current 382 # Tests imported in submodules (subdirectories) assume that the current
382 # directory is in the PYTHONPATH. Manually fix that. 383 # directory is in the PYTHONPATH. Manually fix that.
383 unit_test = unit_test.replace('.', '/') 384 unit_test = unit_test.replace('.', '/')
384 cwd = input_api.os_path.dirname(unit_test) 385 cwd = input_api.os_path.dirname(unit_test)
385 unit_test = input_api.os_path.basename(unit_test) 386 unit_test = input_api.os_path.basename(unit_test)
386 env = input_api.environ.copy() 387 env = input_api.environ.copy()
387 # At least on Windows, it seems '.' must explicitly be in PYTHONPATH 388 # At least on Windows, it seems '.' must explicitly be in PYTHONPATH
388 backpath = [ 389 backpath = [
389 '.', input_api.os_path.pathsep.join(['..'] * (cwd.count('/') + 1)) 390 '.', input_api.os_path.pathsep.join(['..'] * (cwd.count('/') + 1))
390 ] 391 ]
391 if env.get('PYTHONPATH'): 392 if env.get('PYTHONPATH'):
392 backpath.append(env.get('PYTHONPATH')) 393 backpath.append(env.get('PYTHONPATH'))
393 env['PYTHONPATH'] = input_api.os_path.pathsep.join((backpath)) 394 env['PYTHONPATH'] = input_api.os_path.pathsep.join((backpath))
394 subproc = input_api.subprocess.Popen( 395 subproc = input_api.subprocess.Popen(
395 [ 396 [
396 input_api.python_executable, 397 input_api.python_executable,
397 "-m", 398 '-m',
398 "%s" % unit_test 399 '%s' % unit_test
399 ], 400 ],
400 cwd=cwd, 401 cwd=cwd,
401 env=env, 402 env=env,
402 stdin=input_api.subprocess.PIPE, 403 stdin=input_api.subprocess.PIPE,
403 stdout=input_api.subprocess.PIPE, 404 stdout=input_api.subprocess.PIPE,
404 stderr=input_api.subprocess.PIPE) 405 stderr=input_api.subprocess.PIPE)
405 stdoutdata, stderrdata = subproc.communicate() 406 stdoutdata, stderrdata = subproc.communicate()
406 # Discard the output if returncode == 0 407 # Discard the output if returncode == 0
407 if subproc.returncode: 408 if subproc.returncode:
408 outputs.append("Test '%s' failed with code %d\n%s\n%s\n" % ( 409 outputs.append('Test \'%s\' failed with code %d\n%s\n%s\n' % (
409 unit_test_name, subproc.returncode, stdoutdata, stderrdata)) 410 unit_test_name, subproc.returncode, stdoutdata, stderrdata))
410 if outputs: 411 if outputs:
411 return [message_type("%d unit tests failed." % len(outputs), 412 return [message_type('%d unit tests failed.' % len(outputs),
412 long_text='\n'.join(outputs))] 413 long_text='\n'.join(outputs))]
413 return [] 414 return []
415
416
417 def CheckRietveldTryJobExecution(input_api, output_api, host_url, platforms,
418 owner):
419 if not input_api.is_committing:
420 return []
421 if not input_api.change.issue or not input_api.change.patchset:
422 return []
423 url = '%s/%d/get_build_results/%d' % (
424 host_url, input_api.change.issue, input_api.change.patchset)
425 try:
426 connection = input_api.urllib2.urlopen(url)
427 # platform|status|url
428 values = [item.split('|', 2) for item in connection.read().splitlines()]
429 connection.close()
430 except input_api.urllib2.HTTPError, e:
431 if e.code == 404:
432 # Fallback to no try job.
433 return [output_api.PresubmitPromptWarning(
434 'You should try the patch first.')]
435 else:
436 # Another HTTP error happened, warn the user.
437 return [output_api.PresubmitPromptWarning(
438 'Got %s while looking for try job status.' % str(e))]
439
440 if not values:
441 # It returned an empty list. Probably a private review.
442 return []
443 # Reformat as an dict of platform: [status, url]
444 values = dict([[v[0], [v[1], v[2]]] for v in values if len(v) == 3])
445 if not values:
446 # It returned useless data.
447 return [output_api.PresubmitNotifyResult('Failed to parse try job results')]
448
449 for platform in platforms:
450 values.setdefault(platform, ['not started', ''])
451 message = None
452 non_success = [k.upper() for k,v in values.iteritems() if v[0] != 'success']
453 if 'failure' in [v[0] for v in values.itervalues()]:
454 message = 'Try job failures on %s!\n' % ', '.join(non_success)
455 elif non_success:
456 message = ('Unfinished (or not even started) try jobs on '
457 '%s.\n') % ', '.join(non_success)
458 if message:
459 message += (
460 'Is try server wrong or broken? Please notify %s. '
461 'Thanks.\n' % owner)
462 return [output_api.PresubmitPromptWarning(message=message)]
463 return []
464
465
466 def CheckBuildbotPendingBuilds(input_api, output_api, url, max_pendings,
467 ignored):
468 if not input_api.json:
469 return [output_api.PresubmitPromptWarning(
470 'Please install simplejson or upgrade to python 2.6+')]
471 try:
472 connection = input_api.urllib2.urlopen(url)
473 raw_data = connection.read()
474 connection.close()
475 except IOError:
476 return [output_api.PresubmitNotifyResult('%s is not accessible' % url)]
477
478 try:
479 data = input_api.json.loads(raw_data)
480 except ValueError:
481 return [output_api.PresubmitNotifyResult('Received malformed json while '
482 'looking up buildbot status')]
483
484 out = []
485 for (builder_name, builder) in data.iteritems():
486 if builder_name in ignored:
487 continue
488 pending_builds_len = len(builder.get('pending_builds', []))
489 if pending_builds_len > max_pendings:
490 out.append('%s has %d build(s) pending' %
491 (builder_name, pending_builds_len))
492 if out:
493 return [output_api.PresubmitPromptWarning(
494 'Build(s) pending. It is suggested to wait that no more than %d '
495 'builds are pending.' % max_pendings,
496 long_text='\n'.join(out))]
497 return []
OLDNEW
« no previous file with comments | « no previous file | tests/presubmit_unittest.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698