| OLD | NEW |
| (Empty) |
| 1 # -*- test-case-name: buildbot.test.test_config -*- | |
| 2 | |
| 3 import os, warnings, exceptions | |
| 4 | |
| 5 from twisted.trial import unittest | |
| 6 from twisted.python import failure | |
| 7 from twisted.internet import defer | |
| 8 | |
| 9 from buildbot.master import BuildMaster | |
| 10 from buildbot import scheduler | |
| 11 from twisted.application import service, internet | |
| 12 from twisted.spread import pb | |
| 13 from twisted.web.server import Site | |
| 14 from twisted.web.distrib import ResourcePublisher | |
| 15 from buildbot.process.builder import Builder | |
| 16 from buildbot.process.factory import BasicBuildFactory, ArgumentsInTheWrongPlace | |
| 17 from buildbot.changes.pb import PBChangeSource | |
| 18 from buildbot.changes.mail import SyncmailMaildirSource | |
| 19 from buildbot.steps.source import CVS, Darcs | |
| 20 from buildbot.steps.shell import Compile, Test, ShellCommand | |
| 21 from buildbot.status import base | |
| 22 from buildbot.steps import dummy, maxq, python, python_twisted, shell, \ | |
| 23 source, transfer | |
| 24 words = None | |
| 25 try: | |
| 26 from buildbot.status import words | |
| 27 except ImportError: | |
| 28 pass | |
| 29 | |
| 30 emptyCfg = \ | |
| 31 """ | |
| 32 from buildbot.buildslave import BuildSlave | |
| 33 BuildmasterConfig = c = {} | |
| 34 c['slaves'] = [] | |
| 35 c['schedulers'] = [] | |
| 36 c['builders'] = [] | |
| 37 c['slavePortnum'] = 9999 | |
| 38 c['projectName'] = 'dummy project' | |
| 39 c['projectURL'] = 'http://dummy.example.com' | |
| 40 c['buildbotURL'] = 'http://dummy.example.com/buildbot' | |
| 41 """ | |
| 42 | |
| 43 buildersCfg = \ | |
| 44 """ | |
| 45 from buildbot.process.factory import BasicBuildFactory | |
| 46 from buildbot.buildslave import BuildSlave | |
| 47 BuildmasterConfig = c = {} | |
| 48 c['slaves'] = [BuildSlave('bot1', 'pw1')] | |
| 49 c['schedulers'] = [] | |
| 50 c['slavePortnum'] = 9999 | |
| 51 f1 = BasicBuildFactory('cvsroot', 'cvsmodule') | |
| 52 c['builders'] = [{'name':'builder1', 'slavename':'bot1', | |
| 53 'builddir':'workdir', 'factory':f1}] | |
| 54 """ | |
| 55 | |
| 56 buildersCfg2 = buildersCfg + \ | |
| 57 """ | |
| 58 f1 = BasicBuildFactory('cvsroot', 'cvsmodule2') | |
| 59 c['builders'] = [{'name':'builder1', 'slavename':'bot1', | |
| 60 'builddir':'workdir', 'factory':f1}] | |
| 61 """ | |
| 62 | |
| 63 buildersCfg3 = buildersCfg2 + \ | |
| 64 """ | |
| 65 c['builders'].append({'name': 'builder2', 'slavename': 'bot1', | |
| 66 'builddir': 'workdir2', 'factory': f1 }) | |
| 67 """ | |
| 68 | |
| 69 buildersCfg4 = buildersCfg2 + \ | |
| 70 """ | |
| 71 c['builders'] = [{ 'name': 'builder1', 'slavename': 'bot1', | |
| 72 'builddir': 'newworkdir', 'factory': f1 }, | |
| 73 { 'name': 'builder2', 'slavename': 'bot1', | |
| 74 'builddir': 'workdir2', 'factory': f1 }] | |
| 75 """ | |
| 76 | |
| 77 buildersCfg5 = buildersCfg2 + \ | |
| 78 """ | |
| 79 from buildbot.config import BuilderConfig | |
| 80 c['builders'] = [ | |
| 81 BuilderConfig( | |
| 82 name = 'builder1', | |
| 83 slavename = 'bot1', | |
| 84 builddir = 'newworkdir', | |
| 85 factory = f1), | |
| 86 BuilderConfig( | |
| 87 name = 'builder2', | |
| 88 slavename = 'bot1', | |
| 89 builddir = 'workdir2', | |
| 90 factory = f1) | |
| 91 ] | |
| 92 """ | |
| 93 | |
| 94 | |
| 95 wpCfg1 = buildersCfg + \ | |
| 96 """ | |
| 97 from buildbot.steps import shell | |
| 98 from buildbot.config import BuilderConfig | |
| 99 f1 = BasicBuildFactory('cvsroot', 'cvsmodule') | |
| 100 f1.addStep(shell.ShellCommand, command=[shell.WithProperties('echo')]) | |
| 101 c['builders'] = [ | |
| 102 BuilderConfig(name='builder1', slavename='bot1', | |
| 103 builddir='workdir1', factory=f1) | |
| 104 ] | |
| 105 """ | |
| 106 | |
| 107 wpCfg2 = buildersCfg + \ | |
| 108 """ | |
| 109 from buildbot.steps import shell | |
| 110 from buildbot.config import BuilderConfig | |
| 111 f1 = BasicBuildFactory('cvsroot', 'cvsmodule') | |
| 112 f1.addStep(shell.ShellCommand, | |
| 113 command=[shell.WithProperties('echo %s', 'revision')]) | |
| 114 c['builders'] = [ | |
| 115 BuilderConfig(name='builder1', slavename='bot1', | |
| 116 builddir='workdir1', factory=f1) | |
| 117 ] | |
| 118 """ | |
| 119 | |
| 120 | |
| 121 | |
| 122 ircCfg1 = emptyCfg + \ | |
| 123 """ | |
| 124 from buildbot.status import words | |
| 125 c['status'] = [words.IRC('irc.us.freenode.net', 'buildbot', ['twisted'])] | |
| 126 """ | |
| 127 | |
| 128 ircCfg2 = emptyCfg + \ | |
| 129 """ | |
| 130 from buildbot.status import words | |
| 131 c['status'] = [words.IRC('irc.us.freenode.net', 'buildbot', ['twisted']), | |
| 132 words.IRC('irc.example.com', 'otherbot', ['chan1', 'chan2'])] | |
| 133 """ | |
| 134 | |
| 135 ircCfg3 = emptyCfg + \ | |
| 136 """ | |
| 137 from buildbot.status import words | |
| 138 c['status'] = [words.IRC('irc.us.freenode.net', 'buildbot', ['knotted'])] | |
| 139 """ | |
| 140 | |
| 141 webCfg1 = emptyCfg + \ | |
| 142 """ | |
| 143 from buildbot.status import html | |
| 144 c['status'] = [html.Waterfall(http_port=9980)] | |
| 145 """ | |
| 146 | |
| 147 webCfg2 = emptyCfg + \ | |
| 148 """ | |
| 149 from buildbot.status import html | |
| 150 c['status'] = [html.Waterfall(http_port=9981)] | |
| 151 """ | |
| 152 | |
| 153 webCfg3 = emptyCfg + \ | |
| 154 """ | |
| 155 from buildbot.status import html | |
| 156 c['status'] = [html.Waterfall(http_port='tcp:9981:interface=127.0.0.1')] | |
| 157 """ | |
| 158 | |
| 159 webNameCfg1 = emptyCfg + \ | |
| 160 """ | |
| 161 from buildbot.status import html | |
| 162 c['status'] = [html.Waterfall(distrib_port='~/.twistd-web-pb')] | |
| 163 """ | |
| 164 | |
| 165 webNameCfg2 = emptyCfg + \ | |
| 166 """ | |
| 167 from buildbot.status import html | |
| 168 c['status'] = [html.Waterfall(distrib_port='./bar.socket')] | |
| 169 """ | |
| 170 | |
| 171 debugPasswordCfg = emptyCfg + \ | |
| 172 """ | |
| 173 c['debugPassword'] = 'sekrit' | |
| 174 """ | |
| 175 | |
| 176 interlockCfgBad = \ | |
| 177 """ | |
| 178 from buildbot.process.factory import BasicBuildFactory | |
| 179 from buildbot.buildslave import BuildSlave | |
| 180 from buildbot.config import BuilderConfig | |
| 181 c = {} | |
| 182 c['slaves'] = [BuildSlave('bot1', 'pw1')] | |
| 183 c['schedulers'] = [] | |
| 184 f1 = BasicBuildFactory('cvsroot', 'cvsmodule') | |
| 185 c['builders'] = [ | |
| 186 BuilderConfig(name='builder1', slavename='bot1', factory=f1), | |
| 187 BuilderConfig(name='builder2', slavename='bot1', factory=f1), | |
| 188 ] | |
| 189 # interlocks have been removed | |
| 190 c['interlocks'] = [('lock1', ['builder1'], ['builder2', 'builder3']), | |
| 191 ] | |
| 192 c['slavePortnum'] = 9999 | |
| 193 BuildmasterConfig = c | |
| 194 """ | |
| 195 | |
| 196 lockCfgBad1 = \ | |
| 197 """ | |
| 198 from buildbot.steps.dummy import Dummy | |
| 199 from buildbot.process.factory import BuildFactory, s | |
| 200 from buildbot.locks import MasterLock | |
| 201 from buildbot.buildslave import BuildSlave | |
| 202 from buildbot.config import BuilderConfig | |
| 203 c = {} | |
| 204 c['slaves'] = [BuildSlave('bot1', 'pw1')] | |
| 205 c['schedulers'] = [] | |
| 206 l1 = MasterLock('lock1') | |
| 207 l2 = MasterLock('lock1') # duplicate lock name | |
| 208 f1 = BuildFactory([s(Dummy, locks=[])]) | |
| 209 c['builders'] = [ | |
| 210 BuilderConfig(name='builder1', slavename='bot1', factory=f1, | |
| 211 locks=[l1, l2]), | |
| 212 BuilderConfig(name='builder2', slavename='bot1', factory=f1), | |
| 213 ] | |
| 214 c['slavePortnum'] = 9999 | |
| 215 BuildmasterConfig = c | |
| 216 """ | |
| 217 | |
| 218 lockCfgBad2 = \ | |
| 219 """ | |
| 220 from buildbot.steps.dummy import Dummy | |
| 221 from buildbot.process.factory import BuildFactory, s | |
| 222 from buildbot.locks import MasterLock, SlaveLock | |
| 223 from buildbot.buildslave import BuildSlave | |
| 224 from buildbot.config import BuilderConfig | |
| 225 c = {} | |
| 226 c['slaves'] = [BuildSlave('bot1', 'pw1')] | |
| 227 c['schedulers'] = [] | |
| 228 l1 = MasterLock('lock1') | |
| 229 l2 = SlaveLock('lock1') # duplicate lock name | |
| 230 f1 = BuildFactory([s(Dummy, locks=[])]) | |
| 231 c['builders'] = [ | |
| 232 BuilderConfig(name='builder1', slavename='bot1', factory=f1, | |
| 233 locks=[l1, l2]), | |
| 234 BuilderConfig(name='builder2', slavename='bot1', factory=f1), | |
| 235 ] | |
| 236 c['slavePortnum'] = 9999 | |
| 237 BuildmasterConfig = c | |
| 238 """ | |
| 239 | |
| 240 lockCfgBad3 = \ | |
| 241 """ | |
| 242 from buildbot.steps.dummy import Dummy | |
| 243 from buildbot.process.factory import BuildFactory, s | |
| 244 from buildbot.locks import MasterLock | |
| 245 from buildbot.buildslave import BuildSlave | |
| 246 c = {} | |
| 247 c['slaves'] = [BuildSlave('bot1', 'pw1')] | |
| 248 c['schedulers'] = [] | |
| 249 l1 = MasterLock('lock1') | |
| 250 l2 = MasterLock('lock1') # duplicate lock name | |
| 251 f1 = BuildFactory([s(Dummy, locks=[l2])]) | |
| 252 f2 = BuildFactory([s(Dummy)]) | |
| 253 c['builders'] = [ | |
| 254 { 'name': 'builder1', 'slavename': 'bot1', | |
| 255 'builddir': 'workdir', 'factory': f2, 'locks': [l1] }, | |
| 256 { 'name': 'builder2', 'slavename': 'bot1', | |
| 257 'builddir': 'workdir2', 'factory': f1 }, | |
| 258 ] | |
| 259 c['slavePortnum'] = 9999 | |
| 260 BuildmasterConfig = c | |
| 261 """ | |
| 262 | |
| 263 lockCfg1a = \ | |
| 264 """ | |
| 265 from buildbot.process.factory import BasicBuildFactory | |
| 266 from buildbot.locks import MasterLock | |
| 267 from buildbot.buildslave import BuildSlave | |
| 268 from buildbot.config import BuilderConfig | |
| 269 c = {} | |
| 270 c['slaves'] = [BuildSlave('bot1', 'pw1')] | |
| 271 c['schedulers'] = [] | |
| 272 f1 = BasicBuildFactory('cvsroot', 'cvsmodule') | |
| 273 l1 = MasterLock('lock1') | |
| 274 l2 = MasterLock('lock2') | |
| 275 c['builders'] = [ | |
| 276 BuilderConfig(name='builder1', slavename='bot1', factory=f1, | |
| 277 locks=[l1, l2]), | |
| 278 BuilderConfig(name='builder2', slavename='bot1', factory=f1), | |
| 279 ] | |
| 280 c['slavePortnum'] = 9999 | |
| 281 BuildmasterConfig = c | |
| 282 """ | |
| 283 | |
| 284 lockCfg1b = \ | |
| 285 """ | |
| 286 from buildbot.process.factory import BasicBuildFactory | |
| 287 from buildbot.locks import MasterLock | |
| 288 from buildbot.buildslave import BuildSlave | |
| 289 from buildbot.config import BuilderConfig | |
| 290 c = {} | |
| 291 c['slaves'] = [BuildSlave('bot1', 'pw1')] | |
| 292 c['schedulers'] = [] | |
| 293 f1 = BasicBuildFactory('cvsroot', 'cvsmodule') | |
| 294 l1 = MasterLock('lock1') | |
| 295 l2 = MasterLock('lock2') | |
| 296 c['builders'] = [ | |
| 297 BuilderConfig(name='builder1', slavename='bot1', factory=f1, locks=[l1]), | |
| 298 BuilderConfig(name='builder2', slavename='bot1', factory=f1), | |
| 299 ] | |
| 300 c['slavePortnum'] = 9999 | |
| 301 BuildmasterConfig = c | |
| 302 """ | |
| 303 | |
| 304 # test out step Locks | |
| 305 lockCfg2a = \ | |
| 306 """ | |
| 307 from buildbot.steps.dummy import Dummy | |
| 308 from buildbot.process.factory import BuildFactory, s | |
| 309 from buildbot.locks import MasterLock | |
| 310 from buildbot.buildslave import BuildSlave | |
| 311 from buildbot.config import BuilderConfig | |
| 312 c = {} | |
| 313 c['slaves'] = [BuildSlave('bot1', 'pw1')] | |
| 314 c['schedulers'] = [] | |
| 315 l1 = MasterLock('lock1') | |
| 316 l2 = MasterLock('lock2') | |
| 317 f1 = BuildFactory([s(Dummy, locks=[l1,l2])]) | |
| 318 f2 = BuildFactory([s(Dummy)]) | |
| 319 | |
| 320 c['builders'] = [ | |
| 321 BuilderConfig(name='builder1', slavename='bot1', factory=f1), | |
| 322 BuilderConfig(name='builder2', slavename='bot1', factory=f2), | |
| 323 ] | |
| 324 c['slavePortnum'] = 9999 | |
| 325 BuildmasterConfig = c | |
| 326 """ | |
| 327 | |
| 328 lockCfg2b = \ | |
| 329 """ | |
| 330 from buildbot.steps.dummy import Dummy | |
| 331 from buildbot.process.factory import BuildFactory, s | |
| 332 from buildbot.locks import MasterLock | |
| 333 from buildbot.buildslave import BuildSlave | |
| 334 from buildbot.config import BuilderConfig | |
| 335 c = {} | |
| 336 c['slaves'] = [BuildSlave('bot1', 'pw1')] | |
| 337 c['schedulers'] = [] | |
| 338 l1 = MasterLock('lock1') | |
| 339 l2 = MasterLock('lock2') | |
| 340 f1 = BuildFactory([s(Dummy, locks=[l1])]) | |
| 341 f2 = BuildFactory([s(Dummy)]) | |
| 342 | |
| 343 c['builders'] = [ | |
| 344 BuilderConfig(name='builder1', slavename='bot1', factory=f1), | |
| 345 BuilderConfig(name='builder2', slavename='bot1', factory=f2), | |
| 346 ] | |
| 347 c['slavePortnum'] = 9999 | |
| 348 BuildmasterConfig = c | |
| 349 """ | |
| 350 | |
| 351 lockCfg2c = \ | |
| 352 """ | |
| 353 from buildbot.steps.dummy import Dummy | |
| 354 from buildbot.process.factory import BuildFactory, s | |
| 355 from buildbot.locks import MasterLock | |
| 356 from buildbot.buildslave import BuildSlave | |
| 357 from buildbot.config import BuilderConfig | |
| 358 c = {} | |
| 359 c['slaves'] = [BuildSlave('bot1', 'pw1')] | |
| 360 c['schedulers'] = [] | |
| 361 l1 = MasterLock('lock1') | |
| 362 l2 = MasterLock('lock2') | |
| 363 f1 = BuildFactory([s(Dummy)]) | |
| 364 f2 = BuildFactory([s(Dummy)]) | |
| 365 | |
| 366 c['builders'] = [ | |
| 367 BuilderConfig(name='builder1', slavename='bot1', factory=f1), | |
| 368 BuilderConfig(name='builder2', slavename='bot1', factory=f2), | |
| 369 ] | |
| 370 c['slavePortnum'] = 9999 | |
| 371 BuildmasterConfig = c | |
| 372 """ | |
| 373 | |
| 374 schedulersCfg = \ | |
| 375 """ | |
| 376 from buildbot.scheduler import Scheduler, Dependent | |
| 377 from buildbot.process.factory import BasicBuildFactory | |
| 378 from buildbot.buildslave import BuildSlave | |
| 379 from buildbot.config import BuilderConfig | |
| 380 c = {} | |
| 381 c['slaves'] = [BuildSlave('bot1', 'pw1')] | |
| 382 f1 = BasicBuildFactory('cvsroot', 'cvsmodule') | |
| 383 b1 = BuilderConfig(name='builder1', slavename='bot1', factory=f1) | |
| 384 c['builders'] = [b1] | |
| 385 c['schedulers'] = [Scheduler('full', None, 60, ['builder1'])] | |
| 386 c['slavePortnum'] = 9999 | |
| 387 c['projectName'] = 'dummy project' | |
| 388 c['projectURL'] = 'http://dummy.example.com' | |
| 389 c['buildbotURL'] = 'http://dummy.example.com/buildbot' | |
| 390 BuildmasterConfig = c | |
| 391 """ | |
| 392 | |
| 393 class ConfigTest(unittest.TestCase): | |
| 394 def setUp(self): | |
| 395 # this class generates several deprecation warnings, which the user | |
| 396 # doesn't need to see. | |
| 397 warnings.simplefilter('ignore', exceptions.DeprecationWarning) | |
| 398 self.buildmaster = BuildMaster(".") | |
| 399 | |
| 400 def failUnlessListsEquivalent(self, list1, list2): | |
| 401 l1 = list1[:] | |
| 402 l1.sort() | |
| 403 l2 = list2[:] | |
| 404 l2.sort() | |
| 405 self.failUnlessEqual(l1, l2) | |
| 406 | |
| 407 def servers(self, s, types): | |
| 408 # perform a recursive search of s.services, looking for instances of | |
| 409 # twisted.application.internet.TCPServer, then extract their .args | |
| 410 # values to find the TCP ports they want to listen on | |
| 411 for child in s: | |
| 412 if service.IServiceCollection.providedBy(child): | |
| 413 for gc in self.servers(child, types): | |
| 414 yield gc | |
| 415 if isinstance(child, types): | |
| 416 yield child | |
| 417 | |
| 418 def TCPports(self, s): | |
| 419 return list(self.servers(s, internet.TCPServer)) | |
| 420 def UNIXports(self, s): | |
| 421 return list(self.servers(s, internet.UNIXServer)) | |
| 422 def TCPclients(self, s): | |
| 423 return list(self.servers(s, internet.TCPClient)) | |
| 424 | |
| 425 def checkPorts(self, svc, expected): | |
| 426 """Verify that the TCPServer and UNIXServer children of the given | |
| 427 service have the expected portnum/pathname and factory classes. As a | |
| 428 side-effect, return a list of servers in the same order as the | |
| 429 'expected' list. This can be used to verify properties of the | |
| 430 factories contained therein.""" | |
| 431 | |
| 432 expTCP = [e for e in expected if type(e[0]) == int] | |
| 433 expUNIX = [e for e in expected if type(e[0]) == str] | |
| 434 haveTCP = [(p.args[0], p.args[1].__class__) | |
| 435 for p in self.TCPports(svc)] | |
| 436 haveUNIX = [(p.args[0], p.args[1].__class__) | |
| 437 for p in self.UNIXports(svc)] | |
| 438 self.failUnlessListsEquivalent(expTCP, haveTCP) | |
| 439 self.failUnlessListsEquivalent(expUNIX, haveUNIX) | |
| 440 ret = [] | |
| 441 for e in expected: | |
| 442 for have in self.TCPports(svc) + self.UNIXports(svc): | |
| 443 if have.args[0] == e[0]: | |
| 444 ret.append(have) | |
| 445 continue | |
| 446 assert(len(ret) == len(expected)) | |
| 447 return ret | |
| 448 | |
| 449 def testEmpty(self): | |
| 450 self.failUnlessRaises(KeyError, self.buildmaster.loadConfig, "") | |
| 451 | |
| 452 def testSimple(self): | |
| 453 # covers slavePortnum, base checker passwords | |
| 454 master = self.buildmaster | |
| 455 master.loadChanges() | |
| 456 | |
| 457 master.loadConfig(emptyCfg) | |
| 458 # note: this doesn't actually start listening, because the app | |
| 459 # hasn't been started running | |
| 460 self.failUnlessEqual(master.slavePortnum, "tcp:9999") | |
| 461 self.checkPorts(master, [(9999, pb.PBServerFactory)]) | |
| 462 self.failUnlessEqual(list(master.change_svc), []) | |
| 463 self.failUnlessEqual(master.botmaster.builders, {}) | |
| 464 self.failUnlessEqual(master.checker.users, | |
| 465 {"change": "changepw"}) | |
| 466 self.failUnlessEqual(master.projectName, "dummy project") | |
| 467 self.failUnlessEqual(master.projectURL, "http://dummy.example.com") | |
| 468 self.failUnlessEqual(master.buildbotURL, | |
| 469 "http://dummy.example.com/buildbot") | |
| 470 | |
| 471 def testSlavePortnum(self): | |
| 472 master = self.buildmaster | |
| 473 master.loadChanges() | |
| 474 | |
| 475 master.loadConfig(emptyCfg) | |
| 476 self.failUnlessEqual(master.slavePortnum, "tcp:9999") | |
| 477 ports = self.checkPorts(master, [(9999, pb.PBServerFactory)]) | |
| 478 p = ports[0] | |
| 479 | |
| 480 master.loadConfig(emptyCfg) | |
| 481 self.failUnlessEqual(master.slavePortnum, "tcp:9999") | |
| 482 ports = self.checkPorts(master, [(9999, pb.PBServerFactory)]) | |
| 483 self.failUnlessIdentical(p, ports[0], | |
| 484 "the slave port was changed even " + \ | |
| 485 "though the configuration was not") | |
| 486 | |
| 487 master.loadConfig(emptyCfg + "c['slavePortnum'] = 9000\n") | |
| 488 self.failUnlessEqual(master.slavePortnum, "tcp:9000") | |
| 489 ports = self.checkPorts(master, [(9000, pb.PBServerFactory)]) | |
| 490 self.failIf(p is ports[0], | |
| 491 "slave port was unchanged but configuration was changed") | |
| 492 | |
| 493 def testSlaves(self): | |
| 494 master = self.buildmaster | |
| 495 master.loadChanges() | |
| 496 master.loadConfig(emptyCfg) | |
| 497 self.failUnlessEqual(master.botmaster.builders, {}) | |
| 498 self.failUnlessEqual(master.checker.users, | |
| 499 {"change": "changepw"}) | |
| 500 # 'botsCfg' is testing backwards compatibility, for 0.7.5 config | |
| 501 # files that have not yet been updated to 0.7.6 . This compatibility | |
| 502 # (and this test) is scheduled for removal in 0.8.0 . | |
| 503 botsCfg = (emptyCfg + | |
| 504 "c['bots'] = [('bot1', 'pw1'), ('bot2', 'pw2')]\n") | |
| 505 master.loadConfig(botsCfg) | |
| 506 self.failUnlessEqual(master.checker.users, | |
| 507 {"change": "changepw", | |
| 508 "bot1": "pw1", | |
| 509 "bot2": "pw2"}) | |
| 510 master.loadConfig(botsCfg) | |
| 511 self.failUnlessEqual(master.checker.users, | |
| 512 {"change": "changepw", | |
| 513 "bot1": "pw1", | |
| 514 "bot2": "pw2"}) | |
| 515 master.loadConfig(emptyCfg) | |
| 516 self.failUnlessEqual(master.checker.users, | |
| 517 {"change": "changepw"}) | |
| 518 slavesCfg = (emptyCfg + | |
| 519 "from buildbot.buildslave import BuildSlave\n" | |
| 520 "c['slaves'] = [BuildSlave('bot1','pw1'), " | |
| 521 "BuildSlave('bot2','pw2')]\n") | |
| 522 master.loadConfig(slavesCfg) | |
| 523 self.failUnlessEqual(master.checker.users, | |
| 524 {"change": "changepw", | |
| 525 "bot1": "pw1", | |
| 526 "bot2": "pw2"}) | |
| 527 | |
| 528 | |
| 529 def testChangeSource(self): | |
| 530 master = self.buildmaster | |
| 531 master.loadChanges() | |
| 532 master.loadConfig(emptyCfg) | |
| 533 self.failUnlessEqual(list(master.change_svc), []) | |
| 534 | |
| 535 sourcesCfg = emptyCfg + \ | |
| 536 """ | |
| 537 from buildbot.changes.pb import PBChangeSource | |
| 538 c['change_source'] = PBChangeSource() | |
| 539 """ | |
| 540 | |
| 541 d = master.loadConfig(sourcesCfg) | |
| 542 def _check1(res): | |
| 543 self.failUnlessEqual(len(list(self.buildmaster.change_svc)), 1) | |
| 544 s1 = list(self.buildmaster.change_svc)[0] | |
| 545 self.failUnless(isinstance(s1, PBChangeSource)) | |
| 546 self.failUnlessEqual(s1, list(self.buildmaster.change_svc)[0]) | |
| 547 self.failUnless(s1.parent) | |
| 548 | |
| 549 # verify that unchanged sources are not interrupted | |
| 550 d1 = self.buildmaster.loadConfig(sourcesCfg) | |
| 551 | |
| 552 def _check2(res): | |
| 553 self.failUnlessEqual(len(list(self.buildmaster.change_svc)), 1) | |
| 554 s2 = list(self.buildmaster.change_svc)[0] | |
| 555 self.failUnlessIdentical(s1, s2) | |
| 556 self.failUnless(s1.parent) | |
| 557 d1.addCallback(_check2) | |
| 558 return d1 | |
| 559 d.addCallback(_check1) | |
| 560 | |
| 561 # make sure we can get rid of the sources too | |
| 562 d.addCallback(lambda res: self.buildmaster.loadConfig(emptyCfg)) | |
| 563 | |
| 564 def _check3(res): | |
| 565 self.failUnlessEqual(list(self.buildmaster.change_svc), []) | |
| 566 d.addCallback(_check3) | |
| 567 | |
| 568 return d | |
| 569 | |
| 570 def testChangeSources(self): | |
| 571 # make sure we can accept a list | |
| 572 master = self.buildmaster | |
| 573 master.loadChanges() | |
| 574 master.loadConfig(emptyCfg) | |
| 575 self.failUnlessEqual(list(master.change_svc), []) | |
| 576 | |
| 577 sourcesCfg = emptyCfg + \ | |
| 578 """ | |
| 579 from buildbot.changes.pb import PBChangeSource | |
| 580 from buildbot.changes.mail import SyncmailMaildirSource | |
| 581 c['change_source'] = [PBChangeSource(), | |
| 582 SyncmailMaildirSource('.'), | |
| 583 ] | |
| 584 """ | |
| 585 | |
| 586 d = master.loadConfig(sourcesCfg) | |
| 587 def _check1(res): | |
| 588 self.failUnlessEqual(len(list(self.buildmaster.change_svc)), 2) | |
| 589 s1,s2 = list(self.buildmaster.change_svc) | |
| 590 if isinstance(s2, PBChangeSource): | |
| 591 s1,s2 = s2,s1 | |
| 592 self.failUnless(isinstance(s1, PBChangeSource)) | |
| 593 self.failUnless(s1.parent) | |
| 594 self.failUnless(isinstance(s2, SyncmailMaildirSource)) | |
| 595 self.failUnless(s2.parent) | |
| 596 d.addCallback(_check1) | |
| 597 return d | |
| 598 | |
| 599 def testSources(self): | |
| 600 # test backwards compatibility. c['sources'] is deprecated. | |
| 601 master = self.buildmaster | |
| 602 master.loadChanges() | |
| 603 master.loadConfig(emptyCfg) | |
| 604 self.failUnlessEqual(list(master.change_svc), []) | |
| 605 | |
| 606 sourcesCfg = emptyCfg + \ | |
| 607 """ | |
| 608 from buildbot.changes.pb import PBChangeSource | |
| 609 c['sources'] = [PBChangeSource()] | |
| 610 """ | |
| 611 | |
| 612 d = master.loadConfig(sourcesCfg) | |
| 613 def _check1(res): | |
| 614 self.failUnlessEqual(len(list(self.buildmaster.change_svc)), 1) | |
| 615 s1 = list(self.buildmaster.change_svc)[0] | |
| 616 self.failUnless(isinstance(s1, PBChangeSource)) | |
| 617 self.failUnless(s1.parent) | |
| 618 d.addCallback(_check1) | |
| 619 return d | |
| 620 | |
| 621 def shouldBeFailure(self, res, *expected): | |
| 622 self.failUnless(isinstance(res, failure.Failure), | |
| 623 "we expected this to fail, not produce %s" % (res,)) | |
| 624 res.trap(*expected) | |
| 625 return None # all is good | |
| 626 | |
| 627 def testSchedulerErrors(self): | |
| 628 master = self.buildmaster | |
| 629 master.loadChanges() | |
| 630 master.loadConfig(emptyCfg) | |
| 631 self.failUnlessEqual(master.allSchedulers(), []) | |
| 632 | |
| 633 def _shouldBeFailure(res, hint=None): | |
| 634 self.shouldBeFailure(res, AssertionError, ValueError) | |
| 635 if hint: | |
| 636 self.failUnless(str(res).find(hint) != -1) | |
| 637 | |
| 638 def _loadConfig(res, newcfg): | |
| 639 return self.buildmaster.loadConfig(newcfg) | |
| 640 d = defer.succeed(None) | |
| 641 | |
| 642 # c['schedulers'] must be a list | |
| 643 badcfg = schedulersCfg + \ | |
| 644 """ | |
| 645 c['schedulers'] = Scheduler('full', None, 60, ['builder1']) | |
| 646 """ | |
| 647 d.addCallback(_loadConfig, badcfg) | |
| 648 d.addBoth(_shouldBeFailure, | |
| 649 "c['schedulers'] must be a list of Scheduler instances") | |
| 650 | |
| 651 # c['schedulers'] must be a list of IScheduler objects | |
| 652 badcfg = schedulersCfg + \ | |
| 653 """ | |
| 654 c['schedulers'] = ['oops', 'problem'] | |
| 655 """ | |
| 656 d.addCallback(_loadConfig, badcfg) | |
| 657 d.addBoth(_shouldBeFailure, | |
| 658 "c['schedulers'] must be a list of Scheduler instances") | |
| 659 | |
| 660 # c['schedulers'] must point at real builders | |
| 661 badcfg = schedulersCfg + \ | |
| 662 """ | |
| 663 c['schedulers'] = [Scheduler('full', None, 60, ['builder-bogus'])] | |
| 664 """ | |
| 665 d.addCallback(_loadConfig, badcfg) | |
| 666 d.addBoth(_shouldBeFailure, "uses unknown builder") | |
| 667 | |
| 668 # builderNames= must be a list | |
| 669 badcfg = schedulersCfg + \ | |
| 670 """ | |
| 671 c['schedulers'] = [Scheduler('full', None, 60, 'builder1')] | |
| 672 """ | |
| 673 d.addCallback(_loadConfig, badcfg) | |
| 674 d.addBoth(_shouldBeFailure, | |
| 675 "must be a list of Builder description names") | |
| 676 | |
| 677 # builderNames= must be a list of strings, not dicts | |
| 678 badcfg = schedulersCfg + \ | |
| 679 """ | |
| 680 c['schedulers'] = [Scheduler('full', None, 60, [b1])] | |
| 681 """ | |
| 682 d.addCallback(_loadConfig, badcfg) | |
| 683 d.addBoth(_shouldBeFailure, | |
| 684 "must be a list of Builder description names") | |
| 685 | |
| 686 # builderNames= must be a list of strings, not a dict | |
| 687 badcfg = schedulersCfg + \ | |
| 688 """ | |
| 689 c['schedulers'] = [Scheduler('full', None, 60, b1)] | |
| 690 """ | |
| 691 d.addCallback(_loadConfig, badcfg) | |
| 692 d.addBoth(_shouldBeFailure, | |
| 693 "must be a list of Builder description names") | |
| 694 | |
| 695 # each Scheduler must have a unique name | |
| 696 badcfg = schedulersCfg + \ | |
| 697 """ | |
| 698 c['schedulers'] = [Scheduler('dup', None, 60, []), | |
| 699 Scheduler('dup', None, 60, [])] | |
| 700 """ | |
| 701 d.addCallback(_loadConfig, badcfg) | |
| 702 d.addBoth(_shouldBeFailure, "Schedulers must have unique names") | |
| 703 | |
| 704 return d | |
| 705 | |
| 706 def testSchedulers(self): | |
| 707 master = self.buildmaster | |
| 708 master.loadChanges() | |
| 709 master.loadConfig(emptyCfg) | |
| 710 self.failUnlessEqual(master.allSchedulers(), []) | |
| 711 | |
| 712 d = self.buildmaster.loadConfig(schedulersCfg) | |
| 713 d.addCallback(self._testSchedulers_1) | |
| 714 return d | |
| 715 | |
| 716 def _testSchedulers_1(self, res): | |
| 717 sch = self.buildmaster.allSchedulers() | |
| 718 self.failUnlessEqual(len(sch), 1) | |
| 719 s = sch[0] | |
| 720 self.failUnless(isinstance(s, scheduler.Scheduler)) | |
| 721 self.failUnlessEqual(s.name, "full") | |
| 722 self.failUnlessEqual(s.branch, None) | |
| 723 self.failUnlessEqual(s.treeStableTimer, 60) | |
| 724 self.failUnlessEqual(s.builderNames, ['builder1']) | |
| 725 | |
| 726 newcfg = schedulersCfg + \ | |
| 727 """ | |
| 728 s1 = Scheduler('full', None, 60, ['builder1']) | |
| 729 c['schedulers'] = [s1, Dependent('downstream', s1, ['builder1'])] | |
| 730 """ | |
| 731 d = self.buildmaster.loadConfig(newcfg) | |
| 732 d.addCallback(self._testSchedulers_2, newcfg) | |
| 733 return d | |
| 734 def _testSchedulers_2(self, res, newcfg): | |
| 735 sch = self.buildmaster.allSchedulers() | |
| 736 self.failUnlessEqual(len(sch), 2) | |
| 737 s = sch[0] | |
| 738 self.failUnless(isinstance(s, scheduler.Scheduler)) | |
| 739 s = sch[1] | |
| 740 self.failUnless(isinstance(s, scheduler.Dependent)) | |
| 741 self.failUnlessEqual(s.name, "downstream") | |
| 742 self.failUnlessEqual(s.builderNames, ['builder1']) | |
| 743 | |
| 744 # reloading the same config file should leave the schedulers in place | |
| 745 d = self.buildmaster.loadConfig(newcfg) | |
| 746 d.addCallback(self._testSchedulers_3, sch) | |
| 747 return d | |
| 748 def _testSchedulers_3(self, res, sch1): | |
| 749 sch2 = self.buildmaster.allSchedulers() | |
| 750 self.failUnlessEqual(len(sch2), 2) | |
| 751 sch1.sort() | |
| 752 sch2.sort() | |
| 753 self.failUnlessEqual(sch1, sch2) | |
| 754 self.failUnlessIdentical(sch1[0], sch2[0]) | |
| 755 self.failUnlessIdentical(sch1[1], sch2[1]) | |
| 756 self.failUnlessIdentical(sch1[0].parent, self.buildmaster) | |
| 757 self.failUnlessIdentical(sch1[1].parent, self.buildmaster) | |
| 758 | |
| 759 | |
| 760 | |
| 761 def testBuilders(self): | |
| 762 master = self.buildmaster | |
| 763 master.loadConfig(emptyCfg) | |
| 764 self.failUnlessEqual(master.botmaster.builders, {}) | |
| 765 | |
| 766 master.loadConfig(buildersCfg) | |
| 767 self.failUnlessEqual(master.botmaster.builderNames, ["builder1"]) | |
| 768 self.failUnlessEqual(master.botmaster.builders.keys(), ["builder1"]) | |
| 769 b = master.botmaster.builders["builder1"] | |
| 770 self.failUnless(isinstance(b, Builder)) | |
| 771 self.failUnlessEqual(b.name, "builder1") | |
| 772 self.failUnlessEqual(b.slavenames, ["bot1"]) | |
| 773 self.failUnlessEqual(b.builddir, "workdir") | |
| 774 f1 = b.buildFactory | |
| 775 self.failUnless(isinstance(f1, BasicBuildFactory)) | |
| 776 steps = f1.steps | |
| 777 self.failUnlessEqual(len(steps), 3) | |
| 778 self.failUnlessEqual(steps[0], (CVS, | |
| 779 {'cvsroot': 'cvsroot', | |
| 780 'cvsmodule': 'cvsmodule', | |
| 781 'mode': 'clobber'})) | |
| 782 self.failUnlessEqual(steps[1], (Compile, | |
| 783 {'command': 'make all'})) | |
| 784 self.failUnlessEqual(steps[2], (Test, | |
| 785 {'command': 'make check'})) | |
| 786 | |
| 787 | |
| 788 # make sure a reload of the same data doesn't interrupt the Builder | |
| 789 master.loadConfig(buildersCfg) | |
| 790 self.failUnlessEqual(master.botmaster.builderNames, ["builder1"]) | |
| 791 self.failUnlessEqual(master.botmaster.builders.keys(), ["builder1"]) | |
| 792 b2 = master.botmaster.builders["builder1"] | |
| 793 self.failUnlessIdentical(b, b2) | |
| 794 # TODO: test that the BuilderStatus object doesn't change | |
| 795 #statusbag2 = master.client_svc.statusbags["builder1"] | |
| 796 #self.failUnlessIdentical(statusbag, statusbag2) | |
| 797 | |
| 798 # but changing something should result in a new Builder | |
| 799 master.loadConfig(buildersCfg2) | |
| 800 self.failUnlessEqual(master.botmaster.builderNames, ["builder1"]) | |
| 801 self.failUnlessEqual(master.botmaster.builders.keys(), ["builder1"]) | |
| 802 b3 = master.botmaster.builders["builder1"] | |
| 803 self.failIf(b is b3) | |
| 804 # the statusbag remains the same TODO | |
| 805 #statusbag3 = master.client_svc.statusbags["builder1"] | |
| 806 #self.failUnlessIdentical(statusbag, statusbag3) | |
| 807 | |
| 808 # adding new builder | |
| 809 master.loadConfig(buildersCfg3) | |
| 810 self.failUnlessEqual(master.botmaster.builderNames, ["builder1", | |
| 811 "builder2"]) | |
| 812 self.failUnlessListsEquivalent(master.botmaster.builders.keys(), | |
| 813 ["builder1", "builder2"]) | |
| 814 b4 = master.botmaster.builders["builder1"] | |
| 815 self.failUnlessIdentical(b3, b4) | |
| 816 | |
| 817 # changing first builder should leave it at the same place in the list | |
| 818 master.loadConfig(buildersCfg4) | |
| 819 self.failUnlessEqual(master.botmaster.builderNames, ["builder1", | |
| 820 "builder2"]) | |
| 821 self.failUnlessListsEquivalent(master.botmaster.builders.keys(), | |
| 822 ["builder1", "builder2"]) | |
| 823 b5 = master.botmaster.builders["builder1"] | |
| 824 self.failIf(b4 is b5) | |
| 825 | |
| 826 master.loadConfig(buildersCfg5) | |
| 827 self.failUnlessEqual(master.botmaster.builderNames, ["builder1", | |
| 828 "builder2"]) | |
| 829 self.failUnlessListsEquivalent(master.botmaster.builders.keys(), | |
| 830 ["builder1", "builder2"]) | |
| 831 b5 = master.botmaster.builders["builder1"] | |
| 832 | |
| 833 # and removing it should make the Builder go away | |
| 834 master.loadConfig(emptyCfg) | |
| 835 self.failUnlessEqual(master.botmaster.builderNames, []) | |
| 836 self.failUnlessEqual(master.botmaster.builders, {}) | |
| 837 #self.failUnlessEqual(master.client_svc.statusbags, {}) # TODO | |
| 838 | |
| 839 def testWithProperties(self): | |
| 840 master = self.buildmaster | |
| 841 master.loadConfig(wpCfg1) | |
| 842 self.failUnlessEqual(master.botmaster.builderNames, ["builder1"]) | |
| 843 self.failUnlessEqual(master.botmaster.builders.keys(), ["builder1"]) | |
| 844 b1 = master.botmaster.builders["builder1"] | |
| 845 | |
| 846 # reloading the same config should leave the builder unchanged | |
| 847 master.loadConfig(wpCfg1) | |
| 848 b2 = master.botmaster.builders["builder1"] | |
| 849 self.failUnlessIdentical(b1, b2) | |
| 850 | |
| 851 # but changing the parameters of the WithProperties should change it | |
| 852 master.loadConfig(wpCfg2) | |
| 853 b3 = master.botmaster.builders["builder1"] | |
| 854 self.failIf(b1 is b3) | |
| 855 | |
| 856 # again, reloading same config should leave the builder unchanged | |
| 857 master.loadConfig(wpCfg2) | |
| 858 b4 = master.botmaster.builders["builder1"] | |
| 859 self.failUnlessIdentical(b3, b4) | |
| 860 | |
| 861 def checkIRC(self, m, expected): | |
| 862 ircs = {} | |
| 863 for irc in self.servers(m, words.IRC): | |
| 864 ircs[irc.host] = (irc.nick, irc.channels) | |
| 865 self.failUnlessEqual(ircs, expected) | |
| 866 | |
| 867 def testIRC(self): | |
| 868 if not words: | |
| 869 raise unittest.SkipTest("Twisted Words package is not installed") | |
| 870 master = self.buildmaster | |
| 871 master.loadChanges() | |
| 872 d = master.loadConfig(emptyCfg) | |
| 873 e1 = {} | |
| 874 d.addCallback(lambda res: self.checkIRC(master, e1)) | |
| 875 d.addCallback(lambda res: master.loadConfig(ircCfg1)) | |
| 876 e2 = {'irc.us.freenode.net': ('buildbot', ['twisted'])} | |
| 877 d.addCallback(lambda res: self.checkIRC(master, e2)) | |
| 878 d.addCallback(lambda res: master.loadConfig(ircCfg2)) | |
| 879 e3 = {'irc.us.freenode.net': ('buildbot', ['twisted']), | |
| 880 'irc.example.com': ('otherbot', ['chan1', 'chan2'])} | |
| 881 d.addCallback(lambda res: self.checkIRC(master, e3)) | |
| 882 d.addCallback(lambda res: master.loadConfig(ircCfg3)) | |
| 883 e4 = {'irc.us.freenode.net': ('buildbot', ['knotted'])} | |
| 884 d.addCallback(lambda res: self.checkIRC(master, e4)) | |
| 885 d.addCallback(lambda res: master.loadConfig(ircCfg1)) | |
| 886 e5 = {'irc.us.freenode.net': ('buildbot', ['twisted'])} | |
| 887 d.addCallback(lambda res: self.checkIRC(master, e5)) | |
| 888 return d | |
| 889 | |
| 890 def testWebPortnum(self): | |
| 891 master = self.buildmaster | |
| 892 master.loadChanges() | |
| 893 | |
| 894 d = master.loadConfig(webCfg1) | |
| 895 def _check1(res): | |
| 896 ports = self.checkPorts(self.buildmaster, | |
| 897 [(9999, pb.PBServerFactory), (9980, Site)]) | |
| 898 p = ports[1] | |
| 899 self.p = p | |
| 900 # nothing should be changed | |
| 901 d.addCallback(_check1) | |
| 902 | |
| 903 d.addCallback(lambda res: self.buildmaster.loadConfig(webCfg1)) | |
| 904 def _check2(res): | |
| 905 ports = self.checkPorts(self.buildmaster, | |
| 906 [(9999, pb.PBServerFactory), (9980, Site)]) | |
| 907 self.failUnlessIdentical(self.p, ports[1], | |
| 908 "web port was changed even though " | |
| 909 "configuration was not") | |
| 910 # WebStatus is no longer a ComparableMixin, so it will be | |
| 911 # rebuilt on each reconfig | |
| 912 #d.addCallback(_check2) | |
| 913 | |
| 914 d.addCallback(lambda res: self.buildmaster.loadConfig(webCfg2)) | |
| 915 # changes port to 9981 | |
| 916 def _check3(p): | |
| 917 ports = self.checkPorts(self.buildmaster, | |
| 918 [(9999, pb.PBServerFactory), (9981, Site)]) | |
| 919 self.failIf(self.p is ports[1], | |
| 920 "configuration was changed but web port was unchanged") | |
| 921 d.addCallback(_check3) | |
| 922 | |
| 923 d.addCallback(lambda res: self.buildmaster.loadConfig(webCfg3)) | |
| 924 # make 9981 on only localhost | |
| 925 def _check4(p): | |
| 926 ports = self.checkPorts(self.buildmaster, | |
| 927 [(9999, pb.PBServerFactory), (9981, Site)]) | |
| 928 self.failUnlessEqual(ports[1].kwargs['interface'], "127.0.0.1") | |
| 929 d.addCallback(_check4) | |
| 930 | |
| 931 d.addCallback(lambda res: self.buildmaster.loadConfig(emptyCfg)) | |
| 932 d.addCallback(lambda res: | |
| 933 self.checkPorts(self.buildmaster, | |
| 934 [(9999, pb.PBServerFactory)])) | |
| 935 return d | |
| 936 | |
| 937 def testWebPathname(self): | |
| 938 master = self.buildmaster | |
| 939 master.loadChanges() | |
| 940 | |
| 941 d = master.loadConfig(webNameCfg1) | |
| 942 def _check1(res): | |
| 943 self.checkPorts(self.buildmaster, | |
| 944 [(9999, pb.PBServerFactory), | |
| 945 ('~/.twistd-web-pb', pb.PBServerFactory)]) | |
| 946 unixports = self.UNIXports(self.buildmaster) | |
| 947 self.f = f = unixports[0].args[1] | |
| 948 self.failUnless(isinstance(f.root, ResourcePublisher)) | |
| 949 d.addCallback(_check1) | |
| 950 | |
| 951 d.addCallback(lambda res: self.buildmaster.loadConfig(webNameCfg1)) | |
| 952 # nothing should be changed | |
| 953 def _check2(res): | |
| 954 self.checkPorts(self.buildmaster, | |
| 955 [(9999, pb.PBServerFactory), | |
| 956 ('~/.twistd-web-pb', pb.PBServerFactory)]) | |
| 957 newf = self.UNIXports(self.buildmaster)[0].args[1] | |
| 958 self.failUnlessIdentical(self.f, newf, | |
| 959 "web factory was changed even though " | |
| 960 "configuration was not") | |
| 961 # WebStatus is no longer a ComparableMixin, so it will be | |
| 962 # rebuilt on each reconfig | |
| 963 #d.addCallback(_check2) | |
| 964 | |
| 965 d.addCallback(lambda res: self.buildmaster.loadConfig(webNameCfg2)) | |
| 966 def _check3(res): | |
| 967 self.checkPorts(self.buildmaster, | |
| 968 [(9999, pb.PBServerFactory), | |
| 969 ('./bar.socket', pb.PBServerFactory)]) | |
| 970 newf = self.UNIXports(self.buildmaster)[0].args[1], | |
| 971 self.failIf(self.f is newf, | |
| 972 "web factory was unchanged but " | |
| 973 "configuration was changed") | |
| 974 d.addCallback(_check3) | |
| 975 | |
| 976 d.addCallback(lambda res: self.buildmaster.loadConfig(emptyCfg)) | |
| 977 d.addCallback(lambda res: | |
| 978 self.checkPorts(self.buildmaster, | |
| 979 [(9999, pb.PBServerFactory)])) | |
| 980 return d | |
| 981 | |
| 982 def testDebugPassword(self): | |
| 983 master = self.buildmaster | |
| 984 | |
| 985 master.loadConfig(debugPasswordCfg) | |
| 986 self.failUnlessEqual(master.checker.users, | |
| 987 {"change": "changepw", | |
| 988 "debug": "sekrit"}) | |
| 989 | |
| 990 master.loadConfig(debugPasswordCfg) | |
| 991 self.failUnlessEqual(master.checker.users, | |
| 992 {"change": "changepw", | |
| 993 "debug": "sekrit"}) | |
| 994 | |
| 995 master.loadConfig(emptyCfg) | |
| 996 self.failUnlessEqual(master.checker.users, | |
| 997 {"change": "changepw"}) | |
| 998 | |
| 999 def testLocks(self): | |
| 1000 master = self.buildmaster | |
| 1001 botmaster = master.botmaster | |
| 1002 | |
| 1003 # make sure that c['interlocks'] is rejected properly | |
| 1004 self.failUnlessRaises(KeyError, master.loadConfig, interlockCfgBad) | |
| 1005 # and that duplicate-named Locks are caught | |
| 1006 self.failUnlessRaises(ValueError, master.loadConfig, lockCfgBad1) | |
| 1007 self.failUnlessRaises(ValueError, master.loadConfig, lockCfgBad2) | |
| 1008 self.failUnlessRaises(ValueError, master.loadConfig, lockCfgBad3) | |
| 1009 | |
| 1010 # create a Builder that uses Locks | |
| 1011 master.loadConfig(lockCfg1a) | |
| 1012 b1 = master.botmaster.builders["builder1"] | |
| 1013 self.failUnlessEqual(len(b1.locks), 2) | |
| 1014 | |
| 1015 # reloading the same config should not change the Builder | |
| 1016 master.loadConfig(lockCfg1a) | |
| 1017 self.failUnlessIdentical(b1, master.botmaster.builders["builder1"]) | |
| 1018 # but changing the set of locks used should change it | |
| 1019 master.loadConfig(lockCfg1b) | |
| 1020 self.failIfIdentical(b1, master.botmaster.builders["builder1"]) | |
| 1021 b1 = master.botmaster.builders["builder1"] | |
| 1022 self.failUnlessEqual(len(b1.locks), 1) | |
| 1023 | |
| 1024 # similar test with step-scoped locks | |
| 1025 master.loadConfig(lockCfg2a) | |
| 1026 b1 = master.botmaster.builders["builder1"] | |
| 1027 # reloading the same config should not change the Builder | |
| 1028 master.loadConfig(lockCfg2a) | |
| 1029 self.failUnlessIdentical(b1, master.botmaster.builders["builder1"]) | |
| 1030 # but changing the set of locks used should change it | |
| 1031 master.loadConfig(lockCfg2b) | |
| 1032 self.failIfIdentical(b1, master.botmaster.builders["builder1"]) | |
| 1033 b1 = master.botmaster.builders["builder1"] | |
| 1034 # remove the locks entirely | |
| 1035 master.loadConfig(lockCfg2c) | |
| 1036 self.failIfIdentical(b1, master.botmaster.builders["builder1"]) | |
| 1037 | |
| 1038 def testNoChangeHorizon(self): | |
| 1039 master = self.buildmaster | |
| 1040 master.loadChanges() | |
| 1041 sourcesCfg = emptyCfg + \ | |
| 1042 """ | |
| 1043 from buildbot.changes.pb import PBChangeSource | |
| 1044 c['change_source'] = PBChangeSource() | |
| 1045 """ | |
| 1046 d = master.loadConfig(sourcesCfg) | |
| 1047 def _check1(res): | |
| 1048 self.failUnlessEqual(len(list(self.buildmaster.change_svc)), 1) | |
| 1049 self.failUnlessEqual(self.buildmaster.change_svc.changeHorizon, 0) | |
| 1050 d.addCallback(_check1) | |
| 1051 return d | |
| 1052 | |
| 1053 def testChangeHorizon(self): | |
| 1054 master = self.buildmaster | |
| 1055 master.loadChanges() | |
| 1056 sourcesCfg = emptyCfg + \ | |
| 1057 """ | |
| 1058 from buildbot.changes.pb import PBChangeSource | |
| 1059 c['change_source'] = PBChangeSource() | |
| 1060 c['changeHorizon'] = 5 | |
| 1061 """ | |
| 1062 d = master.loadConfig(sourcesCfg) | |
| 1063 def _check1(res): | |
| 1064 self.failUnlessEqual(len(list(self.buildmaster.change_svc)), 1) | |
| 1065 self.failUnlessEqual(self.buildmaster.change_svc.changeHorizon, 5) | |
| 1066 d.addCallback(_check1) | |
| 1067 return d | |
| 1068 | |
| 1069 class ConfigElements(unittest.TestCase): | |
| 1070 # verify that ComparableMixin is working | |
| 1071 def testSchedulers(self): | |
| 1072 s1 = scheduler.Scheduler(name='quick', branch=None, | |
| 1073 treeStableTimer=30, | |
| 1074 builderNames=['quick']) | |
| 1075 s2 = scheduler.Scheduler(name="all", branch=None, | |
| 1076 treeStableTimer=5*60, | |
| 1077 builderNames=["a", "b"]) | |
| 1078 s3 = scheduler.Try_Userpass("try", ["a","b"], port=9989, | |
| 1079 userpass=[("foo","bar")]) | |
| 1080 s1a = scheduler.Scheduler(name='quick', branch=None, | |
| 1081 treeStableTimer=30, | |
| 1082 builderNames=['quick']) | |
| 1083 s2a = scheduler.Scheduler(name="all", branch=None, | |
| 1084 treeStableTimer=5*60, | |
| 1085 builderNames=["a", "b"]) | |
| 1086 s3a = scheduler.Try_Userpass("try", ["a","b"], port=9989, | |
| 1087 userpass=[("foo","bar")]) | |
| 1088 self.failUnless(s1 == s1) | |
| 1089 self.failUnless(s1 == s1a) | |
| 1090 self.failUnless(s1a in [s1, s2, s3]) | |
| 1091 self.failUnless(s2a in [s1, s2, s3]) | |
| 1092 self.failUnless(s3a in [s1, s2, s3]) | |
| 1093 | |
| 1094 | |
| 1095 | |
| 1096 class ConfigFileTest(unittest.TestCase): | |
| 1097 | |
| 1098 def testFindConfigFile(self): | |
| 1099 os.mkdir("test_cf") | |
| 1100 open(os.path.join("test_cf", "master.cfg"), "w").write(emptyCfg) | |
| 1101 slaveportCfg = emptyCfg + "c['slavePortnum'] = 9000\n" | |
| 1102 open(os.path.join("test_cf", "alternate.cfg"), "w").write(slaveportCfg) | |
| 1103 | |
| 1104 m = BuildMaster("test_cf") | |
| 1105 m.loadTheConfigFile() | |
| 1106 self.failUnlessEqual(m.slavePortnum, "tcp:9999") | |
| 1107 | |
| 1108 m = BuildMaster("test_cf", "alternate.cfg") | |
| 1109 m.loadTheConfigFile() | |
| 1110 self.failUnlessEqual(m.slavePortnum, "tcp:9000") | |
| 1111 | |
| 1112 | |
| 1113 class MyTarget(base.StatusReceiverMultiService): | |
| 1114 def __init__(self, name): | |
| 1115 self.name = name | |
| 1116 base.StatusReceiverMultiService.__init__(self) | |
| 1117 def startService(self): | |
| 1118 # make a note in a list stashed in the BuildMaster | |
| 1119 self.parent.targetevents.append(("start", self.name)) | |
| 1120 return base.StatusReceiverMultiService.startService(self) | |
| 1121 def stopService(self): | |
| 1122 self.parent.targetevents.append(("stop", self.name)) | |
| 1123 return base.StatusReceiverMultiService.stopService(self) | |
| 1124 | |
| 1125 class MySlowTarget(MyTarget): | |
| 1126 def stopService(self): | |
| 1127 from twisted.internet import reactor | |
| 1128 d = base.StatusReceiverMultiService.stopService(self) | |
| 1129 def stall(res): | |
| 1130 d2 = defer.Deferred() | |
| 1131 reactor.callLater(0.1, d2.callback, res) | |
| 1132 return d2 | |
| 1133 d.addCallback(stall) | |
| 1134 m = self.parent | |
| 1135 def finishedStalling(res): | |
| 1136 m.targetevents.append(("stop", self.name)) | |
| 1137 return res | |
| 1138 d.addCallback(finishedStalling) | |
| 1139 return d | |
| 1140 | |
| 1141 # we can't actually startService a buildmaster with a config that uses a | |
| 1142 # fixed slavePortnum like 9999, so instead this makes it possible to pass '0' | |
| 1143 # for the first time, and then substitute back in the allocated port number | |
| 1144 # on subsequent passes. | |
| 1145 startableEmptyCfg = emptyCfg + \ | |
| 1146 """ | |
| 1147 c['slavePortnum'] = %d | |
| 1148 """ | |
| 1149 | |
| 1150 targetCfg1 = startableEmptyCfg + \ | |
| 1151 """ | |
| 1152 from buildbot.test.test_config import MyTarget | |
| 1153 c['status'] = [MyTarget('a')] | |
| 1154 """ | |
| 1155 | |
| 1156 targetCfg2 = startableEmptyCfg + \ | |
| 1157 """ | |
| 1158 from buildbot.test.test_config import MySlowTarget | |
| 1159 c['status'] = [MySlowTarget('b')] | |
| 1160 """ | |
| 1161 | |
| 1162 class StartService(unittest.TestCase): | |
| 1163 def tearDown(self): | |
| 1164 return self.master.stopService() | |
| 1165 | |
| 1166 def testStartService(self): | |
| 1167 os.mkdir("test_ss") | |
| 1168 self.master = m = BuildMaster("test_ss") | |
| 1169 # inhibit the usual read-config-on-startup behavior | |
| 1170 m.readConfig = True | |
| 1171 m.startService() | |
| 1172 d = m.loadConfig(startableEmptyCfg % 0) | |
| 1173 d.addCallback(self._testStartService_0) | |
| 1174 return d | |
| 1175 | |
| 1176 def _testStartService_0(self, res): | |
| 1177 m = self.master | |
| 1178 m.targetevents = [] | |
| 1179 # figure out what port got allocated | |
| 1180 self.portnum = m.slavePort._port.getHost().port | |
| 1181 d = m.loadConfig(targetCfg1 % self.portnum) | |
| 1182 d.addCallback(self._testStartService_1) | |
| 1183 return d | |
| 1184 | |
| 1185 def _testStartService_1(self, res): | |
| 1186 self.failUnlessEqual(len(self.master.statusTargets), 1) | |
| 1187 self.failUnless(isinstance(self.master.statusTargets[0], MyTarget)) | |
| 1188 self.failUnlessEqual(self.master.targetevents, | |
| 1189 [('start', 'a')]) | |
| 1190 self.master.targetevents = [] | |
| 1191 # reloading the same config should not start or stop the target | |
| 1192 d = self.master.loadConfig(targetCfg1 % self.portnum) | |
| 1193 d.addCallback(self._testStartService_2) | |
| 1194 return d | |
| 1195 | |
| 1196 def _testStartService_2(self, res): | |
| 1197 self.failUnlessEqual(self.master.targetevents, []) | |
| 1198 # but loading a new config file should stop the old one, then | |
| 1199 # start the new one | |
| 1200 d = self.master.loadConfig(targetCfg2 % self.portnum) | |
| 1201 d.addCallback(self._testStartService_3) | |
| 1202 return d | |
| 1203 | |
| 1204 def _testStartService_3(self, res): | |
| 1205 self.failUnlessEqual(self.master.targetevents, | |
| 1206 [('stop', 'a'), ('start', 'b')]) | |
| 1207 self.master.targetevents = [] | |
| 1208 # and going back to the old one should do the same, in the same | |
| 1209 # order, even though the current MySlowTarget takes a moment to shut | |
| 1210 # down | |
| 1211 d = self.master.loadConfig(targetCfg1 % self.portnum) | |
| 1212 d.addCallback(self._testStartService_4) | |
| 1213 return d | |
| 1214 | |
| 1215 def _testStartService_4(self, res): | |
| 1216 self.failUnlessEqual(self.master.targetevents, | |
| 1217 [('stop', 'b'), ('start', 'a')]) | |
| 1218 | |
| 1219 cfg1 = \ | |
| 1220 """ | |
| 1221 from buildbot.process.factory import BuildFactory, s | |
| 1222 from buildbot.steps.shell import ShellCommand | |
| 1223 from buildbot.steps.source import Darcs | |
| 1224 from buildbot.buildslave import BuildSlave | |
| 1225 from buildbot.config import BuilderConfig | |
| 1226 BuildmasterConfig = c = {} | |
| 1227 c['slaves'] = [BuildSlave('bot1', 'pw1')] | |
| 1228 c['schedulers'] = [] | |
| 1229 c['slavePortnum'] = 9999 | |
| 1230 f1 = BuildFactory([ShellCommand(command='echo yes'), | |
| 1231 s(ShellCommand, command='old-style'), | |
| 1232 ]) | |
| 1233 f1.addStep(Darcs(repourl='http://buildbot.net/repos/trunk')) | |
| 1234 f1.addStep(ShellCommand, command='echo old-style') | |
| 1235 c['builders'] = [ | |
| 1236 BuilderConfig(name='builder1', slavename='bot1', factory=f1), | |
| 1237 ] | |
| 1238 """ | |
| 1239 | |
| 1240 cfg1_bad = \ | |
| 1241 """ | |
| 1242 from buildbot.process.factory import BuildFactory, s | |
| 1243 from buildbot.steps.shell import ShellCommand | |
| 1244 from buildbot.steps.source import Darcs | |
| 1245 from buildbot.buildslave import BuildSlave | |
| 1246 from buildbot.config import BuilderConfig | |
| 1247 BuildmasterConfig = c = {} | |
| 1248 c['slaves'] = [BuildSlave('bot1', 'pw1')] | |
| 1249 c['schedulers'] = [] | |
| 1250 c['slavePortnum'] = 9999 | |
| 1251 f1 = BuildFactory([ShellCommand(command='echo yes'), | |
| 1252 s(ShellCommand, command='old-style'), | |
| 1253 ]) | |
| 1254 # it should be this: | |
| 1255 #f1.addStep(ShellCommand(command='echo args')) | |
| 1256 # but an easy mistake is to do this: | |
| 1257 f1.addStep(ShellCommand(), command='echo args') | |
| 1258 # this test makes sure this error doesn't get ignored | |
| 1259 c['builders'] = [ | |
| 1260 BuilderConfig(name='builder1', slavename='bot1', factory=f1), | |
| 1261 ] | |
| 1262 """ | |
| 1263 | |
| 1264 class Factories(unittest.TestCase): | |
| 1265 def printExpecting(self, factory, args): | |
| 1266 factory_keys = factory[1].keys() | |
| 1267 factory_keys.sort() | |
| 1268 args_keys = args.keys() | |
| 1269 args_keys.sort() | |
| 1270 | |
| 1271 print | |
| 1272 print "factory had:" | |
| 1273 for k in factory_keys: | |
| 1274 print k | |
| 1275 print "but we were expecting:" | |
| 1276 for k in args_keys: | |
| 1277 print k | |
| 1278 | |
| 1279 def failUnlessExpectedShell(self, factory, defaults=True, **kwargs): | |
| 1280 shell_args = {} | |
| 1281 if defaults: | |
| 1282 shell_args.update({'descriptionDone': None, | |
| 1283 'description': None, | |
| 1284 'workdir': None, | |
| 1285 'logfiles': {}, | |
| 1286 'lazylogfiles': False, | |
| 1287 'usePTY': "slave-config", | |
| 1288 }) | |
| 1289 shell_args.update(kwargs) | |
| 1290 self.failUnlessIdentical(factory[0], ShellCommand) | |
| 1291 if factory[1] != shell_args: | |
| 1292 self.printExpecting(factory, shell_args) | |
| 1293 self.failUnlessEqual(factory[1], shell_args) | |
| 1294 | |
| 1295 def failUnlessExpectedDarcs(self, factory, **kwargs): | |
| 1296 darcs_args = {'workdir': None, | |
| 1297 'alwaysUseLatest': False, | |
| 1298 'mode': 'update', | |
| 1299 'timeout': 1200, | |
| 1300 'retry': None, | |
| 1301 'baseURL': None, | |
| 1302 'defaultBranch': None, | |
| 1303 'logfiles': {}, | |
| 1304 'lazylogfiles' : False, | |
| 1305 } | |
| 1306 darcs_args.update(kwargs) | |
| 1307 self.failUnlessIdentical(factory[0], Darcs) | |
| 1308 if factory[1] != darcs_args: | |
| 1309 self.printExpecting(factory, darcs_args) | |
| 1310 self.failUnlessEqual(factory[1], darcs_args) | |
| 1311 | |
| 1312 def testSteps(self): | |
| 1313 m = BuildMaster(".") | |
| 1314 m.loadConfig(cfg1) | |
| 1315 b = m.botmaster.builders["builder1"] | |
| 1316 steps = b.buildFactory.steps | |
| 1317 self.failUnlessEqual(len(steps), 4) | |
| 1318 | |
| 1319 self.failUnlessExpectedShell(steps[0], command="echo yes") | |
| 1320 self.failUnlessExpectedShell(steps[1], defaults=False, | |
| 1321 command="old-style") | |
| 1322 self.failUnlessExpectedDarcs(steps[2], | |
| 1323 repourl="http://buildbot.net/repos/trunk") | |
| 1324 self.failUnlessExpectedShell(steps[3], defaults=False, | |
| 1325 command="echo old-style") | |
| 1326 | |
| 1327 def testBadAddStepArguments(self): | |
| 1328 m = BuildMaster(".") | |
| 1329 self.failUnlessRaises(ArgumentsInTheWrongPlace, m.loadConfig, cfg1_bad) | |
| 1330 | |
| 1331 def _loop(self, orig): | |
| 1332 step_class, kwargs = orig.getStepFactory() | |
| 1333 newstep = step_class(**kwargs) | |
| 1334 return newstep | |
| 1335 | |
| 1336 def testAllSteps(self): | |
| 1337 # make sure that steps can be created from the factories that they | |
| 1338 # return | |
| 1339 for s in ( dummy.Dummy(), dummy.FailingDummy(), dummy.RemoteDummy(), | |
| 1340 maxq.MaxQ("testdir"), | |
| 1341 python.BuildEPYDoc(), python.PyFlakes(), | |
| 1342 python_twisted.HLint(), | |
| 1343 python_twisted.Trial(testpath=None, tests="tests"), | |
| 1344 python_twisted.ProcessDocs(), python_twisted.BuildDebs(), | |
| 1345 python_twisted.RemovePYCs(), | |
| 1346 shell.ShellCommand(), shell.TreeSize(), | |
| 1347 shell.Configure(), shell.Compile(), shell.Test(), | |
| 1348 source.CVS("cvsroot", "module"), | |
| 1349 source.SVN("svnurl"), source.Darcs("repourl"), | |
| 1350 source.Git("repourl"), | |
| 1351 source.Arch("url", "version"), | |
| 1352 source.Bazaar("url", "version", "archive"), | |
| 1353 source.Bzr("repourl"), | |
| 1354 source.Mercurial("repourl"), | |
| 1355 source.P4("p4base"), | |
| 1356 source.P4Sync(1234, "p4user", "passwd", "client", | |
| 1357 mode="copy"), | |
| 1358 source.Monotone("server", "branch"), | |
| 1359 transfer.FileUpload("src", "dest"), | |
| 1360 transfer.FileDownload("src", "dest"), | |
| 1361 ): | |
| 1362 try: | |
| 1363 self._loop(s) | |
| 1364 except: | |
| 1365 print "error checking %s" % s | |
| 1366 raise | |
| 1367 | |
| OLD | NEW |