diff options
Diffstat (limited to 'gm/rebaseline_server/imagepair.py')
-rw-r--r-- | gm/rebaseline_server/imagepair.py | 128 |
1 files changed, 128 insertions, 0 deletions
diff --git a/gm/rebaseline_server/imagepair.py b/gm/rebaseline_server/imagepair.py new file mode 100644 index 0000000000..e85c21948b --- /dev/null +++ b/gm/rebaseline_server/imagepair.py @@ -0,0 +1,128 @@ +#!/usr/bin/python + +""" +Copyright 2014 Google Inc. + +Use of this source code is governed by a BSD-style license that can be +found in the LICENSE file. + +ImagePair class (see class docstring for details) +""" + +import posixpath + + +# Keys used within ImagePair dictionary representations. +# NOTE: Keep these in sync with static/constants.js +KEY__IMAGEPAIRS__DIFFERENCES = 'differenceData' +KEY__IMAGEPAIRS__EXPECTATIONS = 'expectations' +KEY__IMAGEPAIRS__EXTRACOLUMNS = 'extraColumns' +KEY__IMAGEPAIRS__IMAGE_A_URL = 'imageAUrl' +KEY__IMAGEPAIRS__IMAGE_B_URL = 'imageBUrl' +KEY__IMAGEPAIRS__IS_DIFFERENT = 'isDifferent' +KEY__IMAGEPAIRS__SOURCE_JSON_FILE = 'sourceJsonFile' + +# If self._diff_record is set to this, we haven't asked ImageDiffDB for the +# image diff details yet. +_DIFF_RECORD_STILL_LOADING = 'still_loading' + + +class ImagePair(object): + """Describes a pair of images, pixel difference info, and optional metadata. + """ + + def __init__(self, image_diff_db, + imageA_base_url, imageB_base_url, + imageA_relative_url, imageB_relative_url, + expectations=None, extra_columns=None, source_json_file=None, + download_all_images=False): + """ + Args: + image_diff_db: ImageDiffDB instance we use to generate/store image diffs + imageA_base_url: string; base URL for image A + imageB_base_url: string; base URL for image B + imageA_relative_url: string; URL pointing at an image, relative to + imageA_base_url; or None, if this image is missing + imageB_relative_url: string; URL pointing at an image, relative to + imageB_base_url; or None, if this image is missing + expectations: optional dictionary containing expectations-specific + metadata (ignore-failure, bug numbers, etc.) + extra_columns: optional dictionary containing more metadata (test name, + builder name, etc.) + source_json_file: relative path of the JSON file where each image came + from; this will be the same for both imageA and imageB, within their + respective directories + download_all_images: if True, download any images associated with this + image pair, even if we don't need them to generate diffs + (imageA == imageB, or one of them is missing) + """ + self._image_diff_db = image_diff_db + self.imageA_base_url = imageA_base_url + self.imageB_base_url = imageB_base_url + self.imageA_relative_url = imageA_relative_url + self.imageB_relative_url = imageB_relative_url + self.expectations_dict = expectations + self.extra_columns_dict = extra_columns + self.source_json_file = source_json_file + if not imageA_relative_url or not imageB_relative_url: + self._is_different = True + self._diff_record = None + elif imageA_relative_url == imageB_relative_url: + self._is_different = False + self._diff_record = None + else: + # Tell image_diff_db to add an entry for this diff asynchronously. + # Later on, we will call image_diff_db.get_diff_record() to find it. + self._is_different = True + self._diff_record = _DIFF_RECORD_STILL_LOADING + + if self._diff_record != None or download_all_images: + image_diff_db.add_image_pair( + expected_image_locator=imageA_relative_url, + expected_image_url=self.posixpath_join(imageA_base_url, + imageA_relative_url), + actual_image_locator=imageB_relative_url, + actual_image_url=self.posixpath_join(imageB_base_url, + imageB_relative_url)) + + def as_dict(self): + """Returns a dictionary describing this ImagePair. + + Uses the KEY__IMAGEPAIRS__* constants as keys. + """ + asdict = { + KEY__IMAGEPAIRS__IMAGE_A_URL: self.imageA_relative_url, + KEY__IMAGEPAIRS__IMAGE_B_URL: self.imageB_relative_url, + } + asdict[KEY__IMAGEPAIRS__IS_DIFFERENT] = self._is_different + if self.expectations_dict: + asdict[KEY__IMAGEPAIRS__EXPECTATIONS] = self.expectations_dict + if self.extra_columns_dict: + asdict[KEY__IMAGEPAIRS__EXTRACOLUMNS] = self.extra_columns_dict + if self.source_json_file: + asdict[KEY__IMAGEPAIRS__SOURCE_JSON_FILE] = self.source_json_file + if self._diff_record is _DIFF_RECORD_STILL_LOADING: + # We have waited as long as we can to ask ImageDiffDB for details of + # this image diff. Now we must block until ImageDiffDB can provide + # those details. + # + # TODO(epoger): Is it wasteful for every imagepair to have its own + # reference to image_diff_db? If so, we could pass an image_diff_db + # reference into this method call instead... + self._diff_record = self._image_diff_db.get_diff_record( + expected_image_locator=self.imageA_relative_url, + actual_image_locator=self.imageB_relative_url) + if self._diff_record != None: + asdict[KEY__IMAGEPAIRS__DIFFERENCES] = self._diff_record.as_dict() + return asdict + + @staticmethod + def posixpath_join(*args): + """Wrapper around posixpath.join(). + + Returns posixpath.join(*args), or None if any arg is None. + """ + for arg in args: + if arg == None: + return None + return posixpath.join(*args) |