8 font-family: Helvetica, sans-serif;
10 display: -webkit-flex;
11 -webkit-flex-direction: column;
29 tr:not(.results-row) td {
33 tr:not(.results-row) td:first-of-type {
37 td:not(:first-of-type) {
38 text-transform: lowercase;
50 -webkit-user-select: none;
51 -moz-user-select: none;
56 min-height: -webkit-min-content;
66 background-color: white;
69 .results-row iframe, .results-row img {
74 .results-row[
data-expanded=
"false"] {
84 background-color: rgba(
255,
255,
255,
0.85);
85 border:
1px solid silver;
90 background-color: white;
91 border:
1px solid gray;
93 display: inline-block;
98 -webkit-user-select: none;
106 .current .expand-button {
114 tbody.flagged .flag {
118 .stopped-running-early-message {
119 border:
3px solid #d00;
121 display: inline-block;
126 display: inline-block;
127 border:
1px solid gray;
131 .result-container iframe, .result-container img {
140 background-color: silver;
143 .pixel-zoom-container {
150 display: -webkit-box;
152 pointer-events: none;
153 background-color: silver;
155 border:
1px solid gray;
156 box-shadow:
0 0 5px rgba(
0,
0,
0,
0.75);
159 .pixel-zoom-container
> * {
162 border:
1px solid black;
165 background-color: white;
168 .pixel-zoom-container .scaled-image-container {
175 .scaled-image-container
> img {
179 image-rendering: -webkit-optimize-contrast;
188 #flagged-test-container h2 {
189 display: inline-block;
193 <style id=
"unexpected-pass-style"></style>
194 <style id=
"flaky-failures-style"></style>
195 <style id=
"stderr-style"></style>
196 <style id=
"unexpected-style"></style>
200 function globalState()
208 hasImageFailures
: false,
209 hasTextFailures
: false,
212 shouldToggleImages
: true,
216 unexpectedPassTests
: []
222 function ADD_RESULTS(input
)
224 globalState().results
= input
;
228 <script src=
"failing_results.json"></script>
231 function stripExtension(test
)
233 var index
= test
.lastIndexOf('.');
234 return test
.substring(0, index
);
237 function matchesSelector(node
, selector
)
239 if (node
.webkitMatchesSelector
)
240 return node
.webkitMatchesSelector(selector
);
242 if (node
.mozMatchesSelector
)
243 return node
.mozMatchesSelector(selector
);
246 function parentOfType(node
, selector
)
248 while (node
= node
.parentNode
) {
249 if (matchesSelector(node
, selector
))
255 function remove(node
)
257 node
.parentNode
.removeChild(node
);
260 function forEach(nodeList
, handler
)
262 Array
.prototype.forEach
.call(nodeList
, handler
);
265 function resultIframe(src
)
267 // FIXME: use audio tags for AUDIO tests?
268 var layoutTestsIndex
= src
.indexOf('LayoutTests');
270 if (layoutTestsIndex
!= -1) {
271 var hasTrac
= src
.indexOf('trac.webkit.org') != -1;
272 var prefix
= hasTrac
? 'trac.webkit.org/.../' : '';
273 name
= prefix
+ src
.substring(layoutTestsIndex
+ 'LayoutTests/'.length
);
275 var lastDashIndex
= src
.lastIndexOf('-pretty');
276 if (lastDashIndex
== -1)
277 lastDashIndex
= src
.lastIndexOf('-');
278 name
= src
.substring(lastDashIndex
+ 1);
281 var tagName
= (src
.lastIndexOf('.png') == -1) ? 'iframe' : 'img';
283 if (tagName
!= 'img')
284 src
+= '?format=txt';
285 return '<div class=result-container><div class=label>' + name
+ '</div><' + tagName
+ ' src="' + src
+ '"></' + tagName
+ '></div>';
288 function togglingImage(prefix
)
290 return '<div class=result-container><div class="label imageText"></div><img class=animatedImage data-prefix="' +
291 prefix
+ '"></img></div>';
294 function toggleExpectations(element
)
296 var expandLink
= element
;
297 if (expandLink
.className
!= 'expand-button-text')
298 expandLink
= expandLink
.querySelector('.expand-button-text');
300 if (expandLink
.textContent
== '+')
301 expandExpectations(expandLink
, true);
303 collapseExpectations(expandLink
);
306 function collapseExpectations(expandLink
)
308 expandLink
.textContent
= '+';
309 var existingResultsRow
= parentOfType(expandLink
, 'tbody').querySelector('.results-row');
310 if (existingResultsRow
)
311 updateExpandedState(existingResultsRow
, false);
314 function updateExpandedState(row
, isExpanded
)
316 row
.setAttribute('data-expanded', isExpanded
);
317 updateImageTogglingTimer();
320 function appendHTML(node
, html
)
322 if (node
.insertAdjacentHTML
)
323 node
.insertAdjacentHTML('beforeEnd', html
);
325 node
.innerHTML
+= html
;
328 function expandExpectations(expandLink
, selectRow
)
330 var row
= parentOfType(expandLink
, 'tr');
331 var parentTbody
= row
.parentNode
;
332 var existingResultsRow
= parentTbody
.querySelector('.results-row');
334 var enDash
= '\u2013';
335 expandLink
.textContent
= enDash
;
336 if (existingResultsRow
) {
337 updateExpandedState(existingResultsRow
, true);
339 TestNavigator
._setCurrentTest(parentTbody
);
343 var newRow
= document
.createElement('tr');
344 newRow
.className
= 'results-row';
345 var newCell
= document
.createElement('td');
346 newCell
.colSpan
= row
.querySelectorAll('td').length
;
348 var resultLinks
= row
.querySelectorAll('.result-link');
349 var hasTogglingImages
= false;
350 for (var i
= 0; i
< resultLinks
.length
; i
++) {
351 var link
= resultLinks
[i
];
353 if (link
.textContent
== 'images') {
354 hasTogglingImages
= true;
355 result
= togglingImage(link
.getAttribute('data-prefix'));
357 result
= resultIframe(link
.href
);
359 appendHTML(newCell
, result
);
362 newRow
.appendChild(newCell
);
363 parentTbody
.appendChild(newRow
);
365 updateExpandedState(newRow
, true);
367 TestNavigator
._setCurrentTest(parentTbody
);
368 updateImageTogglingTimer();
371 function updateImageTogglingTimer()
373 var hasVisibleAnimatedImage
= document
.querySelector('.results-row[data-expanded="true"] .animatedImage');
374 if (!hasVisibleAnimatedImage
) {
375 clearInterval(globalState().togglingImageInterval
);
376 globalState().togglingImageInterval
= null;
380 if (!globalState().togglingImageInterval
) {
382 globalState().togglingImageInterval
= setInterval(toggleImages
, 2000);
386 function async(func
, args
)
388 setTimeout(function() { func
.apply(null, args
); }, 100);
391 function visibleTests(opt_container
)
393 var container
= opt_container
|| document
;
394 if (onlyShowUnexpectedFailures())
395 return container
.querySelectorAll('tbody:not(.expected)');
397 return container
.querySelectorAll('tbody');
400 function visibleExpandLinks()
402 if (onlyShowUnexpectedFailures())
403 return document
.querySelectorAll('tbody:not(.expected) .expand-button-text');
405 return document
.querySelectorAll('.expand-button-text');
408 function expandAllExpectations()
410 var expandLinks
= visibleExpandLinks();
411 for (var i
= 0, len
= expandLinks
.length
; i
< len
; i
++)
412 async(expandExpectations
, [expandLinks
[i
]]);
415 function collapseAllExpectations()
417 var expandLinks
= visibleExpandLinks();
418 for (var i
= 0, len
= expandLinks
.length
; i
< len
; i
++)
419 async(collapseExpectations
, [expandLinks
[i
]]);
422 function shouldUseTracLinks()
424 return !globalState().results
.layout_tests_dir
|| !location
.toString().indexOf('file://') == 0;
427 function testLinkTarget(test
)
430 if (shouldUseTracLinks()) {
431 var revision
= globalState().results
.revision
;
432 target
= 'http://src.chromium.org/viewvc/blink/trunk/LayoutTests/' + test
;
434 target
+= '?pathrev=' + revision
;
437 target
= globalState().results
.layout_tests_dir
+ '/' + test
;
441 function testLink(test
)
443 var target
= testLinkTarget(test
);
444 return '<a class=test-link href="' + target
+ '">' + test
+ '</a><span class=flag onclick="unflag(this)"> \u2691</span>';
447 function unflag(flag
)
449 var shouldFlag
= false;
450 TestNavigator
.flagTest(parentOfType(flag
, 'tbody'), shouldFlag
);
451 TestNavigator
.updateFlaggedTestTextBox();
454 function testLinkWithExpandButton(test
)
456 return '<span class=expand-button onclick="toggleExpectations(this)"><span class=expand-button-text>+</span></span>' + testLink(test
);
459 function resultLink(testPrefix
, suffix
, contents
)
461 return '<a class=result-link href="' + testPrefix
+ suffix
+ '" data-prefix="' + testPrefix
+ '">' + contents
+ '</a> ';
464 function processGlobalStateFor(testObject
)
466 var test
= testObject
.name
;
467 if (testObject
.has_stderr
)
468 globalState().testsWithStderr
.push(testObject
);
470 globalState().hasHttpTests
= globalState().hasHttpTests
|| test
.indexOf('http/') == 0;
472 var actual
= testObject
.actual
;
473 var expected
= testObject
.expected
|| 'PASS';
475 if (actual
== 'MISSING') {
476 // FIXME: make sure that new-run-webkit-tests spits out an -actual.txt file for
477 // tests with MISSING results.
478 globalState().missingResults
.push(testObject
);
482 var actualTokens
= actual
.split(' ');
483 var passedWithImageOnlyFailureInRetry
= actualTokens
[0] == 'TEXT' && actualTokens
[1] == 'IMAGE';
484 if (actualTokens
[1] && actual
.indexOf('PASS') != -1 || (!globalState().results
.pixel_tests_enabled
&& passedWithImageOnlyFailureInRetry
)) {
485 globalState().flakyPassTests
.push(testObject
);
489 if (actual
== 'PASS' && expected
!= 'PASS') {
490 if (expected
!= 'IMAGE' || (globalState().results
.pixel_tests_enabled
|| testObject
.reftest_type
)) {
491 globalState().unexpectedPassTests
.push(testObject
);
496 if (actual
== 'CRASH') {
497 globalState().crashTests
.push(testObject
);
501 if (actual
== 'LEAK') {
502 globalState().leakTests
.push(testObject
);
506 if (actual
== 'TIMEOUT') {
507 globalState().timeoutTests
.push(testObject
);
511 globalState().failingTests
.push(testObject
);
514 function toggleImages()
516 var images
= document
.querySelectorAll('.animatedImage');
517 var imageTexts
= document
.querySelectorAll('.imageText');
518 for (var i
= 0, len
= images
.length
; i
< len
; i
++) {
519 var image
= images
[i
];
520 var text
= imageTexts
[i
];
521 if (text
.textContent
== 'Expected Image') {
522 text
.textContent
= 'Actual Image';
523 image
.src
= image
.getAttribute('data-prefix') + '-actual.png';
525 text
.textContent
= 'Expected Image';
526 image
.src
= image
.getAttribute('data-prefix') + '-expected.png';
531 function textResultLinks(test
, prefix
)
533 var html
= resultLink(prefix
, '-expected.txt', 'expected') +
534 resultLink(prefix
, '-actual.txt', 'actual') +
535 resultLink(prefix
, '-diff.txt', 'diff');
537 if (globalState().results
.has_pretty_patch
)
538 html
+= resultLink(prefix
, '-pretty-diff.html', 'pretty diff');
540 if (globalState().results
.has_wdiff
)
541 html
+= resultLink(prefix
, '-wdiff.html', 'wdiff');
546 function imageResultsCell(testObject
, testPrefix
, actual
) {
549 if (actual
.indexOf('IMAGE') != -1) {
550 globalState().hasImageFailures
= true;
552 if (testObject
.reftest_type
&& testObject
.reftest_type
.indexOf('!=') != -1) {
553 row
+= resultLink(testPrefix
, '-expected-mismatch.html', 'ref mismatch html');
554 row
+= resultLink(testPrefix
, '-actual.png', 'actual');
556 if (testObject
.reftest_type
&& testObject
.reftest_type
.indexOf('==') != -1) {
557 row
+= resultLink(testPrefix
, '-expected.html', 'ref html');
559 if (globalState().shouldToggleImages
) {
560 row
+= resultLink(testPrefix
, '-diffs.html', 'images');
562 row
+= resultLink(testPrefix
, '-expected.png', 'expected');
563 row
+= resultLink(testPrefix
, '-actual.png', 'actual');
566 row
+= resultLink(testPrefix
, '-diff.png', 'diff');
570 if (actual
.indexOf('MISSING') != -1 && testObject
.is_missing_image
)
571 row
+= resultLink(testPrefix
, '-actual.png', 'png result');
576 function tableRow(testObject
)
578 var row
= '<tbody class="' + (testObject
.is_unexpected
? '' : 'expected') + '"';
579 row
+= ' data-testname="' + testObject
.name
+ '"';
580 if (testObject
.reftest_type
&& testObject
.reftest_type
.indexOf('!=') != -1)
581 row
+= ' mismatchreftest=true';
584 row
+= '<td>' + testLinkWithExpandButton(testObject
.name
) + '</td>';
586 var testPrefix
= stripExtension(testObject
.name
);
589 var actual
= testObject
.actual
;
590 if (actual
.indexOf('TEXT') != -1) {
591 globalState().hasTextFailures
= true;
592 if (testObject
.is_testharness_test
) {
593 row
+= resultLink(testPrefix
, '-actual.txt', 'actual');
595 row
+= textResultLinks(testObject
.name
, testPrefix
);
599 if (actual
.indexOf('AUDIO') != -1) {
600 row
+= resultLink(testPrefix
, '-expected.wav', 'expected audio');
601 row
+= resultLink(testPrefix
, '-actual.wav', 'actual audio');
604 if (actual
.indexOf('MISSING') != -1) {
605 if (testObject
.is_missing_audio
)
606 row
+= resultLink(testPrefix
, '-actual.wav', 'audio result');
607 if (testObject
.is_missing_text
)
608 row
+= resultLink(testPrefix
, '-actual.txt', 'result');
611 if (actual
.indexOf('CRASH') != -1) {
612 row
+= resultLink(testPrefix
, '-crash-log.txt', 'crash log');
613 row
+= resultLink(testPrefix
, '-sample.txt', 'sample');
614 if (testObject
.has_stderr
)
615 row
+= resultLink(testPrefix
, '-stderr.txt', 'stderr');
618 if (testObject
.has_repaint_overlay
)
619 row
+= resultLink(testPrefix
, '-overlay.html?' + encodeURIComponent(testLinkTarget(testObject
.name
)), 'overlay');
621 var actualTokens
= actual
.split(/\s+/);
622 var cell
= imageResultsCell(testObject
, testPrefix
, actualTokens
[0]);
623 if (!cell
&& actualTokens
.length
> 1)
624 cell
= imageResultsCell(testObject
, 'retries/' + testPrefix
, actualTokens
[1]);
626 row
+= '</td><td>' + cell
+ '</td>' +
627 '<td>' + actual
+ '</td>' +
628 '<td>' + (actual
.indexOf('MISSING') == -1 ? testObject
.expected
: '') + '</td>' +
633 function forEachTest(handler
, opt_tree
, opt_prefix
)
635 var tree
= opt_tree
|| globalState().results
.tests
;
636 var prefix
= opt_prefix
|| '';
638 for (var key
in tree
) {
639 var newPrefix
= prefix
? (prefix
+ '/' + key
) : key
;
640 if ('actual' in tree
[key
]) {
641 var testObject
= tree
[key
];
642 testObject
.name
= newPrefix
;
645 forEachTest(handler
, tree
[key
], newPrefix
);
649 function hasUnexpected(tests
)
651 return tests
.some(function (test
) { return test
.is_unexpected
; });
654 function updateTestlistCounts()
656 forEach(document
.querySelectorAll('.test-list-count'), function(count
) {
657 var container
= parentOfType(count
, 'div');
659 if (onlyShowUnexpectedFailures())
660 testContainers
= container
.querySelectorAll('tbody:not(.expected)');
662 testContainers
= container
.querySelectorAll('tbody');
664 count
.textContent
= testContainers
.length
;
668 function flagAll(headerLink
)
670 var tests
= visibleTests(parentOfType(headerLink
, 'div'));
671 forEach(tests
, function(test
) {
672 TestNavigator
.flagTest(test
, true);
674 TestNavigator
.updateFlaggedTestTextBox();
677 function unflagAll(headerLink
)
679 var tests
= visibleTests(parentOfType(headerLink
, 'div'));
680 forEach(tests
, function(test
) {
681 TestNavigator
.flagTest(test
, false);
683 TestNavigator
.updateFlaggedTestTextBox();
686 function testListHeaderHtml(header
)
688 return '<h1>' + header
+ ' (<span class=test-list-count></span>): [<a href="#" class=flag-all onclick="flagAll(this)">flag all</a>] [<a href="#" class=flag-all onclick="unflagAll(this)">unflag all</a>]</h1>';
691 function testList(tests
, header
, tableId
)
695 var html
= '<div' + ((!hasUnexpected(tests
) && tableId
!= 'stderr-table') ? ' class=expected' : '') + ' id=' + tableId
+ '>' +
696 testListHeaderHtml(header
) + '<table>';
698 // FIXME: Include this for all testLists.
699 if (tableId
== 'passes-table')
700 html
+= '<thead><th>test</th><th>expected</th></thead>';
702 for (var i
= 0; i
< tests
.length
; i
++) {
703 var testObject
= tests
[i
];
704 var test
= testObject
.name
;
705 html
+= '<tbody class="' + ((testObject
.is_unexpected
|| tableId
== 'stderr-table') ? '' : 'expected') + '" data-testname="' + test
+ '"><tr><td>' +
706 ((tableId
== 'passes-table') ? testLink(test
) : testLinkWithExpandButton(test
)) +
709 if (tableId
== 'stderr-table')
710 html
+= resultLink(stripExtension(test
), '-stderr.txt', 'stderr');
711 else if (tableId
== 'passes-table')
712 html
+= testObject
.expected
;
713 else if (tableId
== 'crash-tests-table') {
714 html
+= resultLink(stripExtension(test
), '-crash-log.txt', 'crash log');
715 html
+= resultLink(stripExtension(test
), '-sample.txt', 'sample');
716 if (testObject
.has_stderr
)
717 html
+= resultLink(stripExtension(test
), '-stderr.txt', 'stderr');
718 } else if (tableId
== 'leak-tests-table')
719 html
+= resultLink(stripExtension(test
), '-leak-log.txt', 'leak log');
720 else if (tableId
== 'timeout-tests-table') {
721 // FIXME: only include timeout actual/diff results here if we actually spit out results for timeout tests.
722 html
+= textResultLinks(test
, stripExtension(test
));
725 if (testObject
.has_repaint_overlay
)
726 html
+= resultLink(stripExtension(test
), '-overlay.html?' + encodeURIComponent(testLinkTarget(test
)), 'overlay');
728 html
+= '</td></tr></tbody>';
730 html
+= '</table></div>';
734 function toArray(nodeList
)
736 return Array
.prototype.slice
.call(nodeList
);
739 function trim(string
)
741 return string
.replace(/^[\s\xa0]+|[\s\xa0]+$/g, '');
744 // Just a namespace for code management.
745 var TableSorter
= {};
747 TableSorter
._forwardArrow
= '<svg style="width:10px;height:10px"><polygon points="0,0 10,0 5,10" style="fill:#ccc"></svg>';
749 TableSorter
._backwardArrow
= '<svg style="width:10px;height:10px"><polygon points="0,10 10,10 5,0" style="fill:#ccc"></svg>';
751 TableSorter
._sortedContents = function(header
, arrow
)
753 return arrow
+ ' ' + trim(header
.textContent
) + ' ' + arrow
;
756 TableSorter
._updateHeaderClassNames = function(newHeader
)
758 var sortHeader
= document
.querySelector('.sortHeader');
760 if (sortHeader
== newHeader
) {
761 var isAlreadyReversed
= sortHeader
.classList
.contains('reversed');
762 if (isAlreadyReversed
)
763 sortHeader
.classList
.remove('reversed');
765 sortHeader
.classList
.add('reversed');
767 sortHeader
.textContent
= sortHeader
.textContent
;
768 sortHeader
.classList
.remove('sortHeader');
769 sortHeader
.classList
.remove('reversed');
773 newHeader
.classList
.add('sortHeader');
776 TableSorter
._textContent = function(tbodyRow
, column
)
778 return tbodyRow
.querySelectorAll('td')[column
].textContent
;
781 TableSorter
._sortRows = function(newHeader
, reversed
)
783 var testsTable
= document
.getElementById('results-table');
784 var headers
= toArray(testsTable
.querySelectorAll('th'));
785 var sortColumn
= headers
.indexOf(newHeader
);
787 var rows
= toArray(testsTable
.querySelectorAll('tbody'));
789 rows
.sort(function(a
, b
) {
790 // Only need to support lexicographic sort for now.
791 var aText
= TableSorter
._textContent(a
, sortColumn
);
792 var bText
= TableSorter
._textContent(b
, sortColumn
);
794 // Forward sort equal values by test name.
795 if (sortColumn
&& aText
== bText
) {
796 var aTestName
= TableSorter
._textContent(a
, 0);
797 var bTestName
= TableSorter
._textContent(b
, 0);
798 if (aTestName
== bTestName
)
800 return aTestName
< bTestName
? -1 : 1;
804 return aText
< bText
? 1 : -1;
806 return aText
< bText
? -1 : 1;
809 for (var i
= 0; i
< rows
.length
; i
++)
810 testsTable
.appendChild(rows
[i
]);
813 TableSorter
.sortColumn = function(columnNumber
)
815 var newHeader
= document
.getElementById('results-table').querySelectorAll('th')[columnNumber
];
816 TableSorter
._sort(newHeader
);
819 TableSorter
.handleClick = function(e
)
821 var newHeader
= e
.target
;
822 if (newHeader
.localName
!= 'th')
824 TableSorter
._sort(newHeader
);
827 TableSorter
._sort = function(newHeader
)
829 TableSorter
._updateHeaderClassNames(newHeader
);
831 var reversed
= newHeader
.classList
.contains('reversed');
832 var sortArrow
= reversed
? TableSorter
._backwardArrow
: TableSorter
._forwardArrow
;
833 newHeader
.innerHTML
= TableSorter
._sortedContents(newHeader
, sortArrow
);
835 TableSorter
._sortRows(newHeader
, reversed
);
838 var PixelZoomer
= {};
840 PixelZoomer
.showOnDelay
= true;
841 PixelZoomer
._zoomFactor
= 6;
843 var kResultWidth
= 800;
844 var kResultHeight
= 600;
846 var kZoomedResultWidth
= kResultWidth
* PixelZoomer
._zoomFactor
;
847 var kZoomedResultHeight
= kResultHeight
* PixelZoomer
._zoomFactor
;
849 PixelZoomer
._zoomImageContainer = function(url
)
851 var container
= document
.createElement('div');
852 container
.className
= 'zoom-image-container';
854 var title
= url
.match(/\-([^\-]*)\.png/)[1];
856 var label
= document
.createElement('div');
857 label
.className
= 'label';
858 label
.appendChild(document
.createTextNode(title
));
859 container
.appendChild(label
);
861 var imageContainer
= document
.createElement('div');
862 imageContainer
.className
= 'scaled-image-container';
864 var image
= new Image();
866 image
.style
.display
= 'none';
868 var canvas
= document
.createElement('canvas');
870 imageContainer
.appendChild(image
);
871 imageContainer
.appendChild(canvas
);
872 container
.appendChild(imageContainer
);
877 PixelZoomer
._createContainer = function(e
)
879 var tbody
= parentOfType(e
.target
, 'tbody');
880 var row
= tbody
.querySelector('tr');
881 var imageDiffLinks
= row
.querySelectorAll('a[href$=".png"]');
883 var container
= document
.createElement('div');
884 container
.className
= 'pixel-zoom-container';
888 var togglingImageLink
= row
.querySelector('a[href$="-diffs.html"]');
889 if (togglingImageLink
) {
890 var prefix
= togglingImageLink
.getAttribute('data-prefix');
891 container
.appendChild(PixelZoomer
._zoomImageContainer(prefix
+ '-expected.png'));
892 container
.appendChild(PixelZoomer
._zoomImageContainer(prefix
+ '-actual.png'));
895 for (var i
= 0; i
< imageDiffLinks
.length
; i
++)
896 container
.appendChild(PixelZoomer
._zoomImageContainer(imageDiffLinks
[i
].href
));
898 document
.body
.appendChild(container
);
899 PixelZoomer
._drawAll();
902 PixelZoomer
._draw = function(imageContainer
)
904 var image
= imageContainer
.querySelector('img');
905 var canvas
= imageContainer
.querySelector('canvas');
907 if (!image
.complete
) {
908 image
.onload = function() {
909 PixelZoomer
._draw(imageContainer
);
914 canvas
.width
= imageContainer
.clientWidth
;
915 canvas
.height
= imageContainer
.clientHeight
;
917 var ctx
= canvas
.getContext('2d');
918 ctx
.mozImageSmoothingEnabled
= false;
919 ctx
.imageSmoothingEnabled
= false;
920 ctx
.translate(imageContainer
.clientWidth
/ 2, imageContainer
.clientHeight
/ 2);
921 ctx
.translate(-PixelZoomer
._percentX
* kZoomedResultWidth
, -PixelZoomer
._percentY
* kZoomedResultHeight
);
922 ctx
.strokeRect(-1.5, -1.5, kZoomedResultWidth
+ 2, kZoomedResultHeight
+ 2);
923 ctx
.scale(PixelZoomer
._zoomFactor
, PixelZoomer
._zoomFactor
);
924 ctx
.drawImage(image
, 0, 0);
927 PixelZoomer
._drawAll = function()
929 forEach(document
.querySelectorAll('.pixel-zoom-container .scaled-image-container'), PixelZoomer
._draw
);
932 PixelZoomer
.handleMouseOut = function(e
)
934 if (e
.relatedTarget
&& e
.relatedTarget
.tagName
!= 'IFRAME')
937 // If e.relatedTarget is null, we've moused out of the document.
938 var container
= document
.querySelector('.pixel-zoom-container');
943 PixelZoomer
.handleMouseMove = function(e
) {
944 if (PixelZoomer
._mouseMoveTimeout
)
945 clearTimeout(PixelZoomer
._mouseMoveTimeout
);
947 if (parentOfType(e
.target
, '.pixel-zoom-container'))
950 var container
= document
.querySelector('.pixel-zoom-container');
952 var resultContainer
= (e
.target
.className
== 'result-container') ?
953 e
.target
: parentOfType(e
.target
, '.result-container');
954 if (!resultContainer
|| !resultContainer
.querySelector('img')) {
960 var targetLocation
= e
.target
.getBoundingClientRect();
961 PixelZoomer
._percentX
= (e
.clientX
- targetLocation
.left
) / targetLocation
.width
;
962 PixelZoomer
._percentY
= (e
.clientY
- targetLocation
.top
) / targetLocation
.height
;
965 if (PixelZoomer
.showOnDelay
) {
966 PixelZoomer
._mouseMoveTimeout
= setTimeout(function() {
967 PixelZoomer
._createContainer(e
);
972 PixelZoomer
._createContainer(e
);
976 PixelZoomer
._drawAll();
979 document
.addEventListener('mousemove', PixelZoomer
.handleMouseMove
, false);
980 document
.addEventListener('mouseout', PixelZoomer
.handleMouseOut
, false);
982 var TestNavigator
= {};
984 TestNavigator
.reset = function() {
985 TestNavigator
.currentTest
= null;
986 TestNavigator
.flaggedTests
= {};
987 TestNavigator
._createFlaggedTestContainer();
990 TestNavigator
.handleKeyEvent = function(event
)
992 if (event
.metaKey
|| event
.shiftKey
|| event
.ctrlKey
)
995 switch (String
.fromCharCode(event
.charCode
)) {
997 TestNavigator
._scrollToFirstTest();
1000 TestNavigator
._scrollToNextTest();
1003 TestNavigator
._scrollToPreviousTest();
1006 TestNavigator
._scrollToLastTest();
1009 TestNavigator
._expandCurrentTest();
1012 TestNavigator
._collapseCurrentTest();
1015 TestNavigator
._toggleCurrentTest();
1018 TestNavigator
._toggleCurrentTestFlagged();
1023 TestNavigator
._scrollToFirstTest = function()
1025 var links
= visibleTests();
1026 if (links
.length
== 0)
1028 if (TestNavigator
._setCurrentTest(links
[0]))
1029 TestNavigator
._scrollToCurrentTest();
1032 TestNavigator
._scrollToLastTest = function()
1034 var links
= visibleTests();
1035 if (links
.length
== 0)
1037 if (TestNavigator
._setCurrentTest(links
[links
.length
- 1]))
1038 TestNavigator
._scrollToCurrentTest();
1041 TestNavigator
._scrollToNextTest = function()
1043 if (!TestNavigator
.currentTest
) {
1044 TestNavigator
._scrollToFirstTest();
1047 var onlyUnexpected
= onlyShowUnexpectedFailures();
1048 for (var tbody
= TestNavigator
.currentTest
.nextElementSibling
; tbody
; tbody
= tbody
.nextElementSibling
) {
1049 if (tbody
.tagName
.toLowerCase() != 'tbody')
1051 if (onlyUnexpected
&& tbody
.classList
.contains('expected'))
1053 if (TestNavigator
._setCurrentTest(tbody
))
1054 TestNavigator
._scrollToCurrentTest();
1059 TestNavigator
._scrollToPreviousTest = function()
1061 if (!TestNavigator
.currentTest
) {
1062 TestNavigator
._scrollToLastTest();
1065 var onlyUnexpected
= onlyShowUnexpectedFailures();
1066 for (var tbody
= TestNavigator
.currentTest
.previousElementSibling
; tbody
; tbody
= tbody
.previousElementSibling
) {
1067 if (tbody
.tagName
.toLowerCase() != 'tbody')
1069 if (onlyUnexpected
&& tbody
.classList
.contains('expected'))
1071 if (TestNavigator
._setCurrentTest(tbody
))
1072 TestNavigator
._scrollToCurrentTest();
1077 TestNavigator
._currentTestExpandLink = function()
1079 return TestNavigator
.currentTest
.querySelector('.expand-button-text');
1082 TestNavigator
._expandCurrentTest = function()
1084 expandExpectations(TestNavigator
._currentTestExpandLink());
1087 TestNavigator
._collapseCurrentTest = function()
1089 collapseExpectations(TestNavigator
._currentTestExpandLink());
1092 TestNavigator
._toggleCurrentTest = function()
1094 toggleExpectations(TestNavigator
._currentTestExpandLink());
1097 TestNavigator
._toggleCurrentTestFlagged = function()
1099 var testLink
= TestNavigator
.currentTest
;
1100 TestNavigator
.flagTest(testLink
, !testLink
.classList
.contains('flagged'));
1101 TestNavigator
.updateFlaggedTestTextBox();
1104 // FIXME: Test navigator shouldn't know anything about flagging. It should probably call out to TestFlagger or something.
1105 TestNavigator
.flagTest = function(testTbody
, shouldFlag
)
1107 var testName
= testTbody
.getAttribute('data-testname');
1110 testTbody
.classList
.add('flagged');
1111 TestNavigator
.flaggedTests
[testName
] = 1;
1113 testTbody
.classList
.remove('flagged');
1114 delete TestNavigator
.flaggedTests
[testName
];
1118 TestNavigator
._createFlaggedTestContainer = function()
1120 var flaggedTestContainer
= document
.createElement('div');
1121 flaggedTestContainer
.id
= 'flagged-test-container';
1122 flaggedTestContainer
.innerHTML
= '<h2>Flagged Tests</h2>' +
1123 '<label title="Use newlines instead of spaces to separate flagged tests">' +
1124 '<input id="use-newlines" type=checkbox checked onchange="handleToggleUseNewlines()">Use newlines</input>' +
1126 '<pre id="flagged-tests" contentEditable></pre>';
1127 document
.body
.appendChild(flaggedTestContainer
);
1130 TestNavigator
.updateFlaggedTestTextBox = function()
1132 var flaggedTestTextbox
= document
.getElementById('flagged-tests');
1133 var flaggedTests
= Object
.keys(this.flaggedTests
);
1134 flaggedTests
.sort();
1135 var separator
= document
.getElementById('use-newlines').checked
? '\n' : ' ';
1136 flaggedTestTextbox
.innerHTML
= flaggedTests
.join(separator
);
1137 document
.getElementById('flagged-test-container').style
.display
= flaggedTests
.length
? '' : 'none';
1140 TestNavigator
._setCurrentTest = function(tbody
)
1142 if (TestNavigator
.currentTest
)
1143 TestNavigator
.currentTest
.classList
.remove('current');
1145 TestNavigator
.currentTest
= tbody
;
1146 tbody
.classList
.add('current');
1151 TestNavigator
._scrollToCurrentTest = function()
1153 var targetLink
= TestNavigator
.currentTest
;
1157 var rowRect
= targetLink
.getBoundingClientRect();
1158 var container
= document
.querySelector('.content-container');
1159 // rowRect is in client coords (i.e. relative to viewport), so we just want to add its top to the current scroll position.
1160 container
.scrollTop
+= rowRect
.top
- 20;
1163 TestNavigator
.onlyShowUnexpectedFailuresChanged = function()
1165 var currentTest
= document
.querySelector('.current');
1169 // If our currentTest became hidden, reset the currentTestIndex.
1170 if (onlyShowUnexpectedFailures() && currentTest
.classList
.contains('expected'))
1171 TestNavigator
._scrollToFirstTest();
1174 document
.addEventListener('keypress', TestNavigator
.handleKeyEvent
, false);
1177 function onlyShowUnexpectedFailures()
1179 return !document
.getElementById('show-expected-failures').checked
;
1182 function handleStderrChange()
1184 OptionWriter
.save();
1185 document
.getElementById('stderr-style').textContent
= document
.getElementById('show-stderr').checked
?
1186 '' : '#stderr-table { display: none; }';
1189 function handleUnexpectedPassesChange()
1191 OptionWriter
.save();
1192 document
.getElementById('unexpected-pass-style').textContent
= document
.getElementById('show-unexpected-passes').checked
?
1193 '' : '#passes-table { display: none; }';
1196 function handleFlakyFailuresChange()
1198 OptionWriter
.save();
1199 document
.getElementById('flaky-failures-style').textContent
= document
.getElementById('show-flaky-failures').checked
?
1200 '' : '.flaky { display: none; }';
1203 function handleUnexpectedResultsChange()
1205 OptionWriter
.save();
1206 updateExpectedFailures();
1209 function updateExpectedFailures()
1211 document
.getElementById('unexpected-style').textContent
= onlyShowUnexpectedFailures() ?
1212 '.expected { display: none; }' : '';
1214 updateTestlistCounts();
1215 TestNavigator
.onlyShowUnexpectedFailuresChanged();
1218 var OptionWriter
= {};
1220 OptionWriter
._key
= 'run-webkit-tests-options';
1222 OptionWriter
.save = function()
1224 var options
= document
.querySelectorAll('label input');
1226 for (var i
= 0, len
= options
.length
; i
< len
; i
++) {
1227 var option
= options
[i
];
1228 data
[option
.id
] = option
.checked
;
1230 localStorage
.setItem(OptionWriter
._key
, JSON
.stringify(data
));
1233 OptionWriter
.apply = function()
1235 var json
= localStorage
.getItem(OptionWriter
._key
);
1241 var data
= JSON
.parse(json
);
1242 for (var id
in data
) {
1243 var input
= document
.getElementById(id
);
1245 input
.checked
= data
[id
];
1250 function updateAllOptions()
1252 forEach(document
.querySelectorAll('input'), function(input
) { input
.onchange(); });
1255 function handleToggleUseNewlines()
1257 OptionWriter
.save();
1258 TestNavigator
.updateFlaggedTestTextBox();
1261 function handleToggleImagesChange()
1263 OptionWriter
.save();
1264 updateTogglingImages();
1267 function updateTogglingImages()
1269 var shouldToggle
= document
.getElementById('toggle-images').checked
;
1270 globalState().shouldToggleImages
= shouldToggle
;
1273 forEach(document
.querySelectorAll('table:not(#missing-table) tbody:not([mismatchreftest]) a[href$=".png"]'), convertToTogglingHandler(function(prefix
) {
1274 return resultLink(prefix
, '-diffs.html', 'images');
1276 forEach(document
.querySelectorAll('table:not(#missing-table) tbody:not([mismatchreftest]) img[src$=".png"]'), convertToTogglingHandler(togglingImage
));
1278 forEach(document
.querySelectorAll('a[href$="-diffs.html"]'), convertToNonTogglingHandler(resultLink
));
1279 forEach(document
.querySelectorAll('.animatedImage'), convertToNonTogglingHandler(function (absolutePrefix
, suffix
) {
1280 return resultIframe(absolutePrefix
+ suffix
);
1284 updateImageTogglingTimer();
1287 function getResultContainer(node
)
1289 return (node
.tagName
== 'IMG') ? parentOfType(node
, '.result-container') : node
;
1292 function convertToTogglingHandler(togglingImageFunction
)
1294 return function(node
) {
1295 var url
= (node
.tagName
== 'IMG') ? node
.src
: node
.href
;
1296 if (url
.match('-expected.png$'))
1297 remove(getResultContainer(node
));
1298 else if (url
.match('-actual.png$')) {
1299 var name
= parentOfType(node
, 'tbody').querySelector('.test-link').textContent
;
1300 getResultContainer(node
).outerHTML
= togglingImageFunction(stripExtension(name
));
1305 function convertToNonTogglingHandler(resultFunction
)
1307 return function(node
) {
1308 var prefix
= node
.getAttribute('data-prefix');
1309 getResultContainer(node
).outerHTML
= resultFunction(prefix
, '-expected.png', 'expected') + resultFunction(prefix
, '-actual.png', 'actual');
1313 function failingTestsTable(tests
, title
, id
)
1318 var numberofUnexpectedFailures
= 0;
1319 var tableRowHtml
= '';
1320 for (var i
= 0; i
< tests
.length
; i
++){
1321 tableRowHtml
+= tableRow(tests
[i
]);
1322 if (tests
[i
].is_unexpected
)
1323 numberofUnexpectedFailures
++;
1328 className
+= id
.split('-')[0];
1329 if (!hasUnexpected(tests
))
1330 className
+= ' expected';
1332 var header
= '<div';
1334 header
+= ' class="' + className
+ '"';
1336 header
+= '>' + testListHeaderHtml(title
) +
1337 '<table id="' + id
+ '"><thead><tr>' +
1339 '<th id="text-results-header">results</th>' +
1340 '<th id="image-results-header">image results</th>' +
1342 '<th>expected</th>';
1344 if (id
== 'flaky-tests-table')
1345 header
+= '<th>failures</th>';
1347 header
+= '</tr></thead>';
1350 return header
+ tableRowHtml
+ '</table></div>';
1353 function generatePage()
1355 forEachTest(processGlobalStateFor
);
1357 var html
= '<div class=content-container><div id=toolbar>' +
1358 '<div class="note">Use the i, j, k and l keys to navigate, e, c to expand and collapse, and f to flag</div>' +
1359 '<a href="dashboard.html" >Archived results </a>' +
1360 '<a href="javascript:void()" onclick="expandAllExpectations()">expand all</a> ' +
1361 '<a href="javascript:void()" onclick="collapseAllExpectations()">collapse all</a> ' +
1362 '<label><input id="toggle-images" type=checkbox checked onchange="handleToggleImagesChange()">Toggle images</label>' +
1363 '<div id=container>Show: '+
1364 '<label><input id="show-expected-failures" type=checkbox onchange="handleUnexpectedResultsChange()">expected failures</label>' +
1365 '<label><input id="show-flaky-failures" type=checkbox onchange="handleFlakyFailuresChange()">flaky failures</label>' +
1366 '<label><input id="show-unexpected-passes" type=checkbox onchange="handleUnexpectedPassesChange()">unexpected passes</label>' +
1367 '<label><input id="show-stderr" type=checkbox onchange="handleStderrChange()">stderr</label>' +
1370 if (globalState().results
.interrupted
)
1371 html
+= "<p class='stopped-running-early-message'>Testing exited early.</p>"
1373 if (globalState().crashTests
.length
)
1374 html
+= testList(globalState().crashTests
, 'Tests that crashed', 'crash-tests-table');
1376 if (globalState().leakTests
.length
)
1377 html
+= testList(globalState().leakTests
, 'Tests that leaked', 'leak-tests-table');
1379 html
+= failingTestsTable(globalState().failingTests
,
1380 'Tests that failed text/pixel/audio diff', 'results-table');
1382 html
+= failingTestsTable(globalState().missingResults
,
1383 'Tests that had no expected results (probably new)', 'missing-table');
1385 if (globalState().timeoutTests
.length
)
1386 html
+= testList(globalState().timeoutTests
, 'Tests that timed out', 'timeout-tests-table');
1388 if (globalState().testsWithStderr
.length
)
1389 html
+= testList(globalState().testsWithStderr
, 'Tests that had stderr output', 'stderr-table');
1391 html
+= failingTestsTable(globalState().flakyPassTests
,
1392 'Flaky tests (failed the first run and passed on retry)', 'flaky-tests-table');
1394 if (globalState().unexpectedPassTests
.length
)
1395 html
+= testList(globalState().unexpectedPassTests
, 'Tests expected to fail but passed', 'passes-table');
1397 if (globalState().hasHttpTests
) {
1398 html
+= '<p>httpd access log: <a href="access_log.txt">access_log.txt</a></p>' +
1399 '<p>httpd error log: <a href="error_log.txt">error_log.txt</a></p>';
1404 document
.body
.innerHTML
= html
;
1406 if (document
.getElementById('results-table')) {
1407 document
.getElementById('results-table').addEventListener('click', TableSorter
.handleClick
, false);
1408 TableSorter
.sortColumn(0);
1409 if (!globalState().hasTextFailures
)
1410 document
.getElementById('text-results-header').textContent
= '';
1411 if (!globalState().hasImageFailures
) {
1412 document
.getElementById('image-results-header').textContent
= '';
1413 parentOfType(document
.getElementById('toggle-images'), 'label').style
.display
= 'none';
1417 updateTestlistCounts();
1419 TestNavigator
.reset();
1420 OptionWriter
.apply();
1423 <!-- HACK: when json_results_test.js is included, loading this page runs the tests.
1424 It is not copied to the layout-test-results output directory. -->
1425 <script src=
"resources/results-test.js"></script>
1426 <body onload=
"generatePage()"></body>