blob: e81f77c57c948678efa301d34d76746d47fedf2d [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
rmistry80bd3ae2015-04-03 08:22:51 -0700389 for prefix in ('http___', 'https___', 'www_'):
rmistry2a3c8492015-03-31 10:59:15 -0700390 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)
rmistry80bd3ae2015-04-03 08:22:51 -0700410 filename = filename.lower()
rmistry49d093c2015-03-31 05:04:29 -0700411
borenetdc89ca52014-10-17 07:37:05 -0700412 # We choose the largest .skp as the most likely to be interesting.
413 largest_skp = max(glob.glob(os.path.join(site, '*.skp')),
414 key=lambda path: os.stat(path).st_size)
415 dest = os.path.join(self._local_skp_dir, filename)
416 print 'Moving', largest_skp, 'to', dest
417 shutil.move(largest_skp, dest)
418 self._skp_files.append(filename)
419 shutil.rmtree(site)
420
421 def _CreateLocalStorageDirs(self):
422 """Creates required local storage directories for this script."""
423 for d in (self._local_record_webpages_archive_dir,
424 self._local_skp_dir):
425 if os.path.exists(d):
426 shutil.rmtree(d)
427 os.makedirs(d)
428
rmistry7620bf02014-10-27 06:42:11 -0700429 def _DownloadWebpagesArchive(self, wpr_data_file, page_set_json_name):
borenetdc89ca52014-10-17 07:37:05 -0700430 """Downloads the webpages archive and its required page set from GS."""
431 wpr_source = posixpath.join(ROOT_PLAYBACK_DIR_NAME, 'webpages_archive',
432 wpr_data_file)
433 page_set_source = posixpath.join(ROOT_PLAYBACK_DIR_NAME,
434 'webpages_archive',
rmistry7620bf02014-10-27 06:42:11 -0700435 page_set_json_name)
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700436 gs = self.gs
437 if (gs.does_storage_object_exist(wpr_source) and
438 gs.does_storage_object_exist(page_set_source)):
439 gs.download_file(wpr_source,
borenetdc89ca52014-10-17 07:37:05 -0700440 os.path.join(LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR,
441 wpr_data_file))
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700442 gs.download_file(page_set_source,
borenetdc89ca52014-10-17 07:37:05 -0700443 os.path.join(LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR,
rmistry7620bf02014-10-27 06:42:11 -0700444 page_set_json_name))
borenetdc89ca52014-10-17 07:37:05 -0700445 else:
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700446 raise Exception('%s and %s do not exist in %s!' % (gs.target_type(),
447 wpr_source, page_set_source))
borenetdc89ca52014-10-17 07:37:05 -0700448
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700449class DataStore:
450 """An abstract base class for uploading recordings to a data storage.
451 The interface emulates the google storage api."""
452 def target_name(self):
453 raise NotImplementedError()
454 def target_type(self):
455 raise NotImplementedError()
456 def does_storage_object_exist(self, *args):
457 raise NotImplementedError()
458 def download_file(self, *args):
459 raise NotImplementedError()
460 def upload_dir_contents(self, source_dir, **kwargs):
461 raise NotImplementedError()
462
463class GoogleStorageDataStore(DataStore):
464 def __init__(self, data_store_url):
465 self._data_store_url = data_store_url
rmistry49d093c2015-03-31 05:04:29 -0700466 self._bucket = remove_prefix(self._data_store_url.lstrip(),
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700467 gs_utils.GS_PREFIX)
468 self.gs = gs_utils.GSUtils()
469 def target_name(self):
470 return self._data_store_url
471 def target_type(self):
472 return 'Google Storage'
473 def does_storage_object_exist(self, *args):
474 return self.gs.does_storage_object_exist(self._bucket, *args)
475 def download_file(self, *args):
476 self.gs.download_file(self._bucket, *args)
477 def upload_dir_contents(self, source_dir, **kwargs):
478 self.gs.upload_dir_contents(source_dir, self._bucket, **kwargs)
479
480class LocalFileSystemDataStore(DataStore):
481 def __init__(self, data_store_location):
482 self._base_dir = data_store_location
483 def target_name(self):
484 return self._base_dir
485 def target_type(self):
486 return self._base_dir
487 def does_storage_object_exist(self, name, *args):
488 return os.path.isfile(os.path.join(self._base_dir, name))
489 def download_file(self, name, local_path, *args):
490 shutil.copyfile(os.path.join(self._base_dir, name), local_path)
491 def upload_dir_contents(self, source_dir, dest_dir, **kwargs):
492 def copytree(source_dir, dest_dir):
493 if not os.path.exists(dest_dir):
494 os.makedirs(dest_dir)
495 for item in os.listdir(source_dir):
496 source = os.path.join(source_dir, item)
497 dest = os.path.join(dest_dir, item)
498 if os.path.isdir(source):
499 copytree(source, dest)
500 else:
501 shutil.copy2(source, dest)
502 copytree(source_dir, os.path.join(self._base_dir, dest_dir))
borenetdc89ca52014-10-17 07:37:05 -0700503
504if '__main__' == __name__:
505 option_parser = optparse.OptionParser()
506 option_parser.add_option(
507 '', '--page_sets',
508 help='Specifies the page sets to use to archive. Supports globs.',
509 default='all')
510 option_parser.add_option(
borenetdc89ca52014-10-17 07:37:05 -0700511 '', '--record', action='store_true',
512 help='Specifies whether a new website archive should be created.',
513 default=False)
514 option_parser.add_option(
borenetdc89ca52014-10-17 07:37:05 -0700515 '', '--skia_tools',
516 help=('Path to compiled Skia executable tools. '
517 'render_pictures/render_pdfs is run on the set '
518 'after all SKPs are captured. If the script is run without '
519 '--non-interactive then the debugger is also run at the end. Debug '
520 'builds are recommended because they seem to catch more failures '
521 'than Release builds.'),
522 default=None)
523 option_parser.add_option(
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700524 '', '--upload', action='store_true',
525 help=('Uploads to Google Storage or copies to local filesystem storage '
526 ' if this is True.'),
borenetdc89ca52014-10-17 07:37:05 -0700527 default=False)
528 option_parser.add_option(
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700529 '', '--data_store',
530 help=('The location of the file storage to use to download and upload '
531 'files. Can be \'gs://<bucket>\' for Google Storage, or '
532 'a directory for local filesystem storage'),
533 default='gs://chromium-skia-gm')
534 option_parser.add_option(
borenetdc89ca52014-10-17 07:37:05 -0700535 '', '--alternate_upload_dir',
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700536 help= ('Uploads to a different directory in Google Storage or local '
537 'storage if this flag is specified'),
borenetdc89ca52014-10-17 07:37:05 -0700538 default=None)
539 option_parser.add_option(
540 '', '--output_dir',
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700541 help=('Temporary directory where SKPs and webpage archives will be '
542 'outputted to.'),
borenetdc89ca52014-10-17 07:37:05 -0700543 default=tempfile.gettempdir())
544 option_parser.add_option(
545 '', '--browser_executable',
546 help='The exact browser executable to run.',
547 default=None)
548 option_parser.add_option(
549 '', '--chrome_src_path',
550 help='Path to the chromium src directory.',
551 default=None)
552 option_parser.add_option(
553 '', '--non-interactive', action='store_true',
554 help='Runs the script without any prompts. If this flag is specified and '
555 '--skia_tools is specified then the debugger is not run.',
556 default=False)
557 options, unused_args = option_parser.parse_args()
558
559 playback = SkPicturePlayback(options)
560 sys.exit(playback.Run())