1 # -*- test-case-name: buildbot.test.test_run -*-
3 from twisted
.trial
import unittest
4 from twisted
.internet
import reactor
, defer
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
16 from buildbot.process import factory
17 from buildbot.steps import dummy
18 from buildbot.buildslave import BuildSlave
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')]
32 c['builders'].append({'name':'quick', 'slavename':'bot1',
33 'builddir': 'quickdir', 'factory': f1})
37 config_run
= config_base
+ """
38 from buildbot.scheduler import Scheduler
39 c['schedulers'] = [Scheduler('quick', None, 120, ['quick'])]
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}]
53 config_cant_build
= config_can_build
+ """
54 class MyBuildSlave(BuildSlave):
55 def canStartBuild(self): return False
56 c['slaves'] = [ MyBuildSlave('bot1', 'sekrit') ]
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})
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'}]
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,
87 config_4
= config_base
+ """
88 c['builders'] = [{'name': 'dummy', 'slavename': 'bot1',
89 'builddir': 'dummy', 'factory': f2}]
92 config_4_newbasedir
= config_4
+ """
93 c['builders'] = [{'name': 'dummy', 'slavename': 'bot1',
94 'builddir': 'dummy2', 'factory': f2}]
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
):
106 def testMaster(self
):
107 self
.rmtree("basedir")
109 m
= master
.BuildMaster("basedir")
110 m
.loadConfig(config_run
)
114 c
= changes
.Change("bob", ["Makefile", "foo/bar.c"], "changed stuff")
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
)
123 class CanStartBuild(RunMixin
, unittest
.TestCase
):
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()
140 cm
= self
.master
.change_svc
141 c
= changes
.Change("bob", ["Makefile", "foo/bar.c"], "changed stuff")
144 d
.addCallback(self
._do
_test
1, builder_should_run
)
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.
159 reactor
.callLater(.5, d
.callback
, builder_should_run
)
160 d
.addCallback(self
._do
_test
2)
163 def _do_test2(self
, builder_should_run
):
164 b
= self
.master
.botmaster
.builders
['dummy']
165 self
.failUnless(len(b
.slaves
) == 1)
168 from buildbot
.process
.builder
import IDLE
, BUILDING
169 if builder_should_run
:
170 self
.failUnlessEqual(bs
.state
, BUILDING
)
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())
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"],
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.
198 d
.addCallback(_delay
)
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
)
218 class Ping(RunMixin
, unittest
.TestCase
):
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)
228 def _testPing_1(self
, res
):
229 d
= interfaces
.IControl(self
.master
).getBuilder("dummy").ping(1)
230 d
.addCallback(self
._testPing
_2)
233 def _testPing_2(self
, res
):
236 class BuilderNames(unittest
.TestCase
):
238 def testGetBuilderNames(self
):
240 m
= master
.BuildMaster("bnames")
243 m
.loadConfig(config_3
)
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
):
256 # verify that disconnecting the slave during a build properly
257 # terminates the build
262 m
.loadConfig(config_2
)
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)
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
):
303 br
= BuildRequest("forced build", ss
, "dummy")
304 self
.control
.getBuilder("dummy").requestBuild(br
)
307 br
.unsubscribe(_started
)
309 br
.subscribe(_started
)
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.
327 d
.addCallback(self
._testIdle
2_1, req
)
328 reactor
.callLater(3, d
.callback
, None)
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
._testBuild
1_1)
347 def _testBuild1_1(self
, bc
):
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
._testBuild
1_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
._testBuild
1_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
._testBuild
2_1)
375 testBuild2
.timeout
= 30
377 def _testBuild2_1(self
, bc
):
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
._testBuild
2_2, bs
)
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.
391 reactor
.callLater(1, d
.callback
, None)
392 d
.addCallback(self
._testBuild
2_3, bs
)
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
._testBuild
3_1)
405 testBuild3
.timeout
= 30
407 def _testBuild3_1(self
, bc
):
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
._testBuild
3_2, bs
)
415 def _testBuild3_2(self
, res
, bs
):
416 # the builder is still being torn down, so give it another second
418 reactor
.callLater(1, d
.callback
, None)
419 d
.addCallback(self
._testBuild
3_3, bs
)
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
._testBuild
4_1)
432 testBuild4
.timeout
= 30
434 def _testBuild4_1(self
, bc
):
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
._testBuild
4_2, bs
)
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)
457 testInterrupt
.timeout
= 30
459 def _testInterrupt_1(self
, bc
):
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
)
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
476 d
.addCallback(self
._testDisappear
_1, bc
)
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
487 d
.addCallback(self
. _testDisappear_2
)
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
506 d
.addCallback(self
._testDuplicate
_1, ss
)
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
)
515 def _testDuplicate_2(self
, res
, ss
):
516 self
.failUnless(ss
.isConnected())
517 self
.failUnlessEqual(ss
.getAdmin(), "two")
520 class Disconnect2(RunMixin
, unittest
.TestCase
):
524 # verify that disconnecting the slave during a build properly
525 # terminates the build
530 m
.loadConfig(config_2
)
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
_disconnect
2_1)
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
558 # make sure the keepalives will keep the connection up
560 reactor
.callLater(5, d
.callback
, None)
561 d
.addCallback(self
._testSlaveTimeout
_1)
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
575 d
.addCallback(self
._testSlaveTimeout
_2)
578 def _testSlaveTimeout_2(self
, res
):
579 # the ReconnectingPBClientFactory will attempt a reconnect in two
581 d
= self
.master
.botmaster
.waitUntilBuilderAttached("dummy")
582 d
.addCallback(self
._testSlaveTimeout
_3)
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
):
596 m
.loadConfig(config_4
)
600 d
= self
.connectSlave()
601 d
.addCallback(self
._testChangeBuilddir
_1)
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)
616 def _testChangeBuilddir_2(self
, res
):
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
)
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
638 Scheduler('triggerer', None, 0.1, ['triggerer']),
639 Triggerable('triggeree', ['triggeree'])
641 triggerer = factory.BuildFactory()
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'),
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
):
664 m
.loadConfig(self
.mkConfig(args
, dummyclass
))
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
)
675 def startTimer(self
, res
, time
, next_fn
):
677 reactor
.callLater(time
, d
.callback
, None)
678 d
.addCallback(next_fn
)
681 def testTriggerBuild(self
):
682 return self
.setupTest("schedulerNames=['triggeree']",
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",
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
):
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
)
718 def startTimer(self
, res
, time
, next_fn
):
720 reactor
.callLater(time
, d
.callback
, None)
721 d
.addCallback(next_fn
)
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
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
):
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
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},
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
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
):
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
):
822 m
.loadConfig(config_test_flag
)
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)
833 def _testTestFlag_1(self
, res
):
835 reactor
.callLater(0.5, d
.callback
, None)
836 d
.addCallback(self
._testTestFlag
_2)
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',
865 f4 = factory.BuildFactory([
866 s(ShellCommand, command="sleep 3")
869 c['builders'].append({'name': 'dummy3', 'slavename': 'bot1',
870 'env': {'blah':'bar'}, 'builddir': 'dummy3',
874 class TestBuilder(RunMixin
, unittest
.TestCase
):
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",
885 self
.master
.change_svc
.addChange(c
)
888 reactor
.callLater(0.5, d
.callback
, None)
893 def testBuilderEnv(self
):
894 return self
.doBuilderEnvTest("dummy", self
._testBuilderEnv
1)
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
._testBuilderEnvOverride
1)
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
._testBuilderNoStepEnv
1)
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(
934 builderNames=['a', 'b'])
935 c['schedulers'] = [ s ]
937 # count the number of times a success watcher is called
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
):
955 m
.loadConfig(self
.config_watchable
)
959 c
= changes
.Change("bob", ["Makefile", "foo/bar.c"], "changed stuff")
960 m
.change_svc
.addChange(c
)
962 d
= self
.connectSlave(builders
=['a', 'b'])
966 reactor
.callLater(1, d
.callback
, res
)
971 self
.failUnlessEqual(self
.getFlag('numCalls'), 1)
972 d
.addCallback(checkFn
)
975 config_priority
= """
976 from buildbot.process import factory
977 from buildbot.steps import dummy
978 from buildbot.buildslave import BuildSlave
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)]
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
):
999 def testPriority(self
):
1000 self
.rmtree("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
)
1011 def _connected(self
, *args
):
1012 # Our fake source stamp
1013 # we override canBeMergedWith so that our requests don't get merged together
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.
1026 self
.finish_order
= []
1028 req
= BuildRequest(str(i
), ss
, "test_builder")
1030 self
.master
.botmaster
.builders
['quick%i' % j
].submitBuildRequest(req
)
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
)
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))