blob: 7fbb372fd39c6a69373a90b9520ec869cef6bd9a [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',
rmistry49d093c2015-03-31 05:04:29 -0700124 # TODO(rmistry): Uncomment the below after it is verified that the above
125 # works.
126 # 'top_25_smooth.py': 'top25desk',
127}
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
143
rmistry49d093c2015-03-31 05:04:29 -0700144 self._chrome_page_sets_path = os.path.join(parse_options.chrome_src_path,
145 CHROMIUM_PAGE_SETS_PATH)
borenetdc89ca52014-10-17 07:37:05 -0700146 self._all_page_sets_specified = parse_options.page_sets == 'all'
147 self._page_sets = self._ParsePageSets(parse_options.page_sets)
148
borenetdc89ca52014-10-17 07:37:05 -0700149 self._record = parse_options.record
150 self._skia_tools = parse_options.skia_tools
151 self._non_interactive = parse_options.non_interactive
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700152 self._upload = parse_options.upload
153 data_store_location = parse_options.data_store
154 if data_store_location.startswith(gs_utils.GS_PREFIX):
155 self.gs = GoogleStorageDataStore(data_store_location)
156 else:
157 self.gs = LocalFileSystemDataStore(data_store_location)
borenetdc89ca52014-10-17 07:37:05 -0700158 self._alternate_upload_dir = parse_options.alternate_upload_dir
borenetdc89ca52014-10-17 07:37:05 -0700159 self._telemetry_binaries_dir = os.path.join(parse_options.chrome_src_path,
160 'tools', 'perf')
161
162 self._local_skp_dir = os.path.join(
163 parse_options.output_dir, ROOT_PLAYBACK_DIR_NAME, SKPICTURES_DIR_NAME)
164 self._local_record_webpages_archive_dir = os.path.join(
165 parse_options.output_dir, ROOT_PLAYBACK_DIR_NAME, 'webpages_archive')
166
167 # List of SKP files generated by this script.
168 self._skp_files = []
169
170 def _ParsePageSets(self, page_sets):
171 if not page_sets:
172 raise ValueError('Must specify at least one page_set!')
173 elif self._all_page_sets_specified:
174 # Get everything from the page_sets directory.
175 page_sets_dir = os.path.join(os.path.abspath(os.path.dirname(__file__)),
176 'page_sets')
177 ps = [os.path.join(page_sets_dir, page_set)
178 for page_set in os.listdir(page_sets_dir)
179 if not os.path.isdir(os.path.join(page_sets_dir, page_set)) and
180 page_set.endswith('.py')]
rmistry49d093c2015-03-31 05:04:29 -0700181 chromium_ps = [
182 os.path.join(self._chrome_page_sets_path, cr_page_set)
183 for cr_page_set in CHROMIUM_PAGE_SETS_TO_PREFIX]
184 ps.extend(chromium_ps)
borenetdc89ca52014-10-17 07:37:05 -0700185 elif '*' in page_sets:
186 # Explode and return the glob.
187 ps = glob.glob(page_sets)
188 else:
189 ps = page_sets.split(',')
190 ps.sort()
191 return ps
192
rmistry49d093c2015-03-31 05:04:29 -0700193 def _IsChromiumPageSet(self, page_set):
194 """Returns true if the specified page set is a Chromium page set."""
195 return page_set.startswith(self._chrome_page_sets_path)
196
borenetdc89ca52014-10-17 07:37:05 -0700197 def Run(self):
198 """Run the SkPicturePlayback BuildStep."""
199
rmistryf802f322014-10-22 05:04:43 -0700200 # Download the credentials file if it was not previously downloaded.
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700201 if not os.path.isfile(CREDENTIALS_FILE_PATH):
202 # Download the credentials.json file from Google Storage.
203 self.gs.download_file(CREDENTIALS_GS_PATH, CREDENTIALS_FILE_PATH)
204
205 if not os.path.isfile(CREDENTIALS_FILE_PATH):
206 print """\n\nCould not locate credentials file in the storage.
207 Please create a %s file that contains:
rmistryf802f322014-10-22 05:04:43 -0700208 {
209 "google": {
210 "username": "google_testing_account_username",
211 "password": "google_testing_account_password"
212 },
213 "facebook": {
214 "username": "facebook_testing_account_username",
215 "password": "facebook_testing_account_password"
216 }
217 }\n\n""" % CREDENTIALS_FILE_PATH
218 raw_input("Please press a key when you are ready to proceed...")
rmistryf802f322014-10-22 05:04:43 -0700219
borenetdc89ca52014-10-17 07:37:05 -0700220 # Delete any left over data files in the data directory.
221 for archive_file in glob.glob(
222 os.path.join(LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR, 'skia_*')):
223 os.remove(archive_file)
224
225 # Delete the local root directory if it already exists.
226 if os.path.exists(LOCAL_PLAYBACK_ROOT_DIR):
227 shutil.rmtree(LOCAL_PLAYBACK_ROOT_DIR)
228
229 # Create the required local storage directories.
230 self._CreateLocalStorageDirs()
231
232 # Start the timer.
233 start_time = time.time()
234
235 # Loop through all page_sets.
236 for page_set in self._page_sets:
237
rmistry7620bf02014-10-27 06:42:11 -0700238 page_set_basename = os.path.basename(page_set).split('.')[0]
239 page_set_json_name = page_set_basename + '.json'
borenetdc89ca52014-10-17 07:37:05 -0700240 wpr_data_file = page_set.split(os.path.sep)[-1].split('.')[0] + '_000.wpr'
rmistry7620bf02014-10-27 06:42:11 -0700241 page_set_dir = os.path.dirname(page_set)
borenetdc89ca52014-10-17 07:37:05 -0700242
rmistry49d093c2015-03-31 05:04:29 -0700243 if self._IsChromiumPageSet(page_set):
244 print 'Using Chromium\'s captured archives for Chromium\'s page sets.'
245 elif self._record:
borenetdc89ca52014-10-17 07:37:05 -0700246 # Create an archive of the specified webpages if '--record=True' is
247 # specified.
248 record_wpr_cmd = (
rmistry7620bf02014-10-27 06:42:11 -0700249 'PYTHONPATH=%s:$PYTHONPATH' % page_set_dir,
borenetdc89ca52014-10-17 07:37:05 -0700250 'DISPLAY=%s' % X11_DISPLAY,
251 os.path.join(self._telemetry_binaries_dir, 'record_wpr'),
252 '--extra-browser-args=--disable-setuid-sandbox',
253 '--browser=exact',
254 '--browser-executable=%s' % self._browser_executable,
rmistry7620bf02014-10-27 06:42:11 -0700255 '%s_page_set' % page_set_basename,
256 '--page-set-base-dir=%s' % page_set_dir
borenetdc89ca52014-10-17 07:37:05 -0700257 )
258 for _ in range(RETRY_RECORD_WPR_COUNT):
rmistry0ec28af2014-10-28 14:25:17 -0700259 try:
260 shell_utils.run(' '.join(record_wpr_cmd), shell=True)
kkinnunenf9310fe2015-03-29 22:33:16 -0700261
262 # Move over the created archive into the local webpages archive
263 # directory.
264 shutil.move(
265 os.path.join(LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR, wpr_data_file),
266 self._local_record_webpages_archive_dir)
267 shutil.move(
268 os.path.join(LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR,
269 page_set_json_name),
270 self._local_record_webpages_archive_dir)
271
borenetdc89ca52014-10-17 07:37:05 -0700272 # Break out of the retry loop since there were no errors.
273 break
rmistry0ec28af2014-10-28 14:25:17 -0700274 except Exception:
275 # There was a failure continue with the loop.
276 traceback.print_exc()
borenetdc89ca52014-10-17 07:37:05 -0700277 else:
278 # If we get here then record_wpr did not succeed and thus did not
279 # break out of the loop.
280 raise Exception('record_wpr failed for page_set: %s' % page_set)
281
282 else:
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700283 # Get the webpages archive so that it can be replayed.
284 self._DownloadWebpagesArchive(wpr_data_file, page_set_json_name)
borenetdc89ca52014-10-17 07:37:05 -0700285
borenet78399152014-10-17 12:15:46 -0700286 run_benchmark_cmd = (
rmistryf802f322014-10-22 05:04:43 -0700287 'PYTHONPATH=%s:$PYTHONPATH' % page_set_dir,
borenetdc89ca52014-10-17 07:37:05 -0700288 'DISPLAY=%s' % X11_DISPLAY,
289 'timeout', '300',
borenet78399152014-10-17 12:15:46 -0700290 os.path.join(self._telemetry_binaries_dir, 'run_benchmark'),
borenetdc89ca52014-10-17 07:37:05 -0700291 '--extra-browser-args=--disable-setuid-sandbox',
292 '--browser=exact',
293 '--browser-executable=%s' % self._browser_executable,
294 SKP_BENCHMARK,
rmistry7620bf02014-10-27 06:42:11 -0700295 '--page-set-name=%s' % page_set_basename,
rmistryf802f322014-10-22 05:04:43 -0700296 '--page-set-base-dir=%s' % page_set_dir,
297 '--skp-outdir=%s' % TMP_SKP_DIR,
298 '--also-run-disabled-tests'
borenetdc89ca52014-10-17 07:37:05 -0700299 )
borenetdc89ca52014-10-17 07:37:05 -0700300
301 for _ in range(RETRY_RUN_MEASUREMENT_COUNT):
302 try:
303 print '\n\n=======Capturing SKP of %s=======\n\n' % page_set
borenet78399152014-10-17 12:15:46 -0700304 shell_utils.run(' '.join(run_benchmark_cmd), shell=True)
borenetdc89ca52014-10-17 07:37:05 -0700305 except shell_utils.CommandFailedException:
306 # skpicture_printer sometimes fails with AssertionError but the
307 # captured SKP is still valid. This is a known issue.
308 pass
309
borenetdc89ca52014-10-17 07:37:05 -0700310 # Rename generated SKP files into more descriptive names.
311 try:
312 self._RenameSkpFiles(page_set)
313 # Break out of the retry loop since there were no errors.
314 break
315 except Exception:
316 # There was a failure continue with the loop.
317 traceback.print_exc()
318 print '\n\n=======Retrying %s=======\n\n' % page_set
319 time.sleep(10)
320 else:
borenet78399152014-10-17 12:15:46 -0700321 # If we get here then run_benchmark did not succeed and thus did not
borenetdc89ca52014-10-17 07:37:05 -0700322 # break out of the loop.
borenet78399152014-10-17 12:15:46 -0700323 raise Exception('run_benchmark failed for page_set: %s' % page_set)
borenetdc89ca52014-10-17 07:37:05 -0700324
borenetdc89ca52014-10-17 07:37:05 -0700325 print '\n\n=======Capturing SKP files took %s seconds=======\n\n' % (
326 time.time() - start_time)
327
328 if self._skia_tools:
329 render_pictures_cmd = [
330 os.path.join(self._skia_tools, 'render_pictures'),
331 '-r', self._local_skp_dir
332 ]
333 render_pdfs_cmd = [
334 os.path.join(self._skia_tools, 'render_pdfs'),
kkinnunen3a6aa862014-12-03 04:22:06 -0800335 '-r', self._local_skp_dir
borenetdc89ca52014-10-17 07:37:05 -0700336 ]
337
338 for tools_cmd in (render_pictures_cmd, render_pdfs_cmd):
339 print '\n\n=======Running %s=======' % ' '.join(tools_cmd)
340 proc = subprocess.Popen(tools_cmd)
rmistry0ec28af2014-10-28 14:25:17 -0700341 (code, _) = shell_utils.log_process_after_completion(proc, echo=False)
borenetdc89ca52014-10-17 07:37:05 -0700342 if code != 0:
343 raise Exception('%s failed!' % ' '.join(tools_cmd))
344
345 if not self._non_interactive:
346 print '\n\n=======Running debugger======='
347 os.system('%s %s' % (os.path.join(self._skia_tools, 'debugger'),
kkinnunen960fb502014-12-03 06:18:12 -0800348 self._local_skp_dir))
borenetdc89ca52014-10-17 07:37:05 -0700349
350 print '\n\n'
351
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700352 if self._upload:
353 print '\n\n=======Uploading to %s=======\n\n' % self.gs.target_type()
borenetdc89ca52014-10-17 07:37:05 -0700354 # Copy the directory structure in the root directory into Google Storage.
355 dest_dir_name = ROOT_PLAYBACK_DIR_NAME
356 if self._alternate_upload_dir:
357 dest_dir_name = self._alternate_upload_dir
358
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700359 self.gs.upload_dir_contents(
rmistry009b0692015-03-31 05:20:12 -0700360 LOCAL_PLAYBACK_ROOT_DIR, dest_dir=dest_dir_name,
borenetdc89ca52014-10-17 07:37:05 -0700361 upload_if=gs_utils.GSUtils.UploadIf.IF_MODIFIED,
362 predefined_acl=GS_PREDEFINED_ACL,
363 fine_grained_acl_list=GS_FINE_GRAINED_ACL_LIST)
364
365 print '\n\n=======New SKPs have been uploaded to %s =======\n\n' % (
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700366 posixpath.join(self.gs.target_name(), dest_dir_name,
367 SKPICTURES_DIR_NAME))
borenetdc89ca52014-10-17 07:37:05 -0700368 else:
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700369 print '\n\n=======Not Uploading to %s=======\n\n' % self.gs.target_type()
borenetdc89ca52014-10-17 07:37:05 -0700370 print 'Generated resources are available in %s\n\n' % (
371 LOCAL_PLAYBACK_ROOT_DIR)
372
373 return 0
374
rmistry49d093c2015-03-31 05:04:29 -0700375 def _GetSkiaSkpFileName(self, page_set):
376 """Returns the SKP file name for Skia page sets."""
377 # /path/to/skia_yahooanswers_desktop.py -> skia_yahooanswers_desktop.py
378 ps_filename = os.path.basename(page_set)
379 # skia_yahooanswers_desktop.py -> skia_yahooanswers_desktop
380 ps_basename, _ = os.path.splitext(ps_filename)
381 # skia_yahooanswers_desktop -> skia, yahooanswers, desktop
382 _, page_name, device = ps_basename.split('_')
383 basename = '%s_%s' % (DEVICE_TO_PLATFORM_PREFIX[device], page_name)
384 return basename[:MAX_SKP_BASE_NAME_LEN] + '.skp'
385
386 def _GetChromiumSkpFileName(self, page_set, site):
387 """Returns the SKP file name for Chromium page sets."""
388 # /path/to/http___mobile_news_sandbox_pt0 -> http___mobile_news_sandbox_pt0
389 _, webpage = os.path.split(site)
390 # http___mobile_news_sandbox_pt0 -> mobile_news_sandbox_pt0
rmistry39383a12015-03-31 08:03:02 -0700391 if webpage.startswith('http___'):
392 webpage = webpage[7:]
393 elif webpage.startswith('https___'):
394 webpage = webpage[8:]
rmistry49d093c2015-03-31 05:04:29 -0700395 # /path/to/skia_yahooanswers_desktop.py -> skia_yahooanswers_desktop.py
396 ps_filename = os.path.basename(page_set)
397 # http___mobile_news_sandbox -> pagesetprefix_http___mobile_news_sandbox
398 basename = '%s_%s' % (CHROMIUM_PAGE_SETS_TO_PREFIX[ps_filename], webpage)
399 return basename[:MAX_SKP_BASE_NAME_LEN] + '.skp'
400
borenetdc89ca52014-10-17 07:37:05 -0700401 def _RenameSkpFiles(self, page_set):
402 """Rename generated SKP files into more descriptive names.
403
404 Look into the subdirectory of TMP_SKP_DIR and find the most interesting
405 .skp in there to be this page_set's representative .skp.
406 """
borenetdc89ca52014-10-17 07:37:05 -0700407 subdirs = glob.glob(os.path.join(TMP_SKP_DIR, '*'))
borenetdc89ca52014-10-17 07:37:05 -0700408 for site in subdirs:
rmistry49d093c2015-03-31 05:04:29 -0700409 if self._IsChromiumPageSet(page_set):
410 filename = self._GetChromiumSkpFileName(page_set, site)
411 else:
412 filename = self._GetSkiaSkpFileName(page_set)
413
borenetdc89ca52014-10-17 07:37:05 -0700414 # We choose the largest .skp as the most likely to be interesting.
415 largest_skp = max(glob.glob(os.path.join(site, '*.skp')),
416 key=lambda path: os.stat(path).st_size)
417 dest = os.path.join(self._local_skp_dir, filename)
418 print 'Moving', largest_skp, 'to', dest
419 shutil.move(largest_skp, dest)
420 self._skp_files.append(filename)
421 shutil.rmtree(site)
422
423 def _CreateLocalStorageDirs(self):
424 """Creates required local storage directories for this script."""
425 for d in (self._local_record_webpages_archive_dir,
426 self._local_skp_dir):
427 if os.path.exists(d):
428 shutil.rmtree(d)
429 os.makedirs(d)
430
rmistry7620bf02014-10-27 06:42:11 -0700431 def _DownloadWebpagesArchive(self, wpr_data_file, page_set_json_name):
borenetdc89ca52014-10-17 07:37:05 -0700432 """Downloads the webpages archive and its required page set from GS."""
433 wpr_source = posixpath.join(ROOT_PLAYBACK_DIR_NAME, 'webpages_archive',
434 wpr_data_file)
435 page_set_source = posixpath.join(ROOT_PLAYBACK_DIR_NAME,
436 'webpages_archive',
rmistry7620bf02014-10-27 06:42:11 -0700437 page_set_json_name)
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700438 gs = self.gs
439 if (gs.does_storage_object_exist(wpr_source) and
440 gs.does_storage_object_exist(page_set_source)):
441 gs.download_file(wpr_source,
borenetdc89ca52014-10-17 07:37:05 -0700442 os.path.join(LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR,
443 wpr_data_file))
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700444 gs.download_file(page_set_source,
borenetdc89ca52014-10-17 07:37:05 -0700445 os.path.join(LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR,
rmistry7620bf02014-10-27 06:42:11 -0700446 page_set_json_name))
borenetdc89ca52014-10-17 07:37:05 -0700447 else:
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700448 raise Exception('%s and %s do not exist in %s!' % (gs.target_type(),
449 wpr_source, page_set_source))
borenetdc89ca52014-10-17 07:37:05 -0700450
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700451class DataStore:
452 """An abstract base class for uploading recordings to a data storage.
453 The interface emulates the google storage api."""
454 def target_name(self):
455 raise NotImplementedError()
456 def target_type(self):
457 raise NotImplementedError()
458 def does_storage_object_exist(self, *args):
459 raise NotImplementedError()
460 def download_file(self, *args):
461 raise NotImplementedError()
462 def upload_dir_contents(self, source_dir, **kwargs):
463 raise NotImplementedError()
464
465class GoogleStorageDataStore(DataStore):
466 def __init__(self, data_store_url):
467 self._data_store_url = data_store_url
rmistry49d093c2015-03-31 05:04:29 -0700468 self._bucket = remove_prefix(self._data_store_url.lstrip(),
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700469 gs_utils.GS_PREFIX)
470 self.gs = gs_utils.GSUtils()
471 def target_name(self):
472 return self._data_store_url
473 def target_type(self):
474 return 'Google Storage'
475 def does_storage_object_exist(self, *args):
476 return self.gs.does_storage_object_exist(self._bucket, *args)
477 def download_file(self, *args):
478 self.gs.download_file(self._bucket, *args)
479 def upload_dir_contents(self, source_dir, **kwargs):
480 self.gs.upload_dir_contents(source_dir, self._bucket, **kwargs)
481
482class LocalFileSystemDataStore(DataStore):
483 def __init__(self, data_store_location):
484 self._base_dir = data_store_location
485 def target_name(self):
486 return self._base_dir
487 def target_type(self):
488 return self._base_dir
489 def does_storage_object_exist(self, name, *args):
490 return os.path.isfile(os.path.join(self._base_dir, name))
491 def download_file(self, name, local_path, *args):
492 shutil.copyfile(os.path.join(self._base_dir, name), local_path)
493 def upload_dir_contents(self, source_dir, dest_dir, **kwargs):
494 def copytree(source_dir, dest_dir):
495 if not os.path.exists(dest_dir):
496 os.makedirs(dest_dir)
497 for item in os.listdir(source_dir):
498 source = os.path.join(source_dir, item)
499 dest = os.path.join(dest_dir, item)
500 if os.path.isdir(source):
501 copytree(source, dest)
502 else:
503 shutil.copy2(source, dest)
504 copytree(source_dir, os.path.join(self._base_dir, dest_dir))
borenetdc89ca52014-10-17 07:37:05 -0700505
506if '__main__' == __name__:
507 option_parser = optparse.OptionParser()
508 option_parser.add_option(
509 '', '--page_sets',
510 help='Specifies the page sets to use to archive. Supports globs.',
511 default='all')
512 option_parser.add_option(
borenetdc89ca52014-10-17 07:37:05 -0700513 '', '--record', action='store_true',
514 help='Specifies whether a new website archive should be created.',
515 default=False)
516 option_parser.add_option(
borenetdc89ca52014-10-17 07:37:05 -0700517 '', '--skia_tools',
518 help=('Path to compiled Skia executable tools. '
519 'render_pictures/render_pdfs is run on the set '
520 'after all SKPs are captured. If the script is run without '
521 '--non-interactive then the debugger is also run at the end. Debug '
522 'builds are recommended because they seem to catch more failures '
523 'than Release builds.'),
524 default=None)
525 option_parser.add_option(
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700526 '', '--upload', action='store_true',
527 help=('Uploads to Google Storage or copies to local filesystem storage '
528 ' if this is True.'),
borenetdc89ca52014-10-17 07:37:05 -0700529 default=False)
530 option_parser.add_option(
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700531 '', '--data_store',
532 help=('The location of the file storage to use to download and upload '
533 'files. Can be \'gs://<bucket>\' for Google Storage, or '
534 'a directory for local filesystem storage'),
535 default='gs://chromium-skia-gm')
536 option_parser.add_option(
borenetdc89ca52014-10-17 07:37:05 -0700537 '', '--alternate_upload_dir',
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700538 help= ('Uploads to a different directory in Google Storage or local '
539 'storage if this flag is specified'),
borenetdc89ca52014-10-17 07:37:05 -0700540 default=None)
541 option_parser.add_option(
542 '', '--output_dir',
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700543 help=('Temporary directory where SKPs and webpage archives will be '
544 'outputted to.'),
borenetdc89ca52014-10-17 07:37:05 -0700545 default=tempfile.gettempdir())
546 option_parser.add_option(
547 '', '--browser_executable',
548 help='The exact browser executable to run.',
549 default=None)
550 option_parser.add_option(
551 '', '--chrome_src_path',
552 help='Path to the chromium src directory.',
553 default=None)
554 option_parser.add_option(
555 '', '--non-interactive', action='store_true',
556 help='Runs the script without any prompts. If this flag is specified and '
557 '--skia_tools is specified then the debugger is not run.',
558 default=False)
559 options, unused_args = option_parser.parse_args()
560
561 playback = SkPicturePlayback(options)
562 sys.exit(playback.Run())