Cleanup: Move js for treemap and aggregate_results into own js files.
authorjparent@chromium.org <jparent@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 18 Mar 2013 21:43:19 +0000 (21:43 +0000)
committerjparent@chromium.org <jparent@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 18 Mar 2013 21:43:19 +0000 (21:43 +0000)
https://bugs.webkit.org/show_bug.cgi?id=112618

Reviewed by Dirk Pranke.

No functional changes, just moving the code into separate js file
rather than inlined in the html, so we can test it, make it more
modular, etc.  Other dashboard types are already done this way.

* TestResultServer/static-dashboards/aggregate_results.html:
* TestResultServer/static-dashboards/aggregate_results.js: Copied from Tools/TestResultServer/static-dashboards/aggregate_results.html.
(generatePage):
(handleValidHashParameter):
(htmlForBuilder):
(rawValuesHTML):
(chartHTML):
(filteredValues):
(chart):
(htmlForRevisionRows):
(wrapHTMLInTable):
(htmlForSummaryTable):
(valuesPerExpectation):
(htmlForTestType):
(htmlForTableRow):
(extendedEncode):
* TestResultServer/static-dashboards/treemap.html:
* TestResultServer/static-dashboards/treemap.js: Copied from Tools/TestResultServer/static-dashboards/treemap.html.
(humanReadableTime):
(convertToWebTreemapFormat):
(reverseSortByAverage):
(generatePage):
(focusPath):
(.switch.return):
(handleQueryParameterChange):
(extractName):
(fullName):
(handleFocus.):
(handleFocus):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@146127 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Tools/ChangeLog
Tools/TestResultServer/static-dashboards/aggregate_results.html
Tools/TestResultServer/static-dashboards/aggregate_results.js [new file with mode: 0644]
Tools/TestResultServer/static-dashboards/treemap.html
Tools/TestResultServer/static-dashboards/treemap.js [new file with mode: 0644]

index ac40dd8..4fb2b6a 100644 (file)
@@ -1,3 +1,44 @@
+2013-03-18  Julie Parent  <jparent@chromium.org>
+
+        Cleanup: Move js for treemap and aggregate_results into own js files.
+        https://bugs.webkit.org/show_bug.cgi?id=112618
+
+        Reviewed by Dirk Pranke.
+        
+        No functional changes, just moving the code into separate js file
+        rather than inlined in the html, so we can test it, make it more
+        modular, etc.  Other dashboard types are already done this way.
+
+        * TestResultServer/static-dashboards/aggregate_results.html:
+        * TestResultServer/static-dashboards/aggregate_results.js: Copied from Tools/TestResultServer/static-dashboards/aggregate_results.html.
+        (generatePage):
+        (handleValidHashParameter):
+        (htmlForBuilder):
+        (rawValuesHTML):
+        (chartHTML):
+        (filteredValues):
+        (chart):
+        (htmlForRevisionRows):
+        (wrapHTMLInTable):
+        (htmlForSummaryTable):
+        (valuesPerExpectation):
+        (htmlForTestType):
+        (htmlForTableRow):
+        (extendedEncode):
+        * TestResultServer/static-dashboards/treemap.html:
+        * TestResultServer/static-dashboards/treemap.js: Copied from Tools/TestResultServer/static-dashboards/treemap.html.
+        (humanReadableTime):
+        (convertToWebTreemapFormat):
+        (reverseSortByAverage):
+        (generatePage):
+        (focusPath):
+        (.switch.return):
+        (handleQueryParameterChange):
+        (extractName):
+        (fullName):
+        (handleFocus.):
+        (handleFocus):
+
 2013-03-18  Zan Dobersek  <zdobersek@igalia.com>
 
         [webkitpy] '/usr/bin/interdiff' output while running test-webkitpy
index 37975d2..f9e5e38 100644 (file)
@@ -55,252 +55,7 @@ img {
 <script src="string.js"></script>
 <script src="dashboard_base.js"></script>
 <script src="ui.js"></script>
-<script>
-// @fileoverview Creates a dashboard for tracking number of passes/failures per run.
-//
-// Currently, only webkit tests are supported, but adding other test types
-// should just require the following steps:
-//     -generate results.json for these tests
-//     -copy them to the appropriate location
-//     -add the builder name to the list of builders in dashboard_base.js.
-
-//////////////////////////////////////////////////////////////////////////////
-// Methods and objects from dashboard_base.js to override.
-//////////////////////////////////////////////////////////////////////////////
-function generatePage()
-{
-    var html = ui.html.testTypeSwitcher(true) + '<br>';
-    for (var builder in currentBuilders())
-        html += htmlForBuilder(builder);
-    document.body.innerHTML = html;
-}
-
-function handleValidHashParameter(key, value)
-{
-    switch(key) {
-    case 'rawValues':
-        g_currentState[key] = value == 'true';
-        return true;
-
-    default:
-        return false;
-    }
-}
-
-g_defaultDashboardSpecificStateValues = {
-    rawValues: false
-};
-
-function htmlForBuilder(builder)
-{
-    var results = g_resultsByBuilder[builder];
-    // Some keys were added later than others, so they don't have as many
-    // builds. Use the shortest.
-    // FIXME: Once 500 runs have finished, we can get rid of passing this
-    // around and just assume all keys have the same number of builders for a
-    // given builder.
-    var numColumns = results[ALL_FIXABLE_COUNT_KEY].length;
-    var html = '<div class=container><h2>' + builder + '</h2>';
-
-    if (g_currentState.rawValues)
-        html += rawValuesHTML(results, numColumns);
-    else {
-        html += '<a href="timeline_explorer.html' + (location.hash ? location.hash + '&' : '#') + 'builder=' + builder + '">' +
-            chartHTML(results, numColumns) + '</a>';
-    }
-
-    html += '</div>';
-    return html;
-}
-
-function rawValuesHTML(results, numColumns)
-{
-    var html = htmlForSummaryTable(results, numColumns) +
-        htmlForTestType(results, FIXABLE_COUNTS_KEY, FIXABLE_DESCRIPTION, numColumns);
-    if (isLayoutTestResults()) {
-        html += htmlForTestType(results, DEFERRED_COUNTS_KEY, DEFERRED_DESCRIPTION, numColumns) +
-            htmlForTestType(results, WONTFIX_COUNTS_KEY, WONTFIX_DESCRIPTION, numColumns);
-    }
-    return html;
-}
-
-function chartHTML(results, numColumns)
-{
-    var shouldShowWebKitRevisions = isTipOfTreeWebKitBuilder();
-    var revisionKey = shouldShowWebKitRevisions ? WEBKIT_REVISIONS_KEY : CHROME_REVISIONS_KEY;
-    var startRevision = results[revisionKey][numColumns - 1];
-    var endRevision = results[revisionKey][0];
-    var revisionLabel = shouldShowWebKitRevisions ? "WebKit Revision" : "Chromium Revision";
-
-    var fixable = results[FIXABLE_COUNT_KEY].slice(0, numColumns);
-    var html = chart("Total failing", {"": fixable}, revisionLabel, startRevision, endRevision);
-
-    var values = valuesPerExpectation(results[FIXABLE_COUNTS_KEY], numColumns);
-    // Don't care about number of passes for the charts.
-    delete(values['P']);
-
-    return html + chart("Detailed breakdown", values, revisionLabel, startRevision, endRevision);
-}
-
-var LABEL_COLORS = ['FF0000', '00FF00', '0000FF', '000000', 'FF6EB4', 'FFA812', '9B30FF', '00FFCC'];
-
-// FIXME: Find a better way to exclude outliers. This is just so we exclude
-// runs where every test failed.
-var MAX_VALUE = 10000;
-
-function filteredValues(values, desiredNumberOfPoints)
-{
-    // Filter out values to make the graph a bit more readable and to keep URLs
-    // from exceeding the browsers max length restriction.
-    var filterAmount = Math.floor(values.length / desiredNumberOfPoints);
-    if (filterAmount < 1)
-        return values;
-
-    return values.filter(function(element, index, array) {
-        // Include the most recent and oldest values and exclude outliers.
-        return (index % filterAmount == 0 || index == array.length - 1) && (array[index] < MAX_VALUE && array[index] != 0);
-    });
-}
-
-function chartUrl(title, values, revisionLabel, startRevision, endRevision, desiredNumberOfPoints) {
-    var maxValue = 0;
-    for (var expectation in values)
-        maxValue = Math.max(maxValue, Math.max.apply(null, filteredValues(values[expectation], desiredNumberOfPoints)));
-
-    var chartData = '';
-    var labels = '';
-    var numLabels = 0;
-
-    var first = true;
-    for (var expectation in values) {
-        chartData += (first ? 'e:' : ',') + extendedEncode(filteredValues(values[expectation], desiredNumberOfPoints).reverse(), maxValue);
-
-        if (expectation) {
-            numLabels++;
-            labels += (first ? '' : '|') + expectationsMap()[expectation];
-        }
-        first = false;
-    }
-
-    var url = "http://chart.apis.google.com/chart?cht=lc&chs=600x400&chd=" +
-            chartData + "&chg=15,15,1,3&chxt=x,x,y&chxl=1:||" + revisionLabel +
-            "|&chxr=0," + startRevision + "," + endRevision + "|2,0," + maxValue + "&chtt=" + title;
-
-
-    if (labels)
-        url += "&chdl=" + labels + "&chco=" + LABEL_COLORS.slice(0, numLabels).join(',');
-    return url;
-}
-
-function chart(title, values, revisionLabel, startRevision, endRevision)
-{
-    var desiredNumberOfPoints = 400;
-    var url = chartUrl(title, values, revisionLabel, startRevision, endRevision, desiredNumberOfPoints);
-
-    while (url.length >= 2048) {
-        // Decrease the desired number of points gradually until we get a URL that
-        // doesn't exceed chartserver's max URL length.
-        desiredNumberOfPoints = 3 / 4 * desiredNumberOfPoints;
-        url = chartUrl(title, values, revisionLabel, startRevision, endRevision, desiredNumberOfPoints);
-    }
-
-    return '<img src="' + url + '">';
-}
-
-function htmlForRevisionRows(results, numColumns)
-{
-    return htmlForTableRow('WebKit Revision', results[WEBKIT_REVISIONS_KEY].slice(0, numColumns)) +
-        htmlForTableRow('Chrome Revision', results[CHROME_REVISIONS_KEY].slice(0, numColumns));
-}
-
-function wrapHTMLInTable(description, html)
-{
-    return '<h3>' + description + '</h3><table><tbody>' + html + '</tbody></table>';
-}
-
-function htmlForSummaryTable(results, numColumns)
-{
-    var percent = [];
-    var fixable = results[FIXABLE_COUNT_KEY].slice(0, numColumns);
-    var allFixable = results[ALL_FIXABLE_COUNT_KEY].slice(0, numColumns);
-    for (var i = 0; i < numColumns; i++) {
-        var percentage = 100 * (allFixable[i] - fixable[i]) / allFixable[i];
-        // Round to the nearest tenth of a percent.
-        percent.push(Math.round(percentage * 10) / 10 + '%');
-    }
-    var html = htmlForRevisionRows(results, numColumns) +
-        htmlForTableRow('Percent passed', percent) +
-        htmlForTableRow('Failures (deduped)', fixable) +
-        htmlForTableRow('Fixable Tests', allFixable);
-    return wrapHTMLInTable('Summary', html);
-}
-
-function valuesPerExpectation(counts, numColumns)
-{
-    var values = {};
-    for (var i = 0; i < numColumns; i++) {
-        for (var expectation in expectationsMap()) {
-            if (expectation in counts[i]) {
-                var count = counts[i][expectation];
-                if (!values[expectation])
-                    values[expectation] = [];
-                values[expectation].push(count);
-            }
-        }
-    }
-    return values;
-}
-
-function htmlForTestType(results, key, description, numColumns)
-{
-    var counts = results[key];
-    var html = htmlForRevisionRows(results, numColumns);
-    var values = valuesPerExpectation(counts, numColumns);
-    for (var expectation in values)
-        html += htmlForTableRow(expectationsMap()[expectation], values[expectation]);
-    return wrapHTMLInTable(description, html);
-}
-
-function htmlForTableRow(columnName, values)
-{
-    return '<tr><td>' + columnName + '</td><td>' + values.join('</td><td>') + '</td></tr>';
-}
-
-// Taken from http://code.google.com/apis/chart/docs/data_formats.html.
-function extendedEncode(arrVals, maxVal)
-{
-    var map = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-.';
-    var mapLength = map.length;
-    var mapLengthSquared = mapLength * mapLength;
-
-    var chartData = '';
-
-    for (var i = 0, len = arrVals.length; i < len; i++) {
-        // In case the array vals were translated to strings.
-        var numericVal = new Number(arrVals[i]);
-        // Scale the value to maxVal.
-        var scaledVal = Math.floor(mapLengthSquared * numericVal / maxVal);
-
-        if(scaledVal > mapLengthSquared - 1)
-            chartData += "..";
-        else if (scaledVal < 0)
-            chartData += '__';
-        else {
-            // Calculate first and second digits and add them to the output.
-            var quotient = Math.floor(scaledVal / mapLength);
-            var remainder = scaledVal - mapLength * quotient;
-            chartData += map.charAt(quotient) + map.charAt(remainder);
-        }
-    }
-
-    return chartData;
-}
-
-window.addEventListener('load', function() {
-    var resourceLoader = new loader.Loader(intializeHistory);
-    resourceLoader.load();
-}, false);
-</script>
+<script src="aggregate_results.js"></script>
 </head>
 <body></body>
 </html>
diff --git a/Tools/TestResultServer/static-dashboards/aggregate_results.js b/Tools/TestResultServer/static-dashboards/aggregate_results.js
new file mode 100644 (file)
index 0000000..4af1327
--- /dev/null
@@ -0,0 +1,272 @@
+// Copyright (C) 2013 Google Inc. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// @fileoverview Creates a dashboard for tracking number of passes/failures per run.
+//
+// Currently, only webkit tests are supported, but adding other test types
+// should just require the following steps:
+//     -generate results.json for these tests
+//     -copy them to the appropriate location
+//     -add the builder name to the list of builders in dashboard_base.js.
+
+//////////////////////////////////////////////////////////////////////////////
+// Methods and objects from dashboard_base.js to override.
+//////////////////////////////////////////////////////////////////////////////
+function generatePage()
+{
+    var html = ui.html.testTypeSwitcher(true) + '<br>';
+    for (var builder in currentBuilders())
+        html += htmlForBuilder(builder);
+    document.body.innerHTML = html;
+}
+
+function handleValidHashParameter(key, value)
+{
+    switch(key) {
+    case 'rawValues':
+        g_currentState[key] = value == 'true';
+        return true;
+
+    default:
+        return false;
+    }
+}
+
+g_defaultDashboardSpecificStateValues = {
+    rawValues: false
+};
+
+function htmlForBuilder(builder)
+{
+    var results = g_resultsByBuilder[builder];
+    // Some keys were added later than others, so they don't have as many
+    // builds. Use the shortest.
+    // FIXME: Once 500 runs have finished, we can get rid of passing this
+    // around and just assume all keys have the same number of builders for a
+    // given builder.
+    var numColumns = results[ALL_FIXABLE_COUNT_KEY].length;
+    var html = '<div class=container><h2>' + builder + '</h2>';
+
+    if (g_currentState.rawValues)
+        html += rawValuesHTML(results, numColumns);
+    else {
+        html += '<a href="timeline_explorer.html' + (location.hash ? location.hash + '&' : '#') + 'builder=' + builder + '">' +
+            chartHTML(results, numColumns) + '</a>';
+    }
+
+    html += '</div>';
+    return html;
+}
+
+function rawValuesHTML(results, numColumns)
+{
+    var html = htmlForSummaryTable(results, numColumns) +
+        htmlForTestType(results, FIXABLE_COUNTS_KEY, FIXABLE_DESCRIPTION, numColumns);
+    if (isLayoutTestResults()) {
+        html += htmlForTestType(results, DEFERRED_COUNTS_KEY, DEFERRED_DESCRIPTION, numColumns) +
+            htmlForTestType(results, WONTFIX_COUNTS_KEY, WONTFIX_DESCRIPTION, numColumns);
+    }
+    return html;
+}
+
+function chartHTML(results, numColumns)
+{
+    var shouldShowWebKitRevisions = isTipOfTreeWebKitBuilder();
+    var revisionKey = shouldShowWebKitRevisions ? WEBKIT_REVISIONS_KEY : CHROME_REVISIONS_KEY;
+    var startRevision = results[revisionKey][numColumns - 1];
+    var endRevision = results[revisionKey][0];
+    var revisionLabel = shouldShowWebKitRevisions ? "WebKit Revision" : "Chromium Revision";
+
+    var fixable = results[FIXABLE_COUNT_KEY].slice(0, numColumns);
+    var html = chart("Total failing", {"": fixable}, revisionLabel, startRevision, endRevision);
+
+    var values = valuesPerExpectation(results[FIXABLE_COUNTS_KEY], numColumns);
+    // Don't care about number of passes for the charts.
+    delete(values['P']);
+
+    return html + chart("Detailed breakdown", values, revisionLabel, startRevision, endRevision);
+}
+
+var LABEL_COLORS = ['FF0000', '00FF00', '0000FF', '000000', 'FF6EB4', 'FFA812', '9B30FF', '00FFCC'];
+
+// FIXME: Find a better way to exclude outliers. This is just so we exclude
+// runs where every test failed.
+var MAX_VALUE = 10000;
+
+function filteredValues(values, desiredNumberOfPoints)
+{
+    // Filter out values to make the graph a bit more readable and to keep URLs
+    // from exceeding the browsers max length restriction.
+    var filterAmount = Math.floor(values.length / desiredNumberOfPoints);
+    if (filterAmount < 1)
+        return values;
+
+    return values.filter(function(element, index, array) {
+        // Include the most recent and oldest values and exclude outliers.
+        return (index % filterAmount == 0 || index == array.length - 1) && (array[index] < MAX_VALUE && array[index] != 0);
+    });
+}
+
+function chartUrl(title, values, revisionLabel, startRevision, endRevision, desiredNumberOfPoints) {
+    var maxValue = 0;
+    for (var expectation in values)
+        maxValue = Math.max(maxValue, Math.max.apply(null, filteredValues(values[expectation], desiredNumberOfPoints)));
+
+    var chartData = '';
+    var labels = '';
+    var numLabels = 0;
+
+    var first = true;
+    for (var expectation in values) {
+        chartData += (first ? 'e:' : ',') + extendedEncode(filteredValues(values[expectation], desiredNumberOfPoints).reverse(), maxValue);
+
+        if (expectation) {
+            numLabels++;
+            labels += (first ? '' : '|') + expectationsMap()[expectation];
+        }
+        first = false;
+    }
+
+    var url = "http://chart.apis.google.com/chart?cht=lc&chs=600x400&chd=" +
+            chartData + "&chg=15,15,1,3&chxt=x,x,y&chxl=1:||" + revisionLabel +
+            "|&chxr=0," + startRevision + "," + endRevision + "|2,0," + maxValue + "&chtt=" + title;
+
+
+    if (labels)
+        url += "&chdl=" + labels + "&chco=" + LABEL_COLORS.slice(0, numLabels).join(',');
+    return url;
+}
+
+function chart(title, values, revisionLabel, startRevision, endRevision)
+{
+    var desiredNumberOfPoints = 400;
+    var url = chartUrl(title, values, revisionLabel, startRevision, endRevision, desiredNumberOfPoints);
+
+    while (url.length >= 2048) {
+        // Decrease the desired number of points gradually until we get a URL that
+        // doesn't exceed chartserver's max URL length.
+        desiredNumberOfPoints = 3 / 4 * desiredNumberOfPoints;
+        url = chartUrl(title, values, revisionLabel, startRevision, endRevision, desiredNumberOfPoints);
+    }
+
+    return '<img src="' + url + '">';
+}
+
+function htmlForRevisionRows(results, numColumns)
+{
+    return htmlForTableRow('WebKit Revision', results[WEBKIT_REVISIONS_KEY].slice(0, numColumns)) +
+        htmlForTableRow('Chrome Revision', results[CHROME_REVISIONS_KEY].slice(0, numColumns));
+}
+
+function wrapHTMLInTable(description, html)
+{
+    return '<h3>' + description + '</h3><table><tbody>' + html + '</tbody></table>';
+}
+
+function htmlForSummaryTable(results, numColumns)
+{
+    var percent = [];
+    var fixable = results[FIXABLE_COUNT_KEY].slice(0, numColumns);
+    var allFixable = results[ALL_FIXABLE_COUNT_KEY].slice(0, numColumns);
+    for (var i = 0; i < numColumns; i++) {
+        var percentage = 100 * (allFixable[i] - fixable[i]) / allFixable[i];
+        // Round to the nearest tenth of a percent.
+        percent.push(Math.round(percentage * 10) / 10 + '%');
+    }
+    var html = htmlForRevisionRows(results, numColumns) +
+        htmlForTableRow('Percent passed', percent) +
+        htmlForTableRow('Failures (deduped)', fixable) +
+        htmlForTableRow('Fixable Tests', allFixable);
+    return wrapHTMLInTable('Summary', html);
+}
+
+function valuesPerExpectation(counts, numColumns)
+{
+    var values = {};
+    for (var i = 0; i < numColumns; i++) {
+        for (var expectation in expectationsMap()) {
+            if (expectation in counts[i]) {
+                var count = counts[i][expectation];
+                if (!values[expectation])
+                    values[expectation] = [];
+                values[expectation].push(count);
+            }
+        }
+    }
+    return values;
+}
+
+function htmlForTestType(results, key, description, numColumns)
+{
+    var counts = results[key];
+    var html = htmlForRevisionRows(results, numColumns);
+    var values = valuesPerExpectation(counts, numColumns);
+    for (var expectation in values)
+        html += htmlForTableRow(expectationsMap()[expectation], values[expectation]);
+    return wrapHTMLInTable(description, html);
+}
+
+function htmlForTableRow(columnName, values)
+{
+    return '<tr><td>' + columnName + '</td><td>' + values.join('</td><td>') + '</td></tr>';
+}
+
+// Taken from http://code.google.com/apis/chart/docs/data_formats.html.
+function extendedEncode(arrVals, maxVal)
+{
+    var map = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-.';
+    var mapLength = map.length;
+    var mapLengthSquared = mapLength * mapLength;
+
+    var chartData = '';
+
+    for (var i = 0, len = arrVals.length; i < len; i++) {
+        // In case the array vals were translated to strings.
+        var numericVal = new Number(arrVals[i]);
+        // Scale the value to maxVal.
+        var scaledVal = Math.floor(mapLengthSquared * numericVal / maxVal);
+
+        if(scaledVal > mapLengthSquared - 1)
+            chartData += "..";
+        else if (scaledVal < 0)
+            chartData += '__';
+        else {
+            // Calculate first and second digits and add them to the output.
+            var quotient = Math.floor(scaledVal / mapLength);
+            var remainder = scaledVal - mapLength * quotient;
+            chartData += map.charAt(quotient) + map.charAt(remainder);
+        }
+    }
+
+    return chartData;
+}
+
+window.addEventListener('load', function() {
+    var resourceLoader = new loader.Loader(intializeHistory);
+    resourceLoader.load();
+}, false);
index d095be7..81b6d2c 100644 (file)
@@ -112,258 +112,9 @@ td {
 <script src="string.js"></script>
 <script src="dashboard_base.js"></script>
 <script src="ui.js"></script>
-<script src='webtreemap.js'></script>
+<script src="treemap.js"></script>
+<script src="webtreemap.js"></script>
 
 <div id='header-container'></div>
 <p>Click on a box to zoom in. Click on the outermost box to zoom out. <a href="" onclick="showAverages();return false;">Show averages</a></p>
 <div id='map'></div>
-
-<script>
-var TEST_URL_BASE_PATH = "http://svn.webkit.org/repository/webkit/trunk/";
-
-function humanReadableTime(milliseconds)
-{
-    if (milliseconds < 1000)
-        return Math.floor(milliseconds) + 'ms';
-    else if (milliseconds < 60000)
-        return (milliseconds / 1000).toPrecision(2) + 's';
-
-    var minutes = Math.floor(milliseconds / 60000);
-    var seconds = Math.floor((milliseconds - minutes * 60000) / 1000);
-    return minutes + 'm' + seconds + 's';
-}
-
-// This looks like:
-// { "data": {"$area": (sum of all timings)},
-//   "name": (name of this node),
-//   "children": [ (child nodes, in the same format as this) ] }
-// childCount is added just to be includes in the node's name
-function convertToWebTreemapFormat(treename, tree, path)
-{
-    var total = 0;
-    var childCount = 0;
-    var children = [];
-    for (var name in tree) {
-        var treeNode = tree[name];
-        if (typeof treeNode == "number") {
-            var time = treeNode;
-            var node = {
-                "data": {"$area": time},
-                "name": name + " (" + humanReadableTime(time) + ")"
-            };
-            children.push(node);
-            total += time;
-            childCount++;
-        } else {
-            var newPath = path ? path + '/' + name : name;
-            var subtree = convertToWebTreemapFormat(name, treeNode, newPath);
-            children.push(subtree);
-            total += subtree["data"]["$area"];
-            childCount += subtree["childCount"];
-        }
-    }
-
-    children.sort(function(a, b) {
-        aTime = a.data["$area"]
-        bTime = b.data["$area"]
-        return bTime - aTime;
-    });
-
-    return {
-        "data": {"$area": total},
-        "name": treename + " (" + humanReadableTime(total) + " - " + childCount + " tests)",
-        "children": children,
-        "childCount": childCount,
-        "path": path
-    };
-}
-
-function listOfAllNonLeafNodes(tree, list)
-{
-    if (!tree.children)
-        return;
-
-    if (!list)
-        list = [];
-    list.push(tree);
-
-    tree.children.forEach(function(child) {
-        listOfAllNonLeafNodes(child, list);
-    });
-    return list;
-}
-
-function reverseSortByAverage(list)
-{
-    list.sort(function(a, b) {
-        var avgA = a.data['$area'] / a.childCount;
-        var avgB = b.data['$area'] / b.childCount;
-        return avgB - avgA;
-    });
-}
-
-function showAverages()
-{
-    if (!document.getElementById('map'))
-        return;
-
-    var table = document.createElement('table');
-    table.innerHTML = '<th>directory</th><th># tests</th><th>avg time / test</th>';
-
-    var allNodes = listOfAllNonLeafNodes(g_webTree);
-    reverseSortByAverage(allNodes);
-    allNodes.forEach(function(node) {
-        var average = node.data['$area'] / node.childCount;
-        if (average > 100 && node.childCount != 1) {
-            var tr = document.createElement('tr');
-            tr.innerHTML = '<td></td><td>' + node.childCount + '</td><td>' + humanReadableTime(average) + '</td>';
-            tr.querySelector('td').innerText = node.path;
-            table.appendChild(tr);
-        }
-    });
-
-    var map = document.getElementById('map');
-    map.parentNode.replaceChild(table, map);
-}
-
-var g_isGeneratingPage = false;
-var g_webTree;
-
-function generatePage()
-{
-    $('header-container').innerHTML = ui.html.testTypeSwitcher();
-
-    g_isGeneratingPage = true;
-
-    var rawTree = g_resultsByBuilder[g_currentState.builder || currentBuilderGroup().defaultBuilder()];
-    g_webTree = convertToWebTreemapFormat('LayoutTests', rawTree);
-    appendTreemap($('map'), g_webTree);
-
-    if (g_currentState.treemapfocus)
-        focusPath(g_webTree, g_currentState.treemapfocus)
-
-    g_isGeneratingPage = false;
-}
-
-function focusPath(tree, path)
-{
-    var parts = decodeURIComponent(path).split('/');
-    if (extractName(tree) != parts[0]) {
-        console.error('Could not focus tree rooted at ' + parts[0]);
-        return;
-    }
-
-    for (var i = 1; i < parts.length; i++) {
-        var children = tree.children;
-        for (var j = 0; j < children.length; j++) {
-            var child = children[j];
-            if (extractName(child) == parts[i]) {
-                tree = child;
-                focus(tree);
-                break;
-            }
-        }
-        if (j == children.length) {
-            console.error('Could not find tree at ' + parts[i]);
-            break;
-        }
-    }
-
-}
-
-function handleValidHashParameter(key, value)
-{
-    switch(key) {
-    case 'builder':
-        validateParameter(g_currentState, key, value,
-            function() { return value in currentBuilders(); });
-        return true;
-
-    case 'treemapfocus':
-        validateParameter(g_currentState, key, value,
-            function() {
-                // FIXME: There's probably a simpler regexp here. Just trying to match ascii + forward-slash.
-                // e.g. LayoutTests/foo/bar.html
-                return (value.match(/^(\w+\/\w*)*$/));
-            });
-        return true;
-
-    default:
-        return false;
-    }
-}
-
-g_defaultDashboardSpecificStateValues = {
-    builder: null,
-    treemapfocus: '',
-};
-
-DB_SPECIFIC_INVALIDATING_PARAMETERS = {
-    'testType': 'builder',
-    'group': 'builder'
-};
-
-function handleQueryParameterChange(params)
-{
-    for (var param in params) {
-        if (param != 'treemapfocus') {
-            $('map').innerHTML = 'Loading...';
-            return true;
-        }
-    }
-    return false;
-}
-
-function extractName(node)
-{
-    return node.name.split(' ')[0];
-}
-
-function fullName(node)
-{
-    var buffer = [extractName(node)];
-    while (node.parent) {
-        node = node.parent;
-        buffer.unshift(extractName(node));
-    }
-    return buffer.join('/');
-}
-
-function handleFocus(tree)
-{
-    var currentlyFocusedNode = $('focused-leaf');
-    if (currentlyFocusedNode)
-        currentlyFocusedNode.id = '';
-
-    if (!tree.children)
-        tree.dom.id = 'focused-leaf';
-
-    var name = fullName(tree);
-
-    if (!tree.children && !tree.extraDom && isLayoutTestResults()) {
-        tree.extraDom = document.createElement('pre');
-        tree.extraDom.className = 'extra-dom';
-        tree.dom.appendChild(tree.extraDom);
-
-        loader.request(TEST_URL_BASE_PATH + name,
-            function(xhr) {
-                tree.extraDom.onmousedown = function(e) {
-                    e.stopPropagation();
-                };
-                tree.extraDom.textContent = xhr.responseText;
-            },
-            function (xhr) {
-                tree.extraDom.textContent = "Could not load test."
-        });
-    }
-
-    // We don't want the focus calls during generatePage to try to modify the query state.
-    if (!g_isGeneratingPage)
-        setQueryParameter('treemapfocus', name);
-}
-
-window.addEventListener('load', function() {
-    var resourceLoader = new loader.Loader(intializeHistory);
-    resourceLoader.load();
-}, false);
-</script>
diff --git a/Tools/TestResultServer/static-dashboards/treemap.js b/Tools/TestResultServer/static-dashboards/treemap.js
new file mode 100644 (file)
index 0000000..a3d0fde
--- /dev/null
@@ -0,0 +1,275 @@
+// Copyright (C) 2013 Google Inc. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+var TEST_URL_BASE_PATH = "http://svn.webkit.org/repository/webkit/trunk/";
+
+function humanReadableTime(milliseconds)
+{
+    if (milliseconds < 1000)
+        return Math.floor(milliseconds) + 'ms';
+    else if (milliseconds < 60000)
+        return (milliseconds / 1000).toPrecision(2) + 's';
+
+    var minutes = Math.floor(milliseconds / 60000);
+    var seconds = Math.floor((milliseconds - minutes * 60000) / 1000);
+    return minutes + 'm' + seconds + 's';
+}
+
+// This looks like:
+// { "data": {"$area": (sum of all timings)},
+//   "name": (name of this node),
+//   "children": [ (child nodes, in the same format as this) ] }
+// childCount is added just to be includes in the node's name
+function convertToWebTreemapFormat(treename, tree, path)
+{
+    var total = 0;
+    var childCount = 0;
+    var children = [];
+    for (var name in tree) {
+        var treeNode = tree[name];
+        if (typeof treeNode == "number") {
+            var time = treeNode;
+            var node = {
+                "data": {"$area": time},
+                "name": name + " (" + humanReadableTime(time) + ")"
+            };
+            children.push(node);
+            total += time;
+            childCount++;
+        } else {
+            var newPath = path ? path + '/' + name : name;
+            var subtree = convertToWebTreemapFormat(name, treeNode, newPath);
+            children.push(subtree);
+            total += subtree["data"]["$area"];
+            childCount += subtree["childCount"];
+        }
+    }
+
+    children.sort(function(a, b) {
+        aTime = a.data["$area"]
+        bTime = b.data["$area"]
+        return bTime - aTime;
+    });
+
+    return {
+        "data": {"$area": total},
+        "name": treename + " (" + humanReadableTime(total) + " - " + childCount + " tests)",
+        "children": children,
+        "childCount": childCount,
+        "path": path
+    };
+}
+
+function listOfAllNonLeafNodes(tree, list)
+{
+    if (!tree.children)
+        return;
+
+    if (!list)
+        list = [];
+    list.push(tree);
+
+    tree.children.forEach(function(child) {
+        listOfAllNonLeafNodes(child, list);
+    });
+    return list;
+}
+
+function reverseSortByAverage(list)
+{
+    list.sort(function(a, b) {
+        var avgA = a.data['$area'] / a.childCount;
+        var avgB = b.data['$area'] / b.childCount;
+        return avgB - avgA;
+    });
+}
+
+function showAverages()
+{
+    if (!document.getElementById('map'))
+        return;
+
+    var table = document.createElement('table');
+    table.innerHTML = '<th>directory</th><th># tests</th><th>avg time / test</th>';
+
+    var allNodes = listOfAllNonLeafNodes(g_webTree);
+    reverseSortByAverage(allNodes);
+    allNodes.forEach(function(node) {
+        var average = node.data['$area'] / node.childCount;
+        if (average > 100 && node.childCount != 1) {
+            var tr = document.createElement('tr');
+            tr.innerHTML = '<td></td><td>' + node.childCount + '</td><td>' + humanReadableTime(average) + '</td>';
+            tr.querySelector('td').innerText = node.path;
+            table.appendChild(tr);
+        }
+    });
+
+    var map = document.getElementById('map');
+    map.parentNode.replaceChild(table, map);
+}
+
+var g_isGeneratingPage = false;
+var g_webTree;
+
+function generatePage()
+{
+    $('header-container').innerHTML = ui.html.testTypeSwitcher();
+
+    g_isGeneratingPage = true;
+
+    var rawTree = g_resultsByBuilder[g_currentState.builder || currentBuilderGroup().defaultBuilder()];
+    g_webTree = convertToWebTreemapFormat('LayoutTests', rawTree);
+    appendTreemap($('map'), g_webTree);
+
+    if (g_currentState.treemapfocus)
+        focusPath(g_webTree, g_currentState.treemapfocus)
+
+    g_isGeneratingPage = false;
+}
+
+function focusPath(tree, path)
+{
+    var parts = decodeURIComponent(path).split('/');
+    if (extractName(tree) != parts[0]) {
+        console.error('Could not focus tree rooted at ' + parts[0]);
+        return;
+    }
+
+    for (var i = 1; i < parts.length; i++) {
+        var children = tree.children;
+        for (var j = 0; j < children.length; j++) {
+            var child = children[j];
+            if (extractName(child) == parts[i]) {
+                tree = child;
+                focus(tree);
+                break;
+            }
+        }
+        if (j == children.length) {
+            console.error('Could not find tree at ' + parts[i]);
+            break;
+        }
+    }
+
+}
+
+function handleValidHashParameter(key, value)
+{
+    switch(key) {
+    case 'builder':
+        validateParameter(g_currentState, key, value,
+            function() { return value in currentBuilders(); });
+        return true;
+
+    case 'treemapfocus':
+        validateParameter(g_currentState, key, value,
+            function() {
+                // FIXME: There's probably a simpler regexp here. Just trying to match ascii + forward-slash.
+                // e.g. LayoutTests/foo/bar.html
+                return (value.match(/^(\w+\/\w*)*$/));
+            });
+        return true;
+
+    default:
+        return false;
+    }
+}
+
+g_defaultDashboardSpecificStateValues = {
+    builder: null,
+    treemapfocus: '',
+};
+
+DB_SPECIFIC_INVALIDATING_PARAMETERS = {
+    'testType': 'builder',
+    'group': 'builder'
+};
+
+function handleQueryParameterChange(params)
+{
+    for (var param in params) {
+        if (param != 'treemapfocus') {
+            $('map').innerHTML = 'Loading...';
+            return true;
+        }
+    }
+    return false;
+}
+
+function extractName(node)
+{
+    return node.name.split(' ')[0];
+}
+
+function fullName(node)
+{
+    var buffer = [extractName(node)];
+    while (node.parent) {
+        node = node.parent;
+        buffer.unshift(extractName(node));
+    }
+    return buffer.join('/');
+}
+
+function handleFocus(tree)
+{
+    var currentlyFocusedNode = $('focused-leaf');
+    if (currentlyFocusedNode)
+        currentlyFocusedNode.id = '';
+
+    if (!tree.children)
+        tree.dom.id = 'focused-leaf';
+
+    var name = fullName(tree);
+
+    if (!tree.children && !tree.extraDom && isLayoutTestResults()) {
+        tree.extraDom = document.createElement('pre');
+        tree.extraDom.className = 'extra-dom';
+        tree.dom.appendChild(tree.extraDom);
+
+        loader.request(TEST_URL_BASE_PATH + name,
+            function(xhr) {
+                tree.extraDom.onmousedown = function(e) {
+                    e.stopPropagation();
+                };
+                tree.extraDom.textContent = xhr.responseText;
+            },
+            function (xhr) {
+                tree.extraDom.textContent = "Could not load test."
+        });
+    }
+
+    // We don't want the focus calls during generatePage to try to modify the query state.
+    if (!g_isGeneratingPage)
+        setQueryParameter('treemapfocus', name);
+}
+
+window.addEventListener('load', function() {
+    var resourceLoader = new loader.Loader(intializeHistory);
+    resourceLoader.load();
+}, false);