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.
<script src="scripts/results.js"></script>
<script src="scripts/results_unittests.js"></script>
<script src="scripts/ui.js"></script>
<script src="scripts/ui_unittests.js"></script>
<script src="bower_components/platform/platform.js"></script>
<link rel="import" href="bower_components/polymer/polymer.html">
......@@ -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-list-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-grouper-tests.html">
<link rel="import" href="ui/ct-failure-stream-tests.html">
......
......@@ -34,15 +34,27 @@ ui.displayNameForBuilder = function(builderName)
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(',') : '';
return 'http://test-results.appspot.com/dashboards/flakiness_dashboard.html#tests=' + encodeURIComponent(testsParameter);
if (Array.isArray(testNames))
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.
background-color: #fffc6c;
}
.FAIL span,
.TEXT span {
background-color: #e98080;
}
......
......@@ -18,14 +18,18 @@ asyncTest("url", 1, function() {
}
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 testName = "foo/bar.html";
embeddedFlakinessDashboard.test = testName;
var step = "foo_tests";
embeddedFlakinessDashboard.test = {
testName: testName,
step: step,
};
requestAnimationFrame(function() {
var iframe = embeddedFlakinessDashboard.shadowRoot.querySelector('#iframe');
equal(iframe.src, ui.urlForEmbeddedFlakinessDashboard([testName]));
equal(iframe.src, ui.urlForEmbeddedFlakinessDashboard(testName, step));
reset();
start();
});
......
......@@ -31,7 +31,7 @@ found in the LICENSE file.
},
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
found in the LICENSE file.
-->
<link rel="import" href="../model/ct-failure.html">
<polymer-element name="ct-failure-analyzer" attributes="failures builderLatestRevisions">
<script>
// FIXME: Don't use a polymer component for this. Instead use a Failures model
......@@ -16,17 +18,28 @@ found in the LICENSE file.
net.json('http://auto-sheriff.appspot.com/data').then(function(data) {
// FIXME: Don't special-case the blink master.
this.builderLatestRevisions = data.latest_revisions['chromium.webkit'];
this.failures.builders = {};
this.failures.unexpected = [];
this.failures = [];
data.range_groups.forEach(function(group) {
this._processFailuresForGroup(group, data.alerts);
}.bind(this));
// FIXME: Sort this.failures by severity of regression, then by oldestFailingRevision.
}.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) {
var failuresByReason = {};
var failingBuildersWithoutReasons = {};
group.failure_keys.forEach(function(failure_key) {
var failure = failures.find(function(item) { return item.key == failure_key; });
......@@ -37,48 +50,55 @@ found in the LICENSE file.
if (!failure.master_url.endsWith('chromium.webkit'))
return;
// FIXME: Have sheriff-o-matic show non-webkit_tests failures by reason.
if (!failure.reason || failure.step_name != 'webkit_tests') {
var builder = failure.builder_name;
if (!failingBuildersWithoutReasons[builder])
failingBuildersWithoutReasons[builder] = {};
failingBuildersWithoutReasons[builder][failure.step_name] = 1;
return;
var reason, failureType;
if (failure.reason) {
// FIXME: Store the actual failure type in a different field instead of smashing it into the reason.
var parts = failure.reason.split(':');
reason = parts[0];
failureType = parts[1] || 'FAIL';
} else {
reason = null;
failureType = 'UNKNOWN';
}
// FIXME: Store the actual failure type in a different field instead of smashing it into the reason.
var parts = failure.reason.split(':');
var reason = parts[0];
var failureType = parts[1];
var failureKey = JSON.stringify({
step: failure.step_name,
reason: reason,
});
if (!failuresByReason[reason])
failuresByReason[reason] = {}
failuresByReason[reason][failure.builder_name] = {
// FIXME: Use a model class instead of a dumb object.
if (!failuresByReason[failureKey])
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,
lastFailingBuild: failure.last_failing_build,
masterUrl: failure.master_url,
};
}.bind(this));
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) {
groupedFailures.push({
testName: reason,
resultNodesByBuilder: resultNodesByBuilder,
oldestFailingRevision: oldestFailingRevision,
newestPassingRevision: newestPassingRevision,
});
var oldestFailingRevision, newestPassingRevision;
// FIXME: This is a workaround for the backend's bogus data when the blink
// regression ranges have no overlap.
if (group.merged_last_passing && group.merged_first_failing.blink > group.merged_last_passing.blink) {
oldestFailingRevision = Number(group.merged_first_failing.blink);
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)
this.failures.unexpected.push(groupedFailures);
// FIXME: Make this a model class intead of a dumb object.
groupedFailures.sort(this._failureComparator);
// FIXME: Show these in the failure stream with regression ranges like
// any other kind of failure.
Object.keys(failingBuildersWithoutReasons, function(builder, steps) {
this.failures.builders[builder] = Object.keys(steps);
}.bind(this));
if (groupedFailures.length)
this.failures.push(groupedFailures);
},
});
</script>
......
......@@ -11,17 +11,6 @@ found in the LICENSE file.
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() {
var card = document.createElement('ct-failure-card');
card.failures = [];
......
......@@ -57,7 +57,7 @@ found in the LICENSE file.
</style>
<ct-builder-grid failures="{{failures}}"></ct-builder-grid>
<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}}"
last="{{failures[0].oldestFailingRevision}}"
commits="{{commits}}"></ct-commit-list>
......@@ -69,10 +69,6 @@ found in the LICENSE file.
failures: [],
commits: {},
testNames: function(failures) {
return failures.map(function(failureAnalysis) { return failureAnalysis.testName }).sort();
},
examine: function() {
this.fire('ct-examine-failures', this.failures);
},
......
......@@ -12,29 +12,20 @@ found in the LICENSE file.
var kExampleFailure = {
"testName": "inspector/console/console-viewport-selection.html",
"resultNodesByBuilder": {
"WebKit Mac10.6 (dbg)": {
"actual": "IMAGE",
},
"WebKit Linux (dbg)": {
"expected": "SLOW",
"is_unexpected": true,
"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,
"newestPassingRevision": 177165
"newestPassingRevision": 177165,
};
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
// results.fetchResultsURLs.
var oldFetchResultsURLs = results.fetchResultsURLs;
......@@ -51,9 +42,8 @@ asyncTest("basic", 11, function() {
var detail = resultsByBuilder.shadowRoot.querySelectorAll('ct-results-detail');
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].result, 'TEXT');
resultsByBuilder.shadowRoot.querySelector('paper-tabs').selected = 1;
......@@ -62,9 +52,8 @@ asyncTest("basic", 11, function() {
var detail = resultsByBuilder.shadowRoot.querySelectorAll('ct-results-detail');
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].result, 'IMAGE');
start();
});
......
......@@ -16,13 +16,12 @@ found in the LICENSE file.
display: block;
}
</style>
<paper-tabs selected="{{selected}}">
<template repeat="{{builder in builders}}">
<paper-tab>{{builder|displayName}}</paper-tab>
<paper-tabs selected="{{ selected }}">
<template repeat="{{ builder in builders }}">
<paper-tab>{{ builder | displayName }}</paper-tab>
</template>
</paper-tabs>
<ct-results-detail test="{{failure.testName}}" builder="{{builders[selected]}}"
result="{{failure.resultNodesByBuilder[builders[selected]].actual}}"></ct-results-detail>
<ct-results-detail failure="{{ failure }}" builder="{{ builders[selected] }}"></ct-results-detail>
</template>
<script>
Polymer({
......@@ -31,7 +30,7 @@ found in the LICENSE file.
selected: 0,
failureChanged: function() {
this.builders = Object.getOwnPropertyNames(this.failure.resultNodesByBuilder);
this.builders = Object.getOwnPropertyNames(this.failure.resultNodesByBuilder).sort();
this.selected = 0;
},
......
......@@ -6,6 +6,8 @@ found in the LICENSE file.
<link rel="import" href="ct-results-detail.html">
<link rel="import" href="../model/ct-failure.html">
<script>
(function () {
......@@ -26,9 +28,18 @@ asyncTest("image+text", 4, function() {
simulator.runTest(function() {
var comparisonResult = document.createElement('ct-results-detail');
comparisonResult.test = 'dummy/test.html';
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() {
deepEqual(probedUrls, [
......@@ -64,9 +75,18 @@ asyncTest("crash", 4, function() {
simulator.runTest(function() {
var crashResult = document.createElement('ct-results-detail');
crashResult.test = 'dummy/test.html';
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() {
deepEqual(probedUrls, [
......@@ -76,11 +96,56 @@ asyncTest("crash", 4, function() {
var crashOutputs = crashResult.shadowRoot.querySelectorAll('ct-test-output');
equal(crashOutputs.length, 1);
equal(crashResult.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: "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>
......@@ -6,7 +6,7 @@ found in the LICENSE file.
<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>
<style>
:host {
......@@ -28,14 +28,12 @@ found in the LICENSE file.
</template>
<script>
Polymer({
test: '',
failure: null,
// FIXME: Initializing builder gives a JS error. Presumably because
// ct-results-by-builder sets builder="{{builders[selected]}}". But,
// it seems wrong that the way the parent uses this element constrains
// what the element can do. Polymer bug?
// builder: '',
// FIXME: Initializing result gives a JS error like above.
// result: '',
_urlGroups: [],
_kExpectedKind: results.kExpectedKind,
......@@ -44,16 +42,30 @@ found in the LICENSE file.
_kUnknownKind: results.kUnknownKind,
observe: {
result: '_update',
test: '_update',
failure: '_update',
builder: '_update',
},
_update: function() {
if (!this.test || !this.builder || !this.result)
if (!this.failure || !this.builder)
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
// can be easily mocked out in tests.
results.fetchResultsURLs(failureInfo).then(function(resultsUrls) {
......@@ -65,7 +77,6 @@ found in the LICENSE file.
resultsUrlsByTypeAndKind[resultType][results.resultKind(url)] = url;
});
this._urlGroups = [];
Object.keys(resultsUrlsByTypeAndKind, function(resultType, resultsUrlsByKind) {
this._urlGroups.push({
type: resultType,
......@@ -74,6 +85,30 @@ found in the LICENSE file.
}.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>
</polymer-element>
......@@ -94,7 +94,7 @@ module("ct-results-panel");
asyncTest("empty", 1, function() {
var panel = document.createElement('ct-results-panel');
Platform.endOfMicrotask(function() {
requestAnimationFrame(function() {
var message = panel.shadowRoot.querySelector('.message');
equal(message.textContent, 'No results to display.');
......@@ -107,7 +107,7 @@ asyncTest("basic", 8, function() {
panel.failures = kExampleFailures;
Platform.endOfMicrotask(function() {
requestAnimationFrame(function() {
var items = panel.shadowRoot.querySelectorAll('paper-item');
equal(items.length, 3);
equal(items[0].label, 'plugins/gesture-events-scrolled.html');
......@@ -120,7 +120,7 @@ asyncTest("basic", 8, function() {
panel.shadowRoot.querySelector('core-menu').selected = 2;
Platform.endOfMicrotask(function() {
requestAnimationFrame(function() {
var results = panel.shadowRoot.querySelectorAll('ct-results-by-builder');
equal(results.length, 1);
equal(results[0].failure, kExampleFailures[2]);
......@@ -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>
......@@ -50,11 +50,15 @@ found in the LICENSE file.
<template if="{{ failures.length }}">
<core-menu selected="{{ selected }}">
<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>
</core-menu>
<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>
</div>
</template>
......
......@@ -10,9 +10,39 @@ found in the LICENSE file.
(function () {
var kExampleTests = [
"plugins/gesture-events-scrolled.html",
"plugins/transformed-events.html",
"plugins/gesture-events.html",
{
"testName": "plugins/gesture-events-scrolled.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");
......@@ -25,9 +55,9 @@ asyncTest("basic", 4, function() {
Platform.endOfMicrotask(function() {
var tests = list.shadowRoot.querySelectorAll('a');
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[1].href, 'http://test-results.appspot.com/dashboards/flakiness_dashboard.html#tests=plugins%2Ftransformed-events.html');
equal(tests[2].href, 'http://test-results.appspot.com/dashboards/flakiness_dashboard.html#tests=plugins%2Fgesture-events.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&testType=foo_step');
equal(tests[2].href, 'http://test-results.appspot.com/dashboards/flakiness_dashboard.html#tests=plugins%2Fgesture-events.html&testType=foo_step');
start();
});
......
......@@ -11,14 +11,19 @@ found in the LICENSE file.
display: block;
}
</style>
<template repeat="{{testName in tests}}">
<div><a href="{{testName|flakinessDashboardURL}}">{{testName}}</a></div>
<template repeat="{{ test in tests }}">
<!-- 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>
<script>
Polymer({
flakinessDashboardURL: function(testName) {
return ui.urlForFlakinessDashboard([testName]);
flakinessDashboardURL: function(test) {
return ui.urlForFlakinessDashboard(test.testName, test.step);
},
});
</script>
......
......@@ -6,7 +6,6 @@ found in the LICENSE file.
<link rel="import" href="../bower_components/paper-button/paper-button.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-grouper.html">
<link rel="import" href="ct-failure-stream.html">
......@@ -15,10 +14,6 @@ found in the LICENSE file.
<polymer-element name="ct-unexpected-failures">
<template>
<style>
ct-failing-builders {
margin: 5px;
}
ct-tree-status {
white-space: nowrap;
overflow: hidden;
......@@ -35,8 +30,7 @@ found in the LICENSE file.
<ct-failure-analyzer id="analyzer" failures="{{ failures }}" builderLatestRevisions="{{ builderLatestRevisions }}"></ct-failure-analyzer>
<ct-tree-status project="chromium"></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.unexpected }}" commits="{{revisionLog.commits}}"></ct-failure-stream>
<ct-failure-stream groups="{{ failures }}" commits="{{revisionLog.commits}}"></ct-failure-stream>
</template>
<script>
(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