Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(153)

Side by Side Diff: tools/telemetry/telemetry/page/extensions_profile_creator.py

Issue 311193003: [telemetry] Cleanups for benchmarks importing page sets. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Remove description fo' realz. (from gpu tests) Created 6 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « tools/telemetry/telemetry/core/discover.py ('k') | tools/telemetry/telemetry/page/page_set.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 # Copyright 2013 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
4 import json
5 import logging
6 import os
7 import platform
8 import shutil
9 import socket
10 import sys
11 import tempfile
12 import time
13 import urllib2
14 import zipfile
15
16 from telemetry.core import util
17 from telemetry.page import page_set
18 from telemetry.page import profile_creator
19
20
21 def _ExternalExtensionsPath():
22 """Returns the OS-dependent path at which to install the extension deployment
23 files"""
24 if platform.system() == 'Darwin':
25 return os.path.join('/Library', 'Application Support', 'Google', 'Chrome',
26 'External Extensions')
27 elif platform.system() == 'Linux':
28 return os.path.join('/opt', 'google', 'chrome', 'extensions' )
29 else:
30 raise NotImplementedError('Extension install on %s is not yet supported' %
31 platform.system())
32
33 def _DownloadExtension(extension_id, output_dir):
34 """Download an extension to disk.
35
36 Args:
37 extension_id: the extension id.
38 output_dir: Directory to download into.
39
40 Returns:
41 Extension file downloaded."""
42 extension_download_path = os.path.join(output_dir, "%s.crx" % extension_id)
43 extension_url = (
44 "https://clients2.google.com/service/update2/crx?response=redirect"
45 "&x=id%%3D%s%%26lang%%3Den-US%%26uc" % extension_id)
46 response = urllib2.urlopen(extension_url)
47 assert(response.getcode() == 200)
48
49 with open(extension_download_path, "w") as f:
50 f.write(response.read())
51
52 return extension_download_path
53
54 def _GetExtensionInfoFromCRX(crx_path):
55 """Parse an extension archive and return information.
56
57 Note:
58 The extension name returned by this function may not be valid
59 (e.g. in the case of a localized extension name). It's use is just
60 meant to be informational.
61
62 Args:
63 crx_path: path to crx archive to look at.
64
65 Returns:
66 Tuple consisting of:
67 (crx_version, extension_name)"""
68 crx_zip = zipfile.ZipFile(crx_path)
69 manifest_contents = crx_zip.read('manifest.json')
70 decoded_manifest = json.loads(manifest_contents)
71 crx_version = decoded_manifest['version']
72 extension_name = decoded_manifest['name']
73
74 return (crx_version, extension_name)
75
76 class ExtensionsProfileCreator(profile_creator.ProfileCreator):
77 """Virtual base class for profile creators that install extensions.
78
79 Extensions are installed using the mechanism described in
80 https://developer.chrome.com/extensions/external_extensions.html .
81
82 Subclasses are meant to be run interactively.
83 """
84
85 def __init__(self):
86 super(ExtensionsProfileCreator, self).__init__()
87 typical_25 = os.path.join(util.GetBaseDir(), 'page_sets', 'typical_25.py')
88 self._page_set = page_set.PageSet.FromFile(typical_25)
89
90 # Directory into which the output profile is written.
91 self._output_profile_path = None
92
93 # List of extensions to install.
94 self._extensions_to_install = []
95
96 # Theme to install (if any).
97 self._theme_to_install = None
98
99 # Directory to download extension files into.
100 self._extension_download_dir = None
101
102 # Have the extensions been installed yet?
103 self._extensions_installed = False
104
105 # List of files to delete after run.
106 self._files_to_cleanup = []
107
108 def _PrepareExtensionInstallFiles(self):
109 """Download extension archives and create extension install files."""
110 extensions_to_install = self._extensions_to_install
111 if self._theme_to_install:
112 extensions_to_install = extensions_to_install + [self._theme_to_install]
113 num_extensions = len(extensions_to_install)
114 if not num_extensions:
115 raise ValueError("No extensions or themes to install:",
116 extensions_to_install)
117
118 # Create external extensions path if it doesn't exist already.
119 external_extensions_dir = _ExternalExtensionsPath()
120 if not os.path.isdir(external_extensions_dir):
121 os.makedirs(external_extensions_dir)
122
123 self._extension_download_dir = tempfile.mkdtemp()
124
125 for i in xrange(num_extensions):
126 extension_id = extensions_to_install[i]
127 logging.info("Downloading %s - %d/%d" % (
128 extension_id, (i + 1), num_extensions))
129 extension_path = _DownloadExtension(extension_id,
130 self._extension_download_dir)
131 (version, name) = _GetExtensionInfoFromCRX(extension_path)
132 extension_info = {'external_crx' : extension_path,
133 'external_version' : version,
134 '_comment' : name}
135 extension_json_path = os.path.join(external_extensions_dir,
136 "%s.json" % extension_id)
137 with open(extension_json_path, 'w') as f:
138 f.write(json.dumps(extension_info))
139 self._files_to_cleanup.append(extension_json_path)
140
141 def _CleanupExtensionInstallFiles(self):
142 """Cleanup stray files before exiting."""
143 logging.info("Cleaning up stray files")
144 for filename in self._files_to_cleanup:
145 os.remove(filename)
146
147 if self._extension_download_dir:
148 # Simple sanity check to lessen the impact of a stray rmtree().
149 if len(self._extension_download_dir.split(os.sep)) < 3:
150 raise Exception("Path too shallow: %s" % self._extension_download_dir)
151 shutil.rmtree(self._extension_download_dir)
152 self._extension_download_dir = None
153
154 def CustomizeBrowserOptions(self, options):
155 self._output_profile_path = options.output_profile_path
156
157 def WillRunTest(self, options):
158 """Run before browser starts.
159
160 Download extensions and write installation files."""
161 super(ExtensionsProfileCreator, self).WillRunTest(options)
162
163 # Running this script on a corporate network or other managed environment
164 # could potentially alter the profile contents.
165 hostname = socket.gethostname()
166 if hostname.endswith('corp.google.com'):
167 raise Exception("It appears you are connected to a corporate network "
168 "(hostname=%s). This script needs to be run off the corp "
169 "network." % hostname)
170
171 prompt = ("\n!!!This script must be run on a fresh OS installation, "
172 "disconnected from any corporate network. Are you sure you want to "
173 "continue? (y/N) ")
174 if (raw_input(prompt).lower() != 'y'):
175 sys.exit(-1)
176 self._PrepareExtensionInstallFiles()
177
178 def DidRunTest(self, browser, results):
179 """Run before exit."""
180 super(ExtensionsProfileCreator, self).DidRunTest()
181 # Do some basic sanity checks to make sure the profile is complete.
182 installed_extensions = browser.extensions.keys()
183 if not len(installed_extensions) == len(self._extensions_to_install):
184 # Diagnosing errors:
185 # Too many extensions: Managed environment may be installing additional
186 # extensions.
187 raise Exception("Unexpected number of extensions installed in browser",
188 installed_extensions)
189
190 # Check that files on this list exist and have content.
191 expected_files = [
192 os.path.join('Default', 'Network Action Predictor')]
193 for filename in expected_files:
194 filename = os.path.join(self._output_profile_path, filename)
195 if not os.path.getsize(filename) > 0:
196 raise Exception("Profile not complete: %s is zero length." % filename)
197
198 self._CleanupExtensionInstallFiles()
199
200 def CanRunForPage(self, page):
201 # No matter how many pages in the pageset, just perform two test iterations.
202 return page.page_set.pages.index(page) < 2
203
204 def MeasurePage(self, _, tab, results):
205 # Profile setup works in 2 phases:
206 # Phase 1: When the first page is loaded: we wait for a timeout to allow
207 # all extensions to install and to prime safe browsing and other
208 # caches. Extensions may open tabs as part of the install process.
209 # Phase 2: When the second page loads, page_runner closes all tabs -
210 # we are left with one open tab, wait for that to finish loading.
211
212 # Sleep for a bit to allow safe browsing and other data to load +
213 # extensions to install.
214 if not self._extensions_installed:
215 sleep_seconds = 5 * 60
216 logging.info("Sleeping for %d seconds." % sleep_seconds)
217 time.sleep(sleep_seconds)
218 self._extensions_installed = True
219 else:
220 # Phase 2: Wait for tab to finish loading.
221 for i in xrange(len(tab.browser.tabs)):
222 t = tab.browser.tabs[i]
223 t.WaitForDocumentReadyStateToBeComplete()
OLDNEW
« no previous file with comments | « tools/telemetry/telemetry/core/discover.py ('k') | tools/telemetry/telemetry/page/page_set.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698