blob: 3d9e20bd0243b7472e19cc3834a1422b37914690 [file] [log] [blame]
borenetdc89ca52014-10-17 07:37:05 -07001#!/usr/bin/env python
2# Copyright (c) 2012 The Chromium Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6"""Archives or replays webpages and creates SKPs in a Google Storage location.
7
8To archive webpages and store SKP files (archives should be rarely updated):
9
kkinnunenfcf35c52014-12-03 05:51:24 -080010cd skia
kkinnunenb4ee7ea2015-03-31 00:18:26 -070011python tools/skp/webpages_playback.py --data_store=gs://rmistry --record \
borenetdc89ca52014-10-17 07:37:05 -070012--page_sets=all --skia_tools=/home/default/trunk/out/Debug/ \
13--browser_executable=/tmp/chromium/out/Release/chrome
14
kkinnunenb4ee7ea2015-03-31 00:18:26 -070015The above command uses Google Storage bucket 'rmistry' to download needed files.
borenetdc89ca52014-10-17 07:37:05 -070016
17To replay archived webpages and re-generate SKP files (should be run whenever
18SkPicture.PICTURE_VERSION changes):
19
kkinnunenfcf35c52014-12-03 05:51:24 -080020cd skia
kkinnunenb4ee7ea2015-03-31 00:18:26 -070021python tools/skp/webpages_playback.py --data_store=gs://rmistry \
borenetdc89ca52014-10-17 07:37:05 -070022--page_sets=all --skia_tools=/home/default/trunk/out/Debug/ \
23--browser_executable=/tmp/chromium/out/Release/chrome
24
25
26Specify the --page_sets flag (default value is 'all') to pick a list of which
27webpages should be archived and/or replayed. Eg:
28
kkinnunenfcf35c52014-12-03 05:51:24 -080029--page_sets=tools/skp/page_sets/skia_yahooanswers_desktop.py,\
30tools/skp/page_sets/skia_googlecalendar_nexus10.py
borenetdc89ca52014-10-17 07:37:05 -070031
32The --browser_executable flag should point to the browser binary you want to use
33to capture archives and/or capture SKP files. Majority of the time it should be
34a newly built chrome binary.
35
kkinnunenb4ee7ea2015-03-31 00:18:26 -070036The --data_store flag controls where the needed artifacts, such as
37credential files, are downloaded from. It also controls where the
38generated artifacts, such as recorded webpages and resulting skp renderings,
39are uploaded to. URLs with scheme 'gs://' use Google Storage. Otherwise
40use local filesystem.
41
42The --upload=True flag means generated artifacts will be
43uploaded or copied to the location specified by --data_store. (default value is
44False if not specified).
borenetdc89ca52014-10-17 07:37:05 -070045
46The --non-interactive flag controls whether the script will prompt the user
47(default value is False if not specified).
48
49The --skia_tools flag if specified will allow this script to run
50debugger, render_pictures, and render_pdfs on the captured
51SKP(s). The tools are run after all SKPs are succesfully captured to make sure
52they can be added to the buildbots with no breakages.
borenetdc89ca52014-10-17 07:37:05 -070053"""
54
55import glob
56import optparse
57import os
58import posixpath
59import shutil
60import subprocess
61import sys
62import tempfile
63import time
64import traceback
65
borenet85f0e632016-07-21 10:27:00 -070066SKIA_DIR = os.path.abspath(os.path.join(
67 os.path.realpath(os.path.dirname(__file__)),
68 os.pardir, os.pardir))
69sys.path.insert(0, SKIA_DIR)
borenetdc89ca52014-10-17 07:37:05 -070070
71from common.py.utils import gs_utils
72from common.py.utils import shell_utils
73
74ROOT_PLAYBACK_DIR_NAME = 'playback'
75SKPICTURES_DIR_NAME = 'skps'
76
rmistry0575c492016-02-01 10:27:05 -080077PARTNERS_GS_BUCKET = 'gs://chrome-partner-telemetry'
borenetdc89ca52014-10-17 07:37:05 -070078
79# Local archive and SKP directories.
80LOCAL_PLAYBACK_ROOT_DIR = os.path.join(
81 tempfile.gettempdir(), ROOT_PLAYBACK_DIR_NAME)
82LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR = os.path.join(
83 os.path.abspath(os.path.dirname(__file__)), 'page_sets', 'data')
84TMP_SKP_DIR = tempfile.mkdtemp()
85
rmistryf802f322014-10-22 05:04:43 -070086# Location of the credentials.json file and the string that represents missing
87# passwords.
88CREDENTIALS_FILE_PATH = os.path.join(
89 os.path.abspath(os.path.dirname(__file__)), 'page_sets', 'data',
90 'credentials.json'
91)
92
borenetdc89ca52014-10-17 07:37:05 -070093# Name of the SKP benchmark
94SKP_BENCHMARK = 'skpicture_printer'
95
96# The max base name length of Skp files.
97MAX_SKP_BASE_NAME_LEN = 31
98
99# Dictionary of device to platform prefixes for SKP files.
100DEVICE_TO_PLATFORM_PREFIX = {
101 'desktop': 'desk',
102 'galaxynexus': 'mobi',
103 'nexus10': 'tabl'
104}
105
106# How many times the record_wpr binary should be retried.
107RETRY_RECORD_WPR_COUNT = 5
borenet78399152014-10-17 12:15:46 -0700108# How many times the run_benchmark binary should be retried.
borenetdc89ca52014-10-17 07:37:05 -0700109RETRY_RUN_MEASUREMENT_COUNT = 5
110
rmistryf802f322014-10-22 05:04:43 -0700111# Location of the credentials.json file in Google Storage.
112CREDENTIALS_GS_PATH = '/playback/credentials/credentials.json'
113
borenetdc89ca52014-10-17 07:37:05 -0700114X11_DISPLAY = os.getenv('DISPLAY', ':0')
115
116GS_PREDEFINED_ACL = gs_utils.GSUtils.PredefinedACL.PRIVATE
117GS_FINE_GRAINED_ACL_LIST = [
118 (gs_utils.GSUtils.IdType.GROUP_BY_DOMAIN, 'google.com',
119 gs_utils.GSUtils.Permission.READ),
120]
121
rmistry49d093c2015-03-31 05:04:29 -0700122# Path to Chromium's page sets.
123CHROMIUM_PAGE_SETS_PATH = os.path.join('tools', 'perf', 'page_sets')
124
125# Dictionary of supported Chromium page sets to their file prefixes.
126CHROMIUM_PAGE_SETS_TO_PREFIX = {
rmistry5af9a372015-03-31 06:22:55 -0700127 'key_mobile_sites_smooth.py': 'keymobi',
rmistry2a3c8492015-03-31 10:59:15 -0700128 'top_25_smooth.py': 'top25desk',
rmistry49d093c2015-03-31 05:04:29 -0700129}
130
131
kkinnunene75d2d22014-12-03 04:38:46 -0800132def remove_prefix(s, prefix):
133 if s.startswith(prefix):
134 return s[len(prefix):]
135 return s
borenetdc89ca52014-10-17 07:37:05 -0700136
rmistry49d093c2015-03-31 05:04:29 -0700137
borenetdc89ca52014-10-17 07:37:05 -0700138class SkPicturePlayback(object):
139 """Class that archives or replays webpages and creates SKPs."""
140
141 def __init__(self, parse_options):
142 """Constructs a SkPicturePlayback BuildStep instance."""
143 assert parse_options.browser_executable, 'Must specify --browser_executable'
144 self._browser_executable = parse_options.browser_executable
rmistryaa31ee72015-04-23 12:47:33 -0700145 self._browser_args = '--disable-setuid-sandbox'
146 if parse_options.browser_extra_args:
147 self._browser_args = '%s %s' % (
148 self._browser_args, parse_options.browser_extra_args)
borenetdc89ca52014-10-17 07:37:05 -0700149
rmistry49d093c2015-03-31 05:04:29 -0700150 self._chrome_page_sets_path = os.path.join(parse_options.chrome_src_path,
151 CHROMIUM_PAGE_SETS_PATH)
borenetdc89ca52014-10-17 07:37:05 -0700152 self._all_page_sets_specified = parse_options.page_sets == 'all'
153 self._page_sets = self._ParsePageSets(parse_options.page_sets)
154
borenetdc89ca52014-10-17 07:37:05 -0700155 self._record = parse_options.record
156 self._skia_tools = parse_options.skia_tools
157 self._non_interactive = parse_options.non_interactive
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700158 self._upload = parse_options.upload
rmistryaa31ee72015-04-23 12:47:33 -0700159 self._skp_prefix = parse_options.skp_prefix
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700160 data_store_location = parse_options.data_store
161 if data_store_location.startswith(gs_utils.GS_PREFIX):
162 self.gs = GoogleStorageDataStore(data_store_location)
163 else:
164 self.gs = LocalFileSystemDataStore(data_store_location)
rmistry0575c492016-02-01 10:27:05 -0800165 self._upload_to_partner_bucket = parse_options.upload_to_partner_bucket
borenetdc89ca52014-10-17 07:37:05 -0700166 self._alternate_upload_dir = parse_options.alternate_upload_dir
borenetdc89ca52014-10-17 07:37:05 -0700167 self._telemetry_binaries_dir = os.path.join(parse_options.chrome_src_path,
168 'tools', 'perf')
rmistryafaf4962016-02-27 10:04:57 -0800169 self._catapult_dir = os.path.join(parse_options.chrome_src_path,
170 'third_party', 'catapult')
borenetdc89ca52014-10-17 07:37:05 -0700171
172 self._local_skp_dir = os.path.join(
173 parse_options.output_dir, ROOT_PLAYBACK_DIR_NAME, SKPICTURES_DIR_NAME)
174 self._local_record_webpages_archive_dir = os.path.join(
175 parse_options.output_dir, ROOT_PLAYBACK_DIR_NAME, 'webpages_archive')
176
177 # List of SKP files generated by this script.
178 self._skp_files = []
179
180 def _ParsePageSets(self, page_sets):
181 if not page_sets:
182 raise ValueError('Must specify at least one page_set!')
183 elif self._all_page_sets_specified:
184 # Get everything from the page_sets directory.
185 page_sets_dir = os.path.join(os.path.abspath(os.path.dirname(__file__)),
186 'page_sets')
187 ps = [os.path.join(page_sets_dir, page_set)
188 for page_set in os.listdir(page_sets_dir)
189 if not os.path.isdir(os.path.join(page_sets_dir, page_set)) and
190 page_set.endswith('.py')]
rmistry49d093c2015-03-31 05:04:29 -0700191 chromium_ps = [
192 os.path.join(self._chrome_page_sets_path, cr_page_set)
193 for cr_page_set in CHROMIUM_PAGE_SETS_TO_PREFIX]
194 ps.extend(chromium_ps)
borenetdc89ca52014-10-17 07:37:05 -0700195 elif '*' in page_sets:
196 # Explode and return the glob.
197 ps = glob.glob(page_sets)
198 else:
199 ps = page_sets.split(',')
200 ps.sort()
201 return ps
202
rmistry49d093c2015-03-31 05:04:29 -0700203 def _IsChromiumPageSet(self, page_set):
204 """Returns true if the specified page set is a Chromium page set."""
205 return page_set.startswith(self._chrome_page_sets_path)
206
borenetdc89ca52014-10-17 07:37:05 -0700207 def Run(self):
208 """Run the SkPicturePlayback BuildStep."""
209
rmistryf802f322014-10-22 05:04:43 -0700210 # Download the credentials file if it was not previously downloaded.
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700211 if not os.path.isfile(CREDENTIALS_FILE_PATH):
212 # Download the credentials.json file from Google Storage.
213 self.gs.download_file(CREDENTIALS_GS_PATH, CREDENTIALS_FILE_PATH)
214
215 if not os.path.isfile(CREDENTIALS_FILE_PATH):
216 print """\n\nCould not locate credentials file in the storage.
217 Please create a %s file that contains:
rmistryf802f322014-10-22 05:04:43 -0700218 {
219 "google": {
220 "username": "google_testing_account_username",
221 "password": "google_testing_account_password"
222 },
223 "facebook": {
224 "username": "facebook_testing_account_username",
225 "password": "facebook_testing_account_password"
226 }
227 }\n\n""" % CREDENTIALS_FILE_PATH
228 raw_input("Please press a key when you are ready to proceed...")
rmistryf802f322014-10-22 05:04:43 -0700229
borenetdc89ca52014-10-17 07:37:05 -0700230 # Delete any left over data files in the data directory.
231 for archive_file in glob.glob(
232 os.path.join(LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR, 'skia_*')):
233 os.remove(archive_file)
234
235 # Delete the local root directory if it already exists.
236 if os.path.exists(LOCAL_PLAYBACK_ROOT_DIR):
237 shutil.rmtree(LOCAL_PLAYBACK_ROOT_DIR)
238
239 # Create the required local storage directories.
240 self._CreateLocalStorageDirs()
241
242 # Start the timer.
243 start_time = time.time()
244
245 # Loop through all page_sets.
246 for page_set in self._page_sets:
247
rmistry7620bf02014-10-27 06:42:11 -0700248 page_set_basename = os.path.basename(page_set).split('.')[0]
249 page_set_json_name = page_set_basename + '.json'
borenetdc89ca52014-10-17 07:37:05 -0700250 wpr_data_file = page_set.split(os.path.sep)[-1].split('.')[0] + '_000.wpr'
rmistry7620bf02014-10-27 06:42:11 -0700251 page_set_dir = os.path.dirname(page_set)
borenetdc89ca52014-10-17 07:37:05 -0700252
rmistry49d093c2015-03-31 05:04:29 -0700253 if self._IsChromiumPageSet(page_set):
254 print 'Using Chromium\'s captured archives for Chromium\'s page sets.'
255 elif self._record:
borenetdc89ca52014-10-17 07:37:05 -0700256 # Create an archive of the specified webpages if '--record=True' is
257 # specified.
258 record_wpr_cmd = (
rmistryafaf4962016-02-27 10:04:57 -0800259 'PYTHONPATH=%s:%s:$PYTHONPATH' % (page_set_dir, self._catapult_dir),
borenetdc89ca52014-10-17 07:37:05 -0700260 'DISPLAY=%s' % X11_DISPLAY,
261 os.path.join(self._telemetry_binaries_dir, 'record_wpr'),
rmistryaa31ee72015-04-23 12:47:33 -0700262 '--extra-browser-args="%s"' % self._browser_args,
borenetdc89ca52014-10-17 07:37:05 -0700263 '--browser=exact',
264 '--browser-executable=%s' % self._browser_executable,
rmistry7620bf02014-10-27 06:42:11 -0700265 '%s_page_set' % page_set_basename,
266 '--page-set-base-dir=%s' % page_set_dir
borenetdc89ca52014-10-17 07:37:05 -0700267 )
268 for _ in range(RETRY_RECORD_WPR_COUNT):
rmistry0ec28af2014-10-28 14:25:17 -0700269 try:
270 shell_utils.run(' '.join(record_wpr_cmd), shell=True)
kkinnunenf9310fe2015-03-29 22:33:16 -0700271
272 # Move over the created archive into the local webpages archive
273 # directory.
274 shutil.move(
275 os.path.join(LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR, wpr_data_file),
276 self._local_record_webpages_archive_dir)
277 shutil.move(
278 os.path.join(LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR,
279 page_set_json_name),
280 self._local_record_webpages_archive_dir)
281
borenetdc89ca52014-10-17 07:37:05 -0700282 # Break out of the retry loop since there were no errors.
283 break
rmistry0ec28af2014-10-28 14:25:17 -0700284 except Exception:
285 # There was a failure continue with the loop.
286 traceback.print_exc()
borenetdc89ca52014-10-17 07:37:05 -0700287 else:
288 # If we get here then record_wpr did not succeed and thus did not
289 # break out of the loop.
290 raise Exception('record_wpr failed for page_set: %s' % page_set)
291
292 else:
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700293 # Get the webpages archive so that it can be replayed.
294 self._DownloadWebpagesArchive(wpr_data_file, page_set_json_name)
borenetdc89ca52014-10-17 07:37:05 -0700295
borenet78399152014-10-17 12:15:46 -0700296 run_benchmark_cmd = (
rmistryafaf4962016-02-27 10:04:57 -0800297 'PYTHONPATH=%s:%s:$PYTHONPATH' % (page_set_dir, self._catapult_dir),
borenetdc89ca52014-10-17 07:37:05 -0700298 'DISPLAY=%s' % X11_DISPLAY,
299 'timeout', '300',
borenet78399152014-10-17 12:15:46 -0700300 os.path.join(self._telemetry_binaries_dir, 'run_benchmark'),
rmistryaa31ee72015-04-23 12:47:33 -0700301 '--extra-browser-args="%s"' % self._browser_args,
borenetdc89ca52014-10-17 07:37:05 -0700302 '--browser=exact',
303 '--browser-executable=%s' % self._browser_executable,
304 SKP_BENCHMARK,
rmistry7620bf02014-10-27 06:42:11 -0700305 '--page-set-name=%s' % page_set_basename,
rmistryf802f322014-10-22 05:04:43 -0700306 '--page-set-base-dir=%s' % page_set_dir,
307 '--skp-outdir=%s' % TMP_SKP_DIR,
308 '--also-run-disabled-tests'
borenetdc89ca52014-10-17 07:37:05 -0700309 )
borenetdc89ca52014-10-17 07:37:05 -0700310
311 for _ in range(RETRY_RUN_MEASUREMENT_COUNT):
312 try:
313 print '\n\n=======Capturing SKP of %s=======\n\n' % page_set
borenet78399152014-10-17 12:15:46 -0700314 shell_utils.run(' '.join(run_benchmark_cmd), shell=True)
borenetdc89ca52014-10-17 07:37:05 -0700315 except shell_utils.CommandFailedException:
316 # skpicture_printer sometimes fails with AssertionError but the
317 # captured SKP is still valid. This is a known issue.
318 pass
319
borenetdc89ca52014-10-17 07:37:05 -0700320 # Rename generated SKP files into more descriptive names.
321 try:
322 self._RenameSkpFiles(page_set)
323 # Break out of the retry loop since there were no errors.
324 break
325 except Exception:
326 # There was a failure continue with the loop.
327 traceback.print_exc()
328 print '\n\n=======Retrying %s=======\n\n' % page_set
329 time.sleep(10)
330 else:
borenet78399152014-10-17 12:15:46 -0700331 # If we get here then run_benchmark did not succeed and thus did not
borenetdc89ca52014-10-17 07:37:05 -0700332 # break out of the loop.
borenet78399152014-10-17 12:15:46 -0700333 raise Exception('run_benchmark failed for page_set: %s' % page_set)
borenetdc89ca52014-10-17 07:37:05 -0700334
borenetdc89ca52014-10-17 07:37:05 -0700335 print '\n\n=======Capturing SKP files took %s seconds=======\n\n' % (
336 time.time() - start_time)
337
338 if self._skia_tools:
339 render_pictures_cmd = [
340 os.path.join(self._skia_tools, 'render_pictures'),
341 '-r', self._local_skp_dir
342 ]
343 render_pdfs_cmd = [
344 os.path.join(self._skia_tools, 'render_pdfs'),
kkinnunen3a6aa862014-12-03 04:22:06 -0800345 '-r', self._local_skp_dir
borenetdc89ca52014-10-17 07:37:05 -0700346 ]
347
348 for tools_cmd in (render_pictures_cmd, render_pdfs_cmd):
349 print '\n\n=======Running %s=======' % ' '.join(tools_cmd)
350 proc = subprocess.Popen(tools_cmd)
rmistry0ec28af2014-10-28 14:25:17 -0700351 (code, _) = shell_utils.log_process_after_completion(proc, echo=False)
borenetdc89ca52014-10-17 07:37:05 -0700352 if code != 0:
353 raise Exception('%s failed!' % ' '.join(tools_cmd))
354
355 if not self._non_interactive:
356 print '\n\n=======Running debugger======='
357 os.system('%s %s' % (os.path.join(self._skia_tools, 'debugger'),
kkinnunen960fb502014-12-03 06:18:12 -0800358 self._local_skp_dir))
borenetdc89ca52014-10-17 07:37:05 -0700359
360 print '\n\n'
361
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700362 if self._upload:
363 print '\n\n=======Uploading to %s=======\n\n' % self.gs.target_type()
borenetdc89ca52014-10-17 07:37:05 -0700364 # Copy the directory structure in the root directory into Google Storage.
365 dest_dir_name = ROOT_PLAYBACK_DIR_NAME
366 if self._alternate_upload_dir:
367 dest_dir_name = self._alternate_upload_dir
368
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700369 self.gs.upload_dir_contents(
rmistry009b0692015-03-31 05:20:12 -0700370 LOCAL_PLAYBACK_ROOT_DIR, dest_dir=dest_dir_name,
borenetdc89ca52014-10-17 07:37:05 -0700371 upload_if=gs_utils.GSUtils.UploadIf.IF_MODIFIED,
372 predefined_acl=GS_PREDEFINED_ACL,
373 fine_grained_acl_list=GS_FINE_GRAINED_ACL_LIST)
374
375 print '\n\n=======New SKPs have been uploaded to %s =======\n\n' % (
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700376 posixpath.join(self.gs.target_name(), dest_dir_name,
377 SKPICTURES_DIR_NAME))
rmistry0575c492016-02-01 10:27:05 -0800378
borenetdc89ca52014-10-17 07:37:05 -0700379 else:
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700380 print '\n\n=======Not Uploading to %s=======\n\n' % self.gs.target_type()
borenetdc89ca52014-10-17 07:37:05 -0700381 print 'Generated resources are available in %s\n\n' % (
382 LOCAL_PLAYBACK_ROOT_DIR)
383
borenet28698472016-07-25 05:18:15 -0700384 if self._upload_to_partner_bucket:
385 print '\n\n=======Uploading to Partner bucket %s =======\n\n' % (
386 PARTNERS_GS_BUCKET)
387 partner_gs = GoogleStorageDataStore(PARTNERS_GS_BUCKET)
388 partner_gs.delete_path(SKPICTURES_DIR_NAME)
389 partner_gs.upload_dir_contents(
390 os.path.join(LOCAL_PLAYBACK_ROOT_DIR, SKPICTURES_DIR_NAME),
391 dest_dir=SKPICTURES_DIR_NAME,
392 upload_if=gs_utils.GSUtils.UploadIf.IF_MODIFIED)
393 print '\n\n=======New SKPs have been uploaded to %s =======\n\n' % (
394 posixpath.join(partner_gs.target_name(), SKPICTURES_DIR_NAME))
395
borenetdc89ca52014-10-17 07:37:05 -0700396 return 0
397
rmistry49d093c2015-03-31 05:04:29 -0700398 def _GetSkiaSkpFileName(self, page_set):
399 """Returns the SKP file name for Skia page sets."""
400 # /path/to/skia_yahooanswers_desktop.py -> skia_yahooanswers_desktop.py
401 ps_filename = os.path.basename(page_set)
402 # skia_yahooanswers_desktop.py -> skia_yahooanswers_desktop
403 ps_basename, _ = os.path.splitext(ps_filename)
404 # skia_yahooanswers_desktop -> skia, yahooanswers, desktop
405 _, page_name, device = ps_basename.split('_')
406 basename = '%s_%s' % (DEVICE_TO_PLATFORM_PREFIX[device], page_name)
407 return basename[:MAX_SKP_BASE_NAME_LEN] + '.skp'
408
409 def _GetChromiumSkpFileName(self, page_set, site):
410 """Returns the SKP file name for Chromium page sets."""
411 # /path/to/http___mobile_news_sandbox_pt0 -> http___mobile_news_sandbox_pt0
412 _, webpage = os.path.split(site)
413 # http___mobile_news_sandbox_pt0 -> mobile_news_sandbox_pt0
rmistry80bd3ae2015-04-03 08:22:51 -0700414 for prefix in ('http___', 'https___', 'www_'):
rmistry2a3c8492015-03-31 10:59:15 -0700415 if webpage.startswith(prefix):
416 webpage = webpage[len(prefix):]
rmistry49d093c2015-03-31 05:04:29 -0700417 # /path/to/skia_yahooanswers_desktop.py -> skia_yahooanswers_desktop.py
418 ps_filename = os.path.basename(page_set)
419 # http___mobile_news_sandbox -> pagesetprefix_http___mobile_news_sandbox
420 basename = '%s_%s' % (CHROMIUM_PAGE_SETS_TO_PREFIX[ps_filename], webpage)
421 return basename[:MAX_SKP_BASE_NAME_LEN] + '.skp'
422
borenetdc89ca52014-10-17 07:37:05 -0700423 def _RenameSkpFiles(self, page_set):
424 """Rename generated SKP files into more descriptive names.
425
426 Look into the subdirectory of TMP_SKP_DIR and find the most interesting
427 .skp in there to be this page_set's representative .skp.
428 """
borenetdc89ca52014-10-17 07:37:05 -0700429 subdirs = glob.glob(os.path.join(TMP_SKP_DIR, '*'))
borenetdc89ca52014-10-17 07:37:05 -0700430 for site in subdirs:
rmistry49d093c2015-03-31 05:04:29 -0700431 if self._IsChromiumPageSet(page_set):
432 filename = self._GetChromiumSkpFileName(page_set, site)
433 else:
434 filename = self._GetSkiaSkpFileName(page_set)
rmistry80bd3ae2015-04-03 08:22:51 -0700435 filename = filename.lower()
rmistry49d093c2015-03-31 05:04:29 -0700436
rmistryaa31ee72015-04-23 12:47:33 -0700437 if self._skp_prefix:
438 filename = '%s%s' % (self._skp_prefix, filename)
439
borenetdc89ca52014-10-17 07:37:05 -0700440 # We choose the largest .skp as the most likely to be interesting.
441 largest_skp = max(glob.glob(os.path.join(site, '*.skp')),
442 key=lambda path: os.stat(path).st_size)
443 dest = os.path.join(self._local_skp_dir, filename)
444 print 'Moving', largest_skp, 'to', dest
445 shutil.move(largest_skp, dest)
446 self._skp_files.append(filename)
447 shutil.rmtree(site)
448
449 def _CreateLocalStorageDirs(self):
450 """Creates required local storage directories for this script."""
451 for d in (self._local_record_webpages_archive_dir,
452 self._local_skp_dir):
453 if os.path.exists(d):
454 shutil.rmtree(d)
455 os.makedirs(d)
456
rmistry7620bf02014-10-27 06:42:11 -0700457 def _DownloadWebpagesArchive(self, wpr_data_file, page_set_json_name):
borenetdc89ca52014-10-17 07:37:05 -0700458 """Downloads the webpages archive and its required page set from GS."""
459 wpr_source = posixpath.join(ROOT_PLAYBACK_DIR_NAME, 'webpages_archive',
460 wpr_data_file)
461 page_set_source = posixpath.join(ROOT_PLAYBACK_DIR_NAME,
462 'webpages_archive',
rmistry7620bf02014-10-27 06:42:11 -0700463 page_set_json_name)
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700464 gs = self.gs
465 if (gs.does_storage_object_exist(wpr_source) and
466 gs.does_storage_object_exist(page_set_source)):
467 gs.download_file(wpr_source,
borenetdc89ca52014-10-17 07:37:05 -0700468 os.path.join(LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR,
469 wpr_data_file))
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700470 gs.download_file(page_set_source,
borenetdc89ca52014-10-17 07:37:05 -0700471 os.path.join(LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR,
rmistry7620bf02014-10-27 06:42:11 -0700472 page_set_json_name))
borenetdc89ca52014-10-17 07:37:05 -0700473 else:
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700474 raise Exception('%s and %s do not exist in %s!' % (gs.target_type(),
475 wpr_source, page_set_source))
borenetdc89ca52014-10-17 07:37:05 -0700476
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700477class DataStore:
478 """An abstract base class for uploading recordings to a data storage.
479 The interface emulates the google storage api."""
480 def target_name(self):
481 raise NotImplementedError()
482 def target_type(self):
483 raise NotImplementedError()
484 def does_storage_object_exist(self, *args):
485 raise NotImplementedError()
486 def download_file(self, *args):
487 raise NotImplementedError()
488 def upload_dir_contents(self, source_dir, **kwargs):
489 raise NotImplementedError()
490
491class GoogleStorageDataStore(DataStore):
492 def __init__(self, data_store_url):
493 self._data_store_url = data_store_url
rmistry49d093c2015-03-31 05:04:29 -0700494 self._bucket = remove_prefix(self._data_store_url.lstrip(),
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700495 gs_utils.GS_PREFIX)
496 self.gs = gs_utils.GSUtils()
497 def target_name(self):
498 return self._data_store_url
499 def target_type(self):
500 return 'Google Storage'
501 def does_storage_object_exist(self, *args):
502 return self.gs.does_storage_object_exist(self._bucket, *args)
rmistryc33c79c2016-02-03 04:27:54 -0800503 def delete_path(self, path):
rmistryf5e83952016-02-03 05:58:31 -0800504 _, files = self.gs.list_bucket_contents(self._bucket, subdir=path)
505 for f in files:
506 self.gs.delete_file(self._bucket, posixpath.join(path, f))
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700507 def download_file(self, *args):
508 self.gs.download_file(self._bucket, *args)
509 def upload_dir_contents(self, source_dir, **kwargs):
510 self.gs.upload_dir_contents(source_dir, self._bucket, **kwargs)
511
512class LocalFileSystemDataStore(DataStore):
513 def __init__(self, data_store_location):
514 self._base_dir = data_store_location
515 def target_name(self):
516 return self._base_dir
517 def target_type(self):
518 return self._base_dir
519 def does_storage_object_exist(self, name, *args):
520 return os.path.isfile(os.path.join(self._base_dir, name))
rmistryc33c79c2016-02-03 04:27:54 -0800521 def delete_path(self, path):
522 shutil.rmtree(path)
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700523 def download_file(self, name, local_path, *args):
524 shutil.copyfile(os.path.join(self._base_dir, name), local_path)
525 def upload_dir_contents(self, source_dir, dest_dir, **kwargs):
526 def copytree(source_dir, dest_dir):
527 if not os.path.exists(dest_dir):
528 os.makedirs(dest_dir)
529 for item in os.listdir(source_dir):
530 source = os.path.join(source_dir, item)
531 dest = os.path.join(dest_dir, item)
532 if os.path.isdir(source):
533 copytree(source, dest)
534 else:
535 shutil.copy2(source, dest)
536 copytree(source_dir, os.path.join(self._base_dir, dest_dir))
borenetdc89ca52014-10-17 07:37:05 -0700537
538if '__main__' == __name__:
539 option_parser = optparse.OptionParser()
540 option_parser.add_option(
541 '', '--page_sets',
542 help='Specifies the page sets to use to archive. Supports globs.',
543 default='all')
544 option_parser.add_option(
borenetdc89ca52014-10-17 07:37:05 -0700545 '', '--record', action='store_true',
546 help='Specifies whether a new website archive should be created.',
547 default=False)
548 option_parser.add_option(
borenetdc89ca52014-10-17 07:37:05 -0700549 '', '--skia_tools',
550 help=('Path to compiled Skia executable tools. '
551 'render_pictures/render_pdfs is run on the set '
552 'after all SKPs are captured. If the script is run without '
553 '--non-interactive then the debugger is also run at the end. Debug '
554 'builds are recommended because they seem to catch more failures '
555 'than Release builds.'),
556 default=None)
557 option_parser.add_option(
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700558 '', '--upload', action='store_true',
559 help=('Uploads to Google Storage or copies to local filesystem storage '
560 ' if this is True.'),
borenetdc89ca52014-10-17 07:37:05 -0700561 default=False)
562 option_parser.add_option(
rmistry0575c492016-02-01 10:27:05 -0800563 '', '--upload_to_partner_bucket', action='store_true',
564 help=('Uploads SKPs to the chrome-partner-telemetry Google Storage '
565 'bucket if true.'),
566 default=False)
567 option_parser.add_option(
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700568 '', '--data_store',
569 help=('The location of the file storage to use to download and upload '
570 'files. Can be \'gs://<bucket>\' for Google Storage, or '
571 'a directory for local filesystem storage'),
572 default='gs://chromium-skia-gm')
573 option_parser.add_option(
borenetdc89ca52014-10-17 07:37:05 -0700574 '', '--alternate_upload_dir',
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700575 help= ('Uploads to a different directory in Google Storage or local '
576 'storage if this flag is specified'),
borenetdc89ca52014-10-17 07:37:05 -0700577 default=None)
578 option_parser.add_option(
579 '', '--output_dir',
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700580 help=('Temporary directory where SKPs and webpage archives will be '
581 'outputted to.'),
borenetdc89ca52014-10-17 07:37:05 -0700582 default=tempfile.gettempdir())
583 option_parser.add_option(
584 '', '--browser_executable',
585 help='The exact browser executable to run.',
586 default=None)
587 option_parser.add_option(
rmistryaa31ee72015-04-23 12:47:33 -0700588 '', '--browser_extra_args',
589 help='Additional arguments to pass to the browser.',
590 default=None)
591 option_parser.add_option(
borenetdc89ca52014-10-17 07:37:05 -0700592 '', '--chrome_src_path',
593 help='Path to the chromium src directory.',
594 default=None)
595 option_parser.add_option(
596 '', '--non-interactive', action='store_true',
597 help='Runs the script without any prompts. If this flag is specified and '
598 '--skia_tools is specified then the debugger is not run.',
599 default=False)
rmistryaa31ee72015-04-23 12:47:33 -0700600 option_parser.add_option(
601 '', '--skp_prefix',
602 help='Prefix to add to the names of generated SKPs.',
603 default=None)
borenetdc89ca52014-10-17 07:37:05 -0700604 options, unused_args = option_parser.parse_args()
605
606 playback = SkPicturePlayback(options)
607 sys.exit(playback.Run())