blob: 7f79c4665df38d4419e8bcafe00d32bd8fbbdd90 [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
66sys.path.insert(0, os.getcwd())
67
68from common.py.utils import gs_utils
69from common.py.utils import shell_utils
70
71ROOT_PLAYBACK_DIR_NAME = 'playback'
72SKPICTURES_DIR_NAME = 'skps'
73
74
75# Local archive and SKP directories.
76LOCAL_PLAYBACK_ROOT_DIR = os.path.join(
77 tempfile.gettempdir(), ROOT_PLAYBACK_DIR_NAME)
78LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR = os.path.join(
79 os.path.abspath(os.path.dirname(__file__)), 'page_sets', 'data')
80TMP_SKP_DIR = tempfile.mkdtemp()
81
rmistryf802f322014-10-22 05:04:43 -070082# Location of the credentials.json file and the string that represents missing
83# passwords.
84CREDENTIALS_FILE_PATH = os.path.join(
85 os.path.abspath(os.path.dirname(__file__)), 'page_sets', 'data',
86 'credentials.json'
87)
88
borenetdc89ca52014-10-17 07:37:05 -070089# Name of the SKP benchmark
90SKP_BENCHMARK = 'skpicture_printer'
91
92# The max base name length of Skp files.
93MAX_SKP_BASE_NAME_LEN = 31
94
95# Dictionary of device to platform prefixes for SKP files.
96DEVICE_TO_PLATFORM_PREFIX = {
97 'desktop': 'desk',
98 'galaxynexus': 'mobi',
99 'nexus10': 'tabl'
100}
101
102# How many times the record_wpr binary should be retried.
103RETRY_RECORD_WPR_COUNT = 5
borenet78399152014-10-17 12:15:46 -0700104# How many times the run_benchmark binary should be retried.
borenetdc89ca52014-10-17 07:37:05 -0700105RETRY_RUN_MEASUREMENT_COUNT = 5
106
rmistryf802f322014-10-22 05:04:43 -0700107# Location of the credentials.json file in Google Storage.
108CREDENTIALS_GS_PATH = '/playback/credentials/credentials.json'
109
borenetdc89ca52014-10-17 07:37:05 -0700110X11_DISPLAY = os.getenv('DISPLAY', ':0')
111
112GS_PREDEFINED_ACL = gs_utils.GSUtils.PredefinedACL.PRIVATE
113GS_FINE_GRAINED_ACL_LIST = [
114 (gs_utils.GSUtils.IdType.GROUP_BY_DOMAIN, 'google.com',
115 gs_utils.GSUtils.Permission.READ),
116]
117
rmistry49d093c2015-03-31 05:04:29 -0700118# Path to Chromium's page sets.
119CHROMIUM_PAGE_SETS_PATH = os.path.join('tools', 'perf', 'page_sets')
120
121# Dictionary of supported Chromium page sets to their file prefixes.
122CHROMIUM_PAGE_SETS_TO_PREFIX = {
rmistry5af9a372015-03-31 06:22:55 -0700123 'key_mobile_sites_smooth.py': 'keymobi',
rmistry2a3c8492015-03-31 10:59:15 -0700124 'top_25_smooth.py': 'top25desk',
rmistry49d093c2015-03-31 05:04:29 -0700125}
126
127
kkinnunene75d2d22014-12-03 04:38:46 -0800128def remove_prefix(s, prefix):
129 if s.startswith(prefix):
130 return s[len(prefix):]
131 return s
borenetdc89ca52014-10-17 07:37:05 -0700132
rmistry49d093c2015-03-31 05:04:29 -0700133
borenetdc89ca52014-10-17 07:37:05 -0700134class SkPicturePlayback(object):
135 """Class that archives or replays webpages and creates SKPs."""
136
137 def __init__(self, parse_options):
138 """Constructs a SkPicturePlayback BuildStep instance."""
139 assert parse_options.browser_executable, 'Must specify --browser_executable'
140 self._browser_executable = parse_options.browser_executable
141
rmistry49d093c2015-03-31 05:04:29 -0700142 self._chrome_page_sets_path = os.path.join(parse_options.chrome_src_path,
143 CHROMIUM_PAGE_SETS_PATH)
borenetdc89ca52014-10-17 07:37:05 -0700144 self._all_page_sets_specified = parse_options.page_sets == 'all'
145 self._page_sets = self._ParsePageSets(parse_options.page_sets)
146
borenetdc89ca52014-10-17 07:37:05 -0700147 self._record = parse_options.record
148 self._skia_tools = parse_options.skia_tools
149 self._non_interactive = parse_options.non_interactive
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700150 self._upload = parse_options.upload
151 data_store_location = parse_options.data_store
152 if data_store_location.startswith(gs_utils.GS_PREFIX):
153 self.gs = GoogleStorageDataStore(data_store_location)
154 else:
155 self.gs = LocalFileSystemDataStore(data_store_location)
borenetdc89ca52014-10-17 07:37:05 -0700156 self._alternate_upload_dir = parse_options.alternate_upload_dir
borenetdc89ca52014-10-17 07:37:05 -0700157 self._telemetry_binaries_dir = os.path.join(parse_options.chrome_src_path,
158 'tools', 'perf')
159
160 self._local_skp_dir = os.path.join(
161 parse_options.output_dir, ROOT_PLAYBACK_DIR_NAME, SKPICTURES_DIR_NAME)
162 self._local_record_webpages_archive_dir = os.path.join(
163 parse_options.output_dir, ROOT_PLAYBACK_DIR_NAME, 'webpages_archive')
164
165 # List of SKP files generated by this script.
166 self._skp_files = []
167
168 def _ParsePageSets(self, page_sets):
169 if not page_sets:
170 raise ValueError('Must specify at least one page_set!')
171 elif self._all_page_sets_specified:
172 # Get everything from the page_sets directory.
173 page_sets_dir = os.path.join(os.path.abspath(os.path.dirname(__file__)),
174 'page_sets')
175 ps = [os.path.join(page_sets_dir, page_set)
176 for page_set in os.listdir(page_sets_dir)
177 if not os.path.isdir(os.path.join(page_sets_dir, page_set)) and
178 page_set.endswith('.py')]
rmistry49d093c2015-03-31 05:04:29 -0700179 chromium_ps = [
180 os.path.join(self._chrome_page_sets_path, cr_page_set)
181 for cr_page_set in CHROMIUM_PAGE_SETS_TO_PREFIX]
182 ps.extend(chromium_ps)
borenetdc89ca52014-10-17 07:37:05 -0700183 elif '*' in page_sets:
184 # Explode and return the glob.
185 ps = glob.glob(page_sets)
186 else:
187 ps = page_sets.split(',')
188 ps.sort()
189 return ps
190
rmistry49d093c2015-03-31 05:04:29 -0700191 def _IsChromiumPageSet(self, page_set):
192 """Returns true if the specified page set is a Chromium page set."""
193 return page_set.startswith(self._chrome_page_sets_path)
194
borenetdc89ca52014-10-17 07:37:05 -0700195 def Run(self):
196 """Run the SkPicturePlayback BuildStep."""
197
rmistryf802f322014-10-22 05:04:43 -0700198 # Download the credentials file if it was not previously downloaded.
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700199 if not os.path.isfile(CREDENTIALS_FILE_PATH):
200 # Download the credentials.json file from Google Storage.
201 self.gs.download_file(CREDENTIALS_GS_PATH, CREDENTIALS_FILE_PATH)
202
203 if not os.path.isfile(CREDENTIALS_FILE_PATH):
204 print """\n\nCould not locate credentials file in the storage.
205 Please create a %s file that contains:
rmistryf802f322014-10-22 05:04:43 -0700206 {
207 "google": {
208 "username": "google_testing_account_username",
209 "password": "google_testing_account_password"
210 },
211 "facebook": {
212 "username": "facebook_testing_account_username",
213 "password": "facebook_testing_account_password"
214 }
215 }\n\n""" % CREDENTIALS_FILE_PATH
216 raw_input("Please press a key when you are ready to proceed...")
rmistryf802f322014-10-22 05:04:43 -0700217
borenetdc89ca52014-10-17 07:37:05 -0700218 # Delete any left over data files in the data directory.
219 for archive_file in glob.glob(
220 os.path.join(LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR, 'skia_*')):
221 os.remove(archive_file)
222
223 # Delete the local root directory if it already exists.
224 if os.path.exists(LOCAL_PLAYBACK_ROOT_DIR):
225 shutil.rmtree(LOCAL_PLAYBACK_ROOT_DIR)
226
227 # Create the required local storage directories.
228 self._CreateLocalStorageDirs()
229
230 # Start the timer.
231 start_time = time.time()
232
233 # Loop through all page_sets.
234 for page_set in self._page_sets:
235
rmistry7620bf02014-10-27 06:42:11 -0700236 page_set_basename = os.path.basename(page_set).split('.')[0]
237 page_set_json_name = page_set_basename + '.json'
borenetdc89ca52014-10-17 07:37:05 -0700238 wpr_data_file = page_set.split(os.path.sep)[-1].split('.')[0] + '_000.wpr'
rmistry7620bf02014-10-27 06:42:11 -0700239 page_set_dir = os.path.dirname(page_set)
borenetdc89ca52014-10-17 07:37:05 -0700240
rmistry49d093c2015-03-31 05:04:29 -0700241 if self._IsChromiumPageSet(page_set):
242 print 'Using Chromium\'s captured archives for Chromium\'s page sets.'
243 elif self._record:
borenetdc89ca52014-10-17 07:37:05 -0700244 # Create an archive of the specified webpages if '--record=True' is
245 # specified.
246 record_wpr_cmd = (
rmistry7620bf02014-10-27 06:42:11 -0700247 'PYTHONPATH=%s:$PYTHONPATH' % page_set_dir,
borenetdc89ca52014-10-17 07:37:05 -0700248 'DISPLAY=%s' % X11_DISPLAY,
249 os.path.join(self._telemetry_binaries_dir, 'record_wpr'),
250 '--extra-browser-args=--disable-setuid-sandbox',
251 '--browser=exact',
252 '--browser-executable=%s' % self._browser_executable,
rmistry7620bf02014-10-27 06:42:11 -0700253 '%s_page_set' % page_set_basename,
254 '--page-set-base-dir=%s' % page_set_dir
borenetdc89ca52014-10-17 07:37:05 -0700255 )
256 for _ in range(RETRY_RECORD_WPR_COUNT):
rmistry0ec28af2014-10-28 14:25:17 -0700257 try:
258 shell_utils.run(' '.join(record_wpr_cmd), shell=True)
kkinnunenf9310fe2015-03-29 22:33:16 -0700259
260 # Move over the created archive into the local webpages archive
261 # directory.
262 shutil.move(
263 os.path.join(LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR, wpr_data_file),
264 self._local_record_webpages_archive_dir)
265 shutil.move(
266 os.path.join(LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR,
267 page_set_json_name),
268 self._local_record_webpages_archive_dir)
269
borenetdc89ca52014-10-17 07:37:05 -0700270 # Break out of the retry loop since there were no errors.
271 break
rmistry0ec28af2014-10-28 14:25:17 -0700272 except Exception:
273 # There was a failure continue with the loop.
274 traceback.print_exc()
borenetdc89ca52014-10-17 07:37:05 -0700275 else:
276 # If we get here then record_wpr did not succeed and thus did not
277 # break out of the loop.
278 raise Exception('record_wpr failed for page_set: %s' % page_set)
279
280 else:
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700281 # Get the webpages archive so that it can be replayed.
282 self._DownloadWebpagesArchive(wpr_data_file, page_set_json_name)
borenetdc89ca52014-10-17 07:37:05 -0700283
borenet78399152014-10-17 12:15:46 -0700284 run_benchmark_cmd = (
rmistryf802f322014-10-22 05:04:43 -0700285 'PYTHONPATH=%s:$PYTHONPATH' % page_set_dir,
borenetdc89ca52014-10-17 07:37:05 -0700286 'DISPLAY=%s' % X11_DISPLAY,
287 'timeout', '300',
borenet78399152014-10-17 12:15:46 -0700288 os.path.join(self._telemetry_binaries_dir, 'run_benchmark'),
borenetdc89ca52014-10-17 07:37:05 -0700289 '--extra-browser-args=--disable-setuid-sandbox',
290 '--browser=exact',
291 '--browser-executable=%s' % self._browser_executable,
292 SKP_BENCHMARK,
rmistry7620bf02014-10-27 06:42:11 -0700293 '--page-set-name=%s' % page_set_basename,
rmistryf802f322014-10-22 05:04:43 -0700294 '--page-set-base-dir=%s' % page_set_dir,
295 '--skp-outdir=%s' % TMP_SKP_DIR,
296 '--also-run-disabled-tests'
borenetdc89ca52014-10-17 07:37:05 -0700297 )
borenetdc89ca52014-10-17 07:37:05 -0700298
299 for _ in range(RETRY_RUN_MEASUREMENT_COUNT):
300 try:
301 print '\n\n=======Capturing SKP of %s=======\n\n' % page_set
borenet78399152014-10-17 12:15:46 -0700302 shell_utils.run(' '.join(run_benchmark_cmd), shell=True)
borenetdc89ca52014-10-17 07:37:05 -0700303 except shell_utils.CommandFailedException:
304 # skpicture_printer sometimes fails with AssertionError but the
305 # captured SKP is still valid. This is a known issue.
306 pass
307
borenetdc89ca52014-10-17 07:37:05 -0700308 # Rename generated SKP files into more descriptive names.
309 try:
310 self._RenameSkpFiles(page_set)
311 # Break out of the retry loop since there were no errors.
312 break
313 except Exception:
314 # There was a failure continue with the loop.
315 traceback.print_exc()
316 print '\n\n=======Retrying %s=======\n\n' % page_set
317 time.sleep(10)
318 else:
borenet78399152014-10-17 12:15:46 -0700319 # If we get here then run_benchmark did not succeed and thus did not
borenetdc89ca52014-10-17 07:37:05 -0700320 # break out of the loop.
borenet78399152014-10-17 12:15:46 -0700321 raise Exception('run_benchmark failed for page_set: %s' % page_set)
borenetdc89ca52014-10-17 07:37:05 -0700322
borenetdc89ca52014-10-17 07:37:05 -0700323 print '\n\n=======Capturing SKP files took %s seconds=======\n\n' % (
324 time.time() - start_time)
325
326 if self._skia_tools:
327 render_pictures_cmd = [
328 os.path.join(self._skia_tools, 'render_pictures'),
329 '-r', self._local_skp_dir
330 ]
331 render_pdfs_cmd = [
332 os.path.join(self._skia_tools, 'render_pdfs'),
kkinnunen3a6aa862014-12-03 04:22:06 -0800333 '-r', self._local_skp_dir
borenetdc89ca52014-10-17 07:37:05 -0700334 ]
335
336 for tools_cmd in (render_pictures_cmd, render_pdfs_cmd):
337 print '\n\n=======Running %s=======' % ' '.join(tools_cmd)
338 proc = subprocess.Popen(tools_cmd)
rmistry0ec28af2014-10-28 14:25:17 -0700339 (code, _) = shell_utils.log_process_after_completion(proc, echo=False)
borenetdc89ca52014-10-17 07:37:05 -0700340 if code != 0:
341 raise Exception('%s failed!' % ' '.join(tools_cmd))
342
343 if not self._non_interactive:
344 print '\n\n=======Running debugger======='
345 os.system('%s %s' % (os.path.join(self._skia_tools, 'debugger'),
kkinnunen960fb502014-12-03 06:18:12 -0800346 self._local_skp_dir))
borenetdc89ca52014-10-17 07:37:05 -0700347
348 print '\n\n'
349
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700350 if self._upload:
351 print '\n\n=======Uploading to %s=======\n\n' % self.gs.target_type()
borenetdc89ca52014-10-17 07:37:05 -0700352 # Copy the directory structure in the root directory into Google Storage.
353 dest_dir_name = ROOT_PLAYBACK_DIR_NAME
354 if self._alternate_upload_dir:
355 dest_dir_name = self._alternate_upload_dir
356
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700357 self.gs.upload_dir_contents(
rmistry009b0692015-03-31 05:20:12 -0700358 LOCAL_PLAYBACK_ROOT_DIR, dest_dir=dest_dir_name,
borenetdc89ca52014-10-17 07:37:05 -0700359 upload_if=gs_utils.GSUtils.UploadIf.IF_MODIFIED,
360 predefined_acl=GS_PREDEFINED_ACL,
361 fine_grained_acl_list=GS_FINE_GRAINED_ACL_LIST)
362
363 print '\n\n=======New SKPs have been uploaded to %s =======\n\n' % (
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700364 posixpath.join(self.gs.target_name(), dest_dir_name,
365 SKPICTURES_DIR_NAME))
borenetdc89ca52014-10-17 07:37:05 -0700366 else:
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700367 print '\n\n=======Not Uploading to %s=======\n\n' % self.gs.target_type()
borenetdc89ca52014-10-17 07:37:05 -0700368 print 'Generated resources are available in %s\n\n' % (
369 LOCAL_PLAYBACK_ROOT_DIR)
370
371 return 0
372
rmistry49d093c2015-03-31 05:04:29 -0700373 def _GetSkiaSkpFileName(self, page_set):
374 """Returns the SKP file name for Skia page sets."""
375 # /path/to/skia_yahooanswers_desktop.py -> skia_yahooanswers_desktop.py
376 ps_filename = os.path.basename(page_set)
377 # skia_yahooanswers_desktop.py -> skia_yahooanswers_desktop
378 ps_basename, _ = os.path.splitext(ps_filename)
379 # skia_yahooanswers_desktop -> skia, yahooanswers, desktop
380 _, page_name, device = ps_basename.split('_')
381 basename = '%s_%s' % (DEVICE_TO_PLATFORM_PREFIX[device], page_name)
382 return basename[:MAX_SKP_BASE_NAME_LEN] + '.skp'
383
384 def _GetChromiumSkpFileName(self, page_set, site):
385 """Returns the SKP file name for Chromium page sets."""
386 # /path/to/http___mobile_news_sandbox_pt0 -> http___mobile_news_sandbox_pt0
387 _, webpage = os.path.split(site)
388 # http___mobile_news_sandbox_pt0 -> mobile_news_sandbox_pt0
rmistry2a3c8492015-03-31 10:59:15 -0700389 for prefix in ('http___', 'https___'):
390 if webpage.startswith(prefix):
391 webpage = webpage[len(prefix):]
rmistry49d093c2015-03-31 05:04:29 -0700392 # /path/to/skia_yahooanswers_desktop.py -> skia_yahooanswers_desktop.py
393 ps_filename = os.path.basename(page_set)
394 # http___mobile_news_sandbox -> pagesetprefix_http___mobile_news_sandbox
395 basename = '%s_%s' % (CHROMIUM_PAGE_SETS_TO_PREFIX[ps_filename], webpage)
396 return basename[:MAX_SKP_BASE_NAME_LEN] + '.skp'
397
borenetdc89ca52014-10-17 07:37:05 -0700398 def _RenameSkpFiles(self, page_set):
399 """Rename generated SKP files into more descriptive names.
400
401 Look into the subdirectory of TMP_SKP_DIR and find the most interesting
402 .skp in there to be this page_set's representative .skp.
403 """
borenetdc89ca52014-10-17 07:37:05 -0700404 subdirs = glob.glob(os.path.join(TMP_SKP_DIR, '*'))
borenetdc89ca52014-10-17 07:37:05 -0700405 for site in subdirs:
rmistry49d093c2015-03-31 05:04:29 -0700406 if self._IsChromiumPageSet(page_set):
407 filename = self._GetChromiumSkpFileName(page_set, site)
408 else:
409 filename = self._GetSkiaSkpFileName(page_set)
410
borenetdc89ca52014-10-17 07:37:05 -0700411 # We choose the largest .skp as the most likely to be interesting.
412 largest_skp = max(glob.glob(os.path.join(site, '*.skp')),
413 key=lambda path: os.stat(path).st_size)
414 dest = os.path.join(self._local_skp_dir, filename)
415 print 'Moving', largest_skp, 'to', dest
416 shutil.move(largest_skp, dest)
417 self._skp_files.append(filename)
418 shutil.rmtree(site)
419
420 def _CreateLocalStorageDirs(self):
421 """Creates required local storage directories for this script."""
422 for d in (self._local_record_webpages_archive_dir,
423 self._local_skp_dir):
424 if os.path.exists(d):
425 shutil.rmtree(d)
426 os.makedirs(d)
427
rmistry7620bf02014-10-27 06:42:11 -0700428 def _DownloadWebpagesArchive(self, wpr_data_file, page_set_json_name):
borenetdc89ca52014-10-17 07:37:05 -0700429 """Downloads the webpages archive and its required page set from GS."""
430 wpr_source = posixpath.join(ROOT_PLAYBACK_DIR_NAME, 'webpages_archive',
431 wpr_data_file)
432 page_set_source = posixpath.join(ROOT_PLAYBACK_DIR_NAME,
433 'webpages_archive',
rmistry7620bf02014-10-27 06:42:11 -0700434 page_set_json_name)
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700435 gs = self.gs
436 if (gs.does_storage_object_exist(wpr_source) and
437 gs.does_storage_object_exist(page_set_source)):
438 gs.download_file(wpr_source,
borenetdc89ca52014-10-17 07:37:05 -0700439 os.path.join(LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR,
440 wpr_data_file))
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700441 gs.download_file(page_set_source,
borenetdc89ca52014-10-17 07:37:05 -0700442 os.path.join(LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR,
rmistry7620bf02014-10-27 06:42:11 -0700443 page_set_json_name))
borenetdc89ca52014-10-17 07:37:05 -0700444 else:
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700445 raise Exception('%s and %s do not exist in %s!' % (gs.target_type(),
446 wpr_source, page_set_source))
borenetdc89ca52014-10-17 07:37:05 -0700447
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700448class DataStore:
449 """An abstract base class for uploading recordings to a data storage.
450 The interface emulates the google storage api."""
451 def target_name(self):
452 raise NotImplementedError()
453 def target_type(self):
454 raise NotImplementedError()
455 def does_storage_object_exist(self, *args):
456 raise NotImplementedError()
457 def download_file(self, *args):
458 raise NotImplementedError()
459 def upload_dir_contents(self, source_dir, **kwargs):
460 raise NotImplementedError()
461
462class GoogleStorageDataStore(DataStore):
463 def __init__(self, data_store_url):
464 self._data_store_url = data_store_url
rmistry49d093c2015-03-31 05:04:29 -0700465 self._bucket = remove_prefix(self._data_store_url.lstrip(),
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700466 gs_utils.GS_PREFIX)
467 self.gs = gs_utils.GSUtils()
468 def target_name(self):
469 return self._data_store_url
470 def target_type(self):
471 return 'Google Storage'
472 def does_storage_object_exist(self, *args):
473 return self.gs.does_storage_object_exist(self._bucket, *args)
474 def download_file(self, *args):
475 self.gs.download_file(self._bucket, *args)
476 def upload_dir_contents(self, source_dir, **kwargs):
477 self.gs.upload_dir_contents(source_dir, self._bucket, **kwargs)
478
479class LocalFileSystemDataStore(DataStore):
480 def __init__(self, data_store_location):
481 self._base_dir = data_store_location
482 def target_name(self):
483 return self._base_dir
484 def target_type(self):
485 return self._base_dir
486 def does_storage_object_exist(self, name, *args):
487 return os.path.isfile(os.path.join(self._base_dir, name))
488 def download_file(self, name, local_path, *args):
489 shutil.copyfile(os.path.join(self._base_dir, name), local_path)
490 def upload_dir_contents(self, source_dir, dest_dir, **kwargs):
491 def copytree(source_dir, dest_dir):
492 if not os.path.exists(dest_dir):
493 os.makedirs(dest_dir)
494 for item in os.listdir(source_dir):
495 source = os.path.join(source_dir, item)
496 dest = os.path.join(dest_dir, item)
497 if os.path.isdir(source):
498 copytree(source, dest)
499 else:
500 shutil.copy2(source, dest)
501 copytree(source_dir, os.path.join(self._base_dir, dest_dir))
borenetdc89ca52014-10-17 07:37:05 -0700502
503if '__main__' == __name__:
504 option_parser = optparse.OptionParser()
505 option_parser.add_option(
506 '', '--page_sets',
507 help='Specifies the page sets to use to archive. Supports globs.',
508 default='all')
509 option_parser.add_option(
borenetdc89ca52014-10-17 07:37:05 -0700510 '', '--record', action='store_true',
511 help='Specifies whether a new website archive should be created.',
512 default=False)
513 option_parser.add_option(
borenetdc89ca52014-10-17 07:37:05 -0700514 '', '--skia_tools',
515 help=('Path to compiled Skia executable tools. '
516 'render_pictures/render_pdfs is run on the set '
517 'after all SKPs are captured. If the script is run without '
518 '--non-interactive then the debugger is also run at the end. Debug '
519 'builds are recommended because they seem to catch more failures '
520 'than Release builds.'),
521 default=None)
522 option_parser.add_option(
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700523 '', '--upload', action='store_true',
524 help=('Uploads to Google Storage or copies to local filesystem storage '
525 ' if this is True.'),
borenetdc89ca52014-10-17 07:37:05 -0700526 default=False)
527 option_parser.add_option(
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700528 '', '--data_store',
529 help=('The location of the file storage to use to download and upload '
530 'files. Can be \'gs://<bucket>\' for Google Storage, or '
531 'a directory for local filesystem storage'),
532 default='gs://chromium-skia-gm')
533 option_parser.add_option(
borenetdc89ca52014-10-17 07:37:05 -0700534 '', '--alternate_upload_dir',
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700535 help= ('Uploads to a different directory in Google Storage or local '
536 'storage if this flag is specified'),
borenetdc89ca52014-10-17 07:37:05 -0700537 default=None)
538 option_parser.add_option(
539 '', '--output_dir',
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700540 help=('Temporary directory where SKPs and webpage archives will be '
541 'outputted to.'),
borenetdc89ca52014-10-17 07:37:05 -0700542 default=tempfile.gettempdir())
543 option_parser.add_option(
544 '', '--browser_executable',
545 help='The exact browser executable to run.',
546 default=None)
547 option_parser.add_option(
548 '', '--chrome_src_path',
549 help='Path to the chromium src directory.',
550 default=None)
551 option_parser.add_option(
552 '', '--non-interactive', action='store_true',
553 help='Runs the script without any prompts. If this flag is specified and '
554 '--skia_tools is specified then the debugger is not run.',
555 default=False)
556 options, unused_args = option_parser.parse_args()
557
558 playback = SkPicturePlayback(options)
559 sys.exit(playback.Run())