1 # -*- test-case-name: buildbot.test.test_config -*-
3 from __future__
import generators
6 from twisted
.trial
import unittest
7 from twisted
.python
import failure
8 from twisted
.internet
import defer
13 from buildbot
.changes
.freshcvs
import FreshCVSSource
17 from buildbot
.twcompat
import providedBy
, maybeWait
18 from buildbot
.master
import BuildMaster
19 from buildbot
import scheduler
20 from twisted
.application
import service
, internet
21 from twisted
.spread
import pb
22 from twisted
.web
.server
import Site
23 from twisted
.web
.distrib
import ResourcePublisher
24 from buildbot
.process
.builder
import Builder
25 from buildbot
.process
.factory
import BasicBuildFactory
26 from buildbot
.process
import step
27 from buildbot
.status
import base
30 from buildbot
.status
import words
36 BuildmasterConfig = c = {}
41 c['slavePortnum'] = 9999
42 c['projectName'] = 'dummy project'
43 c['projectURL'] = 'http://dummy.example.com'
44 c['buildbotURL'] = 'http://dummy.example.com/buildbot'
49 from buildbot.process.factory import BasicBuildFactory
50 BuildmasterConfig = c = {}
51 c['bots'] = [('bot1', 'pw1')]
54 c['slavePortnum'] = 9999
55 f1 = BasicBuildFactory('cvsroot', 'cvsmodule')
56 c['builders'] = [{'name':'builder1', 'slavename':'bot1',
57 'builddir':'workdir', 'factory':f1}]
60 buildersCfg2
= buildersCfg
+ \
62 f1 = BasicBuildFactory('cvsroot', 'cvsmodule2')
63 c['builders'] = [{'name':'builder1', 'slavename':'bot1',
64 'builddir':'workdir', 'factory':f1}]
67 buildersCfg3
= buildersCfg2
+ \
69 c['builders'].append({'name': 'builder2', 'slavename': 'bot1',
70 'builddir': 'workdir2', 'factory': f1 })
73 buildersCfg4
= buildersCfg2
+ \
75 c['builders'] = [{ 'name': 'builder1', 'slavename': 'bot1',
76 'builddir': 'newworkdir', 'factory': f1 },
77 { 'name': 'builder2', 'slavename': 'bot1',
78 'builddir': 'workdir2', 'factory': f1 }]
81 wpCfg1
= buildersCfg
+ \
83 from buildbot.process import step
84 f1 = BasicBuildFactory('cvsroot', 'cvsmodule')
85 f1.addStep(step.ShellCommand, command=[step.WithProperties('echo')])
86 c['builders'] = [{'name':'builder1', 'slavename':'bot1',
87 'builddir':'workdir1', 'factory': f1}]
90 wpCfg2
= buildersCfg
+ \
92 from buildbot.process import step
93 f1 = BasicBuildFactory('cvsroot', 'cvsmodule')
94 f1.addStep(step.ShellCommand,
95 command=[step.WithProperties('echo %s', 'revision')])
96 c['builders'] = [{'name':'builder1', 'slavename':'bot1',
97 'builddir':'workdir1', 'factory': f1}]
102 ircCfg1
= emptyCfg
+ \
104 from buildbot.status import words
105 c['status'] = [words.IRC('irc.us.freenode.net', 'buildbot', ['twisted'])]
108 ircCfg2
= emptyCfg
+ \
110 from buildbot.status import words
111 c['status'] = [words.IRC('irc.us.freenode.net', 'buildbot', ['twisted']),
112 words.IRC('irc.example.com', 'otherbot', ['chan1', 'chan2'])]
115 ircCfg3
= emptyCfg
+ \
117 from buildbot.status import words
118 c['status'] = [words.IRC('irc.us.freenode.net', 'buildbot', ['knotted'])]
121 webCfg1
= emptyCfg
+ \
123 from buildbot.status import html
124 c['status'] = [html.Waterfall(http_port=9980)]
127 webCfg2
= emptyCfg
+ \
129 from buildbot.status import html
130 c['status'] = [html.Waterfall(http_port=9981)]
133 webCfg3
= emptyCfg
+ \
135 from buildbot.status import html
136 c['status'] = [html.Waterfall(http_port='tcp:9981:interface=127.0.0.1')]
139 webNameCfg1
= emptyCfg
+ \
141 from buildbot.status import html
142 c['status'] = [html.Waterfall(distrib_port='~/.twistd-web-pb')]
145 webNameCfg2
= emptyCfg
+ \
147 from buildbot.status import html
148 c['status'] = [html.Waterfall(distrib_port='./bar.socket')]
151 debugPasswordCfg
= emptyCfg
+ \
153 c['debugPassword'] = 'sekrit'
158 from buildbot.process.factory import BasicBuildFactory
160 c['bots'] = [('bot1', 'pw1')]
163 f1 = BasicBuildFactory('cvsroot', 'cvsmodule')
165 { 'name': 'builder1', 'slavename': 'bot1',
166 'builddir': 'workdir', 'factory': f1 },
167 { 'name': 'builder2', 'slavename': 'bot1',
168 'builddir': 'workdir2', 'factory': f1 },
170 # interlocks have been removed
171 c['interlocks'] = [('lock1', ['builder1'], ['builder2', 'builder3']),
173 c['slavePortnum'] = 9999
174 BuildmasterConfig = c
179 from buildbot.process.step import Dummy
180 from buildbot.process.factory import BuildFactory, s
181 from buildbot.locks import MasterLock
183 c['bots'] = [('bot1', 'pw1')]
186 l1 = MasterLock('lock1')
187 l2 = MasterLock('lock1') # duplicate lock name
188 f1 = BuildFactory([s(Dummy, locks=[])])
190 { 'name': 'builder1', 'slavename': 'bot1',
191 'builddir': 'workdir', 'factory': f1, 'locks': [l1, l2] },
192 { 'name': 'builder2', 'slavename': 'bot1',
193 'builddir': 'workdir2', 'factory': f1 },
195 c['slavePortnum'] = 9999
196 BuildmasterConfig = c
201 from buildbot.process.step import Dummy
202 from buildbot.process.factory import BuildFactory, s
203 from buildbot.locks import MasterLock, SlaveLock
205 c['bots'] = [('bot1', 'pw1')]
208 l1 = MasterLock('lock1')
209 l2 = SlaveLock('lock1') # duplicate lock name
210 f1 = BuildFactory([s(Dummy, locks=[])])
212 { 'name': 'builder1', 'slavename': 'bot1',
213 'builddir': 'workdir', 'factory': f1, 'locks': [l1, l2] },
214 { 'name': 'builder2', 'slavename': 'bot1',
215 'builddir': 'workdir2', 'factory': f1 },
217 c['slavePortnum'] = 9999
218 BuildmasterConfig = c
223 from buildbot.process.step import Dummy
224 from buildbot.process.factory import BuildFactory, s
225 from buildbot.locks import MasterLock
227 c['bots'] = [('bot1', 'pw1')]
230 l1 = MasterLock('lock1')
231 l2 = MasterLock('lock1') # duplicate lock name
232 f1 = BuildFactory([s(Dummy, locks=[l2])])
233 f2 = BuildFactory([s(Dummy)])
235 { 'name': 'builder1', 'slavename': 'bot1',
236 'builddir': 'workdir', 'factory': f2, 'locks': [l1] },
237 { 'name': 'builder2', 'slavename': 'bot1',
238 'builddir': 'workdir2', 'factory': f1 },
240 c['slavePortnum'] = 9999
241 BuildmasterConfig = c
246 from buildbot.process.factory import BasicBuildFactory
247 from buildbot.locks import MasterLock
249 c['bots'] = [('bot1', 'pw1')]
252 f1 = BasicBuildFactory('cvsroot', 'cvsmodule')
253 l1 = MasterLock('lock1')
254 l2 = MasterLock('lock2')
256 { 'name': 'builder1', 'slavename': 'bot1',
257 'builddir': 'workdir', 'factory': f1, 'locks': [l1, l2] },
258 { 'name': 'builder2', 'slavename': 'bot1',
259 'builddir': 'workdir2', 'factory': f1 },
261 c['slavePortnum'] = 9999
262 BuildmasterConfig = c
267 from buildbot.process.factory import BasicBuildFactory
268 from buildbot.locks import MasterLock
270 c['bots'] = [('bot1', 'pw1')]
273 f1 = BasicBuildFactory('cvsroot', 'cvsmodule')
274 l1 = MasterLock('lock1')
275 l2 = MasterLock('lock2')
277 { 'name': 'builder1', 'slavename': 'bot1',
278 'builddir': 'workdir', 'factory': f1, 'locks': [l1] },
279 { 'name': 'builder2', 'slavename': 'bot1',
280 'builddir': 'workdir2', 'factory': f1 },
282 c['slavePortnum'] = 9999
283 BuildmasterConfig = c
286 # test out step Locks
289 from buildbot.process.step import Dummy
290 from buildbot.process.factory import BuildFactory, s
291 from buildbot.locks import MasterLock
293 c['bots'] = [('bot1', 'pw1')]
296 l1 = MasterLock('lock1')
297 l2 = MasterLock('lock2')
298 f1 = BuildFactory([s(Dummy, locks=[l1,l2])])
299 f2 = BuildFactory([s(Dummy)])
302 { 'name': 'builder1', 'slavename': 'bot1',
303 'builddir': 'workdir', 'factory': f1 },
304 { 'name': 'builder2', 'slavename': 'bot1',
305 'builddir': 'workdir2', 'factory': f2 },
307 c['slavePortnum'] = 9999
308 BuildmasterConfig = c
313 from buildbot.process.step import Dummy
314 from buildbot.process.factory import BuildFactory, s
315 from buildbot.locks import MasterLock
317 c['bots'] = [('bot1', 'pw1')]
320 l1 = MasterLock('lock1')
321 l2 = MasterLock('lock2')
322 f1 = BuildFactory([s(Dummy, locks=[l1])])
323 f2 = BuildFactory([s(Dummy)])
326 { 'name': 'builder1', 'slavename': 'bot1',
327 'builddir': 'workdir', 'factory': f1 },
328 { 'name': 'builder2', 'slavename': 'bot1',
329 'builddir': 'workdir2', 'factory': f2 },
331 c['slavePortnum'] = 9999
332 BuildmasterConfig = c
337 from buildbot.process.step import Dummy
338 from buildbot.process.factory import BuildFactory, s
339 from buildbot.locks import MasterLock
341 c['bots'] = [('bot1', 'pw1')]
344 l1 = MasterLock('lock1')
345 l2 = MasterLock('lock2')
346 f1 = BuildFactory([s(Dummy)])
347 f2 = BuildFactory([s(Dummy)])
350 { 'name': 'builder1', 'slavename': 'bot1',
351 'builddir': 'workdir', 'factory': f1 },
352 { 'name': 'builder2', 'slavename': 'bot1',
353 'builddir': 'workdir2', 'factory': f2 },
355 c['slavePortnum'] = 9999
356 BuildmasterConfig = c
359 class ConfigTest(unittest
.TestCase
):
361 self
.buildmaster
= BuildMaster(".")
363 def failUnlessListsEquivalent(self
, list1
, list2
):
368 self
.failUnlessEqual(l1
, l2
)
370 def servers(self
, s
, types
):
371 # perform a recursive search of s.services, looking for instances of
372 # twisted.application.internet.TCPServer, then extract their .args
373 # values to find the TCP ports they want to listen on
375 if providedBy(child
, service
.IServiceCollection
):
376 for gc
in self
.servers(child
, types
):
378 if isinstance(child
, types
):
381 def TCPports(self
, s
):
382 return list(self
.servers(s
, internet
.TCPServer
))
383 def UNIXports(self
, s
):
384 return list(self
.servers(s
, internet
.UNIXServer
))
385 def TCPclients(self
, s
):
386 return list(self
.servers(s
, internet
.TCPClient
))
388 def checkPorts(self
, svc
, expected
):
389 """Verify that the TCPServer and UNIXServer children of the given
390 service have the expected portnum/pathname and factory classes. As a
391 side-effect, return a list of servers in the same order as the
392 'expected' list. This can be used to verify properties of the
393 factories contained therein."""
395 expTCP
= [e
for e
in expected
if type(e
[0]) == int]
396 expUNIX
= [e
for e
in expected
if type(e
[0]) == str]
397 haveTCP
= [(p
.args
[0], p
.args
[1].__class
__)
398 for p
in self
.TCPports(svc
)]
399 haveUNIX
= [(p
.args
[0], p
.args
[1].__class
__)
400 for p
in self
.UNIXports(svc
)]
401 self
.failUnlessListsEquivalent(expTCP
, haveTCP
)
402 self
.failUnlessListsEquivalent(expUNIX
, haveUNIX
)
405 for have
in self
.TCPports(svc
) + self
.UNIXports(svc
):
406 if have
.args
[0] == e
[0]:
409 assert(len(ret
) == len(expected
))
413 self
.failUnlessRaises(KeyError, self
.buildmaster
.loadConfig
, "")
415 def testSimple(self
):
416 # covers slavePortnum, base checker passwords
417 master
= self
.buildmaster
420 master
.loadConfig(emptyCfg
)
421 # note: this doesn't actually start listening, because the app
422 # hasn't been started running
423 self
.failUnlessEqual(master
.slavePortnum
, "tcp:9999")
424 self
.checkPorts(master
, [(9999, pb
.PBServerFactory
)])
425 self
.failUnlessEqual(list(master
.change_svc
), [])
426 self
.failUnlessEqual(master
.botmaster
.builders
, {})
427 self
.failUnlessEqual(master
.checker
.users
,
428 {"change": "changepw"})
429 self
.failUnlessEqual(master
.projectName
, "dummy project")
430 self
.failUnlessEqual(master
.projectURL
, "http://dummy.example.com")
431 self
.failUnlessEqual(master
.buildbotURL
,
432 "http://dummy.example.com/buildbot")
434 def testSlavePortnum(self
):
435 master
= self
.buildmaster
438 master
.loadConfig(emptyCfg
)
439 self
.failUnlessEqual(master
.slavePortnum
, "tcp:9999")
440 ports
= self
.checkPorts(master
, [(9999, pb
.PBServerFactory
)])
443 master
.loadConfig(emptyCfg
)
444 self
.failUnlessEqual(master
.slavePortnum
, "tcp:9999")
445 ports
= self
.checkPorts(master
, [(9999, pb
.PBServerFactory
)])
446 self
.failUnlessIdentical(p
, ports
[0],
447 "the slave port was changed even " + \
448 "though the configuration was not")
450 master
.loadConfig(emptyCfg
+ "c['slavePortnum'] = 9000\n")
451 self
.failUnlessEqual(master
.slavePortnum
, "tcp:9000")
452 ports
= self
.checkPorts(master
, [(9000, pb
.PBServerFactory
)])
453 self
.failIf(p
is ports
[0],
454 "slave port was unchanged but configuration was changed")
457 master
= self
.buildmaster
459 master
.loadConfig(emptyCfg
)
460 self
.failUnlessEqual(master
.botmaster
.builders
, {})
461 self
.failUnlessEqual(master
.checker
.users
,
462 {"change": "changepw"})
463 botsCfg
= (emptyCfg
+
464 "c['bots'] = [('bot1', 'pw1'), ('bot2', 'pw2')]\n")
465 master
.loadConfig(botsCfg
)
466 self
.failUnlessEqual(master
.checker
.users
,
467 {"change": "changepw",
470 master
.loadConfig(botsCfg
)
471 self
.failUnlessEqual(master
.checker
.users
,
472 {"change": "changepw",
475 master
.loadConfig(emptyCfg
)
476 self
.failUnlessEqual(master
.checker
.users
,
477 {"change": "changepw"})
480 def testSources(self
):
482 raise unittest
.SkipTest("this test needs CVSToys installed")
483 master
= self
.buildmaster
485 master
.loadConfig(emptyCfg
)
486 self
.failUnlessEqual(list(master
.change_svc
), [])
488 self
.sourcesCfg
= emptyCfg
+ \
490 from buildbot.changes.freshcvs import FreshCVSSource
491 s1 = FreshCVSSource('cvs.example.com', 1000, 'pname', 'spass',
496 d
= master
.loadConfig(self
.sourcesCfg
)
497 d
.addCallback(self
._testSources
_1)
500 def _testSources_1(self
, res
):
501 self
.failUnlessEqual(len(list(self
.buildmaster
.change_svc
)), 1)
502 s1
= list(self
.buildmaster
.change_svc
)[0]
503 self
.failUnless(isinstance(s1
, FreshCVSSource
))
504 self
.failUnlessEqual(s1
.host
, "cvs.example.com")
505 self
.failUnlessEqual(s1
.port
, 1000)
506 self
.failUnlessEqual(s1
.prefix
, "Prefix/")
507 self
.failUnlessEqual(s1
, list(self
.buildmaster
.change_svc
)[0])
508 self
.failUnless(s1
.parent
)
510 # verify that unchanged sources are not interrupted
511 d
= self
.buildmaster
.loadConfig(self
.sourcesCfg
)
512 d
.addCallback(self
._testSources
_2, s1
)
515 def _testSources_2(self
, res
, s1
):
516 self
.failUnlessEqual(len(list(self
.buildmaster
.change_svc
)), 1)
517 s2
= list(self
.buildmaster
.change_svc
)[0]
518 self
.failUnlessIdentical(s1
, s2
)
519 self
.failUnless(s1
.parent
)
521 # make sure we can get rid of the sources too
522 d
= self
.buildmaster
.loadConfig(emptyCfg
)
523 d
.addCallback(self
._testSources
_3)
526 def _testSources_3(self
, res
):
527 self
.failUnlessEqual(list(self
.buildmaster
.change_svc
), [])
529 def shouldBeFailure(self
, res
, *expected
):
530 self
.failUnless(isinstance(res
, failure
.Failure
),
531 "we expected this to fail, not produce %s" % (res
,))
533 return None # all is good
535 def testSchedulers(self
):
536 master
= self
.buildmaster
538 master
.loadConfig(emptyCfg
)
539 self
.failUnlessEqual(master
.allSchedulers(), [])
541 self
.schedulersCfg
= \
543 from buildbot.scheduler import Scheduler, Dependent
544 from buildbot.process.factory import BasicBuildFactory
546 c['bots'] = [('bot1', 'pw1')]
548 c['schedulers'] = [Scheduler('full', None, 60, ['builder1'])]
549 f1 = BasicBuildFactory('cvsroot', 'cvsmodule')
550 c['builders'] = [{'name':'builder1', 'slavename':'bot1',
551 'builddir':'workdir', 'factory':f1}]
552 c['slavePortnum'] = 9999
553 c['projectName'] = 'dummy project'
554 c['projectURL'] = 'http://dummy.example.com'
555 c['buildbotURL'] = 'http://dummy.example.com/buildbot'
556 BuildmasterConfig = c
559 # c['schedulers'] must be a list
560 badcfg
= self
.schedulersCfg
+ \
562 c['schedulers'] = Scheduler('full', None, 60, ['builder1'])
564 d
= defer
.maybeDeferred(self
.buildmaster
.loadConfig
, badcfg
)
565 d
.addBoth(self
._testSchedulers
_1)
567 def _testSchedulers_1(self
, res
):
568 self
.shouldBeFailure(res
, AssertionError)
569 # c['schedulers'] must be a list of IScheduler objects
570 badcfg
= self
.schedulersCfg
+ \
572 c['schedulers'] = ['oops', 'problem']
574 d
= defer
.maybeDeferred(self
.buildmaster
.loadConfig
, badcfg
)
575 d
.addBoth(self
._testSchedulers
_2)
577 def _testSchedulers_2(self
, res
):
578 self
.shouldBeFailure(res
, AssertionError)
579 # c['schedulers'] must point at real builders
580 badcfg
= self
.schedulersCfg
+ \
582 c['schedulers'] = [Scheduler('full', None, 60, ['builder-bogus'])]
584 d
= defer
.maybeDeferred(self
.buildmaster
.loadConfig
, badcfg
)
585 d
.addBoth(self
._testSchedulers
_3)
587 def _testSchedulers_3(self
, res
):
588 self
.shouldBeFailure(res
, AssertionError)
589 d
= self
.buildmaster
.loadConfig(self
.schedulersCfg
)
590 d
.addCallback(self
._testSchedulers
_4)
592 def _testSchedulers_4(self
, res
):
593 sch
= self
.buildmaster
.allSchedulers()
594 self
.failUnlessEqual(len(sch
), 1)
596 self
.failUnless(isinstance(s
, scheduler
.Scheduler
))
597 self
.failUnlessEqual(s
.name
, "full")
598 self
.failUnlessEqual(s
.branch
, None)
599 self
.failUnlessEqual(s
.treeStableTimer
, 60)
600 self
.failUnlessEqual(s
.builderNames
, ['builder1'])
602 newcfg
= self
.schedulersCfg
+ \
604 s1 = Scheduler('full', None, 60, ['builder1'])
605 c['schedulers'] = [s1, Dependent('downstream', s1, ['builder1'])]
607 d
= self
.buildmaster
.loadConfig(newcfg
)
608 d
.addCallback(self
._testSchedulers
_5, newcfg
)
610 def _testSchedulers_5(self
, res
, newcfg
):
611 sch
= self
.buildmaster
.allSchedulers()
612 self
.failUnlessEqual(len(sch
), 2)
614 self
.failUnless(isinstance(s
, scheduler
.Scheduler
))
616 self
.failUnless(isinstance(s
, scheduler
.Dependent
))
617 self
.failUnlessEqual(s
.name
, "downstream")
618 self
.failUnlessEqual(s
.builderNames
, ['builder1'])
620 # reloading the same config file should leave the schedulers in place
621 d
= self
.buildmaster
.loadConfig(newcfg
)
622 d
.addCallback(self
._testSchedulers
_6, sch
)
624 def _testSchedulers_6(self
, res
, sch1
):
625 sch2
= self
.buildmaster
.allSchedulers()
626 self
.failUnlessEqual(len(sch2
), 2)
629 self
.failUnlessEqual(sch1
, sch2
)
630 self
.failUnlessIdentical(sch1
[0], sch2
[0])
631 self
.failUnlessIdentical(sch1
[1], sch2
[1])
632 self
.failUnlessIdentical(sch1
[0].parent
, self
.buildmaster
)
633 self
.failUnlessIdentical(sch1
[1].parent
, self
.buildmaster
)
634 badcfg
= self
.schedulersCfg
+ \
636 c['schedulers'] = [Scheduler('dup', None, 60, []),
637 Scheduler('dup', None, 60, [])]
639 d
= defer
.maybeDeferred(self
.buildmaster
.loadConfig
, badcfg
)
640 d
.addBoth(self
._testSchedulers
_7)
642 def _testSchedulers_7(self
, res
):
643 self
.shouldBeFailure(res
, ValueError)
647 def testBuilders(self
):
648 master
= self
.buildmaster
649 master
.loadConfig(emptyCfg
)
650 self
.failUnlessEqual(master
.botmaster
.builders
, {})
652 master
.loadConfig(buildersCfg
)
653 self
.failUnlessEqual(master
.botmaster
.builderNames
, ["builder1"])
654 self
.failUnlessEqual(master
.botmaster
.builders
.keys(), ["builder1"])
655 b
= master
.botmaster
.builders
["builder1"]
656 self
.failUnless(isinstance(b
, Builder
))
657 self
.failUnlessEqual(b
.name
, "builder1")
658 self
.failUnlessEqual(b
.slavenames
, ["bot1"])
659 self
.failUnlessEqual(b
.builddir
, "workdir")
661 self
.failUnless(isinstance(f1
, BasicBuildFactory
))
663 self
.failUnlessEqual(len(steps
), 3)
664 self
.failUnlessEqual(steps
[0], (step
.CVS
,
665 {'cvsroot': 'cvsroot',
666 'cvsmodule': 'cvsmodule',
668 self
.failUnlessEqual(steps
[1], (step
.Compile
,
669 {'command': 'make all'}))
670 self
.failUnlessEqual(steps
[2], (step
.Test
,
671 {'command': 'make check'}))
674 # make sure a reload of the same data doesn't interrupt the Builder
675 master
.loadConfig(buildersCfg
)
676 self
.failUnlessEqual(master
.botmaster
.builderNames
, ["builder1"])
677 self
.failUnlessEqual(master
.botmaster
.builders
.keys(), ["builder1"])
678 b2
= master
.botmaster
.builders
["builder1"]
679 self
.failUnlessIdentical(b
, b2
)
680 # TODO: test that the BuilderStatus object doesn't change
681 #statusbag2 = master.client_svc.statusbags["builder1"]
682 #self.failUnlessIdentical(statusbag, statusbag2)
684 # but changing something should result in a new Builder
685 master
.loadConfig(buildersCfg2
)
686 self
.failUnlessEqual(master
.botmaster
.builderNames
, ["builder1"])
687 self
.failUnlessEqual(master
.botmaster
.builders
.keys(), ["builder1"])
688 b3
= master
.botmaster
.builders
["builder1"]
690 # the statusbag remains the same TODO
691 #statusbag3 = master.client_svc.statusbags["builder1"]
692 #self.failUnlessIdentical(statusbag, statusbag3)
695 master
.loadConfig(buildersCfg3
)
696 self
.failUnlessEqual(master
.botmaster
.builderNames
, ["builder1",
698 self
.failUnlessListsEquivalent(master
.botmaster
.builders
.keys(),
699 ["builder1", "builder2"])
700 b4
= master
.botmaster
.builders
["builder1"]
701 self
.failUnlessIdentical(b3
, b4
)
703 # changing first builder should leave it at the same place in the list
704 master
.loadConfig(buildersCfg4
)
705 self
.failUnlessEqual(master
.botmaster
.builderNames
, ["builder1",
707 self
.failUnlessListsEquivalent(master
.botmaster
.builders
.keys(),
708 ["builder1", "builder2"])
709 b5
= master
.botmaster
.builders
["builder1"]
710 self
.failIf(b4
is b5
)
712 # and removing it should make the Builder go away
713 master
.loadConfig(emptyCfg
)
714 self
.failUnlessEqual(master
.botmaster
.builderNames
, [])
715 self
.failUnlessEqual(master
.botmaster
.builders
, {})
716 #self.failUnlessEqual(master.client_svc.statusbags, {}) # TODO
718 def testWithProperties(self
):
719 master
= self
.buildmaster
720 master
.loadConfig(wpCfg1
)
721 self
.failUnlessEqual(master
.botmaster
.builderNames
, ["builder1"])
722 self
.failUnlessEqual(master
.botmaster
.builders
.keys(), ["builder1"])
723 b1
= master
.botmaster
.builders
["builder1"]
725 # reloading the same config should leave the builder unchanged
726 master
.loadConfig(wpCfg1
)
727 b2
= master
.botmaster
.builders
["builder1"]
728 self
.failUnlessIdentical(b1
, b2
)
730 # but changing the parameters of the WithProperties should change it
731 master
.loadConfig(wpCfg2
)
732 b3
= master
.botmaster
.builders
["builder1"]
733 self
.failIf(b1
is b3
)
735 # again, reloading same config should leave the builder unchanged
736 master
.loadConfig(wpCfg2
)
737 b4
= master
.botmaster
.builders
["builder1"]
738 self
.failUnlessIdentical(b3
, b4
)
740 def checkIRC(self
, m
, expected
):
742 for irc
in self
.servers(m
, words
.IRC
):
743 ircs
[irc
.host
] = (irc
.nick
, irc
.channels
)
744 self
.failUnlessEqual(ircs
, expected
)
748 raise unittest
.SkipTest("Twisted Words package is not installed")
749 master
= self
.buildmaster
751 d
= master
.loadConfig(emptyCfg
)
753 d
.addCallback(lambda res
: self
.checkIRC(master
, e1
))
754 d
.addCallback(lambda res
: master
.loadConfig(ircCfg1
))
755 e2
= {'irc.us.freenode.net': ('buildbot', ['twisted'])}
756 d
.addCallback(lambda res
: self
.checkIRC(master
, e2
))
757 d
.addCallback(lambda res
: master
.loadConfig(ircCfg2
))
758 e3
= {'irc.us.freenode.net': ('buildbot', ['twisted']),
759 'irc.example.com': ('otherbot', ['chan1', 'chan2'])}
760 d
.addCallback(lambda res
: self
.checkIRC(master
, e3
))
761 d
.addCallback(lambda res
: master
.loadConfig(ircCfg3
))
762 e4
= {'irc.us.freenode.net': ('buildbot', ['knotted'])}
763 d
.addCallback(lambda res
: self
.checkIRC(master
, e4
))
764 d
.addCallback(lambda res
: master
.loadConfig(ircCfg1
))
765 e5
= {'irc.us.freenode.net': ('buildbot', ['twisted'])}
766 d
.addCallback(lambda res
: self
.checkIRC(master
, e5
))
769 def testWebPortnum(self
):
770 master
= self
.buildmaster
773 d
= master
.loadConfig(webCfg1
)
774 d
.addCallback(self
._testWebPortnum
_1)
776 def _testWebPortnum_1(self
, res
):
777 ports
= self
.checkPorts(self
.buildmaster
, [(9999, pb
.PBServerFactory
),
781 d
= self
.buildmaster
.loadConfig(webCfg1
) # nothing should be changed
782 d
.addCallback(self
._testWebPortnum
_2, p
)
784 def _testWebPortnum_2(self
, res
, p
):
785 ports
= self
.checkPorts(self
.buildmaster
, [(9999, pb
.PBServerFactory
),
787 self
.failUnlessIdentical(p
, ports
[1],
788 "web port was changed even though " + \
789 "configuration was not")
791 d
= self
.buildmaster
.loadConfig(webCfg2
) # changes to 9981
792 d
.addCallback(self
._testWebPortnum
_3, p
)
794 def _testWebPortnum_3(self
, res
, p
):
795 ports
= self
.checkPorts(self
.buildmaster
, [(9999, pb
.PBServerFactory
),
797 self
.failIf(p
is ports
[1],
798 "configuration was changed but web port was unchanged")
799 d
= self
.buildmaster
.loadConfig(webCfg3
) # 9981 on only localhost
800 d
.addCallback(self
._testWebPortnum
_4, ports
[1])
802 def _testWebPortnum_4(self
, res
, p
):
803 ports
= self
.checkPorts(self
.buildmaster
, [(9999, pb
.PBServerFactory
),
805 self
.failUnlessEqual(ports
[1].kwargs
['interface'], "127.0.0.1")
806 d
= self
.buildmaster
.loadConfig(emptyCfg
)
807 d
.addCallback(lambda res
:
808 self
.checkPorts(self
.buildmaster
,
809 [(9999, pb
.PBServerFactory
)]))
812 def testWebPathname(self
):
813 master
= self
.buildmaster
816 d
= master
.loadConfig(webNameCfg1
)
817 d
.addCallback(self
._testWebPathname
_1)
819 def _testWebPathname_1(self
, res
):
820 self
.checkPorts(self
.buildmaster
,
821 [(9999, pb
.PBServerFactory
),
822 ('~/.twistd-web-pb', pb
.PBServerFactory
)])
823 unixports
= self
.UNIXports(self
.buildmaster
)
824 f
= unixports
[0].args
[1]
825 self
.failUnless(isinstance(f
.root
, ResourcePublisher
))
827 d
= self
.buildmaster
.loadConfig(webNameCfg1
)
828 # nothing should be changed
829 d
.addCallback(self
._testWebPathname
_2, f
)
831 def _testWebPathname_2(self
, res
, f
):
832 self
.checkPorts(self
.buildmaster
,
833 [(9999, pb
.PBServerFactory
),
834 ('~/.twistd-web-pb', pb
.PBServerFactory
)])
835 self
.failUnlessIdentical(f
,
836 self
.UNIXports(self
.buildmaster
)[0].args
[1],
837 "web factory was changed even though " + \
838 "configuration was not")
840 d
= self
.buildmaster
.loadConfig(webNameCfg2
)
841 d
.addCallback(self
._testWebPathname
_3, f
)
843 def _testWebPathname_3(self
, res
, f
):
844 self
.checkPorts(self
.buildmaster
,
845 [(9999, pb
.PBServerFactory
),
846 ('./bar.socket', pb
.PBServerFactory
)])
847 self
.failIf(f
is self
.UNIXports(self
.buildmaster
)[0].args
[1],
848 "web factory was unchanged but configuration was changed")
850 d
= self
.buildmaster
.loadConfig(emptyCfg
)
851 d
.addCallback(lambda res
:
852 self
.checkPorts(self
.buildmaster
,
853 [(9999, pb
.PBServerFactory
)]))
856 def testDebugPassword(self
):
857 master
= self
.buildmaster
859 master
.loadConfig(debugPasswordCfg
)
860 self
.failUnlessEqual(master
.checker
.users
,
861 {"change": "changepw",
864 master
.loadConfig(debugPasswordCfg
)
865 self
.failUnlessEqual(master
.checker
.users
,
866 {"change": "changepw",
869 master
.loadConfig(emptyCfg
)
870 self
.failUnlessEqual(master
.checker
.users
,
871 {"change": "changepw"})
874 master
= self
.buildmaster
875 botmaster
= master
.botmaster
877 # make sure that c['interlocks'] is rejected properly
878 self
.failUnlessRaises(KeyError, master
.loadConfig
, interlockCfgBad
)
879 # and that duplicate-named Locks are caught
880 self
.failUnlessRaises(ValueError, master
.loadConfig
, lockCfgBad1
)
881 self
.failUnlessRaises(ValueError, master
.loadConfig
, lockCfgBad2
)
882 self
.failUnlessRaises(ValueError, master
.loadConfig
, lockCfgBad3
)
884 # create a Builder that uses Locks
885 master
.loadConfig(lockCfg1a
)
886 b1
= master
.botmaster
.builders
["builder1"]
887 self
.failUnlessEqual(len(b1
.locks
), 2)
889 # reloading the same config should not change the Builder
890 master
.loadConfig(lockCfg1a
)
891 self
.failUnlessIdentical(b1
, master
.botmaster
.builders
["builder1"])
892 # but changing the set of locks used should change it
893 master
.loadConfig(lockCfg1b
)
894 self
.failIfIdentical(b1
, master
.botmaster
.builders
["builder1"])
895 b1
= master
.botmaster
.builders
["builder1"]
896 self
.failUnlessEqual(len(b1
.locks
), 1)
898 # similar test with step-scoped locks
899 master
.loadConfig(lockCfg2a
)
900 b1
= master
.botmaster
.builders
["builder1"]
901 # reloading the same config should not change the Builder
902 master
.loadConfig(lockCfg2a
)
903 self
.failUnlessIdentical(b1
, master
.botmaster
.builders
["builder1"])
904 # but changing the set of locks used should change it
905 master
.loadConfig(lockCfg2b
)
906 self
.failIfIdentical(b1
, master
.botmaster
.builders
["builder1"])
907 b1
= master
.botmaster
.builders
["builder1"]
908 # remove the locks entirely
909 master
.loadConfig(lockCfg2c
)
910 self
.failIfIdentical(b1
, master
.botmaster
.builders
["builder1"])
912 class ConfigElements(unittest
.TestCase
):
913 # verify that ComparableMixin is working
914 def testSchedulers(self
):
915 s1
= scheduler
.Scheduler(name
='quick', branch
=None,
917 builderNames
=['quick'])
918 s2
= scheduler
.Scheduler(name
="all", branch
=None,
919 treeStableTimer
=5*60,
920 builderNames
=["a", "b"])
921 s3
= scheduler
.Try_Userpass("try", ["a","b"], port
=9989,
922 userpass
=[("foo","bar")])
923 s1a
= scheduler
.Scheduler(name
='quick', branch
=None,
925 builderNames
=['quick'])
926 s2a
= scheduler
.Scheduler(name
="all", branch
=None,
927 treeStableTimer
=5*60,
928 builderNames
=["a", "b"])
929 s3a
= scheduler
.Try_Userpass("try", ["a","b"], port
=9989,
930 userpass
=[("foo","bar")])
931 self
.failUnless(s1
== s1
)
932 self
.failUnless(s1
== s1a
)
933 self
.failUnless(s1a
in [s1
, s2
, s3
])
934 self
.failUnless(s2a
in [s1
, s2
, s3
])
935 self
.failUnless(s3a
in [s1
, s2
, s3
])
939 class ConfigFileTest(unittest
.TestCase
):
941 def testFindConfigFile(self
):
943 open(os
.path
.join("test_cf", "master.cfg"), "w").write(emptyCfg
)
944 slaveportCfg
= emptyCfg
+ "c['slavePortnum'] = 9000\n"
945 open(os
.path
.join("test_cf", "alternate.cfg"), "w").write(slaveportCfg
)
947 m
= BuildMaster("test_cf")
948 m
.loadTheConfigFile()
949 self
.failUnlessEqual(m
.slavePortnum
, "tcp:9999")
951 m
= BuildMaster("test_cf", "alternate.cfg")
952 m
.loadTheConfigFile()
953 self
.failUnlessEqual(m
.slavePortnum
, "tcp:9000")
956 class MyTarget(base
.StatusReceiverMultiService
):
957 def __init__(self
, name
):
959 base
.StatusReceiverMultiService
.__init
__(self
)
960 def startService(self
):
961 # make a note in a list stashed in the BuildMaster
962 self
.parent
.targetevents
.append(("start", self
.name
))
963 return base
.StatusReceiverMultiService
.startService(self
)
964 def stopService(self
):
965 self
.parent
.targetevents
.append(("stop", self
.name
))
966 return base
.StatusReceiverMultiService
.stopService(self
)
968 class MySlowTarget(MyTarget
):
969 def stopService(self
):
970 from twisted
.internet
import reactor
971 d
= base
.StatusReceiverMultiService
.stopService(self
)
973 d2
= defer
.Deferred()
974 reactor
.callLater(0.1, d2
.callback
, res
)
978 def finishedStalling(res
):
979 m
.targetevents
.append(("stop", self
.name
))
981 d
.addCallback(finishedStalling
)
984 # we can't actually startService a buildmaster with a config that uses a
985 # fixed slavePortnum like 9999, so instead this makes it possible to pass '0'
986 # for the first time, and then substitute back in the allocated port number
987 # on subsequent passes.
988 startableEmptyCfg
= emptyCfg
+ \
990 c['slavePortnum'] = %d
993 targetCfg1
= startableEmptyCfg
+ \
995 from buildbot.test.test_config import MyTarget
996 c['status'] = [MyTarget('a')]
999 targetCfg2
= startableEmptyCfg
+ \
1001 from buildbot.test.test_config import MySlowTarget
1002 c['status'] = [MySlowTarget('b')]
1005 class StartService(unittest
.TestCase
):
1007 return self
.master
.stopService()
1009 def testStartService(self
):
1011 self
.master
= m
= BuildMaster("test_ss")
1013 d
= m
.loadConfig(startableEmptyCfg
% 0)
1014 d
.addCallback(self
._testStartService
_0)
1017 def _testStartService_0(self
, res
):
1020 # figure out what port got allocated
1021 self
.portnum
= m
.slavePort
._port
.getHost().port
1022 d
= m
.loadConfig(targetCfg1
% self
.portnum
)
1023 d
.addCallback(self
._testStartService
_1)
1026 def _testStartService_1(self
, res
):
1027 self
.failUnlessEqual(len(self
.master
.statusTargets
), 1)
1028 self
.failUnless(isinstance(self
.master
.statusTargets
[0], MyTarget
))
1029 self
.failUnlessEqual(self
.master
.targetevents
,
1031 self
.master
.targetevents
= []
1032 # reloading the same config should not start or stop the target
1033 d
= self
.master
.loadConfig(targetCfg1
% self
.portnum
)
1034 d
.addCallback(self
._testStartService
_2)
1037 def _testStartService_2(self
, res
):
1038 self
.failUnlessEqual(self
.master
.targetevents
, [])
1039 # but loading a new config file should stop the old one, then
1041 d
= self
.master
.loadConfig(targetCfg2
% self
.portnum
)
1042 d
.addCallback(self
._testStartService
_3)
1045 def _testStartService_3(self
, res
):
1046 self
.failUnlessEqual(self
.master
.targetevents
,
1047 [('stop', 'a'), ('start', 'b')])
1048 self
.master
.targetevents
= []
1049 # and going back to the old one should do the same, in the same
1050 # order, even though the current MySlowTarget takes a moment to shut
1052 d
= self
.master
.loadConfig(targetCfg1
% self
.portnum
)
1053 d
.addCallback(self
._testStartService
_4)
1056 def _testStartService_4(self
, res
):
1057 self
.failUnlessEqual(self
.master
.targetevents
,
1058 [('stop', 'b'), ('start', 'a')])