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 |