Title: [91241] trunk/Tools
Revision
91241
Author
[email protected]
Date
2011-07-19 00:38:05 -0700 (Tue, 19 Jul 2011)

Log Message

garden-o-matic should have a "Triage Failures" button for iterating over failures
https://bugs.webkit.org/show_bug.cgi?id=64769

Reviewed by Eric Seidel.

This patch adds some global static state and refactors things a bit so
that we can iterate over all the failures in the details pane.

* Scripts/webkitpy/tool/servers/data/gardeningserver/base.js:
    - Add a generic callback iterator to iterate through a series of
      callbacks. We use this to iterate through the failures we want to
      display the details of.
* Scripts/webkitpy/tool/servers/data/gardeningserver/index.html:
    - Add a toolbar for the results summary and change the buttons to
      be real buttons.
* Scripts/webkitpy/tool/servers/data/gardeningserver/main.css:
    - Make the detail pane 75% of the window.
    - Make the buttons pretty.
    - Allow the failure type badges to apply to the details pane as well.
* Scripts/webkitpy/tool/servers/data/gardeningserver/main.js:
    - Restructure how we bring up the details pane so we can iterate
      through a bunch of failures.
    - Remove the transition between results details because it's
      annoying when you want to click through a bunch of failures.
* Scripts/webkitpy/tool/servers/data/gardeningserver/ui.js:
    - Add some more structure to the title bar for the results details
      so it's clearer which results we're currently displaying.

Modified Paths

Diff

Modified: trunk/Tools/ChangeLog (91240 => 91241)


--- trunk/Tools/ChangeLog	2011-07-19 07:37:01 UTC (rev 91240)
+++ trunk/Tools/ChangeLog	2011-07-19 07:38:05 UTC (rev 91241)
@@ -1,5 +1,35 @@
 2011-07-19  Adam Barth  <[email protected]>
 
+        garden-o-matic should have a "Triage Failures" button for iterating over failures
+        https://bugs.webkit.org/show_bug.cgi?id=64769
+
+        Reviewed by Eric Seidel.
+
+        This patch adds some global static state and refactors things a bit so
+        that we can iterate over all the failures in the details pane.
+
+        * Scripts/webkitpy/tool/servers/data/gardeningserver/base.js:
+            - Add a generic callback iterator to iterate through a series of
+              callbacks. We use this to iterate through the failures we want to
+              display the details of.
+        * Scripts/webkitpy/tool/servers/data/gardeningserver/index.html:
+            - Add a toolbar for the results summary and change the buttons to
+              be real buttons.
+        * Scripts/webkitpy/tool/servers/data/gardeningserver/main.css:
+            - Make the detail pane 75% of the window.
+            - Make the buttons pretty.
+            - Allow the failure type badges to apply to the details pane as well.
+        * Scripts/webkitpy/tool/servers/data/gardeningserver/main.js:
+            - Restructure how we bring up the details pane so we can iterate
+              through a bunch of failures.
+            - Remove the transition between results details because it's
+              annoying when you want to click through a bunch of failures.
+        * Scripts/webkitpy/tool/servers/data/gardeningserver/ui.js:
+            - Add some more structure to the title bar for the results details
+              so it's clearer which results we're currently displaying.
+
+2011-07-19  Adam Barth  <[email protected]>
+
         garden-o-matic should live update as conditions change on the buildbot
         https://bugs.webkit.org/show_bug.cgi?id=64646
 

Modified: trunk/Tools/Scripts/webkitpy/tool/servers/data/gardeningserver/base.js (91240 => 91241)


--- trunk/Tools/Scripts/webkitpy/tool/servers/data/gardeningserver/base.js	2011-07-19 07:37:01 UTC (rev 91240)
+++ trunk/Tools/Scripts/webkitpy/tool/servers/data/gardeningserver/base.js	2011-07-19 07:38:05 UTC (rev 91241)
@@ -99,16 +99,51 @@
 
 base.RequestTracker = function(requestsInFlight, callback, args)
 {
-    this.m_requestsInFlight = requestsInFlight;
-    this.m_callback = callback;
-    this.m_args = args || [];
+    this._requestsInFlight = requestsInFlight;
+    this._callback = callback;
+    this._args = args || [];
 };
 
 base.RequestTracker.prototype.requestComplete = function()
 {
-    --this.m_requestsInFlight;
-    if (!this.m_requestsInFlight)
-        this.m_callback.apply(null, this.m_args);
+    --this._requestsInFlight;
+    if (!this._requestsInFlight)
+        this._callback.apply(null, this._args);
 };
 
+base.CallbackIterator = function(callback, listOfArgumentArrays)
+{
+    this._callback = callback;
+    this._nextIndex = 0;
+    this._listOfArgumentArrays = listOfArgumentArrays;
+};
+
+base.CallbackIterator.prototype.hasNext = function()
+{
+    return this._nextIndex < this._listOfArgumentArrays.length;
+};
+
+base.CallbackIterator.prototype.hasPrevious = function()
+{
+    return this._nextIndex - 2 >= 0;
+};
+
+base.CallbackIterator.prototype.callNext = function()
+{
+    if (!this.hasNext())
+        return;
+    var args = this._listOfArgumentArrays[this._nextIndex];
+    this._nextIndex++;
+    this._callback.apply(null, args);
+};
+
+base.CallbackIterator.prototype.callPrevious = function()
+{
+    if (!this.hasPrevious())
+        return;
+    var args = this._listOfArgumentArrays[this._nextIndex - 2];
+    this._nextIndex--;
+    this._callback.apply(null, args);
+};
+
 })();

Modified: trunk/Tools/Scripts/webkitpy/tool/servers/data/gardeningserver/base_unittests.js (91240 => 91241)


--- trunk/Tools/Scripts/webkitpy/tool/servers/data/gardeningserver/base_unittests.js	2011-07-19 07:37:01 UTC (rev 91240)
+++ trunk/Tools/Scripts/webkitpy/tool/servers/data/gardeningserver/base_unittests.js	2011-07-19 07:38:05 UTC (rev 91241)
@@ -72,6 +72,38 @@
     tracker.requestComplete();
 });
 
+test("CallbackIterator", 22, function() {
+    var expected = 0;
+    var iterator = new base.CallbackIterator(function(a, b) {
+        equals(a, 'ArgA' + expected);
+        equals(b, 'ArgB' + expected);
+        ++expected;
+    }, [
+        ['ArgA0', 'ArgB0'],
+        ['ArgA1', 'ArgB1'],
+        ['ArgA2', 'ArgB2'],
+    ]);
+    ok(iterator.hasNext())
+    ok(!iterator.hasPrevious())
+    iterator.callNext();
+    ok(iterator.hasNext())
+    ok(!iterator.hasPrevious())
+    iterator.callNext();
+    ok(iterator.hasNext())
+    ok(iterator.hasPrevious())
+    iterator.callNext();
+    ok(!iterator.hasNext())
+    ok(iterator.hasPrevious())
+    expected = 1;
+    iterator.callPrevious();
+    ok(iterator.hasNext())
+    ok(iterator.hasPrevious())
+    expected = 0;
+    iterator.callPrevious();
+    ok(iterator.hasNext())
+    ok(!iterator.hasPrevious())
+});
+
 test("filterTree", 2, function() {
     var tree = {
         'path': {

Modified: trunk/Tools/Scripts/webkitpy/tool/servers/data/gardeningserver/index.html (91240 => 91241)


--- trunk/Tools/Scripts/webkitpy/tool/servers/data/gardeningserver/index.html	2011-07-19 07:37:01 UTC (rev 91240)
+++ trunk/Tools/Scripts/webkitpy/tool/servers/data/gardeningserver/index.html	2011-07-19 07:38:05 UTC (rev 91241)
@@ -12,14 +12,21 @@
 <body>
 <div class="butterbar"><span class="status">Loading...</span></div>
 <div class="alert"><span class="status"></span></div>
-<div class="results"></div>
+<div class="results">
+    <div class="content"></div>
+    <div class="toolbar">
+        <div class="actions">
+            <button class="triage">Triage Failures</button>
+        </div>
+    </div>
+</div>
 <div class="results-detail">
     <div class="toolbar">
         <div class="actions">
-            <a class="rebaseline" href=""
-            <a class="dismiss" href=""
+            <button class="rebaseline default">Rebaseline</button><button class="previous">&#9664;</button><button class="next">&#9654;</button><button class="dismiss">Close</button>
         </div>
         <div class="status"></div>
+        <div class="clear"></div>
     </div>
     <div class="content"></div>
 </div>

Modified: trunk/Tools/Scripts/webkitpy/tool/servers/data/gardeningserver/main.css (91240 => 91241)


--- trunk/Tools/Scripts/webkitpy/tool/servers/data/gardeningserver/main.css	2011-07-19 07:37:01 UTC (rev 91240)
+++ trunk/Tools/Scripts/webkitpy/tool/servers/data/gardeningserver/main.css	2011-07-19 07:38:05 UTC (rev 91241)
@@ -7,7 +7,6 @@
 
 a {
     color: Black;
-    font-weight: bold;
 }
 
 table {
@@ -19,7 +18,7 @@
 td, th {
     overflow: hidden;
     vertical-align: top;
-    border-right: 1px solid #CCC;
+    border-right: 1px solid #c6c6c6;
 }
 
 td:last-of-type, th:last-of-type {
@@ -27,13 +26,74 @@
 }
 
 th {
-    border-bottom: 1px solid #CCC;
+    border-bottom: 1px solid #c6c6c6;
 }
 
+button {
+    border-radius: 2px;
+    background-image: -webkit-linear-gradient(top,#f5f5f5,#f1f1f1);
+    border: 1px solid rgba(0, 0, 0, 0.1);
+    border-radius: 2px;
+    color: #666;
+    cursor: pointer;
+    font-size: 11px;
+    font-weight: bold;
+    height: 29px;
+    line-height: 27px;
+    margin: 11px 6px;
+    min-width: 54px;
+    padding: 0 8px;
+    text-align: center
+}
+
+button.next {
+    margin-left: 0px;
+}
+
+button.previous {
+    margin-right: 0px;
+}
+
+button[disabled] {
+    visibility: hidden;
+}
+
+button:hover {
+    background-image: -webkit-linear-gradient(top,#f8f8f8,#f1f1f1);
+    -webkit-box-shadow: 0 1px 1px rgba(0,0,0,0.1);
+    background-color: #f8f8f8;
+    background-image: linear-gradient(top,#f8f8f8,#f1f1f1);
+    border: 1px solid #c6c6c6;
+    box-shadow: 0 1px 1px rgba(0,0,0,0.1);
+    color: #333
+}
+
+button:focus {
+    border: 1px solid #4d90fe;
+    outline: none
+}
+
+button.default {
+    border: 1px solid #3079ED;
+    color: white;
+    background-image: -webkit-linear-gradient(top,#4d90fe,#4787ed);
+}
+
+button.default:hover {
+    border: 1px solid #2f5bb7;
+    color: white;
+    background-color: #357ae8;
+    background-image: -webkit-gradient(linear,left top,left bottom,from(#4d90fe),to(#357ae8));
+}
+
+.clear {
+    clear: both;
+}
+
 /*** butterbar ***/
 
 .butterbar {
-    background-color: #f3f3f3;
+    background-color: #f5f5f5;
     padding: 5px;
     position: fixed;
     top: 0px;
@@ -76,11 +136,16 @@
 
 /*** results-summary ***/
 
+.results .toolbar {
+    text-align: center;
+    display: none;
+}
+
 table.results-summary {
     width: auto;
     table-layout: auto;
     margin: 5px auto;
-    border: 1px solid #CCC;
+    border: 1px solid #c6c6c6;
 }
 
 .results-summary td, .results-summary th {
@@ -113,8 +178,9 @@
     color: black;
 }
 
-.results-summary .what a[draggable].IMAGE\+TEXT::after {
+.test-name.IMAGE\+TEXT::after {
     content: 'IMAGE+TEXT';
+    font-weight: normal;
     color: white;
     background-color: Indigo;
     font-size: 10px;
@@ -123,8 +189,9 @@
     border-radius: 4px;
 }
 
-.results-summary .what a[draggable].IMAGE::after {
+.test-name.IMAGE::after {
     content: 'IMAGE';
+    font-weight: normal;
     color: white;
     background-color: MidnightBlue;
     font-size: 10px;
@@ -133,8 +200,9 @@
     border-radius: 4px;
 }
 
-.results-summary .what a[draggable].TIMEOUT::after {
+.test-name.TIMEOUT::after {
     content: 'TIMEOUT';
+    font-weight: normal;
     color: white;
     background-color: DarkGoldenRod;
     font-size: 10px;
@@ -143,8 +211,9 @@
     border-radius: 4px;
 }
 
-.results-summary .what a[draggable].TEXT::after {
+.test-name.TEXT::after {
     content: 'TEXT';
+    font-weight: normal;
     color: white;
     background-color: DodgerBlue;
     font-size: 10px;
@@ -153,8 +222,9 @@
     border-radius: 4px;
 }
 
-.results-summary .what a[draggable].CRASH::after {
+.test-name.CRASH::after {
     content: 'CRASH';
+    font-weight: normal;
     color: white;
     background-color: Tomato;
     font-size: 10px;
@@ -174,34 +244,38 @@
 .results-detail {
     display: none;
     position: fixed;
-    background-color: #f3f3f3;
-    top: 50%;
+    background-color: #f5f5f5;
+    top: 25%;
     left: 0px;
     right: 0px;
     bottom: 0px;
 }
 
 .results-detail .toolbar {
-    border-top: 1px solid #CCC;
-    border-bottom: 1px solid #CCC;
+    border-top: 1px solid #c6c6c6;
+    border-bottom: 1px solid #c6c6c6;
 }
 
 .results-detail .toolbar .status {
-    display: inline-block;
-    padding: 5px;
+    font-size: 11px;
+    font-weight: bold;
+    height: 29px;
+    line-height: 27px;
+    margin: 11px 6px;
+    padding: 0 8px;
 }
 
-.results-detail .toolbar .actions {
-    float: right;
+.results-detail .toolbar .status .builder-name {
+    font-weight: normal;
+    padding-left: 20px;
 }
 
-.results-detail .toolbar .actions a {
-    display: inline-block;
-    padding: 5px 5px 5px 0px;
+.results-detail .toolbar .status .selected {
+    font-style: italic;
 }
 
-.results-detail .toolbar {
-    position: relative;
+.results-detail .toolbar .actions {
+    float: right;
 }
 
 .results-detail .content {

Modified: trunk/Tools/Scripts/webkitpy/tool/servers/data/gardeningserver/main.js (91240 => 91241)


--- trunk/Tools/Scripts/webkitpy/tool/servers/data/gardeningserver/main.js	2011-07-19 07:37:01 UTC (rev 91240)
+++ trunk/Tools/Scripts/webkitpy/tool/servers/data/gardeningserver/main.js	2011-07-19 07:38:05 UTC (rev 91241)
@@ -1,6 +1,7 @@
 (function() {
 
 var g_updateTimerId = 0;
+var g_resultsDetailsIterator = null;
 
 var kBuildFailedAlertType = 'build-failed';
 
@@ -12,7 +13,7 @@
 function displayOnButterbar(message)
 {
     $('.butterbar .status').text(message);
-    $('.butterbar').fadeIn();
+    $('.butterbar').fadeIn('fast');
 }
 
 function hideAlert()
@@ -47,16 +48,24 @@
     $('#favicon').attr('href', faviconURL);
 }
 
+function toggleButton(button, isEnabled)
+{
+    if (isEnabled)
+        button.removeAttr('disabled');
+    else
+        button.attr('disabled', true)
+}
+
 function togglePartyTime(hasFailures)
 {
     if (!hasFailures) {
-        $('.results').text('No failures. Party time!');
+        $('.results .content').text('No failures. Party time!');
         var partyTime = $('<div class="partytime"><img src=""
-        $('.results').append(partyTime);
+        $('.results .content').append(partyTime);
         partyTime.fadeIn(1200).delay(7000).fadeOut();
         return;
     }
-    $('.results').empty();
+    $('.results .content').empty();
 }
 
 function ensureResultsSummaryContainer()
@@ -65,7 +74,7 @@
     if (container.length)
         return container;
     container = ui.regressionsContainer();
-    $('.results').append(container);
+    $('.results .content').append(container);
     return container;
 }
 
@@ -117,6 +126,7 @@
         var newTestSummaries = $();
         var requestTracker = new base.RequestTracker(base.keys(unexpectedFailures).length, function() {
             newTestSummaries.fadeIn();
+            $('.results .toolbar').fadeIn();
             callback()
         });
 
@@ -136,61 +146,106 @@
     });
 }
 
-function showResultsDetail()
+function showResultsDetail(testName, selectedBuilderName, failureTypeListByBuilder)
 {
-    var testSummary = $(this).parents('.test');
-    var builderName = $(this).attr(config.kBuilderNameAttr);
-    var testName = testSummary.attr(config.kTestNameAttr);
+    var builderNameList = base.keys(failureTypeListByBuilder);
+    var failureTypeList = failureTypeListByBuilder[selectedBuilderName];
+    var failureTypes = failureTypeList.join(' ');
 
-    // FIXME: It's lame that we have two different representations of multiple failure types.
-    var failureTypes = testSummary.attr(config.kFailureTypesAttr);
-    var failureTypeList = failureTypes.split(' ');
+    var content = $('.results-detail .content');
 
-    var content = $('.results-detail .content');
-    if ($('.failure-details', content).attr(config.kBuilderNameAttr) == builderName &&
+    if ($('.failure-details', content).attr(config.kBuilderNameAttr) == selectedBuilderName &&
         $('.failure-details', content).attr(config.kTestNameAttr) == testName &&
         $('.failure-details', content).attr(config.kFailureTypesAttr) == failureTypes)
         return;
 
     displayOnButterbar('Loading...');
 
-    results.fetchResultsURLs(builderName, testName, failureTypeList, function(resultsURLs) {
+    results.fetchResultsURLs(selectedBuilderName, testName, failureTypeList, function(resultsURLs) {
         var status = $('.results-detail .toolbar .status');
 
-        function updateResults()
-        {
-            status.text(testName + ' [' + builderName + ']');
-            content.empty();
-            content.append(ui.failureDetails(resultsURLs));
-            $('.results-detail .actions .rebaseline').toggle(results.canRebaseline(failureTypeList));
-            $('.failure-details', content).attr(config.kBuilderNameAttr, builderName);
-            $('.failure-details', content).attr(config.kTestNameAttr, testName);
-            $('.failure-details', content).attr(config.kFailureTypesAttr, failureTypes);
-        }
+        status.empty();
+        content.empty();
 
-        var children = content.children();
-        if (children.length && $('.results-detail').is(":visible")) {
-            // The results-detail pane is already open. Let's do a quick cross-fade.
-            status.fadeOut('fast');
-            children.fadeOut('fast', function() {
-                updateResults();
-                status.fadeIn('fast');
-                content.children().hide().fadeIn('fast', dismissButterbar);
-            });
-        } else {
-            updateResults();
+        status.append(ui.failureDetailsStatus(testName, selectedBuilderName, failureTypes, builderNameList));
+        content.append(ui.failureDetails(resultsURLs));
+
+        toggleButton($('.results-detail .actions .next'), g_resultsDetailsIterator.hasNext());
+        toggleButton($('.results-detail .actions .previous'), g_resultsDetailsIterator.hasPrevious());
+        toggleButton($('.results-detail .actions .rebaseline'), results.canRebaseline(failureTypeList));
+
+        $('.failure-details', content).attr(config.kBuilderNameAttr, selectedBuilderName);
+        $('.failure-details', content).attr(config.kTestNameAttr, testName);
+        $('.failure-details', content).attr(config.kFailureTypesAttr, failureTypes);
+
+        if (!$('.results-detail').is(":visible"))
             $('.results-detail').fadeIn('fast', dismissButterbar);
-        }
+        else
+            dismissButterbar();
     });
 }
 
 function hideResultsDetail()
 {
     $('.results-detail').fadeOut('fast', function() {
+        $('.results-detail .status').empty();
         $('.results-detail .content').empty();
+        // Strictly speaking, we don't need to clear g_resultsDetailsIterator,
+        // but doing so helps the garbage collector free memory.
+        g_resultsDetailsIterator = null;
     });
 }
 
+function nextResultsDetail()
+{
+    g_resultsDetailsIterator.callNext();
+}
+
+function previousResultsDetail()
+{
+    g_resultsDetailsIterator.callPrevious();
+}
+
+function resultsDetailArgumentsForBuilderElement(builderBlock)
+{
+    var selectedBuilderName = builderBlock.attr(config.kBuilderNameAttr);
+
+    var testSummary = builderBlock.parents('.test');
+    var testName = testSummary.attr(config.kTestNameAttr);
+
+    // FIXME: It's lame that we have two different representations of multiple failure types.
+    var failureTypes = testSummary.attr(config.kFailureTypesAttr);
+    var failureTypeList = failureTypes.split(' ');
+
+    var failureTypeListByBuilder = {}
+    $('.where .builder-name', testSummary).each(function() {
+        var builderName = $(this).attr(config.kBuilderNameAttr);
+        // FIXME: We should understand that tests can fail in different ways
+        // on different builders.
+        failureTypeListByBuilder[builderName] = failureTypeList;
+    });
+
+    return [testName, selectedBuilderName, failureTypeListByBuilder];
+}
+
+function prepareResultsDetailsIterator(query)
+{
+    var listOfResultsDetailArguments = [];
+
+    query.each(function() {
+        var resultsDetailArguments = resultsDetailArgumentsForBuilderElement($(this));
+        listOfResultsDetailArguments.push(resultsDetailArguments);
+    });
+
+    return new base.CallbackIterator(showResultsDetail, listOfResultsDetailArguments);
+}
+
+function triageFailures()
+{
+    g_resultsDetailsIterator = prepareResultsDetailsIterator($('.results .test .builder-name'));
+    g_resultsDetailsIterator.callNext();
+}
+
 function rebaselineResults()
 {
     var failureDetails = $('.failure-details', $(this).parents('.results-detail'));
@@ -222,9 +277,11 @@
     checkBuilderStatuses();
 }
 
-$('.results-summary .where a').live('click', showResultsDetail);
+$('.results .toolbar .triage').live('click', triageFailures);
+$('.results-detail .actions .next').live('click', nextResultsDetail);
+$('.results-detail .actions .previous').live('click', previousResultsDetail);
+$('.results-detail .actions .rebaseline').live('click', rebaselineResults);
 $('.results-detail .actions .dismiss').live('click', hideResultsDetail);
-$('.results-detail .actions .rebaseline').live('click', rebaselineResults);
 
 $(document).ready(function() {
     g_updateTimerId = window.setInterval(update, config.kUpdateFrequency);

Modified: trunk/Tools/Scripts/webkitpy/tool/servers/data/gardeningserver/ui.js (91240 => 91241)


--- trunk/Tools/Scripts/webkitpy/tool/servers/data/gardeningserver/ui.js	2011-07-19 07:37:01 UTC (rev 91240)
+++ trunk/Tools/Scripts/webkitpy/tool/servers/data/gardeningserver/ui.js	2011-07-19 07:38:05 UTC (rev 91241)
@@ -48,20 +48,20 @@
     var unexpectedResults = results.collectUnexpectedResults(resultNodesByBuilder);
     var block = $(
         '<tr class="test">' +
-          '<td class="what"><a draggable></a></td>' +
+          '<td class="what"><a class="test-name"></a></td>' +
           '<td class="where"><ul></ul></td>' +
           '<td class="when"></td>' +
           '<td class="how-many"></td>' +
         '</tr>');
-    $('.what a', block).text(testName).attr('href', ui.urlForTest(testName)).attr('class', unexpectedResults.join(' '));
+    $('.test-name', block).text(testName).attr('href', ui.urlForTest(testName)).addClass(unexpectedResults.join(' '));
     block.attr(config.kTestNameAttr, testName);
     block.attr(config.kFailureTypesAttr, unexpectedResults);
 
     var where = $('.where', block);
     $.each(resultNodesByBuilder, function(builderName, resultNode) {
-        var listElement = $('<li><a href=""
+        var listElement = $('<li class="builder-name"></li>');
+        listElement.attr(config.kBuilderNameAttr, builderName).text(displayNameForBuilder(builderName));
         where.append(listElement);
-        $('a', listElement).attr(config.kBuilderNameAttr, builderName).text(displayNameForBuilder(builderName));
     });
 
     return block;
@@ -79,7 +79,7 @@
         displayNameForRevision(impliedFirstFailingRevision) :
         displayNameForRevision(impliedFirstFailingRevision) + '-' + displayNameForRevision(oldestFailingRevision);
 
-    var block = $('<div class="regression-range"><a></a></div>');
+    var block = $('<div class="regression-range"><a target="_blank"></a></div>');
     $('a', block).attr('href', href).text(text)
     return block;
 };
@@ -107,6 +107,23 @@
     return 'Seen ' + failureCount + ' times.';
 };
 
+ui.failureDetailsStatus = function(testName, selectedBuilderName, failureTypes, builderNameList)
+{
+    var block = $('<span><span class="test-name"></span><span class="builder-list"></span></span>');
+    $('.test-name', block).addClass(failureTypes).text(testName);
+
+    var builderList = $('.builder-list', block);
+    $.each(builderNameList, function(index, builderName) {
+        var builder = $('<span class="builder-name"></span>')
+        builder.text(builderName);
+        if (builderName == selectedBuilderName)
+            builder.addClass('selected');
+        builderList.append(builder);
+    });
+
+    return block;
+};
+
 ui.failureDetails = function(resultsURLs)
 {
     var block = $('<table class="failure-details"><tbody><tr></tr></tbody></table>');

Modified: trunk/Tools/Scripts/webkitpy/tool/servers/data/gardeningserver/ui_unittests.js (91240 => 91241)


--- trunk/Tools/Scripts/webkitpy/tool/servers/data/gardeningserver/ui_unittests.js	2011-07-19 07:37:01 UTC (rev 91240)
+++ trunk/Tools/Scripts/webkitpy/tool/servers/data/gardeningserver/ui_unittests.js	2011-07-19 07:38:05 UTC (rev 91241)
@@ -37,11 +37,11 @@
 
     var summaryWithMultipleRevisions = ui.summarizeRegressionRange(90424, 90426);
     summaryWithMultipleRevisions.wrap('<wrapper></wrapper>');
-    equal(summaryWithMultipleRevisions.parent().html(), '<div class="regression-range"><a href=""
+    equal(summaryWithMultipleRevisions.parent().html(), '<div class="regression-range"><a target="_blank" href=""
 
     var summaryWithOneRevision = ui.summarizeRegressionRange(90425, 90426);
     summaryWithOneRevision.wrap('<wrapper></wrapper>');
-    equal(summaryWithOneRevision.parent().html(), '<div class="regression-range"><a href=""
+    equal(summaryWithOneRevision.parent().html(), '<div class="regression-range"><a target="_blank" href=""
 });
 
 test("failureCount", 4, function() {
@@ -61,6 +61,19 @@
         '</ul></div>');
 });
 
+test("failureDetailsStatus", 1, function() {
+    var status = ui.failureDetailsStatus('userscripts/another-test.html', 'Mock Builder', 'TEXT', ['Mock Builder', 'Another Builder']);
+    status.wrap('<wrapper></wrapper>');
+    equal(status.parent().html(),
+        '<span>' +
+            '<span class="test-name TEXT">userscripts/another-test.html</span>' +
+            '<span class="builder-list">' +
+                '<span class="builder-name selected">Mock Builder</span>' +
+                '<span class="builder-name">Another Builder</span>' +
+            '</span>' +
+        '</span>');
+});
+
 test("failureDetails", 1, function() {
     var testResults = ui.failureDetails([
         'http://example.com/layout-test-results/foo-bar-diff.txt',
_______________________________________________
webkit-changes mailing list
[email protected]
http://lists.webkit.org/mailman/listinfo.cgi/webkit-changes

Reply via email to