VirtualBox

Ignore:
Timestamp:
May 28, 2016 1:38:58 AM (9 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
107546
Message:

testmanager: Added report on faillure reasons, listing first and last build with each.

Location:
trunk/src/VBox/ValidationKit/testmanager/core
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/ValidationKit/testmanager/core/db.py

    r61217 r61254  
    333333
    334334        aasExplain = None;
    335         if self._oExplainCursor is not None:
     335        if self._oExplainCursor is not None and not sBound.startswith('DROP'):
    336336            try:
    337337                if config.g_kfWebUiSqlTraceExplainTiming:
  • trunk/src/VBox/ValidationKit/testmanager/core/report.py

    r56763 r61254  
    3434from testmanager.core.build         import BuildCategoryData;
    3535from testmanager.core.dbobjcache    import DatabaseObjCache;
     36from testmanager.core.failurereason import FailureReasonLogic;
    3637from testmanager.core.testbox       import TestBoxData;
    3738from common                         import constants;
     
    126127        return sWhere;
    127128
     129    def getPeriodStart(self, iPeriod):
     130        """ Gets the python timestamp for the start of the given period. """
     131        from datetime import timedelta;
     132        tsNow = self.tsNow if self.tsNow is not None else self._oDb.getCurrentTimestamp();
     133        cHoursStart = (self.cPeriods - iPeriod    ) * self.cHoursPerPeriod;
     134        return tsNow - timedelta(hours = cHoursStart);
     135
     136    def getPeriodEnd(self, iPeriod):
     137        """ Gets the python timestamp for the end of the given period. """
     138        from datetime import timedelta;
     139        tsNow = self.tsNow if self.tsNow is not None else self._oDb.getCurrentTimestamp();
     140        cHoursEnd   = (self.cPeriods - iPeriod - 1) * self.cHoursPerPeriod;
     141        return tsNow - timedelta(hours = cHoursEnd);
     142
    128143    def getExtraWhereExprForPeriod(self, iPeriod):
    129144        """
     
    157172        return '%dh ago' % (iPeriod * self.cHoursPerPeriod, );
    158173
     174    def getStraightPeriodDesc(self, iPeriod):
     175        """
     176        Returns the period description, usually for graph data.
     177        """
     178        iWickedPeriod = self.cPeriods - iPeriod - 1;
     179        return self.getPeriodDesc(iWickedPeriod);
     180
     181
     182class ReportFailureReasonRow(object):
     183    """ Simpler to return this than muck about with stupid arrays. """
     184    def __init__(self, aoRow, oReason):
     185        self.idFailureReason = aoRow[0];
     186        self.cHits           = aoRow[1];
     187        self.tsMin           = aoRow[2];
     188        self.tsMax           = aoRow[3];
     189        self.oReason         = oReason; # FailureReasonDataEx
     190
     191class ReportFailureReasonTransient(object):
     192    """ Details the first or last occurence of a reason.  """
     193    def __init__(self, idBuild, iRevision, sRepository, idTestSet, idTestResult, tsDone, oReason, iPeriod, fEnter):
     194        self.idBuild      = idBuild;        # Build ID.
     195        self.iRevision    = iRevision;      # SVN revision for build.
     196        self.sRepository  = sRepository;    # SVN repository for build.
     197        self.idTestSet    = idTestSet;      # Test set.
     198        self.idTestResult = idTestResult;   # Test result.
     199        self.tsDone       = tsDone;         # When the test set was done.
     200        self.oReason      = oReason;        # FailureReasonDataEx
     201        self.iPeriod      = iPeriod;        # Data set period.
     202        self.fEnter       = fEnter;         # True if enter event, False if leave event.
     203
     204class ReportFailureReasonPeriod(object):
     205    """ A period in ReportFailureReasonSet. """
     206    def __init__(self, oSet, iPeriod, sDesc, tsFrom, tsTo):
     207        self.oSet      = oSet   # Reference to the parent ReportFailureReasonSet.
     208        self.iPeriod   = iPeriod;
     209        self.sDesc     = sDesc;
     210        self.aoRows    = [];    # Rows in order the database returned them.
     211        self.dById     = {};    # Same as aoRows but indexed by idFailureReason.
     212        self.cHits     = 0;     # Total number of hits.
     213        self.dFirst    = {};    # The reasons seen for the first time (idFailureReason key).
     214        self.dLast     = {};    # The reasons seen for the last time (idFailureReason key).
     215        self.tsStart   = tsFrom;
     216        self.tsEnd     = tsTo;
     217        self.tsMin     = tsTo;
     218        self.tsMax     = tsFrom;
     219
     220class ReportFailureReasonSet(object):
     221    """ What ReportLazyModel.getFailureReasons returns. """
     222    def __init__(self):
     223        self.aoPeriods   = [];  # Periods in ascending order (time wise).
     224        self.dReasons    = {};  # FailureReasonDataEx objected indexted by idFailureReason
     225        self.dTotals     = {};  # Totals per reason, indexed by idFailureReason.
     226        self.cHits       = 0;   # Total number of hits in all periods and all reasons.
     227        self.cMaxRowHits = 0;   # Max hits in a row.
     228        self.diFirst     = {};  # The period number a reason was first seen (idFailureReason key).
     229        self.diLast      = {};  # The period number a reason was last seen (idFailureReason key).
     230        self.aoEnterInfo = [];  # Array of ReportFailureReasonTransient order by iRevision. Excludes the first period.
     231        self.aoLeaveInfo = [];  # Array of ReportFailureReasonTransient order in descending order by iRevision.  Excludes last.
    159232
    160233
     
    191264
    192265        adPeriods = [];
    193         for iPeriod in range(self.cPeriods):
     266        for iPeriod in xrange(self.cPeriods):
    194267            self._oDb.execute('SELECT   enmStatus, COUNT(TestSets.idTestSet)\n'
    195268                              'FROM     TestSets' + self.getExtraSubjectTables() +'\n'
     
    219292
    220293        return adPeriods;
     294
     295
     296    def getFailureReasons(self):
     297        """
     298        Gets the failure reasons of the subject in the specified period.
     299
     300        Returns an array of data per period (0 is the oldes, self.cPeriods-1 is
     301        the latest) where each entry is a dicationary using failure reason ID as
     302        key.  The dictionary contains a tuple where the first element is the
     303        number of occurences and the second is the corresponding
     304        FailureReasonDataEx object from the cache.
     305
     306        Note that reason IDs may not be present in every period, we only return
     307        those with actual occurences.
     308        """
     309
     310        # Create a temporary table
     311        sTsNow   = 'CURRENT_TIMESTAMP' if self.tsNow is None else self._oDb.formatBindArgs('%s::TIMESTAMP', (self.tsNow,));
     312        sTsFirst = '(%s - interval \'%s hours\')' \
     313                 % (sTsNow, self.cHoursPerPeriod * self.cPeriods,);
     314        self._oDb.execute('CREATE TEMPORARY TABLE TmpReasons ON COMMIT DROP AS\n'
     315                          'SELECT   TestResultFailures.idFailureReason AS idFailureReason,\n'
     316                          '         TestResultFailures.idTestResult    AS idTestResult,\n'
     317                          '         TestSets.idTestSet                 AS idTestSet,\n'
     318                          '         TestSets.tsDone                    AS tsDone,\n'
     319                          '         TestSets.tsCreated                 AS tsCreated,\n'
     320                          '         TestSets.idBuild                   AS idBuild\n'
     321                          'FROM     TestResultFailures,\n'
     322                          '         TestResults,\n'
     323                          '         TestSets' + self.getExtraSubjectTables() + '\n'
     324                          'WHERE    TestResultFailures.idTestResult = TestResults.idTestResult\n'
     325                          '     AND TestResultFailures.tsExpire     = \'infinity\'::TIMESTAMP\n'
     326                          '     AND TestResultFailures.tsEffective >= ' + sTsFirst + '\n'
     327                          '     AND TestResults.enmStatus          <> \'running\'\n'
     328                          '     AND TestResults.enmStatus          <> \'success\'\n'
     329                          '     AND TestResults.tsCreated          >= ' + sTsFirst + '\n'
     330                          '     AND TestResults.tsCreated          <  ' + sTsNow + '\n'
     331                          '     AND TestResults.idTestSet           = TestSets.idTestSet\n'
     332                          '     AND TestSets.tsDone                >= ' + sTsFirst + '\n'
     333                          '     AND TestSets.tsDone                <  ' + sTsNow + '\n'
     334                        + self.getExtraSubjectWhereExpr());
     335        self._oDb.execute('SELECT idFailureReason FROM TmpReasons;');
     336
     337
     338        # Retrieve the period results.
     339        oFailureReasonLogic = FailureReasonLogic(self._oDb);
     340        oSet = ReportFailureReasonSet();
     341        for iPeriod in xrange(self.cPeriods):
     342            #cHoursStarted = (self.cPeriods - iPeriod) * self.cHoursPerPeriod;
     343            #if self.tsNow is None:
     344            #    sTsFirst = '(CURRENT_TIMESTAMP - interval \'%u hours\')' % (cHoursStarted,);
     345            #
     346            #else:
     347            #    sTsFirst = '(%s - interval \'%u hours\')' \
     348            #             % ( self._oDb.formatBindArgs('%s::TIMESTAMP', (self.tsNow,)), cHoursStarted,) ;
     349            #
     350            #self._oDb.execute('SELECT   TestResultFailures.idFailureReason,\n'
     351            #                  '         COUNT(TestResultFailures.idTestResult),\n'
     352            #                  '         MIN(TestSets.tsDone),\n'
     353            #                  '         MAX(TestSets.tsDone)\n'
     354            #                  'FROM     TestResultFailures,\n'
     355            #                  '         TestResults,\n'
     356            #                  '         TestSets' + self.getExtraSubjectTables() + '\n'
     357            #                  'WHERE    TestResultFailures.idTestResult = TestResults.idTestResult\n'
     358            #                  '     AND TestResultFailures.tsExpire = \'infinity\'::TIMESTAMP\n'
     359            #                  '     AND TestResultFailures.tsEffective >= ' + sTsFirst + '\n'
     360            #                  '     AND TestResults.enmStatus <> \'running\'\n'
     361            #                  '     AND TestResults.enmStatus <> \'success\'\n'
     362            #                  '     AND TestResults.tsCreated >= ' + sTsFirst + '\n'
     363            #                  '     AND TestResults.idTestSet = TestSets.idTestSet\n'
     364            #                + self.getExtraSubjectWhereExpr()
     365            #                + self.getExtraWhereExprForPeriod(iPeriod)
     366            #                + 'GROUP BY TestResultFailures.idFailureReason\n');
     367
     368            self._oDb.execute('SELECT   idFailureReason,\n'
     369                              '         COUNT(idTestResult),\n'
     370                              '         MIN(tsDone),\n'
     371                              '         MAX(tsDone)\n'
     372                              'FROM     TmpReasons\n'
     373                              'WHERE    TRUE\n'
     374                            + self.getExtraWhereExprForPeriod(iPeriod).replace('TestSets.', '')
     375                            + 'GROUP BY idFailureReason\n');
     376
     377            aaoRows = self._oDb.fetchAll()
     378
     379            oPeriod = ReportFailureReasonPeriod(oSet, iPeriod, self.getStraightPeriodDesc(iPeriod),
     380                                                self.getPeriodStart(iPeriod), self.getPeriodEnd(iPeriod));
     381            oSet.aoPeriods.append(oPeriod);
     382            for aoRow in aaoRows:
     383                oReason = oFailureReasonLogic.cachedLookup(aoRow[0]);
     384                oPeriodRow = ReportFailureReasonRow(aoRow, oReason);
     385                oPeriod.aoRows.append(oPeriodRow);
     386                oPeriod.dById[oPeriodRow.idFailureReason] = oPeriodRow;
     387                oPeriod.cHits += oPeriodRow.cHits;
     388                if oPeriodRow.idFailureReason in oSet.dReasons:
     389                    oSet.dTotals[oPeriodRow.idFailureReason]  += oPeriodRow.cHits;
     390                else:
     391                    oSet.dTotals[oPeriodRow.idFailureReason]   = oPeriodRow.cHits;
     392                    oSet.dReasons[oPeriodRow.idFailureReason]  = oReason;
     393                    oSet.diFirst[oPeriodRow.idFailureReason]   = iPeriod;
     394                    oPeriod.dFirst[oPeriodRow.idFailureReason] = oReason;
     395                if oPeriodRow.cHits > oSet.cMaxRowHits:
     396                    oSet.cMaxRowHits = oPeriodRow.cHits;
     397                if oPeriodRow.tsMin < oPeriod.tsMin:
     398                    oPeriod.tsMin = oPeriodRow.tsMin;
     399                if oPeriodRow.tsMax > oPeriod.tsMax:
     400                    oPeriod.tsMax = oPeriodRow.tsMax;
     401            oSet.cHits += oPeriod.cHits;
     402
     403        #
     404        # construct the diLast and dLast bits.
     405        #
     406        for iPeriod in xrange(self.cPeriods - 1, 0, -1):
     407            oPeriod = oSet.aoPeriods[iPeriod];
     408            for oRow in oPeriod.aoRows:
     409                if oRow.idFailureReason not in oSet.diLast:
     410                    oSet.diLast[oRow.idFailureReason]   = iPeriod;
     411                    oPeriod.dLast[oRow.idFailureReason] = oRow.oReason;
     412
     413        #
     414        # For reasons entering after the first period, look up the build and
     415        # test set it first occured with.
     416        #
     417        for iPeriod in xrange(1, self.cPeriods):
     418            oPeriod = oSet.aoPeriods[iPeriod];
     419            for oReason in oPeriod.dFirst.values():
     420                oSet.aoEnterInfo.append(self._getEdgeFailureReasonOccurence(oReason, iPeriod, fEnter = True));
     421        oSet.aoEnterInfo = sorted(oSet.aoEnterInfo, key = lambda oTrans: oTrans.iRevision);
     422
     423        # Ditto for reasons leaving before the last.
     424        for iPeriod in xrange(self.cPeriods - 1):
     425            oPeriod = oSet.aoPeriods[iPeriod];
     426            for oReason in oPeriod.dLast.values():
     427                oSet.aoLeaveInfo.append(self._getEdgeFailureReasonOccurence(oReason, iPeriod, fEnter = False));
     428        oSet.aoLeaveInfo = sorted(oSet.aoLeaveInfo, key = lambda oTrans: oTrans.iRevision, reverse = True);
     429
     430        self._oDb.execute('DROP TABLE TmpReasons\n');
     431        return oSet;
     432
     433
     434    def _getEdgeFailureReasonOccurence(self, oReason, iPeriod, fEnter = True):
     435        """
     436        Helper for the failure reason report that finds the oldest or newest build
     437        (SVN rev) and test set (start time) it occured with.
     438
     439        If fEnter is set the oldest occurence is return, if fEnter clear the newest
     440        is is returned.
     441
     442        Returns ReportFailureReasonTransient instant.
     443
     444        """
     445        sSorting = 'ASC' if fEnter else 'DESC';
     446        self._oDb.execute('SELECT   TmpReasons.idTestResult,\n'
     447                          '         TmpReasons.idTestSet,\n'
     448                          '         TmpReasons.tsDone,\n'
     449                          '         TmpReasons.idBuild,\n'
     450                          '         Builds.iRevision,\n'
     451                          '         BuildCategories.sRepository\n'
     452                          'FROM     TmpReasons,\n'
     453                          '         Builds,\n'
     454                          '         BuildCategories\n'
     455                          'WHERE    TmpReasons.idFailureReason  = %s\n'
     456                          '     AND TmpReasons.idBuild          = Builds.idBuild\n'
     457                          '     AND Builds.tsExpire             > TmpReasons.tsCreated\n'
     458                          '     AND Builds.tsEffective         <= TmpReasons.tsCreated\n'
     459                          '     AND Builds.idBuildCategory      = BuildCategories.idBuildCategory\n'
     460                          'ORDER BY Builds.iRevision ' + sSorting + ',\n'
     461                          '         TmpReasons.tsCreated ' + sSorting + '\n'
     462                          'LIMIT 1\n'
     463                          , ( oReason.idFailureReason, ));
     464        aoRow = self._oDb.fetchOne();
     465        if aoRow is None:
     466            return ReportFailureReasonTransient(-1, -1, 'internal-error', -1, -1,
     467                                                self._oDb.getCurrentTimestamp(), oReason, iPeriod);
     468        return ReportFailureReasonTransient(idBuild = aoRow[3], iRevision = aoRow[4], sRepository = aoRow[5],
     469                                            idTestSet = aoRow[1], idTestResult = aoRow[0], tsDone = aoRow[2],
     470                                            oReason = oReason, iPeriod = iPeriod, fEnter = fEnter);
     471
     472
     473
    221474
    222475class ReportGraphModel(ReportModelBase): # pylint: disable=R0903
Note: See TracChangeset for help on using the changeset viewer.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette