Index: webkit/tools/layout_tests/flakiness_dashboard.html |
=================================================================== |
--- webkit/tools/layout_tests/flakiness_dashboard.html (revision 28030) |
+++ webkit/tools/layout_tests/flakiness_dashboard.html (working copy) |
@@ -81,17 +81,15 @@ |
position: fixed; |
top: 5px; |
right: 5px; |
- width: 130px; |
+ width: 200px; |
+ padding: 2px; |
border: 2px solid grey; |
background-color: white; |
} |
#legend-contents * { |
- margin: 3px; |
+ margin: 3px 0; |
padding: 0 2px; |
} |
- body > div > :not(#legend) { |
- margin-right: 145px; |
- } |
#builders * { |
margin: 0 5px; |
display: inline-block; |
@@ -128,7 +126,7 @@ |
.merge { |
background-color: grey; |
} |
- :not(#legend-contents) > .merge { |
+ table .merge { |
width: 1px; |
} |
.separator { |
@@ -179,6 +177,49 @@ |
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; |
+ } |
+ .fallback-list { |
+ margin-top: 0; |
+ } |
+ .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; |
+ } |
+ .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; |
+ } |
</style> |
<script src="dashboards/dashboard_base.js"></script> |
@@ -206,6 +247,9 @@ |
var ALL = 'ALL'; |
var FORWARD = 'forward'; |
var BACKWARD = 'backward'; |
+ var LAYOUT_TESTS_PREFIX = 'LayoutTests/'; |
+ 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/'; |
var BUILDERS_BASE_PATH = |
@@ -251,7 +295,6 @@ |
} |
$('max-results-input').value = currentState.maxResults; |
- updateLegendDisplay(); |
for (var builder in builders) { |
processTestResultsForBuilderAsync(builder); |
@@ -305,11 +348,12 @@ |
return true; |
- case 'showWontFix': |
case 'showCorrectExpectations': |
+ case 'showExpectations': |
case 'showFlaky': |
- case 'showLegend': |
+ case 'showLargeExpectations': |
case 'showSkipped': |
+ case 'showWontFix': |
currentState[key] = value == 'true'; |
return true; |
@@ -322,12 +366,13 @@ |
defaultStateValues = { |
sortOrder: BACKWARD, |
sortColumn: 'flakiness', |
- showWontFix: false, |
showCorrectExpectations: false, |
- showLegend: true, |
+ showExpectations: false, |
showFlaky: true, |
+ showLargeExpectations: false, |
+ showWontFix: false, |
showSkipped: false, |
- maxResults: 200, |
+ maxResults: 200 |
}; |
////////////////////////////////////////////////////////////////////////////// |
@@ -894,6 +939,7 @@ |
function showPopupForTest(e, test) { |
showPopup(e, getHTMLForIndividulTestOnAllBuilders(test)); |
+ appendExpectations(); |
} |
function getHtmlForTestResults(test, builder) { |
@@ -1186,9 +1232,10 @@ |
processTestRunsForBuilder(builder); |
var testResults = testToResultsMap[test]; |
+ var html = ''; |
if (testResults && testResults.length) { |
var tracURL = TEST_URL_BASE_PATH + test |
- var html = getLinkHTMLToOpenWindow(tracURL, tracURL) + |
+ html += getLinkHTMLToOpenWindow(tracURL, tracURL) + |
'<div><b>If a builder is not listed, that means the builder does ' + |
'run that test or all runs of the test passed.</b></div>'; |
@@ -1196,28 +1243,321 @@ |
html += getHTMLForSingleTestRow(testResults[j].results, |
testResults[j].builder, true); |
} |
- return getHTMLForTestTable(html); |
+ html = getHTMLForTestTable(html); |
} else { |
- var html = ''; |
if (expectationsByTest[test]) { |
for (var i = 0; i < expectationsByTest[test].length; i++) { |
html += '<div>' + expectationsByTest[test][i].modifiers + ' | ' + |
expectationsByTest[test][i].expectations + '</div>'; |
} |
} |
- return html + '<div class="not-found">Test not found. Either it does ' + |
+ html += '<div class="not-found">Test not found. Either it does ' + |
'not exist, is skipped or passes on all platforms.</div>'; |
} |
+ return html + '<div class=expectations test=' + test + '><div>' + |
+ getLinkHTMLToToggleState('showExpectations', 'expectations') + ' | ' + |
+ getLinkHTMLToToggleState('showLargeExpectations', 'large thumbnails') + |
+ '</div></div>'; |
} |
+ 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. "LayoutTests/" for non-platform |
+ * specific expectations. |
+ * @param {string} path Relative path to the expectation. |
+ * @param {string} base Base path for the expectation URL. |
+ * @param {string} opt_suffix Suffix to place at the end of the path. |
+ */ |
+ function addExpectationItem(expectationsContainers, parentContainer, platform, |
+ path, base, opt_suffix) { |
+ 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 suffix = opt_suffix || ''; |
+ var platformPart = platform ? ensureTrailingSlash(platform) : ''; |
+ dummyNode.src = base + platformPart + path + suffix; |
+ |
+ var childContainer = document.createElement('span'); |
+ childContainer.className = 'unloaded'; |
+ |
+ dummyNode.onload = function() { |
+ childContainer.appendChild(getExpectationsTitle(platform, path)); |
+ 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 == LAYOUT_TESTS_PREFIX) { |
+ 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/'; |
+ } |
+ 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) { |
+ var header = document.createElement('h3'); |
+ header.className = 'expectations-title'; |
+ |
+ var innerHTML; |
+ if (platform == LAYOUT_TESTS_PREFIX) { |
+ 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.style.clear = 'both'; |
+ header.platform = platform; |
+ return header; |
+ } |
+ |
+ function loadExpectations(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 isUpstreamTest = startsWith(test, LAYOUT_TESTS_PREFIX); |
+ if (isUpstreamTest) { |
+ var testWithoutPrefix = testWithoutSuffix.substring( |
+ LAYOUT_TESTS_PREFIX.length); |
+ var textWithoutPrefix = testWithoutPrefix + "-expected.txt"; |
+ var checksumWithoutPrefix = testWithoutPrefix + "-expected.checksum" |
+ var pngWithoutPrefix = testWithoutPrefix + "-expected.png"; |
+ |
+ addExpectations(expectationsContainers, expectationsContainer, |
+ TEST_URL_BASE_PATH, getTracImageBaseURL(), LAYOUT_TESTS_PREFIX, |
+ textWithoutPrefix, checksumWithoutPrefix, pngWithoutPrefix, |
+ textSuffixWebKit); |
+ } |
+ |
+ var text = testWithoutSuffix + "-expected.txt"; |
+ var checksum = testWithoutSuffix + "-expected.checksum" |
+ var png = testWithoutSuffix + "-expected.png"; |
+ |
+ var textSuffixChrome = '?revision=' + getLatestKnownRevision(true); |
+ |
+ var fallbacks = getAllFallbacks(); |
+ for (var i = 0; i < fallbacks.length; i++) { |
+ var fallback = fallbacks[i]; |
+ if (startsWith(fallback, 'platform')) { |
+ if (isUpstreamTest) { |
+ addExpectations(expectationsContainers, expectationsContainer, |
+ TEST_URL_BASE_PATH + LAYOUT_TESTS_PREFIX, |
+ getTracImageBaseURL() + LAYOUT_TESTS_PREFIX, |
+ fallback, textWithoutPrefix, |
+ checksumWithoutPrefix, pngWithoutPrefix, textSuffixWebKit); |
+ } |
+ } else { |
+ addExpectations(expectationsContainers, expectationsContainer, |
+ CHROME_TEST_BASE_URL, CHROME_TEST_BASE_URL, fallback, text, |
+ checksum, png, textSuffixChrome); |
+ } |
+ } |
+ |
+ // 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() { |
+ if (currentState.showExpectations) { |
+ var expectations = document.getElementsByClassName('expectations'); |
+ for (var i = 0, len = expectations.length; i < len; i++) { |
+ loadExpectations(expectations[i]); |
+ } |
+ } |
+ } |
+ |
function generatePageForIndividualTests(tests) { |
- var html = getHTMLForNavBar(); |
+ var testsHTML = []; |
for (var i = 0; i < tests.length; i++) { |
- html += '<h2>' + tests[i] + '</h2>' + |
- getHTMLForIndividulTestOnAllBuilders(tests[i]); |
+ testsHTML.push('<h2>' + tests[i] + '</h2>' + |
+ getHTMLForIndividulTestOnAllBuilders(tests[i])); |
} |
- setFullPageHTML(html); |
+ setFullPageHTML(getHTMLForNavBar() + testsHTML.join('<hr>')); |
+ appendExpectations(); |
$('tests-input').value = currentState.tests; |
} |
@@ -1229,7 +1569,7 @@ |
' onclick=\'setState("builder", "' + builder + '")\'>' + |
builder + '</span>'; |
} |
- html += '</div>' + |
+ return html + '</div>' + |
'<form id=tests-form ' + |
'onsubmit="setState(\'tests\', tests.value);return false;">' + |
'<div>Show tests on all platforms: </div><input name=tests ' + |
@@ -1240,17 +1580,9 @@ |
'<form id=max-results-form ' + |
'onsubmit="setState(\'maxResults\', maxResults.value);return false;"' + |
'><span>Number of results to show (max=500): </span>' + |
- '<input name=maxResults id=max-results-input></form>' + |
- '<div id="loading-ui">LOADING...</div><div id=legend>' + |
- '<div id=legend-toggle>' + getLinkHTMLToToggleLegendDisplay() + |
- '</div><div id=legend-contents>'; |
- |
- for (var expectation in EXPECTATIONS_MAP) { |
- html += '<div class=' + expectation + '>' + |
- EXPECTATIONS_MAP[expectation] + '</div>'; |
- } |
- return html + '<div class=wrong-expectations>WRONG EXPECTATIONS</div>' + |
- '<div class=merge>WEBKIT MERGE</div></div></div>'; |
+ '<input name=maxResults id=max-results-input></form> | ' + |
+ '<b>Type ? for legend and expectations fallback order</b>' + |
+ '<div id="loading-ui">LOADING...</div>'; |
} |
function getLinkHTMLToToggleState(key, linkText) { |
@@ -1279,8 +1611,8 @@ |
getLinkHTMLToToggleState('showCorrectExpectations', |
'tests with correct expectations') + ' | ' + |
getLinkHTMLToToggleState('showFlaky', 'flaky tests') + ' | ' + |
- '<b>All columns are sortable. | ' + |
- 'Flakiness reader order is newer --> older runs.</b></div>' + |
+ 'All columns are sortable. | ' + |
+ 'Flakiness reader order is newer --> older runs.</div>' + |
testsHTML; |
setFullPageHTML(html); |
@@ -1292,14 +1624,6 @@ |
} |
} |
- function getLinkHTMLToToggleLegendDisplay() { |
- return getLinkHTMLToToggleState('showLegend', 'Legend'); |
- } |
- |
- function updateLegendDisplay() { |
- $('legend-contents').style.display = currentState.showLegend ? '' : 'none'; |
- } |
- |
function createTableHeadersArray(firstColumnHeader) { |
tableHeaders = [firstColumnHeader].concat(BASE_TABLE_HEADERS); |
} |
@@ -1324,6 +1648,13 @@ |
} |
} |
+ var VALID_KEYS_FOR_INDIVIDUAL_TESTS = { |
+ tests: 1, |
+ maxResults: 1, |
+ showExpectations: 1, |
+ showLargeExpectations: 1 |
+ }; |
+ |
/** |
* Sets the page state and regenerates the page. Takes varargs of key, value |
* pairs. |
@@ -1333,7 +1664,7 @@ |
for (var i = 0; i < arguments.length; i += 2) { |
var key = arguments[i]; |
- if (key != 'tests' && key != 'maxResults') { |
+ if (!(key in VALID_KEYS_FOR_INDIVIDUAL_TESTS)) { |
delete currentState.tests; |
} |
@@ -1343,14 +1674,6 @@ |
// but is considerably easier than refactoring all the other code. |
clearProcessedTestState(); |
} |
- |
- if (key == 'showLegend') { |
- // No need to regenerate the page if only the legend's display is being |
- // updated. |
- shouldRegeneratePage = keys.length == 1; |
- updateLegendDisplay(); |
- $('legend-toggle').innerHTML = getLinkHTMLToToggleLegendDisplay(); |
- } |
} |
// Set all the custom state for this dashboard before calling |
@@ -1361,7 +1684,64 @@ |
handleLocationChange(); |
} |
+ 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 innerHTML = '<div id=legend-toggle onclick="hideLegend()">Hide ' + |
+ 'legend (or hit esc to close)</div><div id=legend-contents>'; |
+ for (var expectation in EXPECTATIONS_MAP) { |
+ innerHTML += '<div class=' + expectation + '>' + |
+ EXPECTATIONS_MAP[expectation] + '</div>'; |
+ } |
+ innerHTML += '<div class=wrong-expectations>WRONG EXPECTATIONS</div>' + |
+ '<div class=merge>WEBKIT MERGE</div></div>' +'</div>' + |
+ '<h3>Test expectatons fallback order.</h3>'; |
+ |
+ for (var platform in fallbacksMap) { |
+ innerHTML += '<div class=fallback-header>' + platform + '</div>' + |
+ htmlForFallbackHelp(fallbacksMap[platform]); |
+ } |
+ legend.innerHTML = innerHTML; |
+ } |
+ |
+ document.addEventListener('keydown', function(e) { |
+ if (e.keyCode == 191 && e.shiftKey) { |
+ // ? key |
arv (Not doing code reviews)
2009/10/05 23:59:29
This will not work on different keyboard layouts.
|
+ showLegend(); |
+ } else if (e.keyCode == 27) { |
+ // escape key |
+ hideLegend(); |
+ hidePopup(); |
+ } |
+ }, false); |
+ |
</script> |
</head> |