<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[180959] trunk/Tools</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/180959">180959</a></dd>
<dt>Author</dt> <dd>ap@apple.com</dd>
<dt>Date</dt> <dd>2015-03-03 15:22:59 -0800 (Tue, 03 Mar 2015)</dd>
</dl>

<h3>Log Message</h3>
<pre>build.webkit.org/dashboard: Don't repeatedly handle each test type
https://bugs.webkit.org/show_bug.cgi?id=142211

Reviewed by Tim Horton and Matt Hanson.

* BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/Buildbot.js:
(Buildbot.prototype.javascriptTestResultsURLForIteration): Deleted.
(Buildbot.prototype.apiTestResultsURLForIteration): Deleted.
(Buildbot.prototype.platformAPITestResultsURLForIteration): Deleted.
(Buildbot.prototype.webkitpyTestResultsURLForIteration): Deleted.
(Buildbot.prototype.webkitperlTestResultsURLForIteration): Deleted.
(Buildbot.prototype.bindingsTestResultsURLForIteration): Deleted.
Removed functions that build a link to test step results. The buildbot provides
these links in JSON.

* BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/BuildbotIteration.js:
(BuildbotIteration): Put failing tests into an array, instead of named variables.
(BuildbotIteration.ProductiveSteps): Removed step names that are not used on build.webkit.org.
We can easily add them to the map as needed.
(BuildbotIteration.TestSteps): Added a list of test steps to be displayed by test queues.
(BuildbotIteration.prototype._parseData): Moved code for parsing step results away
to BuildbotTestResults class. We used to parse here, build an intermediate data structure,
and then build a BuildbotTestResults object, which was strange.
(BuildbotIteration.prototype.loadLayoutTestResults): Ditto.

* BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/BuildbotQueueView.js:
Corrected an unrelated assertion that was buggy, and kept firing.

* BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/BuildbotTestResults.js:
(BuildbotTestResults):
(BuildbotTestResults.prototype._parseResults.resultSummarizer):
(BuildbotTestResults.prototype._parseResults):
(BuildbotTestResults.prototype.addFullLayoutTestResults):
Moved the code for parsing JSON results for a single step here.

* BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/BuildbotTesterQueueView.js:
(BuildbotTesterQueueView.prototype._testStepFailureDescription):
(BuildbotTesterQueueView.prototype._testStepFailureDescriptionWithCount):
(BuildbotTesterQueueView.prototype._presentPopoverForGenericTestFailures):
(BuildbotTesterQueueView.prototype.update.appendBuilderQueueStatus): Deleted.
(BuildbotTesterQueueView.prototype.update): Deleted.
(BuildbotTesterQueueView.prototype._presentPopoverForMultipleFailureKinds): Deleted.
Updated for the new data structures. One behavior change is that we now display individual
counts when multiple test kinds fail, e.g. &quot;1 javascript test failure, 83+ layout
test failures, 3 platform api test failures&quot;.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkToolsBuildSlaveSupportbuildwebkitorgconfigpublic_htmldashboardScriptsBuildbotjs">trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/Buildbot.js</a></li>
<li><a href="#trunkToolsBuildSlaveSupportbuildwebkitorgconfigpublic_htmldashboardScriptsBuildbotIterationjs">trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/BuildbotIteration.js</a></li>
<li><a href="#trunkToolsBuildSlaveSupportbuildwebkitorgconfigpublic_htmldashboardScriptsBuildbotQueueViewjs">trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/BuildbotQueueView.js</a></li>
<li><a href="#trunkToolsBuildSlaveSupportbuildwebkitorgconfigpublic_htmldashboardScriptsBuildbotTestResultsjs">trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/BuildbotTestResults.js</a></li>
<li><a href="#trunkToolsBuildSlaveSupportbuildwebkitorgconfigpublic_htmldashboardScriptsBuildbotTesterQueueViewjs">trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/BuildbotTesterQueueView.js</a></li>
<li><a href="#trunkToolsChangeLog">trunk/Tools/ChangeLog</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkToolsBuildSlaveSupportbuildwebkitorgconfigpublic_htmldashboardScriptsBuildbotjs"></a>
<div class="modfile"><h4>Modified: trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/Buildbot.js (180958 => 180959)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/Buildbot.js        2015-03-03 22:45:59 UTC (rev 180958)
+++ trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/Buildbot.js        2015-03-03 23:22:59 UTC (rev 180959)
</span><span class="lines">@@ -113,36 +113,6 @@
</span><span class="cx">         return this.baseURL + &quot;builders/&quot; + encodeURIComponent(iteration.queue.id) + &quot;/builds/&quot; + iteration.id;
</span><span class="cx">     },
</span><span class="cx"> 
</span><del>-    javascriptTestResultsURLForIteration: function(iteration)
-    {
-        return this.baseURL + &quot;builders/&quot; + encodeURIComponent(iteration.queue.id) + &quot;/builds/&quot; + iteration.id + &quot;/steps/jscore-test/logs/stdio&quot;;
-    },
-
-    apiTestResultsURLForIteration: function(iteration)
-    {
-        return this.baseURL + &quot;builders/&quot; + encodeURIComponent(iteration.queue.id) + &quot;/builds/&quot; + iteration.id + &quot;/steps/run-api-tests/logs/stdio&quot;;
-    },
-
-    platformAPITestResultsURLForIteration: function(iteration)
-    {
-        return this.baseURL + &quot;builders/&quot; + encodeURIComponent(iteration.queue.id) + &quot;/builds/&quot; + iteration.id + &quot;/steps/API%20tests/logs/stdio&quot;;
-    },
-
-    webkitpyTestResultsURLForIteration: function(iteration)
-    {
-        return this.baseURL + &quot;builders/&quot; + encodeURIComponent(iteration.queue.id) + &quot;/builds/&quot; + iteration.id + &quot;/steps/webkitpy-test/logs/stdio&quot;;
-    },
-
-    webkitperlTestResultsURLForIteration: function(iteration)
-    {
-        return this.baseURL + &quot;builders/&quot; + encodeURIComponent(iteration.queue.id) + &quot;/builds/&quot; + iteration.id + &quot;/steps/webkitperl-test/logs/stdio&quot;;
-    },
-
-    bindingsTestResultsURLForIteration: function(iteration)
-    {
-        return this.baseURL + &quot;builders/&quot; + encodeURIComponent(iteration.queue.id) + &quot;/builds/&quot; + iteration.id + &quot;/steps/bindings-generation-tests/logs/stdio&quot;;
-    },
-
</del><span class="cx">     layoutTestResultsURLForIteration: function(iteration)
</span><span class="cx">     {
</span><span class="cx">         return this.layoutTestResultsDirectoryURLForIteration(iteration) + &quot;/results.html&quot;;
</span></span></pre></div>
<a id="trunkToolsBuildSlaveSupportbuildwebkitorgconfigpublic_htmldashboardScriptsBuildbotIterationjs"></a>
<div class="modfile"><h4>Modified: trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/BuildbotIteration.js (180958 => 180959)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/BuildbotIteration.js        2015-03-03 22:45:59 UTC (rev 180958)
+++ trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/BuildbotIteration.js        2015-03-03 23:22:59 UTC (rev 180959)
</span><span class="lines">@@ -46,14 +46,10 @@
</span><span class="cx">     this.openSourceRevision = null;
</span><span class="cx">     this.internalRevision = null;
</span><span class="cx"> 
</span><del>-    this.layoutTestResults = null;
-    this.javascriptTestResults = null;
-    this.apiTestResults = null;
-    this.platformAPITestResults = null;
-    this.pythonTestResults = null;
-    this.perlTestResults = null;
-    this.bindingTestResults = null;
</del><ins>+    this.layoutTestResults = null; // Layout test results can be needed even if all tests passed, e.g. for the leaks bot.
</ins><span class="cx"> 
</span><ins>+    this.failedTestSteps = [];
+
</ins><span class="cx">     this._finished = finished;
</span><span class="cx"> };
</span><span class="cx"> 
</span><span class="lines">@@ -70,7 +66,6 @@
</span><span class="cx"> // If none of these steps ran, then we didn't get any real results, and the iteration was not productive.
</span><span class="cx"> BuildbotIteration.ProductiveSteps = {
</span><span class="cx">     &quot;compile-webkit&quot;: 1,
</span><del>-    &quot;build archive&quot;: 1,
</del><span class="cx">     &quot;build ASan archive&quot;: 1,
</span><span class="cx">     &quot;Build&quot; : 1,
</span><span class="cx">     &quot;layout-test&quot;: 1,
</span><span class="lines">@@ -80,12 +75,19 @@
</span><span class="cx">     &quot;webkitpy-test&quot;: 1,
</span><span class="cx">     &quot;webkitperl-test&quot;: 1,
</span><span class="cx">     &quot;bindings-generation-tests&quot;: 1,
</span><del>-    &quot;run Membuster OS Memory&quot;: 1,
-    &quot;run scrollperf&quot;: 1,
-    &quot;run PLT3&quot;: 1,
</del><span class="cx">     &quot;perf-test&quot;: 1
</span><span class="cx"> };
</span><span class="cx"> 
</span><ins>+BuildbotIteration.TestSteps = {
+    &quot;layout-test&quot;: &quot;layout test&quot;,
+    &quot;jscore-test&quot;: &quot;javascript test&quot;,
+    &quot;run-api-tests&quot;: &quot;api test&quot;,
+    &quot;API tests&quot;: &quot;platform api test&quot;,
+    &quot;webkitpy-test&quot;: &quot;webkitpy test&quot;,
+    &quot;webkitperl-test&quot;: &quot;webkitperl test&quot;,
+    &quot;bindings-generation-tests&quot;: &quot;bindings tests&quot;,
+};
+
</ins><span class="cx"> BuildbotIteration.Event = {
</span><span class="cx">     Updated: &quot;updated&quot;,
</span><span class="cx">     UnauthorizedAccess: &quot;unauthorized-access&quot;
</span><span class="lines">@@ -185,60 +187,6 @@
</span><span class="cx"> 
</span><span class="cx">     _parseData: function(data)
</span><span class="cx">     {
</span><del>-        function collectTestResults(data, stepName)
-        {
-            var testStep = data.steps.findFirst(function(step) { return step.name === stepName; });
-            if (!testStep)
-                return null;
-
-            var testResults = {};
-
-            if (!testStep.isFinished) {
-                // The step never even ran, or hasn't finished running.
-                testResults.finished = false;
-                return testResults;
-            }
-
-            testResults.finished = true;
-
-            if (!testStep.results || testStep.results[0] === BuildbotIteration.SUCCESS) {
-                // All tests passed.
-                testResults.allPassed = true;
-                return testResults;
-            }
-
-            if (/Exiting early/.test(testStep.results[1][0]))
-                testResults.tooManyFailures = true;
-
-            function resultSummarizer(matchString, sum, outputLine)
-            {
-                var match = /^(\d+)\s/.exec(outputLine);
-                if (!match)
-                    return sum;
-                if (!outputLine.contains(matchString))
-                    return sum;
-                if (!sum || sum === -1)
-                    sum = 0;
-                return sum + parseInt(match[1], 10);
-            }
-
-            testResults.failureCount = testStep.results[1].reduce(resultSummarizer.bind(null, &quot;fail&quot;), undefined);
-            testResults.flakeyCount = testStep.results[1].reduce(resultSummarizer.bind(null, &quot;flake&quot;), undefined);
-            testResults.totalLeakCount = testStep.results[1].reduce(resultSummarizer.bind(null, &quot;total leak&quot;), undefined);
-            testResults.uniqueLeakCount = testStep.results[1].reduce(resultSummarizer.bind(null, &quot;unique leak&quot;), undefined);
-            testResults.newPassesCount = testStep.results[1].reduce(resultSummarizer.bind(null, &quot;new pass&quot;), undefined);
-            testResults.missingCount = testStep.results[1].reduce(resultSummarizer.bind(null, &quot;missing&quot;), undefined);
-            testResults.crashCount = testStep.results[1].reduce(resultSummarizer.bind(null, &quot;crash&quot;), undefined);
-
-            if (!testResults.failureCount &amp;&amp; !testResults.flakyCount &amp;&amp; !testResults.totalLeakCount &amp;&amp; !testResults.uniqueLeakCount &amp;&amp; !testResults.newPassesCount &amp;&amp; !testResults.missingCount) {
-                // This step exited with a non-zero exit status, but we didn't find any output about the number of failed tests.
-                // Something must have gone wrong (e.g., timed out and was killed by buildbot).
-                testResults.errorOccurred = true;
-            }
-
-            return testResults;
-        }
-
</del><span class="cx">         console.assert(!this.id || this.id === data.number);
</span><span class="cx">         this.id = data.number;
</span><span class="cx"> 
</span><span class="lines">@@ -286,27 +234,18 @@
</span><span class="cx">         this.startTime = new Date(data.times[0] * 1000);
</span><span class="cx">         this.endTime = new Date(data.times[1] * 1000);
</span><span class="cx"> 
</span><del>-        var layoutTestResults = collectTestResults.call(this, data, &quot;layout-test&quot;);
-        this.layoutTestResults = layoutTestResults ? new BuildbotTestResults(this, layoutTestResults) : null;
</del><ins>+        this.failedTestSteps = [];
+        data.steps.forEach(function(step) {
+            if (!step.isFinished || !(step.name in BuildbotIteration.TestSteps))
+                return;
+            var results = new BuildbotTestResults(step);
+            if (step.name === &quot;layout-test&quot;)
+                this.layoutTestResults = results;
+            if (results.allPassed)
+                return;
+            this.failedTestSteps.push(results);
+        }, this);
</ins><span class="cx"> 
</span><del>-        var javascriptTestResults = collectTestResults.call(this, data, &quot;jscore-test&quot;);
-        this.javascriptTestResults = javascriptTestResults ? new BuildbotTestResults(this, javascriptTestResults) : null;
-
-        var apiTestResults = collectTestResults.call(this, data, &quot;run-api-tests&quot;);
-        this.apiTestResults = apiTestResults ? new BuildbotTestResults(this, apiTestResults) : null;
-
-        var platformAPITestResults = collectTestResults.call(this, data, &quot;API tests&quot;);
-        this.platformAPITestResults = platformAPITestResults ? new BuildbotTestResults(this, platformAPITestResults) : null;
-
-        var pythonTestResults = collectTestResults.call(this, data, &quot;webkitpy-test&quot;);
-        this.pythonTestResults = pythonTestResults ? new BuildbotTestResults(this, pythonTestResults) : null;
-
-        var perlTestResults = collectTestResults.call(this, data, &quot;webkitperl-test&quot;);
-        this.perlTestResults = perlTestResults ? new BuildbotTestResults(this, perlTestResults) : null;
-
-        var bindingTestResults = collectTestResults.call(this, data, &quot;bindings-generation-tests&quot;);
-        this.bindingTestResults = bindingTestResults ? new BuildbotTestResults(this, bindingTestResults) : null;
-
</del><span class="cx">         var masterShellCommandStep = data.steps.findFirst(function(step) { return step.name === &quot;MasterShellCommand&quot;; });
</span><span class="cx">         this.resultURLs = masterShellCommandStep ? masterShellCommandStep.urls : null;
</span><span class="cx">         for (var linkName in this.resultURLs) {
</span><span class="lines">@@ -391,83 +330,10 @@
</span><span class="cx">         if (this.queue.buildbot.needsAuthentication &amp;&amp; this.queue.buildbot.authenticationStatus === Buildbot.AuthenticationStatus.InvalidCredentials)
</span><span class="cx">             return;
</span><span class="cx"> 
</span><del>-        function collectResults(subtree, predicate)
-        {
-            // Results object is a trie:
-            // directory
-            //   subdirectory
-            //     test1.html
-            //       expected:&quot;PASS&quot;
-            //       actual: &quot;IMAGE&quot;
-            //       report: &quot;REGRESSION&quot;
-            //     test2.html
-            //       expected:&quot;FAIL&quot;
-            //       actual:&quot;TEXT&quot;
-
-            var result = [];
-            for (var key in subtree) {
-                var value = subtree[key];
-                console.assert(typeof value === &quot;object&quot;);
-                var isIndividualTest = value.hasOwnProperty(&quot;actual&quot;) &amp;&amp; value.hasOwnProperty(&quot;expected&quot;);
-                if (isIndividualTest) {
-                    // Possible values for actual and expected keys: PASS, FAIL, AUDIO, IMAGE, TEXT, IMAGE+TEXT, TIMEOUT, CRASH, MISSING.
-                    // Both actual and expected can be space separated lists. Actual contains two values when retrying a failed test
-                    // gives a different result (retrying may be disabled in tester configuration).
-                    // Possible values for report key (when present): REGRESSION, MISSING, FLAKY.
-
-                    if (predicate(value)) {
-                        var item = {path: key};
-
-                        // FIXME (bug 127186): Crash log URL will be incorrect if crash only happened on retry (e.g. &quot;TEXT CRASH&quot;).
-                        // It should point to retries subdirectory, but the information about which attempt failed gets lost here.
-                        if (value.actual.contains(&quot;CRASH&quot;))
-                            item.crash = true;
-                        if (value.actual.contains(&quot;TIMEOUT&quot;))
-                            item.timeout = true;
-
-                        // FIXME (bug 127186): Similarly, we don't have a good way to present results for something like &quot;TIMEOUT TEXT&quot;,
-                        // not even UI wise. For now, only show a diff link if the first attempt has the diff.
-                        if (value.actual.split(&quot; &quot;)[0].contains(&quot;TEXT&quot;))
-                            item.has_diff = true;
-
-                        // FIXME (bug 127186): It is particularly unfortunate for image diffs, because we currently only check image results
-                        // on retry (except for reftests), so many times, you will see images on buidbot page, but not on the dashboard.
-                        // FIXME: Find a way to display expected mismatch reftest failures. 
-                        if (value.actual.split(&quot; &quot;)[0].contains(&quot;IMAGE&quot;) &amp;&amp; value.reftest_type != &quot;!=&quot;)
-                            item.has_image_diff = true;
-
-                        if (value.has_stderr)
-                            item.has_stderr = true;
-
-                        result.push(item);
-                    }
-
-                } else {
-                    var nestedTests = collectResults(value, predicate);
-                    for (var i = 0, end = nestedTests.length; i &lt; end; ++i)
-                        nestedTests[i].path = key + &quot;/&quot; + nestedTests[i].path;
-                    result = result.concat(nestedTests);
-                }
-            }
-
-            return result;
-        }
-
</del><span class="cx">         JSON.load(this.queue.buildbot.layoutTestFullResultsURLForIteration(this), function(data) {
</span><span class="cx">             this.queue.buildbot.isAuthenticated = true;
</span><del>-            this.hasPrettyPatch = data.has_pretty_patch;
</del><span class="cx"> 
</span><del>-            this.layoutTestResults.regressions = collectResults(data.tests, function(info) { return info[&quot;report&quot;] === &quot;REGRESSION&quot; });
-            console.assert(data.num_regressions === this.layoutTestResults.regressions.length);
-
-            this.layoutTestResults.flakyTests = collectResults(data.tests, function(info) { return info[&quot;report&quot;] === &quot;FLAKY&quot; });
-            console.assert(data.num_flaky === this.layoutTestResults.flakyTests.length);
-
-            this.layoutTestResults.testsWithMissingResults = collectResults(data.tests, function(info) { return info[&quot;report&quot;] === &quot;MISSING&quot; });
-            // data.num_missing is not always equal to the size of testsWithMissingResults array,
-            // because buildbot counts regressions that had missing pixel results on retry (e.g. &quot;TEXT MISSING&quot;).
-            console.assert(data.num_missing &gt;= this.layoutTestResults.testsWithMissingResults.length);
-
</del><ins>+            this.layoutTestResults.addFullLayoutTestResults(data);
</ins><span class="cx">             callback();
</span><span class="cx">         }.bind(this),
</span><span class="cx">         function(data) {
</span></span></pre></div>
<a id="trunkToolsBuildSlaveSupportbuildwebkitorgconfigpublic_htmldashboardScriptsBuildbotQueueViewjs"></a>
<div class="modfile"><h4>Modified: trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/BuildbotQueueView.js (180958 => 180959)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/BuildbotQueueView.js        2015-03-03 22:45:59 UTC (rev 180958)
+++ trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/BuildbotQueueView.js        2015-03-03 23:22:59 UTC (rev 180959)
</span><span class="lines">@@ -144,7 +144,7 @@
</span><span class="cx">             return result;
</span><span class="cx">         }
</span><span class="cx"> 
</span><del>-        console.assert(trac.oldestRecordedRevisionNumber &gt;= firstRevisionNumber);
</del><ins>+        console.assert(trac.oldestRecordedRevisionNumber &lt;= firstRevisionNumber);
</ins><span class="cx"> 
</span><span class="cx">         // FIXME: To be 100% correct, we should also filter out changes that are ignored by
</span><span class="cx">         // the queue, see _should_file_trigger_build in wkbuild.py.
</span></span></pre></div>
<a id="trunkToolsBuildSlaveSupportbuildwebkitorgconfigpublic_htmldashboardScriptsBuildbotTestResultsjs"></a>
<div class="modfile"><h4>Modified: trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/BuildbotTestResults.js (180958 => 180959)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/BuildbotTestResults.js        2015-03-03 22:45:59 UTC (rev 180958)
+++ trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/BuildbotTestResults.js        2015-03-03 23:22:59 UTC (rev 180959)
</span><span class="lines">@@ -23,28 +23,157 @@
</span><span class="cx">  * THE POSSIBILITY OF SUCH DAMAGE.
</span><span class="cx">  */
</span><span class="cx"> 
</span><del>-BuildbotTestResults = function(buildbotIteration, info)
</del><ins>+BuildbotTestResults = function(testStep)
</ins><span class="cx"> {
</span><span class="cx">     BaseObject.call(this);
</span><span class="cx"> 
</span><del>-    this.finished = info.finished || false;
-
-    this.allPassed = info.allPassed || false;
-    this.errorOccurred = info.errorOccurred || false;
-    this.tooManyFailures = info.tooManyFailures || false;
-
-    this.failureCount = info.failureCount || 0;
-    this.flakeyCount = info.flakeyCount || 0;
-    this.totalLeakCount = info.totalLeakCount || 0;
-    this.uniqueLeakCount = info.uniqueLeakCount || 0;
-    this.newPassesCount = info.newPassesCount || 0;
-    this.missingCount = info.missingCount || 0;
-    this.crashCount = info.crashCount || 0;
</del><ins>+    this._parseResults(testStep);
</ins><span class="cx"> };
</span><span class="cx"> 
</span><span class="cx"> BaseObject.addConstructorFunctions(BuildbotTestResults);
</span><span class="cx"> 
</span><span class="cx"> BuildbotTestResults.prototype = {
</span><span class="cx">     constructor: BuildbotTestResults,
</span><del>-    __proto__: BaseObject.prototype
</del><ins>+    __proto__: BaseObject.prototype,
+
+    _parseResults: function(testStep)
+    {
+        this.name = testStep.name;
+        this.URL = testStep.logs[0][1];
+
+        this.allPassed = false;
+        this.errorOccurred = false;
+        this.tooManyFailures = false;
+
+        this.failureCount = 0;
+        this.flakeyCount = 0;
+        this.totalLeakCount = 0;
+        this.uniqueLeakCount = 0;
+        this.newPassesCount = 0;
+        this.missingCount = 0;
+        this.crashCount = 0;
+
+        if (!testStep.isFinished) {
+            // The step never even ran, or hasn't finished running.
+            this.finished = false;
+            return;
+        }
+
+        this.finished = true;
+
+        if (!testStep.results || testStep.results[0] === BuildbotIteration.SUCCESS || testStep.results[0] === BuildbotIteration.WARNINGS) {
+            // All tests passed.
+            this.allPassed = true;
+            return;
+        }
+
+        if (/Exiting early/.test(testStep.results[1][0]))
+            this.tooManyFailures = true;
+
+        function resultSummarizer(matchString, sum, outputLine)
+        {
+            var match = /^(\d+)\s/.exec(outputLine);
+            if (!match)
+                return sum;
+            if (!outputLine.contains(matchString))
+                return sum;
+            if (!sum || sum === -1)
+                sum = 0;
+            return sum + parseInt(match[1], 10);
+        }
+
+        this.failureCount = testStep.results[1].reduce(resultSummarizer.bind(null, &quot;fail&quot;), undefined);
+        this.flakeyCount = testStep.results[1].reduce(resultSummarizer.bind(null, &quot;flake&quot;), undefined);
+        this.totalLeakCount = testStep.results[1].reduce(resultSummarizer.bind(null, &quot;total leak&quot;), undefined);
+        this.uniqueLeakCount = testStep.results[1].reduce(resultSummarizer.bind(null, &quot;unique leak&quot;), undefined);
+        this.newPassesCount = testStep.results[1].reduce(resultSummarizer.bind(null, &quot;new pass&quot;), undefined);
+        this.missingCount = testStep.results[1].reduce(resultSummarizer.bind(null, &quot;missing&quot;), undefined);
+        this.crashCount = testStep.results[1].reduce(resultSummarizer.bind(null, &quot;crash&quot;), undefined);
+
+        if (!this.failureCount &amp;&amp; !this.flakyCount &amp;&amp; !this.totalLeakCount &amp;&amp; !this.uniqueLeakCount &amp;&amp; !this.newPassesCount &amp;&amp; !this.missingCount) {
+            // This step exited with a non-zero exit status, but we didn't find any output about the number of failed tests.
+            // Something must have gone wrong (e.g., timed out and was killed by buildbot).
+            this.errorOccurred = true;
+        }
+    },
+
+    addFullLayoutTestResults: function(data)
+    {
+        console.assert(this.name === &quot;layout-test&quot;);
+
+        function collectResults(subtree, predicate)
+        {
+            // Results object is a trie:
+            // directory
+            //   subdirectory
+            //     test1.html
+            //       expected:&quot;PASS&quot;
+            //       actual: &quot;IMAGE&quot;
+            //       report: &quot;REGRESSION&quot;
+            //     test2.html
+            //       expected:&quot;FAIL&quot;
+            //       actual:&quot;TEXT&quot;
+
+            var result = [];
+            for (var key in subtree) {
+                var value = subtree[key];
+                console.assert(typeof value === &quot;object&quot;);
+                var isIndividualTest = value.hasOwnProperty(&quot;actual&quot;) &amp;&amp; value.hasOwnProperty(&quot;expected&quot;);
+                if (isIndividualTest) {
+                    // Possible values for actual and expected keys: PASS, FAIL, AUDIO, IMAGE, TEXT, IMAGE+TEXT, TIMEOUT, CRASH, MISSING.
+                    // Both actual and expected can be space separated lists. Actual contains two values when retrying a failed test
+                    // gives a different result (retrying may be disabled in tester configuration).
+                    // Possible values for report key (when present): REGRESSION, MISSING, FLAKY.
+
+                    if (predicate(value)) {
+                        var item = {path: key};
+
+                        // FIXME (bug 127186): Crash log URL will be incorrect if crash only happened on retry (e.g. &quot;TEXT CRASH&quot;).
+                        // It should point to retries subdirectory, but the information about which attempt failed gets lost here.
+                        if (value.actual.contains(&quot;CRASH&quot;))
+                            item.crash = true;
+                        if (value.actual.contains(&quot;TIMEOUT&quot;))
+                            item.timeout = true;
+
+                        // FIXME (bug 127186): Similarly, we don't have a good way to present results for something like &quot;TIMEOUT TEXT&quot;,
+                        // not even UI wise. For now, only show a diff link if the first attempt has the diff.
+                        if (value.actual.split(&quot; &quot;)[0].contains(&quot;TEXT&quot;))
+                            item.has_diff = true;
+
+                        // FIXME (bug 127186): It is particularly unfortunate for image diffs, because we currently only check image results
+                        // on retry (except for reftests), so many times, you will see images on buildbot page, but not on the dashboard.
+                        // FIXME: Find a way to display expected mismatch reftest failures. 
+                        if (value.actual.split(&quot; &quot;)[0].contains(&quot;IMAGE&quot;) &amp;&amp; value.reftest_type != &quot;!=&quot;)
+                            item.has_image_diff = true;
+
+                        if (value.has_stderr)
+                            item.has_stderr = true;
+
+                        result.push(item);
+                    }
+
+                } else {
+                    var nestedTests = collectResults(value, predicate);
+                    for (var i = 0, end = nestedTests.length; i &lt; end; ++i)
+                        nestedTests[i].path = key + &quot;/&quot; + nestedTests[i].path;
+                    result = result.concat(nestedTests);
+                }
+            }
+
+            return result;
+        }
+
+        this.hasPrettyPatch = data.has_pretty_patch;
+
+        this.regressions = collectResults(data.tests, function(info) { return info[&quot;report&quot;] === &quot;REGRESSION&quot; });
+        console.assert(data.num_regressions === this.regressions.length);
+
+        this.flakyTests = collectResults(data.tests, function(info) { return info[&quot;report&quot;] === &quot;FLAKY&quot; });
+        console.assert(data.num_flaky === this.flakyTests.length);
+
+        this.testsWithMissingResults = collectResults(data.tests, function(info) { return info[&quot;report&quot;] === &quot;MISSING&quot; });
+        // data.num_missing is not always equal to the size of testsWithMissingResults array,
+        // because buildbot counts regressions that had missing pixel results on retry (e.g. &quot;TEXT MISSING&quot;).
+        console.assert(data.num_missing &gt;= this.testsWithMissingResults.length);
+    },
</ins><span class="cx"> };
</span></span></pre></div>
<a id="trunkToolsBuildSlaveSupportbuildwebkitorgconfigpublic_htmldashboardScriptsBuildbotTesterQueueViewjs"></a>
<div class="modfile"><h4>Modified: trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/BuildbotTesterQueueView.js (180958 => 180959)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/BuildbotTesterQueueView.js        2015-03-03 22:45:59 UTC (rev 180958)
+++ trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/BuildbotTesterQueueView.js        2015-03-03 23:22:59 UTC (rev 180959)
</span><span class="lines">@@ -64,14 +64,6 @@
</span><span class="cx">                 var willHaveAnotherStatusLine = i + 1 &lt; queue.iterations.length &amp;&amp; limit &gt; 0 &amp;&amp; !iteration.successful; // This is not 100% correct, as the remaining iterations may not be finished or loaded yet, but close enough.
</span><span class="cx">                 var messageElement = this.revisionContentForIteration(iteration, (iteration.productive &amp;&amp; willHaveAnotherStatusLine) ? iteration.previousProductiveIteration : null);
</span><span class="cx"> 
</span><del>-                var layoutTestResults = iteration.layoutTestResults || {failureCount: 0};
-                var javascriptTestResults = iteration.javascriptTestResults || {failureCount: 0};
-                var apiTestResults = iteration.apiTestResults || {failureCount: 0};
-                var platformAPITestResults = iteration.platformAPITestResults || {failureCount: 0};
-                var pythonTestResults = iteration.pythonTestResults || {failureCount: 0};
-                var perlTestResults = iteration.perlTestResults || {errorOccurred: false};
-                var bindingTestResults = iteration.bindingTestResults || {errorOccurred: false};
-
</del><span class="cx">                 if (iteration.successful) {
</span><span class="cx">                     var url = iteration.queue.buildbot.buildPageURLForIteration(iteration);
</span><span class="cx">                     var status = new StatusLineView(messageElement, StatusLineView.Status.Good, &quot;all tests passed&quot;, undefined, url);
</span><span class="lines">@@ -79,45 +71,36 @@
</span><span class="cx">                 } else if (!iteration.productive) {
</span><span class="cx">                     var url = iteration.queue.buildbot.buildPageURLForIteration(iteration);
</span><span class="cx">                     var status = new StatusLineView(messageElement, StatusLineView.Status.Danger, iteration.text, undefined, url);
</span><del>-                } else if (queue.crashesOnly &amp;&amp; !layoutTestResults.crashCount) {
-                    var url = iteration.queue.buildbot.buildPageURLForIteration(iteration);
-                    var status = new StatusLineView(messageElement, StatusLineView.Status.Good, &quot;no crashes found&quot;, undefined, url);
-                    limit = 0;
-                } else if (queue.crashesOnly &amp;&amp; layoutTestResults.crashCount) {
-                    var url = iteration.queue.buildbot.layoutTestResultsURLForIteration(iteration);
-                    var status = new StatusLineView(messageElement, StatusLineView.Status.Bad, layoutTestResults.crashCount === 1 ? &quot;crash found&quot; : &quot;crashes found&quot;, layoutTestResults.crashCount, url);
-                    new PopoverTracker(status.statusBubbleElement, this._presentPopoverForLayoutTestRegressions.bind(this), iteration);
-                } else if (!layoutTestResults.failureCount &amp;&amp; !javascriptTestResults.failureCount &amp;&amp; !apiTestResults.failureCount &amp;&amp; !platformAPITestResults.failureCount &amp;&amp; !pythonTestResults.failureCount &amp;&amp; !perlTestResults.errorOccurred &amp;&amp; !bindingTestResults.errorOccurred) {
</del><ins>+                } else if (iteration.failedTestSteps.length === 0) {
</ins><span class="cx">                     // Something wrong happened, but it was not a test failure.
</span><span class="cx">                     var url = iteration.queue.buildbot.buildPageURLForIteration(iteration);
</span><span class="cx">                     var status = new StatusLineView(messageElement, StatusLineView.Status.Danger, iteration.text, undefined, url);
</span><del>-                } else if (layoutTestResults.failureCount &amp;&amp; !javascriptTestResults.failureCount &amp;&amp; !apiTestResults.failureCount &amp;&amp; !platformAPITestResults.failureCount &amp;&amp; !pythonTestResults.failureCount &amp;&amp; !perlTestResults.errorOccurred &amp;&amp; !bindingTestResults.errorOccurred) {
-                    var url = iteration.queue.buildbot.layoutTestResultsURLForIteration(iteration);
-                    var status = new StatusLineView(messageElement, StatusLineView.Status.Bad, layoutTestResults.failureCount === 1 ? &quot;layout test failure&quot; : &quot;layout test failures&quot;, layoutTestResults.tooManyFailures ? layoutTestResults.failureCount + &quot;\uff0b&quot; : layoutTestResults.failureCount, url);
-                    new PopoverTracker(status.statusBubbleElement, this._presentPopoverForLayoutTestRegressions.bind(this), iteration);
-                } else if (!layoutTestResults.failureCount &amp;&amp; javascriptTestResults.failureCount &amp;&amp; !apiTestResults.failureCount &amp;&amp; !platformAPITestResults.failureCount &amp;&amp; !pythonTestResults.failureCount &amp;&amp; !perlTestResults.errorOccurred &amp;&amp; !bindingTestResults.errorOccurred) {
-                    var url = iteration.queue.buildbot.javascriptTestResultsURLForIteration(iteration);
-                    var status = new StatusLineView(messageElement, StatusLineView.Status.Bad, javascriptTestResults.failureCount === 1 ? &quot;javascript test failure&quot; : &quot;javascript test failures&quot;, javascriptTestResults.failureCount, url);
-                } else if (!layoutTestResults.failureCount &amp;&amp; !javascriptTestResults.failureCount &amp;&amp; apiTestResults.failureCount &amp;&amp; !platformAPITestResults.failureCount &amp;&amp; !pythonTestResults.failureCount &amp;&amp; !perlTestResults.errorOccurred &amp;&amp; !bindingTestResults.errorOccurred) {
-                    var url = iteration.queue.buildbot.apiTestResultsURLForIteration(iteration);
-                    var status = new StatusLineView(messageElement, StatusLineView.Status.Bad, apiTestResults.failureCount === 1 ? &quot;api test failure&quot; : &quot;api test failures&quot;, apiTestResults.failureCount, url);
-                } else if (!layoutTestResults.failureCount &amp;&amp; !javascriptTestResults.failureCount &amp;&amp; !apiTestResults.failureCount &amp;&amp; platformAPITestResults.failureCount &amp;&amp; !pythonTestResults.failureCount &amp;&amp; !perlTestResults.errorOccurred &amp;&amp; !bindingTestResults.errorOccurred) {
-                    var url = iteration.queue.buildbot.platformAPITestResultsURLForIteration(iteration);
-                    var status = new StatusLineView(messageElement, StatusLineView.Status.Bad, platformAPITestResults.failureCount === 1 ? &quot;platform api test failure&quot; : &quot;api test failures&quot;, platformAPITestResults.failureCount, url);
-                } else if (!layoutTestResults.failureCount &amp;&amp; !javascriptTestResults.failureCount &amp;&amp; !apiTestResults.failureCount &amp;&amp; !platformAPITestResults.failureCount &amp;&amp; pythonTestResults.failureCount &amp;&amp; !perlTestResults.errorOccurred &amp;&amp; !bindingTestResults.errorOccurred) {
-                    var url = iteration.queue.buildbot.webkitpyTestResultsURLForIteration(iteration);
-                    var status = new StatusLineView(messageElement, StatusLineView.Status.Bad, pythonTestResults.failureCount === 1 ? &quot;webkitpy test failure&quot; : &quot;webkitpy test failures&quot;, pythonTestResults.failureCount, url);
-                } else if (!layoutTestResults.failureCount &amp;&amp; !javascriptTestResults.failureCount &amp;&amp; !apiTestResults.failureCount &amp;&amp; !platformAPITestResults.failureCount &amp;&amp; !pythonTestResults.failureCount &amp;&amp; perlTestResults.errorOccurred &amp;&amp; !bindingTestResults.errorOccurred) {
-                    var url = iteration.queue.buildbot.webkitperlTestResultsURLForIteration(iteration);
-                    var status = new StatusLineView(messageElement, StatusLineView.Status.Bad, &quot;webkitperl test failed&quot;, undefined, url);
-                } else if (!layoutTestResults.failureCount &amp;&amp; !javascriptTestResults.failureCount &amp;&amp; !apiTestResults.failureCount &amp;&amp; !platformAPITestResults.failureCount &amp;&amp; !pythonTestResults.failureCount &amp;&amp; !perlTestResults.errorOccurred &amp;&amp; bindingTestResults.errorOccurred) {
-                    var url = iteration.queue.buildbot.bindingsTestResultsURLForIteration(iteration);
-                    var status = new StatusLineView(messageElement, StatusLineView.Status.Bad, &quot;bindings tests failed&quot;, undefined, url);
</del><ins>+                } else if (queue.crashesOnly) {
+                    // A crashes-only queue is a queue where we are only interested in crashes, e.g. a GuardMalloc or an ASan one.
+                    // Currently, only layout tests are supported in such.
+                    var layoutTestResults = iteration.layoutTestResults;
+                    if (!layoutTestResults.crashCount) {
+                        var url = iteration.queue.buildbot.buildPageURLForIteration(iteration);
+                        var status = new StatusLineView(messageElement, StatusLineView.Status.Good, &quot;no crashes found&quot;, undefined, url);
+                        limit = 0;
+                    } else {
+                        var status = new StatusLineView(messageElement, StatusLineView.Status.Bad, layoutTestResults.crashCount === 1 ? &quot;crash found&quot; : &quot;crashes found&quot;, layoutTestResults.crashCount, iteration.queue.buildbot.layoutTestResultsURLForIteration(iteration));
+                        new PopoverTracker(status.statusBubbleElement, this._presentPopoverForLayoutTestRegressions.bind(this), iteration);
+                    }
+                } else if (iteration.failedTestSteps.length === 1) {
+                    var failedStep = iteration.failedTestSteps[0];
+                    if (failedStep.name === &quot;layout-test&quot;) {
+                        var status = new StatusLineView(messageElement, StatusLineView.Status.Bad, this._testStepFailureDescription(failedStep), failedStep.tooManyFailures ? failedStep.failureCount + &quot;\uff0b&quot; : failedStep.failureCount, iteration.queue.buildbot.layoutTestResultsURLForIteration(iteration));
+                        new PopoverTracker(status.statusBubbleElement, this._presentPopoverForLayoutTestRegressions.bind(this), iteration);
+                    } else {
+                        var status = new StatusLineView(messageElement, StatusLineView.Status.Bad, this._testStepFailureDescription(failedStep), failedStep.failureCount ? failedStep.failureCount : undefined, failedStep.URL);
+                        new PopoverTracker(status.statusBubbleElement, this._presentPopoverForGenericTestFailures.bind(this), iteration);
+                    }
</ins><span class="cx">                 } else {
</span><span class="cx">                     var url = iteration.queue.buildbot.buildPageURLForIteration(iteration);
</span><del>-                    var totalFailures = layoutTestResults.failureCount + javascriptTestResults.failureCount + apiTestResults.failureCount + platformAPITestResults.failureCount + pythonTestResults.failureCount + perlTestResults.errorOccurred + bindingTestResults.errorOccurred;
-                    var status = new StatusLineView(messageElement, StatusLineView.Status.Bad, totalFailures === 1 ? &quot;test failure&quot; : &quot;test failures&quot;, totalFailures, url);
-                    new PopoverTracker(status.statusBubbleElement, this._presentPopoverForMultipleFailureKinds.bind(this), iteration);
</del><ins>+                    var failureDescriptions = iteration.failedTestSteps.map(function(failedStep) { return this._testStepFailureDescriptionWithCount(failedStep) }, this);
+                    var status = new StatusLineView(messageElement, StatusLineView.Status.Bad, failureDescriptions.join(&quot;, &quot;), undefined, url);
+                    new PopoverTracker(status.statusBubbleElement, this._presentPopoverForGenericTestFailures.bind(this), iteration);
</ins><span class="cx">                 }
</span><span class="cx"> 
</span><span class="cx">                 this.element.appendChild(status.element);
</span><span class="lines">@@ -134,6 +117,27 @@
</span><span class="cx">         this.appendBuildStyle.call(this, this.debugQueues, &quot;Debug&quot;, appendBuilderQueueStatus);
</span><span class="cx">     },
</span><span class="cx"> 
</span><ins>+    _testStepFailureDescription: function(failedStep)
+    {
+        if (!failedStep.failureCount)
+            return BuildbotIteration.TestSteps[failedStep.name] + &quot; failed&quot;;
+        if (failedStep.failureCount === 1)
+            return BuildbotIteration.TestSteps[failedStep.name] + &quot; failure&quot;;
+        return BuildbotIteration.TestSteps[failedStep.name] + &quot; failures&quot;;
+    },
+
+    _testStepFailureDescriptionWithCount: function(failedStep)
+    {
+        if (!failedStep.failureCount)
+            return this._testStepFailureDescription(failedStep);
+        if (failedStep.tooManyFailures) {
+            // E.g. &quot;50+ layout test failures&quot;, preventing line breaks around the &quot;+&quot;.
+            return failedStep.failureCount + &quot;\ufeff\uff0b\u00a0&quot; + this._testStepFailureDescription(failedStep);
+        }
+        // E.g. &quot;1 layout test failure&quot;, preventing line break after the number.
+        return failedStep.failureCount + &quot;\u00a0&quot; + this._testStepFailureDescription(failedStep);
+    },
+
</ins><span class="cx">     _popoverContentForLayoutTestRegressions: function(iteration)
</span><span class="cx">     {
</span><span class="cx">         var hasTestHistory = typeof testHistory !== &quot;undefined&quot;;
</span><span class="lines">@@ -263,7 +267,7 @@
</span><span class="cx">         return true;
</span><span class="cx">     },
</span><span class="cx"> 
</span><del>-    _presentPopoverForMultipleFailureKinds: function(element, popover, iteration)
</del><ins>+    _presentPopoverForGenericTestFailures: function(element, popover, iteration)
</ins><span class="cx">     {
</span><span class="cx">         function addResultKind(message, url) {
</span><span class="cx">             var line = document.createElement(&quot;a&quot;);
</span><span class="lines">@@ -274,52 +278,19 @@
</span><span class="cx">             content.appendChild(line);            
</span><span class="cx">         }
</span><span class="cx"> 
</span><del>-        var layoutTestResults = iteration.layoutTestResults || {failureCount: 0};
-        var javascriptTestResults = iteration.javascriptTestResults || {failureCount: 0};
-        var apiTestResults = iteration.apiTestResults || {failureCount: 0};
-        var platformAPITestResults = iteration.platformAPITestResults || {failureCount: 0};
-        var pythonTestResults = iteration.pythonTestResults || {failureCount: 0};
-        var perlTestResults = iteration.perlTestResults || {errorOccurred: false};
-        var bindingTestResults = iteration.bindingTestResults || {errorOccurred: false};
-
</del><span class="cx">         var content = document.createElement(&quot;div&quot;);
</span><span class="cx">         content.className = &quot;test-results-popover&quot;;
</span><span class="cx"> 
</span><span class="cx">         this._addIterationHeadingToPopover(iteration, content);
</span><span class="cx">         this._addDividerToPopover(content);
</span><span class="cx"> 
</span><del>-        if (layoutTestResults.failureCount) {
-            var message = (layoutTestResults.tooManyFailures ? layoutTestResults.failureCount + &quot;\uff0b&quot; : layoutTestResults.failureCount) + &quot;\u00a0&quot; +
-                (layoutTestResults.failureCount === 1 ? &quot;layout test failure&quot; : &quot;layout test failures&quot;);
-            addResultKind(message, iteration.queue.buildbot.layoutTestResultsURLForIteration(iteration));
-        }
</del><ins>+        iteration.failedTestSteps.forEach(function(failedStep) {
+            if (failedStep.name === &quot;layout-test&quot;)
+                addResultKind(this._testStepFailureDescriptionWithCount(failedStep), iteration.queue.buildbot.layoutTestResultsURLForIteration(iteration));
+            else
+                addResultKind(this._testStepFailureDescriptionWithCount(failedStep), failedStep.URL);
+        }, this);
</ins><span class="cx"> 
</span><del>-        if (javascriptTestResults.failureCount) {
-            var message = javascriptTestResults.failureCount + &quot;\u00a0&quot; + (javascriptTestResults.failureCount === 1 ? &quot;javascript test failure&quot; : &quot;javascript test failures&quot;);
-            addResultKind(message, iteration.queue.buildbot.javascriptTestResultsURLForIteration(iteration));
-        }
-
-        if (apiTestResults.failureCount) {
-            var message = apiTestResults.failureCount + &quot;\u00a0&quot; + (apiTestResults.failureCount === 1 ? &quot;api test failure&quot; : &quot;api test failures&quot;);
-            addResultKind(message, iteration.queue.buildbot.apiTestResultsURLForIteration(iteration));
-        }
-
-        if (platformAPITestResults.failureCount) {
-            var message = platformAPITestResults.failureCount + &quot;\u00a0&quot; + (platformAPITestResults.failureCount === 1 ? &quot;platform api test failure&quot; : &quot;platform api test failures&quot;);
-            addResultKind(message, iteration.queue.buildbot.platformAPITestResultsURLForIteration(iteration));
-        }
-
-        if (pythonTestResults.failureCount) {
-            var message = pythonTestResults.failureCount + &quot;\u00a0&quot; + (pythonTestResults.failureCount === 1 ? &quot;webkitpy test failure&quot; : &quot;webkitpy test failures&quot;);
-            addResultKind(message, iteration.queue.buildbot.webkitpyTestResultsURLForIteration(iteration));
-        }
-
-        if (perlTestResults.errorOccurred)
-            addResultKind(&quot;webkitperl tests failed&quot;, iteration.queue.buildbot.webkitperlTestResultsURLForIteration(iteration));
-
-        if (bindingTestResults.errorOccurred)
-            addResultKind(&quot;bindings tests failed&quot;, iteration.queue.buildbot.bindingsTestResultsURLForIteration(iteration));
-
</del><span class="cx">         var rect = Dashboard.Rect.rectFromClientRect(element.getBoundingClientRect());
</span><span class="cx">         popover.content = content;
</span><span class="cx">         popover.present(rect, [Dashboard.RectEdge.MIN_Y, Dashboard.RectEdge.MAX_Y, Dashboard.RectEdge.MAX_X, Dashboard.RectEdge.MIN_X]);
</span></span></pre></div>
<a id="trunkToolsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Tools/ChangeLog (180958 => 180959)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/ChangeLog        2015-03-03 22:45:59 UTC (rev 180958)
+++ trunk/Tools/ChangeLog        2015-03-03 23:22:59 UTC (rev 180959)
</span><span class="lines">@@ -1,3 +1,51 @@
</span><ins>+2015-03-03  Alexey Proskuryakov  &lt;ap@apple.com&gt;
+
+        build.webkit.org/dashboard: Don't repeatedly handle each test type
+        https://bugs.webkit.org/show_bug.cgi?id=142211
+
+        Reviewed by Tim Horton and Matt Hanson.
+
+        * BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/Buildbot.js:
+        (Buildbot.prototype.javascriptTestResultsURLForIteration): Deleted.
+        (Buildbot.prototype.apiTestResultsURLForIteration): Deleted.
+        (Buildbot.prototype.platformAPITestResultsURLForIteration): Deleted.
+        (Buildbot.prototype.webkitpyTestResultsURLForIteration): Deleted.
+        (Buildbot.prototype.webkitperlTestResultsURLForIteration): Deleted.
+        (Buildbot.prototype.bindingsTestResultsURLForIteration): Deleted.
+        Removed functions that build a link to test step results. The buildbot provides
+        these links in JSON.
+
+        * BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/BuildbotIteration.js:
+        (BuildbotIteration): Put failing tests into an array, instead of named variables.
+        (BuildbotIteration.ProductiveSteps): Removed step names that are not used on build.webkit.org.
+        We can easily add them to the map as needed.
+        (BuildbotIteration.TestSteps): Added a list of test steps to be displayed by test queues.
+        (BuildbotIteration.prototype._parseData): Moved code for parsing step results away
+        to BuildbotTestResults class. We used to parse here, build an intermediate data structure,
+        and then build a BuildbotTestResults object, which was strange.
+        (BuildbotIteration.prototype.loadLayoutTestResults): Ditto.
+
+        * BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/BuildbotQueueView.js:
+        Corrected an unrelated assertion that was buggy, and kept firing.
+
+        * BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/BuildbotTestResults.js:
+        (BuildbotTestResults):
+        (BuildbotTestResults.prototype._parseResults.resultSummarizer):
+        (BuildbotTestResults.prototype._parseResults):
+        (BuildbotTestResults.prototype.addFullLayoutTestResults):
+        Moved the code for parsing JSON results for a single step here.
+
+        * BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/BuildbotTesterQueueView.js:
+        (BuildbotTesterQueueView.prototype._testStepFailureDescription):
+        (BuildbotTesterQueueView.prototype._testStepFailureDescriptionWithCount):
+        (BuildbotTesterQueueView.prototype._presentPopoverForGenericTestFailures):
+        (BuildbotTesterQueueView.prototype.update.appendBuilderQueueStatus): Deleted.
+        (BuildbotTesterQueueView.prototype.update): Deleted.
+        (BuildbotTesterQueueView.prototype._presentPopoverForMultipleFailureKinds): Deleted.
+        Updated for the new data structures. One behavior change is that we now display individual
+        counts when multiple test kinds fail, e.g. &quot;1 javascript test failure, 83+ layout
+        test failures, 3 platform api test failures&quot;.
+
</ins><span class="cx"> 2015-03-03  Matthew Mirman  &lt;mmirman@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         JSC tests should not be repeated twice for each branch builder, and should if possible have their own queue.
</span></span></pre>
</div>
</div>

</body>
</html>