summaryrefslogtreecommitdiffstats
path: root/webkit/tools/layout_tests/flakiness_dashboard.html
diff options
context:
space:
mode:
Diffstat (limited to 'webkit/tools/layout_tests/flakiness_dashboard.html')
-rw-r--r--webkit/tools/layout_tests/flakiness_dashboard.html2451
1 files changed, 0 insertions, 2451 deletions
diff --git a/webkit/tools/layout_tests/flakiness_dashboard.html b/webkit/tools/layout_tests/flakiness_dashboard.html
deleted file mode 100644
index c5c4922..0000000
--- a/webkit/tools/layout_tests/flakiness_dashboard.html
+++ /dev/null
@@ -1,2451 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-
-<head>
- <title>Chromium/WebKit Test History</title>
- <style>
- body {
- font-family: arial;
- font-size: 13px;
- }
- h2 {
- font-size: 16px;
- margin-bottom: .25em;
- }
- h3 {
- font-size: 13px;
- margin: 0;
- }
- #max-results-form {
- display: inline;
- }
- #max-results-input {
- width: 30px;
- }
- #tests-form {
- display: -webkit-box;
- }
- #tests-form > * {
- display: -webkit-box;
- }
- #tests-form > div {
- -webkit-box-flex: 0;
- }
- #tests-input {
- -webkit-box-flex: 1;
- }
- .test-link {
- white-space: normal;
- }
- .test-link, .options-container {
- padding: 0 2px;
- }
- .test-table {
- white-space: nowrap;
- }
- .test-table {
- width: 100%;
- }
- .test-table tr {
- border: 1px solid red;
- background-color: #E8E8E8;
- }
- .test-table tbody tr:hover {
- opacity: .7;
- }
- .test-table th {
- -webkit-user-select: none;
- -moz-user-select: none;
- }
- .link, .sortable .header-text {
- color: blue;
- text-decoration: underline;
- cursor: pointer;
- }
- .table-header-content,
- .table-header-content * {
- display: -webkit-box;
- }
- .table-header-content * {
- -webkit-box-flex: 1;
- cursor: pointer;
- white-space: normal;
- }
- .results {
- cursor: pointer;
- padding: 0;
- font-size: 10px;
- text-align: center;
- }
- #legend {
- position: fixed;
- top: 5px;
- right: 5px;
- width: 400px;
- padding: 2px;
- border: 2px solid grey;
- background-color: white;
- z-index: 1;
- }
- #legend ul, #legend ol {
- margin-top: 0;
- margin-bottom: 5px;
- }
- #legend-contents * {
- margin: 3px 0;
- padding: 0 2px;
- float: left;
- border: 1px solid grey;
- }
- #builders * {
- margin: 0 5px;
- display: inline-block;
- white-space: nowrap;
- }
- .test-table .wrong-expectations,
- .wrong-expectations {
- background-color: #pink;
- }
- .P {
- background-color: #8fdf5f;
- }
- .N {
- background-color: #fff;
- }
- .C {
- background-color: #ffc343;
- }
- .T {
- background-color: #fffc6c;
- }
- .I {
- background-color: #69f;
- }
- .S {
- background-color: #c6c;
- }
- .F {
- background-color: #e98080;
- }
- .O {
- background-color: #8a7700;
- }
- .Z {
- background-color: #96f;
- }
- .merge {
- background-color: black;
- color: white;
- }
- table .merge {
- width: 1px;
- }
- .separator {
- border: 1px solid lightgray;
- height: 0px;
- }
- .different-platform {
- color: gray;
- font-size: 10px;
- }
- .current-builder {
- font-weight: bold;
- }
- #passing-tests {
- -webkit-column-count: 3;
- -webkit-column-gap: 25px;
- -webkit-column-rule: 1px dashed black;
- -moz-column-count: 3;
- -moz-column-gap: 25px;
- -moz-column-rule: 1px dashed black;
- }
- .not-found {
- color: red;
- font-size: large;
- }
- #loading-ui {
- position: fixed;
- top: 0;
- left: 0;
- background-color: yellow;
- padding: 5px;
- text-align: center;
- font-weight: bold;
- }
- #popup {
- background-color: white;
- z-index: 1;
- position: absolute;
- border: 3px solid grey;
- padding: 3px;
- -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.5);
- -moz-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.5);
- -webkit-border-radius: 5px;
- -moz-border-radius: 5px;
- }
- #popup > ul {
- margin: 0;
- padding-left: 20px;
- }
- .expectations-container {
- clear: both;
- }
- .expectations-item {
- float: left;
- border: 1px solid grey;
- }
- .expectations-item .expectation {
- width: 400px;
- height: 300px;
- border: 0;
- border-top: 1px solid grey;
- }
- .expectations-item .large {
- width: 800px;
- height: 600px;
- }
- .expectations-item .checksum {
- height: 30px;
- }
- .non-webkit-results {
- width: 99%;
- }
- .used-platform {
- float: right;
- color: darkblue;
- margin: 0 5px;
- }
- .expectations-title {
- /* Hack to make a containing block for absolute positioned elements. */
- position: relative;
- overflow: hidden;
- clear: both;
- }
- .title {
- /* Position absolutely so the container does not grow to contain this. */
- position: absolute;
- }
- .platforms {
- position: absolute;
- background-color: white;
- right: 0;
- z-index: 1;
- }
- .file-bug {
- font-weight: bold;
- font-size: 11px;
- }
- </style>
-
- <!-- Flakiness dashboard is adding to appengine, this is a temporary solution
- to load dashboard_base.js from current and sub dirs so it works in both cases.
- -->
- <script src="dashboard_base.js"></script>
- <script src="dashboards/dashboard_base.js"></script>
- <script>
- /**
- * @fileoverview Creates a dashboard for multiple runs of a given set of tests
- * on the buildbots. Pulls in JSONP-ish files with the results for running
- * tests on a given builder (i.e. ADD_RESULTS(json_here)) and the expectations
- * for all tests on all builders (i.e. ADD_EXPECTATIONS(json_here)).
- *
- * This shows flakiness of the tests as well as runtimes for slow tests.
- *
- * Also, each column in the dashboard is sortable.
- *
- * Currently, only webkit tests are supported, but adding other test types
- * should just require the following steps:
- * -generate results.json and expectations.json for these tests
- * -copy them to the appropriate location
- * -add the builder name to the list of builders below.
- */
-
- //////////////////////////////////////////////////////////////////////////////
- // CONSTANTS
- //////////////////////////////////////////////////////////////////////////////
- var ALL = 'ALL';
- var FORWARD = 'forward';
- var BACKWARD = 'backward';
- var GTEST_FLAKY_MARKER = '\.FLAKY_';
- var CHROME_TEST_BASE_URL = 'http://src.chromium.org/viewvc/chrome/trunk/' +
- 'src/webkit/data/layout_tests/platform/';
- var TEST_URL_BASE_PATH =
- 'http://trac.webkit.org/projects/webkit/browser/trunk/LayoutTests/';
- var BUILDERS_BASE_PATH =
- 'http://build.chromium.org/buildbot/waterfall/builders/';
- var TEST_RESULTS_BASE_PATH =
- 'http://build.chromium.org/buildbot/layout_test_results/';
- var PLATFORMS = {
- 'MAC': 'MAC',
- 'LINUX': 'LINUX',
- 'WIN': 'WIN',
- 'WIN-XP': 'WIN-XP',
- 'WIN-VISTA': 'WIN-VISTA'
- };
- var PLATFORM_FALLBACKS = {
- 'WIN': 'ALL',
- 'WIN-XP': 'WIN',
- 'WIN-VISTA': 'WIN',
- 'LINUX': 'ALL',
- 'MAC': 'ALL'
- };
- var BUILD_TYPES = {'DEBUG': 'DBG', 'RELEASE': 'RELEASE'};
- var BASE_TABLE_HEADERS = ['missing', 'extra', 'slowest run', '% fail',
- 'flakiness (numbers are runtimes in seconds)'];
- var LAYOUT_TEST_TABLE_HEADERS = ['bugs', 'modifiers',
- 'expectations'].concat(BASE_TABLE_HEADERS);
- var MIN_SECONDS_FOR_SLOW_TEST = 4;
- var MIN_SECONDS_FOR_SLOW_TEST_DEBUG = 2 * MIN_SECONDS_FOR_SLOW_TEST;
- var FAIL_RESULTS = ['IMAGE', 'IMAGE+TEXT', 'TEXT', 'SIMPLIFIED', 'MISSING'];
- var CHUNK_SIZE = 25;
-
- //////////////////////////////////////////////////////////////////////////////
- // Methods and objects from dashboard_base.js to override.
- //////////////////////////////////////////////////////////////////////////////
- function generatePage() {
- if (currentState.useTestData)
- return;
-
- document.body.innerHTML = '<div id="loading-ui">LOADING...</div>';
-
- if (currentState.tests) {
- generatePageForIndividualTests(getIndividualTests());
- } else if (currentState.expectationsUpdate) {
- generatePageForExpectationsUpdate();
- } else {
- createTableHeadersArray('test');
- generatePageForBuilder(currentState.builder);
- }
-
- var maxResultsInput = $('max-results-input');
- if (maxResultsInput)
- maxResultsInput.value = getMaxResults();
-
- for (var builder in builders) {
- processTestResultsForBuilderAsync(builder);
- }
- }
-
- function handleValidHashParameter(key, value) {
- switch(key) {
- case 'tests':
- validateParameter(currentState, key, value,
- function() {
- return isValidName(value);
- });
-
- return true;
-
- case 'builder':
- validateParameter(currentState, key, value,
- function() {
- return value in builders;
- });
-
- return true;
-
- case 'sortColumn':
- validateParameter(currentState, key, value,
- function() {
- for (var i = 0; i < BASE_TABLE_HEADERS.length; i++) {
- if (value ==
- getSortColumnFromTableHeader(BASE_TABLE_HEADERS[i]))
- return true;
- }
- return value == 'test' || value == 'builder';
- });
-
- return true;
-
- case 'sortOrder':
- validateParameter(currentState, key, value,
- function() {
- return value == FORWARD || value == BACKWARD;
- });
-
- return true;
-
- case 'resultsHeight':
- case 'maxResults':
- case 'updateIndex':
- validateParameter(currentState, key, Number(value),
- function() {
- return value.match(/^\d+$/);
- });
-
- return true;
-
- case 'showCorrectExpectations':
- case 'showWrongExpectations':
- case 'showExpectations':
- case 'showFlaky':
- case 'showLargeExpectations':
- case 'legacyExpectationsSemantics':
- case 'showSkipped':
- case 'showSlow':
- case 'showWontFixSkip':
- case 'expectationsUpdate':
- currentState[key] = value == 'true';
-
- return true;
-
- default:
- return false;
- }
- }
-
- defaultStateValues = {
- sortOrder: BACKWARD,
- sortColumn: 'flakiness',
- showExpectations: false,
- showFlaky: true,
- showLargeExpectations: false,
- legacyExpectationsSemantics: true,
- showCorrectExpectations: !isLayoutTestResults(),
- showWrongExpectations: !isLayoutTestResults(),
- showWontFixSkip: !isLayoutTestResults(),
- showSlow: !isLayoutTestResults(),
- showSkipped: !isLayoutTestResults(),
- expectationsUpdate: false,
- updateIndex: 0,
- builder: defaultBuilderName,
- resultsHeight: 300
- };
-
- //////////////////////////////////////////////////////////////////////////////
- // GLOBALS
- //////////////////////////////////////////////////////////////////////////////
-
- // Text to put inside the header for each column of the test table.
- var tableHeaders;
- var perBuilderPlatformAndBuildType = {};
- var perBuilderFailures = {};
- // Map of builder to arrays of tests that are listed in the expectations file
- // but have for that builder.
- var perBuilderWithExpectationsButNoFailures = {};
- // Map of builder to arrays of paths that are skipped. This shows the raw
- // path used in test_expectations.txt rather than the test path since we
- // don't actually have any data here for skipped tests.
- var perBuilderSkippedPaths = {};
- // Maps test path to an array of {builder, testResults} objects.
- var testToResultsMap = {};
- var numFlakyTestsPerBuilder = {};
- // Tests that the user wants to update expectations for.
- var confirmedTests = {};
-
- function createResultsObjectForTest(test, builder) {
- return {
- test: test,
- builder: builder,
- // HTML for display of the results in the flakiness column
- html: '',
- flips: 0,
- slowestTime: 0,
- slowestNonTimeoutCrashTime: 0,
- meetsExpectations: true,
- isWontFixSkip: false,
- isFlaky: false,
- // Sorted string of missing expectations
- missing: '',
- // String of extra expectations (i.e. expectations that never occur).
- extra: '',
- // HTML for bug IDs for this test for all platforms
- bugsHTML: '',
- // HTML for expectations for this test for all platforms
- expectationsHTML: '',
- // HTML for modifiers for this test for all platforms
- modifiersHTML: '',
- rawResults: '',
- percentFailed: 0
- };
- }
-
- function getMaxResults() {
- // Max results is just a way of making the page performant. For now, the
- // non layout test results pages are fast even showing max results.
- if (!isLayoutTestResults() || currentState.expectationsUpdate ||
- currentState.tests && !currentState.maxResults) {
- return 750;
- }
- return currentState.maxResults || 200;
- }
-
- function getMatchingElement(stringToMatch, elementsMap) {
- for (var element in elementsMap) {
- if (stringContains(stringToMatch, elementsMap[element]))
- return element;
- }
- }
-
- function getPlatformAndBuildType(builderName) {
- if (!perBuilderPlatformAndBuildType[builderName]) {
- // If the build name does not contain a platform
- // or build type, assume Windows Release.
- var currentBuildUppercase = builderName.toUpperCase();
- var platform = getMatchingElement(currentBuildUppercase, PLATFORMS) ||
- 'WIN';
- var buildType = getMatchingElement(currentBuildUppercase, BUILD_TYPES) ||
- 'RELEASE';
- perBuilderPlatformAndBuildType[builderName] = {platform: platform,
- buildType: buildType};
- }
- return perBuilderPlatformAndBuildType[builderName];
- }
-
- function isDebug(builderName) {
- return getPlatformAndBuildType(builderName).buildType == 'DEBUG';
- }
-
- /**
- * Returns the expectation string for the given single character result.
- * This string should match the expectations that are put into
- * test_expectations.py.
- *
- * For example, if we start explicitly listing IMAGE result failures,
- * this function should start returning 'IMAGE'.
- */
- function getExpectationsFileStringForResult(result) {
- // For the purposes of comparing against the expecations of a test,
- // consider simplified diff failures as just text failures since
- // the test_expectations file doesn't treat them specially.
- if (result == 'S')
- return 'TEXT';
-
- if (result == 'N')
- return '';
-
- return getExpectationsMap()[result];
- }
-
- // Map of all tests to true values. This is just so we can have the list of
- // all tests across all the builders.
- var allTests;
-
- /**
- * Returns a map of all tests to true values. This is just so we can have the
- * list of all tests across all the builders.
- */
- function getAllTests() {
- if (!allTests) {
- allTests = {};
- for (var builder in builders) {
- addTestsForBuilder(builder, allTests);
- }
- }
- return allTests;
- }
-
- /**
- * Returns an array of tests to be displayed in the individual tests view.
- * Note that a directory can be listed as a test, so we expand that into all
- * tests in the directory.
- */
- function getIndividualTests() {
- if (!currentState.tests)
- return [];
-
- switch (currentState.tests) {
- case 'allSlowTests':
- return getSlowTests();
-
- case 'allNeedSlowTests':
- return getNeedSlowTests();
-
- case 'allIncorrectExpectations':
- return getAllTestsWithIncorrectExpectations();
-
- default:
- return getIndividualTestsForSubstringList();
- }
- }
-
- function getIndividualTestsForSubstringList() {
- // Convert windows slashes to unix slashes.
- var tests = currentState.tests.replace(/\\/g, '/');
- var separator = stringContains(tests, ' ') ? ' ' : ',';
- var testList = tests.split(separator);
-
- if (!isLayoutTestResults()) {
- testList.forEach(function(element) {
- // Make sure to search for the flaky and non-flaky names for a test.
- // e.g. for test Foo.Bar, also search for Foo.FLAKY_Bar.
- if (stringContains(element, GTEST_FLAKY_MARKER)) {
- testList.push(element.replace(GTEST_FLAKY_MARKER, '.'));
- } else {
- testList.push(element.replace('.', GTEST_FLAKY_MARKER));
- }
- });
- }
-
- // Put the tests into an object first and then move them into an array
- // as a way of deduping.
- var testsMap = {};
- for (var i = 0; i < testList.length; i++) {
- var path = testList[i];
-
- // Ignore whitespace entries as they'd match every test.
- if (path.match(/^\s*$/))
- continue;
-
- var allTests = getAllTests();
- var hasAnyMatches = false;
- for (var test in allTests) {
- if (caseInsensitiveContains(test, path)) {
- testsMap[test] = 1;
- hasAnyMatches = true;
- }
- }
-
- // If a path doesn't match any tests, then assume it's a full path
- // to a test that passes on all builders.
- if (!hasAnyMatches) {
- testsMap[path] = 1;
- }
- }
-
- var testsArray = [];
- for (var test in testsMap) {
- testsArray.push(test);
- }
- return testsArray;
- }
-
- function getAllTestsWithIncorrectExpectations() {
- return getAllTestsWithCondition(function(resultsForTest) {
- return !resultsForTest.meetsExpectations;
- });
- }
-
- function getSlowTests() {
- return getAllTestsWithCondition(isSlowTest);
- }
-
- function getNeedSlowTests() {
- return getAllTestsWithCondition(function(resultsForTest) {
- if (resultsForTest.isWontFixSkip)
- return false;
-
- return stringContains(resultsForTest.missing, 'SLOW') ||
- stringContains(resultsForTest.extra, 'SLOW');
- });
- }
-
- /**
- * Returns whether this test's slowest time is above the cutoff for
- * being a slow test.
- */
- function isSlowTest(resultsForTest) {
- var maxTime = isDebug(resultsForTest.builder) ?
- MIN_SECONDS_FOR_SLOW_TEST_DEBUG :
- MIN_SECONDS_FOR_SLOW_TEST;
- return resultsForTest.slowestNonTimeoutCrashTime > maxTime;
- }
-
- /**
- * Returns whether this test's slowest time is *well* below the cutoff for
- * being a slow test.
- */
- function isFastTest(resultsForTest) {
- var maxTime = isDebug(resultsForTest.builder) ?
- MIN_SECONDS_FOR_SLOW_TEST_DEBUG :
- MIN_SECONDS_FOR_SLOW_TEST;
- return resultsForTest.slowestNonTimeoutCrashTime < maxTime / 2;
- }
-
- function getAllTestsWithCondition(conditionFn) {
- processTestRunsForAllBuilders();
- var tests = getAllTests();
- var retVal = [];
- for (var test in tests) {
- var resultsArray = testToResultsMap[test];
- for (var i = 0; i < resultsArray.length; i++) {
- if (conditionFn(resultsArray[i])) {
- retVal.push(test);
- break;
- }
- }
- }
- return retVal;
- }
-
-
- /**
- * Adds all the tests for the given builder to the testMapToPopulate.
- */
- function addTestsForBuilder(builder, testMapToPopulate) {
- var tests = resultsByBuilder[builder].tests;
- for (var test in tests) {
- testMapToPopulate[test] = true;
- }
- }
-
- // Map of all tests to true values by platform and build type.
- // e.g. allTestsByPlatformAndBuildType['WIN']['DEBUG'] will have the union
- // of all tests run on the win-debug builders.
- var allTestsByPlatformAndBuildType = {};
- for (var platform in PLATFORMS) {
- allTestsByPlatformAndBuildType[platform] = {};
- }
-
- /**
- * Map of all tests to true values by platform and build type.
- * e.g. allTestsByPlatformAndBuildType['WIN']['DEBUG'] will have the union
- * of all tests run on the win-debug builders.
- */
- function getAllTestsWithSamePlatformAndBuildType(platform, buildType) {
- if (!allTestsByPlatformAndBuildType[platform][buildType]) {
- var tests = {};
- for (var thisBuilder in builders) {
- var thisBuilderBuildInfo = getPlatformAndBuildType(thisBuilder);
- if (thisBuilderBuildInfo.buildType == buildType &&
- thisBuilderBuildInfo.platform == platform) {
- addTestsForBuilder(thisBuilder, tests);
- }
- }
- allTestsByPlatformAndBuildType[platform][buildType] = tests;
- }
-
- return allTestsByPlatformAndBuildType[platform][buildType];
- }
-
- function addHTMLToIndividualOptionsArray(array, html, isCurrentPlatform) {
- if (html) {
- array.push('<div class="option' +
- (isCurrentPlatform ? '' : ' different-platform') + '">' + html +
- '</div>');
- }
- }
-
- function addHtmlToOptionsArrays(htmlArrays, expectations, modifiers, bugs,
- isCurrentPlatform) {
- addHTMLToIndividualOptionsArray(htmlArrays.expectations, expectations,
- isCurrentPlatform);
- addHTMLToIndividualOptionsArray(htmlArrays.modifiers, modifiers,
- isCurrentPlatform);
- addHTMLToIndividualOptionsArray(htmlArrays.bugs, getHtmlForBugs(bugs),
- isCurrentPlatform);
- }
-
- function getExpectations(test, platform, buildType) {
- var testObject = allExpectations[test];
- if (!testObject)
- return null;
-
- var platformObject;
- while (platform && !(platformObject = testObject[platform])) {
- platform = PLATFORM_FALLBACKS[platform];
- }
-
- if (platformObject) {
- if (platformObject[buildType])
- return platformObject[buildType]
-
- if (platformObject[ALL])
- return platformObject[ALL];
-
- if (testObject[ALL]) {
- var allPlatformObject = testObject[ALL];
- if (allPlatformObject[buildType])
- return allPlatformObject[buildType]
-
- if (allPlatformObject[ALL])
- return allPlatformObject[ALL]
- }
- }
- return null;
- }
-
- function populateExpectationsData(resultsObj) {
- var test = resultsObj.test;
-
- var buildInfo = getPlatformAndBuildType(resultsObj.builder);
- var platform = buildInfo.platform;
- var buildType = buildInfo.buildType;
-
- var thisPlatformExpectations = getExpectations(test, platform, buildType);
-
- var htmlArrays = {};
- htmlArrays.expectations = [];
- htmlArrays.modifiers = [];
- htmlArrays.bugs = [];
-
- var tests = resultsByBuilder[resultsObj.builder].tests;
-
- var testObject = allExpectations[test];
- var usedExpectations = {};
-
- for (var platform in allExpectations[test]) {
- var platformObject = testObject[platform];
- for (var buildType in platformObject) {
- var thisExpectations = platformObject[buildType];
- var modifiers = thisExpectations.modifiers;
-
- // A set of modifiers/expectations can apply to multiple platforms.
- // Only add HTML for each entry once.
- if (usedExpectations[modifiers])
- continue;
-
- usedExpectations[modifiers] = true;
-
- var bugs = modifiers.match(/BUG\d+/g);
- if (bugs) {
- for (var j = 0; j < bugs.length; j++) {
- modifiers = modifiers.replace(bugs[j], '');
- }
- modifiers = trimString(modifiers);
- bugs = bugs.join(' ');
- } else {
- bugs = '';
- }
-
- var expectations = thisExpectations.expectations;
- if (thisExpectations == thisPlatformExpectations) {
- resultsObj.bugs = bugs;
- resultsObj.expectations = expectations;
- resultsObj.modifiers = modifiers;
- resultsObj.isWontFixSkip = stringContains(modifiers, 'WONTFIX') ||
- stringContains(modifiers, 'SKIP');
- } else {
- addHtmlToOptionsArrays(htmlArrays, expectations, modifiers, bugs,
- false);
- }
- }
- }
-
- if (resultsObj.expectations) {
- addHtmlToOptionsArrays(htmlArrays, resultsObj.expectations,
- resultsObj.modifiers, resultsObj.bugs, true);
- }
-
- resultsObj.bugsHTML += htmlArrays.bugs.join('<div class=separator></div>');
- resultsObj.expectationsHTML +=
- htmlArrays.expectations.join('<div class=separator></div>');
- resultsObj.modifiersHTML +=
- htmlArrays.modifiers.join('<div class=separator></div>');
- }
-
- function addFallbacks(addFn, candidates, validValues) {
- var hasAnyValidValues = false;
- for (var i = 0; i < candidates.length; i++) {
- if (candidates[i] in validValues) {
- hasAnyValidValues = true;
- addFn(candidates[i]);
- }
- }
- if (!hasAnyValidValues)
- addFn(ALL);
- }
-
- function addTestToAllExpectations(test, expectations) {
- for (var j = 0; j < expectations.length; j++) {
- var modifiers = expectations[j].modifiers.split(' ');
- addFallbacks(function(platformKey) {
- addFallbacks(function(buildTypeKey) {
- // Setting the ALL key overrides any previously seen expectations.
- if (platformKey == ALL) {
- for (var i = 0; i < PLATFORMS.length; i++) {
- if (PLATFORMS[i] in allExpectations[test]) {
- // Setting the ALL key overrides any previously seen
- // expectations.
- if (buildTypeKey == ALL)
- allExpectations[test][PLATFORMS[i]] = {};
- }
- }
- } else if (buildTypeKey == ALL && allExpectations[test]) {
- allExpectations[test][platformKey] = {}
- }
-
- if (!allExpectations[test])
- allExpectations[test] = {};
-
- var testHolder = allExpectations[test];
- if (!testHolder[platformKey])
- testHolder[platformKey] = {}
-
- testHolder[platformKey][buildTypeKey] = expectations[j];
- }, modifiers, BUILD_TYPES);
- }, modifiers, PLATFORMS);
- }
- }
-
- /**
- * Data structure to hold the processed expectations.
- * allExpectations[testPath][platform][buildType] gets the object that has
- * expectations and modifiers properties for this platform/buildType.
- *
- * platform and buildType both go through fallback sets of keys from most
- * specific key to least specific. For example, on Windows Vista, we first
- * check the platform WIN-VISTA, if there's no such object, we check WIN,
- * then finally we check ALL. For build types, we check the current
- * buildType, then ALL.
- */
- var allExpectations;
-
- function processExpectations() {
- if (allExpectations)
- return allExpectations;
-
- allExpectations = {};
-
- var expectationsArray = [];
- for (var path in expectationsByTest) {
- expectationsArray.push(
- {path: path, expectations: expectationsByTest[path]});
- }
-
- // Sort the array to hit more specific paths last. More specific
- // paths (e.g. foo/bar/baz.html) override entries for less-specific ones
- // (e.g. foo/bar).
- expectationsArray.sort(getAlphanumericCompare('path'));
-
- var allTests = getAllTests();
- for (var i = 0; i < expectationsArray.length; i++) {
- var path = expectationsArray[i].path;
- var expectations = expectationsArray[i].expectations;
-
- var pathMatchesAnyTest = false;
- if (allTests[path]) {
- pathMatchesAnyTest = true;
- addTestToAllExpectations(path, expectations);
- } else {
- for (var test in allTests) {
- if (startsWith(test, path)) {
- pathMatchesAnyTest = true;
- addTestToAllExpectations(test, expectations);
- }
- }
- }
-
- if (!pathMatchesAnyTest)
- addTestToAllExpectations(path, expectations);
- }
- }
-
- function processMissingTestsWithExpectations(builder, platform, buildType) {
- var noFailures = [];
- var skipped = [];
-
- var allTestsForPlatformAndBuildType =
- getAllTestsWithSamePlatformAndBuildType(platform, buildType);
- for (var test in allExpectations) {
- var expectations = getExpectations(test, platform, buildType);
-
- if (!expectations)
- continue;
-
- // Test has expectations, but no result in the builders results.
- // This means it's either SKIP or passes on all builds.
- if (!allTestsForPlatformAndBuildType[test] &&
- !stringContains(expectations.modifiers, 'WONTFIX')) {
- if (stringContains(expectations.modifiers, 'SKIP')) {
- skipped.push(test);
- } else if (!expectations.expectations.match(/^\s*PASS\s*$/)) {
- // Don't show tests expected to always pass. This is used in ways like
- // the following:
- // foo/bar = FAIL
- // foo/bar/baz.html = PASS
- noFailures.push({test: test, expectations: expectations.expectations,
- modifiers: expectations.modifiers});
- }
- }
- }
-
- perBuilderSkippedPaths[builder] = skipped.sort();
- perBuilderWithExpectationsButNoFailures[builder] = noFailures.sort();
- }
-
- function processTestResultsForBuilderAsync(builder) {
- setTimeout(function() {
- processTestRunsForBuilder(builder);
- }, 0);
- }
-
- function processTestRunsForAllBuilders() {
- for (var builder in builders)
- processTestRunsForBuilder(builder);
- }
-
- function processTestRunsForBuilder(builderName) {
- if (perBuilderFailures[builderName]) {
- appendNumFlakyTests(builderName);
- return;
- }
-
- var start = Date.now();
- numFlakyTestsPerBuilder[builderName] = 0;
-
- processExpectations();
-
- var buildInfo = getPlatformAndBuildType(builderName);
- var platform = buildInfo.platform;
- var buildType = buildInfo.buildType;
- processMissingTestsWithExpectations(builderName, platform, buildType);
-
- var failures = [];
- var allTestsForThisBuilder = resultsByBuilder[builderName].tests;
-
- for (var test in allTestsForThisBuilder) {
- var resultsForTest = createResultsObjectForTest(test, builderName);
- populateExpectationsData(resultsForTest);
-
- var rawTest = resultsByBuilder[builderName].tests[test];
- resultsForTest.rawTimes = rawTest.times;
- var rawResults = rawTest.results;
- resultsForTest.rawResults = rawResults;
- resultsForTest.flips = rawResults.length - 1;
-
- var times = resultsForTest.rawTimes;
- var numTimesSeen = 0;
- var numResultsSeen = 0;
- var resultsIndex = 0;
- var currentResult;
- for (var i = 0;
- i < times.length && numTimesSeen < getMaxResults();
- i++) {
- numTimesSeen += times[i][RLE.LENGTH];
-
- while (rawResults[resultsIndex] && numTimesSeen > (numResultsSeen +
- rawResults[resultsIndex][RLE.LENGTH])) {
- numResultsSeen += rawResults[resultsIndex][RLE.LENGTH];
- resultsIndex++;
- }
-
- if (rawResults && rawResults[resultsIndex])
- currentResult = rawResults[resultsIndex][RLE.VALUE];
-
- var time = times[i][RLE.VALUE]
-
- // Ignore times for crashing/timeout runs for the sake of seeing if
- // a test should be marked slow.
- if (currentResult != 'C' && currentResult != 'T') {
- resultsForTest.slowestNonTimeoutCrashTime =
- Math.max(resultsForTest.slowestNonTimeoutCrashTime, time);
- }
- resultsForTest.slowestTime = Math.max(resultsForTest.slowestTime, time);
- }
-
- processMissingAndExtraExpectations(resultsForTest);
-
- if (resultsForTest.isFlaky)
- numFlakyTestsPerBuilder[builderName]++;
-
- failures.push(resultsForTest);
-
- if (!testToResultsMap[test])
- testToResultsMap[test] = [];
- testToResultsMap[test].push(resultsForTest);
- }
-
- appendNumFlakyTests(builderName);
- perBuilderFailures[builderName] = failures;
- logTime('processTestRunsForBuilder: ' + builderName, start);
- }
-
- function processMissingAndExtraExpectations(resultsForTest) {
- // Heuristic for determining whether expectations apply to a given test:
- // -If a test result happens < MIN_RUNS_FOR_FLAKE, then consider it a flaky
- // result and include it in the list of expected results.
- // -Otherwise, grab the first contiguous set of runs with the same result
- // for >= MIN_RUNS_FOR_FLAKE and ignore all following runs >=
- // MIN_RUNS_FOR_FLAKE.
- // This lets us rule out common cases of a test changing expectations for
- // a few runs, then being fixed or otherwise modified in a non-flaky way.
- var rawResults = resultsForTest.rawResults;
-
- // If the first result is no-data that means the test is skipped or is
- // being run on a different builder (e.g. moved from one shard to another).
- // Ignore these results since we have no real data about what's going on.
- if (rawResults[0][RLE.VALUE] == 'N')
- return;
-
- // Only consider flake if it doesn't happen twice in a row.
- var MIN_RUNS_FOR_FLAKE = 2;
- var resultsMap = {}
- var numResultsSeen = 0;
- var haveSeenNonFlakeResult = false;
- var numRealResults = 0;
- var failedCount = 0;
-
- var seenResults = {};
- for (var i = 0;
- i < rawResults.length && numResultsSeen < getMaxResults();
- i++) {
- var numResults = rawResults[i][RLE.LENGTH];
- numResultsSeen += numResults;
-
- var result = rawResults[i][RLE.VALUE];
- if (isFailingResult(result)) {
- failedCount += numResults;
- }
-
- var hasMinRuns = numResults >= MIN_RUNS_FOR_FLAKE;
- if (haveSeenNonFlakeResult && hasMinRuns) {
- continue;
- } else if (hasMinRuns) {
- haveSeenNonFlakeResult = true;
- } else if (!seenResults[result]) {
- // Only consider a short-lived result if we've seen it more than once.
- // Otherwise, we include lots of false-positives due to tests that fail
- // for a couple runs and then start passing.
- seenResults[result] = true;
- continue;
- }
-
- var expectation = getExpectationsFileStringForResult(result);
- resultsMap[expectation] = true;
- numRealResults++;
- }
-
- resultsForTest.isFlaky = numRealResults > 1;
- // Calculate the % of times the test failed - how flaky is it?
- resultsForTest.percentFailed =
- Math.round(failedCount / numResultsSeen * 100);
-
- var missingExpectations = [];
- var extraExpectations = [];
-
- if (isLayoutTestResults()) {
- var expectationsArray = resultsForTest.expectations ?
- resultsForTest.expectations.split(' ') : [];
- extraExpectations = expectationsArray.filter(
- function(element) {
- // TODO(ojan): Once all the FAIL lines are removed from
- // test_expectations.txt, delete all the legacyExpectationsSemantics
- // code.
- if (currentState.legacyExpectationsSemantics) {
- if (element == 'FAIL') {
- for (var i = 0; i < FAIL_RESULTS.length; i++) {
- if (resultsMap[FAIL_RESULTS[i]])
- return false;
- }
- return true;
- }
- }
-
- return element && !resultsMap[element] &&
- !stringContains(element, 'BUG');
- });
-
- for (var result in resultsMap) {
- var hasExpectation = false;
- for (var i = 0; i < expectationsArray.length; i++) {
- var expectation = expectationsArray[i];
- // TODO(ojan): Once all the FAIL lines are removed from
- // test_expectations.txt, delete all the legacyExpectationsSemantics
- // code.
- if (currentState.legacyExpectationsSemantics) {
- if (expectation == 'FAIL') {
- for (var j = 0; j < FAIL_RESULTS.length; j++) {
- if (result == FAIL_RESULTS[j]) {
- hasExpectation = true;
- break;
- }
- }
- }
- }
-
- if (result == expectation) {
- hasExpectation = true;
- }
-
- if (hasExpectation)
- break;
- }
- // If we have no expectations for a test and it only passes, then don't
- // list PASS as a missing expectation. We only want to list PASS if it
- // flaky passes, so there would be other expectations.
- if (!hasExpectation &&
- !(!expectationsArray.length && result == 'PASS' &&
- numRealResults == 1))
- missingExpectations.push(result);
- }
-
- // Only highlight tests that take > 2 seconds as needing to be marked as
- // slow. There are too many tests that take ~2 seconds every couple
- // hundred runs. It's not worth the manual maintenance effort.
- // Also, if a test times out, then it should not be marked as slow.
- var minTimeForNeedsSlow = isDebug(resultsForTest.builder) ? 2 : 1;
- if (isSlowTest(resultsForTest) && !resultsMap['TIMEOUT'] &&
- (!resultsForTest.modifiers ||
- !stringContains(resultsForTest.modifiers, 'SLOW'))) {
- missingExpectations.push('SLOW');
- } else if (isFastTest(resultsForTest) &&
- resultsForTest.modifiers &&
- stringContains(resultsForTest.modifiers, 'SLOW')) {
- extraExpectations.push('SLOW');
- }
- } else {
- var isMarkedFlaky = stringContains(resultsForTest.test,
- GTEST_FLAKY_MARKER);
- if (resultsForTest.isFlaky && !isMarkedFlaky) {
- missingExpectations.push('FLAKY');
- } else if (!resultsForTest.isFlaky && isMarkedFlaky) {
- extraExpectations.push('FLAKY');
- }
- }
-
- resultsForTest.meetsExpectations =
- !missingExpectations.length && !extraExpectations.length;
- resultsForTest.missing = missingExpectations.sort().join(' ');
- resultsForTest.extra = extraExpectations.sort().join(' ');
- }
-
- var bugUrlPrefix = '<a href="http://';
- var bugUrlPostfix = '/$1">$1</a> ';
- var internalBugReplaceValue = bugUrlPrefix + 'b' + bugUrlPostfix;
- var externalBugReplaceValue = bugUrlPrefix + 'crbug.com' + bugUrlPostfix;
-
- /**
- * Returns the BUG modifiers linking to the bug.
- * Bugs with 4 or 5 digits are crbug.com bugs. Bugs with 6 or 7 digits
- * are internal google bugs.
- */
- function getHtmlForBugs(bugs) {
- bugs = bugs.replace(/BUG(\d{4})(\ |$)/g, externalBugReplaceValue);
- bugs = bugs.replace(/BUG(\d{5})(\ |$)/g, externalBugReplaceValue);
- bugs = bugs.replace(/BUG(\d{6})(\ |$)/g, internalBugReplaceValue);
- bugs = bugs.replace(/BUG(\d{7})(\ |$)/g, internalBugReplaceValue);
- return bugs;
- }
-
- function getLinkHTMLToOpenWindow(url, text) {
- return '<div class=link onclick="window.open(\'' + url + '\')">' + text +
- '</div>';
- }
-
- function createBlameListHTML(revisions, index, urlBase, separator, repo) {
- var thisRevision = revisions[index];
- if (!thisRevision)
- return '';
-
- var previousRevision = revisions[index + 1];
- if (previousRevision && previousRevision != thisRevision) {
- previousRevision++;
- return getLinkHTMLToOpenWindow(
- urlBase + thisRevision + separator + previousRevision,
- repo + ' blamelist r' + previousRevision + ':r' + thisRevision);
- } else {
- return 'At ' + repo + ' revision: ' + thisRevision;
- }
- }
-
- /**
- * Returns whether the result for index'th result for testName on builder was
- * a failure.
- */
- function isFailure(builder, testName, index) {
- var currentIndex = 0;
- var rawResults = resultsByBuilder[builder].tests[testName].results;
- for (var i = 0; i < rawResults.length; i++) {
- currentIndex += rawResults[i][RLE.LENGTH];
- if (currentIndex > index)
- return isFailingResult(rawResults[i][RLE.VALUE]);
- }
- console.error('Index exceeds number of results: ' + index);
- }
-
- /**
- * Returns an array of buildNumbers for all builds where this test failed.
- */
- function getBuildNumbersForFailures(builder, testName) {
- var rawResults = resultsByBuilder[builder].tests[testName].results;
- var buildNumbers = resultsByBuilder[builder].buildNumbers;
- var index = 0;
- var failures = [];
- for (var i = 0; i < rawResults.length; i++) {
- var numResults = rawResults[i][RLE.LENGTH];
- if (isFailingResult(rawResults[i][RLE.VALUE])) {
- for (var j = 0; j < numResults; j++) {
- failures.push(buildNumbers[index + j]);
- }
- }
- index += numResults;
- }
-
- return failures;
- }
-
- /**
- * Returns the path to the failure log for this non-webkit test.
- */
- function getPathToFailureLog(testName) {
- return '/steps/' + currentState.testType + '/logs/' +
- testName.split('.')[1]
- }
-
- /**
- * Returns the path to the logs for non-webkit tests.
- */
- function getBasePathForBuilderLogs(builder, buildNumber) {
- return BUILDERS_BASE_PATH + builder + '/builds/' + buildNumber
- }
-
- function showPopupForBuild(e, builder, index, opt_testName) {
- var html = '';
-
- var time = resultsByBuilder[builder].secondsSinceEpoch[index];
- if (time) {
- var date = new Date(time * 1000);
- html += date.toLocaleDateString() + ' ' + date.toLocaleTimeString();
- }
-
- html += '<ul><li>' +
- createBlameListHTML(resultsByBuilder[builder].webkitRevision, index,
- 'http://trac.webkit.org/log/?verbose=on&rev=', '&stop_rev=',
- 'WebKit') +
- '</li><li>' +
- createBlameListHTML(resultsByBuilder[builder].chromeRevision, index,
- 'http://build.chromium.org/buildbot/perf/dashboard/ui/' +
- 'changelog.html?url=/trunk/src&mode=html&range=', ':', 'Chrome') +
- '</li>';
-
- var chromeRevision = resultsByBuilder[builder].chromeRevision[index];
- if (chromeRevision && isLayoutTestResults()) {
- html += '<li><a href="' + TEST_RESULTS_BASE_PATH + builders[builder] +
- '/' + chromeRevision + '/layout-test-results.zip' +
- '">layout-test-results.zip</a></li>';
- }
-
- var buildNumber = resultsByBuilder[builder].buildNumbers[index];
- var buildBasePath = getBasePathForBuilderLogs(builder, buildNumber);
-
- if (!isLayoutTestResults() && opt_testName &&
- isFailure(builder, opt_testName, index)) {
- html += '<li>' + getLinkHTMLToOpenWindow(
- buildBasePath + getPathToFailureLog(opt_testName),
- 'Failure log') + '</li>';
- }
-
- html += '<li>' + getLinkHTMLToOpenWindow(buildBasePath, 'Build log') +
- '</li></ul>';
-
- showPopup(e, html);
- }
-
- function showPopupForTest(e, test) {
- showPopup(e, getHTMLForIndividulTestOnAllBuilders(test));
- appendExpectations();
- }
-
- function getHtmlForTestResults(test) {
- var html = '';
- var results = test.rawResults.concat();
- var times = test.rawTimes.concat();
- var builder = test.builder;
- var buildNumbers = resultsByBuilder[builder].buildNumbers;
-
- var indexToReplaceCurrentResult = -1;
- var indexToReplaceCurrentTime = -1;
- var currentResultArray, currentTimeArray, currentResult, innerHTML,
- resultString;
- var maxIndex = Math.min(buildNumbers.length, getMaxResults());
- for (var i = 0; i < maxIndex; i++) {
- if (i > indexToReplaceCurrentResult) {
- currentResultArray = results.shift();
- if (currentResultArray) {
- currentResult = currentResultArray[RLE.VALUE];
- // Treat simplified diff failures as just text failures.
- if (currentResult == 'S')
- currentResult = 'F';
- indexToReplaceCurrentResult += currentResultArray[RLE.LENGTH];
- } else {
- currentResult = 'N';
- indexToReplaceCurrentResult += buildNumbers.length;
- }
-
- resultString = getExpectationsFileStringForResult(currentResult);
- }
-
- if (i > indexToReplaceCurrentTime) {
- currentTimeArray = times.shift();
- var currentTime = 0;
- if (currentResultArray) {
- currentTime = currentTimeArray[RLE.VALUE];
- indexToReplaceCurrentTime += currentTimeArray[RLE.LENGTH];
- } else {
- indexToReplaceCurrentTime += buildNumbers.length;
- }
- innerHTML = currentTime || '&nbsp;';
- }
-
- html += '<td title="' + (resultString || 'NO DATA') +
- '. Click for more info." class="results ' + currentResult +
- '" onclick=\'showPopupForBuild(event, "' + builder + '",' + i +
- ',"' + test.test + '")\'>' + innerHTML + '</td>';
-
- var webkitRevision = resultsByBuilder[builder].webkitRevision;
- var isWebkitMerge = webkitRevision[i + 1] &&
- webkitRevision[i] != webkitRevision[i + 1];
- if (isWebkitMerge)
- html += '<td class=merge></td>';
- }
- return html;
- }
-
- function getHTMLForTestsWithExpectationsButNoFailures(builder) {
- var tests = perBuilderWithExpectationsButNoFailures[builder];
- var skippedPaths = perBuilderSkippedPaths[builder];
-
- var html = '';
- if (tests.length || skippedPaths.length) {
- var buildInfo = getPlatformAndBuildType(builder);
- html += '<h2>Expectations for ' + buildInfo.platform + '-' +
- buildInfo.buildType + ':</h2>';
- }
-
- var open = '<div onclick="selectContents(this)">';
-
- if (tests.length) {
- html += '<h3>Have not failed in last ' +
- resultsByBuilder[builder].buildNumbers.length +
- ' runs.</h3><div id="passing-tests">';
-
- for (var i = 0; i < tests.length; i++) {
- html += open + tests[i].test + '</div>';
- }
-
- html += '</div>';
- }
-
- if (skippedPaths.length) {
- html += '<h3>' +
- getLinkHTMLToToggleState('showSkipped',
- 'Skipped tests in test_expectations.txt') +
- '</h3>';
-
- if (currentState.showSkipped) {
- html += '<div id="passing-tests">' + open +
- skippedPaths.join('</div>' + open) + '</div></div>';
- }
- }
- return html;
- }
-
- /**
- * Returns whether the test has passed continuously for at
- * least as many runs as we are showing results for.
- */
- function hasPassedInMaxRuns(testResult) {
- var results = testResult.rawResults;
- return results[0][RLE.VALUE] == 'P' &&
- results[0][RLE.LENGTH] >= getMaxResults();
- }
-
- /**
- * Returns whether we should exclude test results from the test table.
- */
- function shouldHideTest(testResult) {
- if (testResult.isWontFixSkip)
- return !currentState.showWontFixSkip;
-
- if (testResult.isFlaky)
- return !currentState.showFlaky;
-
- if (isSlowTest(testResult))
- return !currentState.showSlow;
-
- if (testResult.meetsExpectations)
- return !currentState.showCorrectExpectations;
-
- return !currentState.showWrongExpectations;
- }
-
- /**
- * Sets the browser's selection to the element's contents.
- */
- function selectContents(element) {
- window.getSelection().selectAllChildren(element);
- }
-
- function getCreateBugHTML(test) {
- var symptom = test.isFlaky ? 'flaky' : 'failing';
- var title = 'Layout Test ' + test.test + ' is ' + symptom;
- var description = 'The following layout test is ' + symptom + ' on ' +
- '[insert platform]\n\n' + test.test + '\n\nProbable cause:\n\n' +
- '[insert probable cause]';
- return bugUrlPrefix + 'code.google.com/p/chromium/issues/entry?' +
- 'template=Layout%20Test%20Failure&summary=' +
- encodeURIComponent(title) + '&comment=' +
- encodeURIComponent(description) + '" class="file-bug">FILE BUG</a>';
- }
-
- function getHTMLForSingleTestRow(test, opt_isCrossBuilderView) {
- if (!opt_isCrossBuilderView && shouldHideTest(test)) {
- // The innerHTML call is considerably faster if we exclude the rows for
- // items we're not showing than if we hide them using display:none.
- // For the crossBuilderView, we want to show all rows the user is
- // explicitly listing tests to view.
- return '';
- }
-
- // If opt_isCrossBuilderView is true, we're just viewing a single test
- // with results for many builders, so the first column is builder names
- // instead of test paths.
- var testCellHTML = opt_isCrossBuilderView ? test.builder :
- '<span class="link" onclick="showPopupForTest(event, \'' + test.test +
- '\');selectContents(this);return false;">' + test.test + '</span>';
-
- var html = '<tr class="' +
- (test.meetsExpectations ? '' : 'wrong-expectations') +
- '"><td class=test-link>' + testCellHTML;
-
- if (isLayoutTestResults()) {
- html += '</td><td class=options-container>' +
- (test.bugsHTML ? test.bugsHTML : getCreateBugHTML(test)) +
- '</td><td class=options-container>' + test.modifiersHTML +
- '</td><td class=options-container>' + test.expectationsHTML;
- }
-
- return html +
- '</td><td>' + test.missing +
- '</td><td>' + test.extra +
- '</td><td>' + (test.slowestTime ? test.slowestTime + 's' : '') +
- '</td><td>' + test.percentFailed +
- '</td>' + getHtmlForTestResults(test) + '</tr>';
- }
-
- function getSortColumnFromTableHeader(headerText) {
- return headerText.split(' ', 1)[0];
- }
-
- function getHTMLForTestTable(rowsHTML) {
- var html = '<table class=test-table><thead><tr>';
- for (var i = 0; i < tableHeaders.length; i++) {
- // Use the first word of the header title as the sortkey
- var thisSortValue = getSortColumnFromTableHeader(tableHeaders[i]);
- var arrowHTML = thisSortValue == currentState.sortColumn ?
- '<span class=' + currentState.sortOrder + '>' +
- (currentState.sortOrder == FORWARD ? '&uarr;' : '&darr;' ) +
- '</span>' :
- '';
- html += '<th sortValue=' + thisSortValue +
- // Extend last th through all the rest of the columns.
- (i == tableHeaders.length - 1 ? ' colspan=10000' : '') +
- // Extra span here is so flex boxing actually centers.
- // There's probably a better way to do this with CSS only though.
- '><div class=table-header-content><span></span>' + arrowHTML +
- '<span class=header-text>' + tableHeaders[i] + '</span>' +
- arrowHTML + '</div></th>';
- }
- return html + '</tr></thead><tbody>' + rowsHTML + '</tbody></table>';
- }
-
- function appendHTML(html) {
- var startTime = Date.now();
- // InnerHTML to a div that's not in the document. This is
- // ~300ms faster in Safari 4 and Chrome 4 on mac.
- var div = document.createElement('div');
- div.innerHTML = html;
- document.body.appendChild(div);
- logTime('Time to innerHTML', startTime);
- }
-
- function getAlphanumericCompare(column, reverse) {
- return getReversibleCompareFunction(function(a, b) {
- // Put null entries at the bottom
- var a = a[column] ? String(a[column]) : 'z';
- var b = b[column] ? String(b[column]) : 'z';
-
-
- // For gtest tests, we make them as flaky by prefixing the test name with
- // FLAKY_, resulting in two entries for the test.
- // Place flaky tests next to their non-flaky counterparts.
- // TODO(ojan): Merge the non-flaky test with the flaky one so there's
- // only a single entry per test.
- if (!isLayoutTestResults()) {
- a = a.replace(GTEST_FLAKY_MARKER, '.');
- b = b.replace(GTEST_FLAKY_MARKER, '.');
- }
-
- if (a < b)
- return -1;
- else if (a == b)
- return 0;
- else
- return 1;
- }, reverse);
- }
-
- function getNumericSort(column, reverse) {
- return getReversibleCompareFunction(function(a, b) {
- a = parseFloat(a[column]);
- b = parseFloat(b[column]);
- return a - b;
- }, reverse);
- }
-
- function getReversibleCompareFunction(compare, reverse) {
- return function(a, b) {
- return compare(reverse ? b : a, reverse ? a : b);
- }
- }
-
- function changeSort(e) {
- var target = e.currentTarget;
- e.preventDefault();
-
- var sortValue = target.getAttribute('sortValue');
- while (target && target.tagName != 'TABLE') {
- target = target.parentNode;
- }
-
- var sort = 'sortColumn';
- var orderKey = 'sortOrder';
- if (sortValue == currentState[sort] && currentState[orderKey] == FORWARD)
- order = BACKWARD;
- else
- order = FORWARD;
-
- setState(sort, sortValue, orderKey, order);
- }
-
- function sortTests(tests, column, order) {
- var resultsProperty, sortFunctionGetter;
- if (column == 'flakiness') {
- sortFunctionGetter = getNumericSort;
- resultsProperty = 'flips';
- } else if (column == 'slowest') {
- sortFunctionGetter = getNumericSort;
- resultsProperty = 'slowestTime';
- } else if (column == '%') {
- sortFunctionGetter = getNumericSort;
- resultsProperty = 'percentFailed';
- } else {
- sortFunctionGetter = getAlphanumericCompare;
- resultsProperty = column;
- }
-
- tests.sort(sortFunctionGetter(resultsProperty, order == BACKWARD));
- }
-
- function addUpdate(testsNeedingUpdate, test, builderName, missing, extra) {
- if (!testsNeedingUpdate[test])
- testsNeedingUpdate[test] = {};
-
- var buildInfo = getPlatformAndBuildType(builderName);
- var builder = buildInfo.platform + ' ' + buildInfo.buildType;
- if (!testsNeedingUpdate[test][builder])
- testsNeedingUpdate[test][builder] = {};
-
- if (missing)
- testsNeedingUpdate[test][builder].missing = missing;
-
- if (extra)
- testsNeedingUpdate[test][builder].extra = extra;
- }
-
- function generatePageForExpectationsUpdate() {
- processTestRunsForAllBuilders();
- var testsNeedingUpdate = {};
- for (var test in testToResultsMap) {
- var results = testToResultsMap[test];
- for (var i = 0; i < results.length; i++) {
- var thisResult = results[i];
- if (!thisResult.missing && !thisResult.extra)
- continue;
-
- addUpdate(testsNeedingUpdate, test, thisResult.builder,
- thisResult.missing, thisResult.extra);
- }
- }
-
- for (var builder in builders) {
- var tests = perBuilderWithExpectationsButNoFailures[builder]
- for (var i = 0; i < tests.length; i++) {
- addUpdate(testsNeedingUpdate, tests[i].test, builder,
- null, tests[i].expectations + ' ' + tests[i].modifiers);
- }
- }
-
- // Get the keys in alphabetical order, so it is easy to process groups
- // of tests.
- var keys = getKeys(testsNeedingUpdate).sort();
- showUpdateInfoForTest(testsNeedingUpdate, keys);
- }
-
- /**
- * Show the test results and the json for differing expectations, and
- * allow the user to include or exclude this update.
- *
- * @param {Object} testsNeedingUpdate Tests that need updating.
- * @param {Array.<string>} keys Keys into the testNeedingUpdate object.
- */
- function showUpdateInfoForTest(testsNeedingUpdate, keys) {
- var test = keys[currentState.updateIndex];
- document.body.innerHTML = '<b>' + test + '</b>' +
- '<div style="float:right">' + (currentState.updateIndex + 1) + ' of ' +
- + keys.length + ' tests</div><br><br>';
-
- var buttonRegion = document.createElement('div');
- var includeBtn = document.createElement('input');
- includeBtn.type = 'button';
- includeBtn.value = 'include';
- includeBtn.addEventListener('click',
- partial(handleUpdate, true, testsNeedingUpdate, keys),
- false);
- buttonRegion.appendChild(includeBtn);
-
- var excludeBtn = document.createElement('input');
- excludeBtn.type = 'button';
- excludeBtn.value = 'exclude';
- excludeBtn.addEventListener('click',
- partial(handleUpdate, false, testsNeedingUpdate, keys),
- false);
- buttonRegion.appendChild(excludeBtn);
-
- var doneBtn = document.createElement('input');
- doneBtn.type = 'button';
- doneBtn.value = 'done';
- doneBtn.addEventListener('click', finishUpdate, false);
- buttonRegion.appendChild(doneBtn);
-
- document.body.appendChild(buttonRegion);
-
- var confirmStr = JSON.stringify(testsNeedingUpdate[test], null, 4);
-
- var pre = document.createElement('pre');
- pre.style.marginTop = 0;
- pre.innerHTML = confirmStr;
- document.body.appendChild(pre);
- var div = document.createElement('div');
- div.innerHTML = getHTMLForIndividulTestOnAllBuilders(test);
- document.body.appendChild(div);
- }
-
-
- /**
- * When the user has finished selecting expectations to update, provide them
- * with json to copy over.
- * TODO(jparent): This could also probably spit out instructinos on what to
- * do next.
- */
- function finishUpdate() {
- document.body.innerHTML = JSON.stringify(confirmedTests);
- }
-
- /**
- * Handle user click on either "include" or "exclude" buttons.
- * @param {boolean} isAccepted Whether the user accepted this update.
- * @param {Object} testsNeedingUpdate Tests that need updating.
- * @param {Array.<string>} keys Keys into the testNeedingUpdate object.
- *
- */
- function handleUpdate(isAccepted, testsNeedingUpdate, keys) {
- var test = keys[currentState.updateIndex];
- if (isAccepted) {
- confirmedTests[test] = testsNeedingUpdate[test];
- } else if (confirmedTests[test]) {
- delete confirmedTests[test];
- }
-
- setState("updateIndex", currentState.updateIndex + 1);
-
- if (currentState.updateIndex < keys.length) {
- showUpdateInfoForTest(testsNeedingUpdate, keys);
- } else {
- finishUpdate();
- }
- }
-
- function getHTMLForIndividulTestOnAllBuilders(test) {
- createTableHeadersArray('builder');
- processTestRunsForAllBuilders();
-
- var testResults = testToResultsMap[test];
- var html = '';
- if (testResults && testResults.length) {
- if (isLayoutTestResults()) {
- var tracURL = TEST_URL_BASE_PATH + test
- html += getLinkHTMLToOpenWindow(tracURL, tracURL);
- }
-
- html +='<div><b>If a builder is not listed, that means the builder ' +
- 'does not run that test (e.g. it is skipped) or all runs of the ' +
- 'test passed.</b></div>';
-
- for (var j = 0; j < testResults.length; j++) {
- html += getHTMLForSingleTestRow(testResults[j], true);
- }
- html = getHTMLForTestTable(html);
- } else {
- if (expectationsByTest[test]) {
- for (var i = 0; i < expectationsByTest[test].length; i++) {
- html += '<div>' + expectationsByTest[test][i].modifiers + ' | ' +
- expectationsByTest[test][i].expectations + '</div>';
- }
- }
- html += '<div class="not-found">Test not found. Either it does ' +
- 'not exist, is skipped or passes on all platforms.</div>';
- }
-
- html += '<div class=expectations test=' + test + '><div>' +
- getLinkHTMLToToggleState('showExpectations', 'results')
-
- if (isLayoutTestResults()) {
- html += ' | ' + getLinkHTMLToToggleState('showLargeExpectations',
- 'large thumbnails') +
- ' | <b>Only shows actual results/diffs from the most recent' +
- ' *failure* on each bot.</b>';
- } else {
- html += ' | <span>Results height:<input ' +
- 'onchange="setState(\'resultsHeight\',this.value)" value="' +
- currentState.resultsHeight + '" style="width:2.5em">px</span>';
- }
- html += '</div></div>';
- return html;
- }
-
- function getExpectationsContainer(expectationsContainers, parentContainer,
- expectationsType) {
- if (!expectationsContainers[expectationsType]) {
- var container = document.createElement('div');
- container.className = 'expectations-container';
- parentContainer.appendChild(container);
- expectationsContainers[expectationsType] = container;
- }
- return expectationsContainers[expectationsType];
- }
-
- function getExtension(path) {
- var parts = path.split('.')
- var extension = parts[parts.length - 1];
- return extension == 'html' ? 'txt' : extension;
- }
-
- function ensureTrailingSlash(path) {
- if (path.match(/\/$/))
- return path;
- return path + '/';
- }
-
- /**
- * Adds a specific expectation. If it's an image, it's only added on the
- * image's onload handler. If it's a text file, then a script tag is appended
- * as a hack to see if the file 404s (necessary since it's cross-domain).
- * Once all the expectations for a specific type have loaded or errored
- * (e.g. all the checksums), then we go through and identify which platform
- * uses which expectation.
- *
- * @param {Object} expectationsContainers Map from expectations type to
- * container DIV.
- * @param {Element} parentContainer Container element for
- * expectationsContainer divs.
- * @param {string} platform Platform string. Empty string for non-platform
- * specific expectations.
- * @param {string} path Relative path to the expectation.
- * @param {string} base Base path for the expectation URL.
- * @param {string} suffix Suffix to place at the end of the path.
- * @param {string} opt_builder Builder whose actual results this expectation
- * points to.
- */
- function addExpectationItem(expectationsContainers, parentContainer, platform,
- path, base, suffix, opt_builder) {
- var fileExtension = getExtension(path);
- var container = getExpectationsContainer(expectationsContainers,
- parentContainer, fileExtension);
- var isImage = path.match(/\.png$/);
-
- // TODO(ojan): Is there any way to do this that doesn't rely on script
- // tags? They spew a lot of errors to the console.
- var dummyNode = document.createElement(isImage ? 'img' : 'script');
- var platformPart = platform ? ensureTrailingSlash(platform) : '';
-
- // TODO(ojan): Get rid of this once we upstream the chromium expected
- // results or move their contents out of the LayoutTests directory.
- if (startsWith(platformPart, 'chromium'))
- platformPart += 'LayoutTests/';
-
- dummyNode.src = base + platformPart + path + suffix;
-
- var childContainer = document.createElement('span');
- childContainer.className = 'unloaded';
-
- dummyNode.onload = function() {
- childContainer.appendChild(
- getExpectationsTitle(platform, path, opt_builder || ''));
- childContainer.className = 'expectations-item';
-
- var item;
- if (isImage) {
- item = dummyNode;
- } else {
- item = document.createElement('iframe');
- item.src = dummyNode.src;
- }
-
- item.className = 'expectation ' + fileExtension;
- if (currentState.showLargeExpectations)
- item.className += ' large';
- childContainer.appendChild(item);
- handleFinishedLoadingExpectations(container);
- }
- dummyNode.onerror = function() {
- childContainer.parentNode.removeChild(childContainer);
- handleFinishedLoadingExpectations(container);
- }
-
- // Append script elements now so that they load. Images load without being
- // appended to the DOM.
- if (!isImage) {
- childContainer.appendChild(dummyNode);
- }
-
- container.appendChild(childContainer);
- }
-
- /**
- * Identifies which expectations are used on which platform once all the
- * expectations of a given type have loaded (e.g. the container for checksum
- * expectations for this test had no child elements with the class
- * "unloaded").
- *
- * @param {string} container Element containing the expectations for a given
- * test and a given type (e.g. checksum).
- */
- function handleFinishedLoadingExpectations(container) {
- if (container.getElementsByClassName('unloaded').length)
- return;
-
- var titles = container.getElementsByClassName('expectations-title');
- for (var platform in fallbacksMap) {
- var fallbacks = fallbacksMap[platform];
- var winner = null;
- var winningIndex = -1;
- for (var i = 0; i < titles.length; i++) {
- var title = titles[i];
-
- if (!winner && title.platform == "") {
- winner = title;
- continue;
- }
-
- for (var j = 0; j < fallbacks.length; j++) {
- if ((winningIndex == -1 || winningIndex > j) &&
- title.platform == fallbacks[j]) {
- winningIndex = j;
- winner = title;
- break;
- }
- }
- }
- if (winner) {
- winner.getElementsByClassName('platforms')[0].innerHTML +=
- '<div class=used-platform>' + platform + '</div>';
- } else {
- console.log('No expectations identified for this test. This means ' +
- 'there is a logic bug in the dashboard for which expectations a ' +
- 'platform uses or trac.webkit.org/src.chromium.org is giving ' +
- ' 5XXs.');
- }
- }
- }
-
- var TRAC_IMAGE_BASE_URL;
- /**
- * Trac seems to only show the raw image if you're viewing at a specific
- * revision. Use the latest revision on any builder.
- */
- function getTracImageBaseURL() {
- if (!TRAC_IMAGE_BASE_URL) {
- TRAC_IMAGE_BASE_URL = 'http://trac.webkit.org/export/' +
- getLatestKnownRevision(false) + '/trunk/LayoutTests/';
- }
- return TRAC_IMAGE_BASE_URL;
- }
-
- function getLatestKnownRevision(isChrome) {
- var revision = 0;
- for (var builder in builders) {
- var results = resultsByBuilder[builder];
- var revisions = isChrome ? results.chromeRevision :
- results.webkitRevision;
- if (revision < revisions[0])
- revision = revisions[0];
- }
- return revision;
- }
-
- function addExpectations(expectationsContainers, container, base, imageBase,
- platform, text, checksum, png, textSuffix) {
- addExpectationItem(expectationsContainers, container, platform, text, base,
- textSuffix);
- addExpectationItem(expectationsContainers, container, platform, checksum,
- base, textSuffix);
- addExpectationItem(expectationsContainers, container, platform, png,
- imageBase, '');
- }
-
- function getExpectationsTitle(platform, path, builder) {
- var header = document.createElement('h3');
- header.className = 'expectations-title';
-
- var innerHTML;
- if (builder) {
- var resultsType;
- if (endsWidth(path, '-stack.txt')) {
- resultsType = 'STACKTRACE';
- } else if (endsWidth(path, '-actual.txt') ||
- endsWidth(path, '-actual.checksum') ||
- endsWidth(path, '-actual.png')) {
- resultsType = 'ACTUAL RESULTS';
- } else {
- resultsType = 'DIFF';
- }
- innerHTML = resultsType + ': ' + builder;
- } else if (platform === "") {
- var parts = path.split('/');
- innerHTML = parts[parts.length - 1];
- } else {
- innerHTML = platform || path;
- }
-
- header.innerHTML = '<div class=title>' + innerHTML +
- '</div><div style="float:left">&nbsp;</div>' +
- '<div class=platforms style="float:right"></div>';
- header.platform = platform;
- return header;
- }
-
- function loadExpectations(expectationsContainer) {
- if (isLayoutTestResults()) {
- loadExpectationsLayoutTests(expectationsContainer);
- } else {
- var test = expectationsContainer.getAttribute('test');
- var results = testToResultsMap[test];
- for (var i = 0; i < results.length; i++) {
- loadNonWebKitResultsForBuilder(results[i].builder, test,
- expectationsContainer);
- }
- }
- }
-
- function loadNonWebKitResultsForBuilder(builder, test,
- expectationsContainer) {
- var failures = getBuildNumbersForFailures(builder, test);
- var container = document.createElement('div');
- container.innerHTML = '<div><b>' + builder + '</b></div>';
- expectationsContainer.appendChild(container);
- for (var i = 0; i < failures.length; i++) {
- var pathToLog = getBasePathForBuilderLogs(builder, failures[i]) +
- getPathToFailureLog(test);
- appendNonWebKitResults(container, pathToLog);
- }
- }
-
- function appendNonWebKitResults(container, url) {
- // Use a script tag to detect whether the URL 404s.
- // Need to use a script tag since the URL is cross-domain.
- var dummyNode = document.createElement('script');
- dummyNode.src = url;
-
- dummyNode.onload = function() {
- var item = document.createElement('iframe');
- item.src = dummyNode.src;
- item.className = 'non-webkit-results';
- item.style.height = currentState.resultsHeight + 'px';
- container.appendChild(item);
- }
- dummyNode.onerror = function() {
- container.parentNode.removeChild(childContainer);
- }
-
- container.appendChild(dummyNode);
- }
-
- function loadExpectationsLayoutTests(expectationsContainer) {
- // Map from file extension to container div for expectations of that type.
- var expectationsContainers = {};
-
- var test = expectationsContainer.getAttribute('test');
- var textSuffixWebKit = '?format=txt';
- addExpectationItem(expectationsContainers, expectationsContainer, null,
- test, TEST_URL_BASE_PATH, textSuffixWebKit);
-
- var testWithoutSuffix = test.substring(0, test.lastIndexOf('.'));
- var text = testWithoutSuffix + "-expected.txt";
- var checksum = testWithoutSuffix + "-expected.checksum"
- var png = testWithoutSuffix + "-expected.png";
-
- addExpectations(expectationsContainers, expectationsContainer,
- TEST_URL_BASE_PATH, getTracImageBaseURL(), '',
- text, checksum, png, textSuffixWebKit);
-
- var textSuffixChrome = '?revision=' + getLatestKnownRevision(true);
-
- var fallbacks = getAllFallbacks();
- for (var i = 0; i < fallbacks.length; i++) {
- var fallback = fallbacks[i];
- if (startsWith(fallback, 'platform')) {
- addExpectations(expectationsContainers, expectationsContainer,
- TEST_URL_BASE_PATH, getTracImageBaseURL(),
- fallback, text, checksum, png, textSuffixWebKit);
- } else {
- addExpectations(expectationsContainers, expectationsContainer,
- CHROME_TEST_BASE_URL, CHROME_TEST_BASE_URL, fallback, text,
- checksum, png, textSuffixChrome);
- }
- }
-
- var actualResultSuffixes = ['-actual.txt', '-actual.checksum',
- '-actual.png', '-stack.txt', '-diff.txt', '-wdiff.html', '-diff.png'];
-
- for (var builder in builders) {
- var actualResultsBase =
- 'http://build.chromium.org/buildbot/layout_test_results/' +
- builders[builder] + '/results/layout-test-results/';
- for (var i = 0; i < actualResultSuffixes.length; i++) {
- addExpectationItem(expectationsContainers, expectationsContainer, null,
- testWithoutSuffix + actualResultSuffixes[i], actualResultsBase, '',
- builder);
- }
- }
-
- // Add a clearing element so floated elements don't bleed out of their
- // containing block.
- var br = document.createElement('br');
- br.style.clear = 'both';
- expectationsContainer.appendChild(br);
- }
-
- var allFallbacks;
-
- /**
- * Returns the reverse sorted, deduped list of all platform fallback
- * directories.
- */
- function getAllFallbacks() {
- if (!allFallbacks) {
- var holder = {};
- for (var platform in fallbacksMap) {
- var fallbacks = fallbacksMap[platform];
- for (var i = 0; i < fallbacks.length; i++) {
- holder[fallbacks[i]] = 1;
- }
- }
-
- allFallbacks = [];
- for (var fallback in holder) {
- allFallbacks.push(fallback);
- }
- allFallbacks.sort(function(a, b) {
- if (a == b)
- return 0;
- return a < b;
- });
- }
- return allFallbacks;
- }
-
- /**
- * Appends the expectations for each test listed.
- */
- function appendExpectations() {
- var expectations = currentState.showExpectations ?
- document.getElementsByClassName('expectations') : [];
- // Loading expectations is *very* slow. Use a large timeout to avoid
- // totally hanging the renderer.
- performChunkedAction(expectations, function(chunk) {
- for (var i = 0, len = chunk.length; i < len; i++) {
- loadExpectations(chunk[i]);
- }
- }, hideLoadingUI, 10000);
- }
-
- function hideLoadingUI() {
- $('loading-ui').style.display = 'none';
- }
-
- function generatePageForIndividualTests(tests) {
- console.log('Number of tests: ' + tests.length);
- appendHTML(getHTMLForNavBar());
- performChunkedAction(tests, function(chunk) {
- appendHTML(getHTMLForIndividualTests(chunk));
- }, appendExpectations, 500);
- $('tests-input').value = currentState.tests;
- }
-
- function performChunkedAction(tests, handleChunk, onComplete, timeout,
- opt_index) {
- var index = opt_index || 0;
- setTimeout(function() {
- var chunk = Array.prototype.slice.call(tests, index * CHUNK_SIZE,
- (index + 1) * CHUNK_SIZE);
- if (chunk.length) {
- handleChunk(chunk);
- performChunkedAction(tests, handleChunk, onComplete, timeout, ++index);
- } else {
- onComplete();
- }
- // No need for a timeout on the first chunked action.
- }, index ? timeout : 0);
- }
-
- function getHTMLForIndividualTests(tests) {
- var testsHTML = [];
- for (var i = 0; i < tests.length; i++) {
- testsHTML.push('<h2 onclick="selectContents(this)">' + tests[i] +
- '</h2>' + getHTMLForIndividulTestOnAllBuilders(tests[i]));
- }
- return testsHTML.join('<hr>');
- }
-
- function getHTMLForCanaryLink(html, useWebKitCanary, useV8Canary) {
- var className = (currentState.useWebKitCanary == useWebKitCanary &&
- currentState.useV8Canary == useV8Canary) ? 'current-builder' : 'link';
- return '<span class=' + className +
- ' onclick="setState(\'useWebKitCanary\',' + useWebKitCanary +
- ',\'useV8Canary\',' + useV8Canary + ');window.location.reload()">' +
- html + '</span> | ';
- }
-
- function getHTMLForNavBar(opt_builderName) {
- var html = '<div id=builders>' + getHTMLForTestTypeSwitcher();
-
- for (var builder in builders) {
- var className = builder == opt_builderName ? 'current-builder' : 'link';
- html += '<span class=' + className + ' id="' + builder +
- '" onclick=\'setState("builder", "' + builder + '")\'>' +
- builder + '</span>';
- }
-
- html += '</div>' +
- '<form id=tests-form ' +
- 'onsubmit="setState(\'tests\', tests.value);return false;"><div>';
-
- if (isLayoutTestResults()) {
- html += getHTMLForCanaryLink('Main builders', false, false) +
- getHTMLForCanaryLink('WebKit.org Canary', true, false) +
- getHTMLForCanaryLink('V8 Canary', false, true) +
- '<span class=link ' +
- 'onclick="setState(\'tests\', \'allIncorrectExpectations\')">' +
- 'All incorrect expectations</span> | '+
- '<span class=link ' +
- 'onclick="setState(\'tests\', \'allSlowTests\')">All SLOW' +
- '</span> | <span class=link ' +
- 'onclick="setState(\'tests\', \'allNeedSlowTests\')">All need ' +
- 'SLOW</span> | ';
- }
- return html + 'Show tests on all platforms: </div><input name=tests ' +
- 'placeholder="Comma or space-separated list of tests or partial ' +
- 'paths to show test results across all builders, e.g., ' +
- 'foo/bar.html,foo/baz,domstorage" ' +
- 'id=tests-input></form>' +
- '<form id=max-results-form ' +
- 'onsubmit="setState(\'maxResults\', maxResults.value);return false;"' +
- '><span>Number of results to show (max=750): </span>' +
- '<input name=maxResults id=max-results-input></form> | ' +
- '<span class=link onclick="showLegend()">Show legend [type ?]</span>';
- }
-
- function getLinkHTMLToToggleState(key, linkText) {
- var isTrue = currentState[key];
- return '<span class=link onclick="setState(\'' + key + '\', ' + !isTrue +
- ')">' + (isTrue ? 'Hide' : 'Show') + ' ' + linkText + '</span>';
- }
-
- function generatePageForBuilder(builderName) {
- processTestRunsForBuilder(builderName);
-
- var tableRowsHTML = '';
- var results = perBuilderFailures[builderName];
- sortTests(results, currentState.sortColumn, currentState.sortOrder);
- for (var i = 0; i < results.length; i++) {
- tableRowsHTML += getHTMLForSingleTestRow(results[i]);
- }
-
- var testsHTML = tableRowsHTML ? getHTMLForTestTable(tableRowsHTML) :
- '<div>No tests. Try showing tests with correct expectations.</div>';
-
- var html = getHTMLForNavBar(builderName);
-
- if (isLayoutTestResults()) {
- html += getHTMLForTestsWithExpectationsButNoFailures(builderName) +
- '<h2>Failing tests</h2>' +
- getLinkHTMLToToggleState('showWontFixSkip', 'WONTFIX/SKIP') +
- ' | ' +
- getLinkHTMLToToggleState('showCorrectExpectations',
- 'tests with correct expectations') + ' | ' +
- getLinkHTMLToToggleState('showWrongExpectations',
- 'tests with wrong expectations') + ' | ' +
- getLinkHTMLToToggleState('showFlaky', 'flaky') + ' | ' +
- getLinkHTMLToToggleState('showSlow', 'slow');
- }
-
- html += ' | All columns are sortable. | ' +
- 'Flakiness reader order is newer --> older runs.<br>' +
- testsHTML;
-
- appendHTML(html);
-
- var ths = document.getElementsByTagName('th');
- for (var i = 0; i < ths.length; i++) {
- ths[i].addEventListener('click', changeSort, false);
- ths[i].className = "sortable";
- }
-
- appendNumFlakyTests(builderName);
- hideLoadingUI();
- }
-
- function appendNumFlakyTests(builderName) {
- var link = $(builderName);
- if (link) {
- link.innerHTML =
- builderName + ' [' + numFlakyTestsPerBuilder[builderName] + ']';
- }
- }
-
- function createTableHeadersArray(firstColumnHeader) {
- var baseHeaders = isLayoutTestResults() ? LAYOUT_TEST_TABLE_HEADERS :
- BASE_TABLE_HEADERS;
- tableHeaders = [firstColumnHeader].concat(baseHeaders);
- }
-
- /**
- * Clears the processed test state for perBuilderFailures.
- * TODO(ojan): This really should probably clear all the state we've
- * generated, but that's kind of a pain given the many global objects state is
- * stored in. There should probably be one global generatedState
- * object that all the generated state lives off of.
- */
- function clearProcessedTestState() {
- for (var builder in builders) {
- delete perBuilderFailures[builder];
- delete perBuilderPlatformAndBuildType[builder];
- delete perBuilderWithExpectationsButNoFailures[builder];
- delete perBuilderSkippedPaths[builder];
- }
-
- for (var key in testToResultsMap) {
- delete testToResultsMap[key]
- }
- }
-
- var VALID_KEYS_FOR_CROSS_BUILDER_VIEW = {
- tests: 1,
- maxResults: 1,
- showExpectations: 1,
- showLargeExpectations: 1,
- legacyExpectationsSemantics: 1,
- resultsHeight: 1
- };
-
- function isInvalidKeyForCrossBuilderView(key) {
- return !(key in VALID_KEYS_FOR_CROSS_BUILDER_VIEW) &&
- !(key in defaultCrossDashboardStateValues);
- }
-
- /**
- * Sets the page state and regenerates the page. Takes varargs of key, value
- * pairs.
- */
- function setState(var_args) {
- for (var i = 0; i < arguments.length; i += 2) {
- var key = arguments[i];
- if (key == 'tests') {
- // Entering cross-builder view, only keep valid keys for that view.
- for (var currentKey in currentState) {
- if (isInvalidKeyForCrossBuilderView(currentKey)) {
- delete currentState[currentKey];
- }
- }
- } else if (isInvalidKeyForCrossBuilderView(key)) {
- delete currentState.tests;
- }
-
- if (key == 'maxResults' || key == 'expectationsUpdate' ||
- key == 'tests' && !currentState.maxResults) {
- // Processing the test results JSON makes assumptions about the number
- // of results to show. This makes changing the number of maxResults slow
- // but is considerably easier than refactoring all the other code.
- clearProcessedTestState();
- }
- }
-
- // Set all the custom state for this dashboard before calling
- // setQueryParameter since setQueryParameter updates the location bar.
- setQueryParameter.apply(null, arguments);
- }
-
- function hideLegend() {
- var legend = $('legend');
- if (legend)
- legend.parentNode.removeChild(legend);
- }
-
- var fallbacksMap = {};
- fallbacksMap['WIN-VISTA'] = ['chromium-win-vista', 'chromium-win',
- 'platform/win', 'platform/mac'];
- fallbacksMap['WIN-XP'] = ['chromium-win-xp'].concat(
- fallbacksMap['WIN-VISTA']);
- // Should mac look at the tiger results?
- fallbacksMap['MAC'] = ['chromium-mac', 'platform/mac',
- 'platform/mac-snowleopard', 'platform/mac-leopard', 'platform/mac-tiger'];
- fallbacksMap['LINUX'] = ['chromium-linux', 'chromium-win', 'platform/win',
- 'platform/mac'];
-
- function htmlForFallbackHelp(fallbacks) {
- return '<ol class=fallback-list><li>' + fallbacks.join('</li><li>') +
- '</li></ol>';
- }
-
- function showLegend() {
- var legend = $('legend');
- if (!legend) {
- legend = document.createElement('div');
- legend.id = 'legend';
- document.body.appendChild(legend);
- }
-
- var html = '<div id=legend-toggle onclick="hideLegend()">Hide ' +
- 'legend [type esc]</div><div>Number of flaky tests listed next to ' +
- 'each builder</div><div id=legend-contents>';
- for (var expectation in getExpectationsMap()) {
- html += '<div class=' + expectation + '>' +
- getExpectationsMap()[expectation] + '</div>';
- }
-
- html += '<div class=merge>WEBKIT MERGE</div>';
- if (isLayoutTestResults()) {
- html += '<div class=wrong-expectations>WRONG EXPECTATIONS</div>' +
- '</div><br style="clear:both">' +
- '</div><h3>Test expectatons fallback order.</h3>';
-
- for (var platform in fallbacksMap) {
- html += '<div class=fallback-header>' + platform + '</div>' +
- htmlForFallbackHelp(fallbacksMap[platform]);
- }
- html += '<div>TIMES:</div>' +
- getHtmlForSlowTimes(MIN_SECONDS_FOR_SLOW_TEST) +
- '<div>DEBUG TIMES:</div>' +
- getHtmlForSlowTimes(MIN_SECONDS_FOR_SLOW_TEST_DEBUG);
- }
-
- legend.innerHTML = html;
- }
-
- function getHtmlForSlowTimes(minTime) {
- return '<ul><li>&lt;1 second == !SLOW</li><li>&gt;1 second && &lt;' +
- minTime + ' seconds == SLOW || !SLOW is fine</li><li>&gt;' +
- minTime + ' seconds == SLOW</li></ul>';
- }
-
- document.addEventListener('keydown', function(e) {
- if (e.keyIdentifier == 'U+003F' || e.keyIdentifier == 'U+00BF') {
- // WebKit MAC retursn 3F. WebKit WIN returns BF. This is a bug!
- // ? key
- showLegend();
- } else if (e.keyIdentifier == 'U+001B') {
- // escape key
- hideLegend();
- hidePopup();
- }
- }, false);
-
-
-
- </script>
-</head>
-
-<body></body>
-</html>