1<!DocType html> 2<style> 3body { 4 margin: 4px; 5} 6 7body > p:first-of-type { 8 margin-top: 0; 9} 10 11tr:first-of-type:hover { 12 opacity: 0.7 13} 14 15thead, tbody { 16 background-color: #E3E9FF; 17} 18 19td { 20 padding: 0 4px; 21} 22 23th:empty, td:empty { 24 padding: 0; 25} 26 27th { 28 -webkit-user-select: none; 29 -moz-user-select: none; 30} 31 32label { 33 margin-left: 10px; 34} 35 36.results-row { 37 background-color: white; 38} 39 40.results-row iframe { 41 width: 800px; 42 height: 600px; 43} 44 45#options { 46 position: absolute; 47 top: 4px; 48 right: 4px; 49} 50 51.expand-button { 52 background-color: white; 53 color: blue; 54 width: 11px; 55 height: 11px; 56 border: 1px solid blue; 57 display: inline-block; 58 margin: 0 3px 0 0; 59 position: relative; 60} 61 62.expand-button-text { 63 position: absolute; 64 top: -0.3em; 65 left: 1px; 66} 67 68.result-container { 69 display: inline-block; 70 border: 1px solid gray; 71} 72 73.result-container iframe, .result-container img { 74 border: 0; 75 border-top: 1px solid lightgray; 76 vertical-align: top; 77} 78 79.label { 80 padding-left: 3px; 81 font-weight: bold; 82 font-size: small; 83} 84 85.pixel-zoom-container { 86 position: fixed; 87 top: 0; 88 left: 0; 89 width: 100%; 90 display: -webkit-box; 91} 92 93.pixel-zoom-container > * { 94 display: -webkit-box; 95 -webkit-box-flex: 1; 96 border: 1px inset lightgray; 97 height: 100px; 98 overflow: hidden; 99 zoom: 300%; 100 background-color: white; 101} 102 103.pixel-zoom-container img { 104 width: 800px; 105 height: 600px; 106 vertical-align: top; 107} 108</style> 109 110<script> 111var g_results; 112function ADD_RESULTS(input) 113{ 114 g_results = input; 115} 116</script> 117 118<script src="full_results.json"></script> 119 120<script> 121function stripExtension(test) 122{ 123 var index = test.lastIndexOf('.'); 124 return test.substring(0, index); 125} 126 127function parentOfType(node, selector) 128{ 129 while (node = node.parentElement) { 130 if (node.webkitMatchesSelector(selector)) 131 return node; 132 } 133 return null; 134} 135 136function appendResultIframe(src, parent) 137{ 138 // FIXME: use audio tags for AUDIO tests? 139 var layoutTestsIndex = src.indexOf('LayoutTests'); 140 var name; 141 if (layoutTestsIndex != -1) { 142 var hasTrac = src.indexOf('trac.webkit.org') != -1; 143 var prefix = hasTrac ? 'trac.webkit.org/.../' : ''; 144 name = prefix + src.substring(layoutTestsIndex + 'LayoutTests/'.length); 145 } else { 146 var lastDashIndex = src.lastIndexOf('-pretty'); 147 if (lastDashIndex == -1) 148 lastDashIndex = src.lastIndexOf('-'); 149 name = src.substring(lastDashIndex + 1); 150 } 151 152 var tagName = (src.lastIndexOf('.png') == -1) ? 'iframe' : 'img'; 153 154 var container = document.createElement('div'); 155 container.className = 'result-container'; 156 container.innerHTML = '<div class=label>' + name + '</div><' + tagName + ' src="' + src + '?format=txt"></' + tagName + '>'; 157 parent.appendChild(container); 158} 159 160function expandExpectations(e) 161{ 162 var expandLink = e.target; 163 if (expandLink.className != 'expand-button-text') 164 expandLink = expandLink.querySelector('.expand-button-text'); 165 166 var isExpand = expandLink.textContent == '+'; 167 var row = parentOfType(expandLink, 'tr'); 168 var parentTbody = row.parentNode; 169 var existingResultsRow = parentTbody.querySelector('.results-row'); 170 171 if (!isExpand) { 172 expandLink.textContent = '+'; 173 existingResultsRow.style.display = 'none'; 174 return; 175 } 176 177 var enDash = '\u2013'; 178 expandLink.textContent = enDash; 179 if (existingResultsRow) { 180 existingResultsRow.style.display = ''; 181 return; 182 } 183 184 var newRow = document.createElement('tr'); 185 newRow.className = 'results-row'; 186 var newCell = document.createElement('td'); 187 newCell.colSpan = row.querySelectorAll('td').length; 188 189 appendResultIframe(row.querySelector('.test-link').href, newCell); 190 191 var resultLinks = row.querySelectorAll('.result-link'); 192 for (var i = 0; i < resultLinks.length; i++) 193 appendResultIframe(resultLinks[i].href, newCell); 194 195 newRow.appendChild(newCell); 196 parentTbody.appendChild(newRow); 197} 198 199function testLink(test) 200{ 201 var basePath; 202 if (g_results.layout_tests_dir && location.toString().indexOf('file://') == 0) 203 basePath = g_results.layout_tests_dir + '/'; 204 else 205 basePath = 'http://trac.webkit.org/browser/trunk/LayoutTests/'; 206 return '<span class=expand-button onclick="expandExpectations(event)"><span class=expand-button-text>+</span></span>' + 207 '<a class=test-link href="' + basePath + test + '">' + test + '</a>'; 208} 209 210function resultLink(testPrefix, suffix, contents) 211{ 212 return '<a class=result-link href="' + testPrefix + suffix + '">' + contents + '</a> '; 213} 214 215var g_hasTextFailures = false; 216var g_hasImageFailures = false; 217 218var g_testsWithStderr = []; 219var g_newTests = []; 220var g_hasHttpTests = false; 221 222function tableRows() 223{ 224 var html = ''; 225 for (var test in g_results.tests) { 226 if (g_results.tests[test].has_stderr) 227 g_testsWithStderr.push(test); 228 229 g_hasHttpTests = g_hasHttpTests || test.indexOf('http/') == 0; 230 231 var actual = g_results.tests[test].actual; 232 if (actual == 'MISSING') { 233 // FIXME: make sure that new-run-webkit-tests spits out an -actual.txt file for 234 // tests with MISSING results. 235 g_newTests.push(test); 236 continue; 237 } 238 239 var expected = g_results.tests[test].expected || 'PASS'; 240 if (actual == 'PASS' && (!g_results.uses_expectations_file || expected == 'PASS')) 241 continue; 242 243 // FIXME: put unexpected passes in a separate table. 244 245 var row = '<td>' + testLink(test) + '</td>'; 246 var test_prefix = stripExtension(test); 247 248 row += '<td>'; 249 if (actual == 'CRASH') 250 row += resultLink(test_prefix, '-stack.txt', 'stack'); 251 else if (actual == 'AUDIO') { 252 row += resultLink(test_prefix, '-expected.wav', 'expected'); 253 row += resultLink(test_prefix, '-actual.wav', 'actual'); 254 } else if (actual.indexOf('TEXT') != -1 || actual == 'TIMEOUT') { 255 // FIXME: only include timeout actual/expected results here if we actually spit out results for timeout tests. 256 g_hasTextFailures = true; 257 row += resultLink(test_prefix, '-expected.txt', 'expected') + 258 resultLink(test_prefix, '-actual.txt', 'actual') + 259 resultLink(test_prefix, '-diff.txt', 'diff'); 260 261 if (g_results.has_pretty_patch) 262 row += resultLink(test_prefix, '-pretty-diff.html', 'pretty diff'); 263 264 if (g_results.has_wdiff) 265 row += resultLink(test_prefix, '-wdiff.html', 'wdiff'); 266 } 267 268 row += '</td><td>'; 269 270 if (actual.indexOf('IMAGE') != -1) { 271 g_hasImageFailures = true; 272 273 if (g_results.tests[test].is_mismatch_reftest) { 274 row += resultLink(test_prefix, '-expected-mismatch.html', 'ref mismatch html') + 275 resultLink(test_prefix, '-actual.png', 'actual'); 276 } else { 277 if (g_results.tests[test].is_reftest) 278 row += resultLink(test_prefix, '-expected.html', 'ref html'); 279 280 row += resultLink(test_prefix, '-expected.png', 'expected') + 281 resultLink(test_prefix, '-actual.png', 'actual') + 282 resultLink(test_prefix, '-diff.png', 'diff'); 283 } 284 } 285 286 row += '</td>'; 287 row += '<td>' + actual + '</td>'; 288 289 if (g_results.uses_expectations_file) 290 row += '<td>' + expected + '</td>'; 291 292 var isExpected = actual == 'SKIP'; 293 if (!isExpected && g_results.uses_expectations_file) { 294 var expectedArray = expected.split(' '); 295 if (expectedArray.indexOf(actual) != -1) 296 isExpected = true; 297 else if (expectedArray.indexOf('FAIL') != -1) 298 isExpected = actual == 'IMAGE' || actual == 'TEXT' || actual == 'IMAGE+TEXT'; 299 } 300 html += '<tbody class="' + (isExpected ? 'expected' : '') + '"><tr>' + row + '</tr></tbody>'; 301 } 302 return html; 303} 304 305var html = ''; 306if (g_results.uses_expectations_file) 307 html += '<div id=options><label><input class="unexpected-results" type=checkbox checked>Only show unexpected results</label></div>'; 308 309var tableRowsHtml = tableRows(); 310 311if (tableRowsHtml) { 312 html += '<p>Tests where results did not match expected results:</p>' + 313 '<table id="results-table"><thead><tr>' + 314 '<th>test</th>' + 315 '<th id="text-results-header">text results</th>' + 316 '<th id="image-results-header">image results</th>' + 317 '<th>failure type</th>'; 318 319 if (g_results.uses_expectations_file) 320 html += '<th>expected failure type</th>'; 321 322 html += '</tr></thead>' + tableRowsHtml + '</table>'; 323} 324 325function appendTestList(tests, header, tableId, fileSuffix, linkName) 326{ 327 tests.sort(); 328 329 html += '<p>' + header + '</p><table id="' + tableId + '">'; 330 for (var i = 0; i < tests.length; i++) { 331 var test = tests[i]; 332 html += '<tbody><tr><td>' + testLink(test) + '</td><td>'; 333 334 if (fileSuffix.indexOf('actual') == -1) 335 html += resultLink(stripExtension(test), fileSuffix, linkName); 336 else { 337 var testObject = g_results.tests[test]; 338 if (testObject.is_missing_audio) 339 html += resultLink(stripExtension(test), '-actual.wav', 'audio result'); 340 if (testObject.is_missing_text) 341 html += resultLink(stripExtension(test), fileSuffix, linkName); 342 if (testObject.is_missing_image) 343 html += resultLink(stripExtension(test), '-actual.png', 'png result'); 344 } 345 346 html += '</td></tr></tbody>'; 347 } 348 html += '</table>' 349} 350 351if (g_newTests.length) 352 appendTestList(g_newTests, 'Tests that had no expected results (probably new):', 'new-tests-table', '-actual.txt', 'result'); 353 354if (g_testsWithStderr.length) 355 appendTestList(g_testsWithStderr, 'Tests that had stderr output:', 'stderr-table', '-stderr.txt', 'stderr'); 356 357if (g_hasHttpTests) { 358 html += '<p>httpd access log: <a href="access_log.txt">access_log.txt</a></p>' + 359 '<p>httpd error log: <a href="error_log.txt">error_log.txt</a></p>'; 360} 361 362document.write(html); 363 364function toArray(nodeList) 365{ 366 return Array.prototype.slice.call(nodeList); 367} 368 369function trim(string) 370{ 371 return string.replace(/^[\s\xa0]+|[\s\xa0]+$/g, ''); 372} 373 374// Just a namespace for code management. 375var TableSorter = {}; 376 377TableSorter._forwardArrow = '<svg style="width:10px;height:10px"><polygon points="0,0 10,0 5,10" style="fill:#aaa"></svg>'; 378 379TableSorter._backwardArrow = '<svg style="width:10px;height:10px"><polygon points="0,10 10,10 5,0" style="fill:#aaa"></svg>'; 380 381TableSorter._sortedContents = function(header, arrow) 382{ 383 return arrow + ' ' + trim(header.textContent) + ' ' + arrow; 384} 385 386TableSorter._updateHeaderClassNames = function(newHeader) 387{ 388 var sortHeader = document.querySelector('.sortHeader'); 389 if (sortHeader) { 390 if (sortHeader == newHeader) { 391 var isAlreadyReversed = sortHeader.classList.contains('reversed'); 392 if (isAlreadyReversed) 393 sortHeader.classList.remove('reversed'); 394 else 395 sortHeader.classList.add('reversed'); 396 } else { 397 sortHeader.textContent = sortHeader.textContent; 398 sortHeader.classList.remove('sortHeader'); 399 sortHeader.classList.remove('reversed'); 400 } 401 } 402 403 newHeader.classList.add('sortHeader'); 404} 405 406TableSorter._textContent = function(tbodyRow, column) 407{ 408 return tbodyRow.querySelectorAll('td')[column].textContent; 409} 410 411TableSorter._sortRows = function(newHeader, reversed) 412{ 413 var testsTable = document.getElementById('results-table'); 414 var headers = toArray(testsTable.querySelectorAll('th')); 415 var sortColumn = headers.indexOf(newHeader); 416 417 var rows = toArray(testsTable.querySelectorAll('tbody')); 418 419 rows.sort(function(a, b) { 420 // Only need to support lexicographic sort for now. 421 var aText = TableSorter._textContent(a, sortColumn); 422 var bText = TableSorter._textContent(b, sortColumn); 423 424 // Forward sort equal values by test name. 425 if (sortColumn && aText == bText) { 426 var aTestName = TableSorter._textContent(a, 0); 427 var bTestName = TableSorter._textContent(b, 0); 428 if (aTestName == bTestName) 429 return 0; 430 return aTestName < bTestName ? -1 : 1; 431 } 432 433 if (reversed) 434 return aText < bText ? 1 : -1; 435 else 436 return aText < bText ? -1 : 1; 437 }); 438 439 for (var i = 0; i < rows.length; i++) 440 testsTable.appendChild(rows[i]); 441} 442 443TableSorter.sortColumn = function(columnNumber) 444{ 445 var newHeader = document.getElementById('results-table').querySelectorAll('th')[columnNumber]; 446 TableSorter._sort(newHeader); 447} 448 449TableSorter.handleClick = function(e) 450{ 451 var newHeader = e.target; 452 if (newHeader.localName != 'th') 453 return; 454 TableSorter._sort(newHeader); 455} 456 457TableSorter._sort = function(newHeader) 458{ 459 TableSorter._updateHeaderClassNames(newHeader); 460 461 var reversed = newHeader.classList.contains('reversed'); 462 var sortArrow = reversed ? TableSorter._backwardArrow : TableSorter._forwardArrow; 463 newHeader.innerHTML = TableSorter._sortedContents(newHeader, sortArrow); 464 465 TableSorter._sortRows(newHeader, reversed); 466} 467 468if (document.getElementById('results-table')) 469 document.getElementById('results-table').addEventListener('click', TableSorter.handleClick, false); 470TableSorter.sortColumn(0); 471 472var PixelZoomer = {}; 473 474PixelZoomer._createContainer = function(e) 475{ 476 var tbody = parentOfType(e.target, 'tbody'); 477 var imageDiffLinks = tbody.querySelector('tr').querySelectorAll('a[href$=".png"]'); 478 479 var container = document.createElement('div'); 480 container.className = 'pixel-zoom-container'; 481 482 var html = ''; 483 for (var i = 0; i < imageDiffLinks.length; i++) 484 html += '<div class=zoom-image-container><img src="' + imageDiffLinks[i].href + '"></div>'; 485 486 container.innerHTML = html; 487 document.body.appendChild(container); 488 489 PixelZoomer._position(e); 490} 491 492PixelZoomer._position = function(e) 493{ 494 var pageX = e.clientX; 495 var pageY = e.clientY; 496 var targetLocation = e.target.getBoundingClientRect(); 497 var x = pageX - targetLocation.left; 498 var y = pageY - targetLocation.top; 499 500 var zoomContainers = document.querySelectorAll('.pixel-zoom-container > .zoom-image-container'); 501 for (var i = 0; i < zoomContainers.length; i++) { 502 var container = zoomContainers[i]; 503 container.scrollLeft = x - container.offsetWidth / 2; 504 container.scrollTop = y - container.offsetHeight / 2; 505 } 506} 507 508PixelZoomer.handleMouseMove = function(e) { 509 if (PixelZoomer._mouseMoveTimeout) 510 clearTimeout(PixelZoomer._mouseMoveTimeout); 511 512 if (parentOfType(e.target, '.pixel-zoom-container')) 513 return; 514 515 var container = document.querySelector('.pixel-zoom-container'); 516 if (!e.target.src || e.target.src.indexOf('.png') == -1) { 517 if (container) 518 container.parentNode.removeChild(container); 519 return; 520 } 521 522 if (!container) { 523 PixelZoomer._mouseMoveTimeout = setTimeout(function() { 524 PixelZoomer._createContainer(e); 525 }, 200); 526 return; 527 } 528 529 PixelZoomer._position(e); 530} 531 532document.body.addEventListener('mousemove', PixelZoomer.handleMouseMove, false); 533 534 535var unexpectedStyleNode = document.createElement('style'); 536document.body.appendChild(unexpectedStyleNode); 537 538function updateExpectedResults() 539{ 540 var checkBox = document.querySelector('.unexpected-results'); 541 if (!checkBox || checkBox.checked) 542 unexpectedStyleNode.innerText = '.expected { display: none; }'; 543 else 544 unexpectedStyleNode.innerText = ''; 545} 546 547updateExpectedResults(); 548if (document.querySelector('.unexpected-results')) 549 document.querySelector('.unexpected-results').addEventListener('change', updateExpectedResults, false); 550 551if (!g_hasTextFailures) 552 document.body.getElementById('text-results-header').textContent = ''; 553if (!g_hasImageFailures) 554 document.body.getElementById('image-results-header').textContent = ''; 555</script> 556