OLD | NEW |
1 #!/usr/bin/python | 1 #!/usr/bin/python |
2 | 2 |
3 """ | 3 """ |
4 Copyright 2014 Google Inc. | 4 Copyright 2014 Google Inc. |
5 | 5 |
6 Use of this source code is governed by a BSD-style license that can be | 6 Use of this source code is governed by a BSD-style license that can be |
7 found in the LICENSE file. | 7 found in the LICENSE file. |
8 | 8 |
9 ImagePair class (see class docstring for details) | 9 ImagePair class (see class docstring for details) |
10 """ | 10 """ |
11 | 11 |
12 import posixpath | 12 import posixpath |
13 | 13 |
14 | 14 |
15 # Keys used within ImagePair dictionary representations. | 15 # Keys used within ImagePair dictionary representations. |
16 # NOTE: Keep these in sync with static/constants.js | 16 # NOTE: Keep these in sync with static/constants.js |
17 KEY__IMAGEPAIRS__DIFFERENCES = 'differenceData' | 17 KEY__IMAGEPAIRS__DIFFERENCES = 'differenceData' |
18 KEY__IMAGEPAIRS__EXPECTATIONS = 'expectations' | 18 KEY__IMAGEPAIRS__EXPECTATIONS = 'expectations' |
19 KEY__IMAGEPAIRS__EXTRACOLUMNS = 'extraColumns' | 19 KEY__IMAGEPAIRS__EXTRACOLUMNS = 'extraColumns' |
20 KEY__IMAGEPAIRS__IMAGE_A_URL = 'imageAUrl' | 20 KEY__IMAGEPAIRS__IMAGE_A_URL = 'imageAUrl' |
21 KEY__IMAGEPAIRS__IMAGE_B_URL = 'imageBUrl' | 21 KEY__IMAGEPAIRS__IMAGE_B_URL = 'imageBUrl' |
22 KEY__IMAGEPAIRS__IS_DIFFERENT = 'isDifferent' | 22 KEY__IMAGEPAIRS__IS_DIFFERENT = 'isDifferent' |
23 | 23 |
| 24 # If self._diff_record is set to this, we haven't asked ImageDiffDB for the |
| 25 # image diff details yet. |
| 26 _DIFF_RECORD_STILL_LOADING = 'still_loading' |
| 27 |
24 | 28 |
25 class ImagePair(object): | 29 class ImagePair(object): |
26 """Describes a pair of images, pixel difference info, and optional metadata. | 30 """Describes a pair of images, pixel difference info, and optional metadata. |
27 """ | 31 """ |
28 | 32 |
29 def __init__(self, image_diff_db, | 33 def __init__(self, image_diff_db, |
30 base_url, imageA_relative_url, imageB_relative_url, | 34 base_url, imageA_relative_url, imageB_relative_url, |
31 expectations=None, extra_columns=None): | 35 expectations=None, extra_columns=None): |
32 """ | 36 """ |
33 Args: | 37 Args: |
34 image_diff_db: ImageDiffDB instance we use to generate/store image diffs | 38 image_diff_db: ImageDiffDB instance we use to generate/store image diffs |
35 base_url: base of all image URLs | 39 base_url: base of all image URLs |
36 imageA_relative_url: string; URL pointing at an image, relative to | 40 imageA_relative_url: string; URL pointing at an image, relative to |
37 base_url; or None, if this image is missing | 41 base_url; or None, if this image is missing |
38 imageB_relative_url: string; URL pointing at an image, relative to | 42 imageB_relative_url: string; URL pointing at an image, relative to |
39 base_url; or None, if this image is missing | 43 base_url; or None, if this image is missing |
40 expectations: optional dictionary containing expectations-specific | 44 expectations: optional dictionary containing expectations-specific |
41 metadata (ignore-failure, bug numbers, etc.) | 45 metadata (ignore-failure, bug numbers, etc.) |
42 extra_columns: optional dictionary containing more metadata (test name, | 46 extra_columns: optional dictionary containing more metadata (test name, |
43 builder name, etc.) | 47 builder name, etc.) |
44 """ | 48 """ |
| 49 self._image_diff_db = image_diff_db |
45 self.base_url = base_url | 50 self.base_url = base_url |
46 self.imageA_relative_url = imageA_relative_url | 51 self.imageA_relative_url = imageA_relative_url |
47 self.imageB_relative_url = imageB_relative_url | 52 self.imageB_relative_url = imageB_relative_url |
48 self.expectations_dict = expectations | 53 self.expectations_dict = expectations |
49 self.extra_columns_dict = extra_columns | 54 self.extra_columns_dict = extra_columns |
50 if not imageA_relative_url or not imageB_relative_url: | 55 if not imageA_relative_url or not imageB_relative_url: |
51 self._is_different = True | 56 self._is_different = True |
52 self.diff_record = None | 57 self._diff_record = None |
53 elif imageA_relative_url == imageB_relative_url: | 58 elif imageA_relative_url == imageB_relative_url: |
54 self._is_different = False | 59 self._is_different = False |
55 self.diff_record = None | 60 self._diff_record = None |
56 else: | 61 else: |
57 # TODO(epoger): Rather than blocking until image_diff_db can read in | 62 # Tell image_diff_db to add an entry for this diff asynchronously. |
58 # the image pair and generate diffs, it would be better to do it | 63 # Later on, we will call image_diff_db.get_diff_record() to find 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 | 64 self._is_different = True |
| 65 self._diff_record = _DIFF_RECORD_STILL_LOADING |
63 image_diff_db.add_image_pair( | 66 image_diff_db.add_image_pair( |
64 expected_image_locator=imageA_relative_url, | 67 expected_image_locator=imageA_relative_url, |
65 expected_image_url=posixpath.join(base_url, imageA_relative_url), | 68 expected_image_url=posixpath.join(base_url, imageA_relative_url), |
66 actual_image_locator=imageB_relative_url, | 69 actual_image_locator=imageB_relative_url, |
67 actual_image_url=posixpath.join(base_url, imageB_relative_url)) | 70 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 | 71 |
74 def as_dict(self): | 72 def as_dict(self): |
75 """Returns a dictionary describing this ImagePair. | 73 """Returns a dictionary describing this ImagePair. |
76 | 74 |
77 Uses the KEY__IMAGEPAIRS__* constants as keys. | 75 Uses the KEY__IMAGEPAIRS__* constants as keys. |
78 """ | 76 """ |
79 asdict = { | 77 asdict = { |
80 KEY__IMAGEPAIRS__IMAGE_A_URL: self.imageA_relative_url, | 78 KEY__IMAGEPAIRS__IMAGE_A_URL: self.imageA_relative_url, |
81 KEY__IMAGEPAIRS__IMAGE_B_URL: self.imageB_relative_url, | 79 KEY__IMAGEPAIRS__IMAGE_B_URL: self.imageB_relative_url, |
82 } | 80 } |
83 asdict[KEY__IMAGEPAIRS__IS_DIFFERENT] = self._is_different | 81 asdict[KEY__IMAGEPAIRS__IS_DIFFERENT] = self._is_different |
84 if self.expectations_dict: | 82 if self.expectations_dict: |
85 asdict[KEY__IMAGEPAIRS__EXPECTATIONS] = self.expectations_dict | 83 asdict[KEY__IMAGEPAIRS__EXPECTATIONS] = self.expectations_dict |
86 if self.extra_columns_dict: | 84 if self.extra_columns_dict: |
87 asdict[KEY__IMAGEPAIRS__EXTRACOLUMNS] = self.extra_columns_dict | 85 asdict[KEY__IMAGEPAIRS__EXTRACOLUMNS] = self.extra_columns_dict |
88 if self.diff_record and (self.diff_record.get_num_pixels_differing() > 0): | 86 if self._diff_record is _DIFF_RECORD_STILL_LOADING: |
89 asdict[KEY__IMAGEPAIRS__DIFFERENCES] = self.diff_record.as_dict() | 87 # We have waited as long as we can to ask ImageDiffDB for details of |
| 88 # this image diff. Now we must block until ImageDiffDB can provide |
| 89 # those details. |
| 90 # |
| 91 # TODO(epoger): Is it wasteful for every imagepair to have its own |
| 92 # reference to image_diff_db? If so, we could pass an image_diff_db |
| 93 # reference into this method call instead... |
| 94 self._diff_record = self._image_diff_db.get_diff_record( |
| 95 expected_image_locator=self.imageA_relative_url, |
| 96 actual_image_locator=self.imageB_relative_url) |
| 97 if self._diff_record != None: |
| 98 asdict[KEY__IMAGEPAIRS__DIFFERENCES] = self._diff_record.as_dict() |
90 return asdict | 99 return asdict |
OLD | NEW |