Merge branch 'master' of git://github.com/rbosetti/buildbot
[buildbot.git] / buildbot / test / test_run.py
blob2c95d9212b621771bc72cc150c6d5fe459d9afd0
1 # -*- test-case-name: buildbot.test.test_run -*-
3 from twisted.trial import unittest
4 from twisted.internet import reactor, defer
5 import os
7 from buildbot import master, interfaces
8 from buildbot.sourcestamp import SourceStamp
9 from buildbot.changes import changes
10 from buildbot.status import builder
11 from buildbot.process.base import BuildRequest
13 from buildbot.test.runutils import RunMixin, TestFlagMixin, rmtree
15 config_base = """
16 from buildbot.process import factory
17 from buildbot.steps import dummy
18 from buildbot.buildslave import BuildSlave
19 s = factory.s
21 f1 = factory.QuickBuildFactory('fakerep', 'cvsmodule', configure=None)
23 f2 = factory.BuildFactory([
24 dummy.Dummy(timeout=1),
25 dummy.RemoteDummy(timeout=2),
28 BuildmasterConfig = c = {}
29 c['slaves'] = [BuildSlave('bot1', 'sekrit')]
30 c['schedulers'] = []
31 c['builders'] = []
32 c['builders'].append({'name':'quick', 'slavename':'bot1',
33 'builddir': 'quickdir', 'factory': f1})
34 c['slavePortnum'] = 0
35 """
37 config_run = config_base + """
38 from buildbot.scheduler import Scheduler
39 c['schedulers'] = [Scheduler('quick', None, 120, ['quick'])]
40 """
42 config_can_build = config_base + """
43 from buildbot.buildslave import BuildSlave
44 c['slaves'] = [ BuildSlave('bot1', 'sekrit') ]
46 from buildbot.scheduler import Scheduler
47 c['schedulers'] = [Scheduler('dummy', None, 0.1, ['dummy'])]
49 c['builders'] = [{'name': 'dummy', 'slavename': 'bot1',
50 'builddir': 'dummy1', 'factory': f2}]
51 """
53 config_cant_build = config_can_build + """
54 class MyBuildSlave(BuildSlave):
55 def canStartBuild(self): return False
56 c['slaves'] = [ MyBuildSlave('bot1', 'sekrit') ]
57 """
59 config_concurrency = config_base + """
60 from buildbot.buildslave import BuildSlave
61 c['slaves'] = [ BuildSlave('bot1', 'sekrit', max_builds=1) ]
63 from buildbot.scheduler import Scheduler
64 c['schedulers'] = [Scheduler('dummy', None, 0.1, ['dummy', 'dummy2'])]
66 c['builders'].append({'name': 'dummy', 'slavename': 'bot1',
67 'builddir': 'dummy', 'factory': f2})
68 c['builders'].append({'name': 'dummy2', 'slavename': 'bot1',
69 'builddir': 'dummy2', 'factory': f2})
70 """
72 config_2 = config_base + """
73 c['builders'] = [{'name': 'dummy', 'slavename': 'bot1',
74 'builddir': 'dummy1', 'factory': f2},
75 {'name': 'testdummy', 'slavename': 'bot1',
76 'builddir': 'dummy2', 'factory': f2, 'category': 'test'}]
77 """
79 config_3 = config_2 + """
80 c['builders'].append({'name': 'adummy', 'slavename': 'bot1',
81 'builddir': 'adummy3', 'factory': f2})
82 c['builders'].append({'name': 'bdummy', 'slavename': 'bot1',
83 'builddir': 'adummy4', 'factory': f2,
84 'category': 'test'})
85 """
87 config_4 = config_base + """
88 c['builders'] = [{'name': 'dummy', 'slavename': 'bot1',
89 'builddir': 'dummy', 'factory': f2}]
90 """
92 config_4_newbasedir = config_4 + """
93 c['builders'] = [{'name': 'dummy', 'slavename': 'bot1',
94 'builddir': 'dummy2', 'factory': f2}]
95 """
97 config_4_newbuilder = config_4_newbasedir + """
98 c['builders'].append({'name': 'dummy2', 'slavename': 'bot1',
99 'builddir': 'dummy23', 'factory': f2})
102 class Run(unittest.TestCase):
103 def rmtree(self, d):
104 rmtree(d)
106 def testMaster(self):
107 self.rmtree("basedir")
108 os.mkdir("basedir")
109 m = master.BuildMaster("basedir")
110 m.loadConfig(config_run)
111 m.readConfig = True
112 m.startService()
113 cm = m.change_svc
114 c = changes.Change("bob", ["Makefile", "foo/bar.c"], "changed stuff")
115 cm.addChange(c)
116 # verify that the Scheduler is now waiting
117 s = m.allSchedulers()[0]
118 self.failUnless(s.timer)
119 # halting the service will also stop the timer
120 d = defer.maybeDeferred(m.stopService)
121 return d
123 class CanStartBuild(RunMixin, unittest.TestCase):
124 def rmtree(self, d):
125 rmtree(d)
127 def testCanStartBuild(self):
128 return self.do_test(config_can_build, True)
130 def testCantStartBuild(self):
131 return self.do_test(config_cant_build, False)
133 def do_test(self, config, builder_should_run):
134 self.master.loadConfig(config)
135 self.master.readConfig = True
136 self.master.startService()
137 d = self.connectSlave()
139 # send a change
140 cm = self.master.change_svc
141 c = changes.Change("bob", ["Makefile", "foo/bar.c"], "changed stuff")
142 cm.addChange(c)
144 d.addCallback(self._do_test1, builder_should_run)
146 return d
148 def _do_test1(self, res, builder_should_run):
149 # delay a little bit. Note that relying upon timers is a bit fragile,
150 # in this case we're hoping that our 0.5 second timer will land us
151 # somewhere in the middle of the [0.1s, 3.1s] window (after the 0.1
152 # second Scheduler fires, then during the 3-second build), so that
153 # when we sample BuildSlave.state, we'll see BUILDING (or IDLE if the
154 # slave was told to be unavailable). On a heavily loaded system, our
155 # 0.5 second timer might not actually fire until after the build has
156 # completed. In the long run, it would be good to change this test to
157 # pass under those circumstances too.
158 d = defer.Deferred()
159 reactor.callLater(.5, d.callback, builder_should_run)
160 d.addCallback(self._do_test2)
161 return d
163 def _do_test2(self, builder_should_run):
164 b = self.master.botmaster.builders['dummy']
165 self.failUnless(len(b.slaves) == 1)
167 bs = b.slaves[0]
168 from buildbot.process.builder import IDLE, BUILDING
169 if builder_should_run:
170 self.failUnlessEqual(bs.state, BUILDING)
171 else:
172 self.failUnlessEqual(bs.state, IDLE)
175 class ConcurrencyLimit(RunMixin, unittest.TestCase):
177 def testConcurrencyLimit(self):
178 d = self.master.loadConfig(config_concurrency)
179 d.addCallback(lambda res: self.master.startService())
180 d.addCallback(lambda res: self.connectSlave())
182 def _send(res):
183 # send a change. This will trigger both builders at the same
184 # time, but since they share a slave, the max_builds=1 setting
185 # will insure that only one of the two builds gets to run.
186 cm = self.master.change_svc
187 c = changes.Change("bob", ["Makefile", "foo/bar.c"],
188 "changed stuff")
189 cm.addChange(c)
190 d.addCallback(_send)
192 def _delay(res):
193 d1 = defer.Deferred()
194 reactor.callLater(1, d1.callback, None)
195 # this test depends upon this 1s delay landing us in the middle
196 # of one of the builds.
197 return d1
198 d.addCallback(_delay)
200 def _check(res):
201 builders = [ self.master.botmaster.builders[bn]
202 for bn in ('dummy', 'dummy2') ]
203 for builder in builders:
204 self.failUnless(len(builder.slaves) == 1)
206 from buildbot.process.builder import BUILDING
207 building_bs = [ builder
208 for builder in builders
209 if builder.slaves[0].state == BUILDING ]
210 # assert that only one build is running right now. If the
211 # max_builds= weren't in effect, this would be 2.
212 self.failUnlessEqual(len(building_bs), 1)
213 d.addCallback(_check)
215 return d
218 class Ping(RunMixin, unittest.TestCase):
219 def testPing(self):
220 self.master.loadConfig(config_2)
221 self.master.readConfig = True
222 self.master.startService()
224 d = self.connectSlave()
225 d.addCallback(self._testPing_1)
226 return d
228 def _testPing_1(self, res):
229 d = interfaces.IControl(self.master).getBuilder("dummy").ping(1)
230 d.addCallback(self._testPing_2)
231 return d
233 def _testPing_2(self, res):
234 pass
236 class BuilderNames(unittest.TestCase):
238 def testGetBuilderNames(self):
239 os.mkdir("bnames")
240 m = master.BuildMaster("bnames")
241 s = m.getStatus()
243 m.loadConfig(config_3)
244 m.readConfig = True
246 self.failUnlessEqual(s.getBuilderNames(),
247 ["dummy", "testdummy", "adummy", "bdummy"])
248 self.failUnlessEqual(s.getBuilderNames(categories=['test']),
249 ["testdummy", "bdummy"])
251 class Disconnect(RunMixin, unittest.TestCase):
253 def setUp(self):
254 RunMixin.setUp(self)
256 # verify that disconnecting the slave during a build properly
257 # terminates the build
258 m = self.master
259 s = self.status
260 c = self.control
262 m.loadConfig(config_2)
263 m.readConfig = True
264 m.startService()
266 self.failUnlessEqual(s.getBuilderNames(), ["dummy", "testdummy"])
267 self.s1 = s1 = s.getBuilder("dummy")
268 self.failUnlessEqual(s1.getName(), "dummy")
269 self.failUnlessEqual(s1.getState(), ("offline", []))
270 self.failUnlessEqual(s1.getCurrentBuilds(), [])
271 self.failUnlessEqual(s1.getLastFinishedBuild(), None)
272 self.failUnlessEqual(s1.getBuild(-1), None)
274 d = self.connectSlave()
275 d.addCallback(self._disconnectSetup_1)
276 return d
278 def _disconnectSetup_1(self, res):
279 self.failUnlessEqual(self.s1.getState(), ("idle", []))
282 def verifyDisconnect(self, bs):
283 self.failUnless(bs.isFinished())
285 step1 = bs.getSteps()[0]
286 self.failUnlessEqual(step1.getText(), ["delay", "interrupted"])
287 self.failUnlessEqual(step1.getResults()[0], builder.FAILURE)
289 self.failUnlessEqual(bs.getResults(), builder.FAILURE)
291 def verifyDisconnect2(self, bs):
292 self.failUnless(bs.isFinished())
294 step1 = bs.getSteps()[1]
295 self.failUnlessEqual(step1.getText(), ["remote", "delay", "2 secs",
296 "failed", "slave", "lost"])
297 self.failUnlessEqual(step1.getResults()[0], builder.FAILURE)
299 self.failUnlessEqual(bs.getResults(), builder.FAILURE)
301 def submitBuild(self):
302 ss = SourceStamp()
303 br = BuildRequest("forced build", ss, "dummy")
304 self.control.getBuilder("dummy").requestBuild(br)
305 d = defer.Deferred()
306 def _started(bc):
307 br.unsubscribe(_started)
308 d.callback(bc)
309 br.subscribe(_started)
310 return d
312 def testIdle2(self):
313 # now suppose the slave goes missing
314 self.disappearSlave(allowReconnect=False)
316 # forcing a build will work: the build detect that the slave is no
317 # longer available and will be re-queued. Wait 5 seconds, then check
318 # to make sure the build is still in the 'waiting for a slave' queue.
319 self.control.getBuilder("dummy").original.START_BUILD_TIMEOUT = 1
320 req = BuildRequest("forced build", SourceStamp(), "test_builder")
321 self.failUnlessEqual(req.startCount, 0)
322 self.control.getBuilder("dummy").requestBuild(req)
323 # this should ping the slave, which doesn't respond, and then give up
324 # after a second. The BuildRequest will be re-queued, and its
325 # .startCount will be incremented.
326 d = defer.Deferred()
327 d.addCallback(self._testIdle2_1, req)
328 reactor.callLater(3, d.callback, None)
329 return d
330 testIdle2.timeout = 5
332 def _testIdle2_1(self, res, req):
333 self.failUnlessEqual(req.startCount, 1)
334 cancelled = req.cancel()
335 self.failUnless(cancelled)
338 def testBuild1(self):
339 # this next sequence is timing-dependent. The dummy build takes at
340 # least 3 seconds to complete, and this batch of commands must
341 # complete within that time.
343 d = self.submitBuild()
344 d.addCallback(self._testBuild1_1)
345 return d
347 def _testBuild1_1(self, bc):
348 bs = bc.getStatus()
349 # now kill the slave before it gets to start the first step
350 d = self.shutdownAllSlaves() # dies before it gets started
351 d.addCallback(self._testBuild1_2, bs)
352 return d # TODO: this used to have a 5-second timeout
354 def _testBuild1_2(self, res, bs):
355 # now examine the just-stopped build and make sure it is really
356 # stopped. This is checking for bugs in which the slave-detach gets
357 # missed or causes an exception which prevents the build from being
358 # marked as "finished due to an error".
359 d = bs.waitUntilFinished()
360 d2 = self.master.botmaster.waitUntilBuilderDetached("dummy")
361 dl = defer.DeferredList([d, d2])
362 dl.addCallback(self._testBuild1_3, bs)
363 return dl # TODO: this had a 5-second timeout too
365 def _testBuild1_3(self, res, bs):
366 self.failUnlessEqual(self.s1.getState()[0], "offline")
367 self.verifyDisconnect(bs)
370 def testBuild2(self):
371 # this next sequence is timing-dependent
372 d = self.submitBuild()
373 d.addCallback(self._testBuild2_1)
374 return d
375 testBuild2.timeout = 30
377 def _testBuild2_1(self, bc):
378 bs = bc.getStatus()
379 # shutdown the slave while it's running the first step
380 reactor.callLater(0.5, self.shutdownAllSlaves)
382 d = bs.waitUntilFinished()
383 d.addCallback(self._testBuild2_2, bs)
384 return d
386 def _testBuild2_2(self, res, bs):
387 # we hit here when the build has finished. The builder is still being
388 # torn down, however, so spin for another second to allow the
389 # callLater(0) in Builder.detached to fire.
390 d = defer.Deferred()
391 reactor.callLater(1, d.callback, None)
392 d.addCallback(self._testBuild2_3, bs)
393 return d
395 def _testBuild2_3(self, res, bs):
396 self.failUnlessEqual(self.s1.getState()[0], "offline")
397 self.verifyDisconnect(bs)
400 def testBuild3(self):
401 # this next sequence is timing-dependent
402 d = self.submitBuild()
403 d.addCallback(self._testBuild3_1)
404 return d
405 testBuild3.timeout = 30
407 def _testBuild3_1(self, bc):
408 bs = bc.getStatus()
409 # kill the slave while it's running the first step
410 reactor.callLater(0.5, self.killSlave)
411 d = bs.waitUntilFinished()
412 d.addCallback(self._testBuild3_2, bs)
413 return d
415 def _testBuild3_2(self, res, bs):
416 # the builder is still being torn down, so give it another second
417 d = defer.Deferred()
418 reactor.callLater(1, d.callback, None)
419 d.addCallback(self._testBuild3_3, bs)
420 return d
422 def _testBuild3_3(self, res, bs):
423 self.failUnlessEqual(self.s1.getState()[0], "offline")
424 self.verifyDisconnect(bs)
427 def testBuild4(self):
428 # this next sequence is timing-dependent
429 d = self.submitBuild()
430 d.addCallback(self._testBuild4_1)
431 return d
432 testBuild4.timeout = 30
434 def _testBuild4_1(self, bc):
435 bs = bc.getStatus()
436 # kill the slave while it's running the second (remote) step
437 reactor.callLater(1.5, self.killSlave)
438 d = bs.waitUntilFinished()
439 d.addCallback(self._testBuild4_2, bs)
440 return d
442 def _testBuild4_2(self, res, bs):
443 # at this point, the slave is in the process of being removed, so it
444 # could either be 'idle' or 'offline'. I think there is a
445 # reactor.callLater(0) standing between here and the offline state.
446 #reactor.iterate() # TODO: remove the need for this
448 self.failUnlessEqual(self.s1.getState()[0], "offline")
449 self.verifyDisconnect2(bs)
452 def testInterrupt(self):
453 # this next sequence is timing-dependent
454 d = self.submitBuild()
455 d.addCallback(self._testInterrupt_1)
456 return d
457 testInterrupt.timeout = 30
459 def _testInterrupt_1(self, bc):
460 bs = bc.getStatus()
461 # halt the build while it's running the first step
462 reactor.callLater(0.5, bc.stopBuild, "bang go splat")
463 d = bs.waitUntilFinished()
464 d.addCallback(self._testInterrupt_2, bs)
465 return d
467 def _testInterrupt_2(self, res, bs):
468 self.verifyDisconnect(bs)
471 def testDisappear(self):
472 bc = self.control.getBuilder("dummy")
474 # ping should succeed
475 d = bc.ping(1)
476 d.addCallback(self._testDisappear_1, bc)
477 return d
479 def _testDisappear_1(self, res, bc):
480 self.failUnlessEqual(res, True)
482 # now, before any build is run, make the slave disappear
483 self.disappearSlave(allowReconnect=False)
485 # at this point, a ping to the slave should timeout
486 d = bc.ping(1)
487 d.addCallback(self. _testDisappear_2)
488 return d
489 def _testDisappear_2(self, res):
490 self.failUnlessEqual(res, False)
492 def testDuplicate(self):
493 bc = self.control.getBuilder("dummy")
494 bs = self.status.getBuilder("dummy")
495 ss = bs.getSlaves()[0]
497 self.failUnless(ss.isConnected())
498 self.failUnlessEqual(ss.getAdmin(), "one")
500 # now, before any build is run, make the first slave disappear
501 self.disappearSlave(allowReconnect=False)
503 d = self.master.botmaster.waitUntilBuilderDetached("dummy")
504 # now let the new slave take over
505 self.connectSlave2()
506 d.addCallback(self._testDuplicate_1, ss)
507 return d
508 testDuplicate.timeout = 5
510 def _testDuplicate_1(self, res, ss):
511 d = self.master.botmaster.waitUntilBuilderAttached("dummy")
512 d.addCallback(self._testDuplicate_2, ss)
513 return d
515 def _testDuplicate_2(self, res, ss):
516 self.failUnless(ss.isConnected())
517 self.failUnlessEqual(ss.getAdmin(), "two")
520 class Disconnect2(RunMixin, unittest.TestCase):
522 def setUp(self):
523 RunMixin.setUp(self)
524 # verify that disconnecting the slave during a build properly
525 # terminates the build
526 m = self.master
527 s = self.status
528 c = self.control
530 m.loadConfig(config_2)
531 m.readConfig = True
532 m.startService()
534 self.failUnlessEqual(s.getBuilderNames(), ["dummy", "testdummy"])
535 self.s1 = s1 = s.getBuilder("dummy")
536 self.failUnlessEqual(s1.getName(), "dummy")
537 self.failUnlessEqual(s1.getState(), ("offline", []))
538 self.failUnlessEqual(s1.getCurrentBuilds(), [])
539 self.failUnlessEqual(s1.getLastFinishedBuild(), None)
540 self.failUnlessEqual(s1.getBuild(-1), None)
542 d = self.connectSlaveFastTimeout()
543 d.addCallback(self._setup_disconnect2_1)
544 return d
546 def _setup_disconnect2_1(self, res):
547 self.failUnlessEqual(self.s1.getState(), ("idle", []))
550 def testSlaveTimeout(self):
551 # now suppose the slave goes missing. We want to find out when it
552 # creates a new Broker, so we reach inside and mark it with the
553 # well-known sigil of impending messy death.
554 bd = self.slaves['bot1'].getServiceNamed("bot").builders["dummy"]
555 broker = bd.remote.broker
556 broker.redshirt = 1
558 # make sure the keepalives will keep the connection up
559 d = defer.Deferred()
560 reactor.callLater(5, d.callback, None)
561 d.addCallback(self._testSlaveTimeout_1)
562 return d
563 testSlaveTimeout.timeout = 20
565 def _testSlaveTimeout_1(self, res):
566 bd = self.slaves['bot1'].getServiceNamed("bot").builders["dummy"]
567 if not bd.remote or not hasattr(bd.remote.broker, "redshirt"):
568 self.fail("slave disconnected when it shouldn't have")
570 d = self.master.botmaster.waitUntilBuilderDetached("dummy")
571 # whoops! how careless of me.
572 self.disappearSlave(allowReconnect=True)
573 # the slave will realize the connection is lost within 2 seconds, and
574 # reconnect.
575 d.addCallback(self._testSlaveTimeout_2)
576 return d
578 def _testSlaveTimeout_2(self, res):
579 # the ReconnectingPBClientFactory will attempt a reconnect in two
580 # seconds.
581 d = self.master.botmaster.waitUntilBuilderAttached("dummy")
582 d.addCallback(self._testSlaveTimeout_3)
583 return d
585 def _testSlaveTimeout_3(self, res):
586 # make sure it is a new connection (i.e. a new Broker)
587 bd = self.slaves['bot1'].getServiceNamed("bot").builders["dummy"]
588 self.failUnless(bd.remote, "hey, slave isn't really connected")
589 self.failIf(hasattr(bd.remote.broker, "redshirt"),
590 "hey, slave's Broker is still marked for death")
593 class Basedir(RunMixin, unittest.TestCase):
594 def testChangeBuilddir(self):
595 m = self.master
596 m.loadConfig(config_4)
597 m.readConfig = True
598 m.startService()
600 d = self.connectSlave()
601 d.addCallback(self._testChangeBuilddir_1)
602 return d
604 def _testChangeBuilddir_1(self, res):
605 self.bot = bot = self.slaves['bot1'].bot
606 self.builder = builder = bot.builders.get("dummy")
607 self.failUnless(builder)
608 self.failUnlessEqual(builder.builddir, "dummy")
609 self.failUnlessEqual(builder.basedir,
610 os.path.join("slavebase-bot1", "dummy"))
612 d = self.master.loadConfig(config_4_newbasedir)
613 d.addCallback(self._testChangeBuilddir_2)
614 return d
616 def _testChangeBuilddir_2(self, res):
617 bot = self.bot
618 # this does NOT cause the builder to be replaced
619 builder = bot.builders.get("dummy")
620 self.failUnless(builder)
621 self.failUnlessIdentical(self.builder, builder)
622 # the basedir should be updated
623 self.failUnlessEqual(builder.builddir, "dummy2")
624 self.failUnlessEqual(builder.basedir,
625 os.path.join("slavebase-bot1", "dummy2"))
627 # add a new builder, which causes the basedir list to be reloaded
628 d = self.master.loadConfig(config_4_newbuilder)
629 return d
631 class Triggers(RunMixin, TestFlagMixin, unittest.TestCase):
632 config_trigger = config_base + """
633 from buildbot.scheduler import Triggerable, Scheduler
634 from buildbot.steps.trigger import Trigger
635 from buildbot.steps.dummy import Dummy
636 from buildbot.test.runutils import SetTestFlagStep
637 c['schedulers'] = [
638 Scheduler('triggerer', None, 0.1, ['triggerer']),
639 Triggerable('triggeree', ['triggeree'])
641 triggerer = factory.BuildFactory()
642 triggerer.addSteps([
643 SetTestFlagStep(flagname='triggerer_started'),
644 Trigger(flunkOnFailure=True, @ARGS@),
645 SetTestFlagStep(flagname='triggerer_finished'),
647 triggeree = factory.BuildFactory([
648 s(SetTestFlagStep, flagname='triggeree_started'),
649 s(@DUMMYCLASS@),
650 s(SetTestFlagStep, flagname='triggeree_finished'),
652 c['builders'] = [{'name': 'triggerer', 'slavename': 'bot1',
653 'builddir': 'triggerer', 'factory': triggerer},
654 {'name': 'triggeree', 'slavename': 'bot1',
655 'builddir': 'triggeree', 'factory': triggeree}]
658 def mkConfig(self, args, dummyclass="Dummy"):
659 return self.config_trigger.replace("@ARGS@", args).replace("@DUMMYCLASS@", dummyclass)
661 def setupTest(self, args, dummyclass, checkFn):
662 self.clearFlags()
663 m = self.master
664 m.loadConfig(self.mkConfig(args, dummyclass))
665 m.readConfig = True
666 m.startService()
668 c = changes.Change("bob", ["Makefile", "foo/bar.c"], "changed stuff")
669 m.change_svc.addChange(c)
671 d = self.connectSlave(builders=['triggerer', 'triggeree'])
672 d.addCallback(self.startTimer, 0.5, checkFn)
673 return d
675 def startTimer(self, res, time, next_fn):
676 d = defer.Deferred()
677 reactor.callLater(time, d.callback, None)
678 d.addCallback(next_fn)
679 return d
681 def testTriggerBuild(self):
682 return self.setupTest("schedulerNames=['triggeree']",
683 "Dummy",
684 self._checkTriggerBuild)
686 def _checkTriggerBuild(self, res):
687 self.failIfFlagNotSet('triggerer_started')
688 self.failIfFlagNotSet('triggeree_started')
689 self.failIfFlagSet('triggeree_finished')
690 self.failIfFlagNotSet('triggerer_finished')
692 def testTriggerBuildWait(self):
693 return self.setupTest("schedulerNames=['triggeree'], waitForFinish=1",
694 "Dummy",
695 self._checkTriggerBuildWait)
697 def _checkTriggerBuildWait(self, res):
698 self.failIfFlagNotSet('triggerer_started')
699 self.failIfFlagNotSet('triggeree_started')
700 self.failIfFlagSet('triggeree_finished')
701 self.failIfFlagSet('triggerer_finished')
703 class PropertyPropagation(RunMixin, TestFlagMixin, unittest.TestCase):
704 def setupTest(self, config, builders, checkFn):
705 self.clearFlags()
706 m = self.master
707 m.loadConfig(config)
708 m.readConfig = True
709 m.startService()
711 c = changes.Change("bob", ["Makefile", "foo/bar.c"], "changed stuff")
712 m.change_svc.addChange(c)
714 d = self.connectSlave(builders=builders)
715 d.addCallback(self.startTimer, 0.5, checkFn)
716 return d
718 def startTimer(self, res, time, next_fn):
719 d = defer.Deferred()
720 reactor.callLater(time, d.callback, None)
721 d.addCallback(next_fn)
722 return d
724 config_schprop = config_base + """
725 from buildbot.scheduler import Scheduler
726 from buildbot.steps.dummy import Dummy
727 from buildbot.test.runutils import SetTestFlagStep
728 from buildbot.process.properties import WithProperties
729 c['schedulers'] = [
730 Scheduler('mysched', None, 0.1, ['flagcolor'], properties={'color':'red'}),
732 factory = factory.BuildFactory([
733 s(SetTestFlagStep, flagname='testresult',
734 value=WithProperties('color=%(color)s sched=%(scheduler)s')),
736 c['builders'] = [{'name': 'flagcolor', 'slavename': 'bot1',
737 'builddir': 'test', 'factory': factory},
741 def testScheduler(self):
742 def _check(res):
743 self.failUnlessEqual(self.getFlag('testresult'),
744 'color=red sched=mysched')
745 return self.setupTest(self.config_schprop, ['flagcolor'], _check)
747 config_slaveprop = config_base + """
748 from buildbot.scheduler import Scheduler
749 from buildbot.steps.dummy import Dummy
750 from buildbot.test.runutils import SetTestFlagStep
751 from buildbot.process.properties import WithProperties
752 c['schedulers'] = [
753 Scheduler('mysched', None, 0.1, ['flagcolor'])
755 c['slaves'] = [BuildSlave('bot1', 'sekrit', properties={'color':'orange'})]
756 factory = factory.BuildFactory([
757 s(SetTestFlagStep, flagname='testresult',
758 value=WithProperties('color=%(color)s slavename=%(slavename)s')),
760 c['builders'] = [{'name': 'flagcolor', 'slavename': 'bot1',
761 'builddir': 'test', 'factory': factory},
764 def testSlave(self):
765 def _check(res):
766 self.failUnlessEqual(self.getFlag('testresult'),
767 'color=orange slavename=bot1')
768 return self.setupTest(self.config_slaveprop, ['flagcolor'], _check)
770 config_trigger = config_base + """
771 from buildbot.scheduler import Triggerable, Scheduler
772 from buildbot.steps.trigger import Trigger
773 from buildbot.steps.dummy import Dummy
774 from buildbot.test.runutils import SetTestFlagStep
775 from buildbot.process.properties import WithProperties
776 c['schedulers'] = [
777 Scheduler('triggerer', None, 0.1, ['triggerer'],
778 properties={'color':'mauve', 'pls_trigger':'triggeree'}),
779 Triggerable('triggeree', ['triggeree'], properties={'color':'invisible'})
781 triggerer = factory.BuildFactory([
782 s(SetTestFlagStep, flagname='testresult', value='wrongone'),
783 s(Trigger, flunkOnFailure=True,
784 schedulerNames=[WithProperties('%(pls_trigger)s')],
785 set_properties={'color' : WithProperties('%(color)s')}),
786 s(SetTestFlagStep, flagname='testresult', value='triggered'),
788 triggeree = factory.BuildFactory([
789 s(SetTestFlagStep, flagname='testresult',
790 value=WithProperties('sched=%(scheduler)s color=%(color)s')),
792 c['builders'] = [{'name': 'triggerer', 'slavename': 'bot1',
793 'builddir': 'triggerer', 'factory': triggerer},
794 {'name': 'triggeree', 'slavename': 'bot1',
795 'builddir': 'triggeree', 'factory': triggeree}]
797 def testTrigger(self):
798 def _check(res):
799 self.failUnlessEqual(self.getFlag('testresult'),
800 'sched=triggeree color=mauve')
801 return self.setupTest(self.config_trigger,
802 ['triggerer', 'triggeree'], _check)
805 config_test_flag = config_base + """
806 from buildbot.scheduler import Scheduler
807 c['schedulers'] = [Scheduler('quick', None, 0.1, ['dummy'])]
809 from buildbot.test.runutils import SetTestFlagStep
810 f3 = factory.BuildFactory([
811 s(SetTestFlagStep, flagname='foo', value='bar'),
814 c['builders'] = [{'name': 'dummy', 'slavename': 'bot1',
815 'builddir': 'dummy', 'factory': f3}]
818 class TestFlag(RunMixin, TestFlagMixin, unittest.TestCase):
819 """Test for the TestFlag functionality in runutils"""
820 def testTestFlag(self):
821 m = self.master
822 m.loadConfig(config_test_flag)
823 m.readConfig = True
824 m.startService()
826 c = changes.Change("bob", ["Makefile", "foo/bar.c"], "changed stuff")
827 m.change_svc.addChange(c)
829 d = self.connectSlave()
830 d.addCallback(self._testTestFlag_1)
831 return d
833 def _testTestFlag_1(self, res):
834 d = defer.Deferred()
835 reactor.callLater(0.5, d.callback, None)
836 d.addCallback(self._testTestFlag_2)
837 return d
839 def _testTestFlag_2(self, res):
840 self.failUnlessEqual(self.getFlag('foo'), 'bar')
842 # TODO: test everything, from Change submission to Scheduler to Build to
843 # Status. Use all the status types. Specifically I want to catch recurrences
844 # of the bug where I forgot to make Waterfall inherit from StatusReceiver
845 # such that buildSetSubmitted failed.
847 config_test_builder = config_base + """
848 from buildbot.scheduler import Scheduler
849 c['schedulers'] = [Scheduler('quick', 'dummy', 0.1, ['dummy']),
850 Scheduler('quick2', 'dummy2', 0.1, ['dummy2']),
851 Scheduler('quick3', 'dummy3', 0.1, ['dummy3'])]
853 from buildbot.steps.shell import ShellCommand
854 f3 = factory.BuildFactory([
855 s(ShellCommand, command="sleep 3", env={'blah':'blah'})
858 c['builders'] = [{'name': 'dummy', 'slavename': 'bot1', 'env': {'foo':'bar'},
859 'builddir': 'dummy', 'factory': f3}]
861 c['builders'].append({'name': 'dummy2', 'slavename': 'bot1',
862 'env': {'blah':'bar'}, 'builddir': 'dummy2',
863 'factory': f3})
865 f4 = factory.BuildFactory([
866 s(ShellCommand, command="sleep 3")
869 c['builders'].append({'name': 'dummy3', 'slavename': 'bot1',
870 'env': {'blah':'bar'}, 'builddir': 'dummy3',
871 'factory': f4})
874 class TestBuilder(RunMixin, unittest.TestCase):
875 def setUp(self):
876 RunMixin.setUp(self)
877 self.master.loadConfig(config_test_builder)
878 self.master.readConfig = True
879 self.master.startService()
880 self.connectSlave(builders=["dummy", "dummy2", "dummy3"])
882 def doBuilderEnvTest(self, branch, cb):
883 c = changes.Change("bob", ["Makefile", "foo/bar.c"], "changed",
884 branch=branch)
885 self.master.change_svc.addChange(c)
887 d = defer.Deferred()
888 reactor.callLater(0.5, d.callback, None)
889 d.addCallback(cb)
891 return d
893 def testBuilderEnv(self):
894 return self.doBuilderEnvTest("dummy", self._testBuilderEnv1)
896 def _testBuilderEnv1(self, res):
897 b = self.master.botmaster.builders['dummy']
898 build = b.building[0]
899 s = build.currentStep
900 self.failUnless('foo' in s.cmd.args['env'])
901 self.failUnlessEqual('bar', s.cmd.args['env']['foo'])
902 self.failUnless('blah' in s.cmd.args['env'])
903 self.failUnlessEqual('blah', s.cmd.args['env']['blah'])
905 def testBuilderEnvOverride(self):
906 return self.doBuilderEnvTest("dummy2", self._testBuilderEnvOverride1)
908 def _testBuilderEnvOverride1(self, res):
909 b = self.master.botmaster.builders['dummy2']
910 build = b.building[0]
911 s = build.currentStep
912 self.failUnless('blah' in s.cmd.args['env'])
913 self.failUnlessEqual('blah', s.cmd.args['env']['blah'])
915 def testBuilderNoStepEnv(self):
916 return self.doBuilderEnvTest("dummy3", self._testBuilderNoStepEnv1)
918 def _testBuilderNoStepEnv1(self, res):
919 b = self.master.botmaster.builders['dummy3']
920 build = b.building[0]
921 s = build.currentStep
922 self.failUnless('blah' in s.cmd.args['env'])
923 self.failUnlessEqual('bar', s.cmd.args['env']['blah'])
925 class SchedulerWatchers(RunMixin, TestFlagMixin, unittest.TestCase):
926 config_watchable = config_base + """
927 from buildbot.scheduler import AnyBranchScheduler
928 from buildbot.steps.dummy import Dummy
929 from buildbot.test.runutils import setTestFlag, SetTestFlagStep
930 s = AnyBranchScheduler(
931 name='abs',
932 branches=None,
933 treeStableTimer=0,
934 builderNames=['a', 'b'])
935 c['schedulers'] = [ s ]
937 # count the number of times a success watcher is called
938 numCalls = [ 0 ]
939 def watcher(ss):
940 numCalls[0] += 1
941 setTestFlag("numCalls", numCalls[0])
942 s.subscribeToSuccessfulBuilds(watcher)
944 f = factory.BuildFactory()
945 f.addStep(Dummy(timeout=0))
946 c['builders'] = [{'name': 'a', 'slavename': 'bot1',
947 'builddir': 'a', 'factory': f},
948 {'name': 'b', 'slavename': 'bot1',
949 'builddir': 'b', 'factory': f}]
952 def testWatchers(self):
953 self.clearFlags()
954 m = self.master
955 m.loadConfig(self.config_watchable)
956 m.readConfig = True
957 m.startService()
959 c = changes.Change("bob", ["Makefile", "foo/bar.c"], "changed stuff")
960 m.change_svc.addChange(c)
962 d = self.connectSlave(builders=['a', 'b'])
964 def pause(res):
965 d = defer.Deferred()
966 reactor.callLater(1, d.callback, res)
967 return d
968 d.addCallback(pause)
970 def checkFn(res):
971 self.failUnlessEqual(self.getFlag('numCalls'), 1)
972 d.addCallback(checkFn)
973 return d
975 config_priority = """
976 from buildbot.process import factory
977 from buildbot.steps import dummy
978 from buildbot.buildslave import BuildSlave
979 s = factory.s
981 from buildbot.steps.shell import ShellCommand
982 f1 = factory.BuildFactory([
983 s(ShellCommand, command="sleep 3", env={'blah':'blah'})
986 BuildmasterConfig = c = {}
987 c['slaves'] = [BuildSlave('bot1', 'sekrit', max_builds=1)]
988 c['schedulers'] = []
989 c['builders'] = []
990 c['builders'].append({'name':'quick1', 'slavename':'bot1', 'builddir': 'quickdir1', 'factory': f1})
991 c['builders'].append({'name':'quick2', 'slavename':'bot1', 'builddir': 'quickdir2', 'factory': f1})
992 c['slavePortnum'] = 0
995 class BuildPrioritization(RunMixin, unittest.TestCase):
996 def rmtree(self, d):
997 rmtree(d)
999 def testPriority(self):
1000 self.rmtree("basedir")
1001 os.mkdir("basedir")
1002 self.master.loadConfig(config_priority)
1003 self.master.readConfig = True
1004 self.master.startService()
1006 d = self.connectSlave(builders=['quick1', 'quick2'])
1007 d.addCallback(self._connected)
1009 return d
1011 def _connected(self, *args):
1012 # Our fake source stamp
1013 # we override canBeMergedWith so that our requests don't get merged together
1014 ss = SourceStamp()
1015 ss.canBeMergedWith = lambda x: False
1017 # Send one request to tie up the slave before sending future requests
1018 req0 = BuildRequest("reason", ss, "test_builder")
1019 self.master.botmaster.builders['quick1'].submitBuildRequest(req0)
1021 # Send 10 requests to alternating builders
1022 # We fudge the submittedAt field after submitting since they're all
1023 # getting submitted so close together according to time.time()
1024 # and all we care about is what order they're run in.
1025 reqs = []
1026 self.finish_order = []
1027 for i in range(10):
1028 req = BuildRequest(str(i), ss, "test_builder")
1029 j = i % 2 + 1
1030 self.master.botmaster.builders['quick%i' % j].submitBuildRequest(req)
1031 req.submittedAt = i
1032 # Keep track of what order the builds finished in
1033 def append(item, arg):
1034 self.finish_order.append(item)
1035 req.waitUntilFinished().addCallback(append, req)
1036 reqs.append(req.waitUntilFinished())
1038 dl = defer.DeferredList(reqs)
1039 dl.addCallback(self._all_finished)
1041 # After our first build finishes, we should wait for the rest to finish
1042 d = req0.waitUntilFinished()
1043 d.addCallback(lambda x: dl)
1044 return d
1046 def _all_finished(self, *args):
1047 # The builds should have finished in proper order
1048 self.failUnlessEqual([int(b.reason) for b in self.finish_order], range(10))