Commit 3d0eb332 authored by ojan@chromium.org's avatar ojan@chromium.org

Show non-webkit test failures in the failure stream

-Remove ct-failing-builders and instead show all failures in the stream the
same way we do for webkit tests.
-Sort the failures in a group by step name then by test name.
-Do the dumb object --> model classes transition in small pieces to avoid blocking
getting a minimally viable product ready too much on completely doing this
refactoring.

The UI is not awesome, but I wanted to get the functionality in place first.

NOTRY=true
R=abarth@chromium.org

Review URL: https://codereview.chromium.org/416673003

git-svn-id: svn://svn.chromium.org/blink/trunk@179194 bbb929c8-8fbe-4397-9dbb-9b2b20218538
parent e3cdf6d3
<!--
Copyright 2014 The Chromium Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
-->
<script>
function CTFailure(step, reason, resultsByBuilder, oldestFailingRevision, newestPassingRevision) {
this.step = step;
// FIXME: Rename this to reason.
this.testName = reason;
this.resultNodesByBuilder = resultsByBuilder;
// FIXME: Move these up one layer to the groupedFailures object.
this.oldestFailingRevision = oldestFailingRevision;
this.newestPassingRevision = newestPassingRevision;
}
</script>
...@@ -50,6 +50,7 @@ THE POSSIBILITY OF SUCH DAMAGE. ...@@ -50,6 +50,7 @@ THE POSSIBILITY OF SUCH DAMAGE.
<script src="scripts/results.js"></script> <script src="scripts/results.js"></script>
<script src="scripts/results_unittests.js"></script> <script src="scripts/results_unittests.js"></script>
<script src="scripts/ui.js"></script> <script src="scripts/ui.js"></script>
<script src="scripts/ui_unittests.js"></script>
<script src="bower_components/platform/platform.js"></script> <script src="bower_components/platform/platform.js"></script>
<link rel="import" href="bower_components/polymer/polymer.html"> <link rel="import" href="bower_components/polymer/polymer.html">
...@@ -61,7 +62,7 @@ THE POSSIBILITY OF SUCH DAMAGE. ...@@ -61,7 +62,7 @@ THE POSSIBILITY OF SUCH DAMAGE.
<link rel="import" href="ui/ct-commit-tests.html"> <link rel="import" href="ui/ct-commit-tests.html">
<link rel="import" href="ui/ct-commit-list-tests.html"> <link rel="import" href="ui/ct-commit-list-tests.html">
<link rel="import" href="ui/ct-embedded-flakiness-dashboard-tests.html"> <link rel="import" href="ui/ct-embedded-flakiness-dashboard-tests.html">
<link rel="import" href="ui/ct-failing-builders-tests.html"> <link rel="import" href="ui/ct-failure-analyzer-tests.html">
<link rel="import" href="ui/ct-failure-card-tests.html"> <link rel="import" href="ui/ct-failure-card-tests.html">
<link rel="import" href="ui/ct-failure-grouper-tests.html"> <link rel="import" href="ui/ct-failure-grouper-tests.html">
<link rel="import" href="ui/ct-failure-stream-tests.html"> <link rel="import" href="ui/ct-failure-stream-tests.html">
......
...@@ -34,15 +34,27 @@ ui.displayNameForBuilder = function(builderName) ...@@ -34,15 +34,27 @@ ui.displayNameForBuilder = function(builderName)
return builderName.replace(/Webkit /i, ''); return builderName.replace(/Webkit /i, '');
} }
ui.urlForFlakinessDashboard = function(opt_testNameList) // FIXME: Take a master name argument as well.
ui.urlForFlakinessDashboard = function(testNames, testType)
{ {
var testsParameter = opt_testNameList ? opt_testNameList.join(',') : ''; if (Array.isArray(testNames))
return 'http://test-results.appspot.com/dashboards/flakiness_dashboard.html#tests=' + encodeURIComponent(testsParameter); testNames = testNames.join(',');
// FIXME: Remove this once the flakiness dashboard stops having webkit_tests
// masquerade as layout-tests.
if (testType == 'webkit_tests')
testType = 'layout-tests';
return 'http://test-results.appspot.com/dashboards/flakiness_dashboard.html#' +
Object.toQueryString({
tests: testNames,
testType: testType,
});
} }
ui.urlForEmbeddedFlakinessDashboard = function(opt_testNameList) ui.urlForEmbeddedFlakinessDashboard = function(testNames, testType)
{ {
return ui.urlForFlakinessDashboard(opt_testNameList) + '&showChrome=false'; return ui.urlForFlakinessDashboard(testNames, testType) + '&showChrome=false';
} }
})(); })();
/*
* Copyright (C) 2013 Google 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.
*/
(function () {
module("ui");
var flakinessBaseUrl = 'http://test-results.appspot.com/dashboards/flakiness_dashboard.html#';
test('urlForFlakinessDashboard', 4, function() {
equal(ui.urlForFlakinessDashboard('foo', 'bar'),
flakinessBaseUrl + 'tests=foo&testType=bar');
equal(ui.urlForFlakinessDashboard(['foo', 'baz'], 'bar'),
flakinessBaseUrl + 'tests=foo%2Cbaz&testType=bar');
equal(ui.urlForFlakinessDashboard('foo', 'webkit_tests'),
flakinessBaseUrl + 'tests=foo&testType=layout-tests');
equal(ui.urlForFlakinessDashboard('foo', 'layout-tests'),
flakinessBaseUrl + 'tests=foo&testType=layout-tests');
});
test('urlForEmbeddedFlakinessDashboard', 1, function() {
equal(ui.urlForEmbeddedFlakinessDashboard('foo', 'bar'),
flakinessBaseUrl + 'tests=foo&testType=bar&showChrome=false');
});
})();
...@@ -35,6 +35,7 @@ found in the LICENSE file. ...@@ -35,6 +35,7 @@ found in the LICENSE file.
background-color: #fffc6c; background-color: #fffc6c;
} }
.FAIL span,
.TEXT span { .TEXT span {
background-color: #e98080; background-color: #e98080;
} }
......
...@@ -18,14 +18,18 @@ asyncTest("url", 1, function() { ...@@ -18,14 +18,18 @@ asyncTest("url", 1, function() {
} }
try { try {
ui.urlForEmbeddedFlakinessDashboard = function(testList) { return "about:blank#" + testList.join(','); } ui.urlForEmbeddedFlakinessDashboard = function(test, step) { return "about:blank#" + test + step; }
var embeddedFlakinessDashboard = document.createElement('ct-embedded-flakiness-dashboard'); var embeddedFlakinessDashboard = document.createElement('ct-embedded-flakiness-dashboard');
var testName = "foo/bar.html"; var testName = "foo/bar.html";
embeddedFlakinessDashboard.test = testName; var step = "foo_tests";
embeddedFlakinessDashboard.test = {
testName: testName,
step: step,
};
requestAnimationFrame(function() { requestAnimationFrame(function() {
var iframe = embeddedFlakinessDashboard.shadowRoot.querySelector('#iframe'); var iframe = embeddedFlakinessDashboard.shadowRoot.querySelector('#iframe');
equal(iframe.src, ui.urlForEmbeddedFlakinessDashboard([testName])); equal(iframe.src, ui.urlForEmbeddedFlakinessDashboard(testName, step));
reset(); reset();
start(); start();
}); });
......
...@@ -31,7 +31,7 @@ found in the LICENSE file. ...@@ -31,7 +31,7 @@ found in the LICENSE file.
}, },
testChanged: function() { testChanged: function() {
this.$.iframe.src = ui.urlForEmbeddedFlakinessDashboard([this.test]); this.$.iframe.src = ui.urlForEmbeddedFlakinessDashboard(this.test.testName, this.test.step);
}, },
}); });
......
<!--
Copyright 2014 The Chromium Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
-->
<link rel="import" href="ct-failing-builders.html">
<script>
(function () {
var kExampleBuilders = {
'Win7 (dbg)': ['compile'],
'WebKit Mac10.6 (dbg)': ['content_browsertests', 'unit_tests'],
'WebKit XP': ['runhooks', 'webkit_unit_tests', 'webkit_lint'],
};
module("ct-failing-builders");
asyncTest("basic", 3, function() {
var el = document.createElement('ct-failing-builders');
el.builders = kExampleBuilders;
el.async(function() {
var builders = el.shadowRoot.querySelectorAll('ct-builder');
for (var i = 0; i < builders.length; i++) {
var steps = builders[i].shadowRoot.querySelector('.details');
ok(steps.textContent == 'compile' ||
steps.textContent == 'content_browsertests, unit_tests' ||
steps.textContent == 'runhooks, webkit_unit_tests, webkit_lint');
}
start();
});
});
})()
</script>
<!--
Copyright 2014 The Chromium Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
-->
<link rel="import" href="ct-builder.html">
<!-- Lists builders that are failing and the problematic steps. -->
<polymer-element name="ct-failing-builders" attributes="builders">
<template>
<style>
:host {
display: block;
font-size: 0;
}
ct-builder {
display: inline-block;
margin: 0 5px 2px 0;
}
</style>
<template repeat="{{ builder in builders|keys }}">
<ct-builder builder="{{ builder }}" steps="{{ builders[builder] }}"></ct-builder>
</template>
</template>
<script>
Polymer({
builders: {},
keys: function(obj) {
return Object.keys(obj);
},
});
</script>
</polymer-element>
<!--
Copyright 2014 The Chromium Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
-->
<link rel="import" href="ct-failure-analyzer.html">
<script>
(function() {
module("ct-failure-analyzer");
test("_failureComparator", 1, function() {
var analyzer = document.createElement('ct-failure-analyzer');
var resultsByBuilder = {};
var failure1 = new CTFailure("step1", "reason1", resultsByBuilder, 123, 123);
var failure2 = new CTFailure("step1", "reason2", resultsByBuilder, 123, 123);
var failure3 = new CTFailure("step1", "reason3", resultsByBuilder, 123, 123);
var failure4 = new CTFailure("step2", "reason1", resultsByBuilder, 123, 123);
var failures = [failure4, failure3, failure2, failure1];
var expectedFailures = [failure1, failure2, failure3, failure4];
deepEqual(failures.sort(analyzer._failureComparator), expectedFailures);
});
})()
</script>
\ No newline at end of file
...@@ -4,6 +4,8 @@ Use of this source code is governed by a BSD-style license that can be ...@@ -4,6 +4,8 @@ Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file. found in the LICENSE file.
--> -->
<link rel="import" href="../model/ct-failure.html">
<polymer-element name="ct-failure-analyzer" attributes="failures builderLatestRevisions"> <polymer-element name="ct-failure-analyzer" attributes="failures builderLatestRevisions">
<script> <script>
// FIXME: Don't use a polymer component for this. Instead use a Failures model // FIXME: Don't use a polymer component for this. Instead use a Failures model
...@@ -16,17 +18,28 @@ found in the LICENSE file. ...@@ -16,17 +18,28 @@ found in the LICENSE file.
net.json('http://auto-sheriff.appspot.com/data').then(function(data) { net.json('http://auto-sheriff.appspot.com/data').then(function(data) {
// FIXME: Don't special-case the blink master. // FIXME: Don't special-case the blink master.
this.builderLatestRevisions = data.latest_revisions['chromium.webkit']; this.builderLatestRevisions = data.latest_revisions['chromium.webkit'];
this.failures.builders = {}; this.failures = [];
this.failures.unexpected = [];
data.range_groups.forEach(function(group) { data.range_groups.forEach(function(group) {
this._processFailuresForGroup(group, data.alerts); this._processFailuresForGroup(group, data.alerts);
}.bind(this)); }.bind(this));
// FIXME: Sort this.failures by severity of regression, then by oldestFailingRevision.
}.bind(this)); }.bind(this));
}, },
_failureComparator: function(a, b) {
if (a.step > b.step)
return 1;
if (a.step < b.step)
return -1
if (a.testName > b.testName)
return 1;
if (a.testName < b.testName)
return -1
return 0;
},
_processFailuresForGroup: function(group, failures) { _processFailuresForGroup: function(group, failures) {
var failuresByReason = {}; var failuresByReason = {};
var failingBuildersWithoutReasons = {};
group.failure_keys.forEach(function(failure_key) { group.failure_keys.forEach(function(failure_key) {
var failure = failures.find(function(item) { return item.key == failure_key; }); var failure = failures.find(function(item) { return item.key == failure_key; });
...@@ -37,48 +50,55 @@ found in the LICENSE file. ...@@ -37,48 +50,55 @@ found in the LICENSE file.
if (!failure.master_url.endsWith('chromium.webkit')) if (!failure.master_url.endsWith('chromium.webkit'))
return; return;
// FIXME: Have sheriff-o-matic show non-webkit_tests failures by reason. var reason, failureType;
if (!failure.reason || failure.step_name != 'webkit_tests') { if (failure.reason) {
var builder = failure.builder_name; // FIXME: Store the actual failure type in a different field instead of smashing it into the reason.
if (!failingBuildersWithoutReasons[builder]) var parts = failure.reason.split(':');
failingBuildersWithoutReasons[builder] = {}; reason = parts[0];
failingBuildersWithoutReasons[builder][failure.step_name] = 1; failureType = parts[1] || 'FAIL';
return; } else {
reason = null;
failureType = 'UNKNOWN';
} }
// FIXME: Store the actual failure type in a different field instead of smashing it into the reason. var failureKey = JSON.stringify({
var parts = failure.reason.split(':'); step: failure.step_name,
var reason = parts[0]; reason: reason,
var failureType = parts[1]; });
if (!failuresByReason[reason]) // FIXME: Use a model class instead of a dumb object.
failuresByReason[reason] = {} if (!failuresByReason[failureKey])
failuresByReason[reason][failure.builder_name] = { failuresByReason[failureKey] = {};
// FIXME: If we have multiple builders with the same name across masters in
// a failure group, then this will incorrectly overwrite one of the values.
failuresByReason[failureKey][failure.builder_name] = {
// FIXME: Rename to failureType.
actual: failureType, actual: failureType,
lastFailingBuild: failure.last_failing_build,
masterUrl: failure.master_url,
}; };
}.bind(this)); }.bind(this));
var groupedFailures = []; var groupedFailures = [];
var oldestFailingRevision = Number(group.merged_first_failing.blink);
var newestPassingRevision = group.merged_last_passing ? Number(group.merged_last_passing.blink) : undefined;
Object.keys(failuresByReason, function(reason, resultNodesByBuilder) { var oldestFailingRevision, newestPassingRevision;
groupedFailures.push({ // FIXME: This is a workaround for the backend's bogus data when the blink
testName: reason, // regression ranges have no overlap.
resultNodesByBuilder: resultNodesByBuilder, if (group.merged_last_passing && group.merged_first_failing.blink > group.merged_last_passing.blink) {
oldestFailingRevision: oldestFailingRevision, oldestFailingRevision = Number(group.merged_first_failing.blink);
newestPassingRevision: newestPassingRevision, newestPassingRevision = Number(group.merged_last_passing.blink);
}); }
Object.keys(failuresByReason, function(failureKey, resultByBuilder) {
var failure = JSON.parse(failureKey);
groupedFailures.push(new CTFailure(failure.step, failure.reason, resultByBuilder, oldestFailingRevision, newestPassingRevision));
}); });
if (groupedFailures.length) // FIXME: Make this a model class intead of a dumb object.
this.failures.unexpected.push(groupedFailures); groupedFailures.sort(this._failureComparator);
// FIXME: Show these in the failure stream with regression ranges like if (groupedFailures.length)
// any other kind of failure. this.failures.push(groupedFailures);
Object.keys(failingBuildersWithoutReasons, function(builder, steps) {
this.failures.builders[builder] = Object.keys(steps);
}.bind(this));
}, },
}); });
</script> </script>
......
...@@ -11,17 +11,6 @@ found in the LICENSE file. ...@@ -11,17 +11,6 @@ found in the LICENSE file.
module("ct-failure-card"); module("ct-failure-card");
test("testNames", 1, function() {
var card = document.createElement('ct-failure-card');
// FIXME: Use a proper model class instead of a dumb object.
var failures = [
{testName: 'foo'},
{testName: 'bar'},
{testName: 'baz'},
];
deepEqual(card.testNames(failures), ['bar', 'baz', 'foo']);
});
asyncTest("examine", 1, function() { asyncTest("examine", 1, function() {
var card = document.createElement('ct-failure-card'); var card = document.createElement('ct-failure-card');
card.failures = []; card.failures = [];
......
...@@ -57,7 +57,7 @@ found in the LICENSE file. ...@@ -57,7 +57,7 @@ found in the LICENSE file.
</style> </style>
<ct-builder-grid failures="{{failures}}"></ct-builder-grid> <ct-builder-grid failures="{{failures}}"></ct-builder-grid>
<div class="failure"> <div class="failure">
<ct-test-list tests="{{failures|testNames}}"></ct-test-list> <ct-test-list tests="{{failures}}"></ct-test-list>
<ct-commit-list first="{{failures[0].newestPassingRevision + 1}}" <ct-commit-list first="{{failures[0].newestPassingRevision + 1}}"
last="{{failures[0].oldestFailingRevision}}" last="{{failures[0].oldestFailingRevision}}"
commits="{{commits}}"></ct-commit-list> commits="{{commits}}"></ct-commit-list>
...@@ -69,10 +69,6 @@ found in the LICENSE file. ...@@ -69,10 +69,6 @@ found in the LICENSE file.
failures: [], failures: [],
commits: {}, commits: {},
testNames: function(failures) {
return failures.map(function(failureAnalysis) { return failureAnalysis.testName }).sort();
},
examine: function() { examine: function() {
this.fire('ct-examine-failures', this.failures); this.fire('ct-examine-failures', this.failures);
}, },
......
...@@ -12,29 +12,20 @@ found in the LICENSE file. ...@@ -12,29 +12,20 @@ found in the LICENSE file.
var kExampleFailure = { var kExampleFailure = {
"testName": "inspector/console/console-viewport-selection.html", "testName": "inspector/console/console-viewport-selection.html",
"resultNodesByBuilder": { "resultNodesByBuilder": {
"WebKit Mac10.6 (dbg)": {
"actual": "IMAGE",
},
"WebKit Linux (dbg)": { "WebKit Linux (dbg)": {
"expected": "SLOW",
"is_unexpected": true,
"actual": "TEXT", "actual": "TEXT",
"bugs": ["webkit.org/b/90488"],
"time": 5.9
}, },
"WebKit Mac10.6 (dbg)": {
"actual": "IMAGE",
"time": 6.5,
"bugs": ["webkit.org/b/90488"],
"is_unexpected": true,
"expected": "SLOW",
"has_stderr": true
}
}, },
"oldestFailingRevision": 177164, "oldestFailingRevision": 177164,
"newestPassingRevision": 177165 "newestPassingRevision": 177165,
}; };
module("ct-results-by-builder"); module("ct-results-by-builder");
asyncTest("basic", 11, function() { asyncTest("basic", 9, function() {
// FIXME: Remove this override when ct-results-detail is fixed to not use // FIXME: Remove this override when ct-results-detail is fixed to not use
// results.fetchResultsURLs. // results.fetchResultsURLs.
var oldFetchResultsURLs = results.fetchResultsURLs; var oldFetchResultsURLs = results.fetchResultsURLs;
...@@ -51,9 +42,8 @@ asyncTest("basic", 11, function() { ...@@ -51,9 +42,8 @@ asyncTest("basic", 11, function() {
var detail = resultsByBuilder.shadowRoot.querySelectorAll('ct-results-detail'); var detail = resultsByBuilder.shadowRoot.querySelectorAll('ct-results-detail');
equal(detail.length, 1); equal(detail.length, 1);
equal(detail[0].test, 'inspector/console/console-viewport-selection.html'); equal(detail[0].failure.testName, 'inspector/console/console-viewport-selection.html');
equal(detail[0].builder, 'WebKit Linux (dbg)'); equal(detail[0].builder, 'WebKit Linux (dbg)');
equal(detail[0].result, 'TEXT');
resultsByBuilder.shadowRoot.querySelector('paper-tabs').selected = 1; resultsByBuilder.shadowRoot.querySelector('paper-tabs').selected = 1;
...@@ -62,9 +52,8 @@ asyncTest("basic", 11, function() { ...@@ -62,9 +52,8 @@ asyncTest("basic", 11, function() {
var detail = resultsByBuilder.shadowRoot.querySelectorAll('ct-results-detail'); var detail = resultsByBuilder.shadowRoot.querySelectorAll('ct-results-detail');
equal(detail.length, 1); equal(detail.length, 1);
equal(detail[0].test, 'inspector/console/console-viewport-selection.html'); equal(detail[0].failure.testName, 'inspector/console/console-viewport-selection.html');
equal(detail[0].builder, 'WebKit Mac10.6 (dbg)'); equal(detail[0].builder, 'WebKit Mac10.6 (dbg)');
equal(detail[0].result, 'IMAGE');
start(); start();
}); });
......
...@@ -16,13 +16,12 @@ found in the LICENSE file. ...@@ -16,13 +16,12 @@ found in the LICENSE file.
display: block; display: block;
} }
</style> </style>
<paper-tabs selected="{{selected}}"> <paper-tabs selected="{{ selected }}">
<template repeat="{{builder in builders}}"> <template repeat="{{ builder in builders }}">
<paper-tab>{{builder|displayName}}</paper-tab> <paper-tab>{{ builder | displayName }}</paper-tab>
</template> </template>
</paper-tabs> </paper-tabs>
<ct-results-detail test="{{failure.testName}}" builder="{{builders[selected]}}" <ct-results-detail failure="{{ failure }}" builder="{{ builders[selected] }}"></ct-results-detail>
result="{{failure.resultNodesByBuilder[builders[selected]].actual}}"></ct-results-detail>
</template> </template>
<script> <script>
Polymer({ Polymer({
...@@ -31,7 +30,7 @@ found in the LICENSE file. ...@@ -31,7 +30,7 @@ found in the LICENSE file.
selected: 0, selected: 0,
failureChanged: function() { failureChanged: function() {
this.builders = Object.getOwnPropertyNames(this.failure.resultNodesByBuilder); this.builders = Object.getOwnPropertyNames(this.failure.resultNodesByBuilder).sort();
this.selected = 0; this.selected = 0;
}, },
......
...@@ -6,6 +6,8 @@ found in the LICENSE file. ...@@ -6,6 +6,8 @@ found in the LICENSE file.
<link rel="import" href="ct-results-detail.html"> <link rel="import" href="ct-results-detail.html">
<link rel="import" href="../model/ct-failure.html">
<script> <script>
(function () { (function () {
...@@ -26,9 +28,18 @@ asyncTest("image+text", 4, function() { ...@@ -26,9 +28,18 @@ asyncTest("image+text", 4, function() {
simulator.runTest(function() { simulator.runTest(function() {
var comparisonResult = document.createElement('ct-results-detail'); var comparisonResult = document.createElement('ct-results-detail');
comparisonResult.test = 'dummy/test.html';
comparisonResult.builder = 'dummy builder'; comparisonResult.builder = 'dummy builder';
comparisonResult.result = 'IMAGE+TEXT'; // FIXME: Use a proper mock model object.
comparisonResult.failure = {
"testName": "dummy/test.html",
"resultNodesByBuilder": {
"dummy builder": {
"actual": "IMAGE+TEXT",
},
},
"oldestFailingRevision": 177164,
"newestPassingRevision": 177165,
};
comparisonResult.async(function() { comparisonResult.async(function() {
deepEqual(probedUrls, [ deepEqual(probedUrls, [
...@@ -64,9 +75,18 @@ asyncTest("crash", 4, function() { ...@@ -64,9 +75,18 @@ asyncTest("crash", 4, function() {
simulator.runTest(function() { simulator.runTest(function() {
var crashResult = document.createElement('ct-results-detail'); var crashResult = document.createElement('ct-results-detail');
crashResult.test = 'dummy/test.html';
crashResult.builder = 'dummy builder'; crashResult.builder = 'dummy builder';
crashResult.result = 'CRASH'; // FIXME: Use a proper mock model object.
crashResult.failure = {
"testName": "dummy/test.html",
"resultNodesByBuilder": {
"dummy builder": {
"actual": "CRASH",
},
},
"oldestFailingRevision": 177164,
"newestPassingRevision": 177165,
};
crashResult.async(function() { crashResult.async(function() {
deepEqual(probedUrls, [ deepEqual(probedUrls, [
...@@ -76,11 +96,56 @@ asyncTest("crash", 4, function() { ...@@ -76,11 +96,56 @@ asyncTest("crash", 4, function() {
var crashOutputs = crashResult.shadowRoot.querySelectorAll('ct-test-output'); var crashOutputs = crashResult.shadowRoot.querySelectorAll('ct-test-output');
equal(crashOutputs.length, 1); equal(crashOutputs.length, 1);
equal(crashResult.shadowRoot.querySelectorAll('ct-results-comparison').length, 0); equal(crashResult.shadowRoot.querySelectorAll('ct-results-comparison').length, 0);
start(); start();
}); });
}); });
}); });
asyncTest("unknown", 3, function() {
var result = document.createElement('ct-results-detail');
result.builder = 'dummy builder';
var resultsByBuilder = {
"dummy builder": {
actual: "UNKNOWN",
lastFailingBuild: 124,
masterUrl: 'http://masterurl/'
},
};
result.failure = new CTFailure('foo_step', 'test.html', resultsByBuilder, 123, 124);
requestAnimationFrame(function() {
var outputs = result.shadowRoot.querySelectorAll('ct-test-output');
equal(outputs.length, 1);
equal(outputs[0].url, 'http://masterurl//builders/dummy%20builder/builds/124/steps/foo_step/logs/stdio');
equal(result.shadowRoot.querySelectorAll('ct-results-comparison').length, 0);
start();
});
});
asyncTest("unknown", 3, function() {
var result = document.createElement('ct-results-detail');
result.builder = 'dummy builder';
var resultsByBuilder = {
"dummy builder": {
actual: "FAIL",
lastFailingBuild: 124,
masterUrl: 'http://masterurl/'
},
};
result.failure = new CTFailure('foo_step', 'TestSuite.TestName', resultsByBuilder, 123, 124);
requestAnimationFrame(function() {
var outputs = result.shadowRoot.querySelectorAll('ct-test-output');
equal(outputs.length, 1);
equal(outputs[0].url, 'http://masterurl//builders/dummy%20builder/builds/124/steps/foo_step/logs/TestName');
equal(result.shadowRoot.querySelectorAll('ct-results-comparison').length, 0);
start();
});
});
})() })()
</script> </script>
...@@ -6,7 +6,7 @@ found in the LICENSE file. ...@@ -6,7 +6,7 @@ found in the LICENSE file.
<link rel="import" href="ct-results-comparison.html"> <link rel="import" href="ct-results-comparison.html">
<polymer-element name="ct-results-detail" attributes="test builder result"> <polymer-element name="ct-results-detail" attributes="failure builder">
<template> <template>
<style> <style>
:host { :host {
...@@ -28,14 +28,12 @@ found in the LICENSE file. ...@@ -28,14 +28,12 @@ found in the LICENSE file.
</template> </template>
<script> <script>
Polymer({ Polymer({
test: '', failure: null,
// FIXME: Initializing builder gives a JS error. Presumably because // FIXME: Initializing builder gives a JS error. Presumably because
// ct-results-by-builder sets builder="{{builders[selected]}}". But, // ct-results-by-builder sets builder="{{builders[selected]}}". But,
// it seems wrong that the way the parent uses this element constrains // it seems wrong that the way the parent uses this element constrains
// what the element can do. Polymer bug? // what the element can do. Polymer bug?
// builder: '', // builder: '',
// FIXME: Initializing result gives a JS error like above.
// result: '',
_urlGroups: [], _urlGroups: [],
_kExpectedKind: results.kExpectedKind, _kExpectedKind: results.kExpectedKind,
...@@ -44,16 +42,30 @@ found in the LICENSE file. ...@@ -44,16 +42,30 @@ found in the LICENSE file.
_kUnknownKind: results.kUnknownKind, _kUnknownKind: results.kUnknownKind,
observe: { observe: {
result: '_update', failure: '_update',
test: '_update',
builder: '_update', builder: '_update',
}, },
_update: function() { _update: function() {
if (!this.test || !this.builder || !this.result) if (!this.failure || !this.builder)
return; return;
var failureInfo = results.failureInfo(this.test, this.builder, this.result); // FIXME: If the types of groups doesn't change, then it'd be better to do this
// update in place so that the user doesn't see a flicker.
this._urlGroups = [];
var result = this.failure.resultNodesByBuilder[this.builder];
// FIXME: There's probably a less hacky way to check this.
if (result.actual == 'FAIL' || result.actual == 'UNKNOWN')
this._updateUrls();
else
this._updateWebkitTestUrls();
},
_updateWebkitTestUrls: function() {
var result = this.failure.resultNodesByBuilder[this.builder];
var failureInfo = results.failureInfo(this.failure.testName, this.builder, result.actual);
// FIXME: Move this logic to a proper model class so that the network requests this makes // FIXME: Move this logic to a proper model class so that the network requests this makes
// can be easily mocked out in tests. // can be easily mocked out in tests.
results.fetchResultsURLs(failureInfo).then(function(resultsUrls) { results.fetchResultsURLs(failureInfo).then(function(resultsUrls) {
...@@ -65,7 +77,6 @@ found in the LICENSE file. ...@@ -65,7 +77,6 @@ found in the LICENSE file.
resultsUrlsByTypeAndKind[resultType][results.resultKind(url)] = url; resultsUrlsByTypeAndKind[resultType][results.resultKind(url)] = url;
}); });
this._urlGroups = [];
Object.keys(resultsUrlsByTypeAndKind, function(resultType, resultsUrlsByKind) { Object.keys(resultsUrlsByTypeAndKind, function(resultType, resultsUrlsByKind) {
this._urlGroups.push({ this._urlGroups.push({
type: resultType, type: resultType,
...@@ -74,6 +85,30 @@ found in the LICENSE file. ...@@ -74,6 +85,30 @@ found in the LICENSE file.
}.bind(this)); }.bind(this));
}.bind(this)); }.bind(this));
}, },
_updateUrls: function() {
// FIXME: Along with _updateWebkitTestUrls, move this logic to a proper model class
// so that the network requests this makes can be easily mocked out in tests.
var result = this.failure.resultNodesByBuilder[this.builder];
// FIXME: We only store build logs by the test name, not the testsuite.testname,
// which means that two failing tests from different test suites conflict!
var testPart = result.actual == 'UNKNOWN' ? 'stdio' : this.failure.testName.split('.')[1];
var url = result.masterUrl +
'/builders/' + encodeURIComponent(this.builder) +
'/builds/' + result.lastFailingBuild +
'/steps/' + this.failure.step +
'/logs/' + testPart;
var resultsUrlsByKind = {};
resultsUrlsByKind[this._kUnknownKind] = url;
this._urlGroups.push({
type: results.kTextType,
urls: resultsUrlsByKind,
});
},
}); });
</script> </script>
</polymer-element> </polymer-element>
...@@ -94,7 +94,7 @@ module("ct-results-panel"); ...@@ -94,7 +94,7 @@ module("ct-results-panel");
asyncTest("empty", 1, function() { asyncTest("empty", 1, function() {
var panel = document.createElement('ct-results-panel'); var panel = document.createElement('ct-results-panel');
Platform.endOfMicrotask(function() { requestAnimationFrame(function() {
var message = panel.shadowRoot.querySelector('.message'); var message = panel.shadowRoot.querySelector('.message');
equal(message.textContent, 'No results to display.'); equal(message.textContent, 'No results to display.');
...@@ -107,7 +107,7 @@ asyncTest("basic", 8, function() { ...@@ -107,7 +107,7 @@ asyncTest("basic", 8, function() {
panel.failures = kExampleFailures; panel.failures = kExampleFailures;
Platform.endOfMicrotask(function() { requestAnimationFrame(function() {
var items = panel.shadowRoot.querySelectorAll('paper-item'); var items = panel.shadowRoot.querySelectorAll('paper-item');
equal(items.length, 3); equal(items.length, 3);
equal(items[0].label, 'plugins/gesture-events-scrolled.html'); equal(items[0].label, 'plugins/gesture-events-scrolled.html');
...@@ -120,7 +120,7 @@ asyncTest("basic", 8, function() { ...@@ -120,7 +120,7 @@ asyncTest("basic", 8, function() {
panel.shadowRoot.querySelector('core-menu').selected = 2; panel.shadowRoot.querySelector('core-menu').selected = 2;
Platform.endOfMicrotask(function() { requestAnimationFrame(function() {
var results = panel.shadowRoot.querySelectorAll('ct-results-by-builder'); var results = panel.shadowRoot.querySelectorAll('ct-results-by-builder');
equal(results.length, 1); equal(results.length, 1);
equal(results[0].failure, kExampleFailures[2]); equal(results[0].failure, kExampleFailures[2]);
...@@ -130,5 +130,36 @@ asyncTest("basic", 8, function() { ...@@ -130,5 +130,36 @@ asyncTest("basic", 8, function() {
}); });
}); });
asyncTest("whole step failed", 4, function() {
var panel = document.createElement('ct-results-panel');
var failure = {
testName: null,
step: 'foo-step',
resultNodesByBuilder: {
'WebKit Win7 (dbg)': {
actual: 'UNKNOWN',
},
},
oldestFailingRevision: 123,
newestPassingRevision: 124,
};
panel.failures = [failure];
requestAnimationFrame(function() {
var items = panel.shadowRoot.querySelectorAll('paper-item');
equal(items.length, 0);
var results = panel.shadowRoot.querySelectorAll('ct-results-by-builder');
equal(results.length, 1);
equal(results[0].failure, failure);
equal(panel.shadowRoot.querySelectorAll('ct-embedded-flakiness-dashboard').length, 0);
start();
});
});
})() })()
</script> </script>
...@@ -50,11 +50,15 @@ found in the LICENSE file. ...@@ -50,11 +50,15 @@ found in the LICENSE file.
<template if="{{ failures.length }}"> <template if="{{ failures.length }}">
<core-menu selected="{{ selected }}"> <core-menu selected="{{ selected }}">
<template repeat="{{ failure in failures }}"> <template repeat="{{ failure in failures }}">
<paper-item label="{{ failure.testName }}"></paper-item> <template if="{{ failure.testName }}">
<paper-item label="{{ failure.testName }}"></paper-item>
</template>
</template> </template>
</core-menu> </core-menu>
<div class="results"> <div class="results">
<ct-embedded-flakiness-dashboard test="{{ failures[selected].testName }}"></ct-embedded-flakiness-dashboard> <template if="{{ failures[selected].testName }}">
<ct-embedded-flakiness-dashboard test="{{ failures[selected] }}"></ct-embedded-flakiness-dashboard>
</template>
<ct-results-by-builder failure="{{ failures[selected] }}"></ct-results-by-builder> <ct-results-by-builder failure="{{ failures[selected] }}"></ct-results-by-builder>
</div> </div>
</template> </template>
......
...@@ -10,9 +10,39 @@ found in the LICENSE file. ...@@ -10,9 +10,39 @@ found in the LICENSE file.
(function () { (function () {
var kExampleTests = [ var kExampleTests = [
"plugins/gesture-events-scrolled.html", {
"plugins/transformed-events.html", "testName": "plugins/gesture-events-scrolled.html",
"plugins/gesture-events.html", "step": "foo_step",
"resultNodesByBuilder": {
"WebKit Mac10.6 (dbg)": {
"actual": "IMAGE",
},
},
"oldestFailingRevision": 177164,
"newestPassingRevision": 177165,
},
{
"testName": "plugins/transformed-events.html",
"step": "foo_step",
"resultNodesByBuilder": {
"WebKit Mac10.6 (dbg)": {
"actual": "IMAGE",
},
},
"oldestFailingRevision": 177164,
"newestPassingRevision": 177165,
},
{
"testName": "plugins/gesture-events.html",
"step": "foo_step",
"resultNodesByBuilder": {
"WebKit Mac10.6 (dbg)": {
"actual": "IMAGE",
},
},
"oldestFailingRevision": 177164,
"newestPassingRevision": 177165,
},
]; ];
module("ct-test-list"); module("ct-test-list");
...@@ -25,9 +55,9 @@ asyncTest("basic", 4, function() { ...@@ -25,9 +55,9 @@ asyncTest("basic", 4, function() {
Platform.endOfMicrotask(function() { Platform.endOfMicrotask(function() {
var tests = list.shadowRoot.querySelectorAll('a'); var tests = list.shadowRoot.querySelectorAll('a');
equal(tests.length, 3); equal(tests.length, 3);
equal(tests[0].href, 'http://test-results.appspot.com/dashboards/flakiness_dashboard.html#tests=plugins%2Fgesture-events-scrolled.html'); equal(tests[0].href, 'http://test-results.appspot.com/dashboards/flakiness_dashboard.html#tests=plugins%2Fgesture-events-scrolled.html&testType=foo_step');
equal(tests[1].href, 'http://test-results.appspot.com/dashboards/flakiness_dashboard.html#tests=plugins%2Ftransformed-events.html'); equal(tests[1].href, 'http://test-results.appspot.com/dashboards/flakiness_dashboard.html#tests=plugins%2Ftransformed-events.html&testType=foo_step');
equal(tests[2].href, 'http://test-results.appspot.com/dashboards/flakiness_dashboard.html#tests=plugins%2Fgesture-events.html'); equal(tests[2].href, 'http://test-results.appspot.com/dashboards/flakiness_dashboard.html#tests=plugins%2Fgesture-events.html&testType=foo_step');
start(); start();
}); });
......
...@@ -11,14 +11,19 @@ found in the LICENSE file. ...@@ -11,14 +11,19 @@ found in the LICENSE file.
display: block; display: block;
} }
</style> </style>
<template repeat="{{testName in tests}}"> <template repeat="{{ test in tests }}">
<div><a href="{{testName|flakinessDashboardURL}}">{{testName}}</a></div> <!-- FIXME: Find a less redundant UI than repeating the step on each line. -->
<div>
{{ test.step }}
<template if="{{ test.testName }}"><a href="{{ test | flakinessDashboardURL }}">{{ test.testName }}</a></template>
<template if="{{ !test.testName }}"><b>whole step failed</b></template>
</div>
</template> </template>
</template> </template>
<script> <script>
Polymer({ Polymer({
flakinessDashboardURL: function(testName) { flakinessDashboardURL: function(test) {
return ui.urlForFlakinessDashboard([testName]); return ui.urlForFlakinessDashboard(test.testName, test.step);
}, },
}); });
</script> </script>
......
...@@ -6,7 +6,6 @@ found in the LICENSE file. ...@@ -6,7 +6,6 @@ found in the LICENSE file.
<link rel="import" href="../bower_components/paper-button/paper-button.html"> <link rel="import" href="../bower_components/paper-button/paper-button.html">
<link rel="import" href="../model/ct-commit-log.html"> <link rel="import" href="../model/ct-commit-log.html">
<link rel="import" href="ct-failing-builders.html">
<link rel="import" href="ct-failure-analyzer.html"> <link rel="import" href="ct-failure-analyzer.html">
<link rel="import" href="ct-failure-grouper.html"> <link rel="import" href="ct-failure-grouper.html">
<link rel="import" href="ct-failure-stream.html"> <link rel="import" href="ct-failure-stream.html">
...@@ -15,10 +14,6 @@ found in the LICENSE file. ...@@ -15,10 +14,6 @@ found in the LICENSE file.
<polymer-element name="ct-unexpected-failures"> <polymer-element name="ct-unexpected-failures">
<template> <template>
<style> <style>
ct-failing-builders {
margin: 5px;
}
ct-tree-status { ct-tree-status {
white-space: nowrap; white-space: nowrap;
overflow: hidden; overflow: hidden;
...@@ -35,8 +30,7 @@ found in the LICENSE file. ...@@ -35,8 +30,7 @@ found in the LICENSE file.
<ct-failure-analyzer id="analyzer" failures="{{ failures }}" builderLatestRevisions="{{ builderLatestRevisions }}"></ct-failure-analyzer> <ct-failure-analyzer id="analyzer" failures="{{ failures }}" builderLatestRevisions="{{ builderLatestRevisions }}"></ct-failure-analyzer>
<ct-tree-status project="chromium"></ct-tree-status> <ct-tree-status project="chromium"></ct-tree-status>
<ct-tree-status project="blink"></ct-tree-status> <ct-tree-status project="blink"></ct-tree-status>
<ct-failing-builders builders="{{ failures.builders }}"></ct-failing-builders> <ct-failure-stream groups="{{ failures }}" commits="{{revisionLog.commits}}"></ct-failure-stream>
<ct-failure-stream groups="{{ failures.unexpected }}" commits="{{revisionLog.commits}}"></ct-failure-stream>
</template> </template>
<script> <script>
(function() { (function() {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment