imagepair.py revision 280ea8296ff83663a427b8c9aa0ee98b7839f926
1#!/usr/bin/python 2 3""" 4Copyright 2014 Google Inc. 5 6Use of this source code is governed by a BSD-style license that can be 7found in the LICENSE file. 8 9ImagePair class (see class docstring for details) 10""" 11 12import posixpath 13 14# Keys used within ImagePair dictionary representations. 15# NOTE: Keep these in sync with static/constants.js 16KEY__DIFFERENCE_DATA = 'differenceData' 17KEY__EXPECTATIONS_DATA = 'expectations' 18KEY__EXTRA_COLUMN_VALUES = 'extraColumns' 19KEY__IMAGE_A_URL = 'imageAUrl' 20KEY__IMAGE_B_URL = 'imageBUrl' 21KEY__IS_DIFFERENT = 'isDifferent' 22 23 24class ImagePair(object): 25 """Describes a pair of images, pixel difference info, and optional metadata. 26 """ 27 28 def __init__(self, image_diff_db, 29 base_url, imageA_relative_url, imageB_relative_url, 30 expectations=None, extra_columns=None): 31 """ 32 Args: 33 image_diff_db: ImageDiffDB instance we use to generate/store image diffs 34 base_url: base of all image URLs 35 imageA_relative_url: string; URL pointing at an image, relative to 36 base_url; or None, if this image is missing 37 imageB_relative_url: string; URL pointing at an image, relative to 38 base_url; or None, if this image is missing 39 expectations: optional dictionary containing expectations-specific 40 metadata (ignore-failure, bug numbers, etc.) 41 extra_columns: optional dictionary containing more metadata (test name, 42 builder name, etc.) 43 """ 44 self.base_url = base_url 45 self.imageA_relative_url = imageA_relative_url 46 self.imageB_relative_url = imageB_relative_url 47 self.expectations_dict = expectations 48 self.extra_columns_dict = extra_columns 49 if not imageA_relative_url or not imageB_relative_url: 50 self._is_different = True 51 self._diff_record = None 52 self._diff_record_set = True 53 elif imageA_relative_url == imageB_relative_url: 54 self._is_different = False 55 self._diff_record = None 56 self._diff_record_set = True 57 else: 58 # Tell image_diff_db to add this ImagePair. 59 # It will do so in a separate thread so as not to block this one; 60 # when you call self.get_diff_record(), it will block until the results 61 # are ready. 62 image_diff_db.add_image_pair_async( 63 expected_image_locator=imageA_relative_url, 64 expected_image_url=posixpath.join(base_url, imageA_relative_url), 65 actual_image_locator=imageB_relative_url, 66 actual_image_url=posixpath.join(base_url, imageB_relative_url)) 67 self._image_diff_db = image_diff_db 68 self._diff_record_set = False 69 70 def get_diff_record(self): 71 """Returns the DiffRecord associated with this ImagePair. 72 73 Returns None if the images are identical, or one is missing. 74 This method will block until the DiffRecord is available. 75 """ 76 if not self._diff_record_set: 77 self._diff_record = self._image_diff_db.get_diff_record( 78 expected_image_locator=self.imageA_relative_url, 79 actual_image_locator=self.imageB_relative_url) 80 self._image_diff_db = None # release reference, no longer needed 81 if (self._diff_record and 82 self._diff_record.get_num_pixels_differing() == 0): 83 self._is_different = False 84 else: 85 self._is_different = True 86 self._diff_record_set = True 87 return self._diff_record 88 89 def as_dict(self): 90 """Returns a dictionary describing this ImagePair. 91 92 Uses the KEY__* constants as keys. 93 """ 94 asdict = { 95 KEY__IMAGE_A_URL: self.imageA_relative_url, 96 KEY__IMAGE_B_URL: self.imageB_relative_url, 97 } 98 if self.expectations_dict: 99 asdict[KEY__EXPECTATIONS_DATA] = self.expectations_dict 100 if self.extra_columns_dict: 101 asdict[KEY__EXTRA_COLUMN_VALUES] = self.extra_columns_dict 102 diff_record = self.get_diff_record() 103 if diff_record and (diff_record.get_num_pixels_differing() > 0): 104 asdict[KEY__DIFFERENCE_DATA] = diff_record.as_dict() 105 asdict[KEY__IS_DIFFERENT] = self._is_different 106 return asdict 107