OLD | NEW |
| (Empty) |
1 # -*- test-case-name: buildbot.test.test_locks -*- | |
2 | |
3 import random | |
4 | |
5 from twisted.trial import unittest | |
6 from twisted.internet import defer, reactor | |
7 | |
8 from buildbot import master | |
9 from buildbot.steps import dummy | |
10 from buildbot.sourcestamp import SourceStamp | |
11 from buildbot.process.base import BuildRequest | |
12 from buildbot.test.runutils import RunMixin | |
13 from buildbot import locks | |
14 | |
15 def claimHarder(lock, owner, la): | |
16 """Return a Deferred that will fire when the lock is claimed. Keep trying | |
17 until we succeed.""" | |
18 if lock.isAvailable(la): | |
19 #print "claimHarder(%s): claiming" % owner | |
20 lock.claim(owner, la) | |
21 return defer.succeed(lock) | |
22 #print "claimHarder(%s): waiting" % owner | |
23 d = lock.waitUntilMaybeAvailable(owner, la) | |
24 d.addCallback(claimHarder, owner, la) | |
25 return d | |
26 | |
27 def hold(lock, owner, la, mode="now"): | |
28 if mode == "now": | |
29 lock.release(owner, la) | |
30 elif mode == "very soon": | |
31 reactor.callLater(0, lock.release, owner, la) | |
32 elif mode == "soon": | |
33 reactor.callLater(0.1, lock.release, owner, la) | |
34 | |
35 class Unit(unittest.TestCase): | |
36 def testNowCounting(self): | |
37 lid = locks.MasterLock('dummy') | |
38 la = locks.LockAccess(lid, 'counting') | |
39 return self._testNow(la) | |
40 | |
41 def testNowExclusive(self): | |
42 lid = locks.MasterLock('dummy') | |
43 la = locks.LockAccess(lid, 'exclusive') | |
44 return self._testNow(la) | |
45 | |
46 def _testNow(self, la): | |
47 l = locks.BaseLock("name") | |
48 self.failUnless(l.isAvailable(la)) | |
49 l.claim("owner1", la) | |
50 self.failIf(l.isAvailable(la)) | |
51 l.release("owner1", la) | |
52 self.failUnless(l.isAvailable(la)) | |
53 | |
54 def testNowMixed1(self): | |
55 """ Test exclusive is not possible when a counting has the lock """ | |
56 lid = locks.MasterLock('dummy') | |
57 lac = locks.LockAccess(lid, 'counting') | |
58 lae = locks.LockAccess(lid, 'exclusive') | |
59 l = locks.BaseLock("name", maxCount=2) | |
60 self.failUnless(l.isAvailable(lac)) | |
61 l.claim("count-owner", lac) | |
62 self.failIf(l.isAvailable(lae)) | |
63 l.release("count-owner", lac) | |
64 self.failUnless(l.isAvailable(lac)) | |
65 | |
66 def testNowMixed2(self): | |
67 """ Test counting is not possible when an exclsuive has the lock """ | |
68 lid = locks.MasterLock('dummy') | |
69 lac = locks.LockAccess(lid, 'counting') | |
70 lae = locks.LockAccess(lid, 'exclusive') | |
71 l = locks.BaseLock("name", maxCount=2) | |
72 self.failUnless(l.isAvailable(lae)) | |
73 l.claim("count-owner", lae) | |
74 self.failIf(l.isAvailable(lac)) | |
75 l.release("count-owner", lae) | |
76 self.failUnless(l.isAvailable(lae)) | |
77 | |
78 def testLaterCounting(self): | |
79 lid = locks.MasterLock('dummy') | |
80 la = locks.LockAccess(lid, 'counting') | |
81 return self._testLater(la) | |
82 | |
83 def testLaterExclusive(self): | |
84 lid = locks.MasterLock('dummy') | |
85 la = locks.LockAccess(lid, 'exclusive') | |
86 return self._testLater(la) | |
87 | |
88 def _testLater(self, la): | |
89 lock = locks.BaseLock("name") | |
90 d = claimHarder(lock, "owner1", la) | |
91 d.addCallback(lambda lock: lock.release("owner1", la)) | |
92 return d | |
93 | |
94 def testCompetitionCounting(self): | |
95 lid = locks.MasterLock('dummy') | |
96 la = locks.LockAccess(lid, 'counting') | |
97 return self._testCompetition(la) | |
98 | |
99 def testCompetitionExclusive(self): | |
100 lid = locks.MasterLock('dummy') | |
101 la = locks.LockAccess(lid, 'exclusive') | |
102 return self._testCompetition(la) | |
103 | |
104 def _testCompetition(self, la): | |
105 lock = locks.BaseLock("name") | |
106 d = claimHarder(lock, "owner1", la) | |
107 d.addCallback(self._claim1, la) | |
108 return d | |
109 def _claim1(self, lock, la): | |
110 # we should have claimed it by now | |
111 self.failIf(lock.isAvailable(la)) | |
112 # now set up two competing owners. We don't know which will get the | |
113 # lock first. | |
114 d2 = claimHarder(lock, "owner2", la) | |
115 d2.addCallback(hold, "owner2", la, "now") | |
116 d3 = claimHarder(lock, "owner3", la) | |
117 d3.addCallback(hold, "owner3", la, "soon") | |
118 dl = defer.DeferredList([d2,d3]) | |
119 dl.addCallback(self._cleanup, lock, la) | |
120 # and release the lock in a moment | |
121 reactor.callLater(0.1, lock.release, "owner1", la) | |
122 return dl | |
123 | |
124 def _cleanup(self, res, lock, la): | |
125 d = claimHarder(lock, "cleanup", la) | |
126 d.addCallback(lambda lock: lock.release("cleanup", la)) | |
127 return d | |
128 | |
129 def testRandomCounting(self): | |
130 lid = locks.MasterLock('dummy') | |
131 la = locks.LockAccess(lid, 'counting') | |
132 return self._testRandom(la) | |
133 | |
134 def testRandomExclusive(self): | |
135 lid = locks.MasterLock('dummy') | |
136 la = locks.LockAccess(lid, 'exclusive') | |
137 return self._testRandom(la) | |
138 | |
139 def _testRandom(self, la): | |
140 lock = locks.BaseLock("name") | |
141 dl = [] | |
142 for i in range(100): | |
143 owner = "owner%d" % i | |
144 mode = random.choice(["now", "very soon", "soon"]) | |
145 d = claimHarder(lock, owner, la) | |
146 d.addCallback(hold, owner, la, mode) | |
147 dl.append(d) | |
148 d = defer.DeferredList(dl) | |
149 d.addCallback(self._cleanup, lock, la) | |
150 return d | |
151 | |
152 class Multi(unittest.TestCase): | |
153 def testNowCounting(self): | |
154 lid = locks.MasterLock('dummy') | |
155 la = locks.LockAccess(lid, 'counting') | |
156 lock = locks.BaseLock("name", 2) | |
157 self.failUnless(lock.isAvailable(la)) | |
158 lock.claim("owner1", la) | |
159 self.failUnless(lock.isAvailable(la)) | |
160 lock.claim("owner2", la) | |
161 self.failIf(lock.isAvailable(la)) | |
162 lock.release("owner1", la) | |
163 self.failUnless(lock.isAvailable(la)) | |
164 lock.release("owner2", la) | |
165 self.failUnless(lock.isAvailable(la)) | |
166 | |
167 def testLaterCounting(self): | |
168 lid = locks.MasterLock('dummy') | |
169 la = locks.LockAccess(lid, 'counting') | |
170 lock = locks.BaseLock("name", 2) | |
171 lock.claim("owner1", la) | |
172 lock.claim("owner2", la) | |
173 d = claimHarder(lock, "owner3", la) | |
174 d.addCallback(lambda lock: lock.release("owner3", la)) | |
175 lock.release("owner2", la) | |
176 lock.release("owner1", la) | |
177 return d | |
178 | |
179 def _cleanup(self, res, lock, count, la): | |
180 dl = [] | |
181 for i in range(count): | |
182 d = claimHarder(lock, "cleanup%d" % i, la) | |
183 dl.append(d) | |
184 d2 = defer.DeferredList(dl) | |
185 # once all locks are claimed, we know that any previous owners have | |
186 # been flushed out | |
187 def _release(res): | |
188 for i in range(count): | |
189 lock.release("cleanup%d" % i, la) | |
190 d2.addCallback(_release) | |
191 return d2 | |
192 | |
193 def testRandomCounting(self): | |
194 lid = locks.MasterLock('dummy') | |
195 la = locks.LockAccess(lid, 'counting') | |
196 COUNT = 5 | |
197 lock = locks.BaseLock("name", COUNT) | |
198 dl = [] | |
199 for i in range(100): | |
200 owner = "owner%d" % i | |
201 mode = random.choice(["now", "very soon", "soon"]) | |
202 d = claimHarder(lock, owner, la) | |
203 def _check(lock): | |
204 self.failIf(len(lock.owners) > COUNT) | |
205 return lock | |
206 d.addCallback(_check) | |
207 d.addCallback(hold, owner, la, mode) | |
208 dl.append(d) | |
209 d = defer.DeferredList(dl) | |
210 d.addCallback(self._cleanup, lock, COUNT, la) | |
211 return d | |
212 | |
213 class Dummy: | |
214 pass | |
215 | |
216 def slave(slavename): | |
217 slavebuilder = Dummy() | |
218 slavebuilder.slave = Dummy() | |
219 slavebuilder.slave.slavename = slavename | |
220 return slavebuilder | |
221 | |
222 class MakeRealLock(unittest.TestCase): | |
223 | |
224 def make(self, lockid): | |
225 return lockid.lockClass(lockid) | |
226 | |
227 def testMaster(self): | |
228 mid1 = locks.MasterLock("name1") | |
229 mid2 = locks.MasterLock("name1") | |
230 mid3 = locks.MasterLock("name3") | |
231 mid4 = locks.MasterLock("name1", 3) | |
232 self.failUnlessEqual(mid1, mid2) | |
233 self.failIfEqual(mid1, mid3) | |
234 # they should all be hashable | |
235 d = {mid1: 1, mid2: 2, mid3: 3, mid4: 4} | |
236 | |
237 l1 = self.make(mid1) | |
238 self.failUnlessEqual(l1.name, "name1") | |
239 self.failUnlessEqual(l1.maxCount, 1) | |
240 self.failUnlessIdentical(l1.getLock(slave("slave1")), l1) | |
241 l4 = self.make(mid4) | |
242 self.failUnlessEqual(l4.name, "name1") | |
243 self.failUnlessEqual(l4.maxCount, 3) | |
244 self.failUnlessIdentical(l4.getLock(slave("slave1")), l4) | |
245 | |
246 def testSlave(self): | |
247 sid1 = locks.SlaveLock("name1") | |
248 sid2 = locks.SlaveLock("name1") | |
249 sid3 = locks.SlaveLock("name3") | |
250 sid4 = locks.SlaveLock("name1", maxCount=3) | |
251 mcfs = {"bigslave": 4, "smallslave": 1} | |
252 sid5 = locks.SlaveLock("name1", maxCount=3, maxCountForSlave=mcfs) | |
253 mcfs2 = {"bigslave": 4, "smallslave": 1} | |
254 sid5a = locks.SlaveLock("name1", maxCount=3, maxCountForSlave=mcfs2) | |
255 mcfs3 = {"bigslave": 1, "smallslave": 99} | |
256 sid5b = locks.SlaveLock("name1", maxCount=3, maxCountForSlave=mcfs3) | |
257 self.failUnlessEqual(sid1, sid2) | |
258 self.failIfEqual(sid1, sid3) | |
259 self.failIfEqual(sid1, sid4) | |
260 self.failIfEqual(sid1, sid5) | |
261 self.failUnlessEqual(sid5, sid5a) | |
262 self.failIfEqual(sid5a, sid5b) | |
263 # they should all be hashable | |
264 d = {sid1: 1, sid2: 2, sid3: 3, sid4: 4, sid5: 5, sid5a: 6, sid5b: 7} | |
265 | |
266 l1 = self.make(sid1) | |
267 self.failUnlessEqual(l1.name, "name1") | |
268 self.failUnlessEqual(l1.maxCount, 1) | |
269 l1s1 = l1.getLock(slave("slave1")) | |
270 self.failIfIdentical(l1s1, l1) | |
271 | |
272 l4 = self.make(sid4) | |
273 self.failUnlessEqual(l4.maxCount, 3) | |
274 l4s1 = l4.getLock(slave("slave1")) | |
275 self.failUnlessEqual(l4s1.maxCount, 3) | |
276 | |
277 l5 = self.make(sid5) | |
278 l5s1 = l5.getLock(slave("bigslave")) | |
279 l5s2 = l5.getLock(slave("smallslave")) | |
280 l5s3 = l5.getLock(slave("unnamedslave")) | |
281 self.failUnlessEqual(l5s1.maxCount, 4) | |
282 self.failUnlessEqual(l5s2.maxCount, 1) | |
283 self.failUnlessEqual(l5s3.maxCount, 3) | |
284 | |
285 class GetLock(unittest.TestCase): | |
286 def testGet(self): | |
287 # the master.cfg file contains "lock ids", which are instances of | |
288 # MasterLock and SlaveLock but which are not actually Locks per se. | |
289 # When the build starts, these markers are turned into RealMasterLock | |
290 # and RealSlaveLock instances. This insures that any builds running | |
291 # on slaves that were unaffected by the config change are still | |
292 # referring to the same Lock instance as new builds by builders that | |
293 # *were* affected by the change. There have been bugs in the past in | |
294 # which this didn't happen, and the Locks were bypassed because half | |
295 # the builders were using one incarnation of the lock while the other | |
296 # half were using a separate (but equal) incarnation. | |
297 # | |
298 # Changing the lock id in any way should cause it to be replaced in | |
299 # the BotMaster. This will result in a couple of funky artifacts: | |
300 # builds in progress might pay attention to a different lock, so we | |
301 # might bypass the locking for the duration of a couple builds. | |
302 # There's also the problem of old Locks lingering around in | |
303 # BotMaster.locks, but they're small and shouldn't really cause a | |
304 # problem. | |
305 | |
306 b = master.BotMaster() | |
307 l1 = locks.MasterLock("one") | |
308 l1a = locks.MasterLock("one") | |
309 l2 = locks.MasterLock("one", maxCount=4) | |
310 | |
311 rl1 = b.getLockByID(l1) | |
312 rl2 = b.getLockByID(l1a) | |
313 self.failUnlessIdentical(rl1, rl2) | |
314 rl3 = b.getLockByID(l2) | |
315 self.failIfIdentical(rl1, rl3) | |
316 | |
317 s1 = locks.SlaveLock("one") | |
318 s1a = locks.SlaveLock("one") | |
319 s2 = locks.SlaveLock("one", maxCount=4) | |
320 s3 = locks.SlaveLock("one", maxCount=4, | |
321 maxCountForSlave={"a":1, "b":2}) | |
322 s3a = locks.SlaveLock("one", maxCount=4, | |
323 maxCountForSlave={"a":1, "b":2}) | |
324 s4 = locks.SlaveLock("one", maxCount=4, | |
325 maxCountForSlave={"a":4, "b":4}) | |
326 | |
327 rl1 = b.getLockByID(s1) | |
328 rl2 = b.getLockByID(s1a) | |
329 self.failUnlessIdentical(rl1, rl2) | |
330 rl3 = b.getLockByID(s2) | |
331 self.failIfIdentical(rl1, rl3) | |
332 rl4 = b.getLockByID(s3) | |
333 self.failIfIdentical(rl1, rl4) | |
334 self.failIfIdentical(rl3, rl4) | |
335 rl5 = b.getLockByID(s3a) | |
336 self.failUnlessIdentical(rl4, rl5) | |
337 rl6 = b.getLockByID(s4) | |
338 self.failIfIdentical(rl5, rl6) | |
339 | |
340 | |
341 | |
342 class LockStep(dummy.Dummy): | |
343 def start(self): | |
344 number = self.build.requests[0].number | |
345 self.build.requests[0].events.append(("start", number)) | |
346 dummy.Dummy.start(self) | |
347 def done(self): | |
348 number = self.build.requests[0].number | |
349 self.build.requests[0].events.append(("done", number)) | |
350 dummy.Dummy.done(self) | |
351 | |
352 config_1 = """ | |
353 from buildbot import locks | |
354 from buildbot.process import factory | |
355 from buildbot.buildslave import BuildSlave | |
356 from buildbot.config import BuilderConfig | |
357 s = factory.s | |
358 from buildbot.test.test_locks import LockStep | |
359 | |
360 BuildmasterConfig = c = {} | |
361 c['slaves'] = [BuildSlave('bot1', 'sekrit'), BuildSlave('bot2', 'sekrit')] | |
362 c['schedulers'] = [] | |
363 c['slavePortnum'] = 0 | |
364 | |
365 first_lock = locks.SlaveLock('first') | |
366 second_lock = locks.MasterLock('second') | |
367 f1 = factory.BuildFactory([s(LockStep, timeout=2, locks=[first_lock])]) | |
368 f2 = factory.BuildFactory([s(LockStep, timeout=3, locks=[second_lock])]) | |
369 f3 = factory.BuildFactory([s(LockStep, timeout=2, locks=[])]) | |
370 | |
371 b1a = BuilderConfig(name='full1a', slavename='bot1', factory=f1) | |
372 b1b = BuilderConfig(name='full1b', slavename='bot1', factory=f1) | |
373 b1c = BuilderConfig(name='full1c', slavename='bot1', factory=f3, | |
374 locks=[first_lock, second_lock]) | |
375 b1d = BuilderConfig(name='full1d', slavename='bot1', factory=f2) | |
376 | |
377 b2a = BuilderConfig(name='full2a', slavename='bot2', factory=f1) | |
378 b2b = BuilderConfig(name='full2b', slavename='bot2', factory=f3, | |
379 locks=[second_lock]) | |
380 c['builders'] = [b1a, b1b, b1c, b1d, b2a, b2b] | |
381 """ | |
382 | |
383 config_1a = config_1 + \ | |
384 """ | |
385 b1b = BuilderConfig(name='full1b', builddir='1B', slavename='bot1', factory=f1) | |
386 c['builders'] = [b1a, b1b, b1c, b1d, b2a, b2b] | |
387 """ | |
388 | |
389 | |
390 class Locks(RunMixin, unittest.TestCase): | |
391 def setUp(self): | |
392 N = 'test_builder' | |
393 RunMixin.setUp(self) | |
394 self.req1 = req1 = BuildRequest("forced build", SourceStamp(), N) | |
395 req1.number = 1 | |
396 self.req2 = req2 = BuildRequest("forced build", SourceStamp(), N) | |
397 req2.number = 2 | |
398 self.req3 = req3 = BuildRequest("forced build", SourceStamp(), N) | |
399 req3.number = 3 | |
400 req1.events = req2.events = req3.events = self.events = [] | |
401 d = self.master.loadConfig(config_1) | |
402 d.addCallback(lambda res: self.master.startService()) | |
403 d.addCallback(lambda res: self.connectSlave( | |
404 ["full1a", "full1b", "full1c", "full1d"], | |
405 "bot1")) | |
406 d.addCallback(lambda res: self.connectSlave(["full2a", "full2b"], "bot2"
)) | |
407 return d | |
408 | |
409 def testLock1(self): | |
410 self.control.getBuilder("full1a").requestBuild(self.req1) | |
411 self.control.getBuilder("full1b").requestBuild(self.req2) | |
412 d = defer.DeferredList([self.req1.waitUntilFinished(), | |
413 self.req2.waitUntilFinished()]) | |
414 d.addCallback(self._testLock1_1) | |
415 return d | |
416 | |
417 def _testLock1_1(self, res): | |
418 # full1a should complete its step before full1b starts it | |
419 self.failUnlessEqual(self.events, | |
420 [("start", 1), ("done", 1), | |
421 ("start", 2), ("done", 2)]) | |
422 | |
423 def dont_testLock1a(self): ## disabled -- test itself is buggy | |
424 # just like testLock1, but we reload the config file first, with a | |
425 # change that causes full1b to be changed. This tickles a design bug | |
426 # in which full1a and full1b wind up with distinct Lock instances. | |
427 d = self.master.loadConfig(config_1a) | |
428 d.addCallback(self._testLock1a_1) | |
429 return d | |
430 def _testLock1a_1(self, res): | |
431 self.control.getBuilder("full1a").requestBuild(self.req1) | |
432 self.control.getBuilder("full1b").requestBuild(self.req2) | |
433 d = defer.DeferredList([self.req1.waitUntilFinished(), | |
434 self.req2.waitUntilFinished()]) | |
435 d.addCallback(self._testLock1a_2) | |
436 return d | |
437 | |
438 def _testLock1a_2(self, res): | |
439 # full1a should complete its step before full1b starts it | |
440 self.failUnlessEqual(self.events, | |
441 [("start", 1), ("done", 1), | |
442 ("start", 2), ("done", 2)]) | |
443 | |
444 def testLock2(self): | |
445 # two builds run on separate slaves with slave-scoped locks should | |
446 # not interfere | |
447 self.control.getBuilder("full1a").requestBuild(self.req1) | |
448 self.control.getBuilder("full2a").requestBuild(self.req2) | |
449 d = defer.DeferredList([self.req1.waitUntilFinished(), | |
450 self.req2.waitUntilFinished()]) | |
451 d.addCallback(self._testLock2_1) | |
452 return d | |
453 | |
454 def _testLock2_1(self, res): | |
455 # full2a should start its step before full1a finishes it. They run on | |
456 # different slaves, however, so they might start in either order. | |
457 self.failUnless(self.events[:2] == [("start", 1), ("start", 2)] or | |
458 self.events[:2] == [("start", 2), ("start", 1)]) | |
459 | |
460 def dont_testLock3(self): ## disabled -- test fails sporadically | |
461 # two builds run on separate slaves with master-scoped locks should | |
462 # not overlap | |
463 self.control.getBuilder("full1c").requestBuild(self.req1) | |
464 self.control.getBuilder("full2b").requestBuild(self.req2) | |
465 d = defer.DeferredList([self.req1.waitUntilFinished(), | |
466 self.req2.waitUntilFinished()]) | |
467 d.addCallback(self._testLock3_1) | |
468 return d | |
469 | |
470 def _testLock3_1(self, res): | |
471 # full2b should not start until after full1c finishes. The builds run | |
472 # on different slaves, so we can't really predict which will start | |
473 # first. The important thing is that they don't overlap. | |
474 self.failUnless(self.events == [("start", 1), ("done", 1), | |
475 ("start", 2), ("done", 2)] | |
476 or self.events == [("start", 2), ("done", 2), | |
477 ("start", 1), ("done", 1)] | |
478 ) | |
479 | |
480 # This test has been disabled due to flakeyness/intermittentness | |
481 # def testLock4(self): | |
482 # self.control.getBuilder("full1a").requestBuild(self.req1) | |
483 # self.control.getBuilder("full1c").requestBuild(self.req2) | |
484 # self.control.getBuilder("full1d").requestBuild(self.req3) | |
485 # d = defer.DeferredList([self.req1.waitUntilFinished(), | |
486 # self.req2.waitUntilFinished(), | |
487 # self.req3.waitUntilFinished()]) | |
488 # d.addCallback(self._testLock4_1) | |
489 # return d | |
490 # | |
491 # def _testLock4_1(self, res): | |
492 # # full1a starts, then full1d starts (because they do not interfere). | |
493 # # Once both are done, full1c can run. | |
494 # self.failUnlessEqual(self.events, | |
495 # [("start", 1), ("start", 3), | |
496 # ("done", 1), ("done", 3), | |
497 # ("start", 2), ("done", 2)]) | |
498 | |
499 class BuilderLocks(RunMixin, unittest.TestCase): | |
500 config = """\ | |
501 from buildbot import locks | |
502 from buildbot.process import factory | |
503 from buildbot.buildslave import BuildSlave | |
504 from buildbot.config import BuilderConfig | |
505 s = factory.s | |
506 from buildbot.test.test_locks import LockStep | |
507 | |
508 BuildmasterConfig = c = {} | |
509 c['slaves'] = [BuildSlave('bot1', 'sekrit'), BuildSlave('bot2', 'sekrit')] | |
510 c['schedulers'] = [] | |
511 c['slavePortnum'] = 0 | |
512 | |
513 master_lock = locks.MasterLock('master', maxCount=2) | |
514 f_excl = factory.BuildFactory([s(LockStep, timeout=0, | |
515 locks=[master_lock.access("exclusive")])]) | |
516 f_count = factory.BuildFactory([s(LockStep, timeout=0, | |
517 locks=[master_lock])]) | |
518 | |
519 slaves = ['bot1', 'bot2'] | |
520 c['builders'] = [ | |
521 BuilderConfig(name='excl_A', slavenames=slaves, factory=f_excl), | |
522 BuilderConfig(name='excl_B', slavenames=slaves, factory=f_excl), | |
523 BuilderConfig(name='count_A', slavenames=slaves, factory=f_count), | |
524 BuilderConfig(name='count_B', slavenames=slaves, factory=f_count), | |
525 ] | |
526 """ | |
527 | |
528 def setUp(self): | |
529 N = 'test_builder' | |
530 RunMixin.setUp(self) | |
531 self.reqs = [BuildRequest("forced build", SourceStamp(), N) | |
532 for i in range(4)] | |
533 self.events = [] | |
534 for i in range(4): | |
535 self.reqs[i].number = i | |
536 self.reqs[i].events = self.events | |
537 d = self.master.loadConfig(self.config) | |
538 d.addCallback(lambda res: self.master.startService()) | |
539 d.addCallback(lambda res: self.connectSlave( | |
540 ["excl_A", "excl_B", "count_A", "count_B"], "bot1")) | |
541 d.addCallback(lambda res: self.connectSlave( | |
542 ["excl_A", "excl_B", "count_A", "count_B"], "bot2")) | |
543 return d | |
544 | |
545 def testOrder(self): | |
546 self.control.getBuilder("excl_A").requestBuild(self.reqs[0]) | |
547 self.control.getBuilder("excl_B").requestBuild(self.reqs[1]) | |
548 self.control.getBuilder("count_A").requestBuild(self.reqs[2]) | |
549 self.control.getBuilder("count_B").requestBuild(self.reqs[3]) | |
550 d = defer.DeferredList([r.waitUntilFinished() | |
551 for r in self.reqs]) | |
552 d.addCallback(self._testOrder) | |
553 return d | |
554 | |
555 def _testOrder(self, res): | |
556 # excl_A and excl_B cannot overlap with any other steps. | |
557 self.assert_(("start", 0) in self.events) | |
558 self.assert_(("done", 0) in self.events) | |
559 self.assert_(self.events.index(("start", 0)) + 1 == | |
560 self.events.index(("done", 0))) | |
561 | |
562 self.assert_(("start", 1) in self.events) | |
563 self.assert_(("done", 1) in self.events) | |
564 self.assert_(self.events.index(("start", 1)) + 1 == | |
565 self.events.index(("done", 1))) | |
566 | |
567 # FIXME: We really want to test that count_A and count_B were | |
568 # overlapped, but don't have a reliable way to do this. | |
OLD | NEW |