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 24 25 class ImagePair(object): 26 """Describes a pair of images, pixel difference info, and optional metadata. 27 """ 28 29 def __init__(self, image_diff_db, 30 base_url, imageA_relative_url, imageB_relative_url, 31 expectations=None, extra_columns=None): 32 """ 33 Args: 34 image_diff_db: ImageDiffDB instance we use to generate/store image diffs 35 base_url: base of all image URLs 36 imageA_relative_url: string; URL pointing at an image, relative to 37 base_url; or None, if this image is missing 38 imageB_relative_url: string; URL pointing at an image, relative to 39 base_url; or None, if this image is missing 40 expectations: optional dictionary containing expectations-specific 41 metadata (ignore-failure, bug numbers, etc.) 42 extra_columns: optional dictionary containing more metadata (test name, 43 builder name, etc.) 44 """ 45 self.base_url = base_url 46 self.imageA_relative_url = imageA_relative_url 47 self.imageB_relative_url = imageB_relative_url 48 self.expectations_dict = expectations 49 self.extra_columns_dict = extra_columns 50 if not imageA_relative_url or not imageB_relative_url: 51 self._is_different = True 52 self.diff_record = None 53 elif imageA_relative_url == imageB_relative_url: 54 self._is_different = False 55 self.diff_record = None 56 else: 57 # TODO(epoger): Rather than blocking until image_diff_db can read in 58 # the image pair and generate diffs, it would be better to do it 59 # asynchronously: tell image_diff_db to download a bunch of file pairs, 60 # and only block later if we're still waiting for diff_records to come 61 # back. 62 self._is_different = True 63 image_diff_db.add_image_pair( 64 expected_image_locator=imageA_relative_url, 65 expected_image_url=posixpath.join(base_url, imageA_relative_url), 66 actual_image_locator=imageB_relative_url, 67 actual_image_url=posixpath.join(base_url, imageB_relative_url)) 68 self.diff_record = image_diff_db.get_diff_record( 69 expected_image_locator=imageA_relative_url, 70 actual_image_locator=imageB_relative_url) 71 if self.diff_record and self.diff_record.get_num_pixels_differing() == 0: 72 self._is_different = False 73 74 def as_dict(self): 75 """Returns a dictionary describing this ImagePair. 76 77 Uses the KEY__IMAGEPAIRS__* constants as keys. 78 """ 79 asdict = { 80 KEY__IMAGEPAIRS__IMAGE_A_URL: self.imageA_relative_url, 81 KEY__IMAGEPAIRS__IMAGE_B_URL: self.imageB_relative_url, 82 } 83 asdict[KEY__IMAGEPAIRS__IS_DIFFERENT] = self._is_different 84 if self.expectations_dict: 85 asdict[KEY__IMAGEPAIRS__EXPECTATIONS] = self.expectations_dict 86 if self.extra_columns_dict: 87 asdict[KEY__IMAGEPAIRS__EXTRACOLUMNS] = self.extra_columns_dict 88 if self.diff_record and (self.diff_record.get_num_pixels_differing() > 0): 89 asdict[KEY__IMAGEPAIRS__DIFFERENCES] = self.diff_record.as_dict() 90 return asdict 91