| 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 |