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
9Test imagepair.py
10"""
11
12# System-level imports
13import shutil
14import tempfile
15import unittest
16
17# Local imports
18import imagediffdb
19import imagepair
20
21
22IMG_URL_BASE = ('http://chromium-skia-gm.commondatastorage.googleapis.com/'
23                'gm/bitmap-64bitMD5/')
24
25
26class ImagePairTest(unittest.TestCase):
27
28  def setUp(self):
29    self.temp_dir = tempfile.mkdtemp()
30    self.maxDiff = None
31
32  def tearDown(self):
33    shutil.rmtree(self.temp_dir)
34
35  def shortDescription(self):
36    """Tells unittest framework to not print docstrings for test cases."""
37    return None
38
39  def test_endToEnd(self):
40    """Tests ImagePair, using a real ImageDiffDB to download real images.
41
42    TODO(epoger): Either in addition to or instead of this end-to-end test,
43    we should perform some tests using either:
44    1. a mock ImageDiffDB, or
45    2. a real ImageDiffDB that doesn't hit Google Storage looking for input
46       image files (maybe a file:// IMG_URL_BASE)
47    """
48    # params for each self-test:
49    #
50    # inputs:
51    #  0. imageA_relative_URL
52    #  1. imageB_relative_URL
53    #  2. expectations dict
54    #  3. extra_columns dict
55    # expected output:
56    #  4. expected result of ImagePair.as_dict()
57    selftests = [
58        [
59            # inputs:
60            'arcofzorro/16206093933823793653.png',
61            'arcofzorro/16206093933823793653.png',
62            None,
63            {
64                'builder': 'MyBuilder',
65                'test': 'MyTest',
66            },
67            # expected output:
68            {
69                'extraColumns': {
70                    'builder': 'MyBuilder',
71                    'test': 'MyTest',
72                },
73                'imageAUrl': 'arcofzorro/16206093933823793653.png',
74                'imageBUrl': 'arcofzorro/16206093933823793653.png',
75                'isDifferent': False,
76            },
77        ],
78
79        [
80            # inputs:
81            'arcofzorro/16206093933823793653.png',
82            'arcofzorro/13786535001616823825.png',
83            None,
84            None,
85            # expected output:
86            {
87                'differenceData': {
88                    'maxDiffPerChannel': [255, 255, 247],
89                    'numDifferingPixels': 662,
90                    'percentDifferingPixels': 0.0662,
91                    'perceptualDifference': 0.06620300000000157,
92                    'diffUrl': 'arcofzorro_16206093933823793653_png_png-vs-' +
93                        'arcofzorro_13786535001616823825_png_png.png',
94                    'whiteDiffUrl': 'arcofzorro_16206093933823793653_png_png' +
95                        '-vs-arcofzorro_13786535001616823825_png_png.png',
96                },
97                'imageAUrl': 'arcofzorro/16206093933823793653.png',
98                'imageBUrl': 'arcofzorro/13786535001616823825.png',
99                'isDifferent': True,
100            },
101        ],
102
103        [
104            # inputs:
105            'gradients_degenerate_2pt/10552995703607727960.png',
106            'gradients_degenerate_2pt/11198253335583713230.png',
107            {
108                'ignoreFailure': True,
109                'bugs': [1001, 1002],
110            },
111            {
112                'builder': 'MyBuilder',
113                'test': 'MyTest',
114            },
115            # expected output:
116            {
117                'differenceData': {
118                    'maxDiffPerChannel': [255, 0, 255],
119                    'numDifferingPixels': 102400,
120                    'percentDifferingPixels': 100.00,
121                    'perceptualDifference': 100.00,
122                    'diffUrl': 'gradients_degenerate_2pt_10552995703607727960' +
123                               '_png_png-vs-gradients_degenerate_2pt_' +
124                               '11198253335583713230_png_png.png',
125                    'whiteDiffUrl': 'gradients_degenerate_2pt_' +
126                               '10552995703607727960_png_png-vs-' +
127                               'gradients_degenerate_2pt_11198253335583713230' +
128                               '_png_png.png'
129                },
130                'expectations': {
131                    'bugs': [1001, 1002],
132                    'ignoreFailure': True,
133                },
134                'extraColumns': {
135                    'builder': 'MyBuilder',
136                    'test': 'MyTest',
137                },
138                'imageAUrl':
139                    'gradients_degenerate_2pt/10552995703607727960.png',
140                'imageBUrl':
141                    'gradients_degenerate_2pt/11198253335583713230.png',
142                'isDifferent': True,
143            },
144        ],
145
146        # Test fix for http://skbug.com/2368 -- how do we handle an ImagePair
147        # missing one of its images?
148        [
149            # inputs:
150            'arcofzorro/16206093933823793653.png',
151            'nonexistentDir/111111.png',
152            {
153                'ignoreFailure': True,
154                'bugs': [1001, 1002],
155            },
156            {
157                'builder': 'MyBuilder',
158                'test': 'MyTest',
159            },
160            # expected output:
161            {
162                'expectations': {
163                    'bugs': [1001, 1002],
164                    'ignoreFailure': True,
165                },
166                'extraColumns': {
167                    'builder': 'MyBuilder',
168                    'test': 'MyTest',
169                },
170                'imageAUrl': 'arcofzorro/16206093933823793653.png',
171                'imageBUrl': 'nonexistentDir/111111.png',
172                'isDifferent': True,
173            },
174        ],
175
176        # One of the two images is missing, but download_all_images=True so we
177        # should download it anyway.
178        [
179            # inputs:
180            None,
181            'arcofzorro/13786535001616823825.png',
182            None,
183            None,
184            # expected output:
185            {
186                'imageAUrl': None,
187                'imageBUrl': 'arcofzorro/13786535001616823825.png',
188                'isDifferent': True,
189            },
190        ],
191
192    ]
193
194    db = imagediffdb.ImageDiffDB(self.temp_dir)
195    for selftest in selftests:
196      image_pair = imagepair.ImagePair(
197          image_diff_db=db,
198          imageA_base_url=IMG_URL_BASE,
199          imageB_base_url=IMG_URL_BASE,
200          imageA_relative_url=selftest[0],
201          imageB_relative_url=selftest[1],
202          expectations=selftest[2],
203          extra_columns=selftest[3],
204          download_all_images=True)
205      self.assertEqual(image_pair.as_dict(), selftest[4])
206
207
208def main():
209  suite = unittest.TestLoader().loadTestsFromTestCase(ImagePairTest)
210  unittest.TextTestRunner(verbosity=2).run(suite)
211
212
213if __name__ == '__main__':
214  main()
215