OLD | NEW |
---|---|
1 <!DOCTYPE HTML> | 1 <!DOCTYPE HTML> |
2 <html> | 2 <html> |
3 | 3 |
4 <head> | 4 <head> |
5 <title>Webkit Layout Test History</title> | 5 <title>Webkit Layout Test History</title> |
6 <style> | 6 <style> |
7 body { | 7 body { |
8 font-family: arial; | 8 font-family: arial; |
9 font-size: 13px; | 9 font-size: 13px; |
10 } | 10 } |
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
74 .results { | 74 .results { |
75 cursor: pointer; | 75 cursor: pointer; |
76 padding: 0; | 76 padding: 0; |
77 font-size: 10px; | 77 font-size: 10px; |
78 text-align: center; | 78 text-align: center; |
79 } | 79 } |
80 #legend { | 80 #legend { |
81 position: fixed; | 81 position: fixed; |
82 top: 5px; | 82 top: 5px; |
83 right: 5px; | 83 right: 5px; |
84 width: 130px; | 84 width: 200px; |
85 padding: 2px; | |
85 border: 2px solid grey; | 86 border: 2px solid grey; |
86 background-color: white; | 87 background-color: white; |
87 } | 88 } |
88 #legend-contents * { | 89 #legend-contents * { |
89 margin: 3px; | 90 margin: 3px 0; |
90 padding: 0 2px; | 91 padding: 0 2px; |
91 } | 92 } |
92 body > div > :not(#legend) { | |
93 margin-right: 145px; | |
94 } | |
95 #builders * { | 93 #builders * { |
96 margin: 0 5px; | 94 margin: 0 5px; |
97 display: inline-block; | 95 display: inline-block; |
98 white-space: nowrap; | 96 white-space: nowrap; |
99 } | 97 } |
100 .test-table .wrong-expectations, | 98 .test-table .wrong-expectations, |
101 .wrong-expectations { | 99 .wrong-expectations { |
102 background-color: #pink; | 100 background-color: #pink; |
103 } | 101 } |
104 .P { | 102 .P { |
(...skipping 16 matching lines...) Expand all Loading... | |
121 } | 119 } |
122 .F { | 120 .F { |
123 background-color: #e98080; | 121 background-color: #e98080; |
124 } | 122 } |
125 .O { | 123 .O { |
126 background-color: #69f; | 124 background-color: #69f; |
127 } | 125 } |
128 .merge { | 126 .merge { |
129 background-color: grey; | 127 background-color: grey; |
130 } | 128 } |
131 :not(#legend-contents) > .merge { | 129 table .merge { |
132 width: 1px; | 130 width: 1px; |
133 } | 131 } |
134 .separator { | 132 .separator { |
135 border: 1px solid lightgray; | 133 border: 1px solid lightgray; |
136 height: 0px; | 134 height: 0px; |
137 } | 135 } |
138 .different-platform { | 136 .different-platform { |
139 color: gray; | 137 color: gray; |
140 font-size: 10px; | 138 font-size: 10px; |
141 } | 139 } |
(...skipping 30 matching lines...) Expand all Loading... | |
172 padding: 3px; | 170 padding: 3px; |
173 -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.5); | 171 -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.5); |
174 -moz-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.5); | 172 -moz-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.5); |
175 -webkit-border-radius: 5px; | 173 -webkit-border-radius: 5px; |
176 -moz-border-radius: 5px; | 174 -moz-border-radius: 5px; |
177 } | 175 } |
178 #popup > ul { | 176 #popup > ul { |
179 margin: 0; | 177 margin: 0; |
180 padding-left: 20px; | 178 padding-left: 20px; |
181 } | 179 } |
180 .expectations-container { | |
181 clear: both; | |
182 } | |
183 .expectations-item { | |
184 float: left; | |
185 border: 1px solid grey; | |
186 } | |
187 .expectations-item .expectation { | |
188 width: 400px; | |
189 height: 300px; | |
190 border: 0; | |
191 border-top: 1px solid grey; | |
192 } | |
193 .expectations-item .large { | |
194 width: 800px; | |
195 height: 600px; | |
196 } | |
197 .expectations-item .checksum { | |
198 height: 30px; | |
199 } | |
200 .fallback-list { | |
201 margin-top: 0; | |
202 } | |
203 .used-platform { | |
204 float: right; | |
205 color: darkblue; | |
206 margin: 0 5px; | |
207 } | |
208 .expectations-title { | |
209 /* Hack to make a containing block for absolute positioned elements. */ | |
210 position: relative; | |
211 overflow: hidden; | |
212 } | |
213 .title { | |
214 /* Position absolutely so the container does not grow to contain this. */ | |
215 position: absolute; | |
216 } | |
217 .platforms { | |
218 position: absolute; | |
219 background-color: white; | |
220 right: 0; | |
221 z-index: 1; | |
222 } | |
182 </style> | 223 </style> |
183 | 224 |
184 <script src="dashboards/dashboard_base.js"></script> | 225 <script src="dashboards/dashboard_base.js"></script> |
185 <script> | 226 <script> |
186 /** | 227 /** |
187 * @fileoverview Creates a dashboard for multiple runs of a given set of tests | 228 * @fileoverview Creates a dashboard for multiple runs of a given set of tests |
188 * on the buildbots. Pulls in JSONP-ish files with the results for running | 229 * on the buildbots. Pulls in JSONP-ish files with the results for running |
189 * tests on a given builder (i.e. ADD_RESULTS(json_here)) and the expectations | 230 * tests on a given builder (i.e. ADD_RESULTS(json_here)) and the expectations |
190 * for all tests on all builders (i.e. ADD_EXPECTATIONS(json_here)). | 231 * for all tests on all builders (i.e. ADD_EXPECTATIONS(json_here)). |
191 * | 232 * |
192 * This shows flakiness of the tests as well as runtimes for slow tests. | 233 * This shows flakiness of the tests as well as runtimes for slow tests. |
193 * | 234 * |
194 * Also, each column in the dashboard is sortable. | 235 * Also, each column in the dashboard is sortable. |
195 * | 236 * |
196 * Currently, only webkit tests are supported, but adding other test types | 237 * Currently, only webkit tests are supported, but adding other test types |
197 * should just require the following steps: | 238 * should just require the following steps: |
198 * -generate results.json and expectations.json for these tests | 239 * -generate results.json and expectations.json for these tests |
199 * -copy them to the appropriate location | 240 * -copy them to the appropriate location |
200 * -add the builder name to the list of builders below. | 241 * -add the builder name to the list of builders below. |
201 */ | 242 */ |
202 | 243 |
203 ////////////////////////////////////////////////////////////////////////////// | 244 ////////////////////////////////////////////////////////////////////////////// |
204 // CONSTANTS | 245 // CONSTANTS |
205 ////////////////////////////////////////////////////////////////////////////// | 246 ////////////////////////////////////////////////////////////////////////////// |
206 var ALL = 'ALL'; | 247 var ALL = 'ALL'; |
207 var FORWARD = 'forward'; | 248 var FORWARD = 'forward'; |
208 var BACKWARD = 'backward'; | 249 var BACKWARD = 'backward'; |
250 var LAYOUT_TESTS_PREFIX = 'LayoutTests/'; | |
251 var CHROME_TEST_BASE_URL = 'http://src.chromium.org/viewvc/chrome/trunk/' + | |
252 'src/webkit/data/layout_tests/platform/'; | |
209 var TEST_URL_BASE_PATH = | 253 var TEST_URL_BASE_PATH = |
210 'http://trac.webkit.org/projects/webkit/browser/trunk/'; | 254 'http://trac.webkit.org/projects/webkit/browser/trunk/'; |
211 var BUILDERS_BASE_PATH = | 255 var BUILDERS_BASE_PATH = |
212 'http://build.chromium.org/buildbot/waterfall/builders/'; | 256 'http://build.chromium.org/buildbot/waterfall/builders/'; |
213 var TEST_RESULTS_BASE_PATH = | 257 var TEST_RESULTS_BASE_PATH = |
214 'http://build.chromium.org/buildbot/layout_test_results/'; | 258 'http://build.chromium.org/buildbot/layout_test_results/'; |
215 var PLATFORMS = { | 259 var PLATFORMS = { |
216 'MAC': 'MAC', | 260 'MAC': 'MAC', |
217 'LINUX': 'LINUX', | 261 'LINUX': 'LINUX', |
218 'WIN': 'WIN', | 262 'WIN': 'WIN', |
(...skipping 25 matching lines...) Expand all Loading... | |
244 | 288 |
245 if (currentState.tests) { | 289 if (currentState.tests) { |
246 createTableHeadersArray('builder'); | 290 createTableHeadersArray('builder'); |
247 generatePageForIndividualTests(getIndividualTests()); | 291 generatePageForIndividualTests(getIndividualTests()); |
248 } else { | 292 } else { |
249 createTableHeadersArray('test'); | 293 createTableHeadersArray('test'); |
250 generatePageForBuilder(currentState.builder); | 294 generatePageForBuilder(currentState.builder); |
251 } | 295 } |
252 | 296 |
253 $('max-results-input').value = currentState.maxResults; | 297 $('max-results-input').value = currentState.maxResults; |
254 updateLegendDisplay(); | |
255 | 298 |
256 for (var builder in builders) { | 299 for (var builder in builders) { |
257 processTestResultsForBuilderAsync(builder); | 300 processTestResultsForBuilderAsync(builder); |
258 } | 301 } |
259 } | 302 } |
260 | 303 |
261 function handleValidHashParameter(key, value) { | 304 function handleValidHashParameter(key, value) { |
262 switch(key) { | 305 switch(key) { |
263 case 'tests': | 306 case 'tests': |
264 validateParameter(currentState, key, value, | 307 validateParameter(currentState, key, value, |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
298 return true; | 341 return true; |
299 | 342 |
300 case 'maxResults': | 343 case 'maxResults': |
301 validateParameter(currentState, key, value, | 344 validateParameter(currentState, key, value, |
302 function() { | 345 function() { |
303 return value.match(/^\d+$/) | 346 return value.match(/^\d+$/) |
304 }); | 347 }); |
305 | 348 |
306 return true; | 349 return true; |
307 | 350 |
351 case 'showCorrectExpectations': | |
352 case 'showExpectations': | |
353 case 'showFlaky': | |
354 case 'showLargeExpectations': | |
355 case 'showSkipped': | |
308 case 'showWontFix': | 356 case 'showWontFix': |
309 case 'showCorrectExpectations': | |
310 case 'showFlaky': | |
311 case 'showLegend': | |
312 case 'showSkipped': | |
313 currentState[key] = value == 'true'; | 357 currentState[key] = value == 'true'; |
314 | 358 |
315 return true; | 359 return true; |
316 | 360 |
317 default: | 361 default: |
318 return false; | 362 return false; |
319 } | 363 } |
320 } | 364 } |
321 | 365 |
322 defaultStateValues = { | 366 defaultStateValues = { |
323 sortOrder: BACKWARD, | 367 sortOrder: BACKWARD, |
324 sortColumn: 'flakiness', | 368 sortColumn: 'flakiness', |
369 showCorrectExpectations: false, | |
370 showExpectations: false, | |
371 showFlaky: true, | |
372 showLargeExpectations: false, | |
325 showWontFix: false, | 373 showWontFix: false, |
326 showCorrectExpectations: false, | |
327 showLegend: true, | |
328 showFlaky: true, | |
329 showSkipped: false, | 374 showSkipped: false, |
330 maxResults: 200, | 375 maxResults: 200 |
331 }; | 376 }; |
332 | 377 |
333 ////////////////////////////////////////////////////////////////////////////// | 378 ////////////////////////////////////////////////////////////////////////////// |
334 // GLOBALS | 379 // GLOBALS |
335 ////////////////////////////////////////////////////////////////////////////// | 380 ////////////////////////////////////////////////////////////////////////////// |
336 | 381 |
337 // Text to put inside the header for each column of the test table. | 382 // Text to put inside the header for each column of the test table. |
338 var tableHeaders; | 383 var tableHeaders; |
339 var perBuilderPlatformAndBuildType = {}; | 384 var perBuilderPlatformAndBuildType = {}; |
340 var perBuilderFailures = {}; | 385 var perBuilderFailures = {}; |
(...skipping 546 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
887 var buildNumbers = resultsByBuilder[builder].buildNumbers; | 932 var buildNumbers = resultsByBuilder[builder].buildNumbers; |
888 html += '<li>' + | 933 html += '<li>' + |
889 getLinkHTMLToOpenWindow(BUILDERS_BASE_PATH + builder + '/builds/' + | 934 getLinkHTMLToOpenWindow(BUILDERS_BASE_PATH + builder + '/builds/' + |
890 buildNumbers[index], 'Build log and blamelist') + '</li></ul>'; | 935 buildNumbers[index], 'Build log and blamelist') + '</li></ul>'; |
891 | 936 |
892 showPopup(e, html); | 937 showPopup(e, html); |
893 } | 938 } |
894 | 939 |
895 function showPopupForTest(e, test) { | 940 function showPopupForTest(e, test) { |
896 showPopup(e, getHTMLForIndividulTestOnAllBuilders(test)); | 941 showPopup(e, getHTMLForIndividulTestOnAllBuilders(test)); |
942 appendExpectations(); | |
897 } | 943 } |
898 | 944 |
899 function getHtmlForTestResults(test, builder) { | 945 function getHtmlForTestResults(test, builder) { |
900 var html = ''; | 946 var html = ''; |
901 var results = test.rawResults.concat(); | 947 var results = test.rawResults.concat(); |
902 var times = test.rawTimes.concat(); | 948 var times = test.rawTimes.concat(); |
903 var buildNumbers = resultsByBuilder[builder].buildNumbers; | 949 var buildNumbers = resultsByBuilder[builder].buildNumbers; |
904 | 950 |
905 var indexToReplaceCurrentResult = -1; | 951 var indexToReplaceCurrentResult = -1; |
906 var indexToReplaceCurrentTime = -1; | 952 var indexToReplaceCurrentTime = -1; |
(...skipping 272 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1179 } | 1225 } |
1180 | 1226 |
1181 tests.sort(sortFunctionGetter(resultsProperty, order == BACKWARD)); | 1227 tests.sort(sortFunctionGetter(resultsProperty, order == BACKWARD)); |
1182 } | 1228 } |
1183 | 1229 |
1184 function getHTMLForIndividulTestOnAllBuilders(test) { | 1230 function getHTMLForIndividulTestOnAllBuilders(test) { |
1185 for (var builder in builders) | 1231 for (var builder in builders) |
1186 processTestRunsForBuilder(builder); | 1232 processTestRunsForBuilder(builder); |
1187 | 1233 |
1188 var testResults = testToResultsMap[test]; | 1234 var testResults = testToResultsMap[test]; |
1235 var html = ''; | |
1189 if (testResults && testResults.length) { | 1236 if (testResults && testResults.length) { |
1190 var tracURL = TEST_URL_BASE_PATH + test | 1237 var tracURL = TEST_URL_BASE_PATH + test |
1191 var html = getLinkHTMLToOpenWindow(tracURL, tracURL) + | 1238 html += getLinkHTMLToOpenWindow(tracURL, tracURL) + |
1192 '<div><b>If a builder is not listed, that means the builder does ' + | 1239 '<div><b>If a builder is not listed, that means the builder does ' + |
1193 'run that test or all runs of the test passed.</b></div>'; | 1240 'run that test or all runs of the test passed.</b></div>'; |
1194 | 1241 |
1195 for (var j = 0; j < testResults.length; j++) { | 1242 for (var j = 0; j < testResults.length; j++) { |
1196 html += getHTMLForSingleTestRow(testResults[j].results, | 1243 html += getHTMLForSingleTestRow(testResults[j].results, |
1197 testResults[j].builder, true); | 1244 testResults[j].builder, true); |
1198 } | 1245 } |
1199 return getHTMLForTestTable(html); | 1246 html = getHTMLForTestTable(html); |
1200 } else { | 1247 } else { |
1201 var html = ''; | |
1202 if (expectationsByTest[test]) { | 1248 if (expectationsByTest[test]) { |
1203 for (var i = 0; i < expectationsByTest[test].length; i++) { | 1249 for (var i = 0; i < expectationsByTest[test].length; i++) { |
1204 html += '<div>' + expectationsByTest[test][i].modifiers + ' | ' + | 1250 html += '<div>' + expectationsByTest[test][i].modifiers + ' | ' + |
1205 expectationsByTest[test][i].expectations + '</div>'; | 1251 expectationsByTest[test][i].expectations + '</div>'; |
1206 } | 1252 } |
1207 } | 1253 } |
1208 return html + '<div class="not-found">Test not found. Either it does ' + | 1254 html += '<div class="not-found">Test not found. Either it does ' + |
1209 'not exist, is skipped or passes on all platforms.</div>'; | 1255 'not exist, is skipped or passes on all platforms.</div>'; |
1210 } | 1256 } |
1257 return html + '<div class=expectations test=' + test + '><div>' + | |
1258 getLinkHTMLToToggleState('showExpectations', 'expectations') + ' | ' + | |
1259 getLinkHTMLToToggleState('showLargeExpectations', 'large thumbnails') + | |
1260 '</div></div>'; | |
1261 } | |
1262 | |
1263 function getExpectationsContainer(expectationsContainers, parentContainer, | |
1264 expectationsType) { | |
1265 if (!expectationsContainers[expectationsType]) { | |
1266 var container = document.createElement('div'); | |
1267 container.className = 'expectations-container'; | |
1268 parentContainer.appendChild(container); | |
1269 expectationsContainers[expectationsType] = container; | |
1270 } | |
1271 return expectationsContainers[expectationsType]; | |
1272 } | |
1273 | |
1274 function getExtension(path) { | |
1275 var parts = path.split('.') | |
1276 var extension = parts[parts.length - 1]; | |
1277 return extension == 'html' ? 'txt' : extension; | |
1278 } | |
1279 | |
1280 function ensureTrailingSlash(path) { | |
1281 if (path.match(/\/$/)) | |
1282 return path; | |
1283 return path + '/'; | |
1284 } | |
1285 | |
1286 /** | |
1287 * Adds a specific expectation. If it's an image, it's only added on the | |
1288 * image's onload handler. If it's a text file, then a script tag is appended | |
1289 * as a hack to see if the file 404s (necessary since it's cross-domain). | |
1290 * Once all the expectations for a specific type have loaded or errored | |
1291 * (e.g. all the checksums), then we go through and identify which platform | |
1292 * uses which expectation. | |
1293 * | |
1294 * @param {Object} expectationsContainers Map from expectations type to | |
1295 * container DIV. | |
1296 * @param {Element} parentContainer Container element for | |
1297 * expectationsContainer divs. | |
1298 * @param {string} platform Platform string. "LayoutTests/" for non-platform | |
1299 * specific expectations. | |
1300 * @param {string} path Relative path to the expectation. | |
1301 * @param {string} base Base path for the expectation URL. | |
1302 * @param {string} opt_suffix Suffix to place at the end of the path. | |
1303 */ | |
1304 function addExpectationItem(expectationsContainers, parentContainer, platform, | |
1305 path, base, opt_suffix) { | |
1306 var fileExtension = getExtension(path); | |
1307 var container = getExpectationsContainer(expectationsContainers, | |
1308 parentContainer, fileExtension); | |
1309 var isImage = path.match(/\.png$/); | |
1310 | |
1311 // TODO(ojan): Is there any way to do this that doesn't rely on script | |
1312 // tags? They spew a lot of errors to the console. | |
1313 var dummyNode = document.createElement(isImage ? 'img' : 'script'); | |
1314 var suffix = opt_suffix || ''; | |
1315 var platformPart = platform ? ensureTrailingSlash(platform) : ''; | |
1316 dummyNode.src = base + platformPart + path + suffix; | |
1317 | |
1318 var childContainer = document.createElement('span'); | |
1319 childContainer.className = 'unloaded'; | |
1320 | |
1321 dummyNode.onload = function() { | |
1322 childContainer.appendChild(getExpectationsTitle(platform, path)); | |
1323 childContainer.className = 'expectations-item'; | |
1324 | |
1325 var item; | |
1326 if (isImage) { | |
1327 item = dummyNode; | |
1328 } else { | |
1329 item = document.createElement('iframe'); | |
1330 item.src = dummyNode.src; | |
1331 } | |
1332 | |
1333 item.className = 'expectation ' + fileExtension; | |
1334 if (currentState.showLargeExpectations) | |
1335 item.className += ' large'; | |
1336 childContainer.appendChild(item); | |
1337 handleFinishedLoadingExpectations(container); | |
1338 } | |
1339 dummyNode.onerror = function() { | |
1340 childContainer.parentNode.removeChild(childContainer); | |
1341 handleFinishedLoadingExpectations(container); | |
1342 } | |
1343 | |
1344 // Append script elements now so that they load. Images load without being | |
1345 // appended to the DOM. | |
1346 if (!isImage) { | |
1347 childContainer.appendChild(dummyNode); | |
1348 } | |
1349 | |
1350 container.appendChild(childContainer); | |
1351 } | |
1352 | |
1353 /** | |
1354 * Identifies which expectations are used on which platform once all the | |
1355 * expectations of a given type have loaded (e.g. the container for checksum | |
1356 * expectations for this test had no child elements with the class | |
1357 * "unloaded"). | |
1358 * | |
1359 * @param {string} container Element containing the expectations for a given | |
1360 * test and a given type (e.g. checksum). | |
1361 */ | |
1362 function handleFinishedLoadingExpectations(container) { | |
1363 if (container.getElementsByClassName('unloaded').length) | |
1364 return; | |
1365 | |
1366 var titles = container.getElementsByClassName('expectations-title'); | |
1367 for (var platform in fallbacksMap) { | |
1368 var fallbacks = fallbacksMap[platform]; | |
1369 var winner = null; | |
1370 var winningIndex = -1; | |
1371 for (var i = 0; i < titles.length; i++) { | |
1372 var title = titles[i]; | |
1373 | |
1374 if (!winner && title.platform == LAYOUT_TESTS_PREFIX) { | |
1375 winner = title; | |
1376 continue; | |
1377 } | |
1378 | |
1379 for (var j = 0; j < fallbacks.length; j++) { | |
1380 if ((winningIndex == -1 || winningIndex > j) && | |
1381 title.platform == fallbacks[j]) { | |
1382 winningIndex = j; | |
1383 winner = title; | |
1384 break; | |
1385 } | |
1386 } | |
1387 } | |
1388 if (winner) { | |
1389 winner.getElementsByClassName('platforms')[0].innerHTML += | |
1390 '<div class=used-platform>' + platform + '</div>'; | |
1391 } else { | |
1392 console.log('No expectations identified for this test. This means ' + | |
1393 'there is a logic bug in the dashboard for which expectations a ' + | |
1394 'platform uses or trac.webkit.org/src.chromium.org is giving ' + | |
1395 ' 5XXs.'); | |
1396 } | |
1397 } | |
1398 } | |
1399 | |
1400 var TRAC_IMAGE_BASE_URL; | |
1401 /** | |
1402 * Trac seems to only show the raw image if you're viewing at a specific | |
1403 * revision. Use the latest revision on any builder. | |
1404 */ | |
1405 function getTracImageBaseURL() { | |
1406 if (!TRAC_IMAGE_BASE_URL) { | |
1407 TRAC_IMAGE_BASE_URL = 'http://trac.webkit.org/export/' + | |
1408 getLatestKnownRevision(false) + '/trunk/'; | |
1409 } | |
1410 return TRAC_IMAGE_BASE_URL; | |
1411 } | |
1412 | |
1413 function getLatestKnownRevision(isChrome) { | |
1414 var revision = 0; | |
1415 for (var builder in builders) { | |
1416 var results = resultsByBuilder[builder]; | |
1417 var revisions = isChrome ? results.chromeRevision : | |
1418 results.webkitRevision; | |
1419 if (revision < revisions[0]) | |
1420 revision = revisions[0]; | |
1421 } | |
1422 return revision; | |
1423 } | |
1424 | |
1425 function addExpectations(expectationsContainers, container, base, imageBase, | |
1426 platform, text, checksum, png, textSuffix) { | |
1427 addExpectationItem(expectationsContainers, container, platform, text, base, | |
1428 textSuffix); | |
1429 addExpectationItem(expectationsContainers, container, platform, checksum, | |
1430 base, textSuffix); | |
1431 addExpectationItem(expectationsContainers, container, platform, png, | |
1432 imageBase); | |
1433 } | |
1434 | |
1435 function getExpectationsTitle(platform, path) { | |
1436 var header = document.createElement('h3'); | |
1437 header.className = 'expectations-title'; | |
1438 | |
1439 var innerHTML; | |
1440 if (platform == LAYOUT_TESTS_PREFIX) { | |
1441 var parts = path.split('/'); | |
1442 innerHTML = parts[parts.length - 1]; | |
1443 } else { | |
1444 innerHTML = platform || path; | |
1445 } | |
1446 | |
1447 header.innerHTML = '<div class=title>' + innerHTML + | |
1448 '</div><div style="float:left"> </div>' + | |
1449 '<div class=platforms style="float:right"></div>'; | |
1450 header.style.clear = 'both'; | |
1451 header.platform = platform; | |
1452 return header; | |
1453 } | |
1454 | |
1455 function loadExpectations(expectationsContainer) { | |
1456 // Map from file extension to container div for expectations of that type. | |
1457 var expectationsContainers = {}; | |
1458 | |
1459 var test = expectationsContainer.getAttribute('test'); | |
1460 var textSuffixWebKit = '?format=txt'; | |
1461 addExpectationItem(expectationsContainers, expectationsContainer, null, | |
1462 test, TEST_URL_BASE_PATH, textSuffixWebKit); | |
1463 | |
1464 var testWithoutSuffix = test.substring(0, test.lastIndexOf('.')); | |
1465 | |
1466 var isUpstreamTest = startsWith(test, LAYOUT_TESTS_PREFIX); | |
1467 if (isUpstreamTest) { | |
1468 var testWithoutPrefix = testWithoutSuffix.substring( | |
1469 LAYOUT_TESTS_PREFIX.length); | |
1470 var textWithoutPrefix = testWithoutPrefix + "-expected.txt"; | |
1471 var checksumWithoutPrefix = testWithoutPrefix + "-expected.checksum" | |
1472 var pngWithoutPrefix = testWithoutPrefix + "-expected.png"; | |
1473 | |
1474 addExpectations(expectationsContainers, expectationsContainer, | |
1475 TEST_URL_BASE_PATH, getTracImageBaseURL(), LAYOUT_TESTS_PREFIX, | |
1476 textWithoutPrefix, checksumWithoutPrefix, pngWithoutPrefix, | |
1477 textSuffixWebKit); | |
1478 } | |
1479 | |
1480 var text = testWithoutSuffix + "-expected.txt"; | |
1481 var checksum = testWithoutSuffix + "-expected.checksum" | |
1482 var png = testWithoutSuffix + "-expected.png"; | |
1483 | |
1484 var textSuffixChrome = '?revision=' + getLatestKnownRevision(true); | |
1485 | |
1486 var fallbacks = getAllFallbacks(); | |
1487 for (var i = 0; i < fallbacks.length; i++) { | |
1488 var fallback = fallbacks[i]; | |
1489 if (startsWith(fallback, 'platform')) { | |
1490 if (isUpstreamTest) { | |
1491 addExpectations(expectationsContainers, expectationsContainer, | |
1492 TEST_URL_BASE_PATH + LAYOUT_TESTS_PREFIX, | |
1493 getTracImageBaseURL() + LAYOUT_TESTS_PREFIX, | |
1494 fallback, textWithoutPrefix, | |
1495 checksumWithoutPrefix, pngWithoutPrefix, textSuffixWebKit); | |
1496 } | |
1497 } else { | |
1498 addExpectations(expectationsContainers, expectationsContainer, | |
1499 CHROME_TEST_BASE_URL, CHROME_TEST_BASE_URL, fallback, text, | |
1500 checksum, png, textSuffixChrome); | |
1501 } | |
1502 } | |
1503 | |
1504 // Add a clearing element so floated elements don't bleed out of their | |
1505 // containing block. | |
1506 var br = document.createElement('br'); | |
1507 br.style.clear = 'both'; | |
1508 expectationsContainer.appendChild(br); | |
1509 } | |
1510 | |
1511 var allFallbacks; | |
1512 | |
1513 /** | |
1514 * Returns the reverse sorted, deduped list of all platform fallback | |
1515 * directories. | |
1516 */ | |
1517 function getAllFallbacks() { | |
1518 if (!allFallbacks) { | |
1519 var holder = {}; | |
1520 for (var platform in fallbacksMap) { | |
1521 var fallbacks = fallbacksMap[platform]; | |
1522 for (var i = 0; i < fallbacks.length; i++) { | |
1523 holder[fallbacks[i]] = 1; | |
1524 } | |
1525 } | |
1526 | |
1527 allFallbacks = []; | |
1528 for (var fallback in holder) { | |
1529 allFallbacks.push(fallback); | |
1530 } | |
1531 allFallbacks.sort(function(a, b) { | |
1532 if (a == b) | |
1533 return 0; | |
1534 return a < b; | |
1535 }); | |
1536 } | |
1537 return allFallbacks; | |
1538 } | |
1539 | |
1540 /** | |
1541 * Appends the expectations for each test listed. | |
1542 */ | |
1543 function appendExpectations() { | |
1544 if (currentState.showExpectations) { | |
1545 var expectations = document.getElementsByClassName('expectations'); | |
1546 for (var i = 0, len = expectations.length; i < len; i++) { | |
1547 loadExpectations(expectations[i]); | |
1548 } | |
1549 } | |
1211 } | 1550 } |
1212 | 1551 |
1213 function generatePageForIndividualTests(tests) { | 1552 function generatePageForIndividualTests(tests) { |
1214 var html = getHTMLForNavBar(); | 1553 var testsHTML = []; |
1215 for (var i = 0; i < tests.length; i++) { | 1554 for (var i = 0; i < tests.length; i++) { |
1216 html += '<h2>' + tests[i] + '</h2>' + | 1555 testsHTML.push('<h2>' + tests[i] + '</h2>' + |
1217 getHTMLForIndividulTestOnAllBuilders(tests[i]); | 1556 getHTMLForIndividulTestOnAllBuilders(tests[i])); |
1218 } | 1557 } |
1219 setFullPageHTML(html); | 1558 setFullPageHTML(getHTMLForNavBar() + testsHTML.join('<hr>')); |
1220 | 1559 |
1560 appendExpectations(); | |
1221 $('tests-input').value = currentState.tests; | 1561 $('tests-input').value = currentState.tests; |
1222 } | 1562 } |
1223 | 1563 |
1224 function getHTMLForNavBar(opt_builderName) { | 1564 function getHTMLForNavBar(opt_builderName) { |
1225 var html = '<div id=builders>'; | 1565 var html = '<div id=builders>'; |
1226 for (var builder in builders) { | 1566 for (var builder in builders) { |
1227 var className = builder == opt_builderName ? 'current-builder' : 'link'; | 1567 var className = builder == opt_builderName ? 'current-builder' : 'link'; |
1228 html += '<span class=' + className + | 1568 html += '<span class=' + className + |
1229 ' onclick=\'setState("builder", "' + builder + '")\'>' + | 1569 ' onclick=\'setState("builder", "' + builder + '")\'>' + |
1230 builder + '</span>'; | 1570 builder + '</span>'; |
1231 } | 1571 } |
1232 html += '</div>' + | 1572 return html + '</div>' + |
1233 '<form id=tests-form ' + | 1573 '<form id=tests-form ' + |
1234 'onsubmit="setState(\'tests\', tests.value);return false;">' + | 1574 'onsubmit="setState(\'tests\', tests.value);return false;">' + |
1235 '<div>Show tests on all platforms: </div><input name=tests ' + | 1575 '<div>Show tests on all platforms: </div><input name=tests ' + |
1236 'placeholder="Comma or space-separated list of tests or partial ' + | 1576 'placeholder="Comma or space-separated list of tests or partial ' + |
1237 'paths to show test results across all builders, e.g., ' + | 1577 'paths to show test results across all builders, e.g., ' + |
1238 'LayoutTests/foo/bar.html,LayoutTests/foo/baz,forms" ' + | 1578 'LayoutTests/foo/bar.html,LayoutTests/foo/baz,forms" ' + |
1239 'id=tests-input></form>' + | 1579 'id=tests-input></form>' + |
1240 '<form id=max-results-form ' + | 1580 '<form id=max-results-form ' + |
1241 'onsubmit="setState(\'maxResults\', maxResults.value);return false;"' + | 1581 'onsubmit="setState(\'maxResults\', maxResults.value);return false;"' + |
1242 '><span>Number of results to show (max=500): </span>' + | 1582 '><span>Number of results to show (max=500): </span>' + |
1243 '<input name=maxResults id=max-results-input></form>' + | 1583 '<input name=maxResults id=max-results-input></form> | ' + |
1244 '<div id="loading-ui">LOADING...</div><div id=legend>' + | 1584 '<b>Type ? for legend and expectations fallback order</b>' + |
1245 '<div id=legend-toggle>' + getLinkHTMLToToggleLegendDisplay() + | 1585 '<div id="loading-ui">LOADING...</div>'; |
1246 '</div><div id=legend-contents>'; | |
1247 | |
1248 for (var expectation in EXPECTATIONS_MAP) { | |
1249 html += '<div class=' + expectation + '>' + | |
1250 EXPECTATIONS_MAP[expectation] + '</div>'; | |
1251 } | |
1252 return html + '<div class=wrong-expectations>WRONG EXPECTATIONS</div>' + | |
1253 '<div class=merge>WEBKIT MERGE</div></div></div>'; | |
1254 } | 1586 } |
1255 | 1587 |
1256 function getLinkHTMLToToggleState(key, linkText) { | 1588 function getLinkHTMLToToggleState(key, linkText) { |
1257 var isTrue = currentState[key]; | 1589 var isTrue = currentState[key]; |
1258 return '<span class=link onclick="setState(\'' + key + '\', ' + !isTrue + | 1590 return '<span class=link onclick="setState(\'' + key + '\', ' + !isTrue + |
1259 ')">' + (isTrue ? 'Hide' : 'Show') + ' ' + linkText + '</span>'; | 1591 ')">' + (isTrue ? 'Hide' : 'Show') + ' ' + linkText + '</span>'; |
1260 } | 1592 } |
1261 | 1593 |
1262 function generatePageForBuilder(builderName) { | 1594 function generatePageForBuilder(builderName) { |
1263 processTestRunsForBuilder(builderName); | 1595 processTestRunsForBuilder(builderName); |
1264 | 1596 |
1265 var tableRowsHTML = ''; | 1597 var tableRowsHTML = ''; |
1266 var results = perBuilderFailures[builderName]; | 1598 var results = perBuilderFailures[builderName]; |
1267 sortTests(results, currentState.sortColumn, currentState.sortOrder); | 1599 sortTests(results, currentState.sortColumn, currentState.sortOrder); |
1268 for (var i = 0; i < results.length; i++) { | 1600 for (var i = 0; i < results.length; i++) { |
1269 tableRowsHTML += getHTMLForSingleTestRow(results[i], builderName); | 1601 tableRowsHTML += getHTMLForSingleTestRow(results[i], builderName); |
1270 } | 1602 } |
1271 | 1603 |
1272 var testsHTML = tableRowsHTML ? getHTMLForTestTable(tableRowsHTML) : | 1604 var testsHTML = tableRowsHTML ? getHTMLForTestTable(tableRowsHTML) : |
1273 '<div>No tests. Try showing tests with correct expectations.</div>'; | 1605 '<div>No tests. Try showing tests with correct expectations.</div>'; |
1274 | 1606 |
1275 var html = getHTMLForNavBar(builderName) + | 1607 var html = getHTMLForNavBar(builderName) + |
1276 getHTMLForTestsWithExpectationsButNoFailures(builderName) + | 1608 getHTMLForTestsWithExpectationsButNoFailures(builderName) + |
1277 '<h2>Failing tests</h2><div>' + | 1609 '<h2>Failing tests</h2><div>' + |
1278 getLinkHTMLToToggleState('showWontFix', 'WONTFIX tests') + ' | ' + | 1610 getLinkHTMLToToggleState('showWontFix', 'WONTFIX tests') + ' | ' + |
1279 getLinkHTMLToToggleState('showCorrectExpectations', | 1611 getLinkHTMLToToggleState('showCorrectExpectations', |
1280 'tests with correct expectations') + ' | ' + | 1612 'tests with correct expectations') + ' | ' + |
1281 getLinkHTMLToToggleState('showFlaky', 'flaky tests') + ' | ' + | 1613 getLinkHTMLToToggleState('showFlaky', 'flaky tests') + ' | ' + |
1282 '<b>All columns are sortable. | ' + | 1614 'All columns are sortable. | ' + |
1283 'Flakiness reader order is newer --> older runs.</b></div>' + | 1615 'Flakiness reader order is newer --> older runs.</div>' + |
1284 testsHTML; | 1616 testsHTML; |
1285 | 1617 |
1286 setFullPageHTML(html); | 1618 setFullPageHTML(html); |
1287 | 1619 |
1288 var ths = document.getElementsByTagName('th'); | 1620 var ths = document.getElementsByTagName('th'); |
1289 for (var i = 0; i < ths.length; i++) { | 1621 for (var i = 0; i < ths.length; i++) { |
1290 ths[i].addEventListener('click', changeSort, false); | 1622 ths[i].addEventListener('click', changeSort, false); |
1291 ths[i].className = "sortable"; | 1623 ths[i].className = "sortable"; |
1292 } | 1624 } |
1293 } | 1625 } |
1294 | 1626 |
1295 function getLinkHTMLToToggleLegendDisplay() { | |
1296 return getLinkHTMLToToggleState('showLegend', 'Legend'); | |
1297 } | |
1298 | |
1299 function updateLegendDisplay() { | |
1300 $('legend-contents').style.display = currentState.showLegend ? '' : 'none'; | |
1301 } | |
1302 | |
1303 function createTableHeadersArray(firstColumnHeader) { | 1627 function createTableHeadersArray(firstColumnHeader) { |
1304 tableHeaders = [firstColumnHeader].concat(BASE_TABLE_HEADERS); | 1628 tableHeaders = [firstColumnHeader].concat(BASE_TABLE_HEADERS); |
1305 } | 1629 } |
1306 | 1630 |
1307 /** | 1631 /** |
1308 * Clears the processed test state for perBuilderFailures. | 1632 * Clears the processed test state for perBuilderFailures. |
1309 * TODO(ojan): This really should probably clear all the state we've | 1633 * TODO(ojan): This really should probably clear all the state we've |
1310 * generated, but that's kind of a pain given the many global objects state is | 1634 * generated, but that's kind of a pain given the many global objects state is |
1311 * stored in. There should probably be one global generatedState | 1635 * stored in. There should probably be one global generatedState |
1312 * object that all the generated state lives off of. | 1636 * object that all the generated state lives off of. |
1313 */ | 1637 */ |
1314 function clearProcessedTestState() { | 1638 function clearProcessedTestState() { |
1315 for (var builder in builders) { | 1639 for (var builder in builders) { |
1316 delete perBuilderFailures[builder]; | 1640 delete perBuilderFailures[builder]; |
1317 delete perBuilderPlatformAndBuildType[builder]; | 1641 delete perBuilderPlatformAndBuildType[builder]; |
1318 delete perBuilderWithExpectationsButNoFailures[builder]; | 1642 delete perBuilderWithExpectationsButNoFailures[builder]; |
1319 delete perBuilderSkippedPaths[builder]; | 1643 delete perBuilderSkippedPaths[builder]; |
1320 } | 1644 } |
1321 | 1645 |
1322 for (var key in testToResultsMap) { | 1646 for (var key in testToResultsMap) { |
1323 delete testToResultsMap[key] | 1647 delete testToResultsMap[key] |
1324 } | 1648 } |
1325 } | 1649 } |
1326 | 1650 |
1651 var VALID_KEYS_FOR_INDIVIDUAL_TESTS = { | |
1652 tests: 1, | |
1653 maxResults: 1, | |
1654 showExpectations: 1, | |
1655 showLargeExpectations: 1 | |
1656 }; | |
1657 | |
1327 /** | 1658 /** |
1328 * Sets the page state and regenerates the page. Takes varargs of key, value | 1659 * Sets the page state and regenerates the page. Takes varargs of key, value |
1329 * pairs. | 1660 * pairs. |
1330 */ | 1661 */ |
1331 function setState(var_args) { | 1662 function setState(var_args) { |
1332 var shouldRegeneratePage = true; | 1663 var shouldRegeneratePage = true; |
1333 for (var i = 0; i < arguments.length; i += 2) { | 1664 for (var i = 0; i < arguments.length; i += 2) { |
1334 var key = arguments[i]; | 1665 var key = arguments[i]; |
1335 | 1666 |
1336 if (key != 'tests' && key != 'maxResults') { | 1667 if (!(key in VALID_KEYS_FOR_INDIVIDUAL_TESTS)) { |
1337 delete currentState.tests; | 1668 delete currentState.tests; |
1338 } | 1669 } |
1339 | 1670 |
1340 if (key == 'maxResults') { | 1671 if (key == 'maxResults') { |
1341 // Processing the test results JSON makes assumptions about the number | 1672 // Processing the test results JSON makes assumptions about the number |
1342 // of results to show. This makes changing the number of maxResults slow | 1673 // of results to show. This makes changing the number of maxResults slow |
1343 // but is considerably easier than refactoring all the other code. | 1674 // but is considerably easier than refactoring all the other code. |
1344 clearProcessedTestState(); | 1675 clearProcessedTestState(); |
1345 } | 1676 } |
1346 | |
1347 if (key == 'showLegend') { | |
1348 // No need to regenerate the page if only the legend's display is being | |
1349 // updated. | |
1350 shouldRegeneratePage = keys.length == 1; | |
1351 updateLegendDisplay(); | |
1352 $('legend-toggle').innerHTML = getLinkHTMLToToggleLegendDisplay(); | |
1353 } | |
1354 } | 1677 } |
1355 | 1678 |
1356 // Set all the custom state for this dashboard before calling | 1679 // Set all the custom state for this dashboard before calling |
1357 // setQueryParameter since setQueryParameter updates the location bar. | 1680 // setQueryParameter since setQueryParameter updates the location bar. |
1358 setQueryParameter.apply(null, arguments); | 1681 setQueryParameter.apply(null, arguments); |
1359 | 1682 |
1360 if (shouldRegeneratePage) | 1683 if (shouldRegeneratePage) |
1361 handleLocationChange(); | 1684 handleLocationChange(); |
1362 } | 1685 } |
1363 | 1686 |
1687 function hideLegend() { | |
1688 var legend = $('legend'); | |
1689 if (legend) | |
1690 legend.parentNode.removeChild(legend); | |
1691 } | |
1692 | |
1693 var fallbacksMap = {}; | |
1694 fallbacksMap['WIN-VISTA'] = ['chromium-win-vista', 'chromium-win', | |
1695 'platform/win', 'platform/mac']; | |
1696 fallbacksMap['WIN-XP'] = ['chromium-win-xp'].concat( | |
1697 fallbacksMap['WIN-VISTA']); | |
1698 // Should mac look at the tiger results? | |
1699 fallbacksMap['MAC'] = ['chromium-mac', 'platform/mac', | |
1700 'platform/mac-snowleopard', 'platform/mac-leopard', 'platform/mac-tiger']; | |
1701 fallbacksMap['LINUX'] = ['chromium-linux', 'chromium-win', 'platform/win', | |
1702 'platform/mac']; | |
1703 | |
1704 function htmlForFallbackHelp(fallbacks) { | |
1705 return '<ol class=fallback-list><li>' + fallbacks.join('</li><li>') + | |
1706 '</li></ol>'; | |
1707 } | |
1708 | |
1709 function showLegend() { | |
1710 var legend = $('legend'); | |
1711 if (!legend) { | |
1712 legend = document.createElement('div'); | |
1713 legend.id = 'legend'; | |
1714 document.body.appendChild(legend); | |
1715 } | |
1716 | |
1717 var innerHTML = '<div id=legend-toggle onclick="hideLegend()">Hide ' + | |
1718 'legend (or hit esc to close)</div><div id=legend-contents>'; | |
1719 for (var expectation in EXPECTATIONS_MAP) { | |
1720 innerHTML += '<div class=' + expectation + '>' + | |
1721 EXPECTATIONS_MAP[expectation] + '</div>'; | |
1722 } | |
1723 innerHTML += '<div class=wrong-expectations>WRONG EXPECTATIONS</div>' + | |
1724 '<div class=merge>WEBKIT MERGE</div></div>' +'</div>' + | |
1725 '<h3>Test expectatons fallback order.</h3>'; | |
1726 | |
1727 for (var platform in fallbacksMap) { | |
1728 innerHTML += '<div class=fallback-header>' + platform + '</div>' + | |
1729 htmlForFallbackHelp(fallbacksMap[platform]); | |
1730 } | |
1731 legend.innerHTML = innerHTML; | |
1732 } | |
1733 | |
1734 document.addEventListener('keydown', function(e) { | |
1735 if (e.keyCode == 191 && e.shiftKey) { | |
1736 // ? key | |
arv (Not doing code reviews)
2009/10/05 23:59:29
This will not work on different keyboard layouts.
| |
1737 showLegend(); | |
1738 } else if (e.keyCode == 27) { | |
1739 // escape key | |
1740 hideLegend(); | |
1741 hidePopup(); | |
1742 } | |
1743 }, false); | |
1364 | 1744 |
1365 </script> | 1745 </script> |
1366 </head> | 1746 </head> |
1367 | 1747 |
1368 <body></body> | 1748 <body></body> |
1369 </html> | 1749 </html> |
OLD | NEW |