| 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 |