1 # -*- test-case-name: buildbot.test.test_steps -*-
3 # create the BuildStep with a fake .remote instance that logs the
4 # .callRemote invocations and compares them against the expected calls. Then
5 # the test harness should send statusUpdate() messages in with assorted
6 # data, eventually calling remote_complete(). Then we can verify that the
7 # Step's rc was correct, and that the status it was supposed to return
10 # sometimes, .callRemote should raise an exception because of a stale
11 # reference. Sometimes it should errBack with an UnknownCommand failure.
14 # todo: test batched updates, by invoking remote_update(updates) instead of
15 # statusUpdate(update). Also involves interrupted builds.
19 from twisted
.trial
import unittest
20 from twisted
.internet
import reactor
21 from twisted
.internet
.defer
import Deferred
23 from buildbot
.sourcestamp
import SourceStamp
24 from buildbot
.process
import step
, base
, factory
25 from buildbot
.process
.step
import ShellCommand
#, ShellCommands
26 from buildbot
.status
import builder
27 from buildbot
.test
.runutils
import RunMixin
, rmtree
28 from buildbot
.test
.runutils
import makeBuildStep
29 from buildbot
.twcompat
import maybeWait
30 from buildbot
.slave
import commands
33 class MyShellCommand(ShellCommand
):
35 def runCommand(self
, c
):
38 return ShellCommand
.runCommand(self
, c
)
45 class FakeSlaveBuilder
:
46 def getSlaveCommandVersion(self
, command
, oldversion
=None):
53 #self.callRemoteNotifier = None
54 def callRemote(self
, methname
, *args
):
55 event
= ["callRemote", methname
, args
]
56 self
.events
.append(event
)
57 ## if self.callRemoteNotifier:
58 ## reactor.callLater(0, self.callRemoteNotifier, event)
60 self
.deferred
= Deferred()
62 def notifyOnDisconnect(self
, callback
):
64 def dontNotifyOnDisconnect(self
, callback
):
68 class BuildStep(unittest
.TestCase
):
72 self
.builder
= FakeBuilder()
73 self
.builder_status
= builder
.BuilderStatus("fakebuilder")
74 self
.builder_status
.basedir
= "test_steps"
75 self
.builder_status
.nextBuildNumber
= 0
76 os
.mkdir(self
.builder_status
.basedir
)
77 self
.build_status
= self
.builder_status
.newBuild()
78 req
= base
.BuildRequest("reason", SourceStamp())
79 self
.build
= base
.Build([req
])
80 self
.build
.build_status
= self
.build_status
# fake it
81 self
.build
.builder
= self
.builder
82 self
.build
.slavebuilder
= FakeSlaveBuilder()
83 self
.remote
= FakeRemote()
86 def callback(self
, results
):
89 self
.results
= results
91 def errback(self
, failure
):
93 self
.failure
= failure
97 def testShellCommand1(self
):
101 step
.RemoteCommand
.commandCounter
[0] = 3
102 c
= MyShellCommand(workdir
=dir, command
=cmd
, build
=self
.build
,
104 self
.assertEqual(self
.remote
.events
, expectedEvents
)
105 c
.step_status
= self
.build_status
.addStepWithName("myshellcommand")
106 d
= c
.startStep(self
.remote
)
107 self
.failUnless(c
.started
)
109 d
.addCallbacks(self
.callback
, self
.errback
)
110 timeout
= time
.time() + 10
111 while self
.remote
.remoteCalls
== 0:
112 if time
.time() > timeout
:
114 reactor
.iterate(0.01)
115 expectedEvents
.append(["callRemote", "startCommand",
118 {'command': "argle bargle",
125 self
.assertEqual(self
.remote
.events
, expectedEvents
)
127 # we could do self.remote.deferred.errback(UnknownCommand) here. We
128 # could also do .callback(), but generally the master end silently
129 # ignores the slave's ack
131 logs
= c
.step_status
.getLogs()
133 if log
.getName() == "log":
136 rc
.remoteUpdate({'header':
137 "command 'argle bargle' in dir 'murkle'\n\n"})
138 rc
.remoteUpdate({'stdout': "foo\n"})
139 self
.assertEqual(log
.getText(), "foo\n")
140 self
.assertEqual(log
.getTextWithHeaders(),
141 "command 'argle bargle' in dir 'murkle'\n\n"
143 rc
.remoteUpdate({'stderr': "bar\n"})
144 self
.assertEqual(log
.getText(), "foo\nbar\n")
145 self
.assertEqual(log
.getTextWithHeaders(),
146 "command 'argle bargle' in dir 'murkle'\n\n"
148 rc
.remoteUpdate({'rc': 0})
149 self
.assertEqual(rc
.rc
, 0)
152 # that should fire the Deferred
153 timeout
= time
.time() + 10
154 while not self
.finished
:
155 if time
.time() > timeout
:
157 reactor
.iterate(0.01)
158 self
.assertEqual(self
.failed
, 0)
159 self
.assertEqual(self
.results
, 0)
162 class MyObserver(step
.LogObserver
):
164 def outReceived(self
, data
):
165 self
.out
= self
.out
+ data
167 class Steps(unittest
.TestCase
):
168 def testMultipleStepInstances(self
):
170 (step
.CVS
, {'cvsroot': "root", 'cvsmodule': "module"}),
171 (step
.Configure
, {'command': "./configure"}),
172 (step
.Compile
, {'command': "make"}),
173 (step
.Compile
, {'command': "make more"}),
174 (step
.Compile
, {'command': "make evenmore"}),
175 (step
.Test
, {'command': "make test"}),
176 (step
.Test
, {'command': "make testharder"}),
178 f
= factory
.ConfigurableBuildFactory(steps
)
179 req
= base
.BuildRequest("reason", SourceStamp())
180 b
= f
.newBuild([req
])
181 #for s in b.steps: print s.name
183 # test the various methods available to buildsteps
185 def test_getProperty(self
):
186 s
= makeBuildStep("test_steps.Steps.test_getProperty")
187 bs
= s
.step_status
.getBuild()
189 s
.setProperty("prop1", "value1")
190 s
.setProperty("prop2", "value2")
191 self
.failUnlessEqual(s
.getProperty("prop1"), "value1")
192 self
.failUnlessEqual(bs
.getProperty("prop1"), "value1")
193 self
.failUnlessEqual(s
.getProperty("prop2"), "value2")
194 self
.failUnlessEqual(bs
.getProperty("prop2"), "value2")
195 s
.setProperty("prop1", "value1a")
196 self
.failUnlessEqual(s
.getProperty("prop1"), "value1a")
197 self
.failUnlessEqual(bs
.getProperty("prop1"), "value1a")
200 def test_addURL(self
):
201 s
= makeBuildStep("test_steps.Steps.test_addURL")
202 s
.addURL("coverage", "http://coverage.example.org/target")
203 s
.addURL("icon", "http://coverage.example.org/icon.png")
206 expected
= {"coverage": "http://coverage.example.org/target",
207 "icon": "http://coverage.example.org/icon.png",
209 self
.failUnlessEqual(links
, expected
)
211 def test_addLog(self
):
212 s
= makeBuildStep("test_steps.Steps.test_addLog")
213 l
= s
.addLog("newlog")
214 l
.addStdout("some stdout here")
218 self
.failUnlessEqual(len(logs
), 1)
220 self
.failUnlessEqual(l1
.getText(), "some stdout here")
222 def test_addHTMLLog(self
):
223 s
= makeBuildStep("test_steps.Steps.test_addHTMLLog")
224 l
= s
.addHTMLLog("newlog", "some html here")
227 self
.failUnlessEqual(len(logs
), 1)
229 self
.failUnless(isinstance(l1
, builder
.HTMLLogFile
))
230 self
.failUnlessEqual(l1
.getText(), "some html here")
232 def test_addCompleteLog(self
):
233 s
= makeBuildStep("test_steps.Steps.test_addCompleteLog")
234 l
= s
.addCompleteLog("newlog", "some stdout here")
237 self
.failUnlessEqual(len(logs
), 1)
239 self
.failUnlessEqual(l1
.getText(), "some stdout here")
241 def test_addLogObserver(self
):
242 s
= makeBuildStep("test_steps.Steps.test_addLogObserver")
244 o1
,o2
,o3
= MyObserver(), MyObserver(), MyObserver()
246 # add the log before the observer
248 l1
.addStdout("onestuff")
249 s
.addLogObserver("one", o1
)
250 self
.failUnlessEqual(o1
.out
, "onestuff")
251 l1
.addStdout(" morestuff")
252 self
.failUnlessEqual(o1
.out
, "onestuff morestuff")
254 # add the observer before the log
255 s
.addLogObserver("two", o2
)
257 l2
.addStdout("twostuff")
258 self
.failUnlessEqual(o2
.out
, "twostuff")
260 # test more stuff about ShellCommands
262 def test_description(self
):
263 s
= makeBuildStep("test_steps.Steps.test_description.1",
264 step_class
=ShellCommand
,
266 description
=["list", "of", "strings"],
267 descriptionDone
=["another", "list"])
268 self
.failUnlessEqual(s
.description
, ["list", "of", "strings"])
269 self
.failUnlessEqual(s
.descriptionDone
, ["another", "list"])
271 s
= makeBuildStep("test_steps.Steps.test_description.2",
272 step_class
=ShellCommand
,
274 description
="single string",
275 descriptionDone
="another string")
276 self
.failUnlessEqual(s
.description
, ["single string"])
277 self
.failUnlessEqual(s
.descriptionDone
, ["another string"])
279 class VersionCheckingStep(step
.BuildStep
):
281 # give our test a chance to run. It is non-trivial for a buildstep to
282 # claw its way back out to the test case which is currently running.
283 master
= self
.build
.builder
.botmaster
.parent
284 checker
= master
._checker
287 self
.finished(step
.SUCCESS
)
290 from buildbot.process import factory, step
291 from buildbot.test.test_steps import VersionCheckingStep
292 BuildmasterConfig = c = {}
293 f1 = factory.BuildFactory([
294 factory.s(VersionCheckingStep),
296 c['bots'] = [['bot1', 'sekrit']]
299 c['builders'] = [{'name':'quick', 'slavename':'bot1',
300 'builddir': 'quickdir', 'factory': f1}]
301 c['slavePortnum'] = 0
304 class SlaveVersion(RunMixin
, unittest
.TestCase
):
307 self
.master
.loadConfig(version_config
)
308 self
.master
.startService()
309 d
= self
.connectSlave(["quick"])
312 def doBuild(self
, buildername
):
313 br
= base
.BuildRequest("forced", SourceStamp())
314 d
= br
.waitUntilFinished()
315 self
.control
.getBuilder(buildername
).requestBuild(br
)
319 def checkCompare(self
, s
):
320 cver
= commands
.command_version
321 v
= s
.slaveVersion("svn", None)
322 # this insures that we are getting the version correctly
323 self
.failUnlessEqual(s
.slaveVersion("svn", None), cver
)
324 # and that non-existent commands do not provide a version
325 self
.failUnlessEqual(s
.slaveVersion("NOSUCHCOMMAND"), None)
326 # TODO: verify that a <=0.5.0 buildslave (which does not implement
327 # remote_getCommands) handles oldversion= properly. This requires a
328 # mutant slave which does not offer that method.
329 #self.failUnlessEqual(s.slaveVersion("NOSUCHCOMMAND", "old"), "old")
331 # now check the comparison functions
332 self
.failIf(s
.slaveVersionIsOlderThan("svn", cver
))
333 self
.failIf(s
.slaveVersionIsOlderThan("svn", "1.1"))
334 self
.failUnless(s
.slaveVersionIsOlderThan("svn", cver
+ ".1"))
336 self
.failUnlessEqual(s
.getSlaveName(), "bot1")
338 def testCompare(self
):
339 self
.master
._checker
= self
.checkCompare
340 d
= self
.doBuild("quick")