blob: 082ddf219112f3aef234cfb040ccb41dfa47e4ff [file] [log] [blame]
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +00001// Copyright (C) 2012 Google Inc. All rights reserved.
2//
3// Redistribution and use in source and binary forms, with or without
4// modification, are permitted provided that the following conditions are
5// met:
6//
7// * Redistributions of source code must retain the above copyright
8// notice, this list of conditions and the following disclaimer.
9// * Redistributions in binary form must reproduce the above
10// copyright notice, this list of conditions and the following disclaimer
11// in the documentation and/or other materials provided with the
12// distribution.
13// * Neither the name of Google Inc. nor the names of its
14// contributors may be used to endorse or promote products derived from
15// this software without specific prior written permission.
16//
17// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
29//////////////////////////////////////////////////////////////////////////////
30// CONSTANTS
31//////////////////////////////////////////////////////////////////////////////
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +000032var FORWARD = 'forward';
33var BACKWARD = 'backward';
34var GTEST_MODIFIERS = ['FLAKY', 'FAILS', 'MAYBE', 'DISABLED'];
Torne (Richard Coles)93ac45c2013-05-29 14:40:20 +010035var TEST_URL_BASE_PATH_FOR_BROWSING = 'http://src.chromium.org/viewvc/blink/trunk/LayoutTests/';
36var TEST_URL_BASE_PATH_FOR_XHR = 'http://src.chromium.org/blink/trunk/LayoutTests/';
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +000037var TEST_RESULTS_BASE_PATH = 'http://build.chromium.org/f/chromium/layout_test_results/';
38var GPU_RESULTS_BASE_PATH = 'http://chromium-browser-gpu-tests.commondatastorage.googleapis.com/runs/'
39
Torne (Richard Coles)93ac45c2013-05-29 14:40:20 +010040var RELEASE_TIMEOUT = 6;
41var DEBUG_TIMEOUT = 12;
42var SLOW_MULTIPLIER = 5;
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +000043var CHUNK_SIZE = 25;
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +000044
45// FIXME: Figure out how to make this not be hard-coded.
Torne (Richard Coles)5267f702013-06-11 10:57:24 +010046// Probably just include in the results.json files and get it from there.
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +000047var VIRTUAL_SUITES = {
Torne (Richard Coles)93ac45c2013-05-29 14:40:20 +010048 'virtual/gpu/fast/canvas': 'fast/canvas',
Torne (Richard Coles)5267f702013-06-11 10:57:24 +010049 'virtual/gpu/canvas/philip': 'canvas/philip',
50 'virtual/threaded/compositing/visibility': 'compositing/visibility',
51 'virtual/threaded/compositing/webgl': 'compositing/webgl',
52 'virtual/gpu/fast/hidpi': 'fast/hidpi',
53 'virtual/softwarecompositing': 'compositing',
54 'virtual/deferred/fast/images': 'fast/images',
55 'virtual/gpu/compositedscrolling/overflow': 'compositing/overflow',
56 'virtual/gpu/compositedscrolling/scrollbars': 'scrollbars',
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +000057};
58
Torne (Richard Coles)5267f702013-06-11 10:57:24 +010059var ACTUAL_RESULT_SUFFIXES = ['expected.txt', 'expected.png', 'actual.txt', 'actual.png', 'diff.txt', 'diff.png', 'wdiff.html', 'crash-log.txt'];
60
61var EXPECTATIONS_ORDER = ACTUAL_RESULT_SUFFIXES.filter(function(suffix) {
62 return !string.endsWith(suffix, 'png');
63}).map(function(suffix) {
64 return suffix.split('.')[0]
65});
66
Torne (Richard Coles)926b0012013-03-28 15:32:48 +000067var resourceLoader;
68
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +010069function generatePage(historyInstance)
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +000070{
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +010071 if (historyInstance.crossDashboardState.useTestData)
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +000072 return;
73
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +000074 document.body.innerHTML = '<div id="loading-ui">LOADING...</div>';
Torne (Richard Coles)926b0012013-03-28 15:32:48 +000075 resourceLoader.showErrors();
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +000076
77 // tests expands to all tests that match the CSV list.
78 // result expands to all tests that ever have the given result
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +010079 if (historyInstance.dashboardSpecificState.tests || historyInstance.dashboardSpecificState.result)
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +000080 generatePageForIndividualTests(individualTests());
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +000081 else
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +010082 generatePageForBuilder(historyInstance.dashboardSpecificState.builder || currentBuilderGroup().defaultBuilder());
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +000083
Torne (Richard Coles)926b0012013-03-28 15:32:48 +000084 for (var builder in currentBuilders())
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +000085 processTestResultsForBuilderAsync(builder);
86
87 postHeightChangedMessage();
88}
89
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +010090function handleValidHashParameter(historyInstance, key, value)
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +000091{
92 switch(key) {
Torne (Richard Coles)93ac45c2013-05-29 14:40:20 +010093 case 'result':
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +000094 case 'tests':
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +010095 history.validateParameter(historyInstance.dashboardSpecificState, key, value,
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +000096 function() {
Torne (Richard Coles)926b0012013-03-28 15:32:48 +000097 return string.isValidName(value);
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +000098 });
99 return true;
100
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000101 case 'builder':
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +0100102 history.validateParameter(historyInstance.dashboardSpecificState, key, value,
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000103 function() {
Torne (Richard Coles)926b0012013-03-28 15:32:48 +0000104 return value in currentBuilders();
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000105 });
Torne (Richard Coles)926b0012013-03-28 15:32:48 +0000106
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000107 return true;
108
109 case 'sortColumn':
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +0100110 history.validateParameter(historyInstance.dashboardSpecificState, key, value,
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000111 function() {
112 // Get all possible headers since the actual used set of headers
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +0100113 // depends on the values in historyInstance.dashboardSpecificState, which are currently being set.
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000114 var headers = tableHeaders(true);
115 for (var i = 0; i < headers.length; i++) {
116 if (value == sortColumnFromTableHeader(headers[i]))
117 return true;
118 }
119 return value == 'test' || value == 'builder';
120 });
121 return true;
122
123 case 'sortOrder':
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +0100124 history.validateParameter(historyInstance.dashboardSpecificState, key, value,
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000125 function() {
126 return value == FORWARD || value == BACKWARD;
127 });
128 return true;
129
130 case 'resultsHeight':
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000131 case 'revision':
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +0100132 history.validateParameter(historyInstance.dashboardSpecificState, key, Number(value),
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000133 function() {
134 return value.match(/^\d+$/);
135 });
136 return true;
137
138 case 'showChrome':
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000139 case 'showExpectations':
140 case 'showFlaky':
141 case 'showLargeExpectations':
Torne (Richard Coles)93ac45c2013-05-29 14:40:20 +0100142 case 'showNonFlaky':
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000143 case 'showSlow':
Torne (Richard Coles)93ac45c2013-05-29 14:40:20 +0100144 case 'showSkip':
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000145 case 'showUnexpectedPasses':
Torne (Richard Coles)93ac45c2013-05-29 14:40:20 +0100146 case 'showWontFix':
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +0100147 historyInstance.dashboardSpecificState[key] = value == 'true';
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000148 return true;
149
150 default:
151 return false;
152 }
153}
154
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +0100155// @param {Object} params New or modified query parameters as key: value.
156function handleQueryParameterChange(historyInstance, params)
157{
158 for (key in params) {
159 if (key == 'tests') {
160 // Entering cross-builder view, only keep valid keys for that view.
161 for (var currentKey in historyInstance.dashboardSpecificState) {
162 if (isInvalidKeyForCrossBuilderView(currentKey)) {
163 delete historyInstance.dashboardSpecificState[currentKey];
164 }
165 }
166 } else if (isInvalidKeyForCrossBuilderView(key)) {
167 delete historyInstance.dashboardSpecificState.tests;
168 delete historyInstance.dashboardSpecificState.result;
169 }
170 }
171
172 return true;
173}
174
175var defaultDashboardSpecificStateValues = {
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000176 sortOrder: BACKWARD,
177 sortColumn: 'flakiness',
178 showExpectations: false,
Torne (Richard Coles)93ac45c2013-05-29 14:40:20 +0100179 // FIXME: Show flaky tests by default if you have a builder picked.
180 // Ideally, we'd fix the dashboard to not pick a default builder and have
181 // you pick one. In the interim, this is a good way to make the default
182 // page load faster since we don't need to generate/layout a large table.
183 showFlaky: false,
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000184 showLargeExpectations: false,
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000185 showChrome: true,
Torne (Richard Coles)93ac45c2013-05-29 14:40:20 +0100186 showWontFix: false,
187 showNonFlaky: false,
188 showSkip: false,
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +0100189 showUnexpectedPasses: false,
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000190 resultsHeight: 300,
191 revision: null,
192 tests: '',
193 result: '',
Torne (Richard Coles)926b0012013-03-28 15:32:48 +0000194 builder: null
195};
196
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +0100197var DB_SPECIFIC_INVALIDATING_PARAMETERS = {
Torne (Richard Coles)926b0012013-03-28 15:32:48 +0000198 'tests' : 'builder',
199 'testType': 'builder',
200 'group': 'builder'
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000201};
202
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +0100203var flakinessConfig = {
204 defaultStateValues: defaultDashboardSpecificStateValues,
205 generatePage: generatePage,
206 handleValidHashParameter: handleValidHashParameter,
207 handleQueryParameterChange: handleQueryParameterChange,
208 invalidatingHashParameters: DB_SPECIFIC_INVALIDATING_PARAMETERS
209};
210
211// FIXME(jparent): Eventually remove all usage of global history object.
212var g_history = new history.History(flakinessConfig);
213g_history.parseCrossDashboardParameters();
214
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000215//////////////////////////////////////////////////////////////////////////////
216// GLOBALS
217//////////////////////////////////////////////////////////////////////////////
218
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000219var g_perBuilderFailures = {};
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000220// Maps test path to an array of {builder, testResults} objects.
221var g_testToResultsMap = {};
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000222
223function createResultsObjectForTest(test, builder)
224{
225 return {
226 test: test,
227 builder: builder,
228 // HTML for display of the results in the flakiness column
229 html: '',
230 flips: 0,
231 slowestTime: 0,
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000232 isFlaky: false,
Torne (Richard Coles)93ac45c2013-05-29 14:40:20 +0100233 bugs: [],
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000234 expectations : '',
235 rawResults: '',
236 // List of all the results the test actually has.
237 actualResults: []
238 };
239}
240
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000241var TestTrie = function(builders, resultsByBuilder)
242{
243 this._trie = {};
244
245 for (var builder in builders) {
Torne (Richard Coles)5267f702013-06-11 10:57:24 +0100246 if (!resultsByBuilder[builder]) {
247 console.warn("No results for builder: ", builder)
248 continue;
249 }
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000250 var testsForBuilder = resultsByBuilder[builder].tests;
251 for (var test in testsForBuilder)
252 this._addTest(test.split('/'), this._trie);
253 }
254}
255
256TestTrie.prototype.forEach = function(callback, startingTriePath)
257{
258 var testsTrie = this._trie;
259 if (startingTriePath) {
260 var splitPath = startingTriePath.split('/');
261 while (splitPath.length && testsTrie)
262 testsTrie = testsTrie[splitPath.shift()];
263 }
264
265 if (!testsTrie)
266 return;
267
268 function traverse(trie, triePath) {
269 if (trie == true)
270 callback(triePath);
271 else {
272 for (var member in trie)
273 traverse(trie[member], triePath ? triePath + '/' + member : member);
274 }
275 }
276 traverse(testsTrie, startingTriePath);
277}
278
279TestTrie.prototype._addTest = function(test, trie)
280{
281 var rootComponent = test.shift();
282 if (!test.length) {
283 if (!trie[rootComponent])
284 trie[rootComponent] = true;
285 return;
286 }
287
288 if (!trie[rootComponent] || trie[rootComponent] == true)
289 trie[rootComponent] = {};
290 this._addTest(test, trie[rootComponent]);
291}
292
293// Map of all tests to true values. This is just so we can have the list of
294// all tests across all the builders.
295var g_allTestsTrie;
296
297function getAllTestsTrie()
298{
299 if (!g_allTestsTrie)
Torne (Richard Coles)926b0012013-03-28 15:32:48 +0000300 g_allTestsTrie = new TestTrie(currentBuilders(), g_resultsByBuilder);
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000301
302 return g_allTestsTrie;
303}
304
305// Returns an array of tests to be displayed in the individual tests view.
306// Note that a directory can be listed as a test, so we expand that into all
307// tests in the directory.
308function individualTests()
309{
Torne (Richard Coles)926b0012013-03-28 15:32:48 +0000310 if (g_history.dashboardSpecificState.result)
311 return allTestsWithResult(g_history.dashboardSpecificState.result);
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000312
Torne (Richard Coles)926b0012013-03-28 15:32:48 +0000313 if (!g_history.dashboardSpecificState.tests)
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000314 return [];
315
316 return individualTestsForSubstringList();
317}
318
319function substringList()
320{
321 // Convert windows slashes to unix slashes.
Torne (Richard Coles)926b0012013-03-28 15:32:48 +0000322 var tests = g_history.dashboardSpecificState.tests.replace(/\\/g, '/');
323 var separator = string.contains(tests, ' ') ? ' ' : ',';
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000324 var testList = tests.split(separator);
325
Torne (Richard Coles)926b0012013-03-28 15:32:48 +0000326 if (g_history.isLayoutTestResults())
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000327 return testList;
328
329 var testListWithoutModifiers = [];
330 testList.forEach(function(path) {
331 GTEST_MODIFIERS.forEach(function(modifier) {
332 path = path.replace('.' + modifier + '_', '.');
333 });
334 testListWithoutModifiers.push(path);
335 });
336 return testListWithoutModifiers;
337}
338
339function individualTestsForSubstringList()
340{
341 var testList = substringList();
342
343 // Put the tests into an object first and then move them into an array
344 // as a way of deduping.
345 var testsMap = {};
346 for (var i = 0; i < testList.length; i++) {
347 var path = testList[i];
348
349 // Ignore whitespace entries as they'd match every test.
350 if (path.match(/^\s*$/))
351 continue;
352
353 var hasAnyMatches = false;
354 getAllTestsTrie().forEach(function(triePath) {
Torne (Richard Coles)926b0012013-03-28 15:32:48 +0000355 if (string.caseInsensitiveContains(triePath, path)) {
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000356 testsMap[triePath] = 1;
357 hasAnyMatches = true;
358 }
359 });
360
361 // If a path doesn't match any tests, then assume it's a full path
362 // to a test that passes on all builders.
363 if (!hasAnyMatches)
364 testsMap[path] = 1;
365 }
366
367 var testsArray = [];
368 for (var test in testsMap)
369 testsArray.push(test);
370 return testsArray;
371}
372
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000373function allTestsWithResult(result)
374{
375 processTestRunsForAllBuilders();
376 var retVal = [];
377
378 getAllTestsTrie().forEach(function(triePath) {
379 for (var i = 0; i < g_testToResultsMap[triePath].length; i++) {
Torne (Richard Coles)93ac45c2013-05-29 14:40:20 +0100380 if (g_testToResultsMap[triePath][i].actualResults.indexOf(result.toUpperCase()) != -1) {
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000381 retVal.push(triePath);
382 break;
383 }
384 }
385 });
386
387 return retVal;
388}
389
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000390function processTestResultsForBuilderAsync(builder)
391{
392 setTimeout(function() { processTestRunsForBuilder(builder); }, 0);
393}
394
395function processTestRunsForAllBuilders()
396{
Torne (Richard Coles)926b0012013-03-28 15:32:48 +0000397 for (var builder in currentBuilders())
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000398 processTestRunsForBuilder(builder);
399}
400
401function processTestRunsForBuilder(builderName)
402{
403 if (g_perBuilderFailures[builderName])
404 return;
405
406 if (!g_resultsByBuilder[builderName]) {
407 console.error('No tests found for ' + builderName);
408 g_perBuilderFailures[builderName] = [];
409 return;
410 }
Torne (Richard Coles)5267f702013-06-11 10:57:24 +0100411
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000412 var failures = [];
413 var allTestsForThisBuilder = g_resultsByBuilder[builderName].tests;
414
415 for (var test in allTestsForThisBuilder) {
416 var resultsForTest = createResultsObjectForTest(test, builderName);
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000417
418 var rawTest = g_resultsByBuilder[builderName].tests[test];
419 resultsForTest.rawTimes = rawTest.times;
420 var rawResults = rawTest.results;
421 resultsForTest.rawResults = rawResults;
422
Torne (Richard Coles)93ac45c2013-05-29 14:40:20 +0100423 if (rawTest.expected)
424 resultsForTest.expectations = rawTest.expected;
425
426 if (rawTest.bugs)
427 resultsForTest.bugs = rawTest.bugs;
428
Torne (Richard Coles)5267f702013-06-11 10:57:24 +0100429 var failureMap = g_resultsByBuilder[builderName][results.FAILURE_MAP];
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000430 // FIXME: Switch to resultsByBuild
431 var times = resultsForTest.rawTimes;
432 var numTimesSeen = 0;
433 var numResultsSeen = 0;
434 var resultsIndex = 0;
Torne (Richard Coles)5267f702013-06-11 10:57:24 +0100435 var resultsMap = {}
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000436
Torne (Richard Coles)5267f702013-06-11 10:57:24 +0100437 for (var i = 0; i < times.length; i++) {
438 numTimesSeen += times[i][results.RLE.LENGTH];
439
440 while (rawResults[resultsIndex] && numTimesSeen > (numResultsSeen + rawResults[resultsIndex][results.RLE.LENGTH])) {
441 numResultsSeen += rawResults[resultsIndex][results.RLE.LENGTH];
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000442 resultsIndex++;
443 }
444
Torne (Richard Coles)5267f702013-06-11 10:57:24 +0100445 if (rawResults && rawResults[resultsIndex]) {
446 var result = rawResults[resultsIndex][results.RLE.VALUE];
447 resultsMap[failureMap[result]] = true;
448 }
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000449
Torne (Richard Coles)5267f702013-06-11 10:57:24 +0100450 resultsForTest.slowestTime = Math.max(resultsForTest.slowestTime, times[i][results.RLE.VALUE]);
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000451 }
452
Torne (Richard Coles)5267f702013-06-11 10:57:24 +0100453 resultsForTest.actualResults = Object.keys(resultsMap);
454
455 results.determineFlakiness(failureMap, rawResults, resultsForTest);
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000456 failures.push(resultsForTest);
457
458 if (!g_testToResultsMap[test])
459 g_testToResultsMap[test] = [];
460 g_testToResultsMap[test].push(resultsForTest);
461 }
462
463 g_perBuilderFailures[builderName] = failures;
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000464}
465
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000466function linkHTMLToOpenWindow(url, text)
467{
468 return '<a href="' + url + '" target="_blank">' + text + '</a>';
469}
470
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000471// Returns whether the result for index'th result for testName on builder was
472// a failure.
473function isFailure(builder, testName, index)
474{
475 var currentIndex = 0;
476 var rawResults = g_resultsByBuilder[builder].tests[testName].results;
Torne (Richard Coles)5267f702013-06-11 10:57:24 +0100477 var failureMap = g_resultsByBuilder[builder][results.FAILURE_MAP];
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000478 for (var i = 0; i < rawResults.length; i++) {
Torne (Richard Coles)5267f702013-06-11 10:57:24 +0100479 currentIndex += rawResults[i][results.RLE.LENGTH];
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000480 if (currentIndex > index)
Torne (Richard Coles)5267f702013-06-11 10:57:24 +0100481 return results.isFailingResult(failureMap, rawResults[i][results.RLE.VALUE]);
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000482 }
483 console.error('Index exceeds number of results: ' + index);
484}
485
486// Returns an array of indexes for all builds where this test failed.
487function indexesForFailures(builder, testName)
488{
489 var rawResults = g_resultsByBuilder[builder].tests[testName].results;
490 var buildNumbers = g_resultsByBuilder[builder].buildNumbers;
Torne (Richard Coles)5267f702013-06-11 10:57:24 +0100491 var failureMap = g_resultsByBuilder[builder][results.FAILURE_MAP];
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000492 var index = 0;
493 var failures = [];
494 for (var i = 0; i < rawResults.length; i++) {
Torne (Richard Coles)5267f702013-06-11 10:57:24 +0100495 var numResults = rawResults[i][results.RLE.LENGTH];
496 if (results.isFailingResult(failureMap, rawResults[i][results.RLE.VALUE])) {
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000497 for (var j = 0; j < numResults; j++)
498 failures.push(index + j);
499 }
500 index += numResults;
501 }
502 return failures;
503}
504
505// Returns the path to the failure log for this non-webkit test.
506function pathToFailureLog(testName)
507{
Torne (Richard Coles)926b0012013-03-28 15:32:48 +0000508 return '/steps/' + g_history.crossDashboardState.testType + '/logs/' + testName.split('.')[1]
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000509}
510
511function showPopupForBuild(e, builder, index, opt_testName)
512{
513 var html = '';
514
515 var time = g_resultsByBuilder[builder].secondsSinceEpoch[index];
516 if (time) {
517 var date = new Date(time * 1000);
518 html += date.toLocaleDateString() + ' ' + date.toLocaleTimeString();
519 }
520
521 var buildNumber = g_resultsByBuilder[builder].buildNumbers[index];
Torne (Richard Coles)5267f702013-06-11 10:57:24 +0100522 var master = builders.master(builder);
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000523 var buildBasePath = master.logPath(builder, buildNumber);
524
Torne (Richard Coles)93ac45c2013-05-29 14:40:20 +0100525 html += '<ul><li>' + linkHTMLToOpenWindow(buildBasePath, 'Build log');
526
Torne (Richard Coles)5267f702013-06-11 10:57:24 +0100527 if (g_resultsByBuilder[builder][results.BLINK_REVISIONS])
Torne (Richard Coles)93ac45c2013-05-29 14:40:20 +0100528 html += '</li><li>Blink: ' + ui.html.blinkRevisionLink(g_resultsByBuilder[builder], index) + '</li>';
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000529
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +0100530 html += '</li><li>Chromium: ' + ui.html.chromiumRevisionLink(g_resultsByBuilder[builder], index) + '</li>';
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000531
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +0100532 var chromeRevision = g_resultsByBuilder[builder].chromeRevision[index];
533 if (chromeRevision && g_history.isLayoutTestResults()) {
534 html += '<li><a href="' + TEST_RESULTS_BASE_PATH + currentBuilders()[builder] +
535 '/' + chromeRevision + '/layout-test-results.zip">layout-test-results.zip</a></li>';
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000536 }
537
Torne (Richard Coles)926b0012013-03-28 15:32:48 +0000538 if (!g_history.isLayoutTestResults() && opt_testName && isFailure(builder, opt_testName, index))
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000539 html += '<li>' + linkHTMLToOpenWindow(buildBasePath + pathToFailureLog(opt_testName), 'Failure log') + '</li>';
540
541 html += '</ul>';
Torne (Richard Coles)926b0012013-03-28 15:32:48 +0000542 ui.popup.show(e.target, html);
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000543}
544
Torne (Richard Coles)93ac45c2013-05-29 14:40:20 +0100545function classNameForFailureString(failure)
546{
547 return failure.replace(/(\+|\ )/, '');
548}
549
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000550function htmlForTestResults(test)
551{
552 var html = '';
Torne (Richard Coles)5267f702013-06-11 10:57:24 +0100553 var testResults = test.rawResults.concat();
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000554 var times = test.rawTimes.concat();
555 var builder = test.builder;
Torne (Richard Coles)5267f702013-06-11 10:57:24 +0100556 var master = builders.master(builder);
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000557 var buildNumbers = g_resultsByBuilder[builder].buildNumbers;
558
559 var indexToReplaceCurrentResult = -1;
560 var indexToReplaceCurrentTime = -1;
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000561 for (var i = 0; i < buildNumbers.length; i++) {
Torne (Richard Coles)93ac45c2013-05-29 14:40:20 +0100562 var currentResultArray, currentTimeArray, innerHTML, resultString;
563
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000564 if (i > indexToReplaceCurrentResult) {
Torne (Richard Coles)5267f702013-06-11 10:57:24 +0100565 currentResultArray = testResults.shift();
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000566 if (currentResultArray) {
Torne (Richard Coles)5267f702013-06-11 10:57:24 +0100567 resultString = g_resultsByBuilder[builder][results.FAILURE_MAP][currentResultArray[results.RLE.VALUE]];
568 indexToReplaceCurrentResult += currentResultArray[results.RLE.LENGTH];
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000569 } else {
Torne (Richard Coles)5267f702013-06-11 10:57:24 +0100570 resultString = results.NO_DATA;
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000571 indexToReplaceCurrentResult += buildNumbers.length;
572 }
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000573 }
574
575 if (i > indexToReplaceCurrentTime) {
576 currentTimeArray = times.shift();
577 var currentTime = 0;
578 if (currentResultArray) {
Torne (Richard Coles)5267f702013-06-11 10:57:24 +0100579 currentTime = currentTimeArray[results.RLE.VALUE];
580 indexToReplaceCurrentTime += currentTimeArray[results.RLE.LENGTH];
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000581 } else
582 indexToReplaceCurrentTime += buildNumbers.length;
583
584 innerHTML = currentTime || '&nbsp;';
585 }
586
Torne (Richard Coles)93ac45c2013-05-29 14:40:20 +0100587 html += '<td title="' + resultString + '. Click for more info." class="results ' + classNameForFailureString(resultString) +
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +0100588 '" onclick=\'showPopupForBuild(event, "' + builder + '",' + i + ',"' + test.test + '")\'>' + innerHTML;
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000589 }
590 return html;
591}
592
Torne (Richard Coles)93ac45c2013-05-29 14:40:20 +0100593function shouldShowTest(testResult)
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000594{
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +0100595 if (!g_history.isLayoutTestResults())
Torne (Richard Coles)93ac45c2013-05-29 14:40:20 +0100596 return true;
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +0100597
Torne (Richard Coles)93ac45c2013-05-29 14:40:20 +0100598 if (testResult.expectations == 'WONTFIX')
599 return g_history.dashboardSpecificState.showWontFix;
600
Torne (Richard Coles)5267f702013-06-11 10:57:24 +0100601 if (testResult.expectations == results.SKIP)
Torne (Richard Coles)93ac45c2013-05-29 14:40:20 +0100602 return g_history.dashboardSpecificState.showSkip;
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000603
604 if (testResult.isFlaky)
Torne (Richard Coles)93ac45c2013-05-29 14:40:20 +0100605 return g_history.dashboardSpecificState.showFlaky;
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000606
Torne (Richard Coles)93ac45c2013-05-29 14:40:20 +0100607 return g_history.dashboardSpecificState.showNonFlaky;
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000608}
609
610function createBugHTML(test)
611{
612 var symptom = test.isFlaky ? 'flaky' : 'failing';
613 var title = encodeURIComponent('Layout Test ' + test.test + ' is ' + symptom);
614 var description = encodeURIComponent('The following layout test is ' + symptom + ' on ' +
615 '[insert platform]\n\n' + test.test + '\n\nProbable cause:\n\n' +
616 '[insert probable cause]');
Torne (Richard Coles)5267f702013-06-11 10:57:24 +0100617
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +0100618 url = 'https://code.google.com/p/chromium/issues/entry?template=Layout%20Test%20Failure&summary=' + title + '&comment=' + description;
Torne (Richard Coles)93ac45c2013-05-29 14:40:20 +0100619 return '<a href="' + url + '">File new bug</a>';
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000620}
621
622function isCrossBuilderView()
623{
Torne (Richard Coles)93ac45c2013-05-29 14:40:20 +0100624 return g_history.dashboardSpecificState.tests || g_history.dashboardSpecificState.result;
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000625}
626
627function tableHeaders(opt_getAll)
628{
629 var headers = [];
630 if (isCrossBuilderView() || opt_getAll)
631 headers.push('builder');
632
633 if (!isCrossBuilderView() || opt_getAll)
634 headers.push('test');
635
Torne (Richard Coles)926b0012013-03-28 15:32:48 +0000636 if (g_history.isLayoutTestResults() || opt_getAll)
Torne (Richard Coles)93ac45c2013-05-29 14:40:20 +0100637 headers.push('bugs', 'expectations');
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000638
639 headers.push('slowest run', 'flakiness (numbers are runtimes in seconds)');
640 return headers;
641}
642
Torne (Richard Coles)93ac45c2013-05-29 14:40:20 +0100643function linkifyBugs(bugs)
644{
645 var html = '';
646 bugs.forEach(function(bug) {
647 var bugHtml;
648 if (string.startsWith(bug, 'Bug('))
649 bugHtml = bug;
650 else
651 bugHtml = '<a href="http://' + bug + '">' + bug + '</a>';
652 html += '<div>' + bugHtml + '</div>'
653 });
654 return html;
655}
656
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000657function htmlForSingleTestRow(test)
658{
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000659 var headers = tableHeaders();
660 var html = '';
661 for (var i = 0; i < headers.length; i++) {
662 var header = headers[i];
Torne (Richard Coles)926b0012013-03-28 15:32:48 +0000663 if (string.startsWith(header, 'test') || string.startsWith(header, 'builder')) {
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000664 // If isCrossBuilderView() is true, we're just viewing a single test
665 // with results for many builders, so the first column is builder names
666 // instead of test paths.
667 var testCellClassName = 'test-link' + (isCrossBuilderView() ? ' builder-name' : '');
Torne (Richard Coles)926b0012013-03-28 15:32:48 +0000668 var testCellHTML = isCrossBuilderView() ? test.builder : '<span class="link" onclick="g_history.setQueryParameter(\'tests\',\'' + test.test +'\');">' + test.test + '</span>';
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000669
670 html += '<tr><td class="' + testCellClassName + '">' + testCellHTML;
Torne (Richard Coles)926b0012013-03-28 15:32:48 +0000671 } else if (string.startsWith(header, 'bugs'))
Torne (Richard Coles)93ac45c2013-05-29 14:40:20 +0100672 // FIXME: linkify bugs.
673 html += '<td class=options-container>' + (linkifyBugs(test.bugs) || createBugHTML(test));
Torne (Richard Coles)926b0012013-03-28 15:32:48 +0000674 else if (string.startsWith(header, 'expectations'))
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000675 html += '<td class=options-container>' + test.expectations;
Torne (Richard Coles)926b0012013-03-28 15:32:48 +0000676 else if (string.startsWith(header, 'slowest'))
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000677 html += '<td>' + (test.slowestTime ? test.slowestTime + 's' : '');
Torne (Richard Coles)926b0012013-03-28 15:32:48 +0000678 else if (string.startsWith(header, 'flakiness'))
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000679 html += htmlForTestResults(test);
680 }
681 return html;
682}
683
684function sortColumnFromTableHeader(headerText)
685{
686 return headerText.split(' ', 1)[0];
687}
688
689function htmlForTableColumnHeader(headerName, opt_fillColSpan)
690{
691 // Use the first word of the header title as the sortkey
692 var thisSortValue = sortColumnFromTableHeader(headerName);
Torne (Richard Coles)926b0012013-03-28 15:32:48 +0000693 var arrowHTML = thisSortValue == g_history.dashboardSpecificState.sortColumn ?
694 '<span class=' + g_history.dashboardSpecificState.sortOrder + '>' + (g_history.dashboardSpecificState.sortOrder == FORWARD ? '&uarr;' : '&darr;' ) + '</span>' : '';
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000695 return '<th sortValue=' + thisSortValue +
696 // Extend last th through all the rest of the columns.
697 (opt_fillColSpan ? ' colspan=10000' : '') +
698 // Extra span here is so flex boxing actually centers.
699 // There's probably a better way to do this with CSS only though.
700 '><div class=table-header-content><span></span>' + arrowHTML +
701 '<span class=header-text>' + headerName + '</span>' + arrowHTML + '</div></th>';
702}
703
704function htmlForTestTable(rowsHTML, opt_excludeHeaders)
705{
706 var html = '<table class=test-table>';
707 if (!opt_excludeHeaders) {
708 html += '<thead><tr>';
709 var headers = tableHeaders();
710 for (var i = 0; i < headers.length; i++)
711 html += htmlForTableColumnHeader(headers[i], i == headers.length - 1);
712 html += '</tr></thead>';
713 }
714 return html + '<tbody>' + rowsHTML + '</tbody></table>';
715}
716
717function appendHTML(html)
718{
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000719 // InnerHTML to a div that's not in the document. This is
720 // ~300ms faster in Safari 4 and Chrome 4 on mac.
721 var div = document.createElement('div');
722 div.innerHTML = html;
723 document.body.appendChild(div);
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000724 postHeightChangedMessage();
725}
726
727function alphanumericCompare(column, reverse)
728{
729 return reversibleCompareFunction(function(a, b) {
730 // Put null entries at the bottom
731 var a = a[column] ? String(a[column]) : 'z';
732 var b = b[column] ? String(b[column]) : 'z';
733
734 if (a < b)
735 return -1;
736 else if (a == b)
737 return 0;
738 else
739 return 1;
740 }, reverse);
741}
742
743function numericSort(column, reverse)
744{
745 return reversibleCompareFunction(function(a, b) {
746 a = parseFloat(a[column]);
747 b = parseFloat(b[column]);
748 return a - b;
749 }, reverse);
750}
751
752function reversibleCompareFunction(compare, reverse)
753{
754 return function(a, b) {
755 return compare(reverse ? b : a, reverse ? a : b);
756 };
757}
758
759function changeSort(e)
760{
761 var target = e.currentTarget;
762 e.preventDefault();
763
764 var sortValue = target.getAttribute('sortValue');
765 while (target && target.tagName != 'TABLE')
766 target = target.parentNode;
767
768 var sort = 'sortColumn';
769 var orderKey = 'sortOrder';
Torne (Richard Coles)926b0012013-03-28 15:32:48 +0000770 if (sortValue == g_history.dashboardSpecificState[sort] && g_history.dashboardSpecificState[orderKey] == FORWARD)
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000771 order = BACKWARD;
772 else
773 order = FORWARD;
774
Torne (Richard Coles)926b0012013-03-28 15:32:48 +0000775 g_history.setQueryParameter(sort, sortValue, orderKey, order);
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000776}
777
778function sortTests(tests, column, order)
779{
780 var resultsProperty, sortFunctionGetter;
781 if (column == 'flakiness') {
782 sortFunctionGetter = numericSort;
783 resultsProperty = 'flips';
784 } else if (column == 'slowest') {
785 sortFunctionGetter = numericSort;
786 resultsProperty = 'slowestTime';
787 } else {
788 sortFunctionGetter = alphanumericCompare;
789 resultsProperty = column;
790 }
791
792 tests.sort(sortFunctionGetter(resultsProperty, order == BACKWARD));
793}
794
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000795function htmlForIndividualTestOnAllBuilders(test)
796{
797 processTestRunsForAllBuilders();
798
799 var testResults = g_testToResultsMap[test];
800 if (!testResults)
Torne (Richard Coles)5267f702013-06-11 10:57:24 +0100801 return '<div class="not-found">Test not found. Either it does not exist, is skipped or passes on all recorded runs.</div>';
802
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000803 var html = '';
804 var shownBuilders = [];
805 for (var j = 0; j < testResults.length; j++) {
806 shownBuilders.push(testResults[j].builder);
807 html += htmlForSingleTestRow(testResults[j]);
808 }
809
810 var skippedBuilders = []
Torne (Richard Coles)926b0012013-03-28 15:32:48 +0000811 for (builder in currentBuilders()) {
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000812 if (shownBuilders.indexOf(builder) == -1)
813 skippedBuilders.push(builder);
814 }
815
816 var skippedBuildersHtml = '';
817 if (skippedBuilders.length) {
Torne (Richard Coles)5267f702013-06-11 10:57:24 +0100818 skippedBuildersHtml = '<div>The following builders either don\'t run this test (e.g. it\'s skipped) or all recorded runs passed:</div>' +
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000819 '<div class=skipped-builder-list><div class=skipped-builder>' + skippedBuilders.join('</div><div class=skipped-builder>') + '</div></div>';
820 }
821
822 return htmlForTestTable(html) + skippedBuildersHtml;
823}
824
825function htmlForIndividualTestOnAllBuildersWithResultsLinks(test)
826{
827 processTestRunsForAllBuilders();
828
829 var testResults = g_testToResultsMap[test];
830 var html = '';
831 html += htmlForIndividualTestOnAllBuilders(test);
832
833 html += '<div class=expectations test=' + test + '><div>' +
834 linkHTMLToToggleState('showExpectations', 'results')
835
Torne (Richard Coles)926b0012013-03-28 15:32:48 +0000836 if (g_history.isLayoutTestResults() || g_history.isGPUTestResults()) {
837 if (g_history.isLayoutTestResults())
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000838 html += ' | ' + linkHTMLToToggleState('showLargeExpectations', 'large thumbnails');
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000839 html += ' | <b>Only shows actual results/diffs from the most recent *failure* on each bot.</b>';
840 } else {
841 html += ' | <span>Results height:<input ' +
Torne (Richard Coles)926b0012013-03-28 15:32:48 +0000842 'onchange="g_history.setQueryParameter(\'resultsHeight\',this.value)" value="' +
843 g_history.dashboardSpecificState.resultsHeight + '" style="width:2.5em">px</span>';
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000844 }
845 html += '</div></div>';
846 return html;
847}
848
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000849function maybeAddPngChecksum(expectationDiv, pngUrl)
850{
851 // pngUrl gets served from the browser cache since we just loaded it in an
852 // <img> tag.
853 loader.request(pngUrl,
854 function(xhr) {
855 // Convert the first 2k of the response to a byte string.
856 var bytes = xhr.responseText.substring(0, 2048);
857 for (var position = 0; position < bytes.length; ++position)
858 bytes[position] = bytes[position] & 0xff;
859
860 // Look for the comment.
861 var commentKey = 'tEXtchecksum\x00';
862 var checksumPosition = bytes.indexOf(commentKey);
863 if (checksumPosition == -1)
864 return;
865
866 var checksum = bytes.substring(checksumPosition + commentKey.length, checksumPosition + commentKey.length + 32);
867 var checksumContainer = document.createElement('span');
868 checksumContainer.innerText = 'Embedded checksum: ' + checksum;
869 checksumContainer.setAttribute('class', 'pngchecksum');
870 expectationDiv.parentNode.appendChild(checksumContainer);
871 },
872 function(xhr) {},
873 true);
874}
875
Torne (Richard Coles)5267f702013-06-11 10:57:24 +0100876function getOrCreate(className, parent)
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000877{
Torne (Richard Coles)5267f702013-06-11 10:57:24 +0100878 var element = parent.querySelector('.' + className);
879 if (!element) {
880 element = document.createElement('div');
881 element.className = className;
882 parent.appendChild(element);
883 }
884 return element;
885}
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000886
Torne (Richard Coles)5267f702013-06-11 10:57:24 +0100887function handleExpectationsItemLoad(title, item, itemType, parent)
888{
889 item.className = 'expectation';
890 if (g_history.dashboardSpecificState.showLargeExpectations)
891 item.className += ' large';
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000892
Torne (Richard Coles)5267f702013-06-11 10:57:24 +0100893 var titleContainer = document.createElement('h3');
894 titleContainer.className = 'expectations-title';
895 titleContainer.textContent = title;
896
897 var itemContainer = document.createElement('span');
898 itemContainer.appendChild(titleContainer);
899 itemContainer.className = 'expectations-item ' + title;
900 itemContainer.appendChild(item);
901
902 // Separate text and image results into separate divs..
903 var typeContainer = getOrCreate(itemType, parent);
904
905 // Insert results in a consistent order.
906 var index = EXPECTATIONS_ORDER.indexOf(title);
907 while (index < EXPECTATIONS_ORDER.length) {
908 index++;
909 var elementAfter = typeContainer.querySelector('.' + EXPECTATIONS_ORDER[index]);
910 if (elementAfter) {
911 typeContainer.insertBefore(itemContainer, elementAfter);
912 break;
913 }
914 }
915 if (!itemContainer.parentNode)
916 typeContainer.appendChild(itemContainer);
917
918 handleFinishedLoadingExpectations(parent);
919}
920
921function addExpectationItem(expectationsContainers, parentContainer, url, opt_builder)
922{
923 // Group expectations by builder, putting test and reference files first.
924 var builder = opt_builder || "Test and reference files";
925 var container = expectationsContainers[builder];
926
927 if (!container) {
928 container = document.createElement('div');
929 container.className = 'expectations-container';
930 container.setAttribute('data-builder', builder);
931 parentContainer.appendChild(container);
932 expectationsContainers[builder] = container;
933 }
934
935 var numUnloaded = container.getAttribute('data-unloaded') || 0;
936 container.setAttribute('data-unloaded', ++numUnloaded);
937
938 var isImage = url.match(/\.png$/);
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000939
940 var appendExpectationsItem = function(item) {
Torne (Richard Coles)5267f702013-06-11 10:57:24 +0100941 var itemType = isImage ? 'image' : 'text';
942 handleExpectationsItemLoad(expectationsTitle(url), item, itemType, container);
943 };
944
945 var handleLoadError = function() {
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000946 handleFinishedLoadingExpectations(container);
947 };
948
Torne (Richard Coles)93ac45c2013-05-29 14:40:20 +0100949 if (isImage) {
Torne (Richard Coles)5267f702013-06-11 10:57:24 +0100950 var dummyNode = document.createElement('img');
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000951 dummyNode.onload = function() {
Torne (Richard Coles)5267f702013-06-11 10:57:24 +0100952 var item = dummyNode;
953 maybeAddPngChecksum(item, url);
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000954 appendExpectationsItem(item);
955 }
Torne (Richard Coles)5267f702013-06-11 10:57:24 +0100956 dummyNode.onerror = handleLoadError;
957 dummyNode.src = url;
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000958 } else {
959 loader.request(url,
960 function(xhr) {
961 var item = document.createElement('pre');
Torne (Richard Coles)5267f702013-06-11 10:57:24 +0100962 if (string.endsWith(url, '-wdiff.html'))
963 item.innerHTML = xhr.responseText;
964 else
965 item.textContent = xhr.responseText;
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000966 appendExpectationsItem(item);
967 },
Torne (Richard Coles)5267f702013-06-11 10:57:24 +0100968 handleLoadError);
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000969 }
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000970}
971
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000972function handleFinishedLoadingExpectations(container)
973{
Torne (Richard Coles)5267f702013-06-11 10:57:24 +0100974 var numUnloaded = container.getAttribute('data-unloaded') - 1;
975 container.setAttribute('data-unloaded', numUnloaded);
976 if (numUnloaded)
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000977 return;
978
Torne (Richard Coles)5267f702013-06-11 10:57:24 +0100979 if (!container.firstChild) {
980 container.remove();
981 return;
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000982 }
983
Torne (Richard Coles)5267f702013-06-11 10:57:24 +0100984 var builder = container.getAttribute('data-builder');
985 if (!builder)
986 return;
987
988 var header = document.createElement('h2');
989 header.textContent = builder;
990 container.insertBefore(header, container.firstChild);
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000991}
992
Torne (Richard Coles)5267f702013-06-11 10:57:24 +0100993function expectationsTitle(url)
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000994{
Torne (Richard Coles)5267f702013-06-11 10:57:24 +0100995 var matchingSuffixes = ACTUAL_RESULT_SUFFIXES.filter(function(suffix) {
996 return string.endsWith(url, suffix);
997 });
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +0000998
Torne (Richard Coles)5267f702013-06-11 10:57:24 +0100999 if (matchingSuffixes.length)
1000 return matchingSuffixes[0].split('.')[0];
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +00001001
Torne (Richard Coles)5267f702013-06-11 10:57:24 +01001002 var parts = url.split('/');
1003 return parts[parts.length - 1];
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +00001004}
1005
1006function loadExpectations(expectationsContainer)
1007{
1008 var test = expectationsContainer.getAttribute('test');
Torne (Richard Coles)926b0012013-03-28 15:32:48 +00001009 if (g_history.isLayoutTestResults())
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +00001010 loadExpectationsLayoutTests(test, expectationsContainer);
1011 else {
Torne (Richard Coles)5267f702013-06-11 10:57:24 +01001012 var testResults = g_testToResultsMap[test];
1013 for (var i = 0; i < testResults.length; i++)
Torne (Richard Coles)926b0012013-03-28 15:32:48 +00001014 if (g_history.isGPUTestResults())
Torne (Richard Coles)5267f702013-06-11 10:57:24 +01001015 loadGPUResultsForBuilder(testResults[i].builder, test, expectationsContainer);
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +00001016 else
Torne (Richard Coles)5267f702013-06-11 10:57:24 +01001017 loadNonWebKitResultsForBuilder(testResults[i].builder, test, expectationsContainer);
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +00001018 }
1019}
1020
1021function gpuResultsPath(chromeRevision, builder)
1022{
1023 return chromeRevision + '_' + builder.replace(/[^A-Za-z0-9]+/g, '_');
1024}
1025
1026function loadGPUResultsForBuilder(builder, test, expectationsContainer)
1027{
1028 var container = document.createElement('div');
1029 container.className = 'expectations-container';
1030 container.innerHTML = '<div><b>' + builder + '</b></div>';
1031 expectationsContainer.appendChild(container);
1032
1033 var failureIndex = indexesForFailures(builder, test)[0];
1034
1035 var buildNumber = g_resultsByBuilder[builder].buildNumbers[failureIndex];
Torne (Richard Coles)5267f702013-06-11 10:57:24 +01001036 var pathToLog = builders.master(builder).logPath(builder, buildNumber) + pathToFailureLog(test);
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +00001037
1038 var chromeRevision = g_resultsByBuilder[builder].chromeRevision[failureIndex];
1039 var resultsUrl = GPU_RESULTS_BASE_PATH + gpuResultsPath(chromeRevision, builder);
1040 var filename = test.split(/\./)[1] + '.png';
1041
1042 appendNonWebKitResults(container, pathToLog, 'non-webkit-results');
1043 appendNonWebKitResults(container, resultsUrl + '/gen/' + filename, 'gpu-test-results', 'Generated');
1044 appendNonWebKitResults(container, resultsUrl + '/ref/' + filename, 'gpu-test-results', 'Reference');
1045 appendNonWebKitResults(container, resultsUrl + '/diff/' + filename, 'gpu-test-results', 'Diff');
1046}
1047
1048function loadNonWebKitResultsForBuilder(builder, test, expectationsContainer)
1049{
1050 var failureIndexes = indexesForFailures(builder, test);
1051 var container = document.createElement('div');
1052 container.innerHTML = '<div><b>' + builder + '</b></div>';
1053 expectationsContainer.appendChild(container);
1054 for (var i = 0; i < failureIndexes.length; i++) {
1055 // FIXME: This doesn't seem to work anymore. Did the paths change?
1056 // Once that's resolved, see if we need to try each GTEST_MODIFIERS prefix as well.
1057 var buildNumber = g_resultsByBuilder[builder].buildNumbers[failureIndexes[i]];
Torne (Richard Coles)5267f702013-06-11 10:57:24 +01001058 var pathToLog = builders.master(builder).logPath(builder, buildNumber) + pathToFailureLog(test);
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +00001059 appendNonWebKitResults(container, pathToLog, 'non-webkit-results');
1060 }
1061}
1062
1063function appendNonWebKitResults(container, url, itemClassName, opt_title)
1064{
1065 // Use a script tag to detect whether the URL 404s.
1066 // Need to use a script tag since the URL is cross-domain.
1067 var dummyNode = document.createElement('script');
1068 dummyNode.src = url;
1069
1070 dummyNode.onload = function() {
1071 var item = document.createElement('iframe');
1072 item.src = dummyNode.src;
1073 item.className = itemClassName;
Torne (Richard Coles)926b0012013-03-28 15:32:48 +00001074 item.style.height = g_history.dashboardSpecificState.resultsHeight + 'px';
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +00001075
1076 if (opt_title) {
1077 var childContainer = document.createElement('div');
1078 childContainer.style.display = 'inline-block';
1079 var title = document.createElement('div');
1080 title.textContent = opt_title;
1081 childContainer.appendChild(title);
1082 childContainer.appendChild(item);
1083 container.replaceChild(childContainer, dummyNode);
1084 } else
1085 container.replaceChild(item, dummyNode);
1086 }
1087 dummyNode.onerror = function() {
1088 container.removeChild(dummyNode);
1089 }
1090
1091 container.appendChild(dummyNode);
1092}
1093
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +00001094function lookupVirtualTestSuite(test) {
1095 for (var suite in VIRTUAL_SUITES) {
1096 if (test.indexOf(suite) != -1)
1097 return suite;
1098 }
1099 return '';
1100}
1101
1102function baseTest(test, suite) {
1103 base = VIRTUAL_SUITES[suite];
1104 return base ? test.replace(suite, base) : test;
1105}
1106
Torne (Richard Coles)5267f702013-06-11 10:57:24 +01001107function loadTestAndReferenceFiles(expectationsContainers, expectationsContainer, test) {
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +00001108 var testWithoutSuffix = test.substring(0, test.lastIndexOf('.'));
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +00001109 var reftest_html_file = testWithoutSuffix + "-expected.html";
1110 var reftest_mismatch_html_file = testWithoutSuffix + "-expected-mismatch.html";
Torne (Richard Coles)5267f702013-06-11 10:57:24 +01001111
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +00001112 var suite = lookupVirtualTestSuite(test);
Torne (Richard Coles)5267f702013-06-11 10:57:24 +01001113 if (suite) {
1114 loadTestAndReferenceFiles(expectationsContainers, expectationsContainer, baseTest(test, suite));
1115 return;
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +00001116 }
1117
Torne (Richard Coles)5267f702013-06-11 10:57:24 +01001118 addExpectationItem(expectationsContainers, expectationsContainer, TEST_URL_BASE_PATH_FOR_XHR + test);
1119 addExpectationItem(expectationsContainers, expectationsContainer, TEST_URL_BASE_PATH_FOR_XHR + reftest_html_file);
1120 addExpectationItem(expectationsContainers, expectationsContainer, TEST_URL_BASE_PATH_FOR_XHR + reftest_mismatch_html_file);
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +00001121}
1122
1123function loadExpectationsLayoutTests(test, expectationsContainer)
1124{
1125 // Map from file extension to container div for expectations of that type.
1126 var expectationsContainers = {};
Torne (Richard Coles)5267f702013-06-11 10:57:24 +01001127 loadTestAndReferenceFiles(expectationsContainers, expectationsContainer, test);
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +00001128
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +00001129 var testWithoutSuffix = test.substring(0, test.lastIndexOf('.'));
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +00001130
Torne (Richard Coles)926b0012013-03-28 15:32:48 +00001131 for (var builder in currentBuilders()) {
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +01001132 var actualResultsBase = TEST_RESULTS_BASE_PATH + currentBuilders()[builder] + '/results/layout-test-results/';
Torne (Richard Coles)5267f702013-06-11 10:57:24 +01001133 ACTUAL_RESULT_SUFFIXES.forEach(function(suffix) {{
1134 addExpectationItem(expectationsContainers, expectationsContainer, actualResultsBase + testWithoutSuffix + '-' + suffix, builder);
1135 }})
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +00001136 }
1137
1138 // Add a clearing element so floated elements don't bleed out of their
1139 // containing block.
1140 var br = document.createElement('br');
1141 br.style.clear = 'both';
1142 expectationsContainer.appendChild(br);
1143}
1144
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +00001145function appendExpectations()
1146{
Torne (Richard Coles)926b0012013-03-28 15:32:48 +00001147 var expectations = g_history.dashboardSpecificState.showExpectations ? document.getElementsByClassName('expectations') : [];
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +00001148 // Loading expectations is *very* slow. Use a large timeout to avoid
1149 // totally hanging the renderer.
1150 performChunkedAction(expectations, function(chunk) {
1151 for (var i = 0, len = chunk.length; i < len; i++)
1152 loadExpectations(chunk[i]);
1153 postHeightChangedMessage();
1154
1155 }, hideLoadingUI, 10000);
1156}
1157
1158function hideLoadingUI()
1159{
1160 var loadingDiv = $('loading-ui');
1161 if (loadingDiv)
1162 loadingDiv.style.display = 'none';
1163 postHeightChangedMessage();
1164}
1165
1166function generatePageForIndividualTests(tests)
1167{
1168 console.log('Number of tests: ' + tests.length);
Torne (Richard Coles)926b0012013-03-28 15:32:48 +00001169 if (g_history.dashboardSpecificState.showChrome)
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +00001170 appendHTML(htmlForNavBar());
1171 performChunkedAction(tests, function(chunk) {
1172 appendHTML(htmlForIndividualTests(chunk));
1173 }, appendExpectations, 500);
Torne (Richard Coles)5267f702013-06-11 10:57:24 +01001174 if (g_history.dashboardSpecificState.showChrome) {
Torne (Richard Coles)926b0012013-03-28 15:32:48 +00001175 $('tests-input').value = g_history.dashboardSpecificState.tests;
Torne (Richard Coles)5267f702013-06-11 10:57:24 +01001176 $('result-input').value = g_history.dashboardSpecificState.result;
1177 }
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +00001178}
1179
1180function performChunkedAction(tests, handleChunk, onComplete, timeout, opt_index) {
1181 var index = opt_index || 0;
1182 setTimeout(function() {
1183 var chunk = Array.prototype.slice.call(tests, index * CHUNK_SIZE, (index + 1) * CHUNK_SIZE);
1184 if (chunk.length) {
1185 handleChunk(chunk);
1186 performChunkedAction(tests, handleChunk, onComplete, timeout, ++index);
1187 } else
1188 onComplete();
1189 // No need for a timeout on the first chunked action.
1190 }, index ? timeout : 0);
1191}
1192
1193function htmlForIndividualTests(tests)
1194{
1195 var testsHTML = [];
1196 for (var i = 0; i < tests.length; i++) {
1197 var test = tests[i];
1198 var testNameHtml = '';
Torne (Richard Coles)926b0012013-03-28 15:32:48 +00001199 if (g_history.dashboardSpecificState.showChrome || tests.length > 1) {
1200 if (g_history.isLayoutTestResults()) {
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +00001201 var suite = lookupVirtualTestSuite(test);
1202 var base = suite ? baseTest(test, suite) : test;
Torne (Richard Coles)93ac45c2013-05-29 14:40:20 +01001203 var versionControlUrl = TEST_URL_BASE_PATH_FOR_BROWSING + base;
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +01001204 testNameHtml += '<h2>' + linkHTMLToOpenWindow(versionControlUrl, test) + '</h2>';
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +00001205 } else
1206 testNameHtml += '<h2>' + test + '</h2>';
1207 }
1208
1209 testsHTML.push(testNameHtml + htmlForIndividualTestOnAllBuildersWithResultsLinks(test));
1210 }
1211 return testsHTML.join('<hr>');
1212}
1213
1214function htmlForNavBar()
1215{
1216 var extraHTML = '';
Torne (Richard Coles)926b0012013-03-28 15:32:48 +00001217 var html = ui.html.testTypeSwitcher(false, extraHTML, isCrossBuilderView());
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +00001218 html += '<div class=forms><form id=result-form ' +
Torne (Richard Coles)926b0012013-03-28 15:32:48 +00001219 'onsubmit="g_history.setQueryParameter(\'result\', result.value);' +
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +00001220 'return false;">Show all tests with result: ' +
1221 '<input name=result placeholder="e.g. CRASH" id=result-input>' +
1222 '</form><form id=tests-form ' +
Torne (Richard Coles)926b0012013-03-28 15:32:48 +00001223 'onsubmit="g_history.setQueryParameter(\'tests\', tests.value);' +
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +00001224 'return false;"><span>Show tests on all platforms: </span>' +
1225 '<input name=tests ' +
1226 'placeholder="Comma or space-separated list of tests or partial ' +
1227 'paths to show test results across all builders, e.g., ' +
1228 'foo/bar.html,foo/baz,domstorage" id=tests-input></form>' +
1229 '<span class=link onclick="showLegend()">Show legend [type ?]</span></div>';
1230 return html;
1231}
1232
1233function checkBoxToToggleState(key, text)
1234{
Torne (Richard Coles)926b0012013-03-28 15:32:48 +00001235 var stateEnabled = g_history.dashboardSpecificState[key];
1236 return '<label><input type=checkbox ' + (stateEnabled ? 'checked ' : '') + 'onclick="g_history.setQueryParameter(\'' + key + '\', ' + !stateEnabled + ')">' + text + '</label> ';
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +00001237}
1238
1239function linkHTMLToToggleState(key, linkText)
1240{
Torne (Richard Coles)926b0012013-03-28 15:32:48 +00001241 var stateEnabled = g_history.dashboardSpecificState[key];
1242 return '<span class=link onclick="g_history.setQueryParameter(\'' + key + '\', ' + !stateEnabled + ')">' + (stateEnabled ? 'Hide' : 'Show') + ' ' + linkText + '</span>';
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +00001243}
1244
1245function headerForTestTableHtml()
1246{
1247 return '<h2 style="display:inline-block">Failing tests</h2>' +
Torne (Richard Coles)93ac45c2013-05-29 14:40:20 +01001248 checkBoxToToggleState('showFlaky', 'Show flaky') +
1249 checkBoxToToggleState('showNonFlaky', 'Show non-flaky') +
1250 checkBoxToToggleState('showSkip', 'Show Skip') +
1251 checkBoxToToggleState('showWontFix', 'Show WontFix');
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +00001252}
1253
1254function generatePageForBuilder(builderName)
1255{
1256 processTestRunsForBuilder(builderName);
1257
Torne (Richard Coles)5267f702013-06-11 10:57:24 +01001258 var filteredResults = g_perBuilderFailures[builderName].filter(shouldShowTest);
1259 sortTests(filteredResults, g_history.dashboardSpecificState.sortColumn, g_history.dashboardSpecificState.sortOrder);
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +00001260
1261 var testsHTML = '';
Torne (Richard Coles)5267f702013-06-11 10:57:24 +01001262 if (filteredResults.length) {
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +00001263 var tableRowsHTML = '';
Torne (Richard Coles)5267f702013-06-11 10:57:24 +01001264 for (var i = 0; i < filteredResults.length; i++)
1265 tableRowsHTML += htmlForSingleTestRow(filteredResults[i])
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +00001266 testsHTML = htmlForTestTable(tableRowsHTML);
1267 } else {
Torne (Richard Coles)926b0012013-03-28 15:32:48 +00001268 if (g_history.isLayoutTestResults())
Torne (Richard Coles)93ac45c2013-05-29 14:40:20 +01001269 testsHTML += '<div>Fill in one of the text inputs or checkboxes above to show failures.</div>';
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +00001270 else
Torne (Richard Coles)93ac45c2013-05-29 14:40:20 +01001271 testsHTML += '<div>No tests have failed!</div>';
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +00001272 }
1273
1274 var html = htmlForNavBar();
1275
Torne (Richard Coles)926b0012013-03-28 15:32:48 +00001276 if (g_history.isLayoutTestResults())
Torne (Richard Coles)93ac45c2013-05-29 14:40:20 +01001277 html += headerForTestTableHtml();
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +00001278
1279 html += '<br>' + testsHTML;
1280 appendHTML(html);
1281
1282 var ths = document.getElementsByTagName('th');
1283 for (var i = 0; i < ths.length; i++) {
1284 ths[i].addEventListener('click', changeSort, false);
1285 ths[i].className = "sortable";
1286 }
1287
1288 hideLoadingUI();
1289}
1290
1291var VALID_KEYS_FOR_CROSS_BUILDER_VIEW = {
1292 tests: 1,
1293 result: 1,
1294 showChrome: 1,
1295 showExpectations: 1,
1296 showLargeExpectations: 1,
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +00001297 resultsHeight: 1,
1298 revision: 1
1299};
1300
1301function isInvalidKeyForCrossBuilderView(key)
1302{
Torne (Richard Coles)926b0012013-03-28 15:32:48 +00001303 return !(key in VALID_KEYS_FOR_CROSS_BUILDER_VIEW) && !(key in history.DEFAULT_CROSS_DASHBOARD_STATE_VALUES);
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +00001304}
1305
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +00001306function hideLegend()
1307{
1308 var legend = $('legend');
1309 if (legend)
1310 legend.parentNode.removeChild(legend);
1311}
1312
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +00001313function showLegend()
1314{
1315 var legend = $('legend');
1316 if (!legend) {
1317 legend = document.createElement('div');
1318 legend.id = 'legend';
1319 document.body.appendChild(legend);
1320 }
1321
1322 var html = '<div id=legend-toggle onclick="hideLegend()">Hide ' +
1323 'legend [type esc]</div><div id=legend-contents>';
Torne (Richard Coles)93ac45c2013-05-29 14:40:20 +01001324
1325 // Just grab the first failureMap. Technically, different builders can have different maps if they
1326 // haven't all cycled after the map was changed, but meh.
Torne (Richard Coles)5267f702013-06-11 10:57:24 +01001327 var failureMap = g_resultsByBuilder[Object.keys(g_resultsByBuilder)[0]][results.FAILURE_MAP];
Torne (Richard Coles)93ac45c2013-05-29 14:40:20 +01001328 for (var expectation in failureMap) {
1329 var failureString = failureMap[expectation];
1330 html += '<div class=' + classNameForFailureString(failureString) + '>' + failureString + '</div>';
1331 }
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +00001332
Torne (Richard Coles)926b0012013-03-28 15:32:48 +00001333 if (g_history.isLayoutTestResults()) {
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +00001334 html += '</div><br style="clear:both">' +
Torne (Richard Coles)5267f702013-06-11 10:57:24 +01001335 '</div>';
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +00001336
Torne (Richard Coles)93ac45c2013-05-29 14:40:20 +01001337 html += '<div>RELEASE TIMEOUTS:</div>' +
1338 htmlForSlowTimes(RELEASE_TIMEOUT) +
1339 '<div>DEBUG TIMEOUTS:</div>' +
1340 htmlForSlowTimes(DEBUG_TIMEOUT);
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +00001341 }
1342
1343 legend.innerHTML = html;
1344}
1345
1346function htmlForSlowTimes(minTime)
1347{
Torne (Richard Coles)93ac45c2013-05-29 14:40:20 +01001348 return '<ul><li>' + minTime + ' seconds</li><li>' +
1349 SLOW_MULTIPLIER * minTime + ' seconds if marked Slow in TestExpectations</li></ul>';
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +00001350}
1351
1352function postHeightChangedMessage()
1353{
1354 if (window == parent)
1355 return;
1356
1357 var root = document.documentElement;
1358 var height = root.offsetHeight;
1359 if (root.offsetWidth < root.scrollWidth) {
1360 // We have a horizontal scrollbar. Include it in the height.
1361 var dummyNode = document.createElement('div');
1362 dummyNode.style.overflow = 'scroll';
1363 document.body.appendChild(dummyNode);
1364 var scrollbarWidth = dummyNode.offsetHeight - dummyNode.clientHeight;
1365 document.body.removeChild(dummyNode);
1366 height += scrollbarWidth;
1367 }
1368 parent.postMessage({command: 'heightChanged', height: height}, '*')
1369}
1370
1371if (window != parent)
Torne (Richard Coles)926b0012013-03-28 15:32:48 +00001372 window.addEventListener('blur', ui.popup.hide);
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +00001373
1374document.addEventListener('keydown', function(e) {
1375 if (e.keyIdentifier == 'U+003F' || e.keyIdentifier == 'U+00BF') {
1376 // WebKit MAC retursn 3F. WebKit WIN returns BF. This is a bug!
1377 // ? key
1378 showLegend();
1379 } else if (e.keyIdentifier == 'U+001B') {
1380 // escape key
1381 hideLegend();
Torne (Richard Coles)926b0012013-03-28 15:32:48 +00001382 ui.popup.hide();
Torne (Richard Coles)5c87bf82012-11-14 11:46:17 +00001383 }
1384}, false);
Torne (Richard Coles)926b0012013-03-28 15:32:48 +00001385
1386window.addEventListener('load', function() {
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +01001387 resourceLoader = new loader.Loader();
Torne (Richard Coles)926b0012013-03-28 15:32:48 +00001388 resourceLoader.load();
1389}, false);