2014-02-10 18:19:30 +00:00
|
|
|
#!/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
|
|
|
|
|
2014-05-12 20:40:29 +00:00
|
|
|
|
2014-02-10 18:19:30 +00:00
|
|
|
# Keys used within ImagePair dictionary representations.
|
2014-02-26 19:05:20 +00:00
|
|
|
# NOTE: Keep these in sync with static/constants.js
|
2014-05-12 20:40:29 +00:00
|
|
|
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'
|
2014-08-20 15:00:28 +00:00
|
|
|
KEY__IMAGEPAIRS__SOURCE_JSON_FILE = 'sourceJsonFile'
|
2014-02-10 18:19:30 +00:00
|
|
|
|
2014-08-05 17:07:22 +00:00
|
|
|
# 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'
|
|
|
|
|
2014-02-10 18:19:30 +00:00
|
|
|
|
|
|
|
class ImagePair(object):
|
2014-02-13 17:17:05 +00:00
|
|
|
"""Describes a pair of images, pixel difference info, and optional metadata.
|
2014-02-10 18:19:30 +00:00
|
|
|
"""
|
|
|
|
|
|
|
|
def __init__(self, image_diff_db,
|
2014-08-22 11:46:30 +00:00
|
|
|
imageA_base_url, imageB_base_url,
|
|
|
|
imageA_relative_url, imageB_relative_url,
|
2014-08-20 15:00:28 +00:00
|
|
|
expectations=None, extra_columns=None, source_json_file=None,
|
2014-08-06 17:56:50 +00:00
|
|
|
download_all_images=False):
|
2014-02-10 18:19:30 +00:00
|
|
|
"""
|
|
|
|
Args:
|
|
|
|
image_diff_db: ImageDiffDB instance we use to generate/store image diffs
|
2014-08-22 11:46:30 +00:00
|
|
|
imageA_base_url: string; base URL for image A
|
|
|
|
imageB_base_url: string; base URL for image B
|
2014-02-26 19:05:20 +00:00
|
|
|
imageA_relative_url: string; URL pointing at an image, relative to
|
2014-08-22 11:46:30 +00:00
|
|
|
imageA_base_url; or None, if this image is missing
|
2014-02-26 19:05:20 +00:00
|
|
|
imageB_relative_url: string; URL pointing at an image, relative to
|
2014-08-22 11:46:30 +00:00
|
|
|
imageB_base_url; or None, if this image is missing
|
2014-02-10 18:19:30 +00:00
|
|
|
expectations: optional dictionary containing expectations-specific
|
|
|
|
metadata (ignore-failure, bug numbers, etc.)
|
|
|
|
extra_columns: optional dictionary containing more metadata (test name,
|
|
|
|
builder name, etc.)
|
2014-08-20 15:00:28 +00:00
|
|
|
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
|
2014-08-06 17:56:50 +00:00
|
|
|
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)
|
2014-02-10 18:19:30 +00:00
|
|
|
"""
|
2014-08-05 17:07:22 +00:00
|
|
|
self._image_diff_db = image_diff_db
|
2014-08-22 11:46:30 +00:00
|
|
|
self.imageA_base_url = imageA_base_url
|
|
|
|
self.imageB_base_url = imageB_base_url
|
2014-02-10 18:19:30 +00:00
|
|
|
self.imageA_relative_url = imageA_relative_url
|
|
|
|
self.imageB_relative_url = imageB_relative_url
|
|
|
|
self.expectations_dict = expectations
|
|
|
|
self.extra_columns_dict = extra_columns
|
2014-08-20 15:00:28 +00:00
|
|
|
self.source_json_file = source_json_file
|
2014-02-26 19:05:20 +00:00
|
|
|
if not imageA_relative_url or not imageB_relative_url:
|
2014-04-01 22:16:33 +00:00
|
|
|
self._is_different = True
|
2014-08-05 17:07:22 +00:00
|
|
|
self._diff_record = None
|
2014-02-26 19:05:20 +00:00
|
|
|
elif imageA_relative_url == imageB_relative_url:
|
2014-04-01 22:16:33 +00:00
|
|
|
self._is_different = False
|
2014-08-05 17:07:22 +00:00
|
|
|
self._diff_record = None
|
2014-02-10 18:19:30 +00:00
|
|
|
else:
|
2014-08-05 17:07:22 +00:00
|
|
|
# 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.
|
2014-04-15 18:50:12 +00:00
|
|
|
self._is_different = True
|
2014-08-05 17:07:22 +00:00
|
|
|
self._diff_record = _DIFF_RECORD_STILL_LOADING
|
2014-08-06 17:56:50 +00:00
|
|
|
|
|
|
|
if self._diff_record != None or download_all_images:
|
2014-04-15 18:50:12 +00:00
|
|
|
image_diff_db.add_image_pair(
|
2014-02-10 18:19:30 +00:00
|
|
|
expected_image_locator=imageA_relative_url,
|
2014-08-22 11:46:30 +00:00
|
|
|
expected_image_url=self.posixpath_join(imageA_base_url,
|
|
|
|
imageA_relative_url),
|
2014-02-10 18:19:30 +00:00
|
|
|
actual_image_locator=imageB_relative_url,
|
2014-08-22 11:46:30 +00:00
|
|
|
actual_image_url=self.posixpath_join(imageB_base_url,
|
|
|
|
imageB_relative_url))
|
2014-02-10 18:19:30 +00:00
|
|
|
|
|
|
|
def as_dict(self):
|
2014-02-13 17:17:05 +00:00
|
|
|
"""Returns a dictionary describing this ImagePair.
|
|
|
|
|
2014-05-12 20:40:29 +00:00
|
|
|
Uses the KEY__IMAGEPAIRS__* constants as keys.
|
2014-02-10 18:19:30 +00:00
|
|
|
"""
|
|
|
|
asdict = {
|
2014-05-12 20:40:29 +00:00
|
|
|
KEY__IMAGEPAIRS__IMAGE_A_URL: self.imageA_relative_url,
|
|
|
|
KEY__IMAGEPAIRS__IMAGE_B_URL: self.imageB_relative_url,
|
2014-02-10 18:19:30 +00:00
|
|
|
}
|
2014-05-12 20:40:29 +00:00
|
|
|
asdict[KEY__IMAGEPAIRS__IS_DIFFERENT] = self._is_different
|
2014-02-10 18:19:30 +00:00
|
|
|
if self.expectations_dict:
|
2014-05-12 20:40:29 +00:00
|
|
|
asdict[KEY__IMAGEPAIRS__EXPECTATIONS] = self.expectations_dict
|
2014-02-10 18:19:30 +00:00
|
|
|
if self.extra_columns_dict:
|
2014-05-12 20:40:29 +00:00
|
|
|
asdict[KEY__IMAGEPAIRS__EXTRACOLUMNS] = self.extra_columns_dict
|
2014-08-20 15:00:28 +00:00
|
|
|
if self.source_json_file:
|
|
|
|
asdict[KEY__IMAGEPAIRS__SOURCE_JSON_FILE] = self.source_json_file
|
2014-08-05 17:07:22 +00:00
|
|
|
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()
|
2014-02-10 18:19:30 +00:00
|
|
|
return asdict
|
2014-08-06 17:56:50 +00:00
|
|
|
|
|
|
|
@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)
|