Ticket #228: 228-step-statistics.patch

File 228-step-statistics.patch, 5.9 kB (added by dustin, 5 months ago)

step stats + tests

  • old-dustin/buildbot/status/builder.py

    old new  
    449449            self.finish() # releases self.openfile, which will be closed 
    450450        del self.entries 
    451451 
    452  
    453452class HTMLLogFile: 
    454453    implements(interfaces.IStatusLog) 
    455454 
     
    642641    I represent a collection of output status for a 
    643642    L{buildbot.process.step.BuildStep}. 
    644643 
     644    Statistics contain any information gleaned from a step that is 
     645    not in the form of a logfile.  As an example, steps that run 
     646    tests might gather statistics about the number of passed, failed, 
     647    or skipped tests. 
     648 
    645649    @type color: string 
    646650    @cvar color: color that this step feels best represents its 
    647651                 current mood. yellow,green,red,orange are the 
     
    655659    @cvar text2: list of short texts added to the overall build description 
    656660    @type logs: dict of string -> L{buildbot.status.builder.LogFile} 
    657661    @ivar logs: logs of steps 
     662    @type statistics: dict 
     663    @ivar statistics: results from running this step 
    658664    """ 
    659665    # note that these are created when the Build is set up, before each 
    660666    # corresponding BuildStep has started. 
    661667    implements(interfaces.IBuildStepStatus, interfaces.IStatusEvent) 
    662     persistenceVersion = 1 
     668    persistenceVersion = 2 
    663669 
    664670    started = None 
    665671    finished = None 
     
    671677    watchers = [] 
    672678    updates = {} 
    673679    finishedWatchers = [] 
     680    statistics = {} 
    674681 
    675682    def __init__(self, parent): 
    676683        assert interfaces.IBuildStatus(parent) 
     
    680687        self.watchers = [] 
    681688        self.updates = {} 
    682689        self.finishedWatchers = [] 
     690        self.statistics = {} 
    683691 
    684692    def getName(self): 
    685693        """Returns a short string with the name of this step. This string 
     
    764772        """ 
    765773        return (self.results, self.text2) 
    766774 
     775    def hasStatistic(self, name): 
     776        """Return true if this step has a value for the given statistic. 
     777        """ 
     778        return self.statistics.has_key(name) 
     779 
     780    def getStatistic(self, name, default=None): 
     781        """Return the given statistic, if present 
     782        """ 
     783        return self.statistics.get(name, default) 
     784 
    767785    # subscription interface 
    768786 
    769787    def subscribe(self, receiver, updateInterval=10): 
     
    847865    def setText2(self, text): 
    848866        self.text2 = text 
    849867 
     868    def setStatistic(self, name, value): 
     869        """Set the given statistic.  Usually called by subclasses. 
     870        """ 
     871        self.statistics[name] = value 
     872 
    850873    def stepFinished(self, results): 
    851874        self.finished = util.now() 
    852875        self.results = results 
     
    886909        if not hasattr(self, "urls"): 
    887910            self.urls = {} 
    888911 
     912    def upgradeToVersion2(self): 
     913        if not hasattr(self, "statistics"): 
     914            self.statistics = {} 
     915 
    889916 
    890917class BuildStatus(styles.Versioned): 
    891918    implements(interfaces.IBuildStatus, interfaces.IStatusEvent) 
     
    9781005    def getTimes(self): 
    9791006        return (self.started, self.finished) 
    9801007 
     1008    _sentinel = [] # used as a sentinel to indicate unspecified initial_value 
     1009    def getSummaryStatistic(self, name, summary_fn, initial_value=_sentinel): 
     1010        """Summarize the named statistic over all steps in which it 
     1011        exists, using combination_fn and initial_value to combine multiple 
     1012        results into a single result.  This translates to a call to Python's 
     1013        X{reduce}:: 
     1014            return reduce(summary_fn, step_stats_list, initial_value) 
     1015        """ 
     1016        step_stats_list = [ 
     1017                st.getStatistic(name) 
     1018                for st in self.steps 
     1019                if st.hasStatistic(name) ] 
     1020        if initial_value is self._sentinel: 
     1021            return reduce(summary_fn, step_stats_list) 
     1022        else: 
     1023            return reduce(summary_fn, step_stats_list, initial_value) 
     1024 
    9811025    def isFinished(self): 
    9821026        return (self.finished is not None) 
    9831027 
  • old-dustin/buildbot/test/test_status.py

    old new  
    11# -*- test-case-name: buildbot.test.test_status -*- 
    22 
    33import email, os 
     4import operator 
    45 
    56from zope.interface import implements 
    67from twisted.internet import defer, reactor 
     
    11941195 
    11951196    def send(self, msg): 
    11961197        self.message += msg 
     1198 
     1199class StepStatistics(unittest.TestCase): 
     1200    def testStepStatistics(self): 
     1201        status = builder.BuildStatus(builder.BuilderStatus("test"), 123) 
     1202        status.addStepWithName('step1') 
     1203        status.addStepWithName('step2') 
     1204        status.addStepWithName('step3') 
     1205        status.addStepWithName('step4') 
     1206 
     1207        steps = status.getSteps() 
     1208        (step1, step2, step3, step4) = steps 
     1209 
     1210        step1.setStatistic('test-prop', 1) 
     1211        step3.setStatistic('test-prop', 2) 
     1212        step4.setStatistic('test-prop', 4) 
     1213 
     1214        step1.setStatistic('other-prop', 27) 
     1215        # Just to have some other properties around 
     1216 
     1217        self.failUnlessEqual(step1.getStatistic('test-prop'), 1, 
     1218            'Retrieve an existing property') 
     1219        self.failUnlessEqual(step1.getStatistic('test-prop', 99), 1, 
     1220            "Don't default an existing property") 
     1221        self.failUnlessEqual(step2.getStatistic('test-prop', 99), 99, 
     1222            'Default a non-existant property') 
     1223 
     1224        self.failUnlessEqual( 
     1225            status.getSummaryStatistic('test-prop', operator.add), 7, 
     1226            'Sum property across the build') 
     1227 
     1228        self.failUnlessEqual( 
     1229            status.getSummaryStatistic('test-prop', operator.add, 13), 20, 
     1230            'Sum property across the build with initial value')