2 # Copyright (c) 2013 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
6 """Runs all the buildbot steps for ChromeDriver except for update/compile."""
15 import platform as platform_module
29 _THIS_DIR = os.path.abspath(os.path.dirname(__file__))
30 GS_CHROMEDRIVER_BUCKET = 'gs://chromedriver'
31 GS_CHROMEDRIVER_DATA_BUCKET = 'gs://chromedriver-data'
32 GS_CONTINUOUS_URL = GS_CHROMEDRIVER_DATA_BUCKET + '/continuous'
33 GS_PREBUILTS_URL = GS_CHROMEDRIVER_DATA_BUCKET + '/prebuilts'
34 GS_SERVER_LOGS_URL = GS_CHROMEDRIVER_DATA_BUCKET + '/server_logs'
36 'http://chromedriver-data.storage.googleapis.com/server_logs')
37 TEST_LOG_FORMAT = '%s_log.json'
39 SCRIPT_DIR = os.path.join(_THIS_DIR, os.pardir, os.pardir, os.pardir, os.pardir,
40 os.pardir, os.pardir, os.pardir, 'scripts')
41 SITE_CONFIG_DIR = os.path.join(_THIS_DIR, os.pardir, os.pardir, os.pardir,
42 os.pardir, os.pardir, os.pardir, os.pardir,
44 sys.path.append(SCRIPT_DIR)
45 sys.path.append(SITE_CONFIG_DIR)
46 from slave import gsutil_download
47 from slave import slave_utils
50 def _ArchivePrebuilts(revision):
51 """Uploads the prebuilts to google storage."""
52 util.MarkBuildStepStart('archive prebuilts')
53 zip_path = util.Zip(os.path.join(chrome_paths.GetBuildDir(['chromedriver']),
55 if slave_utils.GSUtilCopy(
57 '%s/%s' % (GS_PREBUILTS_URL, 'r%s.zip' % revision)):
58 util.MarkBuildStepError()
61 def _ArchiveServerLogs():
62 """Uploads chromedriver server logs to google storage."""
63 util.MarkBuildStepStart('archive chromedriver server logs')
64 for server_log in glob.glob(os.path.join(tempfile.gettempdir(),
66 base_name = os.path.basename(server_log)
67 util.AddLink(base_name, '%s/%s' % (SERVER_LOGS_LINK, base_name))
68 slave_utils.GSUtilCopy(
70 '%s/%s' % (GS_SERVER_LOGS_URL, base_name),
71 mimetype='text/plain')
74 def _DownloadPrebuilts():
75 """Downloads the most recent prebuilts from google storage."""
76 util.MarkBuildStepStart('Download latest chromedriver')
78 zip_path = os.path.join(util.MakeTempDir(), 'build.zip')
79 if gsutil_download.DownloadLatestFile(GS_PREBUILTS_URL, 'r', zip_path):
80 util.MarkBuildStepError()
82 util.Unzip(zip_path, chrome_paths.GetBuildDir(['host_forwarder']))
85 def _GetTestResultsLog(platform):
86 """Gets the test results log for the given platform.
89 A dictionary where the keys are SVN revisions and the values are booleans
90 indicating whether the tests passed.
92 temp_log = tempfile.mkstemp()[1]
93 log_name = TEST_LOG_FORMAT % platform
94 result = slave_utils.GSUtilDownloadFile(
95 '%s/%s' % (GS_CHROMEDRIVER_DATA_BUCKET, log_name), temp_log)
98 with open(temp_log, 'rb') as log_file:
99 json_dict = json.load(log_file)
100 # Workaround for json encoding dictionary keys as strings.
101 return dict([(int(v[0]), v[1]) for v in json_dict.items()])
104 def _PutTestResultsLog(platform, test_results_log):
105 """Pushes the given test results log to google storage."""
106 temp_dir = util.MakeTempDir()
107 log_name = TEST_LOG_FORMAT % platform
108 log_path = os.path.join(temp_dir, log_name)
109 with open(log_path, 'wb') as log_file:
110 json.dump(test_results_log, log_file)
111 if slave_utils.GSUtilCopyFile(log_path, GS_CHROMEDRIVER_DATA_BUCKET):
112 raise Exception('Failed to upload test results log to google storage')
115 def _UpdateTestResultsLog(platform, revision, passed):
116 """Updates the test results log for the given platform.
119 platform: The platform name.
120 revision: The SVN revision number.
121 passed: Boolean indicating whether the tests passed at this revision.
123 assert isinstance(revision, int), 'The revision must be an integer'
124 log = _GetTestResultsLog(platform)
126 del log[min(log.keys())]
127 assert revision not in log, 'Results already exist for revision %s' % revision
128 log[revision] = bool(passed)
129 _PutTestResultsLog(platform, log)
133 """Get the current chromedriver version."""
134 with open(os.path.join(_THIS_DIR, 'VERSION'), 'r') as f:
135 return f.read().strip()
138 def _GetSupportedChromeVersions():
139 """Get the minimum and maximum supported Chrome versions.
142 A tuple of the form (min_version, max_version).
144 # Minimum supported Chrome version is embedded as:
145 # const int kMinimumSupportedChromeVersion[] = {27, 0, 1453, 0};
146 with open(os.path.join(_THIS_DIR, 'chrome', 'version.cc'), 'r') as f:
147 lines = f.readlines()
148 chrome_min_version_line = filter(
149 lambda x: 'kMinimumSupportedChromeVersion' in x, lines)
150 chrome_min_version = chrome_min_version_line[0].split('{')[1].split(',')[0]
151 with open(os.path.join(chrome_paths.GetSrc(), 'chrome', 'VERSION'), 'r') as f:
152 chrome_max_version = f.readlines()[0].split('=')[1].strip()
153 return (chrome_min_version, chrome_max_version)
156 def _RevisionState(test_results_log, revision):
157 """Check the state of tests at a given SVN revision.
159 Considers tests as having passed at a revision if they passed at revisons both
163 test_results_log: A test results log dictionary from _GetTestResultsLog().
164 revision: The revision to check at.
167 'passed', 'failed', or 'unknown'
169 assert isinstance(revision, int), 'The revision must be an integer'
170 keys = sorted(test_results_log.keys())
171 # Return passed if the exact revision passed on Android.
172 if revision in test_results_log:
173 return 'passed' if test_results_log[revision] else 'failed'
174 # Tests were not run on this exact revision on Android.
175 index = bisect.bisect_right(keys, revision)
176 # Tests have not yet run on Android at or above this revision.
177 if index == len(test_results_log):
179 # No log exists for any prior revision, assume it failed.
182 # Return passed if the revisions on both sides passed.
183 if test_results_log[keys[index]] and test_results_log[keys[index - 1]]:
188 def _ArchiveGoodBuild(platform, revision):
189 assert platform != 'android'
190 util.MarkBuildStepStart('archive build')
192 server_name = 'chromedriver'
194 server_name += '.exe'
195 zip_path = util.Zip(os.path.join(chrome_paths.GetBuildDir([server_name]),
198 build_url = '%s/chromedriver_%s_%s.%s.zip' % (
199 GS_CONTINUOUS_URL, platform, _GetVersion(), revision)
200 if slave_utils.GSUtilCopy(zip_path, build_url):
201 util.MarkBuildStepError()
204 def _MaybeRelease(platform):
205 """Releases a release candidate if conditions are right."""
206 assert platform != 'android'
208 # Check if the current version has already been released.
209 result, _ = slave_utils.GSUtilListBucket(
210 '%s/%s/chromedriver_%s*' % (
211 GS_CHROMEDRIVER_BUCKET, _GetVersion(), platform),
216 # Fetch Android test results.
217 android_test_results = _GetTestResultsLog('android')
219 # Fetch release candidates.
220 result, output = slave_utils.GSUtilListBucket(
221 '%s/chromedriver_%s_%s*' % (
222 GS_CONTINUOUS_URL, platform, _GetVersion()),
224 assert result == 0 and output, 'No release candidates found'
225 candidates = [b.split('/')[-1] for b in output.strip().split('\n')]
227 # Release the first candidate build that passed Android, if any.
228 for candidate in candidates:
229 if not candidate.startswith('chromedriver_%s' % platform):
231 revision = candidate.split('.')[2]
232 android_result = _RevisionState(android_test_results, int(revision))
233 if android_result == 'failed':
234 print 'Android tests did not pass at revision', revision
235 elif android_result == 'passed':
236 print 'Android tests passed at revision', revision
237 _Release('%s/%s' % (GS_CONTINUOUS_URL, candidate), platform)
240 print 'Android tests have not run at a revision as recent as', revision
243 def _Release(build, platform):
244 """Releases the given candidate build."""
245 release_name = 'chromedriver_%s.zip' % platform
246 util.MarkBuildStepStart('releasing %s' % release_name)
247 slave_utils.GSUtilCopy(
248 build, '%s/%s/%s' % (GS_CHROMEDRIVER_BUCKET, _GetVersion(), release_name))
250 _MaybeUploadReleaseNotes()
253 def _MaybeUploadReleaseNotes():
254 """Upload release notes if conditions are right."""
255 # Check if the current version has already been released.
256 version = _GetVersion()
257 notes_name = 'notes.txt'
258 notes_url = '%s/%s/%s' % (GS_CHROMEDRIVER_BUCKET, version, notes_name)
259 prev_version = '.'.join([version.split('.')[0],
260 str(int(version.split('.')[1]) - 1)])
261 prev_notes_url = '%s/%s/%s' % (
262 GS_CHROMEDRIVER_BUCKET, prev_version, notes_name)
264 result, _ = slave_utils.GSUtilListBucket(notes_url, [])
269 query = ('https://code.google.com/p/chromedriver/issues/csv?'
270 'q=status%3AToBeReleased&colspec=ID%20Summary')
271 issues = StringIO.StringIO(urllib2.urlopen(query).read().split('\n', 1)[1])
272 for issue in csv.reader(issues):
278 fixed_issues += ['Resolved issue %s: %s [%s]' % (id, desc, labels)]
281 temp_notes_fname = tempfile.mkstemp()[1]
282 if not slave_utils.GSUtilDownloadFile(prev_notes_url, temp_notes_fname):
283 with open(temp_notes_fname, 'rb') as f:
286 new_notes = '----------ChromeDriver v%s (%s)----------\n%s\n%s\n\n%s' % (
287 version, datetime.date.today().isoformat(),
288 'Supports Chrome v%s-%s' % _GetSupportedChromeVersions(),
289 '\n'.join(fixed_issues),
291 with open(temp_notes_fname, 'w') as f:
294 if slave_utils.GSUtilCopy(temp_notes_fname, notes_url, mimetype='text/plain'):
295 util.MarkBuildStepError()
305 cmd = ['taskkill', '/F', '/IM']
307 cmd = ['killall', '-9']
308 cmd.append(chrome_map[util.GetPlatformName()])
313 tmp_dir = tempfile.gettempdir()
314 print 'cleaning temp directory:', tmp_dir
315 for file_name in os.listdir(tmp_dir):
316 file_path = os.path.join(tmp_dir, file_name)
317 if os.path.isdir(file_path):
318 print 'deleting sub-directory', file_path
319 shutil.rmtree(file_path, True)
320 if file_name.startswith('chromedriver_'):
321 print 'deleting file', file_path
325 def _WaitForLatestSnapshot(revision):
326 util.MarkBuildStepStart('wait_for_snapshot')
328 snapshot_revision = archive.GetLatestRevision(archive.Site.SNAPSHOT)
329 if int(snapshot_revision) >= int(revision):
331 util.PrintAndFlush('Waiting for snapshot >= %s, found %s' %
332 (revision, snapshot_revision))
334 util.PrintAndFlush('Got snapshot revision %s' % snapshot_revision)
338 parser = optparse.OptionParser()
340 '', '--android-packages',
341 help='Comma separated list of application package names, '
342 'if running tests on Android.')
344 '-r', '--revision', type='int', help='Chromium revision')
345 parser.add_option('', '--update-log', action='store_true',
346 help='Update the test results log (only applicable to Android)')
347 options, _ = parser.parse_args()
350 if util.IsLinux() and platform_module.architecture()[0] == '64bit':
352 platform = '%s%s' % (util.GetPlatformName(), bitness)
353 if options.android_packages:
356 if platform != 'android':
360 if platform == 'android':
361 if not options.revision and options.update_log:
362 parser.error('Must supply a --revision with --update-log')
365 if not options.revision:
366 parser.error('Must supply a --revision')
367 if platform == 'linux64':
368 _ArchivePrebuilts(options.revision)
369 _WaitForLatestSnapshot(options.revision)
373 os.path.join(_THIS_DIR, 'test', 'run_all_tests.py'),
375 if platform == 'android':
376 cmd.append('--android-packages=' + options.android_packages)
378 passed = (util.RunCommand(cmd) == 0)
382 if platform == 'android':
383 if options.update_log:
384 util.MarkBuildStepStart('update test result log')
385 _UpdateTestResultsLog(platform, options.revision, passed)
387 _ArchiveGoodBuild(platform, options.revision)
388 _MaybeRelease(platform)
391 if __name__ == '__main__':