blob: 77c50efd3d8dc7d4a9c41dee78fad6c0fc5548a1 [file]
<!-- The <detail-table-sk> custom element declaration.
Presents detailed information about the differences
between two images, or just one image if the bothcolumns
attribute is false.
Attributes:
top - The top digest.
left - The left digest.
test - The name of the test the digests are from.
bothcolumns - A boolean, if present then display both top and left values,
otherwise only display the left.
Events:
triage - The change event e.detail has the form:
{
test: "testname1",
digest: "aabb112233",
status: "positive",
}
Methods:
-->
<polymer-element name="detail-table-sk" attributes="top left test bothcolumns">
<template>
<style type="text/css" media="screen">
#legend {
margin-top: 2em;
}
#commits {
padding: 1em;
padding-left: 3em;
}
dots-sk {
box-shadow: 5px 5px 10px 2px rgba(133,133,133,1);
}
.perTrace {
width: 100%;
}
th {
text-align: right;
}
th, td {
text-align: left;
padding: 5px;
vertical-align: top;
}
tri-sk {
min-width: 15em;
display: block;
}
.sectionHeader {
padding-top: 2em;
text-decoration: underline;
}
.openInNew {
position: relative;
}
.openInNew a {
top: 5px;
position: absolute;
}
table.singlecolumn .both {
display: none;
}
.single {
display: none;
}
table.singlecolumn .single {
display: table-row;
}
img.small {
border: solid 1px lightgray;
max-width: 128px;
max-height: 128px;
width: auto;
height: auto;
}
circle.status0 {
fill: #000000;
stroke: #000000;
}
circle.status1 {
fill: #ffffff;
stroke: #1B9E77;
}
circle.status2 {
fill: #ffffff;
stroke: #D95F02;
}
circle.status3 {
fill: #ffffff;
stroke: #7570B3;
}
circle.status4 {
fill: #ffffff;
stroke: #E7298A;
}
circle.status5 {
fill: #ffffff;
stroke: #66A61E;
}
circle.status6 {
fill: #ffffff;
stroke: #E6AB02;
}
circle.status7 {
fill: #ffffff;
stroke: #A6761D;
}
circle.status8 {
fill: #ffffff;
stroke: #999999;
}
.preview {
margin: 5px;
border: solid 2px lightgray;
display: block;
width: 132px;
height: 132px;
}
.preview img {
display: block;
max-width: 128px;
max-height: 128px;
width: auto;
height: auto;
}
.closestDetails div {
padding-bottom: 0.5em;
padding-left: 0.5em;
}
paper-spinner {
margin-left: 1em;
}
</style>
<table class="{{bothcolumns?'':'singlecolumn'}}">
<tr class=both>
<th></th>
<th>Left</th>
<th class=both>Top</th>
</tr>
<tr>
<th>Status</th>
<td><tri-sk id=leftDetail value="{{details.leftStatus}}" title="{{left}}"></tri-sk> </td>
<td class=both><tri-sk id=topDetail value="{{details.topStatus}}" title="{{top}}"></tri-sk></td>
</tr>
<tr>
<th>Image</th>
<td class=openInNew><img class=small src="/img/images/{{left}}.png"> <a href="/img/images/{{left}}.png" target=_blank><core-icon icon="open-in-new"></core-icon></a></td>
<td class="openInNew both"><img class=small src="/img/images/{{top}}.png"> <a href="/img/images/{{top}}.png" target=_blank><core-icon icon="open-in-new"></core-icon></a></td>
</tr>
<tr class=single>
<th>Test</th>
<td><a href="/cmp/{{test}}">{{test}}</a></td>
<td class=both><a href="/cmp/{{test}}">{{test}}</a></td>
</tr>
<tr>
<th>Digest</th>
<td><a href="detail?test={{test}}&digest={{left}}">{{left}}</a></td>
<td class=both><a href="detail?test={{test}}&digest={{top}}">{{top}}</a></td>
</tr>
<tr><th colspan=2 class=sectionHeader>Parameters <paper-spinner active="{{getInProgress}}"></paper-spinner></th></tr>
<template repeat="{{p in details.params}}">
<tr>
<th>{{p.name}}</th>
<td class=pval>
<template repeat="{{n in p.left}}">
{{n}}
</template>
</td>
<td class="pval both">
<template repeat="{{n in p.top}}">
{{n}}
</template>
</td>
</tr>
</template>
<template if="{{!bothcolumns}}">
<tr><th colspan=2 class=sectionHeader>Graphs</th></tr>
<tr>
<th>Per Trace</th>
<td class=perTrace>
<div horizontal layout>
<div vertical layout>
<paper-input disabled value="" label="Highlighted Trace ID" id="tracename"></paper-input>
<dots-sk id=dots></dots-sk>
<table id=legend>
<tr><th colspan=3>Legend</th></tr>
<tr> <td> <svg width=10 height=10 viewBox="-1 -1 2 2"> <circle cx=0 cy=0 r="0.3" class="status0"/> </svg> </td> <td><code>{{top}}</code></td> </tr>
<template repeat="{{digestInfo, i in details.otherDigests}}">
<tr>
<td>
<svg width=10 height=10 viewBox="-1 -1 2 2">
<circle cx=0 cy=0 r="0.3" class="status{{i+1}}"/>
</svg>
</td>
<td>
<template if="{{i<7}}">
<code><a href="detail?test={{test}}&digest={{digestInfo.digest}}">{{digestInfo.digest}}</a></code>
</template>
<template if="{{i==7}}">
One of many other digests.
</template>
</td>
<td>
<template if="{{i<7}}">
<tricon-sk value="{{digestInfo.status}}"></tricon-sk>
</template>
</td>
<td>
<template if="{{i<7}}">
<a href="diff?test={{test}}&left={{digestInfo.digest}}&top={{top}}">diff</a>
</template>
</td>
</tr>
</template>
</table>
</div>
<commits-sk id=commits></commits-sk>
</div>
</td>
<td class=both></td>
</tr>
<template if="{{details.topStatus!='positive'}}">
<tr><th colspan=2 class=sectionHeader>Closest Postive Digest</th></tr>
<tr>
<td colspan=2>
<div vertical layout class=closestDetails>
<div>
<a href="detail?test={{test}}&digest={{details.posClosest.digest}}">{{details.posClosest.digest}}</a>
<a href="diff?test=blurs&left={{top}}&top={{details.posClosest.digest}}">Details</a>
</div>
<div><strong>Diff:</strong> {{details.posClosest.diff | fixedPercent}}%</div>
<div><strong>RGBA:</strong> {{details.posClosest.maxRGBA}}</div>
</div>
<div horizontal layout id=images>
<div class=preview>
<img src="/img/images/{{details.posClosest.digest}}.png" />
</div>
<a href="/img/images/{{details.posClosest.digest}}.png" target=_blank><core-icon icon="open-in-new"></core-icon></a>
<div class=preview>
<img src="/img/diffs/{{details.posClosest.digest | diffTo}}.png" />
</div>
<a href="/img/diffs/{{details.posClosest.digest | diffTo}}.png" target=_blank><core-icon icon="open-in-new"></core-icon></a>
<div class=preview>
<img src="/img/images/{{top}}.png" />
</div>
<a href="/img/images/{{top}}.png" target=_blank><core-icon icon="open-in-new"></core-icon></a>
</div>
</td>
</tr>
</template>
<template if="{{details.topStatus!='negative'}}">
<tr><th colspan=2 class=sectionHeader>Closest Negative Digest</th></tr>
<tr>
<td colspan=2>
<div vertical layout class=closestDetails>
<div>
<a href="detail?test={{test}}&digest={{details.negClosest.digest}}">{{details.negClosest.digest}}</a>
<a href="diff?test=blurs&left={{top}}&top={{details.negClosest.digest}}">Details</a>
</div>
<div><strong>Diff:</strong> {{details.negClosest.diff | fixedPercent}}%</div>
<div><strong>RGBA:</strong> {{details.negClosest.maxRGBA}}</div>
</div>
<div horizontal layout id=images>
<div class=preview>
<img src="/img/images/{{details.negClosest.digest}}.png" />
</div>
<a href="/img/images/{{details.negClosest.digest}}.png" target=_blank><core-icon icon="open-in-new"></core-icon></a>
<div class=preview>
<img src="/img/diffs/{{details.negClosest.digest | diffTo}}.png" />
</div>
<a href="/img/diffs/{{details.negClosest.digest | diffTo}}.png" target=_blank><core-icon icon="open-in-new"></core-icon></a>
<div class=preview>
<img src="/img/images/{{top}}.png" />
</div>
<a href="/img/images/{{top}}.png" target=_blank><core-icon icon="open-in-new"></core-icon></a>
</div>
</td>
</tr>
</template>
</template>
</table>
</template>
<script>
(function(){
// removeSecurityHeader drops the first 4 chars from JSON returned from
// the gitiles server to make it into valid JSON.
function removeSecurityHeader(s) {
return s.slice(4, s.length);
}
Polymer({
publish: {
top: {
value: "",
reflect: true,
},
left: {
value: "",
reflect: true,
},
test: {
value: "",
reflect: true,
},
bothcolumns: {
value: false,
reflect: true,
}
},
ready: function() {
// A serialized PolyDetailsGUI which has the form:
//
// {
// topStatus: "positive",
// leftStatus: "negative",
// params: [
// {
// name: "arch",
// top: ["Arm7"],
// left: ["Arm8", "x86"],
// }
// ...
// ],
// }
this.details = {
topStatus: "untriaged",
leftStatus: "untriaged",
};
this.getInProgress = false;
var that = this;
this.$.topDetail.addEventListener('change', function(e) {
e.stopPropagation();
var detail = {
test: that.test,
digest: [that.top],
status: e.detail,
};
that.dispatchEvent(new CustomEvent('triage', {detail: detail}));
});
this.$.leftDetail.addEventListener('change', function(e) {
e.stopPropagation();
var detail = {
test: that.test,
digest: [that.left],
status: e.detail,
};
that.dispatchEvent(new CustomEvent('triage', {detail: detail}));
});
this.hoverElement = null;
if (this.$.dots) {
this.$.dots.addEventListener('hover', function(e) {
that.$.tracename.value = e.detail;
});
this.$.dots.addEventListener('commit-selected', function(e) {
that.$.commits.setCommitInfo(e.detail);
});
}
},
changeImpl_: function() {
if (this.test == "" || this.top == "" || this.left == "" || this.getInProgress) {
return
}
this.getInProgress = true;
var that = this;
var q = "?test=" + this.test + "&top=" + this.top + "&left=" + this.left + "&graphs=" + !this.bothcolumns + "&closest=" + !this.bothcolumns;
sk.get('_/details'+q).then(JSON.parse).then(function(json) {
that.details = json;
that.getInProgress = false;
if (that.$.dots) {
that.$.dots.setValue(json);
that.$.dots.setCommits(json.commits);
}
if (json.commits) {
// Now populate the commit messages.
var lastHash = json.commits[json.commits.length-1].hash;
var url = "https://skia.googlesource.com/skia/+log/" + lastHash + "~" + json.commits.length + ".." + lastHash + "?format=json";
sk.get(url).then(removeSecurityHeader).then(JSON.parse).then(function(json) {
var len = that.details.commits.length;
for (var i=0; i<json.log.length; i++) {
that.details.commits[len-i-1].message = json.log[i].message.slice(0, 40);
}
});
}
});
},
// diffTo is a template filter that returns "d1-d2", where d1 and d2
// are two digests, with d1 < d2, which makes it useful for
// referencing diff images.
diffTo: function(s) {
if (s<this.top) {
return s+"-"+this.top;
} else {
return this.top+"-"+s;;
}
},
fixedPercent: function(i) {
if (i) {
return i.toFixed(2);
} else {
return '';
}
},
testChanged: function() {
this.changeImpl_();
},
topChanged: function() {
this.changeImpl_();
},
leftChanged: function() {
this.changeImpl_();
},
});
})();
</script>
</polymer-element>