remove a lot of unused imports, marked by pyflakes
[buildbot.git] / buildbot / test / test_config.py
blobd5857f1f83a510a9742362e14a34ff99bcea9bc4
1 # -*- test-case-name: buildbot.test.test_config -*-
3 from __future__ import generators
4 import os
6 from twisted.trial import unittest
7 from twisted.python import failure
8 from twisted.internet import defer
10 cvstoys = None
11 try:
12 import cvstoys
13 from buildbot.changes.freshcvs import FreshCVSSource
14 except ImportError:
15 pass
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
28 words = None
29 try:
30 from buildbot.status import words
31 except ImportError:
32 pass
34 emptyCfg = \
35 """
36 BuildmasterConfig = c = {}
37 c['bots'] = []
38 c['sources'] = []
39 c['schedulers'] = []
40 c['builders'] = []
41 c['slavePortnum'] = 9999
42 c['projectName'] = 'dummy project'
43 c['projectURL'] = 'http://dummy.example.com'
44 c['buildbotURL'] = 'http://dummy.example.com/buildbot'
45 """
47 buildersCfg = \
48 """
49 from buildbot.process.factory import BasicBuildFactory
50 BuildmasterConfig = c = {}
51 c['bots'] = [('bot1', 'pw1')]
52 c['sources'] = []
53 c['schedulers'] = []
54 c['slavePortnum'] = 9999
55 f1 = BasicBuildFactory('cvsroot', 'cvsmodule')
56 c['builders'] = [{'name':'builder1', 'slavename':'bot1',
57 'builddir':'workdir', 'factory':f1}]
58 """
60 buildersCfg2 = buildersCfg + \
61 """
62 f1 = BasicBuildFactory('cvsroot', 'cvsmodule2')
63 c['builders'] = [{'name':'builder1', 'slavename':'bot1',
64 'builddir':'workdir', 'factory':f1}]
65 """
67 buildersCfg3 = buildersCfg2 + \
68 """
69 c['builders'].append({'name': 'builder2', 'slavename': 'bot1',
70 'builddir': 'workdir2', 'factory': f1 })
71 """
73 buildersCfg4 = buildersCfg2 + \
74 """
75 c['builders'] = [{ 'name': 'builder1', 'slavename': 'bot1',
76 'builddir': 'newworkdir', 'factory': f1 },
77 { 'name': 'builder2', 'slavename': 'bot1',
78 'builddir': 'workdir2', 'factory': f1 }]
79 """
81 wpCfg1 = buildersCfg + \
82 """
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}]
88 """
90 wpCfg2 = buildersCfg + \
91 """
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}]
98 """
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'
156 interlockCfgBad = \
158 from buildbot.process.factory import BasicBuildFactory
159 c = {}
160 c['bots'] = [('bot1', 'pw1')]
161 c['sources'] = []
162 c['schedulers'] = []
163 f1 = BasicBuildFactory('cvsroot', 'cvsmodule')
164 c['builders'] = [
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
177 lockCfgBad1 = \
179 from buildbot.process.step import Dummy
180 from buildbot.process.factory import BuildFactory, s
181 from buildbot.locks import MasterLock
182 c = {}
183 c['bots'] = [('bot1', 'pw1')]
184 c['sources'] = []
185 c['schedulers'] = []
186 l1 = MasterLock('lock1')
187 l2 = MasterLock('lock1') # duplicate lock name
188 f1 = BuildFactory([s(Dummy, locks=[])])
189 c['builders'] = [
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
199 lockCfgBad2 = \
201 from buildbot.process.step import Dummy
202 from buildbot.process.factory import BuildFactory, s
203 from buildbot.locks import MasterLock, SlaveLock
204 c = {}
205 c['bots'] = [('bot1', 'pw1')]
206 c['sources'] = []
207 c['schedulers'] = []
208 l1 = MasterLock('lock1')
209 l2 = SlaveLock('lock1') # duplicate lock name
210 f1 = BuildFactory([s(Dummy, locks=[])])
211 c['builders'] = [
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
221 lockCfgBad3 = \
223 from buildbot.process.step import Dummy
224 from buildbot.process.factory import BuildFactory, s
225 from buildbot.locks import MasterLock
226 c = {}
227 c['bots'] = [('bot1', 'pw1')]
228 c['sources'] = []
229 c['schedulers'] = []
230 l1 = MasterLock('lock1')
231 l2 = MasterLock('lock1') # duplicate lock name
232 f1 = BuildFactory([s(Dummy, locks=[l2])])
233 f2 = BuildFactory([s(Dummy)])
234 c['builders'] = [
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
244 lockCfg1a = \
246 from buildbot.process.factory import BasicBuildFactory
247 from buildbot.locks import MasterLock
248 c = {}
249 c['bots'] = [('bot1', 'pw1')]
250 c['sources'] = []
251 c['schedulers'] = []
252 f1 = BasicBuildFactory('cvsroot', 'cvsmodule')
253 l1 = MasterLock('lock1')
254 l2 = MasterLock('lock2')
255 c['builders'] = [
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
265 lockCfg1b = \
267 from buildbot.process.factory import BasicBuildFactory
268 from buildbot.locks import MasterLock
269 c = {}
270 c['bots'] = [('bot1', 'pw1')]
271 c['sources'] = []
272 c['schedulers'] = []
273 f1 = BasicBuildFactory('cvsroot', 'cvsmodule')
274 l1 = MasterLock('lock1')
275 l2 = MasterLock('lock2')
276 c['builders'] = [
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
287 lockCfg2a = \
289 from buildbot.process.step import Dummy
290 from buildbot.process.factory import BuildFactory, s
291 from buildbot.locks import MasterLock
292 c = {}
293 c['bots'] = [('bot1', 'pw1')]
294 c['sources'] = []
295 c['schedulers'] = []
296 l1 = MasterLock('lock1')
297 l2 = MasterLock('lock2')
298 f1 = BuildFactory([s(Dummy, locks=[l1,l2])])
299 f2 = BuildFactory([s(Dummy)])
301 c['builders'] = [
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
311 lockCfg2b = \
313 from buildbot.process.step import Dummy
314 from buildbot.process.factory import BuildFactory, s
315 from buildbot.locks import MasterLock
316 c = {}
317 c['bots'] = [('bot1', 'pw1')]
318 c['sources'] = []
319 c['schedulers'] = []
320 l1 = MasterLock('lock1')
321 l2 = MasterLock('lock2')
322 f1 = BuildFactory([s(Dummy, locks=[l1])])
323 f2 = BuildFactory([s(Dummy)])
325 c['builders'] = [
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
335 lockCfg2c = \
337 from buildbot.process.step import Dummy
338 from buildbot.process.factory import BuildFactory, s
339 from buildbot.locks import MasterLock
340 c = {}
341 c['bots'] = [('bot1', 'pw1')]
342 c['sources'] = []
343 c['schedulers'] = []
344 l1 = MasterLock('lock1')
345 l2 = MasterLock('lock2')
346 f1 = BuildFactory([s(Dummy)])
347 f2 = BuildFactory([s(Dummy)])
349 c['builders'] = [
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):
360 def setUp(self):
361 self.buildmaster = BuildMaster(".")
363 def failUnlessListsEquivalent(self, list1, list2):
364 l1 = list1[:]
365 l1.sort()
366 l2 = list2[:]
367 l2.sort()
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
374 for child in s:
375 if providedBy(child, service.IServiceCollection):
376 for gc in self.servers(child, types):
377 yield gc
378 if isinstance(child, types):
379 yield child
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)
403 ret = []
404 for e in expected:
405 for have in self.TCPports(svc) + self.UNIXports(svc):
406 if have.args[0] == e[0]:
407 ret.append(have)
408 continue
409 assert(len(ret) == len(expected))
410 return ret
412 def testEmpty(self):
413 self.failUnlessRaises(KeyError, self.buildmaster.loadConfig, "")
415 def testSimple(self):
416 # covers slavePortnum, base checker passwords
417 master = self.buildmaster
418 master.loadChanges()
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
436 master.loadChanges()
438 master.loadConfig(emptyCfg)
439 self.failUnlessEqual(master.slavePortnum, "tcp:9999")
440 ports = self.checkPorts(master, [(9999, pb.PBServerFactory)])
441 p = ports[0]
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")
456 def testBots(self):
457 master = self.buildmaster
458 master.loadChanges()
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",
468 "bot1": "pw1",
469 "bot2": "pw2"})
470 master.loadConfig(botsCfg)
471 self.failUnlessEqual(master.checker.users,
472 {"change": "changepw",
473 "bot1": "pw1",
474 "bot2": "pw2"})
475 master.loadConfig(emptyCfg)
476 self.failUnlessEqual(master.checker.users,
477 {"change": "changepw"})
480 def testSources(self):
481 if not cvstoys:
482 raise unittest.SkipTest("this test needs CVSToys installed")
483 master = self.buildmaster
484 master.loadChanges()
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',
492 prefix='Prefix/')
493 c['sources'] = [s1]
496 d = master.loadConfig(self.sourcesCfg)
497 d.addCallback(self._testSources_1)
498 return maybeWait(d)
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)
513 return d
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)
524 return d
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,))
532 res.trap(*expected)
533 return None # all is good
535 def testSchedulers(self):
536 master = self.buildmaster
537 master.loadChanges()
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
545 c = {}
546 c['bots'] = [('bot1', 'pw1')]
547 c['sources'] = []
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)
566 return maybeWait(d)
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)
576 return d
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)
586 return d
587 def _testSchedulers_3(self, res):
588 self.shouldBeFailure(res, AssertionError)
589 d = self.buildmaster.loadConfig(self.schedulersCfg)
590 d.addCallback(self._testSchedulers_4)
591 return d
592 def _testSchedulers_4(self, res):
593 sch = self.buildmaster.allSchedulers()
594 self.failUnlessEqual(len(sch), 1)
595 s = sch[0]
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)
609 return d
610 def _testSchedulers_5(self, res, newcfg):
611 sch = self.buildmaster.allSchedulers()
612 self.failUnlessEqual(len(sch), 2)
613 s = sch[0]
614 self.failUnless(isinstance(s, scheduler.Scheduler))
615 s = sch[1]
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)
623 return d
624 def _testSchedulers_6(self, res, sch1):
625 sch2 = self.buildmaster.allSchedulers()
626 self.failUnlessEqual(len(sch2), 2)
627 sch1.sort()
628 sch2.sort()
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)
641 return d
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")
660 f1 = b.buildFactory
661 self.failUnless(isinstance(f1, BasicBuildFactory))
662 steps = f1.steps
663 self.failUnlessEqual(len(steps), 3)
664 self.failUnlessEqual(steps[0], (step.CVS,
665 {'cvsroot': 'cvsroot',
666 'cvsmodule': 'cvsmodule',
667 'mode': 'clobber'}))
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"]
689 self.failIf(b is b3)
690 # the statusbag remains the same TODO
691 #statusbag3 = master.client_svc.statusbags["builder1"]
692 #self.failUnlessIdentical(statusbag, statusbag3)
694 # adding new builder
695 master.loadConfig(buildersCfg3)
696 self.failUnlessEqual(master.botmaster.builderNames, ["builder1",
697 "builder2"])
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",
706 "builder2"])
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):
741 ircs = {}
742 for irc in self.servers(m, words.IRC):
743 ircs[irc.host] = (irc.nick, irc.channels)
744 self.failUnlessEqual(ircs, expected)
746 def testIRC(self):
747 if not words:
748 raise unittest.SkipTest("Twisted Words package is not installed")
749 master = self.buildmaster
750 master.loadChanges()
751 d = master.loadConfig(emptyCfg)
752 e1 = {}
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))
767 return maybeWait(d)
769 def testWebPortnum(self):
770 master = self.buildmaster
771 master.loadChanges()
773 d = master.loadConfig(webCfg1)
774 d.addCallback(self._testWebPortnum_1)
775 return maybeWait(d)
776 def _testWebPortnum_1(self, res):
777 ports = self.checkPorts(self.buildmaster, [(9999, pb.PBServerFactory),
778 (9980, Site)])
779 p = ports[1]
781 d = self.buildmaster.loadConfig(webCfg1) # nothing should be changed
782 d.addCallback(self._testWebPortnum_2, p)
783 return d
784 def _testWebPortnum_2(self, res, p):
785 ports = self.checkPorts(self.buildmaster, [(9999, pb.PBServerFactory),
786 (9980, Site)])
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)
793 return d
794 def _testWebPortnum_3(self, res, p):
795 ports = self.checkPorts(self.buildmaster, [(9999, pb.PBServerFactory),
796 (9981, Site)])
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])
801 return d
802 def _testWebPortnum_4(self, res, p):
803 ports = self.checkPorts(self.buildmaster, [(9999, pb.PBServerFactory),
804 (9981, Site)])
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)]))
810 return d
812 def testWebPathname(self):
813 master = self.buildmaster
814 master.loadChanges()
816 d = master.loadConfig(webNameCfg1)
817 d.addCallback(self._testWebPathname_1)
818 return maybeWait(d)
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)
830 return d
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)
842 return d
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)]))
854 return d
856 def testDebugPassword(self):
857 master = self.buildmaster
859 master.loadConfig(debugPasswordCfg)
860 self.failUnlessEqual(master.checker.users,
861 {"change": "changepw",
862 "debug": "sekrit"})
864 master.loadConfig(debugPasswordCfg)
865 self.failUnlessEqual(master.checker.users,
866 {"change": "changepw",
867 "debug": "sekrit"})
869 master.loadConfig(emptyCfg)
870 self.failUnlessEqual(master.checker.users,
871 {"change": "changepw"})
873 def testLocks(self):
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,
916 treeStableTimer=30,
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,
924 treeStableTimer=30,
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):
942 os.mkdir("test_cf")
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):
958 self.name = 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)
972 def stall(res):
973 d2 = defer.Deferred()
974 reactor.callLater(0.1, d2.callback, res)
975 return d2
976 d.addCallback(stall)
977 m = self.parent
978 def finishedStalling(res):
979 m.targetevents.append(("stop", self.name))
980 return res
981 d.addCallback(finishedStalling)
982 return d
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):
1006 def tearDown(self):
1007 return self.master.stopService()
1009 def testStartService(self):
1010 os.mkdir("test_ss")
1011 self.master = m = BuildMaster("test_ss")
1012 m.startService()
1013 d = m.loadConfig(startableEmptyCfg % 0)
1014 d.addCallback(self._testStartService_0)
1015 return maybeWait(d)
1017 def _testStartService_0(self, res):
1018 m = self.master
1019 m.targetevents = []
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)
1024 return d
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,
1030 [('start', 'a')])
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)
1035 return d
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
1040 # start the new one
1041 d = self.master.loadConfig(targetCfg2 % self.portnum)
1042 d.addCallback(self._testStartService_3)
1043 return d
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
1051 # down
1052 d = self.master.loadConfig(targetCfg1 % self.portnum)
1053 d.addCallback(self._testStartService_4)
1054 return d
1056 def _testStartService_4(self, res):
1057 self.failUnlessEqual(self.master.targetevents,
1058 [('stop', 'b'), ('start', 'a')])