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

Side by Side Diff: third_party/buildbot_7_12/buildbot/steps/shell.py

Issue 12207158: Bye bye buildbot 0.7.12. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/build
Patch Set: Created 7 years, 10 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 | Annotate | Revision Log
OLDNEW
(Empty)
1 # -*- test-case-name: buildbot.test.test_steps,buildbot.test.test_properties -*-
2
3 import re
4 from twisted.python import log
5 from twisted.spread import pb
6 from buildbot.process.buildstep import LoggingBuildStep, RemoteShellCommand
7 from buildbot.process.buildstep import RemoteCommand
8 from buildbot.status.builder import SUCCESS, WARNINGS, FAILURE, STDOUT, STDERR
9
10 # for existing configurations that import WithProperties from here. We like
11 # to move this class around just to keep our readers guessing.
12 from buildbot.process.properties import WithProperties
13 _hush_pyflakes = [WithProperties]
14 del _hush_pyflakes
15
16 class ShellCommand(LoggingBuildStep):
17 """I run a single shell command on the buildslave. I return FAILURE if
18 the exit code of that command is non-zero, SUCCESS otherwise. To change
19 this behavior, override my .evaluateCommand method.
20
21 By default, a failure of this step will mark the whole build as FAILURE.
22 To override this, give me an argument of flunkOnFailure=False .
23
24 I create a single Log named 'log' which contains the output of the
25 command. To create additional summary Logs, override my .createSummary
26 method.
27
28 The shell command I run (a list of argv strings) can be provided in
29 several ways:
30 - a class-level .command attribute
31 - a command= parameter to my constructor (overrides .command)
32 - set explicitly with my .setCommand() method (overrides both)
33
34 @ivar command: a list of renderable objects (typically strings or
35 WithProperties instances). This will be used by start()
36 to create a RemoteShellCommand instance.
37
38 @ivar logfiles: a dict mapping log NAMEs to workdir-relative FILENAMEs
39 of their corresponding logfiles. The contents of the file
40 named FILENAME will be put into a LogFile named NAME, ina
41 something approximating real-time. (note that logfiles=
42 is actually handled by our parent class LoggingBuildStep)
43
44 @ivar lazylogfiles: Defaults to False. If True, logfiles will be tracked
45 `lazily', meaning they will only be added when and if
46 they are written to. Empty or nonexistent logfiles
47 will be omitted. (Also handled by class
48 LoggingBuildStep.)
49
50 """
51
52 name = "shell"
53 description = None # set this to a list of short strings to override
54 descriptionDone = None # alternate description when the step is complete
55 command = None # set this to a command, or set in kwargs
56 # logfiles={} # you can also set 'logfiles' to a dictionary, and it
57 # will be merged with any logfiles= argument passed in
58 # to __init__
59
60 # override this on a specific ShellCommand if you want to let it fail
61 # without dooming the entire build to a status of FAILURE
62 flunkOnFailure = True
63
64 def __init__(self, workdir=None,
65 description=None, descriptionDone=None,
66 command=None,
67 usePTY="slave-config",
68 **kwargs):
69 # most of our arguments get passed through to the RemoteShellCommand
70 # that we create, but first strip out the ones that we pass to
71 # BuildStep (like haltOnFailure and friends), and a couple that we
72 # consume ourselves.
73
74 if description:
75 self.description = description
76 if isinstance(self.description, str):
77 self.description = [self.description]
78 if descriptionDone:
79 self.descriptionDone = descriptionDone
80 if isinstance(self.descriptionDone, str):
81 self.descriptionDone = [self.descriptionDone]
82 if command:
83 self.setCommand(command)
84
85 # pull out the ones that LoggingBuildStep wants, then upcall
86 buildstep_kwargs = {}
87 for k in kwargs.keys()[:]:
88 if k in self.__class__.parms:
89 buildstep_kwargs[k] = kwargs[k]
90 del kwargs[k]
91 LoggingBuildStep.__init__(self, **buildstep_kwargs)
92 self.addFactoryArguments(workdir=workdir,
93 description=description,
94 descriptionDone=descriptionDone,
95 command=command)
96
97 # everything left over goes to the RemoteShellCommand
98 kwargs['workdir'] = workdir # including a copy of 'workdir'
99 kwargs['usePTY'] = usePTY
100 self.remote_kwargs = kwargs
101 # we need to stash the RemoteShellCommand's args too
102 self.addFactoryArguments(**kwargs)
103
104 def setStepStatus(self, step_status):
105 LoggingBuildStep.setStepStatus(self, step_status)
106
107 # start doesn't set text soon enough to capture our description in
108 # the stepStarted status notification. Set text here so it's included.
109 self.step_status.setText(self.describe(False))
110
111 def setDefaultWorkdir(self, workdir):
112 rkw = self.remote_kwargs
113 rkw['workdir'] = rkw['workdir'] or workdir
114
115 def setCommand(self, command):
116 self.command = command
117
118 def describe(self, done=False):
119 """Return a list of short strings to describe this step, for the
120 status display. This uses the first few words of the shell command.
121 You can replace this by setting .description in your subclass, or by
122 overriding this method to describe the step better.
123
124 @type done: boolean
125 @param done: whether the command is complete or not, to improve the
126 way the command is described. C{done=False} is used
127 while the command is still running, so a single
128 imperfect-tense verb is appropriate ('compiling',
129 'testing', ...) C{done=True} is used when the command
130 has finished, and the default getText() method adds some
131 text, so a simple noun is appropriate ('compile',
132 'tests' ...)
133 """
134
135 if done and self.descriptionDone is not None:
136 return list(self.descriptionDone)
137 if self.description is not None:
138 return list(self.description)
139
140 properties = self.build.getProperties()
141 words = self.command
142 if isinstance(words, (str, unicode)):
143 words = words.split()
144 # render() each word to handle WithProperties objects
145 words = properties.render(words)
146 if len(words) < 1:
147 return ["???"]
148 if len(words) == 1:
149 return ["'%s'" % words[0]]
150 if len(words) == 2:
151 return ["'%s" % words[0], "%s'" % words[1]]
152 return ["'%s" % words[0], "%s" % words[1], "...'"]
153
154 def setupEnvironment(self, cmd):
155 # merge in anything from Build.slaveEnvironment
156 # This can be set from a Builder-level environment, or from earlier
157 # BuildSteps. The latter method is deprecated and superceded by
158 # BuildProperties.
159 # Environment variables passed in by a BuildStep override
160 # those passed in at the Builder level.
161 properties = self.build.getProperties()
162 slaveEnv = self.build.slaveEnvironment
163 if slaveEnv:
164 if cmd.args['env'] is None:
165 cmd.args['env'] = {}
166 fullSlaveEnv = slaveEnv.copy()
167 fullSlaveEnv.update(cmd.args['env'])
168 cmd.args['env'] = properties.render(fullSlaveEnv)
169 # note that each RemoteShellCommand gets its own copy of the
170 # dictionary, so we shouldn't be affecting anyone but ourselves.
171
172 def checkForOldSlaveAndLogfiles(self):
173 if not self.logfiles:
174 return # doesn't matter
175 if not self.slaveVersionIsOlderThan("shell", "2.1"):
176 return # slave is new enough
177 # this buildslave is too old and will ignore the 'logfiles'
178 # argument. You'll either have to pull the logfiles manually
179 # (say, by using 'cat' in a separate RemoteShellCommand) or
180 # upgrade the buildslave.
181 msg1 = ("Warning: buildslave %s is too old "
182 "to understand logfiles=, ignoring it."
183 % self.getSlaveName())
184 msg2 = "You will have to pull this logfile (%s) manually."
185 log.msg(msg1)
186 for logname,remotefilevalue in self.logfiles.items():
187 remotefilename = remotefilevalue
188 # check for a dictionary of options
189 if type(remotefilevalue) == dict:
190 remotefilename = remotefilevalue['filename']
191
192 newlog = self.addLog(logname)
193 newlog.addHeader(msg1 + "\n")
194 newlog.addHeader(msg2 % remotefilename + "\n")
195 newlog.finish()
196 # now prevent setupLogfiles() from adding them
197 self.logfiles = {}
198
199 def start(self):
200 # this block is specific to ShellCommands. subclasses that don't need
201 # to set up an argv array, an environment, or extra logfiles= (like
202 # the Source subclasses) can just skip straight to startCommand()
203 properties = self.build.getProperties()
204
205 warnings = []
206
207 # create the actual RemoteShellCommand instance now
208 kwargs = properties.render(self.remote_kwargs)
209 kwargs['command'] = properties.render(self.command)
210 kwargs['logfiles'] = self.logfiles
211
212 # check for the usePTY flag
213 if kwargs.has_key('usePTY') and kwargs['usePTY'] != 'slave-config':
214 slavever = self.slaveVersion("shell", "old")
215 if self.slaveVersionIsOlderThan("svn", "2.7"):
216 warnings.append("NOTE: slave does not allow master to override u sePTY\n")
217
218 cmd = RemoteShellCommand(**kwargs)
219 self.setupEnvironment(cmd)
220 self.checkForOldSlaveAndLogfiles()
221
222 self.startCommand(cmd, warnings)
223
224
225
226 class TreeSize(ShellCommand):
227 name = "treesize"
228 command = ["du", "-s", "-k", "."]
229 description = "measuring tree size"
230 descriptionDone = "tree size measured"
231 kib = None
232
233 def __init__(self, *args, **kwargs):
234 ShellCommand.__init__(self, *args, **kwargs)
235
236 def commandComplete(self, cmd):
237 out = cmd.logs['stdio'].getText()
238 m = re.search(r'^(\d+)', out)
239 if m:
240 self.kib = int(m.group(1))
241 self.setProperty("tree-size-KiB", self.kib, "treesize")
242
243 def evaluateCommand(self, cmd):
244 if cmd.rc != 0:
245 return FAILURE
246 if self.kib is None:
247 return WARNINGS # not sure how 'du' could fail, but whatever
248 return SUCCESS
249
250 def getText(self, cmd, results):
251 if self.kib is not None:
252 return ["treesize", "%d KiB" % self.kib]
253 return ["treesize", "unknown"]
254
255 class SetProperty(ShellCommand):
256 name = "setproperty"
257
258 def __init__(self, **kwargs):
259 self.property = None
260 self.extract_fn = None
261 self.strip = True
262
263 if kwargs.has_key('property'):
264 self.property = kwargs['property']
265 del kwargs['property']
266 if kwargs.has_key('extract_fn'):
267 self.extract_fn = kwargs['extract_fn']
268 del kwargs['extract_fn']
269 if kwargs.has_key('strip'):
270 self.strip = kwargs['strip']
271 del kwargs['strip']
272
273 ShellCommand.__init__(self, **kwargs)
274
275 self.addFactoryArguments(property=self.property)
276 self.addFactoryArguments(extract_fn=self.extract_fn)
277 self.addFactoryArguments(strip=self.strip)
278
279 assert self.property or self.extract_fn, \
280 "SetProperty step needs either property= or extract_fn="
281
282 self.property_changes = {}
283
284 def commandComplete(self, cmd):
285 if self.property:
286 result = cmd.logs['stdio'].getText()
287 if self.strip: result = result.strip()
288 propname = self.build.getProperties().render(self.property)
289 self.setProperty(propname, result, "SetProperty Step")
290 self.property_changes[propname] = result
291 else:
292 log = cmd.logs['stdio']
293 new_props = self.extract_fn(cmd.rc,
294 ''.join(log.getChunks([STDOUT], onlyText=True)),
295 ''.join(log.getChunks([STDERR], onlyText=True)))
296 for k,v in new_props.items():
297 self.setProperty(k, v, "SetProperty Step")
298 self.property_changes = new_props
299
300 def createSummary(self, log):
301 props_set = [ "%s: %r" % (k,v) for k,v in self.property_changes.items() ]
302 self.addCompleteLog('property changes', "\n".join(props_set))
303
304 def getText(self, cmd, results):
305 if self.property_changes:
306 return [ "set props:" ] + self.property_changes.keys()
307 else:
308 return [ "no change" ]
309
310 class Configure(ShellCommand):
311
312 name = "configure"
313 haltOnFailure = 1
314 flunkOnFailure = 1
315 description = ["configuring"]
316 descriptionDone = ["configure"]
317 command = ["./configure"]
318
319 class StringFileWriter(pb.Referenceable):
320 """
321 FileWriter class that just puts received data into a buffer.
322
323 Used to upload a file from slave for inline processing rather than
324 writing into a file on master.
325 """
326 def __init__(self):
327 self.buffer = ""
328
329 def remote_write(self, data):
330 self.buffer += data
331
332 def remote_close(self):
333 pass
334
335 class SilentRemoteCommand(RemoteCommand):
336 """
337 Remote command subclass used to run an internal file upload command on the
338 slave. We do not need any progress updates from such command, so override
339 remoteUpdate() with an empty method.
340 """
341 def remoteUpdate(self, update):
342 pass
343
344 class WarningCountingShellCommand(ShellCommand):
345 warnCount = 0
346 warningPattern = '.*warning[: ].*'
347 # The defaults work for GNU Make.
348 directoryEnterPattern = "make.*: Entering directory [\"`'](.*)['`\"]"
349 directoryLeavePattern = "make.*: Leaving directory"
350 suppressionFile = None
351
352 commentEmptyLineRe = re.compile(r"^\s*(\#.*)?$")
353 suppressionLineRe = re.compile(r"^\s*(.+?)\s*:\s*(.+?)\s*(?:[:]\s*([0-9]+)(? :-([0-9]+))?\s*)?$")
354
355 def __init__(self, workdir=None,
356 warningPattern=None, warningExtractor=None,
357 directoryEnterPattern=None, directoryLeavePattern=None,
358 suppressionFile=None, **kwargs):
359 self.workdir = workdir
360 # See if we've been given a regular expression to use to match
361 # warnings. If not, use a default that assumes any line with "warning"
362 # present is a warning. This may lead to false positives in some cases.
363 if warningPattern:
364 self.warningPattern = warningPattern
365 if directoryEnterPattern:
366 self.directoryEnterPattern = directoryEnterPattern
367 if directoryLeavePattern:
368 self.directoryLeavePattern = directoryLeavePattern
369 if suppressionFile:
370 self.suppressionFile = suppressionFile
371 if warningExtractor:
372 self.warningExtractor = warningExtractor
373 else:
374 self.warningExtractor = WarningCountingShellCommand.warnExtractWhole Line
375
376 # And upcall to let the base class do its work
377 ShellCommand.__init__(self, workdir=workdir, **kwargs)
378
379 self.addFactoryArguments(warningPattern=warningPattern,
380 directoryEnterPattern=directoryEnterPattern,
381 directoryLeavePattern=directoryLeavePattern,
382 warningExtractor=warningExtractor,
383 suppressionFile=suppressionFile)
384 self.suppressions = []
385 self.directoryStack = []
386
387 def setDefaultWorkdir(self, workdir):
388 if self.workdir is None:
389 self.workdir = workdir
390 ShellCommand.setDefaultWorkdir(self, workdir)
391
392 def addSuppression(self, suppressionList):
393 """
394 This method can be used to add patters of warnings that should
395 not be counted.
396
397 It takes a single argument, a list of patterns.
398
399 Each pattern is a 4-tuple (FILE-RE, WARN-RE, START, END).
400
401 FILE-RE is a regular expression (string or compiled regexp), or None.
402 If None, the pattern matches all files, else only files matching the
403 regexp. If directoryEnterPattern is specified in the class constructor,
404 matching is against the full path name, eg. src/main.c.
405
406 WARN-RE is similarly a regular expression matched against the
407 text of the warning, or None to match all warnings.
408
409 START and END form an inclusive line number range to match against. If
410 START is None, there is no lower bound, similarly if END is none there
411 is no upper bound."""
412
413 for fileRe, warnRe, start, end in suppressionList:
414 if fileRe != None and isinstance(fileRe, str):
415 fileRe = re.compile(fileRe)
416 if warnRe != None and isinstance(warnRe, str):
417 warnRe = re.compile(warnRe)
418 self.suppressions.append((fileRe, warnRe, start, end))
419
420 def warnExtractWholeLine(self, line, match):
421 """
422 Extract warning text as the whole line.
423 No file names or line numbers."""
424 return (None, None, line)
425
426 def warnExtractFromRegexpGroups(self, line, match):
427 """
428 Extract file name, line number, and warning text as groups (1,2,3)
429 of warningPattern match."""
430 file = match.group(1)
431 lineNo = match.group(2)
432 if lineNo != None:
433 lineNo = int(lineNo)
434 text = match.group(3)
435 return (file, lineNo, text)
436
437 def maybeAddWarning(self, warnings, line, match):
438 if self.suppressions:
439 (file, lineNo, text) = self.warningExtractor(self, line, match)
440
441 if file != None and file != "" and self.directoryStack:
442 currentDirectory = self.directoryStack[-1]
443 if currentDirectory != None and currentDirectory != "":
444 file = "%s/%s" % (currentDirectory, file)
445
446 # Skip adding the warning if any suppression matches.
447 for fileRe, warnRe, start, end in self.suppressions:
448 if ( (file == None or fileRe == None or fileRe.search(file)) and
449 (warnRe == None or warnRe.search(text)) and
450 lineNo != None and
451 (start == None or start <= lineNo) and
452 (end == None or end >= lineNo) ):
453 return
454
455 warnings.append(line)
456 self.warnCount += 1
457
458 def start(self):
459 if self.suppressionFile == None:
460 return ShellCommand.start(self)
461
462 version = self.slaveVersion("uploadFile")
463 if not version:
464 m = "Slave is too old, does not know about uploadFile"
465 raise BuildSlaveTooOldError(m)
466
467 self.myFileWriter = StringFileWriter()
468
469 properties = self.build.getProperties()
470
471 args = {
472 'slavesrc': properties.render(self.suppressionFile),
473 'workdir': self.workdir,
474 'writer': self.myFileWriter,
475 'maxsize': None,
476 'blocksize': 32*1024,
477 }
478 cmd = SilentRemoteCommand('uploadFile', args)
479 d = self.runCommand(cmd)
480 d.addCallback(self.uploadDone)
481 d.addErrback(self.failed)
482
483 def uploadDone(self, dummy):
484 lines = self.myFileWriter.buffer.split("\n")
485 del(self.myFileWriter)
486
487 list = []
488 for line in lines:
489 if self.commentEmptyLineRe.match(line):
490 continue
491 match = self.suppressionLineRe.match(line)
492 if (match):
493 file, test, start, end = match.groups()
494 if (end != None):
495 end = int(end)
496 if (start != None):
497 start = int(start)
498 if end == None:
499 end = start
500 list.append((file, test, start, end))
501
502 self.addSuppression(list)
503 return ShellCommand.start(self)
504
505 def createSummary(self, log):
506 self.warnCount = 0
507
508 # Now compile a regular expression from whichever warning pattern we're
509 # using
510 if not self.warningPattern:
511 return
512
513 wre = self.warningPattern
514 if isinstance(wre, str):
515 wre = re.compile(wre)
516
517 directoryEnterRe = self.directoryEnterPattern
518 if directoryEnterRe != None and isinstance(directoryEnterRe, str):
519 directoryEnterRe = re.compile(directoryEnterRe)
520
521 directoryLeaveRe = self.directoryLeavePattern
522 if directoryLeaveRe != None and isinstance(directoryLeaveRe, str):
523 directoryLeaveRe = re.compile(directoryLeaveRe)
524
525 # Check if each line in the output from this command matched our
526 # warnings regular expressions. If did, bump the warnings count and
527 # add the line to the collection of lines with warnings
528 warnings = []
529 # TODO: use log.readlines(), except we need to decide about stdout vs
530 # stderr
531 for line in log.getText().split("\n"):
532 if directoryEnterRe:
533 match = directoryEnterRe.search(line)
534 if match:
535 self.directoryStack.append(match.group(1))
536 if (directoryLeaveRe and
537 self.directoryStack and
538 directoryLeaveRe.search(line)):
539 self.directoryStack.pop()
540
541 match = wre.match(line)
542 if match:
543 self.maybeAddWarning(warnings, line, match)
544
545 # If there were any warnings, make the log if lines with warnings
546 # available
547 if self.warnCount:
548 self.addCompleteLog("warnings", "\n".join(warnings) + "\n")
549
550 warnings_stat = self.step_status.getStatistic('warnings', 0)
551 self.step_status.setStatistic('warnings', warnings_stat + self.warnCount )
552
553 try:
554 old_count = self.getProperty("warnings-count")
555 except KeyError:
556 old_count = 0
557 self.setProperty("warnings-count", old_count + self.warnCount, "WarningC ountingShellCommand")
558
559
560 def evaluateCommand(self, cmd):
561 if cmd.rc != 0:
562 return FAILURE
563 if self.warnCount:
564 return WARNINGS
565 return SUCCESS
566
567
568 class Compile(WarningCountingShellCommand):
569
570 name = "compile"
571 haltOnFailure = 1
572 flunkOnFailure = 1
573 description = ["compiling"]
574 descriptionDone = ["compile"]
575 command = ["make", "all"]
576
577 OFFprogressMetrics = ('output',)
578 # things to track: number of files compiled, number of directories
579 # traversed (assuming 'make' is being used)
580
581 def createSummary(self, log):
582 # TODO: grep for the characteristic GCC error lines and
583 # assemble them into a pair of buffers
584 WarningCountingShellCommand.createSummary(self, log)
585
586 class Test(WarningCountingShellCommand):
587
588 name = "test"
589 warnOnFailure = 1
590 description = ["testing"]
591 descriptionDone = ["test"]
592 command = ["make", "test"]
593
594 def setTestResults(self, total=0, failed=0, passed=0, warnings=0):
595 """
596 Called by subclasses to set the relevant statistics; this actually
597 adds to any statistics already present
598 """
599 total += self.step_status.getStatistic('tests-total', 0)
600 self.step_status.setStatistic('tests-total', total)
601 failed += self.step_status.getStatistic('tests-failed', 0)
602 self.step_status.setStatistic('tests-failed', failed)
603 warnings += self.step_status.getStatistic('tests-warnings', 0)
604 self.step_status.setStatistic('tests-warnings', warnings)
605 passed += self.step_status.getStatistic('tests-passed', 0)
606 self.step_status.setStatistic('tests-passed', passed)
607
608 def describe(self, done=False):
609 description = WarningCountingShellCommand.describe(self, done)
610 if done:
611 if self.step_status.hasStatistic('tests-total'):
612 total = self.step_status.getStatistic("tests-total", 0)
613 failed = self.step_status.getStatistic("tests-failed", 0)
614 passed = self.step_status.getStatistic("tests-passed", 0)
615 warnings = self.step_status.getStatistic("tests-warnings", 0)
616 if not total:
617 total = failed + passed + warnings
618
619 if total:
620 description.append('%d tests' % total)
621 if passed:
622 description.append('%d passed' % passed)
623 if warnings:
624 description.append('%d warnings' % warnings)
625 if failed:
626 description.append('%d failed' % failed)
627 return description
628
629 class PerlModuleTest(Test):
630 command=["prove", "--lib", "lib", "-r", "t"]
631 total = 0
632
633 def evaluateCommand(self, cmd):
634 # Get stdio, stripping pesky newlines etc.
635 lines = map(
636 lambda line : line.replace('\r\n','').replace('\r','').replace('\n', ''),
637 self.getLog('stdio').readlines()
638 )
639
640 total = 0
641 passed = 0
642 failed = 0
643 rc = cmd.rc
644
645 # New version of Test::Harness?
646 try:
647 test_summary_report_index = lines.index("Test Summary Report")
648
649 del lines[0:test_summary_report_index + 2]
650
651 re_test_result = re.compile("^Result: (PASS|FAIL)$|Tests: \d+ Failed : (\d+)\)|Files=\d+, Tests=(\d+)")
652
653 mos = map(lambda line: re_test_result.search(line), lines)
654 test_result_lines = [mo.groups() for mo in mos if mo]
655
656 for line in test_result_lines:
657 if line[0] == 'PASS':
658 rc = SUCCESS
659 elif line[0] == 'FAIL':
660 rc = FAILURE
661 elif line[1]:
662 failed += int(line[1])
663 elif line[2]:
664 total = int(line[2])
665
666 except ValueError: # Nope, it's the old version
667 re_test_result = re.compile("^(All tests successful)|(\d+)/(\d+) sub tests failed|Files=\d+, Tests=(\d+),")
668
669 mos = map(lambda line: re_test_result.search(line), lines)
670 test_result_lines = [mo.groups() for mo in mos if mo]
671
672 if test_result_lines:
673 test_result_line = test_result_lines[0]
674
675 success = test_result_line[0]
676
677 if success:
678 failed = 0
679
680 test_totals_line = test_result_lines[1]
681 total_str = test_totals_line[3]
682 rc = SUCCESS
683 else:
684 failed_str = test_result_line[1]
685 failed = int(failed_str)
686
687 total_str = test_result_line[2]
688
689 rc = FAILURE
690
691 total = int(total_str)
692
693 warnings = 0
694 if self.warningPattern:
695 wre = self.warningPattern
696 if isinstance(wre, str):
697 wre = re.compile(wre)
698
699 warnings = len([l for l in lines if wre.search(l)])
700
701 # Because there are two paths that are used to determine
702 # the success/fail result, I have to modify it here if
703 # there were warnings.
704 if rc == SUCCESS and warnings:
705 rc = WARNINGS
706
707 if total:
708 passed = total - failed
709
710 self.setTestResults(total=total, failed=failed, passed=passed,
711 warnings=warnings)
712
713 return rc
OLDNEW
« no previous file with comments | « third_party/buildbot_7_12/buildbot/steps/python_twisted.py ('k') | third_party/buildbot_7_12/buildbot/steps/source.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698