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.
8 from distutils
.version
import LooseVersion
11 from common
import cloud_bucket
12 from common
import ispy_utils
15 class ISpyApi(object):
16 """The public API for interacting with ISpy."""
18 def __init__(self
, cloud_bucket
):
19 """Initializes the utility class.
22 cloud_bucket: a BaseCloudBucket in which to the version file,
23 expectations and results are to be stored.
25 self
._cloud
_bucket
= cloud_bucket
26 self
._ispy
= ispy_utils
.ISpyUtils(self
._cloud
_bucket
)
27 self
._rebaselineable
_cache
= {}
29 def UpdateExpectationVersion(self
, chrome_version
, version_file
):
30 """Updates the most recent expectation version to the Chrome version.
32 Should be called after generating a new set of expectations.
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
41 expectation_versions
= []
43 expectation_versions
= self
._GetExpectationVersionList
(version_file
)
44 if expectation_versions
:
46 version
= self
._GetExpectationVersion
(
47 chrome_version
, expectation_versions
)
48 if version
== chrome_version
:
50 insert_pos
= expectation_versions
.index(version
)
52 insert_pos
= len(expectation_versions
)
53 except cloud_bucket
.FileNotFoundError
:
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
),
61 def _GetExpectationVersion(self
, chrome_version
, expectation_versions
):
62 """Returns the expectation version for the given Chrome version.
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.
70 Expectation version string.
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
):
76 raise Exception('No expectation exists for Chrome %s' % chrome_version
)
78 def _GetExpectationVersionList(self
, version_file
):
79 """Gets the list of expectation versions from google storage.
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
87 Ordered list of Chrome versions.
90 return json
.loads(self
._cloud
_bucket
.DownloadFile(version_file
))
94 def _GetExpectationNameWithVersion(self
, device_type
, expectation
,
95 chrome_version
, version_file
):
96 """Get the expectation to be used with the current Chrome version.
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".
104 Version as an integer.
106 version
= self
._GetExpectationVersion
(
107 chrome_version
, self
._GetExpectationVersionList
(version_file
))
108 return self
._CreateExpectationName
(device_type
, expectation
, version
)
110 def _CreateExpectationName(self
, device_type
, expectation
, version
):
111 """Create the full expectation name from the expectation and version.
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
119 Full expectation name as a string, example: mako:google.com(31.0.23.1)
121 return '%s:%s(%s)' % (device_type
, expectation
, version
)
123 def GenerateExpectation(self
, device_type
, expectation
, chrome_version
,
124 version_file
, screenshots
):
125 """Create an expectation for I-Spy.
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.
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
):
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
)
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.
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.
154 # https://code.google.com/p/chromedriver/issues/detail?id=463
155 logging
.info('Performing I-Spy comparison...')
156 self
._ispy
.PerformComparison(
158 self
._GetExpectationNameWithVersion
(
159 device_type
, expectation
, chrome_version
, version_file
),
162 def CanRebaselineToTestRun(self
, test_run
):
163 """Returns whether the test run has associated expectations.
166 True if RebaselineToTestRun() can be called for this test run.
168 if test_run
in self
._rebaselineable
_cache
:
170 return self
._cloud
_bucket
.FileExists(
171 ispy_utils
.GetTestRunPath(test_run
, 'rebaseline.txt'))
173 def RebaselineToTestRun(self
, test_run
):
174 """Update the version file to use expectations associated with |test_run|.
177 test_run: The name of the test run to rebaseline.
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
)
186 def _SetTestRunRebaselineable(self
, test_run
, chrome_version
, version_file
):
187 """Writes a JSON file containing the data needed to rebaseline.
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.
195 self
._rebaselineable
_cache
[test_run
] = True
196 self
._cloud
_bucket
.UploadFile(
197 ispy_utils
.GetTestRunPath(test_run
, 'rebaseline.txt'),
199 'version': chrome_version
,
200 'version_file': version_file
}),
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.
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.
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.
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(
226 self
._GetExpectationNameWithVersion
(
227 device_type
, expectation
, chrome_version
, version_file
),