OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 2011 Google Inc. All rights reserved. | 2 * Copyright (C) 2011 Google Inc. All rights reserved. |
3 * | 3 * |
4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
5 * modification, are permitted provided that the following conditions | 5 * modification, are permitted provided that the following conditions |
6 * are met: | 6 * are met: |
7 * 1. Redistributions of source code must retain the above copyright | 7 * 1. Redistributions of source code must retain the above copyright |
8 * notice, this list of conditions and the following disclaimer. | 8 * notice, this list of conditions and the following disclaimer. |
9 * 2. Redistributions in binary form must reproduce the above copyright | 9 * 2. Redistributions in binary form must reproduce the above copyright |
10 * notice, this list of conditions and the following disclaimer in the | 10 * notice, this list of conditions and the following disclaimer in the |
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
67 // FIXME: Add support for the rest of the result types. | 67 // FIXME: Add support for the rest of the result types. |
68 ]; | 68 ]; |
69 | 69 |
70 // Kinds of results. | 70 // Kinds of results. |
71 results.kActualKind = 'actual'; | 71 results.kActualKind = 'actual'; |
72 results.kExpectedKind = 'expected'; | 72 results.kExpectedKind = 'expected'; |
73 results.kDiffKind = 'diff'; | 73 results.kDiffKind = 'diff'; |
74 results.kUnknownKind = 'unknown'; | 74 results.kUnknownKind = 'unknown'; |
75 | 75 |
76 // Types of tests. | 76 // Types of tests. |
77 results.kImageType = 'image' | 77 results.kImageType = 'image'; |
78 results.kAudioType = 'audio' | 78 results.kAudioType = 'audio'; |
79 results.kTextType = 'text' | 79 results.kTextType = 'text'; |
80 // FIXME: There are more types of tests. | 80 // FIXME: There are more types of tests. |
81 | 81 |
82 function possibleSuffixListFor(failureTypeList) | 82 function possibleSuffixListFor(failureTypeList) |
83 { | 83 { |
84 var suffixList = []; | 84 var suffixList = []; |
85 | 85 |
86 function pushImageSuffixes() | 86 function pushImageSuffixes() |
87 { | 87 { |
88 suffixList.push(kExpectedImageSuffix); | 88 suffixList.push(kExpectedImageSuffix); |
89 suffixList.push(kActualImageSuffix); | 89 suffixList.push(kActualImageSuffix); |
(...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
188 function resultsSummaryURL(builderName) | 188 function resultsSummaryURL(builderName) |
189 { | 189 { |
190 return resultsDirectoryURL(builderName) + kResultsName; | 190 return resultsDirectoryURL(builderName) + kResultsName; |
191 } | 191 } |
192 | 192 |
193 function resultsSummaryURLForBuildNumber(builderName, buildNumber) | 193 function resultsSummaryURLForBuildNumber(builderName, buildNumber) |
194 { | 194 { |
195 return resultsDirectoryURLForBuildNumber(builderName, buildNumber) + kResult
sName; | 195 return resultsDirectoryURLForBuildNumber(builderName, buildNumber) + kResult
sName; |
196 } | 196 } |
197 | 197 |
198 var g_resultsCache = new base.AsynchronousCache(function (key, callback) { | 198 var g_resultsCache = new base.AsynchronousCache(function(key) { |
199 net.jsonp(key).then(callback); | 199 return net.jsonp(key); |
200 }); | 200 }); |
201 | 201 |
202 results.ResultAnalyzer = base.extends(Object, { | 202 results.ResultAnalyzer = base.extends(Object, { |
203 init: function(resultNode) | 203 init: function(resultNode) |
204 { | 204 { |
205 this._isUnexpected = resultNode.is_unexpected; | 205 this._isUnexpected = resultNode.is_unexpected; |
206 this._actual = resultNode ? results.failureTypeList(resultNode.actual) :
[]; | 206 this._actual = resultNode ? results.failureTypeList(resultNode.actual) :
[]; |
207 this._expected = resultNode ? this._addImpliedExpectations(results.failu
reTypeList(resultNode.expected)) : []; | 207 this._expected = resultNode ? this._addImpliedExpectations(results.failu
reTypeList(resultNode.expected)) : []; |
208 }, | 208 }, |
209 _addImpliedExpectations: function(resultsList) | 209 _addImpliedExpectations: function(resultsList) |
(...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
306 { | 306 { |
307 var collectedResults = []; | 307 var collectedResults = []; |
308 $.each(dictionaryOfResultNodes, function(key, resultNode) { | 308 $.each(dictionaryOfResultNodes, function(key, resultNode) { |
309 var analyzer = new results.ResultAnalyzer(resultNode); | 309 var analyzer = new results.ResultAnalyzer(resultNode); |
310 collectedResults = collectedResults.concat(analyzer.unexpectedResults())
; | 310 collectedResults = collectedResults.concat(analyzer.unexpectedResults())
; |
311 }); | 311 }); |
312 return base.uniquifyArray(collectedResults); | 312 return base.uniquifyArray(collectedResults); |
313 }; | 313 }; |
314 | 314 |
315 // Callback data is [{ buildNumber:, url: }] | 315 // Callback data is [{ buildNumber:, url: }] |
316 function historicalResultsLocations(builderName, callback) | 316 function historicalResultsLocations(builderName) |
317 { | 317 { |
318 var historicalResultsData = []; | 318 var historicalResultsData = []; |
319 | |
320 function parseListingDocument(prefixListingDocument) { | 319 function parseListingDocument(prefixListingDocument) { |
321 $(prefixListingDocument).find("Prefix").each(function() { | 320 $(prefixListingDocument).find("Prefix").each(function() { |
322 var buildString = this.textContent.replace(config.resultsDirectoryNa
meFromBuilderName(builderName) + '/', ''); | 321 var buildString = this.textContent.replace(config.resultsDirectoryNa
meFromBuilderName(builderName) + '/', ''); |
323 if (buildString.match(/\d+\//)) { | 322 if (buildString.match(/\d+\//)) { |
324 var buildNumber = parseInt(buildString); | 323 var buildNumber = parseInt(buildString); |
325 var resultsData = { | 324 var resultsData = { |
326 'buildNumber': buildNumber, | 325 'buildNumber': buildNumber, |
327 'url': resultsSummaryURLForBuildNumber(builderName, buildNum
ber) | 326 'url': resultsSummaryURLForBuildNumber(builderName, buildNum
ber) |
328 }; | 327 }; |
329 historicalResultsData.unshift(resultsData); | 328 historicalResultsData.unshift(resultsData); |
330 } | 329 } |
331 }); | 330 }); |
332 var nextMarker = $(prefixListingDocument).find('NextMarker').get(); | 331 var nextMarker = $(prefixListingDocument).find('NextMarker').get(); |
333 if (nextMarker.length) { | 332 if (nextMarker.length) { |
334 var nextListingURL = resultsPrefixListingURL(builderName, nextMarker
[0].textContent); | 333 var nextListingURL = resultsPrefixListingURL(builderName, nextMarker
[0].textContent); |
335 net.xml(nextListingURL).then(parseListingDocument); | 334 return net.xml(nextListingURL).then(function(doc) { |
| 335 return parseListingDocument(doc); |
| 336 }); |
336 } else { | 337 } else { |
337 callback(historicalResultsData); | 338 return historicalResultsData; |
338 } | 339 } |
339 } | 340 } |
340 | 341 |
341 builders.mostRecentBuildForBuilder(builderName, function (mostRecentBuildNum
ber) { | 342 return builders.mostRecentBuildForBuilder(builderName).then(function (mostRe
centBuildNumber) { |
342 var marker = config.resultsDirectoryNameFromBuilderName(builderName) + "
/" + (mostRecentBuildNumber - 100) + "/"; | 343 var marker = config.resultsDirectoryNameFromBuilderName(builderName) + "
/" + (mostRecentBuildNumber - 100) + "/"; |
343 var listingURL = resultsPrefixListingURL(builderName, marker); | 344 var listingURL = resultsPrefixListingURL(builderName, marker); |
344 net.xml(listingURL).then(parseListingDocument); | 345 return net.xml(listingURL).then(function(doc) { |
| 346 return parseListingDocument(doc); |
| 347 }); |
345 }); | 348 }); |
346 } | 349 } |
347 | 350 |
348 function walkHistory(builderName, testName, callback) | 351 // This will repeatedly call continueCallback(revision, resultNode) until it ret
urns false. |
| 352 function walkHistory(builderName, testName, continueCallback) |
349 { | 353 { |
350 var indexOfNextKeyToFetch = 0; | 354 var indexOfNextKeyToFetch = 0; |
351 var keyList = []; | 355 var keyList = []; |
352 | 356 |
353 function continueWalk() | 357 function continueWalk() |
354 { | 358 { |
355 if (indexOfNextKeyToFetch >= keyList.length) { | 359 if (indexOfNextKeyToFetch >= keyList.length) { |
356 processResultNode(0, null); | 360 processResultNode(0, null); |
357 return; | 361 return; |
358 } | 362 } |
359 | 363 |
360 var resultsURL = keyList[indexOfNextKeyToFetch].url; | 364 var resultsURL = keyList[indexOfNextKeyToFetch].url; |
361 ++indexOfNextKeyToFetch; | 365 ++indexOfNextKeyToFetch; |
362 g_resultsCache.get(resultsURL, function(resultsTree) { | 366 g_resultsCache.get(resultsURL).then(function(resultsTree) { |
363 if ($.isEmptyObject(resultsTree)) { | 367 if ($.isEmptyObject(resultsTree)) { |
364 continueWalk(); | 368 continueWalk(); |
365 return; | 369 return; |
366 } | 370 } |
367 var resultNode = results.resultNodeForTest(resultsTree, testName); | 371 var resultNode = results.resultNodeForTest(resultsTree, testName); |
368 var revision = parseInt(resultsTree['blink_revision']) | 372 var revision = parseInt(resultsTree['blink_revision']); |
369 if (isNaN(revision)) | 373 if (isNaN(revision)) |
370 revision = 0; | 374 revision = 0; |
371 processResultNode(revision, resultNode); | 375 processResultNode(revision, resultNode); |
372 }); | 376 }); |
373 } | 377 } |
374 | 378 |
375 function processResultNode(revision, resultNode) | 379 function processResultNode(revision, resultNode) |
376 { | 380 { |
377 var shouldContinue = callback(revision, resultNode); | 381 var shouldContinue = continueCallback(revision, resultNode); |
378 if (!shouldContinue) | 382 if (!shouldContinue) |
379 return; | 383 return; |
380 continueWalk(); | 384 continueWalk(); |
381 } | 385 } |
382 | 386 |
383 historicalResultsLocations(builderName, function(resultsLocations) { | 387 historicalResultsLocations(builderName).then(function(resultsLocations) { |
384 keyList = resultsLocations; | 388 keyList = resultsLocations; |
385 continueWalk(); | 389 continueWalk(); |
386 }); | 390 }); |
387 } | 391 } |
388 | 392 |
389 results.regressionRangeForFailure = function(builderName, testName, callback) | 393 results.regressionRangeForFailure = function(builderName, testName) { |
390 { | 394 return new Promise(function(resolve, reject) { |
391 var oldestFailingRevision = 0; | 395 var oldestFailingRevision = 0; |
392 var newestPassingRevision = 0; | 396 var newestPassingRevision = 0; |
393 | 397 |
394 walkHistory(builderName, testName, function(revision, resultNode) { | 398 walkHistory(builderName, testName, function(revision, resultNode) { |
395 if (!revision) { | 399 if (!revision) { |
396 callback(oldestFailingRevision, newestPassingRevision); | 400 resolve([oldestFailingRevision, newestPassingRevision]); |
| 401 return false; |
| 402 } |
| 403 if (!resultNode) { |
| 404 newestPassingRevision = revision; |
| 405 resolve([oldestFailingRevision, newestPassingRevision]); |
| 406 return false; |
| 407 } |
| 408 if (isUnexpectedFailure(resultNode)) { |
| 409 oldestFailingRevision = revision; |
| 410 return true; |
| 411 } |
| 412 if (!oldestFailingRevision) |
| 413 return true; // We need to keep looking for a failing revision. |
| 414 newestPassingRevision = revision; |
| 415 resolve([oldestFailingRevision, newestPassingRevision]); |
397 return false; | 416 return false; |
398 } | 417 }); |
399 if (!resultNode) { | |
400 newestPassingRevision = revision; | |
401 callback(oldestFailingRevision, newestPassingRevision); | |
402 return false; | |
403 } | |
404 if (isUnexpectedFailure(resultNode)) { | |
405 oldestFailingRevision = revision; | |
406 return true; | |
407 } | |
408 if (!oldestFailingRevision) | |
409 return true; // We need to keep looking for a failing revision. | |
410 newestPassingRevision = revision; | |
411 callback(oldestFailingRevision, newestPassingRevision); | |
412 return false; | |
413 }); | 418 }); |
414 }; | 419 }; |
415 | 420 |
416 function mergeRegressionRanges(regressionRanges) | 421 function mergeRegressionRanges(regressionRanges) |
417 { | 422 { |
418 var mergedRange = {}; | 423 var mergedRange = {}; |
419 | 424 |
420 mergedRange.oldestFailingRevision = 0; | 425 mergedRange.oldestFailingRevision = 0; |
421 mergedRange.newestPassingRevision = 0; | 426 mergedRange.newestPassingRevision = 0; |
422 | 427 |
423 $.each(regressionRanges, function(builderName, range) { | 428 $.each(regressionRanges, function(builderName, range) { |
424 if (!range.oldestFailingRevision && !range.newestPassingRevision) | 429 if (!range.oldestFailingRevision && !range.newestPassingRevision) |
425 return | 430 return |
426 | 431 |
427 if (!mergedRange.oldestFailingRevision) | 432 if (!mergedRange.oldestFailingRevision) |
428 mergedRange.oldestFailingRevision = range.oldestFailingRevision; | 433 mergedRange.oldestFailingRevision = range.oldestFailingRevision; |
429 if (!mergedRange.newestPassingRevision) | 434 if (!mergedRange.newestPassingRevision) |
430 mergedRange.newestPassingRevision = range.newestPassingRevision; | 435 mergedRange.newestPassingRevision = range.newestPassingRevision; |
431 | 436 |
432 if (range.oldestFailingRevision && range.oldestFailingRevision < mergedR
ange.oldestFailingRevision) | 437 if (range.oldestFailingRevision && range.oldestFailingRevision < mergedR
ange.oldestFailingRevision) |
433 mergedRange.oldestFailingRevision = range.oldestFailingRevision; | 438 mergedRange.oldestFailingRevision = range.oldestFailingRevision; |
434 if (range.newestPassingRevision > mergedRange.newestPassingRevision) | 439 if (range.newestPassingRevision > mergedRange.newestPassingRevision) |
435 mergedRange.newestPassingRevision = range.newestPassingRevision; | 440 mergedRange.newestPassingRevision = range.newestPassingRevision; |
436 }); | 441 }); |
437 | 442 |
438 return mergedRange; | 443 return mergedRange; |
439 } | 444 } |
440 | 445 |
441 results.unifyRegressionRanges = function(builderNameList, testName, callback) | 446 results.unifyRegressionRanges = function(builderNameList, testName) { |
442 { | |
443 var regressionRanges = {}; | 447 var regressionRanges = {}; |
444 | 448 |
445 var tracker = new base.RequestTracker(builderNameList.length, function() { | 449 var rangePromises = []; |
| 450 $.each(builderNameList, function(index, builderName) { |
| 451 rangePromises.push(results.regressionRangeForFailure(builderName, testNa
me) |
| 452 .then(function(result) { |
| 453 var oldestFailingRevision = result[0]; |
| 454 var newestPassingRevision = result[1]; |
| 455 var range = {}; |
| 456 range.oldestFailingRevision = oldestFailingRevisi
on; |
| 457 range.newestPassingRevision = newestPassingRevisi
on; |
| 458 regressionRanges[builderName] = range; |
| 459 })); |
| 460 }); |
| 461 return Promise.all(rangePromises).then(function() { |
446 var mergedRange = mergeRegressionRanges(regressionRanges); | 462 var mergedRange = mergeRegressionRanges(regressionRanges); |
447 callback(mergedRange.oldestFailingRevision, mergedRange.newestPassingRev
ision); | 463 return [mergedRange.oldestFailingRevision, mergedRange.newestPassingRevi
sion]; |
448 }); | |
449 | |
450 $.each(builderNameList, function(index, builderName) { | |
451 results.regressionRangeForFailure(builderName, testName, function(oldest
FailingRevision, newestPassingRevision) { | |
452 var range = {}; | |
453 range.oldestFailingRevision = oldestFailingRevision; | |
454 range.newestPassingRevision = newestPassingRevision; | |
455 regressionRanges[builderName] = range; | |
456 tracker.requestComplete(); | |
457 }); | |
458 }); | 464 }); |
459 }; | 465 }; |
460 | 466 |
461 results.resultNodeForTest = function(resultsTree, testName) | 467 results.resultNodeForTest = function(resultsTree, testName) |
462 { | 468 { |
463 var testNamePath = testName.split('/'); | 469 var testNamePath = testName.split('/'); |
464 var currentNode = resultsTree['tests']; | 470 var currentNode = resultsTree['tests']; |
465 $.each(testNamePath, function(index, segmentName) { | 471 $.each(testNamePath, function(index, segmentName) { |
466 if (!currentNode) | 472 if (!currentNode) |
467 return; | 473 return; |
(...skipping 26 matching lines...) Expand all Loading... |
494 { | 500 { |
495 var sortedURLs = []; | 501 var sortedURLs = []; |
496 $.each(kPreferredSuffixOrder, function(i, suffix) { | 502 $.each(kPreferredSuffixOrder, function(i, suffix) { |
497 $.each(urls, function(j, url) { | 503 $.each(urls, function(j, url) { |
498 if (!base.endsWith(url, suffix)) | 504 if (!base.endsWith(url, suffix)) |
499 return; | 505 return; |
500 sortedURLs.push(url); | 506 sortedURLs.push(url); |
501 }); | 507 }); |
502 }); | 508 }); |
503 if (sortedURLs.length != urls.length) | 509 if (sortedURLs.length != urls.length) |
504 throw "sortResultURLsBySuffix failed to return the same number of URLs." | 510 throw "sortResultURLsBySuffix failed to return the same number of URLs."
; |
505 return sortedURLs; | 511 return sortedURLs; |
506 } | 512 } |
507 | 513 |
508 results.fetchResultsURLs = function(failureInfo, callback) | 514 results.fetchResultsURLs = function(failureInfo) |
509 { | 515 { |
510 var testNameStem = base.trimExtension(failureInfo.testName); | 516 var testNameStem = base.trimExtension(failureInfo.testName); |
511 var urlStem = resultsDirectoryURL(failureInfo.builderName); | 517 var urlStem = resultsDirectoryURL(failureInfo.builderName); |
512 | 518 |
513 var suffixList = possibleSuffixListFor(failureInfo.failureTypeList); | 519 var suffixList = possibleSuffixListFor(failureInfo.failureTypeList); |
514 var resultURLs = []; | 520 var resultURLs = []; |
515 var tracker = new base.RequestTracker(suffixList.length, function() { | 521 var probePromises = []; |
516 callback(sortResultURLsBySuffix(resultURLs)); | |
517 }); | |
518 $.each(suffixList, function(index, suffix) { | 522 $.each(suffixList, function(index, suffix) { |
519 var url = urlStem + testNameStem + suffix; | 523 var url = urlStem + testNameStem + suffix; |
520 net.probe(url).then( | 524 probePromises.push(net.probe(url).then( |
521 function() { | 525 function() { |
522 resultURLs.push(url); | 526 resultURLs.push(url); |
523 tracker.requestComplete(); | |
524 }, | 527 }, |
525 function() { | 528 function() {})); |
526 tracker.requestComplete(); | 529 }); |
527 }); | 530 return Promise.all(probePromises).then(function() { |
| 531 return sortResultURLsBySuffix(resultURLs); |
528 }); | 532 }); |
529 }; | 533 }; |
530 | 534 |
531 results.fetchResultsByBuilder = function(builderNameList, callback) | 535 results.fetchResultsByBuilder = function(builderNameList) |
532 { | 536 { |
533 var resultsByBuilder = {}; | 537 var resultsByBuilder = {}; |
534 var tracker = new base.RequestTracker(builderNameList.length, function() { | 538 var fetchPromises = []; |
535 callback(resultsByBuilder); | |
536 }); | |
537 $.each(builderNameList, function(index, builderName) { | 539 $.each(builderNameList, function(index, builderName) { |
538 var resultsURL = resultsSummaryURL(builderName); | 540 var resultsURL = resultsSummaryURL(builderName); |
539 net.jsonp(resultsURL).then(function(resultsTree) { | 541 fetchPromises.push(net.jsonp(resultsURL).then(function(resultsTree) { |
540 resultsByBuilder[builderName] = resultsTree; | 542 resultsByBuilder[builderName] = resultsTree; |
541 tracker.requestComplete(); | 543 })); |
542 }); | 544 }); |
| 545 return Promise.all(fetchPromises).then(function() { |
| 546 return resultsByBuilder; |
543 }); | 547 }); |
544 }; | 548 }; |
545 | 549 |
546 })(); | 550 })(); |
OLD | NEW |