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

Side by Side Diff: Tools/BuildSlaveSupport/build.webkit.org-config/public_html/TestFailures/scripts/results.js

Issue 13712005: Move GardeningServer out of BuildSlaveSupport (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: Created 7 years, 8 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
OLDNEW
(Empty)
1 /*
2 * Copyright (C) 2011 Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26 var results = results || {};
27
28 (function() {
29
30 var kResultsName = 'full_results.json';
31
32 var kBuildLinkRegexp = /a href="\d+\/"/g;
33 var kBuildNumberRegexp = /\d+/;
34
35 var PASS = 'PASS';
36 var TIMEOUT = 'TIMEOUT';
37 var TEXT = 'TEXT';
38 var CRASH = 'CRASH';
39 var IMAGE = 'IMAGE';
40 var IMAGE_TEXT = 'IMAGE+TEXT';
41 var AUDIO = 'AUDIO';
42 var MISSING = 'MISSING';
43
44 var kFailingResults = [TEXT, IMAGE_TEXT, AUDIO];
45
46 var kExpectedImageSuffix = '-expected.png';
47 var kActualImageSuffix = '-actual.png';
48 var kImageDiffSuffix = '-diff.png';
49 var kExpectedAudioSuffix = '-expected.wav';
50 var kActualAudioSuffix = '-actual.wav';
51 var kExpectedTextSuffix = '-expected.txt';
52 var kActualTextSuffix = '-actual.txt';
53 var kDiffTextSuffix = '-diff.txt';
54 var kCrashLogSuffix = '-crash-log.txt';
55
56 var kPNGExtension = 'png';
57 var kTXTExtension = 'txt';
58 var kWAVExtension = 'wav';
59
60 var kPreferredSuffixOrder = [
61 kExpectedImageSuffix,
62 kActualImageSuffix,
63 kImageDiffSuffix,
64 kExpectedTextSuffix,
65 kActualTextSuffix,
66 kDiffTextSuffix,
67 kCrashLogSuffix,
68 kExpectedAudioSuffix,
69 kActualAudioSuffix,
70 // FIXME: Add support for the rest of the result types.
71 ];
72
73 // Kinds of results.
74 results.kActualKind = 'actual';
75 results.kExpectedKind = 'expected';
76 results.kDiffKind = 'diff';
77 results.kUnknownKind = 'unknown';
78
79 // Types of tests.
80 results.kImageType = 'image'
81 results.kAudioType = 'audio'
82 results.kTextType = 'text'
83 // FIXME: There are more types of tests.
84
85 function layoutTestResultsURL(platform)
86 {
87 return config.kPlatforms[platform].layoutTestResultsURL;
88 }
89
90 function possibleSuffixListFor(failureTypeList)
91 {
92 var suffixList = [];
93
94 function pushImageSuffixes()
95 {
96 suffixList.push(kExpectedImageSuffix);
97 suffixList.push(kActualImageSuffix);
98 suffixList.push(kImageDiffSuffix);
99 }
100
101 function pushAudioSuffixes()
102 {
103 suffixList.push(kExpectedAudioSuffix);
104 suffixList.push(kActualAudioSuffix);
105 }
106
107 function pushTextSuffixes()
108 {
109 suffixList.push(kActualTextSuffix);
110 suffixList.push(kExpectedTextSuffix);
111 suffixList.push(kDiffTextSuffix);
112 // '-wdiff.html',
113 // '-pretty-diff.html',
114 }
115
116 $.each(failureTypeList, function(index, failureType) {
117 switch(failureType) {
118 case IMAGE:
119 pushImageSuffixes();
120 break;
121 case TEXT:
122 pushTextSuffixes();
123 break;
124 case AUDIO:
125 pushAudioSuffixes();
126 break;
127 case IMAGE_TEXT:
128 pushImageSuffixes();
129 pushTextSuffixes();
130 break;
131 case CRASH:
132 suffixList.push(kCrashLogSuffix);
133 break;
134 case MISSING:
135 pushImageSuffixes();
136 pushTextSuffixes();
137 break;
138 default:
139 // FIXME: Add support for the rest of the result types.
140 // '-expected.html',
141 // '-expected-mismatch.html',
142 // ... and possibly more.
143 break;
144 }
145 });
146
147 return base.uniquifyArray(suffixList);
148 }
149
150 results.failureTypeToExtensionList = function(failureType)
151 {
152 switch(failureType) {
153 case IMAGE:
154 return [kPNGExtension];
155 case AUDIO:
156 return [kWAVExtension];
157 case TEXT:
158 return [kTXTExtension];
159 case MISSING:
160 case IMAGE_TEXT:
161 return [kTXTExtension, kPNGExtension];
162 default:
163 // FIXME: Add support for the rest of the result types.
164 // '-expected.html',
165 // '-expected-mismatch.html',
166 // ... and possibly more.
167 return [];
168 }
169 };
170
171 results.failureTypeList = function(failureBlob)
172 {
173 return failureBlob.split(' ');
174 };
175
176 results.canRebaseline = function(failureTypeList)
177 {
178 return failureTypeList.some(function(element) {
179 return results.failureTypeToExtensionList(element).length > 0;
180 });
181 };
182
183 results.directoryForBuilder = function(builderName)
184 {
185 return config.kPlatforms[config.currentPlatform].resultsDirectoryNameFromBui lderName(builderName);
186 }
187
188 function resultsDirectoryURL(platform, builderName)
189 {
190 if (config.useLocalResults)
191 return '/localresult?path=';
192 return resultsDirectoryListingURL(platform, builderName) + 'results/layout-t est-results/';
193 }
194
195 function resultsDirectoryListingURL(platform, builderName)
196 {
197 return layoutTestResultsURL(platform) + '/' + results.directoryForBuilder(bu ilderName) + '/';
198 }
199
200 function resultsDirectoryURLForBuildNumber(platform, builderName, buildNumber, r evision)
201 {
202 return resultsDirectoryListingURL(platform, builderName) + config.kPlatforms [platform].resultsDirectoryForBuildNumber(buildNumber, revision) + '/';
203 }
204
205 function resultsSummaryURL(platform, builderName)
206 {
207 return resultsDirectoryURL(platform, builderName) + kResultsName;
208 }
209
210 function resultsSummaryURLForBuildNumber(platform, builderName, buildNumber, rev ision)
211 {
212 return resultsDirectoryURLForBuildNumber(platform, builderName, buildNumber, revision) + kResultsName;
213 }
214
215 var g_resultsCache = new base.AsynchronousCache(function (key, callback) {
216 net.jsonp(key, callback);
217 });
218
219 results.ResultAnalyzer = base.extends(Object, {
220 init: function(resultNode)
221 {
222 this._actual = resultNode ? results.failureTypeList(resultNode.actual) : [];
223 this._expected = resultNode ? this._addImpliedExpectations(results.failu reTypeList(resultNode.expected)) : [];
224 this._wontfix = resultNode ? resultNode.wontfix : false;
225 },
226 _addImpliedExpectations: function(resultsList)
227 {
228 if (resultsList.indexOf('FAIL') == -1)
229 return resultsList;
230 return resultsList.concat(kFailingResults);
231 },
232 _hasPass: function(results)
233 {
234 return results.indexOf(PASS) != -1;
235 },
236 unexpectedResults: function()
237 {
238 return this._actual.filter(function(result) {
239 return this._expected.indexOf(result) == -1;
240 }, this);
241 },
242 succeeded: function()
243 {
244 return this._hasPass(this._actual);
245 },
246 expectedToSucceed: function()
247 {
248 return this._hasPass(this._expected);
249 },
250 flaky: function()
251 {
252 return this._actual.length > 1;
253 },
254 wontfix: function()
255 {
256 return this._wontfix;
257 },
258 hasUnexpectedFailures: function()
259 {
260 var difference = {};
261 this._actual.forEach(function(actual) {
262 difference[actual] = actual !== PASS;
263 });
264 this._expected.forEach(function(expected) {
265 if (expected !== PASS)
266 delete difference[expected];
267 });
268 return Object.keys(difference).some(function(key) {
269 return difference[key];
270 });
271 }
272 })
273
274 function isExpectedFailure(resultNode)
275 {
276 var analyzer = new results.ResultAnalyzer(resultNode);
277 return !analyzer.hasUnexpectedFailures() && !analyzer.succeeded() && !analyz er.flaky() && !analyzer.wontfix();
278 }
279
280 function isUnexpectedFailure(resultNode)
281 {
282 var analyzer = new results.ResultAnalyzer(resultNode);
283 return analyzer.hasUnexpectedFailures() && !analyzer.succeeded() && !analyze r.flaky() && !analyzer.wontfix();
284 }
285
286 function isResultNode(node)
287 {
288 return !!node.actual;
289 }
290
291 results.expectedFailures = function(resultsTree)
292 {
293 return base.filterTree(resultsTree.tests, isResultNode, isExpectedFailure);
294 };
295
296 results.unexpectedFailures = function(resultsTree)
297 {
298 return base.filterTree(resultsTree.tests, isResultNode, isUnexpectedFailure) ;
299 };
300
301 results.unexpectedSuccesses = function(resultsTree)
302 {
303 return base.filterTree(resultsTree.tests, isResultNode, function(resultNode) {
304 var analyzer = new results.ResultAnalyzer(resultNode);
305 return !analyzer.expectedToSucceed() && analyzer.succeeded() && !analyze r.flaky();
306 });
307 };
308
309 function resultsByTest(resultsByBuilder, filter)
310 {
311 var resultsByTest = {};
312
313 $.each(resultsByBuilder, function(builderName, resultsTree) {
314 $.each(filter(resultsTree), function(testName, resultNode) {
315 resultsByTest[testName] = resultsByTest[testName] || {};
316 if (!config.kPlatforms[config.currentPlatform].haveBuilderAccumulate dResults)
317 resultNode._buildLocation = resultsTree._buildLocation;
318 resultsByTest[testName][builderName] = resultNode;
319 });
320 });
321
322 return resultsByTest;
323 }
324
325 results.expectedFailuresByTest = function(resultsByBuilder)
326 {
327 return resultsByTest(resultsByBuilder, results.expectedFailures);
328 };
329
330 results.unexpectedFailuresByTest = function(resultsByBuilder)
331 {
332 return resultsByTest(resultsByBuilder, results.unexpectedFailures);
333 };
334
335 results.unexpectedSuccessesByTest = function(resultsByBuilder)
336 {
337 return resultsByTest(resultsByBuilder, results.unexpectedSuccesses);
338 };
339
340 results.failureInfoForTestAndBuilder = function(resultsByTest, testName, builder Name)
341 {
342 var failureInfoForTest = {
343 'testName': testName,
344 'builderName': builderName,
345 'failureTypeList': results.failureTypeList(resultsByTest[testName][build erName].actual),
346 };
347
348 if (!config.kPlatforms[config.currentPlatform].haveBuilderAccumulatedResults )
349 failureInfoForTest.buildLocation = resultsByTest[testName][builderName]. _buildLocation;
350
351 return failureInfoForTest;
352 };
353
354 results.collectUnexpectedResults = function(dictionaryOfResultNodes)
355 {
356 var collectedResults = [];
357 $.each(dictionaryOfResultNodes, function(key, resultNode) {
358 var analyzer = new results.ResultAnalyzer(resultNode);
359 collectedResults = collectedResults.concat(analyzer.unexpectedResults()) ;
360 });
361 return base.uniquifyArray(collectedResults);
362 };
363
364 // Callback data is [{ buildNumber:, revision:, url: }]
365 function historicalResultsLocations(platform, builderName, callback)
366 {
367 if (config.kPlatforms[platform].useDirectoryListingForOldBuilds) {
368 var listingURL = resultsDirectoryListingURL(platform, builderName);
369 net.get(listingURL, function(directoryListing) {
370 var historicalResultsData = directoryListing.match(kBuildLinkRegexp) .map(function(buildLink) {
371 var buildNumber = parseInt(buildLink.match(kBuildNumberRegexp)[0 ]);
372 var revision = 0; // unused for Chromium.
373 var resultsData = {
374 'buildNumber': buildNumber,
375 'revision': revision,
376 'url': resultsSummaryURLForBuildNumber(platform, builderName , buildNumber, revision)
377 };
378 return resultsData;
379 }).reverse();
380
381 callback(historicalResultsData);
382 });
383 } else {
384 var historicalResultsData = [];
385 builders.cachedBuildInfos(platform, builderName, function(cachedBuildInf os) {
386 $.each(cachedBuildInfos, function(buildNumber, buildInfo) {
387 var resultsData = {
388 'buildNumber': buildNumber,
389 'revision': buildInfo.sourceStamp.revision,
390 'url': resultsSummaryURLForBuildNumber(platform, builderName , buildNumber, buildInfo.sourceStamp.revision),
391 }
392 historicalResultsData.push(resultsData);
393 });
394
395 callback(historicalResultsData.reverse());
396 });
397 }
398 }
399
400 function walkHistory(platform, builderName, testName, callback)
401 {
402 var indexOfNextKeyToFetch = 0;
403 var keyList = [];
404
405 function continueWalk()
406 {
407 if (indexOfNextKeyToFetch >= keyList.length) {
408 processResultNode(0, null);
409 return;
410 }
411
412 var resultsURL = keyList[indexOfNextKeyToFetch].url;
413 ++indexOfNextKeyToFetch;
414 g_resultsCache.get(resultsURL, function(resultsTree) {
415 if ($.isEmptyObject(resultsTree)) {
416 continueWalk();
417 return;
418 }
419 var resultNode = results.resultNodeForTest(resultsTree, testName);
420 var revision = parseInt(resultsTree['revision'])
421 if (isNaN(revision))
422 revision = 0;
423 processResultNode(revision, resultNode);
424 });
425 }
426
427 function processResultNode(revision, resultNode)
428 {
429 var shouldContinue = callback(revision, resultNode);
430 if (!shouldContinue)
431 return;
432 continueWalk();
433 }
434
435 historicalResultsLocations(platform, builderName, function(resultsLocations) {
436 keyList = resultsLocations;
437 continueWalk();
438 });
439 }
440
441 results.regressionRangeForFailure = function(builderName, testName, callback)
442 {
443 var oldestFailingRevision = 0;
444 var newestPassingRevision = 0;
445
446 // FIXME: should treat {platform, builderName} as a tuple
447 walkHistory(config.currentPlatform, builderName, testName, function(revision , resultNode) {
448 if (!revision) {
449 callback(oldestFailingRevision, newestPassingRevision);
450 return false;
451 }
452 if (!resultNode) {
453 newestPassingRevision = revision;
454 callback(oldestFailingRevision, newestPassingRevision);
455 return false;
456 }
457 if (isUnexpectedFailure(resultNode)) {
458 oldestFailingRevision = revision;
459 return true;
460 }
461 if (!oldestFailingRevision)
462 return true; // We need to keep looking for a failing revision.
463 newestPassingRevision = revision;
464 callback(oldestFailingRevision, newestPassingRevision);
465 return false;
466 });
467 };
468
469 function mergeRegressionRanges(regressionRanges)
470 {
471 var mergedRange = {};
472
473 mergedRange.oldestFailingRevision = 0;
474 mergedRange.newestPassingRevision = 0;
475
476 $.each(regressionRanges, function(builderName, range) {
477 if (!range.oldestFailingRevision && !range.newestPassingRevision)
478 return
479
480 if (!mergedRange.oldestFailingRevision)
481 mergedRange.oldestFailingRevision = range.oldestFailingRevision;
482 if (!mergedRange.newestPassingRevision)
483 mergedRange.newestPassingRevision = range.newestPassingRevision;
484
485 if (range.oldestFailingRevision && range.oldestFailingRevision < mergedR ange.oldestFailingRevision)
486 mergedRange.oldestFailingRevision = range.oldestFailingRevision;
487 if (range.newestPassingRevision > mergedRange.newestPassingRevision)
488 mergedRange.newestPassingRevision = range.newestPassingRevision;
489 });
490
491 return mergedRange;
492 }
493
494 results.unifyRegressionRanges = function(builderNameList, testName, callback)
495 {
496 var regressionRanges = {};
497
498 var tracker = new base.RequestTracker(builderNameList.length, function() {
499 var mergedRange = mergeRegressionRanges(regressionRanges);
500 callback(mergedRange.oldestFailingRevision, mergedRange.newestPassingRev ision);
501 });
502
503 $.each(builderNameList, function(index, builderName) {
504 results.regressionRangeForFailure(builderName, testName, function(oldest FailingRevision, newestPassingRevision) {
505 var range = {};
506 range.oldestFailingRevision = oldestFailingRevision;
507 range.newestPassingRevision = newestPassingRevision;
508 regressionRanges[builderName] = range;
509 tracker.requestComplete();
510 });
511 });
512 };
513
514 results.countFailureOccurences = function(builderNameList, testName, callback)
515 {
516 var failureCount = 0;
517
518 var tracker = new base.RequestTracker(builderNameList.length, function() {
519 callback(failureCount);
520 });
521
522 $.each(builderNameList, function(index, builderName) {
523 walkHistory(config.currentPlatform, builderName, testName, function(revi sion, resultNode) {
524 if (isUnexpectedFailure(resultNode)) {
525 ++failureCount;
526 return true;
527 }
528 tracker.requestComplete();
529 return false;
530 });
531 });
532 };
533
534 results.resultNodeForTest = function(resultsTree, testName)
535 {
536 var testNamePath = testName.split('/');
537 var currentNode = resultsTree['tests'];
538 $.each(testNamePath, function(index, segmentName) {
539 if (!currentNode)
540 return;
541 currentNode = (segmentName in currentNode) ? currentNode[segmentName] : null;
542 });
543 return currentNode;
544 };
545
546 results.resultKind = function(url)
547 {
548 if (/-actual\.[a-z]+$/.test(url))
549 return results.kActualKind;
550 else if (/-expected\.[a-z]+$/.test(url))
551 return results.kExpectedKind;
552 else if (/diff\.[a-z]+$/.test(url))
553 return results.kDiffKind;
554 return results.kUnknownKind;
555 }
556
557 results.resultType = function(url)
558 {
559 if (/\.png$/.test(url))
560 return results.kImageType;
561 if (/\.wav$/.test(url))
562 return results.kAudioType;
563 return results.kTextType;
564 }
565
566 function sortResultURLsBySuffix(urls)
567 {
568 var sortedURLs = [];
569 $.each(kPreferredSuffixOrder, function(i, suffix) {
570 $.each(urls, function(j, url) {
571 if (!base.endsWith(url, suffix))
572 return;
573 sortedURLs.push(url);
574 });
575 });
576 if (sortedURLs.length != urls.length)
577 throw "sortResultURLsBySuffix failed to return the same number of URLs."
578 return sortedURLs;
579 }
580
581 results.fetchResultsURLs = function(failureInfo, callback)
582 {
583 var testNameStem = base.trimExtension(failureInfo.testName);
584 var urlStem;
585
586 if (config.kPlatforms[config.currentPlatform].haveBuilderAccumulatedResults)
587 urlStem = resultsDirectoryURL(config.currentPlatform, failureInfo.builde rName);
588 else
589 urlStem = failureInfo.buildLocation.url.replace(kResultsName, '');
590
591 var suffixList = possibleSuffixListFor(failureInfo.failureTypeList);
592 var resultURLs = [];
593 var tracker = new base.RequestTracker(suffixList.length, function() {
594 callback(sortResultURLsBySuffix(resultURLs));
595 });
596 $.each(suffixList, function(index, suffix) {
597 var url = urlStem + testNameStem + suffix;
598 net.probe(url, {
599 success: function() {
600 resultURLs.push(url);
601 tracker.requestComplete();
602 },
603 error: function() {
604 tracker.requestComplete();
605 },
606 });
607 });
608 };
609
610 results.fetchResultsForBuilder = function(builderName, callback)
611 {
612 var resultsURL = resultsSummaryURL(config.currentPlatform, builderName);
613 net.jsonp(resultsURL, callback);
614 };
615
616 results.fetchResultsForBuildOnBuilder = function(builderName, buildNumber, revis ion, callback)
617 {
618 var resultsURL = resultsSummaryURLForBuildNumber(config.currentPlatform, bui lderName, buildNumber, revision);
619 net.jsonp(resultsURL, callback);
620 };
621
622 // Look for the most recent completed build that has full results.
623 results.fetchResultsForMostRecentCompletedBuildOnBuilder = function(builderName, callback)
624 {
625 historicalResultsLocations(config.currentPlatform, builderName, function(bui ldLocations) {
626 var currentIndex = 0;
627 var resultsCallback = function(buildResults) {
628 if ($.isEmptyObject(buildResults)) {
629 ++currentIndex;
630 if (currentIndex >= buildLocations.length) {
631 callback(null);
632 return;
633 }
634
635 net.jsonp(buildLocations[currentIndex].url, resultsCallback);
636 return;
637 }
638 if (!config.kPlatforms[config.currentPlatform].haveBuilderAccumulate dResults)
639 buildResults._buildLocation = buildLocations[currentIndex];
640 callback(buildResults);
641 };
642 net.jsonp(buildLocations[currentIndex].url, resultsCallback);
643 });
644 };
645
646 results.fetchResultsByBuilder = function(builderNameList, callback)
647 {
648 var resultsByBuilder = {};
649 if (config.kPlatforms[config.currentPlatform].haveBuilderAccumulatedResults) {
650 var tracker = new base.RequestTracker(builderNameList.length, function() {
651 callback(resultsByBuilder);
652 });
653 $.each(builderNameList, function(index, builderName) {
654 results.fetchResultsForBuilder(builderName, function(resultsTree) {
655 resultsByBuilder[builderName] = resultsTree;
656 tracker.requestComplete();
657 });
658 });
659 } else {
660 builders.recentBuildInfos(function(recentBuildInfos) {
661 var requestsInFlight = 0;
662 $.each(builderNameList, function(index, builderName) {
663 if (recentBuildInfos[builderName]) {
664 // FIXME: use RequestTracker
665 ++requestsInFlight;
666 results.fetchResultsForMostRecentCompletedBuildOnBuilder(bui lderName, function(resultsTree) {
667 if (resultsTree)
668 resultsByBuilder[builderName] = resultsTree;
669 --requestsInFlight;
670 if (!requestsInFlight)
671 callback(resultsByBuilder);
672 });
673 }
674 });
675 });
676 }
677 };
678
679 })();
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698