Skip to content

Commit

Permalink
Test execution of a shell command via PB
Browse files Browse the repository at this point in the history
  • Loading branch information
Dustin J. Mitchell committed Aug 18, 2010
1 parent 7020f05 commit bf5034c
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 4 deletions.
23 changes: 22 additions & 1 deletion master/docs/developer.texinfo
Expand Up @@ -517,7 +517,28 @@ To ping a remote SlaveBuilder, the master calls the @code{print} method.
@heading Building

When a build starts, the msater calls the slave's @code{startBuild} method.
The BuildStep classes will subsequently call the @code{startCommand} method.
Each BuildStep instance will subsequently call the @code{startCommand} method,
passing a reference to itself as the @code{stepRef} parameter. The
@code{startCommand} method returns immediately, and the end of the command is
signalled with a call to a method on the master-side BuildStep object.

@subheading master-side BuildStep methods

@table @code
@item remote_update
Update information about the running command. See below for the format.

@item remote_complete
Signal that the command is complete, either successfully or with a Twisted failure.

@end table

Updates from the slave are a list of individual update elements. Each update
element is, in turn, a list of the form @code{[data, 0]} where the 0 is present
for historical reasons. The data is a dictionary, with keys describing the
contents, e.g., @code{header}, @code{stdout}, or the name of a logfile. If the
key is @code{rc}, then the value is the exit status of the command. No further
updates should be sent after an @code{rc}.

@node Twisted Idioms
@section Twisted Idioms
Expand Down
2 changes: 1 addition & 1 deletion slave/buildslave/bot.py
Expand Up @@ -183,7 +183,7 @@ def ackComplete(self, dummy):

def _ackFailed(self, why, where):
log.msg("SlaveBuilder._ackFailed:", where)
#log.err(why) # we don't really care
log.err(why) # we don't really care


# this is fired by the Deferred attached to each Command
Expand Down
51 changes: 49 additions & 2 deletions slave/buildslave/test/unit/test_bot.py
Expand Up @@ -6,9 +6,10 @@
from zope.interface import implements
import mock

from buildslave.test.util import fakeremote, command
from buildslave.test.fake.runprocess import Expect
import buildslave
from buildslave import bot
from buildslave.test.util import fakeremote

class TestBot(unittest.TestCase):

Expand Down Expand Up @@ -140,7 +141,23 @@ def check(builders):

return d

class TestSlaveBuilder(unittest.TestCase):
class FakeStep(object):
"A fake master-side BuildStep that records its activities."
def __init__(self):
self.finished_d = defer.Deferred()
self.actions = []

def wait_for_finish(self):
return self.finished_d

def remote_update(self, updates):
self.actions.append(["update", updates])

def remote_complete(self, f):
self.actions.append(["complete", f])
self.finished_d.callback(None)

class TestSlaveBuilder(command.CommandTestMixin, unittest.TestCase):

def setUp(self):
self.basedir = os.path.abspath("basedir")
Expand Down Expand Up @@ -182,3 +199,33 @@ def check(_):

def test_startBuild(self):
return self.sb.callRemote("startBuild")

def test_startCommand(self):
# set up a fake step to receive updates
st = FakeStep()

# patch runprocess to handle the 'echo', below
self.patch_runprocess(
Expect([ 'echo', 'hello' ], os.path.join(self.basedir, 'sb', 'workdir'))
+ { 'hdr' : 'headers' } + { 'stdout' : 'hello\n' } + { 'rc' : 0 }
+ 0,
)

d = defer.succeed(None)
def do_start(_):
return self.sb.callRemote("startCommand", fakeremote.FakeRemote(st),
"13", "shell", dict(
command=[ 'echo', 'hello' ],
workdir='workdir',
))
d.addCallback(do_start)
d.addCallback(lambda _ : st.wait_for_finish())
def check(_):
self.assertEqual(st.actions, [
['update', [[{'hdr': 'headers'}, 0]]],
['update', [[{'stdout': 'hello\n'}, 0]]],
['update', [[{'rc': 0}, 0]]],
['complete', None],
])
d.addCallback(check)
return d
3 changes: 3 additions & 0 deletions slave/buildslave/test/util/fakeremote.py
Expand Up @@ -11,3 +11,6 @@ def __init__(self, original, method_prefix="remote_"):
def callRemote(self, meth, *args, **kwargs):
fn = getattr(self.original, self.method_prefix + meth)
return defer.maybeDeferred(fn, *args, **kwargs)

def notifyOnDisconnect(self, what): pass
def dontNotifyOnDisconnect(self, what): pass

0 comments on commit bf5034c

Please sign in to comment.