class RunMasterBase(unittest.TestCase): proto = "null" if Worker is None: skip = "buildbot-worker package is not installed" @defer.inlineCallbacks def setupConfig(self, config_dict, startWorker=True): """ Setup and start a master configured by the function configFunc defined in the test module. @type config_dict: dict @param configFunc: The BuildmasterConfig dictionary. """ # mock reactor.stop (which trial *really* doesn't # like test code to call!) stop = mock.create_autospec(reactor.stop) self.patch(reactor, 'stop', stop) if startWorker: if self.proto == 'pb': proto = {"pb": {"port": "tcp:0:interface=127.0.0.1"}} workerclass = worker.Worker elif self.proto == 'null': proto = {"null": {}} workerclass = worker.LocalWorker config_dict['workers'] = [workerclass("local1", "localpw")] config_dict['protocols'] = proto m = yield getMaster(self, reactor, config_dict) self.master = m self.assertFalse(stop.called, "startService tried to stop the reactor; check logs") if not startWorker: return if self.proto == 'pb': # We find out the worker port automatically workerPort = list(itervalues(m.pbmanager.dispatchers))[ 0].port.getHost().port # create a worker, and attach it to the master, it will be started, and stopped # along with the master worker_dir = FilePath(self.mktemp()) worker_dir.createDirectory() self.w = Worker( "127.0.0.1", workerPort, "local1", "localpw", worker_dir.path, False) elif self.proto == 'null': self.w = None if self.w is not None: self.w.startService() self.addCleanup(self.w.stopService) @defer.inlineCallbacks def dump(): if not self._passed: dump = StringIO.StringIO() print("FAILED! dumping build db for debug", file=dump) builds = yield self.master.data.get(("builds",)) for build in builds: yield self.printBuild(build, dump, withLogs=True) raise self.failureException(dump.getvalue()) self.addCleanup(dump) @defer.inlineCallbacks def doForceBuild(self, wantSteps=False, wantProperties=False, wantLogs=False, useChange=False): # force a build, and wait until it is finished d = defer.Deferred() # in order to allow trigger based integration tests # we wait until the first started build is finished self.firstBuildRequestId = None def newCallback(_, data): if self.firstBuildRequestId is None: self.firstBuildRequestId = data['buildrequestid'] newConsumer.stopConsuming() def finishedCallback(_, data): if self.firstBuildRequestId == data['buildrequestid']: d.callback(data) newConsumer = yield self.master.mq.startConsuming( newCallback, ('buildrequests', None, 'new')) finishedConsumer = yield self.master.mq.startConsuming( finishedCallback, ('buildrequests', None, 'complete')) if useChange is False: # use data api to force a build yield self.master.data.control("force", {}, ("forceschedulers", "force")) else: # use data api to force a build, via a new change yield self.master.data.updates.addChange(**useChange) # wait until we receive the build finished event buildrequest = yield d builds = yield self.master.data.get( ('builds',), filters=[resultspec.Filter('buildrequestid', 'eq', [buildrequest['buildrequestid']])]) # if the build has been retried, there will be several matching builds. We return the last build build = builds[-1] finishedConsumer.stopConsuming() yield self.enrichBuild(build, wantSteps, wantProperties, wantLogs) defer.returnValue(build) @defer.inlineCallbacks def enrichBuild(self, build, wantSteps=False, wantProperties=False, wantLogs=False): # enrich the build result, with the step results if wantSteps: build["steps"] = yield self.master.data.get(("builds", build['buildid'], "steps")) # enrich the step result, with the logs results if wantLogs: build["steps"] = list(build["steps"]) for step in build["steps"]: step['logs'] = yield self.master.data.get(("steps", step['stepid'], "logs")) step["logs"] = list(step['logs']) for log in step["logs"]: log['contents'] = yield self.master.data.get(("logs", log['logid'], "contents")) if wantProperties: build["properties"] = yield self.master.data.get(("builds", build['buildid'], "properties")) @defer.inlineCallbacks def printBuild(self, build, out=sys.stdout, withLogs=False): # helper for debugging: print a build yield self.enrichBuild(build, wantSteps=True, wantProperties=True, wantLogs=True) print("*** BUILD %d *** ==> %s (%s)" % (build['buildid'], build['state_string'], statusToString(build['results'])), file=out) for step in build['steps']: print(" *** STEP %s *** ==> %s (%s)" % (step['name'], step['state_string'], statusToString(step['results'])), file=out) for url in step['urls']: print(" url:%s (%s)" % (url['name'], url['url']), file=out) for log in step['logs']: print(" log:%s (%d)" % (log['name'], log['num_lines']), file=out) if step['results'] != SUCCESS or withLogs: self.printLog(log, out) def printLog(self, log, out): print(" " * 8 + "*********** LOG: %s *********" % (log['name'],), file=out) if log['type'] == 's': for line in log['contents']['content'].splitlines(): linetype = line[0] line = line[1:] if linetype == 'h': # cyan line = "\x1b[36m" + line + "\x1b[0m" if linetype == 'e': # red line = "\x1b[31m" + line + "\x1b[0m" print(" " * 8 + line) else: print(log['contents']['content'], file=out) print(" " * 8 + "********************************", file=out)