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

Side by Side 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 unified diff | Download patch
OLDNEW
(Empty)
1 <!DOCTYPE html>
Dirk Pranke 2014/07/15 20:50:00 There's a fair amount of duplication between dashb
patro 2014/07/16 14:37:36 Yes both dashboard.html and results.html can share
2 <style>
3 html {
4 height: 100%;
5 }
6 body {
7 margin: 0;
8 font-family: Helvetica, sans-serif;
9 font-size: 11pt;
10 display: -webkit-flex;
11 -webkit-flex-direction: column;
12 height: 100%;
13 }
14
15 body > * {
16 margin-left: 4px;
17 margin-top: 4px;
18 }
19
20 h1 {
21 font-size: 14pt;
22 margin-top: 1.5em;
23 }
24
25 p {
26 margin-bottom: 0.3em;
27 }
28
29 tr {
30 background-color: white;
31 }
32
33 tr:hover {
34 background-color: #999999;
35 }
36
37 tr:not(.results-row) td {
38 white-space: nowrap;
39 }
40
41 tr:not(.results-row) td:first-of-type {
42 white-space: normal;
43 }
44
45 td:not(:first-of-type) {
46 text-transform: lowercase;
47 }
48
49 td {
50 padding: 1px 4px;
51 }
52
53 th:empty, td:empty {
54 padding: 0;
55 }
56
57 th {
58 -webkit-user-select: none;
59 -moz-user-select: none;
60 }
61
62 .content-container {
63 -webkit-flex: 1;
64 min-height: -webkit-min-content;
65 overflow: auto;
66 }
67
68 .note {
69 color: gray;
70 font-size: smaller;
71 }
72
73 .results-row {
74 background-color: white;
75 }
76
77 .results-row iframe, .results-row img {
78 width: 800px;
79 height: 600px;
80 }
81
82 .results-row[data-expanded="false"] {
83 display: none;
84 }
85
86 #toolbar {
87 position: fixed;
88 padding: 4px;
89 top: 2px;
90 right: 2px;
91 text-align: right;
92 background-color: rgba(255, 255, 255, 0.85);
93 border: 1px solid silver;
94 border-radius: 4px;
95 }
96
97 .expand-button {
98 background-color: white;
99 width: 11px;
100 height: 12px;
101 border: 1px solid gray;
102 display: inline-block;
103 margin: 0 3px 0 0;
104 position: relative;
105 cursor: default;
106 }
107
108 .current {
109 color: red;
110 }
111
112 .current .expand-button {
113 border-color: red;
114 }
115
116 .expand-button-text {
117 position: absolute;
118 top: -0.3em;
119 left: 1px;
120 }
121
122 tbody .flag {
123 display: none;
124 }
125
126 tbody.flagged .flag {
127 display: inline;
128 }
129
130 .stopped-running-early-message {
131 border: 3px solid #d00;
132 font-weight: bold;
133 display: inline-block;
134 padding: 3px;
135 }
136
137 .result-container {
138 display: inline-block;
139 border: 1px solid gray;
140 margin: 4px;
141 }
142
143 .result-container iframe, .result-container img {
144 border: 0;
145 vertical-align: top;
146 }
147
148 .label {
149 padding-left: 3px;
150 font-weight: bold;
151 font-size: small;
152 background-color: silver;
153 }
154
155 .pixel-zoom-container {
156 position: fixed;
157 top: 0;
158 left: 0;
159 width: 96%;
160 margin: 10px;
161 padding: 10px;
162 display: -webkit-box;
163 display: -moz-box;
164 pointer-events: none;
165 background-color: silver;
166 border-radius: 20px;
167 border: 1px solid gray;
168 box-shadow: 0 0 5px rgba(0, 0, 0, 0.75);
169 }
170
171 .pixel-zoom-container > * {
172 -webkit-box-flex: 1;
173 -moz-box-flex: 1;
174 border: 1px solid black;
175 margin: 4px;
176 overflow: hidden;
177 background-color: white;
178 }
179
180 .pixel-zoom-container .scaled-image-container {
181 position: relative;
182 overflow: hidden;
183 width: 100%;
184 height: 400px;
185 }
186
187 .scaled-image-container > img {
188 position: absolute;
189 top: 0;
190 left: 0;
191 image-rendering: -webkit-optimize-contrast;
192 }
193 .test-pass {
194 background-color:rgb(0,255,0);
195 }
196 .test-image-fail {
197 background-color:rgb(0,128,255);
198 }
199 .test-text-fail {
200 background-color:rgb(250,100,100);
201 }
202 .test-image-text-fail {
203 background-color:rgb(161,128,250);
204 }
205 .test-flaky {
206 background-color:rgb(255,0,255);
207 }
208 .test-missing {
209 background-color:rgb(255,255,255);
210 }
211 .test-fail {
212 background-color:rgb(255,0,0);
213 }
214
215 #flagged-tests {
216 margin: 1px;
217 padding: 5px;
218 height: 100px;
219 }
220
221 #flagged-test-container h2 {
222 display: inline-block;
223 margin: 0 10px 0 0;
224 }
225 </style>
226 <style id="unexpected-pass-style"></style>
227 <style id="flaky-failures-style"></style>
228 <style id="stderr-style"></style>
229 <style id="unexpected-style"></style>
230
231 <script>
232 var g_state;
233 function globalState()
234 {
235 if (!g_state) {
236 g_state = {
237 crashTests: [],
238 leakTests: [],
239 flakyPassTests: [],
240 hasHttpTests: false,
241 hasImageFailures: false,
242 hasTextFailures: false,
243 missingResults: [],
244 results: {},
245 failingTests: [],
246 testsWithStderr: [],
247 timeoutTests: [],
248 unexpectedPassTests: []
249 }
250 }
251 return g_state;
252 }
253
254 function ADD_RESULTS(input)
255 {
256 globalState().results = input;
257 }
258 </script>
259
260 <script src="archived_results.json"></script>
261
262 <script>
263
264 function matchesSelector(node, selector)
265 {
266 if (node.webkitMatchesSelector)
267 return node.webkitMatchesSelector(selector);
268
269 if (node.mozMatchesSelector)
270 return node.mozMatchesSelector(selector);
271 }
272
273 function parentOfType(node, selector)
274 {
275 while (node = node.parentNode) {
276 if (matchesSelector(node, selector))
277 return node;
278 }
279 return null;
280 }
281
282 function remove(node)
283 {
284 node.parentNode.removeChild(node);
285 }
286
287 function forEach(nodeList, handler)
288 {
289 Array.prototype.forEach.call(nodeList, handler);
290 }
291
292 function resultIframe(src)
293 {
294 // FIXME: use audio tags for AUDIO tests?
295 var layoutTestsIndex = src.indexOf('LayoutTests');
296 var name;
297 if (layoutTestsIndex != -1) {
298 var hasTrac = src.indexOf('trac.webkit.org') != -1;
299 var prefix = hasTrac ? 'trac.webkit.org/.../' : '';
Dirk Pranke 2014/07/15 20:50:00 links to trac.webkit.org can't be right ... is thi
300 name = prefix + src.substring(layoutTestsIndex + 'LayoutTests/'.length);
301 } else {
302 var lastDashIndex = src.lastIndexOf('-pretty');
303 if (lastDashIndex == -1)
304 lastDashIndex = src.lastIndexOf('-');
305 name = src.substring(lastDashIndex + 1);
306 }
307
308 var tagName = (src.lastIndexOf('.png') == -1) ? 'iframe' : 'img';
309
310 if (tagName != 'img')
311 src += '?format=txt';
312 return '<div class=result-container><div class=label>' + name + '</div><' + tagName + ' src="' + src + '"></' + tagName + '></div>';
313 }
314
315 function togglingImage(prefix)
316 {
317 return '<div class=result-container><div class="label imageText"></div><img class=animatedImage data-prefix="' +
318 prefix + '"></img></div>';
319 }
320
321 function appendHTML(node, html)
322 {
323 if (node.insertAdjacentHTML)
324 node.insertAdjacentHTML('beforeEnd', html);
325 else
326 node.innerHTML += html;
327 }
328
329 function shouldUseTracLinks()
Dirk Pranke 2014/07/15 20:50:00 same comment
330 {
331 return !globalState().results.layout_tests_dir || !location.toString().index Of('file://') == 0;
332 }
333
334 function testLinkTarget(test)
335 {
336 var target;
337 if (shouldUseTracLinks()) {
338 var revision = globalState().results.revision;
339 target = 'http://src.chromium.org/viewvc/blink/trunk/LayoutTests/' + tes t;
340 if (revision)
341 target += '?pathrev=' + revision;
342 target += '#l1';
343 } else
344 target = globalState().results.layout_tests_dir + '/' + test;
345 return target;
346 }
347
348 function testLink(test)
349 {
350 var target = testLinkTarget(test);
351 return '<a class=test-link href="' + target + '">' + test + '</a>';
352 }
353
354 function resultLink(testPrefix, suffix, contents)
355 {
356 return '<a class=result-link href="' + testPrefix + suffix + '" data-prefix= "' + testPrefix + '">' + contents + '</a> ';
357 }
358
359 function processGlobalStateFor(testObject)
360 {
361 var test = testObject.name;
362 if (testObject.has_stderr)
363 globalState().testsWithStderr.push(testObject);
364
365 globalState().hasHttpTests = globalState().hasHttpTests || test.indexOf('htt p/') == 0;
366
367 var actual = testObject.actual;
368 var expected = testObject.expected || 'PASS';
369
370 if (actual == 'MISSING') {
371 // FIXME: make sure that new-run-webkit-tests spits out an -actual.txt f ile for
372 // tests with MISSING results.
373 globalState().missingResults.push(testObject);
374 return;
375 }
376
377 var actualTokens = actual.split(' ');
378 var passedWithImageOnlyFailureInRetry = actualTokens[0] == 'TEXT' && actualT okens[1] == 'IMAGE';
379 if (actualTokens[1] && actual.indexOf('PASS') != -1 || (!globalState().resul ts.pixel_tests_enabled && passedWithImageOnlyFailureInRetry)) {
380 globalState().flakyPassTests.push(testObject);
381 return;
382 }
383
384 if (actual == 'PASS' && expected != 'PASS') {
385 if (expected != 'IMAGE' || (globalState().results.pixel_tests_enabled || testObject.reftest_type)) {
386 globalState().unexpectedPassTests.push(testObject);
387 }
388 return;
389 }
390
391 if (actual == 'CRASH') {
392 globalState().crashTests.push(testObject);
393 return;
394 }
395
396 if (actual == 'LEAK') {
397 globalState().leakTests.push(testObject);
398 return;
399 }
400
401 if (actual == 'TIMEOUT') {
402 globalState().timeoutTests.push(testObject);
403 return;
404 }
405
406 globalState().failingTests.push(testObject);
407 }
408
409 function getResultLink(index)
410 {
411 if(index < globalState().results.result_links.length)
412 return globalState().results.result_links[index];
413 else
414 return '';
415 }
416
417 function processArchivedResults(archivedResults)
418 {
419 var result = '';
420 var i = 1;
421 for (var i = 0; i < archivedResults.length; i++) {
422 switch(archivedResults[i]) {
423 case "PASS":
424 result += '<td class=test-pass>';
425 break;
426 case "IMAGE":
427 result += '<td class=test-image-fail>';
428 break;
429 case "TEXT":
430 result += '<td class=test-text-fail>';
431 break;
432 case "IMAGE+TEXT":
433 result += '<td class=test-image-text-fail>';
434 break;
435 case "NOTFOUND":
436 result += '<td class=test-missing>';
437 break;
438 case "SKIP":
439 result += '<td class=test-missing>';
440 break;
441 default:
442 result += '<td class=test-fail>';
443 }
444 result += '<a href="' + getResultLink(i) + '">' + (i+1).toString() + '< /a>' + '</td>';
445 }
446
447 return result;
448 }
449
450 function tableRow(testObject)
451 {
452 var row = '<tbody class="' + (testObject.is_unexpected ? '' : 'expected') + '"';
453 if (testObject.reftest_type && testObject.reftest_type.indexOf('!=') != -1)
454 row += ' mismatchreftest=true';
455 row += '><tr>';
456
457 row += '<td>' + testLink(testObject.name) + '</td>';
458
459 var archivedResults = testObject.archived_results;
460 row += processArchivedResults(archivedResults);
461 row += '</tr></tbody>';
462 return row;
463 }
464
465 function forEachTest(handler, opt_tree, opt_prefix)
466 {
467 var tree = opt_tree || globalState().results.tests;
468 var prefix = opt_prefix || '';
469
470 for (var key in tree) {
471 var newPrefix = prefix ? (prefix + '/' + key) : key;
472 if ('actual' in tree[key]) {
473 var testObject = tree[key];
474 testObject.name = newPrefix;
475 handler(testObject);
476 } else
477 forEachTest(handler, tree[key], newPrefix);
478 }
479 }
480
481 function hasUnexpected(tests)
482 {
483 return tests.some(function (test) { return test.is_unexpected; });
484 }
485
486 function updateTestlistCounts()
487 {
488 forEach(document.querySelectorAll('.test-list-count'), function(count) {
489 var container = parentOfType(count, 'div');
490 var testContainers;
491 if (onlyShowUnexpectedFailures())
492 testContainers = container.querySelectorAll('tbody:not(.expected)');
493 else
494 testContainers = container.querySelectorAll('tbody');
495
496 count.textContent = testContainers.length;
497
498 })
499 }
500
501 function testListHeaderHtml(header)
502 {
503 return '<h1>' + header + ' (<span class=test-list-count></span>): </h1>';
504 }
505
506 function testList(tests, header, tableId)
507 {
508 tests.sort();
509
510 var html = '<div' + ((!hasUnexpected(tests) && tableId != 'stderr-table') ? ' class=expected' : '') + ' id=' + tableId + '>' +
511 testListHeaderHtml(header) + '<table>';
512
513 // FIXME: Include this for all testLists.
514 if (tableId == 'passes-table')
515 html += '<thead><th>test</th><th>expected</th></thead>';
516
517 for (var i = 0; i < tests.length; i++) {
518 var testObject = tests[i];
519 var test = testObject.name;
520 html += '<tbody class="' + ((testObject.is_unexpected || tableId == 'std err-table') ? '' : 'expected') + '"><tr><td>' +
521 testLink(test) +
522 '</td>';
523
524 html += processArchivedResults(testObject.archived_results)
525 html += '</tr></tbody>';
526 }
527 html += '</table></div>';
528 return html;
529 }
530
531 function toArray(nodeList)
532 {
533 return Array.prototype.slice.call(nodeList);
534 }
535
536 function trim(string)
537 {
538 return string.replace(/^[\s\xa0]+|[\s\xa0]+$/g, '');
539 }
540
541 // Just a namespace for code management.
542 var TableSorter = {};
543
544 TableSorter._forwardArrow = '<svg style="width:10px;height:10px"><polygon points ="0,0 10,0 5,10" style="fill:#ccc"></svg>';
545
546 TableSorter._backwardArrow = '<svg style="width:10px;height:10px"><polygon point s="0,10 10,10 5,0" style="fill:#ccc"></svg>';
547
548 TableSorter._sortedContents = function(header, arrow)
549 {
550 return arrow + ' ' + trim(header.textContent) + ' ' + arrow;
551 }
552
553 TableSorter._updateHeaderClassNames = function(newHeader)
554 {
555 var sortHeader = document.querySelector('.sortHeader');
556 if (sortHeader) {
557 if (sortHeader == newHeader) {
558 var isAlreadyReversed = sortHeader.classList.contains('reversed');
559 if (isAlreadyReversed)
560 sortHeader.classList.remove('reversed');
561 else
562 sortHeader.classList.add('reversed');
563 } else {
564 sortHeader.textContent = sortHeader.textContent;
565 sortHeader.classList.remove('sortHeader');
566 sortHeader.classList.remove('reversed');
567 }
568 }
569
570 newHeader.classList.add('sortHeader');
571 }
572
573 TableSorter._textContent = function(tbodyRow, column)
574 {
575 return tbodyRow.querySelectorAll('td')[column].textContent;
576 }
577
578 TableSorter._sortRows = function(newHeader, reversed)
579 {
580 var testsTable = document.getElementById('results-table');
581 var headers = toArray(testsTable.querySelectorAll('th'));
582 var sortColumn = headers.indexOf(newHeader);
583
584 var rows = toArray(testsTable.querySelectorAll('tbody'));
585
586 rows.sort(function(a, b) {
587 // Only need to support lexicographic sort for now.
588 var aText = TableSorter._textContent(a, sortColumn);
589 var bText = TableSorter._textContent(b, sortColumn);
590
591 // Forward sort equal values by test name.
592 if (sortColumn && aText == bText) {
593 var aTestName = TableSorter._textContent(a, 0);
594 var bTestName = TableSorter._textContent(b, 0);
595 if (aTestName == bTestName)
596 return 0;
597 return aTestName < bTestName ? -1 : 1;
598 }
599
600 if (reversed)
601 return aText < bText ? 1 : -1;
602 else
603 return aText < bText ? -1 : 1;
604 });
605
606 for (var i = 0; i < rows.length; i++)
607 testsTable.appendChild(rows[i]);
608 }
609
610 TableSorter.sortColumn = function(columnNumber)
611 {
612 var newHeader = document.getElementById('results-table').querySelectorAll('t h')[columnNumber];
613 TableSorter._sort(newHeader);
614 }
615
616 TableSorter.handleClick = function(e)
617 {
618 var newHeader = e.target;
619 if (newHeader.localName != 'th')
620 return;
621 TableSorter._sort(newHeader);
622 }
623
624 TableSorter._sort = function(newHeader)
625 {
626 TableSorter._updateHeaderClassNames(newHeader);
627
628 var reversed = newHeader.classList.contains('reversed');
629 var sortArrow = reversed ? TableSorter._backwardArrow : TableSorter._forward Arrow;
630 newHeader.innerHTML = TableSorter._sortedContents(newHeader, sortArrow);
631
632 TableSorter._sortRows(newHeader, reversed);
633 }
634
635 document.addEventListener('keypress', TestNavigator.handleKeyEvent, false);
636
637 function onlyShowUnexpectedFailures()
638 {
639 return !document.getElementById('show-expected-failures').checked;
640 }
641
642 function handleStderrChange()
643 {
644 OptionWriter.save();
645 document.getElementById('stderr-style').textContent = document.getElementByI d('show-stderr').checked ?
646 '' : '#stderr-table { display: none; }';
647 }
648
649 function handleUnexpectedPassesChange()
650 {
651 OptionWriter.save();
652 document.getElementById('unexpected-pass-style').textContent = document.getE lementById('show-unexpected-passes').checked ?
653 '' : '#passes-table { display: none; }';
654 }
655
656 function handleFlakyFailuresChange()
657 {
658 OptionWriter.save();
659 document.getElementById('flaky-failures-style').textContent = document.getEl ementById('show-flaky-failures').checked ?
660 '' : '.flaky { display: none; }';
661 }
662
663 function handleUnexpectedResultsChange()
664 {
665 OptionWriter.save();
666 updateExpectedFailures();
667 }
668
669 function updateExpectedFailures()
670 {
671 document.getElementById('unexpected-style').textContent = onlyShowUnexpected Failures() ?
672 '.expected { display: none; }' : '';
673
674 updateTestlistCounts();
675 TestNavigator.onlyShowUnexpectedFailuresChanged();
676 }
677
678 var OptionWriter = {};
679
680 OptionWriter._key = 'run-webkit-tests-options';
681
682 OptionWriter.save = function()
683 {
684 var options = document.querySelectorAll('label input');
685 var data = {};
686 for (var i = 0, len = options.length; i < len; i++) {
687 var option = options[i];
688 data[option.id] = option.checked;
689 }
690 localStorage.setItem(OptionWriter._key, JSON.stringify(data));
691 }
692
693 OptionWriter.apply = function()
694 {
695 var json = localStorage.getItem(OptionWriter._key);
696 if (!json) {
697 updateAllOptions();
698 return;
699 }
700
701 var data = JSON.parse(json);
702 for (var id in data) {
703 var input = document.getElementById(id);
704 if (input)
705 input.checked = data[id];
706 }
707 updateAllOptions();
708 }
709
710 function updateAllOptions()
711 {
712 forEach(document.querySelectorAll('input'), function(input) { input.onchange (); });
713 }
714
715 function failingTestsTable(tests, title, id)
716 {
717 if (!tests.length)
718 return '';
719
720 var numberofUnexpectedFailures = 0;
721 var tableRowHtml = '';
722 for (var i = 0; i < tests.length; i++){
723 tableRowHtml += tableRow(tests[i]);
724 if (tests[i].is_unexpected)
725 numberofUnexpectedFailures++;
726 }
727
728 var className = '';
729 if (id)
730 className += id.split('-')[0];
731 if (!hasUnexpected(tests))
732 className += ' expected';
733
734 var header = '<div';
735 if (className)
736 header += ' class="' + className + '"';
737
738 header += '>' + testListHeaderHtml(title) +
739 '<table id="' + id + '"><thead><tr>' +
740 '<th>test</th>';
741
742 header += '</tr></thead>';
743
744
745 return header + tableRowHtml + '</table></div>';
746 }
747
748 function generatePage()
749 {
750 forEachTest(processGlobalStateFor);
751
752 var html = '<div><div id=toolbar>' +
753 '<div id=container>Show: '+
754 '<label><input id="show-expected-failures" type=checkbox onchange="handl eUnexpectedResultsChange()">expected failures</label>' +
755 '<label><input id="show-flaky-failures" type=checkbox onchange="handleFl akyFailuresChange()">flaky failures</label>' +
756 '<label><input id="show-unexpected-passes" type=checkbox onchange="handl eUnexpectedPassesChange()">unexpected passes</label>' +
757 '<label><input id="show-stderr" type=checkbox onchange="handleStderrChan ge()">stderr</label>' +
758 '</div></div>';
759
760 if (globalState().results.interrupted)
761 html += "<p class='stopped-running-early-message'>Testing exited early.< /p>"
762
763 if (globalState().crashTests.length)
764 html += testList(globalState().crashTests, 'Tests that crashed', 'crash- tests-table');
765
766 if (globalState().leakTests.length)
767 html += testList(globalState().leakTests, 'Tests that leaked', 'leak-tes ts-table');
768
769 html += failingTestsTable(globalState().failingTests,
770 'Tests that failed text/pixel/audio diff', 'results-table');
771
772 html += failingTestsTable(globalState().missingResults,
773 'Tests that had no expected results (probably new)', 'missing-table');
774
775 if (globalState().timeoutTests.length)
776 html += testList(globalState().timeoutTests, 'Tests that timed out', 'ti meout-tests-table');
777
778 if (globalState().testsWithStderr.length)
779 html += testList(globalState().testsWithStderr, 'Tests that had stderr o utput', 'stderr-table');
780
781 html += failingTestsTable(globalState().flakyPassTests,
782 'Flaky tests (failed the first run and passed on retry)', 'flaky-tests-t able');
783
784 if (globalState().unexpectedPassTests.length)
785 html += testList(globalState().unexpectedPassTests, 'Tests expected to f ail but passed', 'passes-table');
786
787 html += '</div>';
788
789
790 document.body.innerHTML = html;
791
792 if (document.getElementById('results-table')) {
793 document.getElementById('results-table').addEventListener('click', Table Sorter.handleClick, false);
794 TableSorter.sortColumn(0);
795 if (!globalState().hasTextFailures)
796 document.getElementById('text-results-header').textContent = '';
797 if (!globalState().hasImageFailures) {
798 document.getElementById('image-results-header').textContent = '';
799 parentOfType(document.getElementById('toggle-images'), 'label').styl e.display = 'none';
800 }
801 }
802
803 updateTestlistCounts();
804
805 OptionWriter.apply();
806 }
807 </script>
808 <body onload="generatePage()"></body>
OLDNEW
« 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