| OLD | NEW |
| (Empty) |
| 1 # -*- test-case-name: buildbot.test.test_status -*- | |
| 2 | |
| 3 import email, os | |
| 4 import operator | |
| 5 | |
| 6 from zope.interface import implements | |
| 7 from twisted.internet import defer, reactor | |
| 8 from twisted.trial import unittest | |
| 9 | |
| 10 from buildbot import interfaces | |
| 11 from buildbot.sourcestamp import SourceStamp | |
| 12 from buildbot.process.base import BuildRequest, Build | |
| 13 from buildbot.status import builder, base, words, progress | |
| 14 from buildbot.changes.changes import Change | |
| 15 from buildbot.process.builder import Builder | |
| 16 from time import sleep | |
| 17 | |
| 18 import sys | |
| 19 if sys.version_info[:3] < (2,4,0): | |
| 20 from sets import Set as set | |
| 21 | |
| 22 mail = None | |
| 23 try: | |
| 24 from buildbot.status import mail | |
| 25 except ImportError: | |
| 26 pass | |
| 27 from buildbot.status import progress, client # NEEDS COVERAGE | |
| 28 from buildbot.test.runutils import RunMixin, setupBuildStepStatus, rmtree | |
| 29 | |
| 30 class MyStep: | |
| 31 build = None | |
| 32 def getName(self): | |
| 33 return "step" | |
| 34 def getResults(self): | |
| 35 return (builder.SUCCESS, "yay") | |
| 36 | |
| 37 class MyLogFileProducer(builder.LogFileProducer): | |
| 38 # The reactor.callLater(0) in LogFileProducer.resumeProducing is a bit of | |
| 39 # a nuisance from a testing point of view. This subclass adds a Deferred | |
| 40 # to that call so we can find out when it is complete. | |
| 41 def resumeProducing(self): | |
| 42 d = defer.Deferred() | |
| 43 reactor.callLater(0, self._resumeProducing, d) | |
| 44 return d | |
| 45 def _resumeProducing(self, d): | |
| 46 builder.LogFileProducer._resumeProducing(self) | |
| 47 reactor.callLater(0, d.callback, None) | |
| 48 | |
| 49 class MyLog(builder.LogFile): | |
| 50 def __init__(self, basedir, name, text=None, step=None): | |
| 51 self.fakeBuilderBasedir = basedir | |
| 52 if not step: | |
| 53 step = MyStep() | |
| 54 builder.LogFile.__init__(self, step, name, name) | |
| 55 if text: | |
| 56 self.addStdout(text) | |
| 57 self.finish() | |
| 58 def getFilename(self): | |
| 59 return os.path.join(self.fakeBuilderBasedir, self.name) | |
| 60 | |
| 61 def subscribeConsumer(self, consumer): | |
| 62 p = MyLogFileProducer(self, consumer) | |
| 63 d = p.resumeProducing() | |
| 64 return d | |
| 65 | |
| 66 class MyHTMLLog(builder.HTMLLogFile): | |
| 67 def __init__(self, basedir, name, html): | |
| 68 step = MyStep() | |
| 69 builder.HTMLLogFile.__init__(self, step, name, name, html) | |
| 70 | |
| 71 class MyLogSubscriber: | |
| 72 def __init__(self): | |
| 73 self.chunks = [] | |
| 74 def logChunk(self, build, step, log, channel, text): | |
| 75 self.chunks.append((channel, text)) | |
| 76 | |
| 77 class MyLogConsumer: | |
| 78 def __init__(self, limit=None): | |
| 79 self.chunks = [] | |
| 80 self.finished = False | |
| 81 self.limit = limit | |
| 82 def registerProducer(self, producer, streaming): | |
| 83 self.producer = producer | |
| 84 self.streaming = streaming | |
| 85 def unregisterProducer(self): | |
| 86 self.producer = None | |
| 87 def writeChunk(self, chunk): | |
| 88 self.chunks.append(chunk) | |
| 89 if self.limit: | |
| 90 self.limit -= 1 | |
| 91 if self.limit == 0: | |
| 92 self.producer.pauseProducing() | |
| 93 def finish(self): | |
| 94 self.finished = True | |
| 95 | |
| 96 if mail: | |
| 97 class MyMailer(mail.MailNotifier): | |
| 98 def sendMessage(self, m, recipients): | |
| 99 self.parent.messages.append((m, recipients)) | |
| 100 | |
| 101 class MyStatus: | |
| 102 def getBuildbotURL(self): | |
| 103 return self.url | |
| 104 def getURLForThing(self, thing): | |
| 105 return None | |
| 106 def getProjectName(self): | |
| 107 return "myproj" | |
| 108 | |
| 109 class MyBuilder(builder.BuilderStatus): | |
| 110 nextBuildNumber = 0 | |
| 111 def newBuild(self): | |
| 112 number = self.nextBuildNumber | |
| 113 self.nextBuildNumber += 1 | |
| 114 s = MyBuild(self, number, None, False) | |
| 115 s.waitUntilFinished().addCallback(self._buildFinished) | |
| 116 return s | |
| 117 | |
| 118 class MyBuild(builder.BuildStatus): | |
| 119 testlogs = [] | |
| 120 def __init__(self, parent, number, results, finished=True): | |
| 121 builder.BuildStatus.__init__(self, parent, number) | |
| 122 self.results = results | |
| 123 self.source = SourceStamp(revision="1.14") | |
| 124 self.reason = "build triggered by changes" | |
| 125 self.finished = finished | |
| 126 def getLogs(self): | |
| 127 return self.testlogs | |
| 128 def buildStarted(self, build, started): | |
| 129 self.started = started | |
| 130 self.builder.buildStarted(self) | |
| 131 | |
| 132 class MyLookup: | |
| 133 implements(interfaces.IEmailLookup) | |
| 134 | |
| 135 def getAddress(self, user): | |
| 136 d = defer.Deferred() | |
| 137 # With me now is Mr Thomas Walters of West Hartlepool who is totally | |
| 138 # invisible. | |
| 139 if user == "Thomas_Walters": | |
| 140 d.callback(None) | |
| 141 else: | |
| 142 d.callback(user + "@" + "dev.com") | |
| 143 return d | |
| 144 | |
| 145 def customTextMailMessage(attrs): | |
| 146 logLines = 3 | |
| 147 text = list() | |
| 148 text.append("STATUS: %s" % attrs['result'].title()) | |
| 149 text.append("") | |
| 150 text.extend([c.asText() for c in attrs['changes']]) | |
| 151 text.append("") | |
| 152 name, url, lines, status = attrs['logs'][-1] | |
| 153 text.append("Last %d lines of '%s':" % (logLines, name)) | |
| 154 text.extend(["\t%s\n" % line for line in lines[len(lines)-logLines:]]) | |
| 155 text.append("") | |
| 156 text.append("Build number was: %s" % attrs['buildProperties']['buildnumber']
) | |
| 157 text.append("") | |
| 158 text.append("-buildbot") | |
| 159 return ("\n".join(text), 'plain') | |
| 160 | |
| 161 def customHTMLMailMessage(attrs): | |
| 162 logLines = 3 | |
| 163 text = list() | |
| 164 text.append("<h3>STATUS <a href='%s'>%s</a>:</h3>" % (attrs['buildURL'], | |
| 165 attrs['result'].title(
))) | |
| 166 text.append("<h4>Recent Changes:</h4>") | |
| 167 text.extend([c.asHTML() for c in attrs['changes']]) | |
| 168 name, url, lines, status = attrs['logs'][-1] | |
| 169 text.append("<h4>Last %d lines of '%s':</h4>" % (logLines, name)) | |
| 170 text.append("<p>") | |
| 171 text.append("<br>".join([line for line in lines[len(lines)-logLines:]])) | |
| 172 text.append("</p>") | |
| 173 text.append("<p>Build number was: %s</p>" % attrs['buildProperties']['buildn
umber']) | |
| 174 text.append("<br>") | |
| 175 text.append("<b>-<a href='%s'>buildbot</a></b>" % attrs['buildbotURL']) | |
| 176 return ("\n".join(text), 'html') | |
| 177 | |
| 178 class Mail(unittest.TestCase): | |
| 179 | |
| 180 def setUp(self): | |
| 181 self.builder = MyBuilder("builder1") | |
| 182 | |
| 183 def stall(self, res, timeout): | |
| 184 d = defer.Deferred() | |
| 185 reactor.callLater(timeout, d.callback, res) | |
| 186 return d | |
| 187 | |
| 188 def makeBuild(self, number, results): | |
| 189 return MyBuild(self.builder, number, results) | |
| 190 | |
| 191 def failUnlessIn(self, substring, string): | |
| 192 self.failUnless(string.find(substring) != -1, | |
| 193 "didn't see '%s' in '%s'" % (substring, string)) | |
| 194 | |
| 195 def getProjectName(self): | |
| 196 return "PROJECT" | |
| 197 | |
| 198 def getBuildbotURL(self): | |
| 199 return "BUILDBOT_URL" | |
| 200 | |
| 201 def getURLForThing(self, thing): | |
| 202 return None | |
| 203 | |
| 204 def testBuild1(self): | |
| 205 mailer = MyMailer(fromaddr="buildbot@example.com", | |
| 206 extraRecipients=["recip@example.com", | |
| 207 "recip2@example.com"], | |
| 208 lookup=mail.Domain("dev.com")) | |
| 209 mailer.parent = self | |
| 210 mailer.master_status = self | |
| 211 self.messages = [] | |
| 212 | |
| 213 b1 = self.makeBuild(3, builder.SUCCESS) | |
| 214 b1.blamelist = ["bob"] | |
| 215 | |
| 216 mailer.buildFinished("builder1", b1, b1.results) | |
| 217 self.failUnless(len(self.messages) == 1) | |
| 218 m,r = self.messages.pop() | |
| 219 t = m.as_string() | |
| 220 self.failUnlessIn("To: bob@dev.com\n", t) | |
| 221 self.failUnlessIn("CC: recip2@example.com, recip@example.com\n", t) | |
| 222 self.failUnlessIn("From: buildbot@example.com\n", t) | |
| 223 self.failUnlessIn("Subject: buildbot success in PROJECT on builder1\n",
t) | |
| 224 self.failUnlessIn("Date: ", t) | |
| 225 self.failUnlessIn("Build succeeded!\n", t) | |
| 226 self.failUnlessIn("Buildbot URL: BUILDBOT_URL\n", t) | |
| 227 | |
| 228 def testBuild2(self): | |
| 229 mailer = MyMailer(fromaddr="buildbot@example.com", | |
| 230 extraRecipients=["recip@example.com", | |
| 231 "recip2@example.com"], | |
| 232 lookup="dev.com", | |
| 233 sendToInterestedUsers=False) | |
| 234 mailer.parent = self | |
| 235 mailer.master_status = self | |
| 236 self.messages = [] | |
| 237 | |
| 238 b1 = self.makeBuild(3, builder.SUCCESS) | |
| 239 b1.blamelist = ["bob"] | |
| 240 | |
| 241 mailer.buildFinished("builder1", b1, b1.results) | |
| 242 self.failUnless(len(self.messages) == 1) | |
| 243 m,r = self.messages.pop() | |
| 244 t = m.as_string() | |
| 245 self.failUnlessIn("To: recip2@example.com, " | |
| 246 "recip@example.com\n", t) | |
| 247 self.failUnlessIn("From: buildbot@example.com\n", t) | |
| 248 self.failUnlessIn("Subject: buildbot success in PROJECT on builder1\n",
t) | |
| 249 self.failUnlessIn("Build succeeded!\n", t) | |
| 250 self.failUnlessIn("Buildbot URL: BUILDBOT_URL\n", t) | |
| 251 | |
| 252 def testBuildStatusCategory(self): | |
| 253 # a status client only interested in a category should only receive | |
| 254 # from that category | |
| 255 mailer = MyMailer(fromaddr="buildbot@example.com", | |
| 256 extraRecipients=["recip@example.com", | |
| 257 "recip2@example.com"], | |
| 258 lookup="dev.com", | |
| 259 sendToInterestedUsers=False, | |
| 260 categories=["debug"]) | |
| 261 | |
| 262 mailer.parent = self | |
| 263 mailer.master_status = self | |
| 264 self.messages = [] | |
| 265 | |
| 266 b1 = self.makeBuild(3, builder.SUCCESS) | |
| 267 b1.blamelist = ["bob"] | |
| 268 | |
| 269 mailer.buildFinished("builder1", b1, b1.results) | |
| 270 self.failIf(self.messages) | |
| 271 | |
| 272 def testBuilderCategory(self): | |
| 273 # a builder in a certain category should notify status clients that | |
| 274 # did not list categories, or categories including this one | |
| 275 mailer1 = MyMailer(fromaddr="buildbot@example.com", | |
| 276 extraRecipients=["recip@example.com", | |
| 277 "recip2@example.com"], | |
| 278 lookup="dev.com", | |
| 279 sendToInterestedUsers=False) | |
| 280 mailer2 = MyMailer(fromaddr="buildbot@example.com", | |
| 281 extraRecipients=["recip@example.com", | |
| 282 "recip2@example.com"], | |
| 283 lookup="dev.com", | |
| 284 sendToInterestedUsers=False, | |
| 285 categories=["active"]) | |
| 286 mailer3 = MyMailer(fromaddr="buildbot@example.com", | |
| 287 extraRecipients=["recip@example.com", | |
| 288 "recip2@example.com"], | |
| 289 lookup="dev.com", | |
| 290 sendToInterestedUsers=False, | |
| 291 categories=["active", "debug"]) | |
| 292 | |
| 293 builderd = MyBuilder("builder2", "debug") | |
| 294 | |
| 295 mailer1.parent = self | |
| 296 mailer1.master_status = self | |
| 297 mailer2.parent = self | |
| 298 mailer2.master_status = self | |
| 299 mailer3.parent = self | |
| 300 mailer3.master_status = self | |
| 301 self.messages = [] | |
| 302 | |
| 303 t = mailer1.builderAdded("builder2", builderd) | |
| 304 self.assertEqual(len(mailer1.watched), 1) | |
| 305 self.assertEqual(t, mailer1) | |
| 306 t = mailer2.builderAdded("builder2", builderd) | |
| 307 self.assertEqual(len(mailer2.watched), 0) | |
| 308 self.assertEqual(t, None) | |
| 309 t = mailer3.builderAdded("builder2", builderd) | |
| 310 self.assertEqual(len(mailer3.watched), 1) | |
| 311 self.assertEqual(t, mailer3) | |
| 312 | |
| 313 b2 = MyBuild(builderd, 3, builder.SUCCESS) | |
| 314 b2.blamelist = ["bob"] | |
| 315 | |
| 316 mailer1.buildFinished("builder2", b2, b2.results) | |
| 317 self.failUnlessEqual(len(self.messages), 1) | |
| 318 self.messages = [] | |
| 319 mailer2.buildFinished("builder2", b2, b2.results) | |
| 320 self.failUnlessEqual(len(self.messages), 0) | |
| 321 self.messages = [] | |
| 322 mailer3.buildFinished("builder2", b2, b2.results) | |
| 323 self.failUnlessEqual(len(self.messages), 1) | |
| 324 | |
| 325 def testCustomTextMessage(self): | |
| 326 basedir = "test_custom_text_mesg" | |
| 327 os.mkdir(basedir) | |
| 328 mailer = MyMailer(fromaddr="buildbot@example.com", mode="problem", | |
| 329 extraRecipients=["recip@example.com", | |
| 330 "recip2@example.com"], | |
| 331 lookup=MyLookup(), | |
| 332 customMesg=customTextMailMessage) | |
| 333 mailer.parent = self | |
| 334 mailer.master_status = self | |
| 335 self.messages = [] | |
| 336 | |
| 337 b1 = self.makeBuild(4, builder.FAILURE) | |
| 338 b1.setProperty('buildnumber', 1, 'Build') | |
| 339 b1.setText(["snarkleack", "polarization", "failed"]) | |
| 340 b1.blamelist = ["dev3", "dev3", "dev3", "dev4", | |
| 341 "Thomas_Walters"] | |
| 342 b1.source.changes = (Change(who = 'author1', files = ['file1'], comments
= 'comment1', revision = 123), | |
| 343 Change(who = 'author2', files = ['file2'], comments
= 'comment2', revision = 456)) | |
| 344 b1.testlogs = [MyLog(basedir, 'compile', "Compile log here\n"), | |
| 345 MyLog(basedir, 'test', "Test log here\nTest 1 failed\nTes
t 2 failed\nTest 3 failed\nTest 4 failed\n")] | |
| 346 | |
| 347 mailer.buildFinished("builder1", b1, b1.results) | |
| 348 m,r = self.messages.pop() | |
| 349 t = m.as_string() | |
| 350 # | |
| 351 # Uncomment to review custom message | |
| 352 # | |
| 353 #self.fail(t) | |
| 354 self.failUnlessIn("comment1", t) | |
| 355 self.failUnlessIn("comment2", t) | |
| 356 self.failUnlessIn("Test 4 failed", t) | |
| 357 self.failUnlessIn("number was: 1", t) | |
| 358 | |
| 359 | |
| 360 def testCustomHTMLMessage(self): | |
| 361 basedir = "test_custom_HTML_mesg" | |
| 362 os.mkdir(basedir) | |
| 363 mailer = MyMailer(fromaddr="buildbot@example.com", mode="problem", | |
| 364 extraRecipients=["recip@example.com", | |
| 365 "recip2@example.com"], | |
| 366 lookup=MyLookup(), | |
| 367 customMesg=customHTMLMailMessage) | |
| 368 mailer.parent = self | |
| 369 mailer.master_status = self | |
| 370 self.messages = [] | |
| 371 | |
| 372 b1 = self.makeBuild(4, builder.FAILURE) | |
| 373 b1.setProperty('buildnumber', 1, 'Build') | |
| 374 b1.setText(["snarkleack", "polarization", "failed"]) | |
| 375 b1.blamelist = ["dev3", "dev3", "dev3", "dev4", | |
| 376 "Thomas_Walters"] | |
| 377 b1.source.changes = (Change(who = 'author1', files = ['file1'], comments
= 'comment1', revision = 123), | |
| 378 Change(who = 'author2', files = ['file2'], comments
= 'comment2', revision = 456)) | |
| 379 b1.testlogs = [MyLog(basedir, 'compile', "Compile log here\n"), | |
| 380 MyLog(basedir, 'test', "Test log here\nTest 1 failed\nTes
t 2 failed\nTest 3 failed\nTest 4 failed\n")] | |
| 381 | |
| 382 mailer.buildFinished("builder1", b1, b1.results) | |
| 383 m,r = self.messages.pop() | |
| 384 t = m.as_string() | |
| 385 # | |
| 386 # Uncomment to review custom message | |
| 387 # | |
| 388 #self.fail(t) | |
| 389 self.failUnlessIn("<h4>Last 3 lines of 'step.test':</h4>", t) | |
| 390 self.failUnlessIn("<p>Changed by: <b>author2</b><br />", t) | |
| 391 self.failUnlessIn("Test 3 failed", t) | |
| 392 self.failUnlessIn("number was: 1", t) | |
| 393 | |
| 394 def testShouldAttachLog(self): | |
| 395 mailer = mail.MailNotifier(fromaddr="buildbot@example.com", addLogs=True
) | |
| 396 self.assertTrue(mailer._shouldAttachLog('anything')) | |
| 397 mailer = mail.MailNotifier(fromaddr="buildbot@example.com", addLogs=Fals
e) | |
| 398 self.assertFalse(mailer._shouldAttachLog('anything')) | |
| 399 mailer = mail.MailNotifier(fromaddr="buildbot@example.com", addLogs=['so
mething']) | |
| 400 self.assertFalse(mailer._shouldAttachLog('anything')) | |
| 401 self.assertTrue(mailer._shouldAttachLog('something')) | |
| 402 | |
| 403 def testShouldAttachPatches(self): | |
| 404 basedir = "test_should_attach_patches" | |
| 405 os.mkdir(basedir) | |
| 406 b1 = self.makeBuild(4, builder.FAILURE) | |
| 407 b1.setProperty('buildnumber', 1, 'Build') | |
| 408 b1.setText(["snarkleack", "polarization", "failed"]) | |
| 409 b1.blamelist = ["dev3", "dev3", "dev3", "dev4", | |
| 410 "Thomas_Walters"] | |
| 411 b1.source.changes = (Change(who = 'author1', files = ['file1'], comments
= 'comment1', revision = 123), | |
| 412 Change(who = 'author2', files = ['file2'], comments
= 'comment2', revision = 456)) | |
| 413 b1.testlogs = [MyLog(basedir, 'compile', "Compile log here\n"), | |
| 414 MyLog(basedir, 'test', "Test log here\nTest 1 failed\nTes
t 2 failed\nTest 3 failed\nTest 4 failed\n")] | |
| 415 b1.source.patch = (0, '--- /dev/null\n+++ a_file\n', None) | |
| 416 | |
| 417 mailer = MyMailer(fromaddr="buildbot@example.com", addPatch=True) | |
| 418 mailer.parent = self | |
| 419 mailer.master_status = self | |
| 420 self.messages = [] | |
| 421 mailer.buildFinished("builder1", b1, b1.results) | |
| 422 m,r = self.messages.pop() | |
| 423 self.assertTrue(m.is_multipart()) | |
| 424 self.assertEqual(len([True for i in m.walk()]), 3) | |
| 425 | |
| 426 mailer = MyMailer(fromaddr="buildbot@example.com", addPatch=False) | |
| 427 mailer.parent = self | |
| 428 mailer.master_status = self | |
| 429 self.messages = [] | |
| 430 mailer.buildFinished("builder1", b1, b1.results) | |
| 431 m,r = self.messages.pop() | |
| 432 self.assertFalse(m.is_multipart()) | |
| 433 self.assertEqual(len([True for i in m.walk()]), 1) | |
| 434 | |
| 435 mailer = MyMailer(fromaddr="buildbot@example.com") | |
| 436 mailer.parent = self | |
| 437 mailer.master_status = self | |
| 438 self.messages = [] | |
| 439 mailer.buildFinished("builder1", b1, b1.results) | |
| 440 m,r = self.messages.pop() | |
| 441 self.assertTrue(m.is_multipart()) | |
| 442 self.assertEqual(len([True for i in m.walk()]), 3) | |
| 443 | |
| 444 | |
| 445 def testFailure(self): | |
| 446 mailer = MyMailer(fromaddr="buildbot@example.com", mode="problem", | |
| 447 extraRecipients=["recip@example.com", | |
| 448 "recip2@example.com"], | |
| 449 lookup=MyLookup()) | |
| 450 mailer.parent = self | |
| 451 mailer.master_status = self | |
| 452 self.messages = [] | |
| 453 | |
| 454 b1 = self.makeBuild(3, builder.SUCCESS) | |
| 455 b1.blamelist = ["dev1", "dev2"] | |
| 456 b2 = self.makeBuild(4, builder.FAILURE) | |
| 457 b2.setText(["snarkleack", "polarization", "failed"]) | |
| 458 b2.blamelist = ["dev3", "dev3", "dev3", "dev4", | |
| 459 "Thomas_Walters"] | |
| 460 mailer.buildFinished("builder1", b1, b1.results) | |
| 461 self.failIf(self.messages) | |
| 462 mailer.buildFinished("builder1", b2, b2.results) | |
| 463 self.failUnless(len(self.messages) == 1) | |
| 464 m,r = self.messages.pop() | |
| 465 t = m.as_string() | |
| 466 self.failUnlessIn("To: dev3@dev.com, dev4@dev.com\n", t) | |
| 467 self.failUnlessIn("CC: recip2@example.com, recip@example.com\n", t) | |
| 468 self.failUnlessIn("From: buildbot@example.com\n", t) | |
| 469 self.failUnlessIn("Subject: buildbot failure in PROJECT on builder1\n",
t) | |
| 470 self.failUnlessIn("The Buildbot has detected a new failure", t) | |
| 471 self.failUnlessIn("BUILD FAILED: snarkleack polarization failed\n", t) | |
| 472 self.failUnlessEqual(set(r), set(["dev3@dev.com", "dev4@dev.com", | |
| 473 "recip2@example.com", "recip@example.com"])) | |
| 474 | |
| 475 | |
| 476 def testChange(self): | |
| 477 raise unittest.SkipTest("TODO: Fix Build/Builder mock objects to support
getPrevBuild()") | |
| 478 | |
| 479 mailer = MyMailer(fromaddr="buildbot@example.com", mode="change", | |
| 480 extraRecipients=["bah@bah.bah"], | |
| 481 lookup=MyLookup()) | |
| 482 mailer.parent = self | |
| 483 mailer.master_status = self | |
| 484 self.messages = [] | |
| 485 | |
| 486 b1 = self.makeBuild(1, builder.SUCCESS) | |
| 487 b2 = self.makeBuild(2, builder.SUCCESS) | |
| 488 b3 = self.makeBuild(3, builder.FAILURE) | |
| 489 b4 = self.makeBuild(4, builder.FAILURE) | |
| 490 b5 = self.makeBuild(5, builder.SUCCESS) | |
| 491 b6 = self.makeBuild(6, builder.SUCCESS) | |
| 492 | |
| 493 # no message on first or repetetive success | |
| 494 mailer.buildFinished("builder1", b1, b1.results) | |
| 495 self.failIf(self.messages) | |
| 496 mailer.buildFinished("builder1", b2, b2.results) | |
| 497 self.failIf(self.messages) | |
| 498 | |
| 499 # message on first fail only | |
| 500 mailer.buildFinished("builder1", b3, b3.results) | |
| 501 self.failUnless(len(self.messages) == 1) | |
| 502 self.messages.pop() | |
| 503 mailer.buildFinished("builder1", b4, b4.results) | |
| 504 self.failIf(self.messages) | |
| 505 | |
| 506 # message on first following success | |
| 507 mailer.buildFinished("builder1", b5, b5.results) | |
| 508 self.failUnless(len(self.messages) == 1) | |
| 509 self.messages.pop() | |
| 510 mailer.buildFinished("builder1", b6, b6.results) | |
| 511 self.failIf(self.messages) | |
| 512 | |
| 513 | |
| 514 def testLogs(self): | |
| 515 basedir = "test_status_logs" | |
| 516 os.mkdir(basedir) | |
| 517 mailer = MyMailer(fromaddr="buildbot@example.com", addLogs=True, | |
| 518 extraRecipients=["recip@example.com", | |
| 519 "recip2@example.com"]) | |
| 520 mailer.parent = self | |
| 521 mailer.master_status = self | |
| 522 self.messages = [] | |
| 523 | |
| 524 b1 = self.makeBuild(3, builder.WARNINGS) | |
| 525 b1.testlogs = [MyLog(basedir, 'compile', "Compile log here\n"), | |
| 526 MyLog(basedir, | |
| 527 'test', "Test log here\nTest 4 failed\n"), | |
| 528 ] | |
| 529 b1.text = ["unusual", "gnarzzler", "output"] | |
| 530 mailer.buildFinished("builder1", b1, b1.results) | |
| 531 self.failUnless(len(self.messages) == 1) | |
| 532 m,r = self.messages.pop() | |
| 533 t = m.as_string() | |
| 534 self.failUnlessIn("Subject: buildbot warnings in PROJECT on builder1\n",
t) | |
| 535 m2 = email.message_from_string(t) | |
| 536 p = m2.get_payload() | |
| 537 self.failUnlessEqual(len(p), 3) | |
| 538 | |
| 539 self.failUnlessIn("Build Had Warnings: unusual gnarzzler output\n", | |
| 540 p[0].get_payload()) | |
| 541 | |
| 542 self.failUnlessEqual(p[1].get_filename(), "step.compile") | |
| 543 self.failUnlessEqual(p[1].get_payload(), "Compile log here\n") | |
| 544 | |
| 545 self.failUnlessEqual(p[2].get_filename(), "step.test") | |
| 546 self.failUnlessIn("Test log here\n", p[2].get_payload()) | |
| 547 | |
| 548 def testMail(self): | |
| 549 basedir = "test_status_mail" | |
| 550 os.mkdir(basedir) | |
| 551 dest = os.environ.get("BUILDBOT_TEST_MAIL") | |
| 552 if not dest: | |
| 553 raise unittest.SkipTest("define BUILDBOT_TEST_MAIL=dest to run this"
) | |
| 554 mailer = mail.MailNotifier(fromaddr="buildbot@example.com", | |
| 555 addLogs=True, | |
| 556 extraRecipients=[dest]) | |
| 557 s = MyStatus() | |
| 558 s.url = "project URL" | |
| 559 mailer.master_status = s | |
| 560 | |
| 561 b1 = self.makeBuild(3, builder.SUCCESS) | |
| 562 b1.testlogs = [MyLog(basedir, 'compile', "Compile log here\n"), | |
| 563 MyLog(basedir, | |
| 564 'test', "Test log here\nTest 4 failed\n"), | |
| 565 ] | |
| 566 | |
| 567 d = mailer.buildFinished("builder1", b1, b1.results) | |
| 568 # When this fires, the mail has been sent, but the SMTP connection is | |
| 569 # still up (because smtp.sendmail relies upon the server to hang up). | |
| 570 # Spin for a moment to avoid the "unclean reactor" warning that Trial | |
| 571 # gives us if we finish before the socket is disconnected. Really, | |
| 572 # sendmail() ought to hang up the connection once it is finished: | |
| 573 # otherwise a malicious SMTP server could make us consume lots of | |
| 574 # memory. | |
| 575 d.addCallback(self.stall, 0.1) | |
| 576 return d | |
| 577 | |
| 578 if not mail: | |
| 579 Mail.skip = "the Twisted Mail package is not installed" | |
| 580 | |
| 581 class Progress(unittest.TestCase): | |
| 582 def testWavg(self): | |
| 583 bp = progress.BuildProgress([]) | |
| 584 e = progress.Expectations(bp) | |
| 585 # wavg(old, current) | |
| 586 self.failUnlessEqual(e.wavg(None, None), None) | |
| 587 self.failUnlessEqual(e.wavg(None, 3), 3) | |
| 588 self.failUnlessEqual(e.wavg(3, None), 3) | |
| 589 self.failUnlessEqual(e.wavg(3, 4), 3.5) | |
| 590 e.decay = 0.1 | |
| 591 self.failUnlessEqual(e.wavg(3, 4), 3.1) | |
| 592 | |
| 593 | |
| 594 class Results(unittest.TestCase): | |
| 595 | |
| 596 def testAddResults(self): | |
| 597 b = builder.BuildStatus(builder.BuilderStatus("test"), 12) | |
| 598 testname = ("buildbot", "test", "test_status", "Results", | |
| 599 "testAddResults") | |
| 600 r1 = builder.TestResult(name=testname, | |
| 601 results=builder.SUCCESS, | |
| 602 text=["passed"], | |
| 603 logs={'output': ""}, | |
| 604 ) | |
| 605 b.addTestResult(r1) | |
| 606 | |
| 607 res = b.getTestResults() | |
| 608 self.failUnlessEqual(res.keys(), [testname]) | |
| 609 t = res[testname] | |
| 610 self.failUnless(interfaces.ITestResult.providedBy(t)) | |
| 611 self.failUnlessEqual(t.getName(), testname) | |
| 612 self.failUnlessEqual(t.getResults(), builder.SUCCESS) | |
| 613 self.failUnlessEqual(t.getText(), ["passed"]) | |
| 614 self.failUnlessEqual(t.getLogs(), {'output': ""}) | |
| 615 | |
| 616 class Log(unittest.TestCase): | |
| 617 basedir = "status_log_add" | |
| 618 | |
| 619 def setUp(self): | |
| 620 self.tearDown() | |
| 621 os.mkdir(self.basedir) | |
| 622 | |
| 623 def tearDown(self): | |
| 624 if os.path.exists(self.basedir): | |
| 625 rmtree(self.basedir) | |
| 626 | |
| 627 def testAdd(self): | |
| 628 l = MyLog(self.basedir, "compile", step=13) | |
| 629 self.failUnlessEqual(l.getName(), "compile") | |
| 630 self.failUnlessEqual(l.getStep(), 13) | |
| 631 l.addHeader("HEADER\n") | |
| 632 l.addStdout("Some text\n") | |
| 633 l.addStderr("Some error\n") | |
| 634 l.addStdout("Some more text\n") | |
| 635 self.failIf(l.isFinished()) | |
| 636 l.finish() | |
| 637 self.failUnless(l.isFinished()) | |
| 638 self.failUnlessEqual(l.getText(), | |
| 639 "Some text\nSome error\nSome more text\n") | |
| 640 self.failUnlessEqual(l.getTextWithHeaders(), | |
| 641 "HEADER\n" + | |
| 642 "Some text\nSome error\nSome more text\n") | |
| 643 self.failUnlessEqual(len(list(l.getChunks())), 4) | |
| 644 | |
| 645 self.failUnless(l.hasContents()) | |
| 646 try: | |
| 647 os.unlink(l.getFilename()) | |
| 648 except OSError: | |
| 649 os.unlink(l.getFilename() + ".bz2") | |
| 650 self.failIf(l.hasContents()) | |
| 651 | |
| 652 def TODO_testDuplicate(self): | |
| 653 # create multiple logs for the same step with the same logname, make | |
| 654 # sure their on-disk filenames are suitably uniquified. This | |
| 655 # functionality actually lives in BuildStepStatus and BuildStatus, so | |
| 656 # this test must involve more than just the MyLog class. | |
| 657 | |
| 658 # naieve approach, doesn't work | |
| 659 l1 = MyLog(self.basedir, "duplicate") | |
| 660 l1.addStdout("Some text\n") | |
| 661 l1.finish() | |
| 662 l2 = MyLog(self.basedir, "duplicate") | |
| 663 l2.addStdout("Some more text\n") | |
| 664 l2.finish() | |
| 665 self.failIfEqual(l1.getFilename(), l2.getFilename()) | |
| 666 | |
| 667 def testMerge1(self): | |
| 668 l = MyLog(self.basedir, "merge1") | |
| 669 l.addHeader("HEADER\n") | |
| 670 l.addStdout("Some text\n") | |
| 671 l.addStdout("Some more text\n") | |
| 672 l.addStdout("more\n") | |
| 673 l.finish() | |
| 674 self.failUnlessEqual(l.getText(), | |
| 675 "Some text\nSome more text\nmore\n") | |
| 676 self.failUnlessEqual(l.getTextWithHeaders(), | |
| 677 "HEADER\n" + | |
| 678 "Some text\nSome more text\nmore\n") | |
| 679 self.failUnlessEqual(len(list(l.getChunks())), 2) | |
| 680 | |
| 681 def testMerge2(self): | |
| 682 l = MyLog(self.basedir, "merge2") | |
| 683 l.addHeader("HEADER\n") | |
| 684 for i in xrange(1000): | |
| 685 l.addStdout("aaaa") | |
| 686 for i in xrange(30): | |
| 687 l.addStderr("bbbb") | |
| 688 for i in xrange(10): | |
| 689 l.addStdout("cc") | |
| 690 target = 1000*"aaaa" + 30 * "bbbb" + 10 * "cc" | |
| 691 self.failUnlessEqual(len(l.getText()), len(target)) | |
| 692 self.failUnlessEqual(l.getText(), target) | |
| 693 l.finish() | |
| 694 self.failUnlessEqual(len(l.getText()), len(target)) | |
| 695 self.failUnlessEqual(l.getText(), target) | |
| 696 self.failUnlessEqual(len(list(l.getChunks())), 4) | |
| 697 | |
| 698 def testMerge3(self): | |
| 699 l = MyLog(self.basedir, "merge3") | |
| 700 l.chunkSize = 100 | |
| 701 l.addHeader("HEADER\n") | |
| 702 for i in xrange(8): | |
| 703 l.addStdout(10*"a") | |
| 704 for i in xrange(8): | |
| 705 l.addStdout(10*"a") | |
| 706 self.failUnlessEqual(list(l.getChunks()), | |
| 707 [(builder.HEADER, "HEADER\n"), | |
| 708 (builder.STDOUT, 100*"a"), | |
| 709 (builder.STDOUT, 60*"a")]) | |
| 710 l.finish() | |
| 711 self.failUnlessEqual(l.getText(), 160*"a") | |
| 712 | |
| 713 def testReadlines(self): | |
| 714 l = MyLog(self.basedir, "chunks1") | |
| 715 l.addHeader("HEADER\n") # should be ignored | |
| 716 l.addStdout("Some text\n") | |
| 717 l.addStdout("Some More Text\nAnd Some More\n") | |
| 718 l.addStderr("Some Stderr\n") | |
| 719 l.addStdout("Last line\n") | |
| 720 l.finish() | |
| 721 alllines = list(l.readlines()) | |
| 722 self.failUnlessEqual(len(alllines), 4) | |
| 723 self.failUnlessEqual(alllines[0], "Some text\n") | |
| 724 self.failUnlessEqual(alllines[2], "And Some More\n") | |
| 725 self.failUnlessEqual(alllines[3], "Last line\n") | |
| 726 stderr = list(l.readlines(interfaces.LOG_CHANNEL_STDERR)) | |
| 727 self.failUnlessEqual(len(stderr), 1) | |
| 728 self.failUnlessEqual(stderr[0], "Some Stderr\n") | |
| 729 lines = l.readlines() | |
| 730 if False: # TODO: l.readlines() is not yet an iterator | |
| 731 # verify that it really is an iterator | |
| 732 line0 = lines.next() | |
| 733 self.failUnlessEqual(line0, "Some text\n") | |
| 734 line1 = lines.next() | |
| 735 line2 = lines.next() | |
| 736 self.failUnlessEqual(line2, "And Some More\n") | |
| 737 | |
| 738 | |
| 739 def testChunks(self): | |
| 740 l = MyLog(self.basedir, "chunks2") | |
| 741 c1 = l.getChunks() | |
| 742 l.addHeader("HEADER\n") | |
| 743 l.addStdout("Some text\n") | |
| 744 self.failUnlessEqual("".join(l.getChunks(onlyText=True)), | |
| 745 "HEADER\nSome text\n") | |
| 746 c2 = l.getChunks() | |
| 747 | |
| 748 l.addStdout("Some more text\n") | |
| 749 self.failUnlessEqual("".join(l.getChunks(onlyText=True)), | |
| 750 "HEADER\nSome text\nSome more text\n") | |
| 751 c3 = l.getChunks() | |
| 752 | |
| 753 l.addStdout("more\n") | |
| 754 l.finish() | |
| 755 | |
| 756 self.failUnlessEqual(list(c1), []) | |
| 757 self.failUnlessEqual(list(c2), [(builder.HEADER, "HEADER\n"), | |
| 758 (builder.STDOUT, "Some text\n")]) | |
| 759 self.failUnlessEqual(list(c3), [(builder.HEADER, "HEADER\n"), | |
| 760 (builder.STDOUT, | |
| 761 "Some text\nSome more text\n")]) | |
| 762 | |
| 763 self.failUnlessEqual(l.getText(), | |
| 764 "Some text\nSome more text\nmore\n") | |
| 765 self.failUnlessEqual(l.getTextWithHeaders(), | |
| 766 "HEADER\n" + | |
| 767 "Some text\nSome more text\nmore\n") | |
| 768 self.failUnlessEqual(len(list(l.getChunks())), 2) | |
| 769 | |
| 770 def testUpgrade(self): | |
| 771 l = MyLog(self.basedir, "upgrade") | |
| 772 l.addHeader("HEADER\n") | |
| 773 l.addStdout("Some text\n") | |
| 774 l.addStdout("Some more text\n") | |
| 775 l.addStdout("more\n") | |
| 776 l.finish() | |
| 777 self.failUnless(l.hasContents()) | |
| 778 # now doctor it to look like a 0.6.4-era non-upgraded logfile | |
| 779 l.entries = list(l.getChunks()) | |
| 780 del l.filename | |
| 781 try: | |
| 782 os.unlink(l.getFilename() + ".bz2") | |
| 783 except OSError: | |
| 784 os.unlink(l.getFilename()) | |
| 785 # now make sure we can upgrade it | |
| 786 l.upgrade("upgrade") | |
| 787 self.failUnlessEqual(l.getText(), | |
| 788 "Some text\nSome more text\nmore\n") | |
| 789 self.failUnlessEqual(len(list(l.getChunks())), 2) | |
| 790 self.failIf(l.entries) | |
| 791 | |
| 792 # now, do it again, but make it look like an upgraded 0.6.4 logfile | |
| 793 # (i.e. l.filename is missing, but the contents are there on disk) | |
| 794 l.entries = list(l.getChunks()) | |
| 795 del l.filename | |
| 796 l.upgrade("upgrade") | |
| 797 self.failUnlessEqual(l.getText(), | |
| 798 "Some text\nSome more text\nmore\n") | |
| 799 self.failUnlessEqual(len(list(l.getChunks())), 2) | |
| 800 self.failIf(l.entries) | |
| 801 self.failUnless(l.hasContents()) | |
| 802 | |
| 803 def testHTMLUpgrade(self): | |
| 804 l = MyHTMLLog(self.basedir, "upgrade", "log contents") | |
| 805 l.upgrade("filename") | |
| 806 | |
| 807 def testSubscribe(self): | |
| 808 l1 = MyLog(self.basedir, "subscribe1") | |
| 809 l1.finish() | |
| 810 self.failUnless(l1.isFinished()) | |
| 811 | |
| 812 s = MyLogSubscriber() | |
| 813 l1.subscribe(s, True) | |
| 814 l1.unsubscribe(s) | |
| 815 self.failIf(s.chunks) | |
| 816 | |
| 817 s = MyLogSubscriber() | |
| 818 l1.subscribe(s, False) | |
| 819 l1.unsubscribe(s) | |
| 820 self.failIf(s.chunks) | |
| 821 | |
| 822 finished = [] | |
| 823 l2 = MyLog(self.basedir, "subscribe2") | |
| 824 l2.waitUntilFinished().addCallback(finished.append) | |
| 825 l2.addHeader("HEADER\n") | |
| 826 s1 = MyLogSubscriber() | |
| 827 l2.subscribe(s1, True) | |
| 828 s2 = MyLogSubscriber() | |
| 829 l2.subscribe(s2, False) | |
| 830 self.failUnlessEqual(s1.chunks, [(builder.HEADER, "HEADER\n")]) | |
| 831 self.failUnlessEqual(s2.chunks, []) | |
| 832 | |
| 833 l2.addStdout("Some text\n") | |
| 834 self.failUnlessEqual(s1.chunks, [(builder.HEADER, "HEADER\n"), | |
| 835 (builder.STDOUT, "Some text\n")]) | |
| 836 self.failUnlessEqual(s2.chunks, [(builder.STDOUT, "Some text\n")]) | |
| 837 l2.unsubscribe(s1) | |
| 838 | |
| 839 l2.addStdout("Some more text\n") | |
| 840 self.failUnlessEqual(s1.chunks, [(builder.HEADER, "HEADER\n"), | |
| 841 (builder.STDOUT, "Some text\n")]) | |
| 842 self.failUnlessEqual(s2.chunks, [(builder.STDOUT, "Some text\n"), | |
| 843 (builder.STDOUT, "Some more text\n"), | |
| 844 ]) | |
| 845 self.failIf(finished) | |
| 846 l2.finish() | |
| 847 self.failUnlessEqual(finished, [l2]) | |
| 848 | |
| 849 def testConsumer(self): | |
| 850 l1 = MyLog(self.basedir, "consumer1") | |
| 851 l1.finish() | |
| 852 self.failUnless(l1.isFinished()) | |
| 853 | |
| 854 s = MyLogConsumer() | |
| 855 d = l1.subscribeConsumer(s) | |
| 856 d.addCallback(self._testConsumer_1, s) | |
| 857 return d | |
| 858 testConsumer.timeout = 5 | |
| 859 def _testConsumer_1(self, res, s): | |
| 860 self.failIf(s.chunks) | |
| 861 self.failUnless(s.finished) | |
| 862 self.failIf(s.producer) # producer should be registered and removed | |
| 863 | |
| 864 l2 = MyLog(self.basedir, "consumer2") | |
| 865 l2.addHeader("HEADER\n") | |
| 866 l2.finish() | |
| 867 self.failUnless(l2.isFinished()) | |
| 868 | |
| 869 s = MyLogConsumer() | |
| 870 d = l2.subscribeConsumer(s) | |
| 871 d.addCallback(self._testConsumer_2, s) | |
| 872 return d | |
| 873 def _testConsumer_2(self, res, s): | |
| 874 self.failUnlessEqual(s.chunks, [(builder.HEADER, "HEADER\n")]) | |
| 875 self.failUnless(s.finished) | |
| 876 self.failIf(s.producer) # producer should be registered and removed | |
| 877 | |
| 878 | |
| 879 l2 = MyLog(self.basedir, "consumer3") | |
| 880 l2.chunkSize = 1000 | |
| 881 l2.addHeader("HEADER\n") | |
| 882 l2.addStdout(800*"a") | |
| 883 l2.addStdout(800*"a") # should now have two chunks on disk, 1000+600 | |
| 884 l2.addStdout(800*"b") # HEADER,1000+600*a on disk, 800*a in memory | |
| 885 l2.addStdout(800*"b") # HEADER,1000+600*a,1000+600*b on disk | |
| 886 l2.addStdout(200*"c") # HEADER,1000+600*a,1000+600*b on disk, | |
| 887 # 200*c in memory | |
| 888 | |
| 889 s = MyLogConsumer(limit=1) | |
| 890 d = l2.subscribeConsumer(s) | |
| 891 d.addCallback(self._testConsumer_3, l2, s) | |
| 892 return d | |
| 893 def _testConsumer_3(self, res, l2, s): | |
| 894 self.failUnless(s.streaming) | |
| 895 self.failUnlessEqual(s.chunks, [(builder.HEADER, "HEADER\n")]) | |
| 896 s.limit = 1 | |
| 897 d = s.producer.resumeProducing() | |
| 898 d.addCallback(self._testConsumer_4, l2, s) | |
| 899 return d | |
| 900 def _testConsumer_4(self, res, l2, s): | |
| 901 self.failUnlessEqual(s.chunks, [(builder.HEADER, "HEADER\n"), | |
| 902 (builder.STDOUT, 1000*"a"), | |
| 903 ]) | |
| 904 s.limit = None | |
| 905 d = s.producer.resumeProducing() | |
| 906 d.addCallback(self._testConsumer_5, l2, s) | |
| 907 return d | |
| 908 def _testConsumer_5(self, res, l2, s): | |
| 909 self.failUnlessEqual(s.chunks, [(builder.HEADER, "HEADER\n"), | |
| 910 (builder.STDOUT, 1000*"a"), | |
| 911 (builder.STDOUT, 600*"a"), | |
| 912 (builder.STDOUT, 1000*"b"), | |
| 913 (builder.STDOUT, 600*"b"), | |
| 914 (builder.STDOUT, 200*"c")]) | |
| 915 l2.addStdout(1000*"c") # HEADER,1600*a,1600*b,1200*c on disk | |
| 916 self.failUnlessEqual(s.chunks, [(builder.HEADER, "HEADER\n"), | |
| 917 (builder.STDOUT, 1000*"a"), | |
| 918 (builder.STDOUT, 600*"a"), | |
| 919 (builder.STDOUT, 1000*"b"), | |
| 920 (builder.STDOUT, 600*"b"), | |
| 921 (builder.STDOUT, 200*"c"), | |
| 922 (builder.STDOUT, 1000*"c")]) | |
| 923 l2.finish() | |
| 924 self.failUnlessEqual(s.chunks, [(builder.HEADER, "HEADER\n"), | |
| 925 (builder.STDOUT, 1000*"a"), | |
| 926 (builder.STDOUT, 600*"a"), | |
| 927 (builder.STDOUT, 1000*"b"), | |
| 928 (builder.STDOUT, 600*"b"), | |
| 929 (builder.STDOUT, 200*"c"), | |
| 930 (builder.STDOUT, 1000*"c")]) | |
| 931 self.failIf(s.producer) | |
| 932 self.failUnless(s.finished) | |
| 933 | |
| 934 def testLargeSummary(self): | |
| 935 bigtext = "a" * 200000 # exceed the NetstringReceiver 100KB limit | |
| 936 l = MyLog(self.basedir, "large", bigtext) | |
| 937 s = MyLogConsumer() | |
| 938 d = l.subscribeConsumer(s) | |
| 939 def _check(res): | |
| 940 for ctype,chunk in s.chunks: | |
| 941 self.failUnless(len(chunk) < 100000) | |
| 942 merged = "".join([c[1] for c in s.chunks]) | |
| 943 self.failUnless(merged == bigtext) | |
| 944 d.addCallback(_check) | |
| 945 # when this fails, it fails with a timeout, and there is an exception | |
| 946 # sent to log.err(). This AttributeError exception is in | |
| 947 # NetstringReceiver.dataReceived where it does | |
| 948 # self.transport.loseConnection() because of the NetstringParseError, | |
| 949 # however self.transport is None | |
| 950 return d | |
| 951 testLargeSummary.timeout = 5 | |
| 952 | |
| 953 def testLimit(self): | |
| 954 l = MyLog(self.basedir, "limit") | |
| 955 l.logMaxSize = 150 | |
| 956 for i in range(1000): | |
| 957 l.addStdout("Some data") | |
| 958 l.finish() | |
| 959 t = l.getText() | |
| 960 # Compare against 175 since we truncate logs based on chunks, so we may | |
| 961 # go slightly over the limit | |
| 962 self.failIf(len(t) > 175, "Text too long (%i)" % len(t)) | |
| 963 self.failUnless("truncated" in l.getTextWithHeaders(), | |
| 964 "No truncated message found") | |
| 965 | |
| 966 class CompressLog(unittest.TestCase): | |
| 967 # compression is not supported unless bz2 is installed | |
| 968 try: | |
| 969 import bz2 | |
| 970 except: | |
| 971 skip = "compression not supported (no bz2 module available)" | |
| 972 | |
| 973 def testCompressLogs(self): | |
| 974 bss = setupBuildStepStatus("test-compress") | |
| 975 bss.build.builder.setLogCompressionLimit(1024) | |
| 976 l = bss.addLog('not-compress') | |
| 977 l.addStdout('a' * 512) | |
| 978 l.finish() | |
| 979 lc = bss.addLog('to-compress') | |
| 980 lc.addStdout('b' * 1024) | |
| 981 lc.finish() | |
| 982 d = bss.stepFinished(builder.SUCCESS) | |
| 983 self.failUnless(d is not None) | |
| 984 d.addCallback(self._verifyCompression, bss) | |
| 985 return d | |
| 986 | |
| 987 def _verifyCompression(self, result, bss): | |
| 988 self.failUnless(len(bss.getLogs()), 2) | |
| 989 (ncl, cl) = bss.getLogs() # not compressed, compressed log | |
| 990 self.failUnless(os.path.isfile(ncl.getFilename())) | |
| 991 self.failIf(os.path.isfile(ncl.getFilename() + ".bz2")) | |
| 992 self.failIf(os.path.isfile(cl.getFilename())) | |
| 993 self.failUnless(os.path.isfile(cl.getFilename() + ".bz2")) | |
| 994 content = ncl.getText() | |
| 995 self.failUnless(len(content), 512) | |
| 996 content = cl.getText() | |
| 997 self.failUnless(len(content), 1024) | |
| 998 pass | |
| 999 | |
| 1000 config_base = """ | |
| 1001 from buildbot.process import factory | |
| 1002 from buildbot.steps import dummy | |
| 1003 from buildbot.buildslave import BuildSlave | |
| 1004 from buildbot.config import BuilderConfig | |
| 1005 s = factory.s | |
| 1006 | |
| 1007 f1 = factory.QuickBuildFactory('fakerep', 'cvsmodule', configure=None) | |
| 1008 | |
| 1009 f2 = factory.BuildFactory([ | |
| 1010 s(dummy.Dummy, timeout=1), | |
| 1011 s(dummy.RemoteDummy, timeout=2), | |
| 1012 ]) | |
| 1013 | |
| 1014 BuildmasterConfig = c = {} | |
| 1015 c['slaves'] = [BuildSlave('bot1', 'sekrit')] | |
| 1016 c['schedulers'] = [] | |
| 1017 c['builders'] = [ | |
| 1018 BuilderConfig(name='quick', slavename='bot1', factory=f1), | |
| 1019 ] | |
| 1020 c['slavePortnum'] = 0 | |
| 1021 """ | |
| 1022 | |
| 1023 config_2 = config_base + """ | |
| 1024 c['builders'] = [ | |
| 1025 BuilderConfig(name='dummy', slavename='bot1', factory=f2), | |
| 1026 BuilderConfig(name='testdummy', slavename='bot1', | |
| 1027 factory=f2, category='test'), | |
| 1028 ] | |
| 1029 """ | |
| 1030 | |
| 1031 class STarget(base.StatusReceiver): | |
| 1032 debug = False | |
| 1033 | |
| 1034 def __init__(self, mode): | |
| 1035 self.mode = mode | |
| 1036 self.events = [] | |
| 1037 def announce(self): | |
| 1038 if self.debug: | |
| 1039 print self.events[-1] | |
| 1040 | |
| 1041 def builderAdded(self, name, builder): | |
| 1042 self.events.append(("builderAdded", name, builder)) | |
| 1043 self.announce() | |
| 1044 if "builder" in self.mode: | |
| 1045 return self | |
| 1046 def builderChangedState(self, name, state): | |
| 1047 self.events.append(("builderChangedState", name, state)) | |
| 1048 self.announce() | |
| 1049 def buildStarted(self, name, build): | |
| 1050 self.events.append(("buildStarted", name, build)) | |
| 1051 self.announce() | |
| 1052 if "eta" in self.mode: | |
| 1053 self.eta_build = build.getETA() | |
| 1054 if "build" in self.mode: | |
| 1055 return self | |
| 1056 def buildETAUpdate(self, build, ETA): | |
| 1057 self.events.append(("buildETAUpdate", build, ETA)) | |
| 1058 self.announce() | |
| 1059 def changeAdded(self, change): | |
| 1060 self.events.append(("changeAdded", change)) | |
| 1061 self.announce() | |
| 1062 def stepStarted(self, build, step): | |
| 1063 self.events.append(("stepStarted", build, step)) | |
| 1064 self.announce() | |
| 1065 if 0 and "eta" in self.mode: | |
| 1066 print "TIMES", step.getTimes() | |
| 1067 print "ETA", step.getETA() | |
| 1068 print "EXP", step.getExpectations() | |
| 1069 if "step" in self.mode: | |
| 1070 return self | |
| 1071 def stepTextChanged(self, build, step, text): | |
| 1072 self.events.append(("stepTextChanged", step, text)) | |
| 1073 def stepText2Changed(self, build, step, text2): | |
| 1074 self.events.append(("stepText2Changed", step, text2)) | |
| 1075 def stepETAUpdate(self, build, step, ETA, expectations): | |
| 1076 self.events.append(("stepETAUpdate", build, step, ETA, expectations)) | |
| 1077 self.announce() | |
| 1078 def logStarted(self, build, step, log): | |
| 1079 self.events.append(("logStarted", build, step, log)) | |
| 1080 self.announce() | |
| 1081 def logFinished(self, build, step, log): | |
| 1082 self.events.append(("logFinished", build, step, log)) | |
| 1083 self.announce() | |
| 1084 def stepFinished(self, build, step, results): | |
| 1085 self.events.append(("stepFinished", build, step, results)) | |
| 1086 if 0 and "eta" in self.mode: | |
| 1087 print "post-EXP", step.getExpectations() | |
| 1088 self.announce() | |
| 1089 def buildFinished(self, name, build, results): | |
| 1090 self.events.append(("buildFinished", name, build, results)) | |
| 1091 self.announce() | |
| 1092 def builderRemoved(self, name): | |
| 1093 self.events.append(("builderRemoved", name)) | |
| 1094 self.announce() | |
| 1095 def slaveConnected(self, name): | |
| 1096 self.events.append(("slaveConnected", name)) | |
| 1097 self.announce() | |
| 1098 def slaveDisconnected(self, name): | |
| 1099 self.events.append(("slaveDisconnected", name)) | |
| 1100 self.announce() | |
| 1101 | |
| 1102 class VerifyChangeAdded(RunMixin, unittest.TestCase): | |
| 1103 def testChangeAdded(self): | |
| 1104 m = self.master | |
| 1105 | |
| 1106 s = m.getStatus() | |
| 1107 self.t1 = t1 = STarget(["builder"]) | |
| 1108 s.subscribe(t1) | |
| 1109 | |
| 1110 m.loadConfig(config_2) | |
| 1111 m.readConfig = True | |
| 1112 m.startService() | |
| 1113 | |
| 1114 cm = self.master.change_svc | |
| 1115 c = Change("bob", ["Makefile", "foo/bar.c"], "changed stuff") | |
| 1116 cm.addChange(c) | |
| 1117 self.failUnlessEqual(t1.events[-1], ("changeAdded", c)) | |
| 1118 | |
| 1119 class Subscription(RunMixin, unittest.TestCase): | |
| 1120 # verify that StatusTargets can subscribe/unsubscribe properly | |
| 1121 | |
| 1122 def testSlave(self): | |
| 1123 m = self.master | |
| 1124 s = m.getStatus() | |
| 1125 self.t1 = t1 = STarget(["builder"]) | |
| 1126 #t1.debug = True; print | |
| 1127 s.subscribe(t1) | |
| 1128 self.failUnlessEqual(len(t1.events), 0) | |
| 1129 | |
| 1130 self.t3 = t3 = STarget(["builder", "build", "step"]) | |
| 1131 s.subscribe(t3) | |
| 1132 | |
| 1133 m.loadConfig(config_2) | |
| 1134 m.readConfig = True | |
| 1135 m.startService() | |
| 1136 | |
| 1137 self.failUnlessEqual(len(t1.events), 4) | |
| 1138 self.failUnlessEqual(t1.events[0][0:2], ("builderAdded", "dummy")) | |
| 1139 self.failUnlessEqual(t1.events[1], | |
| 1140 ("builderChangedState", "dummy", "offline")) | |
| 1141 self.failUnlessEqual(t1.events[2][0:2], ("builderAdded", "testdummy")) | |
| 1142 self.failUnlessEqual(t1.events[3], | |
| 1143 ("builderChangedState", "testdummy", "offline")) | |
| 1144 t1.events = [] | |
| 1145 | |
| 1146 self.failUnlessEqual(s.getBuilderNames(), ["dummy", "testdummy"]) | |
| 1147 self.failUnlessEqual(s.getBuilderNames(categories=['test']), | |
| 1148 ["testdummy"]) | |
| 1149 self.s1 = s1 = s.getBuilder("dummy") | |
| 1150 self.failUnlessEqual(s1.getName(), "dummy") | |
| 1151 self.failUnlessEqual(s1.getState(), ("offline", [])) | |
| 1152 self.failUnlessEqual(s1.getCurrentBuilds(), []) | |
| 1153 self.failUnlessEqual(s1.getLastFinishedBuild(), None) | |
| 1154 self.failUnlessEqual(s1.getBuild(-1), None) | |
| 1155 #self.failUnlessEqual(s1.getEvent(-1), foo("created")) | |
| 1156 | |
| 1157 # status targets should, upon being subscribed, immediately get a | |
| 1158 # list of all current builders matching their category | |
| 1159 self.t2 = t2 = STarget([]) | |
| 1160 s.subscribe(t2) | |
| 1161 self.failUnlessEqual(len(t2.events), 2) | |
| 1162 self.failUnlessEqual(t2.events[0][0:2], ("builderAdded", "dummy")) | |
| 1163 self.failUnlessEqual(t2.events[1][0:2], ("builderAdded", "testdummy")) | |
| 1164 | |
| 1165 d = self.connectSlave(builders=["dummy", "testdummy"]) | |
| 1166 d.addCallback(self._testSlave_1, t1) | |
| 1167 return d | |
| 1168 | |
| 1169 def _testSlave_1(self, res, t1): | |
| 1170 self.failUnlessEqual(len(t1.events), 3) | |
| 1171 self.failUnlessEqual(t1.events[0], | |
| 1172 ("slaveConnected", "bot1")) | |
| 1173 self.failUnlessEqual(t1.events[1], | |
| 1174 ("builderChangedState", "dummy", "idle")) | |
| 1175 self.failUnlessEqual(t1.events[2], | |
| 1176 ("builderChangedState", "testdummy", "idle")) | |
| 1177 t1.events = [] | |
| 1178 | |
| 1179 c = interfaces.IControl(self.master) | |
| 1180 req = BuildRequest("forced build for testing", SourceStamp(), 'test_buil
der') | |
| 1181 c.getBuilder("dummy").requestBuild(req) | |
| 1182 d = req.waitUntilFinished() | |
| 1183 d2 = self.master.botmaster.waitUntilBuilderIdle("dummy") | |
| 1184 dl = defer.DeferredList([d, d2]) | |
| 1185 dl.addCallback(self._testSlave_2) | |
| 1186 return dl | |
| 1187 | |
| 1188 def _testSlave_2(self, res): | |
| 1189 # t1 subscribes to builds, but not anything lower-level | |
| 1190 ev = self.t1.events | |
| 1191 self.failUnlessEqual(len(ev), 4) | |
| 1192 self.failUnlessEqual(ev[0][0:3], | |
| 1193 ("builderChangedState", "dummy", "building")) | |
| 1194 self.failUnlessEqual(ev[1][0], "buildStarted") | |
| 1195 self.failUnlessEqual(ev[2][0:2]+ev[2][3:4], | |
| 1196 ("buildFinished", "dummy", builder.SUCCESS)) | |
| 1197 self.failUnlessEqual(ev[3][0:3], | |
| 1198 ("builderChangedState", "dummy", "idle")) | |
| 1199 | |
| 1200 self.failUnlessEqual([ev[0] for ev in self.t3.events], | |
| 1201 ["builderAdded", | |
| 1202 "builderChangedState", # offline | |
| 1203 "builderAdded", | |
| 1204 "builderChangedState", # idle | |
| 1205 "slaveConnected", | |
| 1206 "builderChangedState", # offline | |
| 1207 "builderChangedState", # idle | |
| 1208 "builderChangedState", # building | |
| 1209 "buildStarted", | |
| 1210 "stepStarted", "stepETAUpdate", | |
| 1211 "stepTextChanged", "stepFinished", | |
| 1212 "stepStarted", "stepETAUpdate", | |
| 1213 "stepTextChanged", "logStarted", "logFinished", | |
| 1214 "stepTextChanged", "stepText2Changed", | |
| 1215 "stepFinished", | |
| 1216 "buildFinished", | |
| 1217 "builderChangedState", # idle | |
| 1218 ]) | |
| 1219 | |
| 1220 b = self.s1.getLastFinishedBuild() | |
| 1221 self.failUnless(b) | |
| 1222 self.failUnlessEqual(b.getBuilder().getName(), "dummy") | |
| 1223 self.failUnlessEqual(b.getNumber(), 0) | |
| 1224 self.failUnlessEqual(b.getSourceStamp().branch, None) | |
| 1225 self.failUnlessEqual(b.getSourceStamp().patch, None) | |
| 1226 self.failUnlessEqual(b.getSourceStamp().revision, None) | |
| 1227 self.failUnlessEqual(b.getReason(), "forced build for testing") | |
| 1228 self.failUnlessEqual(b.getChanges(), ()) | |
| 1229 self.failUnlessEqual(b.getResponsibleUsers(), []) | |
| 1230 self.failUnless(b.isFinished()) | |
| 1231 self.failUnlessEqual(b.getText(), ['build', 'successful']) | |
| 1232 self.failUnlessEqual(b.getResults(), builder.SUCCESS) | |
| 1233 | |
| 1234 steps = b.getSteps() | |
| 1235 self.failUnlessEqual(len(steps), 2) | |
| 1236 | |
| 1237 eta = 0 | |
| 1238 st1 = steps[0] | |
| 1239 self.failUnlessEqual(st1.getName(), "dummy") | |
| 1240 self.failUnless(st1.isFinished()) | |
| 1241 self.failUnlessEqual(st1.getText(), ["delay", "1 secs"]) | |
| 1242 start,finish = st1.getTimes() | |
| 1243 self.failUnless(0.5 < (finish-start) < 10) | |
| 1244 self.failUnlessEqual(st1.getExpectations(), []) | |
| 1245 self.failUnlessEqual(st1.getLogs(), []) | |
| 1246 eta += finish-start | |
| 1247 | |
| 1248 st2 = steps[1] | |
| 1249 self.failUnlessEqual(st2.getName(), "remote dummy") | |
| 1250 self.failUnless(st2.isFinished()) | |
| 1251 self.failUnlessEqual(st2.getText(), | |
| 1252 ["remote", "delay", "2 secs"]) | |
| 1253 start,finish = st2.getTimes() | |
| 1254 self.failUnless(1.5 < (finish-start) < 10) | |
| 1255 eta += finish-start | |
| 1256 self.failUnlessEqual(st2.getExpectations(), [('output', 38, None)]) | |
| 1257 logs = st2.getLogs() | |
| 1258 self.failUnlessEqual(len(logs), 1) | |
| 1259 self.failUnlessEqual(logs[0].getName(), "stdio") | |
| 1260 self.failUnlessEqual(logs[0].getText(), "data") | |
| 1261 | |
| 1262 self.eta = eta | |
| 1263 # now we run it a second time, and we should have an ETA | |
| 1264 | |
| 1265 self.t4 = t4 = STarget(["builder", "build", "eta"]) | |
| 1266 self.master.getStatus().subscribe(t4) | |
| 1267 c = interfaces.IControl(self.master) | |
| 1268 req = BuildRequest("forced build for testing", SourceStamp(), 'test_buil
der') | |
| 1269 c.getBuilder("dummy").requestBuild(req) | |
| 1270 d = req.waitUntilFinished() | |
| 1271 d2 = self.master.botmaster.waitUntilBuilderIdle("dummy") | |
| 1272 dl = defer.DeferredList([d, d2]) | |
| 1273 dl.addCallback(lambda ign: self.shutdownAllSlaves()) | |
| 1274 dl.addCallback(self._testSlave_3) | |
| 1275 return dl | |
| 1276 | |
| 1277 def _testSlave_3(self, res): | |
| 1278 t4 = self.t4 | |
| 1279 eta = self.eta | |
| 1280 self.failUnless(eta-1 < t4.eta_build < eta+1, # should be 3 seconds | |
| 1281 "t4.eta_build was %g, not in (%g,%g)" | |
| 1282 % (t4.eta_build, eta-1, eta+1)) | |
| 1283 | |
| 1284 self.failUnlessEqual([ev[0] for ev in self.t4.events], | |
| 1285 ["builderAdded", | |
| 1286 "builderChangedState", # offline | |
| 1287 "builderAdded", | |
| 1288 "builderChangedState", # idle | |
| 1289 "builderChangedState", # building | |
| 1290 "buildStarted", | |
| 1291 "stepStarted", "stepFinished", | |
| 1292 "stepStarted", "stepFinished", | |
| 1293 "buildFinished", | |
| 1294 "builderChangedState", | |
| 1295 "slaveDisconnected", | |
| 1296 "builderChangedState", | |
| 1297 "builderChangedState", | |
| 1298 ]) | |
| 1299 | |
| 1300 class Client(unittest.TestCase): | |
| 1301 def testAdaptation(self): | |
| 1302 b = builder.BuilderStatus("bname") | |
| 1303 b2 = client.makeRemote(b) | |
| 1304 self.failUnless(isinstance(b2, client.RemoteBuilder)) | |
| 1305 b3 = client.makeRemote(None) | |
| 1306 self.failUnless(b3 is None) | |
| 1307 | |
| 1308 | |
| 1309 class ContactTester(unittest.TestCase): | |
| 1310 def test_notify_invalid_syntax(self): | |
| 1311 irc = MyContact() | |
| 1312 self.assertRaises(words.UsageError, lambda args, who: irc.command_NOTIFY
(args, who), "", "mynick") | |
| 1313 | |
| 1314 def test_notify_list(self): | |
| 1315 irc = MyContact() | |
| 1316 irc.command_NOTIFY("list", "mynick") | |
| 1317 self.failUnlessEqual(irc.message, "The following events are being notifi
ed: []", "empty notify list") | |
| 1318 | |
| 1319 irc.message = "" | |
| 1320 irc.command_NOTIFY("on started", "mynick") | |
| 1321 self.failUnlessEqual(irc.message, "The following events are being notifi
ed: ['started']", "on started") | |
| 1322 | |
| 1323 irc.message = "" | |
| 1324 irc.command_NOTIFY("on finished", "mynick") | |
| 1325 self.failUnlessEqual(irc.message, "The following events are being notifi
ed: ['started', 'finished']", "on finished") | |
| 1326 | |
| 1327 irc.message = "" | |
| 1328 irc.command_NOTIFY("off", "mynick") | |
| 1329 self.failUnlessEqual(irc.message, "The following events are being notifi
ed: []", "off all") | |
| 1330 | |
| 1331 irc.message = "" | |
| 1332 irc.command_NOTIFY("on", "mynick") | |
| 1333 self.failUnlessEqual(irc.message, "The following events are being notifi
ed: ['started', 'finished']", "on default set") | |
| 1334 | |
| 1335 irc.message = "" | |
| 1336 irc.command_NOTIFY("off started", "mynick") | |
| 1337 self.failUnlessEqual(irc.message, "The following events are being notifi
ed: ['finished']", "off started") | |
| 1338 | |
| 1339 irc.message = "" | |
| 1340 irc.command_NOTIFY("on success failure exception", "mynick") | |
| 1341 self.failUnlessEqual(irc.message, "The following events are being notifi
ed: ['failure', 'finished', 'exception', 'success']", "on multiple events") | |
| 1342 | |
| 1343 def test_notification_default(self): | |
| 1344 irc = MyContact() | |
| 1345 | |
| 1346 my_builder = MyBuilder("builder78") | |
| 1347 my_build = MyIrcBuild(my_builder, 23, builder.SUCCESS) | |
| 1348 | |
| 1349 irc.buildStarted(my_builder.getName(), my_build) | |
| 1350 self.failUnlessEqual(irc.message, "", "No notification with default sett
ings") | |
| 1351 | |
| 1352 irc.buildFinished(my_builder.getName(), my_build, None) | |
| 1353 self.failUnlessEqual(irc.message, "", "No notification with default sett
ings") | |
| 1354 | |
| 1355 def test_notification_started(self): | |
| 1356 irc = MyContact() | |
| 1357 | |
| 1358 my_builder = MyBuilder("builder78") | |
| 1359 my_build = MyIrcBuild(my_builder, 23, builder.SUCCESS) | |
| 1360 my_build.changes = ( | |
| 1361 Change(who = 'author1', files = ['file1'], comments = 'comment1', re
vision = 123), | |
| 1362 Change(who = 'author2', files = ['file2'], comments = 'comment2', re
vision = 456), | |
| 1363 ) | |
| 1364 | |
| 1365 irc.command_NOTIFY("on started", "mynick") | |
| 1366 | |
| 1367 irc.message = "" | |
| 1368 irc.buildStarted(my_builder.getName(), my_build) | |
| 1369 self.failUnlessEqual(irc.message, "build #23 of builder78 started includ
ing [123, 456]", "Start notification generated with notify_events=['started']") | |
| 1370 | |
| 1371 irc.message = "" | |
| 1372 irc.buildFinished(my_builder.getName(), my_build, None) | |
| 1373 self.failUnlessEqual(irc.message, "", "No finished notification with not
ify_events=['started']") | |
| 1374 | |
| 1375 def test_notification_finished(self): | |
| 1376 irc = MyContact() | |
| 1377 | |
| 1378 my_builder = MyBuilder("builder834") | |
| 1379 my_build = MyIrcBuild(my_builder, 862, builder.SUCCESS) | |
| 1380 my_build.changes = ( | |
| 1381 Change(who = 'author1', files = ['file1'], comments = 'comment1', re
vision = 943), | |
| 1382 ) | |
| 1383 | |
| 1384 irc.command_NOTIFY("on finished", "mynick") | |
| 1385 | |
| 1386 irc.message = "" | |
| 1387 irc.buildStarted(my_builder.getName(), my_build) | |
| 1388 self.failUnlessEqual(irc.message, "", "No started notification with noti
fy_events=['finished']") | |
| 1389 | |
| 1390 irc.message = "" | |
| 1391 irc.buildFinished(my_builder.getName(), my_build, None) | |
| 1392 self.failUnlessEqual(irc.message, "build #862 of builder834 is complete:
Success [step1 step2] Build details are at http://myserver/mypath?build=765",
"Finish notification generated with notify_events=['finished']") | |
| 1393 | |
| 1394 def test_notification_success(self): | |
| 1395 irc = MyContact() | |
| 1396 | |
| 1397 my_builder = MyBuilder("builder834") | |
| 1398 my_build = MyIrcBuild(my_builder, 862, builder.SUCCESS) | |
| 1399 my_build.changes = ( | |
| 1400 Change(who = 'author1', files = ['file1'], comments = 'comment1', re
vision = 943), | |
| 1401 ) | |
| 1402 | |
| 1403 irc.command_NOTIFY("on success", "mynick") | |
| 1404 | |
| 1405 irc.message = "" | |
| 1406 irc.buildStarted(my_builder.getName(), my_build) | |
| 1407 self.failUnlessEqual(irc.message, "", "No started notification with noti
fy_events=['success']") | |
| 1408 | |
| 1409 irc.message = "" | |
| 1410 irc.buildFinished(my_builder.getName(), my_build, None) | |
| 1411 self.failUnlessEqual(irc.message, "build #862 of builder834 is complete:
Success [step1 step2] Build details are at http://myserver/mypath?build=765",
"Finish notification generated on success with notify_events=['success']") | |
| 1412 | |
| 1413 irc.message = "" | |
| 1414 my_build.results = builder.FAILURE | |
| 1415 irc.buildFinished(my_builder.getName(), my_build, None) | |
| 1416 self.failUnlessEqual(irc.message, "", "No finish notification generated
on failure with notify_events=['success']") | |
| 1417 | |
| 1418 irc.message = "" | |
| 1419 my_build.results = builder.EXCEPTION | |
| 1420 irc.buildFinished(my_builder.getName(), my_build, None) | |
| 1421 self.failUnlessEqual(irc.message, "", "No finish notification generated
on exception with notify_events=['success']") | |
| 1422 | |
| 1423 def test_notification_failed(self): | |
| 1424 irc = MyContact() | |
| 1425 | |
| 1426 my_builder = MyBuilder("builder834") | |
| 1427 my_build = MyIrcBuild(my_builder, 862, builder.FAILURE) | |
| 1428 my_build.changes = ( | |
| 1429 Change(who = 'author1', files = ['file1'], comments = 'comment1', re
vision = 943), | |
| 1430 ) | |
| 1431 | |
| 1432 irc.command_NOTIFY("on failure", "mynick") | |
| 1433 | |
| 1434 irc.message = "" | |
| 1435 irc.buildStarted(my_builder.getName(), my_build) | |
| 1436 self.failUnlessEqual(irc.message, "", "No started notification with noti
fy_events=['failed']") | |
| 1437 | |
| 1438 irc.message = "" | |
| 1439 irc.channel.showBlameList = True | |
| 1440 irc.buildFinished(my_builder.getName(), my_build, None) | |
| 1441 self.failUnlessEqual(irc.message, "build #862 of builder834 is complete:
Failure [step1 step2] Build details are at http://myserver/mypath?build=765 b
lamelist: author1", "Finish notification generated on failure with notify_events
=['failed']") | |
| 1442 irc.channel.showBlameList = False | |
| 1443 | |
| 1444 irc.message = "" | |
| 1445 my_build.results = builder.SUCCESS | |
| 1446 irc.buildFinished(my_builder.getName(), my_build, None) | |
| 1447 self.failUnlessEqual(irc.message, "", "No finish notification generated
on success with notify_events=['failed']") | |
| 1448 | |
| 1449 irc.message = "" | |
| 1450 my_build.results = builder.EXCEPTION | |
| 1451 irc.buildFinished(my_builder.getName(), my_build, None) | |
| 1452 self.failUnlessEqual(irc.message, "", "No finish notification generated
on exception with notify_events=['failed']") | |
| 1453 | |
| 1454 def test_notification_exception(self): | |
| 1455 irc = MyContact() | |
| 1456 | |
| 1457 my_builder = MyBuilder("builder834") | |
| 1458 my_build = MyIrcBuild(my_builder, 862, builder.EXCEPTION) | |
| 1459 my_build.changes = ( | |
| 1460 Change(who = 'author1', files = ['file1'], comments = 'comment1', re
vision = 943), | |
| 1461 ) | |
| 1462 | |
| 1463 irc.command_NOTIFY("on exception", "mynick") | |
| 1464 | |
| 1465 irc.message = "" | |
| 1466 irc.buildStarted(my_builder.getName(), my_build) | |
| 1467 self.failUnlessEqual(irc.message, "", "No started notification with noti
fy_events=['exception']") | |
| 1468 | |
| 1469 irc.message = "" | |
| 1470 irc.buildFinished(my_builder.getName(), my_build, None) | |
| 1471 self.failUnlessEqual(irc.message, "build #862 of builder834 is complete:
Exception [step1 step2] Build details are at http://myserver/mypath?build=765"
, "Finish notification generated on failure with notify_events=['exception']") | |
| 1472 | |
| 1473 irc.message = "" | |
| 1474 irc.channel.showBlameList = True | |
| 1475 irc.buildFinished(my_builder.getName(), my_build, None) | |
| 1476 self.failUnlessEqual(irc.message, "build #862 of builder834 is complete:
Exception [step1 step2] Build details are at http://myserver/mypath?build=765
blamelist: author1", "Finish notification generated on failure with notify_even
ts=['exception']") | |
| 1477 irc.channel.showBlameList = False | |
| 1478 | |
| 1479 irc.message = "" | |
| 1480 my_build.results = builder.SUCCESS | |
| 1481 irc.buildFinished(my_builder.getName(), my_build, None) | |
| 1482 self.failUnlessEqual(irc.message, "", "No finish notification generated
on success with notify_events=['exception']") | |
| 1483 | |
| 1484 irc.message = "" | |
| 1485 my_build.results = builder.FAILURE | |
| 1486 irc.buildFinished(my_builder.getName(), my_build, None) | |
| 1487 self.failUnlessEqual(irc.message, "", "No finish notification generated
on exception with notify_events=['exception']") | |
| 1488 | |
| 1489 def do_x_to_y_notification_test(self, notify, previous_result, new_result, e
xpected_msg): | |
| 1490 irc = MyContact() | |
| 1491 irc.command_NOTIFY("on %s" % notify, "mynick") | |
| 1492 | |
| 1493 my_builder = MyBuilder("builder834") | |
| 1494 my_build = MyIrcBuild(my_builder, 862, builder.FAILURE) | |
| 1495 my_build.changes = ( | |
| 1496 Change(who = 'author1', files = ['file1'], comments = 'comment1', re
vision = 943), | |
| 1497 ) | |
| 1498 | |
| 1499 previous_build = MyIrcBuild(my_builder, 861, previous_result) | |
| 1500 my_build.setPreviousBuild(previous_build) | |
| 1501 | |
| 1502 irc.message = "" | |
| 1503 my_build.results = new_result | |
| 1504 irc.buildFinished(my_builder.getName(), my_build, None) | |
| 1505 self.failUnlessEqual(irc.message, expected_msg, "Finish notification gen
erated on failure with notify_events=['successToFailure']") | |
| 1506 | |
| 1507 def test_notification_successToFailure(self): | |
| 1508 self.do_x_to_y_notification_test(notify="successToFailure", previous_res
ult=builder.SUCCESS, new_result=builder.FAILURE, | |
| 1509 expected_msg="build #862 of builder834
is complete: Failure [step1 step2] Build details are at http://myserver/mypath?
build=765" ) | |
| 1510 | |
| 1511 self.do_x_to_y_notification_test(notify="successToFailure", previous_res
ult=builder.SUCCESS, new_result=builder.SUCCESS, | |
| 1512 expected_msg = "" ) | |
| 1513 | |
| 1514 self.do_x_to_y_notification_test(notify="successToFailure", previous_res
ult=builder.SUCCESS, new_result=builder.WARNINGS, | |
| 1515 expected_msg = "" ) | |
| 1516 | |
| 1517 self.do_x_to_y_notification_test(notify="successToFailure", previous_res
ult=builder.SUCCESS, new_result=builder.EXCEPTION, | |
| 1518 expected_msg = "" ) | |
| 1519 | |
| 1520 def test_notification_successToWarnings(self): | |
| 1521 self.do_x_to_y_notification_test(notify="successToWarnings", previous_re
sult=builder.SUCCESS, new_result=builder.WARNINGS, | |
| 1522 expected_msg="build #862 of builder834
is complete: Warnings [step1 step2] Build details are at http://myserver/mypath
?build=765" ) | |
| 1523 | |
| 1524 self.do_x_to_y_notification_test(notify="successToWarnings", previous_re
sult=builder.SUCCESS, new_result=builder.SUCCESS, | |
| 1525 expected_msg = "" ) | |
| 1526 | |
| 1527 self.do_x_to_y_notification_test(notify="successToWarnings", previous_re
sult=builder.SUCCESS, new_result=builder.FAILURE, | |
| 1528 expected_msg = "" ) | |
| 1529 | |
| 1530 self.do_x_to_y_notification_test(notify="successToWarnings", previous_re
sult=builder.SUCCESS, new_result=builder.EXCEPTION, | |
| 1531 expected_msg = "" ) | |
| 1532 | |
| 1533 def test_notification_successToException(self): | |
| 1534 self.do_x_to_y_notification_test(notify="successToException", previous_r
esult=builder.SUCCESS, new_result=builder.EXCEPTION, | |
| 1535 expected_msg="build #862 of builder834
is complete: Exception [step1 step2] Build details are at http://myserver/mypat
h?build=765" ) | |
| 1536 | |
| 1537 self.do_x_to_y_notification_test(notify="successToException", previous_r
esult=builder.SUCCESS, new_result=builder.SUCCESS, | |
| 1538 expected_msg = "" ) | |
| 1539 | |
| 1540 self.do_x_to_y_notification_test(notify="successToException", previous_r
esult=builder.SUCCESS, new_result=builder.FAILURE, | |
| 1541 expected_msg = "" ) | |
| 1542 | |
| 1543 self.do_x_to_y_notification_test(notify="successToException", previous_r
esult=builder.SUCCESS, new_result=builder.WARNINGS, | |
| 1544 expected_msg = "" ) | |
| 1545 | |
| 1546 | |
| 1547 | |
| 1548 | |
| 1549 | |
| 1550 def test_notification_failureToSuccess(self): | |
| 1551 self.do_x_to_y_notification_test(notify="failureToSuccess", previous_res
ult=builder.FAILURE,new_result=builder.SUCCESS, | |
| 1552 expected_msg="build #862 of builder834
is complete: Success [step1 step2] Build details are at http://myserver/mypath?
build=765" ) | |
| 1553 | |
| 1554 self.do_x_to_y_notification_test(notify="failureToSuccess", previous_res
ult=builder.FAILURE,new_result=builder.FAILURE, | |
| 1555 expected_msg = "" ) | |
| 1556 | |
| 1557 self.do_x_to_y_notification_test(notify="failureToSuccess", previous_res
ult=builder.FAILURE,new_result=builder.WARNINGS, | |
| 1558 expected_msg = "" ) | |
| 1559 | |
| 1560 self.do_x_to_y_notification_test(notify="failureToSuccess", previous_res
ult=builder.FAILURE,new_result=builder.EXCEPTION, | |
| 1561 expected_msg = "" ) | |
| 1562 | |
| 1563 def test_notification_failureToWarnings(self): | |
| 1564 self.do_x_to_y_notification_test(notify="failureToWarnings", previous_re
sult=builder.FAILURE, new_result=builder.WARNINGS, | |
| 1565 expected_msg="build #862 of builder834
is complete: Warnings [step1 step2] Build details are at http://myserver/mypath
?build=765" ) | |
| 1566 | |
| 1567 self.do_x_to_y_notification_test(notify="failureToWarnings", previous_re
sult=builder.FAILURE, new_result=builder.SUCCESS, | |
| 1568 expected_msg = "" ) | |
| 1569 | |
| 1570 self.do_x_to_y_notification_test(notify="failureToWarnings", previous_re
sult=builder.FAILURE, new_result=builder.FAILURE, | |
| 1571 expected_msg = "" ) | |
| 1572 | |
| 1573 self.do_x_to_y_notification_test(notify="failureToWarnings", previous_re
sult=builder.FAILURE, new_result=builder.EXCEPTION, | |
| 1574 expected_msg = "" ) | |
| 1575 | |
| 1576 def test_notification_failureToException(self): | |
| 1577 self.do_x_to_y_notification_test(notify="failureToException", previous_r
esult=builder.FAILURE, new_result=builder.EXCEPTION, | |
| 1578 expected_msg="build #862 of builder834
is complete: Exception [step1 step2] Build details are at http://myserver/mypat
h?build=765" ) | |
| 1579 | |
| 1580 self.do_x_to_y_notification_test(notify="failureToException", previous_r
esult=builder.FAILURE, new_result=builder.SUCCESS, | |
| 1581 expected_msg = "" ) | |
| 1582 | |
| 1583 self.do_x_to_y_notification_test(notify="failureToException", previous_r
esult=builder.FAILURE, new_result=builder.FAILURE, | |
| 1584 expected_msg = "" ) | |
| 1585 | |
| 1586 self.do_x_to_y_notification_test(notify="failureToException", previous_r
esult=builder.FAILURE, new_result=builder.WARNINGS, | |
| 1587 expected_msg = "" ) | |
| 1588 | |
| 1589 | |
| 1590 | |
| 1591 | |
| 1592 | |
| 1593 def test_notification_warningsToFailure(self): | |
| 1594 self.do_x_to_y_notification_test(notify="warningsToFailure", previous_re
sult=builder.WARNINGS, new_result=builder.FAILURE, | |
| 1595 expected_msg="build #862 of builder834
is complete: Failure [step1 step2] Build details are at http://myserver/mypath?
build=765" ) | |
| 1596 | |
| 1597 self.do_x_to_y_notification_test(notify="warningsToFailure", previous_re
sult=builder.WARNINGS, new_result=builder.SUCCESS, | |
| 1598 expected_msg = "" ) | |
| 1599 | |
| 1600 self.do_x_to_y_notification_test(notify="warningsToFailure", previous_re
sult=builder.WARNINGS, new_result=builder.WARNINGS, | |
| 1601 expected_msg = "" ) | |
| 1602 | |
| 1603 self.do_x_to_y_notification_test(notify="warningsToFailure", previous_re
sult=builder.WARNINGS, new_result=builder.EXCEPTION, | |
| 1604 expected_msg = "" ) | |
| 1605 | |
| 1606 def test_notification_warningsToSuccess(self): | |
| 1607 self.do_x_to_y_notification_test(notify="warningsToSuccess", previous_re
sult=builder.WARNINGS, new_result=builder.SUCCESS, | |
| 1608 expected_msg="build #862 of builder834
is complete: Success [step1 step2] Build details are at http://myserver/mypath?
build=765" ) | |
| 1609 | |
| 1610 self.do_x_to_y_notification_test(notify="warningsToSuccess", previous_re
sult=builder.WARNINGS, new_result=builder.WARNINGS, | |
| 1611 expected_msg = "" ) | |
| 1612 | |
| 1613 self.do_x_to_y_notification_test(notify="warningsToSuccess", previous_re
sult=builder.WARNINGS, new_result=builder.FAILURE, | |
| 1614 expected_msg = "" ) | |
| 1615 | |
| 1616 self.do_x_to_y_notification_test(notify="warningsToSuccess", previous_re
sult=builder.WARNINGS, new_result=builder.EXCEPTION, | |
| 1617 expected_msg = "" ) | |
| 1618 | |
| 1619 def test_notification_warningsToException(self): | |
| 1620 self.do_x_to_y_notification_test(notify="warningsToException", previous_
result=builder.WARNINGS, new_result=builder.EXCEPTION, | |
| 1621 expected_msg="build #862 of builder834
is complete: Exception [step1 step2] Build details are at http://myserver/mypat
h?build=765" ) | |
| 1622 | |
| 1623 self.do_x_to_y_notification_test(notify="warningsToException", previous_
result=builder.WARNINGS, new_result=builder.SUCCESS, | |
| 1624 expected_msg = "" ) | |
| 1625 | |
| 1626 self.do_x_to_y_notification_test(notify="warningsToException", previous_
result=builder.WARNINGS, new_result=builder.FAILURE, | |
| 1627 expected_msg = "" ) | |
| 1628 | |
| 1629 self.do_x_to_y_notification_test(notify="warningsToException", previous_
result=builder.WARNINGS, new_result=builder.WARNINGS, | |
| 1630 expected_msg = "" ) | |
| 1631 | |
| 1632 | |
| 1633 | |
| 1634 | |
| 1635 def test_notification_exceptionToFailure(self): | |
| 1636 self.do_x_to_y_notification_test(notify="exceptionToFailure", previous_r
esult=builder.EXCEPTION, new_result=builder.FAILURE, | |
| 1637 expected_msg="build #862 of builder834
is complete: Failure [step1 step2] Build details are at http://myserver/mypath?
build=765" ) | |
| 1638 | |
| 1639 self.do_x_to_y_notification_test(notify="exceptionToFailure", previous_r
esult=builder.EXCEPTION, new_result=builder.SUCCESS, | |
| 1640 expected_msg = "" ) | |
| 1641 | |
| 1642 self.do_x_to_y_notification_test(notify="exceptionToFailure", previous_r
esult=builder.EXCEPTION, new_result=builder.WARNINGS, | |
| 1643 expected_msg = "" ) | |
| 1644 | |
| 1645 self.do_x_to_y_notification_test(notify="exceptionToFailure", previous_r
esult=builder.EXCEPTION, new_result=builder.EXCEPTION, | |
| 1646 expected_msg = "" ) | |
| 1647 | |
| 1648 def test_notification_exceptionToWarnings(self): | |
| 1649 self.do_x_to_y_notification_test(notify="exceptionToWarnings", previous_
result=builder.EXCEPTION, new_result=builder.WARNINGS, | |
| 1650 expected_msg="build #862 of builder834
is complete: Warnings [step1 step2] Build details are at http://myserver/mypath
?build=765" ) | |
| 1651 | |
| 1652 self.do_x_to_y_notification_test(notify="exceptionToWarnings", previous_
result=builder.EXCEPTION, new_result=builder.SUCCESS, | |
| 1653 expected_msg = "" ) | |
| 1654 | |
| 1655 self.do_x_to_y_notification_test(notify="exceptionToWarnings", previous_
result=builder.EXCEPTION, new_result=builder.FAILURE, | |
| 1656 expected_msg = "" ) | |
| 1657 | |
| 1658 self.do_x_to_y_notification_test(notify="exceptionToWarnings", previous_
result=builder.EXCEPTION, new_result=builder.EXCEPTION, | |
| 1659 expected_msg = "" ) | |
| 1660 | |
| 1661 def test_notification_exceptionToSuccess(self): | |
| 1662 self.do_x_to_y_notification_test(notify="exceptionToSuccess", previous_r
esult=builder.EXCEPTION, new_result=builder.SUCCESS, | |
| 1663 expected_msg="build #862 of builder834
is complete: Success [step1 step2] Build details are at http://myserver/mypath?
build=765" ) | |
| 1664 | |
| 1665 self.do_x_to_y_notification_test(notify="exceptionToSuccess", previous_r
esult=builder.EXCEPTION, new_result=builder.EXCEPTION, | |
| 1666 expected_msg = "" ) | |
| 1667 | |
| 1668 self.do_x_to_y_notification_test(notify="exceptionToSuccess", previous_r
esult=builder.EXCEPTION, new_result=builder.FAILURE, | |
| 1669 expected_msg = "" ) | |
| 1670 | |
| 1671 self.do_x_to_y_notification_test(notify="exceptionToSuccess", previous_r
esult=builder.EXCEPTION, new_result=builder.WARNINGS, | |
| 1672 expected_msg = "" ) | |
| 1673 | |
| 1674 def test_notification_set_in_config(self): | |
| 1675 irc = MyContact(channel = MyChannel(notify_events = {'success': 1})) | |
| 1676 | |
| 1677 my_builder = MyBuilder("builder834") | |
| 1678 my_build = MyIrcBuild(my_builder, 862, builder.SUCCESS) | |
| 1679 my_build.changes = ( | |
| 1680 Change(who = 'author1', files = ['file1'], comments = 'comment1', re
vision = 943), | |
| 1681 ) | |
| 1682 | |
| 1683 irc.message = "" | |
| 1684 irc.buildFinished(my_builder.getName(), my_build, None) | |
| 1685 self.failUnlessEqual(irc.message, "build #862 of builder834 is complete:
Success [step1 step2] Build details are at http://myserver/mypath?build=765",
"Finish notification generated on success with notify_events=['success']") | |
| 1686 | |
| 1687 class MyIrcBuild(builder.BuildStatus): | |
| 1688 results = None | |
| 1689 | |
| 1690 def __init__(self, parent, number, results): | |
| 1691 builder.BuildStatus.__init__(self, parent, number) | |
| 1692 self.results = results | |
| 1693 self.previousBuild = None | |
| 1694 | |
| 1695 def getResults(self): | |
| 1696 return self.results | |
| 1697 | |
| 1698 def getText(self): | |
| 1699 return ('step1', 'step2') | |
| 1700 | |
| 1701 def setPreviousBuild(self, pb): | |
| 1702 self.previousBuild = pb | |
| 1703 | |
| 1704 def getPreviousBuild(self): | |
| 1705 return self.previousBuild | |
| 1706 | |
| 1707 class URLProducer: | |
| 1708 def getURLForThing(self, build): | |
| 1709 return 'http://myserver/mypath?build=765' | |
| 1710 | |
| 1711 class MyChannel: | |
| 1712 categories = None | |
| 1713 status = URLProducer() | |
| 1714 notify_events = {} | |
| 1715 | |
| 1716 def __init__(self, notify_events = {}): | |
| 1717 self.notify_events = notify_events | |
| 1718 self.showBlameList = False | |
| 1719 | |
| 1720 class MyContact(words.Contact): | |
| 1721 message = "" | |
| 1722 | |
| 1723 def __init__(self, channel = MyChannel()): | |
| 1724 words.Contact.__init__(self, channel) | |
| 1725 self.message = "" | |
| 1726 | |
| 1727 def subscribe_to_build_events(self): | |
| 1728 pass | |
| 1729 | |
| 1730 def unsubscribe_from_build_events(self): | |
| 1731 pass | |
| 1732 | |
| 1733 def send(self, msg): | |
| 1734 self.message += msg | |
| 1735 | |
| 1736 class MyIrcStatusBot(words.IrcStatusBot): | |
| 1737 def msg(self, dest, message): | |
| 1738 self.message = ['msg', dest, message] | |
| 1739 | |
| 1740 def notice(self, dest, message): | |
| 1741 self.message = ['notice', dest, message] | |
| 1742 | |
| 1743 class IrcStatusBotTester(unittest.TestCase): | |
| 1744 def testMsgOrNotice(self): | |
| 1745 channel = MyIrcStatusBot('alice', 'pa55w0od', ['#here'], | |
| 1746 builder.SUCCESS, None, {}) | |
| 1747 channel.msgOrNotice('bob', 'hello') | |
| 1748 self.failUnlessEqual(channel.message, ['msg', 'bob', 'hello']) | |
| 1749 | |
| 1750 channel.msgOrNotice('#here', 'hello') | |
| 1751 self.failUnlessEqual(channel.message, ['msg', '#here', 'hello']) | |
| 1752 | |
| 1753 channel.noticeOnChannel = True | |
| 1754 | |
| 1755 channel.msgOrNotice('bob', 'hello') | |
| 1756 self.failUnlessEqual(channel.message, ['msg', 'bob', 'hello']) | |
| 1757 | |
| 1758 channel.msgOrNotice('#here', 'hello') | |
| 1759 self.failUnlessEqual(channel.message, ['notice', '#here', 'hello']) | |
| 1760 | |
| 1761 class StepStatistics(unittest.TestCase): | |
| 1762 def testStepStatistics(self): | |
| 1763 status = builder.BuildStatus(builder.BuilderStatus("test"), 123) | |
| 1764 status.addStepWithName('step1') | |
| 1765 status.addStepWithName('step2') | |
| 1766 status.addStepWithName('step3') | |
| 1767 status.addStepWithName('step4') | |
| 1768 | |
| 1769 steps = status.getSteps() | |
| 1770 (step1, step2, step3, step4) = steps | |
| 1771 | |
| 1772 step1.setStatistic('test-prop', 1) | |
| 1773 step3.setStatistic('test-prop', 2) | |
| 1774 step4.setStatistic('test-prop', 4) | |
| 1775 | |
| 1776 step1.setStatistic('other-prop', 27) | |
| 1777 # Just to have some other properties around | |
| 1778 | |
| 1779 self.failUnlessEqual(step1.getStatistic('test-prop'), 1, | |
| 1780 'Retrieve an existing property') | |
| 1781 self.failUnlessEqual(step1.getStatistic('test-prop', 99), 1, | |
| 1782 "Don't default an existing property") | |
| 1783 self.failUnlessEqual(step2.getStatistic('test-prop', 99), 99, | |
| 1784 'Default a non-existant property') | |
| 1785 | |
| 1786 self.failUnlessEqual( | |
| 1787 status.getSummaryStatistic('test-prop', operator.add), 7, | |
| 1788 'Sum property across the build') | |
| 1789 | |
| 1790 self.failUnlessEqual( | |
| 1791 status.getSummaryStatistic('test-prop', operator.add, 13), 20, | |
| 1792 'Sum property across the build with initial value') | |
| 1793 | |
| 1794 class BuildExpectation(unittest.TestCase): | |
| 1795 class MyBuilderStatus: | |
| 1796 implements(interfaces.IBuilderStatus) | |
| 1797 | |
| 1798 def setSlavenames(self, slaveName): | |
| 1799 pass | |
| 1800 | |
| 1801 class MyBuilder(Builder): | |
| 1802 def __init__(self, name): | |
| 1803 Builder.__init__(self, { | |
| 1804 'name': name, | |
| 1805 'builddir': '/tmp/somewhere', | |
| 1806 'slavebuilddir': '/tmp/somewhere_else', | |
| 1807 'factory': 'aFactory' | |
| 1808 }, BuildExpectation.MyBuilderStatus()) | |
| 1809 | |
| 1810 class MyBuild(Build): | |
| 1811 def __init__(self, b): | |
| 1812 self.builder = b | |
| 1813 self.remote = None | |
| 1814 | |
| 1815 step1_progress = progress.StepProgress('step1', ['elapsed']) | |
| 1816 self.progress = progress.BuildProgress([step1_progress]) | |
| 1817 step1_progress.setBuildProgress(self.progress) | |
| 1818 | |
| 1819 step1_progress.start() | |
| 1820 sleep(1); | |
| 1821 step1_progress.finish() | |
| 1822 | |
| 1823 self.deferred = defer.Deferred() | |
| 1824 self.locks = [] | |
| 1825 self.build_status = builder.BuildStatus(b.builder_status, 1) | |
| 1826 | |
| 1827 | |
| 1828 def testBuildExpectation_BuildSuccess(self): | |
| 1829 b = BuildExpectation.MyBuilder("builder1") | |
| 1830 build = BuildExpectation.MyBuild(b) | |
| 1831 | |
| 1832 build.buildFinished(['sometext'], builder.SUCCESS) | |
| 1833 self.failIfEqual(b.expectations.expectedBuildTime(), 0, 'Non-Zero expect
ation for a failed build') | |
| 1834 | |
| 1835 def testBuildExpectation_BuildFailure(self): | |
| 1836 b = BuildExpectation.MyBuilder("builder1") | |
| 1837 build = BuildExpectation.MyBuild(b) | |
| 1838 | |
| 1839 build.buildFinished(['sometext'], builder.FAILURE) | |
| 1840 self.failUnlessEqual(b.expectations, None, 'Zero expectation for a faile
d build') | |
| 1841 | |
| 1842 class Pruning(unittest.TestCase): | |
| 1843 def runTest(self, files, buildHorizon, logHorizon): | |
| 1844 bstat = builder.BuilderStatus("foo") | |
| 1845 bstat.buildHorizon = buildHorizon | |
| 1846 bstat.logHorizon = logHorizon | |
| 1847 bstat.basedir = "prune-test" | |
| 1848 | |
| 1849 rmtree(bstat.basedir) | |
| 1850 os.mkdir(bstat.basedir) | |
| 1851 for filename in files: | |
| 1852 open(os.path.join(bstat.basedir, filename), "w").write("TEST") | |
| 1853 bstat.determineNextBuildNumber() | |
| 1854 | |
| 1855 bstat.prune() | |
| 1856 | |
| 1857 remaining = os.listdir(bstat.basedir) | |
| 1858 remaining.sort() | |
| 1859 return remaining | |
| 1860 | |
| 1861 files_base = [ | |
| 1862 '10', | |
| 1863 '11', | |
| 1864 '12', '12-log-bar', '12-log-foo', | |
| 1865 '13', '13-log-foo', | |
| 1866 '14', '14-log-bar', '14-log-foo', | |
| 1867 ] | |
| 1868 | |
| 1869 def test_rmlogs(self): | |
| 1870 remaining = self.runTest(self.files_base, 5, 2) | |
| 1871 self.failUnlessEqual(remaining, [ | |
| 1872 '10', | |
| 1873 '11', | |
| 1874 '12', | |
| 1875 '13', '13-log-foo', | |
| 1876 '14', '14-log-bar', '14-log-foo', | |
| 1877 ]) | |
| 1878 | |
| 1879 def test_rmbuilds(self): | |
| 1880 remaining = self.runTest(self.files_base, 2, 0) | |
| 1881 self.failUnlessEqual(remaining, [ | |
| 1882 '13', '13-log-foo', | |
| 1883 '14', '14-log-bar', '14-log-foo', | |
| 1884 ]) | |
| 1885 | |
| 1886 class EventGenerator(unittest.TestCase): | |
| 1887 def makeGenerator(self, reasons=[], minTime=0): | |
| 1888 b = MyBuilder("bname") | |
| 1889 b1 = b.newBuild() | |
| 1890 b1.setSourceStamp(SourceStamp(changes=[Change("foo", [], "")])) | |
| 1891 b1.buildStarted(b1, 1) | |
| 1892 b2 = b.newBuild() | |
| 1893 b2.setSourceStamp(SourceStamp(changes=[Change("bar", [], "")])) | |
| 1894 b2.buildStarted(b2, 2) | |
| 1895 return b.eventGenerator([], [], reasons, minTime) | |
| 1896 | |
| 1897 def testEventGenerator_Unfiltered(self): | |
| 1898 gen = self.makeGenerator() | |
| 1899 self.failUnlessEqual(len([e for e in gen]), 2) | |
| 1900 | |
| 1901 def testEventGenerator_Filtered(self): | |
| 1902 gen = self.makeGenerator(["foo"]) | |
| 1903 self.failUnlessEqual(len([e for e in gen]), 1) | |
| 1904 | |
| 1905 def testEventGenerator_MinTime(self): | |
| 1906 gen = self.makeGenerator([], 2) | |
| 1907 self.failUnlessEqual(len([e for e in gen]), 1) | |
| OLD | NEW |