Merged in default production-0.8 FIREFOX_3_5_19_BUILD1 FIREFOX_3_6_17_BUILD1 FIREFOX_3_6_17_BUILD2 FIREFOX_4_0_1_BUILD1 FIREFOX_4_0_1_RELEASE
authorChris AtLee <catlee@mozilla.com>
Wed, 13 Apr 2011 22:53:40 -0400
branchproduction-0.8
changeset 173 aa65fddf81ca11684505fa59673fe14a505a3432
parent 170 4fd7214d3690bda5fa57f2124197f442b2cfaa73 (current diff)
parent 172 4d93829c85093c54b8b03ba8119189256b856e68 (diff)
child 183 509eed75bf25f2d9c28575e4075b10bfd5d01c36
push id75
push usercatlee@mozilla.com
push dateThu, 14 Apr 2011 03:16:52 +0000
bugs634605
Merged in default changeset: 181:67a9e105b7d4 parent: 173:c3083a37cd1a user: Chris AtLee <catlee@mozilla.com> date: Wed Apr 13 11:50:25 2011 -0400 summary: Bug 634605: Make interrupted builds show up as exceptions. r=dustin changeset: 182:4d93829c8509 tag: tip user: Chris AtLee <catlee@mozilla.com> date: Wed Apr 13 11:55:36 2011 -0400 summary: Bug 634605: Make alwaysRun steps run even if build is stopped. r=dustin
--- a/master/buildbot/process/base.py
+++ b/master/buildbot/process/base.py
@@ -331,23 +331,22 @@ class Build:
         self.results = [] # list of FAILURE, SUCCESS, WARNINGS, SKIPPED
         self.result = SUCCESS # overall result, may downgrade after each step
         self.text = [] # list of text string lists (text2)
 
     def getNextStep(self):
         """This method is called to obtain the next BuildStep for this build.
         When it returns None (or raises a StopIteration exception), the build
         is complete."""
-        if self.stopped:
-            return None
         if not self.steps:
             return None
         if not self.remote:
             return None
-        if self.terminate:
+        if self.terminate or self.stopped:
+            # Run any remaining alwaysRun steps, and skip over the others
             while True:
                 s = self.steps.pop(0)
                 if s.alwaysRun:
                     return s
                 if not self.steps:
                     return None
         else:
             return self.steps.pop(0)
@@ -439,18 +438,17 @@ class Build:
         if self.finished:
             return
         # TODO: include 'reason' in this point event
         self.builder.builder_status.addPointEvent(['interrupt'])
         self.stopped = True
         if self.currentStep:
             self.currentStep.interrupt(reason)
 
-        self.result = FAILURE
-        self.text.append("Interrupted")
+        self.result = EXCEPTION
 
         if self._acquiringLock:
             lock, access, d = self._acquiringLock
             lock.stopWaitingUntilAvailable(self, access, d)
             d.callback(None)
 
     def allStepsDone(self):
         if self.result == FAILURE:
@@ -462,17 +460,17 @@ class Build:
         else:
             text = ["build", "successful"]
         text.extend(self.text)
         return self.buildFinished(text, self.result)
 
     def buildException(self, why):
         log.msg("%s.buildException" % self)
         log.err(why)
-        self.buildFinished(["build", "exception"], FAILURE)
+        self.buildFinished(["build", "exception"], EXCEPTION)
 
     def buildFinished(self, text, results):
         """This method must be called when the last Step has completed. It
         marks the Build as complete and returns the Builder to the 'idle'
         state.
 
         It takes two arguments which describe the overall build status:
         text, results. 'results' is one of SUCCESS, WARNINGS, or FAILURE.
--- a/master/buildbot/process/buildstep.py
+++ b/master/buildbot/process/buildstep.py
@@ -746,17 +746,17 @@ class BuildStep:
         # all locks are available, claim them all
         for lock, access in self.locks:
             lock.claim(self, access)
         self.step_status.setWaitingForLocks(False)
         return defer.succeed(None)
 
     def _startStep_2(self, res):
         if self.stopped:
-            self.finished(FAILURE)
+            self.finished(EXCEPTION)
             return
 
         if self.progress:
             self.progress.start()
 
         try:
             skip = None
             if isinstance(self.doStepIf, bool):
@@ -857,16 +857,24 @@ class BuildStep:
         for lock, access in self.locks:
             if lock.isOwner(self, access):
                 lock.release(self, access)
             else:
                 # This should only happen if we've been interrupted
                 assert self.stopped
 
     def finished(self, results):
+        if self.stopped:
+            # We handle this specially because we don't care about
+            # the return code of an interrupted command; we know
+            # that this should just be exception due to interrupt
+            results = EXCEPTION
+            self.step_status.setText(self.describe(True) +
+                                 ["interrupted"])
+            self.step_status.setText2(["interrupted"])
         if self.progress:
             self.progress.finish()
         self.step_status.stepFinished(results)
         self.releaseLocks()
         self.deferred.callback(results)
 
     def failed(self, why):
         # if isinstance(why, pb.CopiedFailure): # a remote exception might
@@ -1108,18 +1116,18 @@ class LoggingBuildStep(BuildStep):
 
         if self.cmd:
             d = self.cmd.interrupt(reason)
             return d
 
     def checkDisconnect(self, f):
         f.trap(error.ConnectionLost)
         self.step_status.setText(self.describe(True) +
-                                 ["failed", "slave", "lost"])
-        self.step_status.setText2(["failed", "slave", "lost"])
+                                 ["exception", "slave", "lost"])
+        self.step_status.setText2(["exception", "slave", "lost"])
         return self.finished(RETRY)
 
     # to refine the status output, override one or more of the following
     # methods. Change as little as possible: start with the first ones on
     # this list and only proceed further if you have to    
     #
     # createSummary: add additional Logfiles with summarized results
     # evaluateCommand: decides whether the step was successful or not
@@ -1165,16 +1173,18 @@ class LoggingBuildStep(BuildStep):
             return FAILURE
         return SUCCESS
 
     def getText(self, cmd, results):
         if results == SUCCESS:
             return self.describe(True)
         elif results == WARNINGS:
             return self.describe(True) + ["warnings"]
+        elif results == EXCEPTION:
+            return self.describe(True) + ["exception"]
         else:
             return self.describe(True) + ["failed"]
 
     def getText2(self, cmd, results):
         """We have decided to add a short note about ourselves to the overall
         build description, probably because something went wrong. Return a
         short list of short strings. If your subclass counts test failures or
         warnings of some sort, this is a good place to announce the count."""
--- a/master/buildbot/status/web/files/default.css
+++ b/master/buildbot/status/web/files/default.css
@@ -353,17 +353,17 @@ div.BuildWaterfall {
 }
 
 .warnings {
 	color: #FFFFFF;
 	background-color: #ffc343;
 	border-color: #C29D46;
 }
 
-.exception {
+.exception,.retry {
 	color: #FFFFFF;
 	background-color: #f6f;
 	border-color: #ACA0B3;
 }
 
 .start,.running,.waiting,td.building {
 	color: #666666;
 	background-color: #ff6;
--- a/master/buildbot/test/unit/test_process_base.py
+++ b/master/buildbot/test/unit/test_process_base.py
@@ -86,21 +86,67 @@ class TestBuild(unittest.TestCase):
         def startStep(*args, **kw):
             # Now interrupt the build
             b.stopBuild("stop it")
             return defer.Deferred()
         step.startStep = startStep
 
         b.startBuild(status, None, slavebuilder)
 
-        self.assert_("Interrupted" in b.text)
-        self.assertEqual(b.result, FAILURE)
+        self.assertEqual(b.result, EXCEPTION)
 
         self.assert_( ('interrupt', ('stop it',), {}) in step.method_calls)
 
+    def testAlwaysRunStepStopBuild(self):
+        """Test that steps marked with alwaysRun=True still get run even if
+        the build is stopped."""
+
+        # Create a build with 2 steps, the first one will get interrupted, and
+        # the second one is marked with alwaysRun=True
+        r = FakeRequest()
+
+        b = Build([r])
+        b.setBuilder(Mock())
+
+        step1 = Mock()
+        step1.return_value = step1
+        step1.alwaysRun = False
+        step2 = Mock()
+        step2.return_value = step2
+        step2.alwaysRun = True
+        b.setStepFactories([
+            (step1, {}),
+            (step2, {}),
+            ])
+
+        slavebuilder = Mock()
+        status = Mock()
+
+        def startStep1(*args, **kw):
+            # Now interrupt the build
+            b.stopBuild("stop it")
+            return defer.succeed( SUCCESS )
+        step1.startStep = startStep1
+        step1.stepDone.return_value = False
+
+        step2Started = [False]
+        def startStep2(*args, **kw):
+            step2Started[0] = True
+            return defer.succeed( SUCCESS )
+        step2.startStep = startStep2
+        step1.stepDone.return_value = False
+
+        d = b.startBuild(status, None, slavebuilder)
+        def check(ign):
+            self.assertEqual(b.result, EXCEPTION)
+            self.assert_( ('interrupt', ('stop it',), {}) in step1.method_calls)
+            self.assert_(step2Started[0])
+        d.addCallback(check)
+        return d
+
     def testBuildLocksAcquired(self):
         r = FakeRequest()
 
         b = Build([r])
         b.setBuilder(Mock())
         b.builder.botmaster = FakeMaster()
         slavebuilder = Mock()
         status = Mock()
@@ -177,32 +223,32 @@ class TestBuild(unittest.TestCase):
         lock_access = l.access('counting')
         l.access = lambda mode: lock_access
         real_lock = b.builder.botmaster.getLockByID(l).getLock(slavebuilder)
         b.setLocks([l])
 
         step = Mock()
         step.return_value = step
         step.startStep.return_value = SUCCESS
+        step.alwaysRun = False
         b.setStepFactories([(step, {})])
 
         real_lock.claim(Mock(), l.access('counting'))
 
         def acquireLocks(res=None):
             retval = Build.acquireLocks(b, res)
             b.stopBuild('stop it')
             return retval
         b.acquireLocks = acquireLocks
 
         b.startBuild(status, None, slavebuilder)
 
         self.assert_( ('startStep', (b.remote,), {}) not in step.method_calls)
         self.assert_(b.currentStep is None)
-        self.assert_("Interrupted" in b.text, b.text)
-        self.assertEqual(b.result, FAILURE)
+        self.assertEqual(b.result, EXCEPTION)
         self.assert_( ('interrupt', ('stop it',), {}) not in step.method_calls)
 
     def testStopBuildWaitingForStepLocks(self):
         r = FakeRequest()
 
         b = Build([r])
         b.setBuilder(Mock())
         b.builder.botmaster = FakeMaster()
@@ -235,18 +281,17 @@ class TestBuild(unittest.TestCase):
         step.step_status = Mock()
         step.step_status.addLog().chunkSize = 10
         step.step_status.getLogs.return_value = []
 
         b.startBuild(status, None, slavebuilder)
 
         self.assertEqual(gotLocks, [True])
         self.assert_(('stepStarted', (), {}) in step.step_status.method_calls)
-        self.assert_("Interrupted" in b.text, b.text)
-        self.assertEqual(b.result, FAILURE)
+        self.assertEqual(b.result, EXCEPTION)
 
     def testStepDone(self):
         r = FakeRequest()
         b = Build([r])
         b.results = [SUCCESS]
         b.result = SUCCESS
         b.remote = Mock()
         step = FakeBuildStep()