| OLD | NEW |
| (Empty) |
| 1 # -*- test-case-name: buildbot.test.test_run -*- | |
| 2 | |
| 3 from twisted.trial import unittest | |
| 4 from twisted.internet import reactor, defer | |
| 5 import os | |
| 6 | |
| 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 | |
| 12 | |
| 13 from buildbot.test.runutils import RunMixin, TestFlagMixin, rmtree | |
| 14 | |
| 15 config_base = """ | |
| 16 from buildbot.process import factory | |
| 17 from buildbot.steps import dummy | |
| 18 from buildbot.buildslave import BuildSlave | |
| 19 from buildbot.config import BuilderConfig | |
| 20 s = factory.s | |
| 21 | |
| 22 f1 = factory.QuickBuildFactory('fakerep', 'cvsmodule', configure=None) | |
| 23 | |
| 24 f2 = factory.BuildFactory([ | |
| 25 dummy.Dummy(timeout=1), | |
| 26 dummy.RemoteDummy(timeout=2), | |
| 27 ]) | |
| 28 | |
| 29 BuildmasterConfig = c = {} | |
| 30 c['slaves'] = [BuildSlave('bot1', 'sekrit')] | |
| 31 c['schedulers'] = [] | |
| 32 c['builders'] = [ | |
| 33 BuilderConfig(name='quick', slavename='bot1', factory=f1, | |
| 34 builddir='quickdir', slavebuilddir='slavequickdir'), | |
| 35 ] | |
| 36 c['slavePortnum'] = 0 | |
| 37 """ | |
| 38 | |
| 39 config_run = config_base + """ | |
| 40 from buildbot.scheduler import Scheduler | |
| 41 c['schedulers'] = [Scheduler('quick', None, 120, ['quick'])] | |
| 42 """ | |
| 43 | |
| 44 config_can_build = config_base + """ | |
| 45 from buildbot.buildslave import BuildSlave | |
| 46 c['slaves'] = [ BuildSlave('bot1', 'sekrit') ] | |
| 47 | |
| 48 from buildbot.scheduler import Scheduler | |
| 49 c['schedulers'] = [Scheduler('dummy', None, 0.1, ['dummy'])] | |
| 50 | |
| 51 c['builders'] = [ | |
| 52 BuilderConfig(name='dummy', slavename='bot1', | |
| 53 factory=f2, builddir='dummy1'), | |
| 54 ] | |
| 55 """ | |
| 56 | |
| 57 config_cant_build = config_can_build + """ | |
| 58 class MyBuildSlave(BuildSlave): | |
| 59 def canStartBuild(self): return False | |
| 60 c['slaves'] = [ MyBuildSlave('bot1', 'sekrit') ] | |
| 61 """ | |
| 62 | |
| 63 config_concurrency = config_base + """ | |
| 64 from buildbot.buildslave import BuildSlave | |
| 65 c['slaves'] = [ BuildSlave('bot1', 'sekrit', max_builds=1) ] | |
| 66 | |
| 67 from buildbot.scheduler import Scheduler | |
| 68 c['schedulers'] = [Scheduler('dummy', None, 0.1, ['dummy', 'dummy2'])] | |
| 69 | |
| 70 c['builders'] = c['builders'] + [ | |
| 71 BuilderConfig(name='dummy', slavename='bot1', factory=f2), | |
| 72 BuilderConfig(name='dummy2', slavename='bot1', factory=f2), | |
| 73 ] | |
| 74 """ | |
| 75 | |
| 76 config_2 = config_base + """ | |
| 77 c['builders'] = [ | |
| 78 BuilderConfig(name='dummy', slavename='bot1', | |
| 79 builddir='dummy1', factory=f2), | |
| 80 BuilderConfig(name='test dummy', slavename='bot1', | |
| 81 factory=f2, category='test'), | |
| 82 ] | |
| 83 """ | |
| 84 | |
| 85 config_3 = config_2 + """ | |
| 86 c['builders'] = c['builders'] + [ | |
| 87 BuilderConfig(name='adummy', slavename='bot1', | |
| 88 builddir='adummy3', factory=f2), | |
| 89 BuilderConfig(name='bdummy', slavename='bot1', | |
| 90 builddir='adummy4', factory=f2, | |
| 91 category='test'), | |
| 92 ] | |
| 93 """ | |
| 94 | |
| 95 config_4 = config_base + """ | |
| 96 c['builders'] = [ | |
| 97 BuilderConfig(name='dummy', slavename='bot1', | |
| 98 slavebuilddir='sdummy', factory=f2), | |
| 99 ] | |
| 100 """ | |
| 101 | |
| 102 config_4_newbasedir = config_4 + """ | |
| 103 c['builders'] = [ | |
| 104 BuilderConfig(name='dummy', slavename='bot1', | |
| 105 builddir='dummy2', factory=f2), | |
| 106 ] | |
| 107 """ | |
| 108 | |
| 109 config_4_newbuilder = config_4_newbasedir + """ | |
| 110 c['builders'] = c['builders'] + [ | |
| 111 BuilderConfig(name='dummy2', slavename='bot1', | |
| 112 builddir='dummy23', factory=f2), | |
| 113 ] | |
| 114 """ | |
| 115 | |
| 116 class Run(unittest.TestCase): | |
| 117 def rmtree(self, d): | |
| 118 rmtree(d) | |
| 119 | |
| 120 def testMaster(self): | |
| 121 self.rmtree("basedir") | |
| 122 os.mkdir("basedir") | |
| 123 m = master.BuildMaster("basedir") | |
| 124 m.loadConfig(config_run) | |
| 125 m.readConfig = True | |
| 126 m.startService() | |
| 127 cm = m.change_svc | |
| 128 c = changes.Change("bob", ["Makefile", "foo/bar.c"], "changed stuff") | |
| 129 cm.addChange(c) | |
| 130 # verify that the Scheduler is now waiting | |
| 131 s = m.allSchedulers()[0] | |
| 132 self.failUnless(s.timer) | |
| 133 # halting the service will also stop the timer | |
| 134 d = defer.maybeDeferred(m.stopService) | |
| 135 return d | |
| 136 | |
| 137 class CanStartBuild(RunMixin, unittest.TestCase): | |
| 138 def rmtree(self, d): | |
| 139 rmtree(d) | |
| 140 | |
| 141 def testCanStartBuild(self): | |
| 142 return self.do_test(config_can_build, True) | |
| 143 | |
| 144 def testCantStartBuild(self): | |
| 145 return self.do_test(config_cant_build, False) | |
| 146 | |
| 147 def do_test(self, config, builder_should_run): | |
| 148 self.master.loadConfig(config) | |
| 149 self.master.readConfig = True | |
| 150 self.master.startService() | |
| 151 d = self.connectSlave() | |
| 152 | |
| 153 # send a change | |
| 154 cm = self.master.change_svc | |
| 155 c = changes.Change("bob", ["Makefile", "foo/bar.c"], "changed stuff") | |
| 156 cm.addChange(c) | |
| 157 | |
| 158 d.addCallback(self._do_test1, builder_should_run) | |
| 159 | |
| 160 return d | |
| 161 | |
| 162 def _do_test1(self, res, builder_should_run): | |
| 163 # delay a little bit. Note that relying upon timers is a bit fragile, | |
| 164 # in this case we're hoping that our 0.5 second timer will land us | |
| 165 # somewhere in the middle of the [0.1s, 3.1s] window (after the 0.1 | |
| 166 # second Scheduler fires, then during the 3-second build), so that | |
| 167 # when we sample BuildSlave.state, we'll see BUILDING (or IDLE if the | |
| 168 # slave was told to be unavailable). On a heavily loaded system, our | |
| 169 # 0.5 second timer might not actually fire until after the build has | |
| 170 # completed. In the long run, it would be good to change this test to | |
| 171 # pass under those circumstances too. | |
| 172 d = defer.Deferred() | |
| 173 reactor.callLater(.5, d.callback, builder_should_run) | |
| 174 d.addCallback(self._do_test2) | |
| 175 return d | |
| 176 | |
| 177 def _do_test2(self, builder_should_run): | |
| 178 b = self.master.botmaster.builders['dummy'] | |
| 179 self.failUnless(len(b.slaves) == 1) | |
| 180 | |
| 181 bs = b.slaves[0] | |
| 182 from buildbot.process.builder import IDLE, BUILDING | |
| 183 if builder_should_run: | |
| 184 self.failUnlessEqual(bs.state, BUILDING) | |
| 185 else: | |
| 186 self.failUnlessEqual(bs.state, IDLE) | |
| 187 | |
| 188 | |
| 189 class ConcurrencyLimit(RunMixin, unittest.TestCase): | |
| 190 | |
| 191 def testConcurrencyLimit(self): | |
| 192 d = self.master.loadConfig(config_concurrency) | |
| 193 d.addCallback(lambda res: self.master.startService()) | |
| 194 d.addCallback(lambda res: self.connectSlave()) | |
| 195 | |
| 196 def _send(res): | |
| 197 # send a change. This will trigger both builders at the same | |
| 198 # time, but since they share a slave, the max_builds=1 setting | |
| 199 # will insure that only one of the two builds gets to run. | |
| 200 cm = self.master.change_svc | |
| 201 c = changes.Change("bob", ["Makefile", "foo/bar.c"], | |
| 202 "changed stuff") | |
| 203 cm.addChange(c) | |
| 204 d.addCallback(_send) | |
| 205 | |
| 206 def _delay(res): | |
| 207 d1 = defer.Deferred() | |
| 208 reactor.callLater(1, d1.callback, None) | |
| 209 # this test depends upon this 1s delay landing us in the middle | |
| 210 # of one of the builds. | |
| 211 return d1 | |
| 212 d.addCallback(_delay) | |
| 213 | |
| 214 def _check(res): | |
| 215 builders = [ self.master.botmaster.builders[bn] | |
| 216 for bn in ('dummy', 'dummy2') ] | |
| 217 for builder in builders: | |
| 218 self.failUnless(len(builder.slaves) == 1) | |
| 219 | |
| 220 from buildbot.process.builder import BUILDING | |
| 221 building_bs = [ builder | |
| 222 for builder in builders | |
| 223 if builder.slaves[0].state == BUILDING ] | |
| 224 # assert that only one build is running right now. If the | |
| 225 # max_builds= weren't in effect, this would be 2. | |
| 226 self.failUnlessEqual(len(building_bs), 1) | |
| 227 d.addCallback(_check) | |
| 228 | |
| 229 return d | |
| 230 | |
| 231 | |
| 232 class Ping(RunMixin, unittest.TestCase): | |
| 233 def testPing(self): | |
| 234 self.master.loadConfig(config_2) | |
| 235 self.master.readConfig = True | |
| 236 self.master.startService() | |
| 237 | |
| 238 d = self.connectSlave() | |
| 239 d.addCallback(self._testPing_1) | |
| 240 return d | |
| 241 | |
| 242 def _testPing_1(self, res): | |
| 243 d = interfaces.IControl(self.master).getBuilder("dummy").ping() | |
| 244 d.addCallback(self._testPing_2) | |
| 245 return d | |
| 246 | |
| 247 def _testPing_2(self, res): | |
| 248 pass | |
| 249 | |
| 250 class BuilderNames(unittest.TestCase): | |
| 251 | |
| 252 def testGetBuilderNames(self): | |
| 253 os.mkdir("bnames") | |
| 254 m = master.BuildMaster("bnames") | |
| 255 s = m.getStatus() | |
| 256 | |
| 257 m.loadConfig(config_3) | |
| 258 m.readConfig = True | |
| 259 | |
| 260 self.failUnlessEqual(s.getBuilderNames(), | |
| 261 ["dummy", "test dummy", "adummy", "bdummy"]) | |
| 262 self.failUnlessEqual(s.getBuilderNames(categories=['test']), | |
| 263 ["test dummy", "bdummy"]) | |
| 264 | |
| 265 class Disconnect(RunMixin, unittest.TestCase): | |
| 266 | |
| 267 def setUp(self): | |
| 268 RunMixin.setUp(self) | |
| 269 | |
| 270 # verify that disconnecting the slave during a build properly | |
| 271 # terminates the build | |
| 272 m = self.master | |
| 273 s = self.status | |
| 274 c = self.control | |
| 275 | |
| 276 m.loadConfig(config_2) | |
| 277 m.readConfig = True | |
| 278 m.startService() | |
| 279 | |
| 280 self.failUnlessEqual(s.getBuilderNames(), ["dummy", "test dummy"]) | |
| 281 self.s1 = s1 = s.getBuilder("dummy") | |
| 282 self.failUnlessEqual(s1.getName(), "dummy") | |
| 283 self.failUnlessEqual(s1.getState(), ("offline", [])) | |
| 284 self.failUnlessEqual(s1.getCurrentBuilds(), []) | |
| 285 self.failUnlessEqual(s1.getLastFinishedBuild(), None) | |
| 286 self.failUnlessEqual(s1.getBuild(-1), None) | |
| 287 | |
| 288 d = self.connectSlave() | |
| 289 d.addCallback(self._disconnectSetup_1) | |
| 290 return d | |
| 291 | |
| 292 def _disconnectSetup_1(self, res): | |
| 293 self.failUnlessEqual(self.s1.getState(), ("idle", [])) | |
| 294 | |
| 295 | |
| 296 def verifyDisconnect(self, bs): | |
| 297 self.failUnless(bs.isFinished()) | |
| 298 | |
| 299 step1 = bs.getSteps()[0] | |
| 300 self.failUnlessEqual(step1.getText(), ["delay", "interrupted"]) | |
| 301 self.failUnlessEqual(step1.getResults()[0], builder.FAILURE) | |
| 302 | |
| 303 self.failUnlessEqual(bs.getResults(), builder.FAILURE) | |
| 304 | |
| 305 def verifyDisconnect2(self, bs): | |
| 306 self.failUnless(bs.isFinished()) | |
| 307 | |
| 308 step1 = bs.getSteps()[1] | |
| 309 self.failUnlessEqual(step1.getText(), ["remote", "delay", "2 secs", | |
| 310 "failed", "slave", "lost"]) | |
| 311 self.failUnlessEqual(step1.getResults()[0], builder.EXCEPTION) | |
| 312 | |
| 313 self.failUnlessEqual(bs.getResults(), builder.EXCEPTION) | |
| 314 | |
| 315 def submitBuild(self): | |
| 316 ss = SourceStamp() | |
| 317 br = BuildRequest("forced build", ss, "dummy") | |
| 318 self.control.getBuilder("dummy").requestBuild(br) | |
| 319 d = defer.Deferred() | |
| 320 def _started(bc): | |
| 321 br.unsubscribe(_started) | |
| 322 d.callback(bc) | |
| 323 br.subscribe(_started) | |
| 324 return d | |
| 325 | |
| 326 def testIdle2(self): | |
| 327 # now suppose the slave goes missing | |
| 328 self.disappearSlave(allowReconnect=False) | |
| 329 | |
| 330 # forcing a build will work: the build detect that the slave is no | |
| 331 # longer available and will be re-queued. Wait 5 seconds, then check | |
| 332 # to make sure the build is still in the 'waiting for a slave' queue. | |
| 333 req = BuildRequest("forced build", SourceStamp(), "test_builder") | |
| 334 self.failUnlessEqual(req.startCount, 0) | |
| 335 self.control.getBuilder("dummy").requestBuild(req) | |
| 336 # this should ping the slave, which doesn't respond (and eventually | |
| 337 # times out). The BuildRequest will be re-queued, and its .startCount | |
| 338 # will be incremented. | |
| 339 self.killSlave() | |
| 340 d = defer.Deferred() | |
| 341 d.addCallback(self._testIdle2_1, req) | |
| 342 reactor.callLater(3, d.callback, None) | |
| 343 return d | |
| 344 testIdle2.timeout = 5 | |
| 345 | |
| 346 def _testIdle2_1(self, res, req): | |
| 347 self.failUnlessEqual(req.startCount, 1) | |
| 348 cancelled = req.cancel() | |
| 349 self.failUnless(cancelled) | |
| 350 | |
| 351 | |
| 352 def testBuild1(self): | |
| 353 # this next sequence is timing-dependent. The dummy build takes at | |
| 354 # least 3 seconds to complete, and this batch of commands must | |
| 355 # complete within that time. | |
| 356 # | |
| 357 d = self.submitBuild() | |
| 358 d.addCallback(self._testBuild1_1) | |
| 359 return d | |
| 360 | |
| 361 def _testBuild1_1(self, bc): | |
| 362 bs = bc.getStatus() | |
| 363 # now kill the slave before it gets to start the first step | |
| 364 d = self.shutdownAllSlaves() # dies before it gets started | |
| 365 d.addCallback(self._testBuild1_2, bs) | |
| 366 return d # TODO: this used to have a 5-second timeout | |
| 367 | |
| 368 def _testBuild1_2(self, res, bs): | |
| 369 # now examine the just-stopped build and make sure it is really | |
| 370 # stopped. This is checking for bugs in which the slave-detach gets | |
| 371 # missed or causes an exception which prevents the build from being | |
| 372 # marked as "finished due to an error". | |
| 373 d = bs.waitUntilFinished() | |
| 374 d2 = self.master.botmaster.waitUntilBuilderDetached("dummy") | |
| 375 dl = defer.DeferredList([d, d2]) | |
| 376 dl.addCallback(self._testBuild1_3, bs) | |
| 377 return dl # TODO: this had a 5-second timeout too | |
| 378 | |
| 379 def _testBuild1_3(self, res, bs): | |
| 380 self.failUnlessEqual(self.s1.getState()[0], "offline") | |
| 381 self.verifyDisconnect(bs) | |
| 382 | |
| 383 | |
| 384 def testBuild2(self): | |
| 385 # this next sequence is timing-dependent | |
| 386 d = self.submitBuild() | |
| 387 d.addCallback(self._testBuild2_1) | |
| 388 return d | |
| 389 testBuild2.timeout = 30 | |
| 390 | |
| 391 def _testBuild2_1(self, bc): | |
| 392 bs = bc.getStatus() | |
| 393 # shutdown the slave while it's running the first step | |
| 394 reactor.callLater(0.5, self.shutdownAllSlaves) | |
| 395 | |
| 396 d = bs.waitUntilFinished() | |
| 397 d.addCallback(self._testBuild2_2, bs) | |
| 398 return d | |
| 399 | |
| 400 def _testBuild2_2(self, res, bs): | |
| 401 # we hit here when the build has finished. The builder is still being | |
| 402 # torn down, however, so spin for another second to allow the | |
| 403 # callLater(0) in Builder.detached to fire. | |
| 404 d = defer.Deferred() | |
| 405 reactor.callLater(1, d.callback, None) | |
| 406 d.addCallback(self._testBuild2_3, bs) | |
| 407 return d | |
| 408 | |
| 409 def _testBuild2_3(self, res, bs): | |
| 410 self.failUnlessEqual(self.s1.getState()[0], "offline") | |
| 411 self.verifyDisconnect(bs) | |
| 412 | |
| 413 | |
| 414 def testBuild3(self): | |
| 415 # this next sequence is timing-dependent | |
| 416 d = self.submitBuild() | |
| 417 d.addCallback(self._testBuild3_1) | |
| 418 return d | |
| 419 testBuild3.timeout = 30 | |
| 420 | |
| 421 def _testBuild3_1(self, bc): | |
| 422 bs = bc.getStatus() | |
| 423 # kill the slave while it's running the first step | |
| 424 reactor.callLater(0.5, self.killSlave) | |
| 425 d = bs.waitUntilFinished() | |
| 426 d.addCallback(self._testBuild3_2, bs) | |
| 427 return d | |
| 428 | |
| 429 def _testBuild3_2(self, res, bs): | |
| 430 # the builder is still being torn down, so give it another second | |
| 431 d = defer.Deferred() | |
| 432 reactor.callLater(1, d.callback, None) | |
| 433 d.addCallback(self._testBuild3_3, bs) | |
| 434 return d | |
| 435 | |
| 436 def _testBuild3_3(self, res, bs): | |
| 437 self.failUnlessEqual(self.s1.getState()[0], "offline") | |
| 438 self.verifyDisconnect(bs) | |
| 439 | |
| 440 | |
| 441 def testBuild4(self): | |
| 442 # this next sequence is timing-dependent | |
| 443 d = self.submitBuild() | |
| 444 d.addCallback(self._testBuild4_1) | |
| 445 return d | |
| 446 testBuild4.timeout = 30 | |
| 447 | |
| 448 def _testBuild4_1(self, bc): | |
| 449 bs = bc.getStatus() | |
| 450 # kill the slave while it's running the second (remote) step | |
| 451 reactor.callLater(1.5, self.killSlave) | |
| 452 d = bs.waitUntilFinished() | |
| 453 d.addCallback(self._testBuild4_2, bs) | |
| 454 return d | |
| 455 | |
| 456 def _testBuild4_2(self, res, bs): | |
| 457 # at this point, the slave is in the process of being removed, so it | |
| 458 # could either be 'idle' or 'offline'. I think there is a | |
| 459 # reactor.callLater(0) standing between here and the offline state. | |
| 460 #reactor.iterate() # TODO: remove the need for this | |
| 461 | |
| 462 self.failUnlessEqual(self.s1.getState()[0], "offline") | |
| 463 self.verifyDisconnect2(bs) | |
| 464 | |
| 465 | |
| 466 def testInterrupt(self): | |
| 467 # this next sequence is timing-dependent | |
| 468 d = self.submitBuild() | |
| 469 d.addCallback(self._testInterrupt_1) | |
| 470 return d | |
| 471 testInterrupt.timeout = 30 | |
| 472 | |
| 473 def _testInterrupt_1(self, bc): | |
| 474 bs = bc.getStatus() | |
| 475 # halt the build while it's running the first step | |
| 476 reactor.callLater(0.5, bc.stopBuild, "bang go splat") | |
| 477 d = bs.waitUntilFinished() | |
| 478 d.addCallback(self._testInterrupt_2, bs) | |
| 479 return d | |
| 480 | |
| 481 def _testInterrupt_2(self, res, bs): | |
| 482 self.verifyDisconnect(bs) | |
| 483 | |
| 484 | |
| 485 def testDisappear(self): | |
| 486 bc = self.control.getBuilder("dummy") | |
| 487 | |
| 488 # ping should succeed | |
| 489 d = bc.ping() | |
| 490 d.addCallback(self._testDisappear_1, bc) | |
| 491 return d | |
| 492 | |
| 493 def _testDisappear_1(self, res, bc): | |
| 494 self.failUnlessEqual(res, True) | |
| 495 | |
| 496 # now, before any build is run, make the slave disappear | |
| 497 self.disappearSlave(allowReconnect=False) | |
| 498 | |
| 499 # initiate the ping and then kill the slave, to simulate a disconnect. | |
| 500 d = bc.ping() | |
| 501 self.killSlave() | |
| 502 d.addCallback(self. _testDisappear_2) | |
| 503 return d | |
| 504 def _testDisappear_2(self, res): | |
| 505 self.failUnlessEqual(res, False) | |
| 506 | |
| 507 def testDuplicate(self): | |
| 508 bc = self.control.getBuilder("dummy") | |
| 509 bs = self.status.getBuilder("dummy") | |
| 510 ss = bs.getSlaves()[0] | |
| 511 | |
| 512 self.failUnless(ss.isConnected()) | |
| 513 self.failUnlessEqual(ss.getAdmin(), "one") | |
| 514 | |
| 515 # now, before any build is run, make the first slave disappear | |
| 516 self.disappearSlave(allowReconnect=False) | |
| 517 | |
| 518 d = self.master.botmaster.waitUntilBuilderDetached("dummy") | |
| 519 # now let the new slave take over | |
| 520 self.connectSlave2() | |
| 521 d.addCallback(self._testDuplicate_1, ss) | |
| 522 return d | |
| 523 testDuplicate.timeout = 5 | |
| 524 | |
| 525 def _testDuplicate_1(self, res, ss): | |
| 526 d = self.master.botmaster.waitUntilBuilderAttached("dummy") | |
| 527 d.addCallback(self._testDuplicate_2, ss) | |
| 528 return d | |
| 529 | |
| 530 def _testDuplicate_2(self, res, ss): | |
| 531 self.failUnless(ss.isConnected()) | |
| 532 self.failUnlessEqual(ss.getAdmin(), "two") | |
| 533 | |
| 534 | |
| 535 class Disconnect2(RunMixin, unittest.TestCase): | |
| 536 | |
| 537 def setUp(self): | |
| 538 RunMixin.setUp(self) | |
| 539 # verify that disconnecting the slave during a build properly | |
| 540 # terminates the build | |
| 541 m = self.master | |
| 542 s = self.status | |
| 543 c = self.control | |
| 544 | |
| 545 m.loadConfig(config_2) | |
| 546 m.readConfig = True | |
| 547 m.startService() | |
| 548 | |
| 549 self.failUnlessEqual(s.getBuilderNames(), ["dummy", "test dummy"]) | |
| 550 self.s1 = s1 = s.getBuilder("dummy") | |
| 551 self.failUnlessEqual(s1.getName(), "dummy") | |
| 552 self.failUnlessEqual(s1.getState(), ("offline", [])) | |
| 553 self.failUnlessEqual(s1.getCurrentBuilds(), []) | |
| 554 self.failUnlessEqual(s1.getLastFinishedBuild(), None) | |
| 555 self.failUnlessEqual(s1.getBuild(-1), None) | |
| 556 | |
| 557 d = self.connectSlaveFastTimeout() | |
| 558 d.addCallback(self._setup_disconnect2_1) | |
| 559 return d | |
| 560 | |
| 561 def _setup_disconnect2_1(self, res): | |
| 562 self.failUnlessEqual(self.s1.getState(), ("idle", [])) | |
| 563 | |
| 564 | |
| 565 def testSlaveTimeout(self): | |
| 566 # now suppose the slave goes missing. We want to find out when it | |
| 567 # creates a new Broker, so we reach inside and mark it with the | |
| 568 # well-known sigil of impending messy death. | |
| 569 bd = self.slaves['bot1'].getServiceNamed("bot").builders["dummy"] | |
| 570 broker = bd.remote.broker | |
| 571 broker.redshirt = 1 | |
| 572 | |
| 573 # make sure the keepalives will keep the connection up | |
| 574 d = defer.Deferred() | |
| 575 reactor.callLater(5, d.callback, None) | |
| 576 d.addCallback(self._testSlaveTimeout_1) | |
| 577 return d | |
| 578 testSlaveTimeout.timeout = 20 | |
| 579 | |
| 580 def _testSlaveTimeout_1(self, res): | |
| 581 bd = self.slaves['bot1'].getServiceNamed("bot").builders["dummy"] | |
| 582 if not bd.remote or not hasattr(bd.remote.broker, "redshirt"): | |
| 583 self.fail("slave disconnected when it shouldn't have") | |
| 584 | |
| 585 d = self.master.botmaster.waitUntilBuilderDetached("dummy") | |
| 586 # whoops! how careless of me. | |
| 587 self.disappearSlave(allowReconnect=True) | |
| 588 # the slave will realize the connection is lost within 2 seconds, and | |
| 589 # reconnect. | |
| 590 d.addCallback(self._testSlaveTimeout_2) | |
| 591 return d | |
| 592 | |
| 593 def _testSlaveTimeout_2(self, res): | |
| 594 # the ReconnectingPBClientFactory will attempt a reconnect in two | |
| 595 # seconds. | |
| 596 d = self.master.botmaster.waitUntilBuilderAttached("dummy") | |
| 597 d.addCallback(self._testSlaveTimeout_3) | |
| 598 return d | |
| 599 | |
| 600 def _testSlaveTimeout_3(self, res): | |
| 601 # make sure it is a new connection (i.e. a new Broker) | |
| 602 bd = self.slaves['bot1'].getServiceNamed("bot").builders["dummy"] | |
| 603 self.failUnless(bd.remote, "hey, slave isn't really connected") | |
| 604 self.failIf(hasattr(bd.remote.broker, "redshirt"), | |
| 605 "hey, slave's Broker is still marked for death") | |
| 606 | |
| 607 | |
| 608 class Basedir(RunMixin, unittest.TestCase): | |
| 609 def testChangeBuilddir(self): | |
| 610 m = self.master | |
| 611 m.loadConfig(config_4) | |
| 612 m.readConfig = True | |
| 613 m.startService() | |
| 614 | |
| 615 d = self.connectSlave() | |
| 616 d.addCallback(self._testChangeBuilddir_1) | |
| 617 return d | |
| 618 | |
| 619 def _testChangeBuilddir_1(self, res): | |
| 620 self.bot = bot = self.slaves['bot1'].bot | |
| 621 self.builder = builder = bot.builders.get("dummy") | |
| 622 self.failUnless(builder) | |
| 623 # slavebuilddir value. | |
| 624 self.failUnlessEqual(builder.builddir, "sdummy") | |
| 625 self.failUnlessEqual(builder.basedir, | |
| 626 os.path.join("slavebase-bot1", "sdummy")) | |
| 627 | |
| 628 d = self.master.loadConfig(config_4_newbasedir) | |
| 629 d.addCallback(self._testChangeBuilddir_2) | |
| 630 return d | |
| 631 | |
| 632 def _testChangeBuilddir_2(self, res): | |
| 633 bot = self.bot | |
| 634 # this does NOT cause the builder to be replaced | |
| 635 builder = bot.builders.get("dummy") | |
| 636 self.failUnless(builder) | |
| 637 self.failUnlessIdentical(self.builder, builder) | |
| 638 # the basedir should be updated | |
| 639 self.failUnlessEqual(builder.builddir, "dummy2") | |
| 640 self.failUnlessEqual(builder.basedir, | |
| 641 os.path.join("slavebase-bot1", "dummy2")) | |
| 642 | |
| 643 # add a new builder, which causes the basedir list to be reloaded | |
| 644 d = self.master.loadConfig(config_4_newbuilder) | |
| 645 return d | |
| 646 | |
| 647 class Triggers(RunMixin, TestFlagMixin, unittest.TestCase): | |
| 648 config_trigger = config_base + """ | |
| 649 from buildbot.scheduler import Triggerable, Scheduler | |
| 650 from buildbot.steps.trigger import Trigger | |
| 651 from buildbot.steps.dummy import Dummy | |
| 652 from buildbot.test.runutils import SetTestFlagStep | |
| 653 from buildbot.process.properties import WithProperties | |
| 654 c['schedulers'] = [ | |
| 655 Scheduler('triggerer', None, 0.1, ['triggerer'], properties={'dyn':'dyn'}), | |
| 656 Triggerable('triggeree', ['triggeree']) | |
| 657 ] | |
| 658 triggerer = factory.BuildFactory() | |
| 659 triggerer.addSteps([ | |
| 660 SetTestFlagStep(flagname='triggerer_started'), | |
| 661 Trigger(flunkOnFailure=True, @ARGS@), | |
| 662 SetTestFlagStep(flagname='triggerer_finished'), | |
| 663 ]) | |
| 664 triggeree = factory.BuildFactory([ | |
| 665 s(SetTestFlagStep, flagname='triggeree_started'), | |
| 666 s(@DUMMYCLASS@), | |
| 667 s(SetTestFlagStep, flagname='triggeree_finished'), | |
| 668 ]) | |
| 669 c['builders'] = [{'name': 'triggerer', 'slavename': 'bot1', | |
| 670 'builddir': 'triggerer', 'factory': triggerer}, | |
| 671 {'name': 'triggeree', 'slavename': 'bot1', | |
| 672 'builddir': 'triggeree', 'factory': triggeree}] | |
| 673 """ | |
| 674 | |
| 675 def mkConfig(self, args, dummyclass="Dummy"): | |
| 676 return self.config_trigger.replace("@ARGS@", args).replace("@DUMMYCLASS@
", dummyclass) | |
| 677 | |
| 678 def setupTest(self, args, dummyclass, checkFn): | |
| 679 self.clearFlags() | |
| 680 m = self.master | |
| 681 m.loadConfig(self.mkConfig(args, dummyclass)) | |
| 682 m.readConfig = True | |
| 683 m.startService() | |
| 684 | |
| 685 c = changes.Change("bob", ["Makefile", "foo/bar.c"], "changed stuff") | |
| 686 m.change_svc.addChange(c) | |
| 687 | |
| 688 d = self.connectSlave(builders=['triggerer', 'triggeree']) | |
| 689 d.addCallback(self.startTimer, 0.5, checkFn) | |
| 690 return d | |
| 691 | |
| 692 def startTimer(self, res, time, next_fn): | |
| 693 d = defer.Deferred() | |
| 694 reactor.callLater(time, d.callback, None) | |
| 695 d.addCallback(next_fn) | |
| 696 return d | |
| 697 | |
| 698 def testTriggerBuild(self): | |
| 699 return self.setupTest("schedulerNames=['triggeree']", | |
| 700 "Dummy", | |
| 701 self._checkTriggerBuild) | |
| 702 | |
| 703 def _checkTriggerBuild(self, res): | |
| 704 self.failIfFlagNotSet('triggerer_started') | |
| 705 self.failIfFlagNotSet('triggeree_started') | |
| 706 self.failIfFlagSet('triggeree_finished') | |
| 707 self.failIfFlagNotSet('triggerer_finished') | |
| 708 | |
| 709 def testTriggerBuildWait(self): | |
| 710 return self.setupTest("schedulerNames=['triggeree'], waitForFinish=1", | |
| 711 "Dummy", | |
| 712 self._checkTriggerBuildWait) | |
| 713 | |
| 714 def _checkTriggerBuildWait(self, res): | |
| 715 self.failIfFlagNotSet('triggerer_started') | |
| 716 self.failIfFlagNotSet('triggeree_started') | |
| 717 self.failIfFlagSet('triggeree_finished') | |
| 718 self.failIfFlagSet('triggerer_finished') | |
| 719 | |
| 720 def testProperties(self): | |
| 721 return self.setupTest(""" | |
| 722 schedulerNames=['triggeree'], | |
| 723 set_properties={'lit' : 'lit'}, | |
| 724 copy_properties=['dyn'] | |
| 725 """, """ | |
| 726 SetTestFlagStep, flagname='props', | |
| 727 value=WithProperties('%(lit:-MISSING)s:%(dyn:-MISSING)s') | |
| 728 """, | |
| 729 self._checkProperties) | |
| 730 | |
| 731 def _checkProperties(self, res): | |
| 732 self.assertEqual(self.getFlag("props"), "lit:dyn") | |
| 733 | |
| 734 class PropertyPropagation(RunMixin, TestFlagMixin, unittest.TestCase): | |
| 735 def setupTest(self, config, builders, checkFn, changeProps={}): | |
| 736 self.clearFlags() | |
| 737 m = self.master | |
| 738 m.loadConfig(config) | |
| 739 m.readConfig = True | |
| 740 m.startService() | |
| 741 | |
| 742 c = changes.Change("bob", ["Makefile", "foo/bar.c"], "changed stuff", | |
| 743 properties=changeProps) | |
| 744 m.change_svc.addChange(c) | |
| 745 | |
| 746 d = self.connectSlave(builders=builders) | |
| 747 d.addCallback(self.startTimer, 0.5, checkFn) | |
| 748 return d | |
| 749 | |
| 750 def startTimer(self, res, time, next_fn): | |
| 751 d = defer.Deferred() | |
| 752 reactor.callLater(time, d.callback, None) | |
| 753 d.addCallback(next_fn) | |
| 754 return d | |
| 755 | |
| 756 config_schprop = config_base + """ | |
| 757 from buildbot.scheduler import Scheduler | |
| 758 from buildbot.steps.dummy import Dummy | |
| 759 from buildbot.test.runutils import SetTestFlagStep | |
| 760 from buildbot.process.properties import WithProperties | |
| 761 c['schedulers'] = [ | |
| 762 Scheduler('mysched', None, 0.1, ['flagcolor'], properties={'color':'red'}), | |
| 763 ] | |
| 764 factory = factory.BuildFactory([ | |
| 765 s(SetTestFlagStep, flagname='testresult', | |
| 766 value=WithProperties('color=%(color)s sched=%(scheduler)s')), | |
| 767 ]) | |
| 768 c['builders'] = [{'name': 'flagcolor', 'slavename': 'bot1', | |
| 769 'builddir': 'test', 'factory': factory}, | |
| 770 ] | |
| 771 """ | |
| 772 | |
| 773 def testScheduler(self): | |
| 774 def _check(res): | |
| 775 self.failUnlessEqual(self.getFlag('testresult'), | |
| 776 'color=red sched=mysched') | |
| 777 return self.setupTest(self.config_schprop, ['flagcolor'], _check) | |
| 778 | |
| 779 config_changeprop = config_base + """ | |
| 780 from buildbot.scheduler import Scheduler | |
| 781 from buildbot.steps.dummy import Dummy | |
| 782 from buildbot.test.runutils import SetTestFlagStep | |
| 783 from buildbot.process.properties import WithProperties | |
| 784 c['schedulers'] = [ | |
| 785 Scheduler('mysched', None, 0.1, ['flagcolor'], properties={'color':'red'}), | |
| 786 ] | |
| 787 factory = factory.BuildFactory([ | |
| 788 s(SetTestFlagStep, flagname='testresult', | |
| 789 value=WithProperties('color=%(color)s sched=%(scheduler)s prop1=%(prop1)s'
)), | |
| 790 ]) | |
| 791 c['builders'] = [{'name': 'flagcolor', 'slavename': 'bot1', | |
| 792 'builddir': 'test', 'factory': factory}, | |
| 793 ] | |
| 794 """ | |
| 795 | |
| 796 def testChangeProp(self): | |
| 797 def _check(res): | |
| 798 self.failUnlessEqual(self.getFlag('testresult'), | |
| 799 'color=blue sched=mysched prop1=prop1') | |
| 800 return self.setupTest(self.config_changeprop, ['flagcolor'], _check, | |
| 801 changeProps={'color': 'blue', 'prop1': 'prop1'}) | |
| 802 | |
| 803 config_slaveprop = config_base + """ | |
| 804 from buildbot.scheduler import Scheduler | |
| 805 from buildbot.steps.dummy import Dummy | |
| 806 from buildbot.test.runutils import SetTestFlagStep | |
| 807 from buildbot.process.properties import WithProperties | |
| 808 c['schedulers'] = [ | |
| 809 Scheduler('mysched', None, 0.1, ['flagcolor']) | |
| 810 ] | |
| 811 c['slaves'] = [BuildSlave('bot1', 'sekrit', properties={'color':'orange'})] | |
| 812 factory = factory.BuildFactory([ | |
| 813 s(SetTestFlagStep, flagname='testresult', | |
| 814 value=WithProperties('color=%(color)s slavename=%(slavename)s')), | |
| 815 ]) | |
| 816 c['builders'] = [{'name': 'flagcolor', 'slavename': 'bot1', | |
| 817 'builddir': 'test', 'factory': factory}, | |
| 818 ] | |
| 819 """ | |
| 820 def testSlave(self): | |
| 821 def _check(res): | |
| 822 self.failUnlessEqual(self.getFlag('testresult'), | |
| 823 'color=orange slavename=bot1') | |
| 824 return self.setupTest(self.config_slaveprop, ['flagcolor'], _check) | |
| 825 | |
| 826 config_trigger = config_base + """ | |
| 827 from buildbot.scheduler import Triggerable, Scheduler | |
| 828 from buildbot.steps.trigger import Trigger | |
| 829 from buildbot.steps.dummy import Dummy | |
| 830 from buildbot.test.runutils import SetTestFlagStep | |
| 831 from buildbot.process.properties import WithProperties | |
| 832 c['schedulers'] = [ | |
| 833 Scheduler('triggerer', None, 0.1, ['triggerer'], | |
| 834 properties={'color':'mauve', 'pls_trigger':'triggeree'}), | |
| 835 Triggerable('triggeree', ['triggeree'], properties={'color':'invisible'}) | |
| 836 ] | |
| 837 triggerer = factory.BuildFactory([ | |
| 838 s(SetTestFlagStep, flagname='testresult', value='wrongone'), | |
| 839 s(Trigger, flunkOnFailure=True, | |
| 840 schedulerNames=[WithProperties('%(pls_trigger)s')], | |
| 841 set_properties={'color' : WithProperties('%(color)s')}), | |
| 842 s(SetTestFlagStep, flagname='testresult', value='triggered'), | |
| 843 ]) | |
| 844 triggeree = factory.BuildFactory([ | |
| 845 s(SetTestFlagStep, flagname='testresult', | |
| 846 value=WithProperties('sched=%(scheduler)s color=%(color)s')), | |
| 847 ]) | |
| 848 c['builders'] = [{'name': 'triggerer', 'slavename': 'bot1', | |
| 849 'builddir': 'triggerer', 'factory': triggerer}, | |
| 850 {'name': 'triggeree', 'slavename': 'bot1', | |
| 851 'builddir': 'triggeree', 'factory': triggeree}] | |
| 852 """ | |
| 853 def testTrigger(self): | |
| 854 def _check(res): | |
| 855 self.failUnlessEqual(self.getFlag('testresult'), | |
| 856 'sched=triggeree color=mauve') | |
| 857 return self.setupTest(self.config_trigger, | |
| 858 ['triggerer', 'triggeree'], _check) | |
| 859 | |
| 860 | |
| 861 config_test_flag = config_base + """ | |
| 862 from buildbot.scheduler import Scheduler | |
| 863 c['schedulers'] = [Scheduler('quick', None, 0.1, ['dummy'])] | |
| 864 | |
| 865 from buildbot.test.runutils import SetTestFlagStep | |
| 866 f3 = factory.BuildFactory([ | |
| 867 s(SetTestFlagStep, flagname='foo', value='bar'), | |
| 868 ]) | |
| 869 | |
| 870 c['builders'] = [{'name': 'dummy', 'slavename': 'bot1', | |
| 871 'builddir': 'dummy', 'factory': f3}] | |
| 872 """ | |
| 873 | |
| 874 class TestFlag(RunMixin, TestFlagMixin, unittest.TestCase): | |
| 875 """Test for the TestFlag functionality in runutils""" | |
| 876 def testTestFlag(self): | |
| 877 m = self.master | |
| 878 m.loadConfig(config_test_flag) | |
| 879 m.readConfig = True | |
| 880 m.startService() | |
| 881 | |
| 882 c = changes.Change("bob", ["Makefile", "foo/bar.c"], "changed stuff") | |
| 883 m.change_svc.addChange(c) | |
| 884 | |
| 885 d = self.connectSlave() | |
| 886 d.addCallback(self._testTestFlag_1) | |
| 887 return d | |
| 888 | |
| 889 def _testTestFlag_1(self, res): | |
| 890 d = defer.Deferred() | |
| 891 reactor.callLater(0.5, d.callback, None) | |
| 892 d.addCallback(self._testTestFlag_2) | |
| 893 return d | |
| 894 | |
| 895 def _testTestFlag_2(self, res): | |
| 896 self.failUnlessEqual(self.getFlag('foo'), 'bar') | |
| 897 | |
| 898 # TODO: test everything, from Change submission to Scheduler to Build to | |
| 899 # Status. Use all the status types. Specifically I want to catch recurrences | |
| 900 # of the bug where I forgot to make Waterfall inherit from StatusReceiver | |
| 901 # such that buildSetSubmitted failed. | |
| 902 | |
| 903 config_test_builder = config_base + """ | |
| 904 from buildbot.scheduler import Scheduler | |
| 905 c['schedulers'] = [Scheduler('quick', 'dummy', 0.1, ['dummy']), | |
| 906 Scheduler('quick2', 'dummy2', 0.1, ['dummy2']), | |
| 907 Scheduler('quick3', 'dummy3', 0.1, ['dummy3'])] | |
| 908 | |
| 909 from buildbot.steps.shell import ShellCommand | |
| 910 f3 = factory.BuildFactory([ | |
| 911 s(ShellCommand, command="sleep 3", env={'blah':'blah'}) | |
| 912 ]) | |
| 913 | |
| 914 c['builders'] = [{'name': 'dummy', 'slavename': 'bot1', 'env': {'foo':'bar'}, | |
| 915 'builddir': 'dummy', 'factory': f3}] | |
| 916 | |
| 917 c['builders'].append({'name': 'dummy2', 'slavename': 'bot1', | |
| 918 'env': {'blah':'bar'}, 'builddir': 'dummy2', | |
| 919 'factory': f3}) | |
| 920 | |
| 921 f4 = factory.BuildFactory([ | |
| 922 s(ShellCommand, command="sleep 3") | |
| 923 ]) | |
| 924 | |
| 925 c['builders'].append({'name': 'dummy3', 'slavename': 'bot1', | |
| 926 'env': {'blah':'bar'}, 'builddir': 'dummy3', | |
| 927 'factory': f4}) | |
| 928 """ | |
| 929 | |
| 930 class TestBuilder(RunMixin, unittest.TestCase): | |
| 931 def setUp(self): | |
| 932 RunMixin.setUp(self) | |
| 933 self.master.loadConfig(config_test_builder) | |
| 934 self.master.readConfig = True | |
| 935 self.master.startService() | |
| 936 self.connectSlave(builders=["dummy", "dummy2", "dummy3"]) | |
| 937 | |
| 938 def doBuilderEnvTest(self, branch, cb): | |
| 939 c = changes.Change("bob", ["Makefile", "foo/bar.c"], "changed", | |
| 940 branch=branch) | |
| 941 self.master.change_svc.addChange(c) | |
| 942 | |
| 943 d = defer.Deferred() | |
| 944 reactor.callLater(0.5, d.callback, None) | |
| 945 d.addCallback(cb) | |
| 946 | |
| 947 return d | |
| 948 | |
| 949 def testBuilderEnv(self): | |
| 950 return self.doBuilderEnvTest("dummy", self._testBuilderEnv1) | |
| 951 | |
| 952 def _testBuilderEnv1(self, res): | |
| 953 b = self.master.botmaster.builders['dummy'] | |
| 954 build = b.building[0] | |
| 955 s = build.currentStep | |
| 956 self.failUnless('foo' in s.cmd.args['env']) | |
| 957 self.failUnlessEqual('bar', s.cmd.args['env']['foo']) | |
| 958 self.failUnless('blah' in s.cmd.args['env']) | |
| 959 self.failUnlessEqual('blah', s.cmd.args['env']['blah']) | |
| 960 | |
| 961 def testBuilderEnvOverride(self): | |
| 962 return self.doBuilderEnvTest("dummy2", self._testBuilderEnvOverride1) | |
| 963 | |
| 964 def _testBuilderEnvOverride1(self, res): | |
| 965 b = self.master.botmaster.builders['dummy2'] | |
| 966 build = b.building[0] | |
| 967 s = build.currentStep | |
| 968 self.failUnless('blah' in s.cmd.args['env']) | |
| 969 self.failUnlessEqual('blah', s.cmd.args['env']['blah']) | |
| 970 | |
| 971 def testBuilderNoStepEnv(self): | |
| 972 return self.doBuilderEnvTest("dummy3", self._testBuilderNoStepEnv1) | |
| 973 | |
| 974 def _testBuilderNoStepEnv1(self, res): | |
| 975 b = self.master.botmaster.builders['dummy3'] | |
| 976 build = b.building[0] | |
| 977 s = build.currentStep | |
| 978 self.failUnless('blah' in s.cmd.args['env']) | |
| 979 self.failUnlessEqual('bar', s.cmd.args['env']['blah']) | |
| 980 | |
| 981 class SchedulerWatchers(RunMixin, TestFlagMixin, unittest.TestCase): | |
| 982 config_watchable = config_base + """ | |
| 983 from buildbot.scheduler import AnyBranchScheduler | |
| 984 from buildbot.steps.dummy import Dummy | |
| 985 from buildbot.test.runutils import setTestFlag, SetTestFlagStep | |
| 986 s = AnyBranchScheduler( | |
| 987 name='abs', | |
| 988 branches=None, | |
| 989 treeStableTimer=0, | |
| 990 builderNames=['a', 'b']) | |
| 991 c['schedulers'] = [ s ] | |
| 992 | |
| 993 # count the number of times a success watcher is called | |
| 994 numCalls = [ 0 ] | |
| 995 def watcher(ss): | |
| 996 numCalls[0] += 1 | |
| 997 setTestFlag("numCalls", numCalls[0]) | |
| 998 s.subscribeToSuccessfulBuilds(watcher) | |
| 999 | |
| 1000 f = factory.BuildFactory() | |
| 1001 f.addStep(Dummy(timeout=0)) | |
| 1002 c['builders'] = [{'name': 'a', 'slavename': 'bot1', | |
| 1003 'builddir': 'a', 'factory': f}, | |
| 1004 {'name': 'b', 'slavename': 'bot1', | |
| 1005 'builddir': 'b', 'factory': f}] | |
| 1006 """ | |
| 1007 | |
| 1008 def testWatchers(self): | |
| 1009 self.clearFlags() | |
| 1010 m = self.master | |
| 1011 m.loadConfig(self.config_watchable) | |
| 1012 m.readConfig = True | |
| 1013 m.startService() | |
| 1014 | |
| 1015 c = changes.Change("bob", ["Makefile", "foo/bar.c"], "changed stuff") | |
| 1016 m.change_svc.addChange(c) | |
| 1017 | |
| 1018 d = self.connectSlave(builders=['a', 'b']) | |
| 1019 | |
| 1020 def pause(res): | |
| 1021 d = defer.Deferred() | |
| 1022 reactor.callLater(1, d.callback, res) | |
| 1023 return d | |
| 1024 d.addCallback(pause) | |
| 1025 | |
| 1026 def checkFn(res): | |
| 1027 self.failUnlessEqual(self.getFlag('numCalls'), 1) | |
| 1028 d.addCallback(checkFn) | |
| 1029 return d | |
| 1030 | |
| 1031 config_priority = """ | |
| 1032 from buildbot.process import factory | |
| 1033 from buildbot.steps import dummy | |
| 1034 from buildbot.buildslave import BuildSlave | |
| 1035 s = factory.s | |
| 1036 | |
| 1037 from buildbot.steps.shell import ShellCommand | |
| 1038 f1 = factory.BuildFactory([ | |
| 1039 s(ShellCommand, command="sleep 1", env={'blah':'blah'}) | |
| 1040 ]) | |
| 1041 | |
| 1042 BuildmasterConfig = c = {} | |
| 1043 slavenames = ['bot%i' % i for i in range(5)] | |
| 1044 c['slaves'] = [BuildSlave(name, 'sekrit', max_builds=1) for name in slavenames] | |
| 1045 c['schedulers'] = [] | |
| 1046 c['builders'] = [] | |
| 1047 c['builders'].append({'name':'quick1', 'slavenames':slavenames, 'builddir': 'qui
ckdir1', 'factory': f1}) | |
| 1048 c['builders'].append({'name':'quick2', 'slavenames':slavenames, 'builddir': 'qui
ckdir2', 'factory': f1}) | |
| 1049 c['slavePortnum'] = 0 | |
| 1050 """ | |
| 1051 | |
| 1052 class BuildPrioritization(RunMixin, unittest.TestCase): | |
| 1053 def rmtree(self, d): | |
| 1054 rmtree(d) | |
| 1055 | |
| 1056 def testPriority(self): | |
| 1057 self.rmtree("basedir") | |
| 1058 os.mkdir("basedir") | |
| 1059 self.master.loadConfig(config_priority) | |
| 1060 self.master.readConfig = True | |
| 1061 self.master.startService() | |
| 1062 | |
| 1063 # Our fake source stamp | |
| 1064 # we override canBeMergedWith so that our requests don't get merged toge
ther | |
| 1065 ss = SourceStamp() | |
| 1066 ss.canBeMergedWith = lambda x: False | |
| 1067 | |
| 1068 # Send 10 requests to alternating builders | |
| 1069 # We fudge the submittedAt field after submitting since they're all | |
| 1070 # getting submitted so close together according to time.time() | |
| 1071 # and all we care about is what order they're run in. | |
| 1072 reqs = [] | |
| 1073 self.start_order = [] | |
| 1074 for i in range(10): | |
| 1075 req = BuildRequest(str(i), ss, "test_builder") | |
| 1076 j = i % 2 + 1 | |
| 1077 self.master.botmaster.builders['quick%i' % j].submitBuildRequest(req
) | |
| 1078 req.submittedAt = i | |
| 1079 # Keep track of what order the builds start in | |
| 1080 def append(build): | |
| 1081 self.start_order.append(int(build.reason)) | |
| 1082 req.subscribe(append) | |
| 1083 reqs.append(req.waitUntilFinished()) | |
| 1084 | |
| 1085 dl = defer.DeferredList(reqs) | |
| 1086 dl.addCallback(self._all_finished) | |
| 1087 | |
| 1088 def _delay(res): | |
| 1089 d1 = defer.Deferred() | |
| 1090 reactor.callLater(0.5, d1.callback, None) | |
| 1091 # this test depends upon this 0.5s delay landing us in the middle | |
| 1092 # of one of the builds. | |
| 1093 return d1 | |
| 1094 | |
| 1095 def _connect(res, i): | |
| 1096 return self.connectSlave(slavename="bot%i" % i, builders=["quick1",
"quick2"]) | |
| 1097 | |
| 1098 # Now add the slaves | |
| 1099 d = self.connectSlave(slavename="bot0", builders=["quick1", "quick2"]) | |
| 1100 for i in range(1,5): | |
| 1101 d.addCallback(_delay) | |
| 1102 d.addCallback(_connect, i) | |
| 1103 | |
| 1104 d.addCallback(lambda x: dl) | |
| 1105 | |
| 1106 return d | |
| 1107 | |
| 1108 def _all_finished(self, *args): | |
| 1109 # The builds should have finished in proper order | |
| 1110 self.failUnlessEqual(self.start_order, range(10)) | |
| 1111 | |
| 1112 # Test graceful shutdown when no builds are active, as well as | |
| 1113 # canStartBuild after graceful shutdown is initiated | |
| 1114 config_graceful_shutdown_idle = config_base | |
| 1115 class GracefulShutdownIdle(RunMixin, unittest.TestCase): | |
| 1116 def testShutdown(self): | |
| 1117 self.rmtree("basedir") | |
| 1118 os.mkdir("basedir") | |
| 1119 self.master.loadConfig(config_graceful_shutdown_idle) | |
| 1120 self.master.readConfig = True | |
| 1121 self.master.startService() | |
| 1122 d = self.connectSlave(builders=['quick']) | |
| 1123 d.addCallback(self._do_shutdown) | |
| 1124 return d | |
| 1125 | |
| 1126 def _do_shutdown(self, res): | |
| 1127 bs = self.master.botmaster.builders['quick'].slaves[0] | |
| 1128 # Check that the slave is accepting builds once it's connected | |
| 1129 self.assertEquals(bs.slave.canStartBuild(), True) | |
| 1130 | |
| 1131 # Monkeypatch the slave's shutdown routine since the real shutdown | |
| 1132 # interrupts the test harness | |
| 1133 self.did_shutdown = False | |
| 1134 def _shutdown(): | |
| 1135 self.did_shutdown = True | |
| 1136 bs.slave.shutdown = _shutdown | |
| 1137 | |
| 1138 # Start a graceful shutdown | |
| 1139 bs.slave.slave_status.setGraceful(True) | |
| 1140 # Check that the slave isn't accepting builds any more | |
| 1141 self.assertEquals(bs.slave.canStartBuild(), False) | |
| 1142 | |
| 1143 # Wait a little bit and then check that we (pretended to) shut down | |
| 1144 d = defer.Deferred() | |
| 1145 d.addCallback(self._check_shutdown) | |
| 1146 reactor.callLater(0.5, d.callback, None) | |
| 1147 return d | |
| 1148 | |
| 1149 def _check_shutdown(self, res): | |
| 1150 self.assertEquals(self.did_shutdown, True) | |
| 1151 | |
| 1152 # Test graceful shutdown when two builds are active | |
| 1153 config_graceful_shutdown_busy = config_base + """ | |
| 1154 from buildbot.buildslave import BuildSlave | |
| 1155 c['slaves'] = [ BuildSlave('bot1', 'sekrit', max_builds=2) ] | |
| 1156 | |
| 1157 from buildbot.scheduler import Scheduler | |
| 1158 c['schedulers'] = [Scheduler('dummy', None, 0.1, ['dummy', 'dummy2'])] | |
| 1159 | |
| 1160 c['builders'].append({'name': 'dummy', 'slavename': 'bot1', | |
| 1161 'builddir': 'dummy', 'factory': f2}) | |
| 1162 c['builders'].append({'name': 'dummy2', 'slavename': 'bot1', | |
| 1163 'builddir': 'dummy2', 'factory': f2}) | |
| 1164 """ | |
| 1165 class GracefulShutdownBusy(RunMixin, unittest.TestCase): | |
| 1166 def testShutdown(self): | |
| 1167 self.rmtree("basedir") | |
| 1168 os.mkdir("basedir") | |
| 1169 d = self.master.loadConfig(config_graceful_shutdown_busy) | |
| 1170 d.addCallback(lambda res: self.master.startService()) | |
| 1171 d.addCallback(lambda res: self.connectSlave()) | |
| 1172 | |
| 1173 def _send(res): | |
| 1174 # send a change. This will trigger both builders at the same | |
| 1175 # time, but since they share a slave, the max_builds=1 setting | |
| 1176 # will insure that only one of the two builds gets to run. | |
| 1177 cm = self.master.change_svc | |
| 1178 c = changes.Change("bob", ["Makefile", "foo/bar.c"], | |
| 1179 "changed stuff") | |
| 1180 cm.addChange(c) | |
| 1181 d.addCallback(_send) | |
| 1182 | |
| 1183 def _delay(res): | |
| 1184 d1 = defer.Deferred() | |
| 1185 reactor.callLater(0.5, d1.callback, None) | |
| 1186 # this test depends upon this 0.5s delay landing us in the middle | |
| 1187 # of one of the builds. | |
| 1188 return d1 | |
| 1189 d.addCallback(_delay) | |
| 1190 | |
| 1191 # Start a graceful shutdown. We should be in the middle of two builds | |
| 1192 def _shutdown(res): | |
| 1193 bs = self.master.botmaster.builders['dummy'].slaves[0] | |
| 1194 # Monkeypatch the slave's shutdown routine since the real shutdown | |
| 1195 # interrupts the test harness | |
| 1196 self.did_shutdown = False | |
| 1197 def _shutdown(): | |
| 1198 self.did_shutdown = True | |
| 1199 return defer.succeed(None) | |
| 1200 bs.slave.shutdown = _shutdown | |
| 1201 # Start a graceful shutdown | |
| 1202 bs.slave.slave_status.setGraceful(True) | |
| 1203 | |
| 1204 builders = [ self.master.botmaster.builders[bn] | |
| 1205 for bn in ('dummy', 'dummy2') ] | |
| 1206 for builder in builders: | |
| 1207 self.failUnless(len(builder.slaves) == 1) | |
| 1208 from buildbot.process.builder import BUILDING | |
| 1209 building_bs = [ builder | |
| 1210 for builder in builders | |
| 1211 if builder.slaves[0].state == BUILDING ] | |
| 1212 # assert that both builds are running right now. | |
| 1213 self.failUnlessEqual(len(building_bs), 2) | |
| 1214 | |
| 1215 d.addCallback(_shutdown) | |
| 1216 | |
| 1217 # Wait a little bit again, and then make sure that we are still running | |
| 1218 # the two builds, and haven't shutdown yet | |
| 1219 d.addCallback(_delay) | |
| 1220 def _check(res): | |
| 1221 self.assertEquals(self.did_shutdown, False) | |
| 1222 builders = [ self.master.botmaster.builders[bn] | |
| 1223 for bn in ('dummy', 'dummy2') ] | |
| 1224 for builder in builders: | |
| 1225 self.failUnless(len(builder.slaves) == 1) | |
| 1226 from buildbot.process.builder import BUILDING | |
| 1227 building_bs = [ builder | |
| 1228 for builder in builders | |
| 1229 if builder.slaves[0].state == BUILDING ] | |
| 1230 # assert that both builds are running right now. | |
| 1231 self.failUnlessEqual(len(building_bs), 2) | |
| 1232 d.addCallback(_check) | |
| 1233 | |
| 1234 # Wait for all the builds to finish | |
| 1235 def _wait_finish(res): | |
| 1236 builders = [ self.master.botmaster.builders[bn] | |
| 1237 for bn in ('dummy', 'dummy2') ] | |
| 1238 builds = [] | |
| 1239 for builder in builders: | |
| 1240 builds.append(builder.builder_status.currentBuilds[0].waitUntilF
inished()) | |
| 1241 dl = defer.DeferredList(builds) | |
| 1242 return dl | |
| 1243 d.addCallback(_wait_finish) | |
| 1244 | |
| 1245 # Wait a little bit after the builds finish, and then | |
| 1246 # check that the slave has shutdown | |
| 1247 d.addCallback(_delay) | |
| 1248 def _check_shutdown(res): | |
| 1249 # assert that we shutdown the slave | |
| 1250 self.assertEquals(self.did_shutdown, True) | |
| 1251 builders = [ self.master.botmaster.builders[bn] | |
| 1252 for bn in ('dummy', 'dummy2') ] | |
| 1253 from buildbot.process.builder import BUILDING | |
| 1254 building_bs = [ builder | |
| 1255 for builder in builders | |
| 1256 if builder.slaves[0].state == BUILDING ] | |
| 1257 # assert that no builds are running right now. | |
| 1258 self.failUnlessEqual(len(building_bs), 0) | |
| 1259 d.addCallback(_check_shutdown) | |
| 1260 | |
| 1261 return d | |
| OLD | NEW |