OLD | NEW |
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 |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
47 # Profile setup works in 2 phases: | 47 # Profile setup works in 2 phases: |
48 # Phase 1: When the first page is loaded: we wait for a timeout to allow | 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 | 49 # all extensions to install and to prime safe browsing and other |
50 # caches. Extensions may open tabs as part of the install process. | 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 - | 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. | 52 # we are left with one open tab, wait for that to finish loading. |
53 | 53 |
54 # Sleep for a bit to allow safe browsing and other data to load + | 54 # Sleep for a bit to allow safe browsing and other data to load + |
55 # extensions to install. | 55 # extensions to install. |
56 if not self._extensions_installed: | 56 if not self._extensions_installed: |
57 sleep_seconds = 5 * 60 | 57 sleep_seconds = 15 |
58 logging.info("Sleeping for %d seconds." % sleep_seconds) | 58 logging.info("Sleeping for %d seconds." % sleep_seconds) |
59 time.sleep(sleep_seconds) | 59 time.sleep(sleep_seconds) |
60 self._extensions_installed = True | 60 self._extensions_installed = True |
61 else: | 61 else: |
62 # Phase 2: Wait for tab to finish loading. | 62 # Phase 2: Wait for tab to finish loading. |
63 for i in xrange(len(tab.browser.tabs)): | 63 for i in xrange(len(tab.browser.tabs)): |
64 t = tab.browser.tabs[i] | 64 t = tab.browser.tabs[i] |
65 t.WaitForDocumentReadyStateToBeComplete() | 65 t.WaitForDocumentReadyStateToBeComplete() |
66 | 66 |
67 def DidRunTest(self, browser, results): | 67 def DidRunTest(self, browser, results): |
68 """Superclass override.""" | 68 """Superclass override.""" |
69 super(_ExtensionPageTest, self).DidRunTest(browser, | 69 super(_ExtensionPageTest, self).DidRunTest(browser, |
70 results) | 70 results) |
71 # Do some basic sanity checks to make sure the profile is complete. | 71 # Do some basic sanity checks to make sure the profile is complete. |
72 installed_extensions = browser.extensions.keys() | 72 installed_extensions = browser.extensions.keys() |
73 if not len(installed_extensions) == self._expected_extension_count: | 73 if not len(installed_extensions) == self._expected_extension_count: |
74 # Diagnosing errors: | 74 # Diagnosing errors: |
75 # Too many extensions: Managed environment may be installing additional | 75 # Too many extensions: Managed environment may be installing additional |
76 # extensions. | 76 # extensions. |
77 raise Exception("Unexpected number of extensions installed in browser", | 77 raise Exception("Unexpected number of extensions installed in browser", |
78 installed_extensions) | 78 installed_extensions) |
79 | 79 |
80 | 80 |
81 def _ExternalExtensionsPath(): | 81 def _ExternalExtensionsPath(profile_path): |
82 """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 |
83 files""" | 83 files. |
| 84 |
| 85 |profile_path| is the path of the profile that will be used to launch the |
| 86 browser. |
| 87 """ |
84 if platform.system() == 'Darwin': | 88 if platform.system() == 'Darwin': |
85 return os.path.join('/Library', 'Application Support', 'Google', 'Chrome', | 89 return str(profile_path) + '/External Extensions' |
86 'External Extensions') | 90 |
87 elif platform.system() == 'Linux': | |
88 return os.path.join('/opt', 'google', 'chrome', 'extensions' ) | |
89 else: | 91 else: |
90 raise NotImplementedError('Extension install on %s is not yet supported' % | 92 raise NotImplementedError('Extension install on %s is not yet supported' % |
91 platform.system()) | 93 platform.system()) |
92 | 94 |
93 | 95 |
94 def _DownloadExtension(extension_id, output_dir): | 96 def _DownloadExtension(extension_id, output_dir): |
95 """Download an extension to disk. | 97 """Download an extension to disk. |
96 | 98 |
97 Args: | 99 Args: |
98 extension_id: the extension id. | 100 extension_id: the extension id. |
99 output_dir: Directory to download into. | 101 output_dir: Directory to download into. |
100 | 102 |
101 Returns: | 103 Returns: |
102 Extension file downloaded.""" | 104 Extension file downloaded.""" |
103 extension_download_path = os.path.join(output_dir, "%s.crx" % extension_id) | 105 extension_download_path = os.path.join(output_dir, "%s.crx" % extension_id) |
| 106 |
| 107 # Ideally, the Chrome version would be dynamically extracted from the binary. |
| 108 # Instead, we use a Chrome version whose release date is expected to be |
| 109 # about a hundred years in the future. |
| 110 chrome_version = '1000.0.0.0' |
104 extension_url = ( | 111 extension_url = ( |
105 "https://clients2.google.com/service/update2/crx?response=redirect" | 112 "https://clients2.google.com/service/update2/crx?response=redirect" |
106 "&x=id%%3D%s%%26lang%%3Den-US%%26uc" % extension_id) | 113 "&prodversion=%s&x=id%%3D%s%%26lang%%3Den-US%%26uc" |
| 114 % (chrome_version, extension_id)) |
107 response = urllib2.urlopen(extension_url) | 115 response = urllib2.urlopen(extension_url) |
108 assert(response.getcode() == 200) | 116 assert(response.getcode() == 200) |
109 | 117 |
110 with open(extension_download_path, "w") as f: | 118 with open(extension_download_path, "w") as f: |
111 f.write(response.read()) | 119 f.write(response.read()) |
112 | 120 |
113 return extension_download_path | 121 return extension_download_path |
114 | 122 |
115 | 123 |
116 def _GetExtensionInfoFromCRX(crx_path): | 124 def _GetExtensionInfoFromCRX(crx_path): |
(...skipping 21 matching lines...) Expand all Loading... |
138 | 146 |
139 class ExtensionsProfileCreator(profile_creator.ProfileCreator): | 147 class ExtensionsProfileCreator(profile_creator.ProfileCreator): |
140 """Abstract base class for profile creators that install extensions. | 148 """Abstract base class for profile creators that install extensions. |
141 | 149 |
142 Extensions are installed using the mechanism described in | 150 Extensions are installed using the mechanism described in |
143 https://developer.chrome.com/extensions/external_extensions.html . | 151 https://developer.chrome.com/extensions/external_extensions.html . |
144 | 152 |
145 Subclasses are meant to be run interactively. | 153 Subclasses are meant to be run interactively. |
146 """ | 154 """ |
147 def __init__(self, extensions_to_install=None, theme_to_install=None): | 155 def __init__(self, extensions_to_install=None, theme_to_install=None): |
148 self._CheckTestEnvironment() | |
149 super(ExtensionsProfileCreator, self).__init__() | 156 super(ExtensionsProfileCreator, self).__init__() |
150 | 157 |
151 # List of extensions to install. | 158 # List of extensions to install. |
152 self._extensions_to_install = list(extensions_to_install or []) | 159 self._extensions_to_install = [] |
153 | 160 if extensions_to_install: |
154 # Theme to install (if any). | 161 self._extensions_to_install.extend(extensions_to_install) |
155 self._theme_to_install = theme_to_install | 162 if theme_to_install: |
| 163 self._extensions_to_install.append(theme_to_install) |
156 | 164 |
157 # Directory to download extension files into. | 165 # Directory to download extension files into. |
158 self._extension_download_dir = None | 166 self._extension_download_dir = None |
159 | 167 |
160 # List of files to delete after run. | 168 # List of files to delete after run. |
161 self._files_to_cleanup = [] | 169 self._files_to_cleanup = [] |
162 | 170 |
163 def _CheckTestEnvironment(self): | 171 def Run(self, options): |
164 # Running this script on a corporate network or other managed environment | 172 # Installing extensions requires that the profile directory exist before |
165 # could potentially alter the profile contents. | 173 # the browser is launched. |
166 hostname = socket.gethostname() | 174 if not options.browser_options.profile_dir: |
167 if hostname.endswith('corp.google.com'): | 175 options.browser_options.profile_dir = tempfile.mkdtemp() |
168 raise Exception("It appears you are connected to a corporate network " | 176 options.browser_options.disable_default_apps = False |
169 "(hostname=%s). This script needs to be run off the corp " | |
170 "network." % hostname) | |
171 | 177 |
172 prompt = ("\n!!!This script must be run on a fresh OS installation, " | 178 self._PrepareExtensionInstallFiles(options.browser_options.profile_dir) |
173 "disconnected from any corporate network. Are you sure you want to " | |
174 "continue? (y/N) ") | |
175 if (raw_input(prompt).lower() != 'y'): | |
176 sys.exit(-1) | |
177 | |
178 def Run(self, options): | |
179 self._PrepareExtensionInstallFiles() | |
180 | 179 |
181 expectations = test_expectations.TestExpectations() | 180 expectations = test_expectations.TestExpectations() |
182 results = results_options.CreateResults( | 181 results = results_options.CreateResults( |
183 benchmark.BenchmarkMetadata(profile_creator.__class__.__name__), | 182 benchmark.BenchmarkMetadata(profile_creator.__class__.__name__), |
184 options) | 183 options) |
185 extension_page_test = _ExtensionPageTest() | 184 extension_page_test = _ExtensionPageTest() |
186 extension_page_test._expected_extension_count = len( | 185 extension_page_test._expected_extension_count = len( |
187 self._extensions_to_install) | 186 self._extensions_to_install) |
188 user_story_runner.Run(extension_page_test, extension_page_test._page_set, | 187 user_story_runner.Run(extension_page_test, extension_page_test._page_set, |
189 expectations, options, results) | 188 expectations, options, results) |
190 | 189 |
191 self._CleanupExtensionInstallFiles() | 190 self._CleanupExtensionInstallFiles() |
192 | 191 |
193 # Check that files on this list exist and have content. | 192 # Check that files on this list exist and have content. |
194 expected_files = [ | 193 expected_files = [ |
195 os.path.join('Default', 'Network Action Predictor')] | 194 os.path.join('Default', 'Network Action Predictor')] |
196 for filename in expected_files: | 195 for filename in expected_files: |
197 filename = os.path.join(options.output_profile_path, filename) | 196 filename = os.path.join(options.output_profile_path, filename) |
198 if not os.path.getsize(filename) > 0: | 197 if not os.path.getsize(filename) > 0: |
199 raise Exception("Profile not complete: %s is zero length." % filename) | 198 raise Exception("Profile not complete: %s is zero length." % filename) |
200 | 199 |
201 if results.failures: | 200 if results.failures: |
202 logging.warning('Some pages failed.') | 201 logging.warning('Some pages failed.') |
203 logging.warning('Failed pages:\n%s', | 202 logging.warning('Failed pages:\n%s', |
204 '\n'.join(map(str, results.pages_that_failed))) | 203 '\n'.join(map(str, results.pages_that_failed))) |
205 raise Exception('ExtensionsProfileCreator failed.') | 204 raise Exception('ExtensionsProfileCreator failed.') |
206 | 205 |
207 def _PrepareExtensionInstallFiles(self): | 206 def _PrepareExtensionInstallFiles(self, profile_path): |
208 """Download extension archives and create extension install files.""" | 207 """Download extension archives and create extension install files.""" |
209 extensions_to_install = self._extensions_to_install | 208 extensions_to_install = self._extensions_to_install |
210 if self._theme_to_install: | |
211 extensions_to_install.append(self._theme_to_install) | |
212 if not extensions_to_install: | 209 if not extensions_to_install: |
213 raise ValueError("No extensions or themes to install:", | 210 raise ValueError("No extensions or themes to install:", |
214 extensions_to_install) | 211 extensions_to_install) |
215 | 212 |
216 # Create external extensions path if it doesn't exist already. | 213 # Create external extensions path if it doesn't exist already. |
217 external_extensions_dir = _ExternalExtensionsPath() | 214 external_extensions_dir = _ExternalExtensionsPath(profile_path) |
218 if not os.path.isdir(external_extensions_dir): | 215 if not os.path.isdir(external_extensions_dir): |
219 os.makedirs(external_extensions_dir) | 216 os.makedirs(external_extensions_dir) |
220 | 217 |
221 self._extension_download_dir = tempfile.mkdtemp() | 218 self._extension_download_dir = tempfile.mkdtemp() |
222 | 219 |
223 num_extensions = len(extensions_to_install) | 220 num_extensions = len(extensions_to_install) |
224 for i, extension_id in extensions_to_install: | 221 for i in range(num_extensions): |
| 222 extension_id = extensions_to_install[i] |
225 logging.info("Downloading %s - %d/%d" % ( | 223 logging.info("Downloading %s - %d/%d" % ( |
226 extension_id, (i + 1), num_extensions)) | 224 extension_id, (i + 1), num_extensions)) |
227 extension_path = _DownloadExtension(extension_id, | 225 extension_path = _DownloadExtension(extension_id, |
228 self._extension_download_dir) | 226 self._extension_download_dir) |
229 (version, name) = _GetExtensionInfoFromCRX(extension_path) | 227 (version, name) = _GetExtensionInfoFromCRX(extension_path) |
230 extension_info = {'external_crx' : extension_path, | 228 extension_info = {'external_crx' : extension_path, |
231 'external_version' : version, | 229 'external_version' : version, |
232 '_comment' : name} | 230 '_comment' : name} |
233 extension_json_path = os.path.join(external_extensions_dir, | 231 extension_json_path = os.path.join(external_extensions_dir, |
234 "%s.json" % extension_id) | 232 "%s.json" % extension_id) |
235 with open(extension_json_path, 'w') as f: | 233 with open(extension_json_path, 'w') as f: |
236 f.write(json.dumps(extension_info)) | 234 f.write(json.dumps(extension_info)) |
237 self._files_to_cleanup.append(extension_json_path) | 235 self._files_to_cleanup.append(extension_json_path) |
238 | 236 |
239 def _CleanupExtensionInstallFiles(self): | 237 def _CleanupExtensionInstallFiles(self): |
240 """Cleanup stray files before exiting.""" | 238 """Cleanup stray files before exiting.""" |
241 logging.info("Cleaning up stray files") | 239 logging.info("Cleaning up stray files") |
242 for filename in self._files_to_cleanup: | 240 for filename in self._files_to_cleanup: |
243 os.remove(filename) | 241 os.remove(filename) |
244 | 242 |
245 if self._extension_download_dir: | 243 if self._extension_download_dir: |
246 # Simple sanity check to lessen the impact of a stray rmtree(). | 244 # Simple sanity check to lessen the impact of a stray rmtree(). |
247 if len(self._extension_download_dir.split(os.sep)) < 3: | 245 if len(self._extension_download_dir.split(os.sep)) < 3: |
248 raise Exception("Path too shallow: %s" % self._extension_download_dir) | 246 raise Exception("Path too shallow: %s" % self._extension_download_dir) |
249 shutil.rmtree(self._extension_download_dir) | 247 shutil.rmtree(self._extension_download_dir) |
250 self._extension_download_dir = None | 248 self._extension_download_dir = None |
OLD | NEW |