blob: a4ec6f70b4f93e9448d3313a9bedaedf8a3f39c3 [file] [log] [blame]
/*
* Copyright (C) 2013, 2016 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
BuildbotTestResults = function(testStep, url)
{
BaseObject.call(this);
this.URL = url;
this._parseResults(testStep);
};
BaseObject.addConstructorFunctions(BuildbotTestResults);
BuildbotTestResults.prototype = {
constructor: BuildbotTestResults,
__proto__: BaseObject.prototype,
_parseResults: function(testStep)
{
this.name = testStep.name;
this.allPassed = false;
this.errorOccurred = false;
this.tooManyFailures = false;
this.failureCount = 0;
this.flakeyCount = 0;
this.totalLeakCount = 0;
this.uniqueLeakCount = 0;
this.newPassesCount = 0;
this.missingCount = 0;
this.crashCount = 0;
if (!testStep.complete) {
// The step never even ran, or hasn't finished running.
this.finished = false;
return;
}
this.finished = true;
if (!testStep.results || testStep.results === BuildbotIteration.SUCCESS || testStep.results === BuildbotIteration.WARNINGS) {
// All tests passed.
this.allPassed = true;
return;
}
if (/Exiting early/.test(testStep.state_string))
this.tooManyFailures = true;
function resultSummarizer(matchString, sum, outputLine)
{
// Sample outputLine: "53 failures 37 new passes 1 crashes"
var regex = new RegExp("(\\d+)\\s" + matchString);
match = regex.exec(outputLine);
if (!match) {
match = /^(\d+)\s/.exec(outputLine);
}
if (!match)
return sum;
if (!outputLine.contains(matchString))
return sum;
if (!sum || sum === -1)
sum = 0;
return sum + parseInt(match[1], 10);
}
this.failureCount = resultSummarizer('fail', null, testStep.state_string);
this.flakeyCount = resultSummarizer("flake", null, testStep.state_string);
this.totalLeakCount = resultSummarizer("total leak", null, testStep.state_string);
this.uniqueLeakCount = resultSummarizer("unique leak", null, testStep.state_string);
this.newPassesCount = resultSummarizer("new pass", null, testStep.state_string);
this.missingCount = resultSummarizer("missing", null, testStep.state_string);
this.crashCount = resultSummarizer("crash", null, testStep.state_string);
this.issueCount = resultSummarizer("issue", null, testStep.state_string);
if (!this.failureCount && !this.flakyCount && !this.totalLeakCount && !this.uniqueLeakCount && !this.newPassesCount && !this.missingCount) {
// This step exited with a non-zero exit status, but we didn't find any output about the number of failed tests.
// Something must have gone wrong (e.g., timed out and was killed by buildbot).
this.errorOccurred = true;
}
},
addFullLayoutTestResults: function(data)
{
console.assert(this.name === "layout-test");
function collectResults(subtree, predicate)
{
// Results object is a trie:
// directory
// subdirectory
// test1.html
// expected:"PASS"
// actual: "IMAGE"
// report: "REGRESSION"
// test2.html
// expected:"FAIL"
// actual:"TEXT"
var result = [];
for (var key in subtree) {
var value = subtree[key];
console.assert(typeof value === "object");
var isIndividualTest = value.hasOwnProperty("actual") && value.hasOwnProperty("expected");
if (isIndividualTest) {
// Possible values for actual and expected keys: PASS, FAIL, AUDIO, IMAGE, TEXT, IMAGE+TEXT, TIMEOUT, CRASH, MISSING.
// Both actual and expected can be space separated lists. Actual contains two values when retrying a failed test
// gives a different result (retrying may be disabled in tester configuration).
// Possible values for report key (when present): REGRESSION, MISSING, FLAKY.
if (predicate(value)) {
var item = {path: key};
// FIXME (bug 127186): Crash log URL will be incorrect if crash only happened on retry (e.g. "TEXT CRASH").
// It should point to retries subdirectory, but the information about which attempt failed gets lost here.
if (value.actual.contains("CRASH"))
item.crash = true;
if (value.actual.contains("TIMEOUT"))
item.timeout = true;
// FIXME (bug 127186): Similarly, we don't have a good way to present results for something like "TIMEOUT TEXT",
// not even UI wise. For now, only show a diff link if the first attempt has the diff.
if (value.actual.split(" ")[0].contains("TEXT"))
item.has_diff = true;
// FIXME (bug 127186): It is particularly unfortunate for image diffs, because we currently only check image results
// on retry (except for reftests), so many times, you will see images on buildbot page, but not on the dashboard.
// FIXME: Find a way to display expected mismatch reftest failures.
if (value.actual.split(" ")[0].contains("IMAGE") && value.reftest_type != "!=")
item.has_image_diff = true;
if (value.has_stderr)
item.has_stderr = true;
result.push(item);
}
} else {
var nestedTests = collectResults(value, predicate);
for (var i = 0, end = nestedTests.length; i < end; ++i)
nestedTests[i].path = key + "/" + nestedTests[i].path;
result = result.concat(nestedTests);
}
}
return result;
}
this.hasPrettyPatch = data.has_pretty_patch;
this.regressions = collectResults(data.tests, function(info) { return info["report"] === "REGRESSION" });
console.assert(data.num_regressions === this.regressions.length);
this.flakyTests = collectResults(data.tests, function(info) { return info["report"] === "FLAKY" });
console.assert(data.num_flaky === this.flakyTests.length);
this.testsWithMissingResults = collectResults(data.tests, function(info) { return info["report"] === "MISSING" });
// data.num_missing is not always equal to the size of testsWithMissingResults array,
// because buildbot counts regressions that had missing pixel results on retry (e.g. "TEXT MISSING").
console.assert(data.num_missing >= this.testsWithMissingResults.length);
},
addJavaScriptCoreTestFailures: function(data)
{
this.regressions = data.stressTestFailures;
},
};