blob: f756dcc5d792d6bb13325ef54e408222f599c88e [file] [log] [blame]
epoger@google.comf9d134d2013-09-27 15:02:44 +00001<!DOCTYPE html>
2
epoger@google.com542b65f2013-10-15 20:10:33 +00003<html ng-app="Loader" ng-controller="Loader.Controller">
epoger@google.comf9d134d2013-09-27 15:02:44 +00004
5<head>
epoger@google.com542b65f2013-10-15 20:10:33 +00006 <title ng-bind="windowTitle"></title>
epoger@google.comf9d134d2013-09-27 15:02:44 +00007 <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.1.5/angular.js"></script>
8 <script src="loader.js"></script>
commit-bot@chromium.org076f0782014-02-04 22:45:40 +00009 <script src="diff_viewer.js"></script>
epoger@google.comeb832592013-10-23 15:07:26 +000010 <link rel="stylesheet" href="view.css">
epoger@google.comf9d134d2013-09-27 15:02:44 +000011</head>
12
13<body>
epoger@google.com4259ef32013-10-29 17:39:09 +000014 <h2>
15 Instructions, roadmap, etc. are at
16 <a href="http://tinyurl.com/SkiaRebaselineServer">
17 http://tinyurl.com/SkiaRebaselineServer
18 </a>
19 </h2>
20
epoger@google.comdcb4e652013-10-11 18:45:33 +000021 <em>
22 {{loadingMessage}}
epoger@google.com5f2bb002013-10-02 18:57:48 +000023 </em>
epoger@google.comafaad3d2013-09-30 15:06:25 +000024
epoger@google.comeb832592013-10-23 15:07:26 +000025 <div ng-hide="!categories"><!-- everything: hide until data is loaded -->
26
epoger@google.comad0e5522013-10-24 15:38:27 +000027 <div class="warning-div"
28 ng-hide="!(header.isEditable && header.isExported)">
epoger@google.com9fb6c8a2013-10-09 18:05:58 +000029 WARNING! These results are editable and exported, so any user
30 who can connect to this server over the network can modify them.
31 </div>
epoger@google.comeb832592013-10-23 15:07:26 +000032
epoger@google.com542b65f2013-10-15 20:10:33 +000033 <div ng-hide="!(header.timeUpdated)">
34 Results current as of {{localTimeString(header.timeUpdated)}}
35 </div>
epoger@google.comeb832592013-10-23 15:07:26 +000036
epoger@google.comad0e5522013-10-24 15:38:27 +000037 <div><!-- tabs -->
38 <div class="tab-spacer" ng-repeat="tab in tabs">
epoger@google.comeb832592013-10-23 15:07:26 +000039 <div class="tab-{{tab == viewingTab}}"
epoger@google.comeb832592013-10-23 15:07:26 +000040 ng-click="setViewingTab(tab)">
41 &nbsp;{{tab}} ({{numResultsPerTab[tab]}})&nbsp;
42 </div>
epoger@google.comad0e5522013-10-24 15:38:27 +000043 <div class="tab-spacer">
epoger@google.comeb832592013-10-23 15:07:26 +000044 &nbsp;
45 </div>
46 </div>
47 </div><!-- tabs -->
48
epoger@google.comad0e5522013-10-24 15:38:27 +000049 <div class="tab-main"><!-- main display area of selected tab -->
epoger@google.comeb832592013-10-23 15:07:26 +000050
51 <br>
epoger@google.comad0e5522013-10-24 15:38:27 +000052 <!-- We only show the filters/settings table on the Unfiled tab. -->
epoger@google.comeb832592013-10-23 15:07:26 +000053 <table ng-hide="viewingTab != defaultTab" border="1">
epoger@google.com5f2bb002013-10-02 18:57:48 +000054 <tr>
epoger@google.comf4394d52013-10-29 15:49:40 +000055 <th colspan="4">
epoger@google.com5f2bb002013-10-02 18:57:48 +000056 Filters
57 </th>
58 <th>
59 Settings
60 </th>
61 </tr>
62 <tr valign="top">
epoger@google.com055e3b52013-10-26 14:31:11 +000063 <!-- TODO(epoger): make this an ng-repeat over resultType, config, etc? -->
epoger@google.com5f2bb002013-10-02 18:57:48 +000064 <td>
65 resultType<br>
epoger@google.com92165bc2013-10-26 15:01:08 +000066 <label ng-repeat="(resultType, count) in categories['resultType'] track by $index">
epoger@google.com5f2bb002013-10-02 18:57:48 +000067 <input type="checkbox"
68 name="resultTypes"
69 value="{{resultType}}"
epoger@google.comad0e5522013-10-24 15:38:27 +000070 ng-checked="!isValueInSet(resultType, hiddenResultTypes)"
71 ng-click="toggleValueInSet(resultType, hiddenResultTypes); setUpdatesPending(true)">
epoger@google.com5f2bb002013-10-02 18:57:48 +000072 {{resultType}} ({{count}})<br>
73 </label>
epoger@google.comf4394d52013-10-29 15:49:40 +000074 <button ng-click="hiddenResultTypes = {}; updateResults()">
75 all
76 </button>
77 <button ng-click="hiddenResultTypes = {}; toggleValuesInSet(allResultTypes, hiddenResultTypes); updateResults()">
78 none
79 </button>
80 <button ng-click="toggleValuesInSet(allResultTypes, hiddenResultTypes); updateResults()">
81 toggle
82 </button>
83 </td>
84 <td ng-repeat="category in ['builder', 'test']">
85 {{category}}
86 <br>
87 <input type="text"
88 ng-model="categoryValueMatch[category]"
89 ng-change="setUpdatesPending(true)"/>
90 <br>
91 <button ng-click="setCategoryValueMatch(category, '')"
92 ng-disabled="('' == categoryValueMatch[category])">
93 clear (show all)
94 </button>
epoger@google.com5f2bb002013-10-02 18:57:48 +000095 </td>
96 <td>
97 config<br>
epoger@google.com92165bc2013-10-26 15:01:08 +000098 <label ng-repeat="(config, count) in categories['config'] track by $index">
epoger@google.com5f2bb002013-10-02 18:57:48 +000099 <input type="checkbox"
100 name="configs"
101 value="{{config}}"
epoger@google.comad0e5522013-10-24 15:38:27 +0000102 ng-checked="!isValueInSet(config, hiddenConfigs)"
103 ng-click="toggleValueInSet(config, hiddenConfigs); setUpdatesPending(true)">
epoger@google.com5f2bb002013-10-02 18:57:48 +0000104 {{config}} ({{count}})<br>
105 </label>
epoger@google.comf4394d52013-10-29 15:49:40 +0000106 <button ng-click="hiddenConfigs = {}; updateResults()">
107 all
108 </button>
109 <button ng-click="hiddenConfigs = {}; toggleValuesInSet(allConfigs, hiddenConfigs); updateResults()">
110 none
111 </button>
112 <button ng-click="toggleValuesInSet(allConfigs, hiddenConfigs); updateResults()">
113 toggle
114 </button>
epoger@google.com5f2bb002013-10-02 18:57:48 +0000115 </td>
116 <td><table>
117 <tr><td>
118 Image size
119 <input type="text" ng-model="imageSizePending"
120 ng-init="imageSizePending=100"
121 ng-change="areUpdatesPending = true"
122 maxlength="4"/>
123 </td></tr>
124 <tr><td>
125 Max records to display
126 <input type="text" ng-model="displayLimitPending"
127 ng-init="displayLimitPending=50"
128 ng-change="areUpdatesPending = true"
129 maxlength="4"/>
130 </td></tr>
131 <tr><td>
epoger@google.comad0e5522013-10-24 15:38:27 +0000132 <button class="update-results-button"
epoger@google.com5f2bb002013-10-02 18:57:48 +0000133 ng-click="updateResults()"
134 ng-disabled="!areUpdatesPending">
135 Update Results
136 </button>
137 </td></tr>
138 </tr></table></td>
139 </tr>
140 </table>
epoger@google.comf9d134d2013-09-27 15:02:44 +0000141
epoger@google.com5f2bb002013-10-02 18:57:48 +0000142 <p>
epoger@google.comeb832592013-10-23 15:07:26 +0000143
epoger@google.comad0e5522013-10-24 15:38:27 +0000144 <!-- Submission UI that we only show in the Pending Approval tab. -->
epoger@google.comeb832592013-10-23 15:07:26 +0000145 <div ng-hide="'Pending Approval' != viewingTab">
146 <div style="display:inline-block">
147 <button style="font-size:20px"
148 ng-click="submitApprovals(filteredTestData)"
149 ng-disabled="submitPending || (filteredTestData.length == 0)">
150 Update these {{filteredTestData.length}} expectations on the server
151 </button>
152 </div>
153 <div style="display:inline-block">
154 <div style="font-size:20px"
155 ng-hide="!submitPending">
156 Submitting, please wait...
157 </div>
158 </div>
epoger@google.com055e3b52013-10-26 14:31:11 +0000159 <div>
160 Advanced settings...
161 <input type="checkbox" ng-model="showSubmitAdvancedSettings">
162 show
163 <ul ng-hide="!showSubmitAdvancedSettings">
epoger@google.com1e698af2013-11-05 21:00:24 +0000164 <li ng-repeat="setting in ['reviewed-by-human', 'ignore-failure']">
epoger@google.com055e3b52013-10-26 14:31:11 +0000165 {{setting}}
166 <input type="checkbox" ng-model="submitAdvancedSettings[setting]">
167 </li>
168 <li ng-repeat="setting in ['bug']">
169 {{setting}}
170 <input type="text" ng-model="submitAdvancedSettings[setting]">
171 </li>
172 </ul>
173 </div>
epoger@google.comeb832592013-10-23 15:07:26 +0000174 </div>
175
176 <p>
177
epoger@google.com7b1c2c12013-11-01 14:29:03 +0000178 <table border="0"><tr><td> <!-- table holding results header + results table -->
179 <table border="0" width="100%"> <!-- results header -->
180 <tr>
181 <td>
182 Found {{filteredTestData.length}} matches;
183 <span ng-hide="filteredTestData.length <= limitedTestData.length">
184 displaying the first {{limitedTestData.length}}
185 </span>
186 <span ng-hide="filteredTestData.length > limitedTestData.length">
187 displaying them all
188 </span>
189 <br>
190 (click on the column header radio buttons to re-sort by that column)
191 </td>
192 <td align="right">
193 <div>
194 all tests shown:
195 <button ng-click="selectAllItems()">
196 select
197 </button>
198 <button ng-click="clearAllItems()">
199 clear
200 </button>
201 <button ng-click="toggleAllItems()">
202 toggle
203 </button>
204 </div>
205 <div ng-repeat="otherTab in tabs">
206 <button ng-click="moveSelectedItemsToTab(otherTab)"
207 ng-disabled="selectedItems.length == 0"
208 ng-hide="otherTab == viewingTab">
209 move {{selectedItems.length}} selected tests to {{otherTab}} tab
210 </button>
211 </div>
212 </td>
213 </tr>
214 </table> <!-- results header -->
215 </td></tr><tr><td>
commit-bot@chromium.org076f0782014-02-04 22:45:40 +0000216 <table border="1" ng-app="diff_viewer"> <!-- results -->
epoger@google.comf9d134d2013-09-27 15:02:44 +0000217 <tr>
epoger@google.comad0e5522013-10-24 15:38:27 +0000218 <!-- Most column headers are displayed in a common fashion... -->
epoger@google.com5f2bb002013-10-02 18:57:48 +0000219 <th ng-repeat="categoryName in ['resultType', 'builder', 'test', 'config']">
220 <input type="radio"
221 name="sortColumnRadio"
222 value="{{categoryName}}"
223 ng-checked="(sortColumn == categoryName)"
224 ng-click="sortResultsBy(categoryName)">
225 {{categoryName}}
226 </th>
epoger@google.comad0e5522013-10-24 15:38:27 +0000227 <!-- ... but there are a few columns where we display things differently. -->
epoger@google.com5f2bb002013-10-02 18:57:48 +0000228 <th>
229 <input type="radio"
230 name="sortColumnRadio"
epoger@google.com055e3b52013-10-26 14:31:11 +0000231 value="bugs"
232 ng-checked="(sortColumn == 'bugs')"
233 ng-click="sortResultsBy('bugs')">
234 bugs
235 </th>
epoger@google.com214a0242013-11-22 19:26:18 +0000236 <th width="{{imageSize}}">
epoger@google.com055e3b52013-10-26 14:31:11 +0000237 <input type="radio"
238 name="sortColumnRadio"
epoger@google.com5f2bb002013-10-02 18:57:48 +0000239 value="expectedHashDigest"
240 ng-checked="(sortColumn == 'expectedHashDigest')"
241 ng-click="sortResultsBy('expectedHashDigest')">
242 expected image
243 </th>
epoger@google.com214a0242013-11-22 19:26:18 +0000244 <th width="{{imageSize}}">
epoger@google.com5f2bb002013-10-02 18:57:48 +0000245 <input type="radio"
246 name="sortColumnRadio"
247 value="actualHashDigest"
248 ng-checked="(sortColumn == 'actualHashDigest')"
249 ng-click="sortResultsBy('actualHashDigest')">
250 actual image
251 </th>
epoger@google.com214a0242013-11-22 19:26:18 +0000252 <th width="{{imageSize}}">
epoger@google.com9dddf6f2013-11-08 16:25:25 +0000253 <input type="radio"
254 name="sortColumnRadio"
255 value="percentDifferingPixels"
256 ng-checked="(sortColumn == 'percentDifferingPixels')"
257 ng-click="sortResultsBy('percentDifferingPixels')">
epoger@google.com214a0242013-11-22 19:26:18 +0000258 differing pixels in white
epoger@google.com9dddf6f2013-11-08 16:25:25 +0000259 </th>
epoger@google.com214a0242013-11-22 19:26:18 +0000260 <th width="{{imageSize}}">
epoger@google.com9dddf6f2013-11-08 16:25:25 +0000261 <input type="radio"
262 name="sortColumnRadio"
263 value="weightedDiffMeasure"
264 ng-checked="(sortColumn == 'weightedDiffMeasure')"
265 ng-click="sortResultsBy('weightedDiffMeasure')">
epoger@google.com214a0242013-11-22 19:26:18 +0000266 difference per pixel
commit-bot@chromium.orgceba0792014-02-05 19:49:17 +0000267 <br>
268 <input type="range" ng-model="pixelDiffBgColorBrightness"
269 ng-init="pixelDiffBgColorBrightness=64; pixelDiffBgColor=brightnessStringToHexColor(pixelDiffBgColorBrightness)"
270 ng-change="pixelDiffBgColor=brightnessStringToHexColor(pixelDiffBgColorBrightness)"
271 title="image background brightness"
272 min="0" max="255"/>
epoger@google.com9dddf6f2013-11-08 16:25:25 +0000273 </th>
274 <th>
epoger@google.com9fb6c8a2013-10-09 18:05:58 +0000275 <!-- item-selection checkbox column -->
epoger@google.com9fb6c8a2013-10-09 18:05:58 +0000276 </th>
epoger@google.comf9d134d2013-09-27 15:02:44 +0000277 </tr>
epoger@google.comf4394d52013-10-29 15:49:40 +0000278
279 <!-- For most columns... if the user clicks on the cell, and we are on
280 the default tab, update the filter to only show results with the
281 same value for this category.
282 This is made a bit tricky by the fact that AngularJS expressions
283 do not allow control flow statements. See
284 http://docs.angularjs.org/guide/expression -->
commit-bot@chromium.org076f0782014-02-04 22:45:40 +0000285 <tr ng-repeat="result in limitedTestData" ng-controller="ImageController">
epoger@google.comf4394d52013-10-29 15:49:40 +0000286 <td ng-click="(viewingTab != defaultTab) || showOnlyResultType(result.resultType)">
287 {{result.resultType}}
288 </td>
289 <td ng-repeat="categoryName in ['builder', 'test']"
290 ng-click="(viewingTab != defaultTab) || setCategoryValueMatch(categoryName, result[categoryName])">
epoger@google.com055e3b52013-10-26 14:31:11 +0000291 {{result[categoryName]}}
292 </td>
epoger@google.comf4394d52013-10-29 15:49:40 +0000293 <td ng-click="(viewingTab != defaultTab) || showOnlyConfig(result.config)">
294 {{result.config}}
295 </td>
epoger@google.com055e3b52013-10-26 14:31:11 +0000296 <td>
297 <a ng-repeat="bug in result['bugs']"
298 href="https://code.google.com/p/skia/issues/detail?id={{bug}}"
299 target="_blank">
300 {{bug}}
301 </a>
302 </td>
epoger@google.com9dddf6f2013-11-08 16:25:25 +0000303
304 <!-- expected image -->
epoger@google.com214a0242013-11-22 19:26:18 +0000305 <td valign="top" width="{{imageSize}}">
commit-bot@chromium.org076f0782014-02-04 22:45:40 +0000306 <br/>
307 <a href="http://chromium-skia-gm.commondatastorage.googleapis.com/gm/{{result.expectedHashType}}/{{result.test}}/{{result.expectedHashDigest}}.png" target="_blank">View Image</a><br/>
308 <img-compare type="baseline"
309 src="http://chromium-skia-gm.commondatastorage.googleapis.com/gm/{{result.expectedHashType}}/{{result.test}}/{{result.expectedHashDigest}}.png" />
310
epoger@google.comf9d134d2013-09-27 15:02:44 +0000311 </td>
epoger@google.com9dddf6f2013-11-08 16:25:25 +0000312
313 <!-- actual image -->
epoger@google.com214a0242013-11-22 19:26:18 +0000314 <td valign="top" width="{{imageSize}}">
commit-bot@chromium.org076f0782014-02-04 22:45:40 +0000315 <br/>
316 <a href="http://chromium-skia-gm.commondatastorage.googleapis.com/gm/{{result.actualHashType}}/{{result.test}}/{{result.actualHashDigest}}.png" target="_blank">View Image</a><br/>
317 <img-compare type="test"
318 src="http://chromium-skia-gm.commondatastorage.googleapis.com/gm/{{result.actualHashType}}/{{result.test}}/{{result.actualHashDigest}}.png" />
319
epoger@google.comf9d134d2013-09-27 15:02:44 +0000320 </td>
epoger@google.com9dddf6f2013-11-08 16:25:25 +0000321
322 <!-- whitediffs: every differing pixel shown in white -->
epoger@google.com214a0242013-11-22 19:26:18 +0000323 <td valign="top" width="{{imageSize}}">
324 <div ng-hide="result.expectedHashDigest == result.actualHashDigest"
325 title="{{result.numDifferingPixels | number:0}} of {{(100 * result.numDifferingPixels / result.percentDifferingPixels) | number:0}} pixels ({{result.percentDifferingPixels.toFixed(4)}}%) differ from expectation.">
commit-bot@chromium.org076f0782014-02-04 22:45:40 +0000326
epoger@google.com9dddf6f2013-11-08 16:25:25 +0000327 {{result.percentDifferingPixels.toFixed(4)}}%
epoger@google.com214a0242013-11-22 19:26:18 +0000328 ({{result.numDifferingPixels}})
commit-bot@chromium.org076f0782014-02-04 22:45:40 +0000329 <br/>
330 <a href="/static/generated-images/whitediffs/{{result.expectedHashDigest}}-vs-{{result.actualHashDigest}}.png" target="_blank">View Image</a><br/>
331 <img-compare type="differingPixelsInWhite"
332 src="/static/generated-images/whitediffs/{{result.expectedHashDigest}}-vs-{{result.actualHashDigest}}.png" />
333
epoger@google.com9dddf6f2013-11-08 16:25:25 +0000334 </div>
335 <div ng-hide="result.expectedHashDigest != result.actualHashDigest"
336 style="text-align:center">
337 &ndash;none&ndash;
338 </div>
339 </td>
340
341 <!-- diffs: per-channel RGB deltas -->
epoger@google.com214a0242013-11-22 19:26:18 +0000342 <td valign="top" width="{{imageSize}}">
343 <div ng-hide="result.expectedHashDigest == result.actualHashDigest"
344 title="Weighted difference measure is {{result.weightedDiffMeasure.toFixed(4)}}%. Maximum difference per channel: R={{result.maxDiffPerChannel[0]}}, G={{result.maxDiffPerChannel[1]}}, B={{result.maxDiffPerChannel[2]}}">
commit-bot@chromium.org076f0782014-02-04 22:45:40 +0000345
epoger@google.com9dddf6f2013-11-08 16:25:25 +0000346 {{result.weightedDiffMeasure.toFixed(4)}}%
epoger@google.com214a0242013-11-22 19:26:18 +0000347 {{result.maxDiffPerChannel}}
commit-bot@chromium.org076f0782014-02-04 22:45:40 +0000348 <br/>
349 <a href="/static/generated-images/diffs/{{result.expectedHashDigest}}-vs-{{result.actualHashDigest}}.png" target="_blank">View Image</a><br/>
commit-bot@chromium.orgceba0792014-02-05 19:49:17 +0000350 <img-compare ng-style="{backgroundColor: pixelDiffBgColor}"
351 type="differencePerPixel"
commit-bot@chromium.org076f0782014-02-04 22:45:40 +0000352 src="/static/generated-images/diffs/{{result.expectedHashDigest}}-vs-{{result.actualHashDigest}}.png"
353 ng-mousedown="MagnifyDraw($event, true)"
354 ng-mousemove="MagnifyDraw($event, false)"
355 ng-mouseup="MagnifyEnd($event)"
356 ng-mouseleave="MagnifyEnd($event)" />
357
epoger@google.com9dddf6f2013-11-08 16:25:25 +0000358 </div>
359 <div ng-hide="result.expectedHashDigest != result.actualHashDigest"
360 style="text-align:center">
361 &ndash;none&ndash;
362 </div>
363 </td>
364
epoger@google.comeb832592013-10-23 15:07:26 +0000365 <td>
epoger@google.com9fb6c8a2013-10-09 18:05:58 +0000366 <input type="checkbox"
367 name="rowSelect"
368 value="{{result.index}}"
epoger@google.comad0e5522013-10-24 15:38:27 +0000369 ng-checked="isValueInArray(result.index, selectedItems)"
370 ng-click="toggleValueInArray(result.index, selectedItems)">
epoger@google.comf9d134d2013-09-27 15:02:44 +0000371 </tr>
epoger@google.com7b1c2c12013-11-01 14:29:03 +0000372 </table> <!-- results -->
373 </td></tr></table> <!-- table holding results header + results table -->
374
epoger@google.comad0e5522013-10-24 15:38:27 +0000375 </div><!-- main display area of selected tab -->
epoger@google.comeb832592013-10-23 15:07:26 +0000376 </div><!-- everything: hide until data is loaded -->
epoger@google.comf9d134d2013-09-27 15:02:44 +0000377
378 <!-- TODO(epoger): Can we get the base URLs (commondatastorage and
379 issues list) from
borenet@google.com6f0f5b42014-01-09 21:41:39 +0000380 https://skia.googlesource.com/buildbot/+/master/site_config/global_variables.json ?
381 I tried importing the
epoger@google.comf9d134d2013-09-27 15:02:44 +0000382 http://skia.googlecode.com/svn/buildbot/skia_tools.js script and using
383 that to do so, but I got Access-Control-Allow-Origin errors.
384 -->
385
386</body>
387</html>