Skip to content

Commit

Permalink
Add 'summary' handling for new-style steps
Browse files Browse the repository at this point in the history
  • Loading branch information
djmitche committed Apr 28, 2014
1 parent 8a45f15 commit 10b439b
Show file tree
Hide file tree
Showing 4 changed files with 122 additions and 2 deletions.
27 changes: 27 additions & 0 deletions master/buildbot/process/buildstep.py
Expand Up @@ -39,6 +39,7 @@
from buildbot.status.results import SUCCESS
from buildbot.status.results import WARNINGS
from buildbot.status.results import worst_status
from buildbot.util import debounce
from buildbot.util import flatten
from buildbot.util.eventual import eventually

Expand Down Expand Up @@ -221,6 +222,32 @@ def setStateStrings(self, strings):
self._step_status.old_setText2(strings)
return defer.succeed(None)

def getCurrentSummary(self):
return u'running'

def getResultSummary(self):
return {}

@debounce.method(wait=1)
@defer.inlineCallbacks
def updateSummary(self):
assert self.isNewStyle(), "updateSummary is a new-style step method"
if self._step_status.isFinished():
resultSummary = yield self.getResultSummary()
stepResult = resultSummary.get('step', u'finished')
assert isinstance(stepResult, unicode), \
"step result must be unicode"
self._step_status.setText([stepResult])
buildResult = resultSummary.get('build', None)
assert buildResult is None or isinstance(buildResult, unicode), \
"build result must be unicode"
self._step_status.setText2([buildResult] if buildResult else [])
else:
stepSummary = yield self.getCurrentSummary()
assert isinstance(stepSummary, unicode), \
"step summary must be unicode"
self._step_status.setText([stepSummary])

@defer.inlineCallbacks
def startStep(self, remote):
self.remote = remote
Expand Down
55 changes: 55 additions & 0 deletions master/buildbot/test/unit/test_process_buildstep.py
Expand Up @@ -34,6 +34,7 @@
from buildbot.test.util import steps
from buildbot.util.eventual import eventually
from twisted.internet import defer
from twisted.internet import task
from twisted.python import log
from twisted.trial import unittest

Expand Down Expand Up @@ -356,6 +357,60 @@ def test_newStyleReturnsNone(self):
yield self.runStep()
self.assertEqual(len(self.flushLoggedErrors(AssertionError)), 1)

def setup_summary_test(self):
self.clock = task.Clock()
self.patch(NewStyleStep, 'getCurrentSummary',
lambda self: defer.succeed(u'C'))
self.patch(NewStyleStep, 'getResultSummary',
lambda self: defer.succeed({'step': u'CS', 'build': u'CB'}))
step = NewStyleStep()
step.updateSummary._reactor = self.clock
return step

def test_updateSummary_running(self):
step = self.setup_summary_test()
step._step_status = mock.Mock()
step._step_status.isFinished = lambda: False
step.updateSummary()
self.clock.advance(1)
step._step_status.setText.assert_called_with(['C'])
step._step_status.setText2.assert_not_called()

def test_updateSummary_running_not_unicode(self):
step = self.setup_summary_test()
step.getCurrentSummary = lambda: 'bytestring'
step._step_status = mock.Mock()
step._step_status.isFinished = lambda: False
step.updateSummary()
self.clock.advance(1)
self.assertEqual(len(self.flushLoggedErrors(AssertionError)), 1)

def test_updateSummary_finished(self):
step = self.setup_summary_test()
step._step_status = mock.Mock()
step._step_status.isFinished = lambda: True
step.updateSummary()
self.clock.advance(1)
step._step_status.setText.assert_called_with(['CS'])
step._step_status.setText2.assert_called_with(['CB'])

def test_updateSummary_finished_empty_dict(self):
step = self.setup_summary_test()
step.getResultSummary = lambda: {}
step._step_status = mock.Mock()
step._step_status.isFinished = lambda: True
step.updateSummary()
self.clock.advance(1)
step._step_status.setText.assert_called_with(['finished'])
step._step_status.setText2.assert_called_with([])

def test_updateSummary_old_style(self):
step = OldStyleStep()
step.updateSummary._reactor = clock = task.Clock()
step.updateSummary()
clock.advance(1)
self.assertEqual(len(self.flushLoggedErrors(AssertionError)), 1)


class TestLoggingBuildStep(unittest.TestCase):

Expand Down
37 changes: 36 additions & 1 deletion master/docs/developer/cls-buildsteps.rst
Expand Up @@ -218,7 +218,37 @@ BuildStep
If false, then the step is running. If true, the step is not running, or has been interrupted.

This method provides a convenient way to summarize the status of the step for status displays:
A step can indicate its up-to-the-moment status using a short summary string.
These methods allow step subclasses to produce such summaries.

.. py:method:: updateSummary()
Update the summary, calling :py:meth:`getCurrentSummary` or :py:meth:`getResultSummary` as appropriate.
New-style build steps should call this method any time the summary may have changed.
This method is debounced, so even calling it for every log line is acceptable.

.. py:method:: getCurrentSummary()
:returns: unicode, optionally via Deferred

Returns a short string summarizing the step's current status.
This method is only called while the step is running.

New-style build steps should override this method to provide a more interesting summary than the default ``u"running"``.

.. py:method:: getResultSummary()
:returns: dictionary, optionally via Deferred

Returns a dictionary containing status information.
The dictionary can have keys ``step`` and ``build``, each with unicode values.
The ``step`` key gives a summary for display with the step, while the ``build`` key gives a summary for display with the entire build.
The latter should be used sparingly, and include only information that the user would find relevant for the entire build, such as a number of test failures.
Either or both keys can be omitted.

This method is only called while the step is finished.

New-style build steps should override this method to provide a more interesting summary than the default ``u"running"``.

.. py:method:: describe(done=False)
Expand All @@ -234,6 +264,11 @@ BuildStep
In relatively rare circumstances, steps are described before they have started.
Ideally, unit tests should be used to ensure that this method is resilient.

.. note::

This method is not called for new-style steps.
Instead, override :py:meth:`getCurrentSummary` and :py:meth:`getResultSummary`.

Build steps have statistics, a simple key/value store of data which can later be aggregated over all steps in a build.
Note that statistics are not preserved after a build is complete.

Expand Down
5 changes: 4 additions & 1 deletion master/docs/manual/new-style-steps.rst
Expand Up @@ -137,7 +137,10 @@ Removed Methods
+++++++++++++++

The ``self.step_status.setText`` and ``setText2`` methods have been removed.
Replace them with asynchronous calls to :py:class:`buildbot.process.buildstep.BuildStep.setStateStrings`.
Similarly, the ``_describe`` and ``describe`` methods are not used in new-style steps.
Steps no longer set their status directly.
Instead, call :py:meth:`buildbot.process.buildstep.BuildStep.updateSummary` whenever the status may have changed.
This method will call :py:meth:`~buildbot.process.buildstep.BuildStep.getCurrentSummary` or :py:meth:`~buildbot.process.buildstep.BuildStep.getResultSummary` as appropriate and update displays of the step's status.

Support for statistics has been moved to the ``BuildStep`` and ``Build`` objects.
Calls to ``self.step_status.setStatistic`` should be rewritten as ``self.setStatistic``.

0 comments on commit 10b439b

Please sign in to comment.