OLD | NEW |
| (Empty) |
1 #!/usr/bin/env python | |
2 # Copyright (c) 2011 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 """ | |
7 This module is a simple qa tool that installs extensions and tests whether the | |
8 browser crashes while visiting a list of urls. | |
9 | |
10 Usage: python extensions.py -v | |
11 | |
12 Note: This assumes that there is a directory of extensions called | |
13 'extensions-tool' and that there is a file of newline-separated urls to visit | |
14 called 'urls.txt' in the data directory. | |
15 """ | |
16 | |
17 import glob | |
18 import logging | |
19 import os | |
20 import sys | |
21 | |
22 import pyauto_functional # must be imported before pyauto | |
23 import pyauto | |
24 | |
25 | |
26 class ExtensionsPage(object): | |
27 """Access options in extensions page (chrome://extensions-frame).""" | |
28 | |
29 _URL = 'chrome://extensions-frame' | |
30 | |
31 def __init__(self, driver): | |
32 self._driver = driver | |
33 self._driver.get(ExtensionsPage._URL) | |
34 | |
35 def CheckExtensionVisible(self, ext_id): | |
36 """Returns True if |ext_id| exists on page.""" | |
37 return len(self._driver.find_elements_by_id(ext_id)) == 1 | |
38 | |
39 def SetEnabled(self, ext_id, enabled): | |
40 """Clicks on 'Enabled' checkbox for specified extension. | |
41 | |
42 Args: | |
43 ext_id: Extension ID to be enabled or disabled. | |
44 enabled: Boolean indicating whether |ext_id| is to be enabled or disabled. | |
45 """ | |
46 checkbox = self._driver.find_element_by_xpath( | |
47 '//*[@id="%s"]//*[@class="enable-controls"]//*[@type="checkbox"]' % | |
48 ext_id) | |
49 if checkbox != enabled: | |
50 checkbox.click() | |
51 # Reload page to ensure that the UI is recreated. | |
52 self._driver.get(ExtensionsPage._URL) | |
53 | |
54 def SetAllowInIncognito(self, ext_id, allowed): | |
55 """Clicks on 'Allow in incognito' checkbox for specified extension. | |
56 | |
57 Args: | |
58 ext_id: Extension ID to be enabled or disabled. | |
59 allowed: Boolean indicating whether |ext_id| is to be allowed or | |
60 disallowed in incognito. | |
61 """ | |
62 checkbox = self._driver.find_element_by_xpath( | |
63 '//*[@id="%s"]//*[@class="incognito-control"]//*[@type="checkbox"]' % | |
64 ext_id) | |
65 if checkbox.is_selected() != allowed: | |
66 checkbox.click() | |
67 # Reload page to ensure that the UI is recreated. | |
68 self._driver.get(ExtensionsPage._URL) | |
69 | |
70 def SetAllowAccessFileURLs(self, ext_id, allowed): | |
71 """Clicks on 'Allow access to file URLs' checkbox for specified extension. | |
72 | |
73 Args: | |
74 ext_id: Extension ID to be enabled or disabled. | |
75 allowed: Boolean indicating whether |ext_id| is to be allowed access to | |
76 file URLs. | |
77 """ | |
78 checkbox = self._driver.find_element_by_xpath( | |
79 '//*[@id="%s"]//*[@class="file-access-control"]//*[@type="checkbox"]' % | |
80 ext_id) | |
81 if checkbox.is_selected() != allowed: | |
82 checkbox.click() | |
83 | |
84 | |
85 class ExtensionsTest(pyauto.PyUITest): | |
86 """Test of extensions.""" | |
87 | |
88 def Debug(self): | |
89 """Test method for experimentation. | |
90 | |
91 This method is not run automatically. | |
92 """ | |
93 while True: | |
94 raw_input('Interact with the browser and hit <enter> to dump history.') | |
95 print '*' * 20 | |
96 self.pprint(self.GetExtensionsInfo()) | |
97 | |
98 def _GetInstalledExtensionIds(self): | |
99 return [extension['id'] for extension in self.GetExtensionsInfo()] | |
100 | |
101 def _ReturnCrashingExtensions(self, extensions, group_size, top_urls): | |
102 """Returns the group of extensions that crashes (if any). | |
103 | |
104 Install the given extensions in groups of group_size and return the | |
105 group of extensions that crashes (if any). | |
106 | |
107 Args: | |
108 extensions: A list of extensions to install. | |
109 group_size: The number of extensions to install at one time. | |
110 top_urls: The list of top urls to visit. | |
111 | |
112 Returns: | |
113 The extensions in the crashing group or None if there is no crash. | |
114 """ | |
115 curr_extension = 0 | |
116 num_extensions = len(extensions) | |
117 self.RestartBrowser() | |
118 orig_extension_ids = self._GetInstalledExtensionIds() | |
119 | |
120 while curr_extension < num_extensions: | |
121 logging.debug('New group of %d extensions.', group_size) | |
122 group_end = curr_extension + group_size | |
123 for extension in extensions[curr_extension:group_end]: | |
124 logging.debug('Installing extension: %s', extension) | |
125 self.InstallExtension(extension) | |
126 | |
127 for url in top_urls: | |
128 self.NavigateToURL(url) | |
129 | |
130 def _LogAndReturnCrashing(): | |
131 crashing_extensions = extensions[curr_extension:group_end] | |
132 logging.debug('Crashing extensions: %s', crashing_extensions) | |
133 return crashing_extensions | |
134 | |
135 # If the browser has crashed, return the extensions in the failing group. | |
136 try: | |
137 num_browser_windows = self.GetBrowserWindowCount() | |
138 except: | |
139 return _LogAndReturnCrashing() | |
140 else: | |
141 if not num_browser_windows: | |
142 return _LogAndReturnCrashing() | |
143 else: | |
144 # Uninstall all extensions that aren't installed by default. | |
145 new_extension_ids = [id for id in self._GetInstalledExtensionIds() | |
146 if id not in orig_extension_ids] | |
147 for extension_id in new_extension_ids: | |
148 self.UninstallExtensionById(extension_id) | |
149 | |
150 curr_extension = group_end | |
151 | |
152 # None of the extensions crashed. | |
153 return None | |
154 | |
155 def _GetExtensionInfoById(self, extensions, id): | |
156 for x in extensions: | |
157 if x['id'] == id: | |
158 return x | |
159 return None | |
160 | |
161 def ExtensionCrashes(self): | |
162 """Add top extensions; confirm browser stays up when visiting top urls.""" | |
163 # TODO: provide a way in pyauto to pass args to a test - take these as args | |
164 extensions_dir = os.path.join(self.DataDir(), 'extensions-tool') | |
165 urls_file = os.path.join(self.DataDir(), 'urls.txt') | |
166 | |
167 error_msg = 'The dir "%s" must exist' % os.path.abspath(extensions_dir) | |
168 assert os.path.exists(extensions_dir), error_msg | |
169 error_msg = 'The file "%s" must exist' % os.path.abspath(urls_file) | |
170 assert os.path.exists(urls_file), error_msg | |
171 | |
172 num_urls_to_visit = 100 | |
173 extensions_group_size = 20 | |
174 | |
175 top_urls = [l.rstrip() for l in | |
176 open(urls_file).readlines()[:num_urls_to_visit]] | |
177 | |
178 failed_extensions = glob.glob(os.path.join(extensions_dir, '*.crx')) | |
179 group_size = extensions_group_size | |
180 | |
181 while (group_size and failed_extensions): | |
182 failed_extensions = self._ReturnCrashingExtensions( | |
183 failed_extensions, group_size, top_urls) | |
184 group_size = group_size // 2 | |
185 | |
186 self.assertFalse(failed_extensions, | |
187 'Extension(s) in failing group: %s' % failed_extensions) | |
188 | |
189 def _InstallExtensionCheckDefaults(self, crx_file): | |
190 """Installs extension at extensions/|crx_file| and checks default status. | |
191 | |
192 Checks that the installed extension is enabled and not allowed in incognito. | |
193 | |
194 Args: | |
195 crx_file: Relative path from self.DataDir()/extensions to .crx extension | |
196 to be installed. | |
197 | |
198 Returns: | |
199 The extension ID. | |
200 """ | |
201 crx_file_path = os.path.abspath( | |
202 os.path.join(self.DataDir(), 'extensions', crx_file)) | |
203 ext_id = self.InstallExtension(crx_file_path) | |
204 extension = self._GetExtensionInfoById(self.GetExtensionsInfo(), ext_id) | |
205 self.assertTrue(extension['is_enabled'], | |
206 msg='Extension was not enabled on installation') | |
207 self.assertFalse(extension['allowed_in_incognito'], | |
208 msg='Extension was allowed in incognito on installation.') | |
209 | |
210 return ext_id | |
211 | |
212 def _ExtensionValue(self, ext_id, key): | |
213 """Returns the value of |key| for |ext_id|. | |
214 | |
215 Args: | |
216 ext_id: The extension ID. | |
217 key: The key for which the extensions info value is required. | |
218 | |
219 Returns: | |
220 The value of extensions info |key| for |ext_id|. | |
221 """ | |
222 return self._GetExtensionInfoById(self.GetExtensionsInfo(), ext_id)[key] | |
223 | |
224 def _FileAccess(self, ext_id): | |
225 """Returns the value of newAllowFileAccess for |ext_id|. | |
226 | |
227 Args: | |
228 ext_id: The extension ID. | |
229 | |
230 Returns: | |
231 The value of extensions settings newAllowFileAccess for |ext_id|. | |
232 """ | |
233 extension_settings = self.GetPrefsInfo().Prefs()['extensions']['settings'] | |
234 return extension_settings[ext_id]['newAllowFileAccess'] | |
235 | |
236 def testGetExtensionPermissions(self): | |
237 """Ensures we can retrieve the host/api permissions for an extension. | |
238 | |
239 This test assumes that the 'Bookmark Manager' extension exists in a fresh | |
240 profile. | |
241 """ | |
242 extensions_info = self.GetExtensionsInfo() | |
243 bm_exts = [x for x in extensions_info if x['name'] == 'Bookmark Manager'] | |
244 self.assertTrue(bm_exts, | |
245 msg='Could not find info for the Bookmark Manager ' | |
246 'extension.') | |
247 ext = bm_exts[0] | |
248 | |
249 permissions_host = ext['host_permissions'] | |
250 self.assertTrue(len(permissions_host) == 2 and | |
251 'chrome://favicon/*' in permissions_host and | |
252 'chrome://resources/*' in permissions_host, | |
253 msg='Unexpected host permissions information.') | |
254 | |
255 permissions_api = ext['api_permissions'] | |
256 print permissions_api | |
257 self.assertTrue(len(permissions_api) == 5 and | |
258 'bookmarks' in permissions_api and | |
259 'bookmarkManagerPrivate' in permissions_api and | |
260 'metricsPrivate' in permissions_api and | |
261 'systemPrivate' in permissions_api and | |
262 'tabs' in permissions_api, | |
263 msg='Unexpected API permissions information.') | |
264 | |
265 def testDisableEnableExtension(self): | |
266 """Tests that an extension can be disabled and enabled with the UI.""" | |
267 ext_id = self._InstallExtensionCheckDefaults('good.crx') | |
268 | |
269 # Disable extension. | |
270 driver = self.NewWebDriver() | |
271 ext_page = ExtensionsPage(driver) | |
272 self.WaitUntil(ext_page.CheckExtensionVisible, args=[ext_id]) | |
273 ext_page.SetEnabled(ext_id, False) | |
274 self.WaitUntil(self._ExtensionValue, args=[ext_id, 'is_enabled'], | |
275 expect_retval=False) | |
276 self.assertFalse(self._ExtensionValue(ext_id, 'is_enabled'), | |
277 msg='Extension did not get disabled.') | |
278 | |
279 # Enable extension. | |
280 self.WaitUntil(ext_page.CheckExtensionVisible, args=[ext_id]) | |
281 ext_page.SetEnabled(ext_id, True) | |
282 self.WaitUntil(self._ExtensionValue, args=[ext_id, 'is_enabled'], | |
283 expect_retval=True) | |
284 self.assertTrue(self._ExtensionValue(ext_id, 'is_enabled'), | |
285 msg='Extension did not get enabled.') | |
286 | |
287 def testAllowIncognitoExtension(self): | |
288 """Tests allowing and disallowing an extension in incognito mode.""" | |
289 ext_id = self._InstallExtensionCheckDefaults('good.crx') | |
290 | |
291 # Allow in incognito. | |
292 driver = self.NewWebDriver() | |
293 ext_page = ExtensionsPage(driver) | |
294 self.WaitUntil(ext_page.CheckExtensionVisible, args=[ext_id]) | |
295 ext_page.SetAllowInIncognito(ext_id, True) | |
296 | |
297 # Check extension now allowed in incognito. | |
298 self.WaitUntil(self._ExtensionValue, args=[ext_id, 'allowed_in_incognito'], | |
299 expect_retval=True) | |
300 self.assertTrue(self._ExtensionValue(ext_id, 'allowed_in_incognito'), | |
301 msg='Extension did not get allowed in incognito.') | |
302 | |
303 # Disallow in incognito. | |
304 self.WaitUntil(ext_page.CheckExtensionVisible, args=[ext_id]) | |
305 ext_page.SetAllowInIncognito(ext_id, False) | |
306 | |
307 # Check extension now disallowed in incognito. | |
308 self.WaitUntil(self._ExtensionValue, args=[ext_id, 'allowed_in_incognito'], | |
309 expect_retval=False) | |
310 self.assertFalse(self._ExtensionValue(ext_id, 'allowed_in_incognito'), | |
311 msg='Extension did not get disallowed in incognito.') | |
312 | |
313 def testAllowAccessFileURLs(self): | |
314 """Tests disallowing and allowing and extension access to file URLs.""" | |
315 ext_id = self._InstallExtensionCheckDefaults(os.path.join('permissions', | |
316 'files')) | |
317 | |
318 # Check extension allowed access to file URLs by default. | |
319 extension_settings = self.GetPrefsInfo().Prefs()['extensions']['settings'] | |
320 self.assertTrue(extension_settings[ext_id]['newAllowFileAccess'], | |
321 msg='Extension was not allowed access to file URLs on ' | |
322 'installation') | |
323 | |
324 # Disallow access to file URLs. | |
325 driver = self.NewWebDriver() | |
326 ext_page = ExtensionsPage(driver) | |
327 self.WaitUntil(ext_page.CheckExtensionVisible, args=[ext_id]) | |
328 ext_page.SetAllowAccessFileURLs(ext_id, False) | |
329 | |
330 # Check that extension does not have access to file URLs. | |
331 self.WaitUntil(self._FileAccess, args=[ext_id], expect_retval=False) | |
332 self.assertFalse(self._FileAccess(ext_id), | |
333 msg='Extension did not have access to file URLs denied.') | |
334 | |
335 # Allow access to file URLs. | |
336 self.WaitUntil(ext_page.CheckExtensionVisible, args=[ext_id]) | |
337 ext_page.SetAllowAccessFileURLs(ext_id, True) | |
338 | |
339 # Check that extension now has access to file URLs. | |
340 self.WaitUntil(self._FileAccess, args=[ext_id], expect_retval=True) | |
341 self.assertTrue(self._FileAccess(ext_id), | |
342 msg='Extension did not have access to file URLs granted.') | |
343 | |
344 | |
345 if __name__ == '__main__': | |
346 pyauto_functional.Main() | |
OLD | NEW |