| Index: third_party/buildbot_7_12/buildbot/steps/shell.py
|
| diff --git a/third_party/buildbot_7_12/buildbot/steps/shell.py b/third_party/buildbot_7_12/buildbot/steps/shell.py
|
| deleted file mode 100644
|
| index 0748a4bff4e3098deb8d7cd0cec801d794f1f306..0000000000000000000000000000000000000000
|
| --- a/third_party/buildbot_7_12/buildbot/steps/shell.py
|
| +++ /dev/null
|
| @@ -1,713 +0,0 @@
|
| -# -*- test-case-name: buildbot.test.test_steps,buildbot.test.test_properties -*-
|
| -
|
| -import re
|
| -from twisted.python import log
|
| -from twisted.spread import pb
|
| -from buildbot.process.buildstep import LoggingBuildStep, RemoteShellCommand
|
| -from buildbot.process.buildstep import RemoteCommand
|
| -from buildbot.status.builder import SUCCESS, WARNINGS, FAILURE, STDOUT, STDERR
|
| -
|
| -# for existing configurations that import WithProperties from here. We like
|
| -# to move this class around just to keep our readers guessing.
|
| -from buildbot.process.properties import WithProperties
|
| -_hush_pyflakes = [WithProperties]
|
| -del _hush_pyflakes
|
| -
|
| -class ShellCommand(LoggingBuildStep):
|
| - """I run a single shell command on the buildslave. I return FAILURE if
|
| - the exit code of that command is non-zero, SUCCESS otherwise. To change
|
| - this behavior, override my .evaluateCommand method.
|
| -
|
| - By default, a failure of this step will mark the whole build as FAILURE.
|
| - To override this, give me an argument of flunkOnFailure=False .
|
| -
|
| - I create a single Log named 'log' which contains the output of the
|
| - command. To create additional summary Logs, override my .createSummary
|
| - method.
|
| -
|
| - The shell command I run (a list of argv strings) can be provided in
|
| - several ways:
|
| - - a class-level .command attribute
|
| - - a command= parameter to my constructor (overrides .command)
|
| - - set explicitly with my .setCommand() method (overrides both)
|
| -
|
| - @ivar command: a list of renderable objects (typically strings or
|
| - WithProperties instances). This will be used by start()
|
| - to create a RemoteShellCommand instance.
|
| -
|
| - @ivar logfiles: a dict mapping log NAMEs to workdir-relative FILENAMEs
|
| - of their corresponding logfiles. The contents of the file
|
| - named FILENAME will be put into a LogFile named NAME, ina
|
| - something approximating real-time. (note that logfiles=
|
| - is actually handled by our parent class LoggingBuildStep)
|
| -
|
| - @ivar lazylogfiles: Defaults to False. If True, logfiles will be tracked
|
| - `lazily', meaning they will only be added when and if
|
| - they are written to. Empty or nonexistent logfiles
|
| - will be omitted. (Also handled by class
|
| - LoggingBuildStep.)
|
| -
|
| - """
|
| -
|
| - name = "shell"
|
| - description = None # set this to a list of short strings to override
|
| - descriptionDone = None # alternate description when the step is complete
|
| - command = None # set this to a command, or set in kwargs
|
| - # logfiles={} # you can also set 'logfiles' to a dictionary, and it
|
| - # will be merged with any logfiles= argument passed in
|
| - # to __init__
|
| -
|
| - # override this on a specific ShellCommand if you want to let it fail
|
| - # without dooming the entire build to a status of FAILURE
|
| - flunkOnFailure = True
|
| -
|
| - def __init__(self, workdir=None,
|
| - description=None, descriptionDone=None,
|
| - command=None,
|
| - usePTY="slave-config",
|
| - **kwargs):
|
| - # most of our arguments get passed through to the RemoteShellCommand
|
| - # that we create, but first strip out the ones that we pass to
|
| - # BuildStep (like haltOnFailure and friends), and a couple that we
|
| - # consume ourselves.
|
| -
|
| - if description:
|
| - self.description = description
|
| - if isinstance(self.description, str):
|
| - self.description = [self.description]
|
| - if descriptionDone:
|
| - self.descriptionDone = descriptionDone
|
| - if isinstance(self.descriptionDone, str):
|
| - self.descriptionDone = [self.descriptionDone]
|
| - if command:
|
| - self.setCommand(command)
|
| -
|
| - # pull out the ones that LoggingBuildStep wants, then upcall
|
| - buildstep_kwargs = {}
|
| - for k in kwargs.keys()[:]:
|
| - if k in self.__class__.parms:
|
| - buildstep_kwargs[k] = kwargs[k]
|
| - del kwargs[k]
|
| - LoggingBuildStep.__init__(self, **buildstep_kwargs)
|
| - self.addFactoryArguments(workdir=workdir,
|
| - description=description,
|
| - descriptionDone=descriptionDone,
|
| - command=command)
|
| -
|
| - # everything left over goes to the RemoteShellCommand
|
| - kwargs['workdir'] = workdir # including a copy of 'workdir'
|
| - kwargs['usePTY'] = usePTY
|
| - self.remote_kwargs = kwargs
|
| - # we need to stash the RemoteShellCommand's args too
|
| - self.addFactoryArguments(**kwargs)
|
| -
|
| - def setStepStatus(self, step_status):
|
| - LoggingBuildStep.setStepStatus(self, step_status)
|
| -
|
| - # start doesn't set text soon enough to capture our description in
|
| - # the stepStarted status notification. Set text here so it's included.
|
| - self.step_status.setText(self.describe(False))
|
| -
|
| - def setDefaultWorkdir(self, workdir):
|
| - rkw = self.remote_kwargs
|
| - rkw['workdir'] = rkw['workdir'] or workdir
|
| -
|
| - def setCommand(self, command):
|
| - self.command = command
|
| -
|
| - def describe(self, done=False):
|
| - """Return a list of short strings to describe this step, for the
|
| - status display. This uses the first few words of the shell command.
|
| - You can replace this by setting .description in your subclass, or by
|
| - overriding this method to describe the step better.
|
| -
|
| - @type done: boolean
|
| - @param done: whether the command is complete or not, to improve the
|
| - way the command is described. C{done=False} is used
|
| - while the command is still running, so a single
|
| - imperfect-tense verb is appropriate ('compiling',
|
| - 'testing', ...) C{done=True} is used when the command
|
| - has finished, and the default getText() method adds some
|
| - text, so a simple noun is appropriate ('compile',
|
| - 'tests' ...)
|
| - """
|
| -
|
| - if done and self.descriptionDone is not None:
|
| - return list(self.descriptionDone)
|
| - if self.description is not None:
|
| - return list(self.description)
|
| -
|
| - properties = self.build.getProperties()
|
| - words = self.command
|
| - if isinstance(words, (str, unicode)):
|
| - words = words.split()
|
| - # render() each word to handle WithProperties objects
|
| - words = properties.render(words)
|
| - if len(words) < 1:
|
| - return ["???"]
|
| - if len(words) == 1:
|
| - return ["'%s'" % words[0]]
|
| - if len(words) == 2:
|
| - return ["'%s" % words[0], "%s'" % words[1]]
|
| - return ["'%s" % words[0], "%s" % words[1], "...'"]
|
| -
|
| - def setupEnvironment(self, cmd):
|
| - # merge in anything from Build.slaveEnvironment
|
| - # This can be set from a Builder-level environment, or from earlier
|
| - # BuildSteps. The latter method is deprecated and superceded by
|
| - # BuildProperties.
|
| - # Environment variables passed in by a BuildStep override
|
| - # those passed in at the Builder level.
|
| - properties = self.build.getProperties()
|
| - slaveEnv = self.build.slaveEnvironment
|
| - if slaveEnv:
|
| - if cmd.args['env'] is None:
|
| - cmd.args['env'] = {}
|
| - fullSlaveEnv = slaveEnv.copy()
|
| - fullSlaveEnv.update(cmd.args['env'])
|
| - cmd.args['env'] = properties.render(fullSlaveEnv)
|
| - # note that each RemoteShellCommand gets its own copy of the
|
| - # dictionary, so we shouldn't be affecting anyone but ourselves.
|
| -
|
| - def checkForOldSlaveAndLogfiles(self):
|
| - if not self.logfiles:
|
| - return # doesn't matter
|
| - if not self.slaveVersionIsOlderThan("shell", "2.1"):
|
| - return # slave is new enough
|
| - # this buildslave is too old and will ignore the 'logfiles'
|
| - # argument. You'll either have to pull the logfiles manually
|
| - # (say, by using 'cat' in a separate RemoteShellCommand) or
|
| - # upgrade the buildslave.
|
| - msg1 = ("Warning: buildslave %s is too old "
|
| - "to understand logfiles=, ignoring it."
|
| - % self.getSlaveName())
|
| - msg2 = "You will have to pull this logfile (%s) manually."
|
| - log.msg(msg1)
|
| - for logname,remotefilevalue in self.logfiles.items():
|
| - remotefilename = remotefilevalue
|
| - # check for a dictionary of options
|
| - if type(remotefilevalue) == dict:
|
| - remotefilename = remotefilevalue['filename']
|
| -
|
| - newlog = self.addLog(logname)
|
| - newlog.addHeader(msg1 + "\n")
|
| - newlog.addHeader(msg2 % remotefilename + "\n")
|
| - newlog.finish()
|
| - # now prevent setupLogfiles() from adding them
|
| - self.logfiles = {}
|
| -
|
| - def start(self):
|
| - # this block is specific to ShellCommands. subclasses that don't need
|
| - # to set up an argv array, an environment, or extra logfiles= (like
|
| - # the Source subclasses) can just skip straight to startCommand()
|
| - properties = self.build.getProperties()
|
| -
|
| - warnings = []
|
| -
|
| - # create the actual RemoteShellCommand instance now
|
| - kwargs = properties.render(self.remote_kwargs)
|
| - kwargs['command'] = properties.render(self.command)
|
| - kwargs['logfiles'] = self.logfiles
|
| -
|
| - # check for the usePTY flag
|
| - if kwargs.has_key('usePTY') and kwargs['usePTY'] != 'slave-config':
|
| - slavever = self.slaveVersion("shell", "old")
|
| - if self.slaveVersionIsOlderThan("svn", "2.7"):
|
| - warnings.append("NOTE: slave does not allow master to override usePTY\n")
|
| -
|
| - cmd = RemoteShellCommand(**kwargs)
|
| - self.setupEnvironment(cmd)
|
| - self.checkForOldSlaveAndLogfiles()
|
| -
|
| - self.startCommand(cmd, warnings)
|
| -
|
| -
|
| -
|
| -class TreeSize(ShellCommand):
|
| - name = "treesize"
|
| - command = ["du", "-s", "-k", "."]
|
| - description = "measuring tree size"
|
| - descriptionDone = "tree size measured"
|
| - kib = None
|
| -
|
| - def __init__(self, *args, **kwargs):
|
| - ShellCommand.__init__(self, *args, **kwargs)
|
| -
|
| - def commandComplete(self, cmd):
|
| - out = cmd.logs['stdio'].getText()
|
| - m = re.search(r'^(\d+)', out)
|
| - if m:
|
| - self.kib = int(m.group(1))
|
| - self.setProperty("tree-size-KiB", self.kib, "treesize")
|
| -
|
| - def evaluateCommand(self, cmd):
|
| - if cmd.rc != 0:
|
| - return FAILURE
|
| - if self.kib is None:
|
| - return WARNINGS # not sure how 'du' could fail, but whatever
|
| - return SUCCESS
|
| -
|
| - def getText(self, cmd, results):
|
| - if self.kib is not None:
|
| - return ["treesize", "%d KiB" % self.kib]
|
| - return ["treesize", "unknown"]
|
| -
|
| -class SetProperty(ShellCommand):
|
| - name = "setproperty"
|
| -
|
| - def __init__(self, **kwargs):
|
| - self.property = None
|
| - self.extract_fn = None
|
| - self.strip = True
|
| -
|
| - if kwargs.has_key('property'):
|
| - self.property = kwargs['property']
|
| - del kwargs['property']
|
| - if kwargs.has_key('extract_fn'):
|
| - self.extract_fn = kwargs['extract_fn']
|
| - del kwargs['extract_fn']
|
| - if kwargs.has_key('strip'):
|
| - self.strip = kwargs['strip']
|
| - del kwargs['strip']
|
| -
|
| - ShellCommand.__init__(self, **kwargs)
|
| -
|
| - self.addFactoryArguments(property=self.property)
|
| - self.addFactoryArguments(extract_fn=self.extract_fn)
|
| - self.addFactoryArguments(strip=self.strip)
|
| -
|
| - assert self.property or self.extract_fn, \
|
| - "SetProperty step needs either property= or extract_fn="
|
| -
|
| - self.property_changes = {}
|
| -
|
| - def commandComplete(self, cmd):
|
| - if self.property:
|
| - result = cmd.logs['stdio'].getText()
|
| - if self.strip: result = result.strip()
|
| - propname = self.build.getProperties().render(self.property)
|
| - self.setProperty(propname, result, "SetProperty Step")
|
| - self.property_changes[propname] = result
|
| - else:
|
| - log = cmd.logs['stdio']
|
| - new_props = self.extract_fn(cmd.rc,
|
| - ''.join(log.getChunks([STDOUT], onlyText=True)),
|
| - ''.join(log.getChunks([STDERR], onlyText=True)))
|
| - for k,v in new_props.items():
|
| - self.setProperty(k, v, "SetProperty Step")
|
| - self.property_changes = new_props
|
| -
|
| - def createSummary(self, log):
|
| - props_set = [ "%s: %r" % (k,v) for k,v in self.property_changes.items() ]
|
| - self.addCompleteLog('property changes', "\n".join(props_set))
|
| -
|
| - def getText(self, cmd, results):
|
| - if self.property_changes:
|
| - return [ "set props:" ] + self.property_changes.keys()
|
| - else:
|
| - return [ "no change" ]
|
| -
|
| -class Configure(ShellCommand):
|
| -
|
| - name = "configure"
|
| - haltOnFailure = 1
|
| - flunkOnFailure = 1
|
| - description = ["configuring"]
|
| - descriptionDone = ["configure"]
|
| - command = ["./configure"]
|
| -
|
| -class StringFileWriter(pb.Referenceable):
|
| - """
|
| - FileWriter class that just puts received data into a buffer.
|
| -
|
| - Used to upload a file from slave for inline processing rather than
|
| - writing into a file on master.
|
| - """
|
| - def __init__(self):
|
| - self.buffer = ""
|
| -
|
| - def remote_write(self, data):
|
| - self.buffer += data
|
| -
|
| - def remote_close(self):
|
| - pass
|
| -
|
| -class SilentRemoteCommand(RemoteCommand):
|
| - """
|
| - Remote command subclass used to run an internal file upload command on the
|
| - slave. We do not need any progress updates from such command, so override
|
| - remoteUpdate() with an empty method.
|
| - """
|
| - def remoteUpdate(self, update):
|
| - pass
|
| -
|
| -class WarningCountingShellCommand(ShellCommand):
|
| - warnCount = 0
|
| - warningPattern = '.*warning[: ].*'
|
| - # The defaults work for GNU Make.
|
| - directoryEnterPattern = "make.*: Entering directory [\"`'](.*)['`\"]"
|
| - directoryLeavePattern = "make.*: Leaving directory"
|
| - suppressionFile = None
|
| -
|
| - commentEmptyLineRe = re.compile(r"^\s*(\#.*)?$")
|
| - suppressionLineRe = re.compile(r"^\s*(.+?)\s*:\s*(.+?)\s*(?:[:]\s*([0-9]+)(?:-([0-9]+))?\s*)?$")
|
| -
|
| - def __init__(self, workdir=None,
|
| - warningPattern=None, warningExtractor=None,
|
| - directoryEnterPattern=None, directoryLeavePattern=None,
|
| - suppressionFile=None, **kwargs):
|
| - self.workdir = workdir
|
| - # See if we've been given a regular expression to use to match
|
| - # warnings. If not, use a default that assumes any line with "warning"
|
| - # present is a warning. This may lead to false positives in some cases.
|
| - if warningPattern:
|
| - self.warningPattern = warningPattern
|
| - if directoryEnterPattern:
|
| - self.directoryEnterPattern = directoryEnterPattern
|
| - if directoryLeavePattern:
|
| - self.directoryLeavePattern = directoryLeavePattern
|
| - if suppressionFile:
|
| - self.suppressionFile = suppressionFile
|
| - if warningExtractor:
|
| - self.warningExtractor = warningExtractor
|
| - else:
|
| - self.warningExtractor = WarningCountingShellCommand.warnExtractWholeLine
|
| -
|
| - # And upcall to let the base class do its work
|
| - ShellCommand.__init__(self, workdir=workdir, **kwargs)
|
| -
|
| - self.addFactoryArguments(warningPattern=warningPattern,
|
| - directoryEnterPattern=directoryEnterPattern,
|
| - directoryLeavePattern=directoryLeavePattern,
|
| - warningExtractor=warningExtractor,
|
| - suppressionFile=suppressionFile)
|
| - self.suppressions = []
|
| - self.directoryStack = []
|
| -
|
| - def setDefaultWorkdir(self, workdir):
|
| - if self.workdir is None:
|
| - self.workdir = workdir
|
| - ShellCommand.setDefaultWorkdir(self, workdir)
|
| -
|
| - def addSuppression(self, suppressionList):
|
| - """
|
| - This method can be used to add patters of warnings that should
|
| - not be counted.
|
| -
|
| - It takes a single argument, a list of patterns.
|
| -
|
| - Each pattern is a 4-tuple (FILE-RE, WARN-RE, START, END).
|
| -
|
| - FILE-RE is a regular expression (string or compiled regexp), or None.
|
| - If None, the pattern matches all files, else only files matching the
|
| - regexp. If directoryEnterPattern is specified in the class constructor,
|
| - matching is against the full path name, eg. src/main.c.
|
| -
|
| - WARN-RE is similarly a regular expression matched against the
|
| - text of the warning, or None to match all warnings.
|
| -
|
| - START and END form an inclusive line number range to match against. If
|
| - START is None, there is no lower bound, similarly if END is none there
|
| - is no upper bound."""
|
| -
|
| - for fileRe, warnRe, start, end in suppressionList:
|
| - if fileRe != None and isinstance(fileRe, str):
|
| - fileRe = re.compile(fileRe)
|
| - if warnRe != None and isinstance(warnRe, str):
|
| - warnRe = re.compile(warnRe)
|
| - self.suppressions.append((fileRe, warnRe, start, end))
|
| -
|
| - def warnExtractWholeLine(self, line, match):
|
| - """
|
| - Extract warning text as the whole line.
|
| - No file names or line numbers."""
|
| - return (None, None, line)
|
| -
|
| - def warnExtractFromRegexpGroups(self, line, match):
|
| - """
|
| - Extract file name, line number, and warning text as groups (1,2,3)
|
| - of warningPattern match."""
|
| - file = match.group(1)
|
| - lineNo = match.group(2)
|
| - if lineNo != None:
|
| - lineNo = int(lineNo)
|
| - text = match.group(3)
|
| - return (file, lineNo, text)
|
| -
|
| - def maybeAddWarning(self, warnings, line, match):
|
| - if self.suppressions:
|
| - (file, lineNo, text) = self.warningExtractor(self, line, match)
|
| -
|
| - if file != None and file != "" and self.directoryStack:
|
| - currentDirectory = self.directoryStack[-1]
|
| - if currentDirectory != None and currentDirectory != "":
|
| - file = "%s/%s" % (currentDirectory, file)
|
| -
|
| - # Skip adding the warning if any suppression matches.
|
| - for fileRe, warnRe, start, end in self.suppressions:
|
| - if ( (file == None or fileRe == None or fileRe.search(file)) and
|
| - (warnRe == None or warnRe.search(text)) and
|
| - lineNo != None and
|
| - (start == None or start <= lineNo) and
|
| - (end == None or end >= lineNo) ):
|
| - return
|
| -
|
| - warnings.append(line)
|
| - self.warnCount += 1
|
| -
|
| - def start(self):
|
| - if self.suppressionFile == None:
|
| - return ShellCommand.start(self)
|
| -
|
| - version = self.slaveVersion("uploadFile")
|
| - if not version:
|
| - m = "Slave is too old, does not know about uploadFile"
|
| - raise BuildSlaveTooOldError(m)
|
| -
|
| - self.myFileWriter = StringFileWriter()
|
| -
|
| - properties = self.build.getProperties()
|
| -
|
| - args = {
|
| - 'slavesrc': properties.render(self.suppressionFile),
|
| - 'workdir': self.workdir,
|
| - 'writer': self.myFileWriter,
|
| - 'maxsize': None,
|
| - 'blocksize': 32*1024,
|
| - }
|
| - cmd = SilentRemoteCommand('uploadFile', args)
|
| - d = self.runCommand(cmd)
|
| - d.addCallback(self.uploadDone)
|
| - d.addErrback(self.failed)
|
| -
|
| - def uploadDone(self, dummy):
|
| - lines = self.myFileWriter.buffer.split("\n")
|
| - del(self.myFileWriter)
|
| -
|
| - list = []
|
| - for line in lines:
|
| - if self.commentEmptyLineRe.match(line):
|
| - continue
|
| - match = self.suppressionLineRe.match(line)
|
| - if (match):
|
| - file, test, start, end = match.groups()
|
| - if (end != None):
|
| - end = int(end)
|
| - if (start != None):
|
| - start = int(start)
|
| - if end == None:
|
| - end = start
|
| - list.append((file, test, start, end))
|
| -
|
| - self.addSuppression(list)
|
| - return ShellCommand.start(self)
|
| -
|
| - def createSummary(self, log):
|
| - self.warnCount = 0
|
| -
|
| - # Now compile a regular expression from whichever warning pattern we're
|
| - # using
|
| - if not self.warningPattern:
|
| - return
|
| -
|
| - wre = self.warningPattern
|
| - if isinstance(wre, str):
|
| - wre = re.compile(wre)
|
| -
|
| - directoryEnterRe = self.directoryEnterPattern
|
| - if directoryEnterRe != None and isinstance(directoryEnterRe, str):
|
| - directoryEnterRe = re.compile(directoryEnterRe)
|
| -
|
| - directoryLeaveRe = self.directoryLeavePattern
|
| - if directoryLeaveRe != None and isinstance(directoryLeaveRe, str):
|
| - directoryLeaveRe = re.compile(directoryLeaveRe)
|
| -
|
| - # Check if each line in the output from this command matched our
|
| - # warnings regular expressions. If did, bump the warnings count and
|
| - # add the line to the collection of lines with warnings
|
| - warnings = []
|
| - # TODO: use log.readlines(), except we need to decide about stdout vs
|
| - # stderr
|
| - for line in log.getText().split("\n"):
|
| - if directoryEnterRe:
|
| - match = directoryEnterRe.search(line)
|
| - if match:
|
| - self.directoryStack.append(match.group(1))
|
| - if (directoryLeaveRe and
|
| - self.directoryStack and
|
| - directoryLeaveRe.search(line)):
|
| - self.directoryStack.pop()
|
| -
|
| - match = wre.match(line)
|
| - if match:
|
| - self.maybeAddWarning(warnings, line, match)
|
| -
|
| - # If there were any warnings, make the log if lines with warnings
|
| - # available
|
| - if self.warnCount:
|
| - self.addCompleteLog("warnings", "\n".join(warnings) + "\n")
|
| -
|
| - warnings_stat = self.step_status.getStatistic('warnings', 0)
|
| - self.step_status.setStatistic('warnings', warnings_stat + self.warnCount)
|
| -
|
| - try:
|
| - old_count = self.getProperty("warnings-count")
|
| - except KeyError:
|
| - old_count = 0
|
| - self.setProperty("warnings-count", old_count + self.warnCount, "WarningCountingShellCommand")
|
| -
|
| -
|
| - def evaluateCommand(self, cmd):
|
| - if cmd.rc != 0:
|
| - return FAILURE
|
| - if self.warnCount:
|
| - return WARNINGS
|
| - return SUCCESS
|
| -
|
| -
|
| -class Compile(WarningCountingShellCommand):
|
| -
|
| - name = "compile"
|
| - haltOnFailure = 1
|
| - flunkOnFailure = 1
|
| - description = ["compiling"]
|
| - descriptionDone = ["compile"]
|
| - command = ["make", "all"]
|
| -
|
| - OFFprogressMetrics = ('output',)
|
| - # things to track: number of files compiled, number of directories
|
| - # traversed (assuming 'make' is being used)
|
| -
|
| - def createSummary(self, log):
|
| - # TODO: grep for the characteristic GCC error lines and
|
| - # assemble them into a pair of buffers
|
| - WarningCountingShellCommand.createSummary(self, log)
|
| -
|
| -class Test(WarningCountingShellCommand):
|
| -
|
| - name = "test"
|
| - warnOnFailure = 1
|
| - description = ["testing"]
|
| - descriptionDone = ["test"]
|
| - command = ["make", "test"]
|
| -
|
| - def setTestResults(self, total=0, failed=0, passed=0, warnings=0):
|
| - """
|
| - Called by subclasses to set the relevant statistics; this actually
|
| - adds to any statistics already present
|
| - """
|
| - total += self.step_status.getStatistic('tests-total', 0)
|
| - self.step_status.setStatistic('tests-total', total)
|
| - failed += self.step_status.getStatistic('tests-failed', 0)
|
| - self.step_status.setStatistic('tests-failed', failed)
|
| - warnings += self.step_status.getStatistic('tests-warnings', 0)
|
| - self.step_status.setStatistic('tests-warnings', warnings)
|
| - passed += self.step_status.getStatistic('tests-passed', 0)
|
| - self.step_status.setStatistic('tests-passed', passed)
|
| -
|
| - def describe(self, done=False):
|
| - description = WarningCountingShellCommand.describe(self, done)
|
| - if done:
|
| - if self.step_status.hasStatistic('tests-total'):
|
| - total = self.step_status.getStatistic("tests-total", 0)
|
| - failed = self.step_status.getStatistic("tests-failed", 0)
|
| - passed = self.step_status.getStatistic("tests-passed", 0)
|
| - warnings = self.step_status.getStatistic("tests-warnings", 0)
|
| - if not total:
|
| - total = failed + passed + warnings
|
| -
|
| - if total:
|
| - description.append('%d tests' % total)
|
| - if passed:
|
| - description.append('%d passed' % passed)
|
| - if warnings:
|
| - description.append('%d warnings' % warnings)
|
| - if failed:
|
| - description.append('%d failed' % failed)
|
| - return description
|
| -
|
| -class PerlModuleTest(Test):
|
| - command=["prove", "--lib", "lib", "-r", "t"]
|
| - total = 0
|
| -
|
| - def evaluateCommand(self, cmd):
|
| - # Get stdio, stripping pesky newlines etc.
|
| - lines = map(
|
| - lambda line : line.replace('\r\n','').replace('\r','').replace('\n',''),
|
| - self.getLog('stdio').readlines()
|
| - )
|
| -
|
| - total = 0
|
| - passed = 0
|
| - failed = 0
|
| - rc = cmd.rc
|
| -
|
| - # New version of Test::Harness?
|
| - try:
|
| - test_summary_report_index = lines.index("Test Summary Report")
|
| -
|
| - del lines[0:test_summary_report_index + 2]
|
| -
|
| - re_test_result = re.compile("^Result: (PASS|FAIL)$|Tests: \d+ Failed: (\d+)\)|Files=\d+, Tests=(\d+)")
|
| -
|
| - mos = map(lambda line: re_test_result.search(line), lines)
|
| - test_result_lines = [mo.groups() for mo in mos if mo]
|
| -
|
| - for line in test_result_lines:
|
| - if line[0] == 'PASS':
|
| - rc = SUCCESS
|
| - elif line[0] == 'FAIL':
|
| - rc = FAILURE
|
| - elif line[1]:
|
| - failed += int(line[1])
|
| - elif line[2]:
|
| - total = int(line[2])
|
| -
|
| - except ValueError: # Nope, it's the old version
|
| - re_test_result = re.compile("^(All tests successful)|(\d+)/(\d+) subtests failed|Files=\d+, Tests=(\d+),")
|
| -
|
| - mos = map(lambda line: re_test_result.search(line), lines)
|
| - test_result_lines = [mo.groups() for mo in mos if mo]
|
| -
|
| - if test_result_lines:
|
| - test_result_line = test_result_lines[0]
|
| -
|
| - success = test_result_line[0]
|
| -
|
| - if success:
|
| - failed = 0
|
| -
|
| - test_totals_line = test_result_lines[1]
|
| - total_str = test_totals_line[3]
|
| - rc = SUCCESS
|
| - else:
|
| - failed_str = test_result_line[1]
|
| - failed = int(failed_str)
|
| -
|
| - total_str = test_result_line[2]
|
| -
|
| - rc = FAILURE
|
| -
|
| - total = int(total_str)
|
| -
|
| - warnings = 0
|
| - if self.warningPattern:
|
| - wre = self.warningPattern
|
| - if isinstance(wre, str):
|
| - wre = re.compile(wre)
|
| -
|
| - warnings = len([l for l in lines if wre.search(l)])
|
| -
|
| - # Because there are two paths that are used to determine
|
| - # the success/fail result, I have to modify it here if
|
| - # there were warnings.
|
| - if rc == SUCCESS and warnings:
|
| - rc = WARNINGS
|
| -
|
| - if total:
|
| - passed = total - failed
|
| -
|
| - self.setTestResults(total=total, failed=failed, passed=passed,
|
| - warnings=warnings)
|
| -
|
| - return rc
|
|
|