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 |