summaryrefslogtreecommitdiffstats
path: root/webkit/tools
diff options
context:
space:
mode:
authorvictorw@chromium.org <victorw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-03-19 20:33:53 +0000
committervictorw@chromium.org <victorw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-03-19 20:33:53 +0000
commitb9954ef12fe3773bfb80a66aa1401e770da2b52d (patch)
tree7b5639da3826b86b7d1ba76ec7381b134b862d16 /webkit/tools
parentfa289830f1172f7daabe9565d416ff90dfce4704 (diff)
downloadchromium_src-b9954ef12fe3773bfb80a66aa1401e770da2b52d.zip
chromium_src-b9954ef12fe3773bfb80a66aa1401e770da2b52d.tar.gz
chromium_src-b9954ef12fe3773bfb80a66aa1401e770da2b52d.tar.bz2
Remove flakiness dashboard code from webkit\tools. The code was moved to trunk/tools/dashboards.
TBR=ojan TEST=none BUG=none git-svn-id: svn://svn.chromium.org/chrome/trunk/src@42142 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'webkit/tools')
-rw-r--r--webkit/tools/layout_tests/dashboards/aggregate_results.html128
-rw-r--r--webkit/tools/layout_tests/dashboards/dashboard_base.js560
-rw-r--r--webkit/tools/layout_tests/dashboards/flakiness_dashboard_tests.js139
-rw-r--r--webkit/tools/layout_tests/flakiness_dashboard.html2451
4 files changed, 0 insertions, 3278 deletions
diff --git a/webkit/tools/layout_tests/dashboards/aggregate_results.html b/webkit/tools/layout_tests/dashboards/aggregate_results.html
deleted file mode 100644
index 0b22755..0000000
--- a/webkit/tools/layout_tests/dashboards/aggregate_results.html
+++ /dev/null
@@ -1,128 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-
-<head>
- <title>Layout test passing status</title>
- <style>
- h2 {
- margin: 0;
- }
- h3 {
- margin-bottom: 0;
- }
- .container {
- border: 2px solid;
- margin: 3px 0;
- }
- </style>
- <script src="dashboard_base.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 = getHTMLForTestTypeSwitcher() + '<br>';
- for (var builder in builders) {
- html += getHTMLForBuilder(builder);
- }
- document.body.innerHTML = html;
- }
-
-
- function getHTMLForBuilder(builder) {
- var results = resultsByBuilder[builder];
- // Some keys were added later than others, so they don't have as many
- // builds. Use the shortest.
- // TODO(ojan): 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>' +
- getHTMLForSummaryTable(results, numColumns) +
- getHTMLForTestType(results, FIXABLE_COUNTS_KEY, FIXABLE_DESCRIPTION,
- numColumns);
- if (isLayoutTestResults()) {
- html +=
- getHTMLForTestType(results, DEFERRED_COUNTS_KEY,
- DEFERRED_DESCRIPTION, numColumns) +
- getHTMLForTestType(results, WONTFIX_COUNTS_KEY, WONTFIX_DESCRIPTION,
- numColumns);
- }
- return html + '</div>';
- }
-
- function getHTMLForRevisionRows(results, numColumns) {
- return getHTMLForTableRow('WebKit Revision',
- results[WEBKIT_REVISIONS_KEY].slice(0, numColumns)) +
- getHTMLForTableRow('Chrome Revision',
- results[CHROME_REVISIONS_KEY].slice(0, numColumns));
- }
-
- function wrapHTMLInTable(description, html) {
- return '<h3>' + description + '</h3><table><tbody>' + html +
- '</tbody></table>';
- }
-
- function getHTMLForSummaryTable(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 = getHTMLForRevisionRows(results, numColumns) +
- getHTMLForTableRow('Percent passed', percent) +
- getHTMLForTableRow('Failures (deduped)', fixable) +
- getHTMLForTableRow('Fixable Tests', allFixable);
- return wrapHTMLInTable('Summary', html);
- }
-
- function getHTMLForTestType(results, key, description, numColumns) {
- // TODO(dglazkov): Make these pretty canvasy graphs.
- var counts = results[key];
- var html = getHTMLForRevisionRows(results, numColumns);
-
- var valuesForEachRow = {};
- for (var i = 0; i < numColumns; i++) {
- for (var expectation in getExpectationsMap()) {
- if (expectation in counts[i]) {
- var count = counts[i][expectation];
-
- if (!valuesForEachRow[expectation])
- valuesForEachRow[expectation] = [];
-
- valuesForEachRow[expectation].push(count);
- }
- }
- }
-
- for (var expectation in valuesForEachRow) {
- html += getHTMLForTableRow(getExpectationsMap()[expectation],
- valuesForEachRow[expectation]);
- }
-
- return wrapHTMLInTable(description, html);
- }
-
- function getHTMLForTableRow(columnName, values) {
- return '<tr><td>' + columnName + '</td><td>' + values.join('</td><td>') +
- '</td></tr>';
- }
-
- </script>
- </head>
- <body></body>
-</html> \ No newline at end of file
diff --git a/webkit/tools/layout_tests/dashboards/dashboard_base.js b/webkit/tools/layout_tests/dashboards/dashboard_base.js
deleted file mode 100644
index f3049f6..0000000
--- a/webkit/tools/layout_tests/dashboards/dashboard_base.js
+++ /dev/null
@@ -1,560 +0,0 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-/**
- * @fileoverview Base JS file for pages that want to parse the results JSON
- * from the testing bots. This deals with generic utility functions, visible
- * history, popups and appending the script elements for the JSON files.
- *
- * The calling page is expected to implement three "abstract" functions/objects.
- * generatePage, validateHashParameter and defaultStateValues.
- */
-var pageLoadStartTime = Date.now();
-
-/**
- * Generates the contents of the dashboard. The page should override this with
- * a function that generates the page assuming all resources have loaded.
- */
-function generatePage() {
-}
-
-/**
- * Takes a key and a value and sets the currentState[key] = value iff key is
- * a valid hash parameter and the value is a valid value for that key.
- *
- * @return {boolean} Whether the key what inserted into the currentState.
- */
-function handleValidHashParameter(key, value) {
- return false;
-}
-
-/**
- * Default hash parameters for the page. The page should override this to create
- * default states.
- */
-var defaultStateValues = {};
-
-
-//////////////////////////////////////////////////////////////////////////////
-// CONSTANTS
-//////////////////////////////////////////////////////////////////////////////
-var GTEST_EXPECTATIONS_MAP_ = {
- 'P': 'PASS',
- 'F': 'FAIL',
- 'N': 'NO DATA',
- 'X': 'DISABLED'
-};
-
-var LAYOUT_TEST_EXPECTATIONS_MAP_ = {
- 'P': 'PASS',
- 'N': 'NO DATA',
- 'X': 'SKIP',
- 'T': 'TIMEOUT',
- 'F': 'TEXT',
- 'C': 'CRASH',
- 'I': 'IMAGE',
- 'Z': 'IMAGE+TEXT',
- // We used to glob a bunch of expectations into "O" as OTHER. Expectations
- // are more precise now though and it just means MISSING.
- 'O': 'MISSING'
-};
-
-// Keys in the JSON files.
-var WONTFIX_COUNTS_KEY = 'wontfixCounts';
-var FIXABLE_COUNTS_KEY = 'fixableCounts';
-var DEFERRED_COUNTS_KEY = 'deferredCounts';
-var WONTFIX_DESCRIPTION = 'Tests never to be fixed (WONTFIX)';
-var FIXABLE_DESCRIPTION = 'All tests for this release';
-var DEFERRED_DESCRIPTION = 'All deferred tests (DEFER)';
-var FIXABLE_COUNT_KEY = 'fixableCount';
-var ALL_FIXABLE_COUNT_KEY = 'allFixableCount';
-var CHROME_REVISIONS_KEY = 'chromeRevision';
-var WEBKIT_REVISIONS_KEY = 'webkitRevision';
-
-var TEST_TYPES = ['app_unittests', 'courgette_unittests', 'googleurl_unittests',
- 'installer_util_unittests', 'ipc_tests', 'layout_test_results',
- 'media_unittests', 'mini_installer_test', 'printing_unittests',
- 'sync_unit_tests', 'ui_tests', 'unit_tests'];
-
-// Enum for indexing into the run-length encoded results in the JSON files.
-// 0 is where the count is length is stored. 1 is the value.
-var RLE = {
- LENGTH: 0,
- VALUE: 1
-}
-
-/**
- * @return {boolean} Whether the value represents a failing result.
- */
-function isFailingResult(value) {
- return 'FSTOCIZ'.indexOf(value) != -1;
-}
-
-function reloadWindowIfKeyHasNewValue(key) {
- // Changing certain keys requires reloading the page since it would result
- // in pulling different JSON files.
- if (oldState && oldState[key] != currentState[key])
- window.location.reload();
-}
-
-/**
- * Takes a key and a value and sets the currentState[key] = value iff key is
- * a valid hash parameter and the value is a valid value for that key. Handles
- * cross-dashboard parameters then falls back to calling
- * handleValidHashParameter for dashboard-specific parameters.
- *
- * @return {boolean} Whether the key what inserted into the currentState.
- */
-function handleValidHashParameterWrapper(key, value) {
- switch(key) {
- case 'testType':
- validateParameter(currentState, key, value,
- function() {
- return TEST_TYPES.indexOf(value) != -1;
- });
-
- return true;
-
- case 'useTestData':
- case 'useV8Canary':
- case 'useWebKitCanary':
- currentState[key] = value == 'true';
- return true;
-
- case 'referringBuilder':
- if (stringContains(value, "(webkit.org)")) {
- currentState.useWebKitCanary = true;
- reloadWindowIfKeyHasNewValue('useWebKitCanary');
- return true;
- }
-
- if (stringContains(value, "(V8-Latest)")) {
- currentState.useV8Canary = true;
- reloadWindowIfKeyHasNewValue('useV8Canary');
- return true;
- }
-
- return false;
-
- case 'buildDir':
- currentState['testType'] = 'layout-test-results';
- if (value === 'Debug' || value == 'Release') {
- currentState[key] = value;
- return true;
- } else {
- return false;
- }
-
- default:
- return handleValidHashParameter(key, value);
- }
-}
-
-var defaultCrossDashboardStateValues = {
- testType: 'layout_test_results',
- useWebKitCanary: false,
- useV8Canary: false,
- buildDir : '',
-}
-
-// Generic utility functions.
-function $(id) {
- return document.getElementById(id);
-}
-
-function stringContains(a, b) {
- return a.indexOf(b) != -1;
-}
-
-function caseInsensitiveContains(a, b) {
- return a.match(new RegExp(b, 'i'));
-}
-
-function startsWith(a, b) {
- return a.indexOf(b) == 0;
-}
-
-function endsWidth(a, b) {
- return a.lastIndexOf(b) == a.length - b.length;
-}
-
-function isValidName(str) {
- return str.match(/[A-Za-z0-9\-\_,]/);
-}
-
-function trimString(str) {
- return str.replace(/^\s+|\s+$/g, '');
-}
-
-function validateParameter(state, key, value, validateFn) {
- if (validateFn()) {
- state[key] = value;
- } else {
- console.log(key + ' value is not valid: ' + value);
- }
-}
-
-/**
- * Parses window.location.hash and set the currentState values appropriately.
- */
-function parseParameters(parameterStr) {
- oldState = currentState;
- currentState = {};
-
- var params = window.location.hash.substring(1).split('&');
- var invalidKeys = [];
- for (var i = 0; i < params.length; i++) {
- var thisParam = params[i].split('=');
- if (thisParam.length != 2) {
- console.log('Invalid query parameter: ' + params[i]);
- continue;
- }
-
- var key = thisParam[0];
- var value = decodeURIComponent(thisParam[1]);
- if (!handleValidHashParameterWrapper(key, value))
- invalidKeys.push(key + '=' + value);
- }
-
- if (invalidKeys.length)
- console.log("Invalid query parameters: " + invalidKeys.join(','));
-
- fillMissingValues(currentState, defaultCrossDashboardStateValues);
- fillMissingValues(currentState, defaultStateValues);
-}
-
-function getDefaultValue(key) {
- if (key in defaultStateValues)
- return defaultStateValues[key];
- return defaultCrossDashboardStateValues[key];
-}
-
-function fillMissingValues(to, from) {
- for (var state in from) {
- if (!(state in to))
- to[state] = from[state];
- }
-}
-
-function appendScript(path) {
- var script = document.createElement('script');
- script.src = path;
- document.getElementsByTagName('head')[0].appendChild(script);
-}
-
-// Permalinkable state of the page.
-var currentState;
-
-// Saved value of previous currentState. This is used to detect changing from
-// one set of builders to another, which requires reloading the page.
-var oldState;
-
-// Parse cross-dashboard parameters before using them.
-parseParameters();
-
-function isLayoutTestResults() {
- return currentState.testType == 'layout_test_results';
-}
-
-var defaultBuilderName, builders, builderBase, expectationsBuilder;
-if (currentState.buildDir) {
- // If buildDir is set, point to the results.json and expectations.json in the
- // local tree. Useful for debugging changes to the python JSON generator.
- defaultBuilderName = 'DUMMY_BUILDER_NAME';
- builders = {'DUMMY_BUILDER_NAME': ''};
- var loc = document.location.toString();
- var offset = loc.indexOf('webkit/');
- builderBase = loc.substr(loc, offset + 7) + currentState.buildDir + "/";
-} else {
- builderBase = 'http://build.chromium.org/buildbot/';
-
- switch (currentState.testType) {
- case 'layout_test_results':
- // Map of builderName (the name shown in the waterfall)
- // to builderPath (the path used in the builder's URL)
- // TODO(ojan): Make this switch based off of the testType.
-
- // For expectationsBuilder, list the fastest builder so that we
- // always have the most up to date expectations.
- if (currentState.useWebKitCanary) {
- defaultBuilderName = 'Webkit (webkit.org)';
- expectationsBuilder = 'Webkit Linux (webkit.org)';
- builders = {
- 'Webkit (webkit.org)': 'webkit-rel-webkit-org',
- 'Webkit Linux (webkit.org)': 'webkit-rel-linux-webkit-org',
- 'Webkit Mac (webkit.org)': 'webkit-rel-mac-webkit-org'
- };
- } else if (currentState.useV8Canary) {
- defaultBuilderName = 'Webkit (V8-Latest)';
- expectationsBuilder = 'Webkit Linux (V8-Latest)';
- builders = {
- 'Webkit (V8-Latest)': 'webkit-rel-v8',
- 'Webkit Mac (V8-Latest)': 'webkit-rel-mac-v8',
- 'Webkit Linux (V8-Latest)': 'webkit-rel-linux-v8'
- }
- } else {
- defaultBuilderName = 'Webkit';
- expectationsBuilder = 'Webkit Linux';
- builders = {
- 'Webkit': 'webkit-rel',
- 'Webkit (dbg)(1)': 'webkit-dbg-1',
- 'Webkit (dbg)(2)': 'webkit-dbg-2',
- 'Webkit (dbg)(3)': 'webkit-dbg-3',
- 'Webkit Linux': 'webkit-rel-linux',
- 'Webkit Linux (dbg)(1)': 'webkit-dbg-linux-1',
- 'Webkit Linux (dbg)(2)': 'webkit-dbg-linux-2',
- 'Webkit Linux (dbg)(3)': 'webkit-dbg-linux-3',
- 'Webkit Mac10.5': 'webkit-rel-mac5',
- 'Webkit Mac10.5 (dbg)(1)': 'webkit-dbg-mac5-1',
- 'Webkit Mac10.5 (dbg)(2)': 'webkit-dbg-mac5-2',
- 'Webkit Mac10.5 (dbg)(3)': 'webkit-dbg-mac5-3'
- };
- }
-
- break;
-
- case 'app_unittests':
- case 'courgette_unittests':
- case 'googleurl_unittests':
- case 'installer_util_unittests':
- case 'ipc_tests':
- case 'media_unittests':
- case 'mini_installer_test':
- case 'printing_unittests':
- case 'sync_unit_tests':
- case 'ui_tests':
- case 'unit_tests':
- defaultBuilderName = 'XP Tests';
- builders = {
- // TODO(ojan): Uncomment this and add more builders.
- // This builder current has bogus data (the builderName in the JSON
- // files points to "Vista Tests" or "XP Tests" instead of "Chromium XP"
- // 'Chromium XP': 'chromium-tests',
- 'Vista Tests': 'chromium-rel-vista-tests',
- 'XP Tests': 'chromium-rel-xp-tests'
- }
-
- break;
-
- default:
- console.log('invalid testType paramenter');
- }
-}
-
-// Append JSON script elements.
-var resultsByBuilder = {};
-var expectationsByTest = {};
-function ADD_RESULTS(builds) {
- for (var builderName in builds) {
- if (builderName != 'version')
- resultsByBuilder[builderName] = builds[builderName];
- }
-
- handleResourceLoad();
-}
-
-function getPathToBuilderResultsFile(builderName) {
- var rootPath, subPath;
- if (isLayoutTestResults()) {
- rootPath = currentState.testType;
- subPath = '';
- } else {
- rootPath = 'gtest_results';
- subPath = currentState.testType + '/';
- }
- return builderBase + rootPath + '/' + builders[builderName] + '/' + subPath;
-}
-
-// Only webkit tests have a sense of test expectations.
-var waitingOnExpectations = isLayoutTestResults();
-if (waitingOnExpectations) {
- function ADD_EXPECTATIONS(expectations) {
- waitingOnExpectations = false;
- expectationsByTest = expectations;
- handleResourceLoad();
- }
-}
-
-function appendJSONScriptElements() {
- parseParameters();
-
- if (currentState.useTestData) {
- appendScript('dashboards/flakiness_dashboard_tests.js');
- return;
- }
-
- for (var builderName in builders) {
- appendScript(getPathToBuilderResultsFile(builderName) + 'results.json');
- }
-
- // Grab expectations file from the fastest builder, which is Linux release
- // right now. Could be changed to any other builder if needed.
- if (waitingOnExpectations) {
- appendScript(getPathToBuilderResultsFile(expectationsBuilder) +
- 'expectations.json');
- }
-}
-
-var hasDoneInitialPageGeneration = false;
-
-function handleResourceLoad() {
- // In case we load a results.json that's not in the list of builders,
- // make sure to only call handleLocationChange once from the resource loads.
- if (!hasDoneInitialPageGeneration)
- handleLocationChange();
-}
-
-function handleLocationChange() {
- if (waitingOnExpectations)
- return;
-
- for (var build in builders) {
- if (!resultsByBuilder[build])
- return;
- }
-
- hasDoneInitialPageGeneration = true;
- parseParameters();
- generatePage();
-}
-
-window.onhashchange = handleLocationChange;
-
-/**
- * Sets the page state. Takes varargs of key, value pairs.
- */
-function setQueryParameter(var_args) {
- for (var i = 0; i < arguments.length; i += 2) {
- currentState[arguments[i]] = arguments[i + 1];
- }
- // Note: We use window.location.hash rather that window.location.replace
- // because of bugs in Chrome where extra entries were getting created
- // when back button was pressed and full page navigation was occuring.
- // TODO: file those bugs.
- window.location.hash = getPermaLinkURLHash();
-}
-
-function getPermaLinkURLHash() {
- return '#' + joinParameters(currentState);
-}
-
-function joinParameters(stateObject) {
- var state = [];
- for (var key in stateObject) {
- var value = stateObject[key];
- if (value != getDefaultValue(key))
- state.push(key + '=' + encodeURIComponent(value));
- }
- return state.join('&');
-}
-
-function logTime(msg, startTime) {
- console.log(msg + ': ' + (Date.now() - startTime));
-}
-
-function hidePopup() {
- var popup = $('popup');
- if (popup)
- popup.parentNode.removeChild(popup);
-}
-
-function showPopup(e, html) {
- var popup = $('popup');
- if (!popup) {
- popup = document.createElement('div');
- popup.id = 'popup';
- document.body.appendChild(popup);
- }
-
- // Set html first so that we can get accurate size metrics on the popup.
- popup.innerHTML = html;
-
- var targetRect = e.target.getBoundingClientRect();
-
- var x = Math.min(targetRect.left - 10,
- document.documentElement.clientWidth - popup.offsetWidth);
- x = Math.max(0, x);
- popup.style.left = x + document.body.scrollLeft + 'px';
-
- var y = targetRect.top + targetRect.height;
- if (y + popup.offsetHeight > document.documentElement.clientHeight) {
- y = targetRect.top - popup.offsetHeight;
- }
- y = Math.max(0, y);
- popup.style.top = y + document.body.scrollTop + 'px';
-}
-
-/**
- * Create a new function with some of its arguements
- * pre-filled.
- * Taken from goog.partial in the Closure library.
- * @param {Function} fn A function to partially apply.
- * @param {...*} var_args Additional arguments that are partially
- * applied to fn.
- * @return {!Function} A partially-applied form of the function bind() was
- * invoked as a method of.
- */
-function partial(fn, var_args) {
- var args = Array.prototype.slice.call(arguments, 1);
- return function() {
- // Prepend the bound arguments to the current arguments.
- var newArgs = Array.prototype.slice.call(arguments);
- newArgs.unshift.apply(newArgs, args);
- return fn.apply(this, newArgs);
- };
-};
-
-/**
- * Returns the keys of the object/map/hash.
- * Taken from goog.object.getKeys in the Closure library.
- *
- * @param {Object} obj The object from which to get the keys.
- * @return {!Array.<string>} Array of property keys.
- */
-function getKeys(obj) {
- var res = [];
- for (var key in obj) {
- res.push(key);
- }
- return res;
-}
-
-/**
- * Returns the appropriate expectatiosn map for the current testType.
- */
-function getExpectationsMap() {
- return isLayoutTestResults() ? LAYOUT_TEST_EXPECTATIONS_MAP_ :
- GTEST_EXPECTATIONS_MAP_;
-}
-
-/**
- * Returns the HTML for the select element to switch to different testTypes.
- */
-function getHTMLForTestTypeSwitcher() {
- var html = '<select onchange="setQueryParameter(\'testType\',' +
- 'this[this.selectedIndex].innerHTML);window.location.reload()">';
-
- TEST_TYPES.forEach(function(elem) {
- html += '<option' + (elem == currentState.testType ? ' selected' : '') +
- '>' + elem + '</option>';
- });
- return html + '</select>';
-}
-
-appendJSONScriptElements();
-
-document.addEventListener('mousedown', function(e) {
- // Clear the open popup, unless the click was inside the popup.
- var popup = $('popup');
- if (popup && e.target != popup &&
- !(popup.compareDocumentPosition(e.target) & 16)) {
- hidePopup();
- }
- }, false);
-
-window.addEventListener('load', function() {
- // This doesn't seem totally accurate as there is a race between
- // onload firing and the last script tag being executed.
- logTime('Time to load JS', pageLoadStartTime);
- }, false);
diff --git a/webkit/tools/layout_tests/dashboards/flakiness_dashboard_tests.js b/webkit/tools/layout_tests/dashboards/flakiness_dashboard_tests.js
deleted file mode 100644
index e2f3ae0..0000000
--- a/webkit/tools/layout_tests/dashboards/flakiness_dashboard_tests.js
+++ /dev/null
@@ -1,139 +0,0 @@
-/**
- * @fileoverview Poor-man's unittests for some of the trickier logic bits of
- * the flakiness dashboard.
- *
- * Currently only tests processExpectations and populateExpectationsData.
- * A test just consists of calling runExpectationsTest with the appropriate
- * arguments.
- */
-
-// TODO(ojan): Add more test cases.
-// TODO(ojan): Add tests for processMissingAndExtraExpectations
-
-/**
- * Clears out the global objects modified or used by processExpectations and
- * populateExpectationsData. A bit gross since it's digging into implementation
- * details, but it's good enough for now.
- */
-function setupExpectationsTest() {
- allExpectations = null;
- allTests = null;
- expectationsByTest = {};
- resultsByBuilder = {};
- builders = {};
-}
-
-/**
- * Processes the expectations for a test and asserts that the final expectations
- * and modifiers we apply to a test match what we expect.
- *
- * @param {string} builder Builder the test is run on.
- * @param {string} test The test name.
- * @param {string} expectations Sorted string of what the expectations for this
- * test ought to be for this builder.
- * @param {string} modifiers Sorted string of what the modifiers for this
- * test ought to be for this builder.
- */
-function runExpectationsTest(builder, test, expectations, modifiers) {
- builders[builder] = true;
-
- // Put in some dummy results. processExpectations expects the test to be
- // there.
- var tests = {};
- tests[test] = {'results': [[100, 'F']], 'times': [[100, 0]]};
- resultsByBuilder[builder] = {'tests': tests};
-
- processExpectations();
- var resultsForTest = createResultsObjectForTest(test, builder);
- populateExpectationsData(resultsForTest);
-
- assertEquals(resultsForTest, resultsForTest.expectations, expectations);
- assertEquals(resultsForTest, resultsForTest.modifiers, modifiers);
-}
-
-function assertEquals(resultsForTest, actual, expected) {
- if (expected !== actual) {
- throw Error('Builder: ' + resultsForTest.builder + ' test: ' +
- resultsForTest.test + ' got: ' + actual + ' expected: ' + expected);
- }
-}
-
-function throwError(resultsForTests, actual, expected) {
-}
-
-function testReleaseFail() {
- var builder = 'Webkit';
- var test = 'foo/1.html';
- var expectationsArray = [
- {'modifiers': 'RELEASE', 'expectations': 'FAIL'}
- ];
- expectationsByTest[test] = expectationsArray;
- runExpectationsTest(builder, test, 'FAIL', 'RELEASE');
-}
-
-function testReleaseFailDebugCrashReleaseBuilder() {
- var builder = 'Webkit';
- var test = 'foo/1.html';
- var expectationsArray = [
- {'modifiers': 'RELEASE', 'expectations': 'FAIL'},
- {'modifiers': 'DEBUG', 'expectations': 'CRASH'}
- ];
- expectationsByTest[test] = expectationsArray;
- runExpectationsTest(builder, test, 'FAIL', 'RELEASE');
-}
-
-function testReleaseFailDebugCrashDebugBuilder() {
- var builder = 'Webkit(dbg)';
- var test = 'foo/1.html';
- var expectationsArray = [
- {'modifiers': 'RELEASE', 'expectations': 'FAIL'},
- {'modifiers': 'DEBUG', 'expectations': 'CRASH'}
- ];
- expectationsByTest[test] = expectationsArray;
- runExpectationsTest(builder, test, 'CRASH', 'DEBUG');
-}
-
-function testOverrideJustBuildType() {
- var test = 'bar/1.html';
- expectationsByTest['bar'] = [
- {'modifiers': 'WONTFIX', 'expectations': 'FAIL PASS TIMEOUT'}
- ];
- expectationsByTest[test] = [
- {'modifiers': 'WONTFIX MAC', 'expectations': 'FAIL'},
- {'modifiers': 'LINUX DEBUG', 'expectations': 'CRASH'},
- ];
- runExpectationsTest('Webkit', test, 'FAIL PASS TIMEOUT', 'WONTFIX');
- runExpectationsTest('Webkit (dbg)(3)', test, 'FAIL PASS TIMEOUT', 'WONTFIX');
- runExpectationsTest('Webkit Linux', test, 'FAIL PASS TIMEOUT', 'WONTFIX');
- runExpectationsTest('Webkit Linux (dbg)(3)', test, 'CRASH', 'LINUX DEBUG');
- runExpectationsTest('Webkit Mac10.5', test, 'FAIL', 'WONTFIX MAC');
- runExpectationsTest('Webkit Mac10.5 (dbg)(3)', test, 'FAIL', 'WONTFIX MAC');
-}
-
-function runTests() {
- document.body.innerHTML = '<pre id=unittest-results></pre>';
- for (var name in window) {
- if (typeof window[name] == 'function' && /^test/.test(name)) {
- setupExpectationsTest();
-
- var test = window[name];
- var error = null;
-
- try {
- test();
- } catch (err) {
- error = err;
- }
-
- var result = error ? error.toString() : 'PASSED';
- $('unittest-results').insertAdjacentHTML("beforeEnd",
- name + ': ' + result + '\n');
- }
- }
-}
-
-if (document.readyState == 'complete') {
- runTests();
-} else {
- window.addEventListener('load', runTests, false);
-}
diff --git a/webkit/tools/layout_tests/flakiness_dashboard.html b/webkit/tools/layout_tests/flakiness_dashboard.html
deleted file mode 100644
index c5c4922..0000000
--- a/webkit/tools/layout_tests/flakiness_dashboard.html
+++ /dev/null
@@ -1,2451 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-
-<head>
- <title>Chromium/WebKit Test History</title>
- <style>
- body {
- font-family: arial;
- font-size: 13px;
- }
- h2 {
- font-size: 16px;
- margin-bottom: .25em;
- }
- h3 {
- font-size: 13px;
- margin: 0;
- }
- #max-results-form {
- display: inline;
- }
- #max-results-input {
- width: 30px;
- }
- #tests-form {
- display: -webkit-box;
- }
- #tests-form > * {
- display: -webkit-box;
- }
- #tests-form > div {
- -webkit-box-flex: 0;
- }
- #tests-input {
- -webkit-box-flex: 1;
- }
- .test-link {
- white-space: normal;
- }
- .test-link, .options-container {
- padding: 0 2px;
- }
- .test-table {
- white-space: nowrap;
- }
- .test-table {
- width: 100%;
- }
- .test-table tr {
- border: 1px solid red;
- background-color: #E8E8E8;
- }
- .test-table tbody tr:hover {
- opacity: .7;
- }
- .test-table th {
- -webkit-user-select: none;
- -moz-user-select: none;
- }
- .link, .sortable .header-text {
- color: blue;
- text-decoration: underline;
- cursor: pointer;
- }
- .table-header-content,
- .table-header-content * {
- display: -webkit-box;
- }
- .table-header-content * {
- -webkit-box-flex: 1;
- cursor: pointer;
- white-space: normal;
- }
- .results {
- cursor: pointer;
- padding: 0;
- font-size: 10px;
- text-align: center;
- }
- #legend {
- position: fixed;
- top: 5px;
- right: 5px;
- width: 400px;
- padding: 2px;
- border: 2px solid grey;
- background-color: white;
- z-index: 1;
- }
- #legend ul, #legend ol {
- margin-top: 0;
- margin-bottom: 5px;
- }
- #legend-contents * {
- margin: 3px 0;
- padding: 0 2px;
- float: left;
- border: 1px solid grey;
- }
- #builders * {
- margin: 0 5px;
- display: inline-block;
- white-space: nowrap;
- }
- .test-table .wrong-expectations,
- .wrong-expectations {
- background-color: #pink;
- }
- .P {
- background-color: #8fdf5f;
- }
- .N {
- background-color: #fff;
- }
- .C {
- background-color: #ffc343;
- }
- .T {
- background-color: #fffc6c;
- }
- .I {
- background-color: #69f;
- }
- .S {
- background-color: #c6c;
- }
- .F {
- background-color: #e98080;
- }
- .O {
- background-color: #8a7700;
- }
- .Z {
- background-color: #96f;
- }
- .merge {
- background-color: black;
- color: white;
- }
- table .merge {
- width: 1px;
- }
- .separator {
- border: 1px solid lightgray;
- height: 0px;
- }
- .different-platform {
- color: gray;
- font-size: 10px;
- }
- .current-builder {
- font-weight: bold;
- }
- #passing-tests {
- -webkit-column-count: 3;
- -webkit-column-gap: 25px;
- -webkit-column-rule: 1px dashed black;
- -moz-column-count: 3;
- -moz-column-gap: 25px;
- -moz-column-rule: 1px dashed black;
- }
- .not-found {
- color: red;
- font-size: large;
- }
- #loading-ui {
- position: fixed;
- top: 0;
- left: 0;
- background-color: yellow;
- padding: 5px;
- text-align: center;
- font-weight: bold;
- }
- #popup {
- background-color: white;
- z-index: 1;
- position: absolute;
- border: 3px solid grey;
- padding: 3px;
- -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.5);
- -moz-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.5);
- -webkit-border-radius: 5px;
- -moz-border-radius: 5px;
- }
- #popup > ul {
- margin: 0;
- padding-left: 20px;
- }
- .expectations-container {
- clear: both;
- }
- .expectations-item {
- float: left;
- border: 1px solid grey;
- }
- .expectations-item .expectation {
- width: 400px;
- height: 300px;
- border: 0;
- border-top: 1px solid grey;
- }
- .expectations-item .large {
- width: 800px;
- height: 600px;
- }
- .expectations-item .checksum {
- height: 30px;
- }
- .non-webkit-results {
- width: 99%;
- }
- .used-platform {
- float: right;
- color: darkblue;
- margin: 0 5px;
- }
- .expectations-title {
- /* Hack to make a containing block for absolute positioned elements. */
- position: relative;
- overflow: hidden;
- clear: both;
- }
- .title {
- /* Position absolutely so the container does not grow to contain this. */
- position: absolute;
- }
- .platforms {
- position: absolute;
- background-color: white;
- right: 0;
- z-index: 1;
- }
- .file-bug {
- font-weight: bold;
- font-size: 11px;
- }
- </style>
-
- <!-- Flakiness dashboard is adding to appengine, this is a temporary solution
- to load dashboard_base.js from current and sub dirs so it works in both cases.
- -->
- <script src="dashboard_base.js"></script>
- <script src="dashboards/dashboard_base.js"></script>
- <script>
- /**
- * @fileoverview Creates a dashboard for multiple runs of a given set of tests
- * on the buildbots. Pulls in JSONP-ish files with the results for running
- * tests on a given builder (i.e. ADD_RESULTS(json_here)) and the expectations
- * for all tests on all builders (i.e. ADD_EXPECTATIONS(json_here)).
- *
- * This shows flakiness of the tests as well as runtimes for slow tests.
- *
- * Also, each column in the dashboard is sortable.
- *
- * Currently, only webkit tests are supported, but adding other test types
- * should just require the following steps:
- * -generate results.json and expectations.json for these tests
- * -copy them to the appropriate location
- * -add the builder name to the list of builders below.
- */
-
- //////////////////////////////////////////////////////////////////////////////
- // CONSTANTS
- //////////////////////////////////////////////////////////////////////////////
- var ALL = 'ALL';
- var FORWARD = 'forward';
- var BACKWARD = 'backward';
- var GTEST_FLAKY_MARKER = '\.FLAKY_';
- var CHROME_TEST_BASE_URL = 'http://src.chromium.org/viewvc/chrome/trunk/' +
- 'src/webkit/data/layout_tests/platform/';
- var TEST_URL_BASE_PATH =
- 'http://trac.webkit.org/projects/webkit/browser/trunk/LayoutTests/';
- var BUILDERS_BASE_PATH =
- 'http://build.chromium.org/buildbot/waterfall/builders/';
- var TEST_RESULTS_BASE_PATH =
- 'http://build.chromium.org/buildbot/layout_test_results/';
- var PLATFORMS = {
- 'MAC': 'MAC',
- 'LINUX': 'LINUX',
- 'WIN': 'WIN',
- 'WIN-XP': 'WIN-XP',
- 'WIN-VISTA': 'WIN-VISTA'
- };
- var PLATFORM_FALLBACKS = {
- 'WIN': 'ALL',
- 'WIN-XP': 'WIN',
- 'WIN-VISTA': 'WIN',
- 'LINUX': 'ALL',
- 'MAC': 'ALL'
- };
- var BUILD_TYPES = {'DEBUG': 'DBG', 'RELEASE': 'RELEASE'};
- var BASE_TABLE_HEADERS = ['missing', 'extra', 'slowest run', '% fail',
- 'flakiness (numbers are runtimes in seconds)'];
- var LAYOUT_TEST_TABLE_HEADERS = ['bugs', 'modifiers',
- 'expectations'].concat(BASE_TABLE_HEADERS);
- var MIN_SECONDS_FOR_SLOW_TEST = 4;
- var MIN_SECONDS_FOR_SLOW_TEST_DEBUG = 2 * MIN_SECONDS_FOR_SLOW_TEST;
- var FAIL_RESULTS = ['IMAGE', 'IMAGE+TEXT', 'TEXT', 'SIMPLIFIED', 'MISSING'];
- var CHUNK_SIZE = 25;
-
- //////////////////////////////////////////////////////////////////////////////
- // Methods and objects from dashboard_base.js to override.
- //////////////////////////////////////////////////////////////////////////////
- function generatePage() {
- if (currentState.useTestData)
- return;
-
- document.body.innerHTML = '<div id="loading-ui">LOADING...</div>';
-
- if (currentState.tests) {
- generatePageForIndividualTests(getIndividualTests());
- } else if (currentState.expectationsUpdate) {
- generatePageForExpectationsUpdate();
- } else {
- createTableHeadersArray('test');
- generatePageForBuilder(currentState.builder);
- }
-
- var maxResultsInput = $('max-results-input');
- if (maxResultsInput)
- maxResultsInput.value = getMaxResults();
-
- for (var builder in builders) {
- processTestResultsForBuilderAsync(builder);
- }
- }
-
- function handleValidHashParameter(key, value) {
- switch(key) {
- case 'tests':
- validateParameter(currentState, key, value,
- function() {
- return isValidName(value);
- });
-
- return true;
-
- case 'builder':
- validateParameter(currentState, key, value,
- function() {
- return value in builders;
- });
-
- return true;
-
- case 'sortColumn':
- validateParameter(currentState, key, value,
- function() {
- for (var i = 0; i < BASE_TABLE_HEADERS.length; i++) {
- if (value ==
- getSortColumnFromTableHeader(BASE_TABLE_HEADERS[i]))
- return true;
- }
- return value == 'test' || value == 'builder';
- });
-
- return true;
-
- case 'sortOrder':
- validateParameter(currentState, key, value,
- function() {
- return value == FORWARD || value == BACKWARD;
- });
-
- return true;
-
- case 'resultsHeight':
- case 'maxResults':
- case 'updateIndex':
- validateParameter(currentState, key, Number(value),
- function() {
- return value.match(/^\d+$/);
- });
-
- return true;
-
- case 'showCorrectExpectations':
- case 'showWrongExpectations':
- case 'showExpectations':
- case 'showFlaky':
- case 'showLargeExpectations':
- case 'legacyExpectationsSemantics':
- case 'showSkipped':
- case 'showSlow':
- case 'showWontFixSkip':
- case 'expectationsUpdate':
- currentState[key] = value == 'true';
-
- return true;
-
- default:
- return false;
- }
- }
-
- defaultStateValues = {
- sortOrder: BACKWARD,
- sortColumn: 'flakiness',
- showExpectations: false,
- showFlaky: true,
- showLargeExpectations: false,
- legacyExpectationsSemantics: true,
- showCorrectExpectations: !isLayoutTestResults(),
- showWrongExpectations: !isLayoutTestResults(),
- showWontFixSkip: !isLayoutTestResults(),
- showSlow: !isLayoutTestResults(),
- showSkipped: !isLayoutTestResults(),
- expectationsUpdate: false,
- updateIndex: 0,
- builder: defaultBuilderName,
- resultsHeight: 300
- };
-
- //////////////////////////////////////////////////////////////////////////////
- // GLOBALS
- //////////////////////////////////////////////////////////////////////////////
-
- // Text to put inside the header for each column of the test table.
- var tableHeaders;
- var perBuilderPlatformAndBuildType = {};
- var perBuilderFailures = {};
- // Map of builder to arrays of tests that are listed in the expectations file
- // but have for that builder.
- var perBuilderWithExpectationsButNoFailures = {};
- // Map of builder to arrays of paths that are skipped. This shows the raw
- // path used in test_expectations.txt rather than the test path since we
- // don't actually have any data here for skipped tests.
- var perBuilderSkippedPaths = {};
- // Maps test path to an array of {builder, testResults} objects.
- var testToResultsMap = {};
- var numFlakyTestsPerBuilder = {};
- // Tests that the user wants to update expectations for.
- var confirmedTests = {};
-
- function createResultsObjectForTest(test, builder) {
- return {
- test: test,
- builder: builder,
- // HTML for display of the results in the flakiness column
- html: '',
- flips: 0,
- slowestTime: 0,
- slowestNonTimeoutCrashTime: 0,
- meetsExpectations: true,
- isWontFixSkip: false,
- isFlaky: false,
- // Sorted string of missing expectations
- missing: '',
- // String of extra expectations (i.e. expectations that never occur).
- extra: '',
- // HTML for bug IDs for this test for all platforms
- bugsHTML: '',
- // HTML for expectations for this test for all platforms
- expectationsHTML: '',
- // HTML for modifiers for this test for all platforms
- modifiersHTML: '',
- rawResults: '',
- percentFailed: 0
- };
- }
-
- function getMaxResults() {
- // Max results is just a way of making the page performant. For now, the
- // non layout test results pages are fast even showing max results.
- if (!isLayoutTestResults() || currentState.expectationsUpdate ||
- currentState.tests && !currentState.maxResults) {
- return 750;
- }
- return currentState.maxResults || 200;
- }
-
- function getMatchingElement(stringToMatch, elementsMap) {
- for (var element in elementsMap) {
- if (stringContains(stringToMatch, elementsMap[element]))
- return element;
- }
- }
-
- function getPlatformAndBuildType(builderName) {
- if (!perBuilderPlatformAndBuildType[builderName]) {
- // If the build name does not contain a platform
- // or build type, assume Windows Release.
- var currentBuildUppercase = builderName.toUpperCase();
- var platform = getMatchingElement(currentBuildUppercase, PLATFORMS) ||
- 'WIN';
- var buildType = getMatchingElement(currentBuildUppercase, BUILD_TYPES) ||
- 'RELEASE';
- perBuilderPlatformAndBuildType[builderName] = {platform: platform,
- buildType: buildType};
- }
- return perBuilderPlatformAndBuildType[builderName];
- }
-
- function isDebug(builderName) {
- return getPlatformAndBuildType(builderName).buildType == 'DEBUG';
- }
-
- /**
- * Returns the expectation string for the given single character result.
- * This string should match the expectations that are put into
- * test_expectations.py.
- *
- * For example, if we start explicitly listing IMAGE result failures,
- * this function should start returning 'IMAGE'.
- */
- function getExpectationsFileStringForResult(result) {
- // For the purposes of comparing against the expecations of a test,
- // consider simplified diff failures as just text failures since
- // the test_expectations file doesn't treat them specially.
- if (result == 'S')
- return 'TEXT';
-
- if (result == 'N')
- return '';
-
- return getExpectationsMap()[result];
- }
-
- // Map of all tests to true values. This is just so we can have the list of
- // all tests across all the builders.
- var allTests;
-
- /**
- * Returns a map of all tests to true values. This is just so we can have the
- * list of all tests across all the builders.
- */
- function getAllTests() {
- if (!allTests) {
- allTests = {};
- for (var builder in builders) {
- addTestsForBuilder(builder, allTests);
- }
- }
- return allTests;
- }
-
- /**
- * Returns an array of tests to be displayed in the individual tests view.
- * Note that a directory can be listed as a test, so we expand that into all
- * tests in the directory.
- */
- function getIndividualTests() {
- if (!currentState.tests)
- return [];
-
- switch (currentState.tests) {
- case 'allSlowTests':
- return getSlowTests();
-
- case 'allNeedSlowTests':
- return getNeedSlowTests();
-
- case 'allIncorrectExpectations':
- return getAllTestsWithIncorrectExpectations();
-
- default:
- return getIndividualTestsForSubstringList();
- }
- }
-
- function getIndividualTestsForSubstringList() {
- // Convert windows slashes to unix slashes.
- var tests = currentState.tests.replace(/\\/g, '/');
- var separator = stringContains(tests, ' ') ? ' ' : ',';
- var testList = tests.split(separator);
-
- if (!isLayoutTestResults()) {
- testList.forEach(function(element) {
- // Make sure to search for the flaky and non-flaky names for a test.
- // e.g. for test Foo.Bar, also search for Foo.FLAKY_Bar.
- if (stringContains(element, GTEST_FLAKY_MARKER)) {
- testList.push(element.replace(GTEST_FLAKY_MARKER, '.'));
- } else {
- testList.push(element.replace('.', GTEST_FLAKY_MARKER));
- }
- });
- }
-
- // Put the tests into an object first and then move them into an array
- // as a way of deduping.
- var testsMap = {};
- for (var i = 0; i < testList.length; i++) {
- var path = testList[i];
-
- // Ignore whitespace entries as they'd match every test.
- if (path.match(/^\s*$/))
- continue;
-
- var allTests = getAllTests();
- var hasAnyMatches = false;
- for (var test in allTests) {
- if (caseInsensitiveContains(test, path)) {
- testsMap[test] = 1;
- hasAnyMatches = true;
- }
- }
-
- // If a path doesn't match any tests, then assume it's a full path
- // to a test that passes on all builders.
- if (!hasAnyMatches) {
- testsMap[path] = 1;
- }
- }
-
- var testsArray = [];
- for (var test in testsMap) {
- testsArray.push(test);
- }
- return testsArray;
- }
-
- function getAllTestsWithIncorrectExpectations() {
- return getAllTestsWithCondition(function(resultsForTest) {
- return !resultsForTest.meetsExpectations;
- });
- }
-
- function getSlowTests() {
- return getAllTestsWithCondition(isSlowTest);
- }
-
- function getNeedSlowTests() {
- return getAllTestsWithCondition(function(resultsForTest) {
- if (resultsForTest.isWontFixSkip)
- return false;
-
- return stringContains(resultsForTest.missing, 'SLOW') ||
- stringContains(resultsForTest.extra, 'SLOW');
- });
- }
-
- /**
- * Returns whether this test's slowest time is above the cutoff for
- * being a slow test.
- */
- function isSlowTest(resultsForTest) {
- var maxTime = isDebug(resultsForTest.builder) ?
- MIN_SECONDS_FOR_SLOW_TEST_DEBUG :
- MIN_SECONDS_FOR_SLOW_TEST;
- return resultsForTest.slowestNonTimeoutCrashTime > maxTime;
- }
-
- /**
- * Returns whether this test's slowest time is *well* below the cutoff for
- * being a slow test.
- */
- function isFastTest(resultsForTest) {
- var maxTime = isDebug(resultsForTest.builder) ?
- MIN_SECONDS_FOR_SLOW_TEST_DEBUG :
- MIN_SECONDS_FOR_SLOW_TEST;
- return resultsForTest.slowestNonTimeoutCrashTime < maxTime / 2;
- }
-
- function getAllTestsWithCondition(conditionFn) {
- processTestRunsForAllBuilders();
- var tests = getAllTests();
- var retVal = [];
- for (var test in tests) {
- var resultsArray = testToResultsMap[test];
- for (var i = 0; i < resultsArray.length; i++) {
- if (conditionFn(resultsArray[i])) {
- retVal.push(test);
- break;
- }
- }
- }
- return retVal;
- }
-
-
- /**
- * Adds all the tests for the given builder to the testMapToPopulate.
- */
- function addTestsForBuilder(builder, testMapToPopulate) {
- var tests = resultsByBuilder[builder].tests;
- for (var test in tests) {
- testMapToPopulate[test] = true;
- }
- }
-
- // Map of all tests to true values by platform and build type.
- // e.g. allTestsByPlatformAndBuildType['WIN']['DEBUG'] will have the union
- // of all tests run on the win-debug builders.
- var allTestsByPlatformAndBuildType = {};
- for (var platform in PLATFORMS) {
- allTestsByPlatformAndBuildType[platform] = {};
- }
-
- /**
- * Map of all tests to true values by platform and build type.
- * e.g. allTestsByPlatformAndBuildType['WIN']['DEBUG'] will have the union
- * of all tests run on the win-debug builders.
- */
- function getAllTestsWithSamePlatformAndBuildType(platform, buildType) {
- if (!allTestsByPlatformAndBuildType[platform][buildType]) {
- var tests = {};
- for (var thisBuilder in builders) {
- var thisBuilderBuildInfo = getPlatformAndBuildType(thisBuilder);
- if (thisBuilderBuildInfo.buildType == buildType &&
- thisBuilderBuildInfo.platform == platform) {
- addTestsForBuilder(thisBuilder, tests);
- }
- }
- allTestsByPlatformAndBuildType[platform][buildType] = tests;
- }
-
- return allTestsByPlatformAndBuildType[platform][buildType];
- }
-
- function addHTMLToIndividualOptionsArray(array, html, isCurrentPlatform) {
- if (html) {
- array.push('<div class="option' +
- (isCurrentPlatform ? '' : ' different-platform') + '">' + html +
- '</div>');
- }
- }
-
- function addHtmlToOptionsArrays(htmlArrays, expectations, modifiers, bugs,
- isCurrentPlatform) {
- addHTMLToIndividualOptionsArray(htmlArrays.expectations, expectations,
- isCurrentPlatform);
- addHTMLToIndividualOptionsArray(htmlArrays.modifiers, modifiers,
- isCurrentPlatform);
- addHTMLToIndividualOptionsArray(htmlArrays.bugs, getHtmlForBugs(bugs),
- isCurrentPlatform);
- }
-
- function getExpectations(test, platform, buildType) {
- var testObject = allExpectations[test];
- if (!testObject)
- return null;
-
- var platformObject;
- while (platform && !(platformObject = testObject[platform])) {
- platform = PLATFORM_FALLBACKS[platform];
- }
-
- if (platformObject) {
- if (platformObject[buildType])
- return platformObject[buildType]
-
- if (platformObject[ALL])
- return platformObject[ALL];
-
- if (testObject[ALL]) {
- var allPlatformObject = testObject[ALL];
- if (allPlatformObject[buildType])
- return allPlatformObject[buildType]
-
- if (allPlatformObject[ALL])
- return allPlatformObject[ALL]
- }
- }
- return null;
- }
-
- function populateExpectationsData(resultsObj) {
- var test = resultsObj.test;
-
- var buildInfo = getPlatformAndBuildType(resultsObj.builder);
- var platform = buildInfo.platform;
- var buildType = buildInfo.buildType;
-
- var thisPlatformExpectations = getExpectations(test, platform, buildType);
-
- var htmlArrays = {};
- htmlArrays.expectations = [];
- htmlArrays.modifiers = [];
- htmlArrays.bugs = [];
-
- var tests = resultsByBuilder[resultsObj.builder].tests;
-
- var testObject = allExpectations[test];
- var usedExpectations = {};
-
- for (var platform in allExpectations[test]) {
- var platformObject = testObject[platform];
- for (var buildType in platformObject) {
- var thisExpectations = platformObject[buildType];
- var modifiers = thisExpectations.modifiers;
-
- // A set of modifiers/expectations can apply to multiple platforms.
- // Only add HTML for each entry once.
- if (usedExpectations[modifiers])
- continue;
-
- usedExpectations[modifiers] = true;
-
- var bugs = modifiers.match(/BUG\d+/g);
- if (bugs) {
- for (var j = 0; j < bugs.length; j++) {
- modifiers = modifiers.replace(bugs[j], '');
- }
- modifiers = trimString(modifiers);
- bugs = bugs.join(' ');
- } else {
- bugs = '';
- }
-
- var expectations = thisExpectations.expectations;
- if (thisExpectations == thisPlatformExpectations) {
- resultsObj.bugs = bugs;
- resultsObj.expectations = expectations;
- resultsObj.modifiers = modifiers;
- resultsObj.isWontFixSkip = stringContains(modifiers, 'WONTFIX') ||
- stringContains(modifiers, 'SKIP');
- } else {
- addHtmlToOptionsArrays(htmlArrays, expectations, modifiers, bugs,
- false);
- }
- }
- }
-
- if (resultsObj.expectations) {
- addHtmlToOptionsArrays(htmlArrays, resultsObj.expectations,
- resultsObj.modifiers, resultsObj.bugs, true);
- }
-
- resultsObj.bugsHTML += htmlArrays.bugs.join('<div class=separator></div>');
- resultsObj.expectationsHTML +=
- htmlArrays.expectations.join('<div class=separator></div>');
- resultsObj.modifiersHTML +=
- htmlArrays.modifiers.join('<div class=separator></div>');
- }
-
- function addFallbacks(addFn, candidates, validValues) {
- var hasAnyValidValues = false;
- for (var i = 0; i < candidates.length; i++) {
- if (candidates[i] in validValues) {
- hasAnyValidValues = true;
- addFn(candidates[i]);
- }
- }
- if (!hasAnyValidValues)
- addFn(ALL);
- }
-
- function addTestToAllExpectations(test, expectations) {
- for (var j = 0; j < expectations.length; j++) {
- var modifiers = expectations[j].modifiers.split(' ');
- addFallbacks(function(platformKey) {
- addFallbacks(function(buildTypeKey) {
- // Setting the ALL key overrides any previously seen expectations.
- if (platformKey == ALL) {
- for (var i = 0; i < PLATFORMS.length; i++) {
- if (PLATFORMS[i] in allExpectations[test]) {
- // Setting the ALL key overrides any previously seen
- // expectations.
- if (buildTypeKey == ALL)
- allExpectations[test][PLATFORMS[i]] = {};
- }
- }
- } else if (buildTypeKey == ALL && allExpectations[test]) {
- allExpectations[test][platformKey] = {}
- }
-
- if (!allExpectations[test])
- allExpectations[test] = {};
-
- var testHolder = allExpectations[test];
- if (!testHolder[platformKey])
- testHolder[platformKey] = {}
-
- testHolder[platformKey][buildTypeKey] = expectations[j];
- }, modifiers, BUILD_TYPES);
- }, modifiers, PLATFORMS);
- }
- }
-
- /**
- * Data structure to hold the processed expectations.
- * allExpectations[testPath][platform][buildType] gets the object that has
- * expectations and modifiers properties for this platform/buildType.
- *
- * platform and buildType both go through fallback sets of keys from most
- * specific key to least specific. For example, on Windows Vista, we first
- * check the platform WIN-VISTA, if there's no such object, we check WIN,
- * then finally we check ALL. For build types, we check the current
- * buildType, then ALL.
- */
- var allExpectations;
-
- function processExpectations() {
- if (allExpectations)
- return allExpectations;
-
- allExpectations = {};
-
- var expectationsArray = [];
- for (var path in expectationsByTest) {
- expectationsArray.push(
- {path: path, expectations: expectationsByTest[path]});
- }
-
- // Sort the array to hit more specific paths last. More specific
- // paths (e.g. foo/bar/baz.html) override entries for less-specific ones
- // (e.g. foo/bar).
- expectationsArray.sort(getAlphanumericCompare('path'));
-
- var allTests = getAllTests();
- for (var i = 0; i < expectationsArray.length; i++) {
- var path = expectationsArray[i].path;
- var expectations = expectationsArray[i].expectations;
-
- var pathMatchesAnyTest = false;
- if (allTests[path]) {
- pathMatchesAnyTest = true;
- addTestToAllExpectations(path, expectations);
- } else {
- for (var test in allTests) {
- if (startsWith(test, path)) {
- pathMatchesAnyTest = true;
- addTestToAllExpectations(test, expectations);
- }
- }
- }
-
- if (!pathMatchesAnyTest)
- addTestToAllExpectations(path, expectations);
- }
- }
-
- function processMissingTestsWithExpectations(builder, platform, buildType) {
- var noFailures = [];
- var skipped = [];
-
- var allTestsForPlatformAndBuildType =
- getAllTestsWithSamePlatformAndBuildType(platform, buildType);
- for (var test in allExpectations) {
- var expectations = getExpectations(test, platform, buildType);
-
- if (!expectations)
- continue;
-
- // Test has expectations, but no result in the builders results.
- // This means it's either SKIP or passes on all builds.
- if (!allTestsForPlatformAndBuildType[test] &&
- !stringContains(expectations.modifiers, 'WONTFIX')) {
- if (stringContains(expectations.modifiers, 'SKIP')) {
- skipped.push(test);
- } else if (!expectations.expectations.match(/^\s*PASS\s*$/)) {
- // Don't show tests expected to always pass. This is used in ways like
- // the following:
- // foo/bar = FAIL
- // foo/bar/baz.html = PASS
- noFailures.push({test: test, expectations: expectations.expectations,
- modifiers: expectations.modifiers});
- }
- }
- }
-
- perBuilderSkippedPaths[builder] = skipped.sort();
- perBuilderWithExpectationsButNoFailures[builder] = noFailures.sort();
- }
-
- function processTestResultsForBuilderAsync(builder) {
- setTimeout(function() {
- processTestRunsForBuilder(builder);
- }, 0);
- }
-
- function processTestRunsForAllBuilders() {
- for (var builder in builders)
- processTestRunsForBuilder(builder);
- }
-
- function processTestRunsForBuilder(builderName) {
- if (perBuilderFailures[builderName]) {
- appendNumFlakyTests(builderName);
- return;
- }
-
- var start = Date.now();
- numFlakyTestsPerBuilder[builderName] = 0;
-
- processExpectations();
-
- var buildInfo = getPlatformAndBuildType(builderName);
- var platform = buildInfo.platform;
- var buildType = buildInfo.buildType;
- processMissingTestsWithExpectations(builderName, platform, buildType);
-
- var failures = [];
- var allTestsForThisBuilder = resultsByBuilder[builderName].tests;
-
- for (var test in allTestsForThisBuilder) {
- var resultsForTest = createResultsObjectForTest(test, builderName);
- populateExpectationsData(resultsForTest);
-
- var rawTest = resultsByBuilder[builderName].tests[test];
- resultsForTest.rawTimes = rawTest.times;
- var rawResults = rawTest.results;
- resultsForTest.rawResults = rawResults;
- resultsForTest.flips = rawResults.length - 1;
-
- var times = resultsForTest.rawTimes;
- var numTimesSeen = 0;
- var numResultsSeen = 0;
- var resultsIndex = 0;
- var currentResult;
- for (var i = 0;
- i < times.length && numTimesSeen < getMaxResults();
- i++) {
- numTimesSeen += times[i][RLE.LENGTH];
-
- while (rawResults[resultsIndex] && numTimesSeen > (numResultsSeen +
- rawResults[resultsIndex][RLE.LENGTH])) {
- numResultsSeen += rawResults[resultsIndex][RLE.LENGTH];
- resultsIndex++;
- }
-
- if (rawResults && rawResults[resultsIndex])
- currentResult = rawResults[resultsIndex][RLE.VALUE];
-
- var time = times[i][RLE.VALUE]
-
- // Ignore times for crashing/timeout runs for the sake of seeing if
- // a test should be marked slow.
- if (currentResult != 'C' && currentResult != 'T') {
- resultsForTest.slowestNonTimeoutCrashTime =
- Math.max(resultsForTest.slowestNonTimeoutCrashTime, time);
- }
- resultsForTest.slowestTime = Math.max(resultsForTest.slowestTime, time);
- }
-
- processMissingAndExtraExpectations(resultsForTest);
-
- if (resultsForTest.isFlaky)
- numFlakyTestsPerBuilder[builderName]++;
-
- failures.push(resultsForTest);
-
- if (!testToResultsMap[test])
- testToResultsMap[test] = [];
- testToResultsMap[test].push(resultsForTest);
- }
-
- appendNumFlakyTests(builderName);
- perBuilderFailures[builderName] = failures;
- logTime('processTestRunsForBuilder: ' + builderName, start);
- }
-
- function processMissingAndExtraExpectations(resultsForTest) {
- // Heuristic for determining whether expectations apply to a given test:
- // -If a test result happens < MIN_RUNS_FOR_FLAKE, then consider it a flaky
- // result and include it in the list of expected results.
- // -Otherwise, grab the first contiguous set of runs with the same result
- // for >= MIN_RUNS_FOR_FLAKE and ignore all following runs >=
- // MIN_RUNS_FOR_FLAKE.
- // This lets us rule out common cases of a test changing expectations for
- // a few runs, then being fixed or otherwise modified in a non-flaky way.
- var rawResults = resultsForTest.rawResults;
-
- // If the first result is no-data that means the test is skipped or is
- // being run on a different builder (e.g. moved from one shard to another).
- // Ignore these results since we have no real data about what's going on.
- if (rawResults[0][RLE.VALUE] == 'N')
- return;
-
- // Only consider flake if it doesn't happen twice in a row.
- var MIN_RUNS_FOR_FLAKE = 2;
- var resultsMap = {}
- var numResultsSeen = 0;
- var haveSeenNonFlakeResult = false;
- var numRealResults = 0;
- var failedCount = 0;
-
- var seenResults = {};
- for (var i = 0;
- i < rawResults.length && numResultsSeen < getMaxResults();
- i++) {
- var numResults = rawResults[i][RLE.LENGTH];
- numResultsSeen += numResults;
-
- var result = rawResults[i][RLE.VALUE];
- if (isFailingResult(result)) {
- failedCount += numResults;
- }
-
- var hasMinRuns = numResults >= MIN_RUNS_FOR_FLAKE;
- if (haveSeenNonFlakeResult && hasMinRuns) {
- continue;
- } else if (hasMinRuns) {
- haveSeenNonFlakeResult = true;
- } else if (!seenResults[result]) {
- // Only consider a short-lived result if we've seen it more than once.
- // Otherwise, we include lots of false-positives due to tests that fail
- // for a couple runs and then start passing.
- seenResults[result] = true;
- continue;
- }
-
- var expectation = getExpectationsFileStringForResult(result);
- resultsMap[expectation] = true;
- numRealResults++;
- }
-
- resultsForTest.isFlaky = numRealResults > 1;
- // Calculate the % of times the test failed - how flaky is it?
- resultsForTest.percentFailed =
- Math.round(failedCount / numResultsSeen * 100);
-
- var missingExpectations = [];
- var extraExpectations = [];
-
- if (isLayoutTestResults()) {
- var expectationsArray = resultsForTest.expectations ?
- resultsForTest.expectations.split(' ') : [];
- extraExpectations = expectationsArray.filter(
- function(element) {
- // TODO(ojan): Once all the FAIL lines are removed from
- // test_expectations.txt, delete all the legacyExpectationsSemantics
- // code.
- if (currentState.legacyExpectationsSemantics) {
- if (element == 'FAIL') {
- for (var i = 0; i < FAIL_RESULTS.length; i++) {
- if (resultsMap[FAIL_RESULTS[i]])
- return false;
- }
- return true;
- }
- }
-
- return element && !resultsMap[element] &&
- !stringContains(element, 'BUG');
- });
-
- for (var result in resultsMap) {
- var hasExpectation = false;
- for (var i = 0; i < expectationsArray.length; i++) {
- var expectation = expectationsArray[i];
- // TODO(ojan): Once all the FAIL lines are removed from
- // test_expectations.txt, delete all the legacyExpectationsSemantics
- // code.
- if (currentState.legacyExpectationsSemantics) {
- if (expectation == 'FAIL') {
- for (var j = 0; j < FAIL_RESULTS.length; j++) {
- if (result == FAIL_RESULTS[j]) {
- hasExpectation = true;
- break;
- }
- }
- }
- }
-
- if (result == expectation) {
- hasExpectation = true;
- }
-
- if (hasExpectation)
- break;
- }
- // If we have no expectations for a test and it only passes, then don't
- // list PASS as a missing expectation. We only want to list PASS if it
- // flaky passes, so there would be other expectations.
- if (!hasExpectation &&
- !(!expectationsArray.length && result == 'PASS' &&
- numRealResults == 1))
- missingExpectations.push(result);
- }
-
- // Only highlight tests that take > 2 seconds as needing to be marked as
- // slow. There are too many tests that take ~2 seconds every couple
- // hundred runs. It's not worth the manual maintenance effort.
- // Also, if a test times out, then it should not be marked as slow.
- var minTimeForNeedsSlow = isDebug(resultsForTest.builder) ? 2 : 1;
- if (isSlowTest(resultsForTest) && !resultsMap['TIMEOUT'] &&
- (!resultsForTest.modifiers ||
- !stringContains(resultsForTest.modifiers, 'SLOW'))) {
- missingExpectations.push('SLOW');
- } else if (isFastTest(resultsForTest) &&
- resultsForTest.modifiers &&
- stringContains(resultsForTest.modifiers, 'SLOW')) {
- extraExpectations.push('SLOW');
- }
- } else {
- var isMarkedFlaky = stringContains(resultsForTest.test,
- GTEST_FLAKY_MARKER);
- if (resultsForTest.isFlaky && !isMarkedFlaky) {
- missingExpectations.push('FLAKY');
- } else if (!resultsForTest.isFlaky && isMarkedFlaky) {
- extraExpectations.push('FLAKY');
- }
- }
-
- resultsForTest.meetsExpectations =
- !missingExpectations.length && !extraExpectations.length;
- resultsForTest.missing = missingExpectations.sort().join(' ');
- resultsForTest.extra = extraExpectations.sort().join(' ');
- }
-
- var bugUrlPrefix = '<a href="http://';
- var bugUrlPostfix = '/$1">$1</a> ';
- var internalBugReplaceValue = bugUrlPrefix + 'b' + bugUrlPostfix;
- var externalBugReplaceValue = bugUrlPrefix + 'crbug.com' + bugUrlPostfix;
-
- /**
- * Returns the BUG modifiers linking to the bug.
- * Bugs with 4 or 5 digits are crbug.com bugs. Bugs with 6 or 7 digits
- * are internal google bugs.
- */
- function getHtmlForBugs(bugs) {
- bugs = bugs.replace(/BUG(\d{4})(\ |$)/g, externalBugReplaceValue);
- bugs = bugs.replace(/BUG(\d{5})(\ |$)/g, externalBugReplaceValue);
- bugs = bugs.replace(/BUG(\d{6})(\ |$)/g, internalBugReplaceValue);
- bugs = bugs.replace(/BUG(\d{7})(\ |$)/g, internalBugReplaceValue);
- return bugs;
- }
-
- function getLinkHTMLToOpenWindow(url, text) {
- return '<div class=link onclick="window.open(\'' + url + '\')">' + text +
- '</div>';
- }
-
- function createBlameListHTML(revisions, index, urlBase, separator, repo) {
- var thisRevision = revisions[index];
- if (!thisRevision)
- return '';
-
- var previousRevision = revisions[index + 1];
- if (previousRevision && previousRevision != thisRevision) {
- previousRevision++;
- return getLinkHTMLToOpenWindow(
- urlBase + thisRevision + separator + previousRevision,
- repo + ' blamelist r' + previousRevision + ':r' + thisRevision);
- } else {
- return 'At ' + repo + ' revision: ' + thisRevision;
- }
- }
-
- /**
- * Returns whether the result for index'th result for testName on builder was
- * a failure.
- */
- function isFailure(builder, testName, index) {
- var currentIndex = 0;
- var rawResults = resultsByBuilder[builder].tests[testName].results;
- for (var i = 0; i < rawResults.length; i++) {
- currentIndex += rawResults[i][RLE.LENGTH];
- if (currentIndex > index)
- return isFailingResult(rawResults[i][RLE.VALUE]);
- }
- console.error('Index exceeds number of results: ' + index);
- }
-
- /**
- * Returns an array of buildNumbers for all builds where this test failed.
- */
- function getBuildNumbersForFailures(builder, testName) {
- var rawResults = resultsByBuilder[builder].tests[testName].results;
- var buildNumbers = resultsByBuilder[builder].buildNumbers;
- var index = 0;
- var failures = [];
- for (var i = 0; i < rawResults.length; i++) {
- var numResults = rawResults[i][RLE.LENGTH];
- if (isFailingResult(rawResults[i][RLE.VALUE])) {
- for (var j = 0; j < numResults; j++) {
- failures.push(buildNumbers[index + j]);
- }
- }
- index += numResults;
- }
-
- return failures;
- }
-
- /**
- * Returns the path to the failure log for this non-webkit test.
- */
- function getPathToFailureLog(testName) {
- return '/steps/' + currentState.testType + '/logs/' +
- testName.split('.')[1]
- }
-
- /**
- * Returns the path to the logs for non-webkit tests.
- */
- function getBasePathForBuilderLogs(builder, buildNumber) {
- return BUILDERS_BASE_PATH + builder + '/builds/' + buildNumber
- }
-
- function showPopupForBuild(e, builder, index, opt_testName) {
- var html = '';
-
- var time = resultsByBuilder[builder].secondsSinceEpoch[index];
- if (time) {
- var date = new Date(time * 1000);
- html += date.toLocaleDateString() + ' ' + date.toLocaleTimeString();
- }
-
- html += '<ul><li>' +
- createBlameListHTML(resultsByBuilder[builder].webkitRevision, index,
- 'http://trac.webkit.org/log/?verbose=on&rev=', '&stop_rev=',
- 'WebKit') +
- '</li><li>' +
- createBlameListHTML(resultsByBuilder[builder].chromeRevision, index,
- 'http://build.chromium.org/buildbot/perf/dashboard/ui/' +
- 'changelog.html?url=/trunk/src&mode=html&range=', ':', 'Chrome') +
- '</li>';
-
- var chromeRevision = resultsByBuilder[builder].chromeRevision[index];
- if (chromeRevision && isLayoutTestResults()) {
- html += '<li><a href="' + TEST_RESULTS_BASE_PATH + builders[builder] +
- '/' + chromeRevision + '/layout-test-results.zip' +
- '">layout-test-results.zip</a></li>';
- }
-
- var buildNumber = resultsByBuilder[builder].buildNumbers[index];
- var buildBasePath = getBasePathForBuilderLogs(builder, buildNumber);
-
- if (!isLayoutTestResults() && opt_testName &&
- isFailure(builder, opt_testName, index)) {
- html += '<li>' + getLinkHTMLToOpenWindow(
- buildBasePath + getPathToFailureLog(opt_testName),
- 'Failure log') + '</li>';
- }
-
- html += '<li>' + getLinkHTMLToOpenWindow(buildBasePath, 'Build log') +
- '</li></ul>';
-
- showPopup(e, html);
- }
-
- function showPopupForTest(e, test) {
- showPopup(e, getHTMLForIndividulTestOnAllBuilders(test));
- appendExpectations();
- }
-
- function getHtmlForTestResults(test) {
- var html = '';
- var results = test.rawResults.concat();
- var times = test.rawTimes.concat();
- var builder = test.builder;
- var buildNumbers = resultsByBuilder[builder].buildNumbers;
-
- var indexToReplaceCurrentResult = -1;
- var indexToReplaceCurrentTime = -1;
- var currentResultArray, currentTimeArray, currentResult, innerHTML,
- resultString;
- var maxIndex = Math.min(buildNumbers.length, getMaxResults());
- for (var i = 0; i < maxIndex; i++) {
- if (i > indexToReplaceCurrentResult) {
- currentResultArray = results.shift();
- if (currentResultArray) {
- currentResult = currentResultArray[RLE.VALUE];
- // Treat simplified diff failures as just text failures.
- if (currentResult == 'S')
- currentResult = 'F';
- indexToReplaceCurrentResult += currentResultArray[RLE.LENGTH];
- } else {
- currentResult = 'N';
- indexToReplaceCurrentResult += buildNumbers.length;
- }
-
- resultString = getExpectationsFileStringForResult(currentResult);
- }
-
- if (i > indexToReplaceCurrentTime) {
- currentTimeArray = times.shift();
- var currentTime = 0;
- if (currentResultArray) {
- currentTime = currentTimeArray[RLE.VALUE];
- indexToReplaceCurrentTime += currentTimeArray[RLE.LENGTH];
- } else {
- indexToReplaceCurrentTime += buildNumbers.length;
- }
- innerHTML = currentTime || '&nbsp;';
- }
-
- html += '<td title="' + (resultString || 'NO DATA') +
- '. Click for more info." class="results ' + currentResult +
- '" onclick=\'showPopupForBuild(event, "' + builder + '",' + i +
- ',"' + test.test + '")\'>' + innerHTML + '</td>';
-
- var webkitRevision = resultsByBuilder[builder].webkitRevision;
- var isWebkitMerge = webkitRevision[i + 1] &&
- webkitRevision[i] != webkitRevision[i + 1];
- if (isWebkitMerge)
- html += '<td class=merge></td>';
- }
- return html;
- }
-
- function getHTMLForTestsWithExpectationsButNoFailures(builder) {
- var tests = perBuilderWithExpectationsButNoFailures[builder];
- var skippedPaths = perBuilderSkippedPaths[builder];
-
- var html = '';
- if (tests.length || skippedPaths.length) {
- var buildInfo = getPlatformAndBuildType(builder);
- html += '<h2>Expectations for ' + buildInfo.platform + '-' +
- buildInfo.buildType + ':</h2>';
- }
-
- var open = '<div onclick="selectContents(this)">';
-
- if (tests.length) {
- html += '<h3>Have not failed in last ' +
- resultsByBuilder[builder].buildNumbers.length +
- ' runs.</h3><div id="passing-tests">';
-
- for (var i = 0; i < tests.length; i++) {
- html += open + tests[i].test + '</div>';
- }
-
- html += '</div>';
- }
-
- if (skippedPaths.length) {
- html += '<h3>' +
- getLinkHTMLToToggleState('showSkipped',
- 'Skipped tests in test_expectations.txt') +
- '</h3>';
-
- if (currentState.showSkipped) {
- html += '<div id="passing-tests">' + open +
- skippedPaths.join('</div>' + open) + '</div></div>';
- }
- }
- return html;
- }
-
- /**
- * Returns whether the test has passed continuously for at
- * least as many runs as we are showing results for.
- */
- function hasPassedInMaxRuns(testResult) {
- var results = testResult.rawResults;
- return results[0][RLE.VALUE] == 'P' &&
- results[0][RLE.LENGTH] >= getMaxResults();
- }
-
- /**
- * Returns whether we should exclude test results from the test table.
- */
- function shouldHideTest(testResult) {
- if (testResult.isWontFixSkip)
- return !currentState.showWontFixSkip;
-
- if (testResult.isFlaky)
- return !currentState.showFlaky;
-
- if (isSlowTest(testResult))
- return !currentState.showSlow;
-
- if (testResult.meetsExpectations)
- return !currentState.showCorrectExpectations;
-
- return !currentState.showWrongExpectations;
- }
-
- /**
- * Sets the browser's selection to the element's contents.
- */
- function selectContents(element) {
- window.getSelection().selectAllChildren(element);
- }
-
- function getCreateBugHTML(test) {
- var symptom = test.isFlaky ? 'flaky' : 'failing';
- var title = 'Layout Test ' + test.test + ' is ' + symptom;
- var description = 'The following layout test is ' + symptom + ' on ' +
- '[insert platform]\n\n' + test.test + '\n\nProbable cause:\n\n' +
- '[insert probable cause]';
- return bugUrlPrefix + 'code.google.com/p/chromium/issues/entry?' +
- 'template=Layout%20Test%20Failure&summary=' +
- encodeURIComponent(title) + '&comment=' +
- encodeURIComponent(description) + '" class="file-bug">FILE BUG</a>';
- }
-
- function getHTMLForSingleTestRow(test, opt_isCrossBuilderView) {
- if (!opt_isCrossBuilderView && shouldHideTest(test)) {
- // The innerHTML call is considerably faster if we exclude the rows for
- // items we're not showing than if we hide them using display:none.
- // For the crossBuilderView, we want to show all rows the user is
- // explicitly listing tests to view.
- return '';
- }
-
- // If opt_isCrossBuilderView is true, we're just viewing a single test
- // with results for many builders, so the first column is builder names
- // instead of test paths.
- var testCellHTML = opt_isCrossBuilderView ? test.builder :
- '<span class="link" onclick="showPopupForTest(event, \'' + test.test +
- '\');selectContents(this);return false;">' + test.test + '</span>';
-
- var html = '<tr class="' +
- (test.meetsExpectations ? '' : 'wrong-expectations') +
- '"><td class=test-link>' + testCellHTML;
-
- if (isLayoutTestResults()) {
- html += '</td><td class=options-container>' +
- (test.bugsHTML ? test.bugsHTML : getCreateBugHTML(test)) +
- '</td><td class=options-container>' + test.modifiersHTML +
- '</td><td class=options-container>' + test.expectationsHTML;
- }
-
- return html +
- '</td><td>' + test.missing +
- '</td><td>' + test.extra +
- '</td><td>' + (test.slowestTime ? test.slowestTime + 's' : '') +
- '</td><td>' + test.percentFailed +
- '</td>' + getHtmlForTestResults(test) + '</tr>';
- }
-
- function getSortColumnFromTableHeader(headerText) {
- return headerText.split(' ', 1)[0];
- }
-
- function getHTMLForTestTable(rowsHTML) {
- var html = '<table class=test-table><thead><tr>';
- for (var i = 0; i < tableHeaders.length; i++) {
- // Use the first word of the header title as the sortkey
- var thisSortValue = getSortColumnFromTableHeader(tableHeaders[i]);
- var arrowHTML = thisSortValue == currentState.sortColumn ?
- '<span class=' + currentState.sortOrder + '>' +
- (currentState.sortOrder == FORWARD ? '&uarr;' : '&darr;' ) +
- '</span>' :
- '';
- html += '<th sortValue=' + thisSortValue +
- // Extend last th through all the rest of the columns.
- (i == tableHeaders.length - 1 ? ' colspan=10000' : '') +
- // Extra span here is so flex boxing actually centers.
- // There's probably a better way to do this with CSS only though.
- '><div class=table-header-content><span></span>' + arrowHTML +
- '<span class=header-text>' + tableHeaders[i] + '</span>' +
- arrowHTML + '</div></th>';
- }
- return html + '</tr></thead><tbody>' + rowsHTML + '</tbody></table>';
- }
-
- function appendHTML(html) {
- var startTime = Date.now();
- // InnerHTML to a div that's not in the document. This is
- // ~300ms faster in Safari 4 and Chrome 4 on mac.
- var div = document.createElement('div');
- div.innerHTML = html;
- document.body.appendChild(div);
- logTime('Time to innerHTML', startTime);
- }
-
- function getAlphanumericCompare(column, reverse) {
- return getReversibleCompareFunction(function(a, b) {
- // Put null entries at the bottom
- var a = a[column] ? String(a[column]) : 'z';
- var b = b[column] ? String(b[column]) : 'z';
-
-
- // For gtest tests, we make them as flaky by prefixing the test name with
- // FLAKY_, resulting in two entries for the test.
- // Place flaky tests next to their non-flaky counterparts.
- // TODO(ojan): Merge the non-flaky test with the flaky one so there's
- // only a single entry per test.
- if (!isLayoutTestResults()) {
- a = a.replace(GTEST_FLAKY_MARKER, '.');
- b = b.replace(GTEST_FLAKY_MARKER, '.');
- }
-
- if (a < b)
- return -1;
- else if (a == b)
- return 0;
- else
- return 1;
- }, reverse);
- }
-
- function getNumericSort(column, reverse) {
- return getReversibleCompareFunction(function(a, b) {
- a = parseFloat(a[column]);
- b = parseFloat(b[column]);
- return a - b;
- }, reverse);
- }
-
- function getReversibleCompareFunction(compare, reverse) {
- return function(a, b) {
- return compare(reverse ? b : a, reverse ? a : b);
- }
- }
-
- function changeSort(e) {
- var target = e.currentTarget;
- e.preventDefault();
-
- var sortValue = target.getAttribute('sortValue');
- while (target && target.tagName != 'TABLE') {
- target = target.parentNode;
- }
-
- var sort = 'sortColumn';
- var orderKey = 'sortOrder';
- if (sortValue == currentState[sort] && currentState[orderKey] == FORWARD)
- order = BACKWARD;
- else
- order = FORWARD;
-
- setState(sort, sortValue, orderKey, order);
- }
-
- function sortTests(tests, column, order) {
- var resultsProperty, sortFunctionGetter;
- if (column == 'flakiness') {
- sortFunctionGetter = getNumericSort;
- resultsProperty = 'flips';
- } else if (column == 'slowest') {
- sortFunctionGetter = getNumericSort;
- resultsProperty = 'slowestTime';
- } else if (column == '%') {
- sortFunctionGetter = getNumericSort;
- resultsProperty = 'percentFailed';
- } else {
- sortFunctionGetter = getAlphanumericCompare;
- resultsProperty = column;
- }
-
- tests.sort(sortFunctionGetter(resultsProperty, order == BACKWARD));
- }
-
- function addUpdate(testsNeedingUpdate, test, builderName, missing, extra) {
- if (!testsNeedingUpdate[test])
- testsNeedingUpdate[test] = {};
-
- var buildInfo = getPlatformAndBuildType(builderName);
- var builder = buildInfo.platform + ' ' + buildInfo.buildType;
- if (!testsNeedingUpdate[test][builder])
- testsNeedingUpdate[test][builder] = {};
-
- if (missing)
- testsNeedingUpdate[test][builder].missing = missing;
-
- if (extra)
- testsNeedingUpdate[test][builder].extra = extra;
- }
-
- function generatePageForExpectationsUpdate() {
- processTestRunsForAllBuilders();
- var testsNeedingUpdate = {};
- for (var test in testToResultsMap) {
- var results = testToResultsMap[test];
- for (var i = 0; i < results.length; i++) {
- var thisResult = results[i];
- if (!thisResult.missing && !thisResult.extra)
- continue;
-
- addUpdate(testsNeedingUpdate, test, thisResult.builder,
- thisResult.missing, thisResult.extra);
- }
- }
-
- for (var builder in builders) {
- var tests = perBuilderWithExpectationsButNoFailures[builder]
- for (var i = 0; i < tests.length; i++) {
- addUpdate(testsNeedingUpdate, tests[i].test, builder,
- null, tests[i].expectations + ' ' + tests[i].modifiers);
- }
- }
-
- // Get the keys in alphabetical order, so it is easy to process groups
- // of tests.
- var keys = getKeys(testsNeedingUpdate).sort();
- showUpdateInfoForTest(testsNeedingUpdate, keys);
- }
-
- /**
- * Show the test results and the json for differing expectations, and
- * allow the user to include or exclude this update.
- *
- * @param {Object} testsNeedingUpdate Tests that need updating.
- * @param {Array.<string>} keys Keys into the testNeedingUpdate object.
- */
- function showUpdateInfoForTest(testsNeedingUpdate, keys) {
- var test = keys[currentState.updateIndex];
- document.body.innerHTML = '<b>' + test + '</b>' +
- '<div style="float:right">' + (currentState.updateIndex + 1) + ' of ' +
- + keys.length + ' tests</div><br><br>';
-
- var buttonRegion = document.createElement('div');
- var includeBtn = document.createElement('input');
- includeBtn.type = 'button';
- includeBtn.value = 'include';
- includeBtn.addEventListener('click',
- partial(handleUpdate, true, testsNeedingUpdate, keys),
- false);
- buttonRegion.appendChild(includeBtn);
-
- var excludeBtn = document.createElement('input');
- excludeBtn.type = 'button';
- excludeBtn.value = 'exclude';
- excludeBtn.addEventListener('click',
- partial(handleUpdate, false, testsNeedingUpdate, keys),
- false);
- buttonRegion.appendChild(excludeBtn);
-
- var doneBtn = document.createElement('input');
- doneBtn.type = 'button';
- doneBtn.value = 'done';
- doneBtn.addEventListener('click', finishUpdate, false);
- buttonRegion.appendChild(doneBtn);
-
- document.body.appendChild(buttonRegion);
-
- var confirmStr = JSON.stringify(testsNeedingUpdate[test], null, 4);
-
- var pre = document.createElement('pre');
- pre.style.marginTop = 0;
- pre.innerHTML = confirmStr;
- document.body.appendChild(pre);
- var div = document.createElement('div');
- div.innerHTML = getHTMLForIndividulTestOnAllBuilders(test);
- document.body.appendChild(div);
- }
-
-
- /**
- * When the user has finished selecting expectations to update, provide them
- * with json to copy over.
- * TODO(jparent): This could also probably spit out instructinos on what to
- * do next.
- */
- function finishUpdate() {
- document.body.innerHTML = JSON.stringify(confirmedTests);
- }
-
- /**
- * Handle user click on either "include" or "exclude" buttons.
- * @param {boolean} isAccepted Whether the user accepted this update.
- * @param {Object} testsNeedingUpdate Tests that need updating.
- * @param {Array.<string>} keys Keys into the testNeedingUpdate object.
- *
- */
- function handleUpdate(isAccepted, testsNeedingUpdate, keys) {
- var test = keys[currentState.updateIndex];
- if (isAccepted) {
- confirmedTests[test] = testsNeedingUpdate[test];
- } else if (confirmedTests[test]) {
- delete confirmedTests[test];
- }
-
- setState("updateIndex", currentState.updateIndex + 1);
-
- if (currentState.updateIndex < keys.length) {
- showUpdateInfoForTest(testsNeedingUpdate, keys);
- } else {
- finishUpdate();
- }
- }
-
- function getHTMLForIndividulTestOnAllBuilders(test) {
- createTableHeadersArray('builder');
- processTestRunsForAllBuilders();
-
- var testResults = testToResultsMap[test];
- var html = '';
- if (testResults && testResults.length) {
- if (isLayoutTestResults()) {
- var tracURL = TEST_URL_BASE_PATH + test
- html += getLinkHTMLToOpenWindow(tracURL, tracURL);
- }
-
- html +='<div><b>If a builder is not listed, that means the builder ' +
- 'does not run that test (e.g. it is skipped) or all runs of the ' +
- 'test passed.</b></div>';
-
- for (var j = 0; j < testResults.length; j++) {
- html += getHTMLForSingleTestRow(testResults[j], true);
- }
- html = getHTMLForTestTable(html);
- } else {
- if (expectationsByTest[test]) {
- for (var i = 0; i < expectationsByTest[test].length; i++) {
- html += '<div>' + expectationsByTest[test][i].modifiers + ' | ' +
- expectationsByTest[test][i].expectations + '</div>';
- }
- }
- html += '<div class="not-found">Test not found. Either it does ' +
- 'not exist, is skipped or passes on all platforms.</div>';
- }
-
- html += '<div class=expectations test=' + test + '><div>' +
- getLinkHTMLToToggleState('showExpectations', 'results')
-
- if (isLayoutTestResults()) {
- html += ' | ' + getLinkHTMLToToggleState('showLargeExpectations',
- 'large thumbnails') +
- ' | <b>Only shows actual results/diffs from the most recent' +
- ' *failure* on each bot.</b>';
- } else {
- html += ' | <span>Results height:<input ' +
- 'onchange="setState(\'resultsHeight\',this.value)" value="' +
- currentState.resultsHeight + '" style="width:2.5em">px</span>';
- }
- html += '</div></div>';
- return html;
- }
-
- function getExpectationsContainer(expectationsContainers, parentContainer,
- expectationsType) {
- if (!expectationsContainers[expectationsType]) {
- var container = document.createElement('div');
- container.className = 'expectations-container';
- parentContainer.appendChild(container);
- expectationsContainers[expectationsType] = container;
- }
- return expectationsContainers[expectationsType];
- }
-
- function getExtension(path) {
- var parts = path.split('.')
- var extension = parts[parts.length - 1];
- return extension == 'html' ? 'txt' : extension;
- }
-
- function ensureTrailingSlash(path) {
- if (path.match(/\/$/))
- return path;
- return path + '/';
- }
-
- /**
- * Adds a specific expectation. If it's an image, it's only added on the
- * image's onload handler. If it's a text file, then a script tag is appended
- * as a hack to see if the file 404s (necessary since it's cross-domain).
- * Once all the expectations for a specific type have loaded or errored
- * (e.g. all the checksums), then we go through and identify which platform
- * uses which expectation.
- *
- * @param {Object} expectationsContainers Map from expectations type to
- * container DIV.
- * @param {Element} parentContainer Container element for
- * expectationsContainer divs.
- * @param {string} platform Platform string. Empty string for non-platform
- * specific expectations.
- * @param {string} path Relative path to the expectation.
- * @param {string} base Base path for the expectation URL.
- * @param {string} suffix Suffix to place at the end of the path.
- * @param {string} opt_builder Builder whose actual results this expectation
- * points to.
- */
- function addExpectationItem(expectationsContainers, parentContainer, platform,
- path, base, suffix, opt_builder) {
- var fileExtension = getExtension(path);
- var container = getExpectationsContainer(expectationsContainers,
- parentContainer, fileExtension);
- var isImage = path.match(/\.png$/);
-
- // TODO(ojan): Is there any way to do this that doesn't rely on script
- // tags? They spew a lot of errors to the console.
- var dummyNode = document.createElement(isImage ? 'img' : 'script');
- var platformPart = platform ? ensureTrailingSlash(platform) : '';
-
- // TODO(ojan): Get rid of this once we upstream the chromium expected
- // results or move their contents out of the LayoutTests directory.
- if (startsWith(platformPart, 'chromium'))
- platformPart += 'LayoutTests/';
-
- dummyNode.src = base + platformPart + path + suffix;
-
- var childContainer = document.createElement('span');
- childContainer.className = 'unloaded';
-
- dummyNode.onload = function() {
- childContainer.appendChild(
- getExpectationsTitle(platform, path, opt_builder || ''));
- childContainer.className = 'expectations-item';
-
- var item;
- if (isImage) {
- item = dummyNode;
- } else {
- item = document.createElement('iframe');
- item.src = dummyNode.src;
- }
-
- item.className = 'expectation ' + fileExtension;
- if (currentState.showLargeExpectations)
- item.className += ' large';
- childContainer.appendChild(item);
- handleFinishedLoadingExpectations(container);
- }
- dummyNode.onerror = function() {
- childContainer.parentNode.removeChild(childContainer);
- handleFinishedLoadingExpectations(container);
- }
-
- // Append script elements now so that they load. Images load without being
- // appended to the DOM.
- if (!isImage) {
- childContainer.appendChild(dummyNode);
- }
-
- container.appendChild(childContainer);
- }
-
- /**
- * Identifies which expectations are used on which platform once all the
- * expectations of a given type have loaded (e.g. the container for checksum
- * expectations for this test had no child elements with the class
- * "unloaded").
- *
- * @param {string} container Element containing the expectations for a given
- * test and a given type (e.g. checksum).
- */
- function handleFinishedLoadingExpectations(container) {
- if (container.getElementsByClassName('unloaded').length)
- return;
-
- var titles = container.getElementsByClassName('expectations-title');
- for (var platform in fallbacksMap) {
- var fallbacks = fallbacksMap[platform];
- var winner = null;
- var winningIndex = -1;
- for (var i = 0; i < titles.length; i++) {
- var title = titles[i];
-
- if (!winner && title.platform == "") {
- winner = title;
- continue;
- }
-
- for (var j = 0; j < fallbacks.length; j++) {
- if ((winningIndex == -1 || winningIndex > j) &&
- title.platform == fallbacks[j]) {
- winningIndex = j;
- winner = title;
- break;
- }
- }
- }
- if (winner) {
- winner.getElementsByClassName('platforms')[0].innerHTML +=
- '<div class=used-platform>' + platform + '</div>';
- } else {
- console.log('No expectations identified for this test. This means ' +
- 'there is a logic bug in the dashboard for which expectations a ' +
- 'platform uses or trac.webkit.org/src.chromium.org is giving ' +
- ' 5XXs.');
- }
- }
- }
-
- var TRAC_IMAGE_BASE_URL;
- /**
- * Trac seems to only show the raw image if you're viewing at a specific
- * revision. Use the latest revision on any builder.
- */
- function getTracImageBaseURL() {
- if (!TRAC_IMAGE_BASE_URL) {
- TRAC_IMAGE_BASE_URL = 'http://trac.webkit.org/export/' +
- getLatestKnownRevision(false) + '/trunk/LayoutTests/';
- }
- return TRAC_IMAGE_BASE_URL;
- }
-
- function getLatestKnownRevision(isChrome) {
- var revision = 0;
- for (var builder in builders) {
- var results = resultsByBuilder[builder];
- var revisions = isChrome ? results.chromeRevision :
- results.webkitRevision;
- if (revision < revisions[0])
- revision = revisions[0];
- }
- return revision;
- }
-
- function addExpectations(expectationsContainers, container, base, imageBase,
- platform, text, checksum, png, textSuffix) {
- addExpectationItem(expectationsContainers, container, platform, text, base,
- textSuffix);
- addExpectationItem(expectationsContainers, container, platform, checksum,
- base, textSuffix);
- addExpectationItem(expectationsContainers, container, platform, png,
- imageBase, '');
- }
-
- function getExpectationsTitle(platform, path, builder) {
- var header = document.createElement('h3');
- header.className = 'expectations-title';
-
- var innerHTML;
- if (builder) {
- var resultsType;
- if (endsWidth(path, '-stack.txt')) {
- resultsType = 'STACKTRACE';
- } else if (endsWidth(path, '-actual.txt') ||
- endsWidth(path, '-actual.checksum') ||
- endsWidth(path, '-actual.png')) {
- resultsType = 'ACTUAL RESULTS';
- } else {
- resultsType = 'DIFF';
- }
- innerHTML = resultsType + ': ' + builder;
- } else if (platform === "") {
- var parts = path.split('/');
- innerHTML = parts[parts.length - 1];
- } else {
- innerHTML = platform || path;
- }
-
- header.innerHTML = '<div class=title>' + innerHTML +
- '</div><div style="float:left">&nbsp;</div>' +
- '<div class=platforms style="float:right"></div>';
- header.platform = platform;
- return header;
- }
-
- function loadExpectations(expectationsContainer) {
- if (isLayoutTestResults()) {
- loadExpectationsLayoutTests(expectationsContainer);
- } else {
- var test = expectationsContainer.getAttribute('test');
- var results = testToResultsMap[test];
- for (var i = 0; i < results.length; i++) {
- loadNonWebKitResultsForBuilder(results[i].builder, test,
- expectationsContainer);
- }
- }
- }
-
- function loadNonWebKitResultsForBuilder(builder, test,
- expectationsContainer) {
- var failures = getBuildNumbersForFailures(builder, test);
- var container = document.createElement('div');
- container.innerHTML = '<div><b>' + builder + '</b></div>';
- expectationsContainer.appendChild(container);
- for (var i = 0; i < failures.length; i++) {
- var pathToLog = getBasePathForBuilderLogs(builder, failures[i]) +
- getPathToFailureLog(test);
- appendNonWebKitResults(container, pathToLog);
- }
- }
-
- function appendNonWebKitResults(container, url) {
- // Use a script tag to detect whether the URL 404s.
- // Need to use a script tag since the URL is cross-domain.
- var dummyNode = document.createElement('script');
- dummyNode.src = url;
-
- dummyNode.onload = function() {
- var item = document.createElement('iframe');
- item.src = dummyNode.src;
- item.className = 'non-webkit-results';
- item.style.height = currentState.resultsHeight + 'px';
- container.appendChild(item);
- }
- dummyNode.onerror = function() {
- container.parentNode.removeChild(childContainer);
- }
-
- container.appendChild(dummyNode);
- }
-
- function loadExpectationsLayoutTests(expectationsContainer) {
- // Map from file extension to container div for expectations of that type.
- var expectationsContainers = {};
-
- var test = expectationsContainer.getAttribute('test');
- var textSuffixWebKit = '?format=txt';
- addExpectationItem(expectationsContainers, expectationsContainer, null,
- test, TEST_URL_BASE_PATH, textSuffixWebKit);
-
- var testWithoutSuffix = test.substring(0, test.lastIndexOf('.'));
- var text = testWithoutSuffix + "-expected.txt";
- var checksum = testWithoutSuffix + "-expected.checksum"
- var png = testWithoutSuffix + "-expected.png";
-
- addExpectations(expectationsContainers, expectationsContainer,
- TEST_URL_BASE_PATH, getTracImageBaseURL(), '',
- text, checksum, png, textSuffixWebKit);
-
- var textSuffixChrome = '?revision=' + getLatestKnownRevision(true);
-
- var fallbacks = getAllFallbacks();
- for (var i = 0; i < fallbacks.length; i++) {
- var fallback = fallbacks[i];
- if (startsWith(fallback, 'platform')) {
- addExpectations(expectationsContainers, expectationsContainer,
- TEST_URL_BASE_PATH, getTracImageBaseURL(),
- fallback, text, checksum, png, textSuffixWebKit);
- } else {
- addExpectations(expectationsContainers, expectationsContainer,
- CHROME_TEST_BASE_URL, CHROME_TEST_BASE_URL, fallback, text,
- checksum, png, textSuffixChrome);
- }
- }
-
- var actualResultSuffixes = ['-actual.txt', '-actual.checksum',
- '-actual.png', '-stack.txt', '-diff.txt', '-wdiff.html', '-diff.png'];
-
- for (var builder in builders) {
- var actualResultsBase =
- 'http://build.chromium.org/buildbot/layout_test_results/' +
- builders[builder] + '/results/layout-test-results/';
- for (var i = 0; i < actualResultSuffixes.length; i++) {
- addExpectationItem(expectationsContainers, expectationsContainer, null,
- testWithoutSuffix + actualResultSuffixes[i], actualResultsBase, '',
- builder);
- }
- }
-
- // Add a clearing element so floated elements don't bleed out of their
- // containing block.
- var br = document.createElement('br');
- br.style.clear = 'both';
- expectationsContainer.appendChild(br);
- }
-
- var allFallbacks;
-
- /**
- * Returns the reverse sorted, deduped list of all platform fallback
- * directories.
- */
- function getAllFallbacks() {
- if (!allFallbacks) {
- var holder = {};
- for (var platform in fallbacksMap) {
- var fallbacks = fallbacksMap[platform];
- for (var i = 0; i < fallbacks.length; i++) {
- holder[fallbacks[i]] = 1;
- }
- }
-
- allFallbacks = [];
- for (var fallback in holder) {
- allFallbacks.push(fallback);
- }
- allFallbacks.sort(function(a, b) {
- if (a == b)
- return 0;
- return a < b;
- });
- }
- return allFallbacks;
- }
-
- /**
- * Appends the expectations for each test listed.
- */
- function appendExpectations() {
- var expectations = currentState.showExpectations ?
- document.getElementsByClassName('expectations') : [];
- // Loading expectations is *very* slow. Use a large timeout to avoid
- // totally hanging the renderer.
- performChunkedAction(expectations, function(chunk) {
- for (var i = 0, len = chunk.length; i < len; i++) {
- loadExpectations(chunk[i]);
- }
- }, hideLoadingUI, 10000);
- }
-
- function hideLoadingUI() {
- $('loading-ui').style.display = 'none';
- }
-
- function generatePageForIndividualTests(tests) {
- console.log('Number of tests: ' + tests.length);
- appendHTML(getHTMLForNavBar());
- performChunkedAction(tests, function(chunk) {
- appendHTML(getHTMLForIndividualTests(chunk));
- }, appendExpectations, 500);
- $('tests-input').value = currentState.tests;
- }
-
- function performChunkedAction(tests, handleChunk, onComplete, timeout,
- opt_index) {
- var index = opt_index || 0;
- setTimeout(function() {
- var chunk = Array.prototype.slice.call(tests, index * CHUNK_SIZE,
- (index + 1) * CHUNK_SIZE);
- if (chunk.length) {
- handleChunk(chunk);
- performChunkedAction(tests, handleChunk, onComplete, timeout, ++index);
- } else {
- onComplete();
- }
- // No need for a timeout on the first chunked action.
- }, index ? timeout : 0);
- }
-
- function getHTMLForIndividualTests(tests) {
- var testsHTML = [];
- for (var i = 0; i < tests.length; i++) {
- testsHTML.push('<h2 onclick="selectContents(this)">' + tests[i] +
- '</h2>' + getHTMLForIndividulTestOnAllBuilders(tests[i]));
- }
- return testsHTML.join('<hr>');
- }
-
- function getHTMLForCanaryLink(html, useWebKitCanary, useV8Canary) {
- var className = (currentState.useWebKitCanary == useWebKitCanary &&
- currentState.useV8Canary == useV8Canary) ? 'current-builder' : 'link';
- return '<span class=' + className +
- ' onclick="setState(\'useWebKitCanary\',' + useWebKitCanary +
- ',\'useV8Canary\',' + useV8Canary + ');window.location.reload()">' +
- html + '</span> | ';
- }
-
- function getHTMLForNavBar(opt_builderName) {
- var html = '<div id=builders>' + getHTMLForTestTypeSwitcher();
-
- for (var builder in builders) {
- var className = builder == opt_builderName ? 'current-builder' : 'link';
- html += '<span class=' + className + ' id="' + builder +
- '" onclick=\'setState("builder", "' + builder + '")\'>' +
- builder + '</span>';
- }
-
- html += '</div>' +
- '<form id=tests-form ' +
- 'onsubmit="setState(\'tests\', tests.value);return false;"><div>';
-
- if (isLayoutTestResults()) {
- html += getHTMLForCanaryLink('Main builders', false, false) +
- getHTMLForCanaryLink('WebKit.org Canary', true, false) +
- getHTMLForCanaryLink('V8 Canary', false, true) +
- '<span class=link ' +
- 'onclick="setState(\'tests\', \'allIncorrectExpectations\')">' +
- 'All incorrect expectations</span> | '+
- '<span class=link ' +
- 'onclick="setState(\'tests\', \'allSlowTests\')">All SLOW' +
- '</span> | <span class=link ' +
- 'onclick="setState(\'tests\', \'allNeedSlowTests\')">All need ' +
- 'SLOW</span> | ';
- }
- return html + 'Show tests on all platforms: </div><input name=tests ' +
- 'placeholder="Comma or space-separated list of tests or partial ' +
- 'paths to show test results across all builders, e.g., ' +
- 'foo/bar.html,foo/baz,domstorage" ' +
- 'id=tests-input></form>' +
- '<form id=max-results-form ' +
- 'onsubmit="setState(\'maxResults\', maxResults.value);return false;"' +
- '><span>Number of results to show (max=750): </span>' +
- '<input name=maxResults id=max-results-input></form> | ' +
- '<span class=link onclick="showLegend()">Show legend [type ?]</span>';
- }
-
- function getLinkHTMLToToggleState(key, linkText) {
- var isTrue = currentState[key];
- return '<span class=link onclick="setState(\'' + key + '\', ' + !isTrue +
- ')">' + (isTrue ? 'Hide' : 'Show') + ' ' + linkText + '</span>';
- }
-
- function generatePageForBuilder(builderName) {
- processTestRunsForBuilder(builderName);
-
- var tableRowsHTML = '';
- var results = perBuilderFailures[builderName];
- sortTests(results, currentState.sortColumn, currentState.sortOrder);
- for (var i = 0; i < results.length; i++) {
- tableRowsHTML += getHTMLForSingleTestRow(results[i]);
- }
-
- var testsHTML = tableRowsHTML ? getHTMLForTestTable(tableRowsHTML) :
- '<div>No tests. Try showing tests with correct expectations.</div>';
-
- var html = getHTMLForNavBar(builderName);
-
- if (isLayoutTestResults()) {
- html += getHTMLForTestsWithExpectationsButNoFailures(builderName) +
- '<h2>Failing tests</h2>' +
- getLinkHTMLToToggleState('showWontFixSkip', 'WONTFIX/SKIP') +
- ' | ' +
- getLinkHTMLToToggleState('showCorrectExpectations',
- 'tests with correct expectations') + ' | ' +
- getLinkHTMLToToggleState('showWrongExpectations',
- 'tests with wrong expectations') + ' | ' +
- getLinkHTMLToToggleState('showFlaky', 'flaky') + ' | ' +
- getLinkHTMLToToggleState('showSlow', 'slow');
- }
-
- html += ' | All columns are sortable. | ' +
- 'Flakiness reader order is newer --> older runs.<br>' +
- testsHTML;
-
- appendHTML(html);
-
- var ths = document.getElementsByTagName('th');
- for (var i = 0; i < ths.length; i++) {
- ths[i].addEventListener('click', changeSort, false);
- ths[i].className = "sortable";
- }
-
- appendNumFlakyTests(builderName);
- hideLoadingUI();
- }
-
- function appendNumFlakyTests(builderName) {
- var link = $(builderName);
- if (link) {
- link.innerHTML =
- builderName + ' [' + numFlakyTestsPerBuilder[builderName] + ']';
- }
- }
-
- function createTableHeadersArray(firstColumnHeader) {
- var baseHeaders = isLayoutTestResults() ? LAYOUT_TEST_TABLE_HEADERS :
- BASE_TABLE_HEADERS;
- tableHeaders = [firstColumnHeader].concat(baseHeaders);
- }
-
- /**
- * Clears the processed test state for perBuilderFailures.
- * TODO(ojan): This really should probably clear all the state we've
- * generated, but that's kind of a pain given the many global objects state is
- * stored in. There should probably be one global generatedState
- * object that all the generated state lives off of.
- */
- function clearProcessedTestState() {
- for (var builder in builders) {
- delete perBuilderFailures[builder];
- delete perBuilderPlatformAndBuildType[builder];
- delete perBuilderWithExpectationsButNoFailures[builder];
- delete perBuilderSkippedPaths[builder];
- }
-
- for (var key in testToResultsMap) {
- delete testToResultsMap[key]
- }
- }
-
- var VALID_KEYS_FOR_CROSS_BUILDER_VIEW = {
- tests: 1,
- maxResults: 1,
- showExpectations: 1,
- showLargeExpectations: 1,
- legacyExpectationsSemantics: 1,
- resultsHeight: 1
- };
-
- function isInvalidKeyForCrossBuilderView(key) {
- return !(key in VALID_KEYS_FOR_CROSS_BUILDER_VIEW) &&
- !(key in defaultCrossDashboardStateValues);
- }
-
- /**
- * Sets the page state and regenerates the page. Takes varargs of key, value
- * pairs.
- */
- function setState(var_args) {
- for (var i = 0; i < arguments.length; i += 2) {
- var key = arguments[i];
- if (key == 'tests') {
- // Entering cross-builder view, only keep valid keys for that view.
- for (var currentKey in currentState) {
- if (isInvalidKeyForCrossBuilderView(currentKey)) {
- delete currentState[currentKey];
- }
- }
- } else if (isInvalidKeyForCrossBuilderView(key)) {
- delete currentState.tests;
- }
-
- if (key == 'maxResults' || key == 'expectationsUpdate' ||
- key == 'tests' && !currentState.maxResults) {
- // Processing the test results JSON makes assumptions about the number
- // of results to show. This makes changing the number of maxResults slow
- // but is considerably easier than refactoring all the other code.
- clearProcessedTestState();
- }
- }
-
- // Set all the custom state for this dashboard before calling
- // setQueryParameter since setQueryParameter updates the location bar.
- setQueryParameter.apply(null, arguments);
- }
-
- function hideLegend() {
- var legend = $('legend');
- if (legend)
- legend.parentNode.removeChild(legend);
- }
-
- var fallbacksMap = {};
- fallbacksMap['WIN-VISTA'] = ['chromium-win-vista', 'chromium-win',
- 'platform/win', 'platform/mac'];
- fallbacksMap['WIN-XP'] = ['chromium-win-xp'].concat(
- fallbacksMap['WIN-VISTA']);
- // Should mac look at the tiger results?
- fallbacksMap['MAC'] = ['chromium-mac', 'platform/mac',
- 'platform/mac-snowleopard', 'platform/mac-leopard', 'platform/mac-tiger'];
- fallbacksMap['LINUX'] = ['chromium-linux', 'chromium-win', 'platform/win',
- 'platform/mac'];
-
- function htmlForFallbackHelp(fallbacks) {
- return '<ol class=fallback-list><li>' + fallbacks.join('</li><li>') +
- '</li></ol>';
- }
-
- function showLegend() {
- var legend = $('legend');
- if (!legend) {
- legend = document.createElement('div');
- legend.id = 'legend';
- document.body.appendChild(legend);
- }
-
- var html = '<div id=legend-toggle onclick="hideLegend()">Hide ' +
- 'legend [type esc]</div><div>Number of flaky tests listed next to ' +
- 'each builder</div><div id=legend-contents>';
- for (var expectation in getExpectationsMap()) {
- html += '<div class=' + expectation + '>' +
- getExpectationsMap()[expectation] + '</div>';
- }
-
- html += '<div class=merge>WEBKIT MERGE</div>';
- if (isLayoutTestResults()) {
- html += '<div class=wrong-expectations>WRONG EXPECTATIONS</div>' +
- '</div><br style="clear:both">' +
- '</div><h3>Test expectatons fallback order.</h3>';
-
- for (var platform in fallbacksMap) {
- html += '<div class=fallback-header>' + platform + '</div>' +
- htmlForFallbackHelp(fallbacksMap[platform]);
- }
- html += '<div>TIMES:</div>' +
- getHtmlForSlowTimes(MIN_SECONDS_FOR_SLOW_TEST) +
- '<div>DEBUG TIMES:</div>' +
- getHtmlForSlowTimes(MIN_SECONDS_FOR_SLOW_TEST_DEBUG);
- }
-
- legend.innerHTML = html;
- }
-
- function getHtmlForSlowTimes(minTime) {
- return '<ul><li>&lt;1 second == !SLOW</li><li>&gt;1 second && &lt;' +
- minTime + ' seconds == SLOW || !SLOW is fine</li><li>&gt;' +
- minTime + ' seconds == SLOW</li></ul>';
- }
-
- document.addEventListener('keydown', function(e) {
- if (e.keyIdentifier == 'U+003F' || e.keyIdentifier == 'U+00BF') {
- // WebKit MAC retursn 3F. WebKit WIN returns BF. This is a bug!
- // ? key
- showLegend();
- } else if (e.keyIdentifier == 'U+001B') {
- // escape key
- hideLegend();
- hidePopup();
- }
- }, false);
-
-
-
- </script>
-</head>
-
-<body></body>
-</html>