blob: 69e56fec2e6ebd25b344e49a46cf12be785a7096 [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
borenet85f0e632016-07-21 10:27:00 -070066SKIA_DIR = os.path.abspath(os.path.join(
67 os.path.realpath(os.path.dirname(__file__)),
68 os.pardir, os.pardir))
69sys.path.insert(0, SKIA_DIR)
borenetdc89ca52014-10-17 07:37:05 -070070
71from common.py.utils import gs_utils
72from common.py.utils import shell_utils
73
74ROOT_PLAYBACK_DIR_NAME = 'playback'
75SKPICTURES_DIR_NAME = 'skps'
76
rmistry0575c492016-02-01 10:27:05 -080077PARTNERS_GS_BUCKET = 'gs://chrome-partner-telemetry'
borenetdc89ca52014-10-17 07:37:05 -070078
79# Local archive and SKP directories.
borenetdc89ca52014-10-17 07:37:05 -070080LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR = os.path.join(
81 os.path.abspath(os.path.dirname(__file__)), 'page_sets', 'data')
82TMP_SKP_DIR = tempfile.mkdtemp()
83
rmistryf802f322014-10-22 05:04:43 -070084# Location of the credentials.json file and the string that represents missing
85# passwords.
86CREDENTIALS_FILE_PATH = os.path.join(
87 os.path.abspath(os.path.dirname(__file__)), 'page_sets', 'data',
88 'credentials.json'
89)
90
borenetdc89ca52014-10-17 07:37:05 -070091# Name of the SKP benchmark
92SKP_BENCHMARK = 'skpicture_printer'
93
94# The max base name length of Skp files.
95MAX_SKP_BASE_NAME_LEN = 31
96
97# Dictionary of device to platform prefixes for SKP files.
98DEVICE_TO_PLATFORM_PREFIX = {
99 'desktop': 'desk',
100 'galaxynexus': 'mobi',
101 'nexus10': 'tabl'
102}
103
104# How many times the record_wpr binary should be retried.
105RETRY_RECORD_WPR_COUNT = 5
borenet78399152014-10-17 12:15:46 -0700106# How many times the run_benchmark binary should be retried.
borenetdc89ca52014-10-17 07:37:05 -0700107RETRY_RUN_MEASUREMENT_COUNT = 5
108
rmistryf802f322014-10-22 05:04:43 -0700109# Location of the credentials.json file in Google Storage.
110CREDENTIALS_GS_PATH = '/playback/credentials/credentials.json'
111
borenetdc89ca52014-10-17 07:37:05 -0700112X11_DISPLAY = os.getenv('DISPLAY', ':0')
113
114GS_PREDEFINED_ACL = gs_utils.GSUtils.PredefinedACL.PRIVATE
115GS_FINE_GRAINED_ACL_LIST = [
116 (gs_utils.GSUtils.IdType.GROUP_BY_DOMAIN, 'google.com',
117 gs_utils.GSUtils.Permission.READ),
118]
119
rmistry49d093c2015-03-31 05:04:29 -0700120# Path to Chromium's page sets.
121CHROMIUM_PAGE_SETS_PATH = os.path.join('tools', 'perf', 'page_sets')
122
123# Dictionary of supported Chromium page sets to their file prefixes.
124CHROMIUM_PAGE_SETS_TO_PREFIX = {
rmistry5af9a372015-03-31 06:22:55 -0700125 'key_mobile_sites_smooth.py': 'keymobi',
rmistry2a3c8492015-03-31 10:59:15 -0700126 'top_25_smooth.py': 'top25desk',
rmistry49d093c2015-03-31 05:04:29 -0700127}
128
129
kkinnunene75d2d22014-12-03 04:38:46 -0800130def remove_prefix(s, prefix):
131 if s.startswith(prefix):
132 return s[len(prefix):]
133 return s
borenetdc89ca52014-10-17 07:37:05 -0700134
rmistry49d093c2015-03-31 05:04:29 -0700135
borenetdc89ca52014-10-17 07:37:05 -0700136class SkPicturePlayback(object):
137 """Class that archives or replays webpages and creates SKPs."""
138
139 def __init__(self, parse_options):
140 """Constructs a SkPicturePlayback BuildStep instance."""
141 assert parse_options.browser_executable, 'Must specify --browser_executable'
142 self._browser_executable = parse_options.browser_executable
rmistryaa31ee72015-04-23 12:47:33 -0700143 self._browser_args = '--disable-setuid-sandbox'
144 if parse_options.browser_extra_args:
145 self._browser_args = '%s %s' % (
146 self._browser_args, parse_options.browser_extra_args)
borenetdc89ca52014-10-17 07:37:05 -0700147
rmistry49d093c2015-03-31 05:04:29 -0700148 self._chrome_page_sets_path = os.path.join(parse_options.chrome_src_path,
149 CHROMIUM_PAGE_SETS_PATH)
borenetdc89ca52014-10-17 07:37:05 -0700150 self._all_page_sets_specified = parse_options.page_sets == 'all'
151 self._page_sets = self._ParsePageSets(parse_options.page_sets)
152
borenetdc89ca52014-10-17 07:37:05 -0700153 self._record = parse_options.record
154 self._skia_tools = parse_options.skia_tools
155 self._non_interactive = parse_options.non_interactive
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700156 self._upload = parse_options.upload
rmistryaa31ee72015-04-23 12:47:33 -0700157 self._skp_prefix = parse_options.skp_prefix
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700158 data_store_location = parse_options.data_store
159 if data_store_location.startswith(gs_utils.GS_PREFIX):
160 self.gs = GoogleStorageDataStore(data_store_location)
161 else:
162 self.gs = LocalFileSystemDataStore(data_store_location)
rmistry0575c492016-02-01 10:27:05 -0800163 self._upload_to_partner_bucket = parse_options.upload_to_partner_bucket
borenetdc89ca52014-10-17 07:37:05 -0700164 self._alternate_upload_dir = parse_options.alternate_upload_dir
borenetdc89ca52014-10-17 07:37:05 -0700165 self._telemetry_binaries_dir = os.path.join(parse_options.chrome_src_path,
166 'tools', 'perf')
rmistryafaf4962016-02-27 10:04:57 -0800167 self._catapult_dir = os.path.join(parse_options.chrome_src_path,
168 'third_party', 'catapult')
borenetdc89ca52014-10-17 07:37:05 -0700169
170 self._local_skp_dir = os.path.join(
171 parse_options.output_dir, ROOT_PLAYBACK_DIR_NAME, SKPICTURES_DIR_NAME)
172 self._local_record_webpages_archive_dir = os.path.join(
173 parse_options.output_dir, ROOT_PLAYBACK_DIR_NAME, 'webpages_archive')
174
175 # List of SKP files generated by this script.
176 self._skp_files = []
177
178 def _ParsePageSets(self, page_sets):
179 if not page_sets:
180 raise ValueError('Must specify at least one page_set!')
181 elif self._all_page_sets_specified:
182 # Get everything from the page_sets directory.
183 page_sets_dir = os.path.join(os.path.abspath(os.path.dirname(__file__)),
184 'page_sets')
185 ps = [os.path.join(page_sets_dir, page_set)
186 for page_set in os.listdir(page_sets_dir)
187 if not os.path.isdir(os.path.join(page_sets_dir, page_set)) and
188 page_set.endswith('.py')]
rmistry49d093c2015-03-31 05:04:29 -0700189 chromium_ps = [
190 os.path.join(self._chrome_page_sets_path, cr_page_set)
191 for cr_page_set in CHROMIUM_PAGE_SETS_TO_PREFIX]
192 ps.extend(chromium_ps)
borenetdc89ca52014-10-17 07:37:05 -0700193 elif '*' in page_sets:
194 # Explode and return the glob.
195 ps = glob.glob(page_sets)
196 else:
197 ps = page_sets.split(',')
198 ps.sort()
199 return ps
200
rmistry49d093c2015-03-31 05:04:29 -0700201 def _IsChromiumPageSet(self, page_set):
202 """Returns true if the specified page set is a Chromium page set."""
203 return page_set.startswith(self._chrome_page_sets_path)
204
borenetdc89ca52014-10-17 07:37:05 -0700205 def Run(self):
206 """Run the SkPicturePlayback BuildStep."""
207
rmistryf802f322014-10-22 05:04:43 -0700208 # Download the credentials file if it was not previously downloaded.
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700209 if not os.path.isfile(CREDENTIALS_FILE_PATH):
210 # Download the credentials.json file from Google Storage.
211 self.gs.download_file(CREDENTIALS_GS_PATH, CREDENTIALS_FILE_PATH)
212
213 if not os.path.isfile(CREDENTIALS_FILE_PATH):
214 print """\n\nCould not locate credentials file in the storage.
215 Please create a %s file that contains:
rmistryf802f322014-10-22 05:04:43 -0700216 {
217 "google": {
218 "username": "google_testing_account_username",
219 "password": "google_testing_account_password"
220 },
221 "facebook": {
222 "username": "facebook_testing_account_username",
223 "password": "facebook_testing_account_password"
224 }
225 }\n\n""" % CREDENTIALS_FILE_PATH
226 raw_input("Please press a key when you are ready to proceed...")
rmistryf802f322014-10-22 05:04:43 -0700227
borenetdc89ca52014-10-17 07:37:05 -0700228 # Delete any left over data files in the data directory.
229 for archive_file in glob.glob(
230 os.path.join(LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR, 'skia_*')):
231 os.remove(archive_file)
232
borenetdc89ca52014-10-17 07:37:05 -0700233 # Create the required local storage directories.
234 self._CreateLocalStorageDirs()
235
236 # Start the timer.
237 start_time = time.time()
238
239 # Loop through all page_sets.
240 for page_set in self._page_sets:
241
rmistry7620bf02014-10-27 06:42:11 -0700242 page_set_basename = os.path.basename(page_set).split('.')[0]
243 page_set_json_name = page_set_basename + '.json'
borenetdc89ca52014-10-17 07:37:05 -0700244 wpr_data_file = page_set.split(os.path.sep)[-1].split('.')[0] + '_000.wpr'
rmistry7620bf02014-10-27 06:42:11 -0700245 page_set_dir = os.path.dirname(page_set)
borenetdc89ca52014-10-17 07:37:05 -0700246
rmistry49d093c2015-03-31 05:04:29 -0700247 if self._IsChromiumPageSet(page_set):
248 print 'Using Chromium\'s captured archives for Chromium\'s page sets.'
249 elif self._record:
borenetdc89ca52014-10-17 07:37:05 -0700250 # Create an archive of the specified webpages if '--record=True' is
251 # specified.
252 record_wpr_cmd = (
rmistryafaf4962016-02-27 10:04:57 -0800253 'PYTHONPATH=%s:%s:$PYTHONPATH' % (page_set_dir, self._catapult_dir),
borenetdc89ca52014-10-17 07:37:05 -0700254 'DISPLAY=%s' % X11_DISPLAY,
255 os.path.join(self._telemetry_binaries_dir, 'record_wpr'),
rmistryaa31ee72015-04-23 12:47:33 -0700256 '--extra-browser-args="%s"' % self._browser_args,
borenetdc89ca52014-10-17 07:37:05 -0700257 '--browser=exact',
258 '--browser-executable=%s' % self._browser_executable,
rmistry7620bf02014-10-27 06:42:11 -0700259 '%s_page_set' % page_set_basename,
260 '--page-set-base-dir=%s' % page_set_dir
borenetdc89ca52014-10-17 07:37:05 -0700261 )
262 for _ in range(RETRY_RECORD_WPR_COUNT):
rmistry0ec28af2014-10-28 14:25:17 -0700263 try:
264 shell_utils.run(' '.join(record_wpr_cmd), shell=True)
kkinnunenf9310fe2015-03-29 22:33:16 -0700265
266 # Move over the created archive into the local webpages archive
267 # directory.
268 shutil.move(
269 os.path.join(LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR, wpr_data_file),
270 self._local_record_webpages_archive_dir)
271 shutil.move(
272 os.path.join(LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR,
273 page_set_json_name),
274 self._local_record_webpages_archive_dir)
275
borenetdc89ca52014-10-17 07:37:05 -0700276 # Break out of the retry loop since there were no errors.
277 break
rmistry0ec28af2014-10-28 14:25:17 -0700278 except Exception:
279 # There was a failure continue with the loop.
280 traceback.print_exc()
borenetdc89ca52014-10-17 07:37:05 -0700281 else:
282 # If we get here then record_wpr did not succeed and thus did not
283 # break out of the loop.
284 raise Exception('record_wpr failed for page_set: %s' % page_set)
285
286 else:
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700287 # Get the webpages archive so that it can be replayed.
288 self._DownloadWebpagesArchive(wpr_data_file, page_set_json_name)
borenetdc89ca52014-10-17 07:37:05 -0700289
borenet78399152014-10-17 12:15:46 -0700290 run_benchmark_cmd = (
rmistryafaf4962016-02-27 10:04:57 -0800291 'PYTHONPATH=%s:%s:$PYTHONPATH' % (page_set_dir, self._catapult_dir),
borenetdc89ca52014-10-17 07:37:05 -0700292 'DISPLAY=%s' % X11_DISPLAY,
Ravi Mistry57d69652016-09-08 09:08:44 -0400293 'timeout', '1800',
borenet78399152014-10-17 12:15:46 -0700294 os.path.join(self._telemetry_binaries_dir, 'run_benchmark'),
rmistryaa31ee72015-04-23 12:47:33 -0700295 '--extra-browser-args="%s"' % self._browser_args,
borenetdc89ca52014-10-17 07:37:05 -0700296 '--browser=exact',
297 '--browser-executable=%s' % self._browser_executable,
298 SKP_BENCHMARK,
rmistry7620bf02014-10-27 06:42:11 -0700299 '--page-set-name=%s' % page_set_basename,
rmistryf802f322014-10-22 05:04:43 -0700300 '--page-set-base-dir=%s' % page_set_dir,
301 '--skp-outdir=%s' % TMP_SKP_DIR,
302 '--also-run-disabled-tests'
borenetdc89ca52014-10-17 07:37:05 -0700303 )
borenetdc89ca52014-10-17 07:37:05 -0700304
305 for _ in range(RETRY_RUN_MEASUREMENT_COUNT):
306 try:
307 print '\n\n=======Capturing SKP of %s=======\n\n' % page_set
borenet78399152014-10-17 12:15:46 -0700308 shell_utils.run(' '.join(run_benchmark_cmd), shell=True)
borenetdc89ca52014-10-17 07:37:05 -0700309 except shell_utils.CommandFailedException:
310 # skpicture_printer sometimes fails with AssertionError but the
311 # captured SKP is still valid. This is a known issue.
312 pass
313
borenetdc89ca52014-10-17 07:37:05 -0700314 # Rename generated SKP files into more descriptive names.
315 try:
316 self._RenameSkpFiles(page_set)
317 # Break out of the retry loop since there were no errors.
318 break
319 except Exception:
320 # There was a failure continue with the loop.
321 traceback.print_exc()
322 print '\n\n=======Retrying %s=======\n\n' % page_set
323 time.sleep(10)
324 else:
borenet78399152014-10-17 12:15:46 -0700325 # If we get here then run_benchmark did not succeed and thus did not
borenetdc89ca52014-10-17 07:37:05 -0700326 # break out of the loop.
borenet78399152014-10-17 12:15:46 -0700327 raise Exception('run_benchmark failed for page_set: %s' % page_set)
borenetdc89ca52014-10-17 07:37:05 -0700328
borenetdc89ca52014-10-17 07:37:05 -0700329 print '\n\n=======Capturing SKP files took %s seconds=======\n\n' % (
330 time.time() - start_time)
331
332 if self._skia_tools:
333 render_pictures_cmd = [
334 os.path.join(self._skia_tools, 'render_pictures'),
335 '-r', self._local_skp_dir
336 ]
337 render_pdfs_cmd = [
338 os.path.join(self._skia_tools, 'render_pdfs'),
kkinnunen3a6aa862014-12-03 04:22:06 -0800339 '-r', self._local_skp_dir
borenetdc89ca52014-10-17 07:37:05 -0700340 ]
341
342 for tools_cmd in (render_pictures_cmd, render_pdfs_cmd):
343 print '\n\n=======Running %s=======' % ' '.join(tools_cmd)
344 proc = subprocess.Popen(tools_cmd)
rmistry0ec28af2014-10-28 14:25:17 -0700345 (code, _) = shell_utils.log_process_after_completion(proc, echo=False)
borenetdc89ca52014-10-17 07:37:05 -0700346 if code != 0:
347 raise Exception('%s failed!' % ' '.join(tools_cmd))
348
349 if not self._non_interactive:
350 print '\n\n=======Running debugger======='
351 os.system('%s %s' % (os.path.join(self._skia_tools, 'debugger'),
kkinnunen960fb502014-12-03 06:18:12 -0800352 self._local_skp_dir))
borenetdc89ca52014-10-17 07:37:05 -0700353
354 print '\n\n'
355
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700356 if self._upload:
357 print '\n\n=======Uploading to %s=======\n\n' % self.gs.target_type()
borenetdc89ca52014-10-17 07:37:05 -0700358 # Copy the directory structure in the root directory into Google Storage.
359 dest_dir_name = ROOT_PLAYBACK_DIR_NAME
360 if self._alternate_upload_dir:
361 dest_dir_name = self._alternate_upload_dir
362
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700363 self.gs.upload_dir_contents(
borenet1413d522016-08-08 06:26:00 -0700364 self._local_skp_dir, dest_dir=dest_dir_name,
borenetdc89ca52014-10-17 07:37:05 -0700365 upload_if=gs_utils.GSUtils.UploadIf.IF_MODIFIED,
366 predefined_acl=GS_PREDEFINED_ACL,
367 fine_grained_acl_list=GS_FINE_GRAINED_ACL_LIST)
368
369 print '\n\n=======New SKPs have been uploaded to %s =======\n\n' % (
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700370 posixpath.join(self.gs.target_name(), dest_dir_name,
371 SKPICTURES_DIR_NAME))
rmistry0575c492016-02-01 10:27:05 -0800372
borenetdc89ca52014-10-17 07:37:05 -0700373 else:
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700374 print '\n\n=======Not Uploading to %s=======\n\n' % self.gs.target_type()
borenetdc89ca52014-10-17 07:37:05 -0700375 print 'Generated resources are available in %s\n\n' % (
borenet1413d522016-08-08 06:26:00 -0700376 self._local_skp_dir)
borenetdc89ca52014-10-17 07:37:05 -0700377
borenet28698472016-07-25 05:18:15 -0700378 if self._upload_to_partner_bucket:
379 print '\n\n=======Uploading to Partner bucket %s =======\n\n' % (
380 PARTNERS_GS_BUCKET)
381 partner_gs = GoogleStorageDataStore(PARTNERS_GS_BUCKET)
382 partner_gs.delete_path(SKPICTURES_DIR_NAME)
borenet1413d522016-08-08 06:26:00 -0700383 print 'Uploading %s to %s' % (self._local_skp_dir, SKPICTURES_DIR_NAME)
borenet28698472016-07-25 05:18:15 -0700384 partner_gs.upload_dir_contents(
borenet1413d522016-08-08 06:26:00 -0700385 self._local_skp_dir,
borenet28698472016-07-25 05:18:15 -0700386 dest_dir=SKPICTURES_DIR_NAME,
387 upload_if=gs_utils.GSUtils.UploadIf.IF_MODIFIED)
388 print '\n\n=======New SKPs have been uploaded to %s =======\n\n' % (
389 posixpath.join(partner_gs.target_name(), SKPICTURES_DIR_NAME))
390
borenetdc89ca52014-10-17 07:37:05 -0700391 return 0
392
rmistry49d093c2015-03-31 05:04:29 -0700393 def _GetSkiaSkpFileName(self, page_set):
394 """Returns the SKP file name for Skia page sets."""
395 # /path/to/skia_yahooanswers_desktop.py -> skia_yahooanswers_desktop.py
396 ps_filename = os.path.basename(page_set)
397 # skia_yahooanswers_desktop.py -> skia_yahooanswers_desktop
398 ps_basename, _ = os.path.splitext(ps_filename)
399 # skia_yahooanswers_desktop -> skia, yahooanswers, desktop
400 _, page_name, device = ps_basename.split('_')
401 basename = '%s_%s' % (DEVICE_TO_PLATFORM_PREFIX[device], page_name)
402 return basename[:MAX_SKP_BASE_NAME_LEN] + '.skp'
403
404 def _GetChromiumSkpFileName(self, page_set, site):
405 """Returns the SKP file name for Chromium page sets."""
406 # /path/to/http___mobile_news_sandbox_pt0 -> http___mobile_news_sandbox_pt0
407 _, webpage = os.path.split(site)
408 # http___mobile_news_sandbox_pt0 -> mobile_news_sandbox_pt0
rmistry80bd3ae2015-04-03 08:22:51 -0700409 for prefix in ('http___', 'https___', 'www_'):
rmistry2a3c8492015-03-31 10:59:15 -0700410 if webpage.startswith(prefix):
411 webpage = webpage[len(prefix):]
rmistry49d093c2015-03-31 05:04:29 -0700412 # /path/to/skia_yahooanswers_desktop.py -> skia_yahooanswers_desktop.py
413 ps_filename = os.path.basename(page_set)
414 # http___mobile_news_sandbox -> pagesetprefix_http___mobile_news_sandbox
415 basename = '%s_%s' % (CHROMIUM_PAGE_SETS_TO_PREFIX[ps_filename], webpage)
416 return basename[:MAX_SKP_BASE_NAME_LEN] + '.skp'
417
borenetdc89ca52014-10-17 07:37:05 -0700418 def _RenameSkpFiles(self, page_set):
419 """Rename generated SKP files into more descriptive names.
420
421 Look into the subdirectory of TMP_SKP_DIR and find the most interesting
422 .skp in there to be this page_set's representative .skp.
423 """
borenetdc89ca52014-10-17 07:37:05 -0700424 subdirs = glob.glob(os.path.join(TMP_SKP_DIR, '*'))
borenetdc89ca52014-10-17 07:37:05 -0700425 for site in subdirs:
rmistry49d093c2015-03-31 05:04:29 -0700426 if self._IsChromiumPageSet(page_set):
427 filename = self._GetChromiumSkpFileName(page_set, site)
428 else:
429 filename = self._GetSkiaSkpFileName(page_set)
rmistry80bd3ae2015-04-03 08:22:51 -0700430 filename = filename.lower()
rmistry49d093c2015-03-31 05:04:29 -0700431
rmistryaa31ee72015-04-23 12:47:33 -0700432 if self._skp_prefix:
433 filename = '%s%s' % (self._skp_prefix, filename)
434
borenetdc89ca52014-10-17 07:37:05 -0700435 # We choose the largest .skp as the most likely to be interesting.
436 largest_skp = max(glob.glob(os.path.join(site, '*.skp')),
437 key=lambda path: os.stat(path).st_size)
438 dest = os.path.join(self._local_skp_dir, filename)
439 print 'Moving', largest_skp, 'to', dest
440 shutil.move(largest_skp, dest)
441 self._skp_files.append(filename)
442 shutil.rmtree(site)
443
444 def _CreateLocalStorageDirs(self):
445 """Creates required local storage directories for this script."""
446 for d in (self._local_record_webpages_archive_dir,
447 self._local_skp_dir):
448 if os.path.exists(d):
449 shutil.rmtree(d)
450 os.makedirs(d)
451
rmistry7620bf02014-10-27 06:42:11 -0700452 def _DownloadWebpagesArchive(self, wpr_data_file, page_set_json_name):
borenetdc89ca52014-10-17 07:37:05 -0700453 """Downloads the webpages archive and its required page set from GS."""
454 wpr_source = posixpath.join(ROOT_PLAYBACK_DIR_NAME, 'webpages_archive',
455 wpr_data_file)
456 page_set_source = posixpath.join(ROOT_PLAYBACK_DIR_NAME,
457 'webpages_archive',
rmistry7620bf02014-10-27 06:42:11 -0700458 page_set_json_name)
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700459 gs = self.gs
460 if (gs.does_storage_object_exist(wpr_source) and
461 gs.does_storage_object_exist(page_set_source)):
462 gs.download_file(wpr_source,
borenetdc89ca52014-10-17 07:37:05 -0700463 os.path.join(LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR,
464 wpr_data_file))
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700465 gs.download_file(page_set_source,
borenetdc89ca52014-10-17 07:37:05 -0700466 os.path.join(LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR,
rmistry7620bf02014-10-27 06:42:11 -0700467 page_set_json_name))
borenetdc89ca52014-10-17 07:37:05 -0700468 else:
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700469 raise Exception('%s and %s do not exist in %s!' % (gs.target_type(),
470 wpr_source, page_set_source))
borenetdc89ca52014-10-17 07:37:05 -0700471
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700472class DataStore:
473 """An abstract base class for uploading recordings to a data storage.
474 The interface emulates the google storage api."""
475 def target_name(self):
476 raise NotImplementedError()
477 def target_type(self):
478 raise NotImplementedError()
479 def does_storage_object_exist(self, *args):
480 raise NotImplementedError()
481 def download_file(self, *args):
482 raise NotImplementedError()
483 def upload_dir_contents(self, source_dir, **kwargs):
484 raise NotImplementedError()
485
486class GoogleStorageDataStore(DataStore):
487 def __init__(self, data_store_url):
488 self._data_store_url = data_store_url
rmistry49d093c2015-03-31 05:04:29 -0700489 self._bucket = remove_prefix(self._data_store_url.lstrip(),
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700490 gs_utils.GS_PREFIX)
491 self.gs = gs_utils.GSUtils()
492 def target_name(self):
493 return self._data_store_url
494 def target_type(self):
495 return 'Google Storage'
496 def does_storage_object_exist(self, *args):
497 return self.gs.does_storage_object_exist(self._bucket, *args)
rmistryc33c79c2016-02-03 04:27:54 -0800498 def delete_path(self, path):
rmistryf5e83952016-02-03 05:58:31 -0800499 _, files = self.gs.list_bucket_contents(self._bucket, subdir=path)
500 for f in files:
501 self.gs.delete_file(self._bucket, posixpath.join(path, f))
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700502 def download_file(self, *args):
503 self.gs.download_file(self._bucket, *args)
504 def upload_dir_contents(self, source_dir, **kwargs):
505 self.gs.upload_dir_contents(source_dir, self._bucket, **kwargs)
506
507class LocalFileSystemDataStore(DataStore):
508 def __init__(self, data_store_location):
509 self._base_dir = data_store_location
510 def target_name(self):
511 return self._base_dir
512 def target_type(self):
513 return self._base_dir
514 def does_storage_object_exist(self, name, *args):
515 return os.path.isfile(os.path.join(self._base_dir, name))
rmistryc33c79c2016-02-03 04:27:54 -0800516 def delete_path(self, path):
517 shutil.rmtree(path)
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700518 def download_file(self, name, local_path, *args):
519 shutil.copyfile(os.path.join(self._base_dir, name), local_path)
520 def upload_dir_contents(self, source_dir, dest_dir, **kwargs):
521 def copytree(source_dir, dest_dir):
522 if not os.path.exists(dest_dir):
523 os.makedirs(dest_dir)
524 for item in os.listdir(source_dir):
525 source = os.path.join(source_dir, item)
526 dest = os.path.join(dest_dir, item)
527 if os.path.isdir(source):
528 copytree(source, dest)
529 else:
530 shutil.copy2(source, dest)
531 copytree(source_dir, os.path.join(self._base_dir, dest_dir))
borenetdc89ca52014-10-17 07:37:05 -0700532
533if '__main__' == __name__:
534 option_parser = optparse.OptionParser()
535 option_parser.add_option(
536 '', '--page_sets',
537 help='Specifies the page sets to use to archive. Supports globs.',
538 default='all')
539 option_parser.add_option(
borenetdc89ca52014-10-17 07:37:05 -0700540 '', '--record', action='store_true',
541 help='Specifies whether a new website archive should be created.',
542 default=False)
543 option_parser.add_option(
borenetdc89ca52014-10-17 07:37:05 -0700544 '', '--skia_tools',
545 help=('Path to compiled Skia executable tools. '
546 'render_pictures/render_pdfs is run on the set '
547 'after all SKPs are captured. If the script is run without '
548 '--non-interactive then the debugger is also run at the end. Debug '
549 'builds are recommended because they seem to catch more failures '
550 'than Release builds.'),
551 default=None)
552 option_parser.add_option(
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700553 '', '--upload', action='store_true',
554 help=('Uploads to Google Storage or copies to local filesystem storage '
555 ' if this is True.'),
borenetdc89ca52014-10-17 07:37:05 -0700556 default=False)
557 option_parser.add_option(
rmistry0575c492016-02-01 10:27:05 -0800558 '', '--upload_to_partner_bucket', action='store_true',
559 help=('Uploads SKPs to the chrome-partner-telemetry Google Storage '
560 'bucket if true.'),
561 default=False)
562 option_parser.add_option(
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700563 '', '--data_store',
564 help=('The location of the file storage to use to download and upload '
565 'files. Can be \'gs://<bucket>\' for Google Storage, or '
566 'a directory for local filesystem storage'),
567 default='gs://chromium-skia-gm')
568 option_parser.add_option(
borenetdc89ca52014-10-17 07:37:05 -0700569 '', '--alternate_upload_dir',
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700570 help= ('Uploads to a different directory in Google Storage or local '
571 'storage if this flag is specified'),
borenetdc89ca52014-10-17 07:37:05 -0700572 default=None)
573 option_parser.add_option(
574 '', '--output_dir',
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700575 help=('Temporary directory where SKPs and webpage archives will be '
576 'outputted to.'),
borenetdc89ca52014-10-17 07:37:05 -0700577 default=tempfile.gettempdir())
578 option_parser.add_option(
579 '', '--browser_executable',
580 help='The exact browser executable to run.',
581 default=None)
582 option_parser.add_option(
rmistryaa31ee72015-04-23 12:47:33 -0700583 '', '--browser_extra_args',
584 help='Additional arguments to pass to the browser.',
585 default=None)
586 option_parser.add_option(
borenetdc89ca52014-10-17 07:37:05 -0700587 '', '--chrome_src_path',
588 help='Path to the chromium src directory.',
589 default=None)
590 option_parser.add_option(
591 '', '--non-interactive', action='store_true',
592 help='Runs the script without any prompts. If this flag is specified and '
593 '--skia_tools is specified then the debugger is not run.',
594 default=False)
rmistryaa31ee72015-04-23 12:47:33 -0700595 option_parser.add_option(
596 '', '--skp_prefix',
597 help='Prefix to add to the names of generated SKPs.',
598 default=None)
borenetdc89ca52014-10-17 07:37:05 -0700599 options, unused_args = option_parser.parse_args()
600
601 playback = SkPicturePlayback(options)
602 sys.exit(playback.Run())