view.html revision 589bd3da3403e305af802d412d14213f64478b2d
1<!DOCTYPE html>
2
3<html ng-app="Loader" ng-controller="Loader.Controller">
4
5<head>
6  <title ng-bind="windowTitle"></title>
7  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.1.5/angular.js"></script>
8  <script src="loader.js"></script>
9  <script src="diff_viewer.js"></script>
10  <link rel="stylesheet" href="view.css">
11</head>
12
13<body>
14  <h2>
15    Instructions, roadmap, etc. are at
16    <a href="http://tinyurl.com/SkiaRebaselineServer">
17      http://tinyurl.com/SkiaRebaselineServer
18    </a>
19  </h2>
20
21  <em>
22    {{loadingMessage}}
23  </em>
24
25  <div ng-hide="!categories"><!-- everything: hide until data is loaded -->
26
27    <div class="warning-div"
28         ng-hide="!(header.isEditable && header.isExported)">
29      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>
32
33    <div ng-hide="!(header.timeUpdated)">
34      Results current as of {{localTimeString(header.timeUpdated)}}
35    </div>
36
37    <div><!-- tabs -->
38      <div class="tab-spacer" ng-repeat="tab in tabs">
39        <div class="tab-{{tab == viewingTab}}"
40             ng-click="setViewingTab(tab)">
41          &nbsp;{{tab}} ({{numResultsPerTab[tab]}})&nbsp;
42        </div>
43        <div class="tab-spacer">
44          &nbsp;
45        </div>
46      </div>
47    </div><!-- tabs -->
48
49    <div class="tab-main"><!-- main display area of selected tab -->
50
51    <br>
52    <!-- We only show the filters/settings table on the Unfiled tab. -->
53    <table ng-hide="viewingTab != defaultTab" border="1">
54    <tr>
55      <th colspan="4">
56        Filters
57      </th>
58      <th>
59        Settings
60      </th>
61    </tr>
62    <tr valign="top">
63      <!-- TODO(epoger): make this an ng-repeat over resultType, config, etc? -->
64      <td>
65        resultType<br>
66        <label ng-repeat="(resultType, count) in categories['resultType'] track by $index">
67          <input type="checkbox"
68                 name="resultTypes"
69                 value="{{resultType}}"
70                 ng-checked="!isValueInSet(resultType, hiddenResultTypes)"
71                 ng-click="toggleValueInSet(resultType, hiddenResultTypes); setUpdatesPending(true)">
72          {{resultType}} ({{count}})<br>
73        </label>
74        <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>
95      </td>
96      <td>
97        config<br>
98        <label ng-repeat="(config, count) in categories['config'] track by $index">
99          <input type="checkbox"
100                 name="configs"
101                 value="{{config}}"
102                 ng-checked="!isValueInSet(config, hiddenConfigs)"
103                 ng-click="toggleValueInSet(config, hiddenConfigs); setUpdatesPending(true)">
104          {{config}} ({{count}})<br>
105        </label>
106        <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>
115      </td>
116      <td><table>
117        <tr><td>
118          Image width
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>
132          <button class="update-results-button"
133                  ng-click="updateResults()"
134                  ng-disabled="!areUpdatesPending">
135            Update Results
136          </button>
137        </td></tr>
138      </tr></table></td>
139    </tr>
140  </table>
141
142      <p>
143
144      <!-- Submission UI that we only show in the Pending Approval tab. -->
145      <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>
159        <div>
160          Advanced settings...
161          <input type="checkbox" ng-model="showSubmitAdvancedSettings">
162          show
163          <ul ng-hide="!showSubmitAdvancedSettings">
164            <li ng-repeat="setting in ['reviewed-by-human', 'ignore-failure']">
165              {{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>
174      </div>
175
176      <p>
177
178    <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>
216      <table border="1" ng-app="diff_viewer"> <!-- results -->
217        <tr>
218          <!-- Most column headers are displayed in a common fashion... -->
219          <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>
227          <!-- ... but there are a few columns where we display things differently. -->
228          <th>
229            <input type="radio"
230                   name="sortColumnRadio"
231                   value="bugs"
232                   ng-checked="(sortColumn == 'bugs')"
233                   ng-click="sortResultsBy('bugs')">
234            bugs
235          </th>
236          <th width="{{imageSize}}">
237            <input type="radio"
238                   name="sortColumnRadio"
239                   value="expectedHashDigest"
240                   ng-checked="(sortColumn == 'expectedHashDigest')"
241                   ng-click="sortResultsBy('expectedHashDigest')">
242            expected image
243          </th>
244          <th width="{{imageSize}}">
245            <input type="radio"
246                   name="sortColumnRadio"
247                   value="actualHashDigest"
248                   ng-checked="(sortColumn == 'actualHashDigest')"
249                   ng-click="sortResultsBy('actualHashDigest')">
250            actual image
251          </th>
252          <th width="{{imageSize}}">
253            <input type="radio"
254                   name="sortColumnRadio"
255                   value="percentDifferingPixels"
256                   ng-checked="(sortColumn == 'percentDifferingPixels')"
257                   ng-click="sortResultsBy('percentDifferingPixels')">
258            differing pixels in white
259          </th>
260          <th width="{{imageSize}}">
261            <input type="radio"
262                   name="sortColumnRadio"
263                   value="weightedDiffMeasure"
264                   ng-checked="(sortColumn == 'weightedDiffMeasure')"
265                   ng-click="sortResultsBy('weightedDiffMeasure')">
266            difference per pixel
267            <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"/>
273          </th>
274          <th>
275            <!-- item-selection checkbox column -->
276          </th>
277        </tr>
278
279        <tr ng-repeat="result in limitedTestData" ng-controller="ImageController">
280          <td>
281            {{result.resultType}}
282            <br>
283            <button class="show-only-button"
284                    ng-hide="viewingTab != defaultTab"
285                    ng-click="showOnlyResultType(result.resultType)"
286                    title="show only results of type '{{result.resultType}}'">
287              show only
288            </button>
289            <br>
290            <button class="show-all-button"
291                    ng-hide="viewingTab != defaultTab"
292                    ng-disabled="0 == setSize(hiddenResultTypes)"
293                    ng-click="showAllResultTypes()"
294                    title="show results of all types">
295              show all
296            </button>
297          </td>
298          <td ng-repeat="categoryName in ['builder', 'test']">
299            {{result[categoryName]}}
300            <br>
301            <button class="show-only-button"
302                    ng-hide="viewingTab != defaultTab"
303                    ng-disabled="result[categoryName] == categoryValueMatch[categoryName]"
304                    ng-click="setCategoryValueMatch(categoryName, result[categoryName])"
305                    title="show only results of {{categoryName}} '{{result[categoryName]}}'">
306              show only
307            </button>
308            <br>
309            <button class="show-all-button"
310                    ng-hide="viewingTab != defaultTab"
311                    ng-disabled="'' == categoryValueMatch[categoryName]"
312                    ng-click="setCategoryValueMatch(categoryName, '')"
313                    title="show results of all {{categoryName}}s">
314              show all
315            </button>
316          </td>
317          <td>
318            {{result.config}}
319            <br>
320            <button class="show-only-button"
321                    ng-hide="viewingTab != defaultTab"
322                    ng-click="showOnlyConfig(result.config)"
323                    title="show only results of config '{{result.config}}'">
324              show only
325            </button>
326            <br>
327            <button class="show-all-button"
328                    ng-hide="viewingTab != defaultTab"
329                    ng-disabled="0 == setSize(hiddenConfigs)"
330                    ng-click="showAllConfigs()"
331                    title="show results of all configs">
332              show all
333            </button>
334          </td>
335          <td>
336            <a ng-repeat="bug in result['bugs']"
337               href="https://code.google.com/p/skia/issues/detail?id={{bug}}"
338               target="_blank">
339              {{bug}}
340            </a>
341          </td>
342
343          <!-- expected image -->
344          <td valign="bottom" width="{{imageSize}}">
345            <a href="http://chromium-skia-gm.commondatastorage.googleapis.com/gm/{{result.expectedHashType}}/{{result.test}}/{{result.expectedHashDigest}}.png" target="_blank">View Image</a><br/>
346            <img-compare type="baseline" width="{{imageSize}}"
347                         src="http://chromium-skia-gm.commondatastorage.googleapis.com/gm/{{result.expectedHashType}}/{{result.test}}/{{result.expectedHashDigest}}.png" />
348
349          </td>
350
351          <!-- actual image -->
352          <td valign="bottom" width="{{imageSize}}">
353            <a href="http://chromium-skia-gm.commondatastorage.googleapis.com/gm/{{result.actualHashType}}/{{result.test}}/{{result.actualHashDigest}}.png" target="_blank">View Image</a><br/>
354            <img-compare type="test" width="{{imageSize}}"
355                         src="http://chromium-skia-gm.commondatastorage.googleapis.com/gm/{{result.actualHashType}}/{{result.test}}/{{result.actualHashDigest}}.png" />
356
357          </td>
358
359          <!-- whitediffs: every differing pixel shown in white -->
360          <td valign="bottom" width="{{imageSize}}">
361            <div ng-hide="result.expectedHashDigest == result.actualHashDigest"
362                 title="{{result.numDifferingPixels | number:0}} of {{(100 * result.numDifferingPixels / result.percentDifferingPixels) | number:0}} pixels ({{result.percentDifferingPixels.toFixed(4)}}%) differ from expectation.">
363
364              {{result.percentDifferingPixels.toFixed(4)}}%
365              ({{result.numDifferingPixels}})
366              <br/>
367              <a href="/static/generated-images/whitediffs/{{result.expectedHashDigest}}-vs-{{result.actualHashDigest}}.png" target="_blank">View Image</a><br/>
368              <img-compare type="differingPixelsInWhite" width="{{imageSize}}"
369                           src="/static/generated-images/whitediffs/{{result.expectedHashDigest}}-vs-{{result.actualHashDigest}}.png" />
370
371            </div>
372            <div ng-hide="result.expectedHashDigest != result.actualHashDigest"
373                 style="text-align:center">
374              &ndash;none&ndash;
375            </div>
376          </td>
377
378          <!-- diffs: per-channel RGB deltas -->
379          <td valign="bottom" width="{{imageSize}}">
380            <div ng-hide="result.expectedHashDigest == result.actualHashDigest"
381                 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]}}">
382
383              {{result.weightedDiffMeasure.toFixed(4)}}%
384              {{result.maxDiffPerChannel}}
385              <br/>
386              <a href="/static/generated-images/diffs/{{result.expectedHashDigest}}-vs-{{result.actualHashDigest}}.png" target="_blank">View Image</a><br/>
387              <img-compare ng-style="{backgroundColor: pixelDiffBgColor}"
388                           type="differencePerPixel" width="{{imageSize}}"
389                           src="/static/generated-images/diffs/{{result.expectedHashDigest}}-vs-{{result.actualHashDigest}}.png"
390                           ng-mousedown="MagnifyDraw($event, true)"
391                           ng-mousemove="MagnifyDraw($event, false)"
392                           ng-mouseup="MagnifyEnd($event)"
393                           ng-mouseleave="MagnifyEnd($event)" />
394
395            </div>
396            <div ng-hide="result.expectedHashDigest != result.actualHashDigest"
397                 style="text-align:center">
398              &ndash;none&ndash;
399            </div>
400          </td>
401
402          <td>
403            <input type="checkbox"
404                   name="rowSelect"
405                   value="{{result.index}}"
406                   ng-checked="isValueInArray(result.index, selectedItems)"
407                   ng-click="toggleValueInArray(result.index, selectedItems)">
408        </tr>
409      </table> <!-- results -->
410    </td></tr></table> <!-- table holding results header + results table -->
411
412  </div><!-- main display area of selected tab -->
413  </div><!-- everything: hide until data is loaded -->
414
415  <!-- TODO(epoger): Can we get the base URLs (commondatastorage and
416       issues list) from
417       https://skia.googlesource.com/buildbot/+/master/site_config/global_variables.json ?
418       I tried importing the
419       http://skia.googlecode.com/svn/buildbot/skia_tools.js script and using
420       that to do so, but I got Access-Control-Allow-Origin errors.
421    -->
422
423</body>
424</html>
425