blob: 62feaa9381ca20577cd0e1786fe5e97100bb2e09 [file] [log] [blame]
borenetdc89ca52014-10-17 07:37:05 -07001#!/usr/bin/env python
2# Copyright (c) 2012 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.
5
6"""Archives or replays webpages and creates SKPs in a Google Storage location.
7
8To archive webpages and store SKP files (archives should be rarely updated):
9
kkinnunenfcf35c52014-12-03 05:51:24 -080010cd skia
kkinnunenb4ee7ea2015-03-31 00:18:26 -070011python tools/skp/webpages_playback.py --data_store=gs://rmistry --record \
borenetdc89ca52014-10-17 07:37:05 -070012--page_sets=all --skia_tools=/home/default/trunk/out/Debug/ \
13--browser_executable=/tmp/chromium/out/Release/chrome
14
kkinnunenb4ee7ea2015-03-31 00:18:26 -070015The above command uses Google Storage bucket 'rmistry' to download needed files.
borenetdc89ca52014-10-17 07:37:05 -070016
17To replay archived webpages and re-generate SKP files (should be run whenever
18SkPicture.PICTURE_VERSION changes):
19
kkinnunenfcf35c52014-12-03 05:51:24 -080020cd skia
kkinnunenb4ee7ea2015-03-31 00:18:26 -070021python tools/skp/webpages_playback.py --data_store=gs://rmistry \
borenetdc89ca52014-10-17 07:37:05 -070022--page_sets=all --skia_tools=/home/default/trunk/out/Debug/ \
23--browser_executable=/tmp/chromium/out/Release/chrome
24
25
26Specify the --page_sets flag (default value is 'all') to pick a list of which
27webpages should be archived and/or replayed. Eg:
28
kkinnunenfcf35c52014-12-03 05:51:24 -080029--page_sets=tools/skp/page_sets/skia_yahooanswers_desktop.py,\
30tools/skp/page_sets/skia_googlecalendar_nexus10.py
borenetdc89ca52014-10-17 07:37:05 -070031
32The --browser_executable flag should point to the browser binary you want to use
33to capture archives and/or capture SKP files. Majority of the time it should be
34a newly built chrome binary.
35
kkinnunenb4ee7ea2015-03-31 00:18:26 -070036The --data_store flag controls where the needed artifacts, such as
37credential files, are downloaded from. It also controls where the
38generated artifacts, such as recorded webpages and resulting skp renderings,
39are uploaded to. URLs with scheme 'gs://' use Google Storage. Otherwise
40use local filesystem.
41
42The --upload=True flag means generated artifacts will be
43uploaded or copied to the location specified by --data_store. (default value is
44False if not specified).
borenetdc89ca52014-10-17 07:37:05 -070045
46The --non-interactive flag controls whether the script will prompt the user
47(default value is False if not specified).
48
49The --skia_tools flag if specified will allow this script to run
50debugger, render_pictures, and render_pdfs on the captured
51SKP(s). The tools are run after all SKPs are succesfully captured to make sure
52they can be added to the buildbots with no breakages.
borenetdc89ca52014-10-17 07:37:05 -070053"""
54
55import glob
56import optparse
57import os
58import posixpath
59import shutil
60import subprocess
61import sys
62import tempfile
63import time
64import traceback
65
borenetdc89ca52014-10-17 07:37:05 -070066
67ROOT_PLAYBACK_DIR_NAME = 'playback'
68SKPICTURES_DIR_NAME = 'skps'
69
borenet644638c2016-10-10 05:57:18 -070070GS_PREFIX = 'gs://'
71
rmistry0575c492016-02-01 10:27:05 -080072PARTNERS_GS_BUCKET = 'gs://chrome-partner-telemetry'
borenetdc89ca52014-10-17 07:37:05 -070073
74# Local archive and SKP directories.
borenetdc89ca52014-10-17 07:37:05 -070075LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR = os.path.join(
76 os.path.abspath(os.path.dirname(__file__)), 'page_sets', 'data')
77TMP_SKP_DIR = tempfile.mkdtemp()
78
rmistryf802f322014-10-22 05:04:43 -070079# Location of the credentials.json file and the string that represents missing
80# passwords.
81CREDENTIALS_FILE_PATH = os.path.join(
82 os.path.abspath(os.path.dirname(__file__)), 'page_sets', 'data',
83 'credentials.json'
84)
85
borenetdc89ca52014-10-17 07:37:05 -070086# Name of the SKP benchmark
87SKP_BENCHMARK = 'skpicture_printer'
88
89# The max base name length of Skp files.
90MAX_SKP_BASE_NAME_LEN = 31
91
92# Dictionary of device to platform prefixes for SKP files.
93DEVICE_TO_PLATFORM_PREFIX = {
94 'desktop': 'desk',
95 'galaxynexus': 'mobi',
96 'nexus10': 'tabl'
97}
98
99# How many times the record_wpr binary should be retried.
100RETRY_RECORD_WPR_COUNT = 5
borenet78399152014-10-17 12:15:46 -0700101# How many times the run_benchmark binary should be retried.
borenetdc89ca52014-10-17 07:37:05 -0700102RETRY_RUN_MEASUREMENT_COUNT = 5
103
rmistryf802f322014-10-22 05:04:43 -0700104# Location of the credentials.json file in Google Storage.
borenet75358672016-10-10 13:20:49 -0700105CREDENTIALS_GS_PATH = 'playback/credentials/credentials.json'
rmistryf802f322014-10-22 05:04:43 -0700106
borenetdc89ca52014-10-17 07:37:05 -0700107X11_DISPLAY = os.getenv('DISPLAY', ':0')
108
rmistry49d093c2015-03-31 05:04:29 -0700109# Path to Chromium's page sets.
110CHROMIUM_PAGE_SETS_PATH = os.path.join('tools', 'perf', 'page_sets')
111
112# Dictionary of supported Chromium page sets to their file prefixes.
113CHROMIUM_PAGE_SETS_TO_PREFIX = {
rmistry5af9a372015-03-31 06:22:55 -0700114 'key_mobile_sites_smooth.py': 'keymobi',
rmistry2a3c8492015-03-31 10:59:15 -0700115 'top_25_smooth.py': 'top25desk',
rmistry49d093c2015-03-31 05:04:29 -0700116}
117
118
kkinnunene75d2d22014-12-03 04:38:46 -0800119def remove_prefix(s, prefix):
120 if s.startswith(prefix):
121 return s[len(prefix):]
122 return s
borenetdc89ca52014-10-17 07:37:05 -0700123
rmistry49d093c2015-03-31 05:04:29 -0700124
borenetdc89ca52014-10-17 07:37:05 -0700125class SkPicturePlayback(object):
126 """Class that archives or replays webpages and creates SKPs."""
127
128 def __init__(self, parse_options):
129 """Constructs a SkPicturePlayback BuildStep instance."""
130 assert parse_options.browser_executable, 'Must specify --browser_executable'
131 self._browser_executable = parse_options.browser_executable
rmistryaa31ee72015-04-23 12:47:33 -0700132 self._browser_args = '--disable-setuid-sandbox'
133 if parse_options.browser_extra_args:
134 self._browser_args = '%s %s' % (
135 self._browser_args, parse_options.browser_extra_args)
borenetdc89ca52014-10-17 07:37:05 -0700136
rmistry49d093c2015-03-31 05:04:29 -0700137 self._chrome_page_sets_path = os.path.join(parse_options.chrome_src_path,
138 CHROMIUM_PAGE_SETS_PATH)
borenetdc89ca52014-10-17 07:37:05 -0700139 self._all_page_sets_specified = parse_options.page_sets == 'all'
140 self._page_sets = self._ParsePageSets(parse_options.page_sets)
141
borenetdc89ca52014-10-17 07:37:05 -0700142 self._record = parse_options.record
143 self._skia_tools = parse_options.skia_tools
144 self._non_interactive = parse_options.non_interactive
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700145 self._upload = parse_options.upload
rmistryaa31ee72015-04-23 12:47:33 -0700146 self._skp_prefix = parse_options.skp_prefix
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700147 data_store_location = parse_options.data_store
borenet644638c2016-10-10 05:57:18 -0700148 if data_store_location.startswith(GS_PREFIX):
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700149 self.gs = GoogleStorageDataStore(data_store_location)
150 else:
151 self.gs = LocalFileSystemDataStore(data_store_location)
rmistry0575c492016-02-01 10:27:05 -0800152 self._upload_to_partner_bucket = parse_options.upload_to_partner_bucket
borenetdc89ca52014-10-17 07:37:05 -0700153 self._alternate_upload_dir = parse_options.alternate_upload_dir
borenetdc89ca52014-10-17 07:37:05 -0700154 self._telemetry_binaries_dir = os.path.join(parse_options.chrome_src_path,
155 'tools', 'perf')
rmistryafaf4962016-02-27 10:04:57 -0800156 self._catapult_dir = os.path.join(parse_options.chrome_src_path,
157 'third_party', 'catapult')
borenetdc89ca52014-10-17 07:37:05 -0700158
159 self._local_skp_dir = os.path.join(
160 parse_options.output_dir, ROOT_PLAYBACK_DIR_NAME, SKPICTURES_DIR_NAME)
161 self._local_record_webpages_archive_dir = os.path.join(
162 parse_options.output_dir, ROOT_PLAYBACK_DIR_NAME, 'webpages_archive')
163
164 # List of SKP files generated by this script.
165 self._skp_files = []
166
167 def _ParsePageSets(self, page_sets):
168 if not page_sets:
169 raise ValueError('Must specify at least one page_set!')
170 elif self._all_page_sets_specified:
171 # Get everything from the page_sets directory.
172 page_sets_dir = os.path.join(os.path.abspath(os.path.dirname(__file__)),
173 'page_sets')
174 ps = [os.path.join(page_sets_dir, page_set)
175 for page_set in os.listdir(page_sets_dir)
176 if not os.path.isdir(os.path.join(page_sets_dir, page_set)) and
177 page_set.endswith('.py')]
rmistry49d093c2015-03-31 05:04:29 -0700178 chromium_ps = [
179 os.path.join(self._chrome_page_sets_path, cr_page_set)
180 for cr_page_set in CHROMIUM_PAGE_SETS_TO_PREFIX]
181 ps.extend(chromium_ps)
borenetdc89ca52014-10-17 07:37:05 -0700182 elif '*' in page_sets:
183 # Explode and return the glob.
184 ps = glob.glob(page_sets)
185 else:
186 ps = page_sets.split(',')
187 ps.sort()
188 return ps
189
rmistry49d093c2015-03-31 05:04:29 -0700190 def _IsChromiumPageSet(self, page_set):
191 """Returns true if the specified page set is a Chromium page set."""
192 return page_set.startswith(self._chrome_page_sets_path)
193
borenetdc89ca52014-10-17 07:37:05 -0700194 def Run(self):
195 """Run the SkPicturePlayback BuildStep."""
196
rmistryf802f322014-10-22 05:04:43 -0700197 # Download the credentials file if it was not previously downloaded.
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700198 if not os.path.isfile(CREDENTIALS_FILE_PATH):
199 # Download the credentials.json file from Google Storage.
200 self.gs.download_file(CREDENTIALS_GS_PATH, CREDENTIALS_FILE_PATH)
201
202 if not os.path.isfile(CREDENTIALS_FILE_PATH):
203 print """\n\nCould not locate credentials file in the storage.
204 Please create a %s file that contains:
rmistryf802f322014-10-22 05:04:43 -0700205 {
206 "google": {
207 "username": "google_testing_account_username",
208 "password": "google_testing_account_password"
209 },
210 "facebook": {
211 "username": "facebook_testing_account_username",
212 "password": "facebook_testing_account_password"
213 }
214 }\n\n""" % CREDENTIALS_FILE_PATH
215 raw_input("Please press a key when you are ready to proceed...")
rmistryf802f322014-10-22 05:04:43 -0700216
borenetdc89ca52014-10-17 07:37:05 -0700217 # Delete any left over data files in the data directory.
218 for archive_file in glob.glob(
219 os.path.join(LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR, 'skia_*')):
220 os.remove(archive_file)
221
borenetdc89ca52014-10-17 07:37:05 -0700222 # Create the required local storage directories.
223 self._CreateLocalStorageDirs()
224
225 # Start the timer.
226 start_time = time.time()
227
228 # Loop through all page_sets.
229 for page_set in self._page_sets:
230
rmistry7620bf02014-10-27 06:42:11 -0700231 page_set_basename = os.path.basename(page_set).split('.')[0]
232 page_set_json_name = page_set_basename + '.json'
Ravi Mistry0d056ba2017-08-02 11:21:58 -0400233 wpr_data_file = (
234 page_set.split(os.path.sep)[-1].split('.')[0] + '_000.wprgo')
rmistry7620bf02014-10-27 06:42:11 -0700235 page_set_dir = os.path.dirname(page_set)
borenetdc89ca52014-10-17 07:37:05 -0700236
rmistry49d093c2015-03-31 05:04:29 -0700237 if self._IsChromiumPageSet(page_set):
238 print 'Using Chromium\'s captured archives for Chromium\'s page sets.'
239 elif self._record:
borenetdc89ca52014-10-17 07:37:05 -0700240 # Create an archive of the specified webpages if '--record=True' is
241 # specified.
242 record_wpr_cmd = (
rmistryafaf4962016-02-27 10:04:57 -0800243 'PYTHONPATH=%s:%s:$PYTHONPATH' % (page_set_dir, self._catapult_dir),
borenetdc89ca52014-10-17 07:37:05 -0700244 'DISPLAY=%s' % X11_DISPLAY,
245 os.path.join(self._telemetry_binaries_dir, 'record_wpr'),
rmistryaa31ee72015-04-23 12:47:33 -0700246 '--extra-browser-args="%s"' % self._browser_args,
borenetdc89ca52014-10-17 07:37:05 -0700247 '--browser=exact',
248 '--browser-executable=%s' % self._browser_executable,
Ravi Mistry0d056ba2017-08-02 11:21:58 -0400249 '--use-wpr-go',
rmistry7620bf02014-10-27 06:42:11 -0700250 '%s_page_set' % page_set_basename,
251 '--page-set-base-dir=%s' % page_set_dir
borenetdc89ca52014-10-17 07:37:05 -0700252 )
253 for _ in range(RETRY_RECORD_WPR_COUNT):
rmistry0ec28af2014-10-28 14:25:17 -0700254 try:
borenet644638c2016-10-10 05:57:18 -0700255 subprocess.check_call(' '.join(record_wpr_cmd), shell=True)
kkinnunenf9310fe2015-03-29 22:33:16 -0700256
Ravi Mistry863ffef2017-07-18 13:13:53 -0400257 # Copy over the created archive into the local webpages archive
kkinnunenf9310fe2015-03-29 22:33:16 -0700258 # directory.
Ravi Mistry863ffef2017-07-18 13:13:53 -0400259 shutil.copy(
kkinnunenf9310fe2015-03-29 22:33:16 -0700260 os.path.join(LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR, wpr_data_file),
261 self._local_record_webpages_archive_dir)
Ravi Mistry863ffef2017-07-18 13:13:53 -0400262 shutil.copy(
kkinnunenf9310fe2015-03-29 22:33:16 -0700263 os.path.join(LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR,
264 page_set_json_name),
265 self._local_record_webpages_archive_dir)
266
borenetdc89ca52014-10-17 07:37:05 -0700267 # Break out of the retry loop since there were no errors.
268 break
rmistry0ec28af2014-10-28 14:25:17 -0700269 except Exception:
270 # There was a failure continue with the loop.
271 traceback.print_exc()
borenetdc89ca52014-10-17 07:37:05 -0700272 else:
273 # If we get here then record_wpr did not succeed and thus did not
274 # break out of the loop.
275 raise Exception('record_wpr failed for page_set: %s' % page_set)
276
277 else:
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700278 # Get the webpages archive so that it can be replayed.
279 self._DownloadWebpagesArchive(wpr_data_file, page_set_json_name)
borenetdc89ca52014-10-17 07:37:05 -0700280
borenet78399152014-10-17 12:15:46 -0700281 run_benchmark_cmd = (
rmistryafaf4962016-02-27 10:04:57 -0800282 'PYTHONPATH=%s:%s:$PYTHONPATH' % (page_set_dir, self._catapult_dir),
borenetdc89ca52014-10-17 07:37:05 -0700283 'DISPLAY=%s' % X11_DISPLAY,
Ravi Mistry57d69652016-09-08 09:08:44 -0400284 'timeout', '1800',
borenet78399152014-10-17 12:15:46 -0700285 os.path.join(self._telemetry_binaries_dir, 'run_benchmark'),
rmistryaa31ee72015-04-23 12:47:33 -0700286 '--extra-browser-args="%s"' % self._browser_args,
borenetdc89ca52014-10-17 07:37:05 -0700287 '--browser=exact',
288 '--browser-executable=%s' % self._browser_executable,
289 SKP_BENCHMARK,
rmistry7620bf02014-10-27 06:42:11 -0700290 '--page-set-name=%s' % page_set_basename,
rmistryf802f322014-10-22 05:04:43 -0700291 '--page-set-base-dir=%s' % page_set_dir,
292 '--skp-outdir=%s' % TMP_SKP_DIR,
293 '--also-run-disabled-tests'
borenetdc89ca52014-10-17 07:37:05 -0700294 )
borenetdc89ca52014-10-17 07:37:05 -0700295
296 for _ in range(RETRY_RUN_MEASUREMENT_COUNT):
297 try:
298 print '\n\n=======Capturing SKP of %s=======\n\n' % page_set
borenet644638c2016-10-10 05:57:18 -0700299 subprocess.check_call(' '.join(run_benchmark_cmd), shell=True)
300 except subprocess.CalledProcessError:
borenetdc89ca52014-10-17 07:37:05 -0700301 # skpicture_printer sometimes fails with AssertionError but the
302 # captured SKP is still valid. This is a known issue.
303 pass
304
borenetdc89ca52014-10-17 07:37:05 -0700305 # Rename generated SKP files into more descriptive names.
306 try:
307 self._RenameSkpFiles(page_set)
308 # Break out of the retry loop since there were no errors.
309 break
310 except Exception:
311 # There was a failure continue with the loop.
312 traceback.print_exc()
313 print '\n\n=======Retrying %s=======\n\n' % page_set
314 time.sleep(10)
315 else:
borenet78399152014-10-17 12:15:46 -0700316 # If we get here then run_benchmark did not succeed and thus did not
borenetdc89ca52014-10-17 07:37:05 -0700317 # break out of the loop.
borenet78399152014-10-17 12:15:46 -0700318 raise Exception('run_benchmark failed for page_set: %s' % page_set)
borenetdc89ca52014-10-17 07:37:05 -0700319
borenetdc89ca52014-10-17 07:37:05 -0700320 print '\n\n=======Capturing SKP files took %s seconds=======\n\n' % (
321 time.time() - start_time)
322
323 if self._skia_tools:
324 render_pictures_cmd = [
325 os.path.join(self._skia_tools, 'render_pictures'),
326 '-r', self._local_skp_dir
327 ]
328 render_pdfs_cmd = [
329 os.path.join(self._skia_tools, 'render_pdfs'),
kkinnunen3a6aa862014-12-03 04:22:06 -0800330 '-r', self._local_skp_dir
borenetdc89ca52014-10-17 07:37:05 -0700331 ]
332
333 for tools_cmd in (render_pictures_cmd, render_pdfs_cmd):
334 print '\n\n=======Running %s=======' % ' '.join(tools_cmd)
borenet644638c2016-10-10 05:57:18 -0700335 subprocess.check_call(tools_cmd)
borenetdc89ca52014-10-17 07:37:05 -0700336
337 if not self._non_interactive:
338 print '\n\n=======Running debugger======='
339 os.system('%s %s' % (os.path.join(self._skia_tools, 'debugger'),
kkinnunen960fb502014-12-03 06:18:12 -0800340 self._local_skp_dir))
borenetdc89ca52014-10-17 07:37:05 -0700341
342 print '\n\n'
343
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700344 if self._upload:
345 print '\n\n=======Uploading to %s=======\n\n' % self.gs.target_type()
borenetdc89ca52014-10-17 07:37:05 -0700346 # Copy the directory structure in the root directory into Google Storage.
347 dest_dir_name = ROOT_PLAYBACK_DIR_NAME
348 if self._alternate_upload_dir:
349 dest_dir_name = self._alternate_upload_dir
350
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700351 self.gs.upload_dir_contents(
borenet644638c2016-10-10 05:57:18 -0700352 self._local_skp_dir, dest_dir=dest_dir_name)
borenetdc89ca52014-10-17 07:37:05 -0700353
354 print '\n\n=======New SKPs have been uploaded to %s =======\n\n' % (
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700355 posixpath.join(self.gs.target_name(), dest_dir_name,
356 SKPICTURES_DIR_NAME))
rmistry0575c492016-02-01 10:27:05 -0800357
borenetdc89ca52014-10-17 07:37:05 -0700358 else:
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700359 print '\n\n=======Not Uploading to %s=======\n\n' % self.gs.target_type()
borenetdc89ca52014-10-17 07:37:05 -0700360 print 'Generated resources are available in %s\n\n' % (
borenet1413d522016-08-08 06:26:00 -0700361 self._local_skp_dir)
borenetdc89ca52014-10-17 07:37:05 -0700362
borenet28698472016-07-25 05:18:15 -0700363 if self._upload_to_partner_bucket:
364 print '\n\n=======Uploading to Partner bucket %s =======\n\n' % (
365 PARTNERS_GS_BUCKET)
366 partner_gs = GoogleStorageDataStore(PARTNERS_GS_BUCKET)
367 partner_gs.delete_path(SKPICTURES_DIR_NAME)
borenet1413d522016-08-08 06:26:00 -0700368 print 'Uploading %s to %s' % (self._local_skp_dir, SKPICTURES_DIR_NAME)
borenet644638c2016-10-10 05:57:18 -0700369 partner_gs.upload_dir_contents(self._local_skp_dir, SKPICTURES_DIR_NAME)
borenet28698472016-07-25 05:18:15 -0700370 print '\n\n=======New SKPs have been uploaded to %s =======\n\n' % (
371 posixpath.join(partner_gs.target_name(), SKPICTURES_DIR_NAME))
372
borenetdc89ca52014-10-17 07:37:05 -0700373 return 0
374
rmistry49d093c2015-03-31 05:04:29 -0700375 def _GetSkiaSkpFileName(self, page_set):
376 """Returns the SKP file name for Skia page sets."""
377 # /path/to/skia_yahooanswers_desktop.py -> skia_yahooanswers_desktop.py
378 ps_filename = os.path.basename(page_set)
379 # skia_yahooanswers_desktop.py -> skia_yahooanswers_desktop
380 ps_basename, _ = os.path.splitext(ps_filename)
381 # skia_yahooanswers_desktop -> skia, yahooanswers, desktop
382 _, page_name, device = ps_basename.split('_')
383 basename = '%s_%s' % (DEVICE_TO_PLATFORM_PREFIX[device], page_name)
384 return basename[:MAX_SKP_BASE_NAME_LEN] + '.skp'
385
386 def _GetChromiumSkpFileName(self, page_set, site):
387 """Returns the SKP file name for Chromium page sets."""
388 # /path/to/http___mobile_news_sandbox_pt0 -> http___mobile_news_sandbox_pt0
389 _, webpage = os.path.split(site)
390 # http___mobile_news_sandbox_pt0 -> mobile_news_sandbox_pt0
rmistry80bd3ae2015-04-03 08:22:51 -0700391 for prefix in ('http___', 'https___', 'www_'):
rmistry2a3c8492015-03-31 10:59:15 -0700392 if webpage.startswith(prefix):
393 webpage = webpage[len(prefix):]
rmistry49d093c2015-03-31 05:04:29 -0700394 # /path/to/skia_yahooanswers_desktop.py -> skia_yahooanswers_desktop.py
395 ps_filename = os.path.basename(page_set)
396 # http___mobile_news_sandbox -> pagesetprefix_http___mobile_news_sandbox
397 basename = '%s_%s' % (CHROMIUM_PAGE_SETS_TO_PREFIX[ps_filename], webpage)
398 return basename[:MAX_SKP_BASE_NAME_LEN] + '.skp'
399
borenetdc89ca52014-10-17 07:37:05 -0700400 def _RenameSkpFiles(self, page_set):
401 """Rename generated SKP files into more descriptive names.
402
403 Look into the subdirectory of TMP_SKP_DIR and find the most interesting
404 .skp in there to be this page_set's representative .skp.
405 """
borenetdc89ca52014-10-17 07:37:05 -0700406 subdirs = glob.glob(os.path.join(TMP_SKP_DIR, '*'))
borenetdc89ca52014-10-17 07:37:05 -0700407 for site in subdirs:
rmistry49d093c2015-03-31 05:04:29 -0700408 if self._IsChromiumPageSet(page_set):
409 filename = self._GetChromiumSkpFileName(page_set, site)
410 else:
411 filename = self._GetSkiaSkpFileName(page_set)
rmistry80bd3ae2015-04-03 08:22:51 -0700412 filename = filename.lower()
rmistry49d093c2015-03-31 05:04:29 -0700413
rmistryaa31ee72015-04-23 12:47:33 -0700414 if self._skp_prefix:
415 filename = '%s%s' % (self._skp_prefix, filename)
416
borenetdc89ca52014-10-17 07:37:05 -0700417 # We choose the largest .skp as the most likely to be interesting.
418 largest_skp = max(glob.glob(os.path.join(site, '*.skp')),
419 key=lambda path: os.stat(path).st_size)
420 dest = os.path.join(self._local_skp_dir, filename)
421 print 'Moving', largest_skp, 'to', dest
422 shutil.move(largest_skp, dest)
423 self._skp_files.append(filename)
424 shutil.rmtree(site)
425
426 def _CreateLocalStorageDirs(self):
427 """Creates required local storage directories for this script."""
428 for d in (self._local_record_webpages_archive_dir,
429 self._local_skp_dir):
430 if os.path.exists(d):
431 shutil.rmtree(d)
432 os.makedirs(d)
433
rmistry7620bf02014-10-27 06:42:11 -0700434 def _DownloadWebpagesArchive(self, wpr_data_file, page_set_json_name):
borenetdc89ca52014-10-17 07:37:05 -0700435 """Downloads the webpages archive and its required page set from GS."""
436 wpr_source = posixpath.join(ROOT_PLAYBACK_DIR_NAME, 'webpages_archive',
437 wpr_data_file)
438 page_set_source = posixpath.join(ROOT_PLAYBACK_DIR_NAME,
439 'webpages_archive',
rmistry7620bf02014-10-27 06:42:11 -0700440 page_set_json_name)
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700441 gs = self.gs
442 if (gs.does_storage_object_exist(wpr_source) and
443 gs.does_storage_object_exist(page_set_source)):
444 gs.download_file(wpr_source,
borenetdc89ca52014-10-17 07:37:05 -0700445 os.path.join(LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR,
446 wpr_data_file))
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700447 gs.download_file(page_set_source,
borenetdc89ca52014-10-17 07:37:05 -0700448 os.path.join(LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR,
rmistry7620bf02014-10-27 06:42:11 -0700449 page_set_json_name))
borenetdc89ca52014-10-17 07:37:05 -0700450 else:
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700451 raise Exception('%s and %s do not exist in %s!' % (gs.target_type(),
452 wpr_source, page_set_source))
borenetdc89ca52014-10-17 07:37:05 -0700453
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700454class DataStore:
455 """An abstract base class for uploading recordings to a data storage.
456 The interface emulates the google storage api."""
457 def target_name(self):
458 raise NotImplementedError()
459 def target_type(self):
460 raise NotImplementedError()
borenet644638c2016-10-10 05:57:18 -0700461 def does_storage_object_exist(self, name):
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700462 raise NotImplementedError()
borenet644638c2016-10-10 05:57:18 -0700463 def download_file(self, name, local_path):
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700464 raise NotImplementedError()
borenet644638c2016-10-10 05:57:18 -0700465 def upload_dir_contents(self, source_dir, dest_dir):
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700466 raise NotImplementedError()
467
borenet644638c2016-10-10 05:57:18 -0700468
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700469class GoogleStorageDataStore(DataStore):
470 def __init__(self, data_store_url):
borenet1cebf7b2016-10-10 08:14:11 -0700471 self._url = data_store_url.rstrip('/')
borenet644638c2016-10-10 05:57:18 -0700472
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700473 def target_name(self):
borenet644638c2016-10-10 05:57:18 -0700474 return self._url
475
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700476 def target_type(self):
477 return 'Google Storage'
borenet644638c2016-10-10 05:57:18 -0700478
479 def does_storage_object_exist(self, name):
480 try:
481 output = subprocess.check_output([
482 'gsutil', 'ls', '/'.join((self._url, name))])
483 except subprocess.CalledProcessError:
484 return False
borenet75358672016-10-10 13:20:49 -0700485 if len(output.splitlines()) != 1:
borenet644638c2016-10-10 05:57:18 -0700486 return False
487 return True
488
rmistryc33c79c2016-02-03 04:27:54 -0800489 def delete_path(self, path):
borenet77874632016-10-17 05:12:10 -0700490 subprocess.check_call(['gsutil', 'rm', '-r', '/'.join((self._url, path))])
borenet644638c2016-10-10 05:57:18 -0700491
492 def download_file(self, name, local_path):
493 subprocess.check_call([
494 'gsutil', 'cp', '/'.join((self._url, name)), local_path])
495
496 def upload_dir_contents(self, source_dir, dest_dir):
497 subprocess.check_call([
borenet27f93e02016-10-17 08:09:33 -0700498 'gsutil', 'cp', '-r', source_dir, '/'.join((self._url, dest_dir))])
borenet644638c2016-10-10 05:57:18 -0700499
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700500
501class LocalFileSystemDataStore(DataStore):
502 def __init__(self, data_store_location):
503 self._base_dir = data_store_location
504 def target_name(self):
505 return self._base_dir
506 def target_type(self):
507 return self._base_dir
borenet644638c2016-10-10 05:57:18 -0700508 def does_storage_object_exist(self, name):
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700509 return os.path.isfile(os.path.join(self._base_dir, name))
rmistryc33c79c2016-02-03 04:27:54 -0800510 def delete_path(self, path):
511 shutil.rmtree(path)
borenet644638c2016-10-10 05:57:18 -0700512 def download_file(self, name, local_path):
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700513 shutil.copyfile(os.path.join(self._base_dir, name), local_path)
borenet644638c2016-10-10 05:57:18 -0700514 def upload_dir_contents(self, source_dir, dest_dir):
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700515 def copytree(source_dir, dest_dir):
516 if not os.path.exists(dest_dir):
517 os.makedirs(dest_dir)
518 for item in os.listdir(source_dir):
519 source = os.path.join(source_dir, item)
520 dest = os.path.join(dest_dir, item)
521 if os.path.isdir(source):
522 copytree(source, dest)
523 else:
524 shutil.copy2(source, dest)
525 copytree(source_dir, os.path.join(self._base_dir, dest_dir))
borenetdc89ca52014-10-17 07:37:05 -0700526
527if '__main__' == __name__:
528 option_parser = optparse.OptionParser()
529 option_parser.add_option(
530 '', '--page_sets',
531 help='Specifies the page sets to use to archive. Supports globs.',
532 default='all')
533 option_parser.add_option(
borenetdc89ca52014-10-17 07:37:05 -0700534 '', '--record', action='store_true',
535 help='Specifies whether a new website archive should be created.',
536 default=False)
537 option_parser.add_option(
borenetdc89ca52014-10-17 07:37:05 -0700538 '', '--skia_tools',
539 help=('Path to compiled Skia executable tools. '
540 'render_pictures/render_pdfs is run on the set '
541 'after all SKPs are captured. If the script is run without '
542 '--non-interactive then the debugger is also run at the end. Debug '
543 'builds are recommended because they seem to catch more failures '
544 'than Release builds.'),
545 default=None)
546 option_parser.add_option(
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700547 '', '--upload', action='store_true',
548 help=('Uploads to Google Storage or copies to local filesystem storage '
549 ' if this is True.'),
borenetdc89ca52014-10-17 07:37:05 -0700550 default=False)
551 option_parser.add_option(
rmistry0575c492016-02-01 10:27:05 -0800552 '', '--upload_to_partner_bucket', action='store_true',
553 help=('Uploads SKPs to the chrome-partner-telemetry Google Storage '
554 'bucket if true.'),
555 default=False)
556 option_parser.add_option(
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700557 '', '--data_store',
558 help=('The location of the file storage to use to download and upload '
559 'files. Can be \'gs://<bucket>\' for Google Storage, or '
560 'a directory for local filesystem storage'),
boreneta86952a2016-10-05 08:23:04 -0700561 default='gs://skia-skps')
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700562 option_parser.add_option(
borenetdc89ca52014-10-17 07:37:05 -0700563 '', '--alternate_upload_dir',
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700564 help= ('Uploads to a different directory in Google Storage or local '
565 'storage if this flag is specified'),
borenetdc89ca52014-10-17 07:37:05 -0700566 default=None)
567 option_parser.add_option(
568 '', '--output_dir',
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700569 help=('Temporary directory where SKPs and webpage archives will be '
570 'outputted to.'),
borenetdc89ca52014-10-17 07:37:05 -0700571 default=tempfile.gettempdir())
572 option_parser.add_option(
573 '', '--browser_executable',
574 help='The exact browser executable to run.',
575 default=None)
576 option_parser.add_option(
rmistryaa31ee72015-04-23 12:47:33 -0700577 '', '--browser_extra_args',
578 help='Additional arguments to pass to the browser.',
579 default=None)
580 option_parser.add_option(
borenetdc89ca52014-10-17 07:37:05 -0700581 '', '--chrome_src_path',
582 help='Path to the chromium src directory.',
583 default=None)
584 option_parser.add_option(
585 '', '--non-interactive', action='store_true',
586 help='Runs the script without any prompts. If this flag is specified and '
587 '--skia_tools is specified then the debugger is not run.',
588 default=False)
rmistryaa31ee72015-04-23 12:47:33 -0700589 option_parser.add_option(
590 '', '--skp_prefix',
591 help='Prefix to add to the names of generated SKPs.',
592 default=None)
borenetdc89ca52014-10-17 07:37:05 -0700593 options, unused_args = option_parser.parse_args()
594
595 playback = SkPicturePlayback(options)
596 sys.exit(playback.Run())