Skip to content

Commit

Permalink
(closes buildbot#610) add subunit log watcher
Browse files Browse the repository at this point in the history
  • Loading branch information
rbtcollins authored and maruel committed Oct 30, 2009
1 parent c95253d commit 7e458ed
Show file tree
Hide file tree
Showing 4 changed files with 128 additions and 1 deletion.
1 change: 1 addition & 0 deletions CREDITS
Expand Up @@ -68,6 +68,7 @@ Phil Thompson
Philipp Frauenfelder
Rene Rivera
Riccardo Magliocchetti
Robert Collins
Rob Helmer
Roch Gadsdon
Roy Rapoport
Expand Down
64 changes: 64 additions & 0 deletions buildbot/process/subunitlogobserver.py
@@ -0,0 +1,64 @@
# -*- test-case-name: buildbot.test.test_buildstep -*-

from unittest import TestResult

from buildbot.steps.shell import ShellCommand
from buildbot.process import buildstep


class DiscardStream:
"""A trivial thunk used to discard passthrough content."""

def write(self, bytes):
pass


class SubunitLogObserver(buildstep.LogLineObserver, TestResult):
"""Observe a log that may contain subunit output.
This class extends TestResult to receive the callbacks from the subunit
parser in the most direct fashion.
"""

def __init__(self):
buildstep.LogLineObserver.__init__(self)
TestResult.__init__(self)
try:
from subunit import TestProtocolServer
except ImportError:
raise ImportError("subunit is not importable, but is required for "
"SubunitLogObserver support.")
self.protocol = TestProtocolServer(self, DiscardStream())

def outLineReceived(self, line):
"""Process a received line."""
# Impedance mismatch: subunit wants lines, observers get lines-no\n
self.protocol.lineReceived(line + '\n')

def startTest(self, test):
TestResult.startTest(self, test)
self.step.setProgress('tests', self.testsRun)

def addError(self, test, err):
TestResult.addError(self, test, err)
self.issue()

def addFailure(self, test, err):
TestResult.addFailure(self, test, err)
self.issue()

def issue(self):
"""An issue - failing, erroring etc test."""
self.step.setProgress('tests failed', len(self.failures) + len(self.errors))


class SubunitShellCommand(ShellCommand):
"""A ShellCommand that sniffs subunit output.
Ideally not needed, and thus here to be trivially deleted. See issue #615
"""

def __init__(self, *args, **kwargs):
ShellCommand.__init__(self, *args, **kwargs)
self.addLogObserver('stdio', SubunitLogObserver())
self.progressMetrics = self.progressMetrics + ('tests', 'tests failed')
45 changes: 45 additions & 0 deletions buildbot/test/test_buildstep.py
Expand Up @@ -7,6 +7,7 @@
from buildbot import interfaces
from buildbot.process import buildstep
from buildbot.process import mtrlogobserver
from buildbot.process import subunitlogobserver

# have to subclass LogObserver in order to test it, since the default
# implementations of outReceived() and errReceived() do nothing
Expand Down Expand Up @@ -270,3 +271,47 @@ def testRepr(self):
testval = repr(rsc)
rsc = buildstep.RemoteShellCommand('.', 'make')
testval = repr(rsc)


class SubunitLogObserver(subunitlogobserver.SubunitLogObserver):
"""Subclassed to allow testing behaviour without a real buildstep."""

def __init__(self):
subunitlogobserver.SubunitLogObserver.__init__(self)
self.testFails = []
self.testWarnLists = []
# We don't have a buildstep in self.step.
# So we'll just install ourself there, so we can check the call of
# setProgress().
# Same for self.step.step_status.setText()
self.step = self
self.step_status = self
self.progresses = []
self.text = []

def setProgress(self, type, value):
self.progresses.append((type, value))

def setText(self, text):
self.text = text


class SubunitLogObserverTests(ObserverTestCase):
observer_cls = SubunitLogObserver

def test1(self):
self._logStdout("""
test: foo
success: foo
test: bar
failure: bar [
string
]
test: gam
skip: gam
test: quux
xfail: quux
""")
self.assertEqual(self.observer.progresses,
[('tests', 1), ('tests', 2), ('tests failed', 1), ('tests', 3),
('tests failed', 2), ('tests', 4)])
19 changes: 18 additions & 1 deletion docs/buildbot.texinfo
Expand Up @@ -225,6 +225,7 @@ Simple ShellCommand Subclasses
* PerlModuleTest::
* Testing with mysql-test-run::
* SetProperty::
* SubunitShellCommand::
Python BuildSteps
Expand Down Expand Up @@ -5747,6 +5748,7 @@ file less verbose.
* PerlModuleTest::
* Testing with mysql-test-run::
* SetProperty::
* SubunitShellCommand::
@end menu

@node Configure, Compile, Simple ShellCommand Subclasses, Simple ShellCommand Subclasses
Expand Down Expand Up @@ -5969,7 +5971,7 @@ The subdirectory in which to look for server error log files. Defaults to
@end table


@node SetProperty, , Testing with mysql-test-run, Simple ShellCommand Subclasses
@node SetProperty, SubunitShellCommand , Testing with mysql-test-run, Simple ShellCommand Subclasses
@subsubsection SetProperty

@bsindex buildbot.steps.shell.SetProperty
Expand Down Expand Up @@ -6012,6 +6014,21 @@ f.addStep(SetProperty(
Then @code{my_extract} will see @code{stdout="output1\noutput2\n"}
and @code{stderr="error\n"}.

@node SubunitShellCommand, , SetProperty, Simple ShellCommand Subclasses
@subsubsection SubunitShellCommand

@bsindex buildbot.process.subunitlogger.SubunitShellCommand

This buildstep is similar to ShellCommand, except that it runs the log content
through a subunit filter to extract test and failure counts.

@example
f.addStep(SubunitShellCommand(command="make test"))
@end example

This runs @code{make test} and filters it through subunit. The 'tests' and
'test failed' progress metrics will now accumulate test data from the test run.

@node Python BuildSteps, Transferring Files, Simple ShellCommand Subclasses, Build Steps
@subsection Python BuildSteps

Expand Down

0 comments on commit 7e458ed

Please sign in to comment.