OLD | NEW |
| (Empty) |
1 # Copyright 2014 The Chromium Authors. All rights reserved. | |
2 # Use of this source code is governed by a BSD-style license that can be | |
3 # found in the LICENSE file. | |
4 | |
5 import json | |
6 import logging | |
7 import os | |
8 from distutils.version import LooseVersion | |
9 from PIL import Image | |
10 | |
11 from common import cloud_bucket | |
12 from common import ispy_utils | |
13 | |
14 | |
15 class ISpyApi(object): | |
16 """The public API for interacting with ISpy.""" | |
17 | |
18 def __init__(self, cloud_bucket): | |
19 """Initializes the utility class. | |
20 | |
21 Args: | |
22 cloud_bucket: a BaseCloudBucket in which to the version file, | |
23 expectations and results are to be stored. | |
24 """ | |
25 self._cloud_bucket = cloud_bucket | |
26 self._ispy = ispy_utils.ISpyUtils(self._cloud_bucket) | |
27 self._rebaselineable_cache = {} | |
28 | |
29 def UpdateExpectationVersion(self, chrome_version, version_file): | |
30 """Updates the most recent expectation version to the Chrome version. | |
31 | |
32 Should be called after generating a new set of expectations. | |
33 | |
34 Args: | |
35 chrome_version: the chrome version as a string of the form "31.0.123.4". | |
36 version_file: path to the version file in the cloud bucket. The version | |
37 file contains a json list of ordered Chrome versions for which | |
38 expectations exist. | |
39 """ | |
40 insert_pos = 0 | |
41 expectation_versions = [] | |
42 try: | |
43 expectation_versions = self._GetExpectationVersionList(version_file) | |
44 if expectation_versions: | |
45 try: | |
46 version = self._GetExpectationVersion( | |
47 chrome_version, expectation_versions) | |
48 if version == chrome_version: | |
49 return | |
50 insert_pos = expectation_versions.index(version) | |
51 except: | |
52 insert_pos = len(expectation_versions) | |
53 except cloud_bucket.FileNotFoundError: | |
54 pass | |
55 expectation_versions.insert(insert_pos, chrome_version) | |
56 logging.info('Updating expectation version...') | |
57 self._cloud_bucket.UploadFile( | |
58 version_file, json.dumps(expectation_versions), | |
59 'application/json') | |
60 | |
61 def _GetExpectationVersion(self, chrome_version, expectation_versions): | |
62 """Returns the expectation version for the given Chrome version. | |
63 | |
64 Args: | |
65 chrome_version: the chrome version as a string of the form "31.0.123.4". | |
66 expectation_versions: Ordered list of Chrome versions for which | |
67 expectations exist, as stored in the version file. | |
68 | |
69 Returns: | |
70 Expectation version string. | |
71 """ | |
72 # Find the closest version that is not greater than the chrome version. | |
73 for version in expectation_versions: | |
74 if LooseVersion(version) <= LooseVersion(chrome_version): | |
75 return version | |
76 raise Exception('No expectation exists for Chrome %s' % chrome_version) | |
77 | |
78 def _GetExpectationVersionList(self, version_file): | |
79 """Gets the list of expectation versions from google storage. | |
80 | |
81 Args: | |
82 version_file: path to the version file in the cloud bucket. The version | |
83 file contains a json list of ordered Chrome versions for which | |
84 expectations exist. | |
85 | |
86 Returns: | |
87 Ordered list of Chrome versions. | |
88 """ | |
89 try: | |
90 return json.loads(self._cloud_bucket.DownloadFile(version_file)) | |
91 except: | |
92 return [] | |
93 | |
94 def _GetExpectationNameWithVersion(self, device_type, expectation, | |
95 chrome_version, version_file): | |
96 """Get the expectation to be used with the current Chrome version. | |
97 | |
98 Args: | |
99 device_type: string identifier for the device type. | |
100 expectation: name for the expectation to generate. | |
101 chrome_version: the chrome version as a string of the form "31.0.123.4". | |
102 | |
103 Returns: | |
104 Version as an integer. | |
105 """ | |
106 version = self._GetExpectationVersion( | |
107 chrome_version, self._GetExpectationVersionList(version_file)) | |
108 return self._CreateExpectationName(device_type, expectation, version) | |
109 | |
110 def _CreateExpectationName(self, device_type, expectation, version): | |
111 """Create the full expectation name from the expectation and version. | |
112 | |
113 Args: | |
114 device_type: string identifier for the device type, example: mako | |
115 expectation: base name for the expectation, example: google.com | |
116 version: expectation version, example: 31.0.23.1 | |
117 | |
118 Returns: | |
119 Full expectation name as a string, example: mako:google.com(31.0.23.1) | |
120 """ | |
121 return '%s:%s(%s)' % (device_type, expectation, version) | |
122 | |
123 def GenerateExpectation(self, device_type, expectation, chrome_version, | |
124 version_file, screenshots): | |
125 """Create an expectation for I-Spy. | |
126 | |
127 Args: | |
128 device_type: string identifier for the device type. | |
129 expectation: name for the expectation to generate. | |
130 chrome_version: the chrome version as a string of the form "31.0.123.4". | |
131 screenshots: a list of similar PIL.Images. | |
132 """ | |
133 # https://code.google.com/p/chromedriver/issues/detail?id=463 | |
134 expectation_with_version = self._CreateExpectationName( | |
135 device_type, expectation, chrome_version) | |
136 if self._ispy.ExpectationExists(expectation_with_version): | |
137 logging.warning( | |
138 'I-Spy expectation \'%s\' already exists, overwriting.', | |
139 expectation_with_version) | |
140 logging.info('Generating I-Spy expectation...') | |
141 self._ispy.GenerateExpectation(expectation_with_version, screenshots) | |
142 | |
143 def PerformComparison(self, test_run, device_type, expectation, | |
144 chrome_version, version_file, screenshot): | |
145 """Compare a screenshot with the given expectation in I-Spy. | |
146 | |
147 Args: | |
148 test_run: name for the test run. | |
149 device_type: string identifier for the device type. | |
150 expectation: name for the expectation to compare against. | |
151 chrome_version: the chrome version as a string of the form "31.0.123.4". | |
152 screenshot: a PIL.Image to compare. | |
153 """ | |
154 # https://code.google.com/p/chromedriver/issues/detail?id=463 | |
155 logging.info('Performing I-Spy comparison...') | |
156 self._ispy.PerformComparison( | |
157 test_run, | |
158 self._GetExpectationNameWithVersion( | |
159 device_type, expectation, chrome_version, version_file), | |
160 screenshot) | |
161 | |
162 def CanRebaselineToTestRun(self, test_run): | |
163 """Returns whether the test run has associated expectations. | |
164 | |
165 Returns: | |
166 True if RebaselineToTestRun() can be called for this test run. | |
167 """ | |
168 if test_run in self._rebaselineable_cache: | |
169 return True | |
170 return self._cloud_bucket.FileExists( | |
171 ispy_utils.GetTestRunPath(test_run, 'rebaseline.txt')) | |
172 | |
173 def RebaselineToTestRun(self, test_run): | |
174 """Update the version file to use expectations associated with |test_run|. | |
175 | |
176 Args: | |
177 test_run: The name of the test run to rebaseline. | |
178 """ | |
179 rebaseline_path = ispy_utils.GetTestRunPath(test_run, 'rebaseline.txt') | |
180 rebaseline_attrib = json.loads( | |
181 self._cloud_bucket.DownloadFile(rebaseline_path)) | |
182 self.UpdateExpectationVersion( | |
183 rebaseline_attrib['version'], rebaseline_attrib['version_file']) | |
184 self._cloud_bucket.RemoveFile(rebaseline_path) | |
185 | |
186 def _SetTestRunRebaselineable(self, test_run, chrome_version, version_file): | |
187 """Writes a JSON file containing the data needed to rebaseline. | |
188 | |
189 Args: | |
190 test_run: The name of the test run to add the rebaseline file to. | |
191 chrome_version: the chrome version that can be rebaselined to (must have | |
192 associated Expectations). | |
193 version_file: the path of the version file associated with the test run. | |
194 """ | |
195 self._rebaselineable_cache[test_run] = True | |
196 self._cloud_bucket.UploadFile( | |
197 ispy_utils.GetTestRunPath(test_run, 'rebaseline.txt'), | |
198 json.dumps({ | |
199 'version': chrome_version, | |
200 'version_file': version_file}), | |
201 'application/json') | |
202 | |
203 def PerformComparisonAndPrepareExpectation(self, test_run, device_type, | |
204 expectation, chrome_version, | |
205 version_file, screenshots): | |
206 """Perform comparison and generate an expectation that can used later. | |
207 | |
208 The test run web UI will have a button to set the Expectations generated for | |
209 this version as the expectation for comparison with later versions. | |
210 | |
211 Args: | |
212 test_run: The name of the test run to add the rebaseline file to. | |
213 device_type: string identifier for the device type. | |
214 chrome_version: the chrome version that can be rebaselined to (must have | |
215 associated Expectations). | |
216 version_file: the path of the version file associated with the test run. | |
217 screenshot: a list of similar PIL.Images. | |
218 """ | |
219 if not self.CanRebaselineToTestRun(test_run): | |
220 self._SetTestRunRebaselineable(test_run, chrome_version, version_file) | |
221 expectation_with_version = self._CreateExpectationName( | |
222 device_type, expectation, chrome_version) | |
223 self._ispy.GenerateExpectation(expectation_with_version, screenshots) | |
224 self._ispy.PerformComparison( | |
225 test_run, | |
226 self._GetExpectationNameWithVersion( | |
227 device_type, expectation, chrome_version, version_file), | |
228 screenshots[-1]) | |
229 | |
OLD | NEW |