diff options
author | victorw@chromium.org <victorw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-03-19 20:33:53 +0000 |
---|---|---|
committer | victorw@chromium.org <victorw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-03-19 20:33:53 +0000 |
commit | b9954ef12fe3773bfb80a66aa1401e770da2b52d (patch) | |
tree | 7b5639da3826b86b7d1ba76ec7381b134b862d16 /webkit/tools | |
parent | fa289830f1172f7daabe9565d416ff90dfce4704 (diff) | |
download | chromium_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')
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 || ' '; - } - - 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 ? '↑' : '↓' ) + - '</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"> </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><1 second == !SLOW</li><li>>1 second && <' + - minTime + ' seconds == SLOW || !SLOW is fine</li><li>>' + - 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> |