Changeset 90879 in webkit


Ignore:
Timestamp:
Jul 12, 2011 7:34:54 PM (13 years ago)
Author:
abarth@webkit.org
Message:

garden-o-matic should display how many times we've seen a failure
https://bugs.webkit.org/show_bug.cgi?id=64417

Reviewed by Ojan Vafai.

This patch adds some UI to display how many times we've seen a given
failure, which can be helpful for determining whether that failure is a
real failure or a flaky test.

When a failure has only been seen once (i.e., only a single run on a
single bot), we set the opacity of to 50% to avoid distracting the
gardener.

This patch also refactors the failure walker to have a simpler API
internally by moving from an object-oriented paradigm to a functional
paradigm.

  • Scripts/webkitpy/tool/servers/data/gardeningserver/main.css:
  • Scripts/webkitpy/tool/servers/data/gardeningserver/main.js:
  • Scripts/webkitpy/tool/servers/data/gardeningserver/results.js:
  • Scripts/webkitpy/tool/servers/data/gardeningserver/results_unittests.js:
  • Scripts/webkitpy/tool/servers/data/gardeningserver/ui.js:
  • Scripts/webkitpy/tool/servers/data/gardeningserver/ui_unittests.js:
Location:
trunk/Tools
Files:
7 edited

Legend:

Unmodified
Added
Removed
  • trunk/Tools/ChangeLog

    r90876 r90879  
     12011-07-12  Adam Barth  <abarth@webkit.org>
     2
     3        garden-o-matic should display how many times we've seen a failure
     4        https://bugs.webkit.org/show_bug.cgi?id=64417
     5
     6        Reviewed by Ojan Vafai.
     7
     8        This patch adds some UI to display how many times we've seen a given
     9        failure, which can be helpful for determining whether that failure is a
     10        real failure or a flaky test.
     11
     12        When a failure has only been seen once (i.e., only a single run on a
     13        single bot), we set the opacity of to 50% to avoid distracting the
     14        gardener.
     15
     16        This patch also refactors the failure walker to have a simpler API
     17        internally by moving from an object-oriented paradigm to a functional
     18        paradigm.
     19
     20        * Scripts/webkitpy/tool/servers/data/gardeningserver/main.css:
     21        * Scripts/webkitpy/tool/servers/data/gardeningserver/main.js:
     22        * Scripts/webkitpy/tool/servers/data/gardeningserver/results.js:
     23        * Scripts/webkitpy/tool/servers/data/gardeningserver/results_unittests.js:
     24        * Scripts/webkitpy/tool/servers/data/gardeningserver/ui.js:
     25        * Scripts/webkitpy/tool/servers/data/gardeningserver/ui_unittests.js:
     26
    1272011-07-12  Chris Rogers  <crogers@google.com>
    228
  • trunk/Tools/Scripts/webkitpy/tool/servers/data/gardeningserver/main.css

    r90871 r90879  
    3939.butterbar .hide {
    4040    margin-left: 20px;
     41}
     42
     43/* If we've only seen a given test failure once, we dim it so as not to distract the gardener. */
     44.test[data-failure-count="1"] {
     45    opacity: 0.5;
    4146}
    4247
     
    125130    color: #555;
    126131}
     132
     133.regression .when, .regression .how-many {
     134    padding: 0px 2px;
     135    display: inline-block;
     136}
     137
     138.regression .how-many {
     139    color: #555;
     140}
  • trunk/Tools/Scripts/webkitpy/tool/servers/data/gardeningserver/main.js

    r90871 r90879  
    3030                var builderNameList = base.keys(resultNodesByBuilder);
    3131                results.unifyRegressionRanges(builderNameList, testName, function(oldestFailingRevision, newestPassingRevision) {
    32                     $('.when', regressions).append(ui.summarizeRegressionRange(oldestFailingRevision, newestPassingRevision));
     32                    $('.when', testSummary).append(ui.summarizeRegressionRange(oldestFailingRevision, newestPassingRevision));
     33                });
     34                results.countFailureOccurances(builderNameList, testName, function(failureCount) {
     35                    $(testSummary).attr('data-failure-count', failureCount);
     36                    $('.how-many', testSummary).text(ui.failureCount(failureCount));
    3337                });
    3438            });
  • trunk/Tools/Scripts/webkitpy/tool/servers/data/gardeningserver/results.js

    r90871 r90879  
    139139function isUnexpectedFailure(resultNode)
    140140{
     141    if (!resultNode)
     142        return false;
    141143    if (anyIsSuccess(resultNode.actual.split(' ')))
    142144        return false;
     
    183185};
    184186
    185 function TestHistoryWalker(builderName, testName)
    186 {
    187     this._builderName = builderName;
    188     this._testName = testName;
    189     this._indexOfNextKeyToFetch = 0;
    190     this._keyList = [];
    191 }
    192 
    193 TestHistoryWalker.prototype.init = function(callback)
    194 {
    195     var self = this;
    196 
    197     base.jsonp(directoryOfResultsSummaryURL(self._builderName, kResultsName), function(keyList) {
    198         self._keyList = keyList.map(function (element) { return element.key; });
    199         callback();
    200     });
    201 };
    202 
    203 TestHistoryWalker.prototype._fetchNextResultNode = function(callback)
    204 {
    205     var self = this;
    206 
    207     if (self._indexOfNextKeyToFetch >= self._keyList) {
    208         callback(0, null);
    209         return;
     187function walkHistory(builderName, testName, callback)
     188{
     189    var indexOfNextKeyToFetch = 0;
     190    var keyList = [];
     191
     192    function continueWalk()
     193    {
     194        if (indexOfNextKeyToFetch >= keyList.length) {
     195            processResultNode(0, null);
     196            return;
     197        }
     198
     199        var key = keyList[indexOfNextKeyToFetch];
     200        ++indexOfNextKeyToFetch;
     201        g_resultsCache.get(key, function(resultsTree) {
     202            var resultNode = results.resultNodeForTest(resultsTree, testName);
     203            var revision = parseInt(resultsTree['revision'])
     204            if (isNaN(revision))
     205                revision = 0;
     206            processResultNode(revision, resultNode);
     207        });
    210208    }
    211209
    212     var key = self._keyList[self._indexOfNextKeyToFetch];
    213     ++self._indexOfNextKeyToFetch;
    214     g_resultsCache.get(key, function(resultsTree) {
    215         var resultNode = results.resultNodeForTest(resultsTree, self._testName);
    216         var revision = parseInt(resultsTree['revision'])
    217         if (isNaN(revision))
    218             revision = 0;
    219         callback(revision, resultNode);
    220     });
    221 };
    222 
    223 TestHistoryWalker.prototype.walkHistory = function(callback)
    224 {
    225     var self = this;
    226     self._fetchNextResultNode(function(revision, resultNode) {
     210    function processResultNode(revision, resultNode)
     211    {
    227212        var shouldContinue = callback(revision, resultNode);
    228213        if (!shouldContinue)
    229214            return;
    230         self.walkHistory(callback);
     215        continueWalk();
     216    }
     217
     218    base.jsonp(directoryOfResultsSummaryURL(builderName, kResultsName), function(directory) {
     219        keyList = directory.map(function (element) { return element.key; });
     220        continueWalk();
    231221    });
    232222}
     
    237227    var newestPassingRevision = 0;
    238228
    239     var historyWalker = new TestHistoryWalker(builderName, testName);
    240     historyWalker.init(function() {
    241         historyWalker.walkHistory(function(revision, resultNode) {
    242             if (!resultNode) {
    243                 newestPassingRevision = revision;
    244                 callback(oldestFailingRevision, newestPassingRevision);
    245                 return false;
    246             }
    247             if (isUnexpectedFailure(resultNode)) {
    248                 oldestFailingRevision = revision;
    249                 return true;
    250             }
    251             if (!oldestFailingRevision)
    252                 return true;  // We need to keep looking for a failing revision.
     229    walkHistory(builderName, testName, function(revision, resultNode) {
     230        if (!resultNode) {
    253231            newestPassingRevision = revision;
    254232            callback(oldestFailingRevision, newestPassingRevision);
    255233            return false;
    256         });
     234        }
     235        if (isUnexpectedFailure(resultNode)) {
     236            oldestFailingRevision = revision;
     237            return true;
     238        }
     239        if (!oldestFailingRevision)
     240            return true;  // We need to keep looking for a failing revision.
     241        newestPassingRevision = revision;
     242        callback(oldestFailingRevision, newestPassingRevision);
     243        return false;
    257244    });
    258245};
     
    298285                callback(mergedRange.oldestFailingRevision, mergedRange.newestPassingRevision);
    299286            }
     287        });
     288    });
     289};
     290
     291results.countFailureOccurances = function(builderNameList, testName, callback)
     292{
     293    var queriesInFlight = builderNameList.length;
     294    if (!queriesInFlight)
     295        callback(0);
     296
     297    var failureCount = 0;
     298    $.each(builderNameList, function(index, builderName) {
     299        walkHistory(builderName, testName, function(revision, resultNode) {
     300            if (isUnexpectedFailure(resultNode)) {
     301                ++failureCount;
     302                return true;
     303            }
     304
     305            --queriesInFlight;
     306            if (!queriesInFlight)
     307                callback(failureCount);
     308            return false;
    300309        });
    301310    });
  • trunk/Tools/Scripts/webkitpy/tool/servers/data/gardeningserver/results_unittests.js

    r90866 r90879  
    138138}
    139139
    140 test("regressionRangeForFailure", 5, function() {
     140test("walkHistory", 6, function() {
    141141    var simulator = new NetworkSimulator();
    142142
     
    246246            equals(newestPassingRevision, 90425);
    247247        });
     248
     249        results.countFailureOccurances(["Mock Builder", "Another Builder"], "userscripts/another-test.html", function(failureCount) {
     250            equals(failureCount, 4);
     251        });
    248252    });
    249253});
  • trunk/Tools/Scripts/webkitpy/tool/servers/data/gardeningserver/ui.js

    r90871 r90879  
    3434          '<ul class="where"></ul>' +
    3535          '<div class="when"></div>' +
     36          '<div class="how-many"></div>' +
    3637        '</div>');
    3738    $('.what a', block).text(testName).attr('href', ui.urlForTest(testName)).attr('class', unexpectedResults.join(' '));
     
    6263};
    6364
     65ui.failureCount = function(failureCount)
     66{
     67    if (failureCount < 1)
     68        return '';
     69    if (failureCount == 1)
     70        return '(Seen once.)';
     71    return '(Seen ' + failureCount + ' times.)';
     72};
     73
    6474ui.results = function(resultsURLs)
    6575{
  • trunk/Tools/Scripts/webkitpy/tool/servers/data/gardeningserver/ui_unittests.js

    r90871 r90879  
    3939});
    4040
     41test("failureCount", 4, function() {
     42    equal(ui.failureCount(0), '');
     43    equal(ui.failureCount(1), '(Seen once.)');
     44    equal(ui.failureCount(2), '(Seen 2 times.)');
     45    equal(ui.failureCount(3), '(Seen 3 times.)');
     46});
     47
    4148test("results", 1, function() {
    4249    var testResults = ui.results([
Note: See TracChangeset for help on using the changeset viewer.