Home | History | Annotate | Download | only in scripts
      1 /*
      2  * Copyright (C) 2011 Google Inc. All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions
      6  * are met:
      7  * 1. Redistributions of source code must retain the above copyright
      8  *    notice, this list of conditions and the following disclaimer.
      9  * 2. Redistributions in binary form must reproduce the above copyright
     10  *    notice, this list of conditions and the following disclaimer in the
     11  *    documentation and/or other materials provided with the distribution.
     12  *
     13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
     14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
     15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
     17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
     23  * THE POSSIBILITY OF SUCH DAMAGE.
     24  */
     25 
     26 var model = model || {};
     27 
     28 (function () {
     29 
     30 var kCommitLogLength = 50;
     31 
     32 model.state = {};
     33 model.state.failureAnalysisByTest = {};
     34 model.state.rebaselineQueue = [];
     35 model.state.expectationsUpdateQueue = [];
     36 
     37 function findAndMarkRevertedRevisions(commitDataList)
     38 {
     39     var revertedRevisions = {};
     40     $.each(commitDataList, function(index, commitData) {
     41         if (commitData.revertedRevision)
     42             revertedRevisions[commitData.revertedRevision] = true;
     43     });
     44     $.each(commitDataList, function(index, commitData) {
     45         if (commitData.revision in revertedRevisions)
     46             commitData.wasReverted = true;
     47     });
     48 }
     49 
     50 function fuzzyFind(testName, commitData)
     51 {
     52     var indexOfLastDot = testName.lastIndexOf('.');
     53     var stem = indexOfLastDot == -1 ? testName : testName.substr(0, indexOfLastDot);
     54     return commitData.message.indexOf(stem) != -1;
     55 }
     56 
     57 function heuristicallyNarrowRegressionRange(failureAnalysis)
     58 {
     59     var commitDataList = model.state.recentCommits;
     60     var commitDataIndex = commitDataList.length - 1;
     61 
     62     for(var revision = failureAnalysis.newestPassingRevision + 1; revision <= failureAnalysis.oldestFailingRevision; ++revision) {
     63         while (commitDataIndex >= 0 && commitDataList[commitDataIndex].revision < revision)
     64             --commitDataIndex;
     65         var commitData = commitDataList[commitDataIndex];
     66         if (commitData.revision != revision)
     67             continue;
     68         if (fuzzyFind(failureAnalysis.testName, commitData)) {
     69             failureAnalysis.oldestFailingRevision = revision;
     70             failureAnalysis.newestPassingRevision = revision - 1;
     71             return;
     72         }
     73     }
     74 }
     75 
     76 model.queueForRebaseline = function(failureInfo)
     77 {
     78     model.state.rebaselineQueue.push(failureInfo);
     79 };
     80 
     81 model.takeRebaselineQueue = function()
     82 {
     83     var queue = model.state.rebaselineQueue;
     84     model.state.rebaselineQueue = [];
     85     return queue;
     86 };
     87 
     88 model.queueForExpectationUpdate = function(failureInfo)
     89 {
     90     model.state.expectationsUpdateQueue.push(failureInfo);
     91 };
     92 
     93 model.takeExpectationUpdateQueue = function()
     94 {
     95     var queue = model.state.expectationsUpdateQueue;
     96     model.state.expectationsUpdateQueue = [];
     97     return queue;
     98 };
     99 
    100 var g_commitIndex = {};
    101 
    102 model.updateRecentCommits = function(callback)
    103 {
    104     trac.recentCommitData('trunk', kCommitLogLength, function(commitDataList) {
    105         model.state.recentCommits = commitDataList;
    106         updateCommitIndex();
    107         findAndMarkRevertedRevisions(model.state.recentCommits);
    108         callback();
    109     });
    110 };
    111 
    112 function updateCommitIndex()
    113 {
    114     model.state.recentCommits.forEach(function(commitData) {
    115         g_commitIndex[commitData.revision] = commitData;
    116     });
    117 }
    118 
    119 model.commitDataListForRevisionRange = function(fromRevision, toRevision)
    120 {
    121     var result = [];
    122     for (var revision = fromRevision; revision <= toRevision; ++revision) {
    123         var commitData = g_commitIndex[revision];
    124         if (commitData)
    125             result.push(commitData);
    126     }
    127     return result;
    128 };
    129 
    130 model.buildersInFlightForRevision = function(revision)
    131 {
    132     var builders = {};
    133     Object.keys(model.state.resultsByBuilder).forEach(function(builderName) {
    134         var results = model.state.resultsByBuilder[builderName];
    135         if (parseInt(results.blink_revision) < revision)
    136             builders[builderName] = { actual: 'BUILDING' };
    137     });
    138     return builders;
    139 };
    140 
    141 model.latestRevision = function()
    142 {
    143     return model.state.recentCommits[0].revision;
    144 }
    145 
    146 model.latestRevisionWithNoBuildersInFlight = function()
    147 {
    148     var revision = 0;
    149     Object.keys(model.state.resultsByBuilder).forEach(function(builderName) {
    150         var results = model.state.resultsByBuilder[builderName];
    151         if (!results.blink_revision)
    152             return;
    153         var testedRevision = parseInt(results.blink_revision);
    154         revision = revision ? Math.min(revision, testedRevision) : testedRevision;
    155     });
    156     return revision;
    157 }
    158 
    159 model.latestRevisionByBuilder = function()
    160 {
    161     var revision = {};
    162     Object.keys(model.state.resultsByBuilder).forEach(function(builderName) {
    163         revision[builderName] = model.state.resultsByBuilder[builderName].blink_revision;
    164     });
    165     return revision;
    166 }
    167 
    168 model.updateResultsByBuilder = function(callback)
    169 {
    170     var platformBuilders = config.currentBuilders();
    171     results.fetchResultsByBuilder(Object.keys(platformBuilders), function(resultsByBuilder) {
    172         model.state.resultsByBuilder = resultsByBuilder;
    173         callback();
    174     });
    175 };
    176 
    177 model.analyzeUnexpectedFailures = function(callback, completionCallback)
    178 {
    179     var unexpectedFailures = results.unexpectedFailuresByTest(model.state.resultsByBuilder);
    180 
    181     $.each(model.state.failureAnalysisByTest, function(testName, failureAnalysis) {
    182         if (!(testName in unexpectedFailures))
    183             delete model.state.failureAnalysisByTest[testName];
    184     });
    185 
    186     var tracker = new base.RequestTracker(Object.keys(unexpectedFailures).length, completionCallback);
    187     $.each(unexpectedFailures, function(testName, resultNodesByBuilder) {
    188         var builderNameList = Object.keys(resultNodesByBuilder);
    189         results.unifyRegressionRanges(builderNameList, testName, function(oldestFailingRevision, newestPassingRevision) {
    190             var failureAnalysis = {
    191                 'testName': testName,
    192                 'resultNodesByBuilder': resultNodesByBuilder,
    193                 'oldestFailingRevision': oldestFailingRevision,
    194                 'newestPassingRevision': newestPassingRevision,
    195             };
    196 
    197             heuristicallyNarrowRegressionRange(failureAnalysis);
    198 
    199             var previousFailureAnalysis = model.state.failureAnalysisByTest[testName];
    200             if (previousFailureAnalysis
    201                 && previousFailureAnalysis.oldestFailingRevision <= failureAnalysis.oldestFailingRevision
    202                 && previousFailureAnalysis.newestPassingRevision >= failureAnalysis.newestPassingRevision) {
    203                 failureAnalysis.oldestFailingRevision = previousFailureAnalysis.oldestFailingRevision;
    204                 failureAnalysis.newestPassingRevision = previousFailureAnalysis.newestPassingRevision;
    205             }
    206 
    207             model.state.failureAnalysisByTest[testName] = failureAnalysis;
    208 
    209             callback(failureAnalysis);
    210             tracker.requestComplete();
    211         });
    212     });
    213 };
    214 
    215 model.unexpectedFailureInfoForTestName = function(testName)
    216 {
    217     var resultsByTest = results.unexpectedFailuresByTest(model.state.resultsByBuilder);
    218 
    219     return Object.keys(resultsByTest[testName]).map(function(builderName) {
    220         return results.failureInfoForTestAndBuilder(resultsByTest, testName, builderName);
    221     });
    222 };
    223 
    224 model.analyzeexpectedFailures = function(callback)
    225 {
    226     var expectedFailures = results.expectedFailuresByTest(model.state.resultsByBuilder);
    227     $.each(expectedFailures, function(testName, resultNodesByBuilder) {
    228         var failureAnalysis = {
    229             'testName': testName,
    230             'resultNodesByBuilder': resultNodesByBuilder,
    231         };
    232 
    233         // FIXME: Consider looking at the history to see how long this test
    234         // has been failing.
    235 
    236         callback(failureAnalysis);
    237     });
    238 };
    239 
    240 })();
    241