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

Side by Side Diff: tools/perf/profile_creators/extensions_profile_creator.py

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

Powered by Google App Engine
This is Rietveld 408576698