Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/buildbot/buildbot/test/test_slaves.py
diff options
context:
space:
mode:
Diffstat (limited to 'buildbot/buildbot/test/test_slaves.py')
-rw-r--r--buildbot/buildbot/test/test_slaves.py991
1 files changed, 0 insertions, 991 deletions
diff --git a/buildbot/buildbot/test/test_slaves.py b/buildbot/buildbot/test/test_slaves.py
deleted file mode 100644
index 4005fc6..0000000
--- a/buildbot/buildbot/test/test_slaves.py
+++ /dev/null
@@ -1,991 +0,0 @@
-# -*- test-case-name: buildbot.test.test_slaves -*-
-
-# Portions copyright Canonical Ltd. 2009
-
-from twisted.trial import unittest
-from twisted.internet import defer, reactor
-from twisted.python import log, runtime, failure
-
-from buildbot.buildslave import AbstractLatentBuildSlave
-from buildbot.test.runutils import RunMixin
-from buildbot.sourcestamp import SourceStamp
-from buildbot.process.base import BuildRequest
-from buildbot.status.builder import SUCCESS
-from buildbot.status import mail
-from buildbot.slave import bot
-
-config_1 = """
-from buildbot.process import factory
-from buildbot.steps import dummy
-from buildbot.buildslave import BuildSlave
-s = factory.s
-
-BuildmasterConfig = c = {}
-c['slaves'] = [BuildSlave('bot1', 'sekrit'), BuildSlave('bot2', 'sekrit'),
- BuildSlave('bot3', 'sekrit')]
-c['schedulers'] = []
-c['slavePortnum'] = 0
-c['schedulers'] = []
-
-f1 = factory.BuildFactory([s(dummy.RemoteDummy, timeout=1)])
-f2 = factory.BuildFactory([s(dummy.RemoteDummy, timeout=2)])
-f3 = factory.BuildFactory([s(dummy.RemoteDummy, timeout=3)])
-f4 = factory.BuildFactory([s(dummy.RemoteDummy, timeout=5)])
-
-c['builders'] = [
- {'name': 'b1', 'slavenames': ['bot1','bot2','bot3'],
- 'builddir': 'b1', 'factory': f1},
- ]
-"""
-
-config_2 = config_1 + """
-
-c['builders'] = [
- {'name': 'b1', 'slavenames': ['bot1','bot2','bot3'],
- 'builddir': 'b1', 'factory': f2},
- ]
-
-"""
-
-config_busyness = config_1 + """
-c['builders'] = [
- {'name': 'b1', 'slavenames': ['bot1'],
- 'builddir': 'b1', 'factory': f3},
- {'name': 'b2', 'slavenames': ['bot1'],
- 'builddir': 'b2', 'factory': f4},
- ]
-"""
-
-class Slave(RunMixin, unittest.TestCase):
-
- def setUp(self):
- RunMixin.setUp(self)
- self.master.loadConfig(config_1)
- self.master.startService()
- d = self.connectSlave(["b1"])
- d.addCallback(lambda res: self.connectSlave(["b1"], "bot2"))
- return d
-
- def doBuild(self, buildername):
- br = BuildRequest("forced", SourceStamp(), 'test_builder')
- d = br.waitUntilFinished()
- self.control.getBuilder(buildername).requestBuild(br)
- return d
-
- def testSequence(self):
- # make sure both slaves appear in the list.
- attached_slaves = [c for c in self.master.botmaster.slaves.values()
- if c.slave]
- self.failUnlessEqual(len(attached_slaves), 2)
- b = self.master.botmaster.builders["b1"]
- self.failUnlessEqual(len(b.slaves), 2)
-
- # since the current scheduling algorithm is simple and does not
- # rotate or attempt any sort of load-balancing, two builds in
- # sequence should both use the first slave. This may change later if
- # we move to a more sophisticated scheme.
- b.CHOOSE_SLAVES_RANDOMLY = False
-
- d = self.doBuild("b1")
- d.addCallback(self._testSequence_1)
- return d
- def _testSequence_1(self, res):
- self.failUnlessEqual(res.getResults(), SUCCESS)
- self.failUnlessEqual(res.getSlavename(), "bot1")
-
- d = self.doBuild("b1")
- d.addCallback(self._testSequence_2)
- return d
- def _testSequence_2(self, res):
- self.failUnlessEqual(res.getSlavename(), "bot1")
-
-
- def testSimultaneous(self):
- # make sure we can actually run two builds at the same time
- d1 = self.doBuild("b1")
- d2 = self.doBuild("b1")
- d1.addCallback(self._testSimultaneous_1, d2)
- return d1
- def _testSimultaneous_1(self, res, d2):
- self.failUnlessEqual(res.getResults(), SUCCESS)
- b1_slavename = res.getSlavename()
- d2.addCallback(self._testSimultaneous_2, b1_slavename)
- return d2
- def _testSimultaneous_2(self, res, b1_slavename):
- self.failUnlessEqual(res.getResults(), SUCCESS)
- b2_slavename = res.getSlavename()
- # make sure the two builds were run by different slaves
- slavenames = [b1_slavename, b2_slavename]
- slavenames.sort()
- self.failUnlessEqual(slavenames, ["bot1", "bot2"])
-
- def testFallback1(self):
- # detach the first slave, verify that a build is run using the second
- # slave instead
- d = self.shutdownSlave("bot1", "b1")
- d.addCallback(self._testFallback1_1)
- return d
- def _testFallback1_1(self, res):
- attached_slaves = [c for c in self.master.botmaster.slaves.values()
- if c.slave]
- self.failUnlessEqual(len(attached_slaves), 1)
- self.failUnlessEqual(len(self.master.botmaster.builders["b1"].slaves),
- 1)
- d = self.doBuild("b1")
- d.addCallback(self._testFallback1_2)
- return d
- def _testFallback1_2(self, res):
- self.failUnlessEqual(res.getResults(), SUCCESS)
- self.failUnlessEqual(res.getSlavename(), "bot2")
-
- def testFallback2(self):
- # Disable the first slave, so that a slaveping will timeout. Then
- # start a build, and verify that the non-failing (second) one is
- # claimed for the build, and that the failing one is removed from the
- # list.
-
- b1 = self.master.botmaster.builders["b1"]
- # reduce the ping time so we'll failover faster
- b1.START_BUILD_TIMEOUT = 1
- assert b1.CHOOSE_SLAVES_RANDOMLY
- b1.CHOOSE_SLAVES_RANDOMLY = False
- self.disappearSlave("bot1", "b1", allowReconnect=False)
- d = self.doBuild("b1")
- d.addCallback(self._testFallback2_1)
- return d
- def _testFallback2_1(self, res):
- self.failUnlessEqual(res.getResults(), SUCCESS)
- self.failUnlessEqual(res.getSlavename(), "bot2")
- b1slaves = self.master.botmaster.builders["b1"].slaves
- self.failUnlessEqual(len(b1slaves), 1, "whoops: %s" % (b1slaves,))
- self.failUnlessEqual(b1slaves[0].slave.slavename, "bot2")
-
-
- def notFinished(self, brs):
- # utility method
- builds = brs.getBuilds()
- self.failIf(len(builds) > 1)
- if builds:
- self.failIf(builds[0].isFinished())
-
- def testDontClaimPingingSlave(self):
- # have two slaves connect for the same builder. Do something to the
- # first one so that slavepings are delayed (but do not fail
- # outright).
- timers = []
- self.slaves['bot1'].debugOpts["stallPings"] = (10, timers)
- br = BuildRequest("forced", SourceStamp(), 'test_builder')
- d1 = br.waitUntilFinished()
- self.master.botmaster.builders["b1"].CHOOSE_SLAVES_RANDOMLY = False
- self.control.getBuilder("b1").requestBuild(br)
- s1 = br.status # this is a BuildRequestStatus
- # give it a chance to start pinging
- d2 = defer.Deferred()
- d2.addCallback(self._testDontClaimPingingSlave_1, d1, s1, timers)
- reactor.callLater(1, d2.callback, None)
- return d2
- def _testDontClaimPingingSlave_1(self, res, d1, s1, timers):
- # now the first build is running (waiting on the ping), so start the
- # second build. This should claim the second slave, not the first,
- # because the first is busy doing the ping.
- self.notFinished(s1)
- d3 = self.doBuild("b1")
- d3.addCallback(self._testDontClaimPingingSlave_2, d1, s1, timers)
- return d3
- def _testDontClaimPingingSlave_2(self, res, d1, s1, timers):
- self.failUnlessEqual(res.getSlavename(), "bot2")
- self.notFinished(s1)
- # now let the ping complete
- self.failUnlessEqual(len(timers), 1)
- timers[0].reset(0)
- d1.addCallback(self._testDontClaimPingingSlave_3)
- return d1
- def _testDontClaimPingingSlave_3(self, res):
- self.failUnlessEqual(res.getSlavename(), "bot1")
-
-class FakeLatentBuildSlave(AbstractLatentBuildSlave):
-
- testcase = None
- stop_wait = None
- start_message = None
- stopped = testing_substantiation_timeout = False
-
- def start_instance(self):
- # responsible for starting instance that will try to connect with
- # this master
- # simulate having to do some work.
- d = defer.Deferred()
- if not self.testing_substantiation_timeout:
- reactor.callLater(0, self._start_instance, d)
- return d
-
- def _start_instance(self, d):
- self.testcase.connectOneSlave(self.slavename)
- d.callback(self.start_message)
-
- def stop_instance(self, fast=False):
- # responsible for shutting down instance
- # we're going to emulate dropping off the net.
-
- # simulate this by replacing the slave Broker's .dataReceived method
- # with one that just throws away all data.
- self.fast_stop_request = fast
- if self.slavename not in self.testcase.slaves:
- assert self.testing_substantiation_timeout
- self.stopped = True
- return defer.succeed(None)
- d = defer.Deferred()
- if self.stop_wait is None:
- self._stop_instance(d)
- else:
- reactor.callLater(self.stop_wait, self._stop_instance, d)
- return d
-
- def _stop_instance(self, d):
- try:
- s = self.testcase.slaves.pop(self.slavename)
- except KeyError:
- pass
- else:
- def discard(data):
- pass
- bot = s.getServiceNamed("bot")
- for buildername in self.slavebuilders:
- remote = bot.builders[buildername].remote
- if remote is None:
- continue
- broker = remote.broker
- broker.dataReceived = discard # seal its ears
- broker.transport.write = discard # and take away its voice
- # also discourage it from reconnecting once the connection goes away
- s.bf.continueTrying = False
- # stop the service for cleanliness
- s.stopService()
- d.callback(None)
-
-latent_config = """
-from buildbot.process import factory
-from buildbot.steps import dummy
-from buildbot.buildslave import BuildSlave
-from buildbot.test.test_slaves import FakeLatentBuildSlave
-s = factory.s
-
-BuildmasterConfig = c = {}
-c['slaves'] = [FakeLatentBuildSlave('bot1', 'sekrit',
- ),
- FakeLatentBuildSlave('bot2', 'sekrit',
- ),
- BuildSlave('bot3', 'sekrit')]
-c['schedulers'] = []
-c['slavePortnum'] = 0
-c['schedulers'] = []
-
-f1 = factory.BuildFactory([s(dummy.RemoteDummy, timeout=1)])
-f2 = factory.BuildFactory([s(dummy.RemoteDummy, timeout=2)])
-f3 = factory.BuildFactory([s(dummy.RemoteDummy, timeout=3)])
-f4 = factory.BuildFactory([s(dummy.RemoteDummy, timeout=5)])
-
-c['builders'] = [
- {'name': 'b1', 'slavenames': ['bot1','bot2','bot3'],
- 'builddir': 'b1', 'factory': f1},
- ]
-"""
-
-
-class LatentSlave(RunMixin, unittest.TestCase):
-
- def setUp(self):
- # debugging
- #import twisted.internet.base
- #twisted.internet.base.DelayedCall.debug = True
- # debugging
- RunMixin.setUp(self)
- self.master.loadConfig(latent_config)
- self.master.startService()
- self.bot1 = self.master.botmaster.slaves['bot1']
- self.bot2 = self.master.botmaster.slaves['bot2']
- self.bot3 = self.master.botmaster.slaves['bot3']
- self.bot1.testcase = self
- self.bot2.testcase = self
- self.b1 = self.master.botmaster.builders['b1']
-
- def doBuild(self, buildername):
- br = BuildRequest("forced", SourceStamp(), 'test_builder')
- d = br.waitUntilFinished()
- self.control.getBuilder(buildername).requestBuild(br)
- return d
-
- def testSequence(self):
- # make sure both slaves appear in the builder. This is automatically,
- # without any attaching.
- self.assertEqual(len(self.b1.slaves), 2)
- self.assertEqual(sorted(sb.slave.slavename for sb in self.b1.slaves),
- ['bot1', 'bot2'])
- # These have not substantiated
- self.assertEqual([sb.slave.substantiated for sb in self.b1.slaves],
- [False, False])
- self.assertEqual([sb.slave.slave for sb in self.b1.slaves],
- [None, None])
- # we can mix and match latent slaves and normal slaves. ATM, they
- # are treated identically in terms of selecting slaves.
- d = self.connectSlave(builders=['b1'], slavename='bot3')
- d.addCallback(self._testSequence_1)
- return d
- def _testSequence_1(self, res):
- # now we have all three slaves. Two are latent slaves, and one is a
- # standard slave.
- self.assertEqual(sorted(sb.slave.slavename for sb in self.b1.slaves),
- ['bot1', 'bot2', 'bot3'])
- # Now it's time to try a build on one of the latent slaves,
- # substantiating it.
- # since the current scheduling algorithm is simple and does not
- # rotate or attempt any sort of load-balancing, two builds in
- # sequence should both use the first slave. This may change later if
- # we move to a more sophisticated scheme.
- self.b1.CHOOSE_SLAVES_RANDOMLY = False
-
- self.build_deferred = self.doBuild("b1")
- # now there's an event waiting for the slave to substantiate.
- e = self.b1.builder_status.getEvent(-1)
- self.assertEqual(e.text, ['substantiating'])
- # the substantiation_deferred is an internal stash of a deferred
- # that we'll grab so we can find the point at which the slave is
- # substantiated but the build has not yet started.
- d = self.bot1.substantiation_deferred
- self.assertNotIdentical(d, None)
- d.addCallback(self._testSequence_2)
- return d
- def _testSequence_2(self, res):
- # bot 1 is substantiated.
- self.assertNotIdentical(self.bot1.slave, None)
- self.failUnless(self.bot1.substantiated)
- # the event has announced it's success
- e = self.b1.builder_status.getEvent(-1)
- self.assertEqual(e.text, ['substantiate', 'success'])
- self.assertNotIdentical(e.finished, None)
- # now we'll wait for the build to complete
- d = self.build_deferred
- del self.build_deferred
- d.addCallback(self._testSequence_3)
- return d
- def _testSequence_3(self, res):
- # build was a success!
- self.failUnlessEqual(res.getResults(), SUCCESS)
- self.failUnlessEqual(res.getSlavename(), "bot1")
- # bot1 is substantiated now. bot2 has not.
- self.failUnless(self.bot1.substantiated)
- self.failIf(self.bot2.substantiated)
- # bot1 is waiting a bit to see if there will be another build before
- # it shuts down the instance ("insubstantiates")
- self.build_wait_timer = self.bot1.build_wait_timer
- self.assertNotIdentical(self.build_wait_timer, None)
- self.failUnless(self.build_wait_timer.active())
- self.assertApproximates(
- self.bot1.build_wait_timeout,
- self.build_wait_timer.time - runtime.seconds(),
- 2)
- # now we'll do another build
- d = self.doBuild("b1")
- # the slave is already substantiated, so no event is created
- e = self.b1.builder_status.getEvent(-1)
- self.assertNotEqual(e.text, ['substantiating'])
- # wait for the next build
- d.addCallback(self._testSequence_4)
- return d
- def _testSequence_4(self, res):
- # build was a success!
- self.failUnlessEqual(res.getResults(), SUCCESS)
- self.failUnlessEqual(res.getSlavename(), "bot1")
- # bot1 is still waiting, but with a new timer
- self.assertNotIdentical(self.bot1.build_wait_timer, None)
- self.assertNotIdentical(self.build_wait_timer,
- self.bot1.build_wait_timer)
- self.assertApproximates(
- self.bot1.build_wait_timeout,
- self.bot1.build_wait_timer.time - runtime.seconds(),
- 2)
- del self.build_wait_timer
- # We'll set the timer to fire sooner, and wait for it to fire.
- self.bot1.build_wait_timer.reset(0)
- d = defer.Deferred()
- reactor.callLater(1, d.callback, None)
- d.addCallback(self._testSequence_5)
- return d
- def _testSequence_5(self, res):
- # slave is insubstantiated
- self.assertIdentical(self.bot1.slave, None)
- self.failIf(self.bot1.substantiated)
- # Now we'll start up another build, to show that the shutdown left
- # things in such a state that we can restart.
- d = self.doBuild("b1")
- # the bot can return an informative message on success that the event
- # will render. Let's use a mechanism of our test latent bot to
- # demonstrate that.
- self.bot1.start_message = ['[instance id]', '[start-up time]']
- # here's our event again:
- self.e = self.b1.builder_status.getEvent(-1)
- self.assertEqual(self.e.text, ['substantiating'])
- d.addCallback(self._testSequence_6)
- return d
- def _testSequence_6(self, res):
- # build was a success!
- self.failUnlessEqual(res.getResults(), SUCCESS)
- self.failUnlessEqual(res.getSlavename(), "bot1")
- # the event has announced it's success. (Just imagine that
- # [instance id] and [start-up time] were actually valuable
- # information.)
- e = self.e
- del self.e
- self.assertEqual(
- e.text,
- ['substantiate', 'success', '[instance id]', '[start-up time]'])
- # Now we need to clean up the timer. We could just cancel it, but
- # we'll go through the full dance once more time to show we can.
- # We'll set the timer to fire sooner, and wait for it to fire.
- # Also, we'll set the build_slave to take a little bit longer to shut
- # down, to see that it doesn't affect anything.
- self.bot1.stop_wait = 2
- self.bot1.build_wait_timer.reset(0)
- d = defer.Deferred()
- reactor.callLater(1, d.callback, None)
- d.addCallback(self._testSequence_7)
- return d
- def _testSequence_7(self, res):
- # slave is insubstantiated
- self.assertIdentical(self.bot1.slave, None)
- self.assertNot(self.bot1.substantiated)
- # the remote is still not cleaned out. We'll wait for it.
- d = defer.Deferred()
- reactor.callLater(1, d.callback, None)
- return d
-
- def testNeverSubstantiated(self):
- # When a substantiation is requested, the slave may never appear.
- # This is a serious problem, and recovering from it is not really
- # handled well right now (in part because a way to handle it is not
- # clear). However, at the least, the status event will show a
- # failure, and the slave will be told to insubstantiate, and to be
- # removed from the botmaster as anavailable slave.
- # This tells our test bot to never start, and to not complain about
- # being told to stop without ever starting
- self.bot1.testing_substantiation_timeout = True
- # normally (by default) we have 20 minutes to try and connect to the
- # remote
- self.assertEqual(self.bot1.missing_timeout, 20*60)
- # for testing purposes, we'll put that down to a tenth of a second!
- self.bot1.missing_timeout = 0.1
- # since the current scheduling algorithm is simple and does not
- # rotate or attempt any sort of load-balancing, two builds in
- # sequence should both use the first slave. This may change later if
- # we move to a more sophisticated scheme.
- self.b1.CHOOSE_SLAVES_RANDOMLY = False
- # start a build
- self.build_deferred = self.doBuild('b1')
- # the event tells us we are instantiating, as usual
- e = self.b1.builder_status.getEvent(-1)
- self.assertEqual(e.text, ['substantiating'])
- # we'll see in a moment that the test flag we have to show that the
- # bot was told to insubstantiate has been fired. Here, we just verify
- # that it is ready to be fired.
- self.failIf(self.bot1.stopped)
- # That substantiation is going to fail. Let's wait for it.
- d = self.bot1.substantiation_deferred
- self.assertNotIdentical(d, None)
- d.addCallbacks(self._testNeverSubstantiated_BadSuccess,
- self._testNeverSubstantiated_1)
- return d
- def _testNeverSubstantiated_BadSuccess(self, res):
- self.fail('we should not have succeeded here.')
- def _testNeverSubstantiated_1(self, res):
- # ok, we failed.
- self.assertIdentical(self.bot1.slave, None)
- self.failIf(self.bot1.substantiated)
- self.failUnless(isinstance(res, failure.Failure))
- self.assertIdentical(self.bot1.substantiation_deferred, None)
- # our event informs us of this
- e1 = self.b1.builder_status.getEvent(-3)
- self.assertEqual(e1.text, ['substantiate', 'failed'])
- self.assertNotIdentical(e1.finished, None)
- # the slave is no longer available to build. The events show it...
- e2 = self.b1.builder_status.getEvent(-2)
- self.assertEqual(e2.text, ['removing', 'latent', 'bot1'])
- e3 = self.b1.builder_status.getEvent(-1)
- self.assertEqual(e3.text, ['disconnect', 'bot1'])
- # ...and the builder shows it.
- self.assertEqual(['bot2'],
- [sb.slave.slavename for sb in self.b1.slaves])
- # ideally, we would retry the build, but that infrastructure (which
- # would be used for other situations in the builder as well) does not
- # yet exist. Therefore the build never completes one way or the
- # other, just as if a normal slave detached.
-
- def testServiceStop(self):
- # if the slave has an instance when it is stopped, the slave should
- # be told to shut down.
- self.b1.CHOOSE_SLAVES_RANDOMLY = False
- d = self.doBuild("b1")
- d.addCallback(self._testServiceStop_1)
- return d
- def _testServiceStop_1(self, res):
- # build was a success!
- self.failUnlessEqual(res.getResults(), SUCCESS)
- self.failUnlessEqual(res.getSlavename(), "bot1")
- # bot 1 is substantiated.
- self.assertNotIdentical(self.bot1.slave, None)
- self.failUnless(self.bot1.substantiated)
- # now let's stop the bot.
- d = self.bot1.stopService()
- d.addCallback(self._testServiceStop_2)
- return d
- def _testServiceStop_2(self, res):
- # bot 1 is NOT substantiated.
- self.assertIdentical(self.bot1.slave, None)
- self.failIf(self.bot1.substantiated)
-
- def testPing(self):
- # While a latent slave pings normally when it is substantiated, (as
- # happens behind the scene when a build is request), when
- # it is insubstantial, the ping is a no-op success.
- self.assertIdentical(self.bot1.slave, None)
- self.failIf(self.bot1.substantiated)
- d = self.connectSlave(builders=['b1'], slavename='bot3')
- d.addCallback(self._testPing_1)
- return d
- def _testPing_1(self, res):
- self.assertEqual(sorted(sb.slave.slavename for sb in self.b1.slaves),
- ['bot1', 'bot2', 'bot3'])
- d = self.control.getBuilder('b1').ping()
- d.addCallback(self._testPing_2)
- return d
- def _testPing_2(self, res):
- # all three pings were successful
- self.assert_(res)
- # but neither bot1 not bot2 substantiated.
- self.assertIdentical(self.bot1.slave, None)
- self.failIf(self.bot1.substantiated)
- self.assertIdentical(self.bot2.slave, None)
- self.failIf(self.bot2.substantiated)
-
-
-class SlaveBusyness(RunMixin, unittest.TestCase):
-
- def setUp(self):
- RunMixin.setUp(self)
- self.master.loadConfig(config_busyness)
- self.master.startService()
- d = self.connectSlave(["b1", "b2"])
- return d
-
- def doBuild(self, buildername):
- br = BuildRequest("forced", SourceStamp(), 'test_builder')
- d = br.waitUntilFinished()
- self.control.getBuilder(buildername).requestBuild(br)
- return d
-
- def getRunningBuilds(self):
- return len(self.status.getSlave("bot1").getRunningBuilds())
-
- def testSlaveNotBusy(self):
- self.failUnlessEqual(self.getRunningBuilds(), 0)
- # now kick a build, wait for it to finish, then check again
- d = self.doBuild("b1")
- d.addCallback(self._testSlaveNotBusy_1)
- return d
-
- def _testSlaveNotBusy_1(self, res):
- self.failUnlessEqual(self.getRunningBuilds(), 0)
-
- def testSlaveBusyOneBuild(self):
- d1 = self.doBuild("b1")
- d2 = defer.Deferred()
- reactor.callLater(.5, d2.callback, None)
- d2.addCallback(self._testSlaveBusyOneBuild_1)
- d1.addCallback(self._testSlaveBusyOneBuild_finished_1)
- return defer.DeferredList([d1,d2])
-
- def _testSlaveBusyOneBuild_1(self, res):
- self.failUnlessEqual(self.getRunningBuilds(), 1)
-
- def _testSlaveBusyOneBuild_finished_1(self, res):
- self.failUnlessEqual(self.getRunningBuilds(), 0)
-
- def testSlaveBusyTwoBuilds(self):
- d1 = self.doBuild("b1")
- d2 = self.doBuild("b2")
- d3 = defer.Deferred()
- reactor.callLater(.5, d3.callback, None)
- d3.addCallback(self._testSlaveBusyTwoBuilds_1)
- d1.addCallback(self._testSlaveBusyTwoBuilds_finished_1, d2)
- return defer.DeferredList([d1,d3])
-
- def _testSlaveBusyTwoBuilds_1(self, res):
- self.failUnlessEqual(self.getRunningBuilds(), 2)
-
- def _testSlaveBusyTwoBuilds_finished_1(self, res, d2):
- self.failUnlessEqual(self.getRunningBuilds(), 1)
- d2.addCallback(self._testSlaveBusyTwoBuilds_finished_2)
- return d2
-
- def _testSlaveBusyTwoBuilds_finished_2(self, res):
- self.failUnlessEqual(self.getRunningBuilds(), 0)
-
- def testSlaveDisconnect(self):
- d1 = self.doBuild("b1")
- d2 = defer.Deferred()
- reactor.callLater(.5, d2.callback, None)
- d2.addCallback(self._testSlaveDisconnect_1)
- d1.addCallback(self._testSlaveDisconnect_finished_1)
- return defer.DeferredList([d1, d2])
-
- def _testSlaveDisconnect_1(self, res):
- self.failUnlessEqual(self.getRunningBuilds(), 1)
- return self.shutdownAllSlaves()
-
- def _testSlaveDisconnect_finished_1(self, res):
- self.failUnlessEqual(self.getRunningBuilds(), 0)
-
-config_3 = """
-from buildbot.process import factory
-from buildbot.steps import dummy
-from buildbot.buildslave import BuildSlave
-s = factory.s
-
-BuildmasterConfig = c = {}
-c['slaves'] = [BuildSlave('bot1', 'sekrit')]
-c['schedulers'] = []
-c['slavePortnum'] = 0
-c['schedulers'] = []
-
-f1 = factory.BuildFactory([s(dummy.Wait, handle='one')])
-f2 = factory.BuildFactory([s(dummy.Wait, handle='two')])
-f3 = factory.BuildFactory([s(dummy.Wait, handle='three')])
-
-c['builders'] = [
- {'name': 'b1', 'slavenames': ['bot1'],
- 'builddir': 'b1', 'factory': f1},
- ]
-"""
-
-config_4 = config_3 + """
-c['builders'] = [
- {'name': 'b1', 'slavenames': ['bot1'],
- 'builddir': 'b1', 'factory': f2},
- ]
-"""
-
-config_5 = config_3 + """
-c['builders'] = [
- {'name': 'b1', 'slavenames': ['bot1'],
- 'builddir': 'b1', 'factory': f3},
- ]
-"""
-
-from buildbot.slave.commands import waitCommandRegistry
-
-class Reconfig(RunMixin, unittest.TestCase):
-
- def setUp(self):
- RunMixin.setUp(self)
- self.master.loadConfig(config_3)
- self.master.startService()
- d = self.connectSlave(["b1"])
- return d
-
- def _one_started(self):
- log.msg("testReconfig._one_started")
- self.build1_started = True
- self.d1.callback(None)
- return self.d2
-
- def _two_started(self):
- log.msg("testReconfig._two_started")
- self.build2_started = True
- self.d3.callback(None)
- return self.d4
-
- def _three_started(self):
- log.msg("testReconfig._three_started")
- self.build3_started = True
- self.d5.callback(None)
- return self.d6
-
- def testReconfig(self):
- # reconfiguring a Builder should not interrupt any running Builds. No
- # queued BuildRequests should be lost. The next Build started should
- # use the new process.
- slave1 = self.slaves['bot1']
- bot1 = slave1.getServiceNamed('bot')
- sb1 = bot1.builders['b1']
- self.failUnless(isinstance(sb1, bot.SlaveBuilder))
- self.failUnless(sb1.running)
- b1 = self.master.botmaster.builders['b1']
- self.orig_b1 = b1
-
- self.d1 = d1 = defer.Deferred()
- self.d2 = d2 = defer.Deferred()
- self.d3, self.d4 = defer.Deferred(), defer.Deferred()
- self.d5, self.d6 = defer.Deferred(), defer.Deferred()
- self.build1_started = False
- self.build2_started = False
- self.build3_started = False
- waitCommandRegistry[("one","build1")] = self._one_started
- waitCommandRegistry[("two","build2")] = self._two_started
- waitCommandRegistry[("three","build3")] = self._three_started
-
- # use different branches to make sure these cannot be merged
- br1 = BuildRequest("build1", SourceStamp(branch="1"), 'test_builder')
- b1.submitBuildRequest(br1)
- br2 = BuildRequest("build2", SourceStamp(branch="2"), 'test_builder')
- b1.submitBuildRequest(br2)
- br3 = BuildRequest("build3", SourceStamp(branch="3"), 'test_builder')
- b1.submitBuildRequest(br3)
- self.requests = (br1, br2, br3)
- # all three are now in the queue
-
- # wait until the first one has started
- d1.addCallback(self._testReconfig_2)
- return d1
-
- def _testReconfig_2(self, res):
- log.msg("_testReconfig_2")
- # confirm that it is building
- brs = self.requests[0].status.getBuilds()
- self.failUnlessEqual(len(brs), 1)
- self.build1 = brs[0]
- self.failUnlessEqual(self.build1.getCurrentStep().getName(), "wait")
- # br1 is building, br2 and br3 are in the queue (in that order). Now
- # we reconfigure the Builder.
- self.failUnless(self.build1_started)
- d = self.master.loadConfig(config_4)
- d.addCallback(self._testReconfig_3)
- return d
-
- def _testReconfig_3(self, res):
- log.msg("_testReconfig_3")
- # now check to see that br1 is still building, and that br2 and br3
- # are in the queue of the new builder
- b1 = self.master.botmaster.builders['b1']
- self.failIfIdentical(b1, self.orig_b1)
- self.failIf(self.build1.isFinished())
- self.failUnlessEqual(self.build1.getCurrentStep().getName(), "wait")
- self.failUnlessEqual(len(b1.buildable), 2)
- self.failUnless(self.requests[1] in b1.buildable)
- self.failUnless(self.requests[2] in b1.buildable)
-
- # allow br1 to finish, and make sure its status is delivered normally
- d = self.requests[0].waitUntilFinished()
- d.addCallback(self._testReconfig_4)
- self.d2.callback(None)
- return d
-
- def _testReconfig_4(self, bs):
- log.msg("_testReconfig_4")
- self.failUnlessEqual(bs.getReason(), "build1")
- self.failUnless(bs.isFinished())
- self.failUnlessEqual(bs.getResults(), SUCCESS)
-
- # at this point, the first build has finished, and there is a pending
- # call to start the second build. Once that pending call fires, there
- # is a network roundtrip before the 'wait' RemoteCommand is delivered
- # to the slave. We need to wait for both events to happen before we
- # can check to make sure it is using the correct process. Just wait a
- # full second.
- d = defer.Deferred()
- d.addCallback(self._testReconfig_5)
- reactor.callLater(1, d.callback, None)
- return d
-
- def _testReconfig_5(self, res):
- log.msg("_testReconfig_5")
- # at this point the next build ought to be running
- b1 = self.master.botmaster.builders['b1']
- self.failUnlessEqual(len(b1.buildable), 1)
- self.failUnless(self.requests[2] in b1.buildable)
- self.failUnlessEqual(len(b1.building), 1)
- # and it ought to be using the new process
- self.failUnless(self.build2_started)
-
- # now, while the second build is running, change the config multiple
- # times.
-
- d = self.master.loadConfig(config_3)
- d.addCallback(lambda res: self.master.loadConfig(config_4))
- d.addCallback(lambda res: self.master.loadConfig(config_5))
- def _done(res):
- # then once that's done, allow the second build to finish and
- # wait for it to complete
- da = self.requests[1].waitUntilFinished()
- self.d4.callback(None)
- return da
- d.addCallback(_done)
- def _done2(res):
- # and once *that*'s done, wait another second to let the third
- # build start
- db = defer.Deferred()
- reactor.callLater(1, db.callback, None)
- return db
- d.addCallback(_done2)
- d.addCallback(self._testReconfig_6)
- return d
-
- def _testReconfig_6(self, res):
- log.msg("_testReconfig_6")
- # now check to see that the third build is running
- self.failUnless(self.build3_started)
-
- # we're done
-
-
-
-class Slave2(RunMixin, unittest.TestCase):
-
- revision = 0
-
- def setUp(self):
- RunMixin.setUp(self)
- self.master.loadConfig(config_1)
- self.master.startService()
-
- def doBuild(self, buildername, reason="forced"):
- # we need to prevent these builds from being merged, so we create
- # each of them with a different revision specifier. The revision is
- # ignored because our build process does not have a source checkout
- # step.
- self.revision += 1
- br = BuildRequest(reason, SourceStamp(revision=self.revision),
- 'test_builder')
- d = br.waitUntilFinished()
- self.control.getBuilder(buildername).requestBuild(br)
- return d
-
- def testFirstComeFirstServed(self):
- # submit three builds, then connect a slave which fails the
- # slaveping. The first build will claim the slave, do the slaveping,
- # give up, and re-queue the build. Verify that the build gets
- # re-queued in front of all other builds. This may be tricky, because
- # the other builds may attempt to claim the just-failed slave.
-
- d1 = self.doBuild("b1", "first")
- d2 = self.doBuild("b1", "second")
- #buildable = self.master.botmaster.builders["b1"].buildable
- #print [b.reason for b in buildable]
-
- # specifically, I want the poor build to get precedence over any
- # others that were waiting. To test this, we need more builds than
- # slaves.
-
- # now connect a broken slave. The first build started as soon as it
- # connects, so by the time we get to our _1 method, the ill-fated
- # build has already started.
- d = self.connectSlave(["b1"], opts={"failPingOnce": True})
- d.addCallback(self._testFirstComeFirstServed_1, d1, d2)
- return d
- def _testFirstComeFirstServed_1(self, res, d1, d2):
- # the master has send the slaveping. When this is received, it will
- # fail, causing the master to hang up on the slave. When it
- # reconnects, it should find the first build at the front of the
- # queue. If we simply wait for both builds to complete, then look at
- # the status logs, we should see that the builds ran in the correct
- # order.
-
- d = defer.DeferredList([d1,d2])
- d.addCallback(self._testFirstComeFirstServed_2)
- return d
- def _testFirstComeFirstServed_2(self, res):
- b = self.status.getBuilder("b1")
- builds = b.getBuild(0), b.getBuild(1)
- reasons = [build.getReason() for build in builds]
- self.failUnlessEqual(reasons, ["first", "second"])
-
-config_multi_builders = config_1 + """
-c['builders'] = [
- {'name': 'dummy', 'slavenames': ['bot1','bot2','bot3'],
- 'builddir': 'b1', 'factory': f2},
- {'name': 'dummy2', 'slavenames': ['bot1','bot2','bot3'],
- 'builddir': 'b2', 'factory': f2},
- {'name': 'dummy3', 'slavenames': ['bot1','bot2','bot3'],
- 'builddir': 'b3', 'factory': f2},
- ]
-
-"""
-
-config_mail_missing = config_1 + """
-c['slaves'] = [BuildSlave('bot1', 'sekrit', notify_on_missing='admin',
- missing_timeout=1)]
-c['builders'] = [
- {'name': 'dummy', 'slavenames': ['bot1'],
- 'builddir': 'b1', 'factory': f1},
- ]
-c['projectName'] = 'myproject'
-c['projectURL'] = 'myURL'
-"""
-
-class FakeMailer(mail.MailNotifier):
- def sendMessage(self, m, recipients):
- self.messages.append((m,recipients))
- return defer.succeed(None)
-
-class BuildSlave(RunMixin, unittest.TestCase):
- def test_track_builders(self):
- self.master.loadConfig(config_multi_builders)
- self.master.readConfig = True
- self.master.startService()
- d = self.connectSlave()
-
- def _check(res):
- b = self.master.botmaster.builders['dummy']
- self.failUnless(len(b.slaves) == 1) # just bot1
-
- bs = b.slaves[0].slave
- self.failUnless(len(bs.slavebuilders) == 3)
- self.failUnless(b in [sb.builder for sb in
- bs.slavebuilders.values()])
-
- d.addCallback(_check)
- return d
-
- def test_mail_on_missing(self):
- self.master.loadConfig(config_mail_missing)
- self.master.readConfig = True
- self.master.startService()
- fm = FakeMailer("buildbot@example.org")
- fm.messages = []
- fm.setServiceParent(self.master)
- self.master.statusTargets.append(fm)
-
- d = self.connectSlave()
- d.addCallback(self.stall, 1)
- d.addCallback(lambda res: self.shutdownSlave("bot1", "dummy"))
- def _not_yet(res):
- self.failIf(fm.messages)
- d.addCallback(_not_yet)
- # we reconnect right away, so the timer shouldn't fire
- d.addCallback(lambda res: self.connectSlave())
- d.addCallback(self.stall, 3)
- d.addCallback(_not_yet)
- d.addCallback(lambda res: self.shutdownSlave("bot1", "dummy"))
- d.addCallback(_not_yet)
- # now we let it sit disconnected for long enough for the timer to
- # fire
- d.addCallback(self.stall, 3)
- def _check(res):
- self.failUnlessEqual(len(fm.messages), 1)
- msg,recips = fm.messages[0]
- self.failUnlessEqual(recips, ["admin"])
- body = msg.as_string()
- self.failUnlessIn("To: admin", body)
- self.failUnlessIn("Subject: Buildbot: buildslave bot1 was lost",
- body)
- self.failUnlessIn("From: buildbot@example.org", body)
- self.failUnlessIn("working for 'myproject'", body)
- self.failUnlessIn("has noticed that the buildslave named bot1 went away",
- body)
- self.failUnlessIn("was 'one'", body)
- self.failUnlessIn("myURL", body)
- d.addCallback(_check)
- return d
-
- def stall(self, result, delay=1):
- d = defer.Deferred()
- reactor.callLater(delay, d.callback, result)
- return d