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

Side by Side Diff: webkit/tools/layout_tests/flakiness_dashboard.html

Issue 201073: Run-length encode the JSON results. This makes them considerably... (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 11 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | webkit/tools/layout_tests/layout_package/json_results_generator.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 <script> 6 <script>
7 var pageLoadStartTime = Date.now(); 7 var pageLoadStartTime = Date.now();
8 </script> 8 </script>
9 <style> 9 <style>
10 body { 10 body {
11 font-family: arial; 11 font-family: arial;
12 font-size: 13px; 12 font-size: 13px;
13 } 13 }
14 h2 { 14 h2 {
15 font-size: 16px; 15 font-size: 16px;
16 margin-bottom: .25em; 16 margin-bottom: .25em;
17 } 17 }
18 form { 18 #max-results-form {
19 margin: 3px 0; 19 display: inline;
20 }
21 #max-results-input {
22 width: 30px;
23 }
24 #tests-form {
20 display: -webkit-box; 25 display: -webkit-box;
21 } 26 }
22 form > * { 27 #tests-form > * {
23 display: -webkit-box; 28 display: -webkit-box;
24 } 29 }
25 form > div { 30 #tests-form > div {
26 -webkit-box-flex: 0; 31 -webkit-box-flex: 0;
27 } 32 }
28 form > input { 33 #tests-input {
29 -webkit-box-flex: 1; 34 -webkit-box-flex: 1;
30 } 35 }
31 .test-link { 36 .test-link {
32 white-space: normal; 37 white-space: normal;
33 } 38 }
34 .test-link, .options-container { 39 .test-link, .options-container {
35 padding: 0 2px; 40 padding: 0 2px;
36 } 41 }
37 .test-table { 42 .test-table {
38 white-space: nowrap; 43 white-space: nowrap;
(...skipping 126 matching lines...) Expand 10 before | Expand all | Expand 10 after
165 * 170 *
166 * Also, each column in the dashboard is sortable. 171 * Also, each column in the dashboard is sortable.
167 * 172 *
168 * Currently, only webkit tests are supported, but adding other test types 173 * Currently, only webkit tests are supported, but adding other test types
169 * should just require the following steps: 174 * should just require the following steps:
170 * -generate results.json and expectations.json for these tests 175 * -generate results.json and expectations.json for these tests
171 * -copy them to the appropriate location 176 * -copy them to the appropriate location
172 * -add the builder name to the list of builders below. 177 * -add the builder name to the list of builders below.
173 */ 178 */
174 179
175 // Default to layout_tests. 180 // CONSTANTS
176 var testType = 'layout_test_results'; 181 var FORWARD = 'forward';
177 var params = window.location.search.substring(1).split('&'); 182 var BACKWARD = 'backward';
178 for (var i = 0; i < params.length; i++) { 183 var TEST_URL_BASE_PATH =
179 var thisParam = params[i].split('='); 184 'http://trac.webkit.org/projects/webkit/browser/trunk/';
180 if (thisParam[0] == 'testtype') { 185 var BUILDERS_BASE_PATH =
181 testType = thisParam[1]; 186 'http://build.chromium.org/buildbot/waterfall/builders/';
182 break; 187 var EXPECTATIONS_MAP = {
183 } 188 'T': 'TIMEOUT',
184 } 189 'C': 'CRASH',
185 190 'P': 'PASS',
186 // Map of builderName (the name shown in the waterfall) 191 'F': 'TEXT FAIL',
187 // to builderPath (the path used in the builder's URL) 192 'S': 'SIMPLIFIED',
188 // TODO(ojan): Make this switch based off of the testType. 193 'I': 'IMAGE',
189 var builders = { 194 'O': 'OTHER',
190 'Webkit': 'webkit-rel', 195 'N': 'NO DATA'
191 'Webkit (dbg)(1)': 'webkit-dbg-1', 196 };
192 'Webkit (dbg)(2)': 'webkit-dbg-2', 197 var PLATFORMS = {'MAC': 'MAC', 'LINUX': 'LINUX', 'WIN': 'WIN'};
193 'Webkit (dbg)(3)': 'webkit-dbg-3', 198 var BUILD_TYPES = {'DEBUG': 'DBG', 'RELEASE': 'RELEASE'};
194 'Webkit Linux': 'webkit-rel-linux', 199
195 'Webkit Linux (dbg)(1)': 'webkit-dbg-linux-1', 200 // GLOBALS
196 'Webkit Linux (dbg)(2)': 'webkit-dbg-linux-2', 201 // The DUMMYVALUE gets shifted off the array in the first call to
197 'Webkit Linux (dbg)(3)': 'webkit-dbg-linux-3', 202 // generatePage.
198 'Webkit Mac10.5': 'webkit-rel-mac5', 203 var tableHeaders = ['DUMMYVALUE', 'bugs', 'modifiers', 'expectations',
199 'Webkit Mac10.5 (dbg)(1)': 'webkit-dbg-mac5-1', 204 'missing', 'extra', 'slowest run',
200 'Webkit Mac10.5 (dbg)(2)': 'webkit-dbg-mac5-2', 205 'flakiness (numbers are runtimes in seconds)'];
201 'Webkit Mac10.5 (dbg)(3)': 'webkit-dbg-mac5-3' 206 var perBuilderPlatformAndBuildType = {};
207 var perBuilderFailures = {};
208 // Map of builder to arrays of tests that are listed in the expectations file
209 // but have for that builder.
210 var perBuilderWithExpectationsButNoFailures = {};
211
212
213 // Generic utility functions.
214 function $(id) {
215 return document.getElementById(id);
216 }
217
218 function stringContains(a, b) {
219 return a.indexOf(b) != -1;
220 }
221
222 function isValidName(str) {
223 return str.match(/[A-Za-z0-9\-\_,]/);
224 }
225
226 function trimString(str) {
227 return str.replace(/^\s+|\s+$/g, '');
228 }
229
230 function anyKeyInString(object, string) {
231 for (var key in object) {
232 if (stringContains(string, key))
233 return true;
234 }
235 return false;
236 }
237
238 function validateParameter(state, key, value, validateFn) {
239 if (validateFn()) {
240 state[key] = value;
241 } else {
242 console.log(key + ' value is not valid: ' + value);
243 }
244 }
245
246 /**
247 * Parses a string (e.g. window.location.hash) and calls
248 * validValueHandler(key, value) for each key-value pair in the string.
249 */
250 function parseParameters(parameterStr, validValueHandler) {
251 var params = parameterStr.split('&');
252 for (var i = 0; i < params.length; i++) {
253 var thisParam = params[i].split('=');
254 if (thisParam.length != 2) {
255 console.log('Invalid query parameter: ' + params[i]);
256 continue;
257 }
258
259 var key = thisParam[0];
260 var value = decodeURIComponent(thisParam[1]);
261 if (!validValueHandler(key, value))
262 console.log('Invalid key: ' + key + ' value: ' + value);
263 }
264 }
265
266 function appendScript(path) {
267 var script = document.createElement('script');
268 script.src = path;
269 document.getElementsByTagName('head')[0].appendChild(script);
270 }
271
272
273 // Parse query parameters.
274 var queryState = {'debug': false, 'testType': 'layout_test_results'};
275
276 function handleValidQueryParameter(key, value) {
277 switch (key) {
278 case 'testType':
279 validateParameter(queryState, key, value,
280 function() { return isValidName(value); });
281
282 return true;
283
284 case 'debug':
285 queryState[key] = value == 'true';
286
287 return true;
288
289 default:
290 return false;
291 }
292 }
293
294 parseParameters(window.location.search.substring(1),
295 handleValidQueryParameter);
296 if (queryState['debug']) {
297 // In debug mode point to the results.json and expectations.json in the
298 // local tree. Useful for debugging changes to the python JSON generator.
299 var builders = {'DUMMY_BUILDER_NAME': ''};
300 var builderBase = '../../Debug/';
301 queryState['testType'] = 'layout-test-results';
302 } else {
303 // Map of builderName (the name shown in the waterfall)
304 // to builderPath (the path used in the builder's URL)
305 // TODO(ojan): Make this switch based off of the testType.
306 var builders = {
307 'Webkit': 'webkit-rel',
308 'Webkit (dbg)(1)': 'webkit-dbg-1',
309 'Webkit (dbg)(2)': 'webkit-dbg-2',
310 'Webkit (dbg)(3)': 'webkit-dbg-3',
311 'Webkit Linux': 'webkit-rel-linux',
312 'Webkit Linux (dbg)(1)': 'webkit-dbg-linux-1',
313 'Webkit Linux (dbg)(2)': 'webkit-dbg-linux-2',
314 'Webkit Linux (dbg)(3)': 'webkit-dbg-linux-3',
315 'Webkit Mac10.5': 'webkit-rel-mac5',
316 'Webkit Mac10.5 (dbg)(1)': 'webkit-dbg-mac5-1',
317 'Webkit Mac10.5 (dbg)(2)': 'webkit-dbg-mac5-2',
318 'Webkit Mac10.5 (dbg)(3)': 'webkit-dbg-mac5-3'
319 };
320 var builderBase = 'http://build.chromium.org/buildbot/';
321 }
322
323 // Parse hash parameters.
324 // Permalinkable state of the page.
325 var currentState = {};
326
327 var defaultStateValues = {
328 sortOrder: BACKWARD,
329 sortColumn: 'flakiness',
330 showWontFix: false,
331 showCorrectExpectations: false,
332 showFlaky: true,
333 maxResults: 200
202 }; 334 };
203 335
336 for (var builder in builders) {
337 defaultStateValues.builder = builder;
338 break;
339 }
340
341 function fillDefaultStateValues() {
342 // tests has no states with default values.
343 if (currentState.tests)
344 return;
345
346 for (var state in defaultStateValues) {
347 if (!(state in currentState))
348 currentState[state] = defaultStateValues[state];
349 }
350 }
351
352 function handleValidHashParameter(key, value) {
353 switch(key) {
354 case 'tests':
355 validateParameter(currentState, key, value,
356 function() { return isValidName(value); });
arv (Not doing code reviews) 2009/09/09 23:46:25 function() { return isValidName(value); }
357
358 return true;
359
360 case 'builder':
361 validateParameter(currentState, key, value,
362 function() { return value in builders; });
363
364 return true;
365
366 case 'sortColumn':
367 validateParameter(currentState, key, value,
368 function() {
369 for (var i = 0; i < tableHeaders.length; i++) {
370 if (value == getSortColumnFromTableHeader(tableHeaders[i]))
371 return true;
372 }
373 return value == 'test';
374 });
375
376 return true;
377
378 case 'sortOrder':
379 validateParameter(currentState, key, value,
380 function() { return value == FORWARD || value == BACKWARD; });
381
382 return true;
383
384 case 'maxResults':
385 validateParameter(currentState, key, value,
386 function() { return value.match(/^\d+$/) });
387
388 return true;
389
390 case 'showWontFix':
391 case 'showCorrectExpectations':
392 case 'showFlaky':
393 currentState[key] = value == 'true';
394
395 return true;
396
397 default:
398 return false;
399 }
400 }
401
402 // Keep the location around for detecting changes to hash arguments
403 // manually typed into the URL bar.
404 var oldLocation;
405
406 function parseAllParameters() {
407 oldLocation = window.location.href;
408 parseParameters(window.location.search.substring(1),
409 handleValidQueryParameter);
410 parseParameters(window.location.hash.substring(1),
411 handleValidHashParameter);
412 fillDefaultStateValues();
413 }
414
415 parseAllParameters();
416
417 // Append JSON script elements.
204 var resultsByBuilder = {}; 418 var resultsByBuilder = {};
205 // Maps test path to an array of {builder, testResults} objects. 419 // Maps test path to an array of {builder, testResults} objects.
206 var testToResultsMap = {}; 420 var testToResultsMap = {};
207 var expectationsByTest = {}; 421 var expectationsByTest = {};
208 function ADD_RESULTS(builds) { 422 function ADD_RESULTS(builds) {
209 for (var builderName in builds) { 423 for (var builderName in builds) {
210 resultsByBuilder[builderName] = builds[builderName]; 424 if (builderName != 'version')
425 resultsByBuilder[builderName] = builds[builderName];
211 } 426 }
212 427
213 generatePage(); 428 generatePage();
214 } 429 }
215 var BUILDER_BASE = 'http://build.chromium.org/buildbot/'; 430
216 function getPathToBuilderResultsFile(builderName) { 431 function getPathToBuilderResultsFile(builderName) {
217 return BUILDER_BASE + testType + '/' + builders[builderName] + '/'; 432 return builderBase + queryState['testType'] + '/' +
218 } 433 builders[builderName] + '/';
434 }
435
219 for (var builderName in builders) { 436 for (var builderName in builders) {
220 var script = document.createElement('script'); 437 appendScript(getPathToBuilderResultsFile(builderName) + 'results.json');
221 script.src = getPathToBuilderResultsFile(builderName) + 'results.json'; 438 }
222 document.getElementsByTagName('head')[0].appendChild(script); 439
223 }
224
225 var script = document.createElement('script');
226 // Grab expectations file from any builder. 440 // Grab expectations file from any builder.
227 script.src = getPathToBuilderResultsFile(builderName) + 'expectations.json'; 441 appendScript(getPathToBuilderResultsFile(builderName) + 'expectations.json');
228 document.getElementsByTagName('head')[0].appendChild(script);
229 442
230 var expectationsLoaded = false; 443 var expectationsLoaded = false;
231 function ADD_EXPECTATIONS(expectations) { 444 function ADD_EXPECTATIONS(expectations) {
232 expectationsLoaded = true; 445 expectationsLoaded = true;
233 expectationsByTest = expectations; 446 expectationsByTest = expectations;
234 generatePage(); 447 generatePage();
235 } 448 }
236 449
237 // CONSTANTS
238 var FORWARD = 'forward';
239 var BACKWARD = 'backward';
240 var TEST_URL_BASE_PATH =
241 'http://trac.webkit.org/projects/webkit/browser/trunk/';
242 var BUILDERS_BASE_PATH =
243 'http://build.chromium.org/buildbot/waterfall/builders/';
244 var EXPECTATIONS_MAP = {
245 'T': 'TIMEOUT',
246 'C': 'CRASH',
247 'P': 'PASS',
248 'F': 'TEXT FAIL',
249 'S': 'SIMPLIFIED',
250 'I': 'IMAGE',
251 'O': 'OTHER',
252 'N': 'NO DATA'
253 };
254 var PLATFORMS = {'MAC': 'MAC', 'LINUX': 'LINUX', 'WIN': 'WIN'};
255 var BUILD_TYPES = {'DEBUG': 'DBG', 'RELEASE': 'RELEASE'};
256
257 // GLOBALS
258 // The DUMMYVALUE gets shifted off the array in the first call to
259 // generatePage.
260 var tableHeaders = ['DUMMYVALUE', 'bugs', 'modifiers', 'expectations',
261 'missing', 'extra', 'slowest run',
262 'flakiness (numbers are runtimes in seconds)'];
263 var perBuilderPlatformAndBuildType = {};
264 var oldLocation;
265 var perBuilderFailures = {};
266 // Map of builder to arrays of tests that are listed in the expectations file
267 // but have for that builder.
268 var perBuilderWithExpectationsButNoFailures = {};
269 450
270 function createResultsObjectForTest(test) { 451 function createResultsObjectForTest(test) {
271 return { 452 return {
272 test: test, 453 test: test,
273 // HTML for display of the results in the flakiness column 454 // HTML for display of the results in the flakiness column
274 html: '', 455 html: '',
275 flips: 0, 456 flips: 0,
276 slowestTime: 0, 457 slowestTime: 0,
277 meetsExpectations: true, 458 meetsExpectations: true,
278 isWontFix: false, 459 isWontFix: false,
(...skipping 11 matching lines...) Expand all
290 }; 471 };
291 } 472 }
292 473
293 function getMatchingElement(stringToMatch, elementsMap) { 474 function getMatchingElement(stringToMatch, elementsMap) {
294 for (var element in elementsMap) { 475 for (var element in elementsMap) {
295 if (stringContains(stringToMatch, elementsMap[element])) 476 if (stringContains(stringToMatch, elementsMap[element]))
296 return element; 477 return element;
297 } 478 }
298 } 479 }
299 480
300 function $(id) {
301 return document.getElementById(id);
302 }
303
304 function stringContains(a, b) {
305 return a.indexOf(b) != -1;
306 }
307
308 function trimString(str) {
309 return str.replace(/^\s+|\s+$/g, '');
310 }
311
312 function anyKeyInString(object, string) {
313 for (var key in object) {
314 if (stringContains(string, key))
315 return true;
316 }
317 return false;
318 }
319
320 /** 481 /**
321 * Returns whether the given string of modifiers applies to the platform and 482 * Returns whether the given string of modifiers applies to the platform and
322 * build type of the given builder. 483 * build type of the given builder.
323 */ 484 */
324 function hasPlatformAndBuildType(builderName, modifiers) { 485 function hasPlatformAndBuildType(builderName, modifiers) {
325 var platformAndBuildType = getPlatFormAndBuildType(builderName); 486 var platformAndBuildType = getPlatFormAndBuildType(builderName);
326 var hasThisPlatform = stringContains(modifiers, 487 var hasThisPlatform = stringContains(modifiers,
327 platformAndBuildType.platform); 488 platformAndBuildType.platform);
328 var hasThisBuildType = stringContains(modifiers, 489 var hasThisBuildType = stringContains(modifiers,
329 platformAndBuildType.buildType); 490 platformAndBuildType.buildType);
(...skipping 248 matching lines...) Expand 10 before | Expand all | Expand 10 after
578 } 739 }
579 740
580 resultsForTest.bugsHTML += 741 resultsForTest.bugsHTML +=
581 htmlArrays.bugs.join('<div class=separator></div>'); 742 htmlArrays.bugs.join('<div class=separator></div>');
582 resultsForTest.expectationsHTML += 743 resultsForTest.expectationsHTML +=
583 htmlArrays.expectations.join('<div class=separator></div>'); 744 htmlArrays.expectations.join('<div class=separator></div>');
584 resultsForTest.modifiersHTML += 745 resultsForTest.modifiersHTML +=
585 htmlArrays.modifiers.join('<div class=separator></div>'); 746 htmlArrays.modifiers.join('<div class=separator></div>');
586 } 747 }
587 748
588 var rawResults = resultsByBuilder[builderName].tests[test].results; 749 var rawTest = resultsByBuilder[builderName].tests[test];
750 resultsForTest.rawTimes = rawTest.times;
751 var rawResults = rawTest.results;
589 resultsForTest.rawResults = rawResults; 752 resultsForTest.rawResults = rawResults;
590 var results = rawResults.split(''); 753 resultsForTest.flips = rawResults.length - 1;
591 754
592 var unexpectedExpectations = []; 755 var unexpectedExpectations = [];
593 var resultsMap = {} 756 var resultsMap = {}
594 for (var i = 0; i < results.length - 1; i++) { 757 for (var i = 0; i < rawResults.length; i++) {
595 if (results[i] != results[i + 1]) 758 var expectation = getExpectationsFileStringForResult(rawResults[i][1]);
596 resultsForTest.flips++;
597
598 var expectation = getExpectationsFileStringForResult(results[i]);
599 resultsMap[expectation] = true; 759 resultsMap[expectation] = true;
600 } 760 }
601 761
602 var expectationsArray = resultsForTest.expectations ? 762 var expectationsArray = resultsForTest.expectations ?
603 resultsForTest.expectations.split(' ') : []; 763 resultsForTest.expectations.split(' ') : [];
604 var extraExpectations = expectationsArray.filter( 764 var extraExpectations = expectationsArray.filter(
605 function(element) { 765 function(element) {
606 return element && !resultsMap[element] && 766 return element && !resultsMap[element] &&
607 !stringContains(element, 'BUG'); 767 !stringContains(element, 'BUG');
608 }); 768 });
609 769
610 var missingExpectations = []; 770 var missingExpectations = [];
611 for (var result in resultsMap) { 771 for (var result in resultsMap) {
612 var hasExpectation = false; 772 var hasExpectation = false;
613 for (var i = 0; i < expectationsArray.length; i++) { 773 for (var i = 0; i < expectationsArray.length; i++) {
614 if (result == expectationsArray[i]) 774 if (result == expectationsArray[i])
615 hasExpectation = true; 775 hasExpectation = true;
616 } 776 }
617 if (!hasExpectation) 777 if (!hasExpectation)
618 missingExpectations.push(result); 778 missingExpectations.push(result);
619 } 779 }
620 780
621 // TODO(ojan): Make this detect the case of a test that has NODATA, 781 var times = resultsByBuilder[builderName].tests[test].times;
622 // then fails for a few runs, then passes for the rest. We should 782 for (var i = 0; i < times.length; i++) {
623 // consider that as meetsExpectations since every new test will have 783 resultsForTest.slowestTime = Math.max(resultsForTest.slowestTime,
624 // that pattern. 784 times[i][1]);
785 }
786
787 if (resultsForTest.slowestTime &&
788 (!resultsForTest.expectations ||
789 !stringContains(resultsForTest.expectations, 'TIMEOUT')) &&
790 (!resultsForTest.modifiers ||
791 !stringContains(resultsForTest.modifiers, 'SLOW'))) {
792 missingExpectations.push('SLOW');
793 }
794
625 resultsForTest.meetsExpectations = 795 resultsForTest.meetsExpectations =
626 !missingExpectations.length && !extraExpectations.length; 796 !missingExpectations.length && !extraExpectations.length;
627 resultsForTest.missing = missingExpectations.sort().join(' '); 797 resultsForTest.missing = missingExpectations.sort().join(' ');
628 resultsForTest.extra = extraExpectations.sort().join(' '); 798 resultsForTest.extra = extraExpectations.sort().join(' ');
629 799
630 var times = resultsByBuilder[builderName].tests[test].times;
631 resultsForTest.slowestTime = Math.max.apply(null, times)
632
633 resultsForTest.html = getHtmlForTestResults(builderName, test);
634
635 failures.push(resultsForTest); 800 failures.push(resultsForTest);
636 801
637 if (!testToResultsMap[test]) 802 if (!testToResultsMap[test])
638 testToResultsMap[test] = []; 803 testToResultsMap[test] = [];
639 testToResultsMap[test].push( 804 testToResultsMap[test].push(
640 {builder: builderName, results: resultsForTest}); 805 {builder: builderName, results: resultsForTest});
641 } 806 }
642 807
643 perBuilderFailures[builderName] = failures; 808 perBuilderFailures[builderName] = failures;
644 logTime('processTestRunsForBuilder: ' + builderName, start); 809 logTime('processTestRunsForBuilder: ' + builderName, start);
(...skipping 20 matching lines...) Expand all
665 * are internal google bugs. 830 * are internal google bugs.
666 */ 831 */
667 function getHtmlForBugs(bugs) { 832 function getHtmlForBugs(bugs) {
668 bugs = bugs.replace(/BUG(\d{4})(\ |$)/g, externalBugReplaceValue); 833 bugs = bugs.replace(/BUG(\d{4})(\ |$)/g, externalBugReplaceValue);
669 bugs = bugs.replace(/BUG(\d{5})(\ |$)/g, externalBugReplaceValue); 834 bugs = bugs.replace(/BUG(\d{5})(\ |$)/g, externalBugReplaceValue);
670 bugs = bugs.replace(/BUG(\d{6})(\ |$)/g, internalBugReplaceValue); 835 bugs = bugs.replace(/BUG(\d{6})(\ |$)/g, internalBugReplaceValue);
671 bugs = bugs.replace(/BUG(\d{7})(\ |$)/g, internalBugReplaceValue); 836 bugs = bugs.replace(/BUG(\d{7})(\ |$)/g, internalBugReplaceValue);
672 return bugs; 837 return bugs;
673 } 838 }
674 839
675 function didTestPassAllRuns(builderName, testPath) {
676 var numBuilds = resultsByBuilder[builderName].buildNumbers.length;
677 var passingResults = Array(numBuilds + 1).join('P');
678 var results = resultsByBuilder[builderName].tests[testPath].results;
679 return results == passingResults;
680 }
681
682 function loadBuilderPageForBuildNumber(builderName, buildNumber) { 840 function loadBuilderPageForBuildNumber(builderName, buildNumber) {
683 window.open(BUILDERS_BASE_PATH + builderName + '/builds/' + buildNumber); 841 window.open(BUILDERS_BASE_PATH + builderName + '/builds/' + buildNumber);
684 } 842 }
685 843
686 function getHtmlForTestResults(builderName, testPath) { 844 function getHtmlForTestResults(test, builder) {
687 var html = ''; 845 var html = '';
688 var test = resultsByBuilder[builderName].tests[testPath]; 846 var results = test.rawResults.concat();
689 var results = test.results.split(''); 847 var times = test.rawTimes.concat();
690 var times = test.times; 848 var buildNumbers = resultsByBuilder[builder].buildNumbers;
691 var buildNumbers = resultsByBuilder[builderName].buildNumbers; 849
692 for (var i = 0; i < results.length; i++) { 850 var indexToReplaceCurrentResult = -1;
851 var indexToReplaceCurrentTime = -1;
852 var currentResultArray, currentTimeArray, currentResult, innerHTML;
853 for (var i = 0;
854 i < buildNumbers.length && i < currentState.maxResults;
855 i++) {
856 if (i > indexToReplaceCurrentResult) {
857 currentResultArray = results.shift();
858 if (currentResultArray) {
859 currentResult = currentResultArray[1];
860 indexToReplaceCurrentResult += currentResultArray[0];
861 } else {
862 currentResult = 'N';
863 indexToReplaceCurrentResult += buildNumbers.length;
864 }
865 }
866
867 if (i > indexToReplaceCurrentTime) {
868 currentTimeArray = times.shift();
869 var currentTime = 0;
870 if (currentResultArray) {
871 currentTime = currentTimeArray[1];
872 indexToReplaceCurrentTime += currentTimeArray[0];
873 } else {
874 indexToReplaceCurrentTime += buildNumbers.length;
875 }
876 innerHTML = currentTime || '&nbsp;';
877 }
878
693 var buildNumber = buildNumbers[i]; 879 var buildNumber = buildNumbers[i];
694 var innerHTML = times[i] > 0 ? times[i] : '&nbsp;';
695 html += '<td title="Build:' + buildNumber + '" class="results ' + 880 html += '<td title="Build:' + buildNumber + '" class="results ' +
696 results[i] + '" onclick=\'loadBuilderPageForBuildNumber("' + 881 currentResult + '" onclick=\'loadBuilderPageForBuildNumber("' +
697 builderName + '","' + buildNumber + '")\'>' + innerHTML + '</td>'; 882 builder + '","' + buildNumber + '")\'>' + innerHTML + '</td>';
698 } 883 }
699 return html; 884 return html;
700 } 885 }
701 886
702 function getHTMLForTestsWithExpectationsButNoFailures(builder) { 887 function getHTMLForTestsWithExpectationsButNoFailures(builder) {
703 var tests = perBuilderWithExpectationsButNoFailures[builder]; 888 var tests = perBuilderWithExpectationsButNoFailures[builder];
704 if (!tests.length) 889 if (!tests.length)
705 return ''; 890 return '';
706 891
707 var buildInfo = getPlatFormAndBuildType(builder); 892 var buildInfo = getPlatFormAndBuildType(builder);
708 return '<h2>Have expectations for ' + buildInfo.platform + '-' + 893 return '<h2>Have expectations for ' + buildInfo.platform + '-' +
709 buildInfo.buildType + ' but have not failed in last ' + 894 buildInfo.buildType + ' but have not failed in last ' +
710 resultsByBuilder[builderName].buildNumbers.length + 895 resultsByBuilder[builderName].buildNumbers.length +
711 ' runs.</h2><div id="passing-tests"><div>' + 896 ' runs.</h2><div id="passing-tests"><div>' +
712 tests.join('</div><div>') + '</div></div>'; 897 tests.join('</div><div>') + '</div></div>';
713 } 898 }
714 899
715 /** 900 /**
716 * Returns true if a test should be considered flaky. Uses heuristics to 901 * Returns true if a test should be considered flaky. Uses heuristics to
717 * avoid common non-flaky cases. 902 * avoid common non-flaky cases.
718 */ 903 */
719 function isTestFlaky(testResult) { 904 function isTestFlaky(testResult) {
720 return testResult.flips > 1 && !isFixedNewTest(testResult); 905 return testResult.flips > 1 && !isFixedTest(testResult);
721 } 906 }
722 907
723 /** 908 /**
724 * Returns whether this tests results match the heuristic for new tests that 909 * Returns whether this tests results match the heuristic for new tests that
725 * have been fixed. Specifically, a new test that fails a couple 910 * have been fixed. Specifically, a new test that fails a couple
726 * times and then passes from then on would have results like PPPPFFFFNNNNN. 911 * times and then passes from then on would have results like PPPPFFFFNNNNN.
727 * Where that middle part can be a series of F's, S's or I's for the 912 * Where that middle part can be a series of F's, S's or I's for the
728 * different types of failures. 913 * different types of failures.
729 */ 914 */
730 function isFixedNewTest(testResult) { 915 function isFixedTest(testResult) {
731 if (testResult.isFixedNewTest === undefined) { 916 if (testResult.isFixedTest === undefined) {
732 testResult.isFixedNewTest = 917 var results = testResult.rawResults;
733 testResult.rawResults.match(/^P+(S+|F+|I+)N+$/); 918 var isFixedTest = results[0][1] == 'P';
919 if (isFixedTest && results.length > 1) {
920 var secondResult = results[1][1];
921 isFixedTest = secondResult == 'S' || secondResult == 'F' ||
922 secondResult == 'I';
923 }
924 if (isFixedTest && results.length > 2) {
925 isFixedTest = results.length == 3 && results[2][1] == 'N';
926 }
927 testResult.isFixedTest = isFixedTest;
734 } 928 }
735 return testResult.isFixedNewTest; 929 return testResult.isFixedTest;
736 } 930 }
737 931
738 /** 932 /**
739 * Returns whether we should exclude test results from the test table. 933 * Returns whether we should exclude test results from the test table.
740 * Note that we never want to exclude tests when we're in the individual 934 * Note that we never want to exclude tests when we're in the individual
741 * tests view of the dashboard since the user is explicitly listing tests 935 * tests view of the dashboard since the user is explicitly listing tests
742 * to view. 936 * to view.
743 */ 937 */
744 function shouldHideTest(testResult) { 938 function shouldHideTest(testResult) {
745 if (currentState.tests) 939 if (currentState.tests)
746 return false; 940 return false;
747 941
748 if (testResult.isWontFix && !currentState.showWontFix) 942 if (testResult.isWontFix && !currentState.showWontFix)
749 return true; 943 return true;
750 944
751 if ((testResult.meetsExpectations || isFixedNewTest(testResult)) && 945 if ((testResult.meetsExpectations || isFixedTest(testResult)) &&
752 !currentState.showCorrectExpectations) { 946 !currentState.showCorrectExpectations) {
753 // Only hide flaky tests that match their expectations if showFlaky 947 // Only hide flaky tests that match their expectations if showFlaky
754 // is false. 948 // is false.
755 return !currentState.showFlaky || !isTestFlaky(testResult); 949 return !currentState.showFlaky || !isTestFlaky(testResult);
756 } 950 }
757 951
758 return !currentState.showFlaky && isTestFlaky(testResult); 952 return !currentState.showFlaky && isTestFlaky(testResult);
759 } 953 }
760 954
761 function getHTMLForSingleTestRow(test, opt_builder) { 955 function getHTMLForSingleTestRow(test, builder, opt_isCrossBuilderView) {
762 if (shouldHideTest(test)) { 956 if (shouldHideTest(test)) {
763 // The innerHTML call is considerably faster if we exclude the rows for 957 // The innerHTML call is considerably faster if we exclude the rows for
764 // items we're not showing than if we hide them using display:none. 958 // items we're not showing than if we hide them using display:none.
765 return ''; 959 return '';
766 } 960 }
767 961
768 // If opt_builder is provided, we're just viewing a single test 962 // If opt_isCrossBuilderView is true, we're just viewing a single test
769 // with results for many builders, so the first column is builder names 963 // with results for many builders, so the first column is builder names
770 // instead of test paths. 964 // instead of test paths.
771 var testCellHTML = opt_builder ? opt_builder : 965 var testCellHTML = opt_isCrossBuilderView ? builder :
772 '<span class="link" onclick="setState(\'tests\', \'' + test.test + 966 '<span class="link" onclick="setState(\'tests\', \'' + test.test +
773 '\');return false;">' + test.test + '</span>'; 967 '\');return false;">' + test.test + '</span>';
774 968
775 return '<tr class="' + 969 return '<tr class="' +
776 (test.meetsExpectations ? '' : 'wrong-expectations') + 970 (test.meetsExpectations ? '' : 'wrong-expectations') +
777 // TODO(ojan): If a test is a chrome/ or a pending/ test, point to 971 // TODO(ojan): If a test is a chrome/ or a pending/ test, point to
778 // src.chromium.org instead of trac.webkit.org. 972 // src.chromium.org instead of trac.webkit.org.
779 '"><td class=test-link>' + testCellHTML + 973 '"><td class=test-link>' + testCellHTML +
780 '</td><td class=options-container>' + test.bugsHTML + 974 '</td><td class=options-container>' + test.bugsHTML +
781 '</td><td class=options-container>' + test.modifiersHTML + 975 '</td><td class=options-container>' + test.modifiersHTML +
782 '</td><td class=options-container>' + test.expectationsHTML + 976 '</td><td class=options-container>' + test.expectationsHTML +
783 '</td><td>' + test.missing + 977 '</td><td>' + test.missing +
784 '</td><td>' + test.extra + 978 '</td><td>' + test.extra +
785 '</td><td>' + (test.slowestTime ? test.slowestTime + 's' : '') + 979 '</td><td>' + (test.slowestTime ? test.slowestTime + 's' : '') +
786 '</td>' + 980 '</td>' + getHtmlForTestResults(test, builder) + '</tr>';
787 test.html +
788 '</tr>';
789 } 981 }
790 982
791 function getSortColumnFromTableHeader(headerText) { 983 function getSortColumnFromTableHeader(headerText) {
792 return headerText.split(' ', 1)[0]; 984 return headerText.split(' ', 1)[0];
793 } 985 }
794 986
795 function getHTMLForTestTable(rowsHTML) { 987 function getHTMLForTestTable(rowsHTML) {
796 var html = '<table class=test-table><thead><tr>'; 988 var html = '<table class=test-table><thead><tr>';
797 for (var i = 0; i < tableHeaders.length; i++) { 989 for (var i = 0; i < tableHeaders.length; i++) {
798 // Use the first word of the header title as the sortkey 990 // Use the first word of the header title as the sortkey
(...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after
883 resultsProperty = 'slowestTime'; 1075 resultsProperty = 'slowestTime';
884 } else { 1076 } else {
885 sortFunctionGetter = getAlphanumericCompare; 1077 sortFunctionGetter = getAlphanumericCompare;
886 resultsProperty = column; 1078 resultsProperty = column;
887 } 1079 }
888 1080
889 tests.sort(sortFunctionGetter(resultsProperty, order == BACKWARD)); 1081 tests.sort(sortFunctionGetter(resultsProperty, order == BACKWARD));
890 } 1082 }
891 1083
892 function generatePage() { 1084 function generatePage() {
893 parseCurrentLocation();
894
895 // Only continue if all the JSON files have loaded. 1085 // Only continue if all the JSON files have loaded.
896 if (!expectationsLoaded) 1086 if (!expectationsLoaded)
897 return; 1087 return;
898 1088
899 for (var build in builders) { 1089 for (var build in builders) {
900 if (!resultsByBuilder[build]) 1090 if (!resultsByBuilder[build])
901 return; 1091 return;
902 } 1092 }
903 1093
904 tableHeaders.shift(); 1094 tableHeaders.shift();
(...skipping 18 matching lines...) Expand all
923 'BUILDER DOES NOT RUN THAT TEST OR ALL RUNS OF THE TEST PASSED.</b>'; 1113 'BUILDER DOES NOT RUN THAT TEST OR ALL RUNS OF THE TEST PASSED.</b>';
924 1114
925 for (var i = 0; i < tests.length; i++) { 1115 for (var i = 0; i < tests.length; i++) {
926 html += '<h2>' + tests[i] + '</h2>'; 1116 html += '<h2>' + tests[i] + '</h2>';
927 1117
928 var testResults = testToResultsMap[tests[i]]; 1118 var testResults = testToResultsMap[tests[i]];
929 if (testResults && testResults.length) { 1119 if (testResults && testResults.length) {
930 var tableRowsHTML = ''; 1120 var tableRowsHTML = '';
931 for (var j = 0; j < testResults.length; j++) { 1121 for (var j = 0; j < testResults.length; j++) {
932 tableRowsHTML += getHTMLForSingleTestRow(testResults[j].results, 1122 tableRowsHTML += getHTMLForSingleTestRow(testResults[j].results,
933 testResults[j].builder); 1123 testResults[j].builder, true);
934 } 1124 }
935 html += getHTMLForTestTable(tableRowsHTML); 1125 html += getHTMLForTestTable(tableRowsHTML);
936 } else { 1126 } else {
937 html +='<div class="not-found">Test not found. Either it does not ' + 1127 html +='<div class="not-found">Test not found. Either it does not ' +
938 'exist or it passes on all platforms.</div>'; 1128 'exist or it passes on all platforms.</div>';
939 } 1129 }
940 } 1130 }
941 setFullPageHTML(html); 1131 setFullPageHTML(html);
942 1132
943 $('tests-input').value = currentState.tests; 1133 $('tests-input').value = currentState.tests;
944 } 1134 }
945 1135
946 function getHTMLForNavBar(opt_builderName) { 1136 function getHTMLForNavBar(opt_builderName) {
947 var html = '<div id=builders>'; 1137 var html = '<div id=builders>';
948 for (var builder in builders) { 1138 for (var builder in builders) {
949 var className = builder == opt_builderName ? 'current-builder' : 'link'; 1139 var className = builder == opt_builderName ? 'current-builder' : 'link';
950 html += '<span class=' + className + 1140 html += '<span class=' + className +
951 ' onclick=\'setState("builder", "' + builder + '")\'>' + 1141 ' onclick=\'setState("builder", "' + builder + '")\'>' +
952 builder + '</span>'; 1142 builder + '</span>';
953 } 1143 }
954 html += '</div>' + 1144 html += '</div>' +
955 '<form onsubmit="setState(\'tests\', tests.value);return false;">' + 1145 '<form id=tests-form ' +
1146 'onsubmit="setState(\'tests\', tests.value);return false;">' +
956 '<div>Show tests on all platforms (slow): </div><input name=tests ' + 1147 '<div>Show tests on all platforms (slow): </div><input name=tests ' +
957 'placeholder="LayoutTests/foo/bar.html,LayoutTests/foo/baz.html" ' + 1148 'placeholder="LayoutTests/foo/bar.html,LayoutTests/foo/baz.html" ' +
958 'id=tests-input></form><div id="loading-ui">LOADING...</div>' + 1149 'id=tests-input></form><div id="loading-ui">LOADING...</div>' +
959 '<div id=legend>'; 1150 '<div id=legend>';
960 1151
961 for (var expectation in EXPECTATIONS_MAP) { 1152 for (var expectation in EXPECTATIONS_MAP) {
962 html += '<div class=' + expectation + '>' + 1153 html += '<div class=' + expectation + '>' +
963 EXPECTATIONS_MAP[expectation] + '</div>'; 1154 EXPECTATIONS_MAP[expectation] + '</div>';
964 } 1155 }
965 return html + '<div class=wrong-expectations>WRONG EXPECTATIONS</div>' + 1156 return html + '<div class=wrong-expectations>WRONG EXPECTATIONS</div>' +
966 '</div>'; 1157 '</div>';
967 } 1158 }
968 1159
969 function getLinkHTMLToToggleState(key, linkText) { 1160 function getLinkHTMLToToggleState(key, linkText) {
970 var isTrue = currentState[key]; 1161 var isTrue = currentState[key];
971 return '<span class=link onclick="setState(\'' + key + '\', \'' + !isTrue + 1162 return '<span class=link onclick="setState(\'' + key + '\', ' + !isTrue +
972 '\')">' + (isTrue ? 'Hide' : 'Show') + ' ' + linkText + '</span> | '; 1163 ')">' + (isTrue ? 'Hide' : 'Show') + ' ' + linkText + '</span> | ';
973 } 1164 }
974 1165
975 function generatePageForBuilder(builderName) { 1166 function generatePageForBuilder(builderName) {
976 processTestRunsForBuilder(builderName); 1167 processTestRunsForBuilder(builderName);
977 1168
978 var tableRowsHTML = ''; 1169 var tableRowsHTML = '';
979 var results = perBuilderFailures[builderName]; 1170 var results = perBuilderFailures[builderName];
980 sortTests(results, currentState.sortColumn, currentState.sortOrder); 1171 sortTests(results, currentState.sortColumn, currentState.sortOrder);
981 for (var i = 0; i < results.length; i++) { 1172 for (var i = 0; i < results.length; i++) {
982 tableRowsHTML += getHTMLForSingleTestRow(results[i]); 1173 tableRowsHTML += getHTMLForSingleTestRow(results[i], builderName);
983 } 1174 }
984 1175
1176 var testsHTML = tableRowsHTML ? getHTMLForTestTable(tableRowsHTML) :
1177 '<div>No tests. Try showing tests with correct expectations.</div>';
1178
985 var html = getHTMLForNavBar(builderName) + 1179 var html = getHTMLForNavBar(builderName) +
986 getHTMLForTestsWithExpectationsButNoFailures(builderName) + 1180 getHTMLForTestsWithExpectationsButNoFailures(builderName) +
987 '<h2>Failing tests</h2><div>' + 1181 '<h2>Failing tests</h2><div>' +
988 getLinkHTMLToToggleState('showWontFix', 'WONTFIX tests') + 1182 getLinkHTMLToToggleState('showWontFix', 'WONTFIX tests') +
989 getLinkHTMLToToggleState('showCorrectExpectations', 1183 getLinkHTMLToToggleState('showCorrectExpectations',
990 'tests with correct expectations') + 1184 'tests with correct expectations') +
991 getLinkHTMLToToggleState('showFlaky', 'flaky tests') + 1185 getLinkHTMLToToggleState('showFlaky', 'flaky tests') +
1186 '<form id=max-results-form ' +
1187 'onsubmit="setState(\'maxResults\', maxResults.value);return false;"' +
1188 '><span>Max results to show: </span>' +
1189 '<input name=maxResults id=max-results-input></form> | ' +
992 '<b>All columns are sortable. | Skipped tests are not listed. | ' + 1190 '<b>All columns are sortable. | Skipped tests are not listed. | ' +
993 'Flakiness reader order is newer --> older runs.</b></div>' + 1191 'Flakiness reader order is newer --> older runs.</b></div>' +
994 getHTMLForTestTable(tableRowsHTML); 1192 testsHTML;
995 1193
996 setFullPageHTML(html); 1194 setFullPageHTML(html);
997 1195
998 var ths = document.getElementsByTagName('th'); 1196 var ths = document.getElementsByTagName('th');
999 for (var i = 0; i < ths.length; i++) { 1197 for (var i = 0; i < ths.length; i++) {
1000 ths[i].addEventListener('click', changeSort, false); 1198 ths[i].addEventListener('click', changeSort, false);
1001 ths[i].className = "sortable"; 1199 ths[i].className = "sortable";
1002 } 1200 }
1003 }
1004 1201
1005 // Permalinkable state of the page. 1202 $('max-results-input').value = currentState.maxResults;
1006 var currentState = {};
1007
1008 var defaultStateValues = {
1009 sortOrder: BACKWARD,
1010 sortColumn: 'flakiness',
1011 showWontFix: false,
1012 showCorrectExpectations: false,
1013 showFlaky: true
1014 };
1015
1016 for (var builder in builders) {
1017 defaultStateValues.builder = builder;
1018 break;
1019 }
1020
1021 function fillDefaultStateValues() {
1022 // tests has no states with default values.
1023 if (currentState.tests)
1024 return;
1025
1026 for (var state in defaultStateValues) {
1027 if (!(state in currentState))
1028 currentState[state] = defaultStateValues[state];
1029 }
1030 } 1203 }
1031 1204
1032 /** 1205 /**
1033 * Sets the page state and regenerates the page. Takes varargs of key, value 1206 * Sets the page state and regenerates the page. Takes varargs of key, value
1034 * pairs. 1207 * pairs.
1035 */ 1208 */
1036 function setState(key, value) { 1209 function setState(key, value) {
1037 for (var i = 0; i < arguments.length; i = i + 2) { 1210 for (var i = 0; i < arguments.length; i = i + 2) {
1038 var key = arguments[i]; 1211 var key = arguments[i];
1039 if (key == 'tests') { 1212 if (key == 'tests') {
1040 for (var state in currentState) { 1213 for (var state in currentState) {
1041 delete currentState[state]; 1214 delete currentState[state];
1042 } 1215 }
1043 } else { 1216 } else {
1044 delete currentState.tests; 1217 delete currentState.tests;
1045 } 1218 }
1046 1219
1047 currentState[key] = arguments[i + 1]; 1220 currentState[key] = arguments[i + 1];
1048 } 1221 }
1222 window.location.replace(getPermaLinkURL());
1223 handleLocationChange();
1224 }
1049 1225
1226 function handleLocationChange() {
1050 $('loading-ui').style.display = 'block'; 1227 $('loading-ui').style.display = 'block';
1051 setTimeout(function() { 1228 setTimeout(function() {
1052 window.location.replace(getPermaLinkURL()); 1229 oldLocation = window.location.href;
1053 generatePage(); 1230 generatePage();
1054 $('loading-ui').style.display = 'none';
1055 }, 0); 1231 }, 0);
1056 } 1232 }
1057 1233
1058 function parseCurrentLocation() { 1234 function getPermaLinkURL() {
1059 oldLocation = window.location.href; 1235 return window.location.pathname + '?' + joinParameters(queryState) + '#' +
1060 var hash = window.location.hash; 1236 joinParameters(currentState);
1061 if (!hash) {
1062 fillDefaultStateValues();
1063 return;
1064 }
1065
1066 var hashParts = hash.slice(1).split('&');
1067 var urlHasTests = false;
1068 for (var i = 0; i < hashParts.length; i++) {
1069 var itemParts = hashParts[i].split('=');
1070 if (itemParts.length != 2) {
1071 console.log("Invalid parameter: " + hashParts[i]);
1072 continue;
1073 }
1074
1075 var key = itemParts[0];
1076 var value = decodeURIComponent(itemParts[1]);
1077 switch(key) {
1078 case 'tests':
1079 validateParameter(key, value,
1080 function() { return value.match(/[A-Za-z0-9\-\_,]/); });
1081 break;
1082
1083 case 'builder':
1084 validateParameter(key, value,
1085 function() { return value in builders; });
1086 break;
1087
1088 case 'sortColumn':
1089 validateParameter(key, value,
1090 function() {
1091 for (var i = 0; i < tableHeaders.length; i++) {
1092 if (value == getSortColumnFromTableHeader(tableHeaders[i]))
1093 return true;
1094 }
1095 return value == 'test';
1096 });
1097 break;
1098
1099 case 'sortOrder':
1100 validateParameter(key, value,
1101 function() { return value == FORWARD || value == BACKWARD; });
1102 break;
1103
1104 case 'showWontFix':
1105 case 'showCorrectExpectations':
1106 case 'showFlaky':
1107 currentState[key] = value == 'true';
1108 break;
1109
1110 default:
1111 console.log('Invalid key: ' + key + ' value: ' + value);
1112 }
1113 }
1114 fillDefaultStateValues();
1115 } 1237 }
1116 1238
1117 function validateParameter(key, value, validateFn) { 1239 function joinParameters(stateObject) {
1118 if (validateFn()) { 1240 var state = [];
1119 currentState[key] = value; 1241 for (var key in stateObject) {
1120 } else { 1242 state.push(key + '=' + encodeURIComponent(stateObject[key]));
1121 console.log(key + ' value is not valid: ' + value);
1122 } 1243 }
1123 } 1244 return state.join('&');
1124
1125 function getPermaLinkURL() {
1126 var state = [];
1127 for (var key in currentState) {
1128 state.push(key + '=' + currentState[key]);
1129 }
1130 return window.location.pathname + '#' + state.join('&');
1131 } 1245 }
1132 1246
1133 function logTime(msg, startTime) { 1247 function logTime(msg, startTime) {
1134 console.log(msg + ': ' + (Date.now() - startTime)); 1248 console.log(msg + ': ' + (Date.now() - startTime));
1135 } 1249 }
1136 1250
1137 window.onload = function() { 1251 window.onload = function() {
1138 // This doesn't seem totally accurate as there is a race between 1252 // This doesn't seem totally accurate as there is a race between
1139 // onload firing and the last script tag being executed. 1253 // onload firing and the last script tag being executed.
1140 logTime('Time to load JS', pageLoadStartTime); 1254 logTime('Time to load JS', pageLoadStartTime);
1141 setInterval(function() { 1255 setInterval(function() {
1142 if (oldLocation != window.location) 1256 if (oldLocation != window.location.href) {
1143 generatePage(); 1257 parseAllParameters();
1258 handleLocationChange();
1259 }
1144 }, 100); 1260 }, 100);
1145 } 1261 }
1146 </script> 1262 </script>
1147 </head> 1263 </head>
1148 1264
1149 <body></body> 1265 <body></body>
1150 </html> 1266 </html>
OLDNEW
« no previous file with comments | « no previous file | webkit/tools/layout_tests/layout_package/json_results_generator.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698