1 # -*- test-case-name: buildbot.test.test_scheduler -*-
5 from twisted
.trial
import unittest
6 from twisted
.internet
import defer
, reactor
7 from twisted
.application
import service
8 from twisted
.spread
import pb
10 from buildbot
import scheduler
, sourcestamp
, buildset
, status
11 from buildbot
.changes
.changes
import Change
12 from buildbot
.scripts
import tryclient
15 class FakeMaster(service
.MultiService
):
17 def submitBuildSet(self
, bs
):
20 reactor
.callLater(0, self
.d
.callback
, bs
)
22 return pb
.Referenceable() # makes the cleanup work correctly
24 class Scheduling(unittest
.TestCase
):
26 self
.master
= master
= FakeMaster()
31 d
= self
.master
.stopService()
34 def addScheduler(self
, s
):
35 s
.setServiceParent(self
.master
)
37 def testPeriodic1(self
):
38 self
.addScheduler(scheduler
.Periodic("quickly", ["a","b"], 2))
40 reactor
.callLater(5, d
.callback
, None)
41 d
.addCallback(self
._testPeriodic
1_1)
43 def _testPeriodic1_1(self
, res
):
44 self
.failUnless(len(self
.master
.sets
) > 1)
45 s1
= self
.master
.sets
[0]
46 self
.failUnlessEqual(s1
.builderNames
, ["a","b"])
47 self
.failUnlessEqual(s1
.reason
, "The Periodic scheduler named 'quickly' triggered this build")
49 def testNightly(self
):
50 # now == 15-Nov-2005, 00:05:36 AM . By using mktime, this is
51 # converted into the local timezone, which happens to match what
52 # Nightly is going to do anyway.
53 MIN
=60; HOUR
=60*MIN
; DAY
=24*3600
54 now
= time
.mktime((2005, 11, 15, 0, 5, 36, 1, 319, 0))
56 s
= scheduler
.Nightly('nightly', ["a"], hour
=3)
57 t
= s
.calculateNextRunTimeFrom(now
)
58 self
.failUnlessEqual(int(t
-now
), 2*HOUR
+54*MIN
+24)
60 s
= scheduler
.Nightly('nightly', ["a"], minute
=[3,8,54])
61 t
= s
.calculateNextRunTimeFrom(now
)
62 self
.failUnlessEqual(int(t
-now
), 2*MIN
+24)
64 s
= scheduler
.Nightly('nightly', ["a"],
65 dayOfMonth
=16, hour
=1, minute
=6)
66 t
= s
.calculateNextRunTimeFrom(now
)
67 self
.failUnlessEqual(int(t
-now
), DAY
+HOUR
+24)
69 s
= scheduler
.Nightly('nightly', ["a"],
70 dayOfMonth
=16, hour
=1, minute
=3)
71 t
= s
.calculateNextRunTimeFrom(now
)
72 self
.failUnlessEqual(int(t
-now
), DAY
+57*MIN
+24)
74 s
= scheduler
.Nightly('nightly', ["a"],
75 dayOfMonth
=15, hour
=1, minute
=3)
76 t
= s
.calculateNextRunTimeFrom(now
)
77 self
.failUnlessEqual(int(t
-now
), 57*MIN
+24)
79 s
= scheduler
.Nightly('nightly', ["a"],
80 dayOfMonth
=15, hour
=0, minute
=3)
81 t
= s
.calculateNextRunTimeFrom(now
)
82 self
.failUnlessEqual(int(t
-now
), 30*DAY
-3*MIN
+24)
85 def isImportant(self
, change
):
86 if "important" in change
.files
:
91 s
= scheduler
.Scheduler("b1", "branch1", 2, ["a","b"],
92 fileIsImportant
=self
.isImportant
)
95 c0
= Change("carol", ["important"], "other branch", branch
="other")
98 self
.failIf(s
.importantChanges
)
100 c1
= Change("alice", ["important", "not important"], "some changes",
103 c2
= Change("bob", ["not important", "boring"], "some more changes",
106 c3
= Change("carol", ["important", "dull"], "even more changes",
110 self
.failUnlessEqual(s
.importantChanges
, [c1
,c3
])
111 self
.failUnlessEqual(s
.unimportantChanges
, [c2
])
112 self
.failUnless(s
.timer
)
115 reactor
.callLater(4, d
.callback
, None)
116 d
.addCallback(self
._testBranch
_1)
118 def _testBranch_1(self
, res
):
119 self
.failUnlessEqual(len(self
.master
.sets
), 1)
120 s
= self
.master
.sets
[0].source
121 self
.failUnlessEqual(s
.branch
, "branch1")
122 self
.failUnlessEqual(s
.revision
, None)
123 self
.failUnlessEqual(len(s
.changes
), 3)
124 self
.failUnlessEqual(s
.patch
, None)
127 def testAnyBranch(self
):
128 s
= scheduler
.AnyBranchScheduler("b1", None, 1, ["a","b"],
129 fileIsImportant
=self
.isImportant
)
132 c1
= Change("alice", ["important", "not important"], "some changes",
135 c2
= Change("bob", ["not important", "boring"], "some more changes",
138 c3
= Change("carol", ["important", "dull"], "even more changes",
142 c4
= Change("carol", ["important"], "other branch", branch
="branch2")
145 c5
= Change("carol", ["important"], "default branch", branch
=None)
149 reactor
.callLater(2, d
.callback
, None)
150 d
.addCallback(self
._testAnyBranch
_1)
152 def _testAnyBranch_1(self
, res
):
153 self
.failUnlessEqual(len(self
.master
.sets
), 3)
154 self
.master
.sets
.sort(lambda a
,b
: cmp(a
.source
.branch
,
157 s1
= self
.master
.sets
[0].source
158 self
.failUnlessEqual(s1
.branch
, None)
159 self
.failUnlessEqual(s1
.revision
, None)
160 self
.failUnlessEqual(len(s1
.changes
), 1)
161 self
.failUnlessEqual(s1
.patch
, None)
163 s2
= self
.master
.sets
[1].source
164 self
.failUnlessEqual(s2
.branch
, "branch1")
165 self
.failUnlessEqual(s2
.revision
, None)
166 self
.failUnlessEqual(len(s2
.changes
), 3)
167 self
.failUnlessEqual(s2
.patch
, None)
169 s3
= self
.master
.sets
[2].source
170 self
.failUnlessEqual(s3
.branch
, "branch2")
171 self
.failUnlessEqual(s3
.revision
, None)
172 self
.failUnlessEqual(len(s3
.changes
), 1)
173 self
.failUnlessEqual(s3
.patch
, None)
175 def testAnyBranch2(self
):
176 # like testAnyBranch but without fileIsImportant
177 s
= scheduler
.AnyBranchScheduler("b1", None, 2, ["a","b"])
179 c1
= Change("alice", ["important", "not important"], "some changes",
182 c2
= Change("bob", ["not important", "boring"], "some more changes",
185 c3
= Change("carol", ["important", "dull"], "even more changes",
189 c4
= Change("carol", ["important"], "other branch", branch
="branch2")
193 reactor
.callLater(2, d
.callback
, None)
194 d
.addCallback(self
._testAnyBranch
2_1)
196 def _testAnyBranch2_1(self
, res
):
197 self
.failUnlessEqual(len(self
.master
.sets
), 2)
198 self
.master
.sets
.sort(lambda a
,b
: cmp(a
.source
.branch
,
200 s1
= self
.master
.sets
[0].source
201 self
.failUnlessEqual(s1
.branch
, "branch1")
202 self
.failUnlessEqual(s1
.revision
, None)
203 self
.failUnlessEqual(len(s1
.changes
), 3)
204 self
.failUnlessEqual(s1
.patch
, None)
206 s2
= self
.master
.sets
[1].source
207 self
.failUnlessEqual(s2
.branch
, "branch2")
208 self
.failUnlessEqual(s2
.revision
, None)
209 self
.failUnlessEqual(len(s2
.changes
), 1)
210 self
.failUnlessEqual(s2
.patch
, None)
213 def createMaildir(self
, jobdir
):
215 os
.mkdir(os
.path
.join(jobdir
, "new"))
216 os
.mkdir(os
.path
.join(jobdir
, "cur"))
217 os
.mkdir(os
.path
.join(jobdir
, "tmp"))
220 def pushJob(self
, jobdir
, job
):
222 filename
= "job_%d" % self
.jobcounter
224 if os
.path
.exists(os
.path
.join(jobdir
, "new", filename
)):
226 if os
.path
.exists(os
.path
.join(jobdir
, "tmp", filename
)):
228 if os
.path
.exists(os
.path
.join(jobdir
, "cur", filename
)):
231 f
= open(os
.path
.join(jobdir
, "tmp", filename
), "w")
234 os
.rename(os
.path
.join(jobdir
, "tmp", filename
),
235 os
.path
.join(jobdir
, "new", filename
))
237 def testTryJobdir(self
):
238 self
.master
.basedir
= "try_jobdir"
239 os
.mkdir(self
.master
.basedir
)
241 jobdir_abs
= os
.path
.join(self
.master
.basedir
, jobdir
)
242 self
.createMaildir(jobdir_abs
)
243 s
= scheduler
.Try_Jobdir("try1", ["a", "b"], jobdir
)
245 self
.failIf(self
.master
.sets
)
246 job1
= tryclient
.createJobfile("buildsetID",
247 "branch1", "123", 1, "diff",
249 self
.master
.d
= d
= defer
.Deferred()
250 self
.pushJob(jobdir_abs
, job1
)
251 d
.addCallback(self
._testTryJobdir
_1)
252 # N.B.: if we don't have DNotify, we poll every 10 seconds, so don't
253 # set a .timeout here shorter than that. TODO: make it possible to
254 # set the polling interval, so we can make it shorter.
257 def _testTryJobdir_1(self
, bs
):
258 self
.failUnlessEqual(bs
.builderNames
, ["a", "b"])
259 self
.failUnlessEqual(bs
.source
.branch
, "branch1")
260 self
.failUnlessEqual(bs
.source
.revision
, "123")
261 self
.failUnlessEqual(bs
.source
.patch
, (1, "diff"))
264 def testTryUserpass(self
):
265 up
= [("alice","pw1"), ("bob","pw2")]
266 s
= scheduler
.Try_Userpass("try2", ["a", "b"], 0, userpass
=up
)
269 config
= {'connect': 'pb',
272 'master': "localhost:%d" % port
,
273 'builders': ["a", "b"],
275 t
= tryclient
.Try(config
)
276 ss
= sourcestamp
.SourceStamp("branch1", "123", (1, "diff"))
278 d2
= self
.master
.d
= defer
.Deferred()
280 d
.addCallback(self
._testTryUserpass
_1, t
, d2
)
282 testTryUserpass
.timeout
= 5
283 def _testTryUserpass_1(self
, res
, t
, d2
):
284 # at this point, the Try object should have a RemoteReference to the
285 # status object. The FakeMaster returns a stub.
286 self
.failUnless(t
.buildsetStatus
)
287 d2
.addCallback(self
._testTryUserpass
_2, t
)
289 def _testTryUserpass_2(self
, bs
, t
):
290 # this should be the BuildSet submitted by the TryScheduler
291 self
.failUnlessEqual(bs
.builderNames
, ["a", "b"])
292 self
.failUnlessEqual(bs
.source
.branch
, "branch1")
293 self
.failUnlessEqual(bs
.source
.revision
, "123")
294 self
.failUnlessEqual(bs
.source
.patch
, (1, "diff"))
298 # twisted-2.0.1 (but not later versions) seems to require a reactor
299 # iteration before stopListening actually works. TODO: investigate
302 reactor
.callLater(0, d
.callback
, None)
305 def testGetBuildSets(self
):
306 # validate IStatus.getBuildSets
307 s
= status
.builder
.Status(None, ".")
308 bs1
= buildset
.BuildSet(["a","b"], sourcestamp
.SourceStamp(),
309 reason
="one", bsid
="1")
310 s
.buildsetSubmitted(bs1
.status
)
311 self
.failUnlessEqual(s
.getBuildSets(), [bs1
.status
])
312 bs1
.status
.notifyFinishedWatchers()
313 self
.failUnlessEqual(s
.getBuildSets(), [])
315 def testCategory(self
):
316 s1
= scheduler
.Scheduler("b1", "branch1", 2, ["a","b"], categories
=["categoryA", "both"])
317 self
.addScheduler(s1
)
318 s2
= scheduler
.Scheduler("b2", "branch1", 2, ["a","b"], categories
=["categoryB", "both"])
319 self
.addScheduler(s2
)
321 c0
= Change("carol", ["important"], "branch1", branch
="branch1", category
="categoryA")
325 c1
= Change("carol", ["important"], "branch1", branch
="branch1", category
="categoryB")
329 c2
= Change("carol", ["important"], "branch1", branch
="branch1")
333 c3
= Change("carol", ["important"], "branch1", branch
="branch1", category
="both")
337 self
.failUnlessEqual(s1
.importantChanges
, [c0
, c3
])
338 self
.failUnlessEqual(s2
.importantChanges
, [c1
, c3
])
340 s
= scheduler
.Scheduler("b3", "branch1", 2, ["a","b"])
343 c0
= Change("carol", ["important"], "branch1", branch
="branch1", category
="categoryA")
345 c1
= Change("carol", ["important"], "branch1", branch
="branch1", category
="categoryB")
348 self
.failUnlessEqual(s
.importantChanges
, [c0
, c1
])