blob: 8e78e0d31e662fcf56fb46cf5c8b1f1826dd05c4 [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.
80LOCAL_PLAYBACK_ROOT_DIR = os.path.join(
81 tempfile.gettempdir(), ROOT_PLAYBACK_DIR_NAME)
82LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR = os.path.join(
83 os.path.abspath(os.path.dirname(__file__)), 'page_sets', 'data')
84TMP_SKP_DIR = tempfile.mkdtemp()
85
rmistryf802f322014-10-22 05:04:43 -070086# Location of the credentials.json file and the string that represents missing
87# passwords.
88CREDENTIALS_FILE_PATH = os.path.join(
89 os.path.abspath(os.path.dirname(__file__)), 'page_sets', 'data',
90 'credentials.json'
91)
92
borenetdc89ca52014-10-17 07:37:05 -070093# Name of the SKP benchmark
94SKP_BENCHMARK = 'skpicture_printer'
95
96# The max base name length of Skp files.
97MAX_SKP_BASE_NAME_LEN = 31
98
99# Dictionary of device to platform prefixes for SKP files.
100DEVICE_TO_PLATFORM_PREFIX = {
101 'desktop': 'desk',
102 'galaxynexus': 'mobi',
103 'nexus10': 'tabl'
104}
105
106# How many times the record_wpr binary should be retried.
107RETRY_RECORD_WPR_COUNT = 5
borenet78399152014-10-17 12:15:46 -0700108# How many times the run_benchmark binary should be retried.
borenetdc89ca52014-10-17 07:37:05 -0700109RETRY_RUN_MEASUREMENT_COUNT = 5
110
rmistryf802f322014-10-22 05:04:43 -0700111# Location of the credentials.json file in Google Storage.
112CREDENTIALS_GS_PATH = '/playback/credentials/credentials.json'
113
borenetdc89ca52014-10-17 07:37:05 -0700114X11_DISPLAY = os.getenv('DISPLAY', ':0')
115
116GS_PREDEFINED_ACL = gs_utils.GSUtils.PredefinedACL.PRIVATE
117GS_FINE_GRAINED_ACL_LIST = [
118 (gs_utils.GSUtils.IdType.GROUP_BY_DOMAIN, 'google.com',
119 gs_utils.GSUtils.Permission.READ),
120]
121
rmistry49d093c2015-03-31 05:04:29 -0700122# Path to Chromium's page sets.
123CHROMIUM_PAGE_SETS_PATH = os.path.join('tools', 'perf', 'page_sets')
124
125# Dictionary of supported Chromium page sets to their file prefixes.
126CHROMIUM_PAGE_SETS_TO_PREFIX = {
rmistry5af9a372015-03-31 06:22:55 -0700127 'key_mobile_sites_smooth.py': 'keymobi',
rmistry2a3c8492015-03-31 10:59:15 -0700128 'top_25_smooth.py': 'top25desk',
rmistry49d093c2015-03-31 05:04:29 -0700129}
130
131
kkinnunene75d2d22014-12-03 04:38:46 -0800132def remove_prefix(s, prefix):
133 if s.startswith(prefix):
134 return s[len(prefix):]
135 return s
borenetdc89ca52014-10-17 07:37:05 -0700136
rmistry49d093c2015-03-31 05:04:29 -0700137
borenetdc89ca52014-10-17 07:37:05 -0700138class SkPicturePlayback(object):
139 """Class that archives or replays webpages and creates SKPs."""
140
141 def __init__(self, parse_options):
142 """Constructs a SkPicturePlayback BuildStep instance."""
143 assert parse_options.browser_executable, 'Must specify --browser_executable'
144 self._browser_executable = parse_options.browser_executable
rmistryaa31ee72015-04-23 12:47:33 -0700145 self._browser_args = '--disable-setuid-sandbox'
146 if parse_options.browser_extra_args:
147 self._browser_args = '%s %s' % (
148 self._browser_args, parse_options.browser_extra_args)
borenetdc89ca52014-10-17 07:37:05 -0700149
rmistry49d093c2015-03-31 05:04:29 -0700150 self._chrome_page_sets_path = os.path.join(parse_options.chrome_src_path,
151 CHROMIUM_PAGE_SETS_PATH)
borenetdc89ca52014-10-17 07:37:05 -0700152 self._all_page_sets_specified = parse_options.page_sets == 'all'
153 self._page_sets = self._ParsePageSets(parse_options.page_sets)
154
borenetdc89ca52014-10-17 07:37:05 -0700155 self._record = parse_options.record
156 self._skia_tools = parse_options.skia_tools
157 self._non_interactive = parse_options.non_interactive
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700158 self._upload = parse_options.upload
rmistryaa31ee72015-04-23 12:47:33 -0700159 self._skp_prefix = parse_options.skp_prefix
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700160 data_store_location = parse_options.data_store
161 if data_store_location.startswith(gs_utils.GS_PREFIX):
162 self.gs = GoogleStorageDataStore(data_store_location)
163 else:
164 self.gs = LocalFileSystemDataStore(data_store_location)
rmistry0575c492016-02-01 10:27:05 -0800165 self._upload_to_partner_bucket = parse_options.upload_to_partner_bucket
borenetdc89ca52014-10-17 07:37:05 -0700166 self._alternate_upload_dir = parse_options.alternate_upload_dir
borenetdc89ca52014-10-17 07:37:05 -0700167 self._telemetry_binaries_dir = os.path.join(parse_options.chrome_src_path,
168 'tools', 'perf')
rmistryafaf4962016-02-27 10:04:57 -0800169 self._catapult_dir = os.path.join(parse_options.chrome_src_path,
170 'third_party', 'catapult')
borenetdc89ca52014-10-17 07:37:05 -0700171
172 self._local_skp_dir = os.path.join(
173 parse_options.output_dir, ROOT_PLAYBACK_DIR_NAME, SKPICTURES_DIR_NAME)
174 self._local_record_webpages_archive_dir = os.path.join(
175 parse_options.output_dir, ROOT_PLAYBACK_DIR_NAME, 'webpages_archive')
176
177 # List of SKP files generated by this script.
178 self._skp_files = []
179
180 def _ParsePageSets(self, page_sets):
181 if not page_sets:
182 raise ValueError('Must specify at least one page_set!')
183 elif self._all_page_sets_specified:
184 # Get everything from the page_sets directory.
185 page_sets_dir = os.path.join(os.path.abspath(os.path.dirname(__file__)),
186 'page_sets')
187 ps = [os.path.join(page_sets_dir, page_set)
188 for page_set in os.listdir(page_sets_dir)
189 if not os.path.isdir(os.path.join(page_sets_dir, page_set)) and
190 page_set.endswith('.py')]
rmistry49d093c2015-03-31 05:04:29 -0700191 chromium_ps = [
192 os.path.join(self._chrome_page_sets_path, cr_page_set)
193 for cr_page_set in CHROMIUM_PAGE_SETS_TO_PREFIX]
194 ps.extend(chromium_ps)
borenetdc89ca52014-10-17 07:37:05 -0700195 elif '*' in page_sets:
196 # Explode and return the glob.
197 ps = glob.glob(page_sets)
198 else:
199 ps = page_sets.split(',')
200 ps.sort()
201 return ps
202
rmistry49d093c2015-03-31 05:04:29 -0700203 def _IsChromiumPageSet(self, page_set):
204 """Returns true if the specified page set is a Chromium page set."""
205 return page_set.startswith(self._chrome_page_sets_path)
206
borenetdc89ca52014-10-17 07:37:05 -0700207 def Run(self):
208 """Run the SkPicturePlayback BuildStep."""
209
rmistryf802f322014-10-22 05:04:43 -0700210 # Download the credentials file if it was not previously downloaded.
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700211 if not os.path.isfile(CREDENTIALS_FILE_PATH):
212 # Download the credentials.json file from Google Storage.
213 self.gs.download_file(CREDENTIALS_GS_PATH, CREDENTIALS_FILE_PATH)
214
215 if not os.path.isfile(CREDENTIALS_FILE_PATH):
216 print """\n\nCould not locate credentials file in the storage.
217 Please create a %s file that contains:
rmistryf802f322014-10-22 05:04:43 -0700218 {
219 "google": {
220 "username": "google_testing_account_username",
221 "password": "google_testing_account_password"
222 },
223 "facebook": {
224 "username": "facebook_testing_account_username",
225 "password": "facebook_testing_account_password"
226 }
227 }\n\n""" % CREDENTIALS_FILE_PATH
228 raw_input("Please press a key when you are ready to proceed...")
rmistryf802f322014-10-22 05:04:43 -0700229
borenetdc89ca52014-10-17 07:37:05 -0700230 # Delete any left over data files in the data directory.
231 for archive_file in glob.glob(
232 os.path.join(LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR, 'skia_*')):
233 os.remove(archive_file)
234
235 # Delete the local root directory if it already exists.
236 if os.path.exists(LOCAL_PLAYBACK_ROOT_DIR):
237 shutil.rmtree(LOCAL_PLAYBACK_ROOT_DIR)
238
239 # Create the required local storage directories.
240 self._CreateLocalStorageDirs()
241
242 # Start the timer.
243 start_time = time.time()
244
245 # Loop through all page_sets.
246 for page_set in self._page_sets:
247
rmistry7620bf02014-10-27 06:42:11 -0700248 page_set_basename = os.path.basename(page_set).split('.')[0]
249 page_set_json_name = page_set_basename + '.json'
borenetdc89ca52014-10-17 07:37:05 -0700250 wpr_data_file = page_set.split(os.path.sep)[-1].split('.')[0] + '_000.wpr'
rmistry7620bf02014-10-27 06:42:11 -0700251 page_set_dir = os.path.dirname(page_set)
borenetdc89ca52014-10-17 07:37:05 -0700252
rmistry49d093c2015-03-31 05:04:29 -0700253 if self._IsChromiumPageSet(page_set):
254 print 'Using Chromium\'s captured archives for Chromium\'s page sets.'
255 elif self._record:
borenetdc89ca52014-10-17 07:37:05 -0700256 # Create an archive of the specified webpages if '--record=True' is
257 # specified.
258 record_wpr_cmd = (
rmistryafaf4962016-02-27 10:04:57 -0800259 'PYTHONPATH=%s:%s:$PYTHONPATH' % (page_set_dir, self._catapult_dir),
borenetdc89ca52014-10-17 07:37:05 -0700260 'DISPLAY=%s' % X11_DISPLAY,
261 os.path.join(self._telemetry_binaries_dir, 'record_wpr'),
rmistryaa31ee72015-04-23 12:47:33 -0700262 '--extra-browser-args="%s"' % self._browser_args,
borenetdc89ca52014-10-17 07:37:05 -0700263 '--browser=exact',
264 '--browser-executable=%s' % self._browser_executable,
rmistry7620bf02014-10-27 06:42:11 -0700265 '%s_page_set' % page_set_basename,
266 '--page-set-base-dir=%s' % page_set_dir
borenetdc89ca52014-10-17 07:37:05 -0700267 )
268 for _ in range(RETRY_RECORD_WPR_COUNT):
rmistry0ec28af2014-10-28 14:25:17 -0700269 try:
270 shell_utils.run(' '.join(record_wpr_cmd), shell=True)
kkinnunenf9310fe2015-03-29 22:33:16 -0700271
272 # Move over the created archive into the local webpages archive
273 # directory.
274 shutil.move(
275 os.path.join(LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR, wpr_data_file),
276 self._local_record_webpages_archive_dir)
277 shutil.move(
278 os.path.join(LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR,
279 page_set_json_name),
280 self._local_record_webpages_archive_dir)
281
borenetdc89ca52014-10-17 07:37:05 -0700282 # Break out of the retry loop since there were no errors.
283 break
rmistry0ec28af2014-10-28 14:25:17 -0700284 except Exception:
285 # There was a failure continue with the loop.
286 traceback.print_exc()
borenetdc89ca52014-10-17 07:37:05 -0700287 else:
288 # If we get here then record_wpr did not succeed and thus did not
289 # break out of the loop.
290 raise Exception('record_wpr failed for page_set: %s' % page_set)
291
292 else:
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700293 # Get the webpages archive so that it can be replayed.
294 self._DownloadWebpagesArchive(wpr_data_file, page_set_json_name)
borenetdc89ca52014-10-17 07:37:05 -0700295
borenet78399152014-10-17 12:15:46 -0700296 run_benchmark_cmd = (
rmistryafaf4962016-02-27 10:04:57 -0800297 'PYTHONPATH=%s:%s:$PYTHONPATH' % (page_set_dir, self._catapult_dir),
borenetdc89ca52014-10-17 07:37:05 -0700298 'DISPLAY=%s' % X11_DISPLAY,
299 'timeout', '300',
borenet78399152014-10-17 12:15:46 -0700300 os.path.join(self._telemetry_binaries_dir, 'run_benchmark'),
rmistryaa31ee72015-04-23 12:47:33 -0700301 '--extra-browser-args="%s"' % self._browser_args,
borenetdc89ca52014-10-17 07:37:05 -0700302 '--browser=exact',
303 '--browser-executable=%s' % self._browser_executable,
304 SKP_BENCHMARK,
rmistry7620bf02014-10-27 06:42:11 -0700305 '--page-set-name=%s' % page_set_basename,
rmistryf802f322014-10-22 05:04:43 -0700306 '--page-set-base-dir=%s' % page_set_dir,
307 '--skp-outdir=%s' % TMP_SKP_DIR,
308 '--also-run-disabled-tests'
borenetdc89ca52014-10-17 07:37:05 -0700309 )
borenetdc89ca52014-10-17 07:37:05 -0700310
311 for _ in range(RETRY_RUN_MEASUREMENT_COUNT):
312 try:
313 print '\n\n=======Capturing SKP of %s=======\n\n' % page_set
borenet78399152014-10-17 12:15:46 -0700314 shell_utils.run(' '.join(run_benchmark_cmd), shell=True)
borenetdc89ca52014-10-17 07:37:05 -0700315 except shell_utils.CommandFailedException:
316 # skpicture_printer sometimes fails with AssertionError but the
317 # captured SKP is still valid. This is a known issue.
318 pass
319
borenetdc89ca52014-10-17 07:37:05 -0700320 # Rename generated SKP files into more descriptive names.
321 try:
322 self._RenameSkpFiles(page_set)
323 # Break out of the retry loop since there were no errors.
324 break
325 except Exception:
326 # There was a failure continue with the loop.
327 traceback.print_exc()
328 print '\n\n=======Retrying %s=======\n\n' % page_set
329 time.sleep(10)
330 else:
borenet78399152014-10-17 12:15:46 -0700331 # If we get here then run_benchmark did not succeed and thus did not
borenetdc89ca52014-10-17 07:37:05 -0700332 # break out of the loop.
borenet78399152014-10-17 12:15:46 -0700333 raise Exception('run_benchmark failed for page_set: %s' % page_set)
borenetdc89ca52014-10-17 07:37:05 -0700334
borenetdc89ca52014-10-17 07:37:05 -0700335 print '\n\n=======Capturing SKP files took %s seconds=======\n\n' % (
336 time.time() - start_time)
337
338 if self._skia_tools:
339 render_pictures_cmd = [
340 os.path.join(self._skia_tools, 'render_pictures'),
341 '-r', self._local_skp_dir
342 ]
343 render_pdfs_cmd = [
344 os.path.join(self._skia_tools, 'render_pdfs'),
kkinnunen3a6aa862014-12-03 04:22:06 -0800345 '-r', self._local_skp_dir
borenetdc89ca52014-10-17 07:37:05 -0700346 ]
347
348 for tools_cmd in (render_pictures_cmd, render_pdfs_cmd):
349 print '\n\n=======Running %s=======' % ' '.join(tools_cmd)
350 proc = subprocess.Popen(tools_cmd)
rmistry0ec28af2014-10-28 14:25:17 -0700351 (code, _) = shell_utils.log_process_after_completion(proc, echo=False)
borenetdc89ca52014-10-17 07:37:05 -0700352 if code != 0:
353 raise Exception('%s failed!' % ' '.join(tools_cmd))
354
355 if not self._non_interactive:
356 print '\n\n=======Running debugger======='
357 os.system('%s %s' % (os.path.join(self._skia_tools, 'debugger'),
kkinnunen960fb502014-12-03 06:18:12 -0800358 self._local_skp_dir))
borenetdc89ca52014-10-17 07:37:05 -0700359
360 print '\n\n'
361
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700362 if self._upload:
363 print '\n\n=======Uploading to %s=======\n\n' % self.gs.target_type()
borenetdc89ca52014-10-17 07:37:05 -0700364 # Copy the directory structure in the root directory into Google Storage.
365 dest_dir_name = ROOT_PLAYBACK_DIR_NAME
366 if self._alternate_upload_dir:
367 dest_dir_name = self._alternate_upload_dir
368
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700369 self.gs.upload_dir_contents(
rmistry009b0692015-03-31 05:20:12 -0700370 LOCAL_PLAYBACK_ROOT_DIR, dest_dir=dest_dir_name,
borenetdc89ca52014-10-17 07:37:05 -0700371 upload_if=gs_utils.GSUtils.UploadIf.IF_MODIFIED,
372 predefined_acl=GS_PREDEFINED_ACL,
373 fine_grained_acl_list=GS_FINE_GRAINED_ACL_LIST)
374
375 print '\n\n=======New SKPs have been uploaded to %s =======\n\n' % (
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700376 posixpath.join(self.gs.target_name(), dest_dir_name,
377 SKPICTURES_DIR_NAME))
rmistry0575c492016-02-01 10:27:05 -0800378
379 if self._upload_to_partner_bucket:
380 print '\n\n=======Uploading to Partner bucket %s =======\n\n' % (
381 PARTNERS_GS_BUCKET)
382 partner_gs = GoogleStorageDataStore(PARTNERS_GS_BUCKET)
rmistryc33c79c2016-02-03 04:27:54 -0800383 partner_gs.delete_path(SKPICTURES_DIR_NAME)
rmistry0575c492016-02-01 10:27:05 -0800384 partner_gs.upload_dir_contents(
385 os.path.join(LOCAL_PLAYBACK_ROOT_DIR, SKPICTURES_DIR_NAME),
rmistry8870e942016-02-02 13:55:38 -0800386 dest_dir=SKPICTURES_DIR_NAME,
rmistry0575c492016-02-01 10:27:05 -0800387 upload_if=gs_utils.GSUtils.UploadIf.IF_MODIFIED)
388 print '\n\n=======New SKPs have been uploaded to %s =======\n\n' % (
rmistry8870e942016-02-02 13:55:38 -0800389 posixpath.join(partner_gs.target_name(), SKPICTURES_DIR_NAME))
borenetdc89ca52014-10-17 07:37:05 -0700390 else:
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700391 print '\n\n=======Not Uploading to %s=======\n\n' % self.gs.target_type()
borenetdc89ca52014-10-17 07:37:05 -0700392 print 'Generated resources are available in %s\n\n' % (
393 LOCAL_PLAYBACK_ROOT_DIR)
394
395 return 0
396
rmistry49d093c2015-03-31 05:04:29 -0700397 def _GetSkiaSkpFileName(self, page_set):
398 """Returns the SKP file name for Skia page sets."""
399 # /path/to/skia_yahooanswers_desktop.py -> skia_yahooanswers_desktop.py
400 ps_filename = os.path.basename(page_set)
401 # skia_yahooanswers_desktop.py -> skia_yahooanswers_desktop
402 ps_basename, _ = os.path.splitext(ps_filename)
403 # skia_yahooanswers_desktop -> skia, yahooanswers, desktop
404 _, page_name, device = ps_basename.split('_')
405 basename = '%s_%s' % (DEVICE_TO_PLATFORM_PREFIX[device], page_name)
406 return basename[:MAX_SKP_BASE_NAME_LEN] + '.skp'
407
408 def _GetChromiumSkpFileName(self, page_set, site):
409 """Returns the SKP file name for Chromium page sets."""
410 # /path/to/http___mobile_news_sandbox_pt0 -> http___mobile_news_sandbox_pt0
411 _, webpage = os.path.split(site)
412 # http___mobile_news_sandbox_pt0 -> mobile_news_sandbox_pt0
rmistry80bd3ae2015-04-03 08:22:51 -0700413 for prefix in ('http___', 'https___', 'www_'):
rmistry2a3c8492015-03-31 10:59:15 -0700414 if webpage.startswith(prefix):
415 webpage = webpage[len(prefix):]
rmistry49d093c2015-03-31 05:04:29 -0700416 # /path/to/skia_yahooanswers_desktop.py -> skia_yahooanswers_desktop.py
417 ps_filename = os.path.basename(page_set)
418 # http___mobile_news_sandbox -> pagesetprefix_http___mobile_news_sandbox
419 basename = '%s_%s' % (CHROMIUM_PAGE_SETS_TO_PREFIX[ps_filename], webpage)
420 return basename[:MAX_SKP_BASE_NAME_LEN] + '.skp'
421
borenetdc89ca52014-10-17 07:37:05 -0700422 def _RenameSkpFiles(self, page_set):
423 """Rename generated SKP files into more descriptive names.
424
425 Look into the subdirectory of TMP_SKP_DIR and find the most interesting
426 .skp in there to be this page_set's representative .skp.
427 """
borenetdc89ca52014-10-17 07:37:05 -0700428 subdirs = glob.glob(os.path.join(TMP_SKP_DIR, '*'))
borenetdc89ca52014-10-17 07:37:05 -0700429 for site in subdirs:
rmistry49d093c2015-03-31 05:04:29 -0700430 if self._IsChromiumPageSet(page_set):
431 filename = self._GetChromiumSkpFileName(page_set, site)
432 else:
433 filename = self._GetSkiaSkpFileName(page_set)
rmistry80bd3ae2015-04-03 08:22:51 -0700434 filename = filename.lower()
rmistry49d093c2015-03-31 05:04:29 -0700435
rmistryaa31ee72015-04-23 12:47:33 -0700436 if self._skp_prefix:
437 filename = '%s%s' % (self._skp_prefix, filename)
438
borenetdc89ca52014-10-17 07:37:05 -0700439 # We choose the largest .skp as the most likely to be interesting.
440 largest_skp = max(glob.glob(os.path.join(site, '*.skp')),
441 key=lambda path: os.stat(path).st_size)
442 dest = os.path.join(self._local_skp_dir, filename)
443 print 'Moving', largest_skp, 'to', dest
444 shutil.move(largest_skp, dest)
445 self._skp_files.append(filename)
446 shutil.rmtree(site)
447
448 def _CreateLocalStorageDirs(self):
449 """Creates required local storage directories for this script."""
450 for d in (self._local_record_webpages_archive_dir,
451 self._local_skp_dir):
452 if os.path.exists(d):
453 shutil.rmtree(d)
454 os.makedirs(d)
455
rmistry7620bf02014-10-27 06:42:11 -0700456 def _DownloadWebpagesArchive(self, wpr_data_file, page_set_json_name):
borenetdc89ca52014-10-17 07:37:05 -0700457 """Downloads the webpages archive and its required page set from GS."""
458 wpr_source = posixpath.join(ROOT_PLAYBACK_DIR_NAME, 'webpages_archive',
459 wpr_data_file)
460 page_set_source = posixpath.join(ROOT_PLAYBACK_DIR_NAME,
461 'webpages_archive',
rmistry7620bf02014-10-27 06:42:11 -0700462 page_set_json_name)
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700463 gs = self.gs
464 if (gs.does_storage_object_exist(wpr_source) and
465 gs.does_storage_object_exist(page_set_source)):
466 gs.download_file(wpr_source,
borenetdc89ca52014-10-17 07:37:05 -0700467 os.path.join(LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR,
468 wpr_data_file))
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700469 gs.download_file(page_set_source,
borenetdc89ca52014-10-17 07:37:05 -0700470 os.path.join(LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR,
rmistry7620bf02014-10-27 06:42:11 -0700471 page_set_json_name))
borenetdc89ca52014-10-17 07:37:05 -0700472 else:
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700473 raise Exception('%s and %s do not exist in %s!' % (gs.target_type(),
474 wpr_source, page_set_source))
borenetdc89ca52014-10-17 07:37:05 -0700475
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700476class DataStore:
477 """An abstract base class for uploading recordings to a data storage.
478 The interface emulates the google storage api."""
479 def target_name(self):
480 raise NotImplementedError()
481 def target_type(self):
482 raise NotImplementedError()
483 def does_storage_object_exist(self, *args):
484 raise NotImplementedError()
485 def download_file(self, *args):
486 raise NotImplementedError()
487 def upload_dir_contents(self, source_dir, **kwargs):
488 raise NotImplementedError()
489
490class GoogleStorageDataStore(DataStore):
491 def __init__(self, data_store_url):
492 self._data_store_url = data_store_url
rmistry49d093c2015-03-31 05:04:29 -0700493 self._bucket = remove_prefix(self._data_store_url.lstrip(),
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700494 gs_utils.GS_PREFIX)
495 self.gs = gs_utils.GSUtils()
496 def target_name(self):
497 return self._data_store_url
498 def target_type(self):
499 return 'Google Storage'
500 def does_storage_object_exist(self, *args):
501 return self.gs.does_storage_object_exist(self._bucket, *args)
rmistryc33c79c2016-02-03 04:27:54 -0800502 def delete_path(self, path):
rmistryf5e83952016-02-03 05:58:31 -0800503 _, files = self.gs.list_bucket_contents(self._bucket, subdir=path)
504 for f in files:
505 self.gs.delete_file(self._bucket, posixpath.join(path, f))
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700506 def download_file(self, *args):
507 self.gs.download_file(self._bucket, *args)
508 def upload_dir_contents(self, source_dir, **kwargs):
509 self.gs.upload_dir_contents(source_dir, self._bucket, **kwargs)
510
511class LocalFileSystemDataStore(DataStore):
512 def __init__(self, data_store_location):
513 self._base_dir = data_store_location
514 def target_name(self):
515 return self._base_dir
516 def target_type(self):
517 return self._base_dir
518 def does_storage_object_exist(self, name, *args):
519 return os.path.isfile(os.path.join(self._base_dir, name))
rmistryc33c79c2016-02-03 04:27:54 -0800520 def delete_path(self, path):
521 shutil.rmtree(path)
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700522 def download_file(self, name, local_path, *args):
523 shutil.copyfile(os.path.join(self._base_dir, name), local_path)
524 def upload_dir_contents(self, source_dir, dest_dir, **kwargs):
525 def copytree(source_dir, dest_dir):
526 if not os.path.exists(dest_dir):
527 os.makedirs(dest_dir)
528 for item in os.listdir(source_dir):
529 source = os.path.join(source_dir, item)
530 dest = os.path.join(dest_dir, item)
531 if os.path.isdir(source):
532 copytree(source, dest)
533 else:
534 shutil.copy2(source, dest)
535 copytree(source_dir, os.path.join(self._base_dir, dest_dir))
borenetdc89ca52014-10-17 07:37:05 -0700536
537if '__main__' == __name__:
538 option_parser = optparse.OptionParser()
539 option_parser.add_option(
540 '', '--page_sets',
541 help='Specifies the page sets to use to archive. Supports globs.',
542 default='all')
543 option_parser.add_option(
borenetdc89ca52014-10-17 07:37:05 -0700544 '', '--record', action='store_true',
545 help='Specifies whether a new website archive should be created.',
546 default=False)
547 option_parser.add_option(
borenetdc89ca52014-10-17 07:37:05 -0700548 '', '--skia_tools',
549 help=('Path to compiled Skia executable tools. '
550 'render_pictures/render_pdfs is run on the set '
551 'after all SKPs are captured. If the script is run without '
552 '--non-interactive then the debugger is also run at the end. Debug '
553 'builds are recommended because they seem to catch more failures '
554 'than Release builds.'),
555 default=None)
556 option_parser.add_option(
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700557 '', '--upload', action='store_true',
558 help=('Uploads to Google Storage or copies to local filesystem storage '
559 ' if this is True.'),
borenetdc89ca52014-10-17 07:37:05 -0700560 default=False)
561 option_parser.add_option(
rmistry0575c492016-02-01 10:27:05 -0800562 '', '--upload_to_partner_bucket', action='store_true',
563 help=('Uploads SKPs to the chrome-partner-telemetry Google Storage '
564 'bucket if true.'),
565 default=False)
566 option_parser.add_option(
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700567 '', '--data_store',
568 help=('The location of the file storage to use to download and upload '
569 'files. Can be \'gs://<bucket>\' for Google Storage, or '
570 'a directory for local filesystem storage'),
571 default='gs://chromium-skia-gm')
572 option_parser.add_option(
borenetdc89ca52014-10-17 07:37:05 -0700573 '', '--alternate_upload_dir',
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700574 help= ('Uploads to a different directory in Google Storage or local '
575 'storage if this flag is specified'),
borenetdc89ca52014-10-17 07:37:05 -0700576 default=None)
577 option_parser.add_option(
578 '', '--output_dir',
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700579 help=('Temporary directory where SKPs and webpage archives will be '
580 'outputted to.'),
borenetdc89ca52014-10-17 07:37:05 -0700581 default=tempfile.gettempdir())
582 option_parser.add_option(
583 '', '--browser_executable',
584 help='The exact browser executable to run.',
585 default=None)
586 option_parser.add_option(
rmistryaa31ee72015-04-23 12:47:33 -0700587 '', '--browser_extra_args',
588 help='Additional arguments to pass to the browser.',
589 default=None)
590 option_parser.add_option(
borenetdc89ca52014-10-17 07:37:05 -0700591 '', '--chrome_src_path',
592 help='Path to the chromium src directory.',
593 default=None)
594 option_parser.add_option(
595 '', '--non-interactive', action='store_true',
596 help='Runs the script without any prompts. If this flag is specified and '
597 '--skia_tools is specified then the debugger is not run.',
598 default=False)
rmistryaa31ee72015-04-23 12:47:33 -0700599 option_parser.add_option(
600 '', '--skp_prefix',
601 help='Prefix to add to the names of generated SKPs.',
602 default=None)
borenetdc89ca52014-10-17 07:37:05 -0700603 options, unused_args = option_parser.parse_args()
604
605 playback = SkPicturePlayback(options)
606 sys.exit(playback.Run())