Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(5)

Unified Diff: LayoutTests/fast/harness/dashboard.html

Issue 339623002: Added support for versioning of layout test results of run-webkit-tests runs (Closed) Base URL: https://chromium.googlesource.com/chromium/blink.git@master
Patch Set: Addressing comments Created 6 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: LayoutTests/fast/harness/dashboard.html
diff --git a/LayoutTests/fast/harness/dashboard.html b/LayoutTests/fast/harness/dashboard.html
new file mode 100644
index 0000000000000000000000000000000000000000..bbba5cecfcf1e0a41998c89875ef504bfe6df49d
--- /dev/null
+++ b/LayoutTests/fast/harness/dashboard.html
@@ -0,0 +1,808 @@
+<!DOCTYPE html>
+<style>
+html {
+ height: 100%;
+}
+body {
+ margin: 0;
+ font-family: Helvetica, sans-serif;
+ font-size: 11pt;
+ display: -webkit-flex;
+ -webkit-flex-direction: column;
+ height: 100%;
+}
+
+body > * {
+ margin-left: 4px;
+ margin-top: 4px;
+}
+
+h1 {
+ font-size: 14pt;
+ margin-top: 1.5em;
+}
+
+p {
+ margin-bottom: 0.3em;
+}
+
+tr {
+ background-color: white;
+}
+
+tr:hover {
+ background-color: #999999;
+}
+
+tr:not(.results-row) td {
+ white-space: nowrap;
+}
+
+tr:not(.results-row) td:first-of-type {
+ white-space: normal;
+}
+
+td:not(:first-of-type) {
+ text-transform: lowercase;
+}
+
+td {
+ padding: 1px 4px;
+}
+
+th:empty, td:empty {
+ padding: 0;
+}
+
+th {
+ -webkit-user-select: none;
+ -moz-user-select: none;
+}
+
+.content-container {
+ -webkit-flex: 1;
+ min-height: -webkit-min-content;
+ overflow: auto;
+}
+
+.note {
+ color: gray;
+ font-size: smaller;
+}
+
+.results-row {
+ background-color: white;
+}
+
+.results-row iframe, .results-row img {
+ width: 800px;
+ height: 600px;
+}
+
+.results-row[data-expanded="false"] {
+ display: none;
+}
+
+#toolbar {
+ position: fixed;
+ padding: 4px;
+ top: 2px;
+ right: 2px;
+ text-align: right;
+ background-color: rgba(255, 255, 255, 0.85);
+ border: 1px solid silver;
+ border-radius: 4px;
+}
+
+.expand-button {
+ background-color: white;
+ width: 11px;
+ height: 12px;
+ border: 1px solid gray;
+ display: inline-block;
+ margin: 0 3px 0 0;
+ position: relative;
+ cursor: default;
+}
+
+.current {
+ color: red;
+}
+
+.current .expand-button {
+ border-color: red;
+}
+
+.expand-button-text {
+ position: absolute;
+ top: -0.3em;
+ left: 1px;
+}
+
+tbody .flag {
+ display: none;
+}
+
+tbody.flagged .flag {
+ display: inline;
+}
+
+.stopped-running-early-message {
+ border: 3px solid #d00;
+ font-weight: bold;
+ display: inline-block;
+ padding: 3px;
+}
+
+.result-container {
+ display: inline-block;
+ border: 1px solid gray;
+ margin: 4px;
+}
+
+.result-container iframe, .result-container img {
+ border: 0;
+ vertical-align: top;
+}
+
+.label {
+ padding-left: 3px;
+ font-weight: bold;
+ font-size: small;
+ background-color: silver;
+}
+
+.pixel-zoom-container {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 96%;
+ margin: 10px;
+ padding: 10px;
+ display: -webkit-box;
+ display: -moz-box;
+ pointer-events: none;
+ background-color: silver;
+ border-radius: 20px;
+ border: 1px solid gray;
+ box-shadow: 0 0 5px rgba(0, 0, 0, 0.75);
+}
+
+.pixel-zoom-container > * {
+ -webkit-box-flex: 1;
+ -moz-box-flex: 1;
+ border: 1px solid black;
+ margin: 4px;
+ overflow: hidden;
+ background-color: white;
+}
+
+.pixel-zoom-container .scaled-image-container {
+ position: relative;
+ overflow: hidden;
+ width: 100%;
+ height: 400px;
+}
+
+.scaled-image-container > img {
+ position: absolute;
+ top: 0;
+ left: 0;
+ image-rendering: -webkit-optimize-contrast;
+}
+.test-pass {
+ background-color:rgb(0,255,0);
+}
+.test-image-fail {
+ background-color:rgb(0,128,255);
+}
+.test-text-fail {
+ background-color:rgb(250,100,100);
+}
+.test-image-text-fail {
+ background-color:rgb(161,128,250);
+}
+.test-flaky {
+ background-color:rgb(255,0,255);
+}
+.test-missing {
+ background-color:rgb(255,255,255);
+}
+.test-fail {
+ background-color:rgb(255,0,0);
+}
+
+#flagged-tests {
+ margin: 1px;
+ padding: 5px;
+ height: 100px;
+}
+
+#flagged-test-container h2 {
+ display: inline-block;
+ margin: 0 10px 0 0;
+}
+</style>
+<style id="unexpected-pass-style"></style>
+<style id="flaky-failures-style"></style>
+<style id="stderr-style"></style>
+<style id="unexpected-style"></style>
+
+<script>
+var g_state;
+function globalState()
+{
+ if (!g_state) {
+ g_state = {
+ crashTests: [],
+ leakTests: [],
+ flakyPassTests: [],
+ hasHttpTests: false,
+ hasImageFailures: false,
+ hasTextFailures: false,
+ missingResults: [],
+ results: {},
+ failingTests: [],
+ testsWithStderr: [],
+ timeoutTests: [],
+ unexpectedPassTests: []
+ }
+ }
+ return g_state;
+}
+
+function ADD_RESULTS(input)
+{
+ globalState().results = input;
+}
+</script>
+
+<script src="archived_results.json"></script>
+
+<script>
+
+function matchesSelector(node, selector)
+{
+ if (node.webkitMatchesSelector)
+ return node.webkitMatchesSelector(selector);
+
+ if (node.mozMatchesSelector)
+ return node.mozMatchesSelector(selector);
+}
+
+function parentOfType(node, selector)
+{
+ while (node = node.parentNode) {
+ if (matchesSelector(node, selector))
+ return node;
+ }
+ return null;
+}
+
+function remove(node)
+{
+ node.parentNode.removeChild(node);
+}
+
+function forEach(nodeList, handler)
+{
+ Array.prototype.forEach.call(nodeList, handler);
+}
+
+function resultIframe(src)
+{
+ // FIXME: use audio tags for AUDIO tests?
+ var layoutTestsIndex = src.indexOf('LayoutTests');
+ var name;
+ if (layoutTestsIndex != -1) {
+ var hasTrac = src.indexOf('trac.webkit.org') != -1;
+ var prefix = hasTrac ? 'trac.webkit.org/.../' : '';
+ name = prefix + src.substring(layoutTestsIndex + 'LayoutTests/'.length);
+ } else {
+ var lastDashIndex = src.lastIndexOf('-pretty');
+ if (lastDashIndex == -1)
+ lastDashIndex = src.lastIndexOf('-');
+ name = src.substring(lastDashIndex + 1);
+ }
+
+ var tagName = (src.lastIndexOf('.png') == -1) ? 'iframe' : 'img';
+
+ if (tagName != 'img')
+ src += '?format=txt';
+ return '<div class=result-container><div class=label>' + name + '</div><' + tagName + ' src="' + src + '"></' + tagName + '></div>';
+}
+
+function togglingImage(prefix)
+{
+ return '<div class=result-container><div class="label imageText"></div><img class=animatedImage data-prefix="' +
+ prefix + '"></img></div>';
+}
+
+function appendHTML(node, html)
+{
+ if (node.insertAdjacentHTML)
+ node.insertAdjacentHTML('beforeEnd', html);
+ else
+ node.innerHTML += html;
+}
+
+function shouldUseTracLinks()
+{
+ return !globalState().results.layout_tests_dir || !location.toString().indexOf('file://') == 0;
+}
+
+function testLinkTarget(test)
+{
+ var target;
+ if (shouldUseTracLinks()) {
+ var revision = globalState().results.revision;
+ target = 'http://src.chromium.org/viewvc/blink/trunk/LayoutTests/' + test;
+ if (revision)
+ target += '?pathrev=' + revision;
+ target += '#l1';
+ } else
+ target = globalState().results.layout_tests_dir + '/' + test;
+ return target;
+}
+
+function testLink(test)
+{
+ var target = testLinkTarget(test);
+ return '<a class=test-link href="' + target + '">' + test + '</a>';
+}
+
+function resultLink(testPrefix, suffix, contents)
+{
+ return '<a class=result-link href="' + testPrefix + suffix + '" data-prefix="' + testPrefix + '">' + contents + '</a> ';
+}
+
+function processGlobalStateFor(testObject)
+{
+ var test = testObject.name;
+ if (testObject.has_stderr)
+ globalState().testsWithStderr.push(testObject);
+
+ globalState().hasHttpTests = globalState().hasHttpTests || test.indexOf('http/') == 0;
+
+ var actual = testObject.actual;
+ var expected = testObject.expected || 'PASS';
+
+ if (actual == 'MISSING') {
+ // FIXME: make sure that new-run-webkit-tests spits out an -actual.txt file for
+ // tests with MISSING results.
+ globalState().missingResults.push(testObject);
+ return;
+ }
+
+ var actualTokens = actual.split(' ');
+ var passedWithImageOnlyFailureInRetry = actualTokens[0] == 'TEXT' && actualTokens[1] == 'IMAGE';
+ if (actualTokens[1] && actual.indexOf('PASS') != -1 || (!globalState().results.pixel_tests_enabled && passedWithImageOnlyFailureInRetry)) {
+ globalState().flakyPassTests.push(testObject);
+ return;
+ }
+
+ if (actual == 'PASS' && expected != 'PASS') {
+ if (expected != 'IMAGE' || (globalState().results.pixel_tests_enabled || testObject.reftest_type)) {
+ globalState().unexpectedPassTests.push(testObject);
+ }
+ return;
+ }
+
+ if (actual == 'CRASH') {
+ globalState().crashTests.push(testObject);
+ return;
+ }
+
+ if (actual == 'LEAK') {
+ globalState().leakTests.push(testObject);
+ return;
+ }
+
+ if (actual == 'TIMEOUT') {
+ globalState().timeoutTests.push(testObject);
+ return;
+ }
+
+ globalState().failingTests.push(testObject);
+}
+
+function getResultLink(index)
+{
+ if(index < globalState().results.result_links.length)
+ return globalState().results.result_links[index];
+ else
+ return '';
+}
+
+function processArchivedResults(archivedResults)
+{
+ var result = '';
+ var i = 1;
+ for (var i = 0; i < archivedResults.length; i++) {
+ switch(archivedResults[i]) {
+ case "PASS":
+ result += '<td class=test-pass>';
+ break;
+ case "IMAGE":
+ result += '<td class=test-image-fail>';
+ break;
+ case "TEXT":
+ result += '<td class=test-text-fail>';
+ break;
+ case "IMAGE+TEXT":
+ result += '<td class=test-image-text-fail>';
+ break;
+ case "NOTFOUND":
+ result += '<td class=test-missing>';
+ break;
+ case "SKIP":
+ result += '<td class=test-missing>';
+ break;
+ default:
+ result += '<td class=test-fail>';
+ }
+ result += '<a href="' + getResultLink(i) + '">' + (i+1).toString() + '</a>' + '</td>';
+ }
+
+ return result;
+}
+
+function tableRow(testObject)
+{
+ var row = '<tbody class="' + (testObject.is_unexpected ? '' : 'expected') + '"';
+ if (testObject.reftest_type && testObject.reftest_type.indexOf('!=') != -1)
+ row += ' mismatchreftest=true';
+ row += '><tr>';
+
+ row += '<td>' + testLink(testObject.name) + '</td>';
+
+ var archivedResults = testObject.archived_results;
+ row += processArchivedResults(archivedResults);
+ row += '</tr></tbody>';
+ return row;
+}
+
+function forEachTest(handler, opt_tree, opt_prefix)
+{
+ var tree = opt_tree || globalState().results.tests;
+ var prefix = opt_prefix || '';
+
+ for (var key in tree) {
+ var newPrefix = prefix ? (prefix + '/' + key) : key;
+ if ('actual' in tree[key]) {
+ var testObject = tree[key];
+ testObject.name = newPrefix;
+ handler(testObject);
+ } else
+ forEachTest(handler, tree[key], newPrefix);
+ }
+}
+
+function hasUnexpected(tests)
+{
+ return tests.some(function (test) { return test.is_unexpected; });
+}
+
+function updateTestlistCounts()
+{
+ forEach(document.querySelectorAll('.test-list-count'), function(count) {
+ var container = parentOfType(count, 'div');
+ var testContainers;
+ if (onlyShowUnexpectedFailures())
+ testContainers = container.querySelectorAll('tbody:not(.expected)');
+ else
+ testContainers = container.querySelectorAll('tbody');
+
+ count.textContent = testContainers.length;
+
+ })
+}
+
+function testListHeaderHtml(header)
+{
+ return '<h1>' + header + ' (<span class=test-list-count></span>): </h1>';
+}
+
+function testList(tests, header, tableId)
+{
+ tests.sort();
+
+ var html = '<div' + ((!hasUnexpected(tests) && tableId != 'stderr-table') ? ' class=expected' : '') + ' id=' + tableId + '>' +
+ testListHeaderHtml(header) + '<table>';
+
+ // FIXME: Include this for all testLists.
+ if (tableId == 'passes-table')
+ html += '<thead><th>test</th><th>expected</th></thead>';
+
+ for (var i = 0; i < tests.length; i++) {
+ var testObject = tests[i];
+ var test = testObject.name;
+ html += '<tbody class="' + ((testObject.is_unexpected || tableId == 'stderr-table') ? '' : 'expected') + '"><tr><td>' +
+ testLink(test) +
+ '</td>';
+
+ html += processArchivedResults(testObject.archived_results)
+ html += '</tr></tbody>';
+ }
+ html += '</table></div>';
+ return html;
+}
+
+function toArray(nodeList)
+{
+ return Array.prototype.slice.call(nodeList);
+}
+
+function trim(string)
+{
+ return string.replace(/^[\s\xa0]+|[\s\xa0]+$/g, '');
+}
+
+// Just a namespace for code management.
+var TableSorter = {};
+
+TableSorter._forwardArrow = '<svg style="width:10px;height:10px"><polygon points="0,0 10,0 5,10" style="fill:#ccc"></svg>';
+
+TableSorter._backwardArrow = '<svg style="width:10px;height:10px"><polygon points="0,10 10,10 5,0" style="fill:#ccc"></svg>';
+
+TableSorter._sortedContents = function(header, arrow)
+{
+ return arrow + ' ' + trim(header.textContent) + ' ' + arrow;
+}
+
+TableSorter._updateHeaderClassNames = function(newHeader)
+{
+ var sortHeader = document.querySelector('.sortHeader');
+ if (sortHeader) {
+ if (sortHeader == newHeader) {
+ var isAlreadyReversed = sortHeader.classList.contains('reversed');
+ if (isAlreadyReversed)
+ sortHeader.classList.remove('reversed');
+ else
+ sortHeader.classList.add('reversed');
+ } else {
+ sortHeader.textContent = sortHeader.textContent;
+ sortHeader.classList.remove('sortHeader');
+ sortHeader.classList.remove('reversed');
+ }
+ }
+
+ newHeader.classList.add('sortHeader');
+}
+
+TableSorter._textContent = function(tbodyRow, column)
+{
+ return tbodyRow.querySelectorAll('td')[column].textContent;
+}
+
+TableSorter._sortRows = function(newHeader, reversed)
+{
+ var testsTable = document.getElementById('results-table');
+ var headers = toArray(testsTable.querySelectorAll('th'));
+ var sortColumn = headers.indexOf(newHeader);
+
+ var rows = toArray(testsTable.querySelectorAll('tbody'));
+
+ rows.sort(function(a, b) {
+ // Only need to support lexicographic sort for now.
+ var aText = TableSorter._textContent(a, sortColumn);
+ var bText = TableSorter._textContent(b, sortColumn);
+
+ // Forward sort equal values by test name.
+ if (sortColumn && aText == bText) {
+ var aTestName = TableSorter._textContent(a, 0);
+ var bTestName = TableSorter._textContent(b, 0);
+ if (aTestName == bTestName)
+ return 0;
+ return aTestName < bTestName ? -1 : 1;
+ }
+
+ if (reversed)
+ return aText < bText ? 1 : -1;
+ else
+ return aText < bText ? -1 : 1;
+ });
+
+ for (var i = 0; i < rows.length; i++)
+ testsTable.appendChild(rows[i]);
+}
+
+TableSorter.sortColumn = function(columnNumber)
+{
+ var newHeader = document.getElementById('results-table').querySelectorAll('th')[columnNumber];
+ TableSorter._sort(newHeader);
+}
+
+TableSorter.handleClick = function(e)
+{
+ var newHeader = e.target;
+ if (newHeader.localName != 'th')
+ return;
+ TableSorter._sort(newHeader);
+}
+
+TableSorter._sort = function(newHeader)
+{
+ TableSorter._updateHeaderClassNames(newHeader);
+
+ var reversed = newHeader.classList.contains('reversed');
+ var sortArrow = reversed ? TableSorter._backwardArrow : TableSorter._forwardArrow;
+ newHeader.innerHTML = TableSorter._sortedContents(newHeader, sortArrow);
+
+ TableSorter._sortRows(newHeader, reversed);
+}
+
+document.addEventListener('keypress', TestNavigator.handleKeyEvent, false);
+
+function onlyShowUnexpectedFailures()
+{
+ return !document.getElementById('show-expected-failures').checked;
+}
+
+function handleStderrChange()
+{
+ OptionWriter.save();
+ document.getElementById('stderr-style').textContent = document.getElementById('show-stderr').checked ?
+ '' : '#stderr-table { display: none; }';
+}
+
+function handleUnexpectedPassesChange()
+{
+ OptionWriter.save();
+ document.getElementById('unexpected-pass-style').textContent = document.getElementById('show-unexpected-passes').checked ?
+ '' : '#passes-table { display: none; }';
+}
+
+function handleFlakyFailuresChange()
+{
+ OptionWriter.save();
+ document.getElementById('flaky-failures-style').textContent = document.getElementById('show-flaky-failures').checked ?
+ '' : '.flaky { display: none; }';
+}
+
+function handleUnexpectedResultsChange()
+{
+ OptionWriter.save();
+ updateExpectedFailures();
+}
+
+function updateExpectedFailures()
+{
+ document.getElementById('unexpected-style').textContent = onlyShowUnexpectedFailures() ?
+ '.expected { display: none; }' : '';
+
+ updateTestlistCounts();
+ TestNavigator.onlyShowUnexpectedFailuresChanged();
+}
+
+var OptionWriter = {};
+
+OptionWriter._key = 'run-webkit-tests-options';
+
+OptionWriter.save = function()
+{
+ var options = document.querySelectorAll('label input');
+ var data = {};
+ for (var i = 0, len = options.length; i < len; i++) {
+ var option = options[i];
+ data[option.id] = option.checked;
+ }
+ localStorage.setItem(OptionWriter._key, JSON.stringify(data));
+}
+
+OptionWriter.apply = function()
+{
+ var json = localStorage.getItem(OptionWriter._key);
+ if (!json) {
+ updateAllOptions();
+ return;
+ }
+
+ var data = JSON.parse(json);
+ for (var id in data) {
+ var input = document.getElementById(id);
+ if (input)
+ input.checked = data[id];
+ }
+ updateAllOptions();
+}
+
+function updateAllOptions()
+{
+ forEach(document.querySelectorAll('input'), function(input) { input.onchange(); });
+}
+
+function failingTestsTable(tests, title, id)
+{
+ if (!tests.length)
+ return '';
+
+ var numberofUnexpectedFailures = 0;
+ var tableRowHtml = '';
+ for (var i = 0; i < tests.length; i++){
+ tableRowHtml += tableRow(tests[i]);
+ if (tests[i].is_unexpected)
+ numberofUnexpectedFailures++;
+ }
+
+ var className = '';
+ if (id)
+ className += id.split('-')[0];
+ if (!hasUnexpected(tests))
+ className += ' expected';
+
+ var header = '<div';
+ if (className)
+ header += ' class="' + className + '"';
+
+ header += '>' + testListHeaderHtml(title) +
+ '<table id="' + id + '"><thead><tr>' +
+ '<th>test</th>';
+
+ header += '</tr></thead>';
+
+
+ return header + tableRowHtml + '</table></div>';
+}
+
+function generatePage()
+{
+ forEachTest(processGlobalStateFor);
+
+ var html = '<div><div id=toolbar>' +
+ '<div id=container>Show: '+
+ '<label><input id="show-expected-failures" type=checkbox onchange="handleUnexpectedResultsChange()">expected failures</label>' +
+ '<label><input id="show-flaky-failures" type=checkbox onchange="handleFlakyFailuresChange()">flaky failures</label>' +
+ '<label><input id="show-unexpected-passes" type=checkbox onchange="handleUnexpectedPassesChange()">unexpected passes</label>' +
+ '<label><input id="show-stderr" type=checkbox onchange="handleStderrChange()">stderr</label>' +
+ '</div></div>';
+
+ if (globalState().results.interrupted)
+ html += "<p class='stopped-running-early-message'>Testing exited early.</p>"
+
+ if (globalState().crashTests.length)
+ html += testList(globalState().crashTests, 'Tests that crashed', 'crash-tests-table');
+
+ if (globalState().leakTests.length)
+ html += testList(globalState().leakTests, 'Tests that leaked', 'leak-tests-table');
+
+ html += failingTestsTable(globalState().failingTests,
+ 'Tests that failed text/pixel/audio diff', 'results-table');
+
+ html += failingTestsTable(globalState().missingResults,
+ 'Tests that had no expected results (probably new)', 'missing-table');
+
+ if (globalState().timeoutTests.length)
+ html += testList(globalState().timeoutTests, 'Tests that timed out', 'timeout-tests-table');
+
+ if (globalState().testsWithStderr.length)
+ html += testList(globalState().testsWithStderr, 'Tests that had stderr output', 'stderr-table');
+
+ html += failingTestsTable(globalState().flakyPassTests,
+ 'Flaky tests (failed the first run and passed on retry)', 'flaky-tests-table');
+
+ if (globalState().unexpectedPassTests.length)
+ html += testList(globalState().unexpectedPassTests, 'Tests expected to fail but passed', 'passes-table');
+
+ html += '</div>';
+
+
+ document.body.innerHTML = html;
+
+ if (document.getElementById('results-table')) {
+ document.getElementById('results-table').addEventListener('click', TableSorter.handleClick, false);
+ TableSorter.sortColumn(0);
+ if (!globalState().hasTextFailures)
+ document.getElementById('text-results-header').textContent = '';
+ if (!globalState().hasImageFailures) {
+ document.getElementById('image-results-header').textContent = '';
+ parentOfType(document.getElementById('toggle-images'), 'label').style.display = 'none';
+ }
+ }
+
+ updateTestlistCounts();
+
+ OptionWriter.apply();
+}
+</script>
+<body onload="generatePage()"></body>
« no previous file with comments | « no previous file | LayoutTests/fast/harness/results.html » ('j') | Tools/Scripts/webkitpy/layout_tests/controllers/manager.py » ('J')

Powered by Google App Engine
This is Rietveld 408576698