aboutsummaryrefslogtreecommitdiffhomepage
path: root/gm/rebaseline_server/imagepair.py
blob: 446858d3a5a9badc405b5a8ad7c7dc0654ad138d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
#!/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


# Keys used within ImagePair dictionary representations.
# NOTE: Keep these in sync with static/constants.js
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'


class ImagePair(object):
  """Describes a pair of images, pixel difference info, and optional metadata.
  """

  def __init__(self, image_diff_db,
               base_url, imageA_relative_url, imageB_relative_url,
               expectations=None, extra_columns=None):
    """
    Args:
      image_diff_db: ImageDiffDB instance we use to generate/store image diffs
      base_url: base of all image URLs
      imageA_relative_url: string; URL pointing at an image, relative to
          base_url; or None, if this image is missing
      imageB_relative_url: string; URL pointing at an image, relative to
          base_url; or None, if this image is missing
      expectations: optional dictionary containing expectations-specific
          metadata (ignore-failure, bug numbers, etc.)
      extra_columns: optional dictionary containing more metadata (test name,
          builder name, etc.)
    """
    self.base_url = base_url
    self.imageA_relative_url = imageA_relative_url
    self.imageB_relative_url = imageB_relative_url
    self.expectations_dict = expectations
    self.extra_columns_dict = extra_columns
    if not imageA_relative_url or not imageB_relative_url:
      self._is_different = True
      self.diff_record = None
    elif imageA_relative_url == imageB_relative_url:
      self._is_different = False
      self.diff_record = None
    else:
      # TODO(epoger): Rather than blocking until image_diff_db can read in
      # the image pair and generate diffs, it would be better to do it
      # asynchronously: tell image_diff_db to download a bunch of file pairs,
      # and only block later if we're still waiting for diff_records to come
      # back.
      self._is_different = True
      image_diff_db.add_image_pair(
          expected_image_locator=imageA_relative_url,
          expected_image_url=posixpath.join(base_url, imageA_relative_url),
          actual_image_locator=imageB_relative_url,
          actual_image_url=posixpath.join(base_url, imageB_relative_url))
      self.diff_record = image_diff_db.get_diff_record(
          expected_image_locator=imageA_relative_url,
          actual_image_locator=imageB_relative_url)
      if self.diff_record and self.diff_record.get_num_pixels_differing() == 0:
        self._is_different = False

  def as_dict(self):
    """Returns a dictionary describing this ImagePair.

    Uses the KEY__IMAGEPAIRS__* constants as keys.
    """
    asdict = {
        KEY__IMAGEPAIRS__IMAGE_A_URL: self.imageA_relative_url,
        KEY__IMAGEPAIRS__IMAGE_B_URL: self.imageB_relative_url,
    }
    asdict[KEY__IMAGEPAIRS__IS_DIFFERENT] = self._is_different
    if self.expectations_dict:
      asdict[KEY__IMAGEPAIRS__EXPECTATIONS] = self.expectations_dict
    if self.extra_columns_dict:
      asdict[KEY__IMAGEPAIRS__EXTRACOLUMNS] = self.extra_columns_dict
    if self.diff_record and (self.diff_record.get_num_pixels_differing() > 0):
      asdict[KEY__IMAGEPAIRS__DIFFERENCES] = self.diff_record.as_dict()
    return asdict