rebaseline_server: make intermediate JSON specify base urls for diff images
authorcommit-bot@chromium.org <commit-bot@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>
Mon, 17 Mar 2014 14:22:02 +0000 (14:22 +0000)
committercommit-bot@chromium.org <commit-bot@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>
Mon, 17 Mar 2014 14:22:02 +0000 (14:22 +0000)
BUG=skia:1919,skia:2230
NOTREECHECKS=True
NOTRY=True
R=rmistry@google.com

Author: epoger@google.com

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

git-svn-id: http://skia.googlecode.com/svn/trunk@13830 2bbb7eff-a529-9590-31e7-b0007b416f81

gm/rebaseline_server/imagepairset.py
gm/rebaseline_server/imagepairset_test.py
gm/rebaseline_server/results.py
gm/rebaseline_server/server.py
gm/rebaseline_server/static/constants.js
gm/rebaseline_server/static/loader.js
gm/rebaseline_server/static/view.html
gm/rebaseline_server/testdata/outputs/expected/results_test.ResultsTest.test_gm/gm.json

index 26c833e..48c3064 100644 (file)
@@ -9,6 +9,10 @@ found in the LICENSE file.
 ImagePairSet class; see its docstring below.
 """
 
+# System-level imports
+import posixpath
+
+# Local imports
 import column
 
 # Keys used within dictionary representation of ImagePairSet.
@@ -16,10 +20,15 @@ import column
 KEY__EXTRACOLUMNHEADERS = 'extraColumnHeaders'
 KEY__IMAGEPAIRS = 'imagePairs'
 KEY__IMAGESETS = 'imageSets'
-KEY__IMAGESETS__BASE_URL = 'baseUrl'
-KEY__IMAGESETS__DESCRIPTION = 'description'
+KEY__IMAGESETS__FIELD__BASE_URL = 'baseUrl'
+KEY__IMAGESETS__FIELD__DESCRIPTION = 'description'
+KEY__IMAGESETS__SET__DIFFS = 'diffs'
+KEY__IMAGESETS__SET__IMAGE_A = 'imageA'
+KEY__IMAGESETS__SET__IMAGE_B = 'imageB'
+KEY__IMAGESETS__SET__WHITEDIFFS = 'whiteDiffs'
 
 DEFAULT_DESCRIPTIONS = ('setA', 'setB')
+DIFF_BASE_URL = '/static/generated-images'
 
 
 class ImagePairSet(object):
@@ -42,16 +51,18 @@ class ImagePairSet(object):
     self._extra_column_tallies = {}  # maps column_id -> values
                                      #                -> instances_per_value
     self._image_pair_dicts = []
+    self._image_base_url = None
+    self._diff_base_url = DIFF_BASE_URL
 
   def add_image_pair(self, image_pair):
     """Adds an ImagePair; this may be repeated any number of times."""
     # Special handling when we add the first ImagePair...
     if not self._image_pair_dicts:
-      self._base_url = image_pair.base_url
+      self._image_base_url = image_pair.base_url
 
-    if image_pair.base_url != self._base_url:
+    if image_pair.base_url != self._image_base_url:
       raise Exception('added ImagePair with base_url "%s" instead of "%s"' % (
-          image_pair.base_url, self._base_url))
+          image_pair.base_url, self._image_base_url))
     self._image_pair_dicts.append(image_pair.as_dict())
     extra_columns_dict = image_pair.extra_columns_dict
     if extra_columns_dict:
@@ -127,14 +138,29 @@ class ImagePairSet(object):
 
     Uses the KEY__* constants as keys.
     """
+    key_description = KEY__IMAGESETS__FIELD__DESCRIPTION
+    key_base_url = KEY__IMAGESETS__FIELD__BASE_URL
     return {
         KEY__EXTRACOLUMNHEADERS: self._column_headers_as_dict(),
         KEY__IMAGEPAIRS: self._image_pair_dicts,
-        KEY__IMAGESETS: [{
-            KEY__IMAGESETS__BASE_URL: self._base_url,
-            KEY__IMAGESETS__DESCRIPTION: self._descriptions[0],
-        }, {
-            KEY__IMAGESETS__BASE_URL: self._base_url,
-            KEY__IMAGESETS__DESCRIPTION: self._descriptions[1],
-        }],
+        KEY__IMAGESETS: {
+            KEY__IMAGESETS__SET__IMAGE_A: {
+                key_description: self._descriptions[0],
+                key_base_url: self._image_base_url,
+            },
+            KEY__IMAGESETS__SET__IMAGE_B: {
+                key_description: self._descriptions[1],
+                key_base_url: self._image_base_url,
+            },
+            KEY__IMAGESETS__SET__DIFFS: {
+                key_description: 'color difference per channel',
+                key_base_url: posixpath.join(
+                    self._diff_base_url, 'diffs'),
+            },
+            KEY__IMAGESETS__SET__WHITEDIFFS: {
+                key_description: 'differing pixels in white',
+                key_base_url: posixpath.join(
+                    self._diff_base_url, 'whitediffs'),
+            },
+        },
     }
index 815fd74..2fc6d72 100755 (executable)
@@ -106,16 +106,24 @@ class ImagePairSetTest(unittest.TestCase):
             IMAGEPAIR_2_AS_DICT,
             IMAGEPAIR_3_AS_DICT,
         ],
-        'imageSets': [
-            {
+        'imageSets': {
+            'imageA': {
                 'baseUrl': BASE_URL_1,
                 'description': SET_A_DESCRIPTION,
             },
-            {
+            'imageB': {
                 'baseUrl': BASE_URL_1,
                 'description': SET_B_DESCRIPTION,
             },
-        ],
+            'diffs': {
+                'baseUrl': '/static/generated-images/diffs',
+                'description': 'color difference per channel',
+            },
+            'whiteDiffs': {
+                'baseUrl': '/static/generated-images/whitediffs',
+                'description': 'differing pixels in white',
+            },
+        },
     }
 
     image_pair_set = imagepairset.ImagePairSet(
index efc898a..0a53136 100755 (executable)
@@ -38,7 +38,7 @@ import imagepairset
 
 # Keys used to link an image to a particular GM test.
 # NOTE: Keep these in sync with static/constants.js
-REBASELINE_SERVER_SCHEMA_VERSION_NUMBER = 1
+REBASELINE_SERVER_SCHEMA_VERSION_NUMBER = 2
 KEY__EXPECTATIONS__BUGS = gm_json.JSONKEY_EXPECTEDRESULTS_BUGS
 KEY__EXPECTATIONS__IGNOREFAILURE = gm_json.JSONKEY_EXPECTEDRESULTS_IGNOREFAILURE
 KEY__EXPECTATIONS__REVIEWED = gm_json.JSONKEY_EXPECTEDRESULTS_REVIEWED
index 19b0035..396f42d 100755 (executable)
@@ -330,6 +330,8 @@ class HTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
       now = int(time.time())
       response_dict = {
           results_mod.KEY__HEADER: {
+              results_mod.KEY__HEADER__SCHEMA_VERSION: (
+                  results_mod.REBASELINE_SERVER_SCHEMA_VERSION_NUMBER),
               results_mod.KEY__HEADER__IS_STILL_LOADING: True,
               results_mod.KEY__HEADER__TIME_UPDATED: now,
               results_mod.KEY__HEADER__TIME_NEXT_UPDATE_AVAILABLE: (
index 8d832f0..87a0775 100644 (file)
@@ -34,11 +34,15 @@ module.constant('constants', (function() {
     KEY__EXTRACOLUMNHEADERS: 'extraColumnHeaders',
     KEY__IMAGEPAIRS: 'imagePairs',
     KEY__IMAGESETS: 'imageSets',
-    KEY__IMAGESETS__BASE_URL: 'baseUrl',
-    KEY__IMAGESETS__DESCRIPTION: 'description',
+    KEY__IMAGESETS__FIELD__BASE_URL: 'baseUrl',
+    KEY__IMAGESETS__FIELD__DESCRIPTION: 'description',
+    KEY__IMAGESETS__SET__DIFFS: 'diffs',
+    KEY__IMAGESETS__SET__IMAGE_A: 'imageA',
+    KEY__IMAGESETS__SET__IMAGE_B: 'imageB',
+    KEY__IMAGESETS__SET__WHITEDIFFS: 'whiteDiffs',
 
     // NOTE: Keep these in sync with ../results.py
-    REBASELINE_SERVER_SCHEMA_VERSION_NUMBER: 1,
+    REBASELINE_SERVER_SCHEMA_VERSION_NUMBER: 2,
     KEY__EXPECTATIONS__BUGS: 'bugs',
     KEY__EXPECTATIONS__IGNOREFAILURE: 'ignore-failure',
     KEY__EXPECTATIONS__REVIEWED: 'reviewed-by-human',
index d2c7288..fea5aa0 100644 (file)
@@ -63,8 +63,7 @@ Loader.controller(
     $scope.constants = constants;
     $scope.windowTitle = "Loading GM Results...";
     $scope.resultsToLoad = $location.search().resultsToLoad;
-    $scope.loadingMessage = "Loading results from '" + $scope.resultsToLoad +
-        "', please wait...";
+    $scope.loadingMessage = "please wait...";
 
     /**
      * On initial page load, load a full dictionary of results.
@@ -74,7 +73,13 @@ Loader.controller(
     $http.get($scope.resultsToLoad).success(
       function(data, status, header, config) {
         var dataHeader = data[constants.KEY__HEADER];
-        if (dataHeader[constants.KEY__HEADER__IS_STILL_LOADING]) {
+        if (dataHeader[constants.KEY__HEADER__SCHEMA_VERSION] !=
+            constants.REBASELINE_SERVER_SCHEMA_VERSION_NUMBER) {
+          $scope.loadingMessage = "ERROR: Got JSON file with schema version "
+              + dataHeader[constants.KEY__HEADER__SCHEMA_VERSION]
+              + " but expected schema version "
+              + constants.REBASELINE_SERVER_SCHEMA_VERSION_NUMBER;
+        } else if (dataHeader[constants.KEY__HEADER__IS_STILL_LOADING]) {
           // Apply the server's requested reload delay to local time,
           // so we will wait the right number of seconds regardless of clock
           // skew between client and server.
@@ -84,19 +89,13 @@ Loader.controller(
           var timeNow = new Date().getTime();
           var timeToReload = timeNow + reloadDelayInSeconds * 1000;
           $scope.loadingMessage =
-              "Server is still loading results; will retry at " +
+              "server is still loading results; will retry at " +
               $scope.localTimeString(timeToReload / 1000);
           $timeout(
               function(){location.reload();},
               timeToReload - timeNow);
-        } else if (dataHeader[constants.KEY__HEADER__SCHEMA_VERSION] !=
-                   constants.REBASELINE_SERVER_SCHEMA_VERSION_NUMBER) {
-          $scope.loadingMessage = "ERROR: Got JSON file with schema version "
-              + dataHeader[constants.KEY__HEADER__SCHEMA_VERSION]
-              + " but expected schema version "
-              + constants.REBASELINE_SERVER_SCHEMA_VERSION_NUMBER;
         } else {
-          $scope.loadingMessage = "Processing data, please wait...";
+          $scope.loadingMessage = "processing data, please wait...";
 
           $scope.header = dataHeader;
           $scope.extraColumnHeaders = data[constants.KEY__EXTRACOLUMNHEADERS];
@@ -172,8 +171,7 @@ Loader.controller(
       }
     ).error(
       function(data, status, header, config) {
-        $scope.loadingMessage = "Failed to load results from '"
-            + $scope.resultsToLoad + "'";
+        $scope.loadingMessage = "FAILED to load.";
         $scope.windowTitle = "Failed to Load GM Results";
       }
     );
index 61abcf0..179a41e 100644 (file)
@@ -18,7 +18,8 @@
     </a>
   </h2>
 
-  <em>
+  <em ng-show="!extraColumnHeaders"><!-- show until data is loaded -->
+    Loading results from <a href="{{resultsToLoad}}">{{resultsToLoad}}</a> ...
     {{loadingMessage}}
   </em>
 
@@ -31,7 +32,7 @@
     </div>
 
     <div ng-show="header[constants.KEY__HEADER__TIME_UPDATED]">
-      Results current as of {{localTimeString(header[constants.KEY__HEADER__TIME_UPDATED])}}
+      Results from <a href="{{resultsToLoad}}">{{resultsToLoad}}</a> current as of {{localTimeString(header[constants.KEY__HEADER__TIME_UPDATED])}}
     </div>
 
     <div><!-- tabs -->
             bugs
           </th>
           <th width="{{imageSize}}">
-            {{imageSets[0][constants.KEY__IMAGESETS__DESCRIPTION]}}
+            {{imageSets[constants.KEY__IMAGESETS__SET__IMAGE_A][constants.KEY__IMAGESETS__FIELD__DESCRIPTION]}}
           </th>
           <th width="{{imageSize}}">
-            {{imageSets[1][constants.KEY__IMAGESETS__DESCRIPTION]}}
+            {{imageSets[constants.KEY__IMAGESETS__SET__IMAGE_B][constants.KEY__IMAGESETS__FIELD__DESCRIPTION]}}
           </th>
           <th width="{{imageSize}}">
             <input type="radio"
           <!-- image A -->
           <td valign="bottom" width="{{imageSize}}">
             <div ng-if="imagePair[constants.KEY__IMAGE_A_URL] != null">
-              <a href="{{imageSets[0][constants.KEY__IMAGESETS__BASE_URL]}}/{{imagePair[constants.KEY__IMAGE_A_URL]}}" target="_blank">View Image</a><br/>
+              <a href="{{imageSets[constants.KEY__IMAGESETS__SET__IMAGE_A][constants.KEY__IMAGESETS__FIELD__BASE_URL]}}/{{imagePair[constants.KEY__IMAGE_A_URL]}}" target="_blank">View Image</a><br/>
               <img ng-if="showThumbnails"
                    width="{{imageSize}}"
-                   src="{{imageSets[0][constants.KEY__IMAGESETS__BASE_URL]}}/{{imagePair[constants.KEY__IMAGE_A_URL]}}" />
+                   src="{{imageSets[constants.KEY__IMAGESETS__SET__IMAGE_A][constants.KEY__IMAGESETS__FIELD__BASE_URL]}}/{{imagePair[constants.KEY__IMAGE_A_URL]}}" />
             </div>
             <div ng-show="imagePair[constants.KEY__IMAGE_A_URL] == null"
                  style="text-align:center">
           <!-- image B -->
           <td valign="bottom" width="{{imageSize}}">
             <div ng-if="imagePair[constants.KEY__IMAGE_B_URL] != null">
-              <a href="{{imageSets[1][constants.KEY__IMAGESETS__BASE_URL]}}/{{imagePair[constants.KEY__IMAGE_B_URL]}}" target="_blank">View Image</a><br/>
+              <a href="{{imageSets[constants.KEY__IMAGESETS__SET__IMAGE_B][constants.KEY__IMAGESETS__FIELD__BASE_URL]}}/{{imagePair[constants.KEY__IMAGE_B_URL]}}" target="_blank">View Image</a><br/>
               <img ng-if="showThumbnails"
                    width="{{imageSize}}"
-                   src="{{imageSets[1][constants.KEY__IMAGESETS__BASE_URL]}}/{{imagePair[constants.KEY__IMAGE_B_URL]}}" />
+                   src="{{imageSets[constants.KEY__IMAGESETS__SET__IMAGE_B][constants.KEY__IMAGESETS__FIELD__BASE_URL]}}/{{imagePair[constants.KEY__IMAGE_B_URL]}}" />
             </div>
             <div ng-show="imagePair[constants.KEY__IMAGE_B_URL] == null"
                  style="text-align:center">
               {{imagePair[constants.KEY__DIFFERENCE_DATA][constants.KEY__DIFFERENCE_DATA__PERCENT_DIFF_PIXELS].toFixed(4)}}%
               ({{imagePair[constants.KEY__DIFFERENCE_DATA][constants.KEY__DIFFERENCE_DATA__NUM_DIFF_PIXELS]}})
               <br/>
-              <a href="/static/generated-images/whitediffs/{{getImageDiffRelativeUrl(imagePair)}}" target="_blank">View Image</a><br/>
+              <a href="{{imageSets[constants.KEY__IMAGESETS__SET__WHITEDIFFS][constants.KEY__IMAGESETS__FIELD__BASE_URL]}}/{{getImageDiffRelativeUrl(imagePair)}}" target="_blank">View Image</a><br/>
               <img ng-if="showThumbnails"
                    width="{{imageSize}}"
-                   src="/static/generated-images/whitediffs/{{getImageDiffRelativeUrl(imagePair)}}" />
+                   src="{{imageSets[constants.KEY__IMAGESETS__SET__WHITEDIFFS][constants.KEY__IMAGESETS__FIELD__BASE_URL]}}/{{getImageDiffRelativeUrl(imagePair)}}" />
             </div>
             <div ng-show="!imagePair[constants.KEY__IS_DIFFERENT]"
                  style="text-align:center">
               {{imagePair[constants.KEY__DIFFERENCE_DATA][constants.KEY__DIFFERENCE_DATA__PERCEPTUAL_DIFF].toFixed(4)}}%
               {{imagePair[constants.KEY__DIFFERENCE_DATA][constants.KEY__DIFFERENCE_DATA__MAX_DIFF_PER_CHANNEL]}}
               <br/>
-              <a href="/static/generated-images/diffs/{{getImageDiffRelativeUrl(imagePair)}}" target="_blank">View Image</a><br/>
+              <a href="{{imageSets[constants.KEY__IMAGESETS__SET__DIFFS][constants.KEY__IMAGESETS__FIELD__BASE_URL]}}/{{getImageDiffRelativeUrl(imagePair)}}" target="_blank">View Image</a><br/>
               <img ng-if="showThumbnails"
                    ng-style="{backgroundColor: pixelDiffBgColor}"
                    width="{{imageSize}}"
-                   src="/static/generated-images/diffs/{{getImageDiffRelativeUrl(imagePair)}}"/>
+                   src="{{imageSets[constants.KEY__IMAGESETS__SET__DIFFS][constants.KEY__IMAGESETS__FIELD__BASE_URL]}}/{{getImageDiffRelativeUrl(imagePair)}}" />
             </div>
             <div ng-show="!imagePair[constants.KEY__IS_DIFFERENT]"
                  style="text-align:center">
index 562d6c1..5c106b0 100644 (file)
@@ -52,7 +52,7 @@
     "dataHash": "2099241024256114776", 
     "isEditable": false, 
     "isExported": true, 
-    "schemaVersion": 1
+    "schemaVersion": 2
     "timeNextUpdateAvailable": null, 
     "timeUpdated": 12345678, 
     "type": "all"
       "isDifferent": false
     }
   ], 
-  "imageSets": [
-    {
+  "imageSets": {
+    "diffs": {
+      "baseUrl": "/static/generated-images/diffs", 
+      "description": "color difference per channel"
+    }, 
+    "imageA": {
       "baseUrl": "http://chromium-skia-gm.commondatastorage.googleapis.com/gm", 
       "description": "expected image"
     }, 
-    {
+    "imageB": {
       "baseUrl": "http://chromium-skia-gm.commondatastorage.googleapis.com/gm", 
       "description": "actual image"
+    }, 
+    "whiteDiffs": {
+      "baseUrl": "/static/generated-images/whitediffs", 
+      "description": "differing pixels in white"
     }
-  ]
+  }
 }
\ No newline at end of file