Changeset 91241 in webkit


Ignore:
Timestamp:
Jul 19, 2011 12:38:05 AM (13 years ago)
Author:
abarth@webkit.org
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.
Location:
trunk/Tools
Files:
8 edited

Legend:

Unmodified
Added
Removed
  • trunk/Tools/ChangeLog

    r91240 r91241  
     12011-07-19  Adam Barth  <abarth@webkit.org>
     2
     3        garden-o-matic should have a "Triage Failures" button for iterating over failures
     4        https://bugs.webkit.org/show_bug.cgi?id=64769
     5
     6        Reviewed by Eric Seidel.
     7
     8        This patch adds some global static state and refactors things a bit so
     9        that we can iterate over all the failures in the details pane.
     10
     11        * Scripts/webkitpy/tool/servers/data/gardeningserver/base.js:
     12            - Add a generic callback iterator to iterate through a series of
     13              callbacks. We use this to iterate through the failures we want to
     14              display the details of.
     15        * Scripts/webkitpy/tool/servers/data/gardeningserver/index.html:
     16            - Add a toolbar for the results summary and change the buttons to
     17              be real buttons.
     18        * Scripts/webkitpy/tool/servers/data/gardeningserver/main.css:
     19            - Make the detail pane 75% of the window.
     20            - Make the buttons pretty.
     21            - Allow the failure type badges to apply to the details pane as well.
     22        * Scripts/webkitpy/tool/servers/data/gardeningserver/main.js:
     23            - Restructure how we bring up the details pane so we can iterate
     24              through a bunch of failures.
     25            - Remove the transition between results details because it's
     26              annoying when you want to click through a bunch of failures.
     27        * Scripts/webkitpy/tool/servers/data/gardeningserver/ui.js:
     28            - Add some more structure to the title bar for the results details
     29              so it's clearer which results we're currently displaying.
     30
    1312011-07-19  Adam Barth  <abarth@webkit.org>
    232
  • trunk/Tools/Scripts/webkitpy/tool/servers/data/gardeningserver/base.js

    r91240 r91241  
    100100base.RequestTracker = function(requestsInFlight, callback, args)
    101101{
    102     this.m_requestsInFlight = requestsInFlight;
    103     this.m_callback = callback;
    104     this.m_args = args || [];
     102    this._requestsInFlight = requestsInFlight;
     103    this._callback = callback;
     104    this._args = args || [];
    105105};
    106106
    107107base.RequestTracker.prototype.requestComplete = function()
    108108{
    109     --this.m_requestsInFlight;
    110     if (!this.m_requestsInFlight)
    111         this.m_callback.apply(null, this.m_args);
     109    --this._requestsInFlight;
     110    if (!this._requestsInFlight)
     111        this._callback.apply(null, this._args);
     112};
     113
     114base.CallbackIterator = function(callback, listOfArgumentArrays)
     115{
     116    this._callback = callback;
     117    this._nextIndex = 0;
     118    this._listOfArgumentArrays = listOfArgumentArrays;
     119};
     120
     121base.CallbackIterator.prototype.hasNext = function()
     122{
     123    return this._nextIndex < this._listOfArgumentArrays.length;
     124};
     125
     126base.CallbackIterator.prototype.hasPrevious = function()
     127{
     128    return this._nextIndex - 2 >= 0;
     129};
     130
     131base.CallbackIterator.prototype.callNext = function()
     132{
     133    if (!this.hasNext())
     134        return;
     135    var args = this._listOfArgumentArrays[this._nextIndex];
     136    this._nextIndex++;
     137    this._callback.apply(null, args);
     138};
     139
     140base.CallbackIterator.prototype.callPrevious = function()
     141{
     142    if (!this.hasPrevious())
     143        return;
     144    var args = this._listOfArgumentArrays[this._nextIndex - 2];
     145    this._nextIndex--;
     146    this._callback.apply(null, args);
    112147};
    113148
  • trunk/Tools/Scripts/webkitpy/tool/servers/data/gardeningserver/base_unittests.js

    r91240 r91241  
    7373});
    7474
     75test("CallbackIterator", 22, function() {
     76    var expected = 0;
     77    var iterator = new base.CallbackIterator(function(a, b) {
     78        equals(a, 'ArgA' + expected);
     79        equals(b, 'ArgB' + expected);
     80        ++expected;
     81    }, [
     82        ['ArgA0', 'ArgB0'],
     83        ['ArgA1', 'ArgB1'],
     84        ['ArgA2', 'ArgB2'],
     85    ]);
     86    ok(iterator.hasNext())
     87    ok(!iterator.hasPrevious())
     88    iterator.callNext();
     89    ok(iterator.hasNext())
     90    ok(!iterator.hasPrevious())
     91    iterator.callNext();
     92    ok(iterator.hasNext())
     93    ok(iterator.hasPrevious())
     94    iterator.callNext();
     95    ok(!iterator.hasNext())
     96    ok(iterator.hasPrevious())
     97    expected = 1;
     98    iterator.callPrevious();
     99    ok(iterator.hasNext())
     100    ok(iterator.hasPrevious())
     101    expected = 0;
     102    iterator.callPrevious();
     103    ok(iterator.hasNext())
     104    ok(!iterator.hasPrevious())
     105});
     106
    75107test("filterTree", 2, function() {
    76108    var tree = {
  • trunk/Tools/Scripts/webkitpy/tool/servers/data/gardeningserver/index.html

    r91196 r91241  
    1313<div class="butterbar"><span class="status">Loading...</span></div>
    1414<div class="alert"><span class="status"></span></div>
    15 <div class="results"></div>
     15<div class="results">
     16    <div class="content"></div>
     17    <div class="toolbar">
     18        <div class="actions">
     19            <button class="triage">Triage Failures</button>
     20        </div>
     21    </div>
     22</div>
    1623<div class="results-detail">
    1724    <div class="toolbar">
    1825        <div class="actions">
    19             <a class="rebaseline" href="#">Rebaseline</a>
    20             <a class="dismiss" href="#">Close</a>
     26            <button class="rebaseline default">Rebaseline</button><button class="previous">&#9664;</button><button class="next">&#9654;</button><button class="dismiss">Close</button>
    2127        </div>
    2228        <div class="status"></div>
     29        <div class="clear"></div>
    2330    </div>
    2431    <div class="content"></div>
  • trunk/Tools/Scripts/webkitpy/tool/servers/data/gardeningserver/main.css

    r91240 r91241  
    88a {
    99    color: Black;
    10     font-weight: bold;
    1110}
    1211
     
    2019    overflow: hidden;
    2120    vertical-align: top;
    22     border-right: 1px solid #CCC;
     21    border-right: 1px solid #c6c6c6;
    2322}
    2423
     
    2827
    2928th {
    30     border-bottom: 1px solid #CCC;
     29    border-bottom: 1px solid #c6c6c6;
     30}
     31
     32button {
     33    border-radius: 2px;
     34    background-image: -webkit-linear-gradient(top,#f5f5f5,#f1f1f1);
     35    border: 1px solid rgba(0, 0, 0, 0.1);
     36    border-radius: 2px;
     37    color: #666;
     38    cursor: pointer;
     39    font-size: 11px;
     40    font-weight: bold;
     41    height: 29px;
     42    line-height: 27px;
     43    margin: 11px 6px;
     44    min-width: 54px;
     45    padding: 0 8px;
     46    text-align: center
     47}
     48
     49button.next {
     50    margin-left: 0px;
     51}
     52
     53button.previous {
     54    margin-right: 0px;
     55}
     56
     57button[disabled] {
     58    visibility: hidden;
     59}
     60
     61button:hover {
     62    background-image: -webkit-linear-gradient(top,#f8f8f8,#f1f1f1);
     63    -webkit-box-shadow: 0 1px 1px rgba(0,0,0,0.1);
     64    background-color: #f8f8f8;
     65    background-image: linear-gradient(top,#f8f8f8,#f1f1f1);
     66    border: 1px solid #c6c6c6;
     67    box-shadow: 0 1px 1px rgba(0,0,0,0.1);
     68    color: #333
     69}
     70
     71button:focus {
     72    border: 1px solid #4d90fe;
     73    outline: none
     74}
     75
     76button.default {
     77    border: 1px solid #3079ED;
     78    color: white;
     79    background-image: -webkit-linear-gradient(top,#4d90fe,#4787ed);
     80}
     81
     82button.default:hover {
     83    border: 1px solid #2f5bb7;
     84    color: white;
     85    background-color: #357ae8;
     86    background-image: -webkit-gradient(linear,left top,left bottom,from(#4d90fe),to(#357ae8));
     87}
     88
     89.clear {
     90    clear: both;
    3191}
    3292
     
    3494
    3595.butterbar {
    36     background-color: #f3f3f3;
     96    background-color: #f5f5f5;
    3797    padding: 5px;
    3898    position: fixed;
     
    77137/*** results-summary ***/
    78138
     139.results .toolbar {
     140    text-align: center;
     141    display: none;
     142}
     143
    79144table.results-summary {
    80145    width: auto;
    81146    table-layout: auto;
    82147    margin: 5px auto;
    83     border: 1px solid #CCC;
     148    border: 1px solid #c6c6c6;
    84149}
    85150
     
    114179}
    115180
    116 .results-summary .what a[draggable].IMAGE\+TEXT::after {
     181.test-name.IMAGE\+TEXT::after {
    117182    content: 'IMAGE+TEXT';
     183    font-weight: normal;
    118184    color: white;
    119185    background-color: Indigo;
     
    124190}
    125191
    126 .results-summary .what a[draggable].IMAGE::after {
     192.test-name.IMAGE::after {
    127193    content: 'IMAGE';
     194    font-weight: normal;
    128195    color: white;
    129196    background-color: MidnightBlue;
     
    134201}
    135202
    136 .results-summary .what a[draggable].TIMEOUT::after {
     203.test-name.TIMEOUT::after {
    137204    content: 'TIMEOUT';
     205    font-weight: normal;
    138206    color: white;
    139207    background-color: DarkGoldenRod;
     
    144212}
    145213
    146 .results-summary .what a[draggable].TEXT::after {
     214.test-name.TEXT::after {
    147215    content: 'TEXT';
     216    font-weight: normal;
    148217    color: white;
    149218    background-color: DodgerBlue;
     
    154223}
    155224
    156 .results-summary .what a[draggable].CRASH::after {
     225.test-name.CRASH::after {
    157226    content: 'CRASH';
     227    font-weight: normal;
    158228    color: white;
    159229    background-color: Tomato;
     
    175245    display: none;
    176246    position: fixed;
    177     background-color: #f3f3f3;
    178     top: 50%;
     247    background-color: #f5f5f5;
     248    top: 25%;
    179249    left: 0px;
    180250    right: 0px;
     
    183253
    184254.results-detail .toolbar {
    185     border-top: 1px solid #CCC;
    186     border-bottom: 1px solid #CCC;
     255    border-top: 1px solid #c6c6c6;
     256    border-bottom: 1px solid #c6c6c6;
    187257}
    188258
    189259.results-detail .toolbar .status {
    190     display: inline-block;
    191     padding: 5px;
     260    font-size: 11px;
     261    font-weight: bold;
     262    height: 29px;
     263    line-height: 27px;
     264    margin: 11px 6px;
     265    padding: 0 8px;
     266}
     267
     268.results-detail .toolbar .status .builder-name {
     269    font-weight: normal;
     270    padding-left: 20px;
     271}
     272
     273.results-detail .toolbar .status .selected {
     274    font-style: italic;
    192275}
    193276
    194277.results-detail .toolbar .actions {
    195278    float: right;
    196 }
    197 
    198 .results-detail .toolbar .actions a {
    199     display: inline-block;
    200     padding: 5px 5px 5px 0px;
    201 }
    202 
    203 .results-detail .toolbar {
    204     position: relative;
    205279}
    206280
  • trunk/Tools/Scripts/webkitpy/tool/servers/data/gardeningserver/main.js

    r91240 r91241  
    22
    33var g_updateTimerId = 0;
     4var g_resultsDetailsIterator = null;
    45
    56var kBuildFailedAlertType = 'build-failed';
     
    1314{
    1415    $('.butterbar .status').text(message);
    15     $('.butterbar').fadeIn();
     16    $('.butterbar').fadeIn('fast');
    1617}
    1718
     
    4849}
    4950
     51function toggleButton(button, isEnabled)
     52{
     53    if (isEnabled)
     54        button.removeAttr('disabled');
     55    else
     56        button.attr('disabled', true)
     57}
     58
    5059function togglePartyTime(hasFailures)
    5160{
    5261    if (!hasFailures) {
    53         $('.results').text('No failures. Party time!');
     62        $('.results .content').text('No failures. Party time!');
    5463        var partyTime = $('<div class="partytime"><img src="partytime.gif"></div>');
    55         $('.results').append(partyTime);
     64        $('.results .content').append(partyTime);
    5665        partyTime.fadeIn(1200).delay(7000).fadeOut();
    5766        return;
    5867    }
    59     $('.results').empty();
     68    $('.results .content').empty();
    6069}
    6170
     
    6675        return container;
    6776    container = ui.regressionsContainer();
    68     $('.results').append(container);
     77    $('.results .content').append(container);
    6978    return container;
    7079}
     
    118127        var requestTracker = new base.RequestTracker(base.keys(unexpectedFailures).length, function() {
    119128            newTestSummaries.fadeIn();
     129            $('.results .toolbar').fadeIn();
    120130            callback()
    121131        });
     
    137147}
    138148
    139 function showResultsDetail()
    140 {
    141     var testSummary = $(this).parents('.test');
    142     var builderName = $(this).attr(config.kBuilderNameAttr);
     149function showResultsDetail(testName, selectedBuilderName, failureTypeListByBuilder)
     150{
     151    var builderNameList = base.keys(failureTypeListByBuilder);
     152    var failureTypeList = failureTypeListByBuilder[selectedBuilderName];
     153    var failureTypes = failureTypeList.join(' ');
     154
     155    var content = $('.results-detail .content');
     156
     157    if ($('.failure-details', content).attr(config.kBuilderNameAttr) == selectedBuilderName &&
     158        $('.failure-details', content).attr(config.kTestNameAttr) == testName &&
     159        $('.failure-details', content).attr(config.kFailureTypesAttr) == failureTypes)
     160        return;
     161
     162    displayOnButterbar('Loading...');
     163
     164    results.fetchResultsURLs(selectedBuilderName, testName, failureTypeList, function(resultsURLs) {
     165        var status = $('.results-detail .toolbar .status');
     166
     167        status.empty();
     168        content.empty();
     169
     170        status.append(ui.failureDetailsStatus(testName, selectedBuilderName, failureTypes, builderNameList));
     171        content.append(ui.failureDetails(resultsURLs));
     172
     173        toggleButton($('.results-detail .actions .next'), g_resultsDetailsIterator.hasNext());
     174        toggleButton($('.results-detail .actions .previous'), g_resultsDetailsIterator.hasPrevious());
     175        toggleButton($('.results-detail .actions .rebaseline'), results.canRebaseline(failureTypeList));
     176
     177        $('.failure-details', content).attr(config.kBuilderNameAttr, selectedBuilderName);
     178        $('.failure-details', content).attr(config.kTestNameAttr, testName);
     179        $('.failure-details', content).attr(config.kFailureTypesAttr, failureTypes);
     180
     181        if (!$('.results-detail').is(":visible"))
     182            $('.results-detail').fadeIn('fast', dismissButterbar);
     183        else
     184            dismissButterbar();
     185    });
     186}
     187
     188function hideResultsDetail()
     189{
     190    $('.results-detail').fadeOut('fast', function() {
     191        $('.results-detail .status').empty();
     192        $('.results-detail .content').empty();
     193        // Strictly speaking, we don't need to clear g_resultsDetailsIterator,
     194        // but doing so helps the garbage collector free memory.
     195        g_resultsDetailsIterator = null;
     196    });
     197}
     198
     199function nextResultsDetail()
     200{
     201    g_resultsDetailsIterator.callNext();
     202}
     203
     204function previousResultsDetail()
     205{
     206    g_resultsDetailsIterator.callPrevious();
     207}
     208
     209function resultsDetailArgumentsForBuilderElement(builderBlock)
     210{
     211    var selectedBuilderName = builderBlock.attr(config.kBuilderNameAttr);
     212
     213    var testSummary = builderBlock.parents('.test');
    143214    var testName = testSummary.attr(config.kTestNameAttr);
    144215
     
    147218    var failureTypeList = failureTypes.split(' ');
    148219
    149     var content = $('.results-detail .content');
    150     if ($('.failure-details', content).attr(config.kBuilderNameAttr) == builderName &&
    151         $('.failure-details', content).attr(config.kTestNameAttr) == testName &&
    152         $('.failure-details', content).attr(config.kFailureTypesAttr) == failureTypes)
    153         return;
    154 
    155     displayOnButterbar('Loading...');
    156 
    157     results.fetchResultsURLs(builderName, testName, failureTypeList, function(resultsURLs) {
    158         var status = $('.results-detail .toolbar .status');
    159 
    160         function updateResults()
    161         {
    162             status.text(testName + ' [' + builderName + ']');
    163             content.empty();
    164             content.append(ui.failureDetails(resultsURLs));
    165             $('.results-detail .actions .rebaseline').toggle(results.canRebaseline(failureTypeList));
    166             $('.failure-details', content).attr(config.kBuilderNameAttr, builderName);
    167             $('.failure-details', content).attr(config.kTestNameAttr, testName);
    168             $('.failure-details', content).attr(config.kFailureTypesAttr, failureTypes);
    169         }
    170 
    171         var children = content.children();
    172         if (children.length && $('.results-detail').is(":visible")) {
    173             // The results-detail pane is already open. Let's do a quick cross-fade.
    174             status.fadeOut('fast');
    175             children.fadeOut('fast', function() {
    176                 updateResults();
    177                 status.fadeIn('fast');
    178                 content.children().hide().fadeIn('fast', dismissButterbar);
    179             });
    180         } else {
    181             updateResults();
    182             $('.results-detail').fadeIn('fast', dismissButterbar);
    183         }
    184     });
    185 }
    186 
    187 function hideResultsDetail()
    188 {
    189     $('.results-detail').fadeOut('fast', function() {
    190         $('.results-detail .content').empty();
    191     });
     220    var failureTypeListByBuilder = {}
     221    $('.where .builder-name', testSummary).each(function() {
     222        var builderName = $(this).attr(config.kBuilderNameAttr);
     223        // FIXME: We should understand that tests can fail in different ways
     224        // on different builders.
     225        failureTypeListByBuilder[builderName] = failureTypeList;
     226    });
     227
     228    return [testName, selectedBuilderName, failureTypeListByBuilder];
     229}
     230
     231function prepareResultsDetailsIterator(query)
     232{
     233    var listOfResultsDetailArguments = [];
     234
     235    query.each(function() {
     236        var resultsDetailArguments = resultsDetailArgumentsForBuilderElement($(this));
     237        listOfResultsDetailArguments.push(resultsDetailArguments);
     238    });
     239
     240    return new base.CallbackIterator(showResultsDetail, listOfResultsDetailArguments);
     241}
     242
     243function triageFailures()
     244{
     245    g_resultsDetailsIterator = prepareResultsDetailsIterator($('.results .test .builder-name'));
     246    g_resultsDetailsIterator.callNext();
    192247}
    193248
     
    223278}
    224279
    225 $('.results-summary .where a').live('click', showResultsDetail);
     280$('.results .toolbar .triage').live('click', triageFailures);
     281$('.results-detail .actions .next').live('click', nextResultsDetail);
     282$('.results-detail .actions .previous').live('click', previousResultsDetail);
     283$('.results-detail .actions .rebaseline').live('click', rebaselineResults);
    226284$('.results-detail .actions .dismiss').live('click', hideResultsDetail);
    227 $('.results-detail .actions .rebaseline').live('click', rebaselineResults);
    228285
    229286$(document).ready(function() {
  • trunk/Tools/Scripts/webkitpy/tool/servers/data/gardeningserver/ui.js

    r91240 r91241  
    4949    var block = $(
    5050        '<tr class="test">' +
    51           '<td class="what"><a draggable></a></td>' +
     51          '<td class="what"><a class="test-name"></a></td>' +
    5252          '<td class="where"><ul></ul></td>' +
    5353          '<td class="when"></td>' +
    5454          '<td class="how-many"></td>' +
    5555        '</tr>');
    56     $('.what a', block).text(testName).attr('href', ui.urlForTest(testName)).attr('class', unexpectedResults.join(' '));
     56    $('.test-name', block).text(testName).attr('href', ui.urlForTest(testName)).addClass(unexpectedResults.join(' '));
    5757    block.attr(config.kTestNameAttr, testName);
    5858    block.attr(config.kFailureTypesAttr, unexpectedResults);
     
    6060    var where = $('.where', block);
    6161    $.each(resultNodesByBuilder, function(builderName, resultNode) {
    62         var listElement = $('<li><a href="#"></a></li>');
     62        var listElement = $('<li class="builder-name"></li>');
     63        listElement.attr(config.kBuilderNameAttr, builderName).text(displayNameForBuilder(builderName));
    6364        where.append(listElement);
    64         $('a', listElement).attr(config.kBuilderNameAttr, builderName).text(displayNameForBuilder(builderName));
    6565    });
    6666
     
    8080        displayNameForRevision(impliedFirstFailingRevision) + '-' + displayNameForRevision(oldestFailingRevision);
    8181
    82     var block = $('<div class="regression-range"><a></a></div>');
     82    var block = $('<div class="regression-range"><a target="_blank"></a></div>');
    8383    $('a', block).attr('href', href).text(text)
    8484    return block;
     
    108108};
    109109
     110ui.failureDetailsStatus = function(testName, selectedBuilderName, failureTypes, builderNameList)
     111{
     112    var block = $('<span><span class="test-name"></span><span class="builder-list"></span></span>');
     113    $('.test-name', block).addClass(failureTypes).text(testName);
     114
     115    var builderList = $('.builder-list', block);
     116    $.each(builderNameList, function(index, builderName) {
     117        var builder = $('<span class="builder-name"></span>')
     118        builder.text(builderName);
     119        if (builderName == selectedBuilderName)
     120            builder.addClass('selected');
     121        builderList.append(builder);
     122    });
     123
     124    return block;
     125};
     126
    110127ui.failureDetails = function(resultsURLs)
    111128{
  • trunk/Tools/Scripts/webkitpy/tool/servers/data/gardeningserver/ui_unittests.js

    r91197 r91241  
    3838    var summaryWithMultipleRevisions = ui.summarizeRegressionRange(90424, 90426);
    3939    summaryWithMultipleRevisions.wrap('<wrapper></wrapper>');
    40     equal(summaryWithMultipleRevisions.parent().html(), '<div class="regression-range"><a href="http://trac.webkit.org/log/trunk/?rev=90424&amp;stop_rev=90427&amp;limit=100&amp;verbose=on">r90427-r90424</a></div>');
     40    equal(summaryWithMultipleRevisions.parent().html(), '<div class="regression-range"><a target="_blank" href="http://trac.webkit.org/log/trunk/?rev=90424&amp;stop_rev=90427&amp;limit=100&amp;verbose=on">r90427-r90424</a></div>');
    4141
    4242    var summaryWithOneRevision = ui.summarizeRegressionRange(90425, 90426);
    4343    summaryWithOneRevision.wrap('<wrapper></wrapper>');
    44     equal(summaryWithOneRevision.parent().html(), '<div class="regression-range"><a href="http://trac.webkit.org/log/trunk/?rev=90425&amp;stop_rev=90427&amp;limit=100&amp;verbose=on">r90427-r90425</a></div>');
     44    equal(summaryWithOneRevision.parent().html(), '<div class="regression-range"><a target="_blank" href="http://trac.webkit.org/log/trunk/?rev=90425&amp;stop_rev=90427&amp;limit=100&amp;verbose=on">r90427-r90425</a></div>');
    4545});
    4646
     
    6060            '<li><a target="_blank" href="http://build.chromium.org/p/chromium.webkit/waterfall?builder=Another+Builder">Another Builder</a></li>' +
    6161        '</ul></div>');
     62});
     63
     64test("failureDetailsStatus", 1, function() {
     65    var status = ui.failureDetailsStatus('userscripts/another-test.html', 'Mock Builder', 'TEXT', ['Mock Builder', 'Another Builder']);
     66    status.wrap('<wrapper></wrapper>');
     67    equal(status.parent().html(),
     68        '<span>' +
     69            '<span class="test-name TEXT">userscripts/another-test.html</span>' +
     70            '<span class="builder-list">' +
     71                '<span class="builder-name selected">Mock Builder</span>' +
     72                '<span class="builder-name">Another Builder</span>' +
     73            '</span>' +
     74        '</span>');
    6275});
    6376
Note: See TracChangeset for help on using the changeset viewer.