OLD | NEW |
(Empty) | |
| 1 #!/usr/bin/python |
| 2 |
| 3 """ |
| 4 Copyright 2014 Google Inc. |
| 5 |
| 6 Use of this source code is governed by a BSD-style license that can be |
| 7 found in the LICENSE file. |
| 8 |
| 9 ImagePair class (see class docstring for details) |
| 10 """ |
| 11 |
| 12 import posixpath |
| 13 |
| 14 |
| 15 # Keys used within ImagePair dictionary representations. |
| 16 # NOTE: Keep these in sync with static/constants.js |
| 17 KEY__IMAGEPAIRS__DIFFERENCES = 'differenceData' |
| 18 KEY__IMAGEPAIRS__EXPECTATIONS = 'expectations' |
| 19 KEY__IMAGEPAIRS__EXTRACOLUMNS = 'extraColumns' |
| 20 KEY__IMAGEPAIRS__IMAGE_A_URL = 'imageAUrl' |
| 21 KEY__IMAGEPAIRS__IMAGE_B_URL = 'imageBUrl' |
| 22 KEY__IMAGEPAIRS__IS_DIFFERENT = 'isDifferent' |
| 23 KEY__IMAGEPAIRS__SOURCE_JSON_FILE = 'sourceJsonFile' |
| 24 |
| 25 # If self._diff_record is set to this, we haven't asked ImageDiffDB for the |
| 26 # image diff details yet. |
| 27 _DIFF_RECORD_STILL_LOADING = 'still_loading' |
| 28 |
| 29 |
| 30 class ImagePair(object): |
| 31 """Describes a pair of images, pixel difference info, and optional metadata. |
| 32 """ |
| 33 |
| 34 def __init__(self, image_diff_db, |
| 35 imageA_base_url, imageB_base_url, |
| 36 imageA_relative_url, imageB_relative_url, |
| 37 expectations=None, extra_columns=None, source_json_file=None, |
| 38 download_all_images=False): |
| 39 """ |
| 40 Args: |
| 41 image_diff_db: ImageDiffDB instance we use to generate/store image diffs |
| 42 imageA_base_url: string; base URL for image A |
| 43 imageB_base_url: string; base URL for image B |
| 44 imageA_relative_url: string; URL pointing at an image, relative to |
| 45 imageA_base_url; or None, if this image is missing |
| 46 imageB_relative_url: string; URL pointing at an image, relative to |
| 47 imageB_base_url; or None, if this image is missing |
| 48 expectations: optional dictionary containing expectations-specific |
| 49 metadata (ignore-failure, bug numbers, etc.) |
| 50 extra_columns: optional dictionary containing more metadata (test name, |
| 51 builder name, etc.) |
| 52 source_json_file: relative path of the JSON file where each image came |
| 53 from; this will be the same for both imageA and imageB, within their |
| 54 respective directories |
| 55 download_all_images: if True, download any images associated with this |
| 56 image pair, even if we don't need them to generate diffs |
| 57 (imageA == imageB, or one of them is missing) |
| 58 """ |
| 59 self._image_diff_db = image_diff_db |
| 60 self.imageA_base_url = imageA_base_url |
| 61 self.imageB_base_url = imageB_base_url |
| 62 self.imageA_relative_url = imageA_relative_url |
| 63 self.imageB_relative_url = imageB_relative_url |
| 64 self.expectations_dict = expectations |
| 65 self.extra_columns_dict = extra_columns |
| 66 self.source_json_file = source_json_file |
| 67 if not imageA_relative_url or not imageB_relative_url: |
| 68 self._is_different = True |
| 69 self._diff_record = None |
| 70 elif imageA_relative_url == imageB_relative_url: |
| 71 self._is_different = False |
| 72 self._diff_record = None |
| 73 else: |
| 74 # Tell image_diff_db to add an entry for this diff asynchronously. |
| 75 # Later on, we will call image_diff_db.get_diff_record() to find it. |
| 76 self._is_different = True |
| 77 self._diff_record = _DIFF_RECORD_STILL_LOADING |
| 78 |
| 79 if self._diff_record != None or download_all_images: |
| 80 image_diff_db.add_image_pair( |
| 81 expected_image_locator=imageA_relative_url, |
| 82 expected_image_url=self.posixpath_join(imageA_base_url, |
| 83 imageA_relative_url), |
| 84 actual_image_locator=imageB_relative_url, |
| 85 actual_image_url=self.posixpath_join(imageB_base_url, |
| 86 imageB_relative_url)) |
| 87 |
| 88 def as_dict(self): |
| 89 """Returns a dictionary describing this ImagePair. |
| 90 |
| 91 Uses the KEY__IMAGEPAIRS__* constants as keys. |
| 92 """ |
| 93 asdict = { |
| 94 KEY__IMAGEPAIRS__IMAGE_A_URL: self.imageA_relative_url, |
| 95 KEY__IMAGEPAIRS__IMAGE_B_URL: self.imageB_relative_url, |
| 96 } |
| 97 asdict[KEY__IMAGEPAIRS__IS_DIFFERENT] = self._is_different |
| 98 if self.expectations_dict: |
| 99 asdict[KEY__IMAGEPAIRS__EXPECTATIONS] = self.expectations_dict |
| 100 if self.extra_columns_dict: |
| 101 asdict[KEY__IMAGEPAIRS__EXTRACOLUMNS] = self.extra_columns_dict |
| 102 if self.source_json_file: |
| 103 asdict[KEY__IMAGEPAIRS__SOURCE_JSON_FILE] = self.source_json_file |
| 104 if self._diff_record is _DIFF_RECORD_STILL_LOADING: |
| 105 # We have waited as long as we can to ask ImageDiffDB for details of |
| 106 # this image diff. Now we must block until ImageDiffDB can provide |
| 107 # those details. |
| 108 # |
| 109 # TODO(epoger): Is it wasteful for every imagepair to have its own |
| 110 # reference to image_diff_db? If so, we could pass an image_diff_db |
| 111 # reference into this method call instead... |
| 112 self._diff_record = self._image_diff_db.get_diff_record( |
| 113 expected_image_locator=self.imageA_relative_url, |
| 114 actual_image_locator=self.imageB_relative_url) |
| 115 if self._diff_record != None: |
| 116 asdict[KEY__IMAGEPAIRS__DIFFERENCES] = self._diff_record.as_dict() |
| 117 return asdict |
| 118 |
| 119 @staticmethod |
| 120 def posixpath_join(*args): |
| 121 """Wrapper around posixpath.join(). |
| 122 |
| 123 Returns posixpath.join(*args), or None if any arg is None. |
| 124 """ |
| 125 for arg in args: |
| 126 if arg == None: |
| 127 return None |
| 128 return posixpath.join(*args) |
OLD | NEW |