Thursday, August 23, 2012

Buildbot Part III: Customizations

On my previous post we configured buildbot to run automated build and tests. Now, let’s add customizations, usually this configurations consists of subclasses set up for use in a regular buildbot configuration file (master.cfg).

Display more information in the waterfall

The first customization will be adding detail information in the waterfall page. One way to do this is writing a new custom buildstep. Most shell commands emit messages to stdout/stderr as they run, we will get output information from xunit.console.exe and use the functions provided by buildbot to perform summarization of the step base on the content of the stdio logfile. Bellow changes done to display details about the tests executed in a dll: total, failed, skipped and the time it took to perform the tests.

class xUnitConsole(ShellCommand):
    def __init__(self, **kwargs):
        ShellCommand.__init__(self, **kwargs)

    def describe(self, done=False):
        description = ShellCommand.describe(self,done)
        if done:
            description.append('total: %d ' % self.step_status.getStatistic('total', 0))
            failed = self.step_status.getStatistic('failed', 0)
            if failed > 0:
                description.append('failed: %d' % failed)
            skipped = self.step_status.getStatistic('skipped', 0)
            if skipped > 0:
                description.append('skipped: %d' % skipped)
            description.append('took: %s seconds' % self.step_status.getStatistic('time', 0))                
            
        return description

    def createSummary(self,log):
        _re_result = re.compile(r'(\d*) total, (\d*) failed, (\d*) skipped, took (.*) seconds')
        
        total = 0
        failed = 0
        skipped = 0
        time = 0
        
        lines = self.getLog('stdio').readlines()
        
        for l in lines:
            m = _re_result.search(l)
            if m:                
                total = int(m.group(1))
                failed = int(m.group(2))
                skipped = int(m.group(3))
                time = m.group(4)
              
        self.step_status.setStatistic('total', total)
        self.step_status.setStatistic('failed', failed)
        self.step_status.setStatistic('skipped', skipped)
        self.step_status.setStatistic('time', time)

def RunTests(factory, testproject, configuration, testdirectory):
        factory.addStep(xUnitConsole(command=['src\\xunit.console\\bin\\%s\\xunit.console.exe' % configuration,
                                '%s\\%s' % (testdirectory, testproject), "/html", "%s\\testresults.html" % testdirectory],
                                     workdir='build\\', name="Run %s" % testproject,
                                     description="Running %s" % testproject ,
                                     descriptionDone="Run %s done" % testproject,
                                     flunkOnFailure=False, warnOnFailure=True))


We switched from using ShellCommand to use the new one xUnitConsole, bellow an image with the new information displayed in the waterfall page. 

buildbot-moreinfo

you can follow a similar approach to display more information in compiling buildstep, check the visual studio buildstep in vstudio.py.

xunit.net report

Next customization will be adding a link to the report generated by xunit.console. The ShellCommand has the parameter logfiles that is useful in this case, when a command logs information in a local file rather than emitting everything to stdout/stderr.  By setting lazylogfiles=True, logfiles will be tracked lazily, meaning they will only be added when and if something is written to them. Finally, the addHtmllog adds a logfile containing pre-formatted HTML.  Bellow the changes done in master.cfg.

class  xUnitConsole(ShellCommand):    

    def __init__(self, xunitreport=None, **kwargs):
        ShellCommand.__init__(self, **kwargs)

    def describe(self, done=False):
        description = ShellCommand.describe(self,done)
        
        if done:
            description.append('total: %d ' % self.step_status.getStatistic('total', 0))
            failed = self.step_status.getStatistic('failed', 0)
            if failed > 0:
                description.append('failed: %d' % failed)
            skipped = self.step_status.getStatistic('skipped', 0)
            if skipped > 0:
                description.append('skipped: %d' % skipped)
            description.append('took: %s seconds' % self.step_status.getStatistic('time', 0))                
            
        return description

    def createSummary(self,log):
        html = self.getLog("xunit-report")
        text = html.getText()
        self.addHTMLLog("xunit-report.html", text)
        
        _re_result = re.compile(r'(\d*) total, (\d*) failed, (\d*) skipped, took (.*) seconds')
        
        total = 0
        failed = 0
        skipped = 0
        time = 0
        
        lines = self.getLog('stdio').readlines()
        
        for l in lines:
            m = _re_result.search(l)
            if m:                
                total = int(m.group(1))
                failed = int(m.group(2))
                skipped = int(m.group(3))
                time = m.group(4)
              
        self.step_status.setStatistic('total', total)
        self.step_status.setStatistic('failed', failed)
        self.step_status.setStatistic('skipped', skipped)
        self.step_status.setStatistic('time', time)

def RunTests(factory, testproject, configuration, testdirectory):
        factory.addStep(xUnitConsole(command=['src\\xunit.console\\bin\\%s\\xunit.console.exe' % configuration,
                                '%s\\%s' % (testdirectory, testproject), "/html", "%s\\testresults.html" % testdirectory],
                                     workdir='build\\', name="Run %s" % testproject,
                                     description="Running %s" % testproject ,
                                     descriptionDone="Run %s done" % testproject,
                                     flunkOnFailure=False, warnOnFailure=True,
                                     logfiles = {"xunit-report" : "%s\\testresults.html" % testdirectory }, lazylogfiles=True))


The waterfall page now has a link to xunit.net report, as you can see bellow:

Buildbot-reportxunitreport

On my next post I’ll show you how to customize the scheduler.

No comments:

Post a Comment