Index: third_party/buildbot_7_12/buildbot/steps/python_twisted.py |
diff --git a/third_party/buildbot_7_12/buildbot/steps/python_twisted.py b/third_party/buildbot_7_12/buildbot/steps/python_twisted.py |
deleted file mode 100644 |
index d0ed5b08845e28a8452ee6d92177b20453767f49..0000000000000000000000000000000000000000 |
--- a/third_party/buildbot_7_12/buildbot/steps/python_twisted.py |
+++ /dev/null |
@@ -1,804 +0,0 @@ |
-# -*- test-case-name: buildbot.test.test_twisted -*- |
- |
-from twisted.python import log |
- |
-from buildbot.status import builder |
-from buildbot.status.builder import SUCCESS, FAILURE, WARNINGS, SKIPPED |
-from buildbot.process.buildstep import LogLineObserver, OutputProgressObserver |
-from buildbot.process.buildstep import RemoteShellCommand |
-from buildbot.steps.shell import ShellCommand |
- |
-try: |
- import cStringIO |
- StringIO = cStringIO |
-except ImportError: |
- import StringIO |
-import re |
- |
-# BuildSteps that are specific to the Twisted source tree |
- |
-class HLint(ShellCommand): |
- """I run a 'lint' checker over a set of .xhtml files. Any deviations |
- from recommended style is flagged and put in the output log. |
- |
- This step looks at .changes in the parent Build to extract a list of |
- Lore XHTML files to check.""" |
- |
- name = "hlint" |
- description = ["running", "hlint"] |
- descriptionDone = ["hlint"] |
- warnOnWarnings = True |
- warnOnFailure = True |
- # TODO: track time, but not output |
- warnings = 0 |
- |
- def __init__(self, python=None, **kwargs): |
- ShellCommand.__init__(self, **kwargs) |
- self.addFactoryArguments(python=python) |
- self.python = python |
- |
- def start(self): |
- # create the command |
- htmlFiles = {} |
- for f in self.build.allFiles(): |
- if f.endswith(".xhtml") and not f.startswith("sandbox/"): |
- htmlFiles[f] = 1 |
- # remove duplicates |
- hlintTargets = htmlFiles.keys() |
- hlintTargets.sort() |
- if not hlintTargets: |
- return SKIPPED |
- self.hlintFiles = hlintTargets |
- c = [] |
- if self.python: |
- c.append(self.python) |
- c += ["bin/lore", "-p", "--output", "lint"] + self.hlintFiles |
- self.setCommand(c) |
- |
- # add an extra log file to show the .html files we're checking |
- self.addCompleteLog("files", "\n".join(self.hlintFiles)+"\n") |
- |
- ShellCommand.start(self) |
- |
- def commandComplete(self, cmd): |
- # TODO: remove the 'files' file (a list of .xhtml files that were |
- # submitted to hlint) because it is available in the logfile and |
- # mostly exists to give the user an idea of how long the step will |
- # take anyway). |
- lines = cmd.logs['stdio'].getText().split("\n") |
- warningLines = filter(lambda line:':' in line, lines) |
- if warningLines: |
- self.addCompleteLog("warnings", "".join(warningLines)) |
- warnings = len(warningLines) |
- self.warnings = warnings |
- |
- def evaluateCommand(self, cmd): |
- # warnings are in stdout, rc is always 0, unless the tools break |
- if cmd.rc != 0: |
- return FAILURE |
- if self.warnings: |
- return WARNINGS |
- return SUCCESS |
- |
- def getText2(self, cmd, results): |
- if cmd.rc != 0: |
- return ["hlint"] |
- return ["%d hlin%s" % (self.warnings, |
- self.warnings == 1 and 't' or 'ts')] |
- |
-def countFailedTests(output): |
- # start scanning 10kb from the end, because there might be a few kb of |
- # import exception tracebacks between the total/time line and the errors |
- # line |
- chunk = output[-10000:] |
- lines = chunk.split("\n") |
- lines.pop() # blank line at end |
- # lines[-3] is "Ran NN tests in 0.242s" |
- # lines[-2] is blank |
- # lines[-1] is 'OK' or 'FAILED (failures=1, errors=12)' |
- # or 'FAILED (failures=1)' |
- # or "PASSED (skips=N, successes=N)" (for Twisted-2.0) |
- # there might be other lines dumped here. Scan all the lines. |
- res = {'total': None, |
- 'failures': 0, |
- 'errors': 0, |
- 'skips': 0, |
- 'expectedFailures': 0, |
- 'unexpectedSuccesses': 0, |
- } |
- for l in lines: |
- out = re.search(r'Ran (\d+) tests', l) |
- if out: |
- res['total'] = int(out.group(1)) |
- if (l.startswith("OK") or |
- l.startswith("FAILED ") or |
- l.startswith("PASSED")): |
- # the extra space on FAILED_ is to distinguish the overall |
- # status from an individual test which failed. The lack of a |
- # space on the OK is because it may be printed without any |
- # additional text (if there are no skips,etc) |
- out = re.search(r'failures=(\d+)', l) |
- if out: res['failures'] = int(out.group(1)) |
- out = re.search(r'errors=(\d+)', l) |
- if out: res['errors'] = int(out.group(1)) |
- out = re.search(r'skips=(\d+)', l) |
- if out: res['skips'] = int(out.group(1)) |
- out = re.search(r'expectedFailures=(\d+)', l) |
- if out: res['expectedFailures'] = int(out.group(1)) |
- out = re.search(r'unexpectedSuccesses=(\d+)', l) |
- if out: res['unexpectedSuccesses'] = int(out.group(1)) |
- # successes= is a Twisted-2.0 addition, and is not currently used |
- out = re.search(r'successes=(\d+)', l) |
- if out: res['successes'] = int(out.group(1)) |
- |
- return res |
- |
- |
-class TrialTestCaseCounter(LogLineObserver): |
- _line_re = re.compile(r'^(?:Doctest: )?([\w\.]+) \.\.\. \[([^\]]+)\]$') |
- numTests = 0 |
- finished = False |
- |
- def outLineReceived(self, line): |
- # different versions of Twisted emit different per-test lines with |
- # the bwverbose reporter. |
- # 2.0.0: testSlave (buildbot.test.test_runner.Create) ... [OK] |
- # 2.1.0: buildbot.test.test_runner.Create.testSlave ... [OK] |
- # 2.4.0: buildbot.test.test_runner.Create.testSlave ... [OK] |
- # Let's just handle the most recent version, since it's the easiest. |
- # Note that doctests create lines line this: |
- # Doctest: viff.field.GF ... [OK] |
- |
- if self.finished: |
- return |
- if line.startswith("=" * 40): |
- self.finished = True |
- return |
- |
- m = self._line_re.search(line.strip()) |
- if m: |
- testname, result = m.groups() |
- self.numTests += 1 |
- self.step.setProgress('tests', self.numTests) |
- |
- |
-UNSPECIFIED=() # since None is a valid choice |
- |
-class Trial(ShellCommand): |
- """I run a unit test suite using 'trial', a unittest-like testing |
- framework that comes with Twisted. Trial is used to implement Twisted's |
- own unit tests, and is the unittest-framework of choice for many projects |
- that use Twisted internally. |
- |
- Projects that use trial typically have all their test cases in a 'test' |
- subdirectory of their top-level library directory. I.e. for my package |
- 'petmail', the tests are in 'petmail/test/test_*.py'. More complicated |
- packages (like Twisted itself) may have multiple test directories, like |
- 'twisted/test/test_*.py' for the core functionality and |
- 'twisted/mail/test/test_*.py' for the email-specific tests. |
- |
- To run trial tests, you run the 'trial' executable and tell it where the |
- test cases are located. The most common way of doing this is with a |
- module name. For petmail, I would run 'trial petmail.test' and it would |
- locate all the test_*.py files under petmail/test/, running every test |
- case it could find in them. Unlike the unittest.py that comes with |
- Python, you do not run the test_foo.py as a script; you always let trial |
- do the importing and running. The 'tests' parameter controls which tests |
- trial will run: it can be a string or a list of strings. |
- |
- To find these test cases, you must set a PYTHONPATH that allows something |
- like 'import petmail.test' to work. For packages that don't use a |
- separate top-level 'lib' directory, PYTHONPATH=. will work, and will use |
- the test cases (and the code they are testing) in-place. |
- PYTHONPATH=build/lib or PYTHONPATH=build/lib.$ARCH are also useful when |
- you do a'setup.py build' step first. The 'testpath' attribute of this |
- class controls what PYTHONPATH= is set to. |
- |
- Trial has the ability (through the --testmodule flag) to run only the set |
- of test cases named by special 'test-case-name' tags in source files. We |
- can get the list of changed source files from our parent Build and |
- provide them to trial, thus running the minimal set of test cases needed |
- to cover the Changes. This is useful for quick builds, especially in |
- trees with a lot of test cases. The 'testChanges' parameter controls this |
- feature: if set, it will override 'tests'. |
- |
- The trial executable itself is typically just 'trial' (which is usually |
- found on your $PATH as /usr/bin/trial), but it can be overridden with the |
- 'trial' parameter. This is useful for Twisted's own unittests, which want |
- to use the copy of bin/trial that comes with the sources. (when bin/trial |
- discovers that it is living in a subdirectory named 'Twisted', it assumes |
- it is being run from the source tree and adds that parent directory to |
- PYTHONPATH. Therefore the canonical way to run Twisted's own unittest |
- suite is './bin/trial twisted.test' rather than 'PYTHONPATH=. |
- /usr/bin/trial twisted.test', especially handy when /usr/bin/trial has |
- not yet been installed). |
- |
- To influence the version of python being used for the tests, or to add |
- flags to the command, set the 'python' parameter. This can be a string |
- (like 'python2.2') or a list (like ['python2.3', '-Wall']). |
- |
- Trial creates and switches into a directory named _trial_temp/ before |
- running the tests, and sends the twisted log (which includes all |
- exceptions) to a file named test.log . This file will be pulled up to |
- the master where it can be seen as part of the status output. |
- |
- There are some class attributes which may be usefully overridden |
- by subclasses. 'trialMode' and 'trialArgs' can influence the trial |
- command line. |
- """ |
- |
- name = "trial" |
- progressMetrics = ('output', 'tests', 'test.log') |
- # note: the slash only works on unix buildslaves, of course, but we have |
- # no way to know what the buildslave uses as a separator. TODO: figure |
- # out something clever. |
- logfiles = {"test.log": "_trial_temp/test.log"} |
- # we use test.log to track Progress at the end of __init__() |
- |
- flunkOnFailure = True |
- python = None |
- trial = "trial" |
- trialMode = ["--reporter=bwverbose"] # requires Twisted-2.1.0 or newer |
- # for Twisted-2.0.0 or 1.3.0, use ["-o"] instead |
- trialArgs = [] |
- testpath = UNSPECIFIED # required (but can be None) |
- testChanges = False # TODO: needs better name |
- recurse = False |
- reactor = None |
- randomly = False |
- tests = None # required |
- |
- def __init__(self, reactor=UNSPECIFIED, python=None, trial=None, |
- testpath=UNSPECIFIED, |
- tests=None, testChanges=None, |
- recurse=None, randomly=None, |
- trialMode=None, trialArgs=None, |
- **kwargs): |
- """ |
- @type testpath: string |
- @param testpath: use in PYTHONPATH when running the tests. If |
- None, do not set PYTHONPATH. Setting this to '.' will |
- cause the source files to be used in-place. |
- |
- @type python: string (without spaces) or list |
- @param python: which python executable to use. Will form the start of |
- the argv array that will launch trial. If you use this, |
- you should set 'trial' to an explicit path (like |
- /usr/bin/trial or ./bin/trial). Defaults to None, which |
- leaves it out entirely (running 'trial args' instead of |
- 'python ./bin/trial args'). Likely values are 'python', |
- ['python2.2'], ['python', '-Wall'], etc. |
- |
- @type trial: string |
- @param trial: which 'trial' executable to run. |
- Defaults to 'trial', which will cause $PATH to be |
- searched and probably find /usr/bin/trial . If you set |
- 'python', this should be set to an explicit path (because |
- 'python2.3 trial' will not work). |
- |
- @type trialMode: list of strings |
- @param trialMode: a list of arguments to pass to trial, specifically |
- to set the reporting mode. This defaults to ['-to'] |
- which means 'verbose colorless output' to the trial |
- that comes with Twisted-2.0.x and at least -2.1.0 . |
- Newer versions of Twisted may come with a trial |
- that prefers ['--reporter=bwverbose']. |
- |
- @type trialArgs: list of strings |
- @param trialArgs: a list of arguments to pass to trial, available to |
- turn on any extra flags you like. Defaults to []. |
- |
- @type tests: list of strings |
- @param tests: a list of test modules to run, like |
- ['twisted.test.test_defer', 'twisted.test.test_process']. |
- If this is a string, it will be converted into a one-item |
- list. |
- |
- @type testChanges: boolean |
- @param testChanges: if True, ignore the 'tests' parameter and instead |
- ask the Build for all the files that make up the |
- Changes going into this build. Pass these filenames |
- to trial and ask it to look for test-case-name |
- tags, running just the tests necessary to cover the |
- changes. |
- |
- @type recurse: boolean |
- @param recurse: If True, pass the --recurse option to trial, allowing |
- test cases to be found in deeper subdirectories of the |
- modules listed in 'tests'. This does not appear to be |
- necessary when using testChanges. |
- |
- @type reactor: string |
- @param reactor: which reactor to use, like 'gtk' or 'java'. If not |
- provided, the Twisted's usual platform-dependent |
- default is used. |
- |
- @type randomly: boolean |
- @param randomly: if True, add the --random=0 argument, which instructs |
- trial to run the unit tests in a random order each |
- time. This occasionally catches problems that might be |
- masked when one module always runs before another |
- (like failing to make registerAdapter calls before |
- lookups are done). |
- |
- @type kwargs: dict |
- @param kwargs: parameters. The following parameters are inherited from |
- L{ShellCommand} and may be useful to set: workdir, |
- haltOnFailure, flunkOnWarnings, flunkOnFailure, |
- warnOnWarnings, warnOnFailure, want_stdout, want_stderr, |
- timeout. |
- """ |
- ShellCommand.__init__(self, **kwargs) |
- self.addFactoryArguments(reactor=reactor, |
- python=python, |
- trial=trial, |
- testpath=testpath, |
- tests=tests, |
- testChanges=testChanges, |
- recurse=recurse, |
- randomly=randomly, |
- trialMode=trialMode, |
- trialArgs=trialArgs, |
- ) |
- |
- if python: |
- self.python = python |
- if self.python is not None: |
- if type(self.python) is str: |
- self.python = [self.python] |
- for s in self.python: |
- if " " in s: |
- # this is not strictly an error, but I suspect more |
- # people will accidentally try to use python="python2.3 |
- # -Wall" than will use embedded spaces in a python flag |
- log.msg("python= component '%s' has spaces") |
- log.msg("To add -Wall, use python=['python', '-Wall']") |
- why = "python= value has spaces, probably an error" |
- raise ValueError(why) |
- |
- if trial: |
- self.trial = trial |
- if " " in self.trial: |
- raise ValueError("trial= value has spaces") |
- if trialMode is not None: |
- self.trialMode = trialMode |
- if trialArgs is not None: |
- self.trialArgs = trialArgs |
- |
- if testpath is not UNSPECIFIED: |
- self.testpath = testpath |
- if self.testpath is UNSPECIFIED: |
- raise ValueError("You must specify testpath= (it can be None)") |
- assert isinstance(self.testpath, str) or self.testpath is None |
- |
- if reactor is not UNSPECIFIED: |
- self.reactor = reactor |
- |
- if tests is not None: |
- self.tests = tests |
- if type(self.tests) is str: |
- self.tests = [self.tests] |
- if testChanges is not None: |
- self.testChanges = testChanges |
- #self.recurse = True # not sure this is necessary |
- |
- if not self.testChanges and self.tests is None: |
- raise ValueError("Must either set testChanges= or provide tests=") |
- |
- if recurse is not None: |
- self.recurse = recurse |
- if randomly is not None: |
- self.randomly = randomly |
- |
- # build up most of the command, then stash it until start() |
- command = [] |
- if self.python: |
- command.extend(self.python) |
- command.append(self.trial) |
- command.extend(self.trialMode) |
- if self.recurse: |
- command.append("--recurse") |
- if self.reactor: |
- command.append("--reactor=%s" % reactor) |
- if self.randomly: |
- command.append("--random=0") |
- command.extend(self.trialArgs) |
- self.command = command |
- |
- if self.reactor: |
- self.description = ["testing", "(%s)" % self.reactor] |
- self.descriptionDone = ["tests"] |
- # commandComplete adds (reactorname) to self.text |
- else: |
- self.description = ["testing"] |
- self.descriptionDone = ["tests"] |
- |
- # this counter will feed Progress along the 'test cases' metric |
- self.addLogObserver('stdio', TrialTestCaseCounter()) |
- # this one just measures bytes of output in _trial_temp/test.log |
- self.addLogObserver('test.log', OutputProgressObserver('test.log')) |
- |
- def setupEnvironment(self, cmd): |
- ShellCommand.setupEnvironment(self, cmd) |
- if self.testpath != None: |
- e = cmd.args['env'] |
- if e is None: |
- cmd.args['env'] = {'PYTHONPATH': self.testpath} |
- else: |
- # TODO: somehow, each build causes another copy of |
- # self.testpath to get prepended |
- if e.get('PYTHONPATH', "") == "": |
- e['PYTHONPATH'] = self.testpath |
- else: |
- e['PYTHONPATH'] = self.testpath + ":" + e['PYTHONPATH'] |
- try: |
- p = cmd.args['env']['PYTHONPATH'] |
- if type(p) is not str: |
- log.msg("hey, not a string:", p) |
- assert False |
- except (KeyError, TypeError): |
- # KeyError if args doesn't have ['env'] |
- # KeyError if args['env'] doesn't have ['PYTHONPATH'] |
- # TypeError if args is None |
- pass |
- |
- def start(self): |
- # now that self.build.allFiles() is nailed down, finish building the |
- # command |
- if self.testChanges: |
- for f in self.build.allFiles(): |
- if f.endswith(".py"): |
- self.command.append("--testmodule=%s" % f) |
- else: |
- self.command.extend(self.tests) |
- log.msg("Trial.start: command is", self.command) |
- |
- # if our slave is too old to understand logfiles=, fetch them |
- # manually. This is a fallback for the Twisted buildbot and some old |
- # buildslaves. |
- self._needToPullTestDotLog = False |
- if self.slaveVersionIsOlderThan("shell", "2.1"): |
- log.msg("Trial: buildslave %s is too old to accept logfiles=" % |
- self.getSlaveName()) |
- log.msg(" falling back to 'cat _trial_temp/test.log' instead") |
- self.logfiles = {} |
- self._needToPullTestDotLog = True |
- |
- ShellCommand.start(self) |
- |
- |
- def commandComplete(self, cmd): |
- if not self._needToPullTestDotLog: |
- return self._gotTestDotLog(cmd) |
- |
- # if the buildslave was too old, pull test.log now |
- catcmd = ["cat", "_trial_temp/test.log"] |
- c2 = RemoteShellCommand(command=catcmd, workdir=self.workdir) |
- loog = self.addLog("test.log") |
- c2.useLog(loog, True, logfileName="stdio") |
- self.cmd = c2 # to allow interrupts |
- d = c2.run(self, self.remote) |
- d.addCallback(lambda res: self._gotTestDotLog(cmd)) |
- return d |
- |
- def rtext(self, fmt='%s'): |
- if self.reactor: |
- rtext = fmt % self.reactor |
- return rtext.replace("reactor", "") |
- return "" |
- |
- def _gotTestDotLog(self, cmd): |
- # figure out all status, then let the various hook functions return |
- # different pieces of it |
- |
- # 'cmd' is the original trial command, so cmd.logs['stdio'] is the |
- # trial output. We don't have access to test.log from here. |
- output = cmd.logs['stdio'].getText() |
- counts = countFailedTests(output) |
- |
- total = counts['total'] |
- failures, errors = counts['failures'], counts['errors'] |
- parsed = (total != None) |
- text = [] |
- text2 = "" |
- |
- if cmd.rc == 0: |
- if parsed: |
- results = SUCCESS |
- if total: |
- text += ["%d %s" % \ |
- (total, |
- total == 1 and "test" or "tests"), |
- "passed"] |
- else: |
- text += ["no tests", "run"] |
- else: |
- results = FAILURE |
- text += ["testlog", "unparseable"] |
- text2 = "tests" |
- else: |
- # something failed |
- results = FAILURE |
- if parsed: |
- text.append("tests") |
- if failures: |
- text.append("%d %s" % \ |
- (failures, |
- failures == 1 and "failure" or "failures")) |
- if errors: |
- text.append("%d %s" % \ |
- (errors, |
- errors == 1 and "error" or "errors")) |
- count = failures + errors |
- text2 = "%d tes%s" % (count, (count == 1 and 't' or 'ts')) |
- else: |
- text += ["tests", "failed"] |
- text2 = "tests" |
- |
- if counts['skips']: |
- text.append("%d %s" % \ |
- (counts['skips'], |
- counts['skips'] == 1 and "skip" or "skips")) |
- if counts['expectedFailures']: |
- text.append("%d %s" % \ |
- (counts['expectedFailures'], |
- counts['expectedFailures'] == 1 and "todo" |
- or "todos")) |
- if 0: # TODO |
- results = WARNINGS |
- if not text2: |
- text2 = "todo" |
- |
- if 0: |
- # ignore unexpectedSuccesses for now, but it should really mark |
- # the build WARNING |
- if counts['unexpectedSuccesses']: |
- text.append("%d surprises" % counts['unexpectedSuccesses']) |
- results = WARNINGS |
- if not text2: |
- text2 = "tests" |
- |
- if self.reactor: |
- text.append(self.rtext('(%s)')) |
- if text2: |
- text2 = "%s %s" % (text2, self.rtext('(%s)')) |
- |
- self.results = results |
- self.text = text |
- self.text2 = [text2] |
- |
- def addTestResult(self, testname, results, text, tlog): |
- if self.reactor is not None: |
- testname = (self.reactor,) + testname |
- tr = builder.TestResult(testname, results, text, logs={'log': tlog}) |
- #self.step_status.build.addTestResult(tr) |
- self.build.build_status.addTestResult(tr) |
- |
- def createSummary(self, loog): |
- output = loog.getText() |
- problems = "" |
- sio = StringIO.StringIO(output) |
- warnings = {} |
- while 1: |
- line = sio.readline() |
- if line == "": |
- break |
- if line.find(" exceptions.DeprecationWarning: ") != -1: |
- # no source |
- warning = line # TODO: consider stripping basedir prefix here |
- warnings[warning] = warnings.get(warning, 0) + 1 |
- elif (line.find(" DeprecationWarning: ") != -1 or |
- line.find(" UserWarning: ") != -1): |
- # next line is the source |
- warning = line + sio.readline() |
- warnings[warning] = warnings.get(warning, 0) + 1 |
- elif line.find("Warning: ") != -1: |
- warning = line |
- warnings[warning] = warnings.get(warning, 0) + 1 |
- |
- if line.find("=" * 60) == 0 or line.find("-" * 60) == 0: |
- problems += line |
- problems += sio.read() |
- break |
- |
- if problems: |
- self.addCompleteLog("problems", problems) |
- # now parse the problems for per-test results |
- pio = StringIO.StringIO(problems) |
- pio.readline() # eat the first separator line |
- testname = None |
- done = False |
- while not done: |
- while 1: |
- line = pio.readline() |
- if line == "": |
- done = True |
- break |
- if line.find("=" * 60) == 0: |
- break |
- if line.find("-" * 60) == 0: |
- # the last case has --- as a separator before the |
- # summary counts are printed |
- done = True |
- break |
- if testname is None: |
- # the first line after the === is like: |
-# EXPECTED FAILURE: testLackOfTB (twisted.test.test_failure.FailureTestCase) |
-# SKIPPED: testRETR (twisted.test.test_ftp.TestFTPServer) |
-# FAILURE: testBatchFile (twisted.conch.test.test_sftp.TestOurServerBatchFile) |
- r = re.search(r'^([^:]+): (\w+) \(([\w\.]+)\)', line) |
- if not r: |
- # TODO: cleanup, if there are no problems, |
- # we hit here |
- continue |
- result, name, case = r.groups() |
- testname = tuple(case.split(".") + [name]) |
- results = {'SKIPPED': SKIPPED, |
- 'EXPECTED FAILURE': SUCCESS, |
- 'UNEXPECTED SUCCESS': WARNINGS, |
- 'FAILURE': FAILURE, |
- 'ERROR': FAILURE, |
- 'SUCCESS': SUCCESS, # not reported |
- }.get(result, WARNINGS) |
- text = result.lower().split() |
- loog = line |
- # the next line is all dashes |
- loog += pio.readline() |
- else: |
- # the rest goes into the log |
- loog += line |
- if testname: |
- self.addTestResult(testname, results, text, loog) |
- testname = None |
- |
- if warnings: |
- lines = warnings.keys() |
- lines.sort() |
- self.addCompleteLog("warnings", "".join(lines)) |
- |
- def evaluateCommand(self, cmd): |
- return self.results |
- |
- def getText(self, cmd, results): |
- return self.text |
- def getText2(self, cmd, results): |
- return self.text2 |
- |
- |
-class ProcessDocs(ShellCommand): |
- """I build all docs. This requires some LaTeX packages to be installed. |
- It will result in the full documentation book (dvi, pdf, etc). |
- |
- """ |
- |
- name = "process-docs" |
- warnOnWarnings = 1 |
- command = ["admin/process-docs"] |
- description = ["processing", "docs"] |
- descriptionDone = ["docs"] |
- # TODO: track output and time |
- |
- def __init__(self, **kwargs): |
- """ |
- @type workdir: string |
- @keyword workdir: the workdir to start from: must be the base of the |
- Twisted tree |
- """ |
- ShellCommand.__init__(self, **kwargs) |
- |
- def createSummary(self, log): |
- output = log.getText() |
- # hlint warnings are of the format: 'WARNING: file:line:col: stuff |
- # latex warnings start with "WARNING: LaTeX Warning: stuff", but |
- # sometimes wrap around to a second line. |
- lines = output.split("\n") |
- warningLines = [] |
- wantNext = False |
- for line in lines: |
- wantThis = wantNext |
- wantNext = False |
- if line.startswith("WARNING: "): |
- wantThis = True |
- wantNext = True |
- if wantThis: |
- warningLines.append(line) |
- |
- if warningLines: |
- self.addCompleteLog("warnings", "\n".join(warningLines) + "\n") |
- self.warnings = len(warningLines) |
- |
- def evaluateCommand(self, cmd): |
- if cmd.rc != 0: |
- return FAILURE |
- if self.warnings: |
- return WARNINGS |
- return SUCCESS |
- |
- def getText(self, cmd, results): |
- if results == SUCCESS: |
- return ["docs", "successful"] |
- if results == WARNINGS: |
- return ["docs", |
- "%d warnin%s" % (self.warnings, |
- self.warnings == 1 and 'g' or 'gs')] |
- if results == FAILURE: |
- return ["docs", "failed"] |
- |
- def getText2(self, cmd, results): |
- if results == WARNINGS: |
- return ["%d do%s" % (self.warnings, |
- self.warnings == 1 and 'c' or 'cs')] |
- return ["docs"] |
- |
- |
- |
-class BuildDebs(ShellCommand): |
- """I build the .deb packages.""" |
- |
- name = "debuild" |
- flunkOnFailure = 1 |
- command = ["debuild", "-uc", "-us"] |
- description = ["building", "debs"] |
- descriptionDone = ["debs"] |
- |
- def __init__(self, **kwargs): |
- """ |
- @type workdir: string |
- @keyword workdir: the workdir to start from (must be the base of the |
- Twisted tree) |
- """ |
- ShellCommand.__init__(self, **kwargs) |
- |
- def commandComplete(self, cmd): |
- errors, warnings = 0, 0 |
- output = cmd.logs['stdio'].getText() |
- summary = "" |
- sio = StringIO.StringIO(output) |
- for line in sio.readlines(): |
- if line.find("E: ") == 0: |
- summary += line |
- errors += 1 |
- if line.find("W: ") == 0: |
- summary += line |
- warnings += 1 |
- if summary: |
- self.addCompleteLog("problems", summary) |
- self.errors = errors |
- self.warnings = warnings |
- |
- def evaluateCommand(self, cmd): |
- if cmd.rc != 0: |
- return FAILURE |
- if self.errors: |
- return FAILURE |
- if self.warnings: |
- return WARNINGS |
- return SUCCESS |
- |
- def getText(self, cmd, results): |
- text = ["debuild"] |
- if cmd.rc != 0: |
- text.append("failed") |
- errors, warnings = self.errors, self.warnings |
- if warnings or errors: |
- text.append("lintian:") |
- if warnings: |
- text.append("%d warnin%s" % (warnings, |
- warnings == 1 and 'g' or 'gs')) |
- if errors: |
- text.append("%d erro%s" % (errors, |
- errors == 1 and 'r' or 'rs')) |
- return text |
- |
- def getText2(self, cmd, results): |
- if cmd.rc != 0: |
- return ["debuild"] |
- if self.errors or self.warnings: |
- return ["%d lintian" % (self.errors + self.warnings)] |
- return [] |
- |
-class RemovePYCs(ShellCommand): |
- name = "remove-.pyc" |
- command = 'find . -name "*.pyc" | xargs rm' |
- description = ["removing", ".pyc", "files"] |
- descriptionDone = ["remove", ".pycs"] |