blob: 6a8562b1582881bc47091d07bb33b84855a13fce [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
borenetdc89ca52014-10-17 07:37:05 -070066
67ROOT_PLAYBACK_DIR_NAME = 'playback'
68SKPICTURES_DIR_NAME = 'skps'
69
borenet644638c2016-10-10 05:57:18 -070070GS_PREFIX = 'gs://'
71
rmistry0575c492016-02-01 10:27:05 -080072PARTNERS_GS_BUCKET = 'gs://chrome-partner-telemetry'
borenetdc89ca52014-10-17 07:37:05 -070073
74# Local archive and SKP directories.
borenetdc89ca52014-10-17 07:37:05 -070075LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR = os.path.join(
76 os.path.abspath(os.path.dirname(__file__)), 'page_sets', 'data')
77TMP_SKP_DIR = tempfile.mkdtemp()
78
rmistryf802f322014-10-22 05:04:43 -070079# Location of the credentials.json file and the string that represents missing
80# passwords.
81CREDENTIALS_FILE_PATH = os.path.join(
82 os.path.abspath(os.path.dirname(__file__)), 'page_sets', 'data',
83 'credentials.json'
84)
85
borenetdc89ca52014-10-17 07:37:05 -070086# Name of the SKP benchmark
87SKP_BENCHMARK = 'skpicture_printer'
88
89# The max base name length of Skp files.
90MAX_SKP_BASE_NAME_LEN = 31
91
92# Dictionary of device to platform prefixes for SKP files.
93DEVICE_TO_PLATFORM_PREFIX = {
94 'desktop': 'desk',
95 'galaxynexus': 'mobi',
96 'nexus10': 'tabl'
97}
98
99# How many times the record_wpr binary should be retried.
100RETRY_RECORD_WPR_COUNT = 5
borenet78399152014-10-17 12:15:46 -0700101# How many times the run_benchmark binary should be retried.
Ravi Mistry0f8e4db2017-11-28 11:24:32 -0500102RETRY_RUN_MEASUREMENT_COUNT = 3
borenetdc89ca52014-10-17 07:37:05 -0700103
rmistryf802f322014-10-22 05:04:43 -0700104# Location of the credentials.json file in Google Storage.
borenet75358672016-10-10 13:20:49 -0700105CREDENTIALS_GS_PATH = 'playback/credentials/credentials.json'
rmistryf802f322014-10-22 05:04:43 -0700106
borenetdc89ca52014-10-17 07:37:05 -0700107X11_DISPLAY = os.getenv('DISPLAY', ':0')
108
rmistry49d093c2015-03-31 05:04:29 -0700109# Path to Chromium's page sets.
110CHROMIUM_PAGE_SETS_PATH = os.path.join('tools', 'perf', 'page_sets')
111
112# Dictionary of supported Chromium page sets to their file prefixes.
113CHROMIUM_PAGE_SETS_TO_PREFIX = {
rmistry5af9a372015-03-31 06:22:55 -0700114 'key_mobile_sites_smooth.py': 'keymobi',
rmistry2a3c8492015-03-31 10:59:15 -0700115 'top_25_smooth.py': 'top25desk',
rmistry49d093c2015-03-31 05:04:29 -0700116}
117
Ravi Mistry19ea45e2017-11-29 08:38:12 -0500118PAGE_SETS_TO_EXCLUSIONS = {
119 # See skbug.com/7348
120 'key_mobile_sites_smooth.py': '"(digg|worldjournal)"',
121}
122
rmistry49d093c2015-03-31 05:04:29 -0700123
kkinnunene75d2d22014-12-03 04:38:46 -0800124def remove_prefix(s, prefix):
125 if s.startswith(prefix):
126 return s[len(prefix):]
127 return s
borenetdc89ca52014-10-17 07:37:05 -0700128
rmistry49d093c2015-03-31 05:04:29 -0700129
borenetdc89ca52014-10-17 07:37:05 -0700130class SkPicturePlayback(object):
131 """Class that archives or replays webpages and creates SKPs."""
132
133 def __init__(self, parse_options):
134 """Constructs a SkPicturePlayback BuildStep instance."""
135 assert parse_options.browser_executable, 'Must specify --browser_executable'
136 self._browser_executable = parse_options.browser_executable
rmistryaa31ee72015-04-23 12:47:33 -0700137 self._browser_args = '--disable-setuid-sandbox'
138 if parse_options.browser_extra_args:
139 self._browser_args = '%s %s' % (
140 self._browser_args, parse_options.browser_extra_args)
borenetdc89ca52014-10-17 07:37:05 -0700141
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
rmistryaa31ee72015-04-23 12:47:33 -0700151 self._skp_prefix = parse_options.skp_prefix
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700152 data_store_location = parse_options.data_store
borenet644638c2016-10-10 05:57:18 -0700153 if data_store_location.startswith(GS_PREFIX):
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700154 self.gs = GoogleStorageDataStore(data_store_location)
155 else:
156 self.gs = LocalFileSystemDataStore(data_store_location)
rmistry0575c492016-02-01 10:27:05 -0800157 self._upload_to_partner_bucket = parse_options.upload_to_partner_bucket
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')
rmistryafaf4962016-02-27 10:04:57 -0800161 self._catapult_dir = os.path.join(parse_options.chrome_src_path,
162 'third_party', 'catapult')
borenetdc89ca52014-10-17 07:37:05 -0700163
164 self._local_skp_dir = os.path.join(
165 parse_options.output_dir, ROOT_PLAYBACK_DIR_NAME, SKPICTURES_DIR_NAME)
166 self._local_record_webpages_archive_dir = os.path.join(
167 parse_options.output_dir, ROOT_PLAYBACK_DIR_NAME, 'webpages_archive')
168
169 # List of SKP files generated by this script.
170 self._skp_files = []
171
172 def _ParsePageSets(self, page_sets):
173 if not page_sets:
174 raise ValueError('Must specify at least one page_set!')
175 elif self._all_page_sets_specified:
176 # Get everything from the page_sets directory.
177 page_sets_dir = os.path.join(os.path.abspath(os.path.dirname(__file__)),
178 'page_sets')
179 ps = [os.path.join(page_sets_dir, page_set)
180 for page_set in os.listdir(page_sets_dir)
181 if not os.path.isdir(os.path.join(page_sets_dir, page_set)) and
182 page_set.endswith('.py')]
rmistry49d093c2015-03-31 05:04:29 -0700183 chromium_ps = [
184 os.path.join(self._chrome_page_sets_path, cr_page_set)
185 for cr_page_set in CHROMIUM_PAGE_SETS_TO_PREFIX]
186 ps.extend(chromium_ps)
borenetdc89ca52014-10-17 07:37:05 -0700187 elif '*' in page_sets:
188 # Explode and return the glob.
189 ps = glob.glob(page_sets)
190 else:
191 ps = page_sets.split(',')
192 ps.sort()
193 return ps
194
rmistry49d093c2015-03-31 05:04:29 -0700195 def _IsChromiumPageSet(self, page_set):
196 """Returns true if the specified page set is a Chromium page set."""
197 return page_set.startswith(self._chrome_page_sets_path)
198
borenetdc89ca52014-10-17 07:37:05 -0700199 def Run(self):
200 """Run the SkPicturePlayback BuildStep."""
201
rmistryf802f322014-10-22 05:04:43 -0700202 # Download the credentials file if it was not previously downloaded.
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700203 if not os.path.isfile(CREDENTIALS_FILE_PATH):
204 # Download the credentials.json file from Google Storage.
205 self.gs.download_file(CREDENTIALS_GS_PATH, CREDENTIALS_FILE_PATH)
206
207 if not os.path.isfile(CREDENTIALS_FILE_PATH):
208 print """\n\nCould not locate credentials file in the storage.
209 Please create a %s file that contains:
rmistryf802f322014-10-22 05:04:43 -0700210 {
211 "google": {
212 "username": "google_testing_account_username",
213 "password": "google_testing_account_password"
214 },
215 "facebook": {
216 "username": "facebook_testing_account_username",
217 "password": "facebook_testing_account_password"
218 }
219 }\n\n""" % CREDENTIALS_FILE_PATH
220 raw_input("Please press a key when you are ready to proceed...")
rmistryf802f322014-10-22 05:04:43 -0700221
borenetdc89ca52014-10-17 07:37:05 -0700222 # Delete any left over data files in the data directory.
223 for archive_file in glob.glob(
224 os.path.join(LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR, 'skia_*')):
225 os.remove(archive_file)
226
borenetdc89ca52014-10-17 07:37:05 -0700227 # 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'
Ravi Mistry0d056ba2017-08-02 11:21:58 -0400238 wpr_data_file = (
239 page_set.split(os.path.sep)[-1].split('.')[0] + '_000.wprgo')
rmistry7620bf02014-10-27 06:42:11 -0700240 page_set_dir = os.path.dirname(page_set)
borenetdc89ca52014-10-17 07:37:05 -0700241
rmistry49d093c2015-03-31 05:04:29 -0700242 if self._IsChromiumPageSet(page_set):
243 print 'Using Chromium\'s captured archives for Chromium\'s page sets.'
244 elif self._record:
borenetdc89ca52014-10-17 07:37:05 -0700245 # Create an archive of the specified webpages if '--record=True' is
246 # specified.
247 record_wpr_cmd = (
rmistryafaf4962016-02-27 10:04:57 -0800248 'PYTHONPATH=%s:%s:$PYTHONPATH' % (page_set_dir, self._catapult_dir),
borenetdc89ca52014-10-17 07:37:05 -0700249 'DISPLAY=%s' % X11_DISPLAY,
250 os.path.join(self._telemetry_binaries_dir, 'record_wpr'),
rmistryaa31ee72015-04-23 12:47:33 -0700251 '--extra-browser-args="%s"' % self._browser_args,
borenetdc89ca52014-10-17 07:37:05 -0700252 '--browser=exact',
253 '--browser-executable=%s' % self._browser_executable,
Ravi Mistry0d056ba2017-08-02 11:21:58 -0400254 '--use-wpr-go',
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:
borenet644638c2016-10-10 05:57:18 -0700260 subprocess.check_call(' '.join(record_wpr_cmd), shell=True)
kkinnunenf9310fe2015-03-29 22:33:16 -0700261
Ravi Mistry863ffef2017-07-18 13:13:53 -0400262 # Copy over the created archive into the local webpages archive
kkinnunenf9310fe2015-03-29 22:33:16 -0700263 # directory.
Ravi Mistry863ffef2017-07-18 13:13:53 -0400264 shutil.copy(
kkinnunenf9310fe2015-03-29 22:33:16 -0700265 os.path.join(LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR, wpr_data_file),
266 self._local_record_webpages_archive_dir)
Ravi Mistry863ffef2017-07-18 13:13:53 -0400267 shutil.copy(
kkinnunenf9310fe2015-03-29 22:33:16 -0700268 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
Ravi Mistry19ea45e2017-11-29 08:38:12 -0500286 run_benchmark_cmd = [
rmistryafaf4962016-02-27 10:04:57 -0800287 'PYTHONPATH=%s:%s:$PYTHONPATH' % (page_set_dir, self._catapult_dir),
borenetdc89ca52014-10-17 07:37:05 -0700288 'DISPLAY=%s' % X11_DISPLAY,
Ravi Mistry57d69652016-09-08 09:08:44 -0400289 'timeout', '1800',
borenet78399152014-10-17 12:15:46 -0700290 os.path.join(self._telemetry_binaries_dir, 'run_benchmark'),
rmistryaa31ee72015-04-23 12:47:33 -0700291 '--extra-browser-args="%s"' % self._browser_args,
borenetdc89ca52014-10-17 07:37:05 -0700292 '--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,
Ravi Mistry0f8e4db2017-11-28 11:24:32 -0500298 '--also-run-disabled-tests',
Ravi Mistry19ea45e2017-11-29 08:38:12 -0500299 ]
300
301 exclusions = PAGE_SETS_TO_EXCLUSIONS.get(os.path.basename(page_set))
302 if exclusions:
303 run_benchmark_cmd.append('--story-filter-exclude=' + exclusions)
borenetdc89ca52014-10-17 07:37:05 -0700304
305 for _ in range(RETRY_RUN_MEASUREMENT_COUNT):
306 try:
307 print '\n\n=======Capturing SKP of %s=======\n\n' % page_set
borenet644638c2016-10-10 05:57:18 -0700308 subprocess.check_call(' '.join(run_benchmark_cmd), shell=True)
309 except subprocess.CalledProcessError:
borenetdc89ca52014-10-17 07:37:05 -0700310 # There was a failure continue with the loop.
311 traceback.print_exc()
312 print '\n\n=======Retrying %s=======\n\n' % page_set
313 time.sleep(10)
Ravi Mistry0f8e4db2017-11-28 11:24:32 -0500314 continue
315
316 # Rename generated SKP files into more descriptive names.
317 self._RenameSkpFiles(page_set)
318 # Break out of the retry loop since there were no errors.
319 break
borenetdc89ca52014-10-17 07:37:05 -0700320 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)
borenet644638c2016-10-10 05:57:18 -0700340 subprocess.check_call(tools_cmd)
borenetdc89ca52014-10-17 07:37:05 -0700341
342 if not self._non_interactive:
343 print '\n\n=======Running debugger======='
344 os.system('%s %s' % (os.path.join(self._skia_tools, 'debugger'),
kkinnunen960fb502014-12-03 06:18:12 -0800345 self._local_skp_dir))
borenetdc89ca52014-10-17 07:37:05 -0700346
347 print '\n\n'
348
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700349 if self._upload:
350 print '\n\n=======Uploading to %s=======\n\n' % self.gs.target_type()
borenetdc89ca52014-10-17 07:37:05 -0700351 # Copy the directory structure in the root directory into Google Storage.
352 dest_dir_name = ROOT_PLAYBACK_DIR_NAME
353 if self._alternate_upload_dir:
354 dest_dir_name = self._alternate_upload_dir
355
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700356 self.gs.upload_dir_contents(
borenet644638c2016-10-10 05:57:18 -0700357 self._local_skp_dir, dest_dir=dest_dir_name)
borenetdc89ca52014-10-17 07:37:05 -0700358
359 print '\n\n=======New SKPs have been uploaded to %s =======\n\n' % (
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700360 posixpath.join(self.gs.target_name(), dest_dir_name,
361 SKPICTURES_DIR_NAME))
rmistry0575c492016-02-01 10:27:05 -0800362
borenetdc89ca52014-10-17 07:37:05 -0700363 else:
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700364 print '\n\n=======Not Uploading to %s=======\n\n' % self.gs.target_type()
borenetdc89ca52014-10-17 07:37:05 -0700365 print 'Generated resources are available in %s\n\n' % (
borenet1413d522016-08-08 06:26:00 -0700366 self._local_skp_dir)
borenetdc89ca52014-10-17 07:37:05 -0700367
borenet28698472016-07-25 05:18:15 -0700368 if self._upload_to_partner_bucket:
369 print '\n\n=======Uploading to Partner bucket %s =======\n\n' % (
370 PARTNERS_GS_BUCKET)
371 partner_gs = GoogleStorageDataStore(PARTNERS_GS_BUCKET)
372 partner_gs.delete_path(SKPICTURES_DIR_NAME)
borenet1413d522016-08-08 06:26:00 -0700373 print 'Uploading %s to %s' % (self._local_skp_dir, SKPICTURES_DIR_NAME)
borenet644638c2016-10-10 05:57:18 -0700374 partner_gs.upload_dir_contents(self._local_skp_dir, SKPICTURES_DIR_NAME)
borenet28698472016-07-25 05:18:15 -0700375 print '\n\n=======New SKPs have been uploaded to %s =======\n\n' % (
376 posixpath.join(partner_gs.target_name(), SKPICTURES_DIR_NAME))
377
borenetdc89ca52014-10-17 07:37:05 -0700378 return 0
379
rmistry49d093c2015-03-31 05:04:29 -0700380 def _GetSkiaSkpFileName(self, page_set):
381 """Returns the SKP file name for Skia page sets."""
382 # /path/to/skia_yahooanswers_desktop.py -> skia_yahooanswers_desktop.py
383 ps_filename = os.path.basename(page_set)
384 # skia_yahooanswers_desktop.py -> skia_yahooanswers_desktop
385 ps_basename, _ = os.path.splitext(ps_filename)
386 # skia_yahooanswers_desktop -> skia, yahooanswers, desktop
387 _, page_name, device = ps_basename.split('_')
388 basename = '%s_%s' % (DEVICE_TO_PLATFORM_PREFIX[device], page_name)
389 return basename[:MAX_SKP_BASE_NAME_LEN] + '.skp'
390
391 def _GetChromiumSkpFileName(self, page_set, site):
392 """Returns the SKP file name for Chromium page sets."""
393 # /path/to/http___mobile_news_sandbox_pt0 -> http___mobile_news_sandbox_pt0
394 _, webpage = os.path.split(site)
395 # http___mobile_news_sandbox_pt0 -> mobile_news_sandbox_pt0
rmistry80bd3ae2015-04-03 08:22:51 -0700396 for prefix in ('http___', 'https___', 'www_'):
rmistry2a3c8492015-03-31 10:59:15 -0700397 if webpage.startswith(prefix):
398 webpage = webpage[len(prefix):]
rmistry49d093c2015-03-31 05:04:29 -0700399 # /path/to/skia_yahooanswers_desktop.py -> skia_yahooanswers_desktop.py
400 ps_filename = os.path.basename(page_set)
401 # http___mobile_news_sandbox -> pagesetprefix_http___mobile_news_sandbox
402 basename = '%s_%s' % (CHROMIUM_PAGE_SETS_TO_PREFIX[ps_filename], webpage)
403 return basename[:MAX_SKP_BASE_NAME_LEN] + '.skp'
404
borenetdc89ca52014-10-17 07:37:05 -0700405 def _RenameSkpFiles(self, page_set):
406 """Rename generated SKP files into more descriptive names.
407
408 Look into the subdirectory of TMP_SKP_DIR and find the most interesting
409 .skp in there to be this page_set's representative .skp.
410 """
borenetdc89ca52014-10-17 07:37:05 -0700411 subdirs = glob.glob(os.path.join(TMP_SKP_DIR, '*'))
borenetdc89ca52014-10-17 07:37:05 -0700412 for site in subdirs:
rmistry49d093c2015-03-31 05:04:29 -0700413 if self._IsChromiumPageSet(page_set):
414 filename = self._GetChromiumSkpFileName(page_set, site)
415 else:
416 filename = self._GetSkiaSkpFileName(page_set)
rmistry80bd3ae2015-04-03 08:22:51 -0700417 filename = filename.lower()
rmistry49d093c2015-03-31 05:04:29 -0700418
rmistryaa31ee72015-04-23 12:47:33 -0700419 if self._skp_prefix:
420 filename = '%s%s' % (self._skp_prefix, filename)
421
borenetdc89ca52014-10-17 07:37:05 -0700422 # We choose the largest .skp as the most likely to be interesting.
423 largest_skp = max(glob.glob(os.path.join(site, '*.skp')),
424 key=lambda path: os.stat(path).st_size)
425 dest = os.path.join(self._local_skp_dir, filename)
426 print 'Moving', largest_skp, 'to', dest
427 shutil.move(largest_skp, dest)
428 self._skp_files.append(filename)
429 shutil.rmtree(site)
430
431 def _CreateLocalStorageDirs(self):
432 """Creates required local storage directories for this script."""
433 for d in (self._local_record_webpages_archive_dir,
434 self._local_skp_dir):
435 if os.path.exists(d):
436 shutil.rmtree(d)
437 os.makedirs(d)
438
rmistry7620bf02014-10-27 06:42:11 -0700439 def _DownloadWebpagesArchive(self, wpr_data_file, page_set_json_name):
borenetdc89ca52014-10-17 07:37:05 -0700440 """Downloads the webpages archive and its required page set from GS."""
441 wpr_source = posixpath.join(ROOT_PLAYBACK_DIR_NAME, 'webpages_archive',
442 wpr_data_file)
443 page_set_source = posixpath.join(ROOT_PLAYBACK_DIR_NAME,
444 'webpages_archive',
rmistry7620bf02014-10-27 06:42:11 -0700445 page_set_json_name)
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700446 gs = self.gs
447 if (gs.does_storage_object_exist(wpr_source) and
448 gs.does_storage_object_exist(page_set_source)):
449 gs.download_file(wpr_source,
borenetdc89ca52014-10-17 07:37:05 -0700450 os.path.join(LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR,
451 wpr_data_file))
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700452 gs.download_file(page_set_source,
borenetdc89ca52014-10-17 07:37:05 -0700453 os.path.join(LOCAL_REPLAY_WEBPAGES_ARCHIVE_DIR,
rmistry7620bf02014-10-27 06:42:11 -0700454 page_set_json_name))
borenetdc89ca52014-10-17 07:37:05 -0700455 else:
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700456 raise Exception('%s and %s do not exist in %s!' % (gs.target_type(),
457 wpr_source, page_set_source))
borenetdc89ca52014-10-17 07:37:05 -0700458
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700459class DataStore:
460 """An abstract base class for uploading recordings to a data storage.
461 The interface emulates the google storage api."""
462 def target_name(self):
463 raise NotImplementedError()
464 def target_type(self):
465 raise NotImplementedError()
borenet644638c2016-10-10 05:57:18 -0700466 def does_storage_object_exist(self, name):
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700467 raise NotImplementedError()
borenet644638c2016-10-10 05:57:18 -0700468 def download_file(self, name, local_path):
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700469 raise NotImplementedError()
borenet644638c2016-10-10 05:57:18 -0700470 def upload_dir_contents(self, source_dir, dest_dir):
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700471 raise NotImplementedError()
472
borenet644638c2016-10-10 05:57:18 -0700473
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700474class GoogleStorageDataStore(DataStore):
475 def __init__(self, data_store_url):
borenet1cebf7b2016-10-10 08:14:11 -0700476 self._url = data_store_url.rstrip('/')
borenet644638c2016-10-10 05:57:18 -0700477
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700478 def target_name(self):
borenet644638c2016-10-10 05:57:18 -0700479 return self._url
480
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700481 def target_type(self):
482 return 'Google Storage'
borenet644638c2016-10-10 05:57:18 -0700483
484 def does_storage_object_exist(self, name):
485 try:
486 output = subprocess.check_output([
487 'gsutil', 'ls', '/'.join((self._url, name))])
488 except subprocess.CalledProcessError:
489 return False
borenet75358672016-10-10 13:20:49 -0700490 if len(output.splitlines()) != 1:
borenet644638c2016-10-10 05:57:18 -0700491 return False
492 return True
493
rmistryc33c79c2016-02-03 04:27:54 -0800494 def delete_path(self, path):
borenet77874632016-10-17 05:12:10 -0700495 subprocess.check_call(['gsutil', 'rm', '-r', '/'.join((self._url, path))])
borenet644638c2016-10-10 05:57:18 -0700496
497 def download_file(self, name, local_path):
498 subprocess.check_call([
499 'gsutil', 'cp', '/'.join((self._url, name)), local_path])
500
501 def upload_dir_contents(self, source_dir, dest_dir):
502 subprocess.check_call([
borenet27f93e02016-10-17 08:09:33 -0700503 'gsutil', 'cp', '-r', source_dir, '/'.join((self._url, dest_dir))])
borenet644638c2016-10-10 05:57:18 -0700504
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700505
506class LocalFileSystemDataStore(DataStore):
507 def __init__(self, data_store_location):
508 self._base_dir = data_store_location
509 def target_name(self):
510 return self._base_dir
511 def target_type(self):
512 return self._base_dir
borenet644638c2016-10-10 05:57:18 -0700513 def does_storage_object_exist(self, name):
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700514 return os.path.isfile(os.path.join(self._base_dir, name))
rmistryc33c79c2016-02-03 04:27:54 -0800515 def delete_path(self, path):
516 shutil.rmtree(path)
borenet644638c2016-10-10 05:57:18 -0700517 def download_file(self, name, local_path):
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700518 shutil.copyfile(os.path.join(self._base_dir, name), local_path)
borenet644638c2016-10-10 05:57:18 -0700519 def upload_dir_contents(self, source_dir, dest_dir):
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700520 def copytree(source_dir, dest_dir):
521 if not os.path.exists(dest_dir):
522 os.makedirs(dest_dir)
523 for item in os.listdir(source_dir):
524 source = os.path.join(source_dir, item)
525 dest = os.path.join(dest_dir, item)
526 if os.path.isdir(source):
527 copytree(source, dest)
528 else:
529 shutil.copy2(source, dest)
530 copytree(source_dir, os.path.join(self._base_dir, dest_dir))
borenetdc89ca52014-10-17 07:37:05 -0700531
532if '__main__' == __name__:
533 option_parser = optparse.OptionParser()
534 option_parser.add_option(
535 '', '--page_sets',
536 help='Specifies the page sets to use to archive. Supports globs.',
537 default='all')
538 option_parser.add_option(
borenetdc89ca52014-10-17 07:37:05 -0700539 '', '--record', action='store_true',
540 help='Specifies whether a new website archive should be created.',
541 default=False)
542 option_parser.add_option(
borenetdc89ca52014-10-17 07:37:05 -0700543 '', '--skia_tools',
544 help=('Path to compiled Skia executable tools. '
545 'render_pictures/render_pdfs is run on the set '
546 'after all SKPs are captured. If the script is run without '
547 '--non-interactive then the debugger is also run at the end. Debug '
548 'builds are recommended because they seem to catch more failures '
549 'than Release builds.'),
550 default=None)
551 option_parser.add_option(
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700552 '', '--upload', action='store_true',
553 help=('Uploads to Google Storage or copies to local filesystem storage '
554 ' if this is True.'),
borenetdc89ca52014-10-17 07:37:05 -0700555 default=False)
556 option_parser.add_option(
rmistry0575c492016-02-01 10:27:05 -0800557 '', '--upload_to_partner_bucket', action='store_true',
558 help=('Uploads SKPs to the chrome-partner-telemetry Google Storage '
559 'bucket if true.'),
560 default=False)
561 option_parser.add_option(
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700562 '', '--data_store',
563 help=('The location of the file storage to use to download and upload '
564 'files. Can be \'gs://<bucket>\' for Google Storage, or '
565 'a directory for local filesystem storage'),
boreneta86952a2016-10-05 08:23:04 -0700566 default='gs://skia-skps')
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700567 option_parser.add_option(
borenetdc89ca52014-10-17 07:37:05 -0700568 '', '--alternate_upload_dir',
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700569 help= ('Uploads to a different directory in Google Storage or local '
570 'storage if this flag is specified'),
borenetdc89ca52014-10-17 07:37:05 -0700571 default=None)
572 option_parser.add_option(
573 '', '--output_dir',
kkinnunenb4ee7ea2015-03-31 00:18:26 -0700574 help=('Temporary directory where SKPs and webpage archives will be '
575 'outputted to.'),
borenetdc89ca52014-10-17 07:37:05 -0700576 default=tempfile.gettempdir())
577 option_parser.add_option(
578 '', '--browser_executable',
579 help='The exact browser executable to run.',
580 default=None)
581 option_parser.add_option(
rmistryaa31ee72015-04-23 12:47:33 -0700582 '', '--browser_extra_args',
583 help='Additional arguments to pass to the browser.',
584 default=None)
585 option_parser.add_option(
borenetdc89ca52014-10-17 07:37:05 -0700586 '', '--chrome_src_path',
587 help='Path to the chromium src directory.',
588 default=None)
589 option_parser.add_option(
590 '', '--non-interactive', action='store_true',
591 help='Runs the script without any prompts. If this flag is specified and '
592 '--skia_tools is specified then the debugger is not run.',
593 default=False)
rmistryaa31ee72015-04-23 12:47:33 -0700594 option_parser.add_option(
595 '', '--skp_prefix',
596 help='Prefix to add to the names of generated SKPs.',
597 default=None)
borenetdc89ca52014-10-17 07:37:05 -0700598 options, unused_args = option_parser.parse_args()
599
600 playback = SkPicturePlayback(options)
601 sys.exit(playback.Run())