blob: 23c6442fa6dfff37ae6c5a8577138e62e33304bf [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
rmistry0575c492016-02-01 10:27:05 -080074PARTNERS_GS_BUCKET = 'gs://chrome-partner-telemetry'
borenetdc89ca52014-10-17 07:37:05 -070075
76# Local archive and SKP directories.
77LOCAL_PLAYBACK_ROOT_DIR = os.path.join(
78 tempfile.gettempdir(), ROOT_PLAYBACK_DIR_NAME)
79LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR = os.path.join(
80 os.path.abspath(os.path.dirname(__file__)), 'page_sets', 'data')
81TMP_SKP_DIR = tempfile.mkdtemp()
82
rmistryf802f322014-10-22 05:04:43 -070083# Location of the credentials.json file and the string that represents missing
84# passwords.
85CREDENTIALS_FILE_PATH = os.path.join(
86 os.path.abspath(os.path.dirname(__file__)), 'page_sets', 'data',
87 'credentials.json'
88)
89
borenetdc89ca52014-10-17 07:37:05 -070090# Name of the SKP benchmark
91SKP_BENCHMARK = 'skpicture_printer'
92
93# The max base name length of Skp files.
94MAX_SKP_BASE_NAME_LEN = 31
95
96# Dictionary of device to platform prefixes for SKP files.
97DEVICE_TO_PLATFORM_PREFIX = {
98 'desktop': 'desk',
99 'galaxynexus': 'mobi',
100 'nexus10': 'tabl'
101}
102
103# How many times the record_wpr binary should be retried.
104RETRY_RECORD_WPR_COUNT = 5
borenet78399152014-10-17 12:15:46 -0700105# How many times the run_benchmark binary should be retried.
borenetdc89ca52014-10-17 07:37:05 -0700106RETRY_RUN_MEASUREMENT_COUNT = 5
107
rmistryf802f322014-10-22 05:04:43 -0700108# Location of the credentials.json file in Google Storage.
109CREDENTIALS_GS_PATH = '/playback/credentials/credentials.json'
110
borenetdc89ca52014-10-17 07:37:05 -0700111X11_DISPLAY = os.getenv('DISPLAY', ':0')
112
113GS_PREDEFINED_ACL = gs_utils.GSUtils.PredefinedACL.PRIVATE
114GS_FINE_GRAINED_ACL_LIST = [
115 (gs_utils.GSUtils.IdType.GROUP_BY_DOMAIN, 'google.com',
116 gs_utils.GSUtils.Permission.READ),
117]
118
rmistry49d093c2015-03-31 05:04:29 -0700119# Path to Chromium's page sets.
120CHROMIUM_PAGE_SETS_PATH = os.path.join('tools', 'perf', 'page_sets')
121
122# Dictionary of supported Chromium page sets to their file prefixes.
123CHROMIUM_PAGE_SETS_TO_PREFIX = {
rmistry5af9a372015-03-31 06:22:55 -0700124 'key_mobile_sites_smooth.py': 'keymobi',
rmistry2a3c8492015-03-31 10:59:15 -0700125 'top_25_smooth.py': 'top25desk',
rmistry49d093c2015-03-31 05:04:29 -0700126}
127
128
kkinnunene75d2d22014-12-03 04:38:46 -0800129def remove_prefix(s, prefix):
130 if s.startswith(prefix):
131 return s[len(prefix):]
132 return s
borenetdc89ca52014-10-17 07:37:05 -0700133
rmistry49d093c2015-03-31 05:04:29 -0700134
borenetdc89ca52014-10-17 07:37:05 -0700135class SkPicturePlayback(object):
136 """Class that archives or replays webpages and creates SKPs."""
137
138 def __init__(self, parse_options):
139 """Constructs a SkPicturePlayback BuildStep instance."""
140 assert parse_options.browser_executable, 'Must specify --browser_executable'
141 self._browser_executable = parse_options.browser_executable
rmistryaa31ee72015-04-23 12:47:33 -0700142 self._browser_args = '--disable-setuid-sandbox'
143 if parse_options.browser_extra_args:
144 self._browser_args = '%s %s' % (
145 self._browser_args, parse_options.browser_extra_args)
borenetdc89ca52014-10-17 07:37:05 -0700146
rmistry49d093c2015-03-31 05:04:29 -0700147 self._chrome_page_sets_path = os.path.join(parse_options.chrome_src_path,
148 CHROMIUM_PAGE_SETS_PATH)
borenetdc89ca52014-10-17 07:37:05 -0700149 self._all_page_sets_specified = parse_options.page_sets == 'all'
150 self._page_sets = self._ParsePageSets(parse_options.page_sets)
151
borenetdc89ca52014-10-17 07:37:05 -0700152 self._record = parse_options.record
153 self._skia_tools = parse_options.skia_tools
154 self._non_interactive = parse_options.non_interactive
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700155 self._upload = parse_options.upload
rmistryaa31ee72015-04-23 12:47:33 -0700156 self._skp_prefix = parse_options.skp_prefix
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700157 data_store_location = parse_options.data_store
158 if data_store_location.startswith(gs_utils.GS_PREFIX):
159 self.gs = GoogleStorageDataStore(data_store_location)
160 else:
161 self.gs = LocalFileSystemDataStore(data_store_location)
rmistry0575c492016-02-01 10:27:05 -0800162 self._upload_to_partner_bucket = parse_options.upload_to_partner_bucket
borenetdc89ca52014-10-17 07:37:05 -0700163 self._alternate_upload_dir = parse_options.alternate_upload_dir
borenetdc89ca52014-10-17 07:37:05 -0700164 self._telemetry_binaries_dir = os.path.join(parse_options.chrome_src_path,
165 'tools', 'perf')
166
167 self._local_skp_dir = os.path.join(
168 parse_options.output_dir, ROOT_PLAYBACK_DIR_NAME, SKPICTURES_DIR_NAME)
169 self._local_record_webpages_archive_dir = os.path.join(
170 parse_options.output_dir, ROOT_PLAYBACK_DIR_NAME, 'webpages_archive')
171
172 # List of SKP files generated by this script.
173 self._skp_files = []
174
175 def _ParsePageSets(self, page_sets):
176 if not page_sets:
177 raise ValueError('Must specify at least one page_set!')
178 elif self._all_page_sets_specified:
179 # Get everything from the page_sets directory.
180 page_sets_dir = os.path.join(os.path.abspath(os.path.dirname(__file__)),
181 'page_sets')
182 ps = [os.path.join(page_sets_dir, page_set)
183 for page_set in os.listdir(page_sets_dir)
184 if not os.path.isdir(os.path.join(page_sets_dir, page_set)) and
185 page_set.endswith('.py')]
rmistry49d093c2015-03-31 05:04:29 -0700186 chromium_ps = [
187 os.path.join(self._chrome_page_sets_path, cr_page_set)
188 for cr_page_set in CHROMIUM_PAGE_SETS_TO_PREFIX]
189 ps.extend(chromium_ps)
borenetdc89ca52014-10-17 07:37:05 -0700190 elif '*' in page_sets:
191 # Explode and return the glob.
192 ps = glob.glob(page_sets)
193 else:
194 ps = page_sets.split(',')
195 ps.sort()
196 return ps
197
rmistry49d093c2015-03-31 05:04:29 -0700198 def _IsChromiumPageSet(self, page_set):
199 """Returns true if the specified page set is a Chromium page set."""
200 return page_set.startswith(self._chrome_page_sets_path)
201
borenetdc89ca52014-10-17 07:37:05 -0700202 def Run(self):
203 """Run the SkPicturePlayback BuildStep."""
204
rmistryf802f322014-10-22 05:04:43 -0700205 # Download the credentials file if it was not previously downloaded.
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700206 if not os.path.isfile(CREDENTIALS_FILE_PATH):
207 # Download the credentials.json file from Google Storage.
208 self.gs.download_file(CREDENTIALS_GS_PATH, CREDENTIALS_FILE_PATH)
209
210 if not os.path.isfile(CREDENTIALS_FILE_PATH):
211 print """\n\nCould not locate credentials file in the storage.
212 Please create a %s file that contains:
rmistryf802f322014-10-22 05:04:43 -0700213 {
214 "google": {
215 "username": "google_testing_account_username",
216 "password": "google_testing_account_password"
217 },
218 "facebook": {
219 "username": "facebook_testing_account_username",
220 "password": "facebook_testing_account_password"
221 }
222 }\n\n""" % CREDENTIALS_FILE_PATH
223 raw_input("Please press a key when you are ready to proceed...")
rmistryf802f322014-10-22 05:04:43 -0700224
borenetdc89ca52014-10-17 07:37:05 -0700225 # Delete any left over data files in the data directory.
226 for archive_file in glob.glob(
227 os.path.join(LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR, 'skia_*')):
228 os.remove(archive_file)
229
230 # Delete the local root directory if it already exists.
231 if os.path.exists(LOCAL_PLAYBACK_ROOT_DIR):
232 shutil.rmtree(LOCAL_PLAYBACK_ROOT_DIR)
233
234 # Create the required local storage directories.
235 self._CreateLocalStorageDirs()
236
237 # Start the timer.
238 start_time = time.time()
239
240 # Loop through all page_sets.
241 for page_set in self._page_sets:
242
rmistry7620bf02014-10-27 06:42:11 -0700243 page_set_basename = os.path.basename(page_set).split('.')[0]
244 page_set_json_name = page_set_basename + '.json'
borenetdc89ca52014-10-17 07:37:05 -0700245 wpr_data_file = page_set.split(os.path.sep)[-1].split('.')[0] + '_000.wpr'
rmistry7620bf02014-10-27 06:42:11 -0700246 page_set_dir = os.path.dirname(page_set)
borenetdc89ca52014-10-17 07:37:05 -0700247
rmistry49d093c2015-03-31 05:04:29 -0700248 if self._IsChromiumPageSet(page_set):
249 print 'Using Chromium\'s captured archives for Chromium\'s page sets.'
250 elif self._record:
borenetdc89ca52014-10-17 07:37:05 -0700251 # Create an archive of the specified webpages if '--record=True' is
252 # specified.
253 record_wpr_cmd = (
rmistry7620bf02014-10-27 06:42:11 -0700254 'PYTHONPATH=%s:$PYTHONPATH' % page_set_dir,
borenetdc89ca52014-10-17 07:37:05 -0700255 'DISPLAY=%s' % X11_DISPLAY,
256 os.path.join(self._telemetry_binaries_dir, 'record_wpr'),
rmistryaa31ee72015-04-23 12:47:33 -0700257 '--extra-browser-args="%s"' % self._browser_args,
borenetdc89ca52014-10-17 07:37:05 -0700258 '--browser=exact',
259 '--browser-executable=%s' % self._browser_executable,
rmistry7620bf02014-10-27 06:42:11 -0700260 '%s_page_set' % page_set_basename,
261 '--page-set-base-dir=%s' % page_set_dir
borenetdc89ca52014-10-17 07:37:05 -0700262 )
263 for _ in range(RETRY_RECORD_WPR_COUNT):
rmistry0ec28af2014-10-28 14:25:17 -0700264 try:
265 shell_utils.run(' '.join(record_wpr_cmd), shell=True)
kkinnunenf9310fe2015-03-29 22:33:16 -0700266
267 # Move over the created archive into the local webpages archive
268 # directory.
269 shutil.move(
270 os.path.join(LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR, wpr_data_file),
271 self._local_record_webpages_archive_dir)
272 shutil.move(
273 os.path.join(LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR,
274 page_set_json_name),
275 self._local_record_webpages_archive_dir)
276
borenetdc89ca52014-10-17 07:37:05 -0700277 # Break out of the retry loop since there were no errors.
278 break
rmistry0ec28af2014-10-28 14:25:17 -0700279 except Exception:
280 # There was a failure continue with the loop.
281 traceback.print_exc()
borenetdc89ca52014-10-17 07:37:05 -0700282 else:
283 # If we get here then record_wpr did not succeed and thus did not
284 # break out of the loop.
285 raise Exception('record_wpr failed for page_set: %s' % page_set)
286
287 else:
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700288 # Get the webpages archive so that it can be replayed.
289 self._DownloadWebpagesArchive(wpr_data_file, page_set_json_name)
borenetdc89ca52014-10-17 07:37:05 -0700290
borenet78399152014-10-17 12:15:46 -0700291 run_benchmark_cmd = (
rmistryf802f322014-10-22 05:04:43 -0700292 'PYTHONPATH=%s:$PYTHONPATH' % page_set_dir,
borenetdc89ca52014-10-17 07:37:05 -0700293 'DISPLAY=%s' % X11_DISPLAY,
294 'timeout', '300',
borenet78399152014-10-17 12:15:46 -0700295 os.path.join(self._telemetry_binaries_dir, 'run_benchmark'),
rmistryaa31ee72015-04-23 12:47:33 -0700296 '--extra-browser-args="%s"' % self._browser_args,
borenetdc89ca52014-10-17 07:37:05 -0700297 '--browser=exact',
298 '--browser-executable=%s' % self._browser_executable,
299 SKP_BENCHMARK,
rmistry7620bf02014-10-27 06:42:11 -0700300 '--page-set-name=%s' % page_set_basename,
rmistryf802f322014-10-22 05:04:43 -0700301 '--page-set-base-dir=%s' % page_set_dir,
302 '--skp-outdir=%s' % TMP_SKP_DIR,
303 '--also-run-disabled-tests'
borenetdc89ca52014-10-17 07:37:05 -0700304 )
borenetdc89ca52014-10-17 07:37:05 -0700305
306 for _ in range(RETRY_RUN_MEASUREMENT_COUNT):
307 try:
308 print '\n\n=======Capturing SKP of %s=======\n\n' % page_set
borenet78399152014-10-17 12:15:46 -0700309 shell_utils.run(' '.join(run_benchmark_cmd), shell=True)
borenetdc89ca52014-10-17 07:37:05 -0700310 except shell_utils.CommandFailedException:
311 # skpicture_printer sometimes fails with AssertionError but the
312 # captured SKP is still valid. This is a known issue.
313 pass
314
borenetdc89ca52014-10-17 07:37:05 -0700315 # Rename generated SKP files into more descriptive names.
316 try:
317 self._RenameSkpFiles(page_set)
318 # Break out of the retry loop since there were no errors.
319 break
320 except Exception:
321 # There was a failure continue with the loop.
322 traceback.print_exc()
323 print '\n\n=======Retrying %s=======\n\n' % page_set
324 time.sleep(10)
325 else:
borenet78399152014-10-17 12:15:46 -0700326 # If we get here then run_benchmark did not succeed and thus did not
borenetdc89ca52014-10-17 07:37:05 -0700327 # break out of the loop.
borenet78399152014-10-17 12:15:46 -0700328 raise Exception('run_benchmark failed for page_set: %s' % page_set)
borenetdc89ca52014-10-17 07:37:05 -0700329
borenetdc89ca52014-10-17 07:37:05 -0700330 print '\n\n=======Capturing SKP files took %s seconds=======\n\n' % (
331 time.time() - start_time)
332
333 if self._skia_tools:
334 render_pictures_cmd = [
335 os.path.join(self._skia_tools, 'render_pictures'),
336 '-r', self._local_skp_dir
337 ]
338 render_pdfs_cmd = [
339 os.path.join(self._skia_tools, 'render_pdfs'),
kkinnunen3a6aa862014-12-03 04:22:06 -0800340 '-r', self._local_skp_dir
borenetdc89ca52014-10-17 07:37:05 -0700341 ]
342
343 for tools_cmd in (render_pictures_cmd, render_pdfs_cmd):
344 print '\n\n=======Running %s=======' % ' '.join(tools_cmd)
345 proc = subprocess.Popen(tools_cmd)
rmistry0ec28af2014-10-28 14:25:17 -0700346 (code, _) = shell_utils.log_process_after_completion(proc, echo=False)
borenetdc89ca52014-10-17 07:37:05 -0700347 if code != 0:
348 raise Exception('%s failed!' % ' '.join(tools_cmd))
349
350 if not self._non_interactive:
351 print '\n\n=======Running debugger======='
352 os.system('%s %s' % (os.path.join(self._skia_tools, 'debugger'),
kkinnunen960fb502014-12-03 06:18:12 -0800353 self._local_skp_dir))
borenetdc89ca52014-10-17 07:37:05 -0700354
355 print '\n\n'
356
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700357 if self._upload:
358 print '\n\n=======Uploading to %s=======\n\n' % self.gs.target_type()
borenetdc89ca52014-10-17 07:37:05 -0700359 # Copy the directory structure in the root directory into Google Storage.
360 dest_dir_name = ROOT_PLAYBACK_DIR_NAME
361 if self._alternate_upload_dir:
362 dest_dir_name = self._alternate_upload_dir
363
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700364 self.gs.upload_dir_contents(
rmistry009b0692015-03-31 05:20:12 -0700365 LOCAL_PLAYBACK_ROOT_DIR, dest_dir=dest_dir_name,
borenetdc89ca52014-10-17 07:37:05 -0700366 upload_if=gs_utils.GSUtils.UploadIf.IF_MODIFIED,
367 predefined_acl=GS_PREDEFINED_ACL,
368 fine_grained_acl_list=GS_FINE_GRAINED_ACL_LIST)
369
370 print '\n\n=======New SKPs have been uploaded to %s =======\n\n' % (
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700371 posixpath.join(self.gs.target_name(), dest_dir_name,
372 SKPICTURES_DIR_NAME))
rmistry0575c492016-02-01 10:27:05 -0800373
374 if self._upload_to_partner_bucket:
375 print '\n\n=======Uploading to Partner bucket %s =======\n\n' % (
376 PARTNERS_GS_BUCKET)
377 partner_gs = GoogleStorageDataStore(PARTNERS_GS_BUCKET)
378 partner_gs.upload_dir_contents(
379 os.path.join(LOCAL_PLAYBACK_ROOT_DIR, SKPICTURES_DIR_NAME),
rmistry8870e942016-02-02 13:55:38 -0800380 dest_dir=SKPICTURES_DIR_NAME,
rmistry0575c492016-02-01 10:27:05 -0800381 upload_if=gs_utils.GSUtils.UploadIf.IF_MODIFIED)
382 print '\n\n=======New SKPs have been uploaded to %s =======\n\n' % (
rmistry8870e942016-02-02 13:55:38 -0800383 posixpath.join(partner_gs.target_name(), SKPICTURES_DIR_NAME))
borenetdc89ca52014-10-17 07:37:05 -0700384 else:
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700385 print '\n\n=======Not Uploading to %s=======\n\n' % self.gs.target_type()
borenetdc89ca52014-10-17 07:37:05 -0700386 print 'Generated resources are available in %s\n\n' % (
387 LOCAL_PLAYBACK_ROOT_DIR)
388
389 return 0
390
rmistry49d093c2015-03-31 05:04:29 -0700391 def _GetSkiaSkpFileName(self, page_set):
392 """Returns the SKP file name for Skia page sets."""
393 # /path/to/skia_yahooanswers_desktop.py -> skia_yahooanswers_desktop.py
394 ps_filename = os.path.basename(page_set)
395 # skia_yahooanswers_desktop.py -> skia_yahooanswers_desktop
396 ps_basename, _ = os.path.splitext(ps_filename)
397 # skia_yahooanswers_desktop -> skia, yahooanswers, desktop
398 _, page_name, device = ps_basename.split('_')
399 basename = '%s_%s' % (DEVICE_TO_PLATFORM_PREFIX[device], page_name)
400 return basename[:MAX_SKP_BASE_NAME_LEN] + '.skp'
401
402 def _GetChromiumSkpFileName(self, page_set, site):
403 """Returns the SKP file name for Chromium page sets."""
404 # /path/to/http___mobile_news_sandbox_pt0 -> http___mobile_news_sandbox_pt0
405 _, webpage = os.path.split(site)
406 # http___mobile_news_sandbox_pt0 -> mobile_news_sandbox_pt0
rmistry80bd3ae2015-04-03 08:22:51 -0700407 for prefix in ('http___', 'https___', 'www_'):
rmistry2a3c8492015-03-31 10:59:15 -0700408 if webpage.startswith(prefix):
409 webpage = webpage[len(prefix):]
rmistry49d093c2015-03-31 05:04:29 -0700410 # /path/to/skia_yahooanswers_desktop.py -> skia_yahooanswers_desktop.py
411 ps_filename = os.path.basename(page_set)
412 # http___mobile_news_sandbox -> pagesetprefix_http___mobile_news_sandbox
413 basename = '%s_%s' % (CHROMIUM_PAGE_SETS_TO_PREFIX[ps_filename], webpage)
414 return basename[:MAX_SKP_BASE_NAME_LEN] + '.skp'
415
borenetdc89ca52014-10-17 07:37:05 -0700416 def _RenameSkpFiles(self, page_set):
417 """Rename generated SKP files into more descriptive names.
418
419 Look into the subdirectory of TMP_SKP_DIR and find the most interesting
420 .skp in there to be this page_set's representative .skp.
421 """
borenetdc89ca52014-10-17 07:37:05 -0700422 subdirs = glob.glob(os.path.join(TMP_SKP_DIR, '*'))
borenetdc89ca52014-10-17 07:37:05 -0700423 for site in subdirs:
rmistry49d093c2015-03-31 05:04:29 -0700424 if self._IsChromiumPageSet(page_set):
425 filename = self._GetChromiumSkpFileName(page_set, site)
426 else:
427 filename = self._GetSkiaSkpFileName(page_set)
rmistry80bd3ae2015-04-03 08:22:51 -0700428 filename = filename.lower()
rmistry49d093c2015-03-31 05:04:29 -0700429
rmistryaa31ee72015-04-23 12:47:33 -0700430 if self._skp_prefix:
431 filename = '%s%s' % (self._skp_prefix, filename)
432
borenetdc89ca52014-10-17 07:37:05 -0700433 # We choose the largest .skp as the most likely to be interesting.
434 largest_skp = max(glob.glob(os.path.join(site, '*.skp')),
435 key=lambda path: os.stat(path).st_size)
436 dest = os.path.join(self._local_skp_dir, filename)
437 print 'Moving', largest_skp, 'to', dest
438 shutil.move(largest_skp, dest)
439 self._skp_files.append(filename)
440 shutil.rmtree(site)
441
442 def _CreateLocalStorageDirs(self):
443 """Creates required local storage directories for this script."""
444 for d in (self._local_record_webpages_archive_dir,
445 self._local_skp_dir):
446 if os.path.exists(d):
447 shutil.rmtree(d)
448 os.makedirs(d)
449
rmistry7620bf02014-10-27 06:42:11 -0700450 def _DownloadWebpagesArchive(self, wpr_data_file, page_set_json_name):
borenetdc89ca52014-10-17 07:37:05 -0700451 """Downloads the webpages archive and its required page set from GS."""
452 wpr_source = posixpath.join(ROOT_PLAYBACK_DIR_NAME, 'webpages_archive',
453 wpr_data_file)
454 page_set_source = posixpath.join(ROOT_PLAYBACK_DIR_NAME,
455 'webpages_archive',
rmistry7620bf02014-10-27 06:42:11 -0700456 page_set_json_name)
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700457 gs = self.gs
458 if (gs.does_storage_object_exist(wpr_source) and
459 gs.does_storage_object_exist(page_set_source)):
460 gs.download_file(wpr_source,
borenetdc89ca52014-10-17 07:37:05 -0700461 os.path.join(LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR,
462 wpr_data_file))
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700463 gs.download_file(page_set_source,
borenetdc89ca52014-10-17 07:37:05 -0700464 os.path.join(LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR,
rmistry7620bf02014-10-27 06:42:11 -0700465 page_set_json_name))
borenetdc89ca52014-10-17 07:37:05 -0700466 else:
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700467 raise Exception('%s and %s do not exist in %s!' % (gs.target_type(),
468 wpr_source, page_set_source))
borenetdc89ca52014-10-17 07:37:05 -0700469
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700470class DataStore:
471 """An abstract base class for uploading recordings to a data storage.
472 The interface emulates the google storage api."""
473 def target_name(self):
474 raise NotImplementedError()
475 def target_type(self):
476 raise NotImplementedError()
477 def does_storage_object_exist(self, *args):
478 raise NotImplementedError()
479 def download_file(self, *args):
480 raise NotImplementedError()
481 def upload_dir_contents(self, source_dir, **kwargs):
482 raise NotImplementedError()
483
484class GoogleStorageDataStore(DataStore):
485 def __init__(self, data_store_url):
486 self._data_store_url = data_store_url
rmistry49d093c2015-03-31 05:04:29 -0700487 self._bucket = remove_prefix(self._data_store_url.lstrip(),
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700488 gs_utils.GS_PREFIX)
489 self.gs = gs_utils.GSUtils()
490 def target_name(self):
491 return self._data_store_url
492 def target_type(self):
493 return 'Google Storage'
494 def does_storage_object_exist(self, *args):
495 return self.gs.does_storage_object_exist(self._bucket, *args)
496 def download_file(self, *args):
497 self.gs.download_file(self._bucket, *args)
498 def upload_dir_contents(self, source_dir, **kwargs):
499 self.gs.upload_dir_contents(source_dir, self._bucket, **kwargs)
500
501class LocalFileSystemDataStore(DataStore):
502 def __init__(self, data_store_location):
503 self._base_dir = data_store_location
504 def target_name(self):
505 return self._base_dir
506 def target_type(self):
507 return self._base_dir
508 def does_storage_object_exist(self, name, *args):
509 return os.path.isfile(os.path.join(self._base_dir, name))
510 def download_file(self, name, local_path, *args):
511 shutil.copyfile(os.path.join(self._base_dir, name), local_path)
512 def upload_dir_contents(self, source_dir, dest_dir, **kwargs):
513 def copytree(source_dir, dest_dir):
514 if not os.path.exists(dest_dir):
515 os.makedirs(dest_dir)
516 for item in os.listdir(source_dir):
517 source = os.path.join(source_dir, item)
518 dest = os.path.join(dest_dir, item)
519 if os.path.isdir(source):
520 copytree(source, dest)
521 else:
522 shutil.copy2(source, dest)
523 copytree(source_dir, os.path.join(self._base_dir, dest_dir))
borenetdc89ca52014-10-17 07:37:05 -0700524
525if '__main__' == __name__:
526 option_parser = optparse.OptionParser()
527 option_parser.add_option(
528 '', '--page_sets',
529 help='Specifies the page sets to use to archive. Supports globs.',
530 default='all')
531 option_parser.add_option(
borenetdc89ca52014-10-17 07:37:05 -0700532 '', '--record', action='store_true',
533 help='Specifies whether a new website archive should be created.',
534 default=False)
535 option_parser.add_option(
borenetdc89ca52014-10-17 07:37:05 -0700536 '', '--skia_tools',
537 help=('Path to compiled Skia executable tools. '
538 'render_pictures/render_pdfs is run on the set '
539 'after all SKPs are captured. If the script is run without '
540 '--non-interactive then the debugger is also run at the end. Debug '
541 'builds are recommended because they seem to catch more failures '
542 'than Release builds.'),
543 default=None)
544 option_parser.add_option(
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700545 '', '--upload', action='store_true',
546 help=('Uploads to Google Storage or copies to local filesystem storage '
547 ' if this is True.'),
borenetdc89ca52014-10-17 07:37:05 -0700548 default=False)
549 option_parser.add_option(
rmistry0575c492016-02-01 10:27:05 -0800550 '', '--upload_to_partner_bucket', action='store_true',
551 help=('Uploads SKPs to the chrome-partner-telemetry Google Storage '
552 'bucket if true.'),
553 default=False)
554 option_parser.add_option(
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700555 '', '--data_store',
556 help=('The location of the file storage to use to download and upload '
557 'files. Can be \'gs://<bucket>\' for Google Storage, or '
558 'a directory for local filesystem storage'),
559 default='gs://chromium-skia-gm')
560 option_parser.add_option(
borenetdc89ca52014-10-17 07:37:05 -0700561 '', '--alternate_upload_dir',
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700562 help= ('Uploads to a different directory in Google Storage or local '
563 'storage if this flag is specified'),
borenetdc89ca52014-10-17 07:37:05 -0700564 default=None)
565 option_parser.add_option(
566 '', '--output_dir',
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700567 help=('Temporary directory where SKPs and webpage archives will be '
568 'outputted to.'),
borenetdc89ca52014-10-17 07:37:05 -0700569 default=tempfile.gettempdir())
570 option_parser.add_option(
571 '', '--browser_executable',
572 help='The exact browser executable to run.',
573 default=None)
574 option_parser.add_option(
rmistryaa31ee72015-04-23 12:47:33 -0700575 '', '--browser_extra_args',
576 help='Additional arguments to pass to the browser.',
577 default=None)
578 option_parser.add_option(
borenetdc89ca52014-10-17 07:37:05 -0700579 '', '--chrome_src_path',
580 help='Path to the chromium src directory.',
581 default=None)
582 option_parser.add_option(
583 '', '--non-interactive', action='store_true',
584 help='Runs the script without any prompts. If this flag is specified and '
585 '--skia_tools is specified then the debugger is not run.',
586 default=False)
rmistryaa31ee72015-04-23 12:47:33 -0700587 option_parser.add_option(
588 '', '--skp_prefix',
589 help='Prefix to add to the names of generated SKPs.',
590 default=None)
borenetdc89ca52014-10-17 07:37:05 -0700591 options, unused_args = option_parser.parse_args()
592
593 playback = SkPicturePlayback(options)
594 sys.exit(playback.Run())