| #!/usr/bin/env python |
| # Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| """ |
| This module is a simple qa tool that installs extensions and tests whether the |
| browser crashes while visiting a list of urls. |
| |
| Usage: python extensions.py -v |
| |
| Note: This assumes that there is a directory of extensions called |
| 'extensions-tool' and that there is a file of newline-separated urls to visit |
| called 'urls.txt' in the data directory. |
| """ |
| |
| import glob |
| import logging |
| import os |
| import sys |
| |
| import pyauto_functional # must be imported before pyauto |
| import pyauto |
| |
| |
| class ExtensionsPage(object): |
| """Access options in extensions page (chrome://extensions-frame).""" |
| |
| _URL = 'chrome://extensions-frame' |
| |
| def __init__(self, driver): |
| self._driver = driver |
| self._driver.get(ExtensionsPage._URL) |
| |
| def CheckExtensionVisible(self, ext_id): |
| """Returns True if |ext_id| exists on page.""" |
| return len(self._driver.find_elements_by_id(ext_id)) == 1 |
| |
| def SetEnabled(self, ext_id, enabled): |
| """Clicks on 'Enabled' checkbox for specified extension. |
| |
| Args: |
| ext_id: Extension ID to be enabled or disabled. |
| enabled: Boolean indicating whether |ext_id| is to be enabled or disabled. |
| """ |
| checkbox = self._driver.find_element_by_xpath( |
| '//*[@id="%s"]//*[@class="enable-controls"]//*[@type="checkbox"]' % |
| ext_id) |
| if checkbox != enabled: |
| checkbox.click() |
| # Reload page to ensure that the UI is recreated. |
| self._driver.get(ExtensionsPage._URL) |
| |
| def SetAllowInIncognito(self, ext_id, allowed): |
| """Clicks on 'Allow in incognito' checkbox for specified extension. |
| |
| Args: |
| ext_id: Extension ID to be enabled or disabled. |
| allowed: Boolean indicating whether |ext_id| is to be allowed or |
| disallowed in incognito. |
| """ |
| checkbox = self._driver.find_element_by_xpath( |
| '//*[@id="%s"]//*[@class="incognito-control"]//*[@type="checkbox"]' % |
| ext_id) |
| if checkbox.is_selected() != allowed: |
| checkbox.click() |
| # Reload page to ensure that the UI is recreated. |
| self._driver.get(ExtensionsPage._URL) |
| |
| def SetAllowAccessFileURLs(self, ext_id, allowed): |
| """Clicks on 'Allow access to file URLs' checkbox for specified extension. |
| |
| Args: |
| ext_id: Extension ID to be enabled or disabled. |
| allowed: Boolean indicating whether |ext_id| is to be allowed access to |
| file URLs. |
| """ |
| checkbox = self._driver.find_element_by_xpath( |
| '//*[@id="%s"]//*[@class="file-access-control"]//*[@type="checkbox"]' % |
| ext_id) |
| if checkbox.is_selected() != allowed: |
| checkbox.click() |
| |
| |
| class ExtensionsTest(pyauto.PyUITest): |
| """Test of extensions.""" |
| |
| def Debug(self): |
| """Test method for experimentation. |
| |
| This method is not run automatically. |
| """ |
| while True: |
| raw_input('Interact with the browser and hit <enter> to dump history.') |
| print '*' * 20 |
| self.pprint(self.GetExtensionsInfo()) |
| |
| def _GetInstalledExtensionIds(self): |
| return [extension['id'] for extension in self.GetExtensionsInfo()] |
| |
| def _ReturnCrashingExtensions(self, extensions, group_size, top_urls): |
| """Returns the group of extensions that crashes (if any). |
| |
| Install the given extensions in groups of group_size and return the |
| group of extensions that crashes (if any). |
| |
| Args: |
| extensions: A list of extensions to install. |
| group_size: The number of extensions to install at one time. |
| top_urls: The list of top urls to visit. |
| |
| Returns: |
| The extensions in the crashing group or None if there is no crash. |
| """ |
| curr_extension = 0 |
| num_extensions = len(extensions) |
| self.RestartBrowser() |
| orig_extension_ids = self._GetInstalledExtensionIds() |
| |
| while curr_extension < num_extensions: |
| logging.debug('New group of %d extensions.', group_size) |
| group_end = curr_extension + group_size |
| for extension in extensions[curr_extension:group_end]: |
| logging.debug('Installing extension: %s', extension) |
| self.InstallExtension(extension) |
| |
| for url in top_urls: |
| self.NavigateToURL(url) |
| |
| def _LogAndReturnCrashing(): |
| crashing_extensions = extensions[curr_extension:group_end] |
| logging.debug('Crashing extensions: %s', crashing_extensions) |
| return crashing_extensions |
| |
| # If the browser has crashed, return the extensions in the failing group. |
| try: |
| num_browser_windows = self.GetBrowserWindowCount() |
| except: |
| return _LogAndReturnCrashing() |
| else: |
| if not num_browser_windows: |
| return _LogAndReturnCrashing() |
| else: |
| # Uninstall all extensions that aren't installed by default. |
| new_extension_ids = [id for id in self._GetInstalledExtensionIds() |
| if id not in orig_extension_ids] |
| for extension_id in new_extension_ids: |
| self.UninstallExtensionById(extension_id) |
| |
| curr_extension = group_end |
| |
| # None of the extensions crashed. |
| return None |
| |
| def _GetExtensionInfoById(self, extensions, id): |
| for x in extensions: |
| if x['id'] == id: |
| return x |
| return None |
| |
| def ExtensionCrashes(self): |
| """Add top extensions; confirm browser stays up when visiting top urls.""" |
| # TODO: provide a way in pyauto to pass args to a test - take these as args |
| extensions_dir = os.path.join(self.DataDir(), 'extensions-tool') |
| urls_file = os.path.join(self.DataDir(), 'urls.txt') |
| |
| error_msg = 'The dir "%s" must exist' % os.path.abspath(extensions_dir) |
| assert os.path.exists(extensions_dir), error_msg |
| error_msg = 'The file "%s" must exist' % os.path.abspath(urls_file) |
| assert os.path.exists(urls_file), error_msg |
| |
| num_urls_to_visit = 100 |
| extensions_group_size = 20 |
| |
| top_urls = [l.rstrip() for l in |
| open(urls_file).readlines()[:num_urls_to_visit]] |
| |
| failed_extensions = glob.glob(os.path.join(extensions_dir, '*.crx')) |
| group_size = extensions_group_size |
| |
| while (group_size and failed_extensions): |
| failed_extensions = self._ReturnCrashingExtensions( |
| failed_extensions, group_size, top_urls) |
| group_size = group_size // 2 |
| |
| self.assertFalse(failed_extensions, |
| 'Extension(s) in failing group: %s' % failed_extensions) |
| |
| def _InstallExtensionCheckDefaults(self, crx_file): |
| """Installs extension at extensions/|crx_file| and checks default status. |
| |
| Checks that the installed extension is enabled and not allowed in incognito. |
| |
| Args: |
| crx_file: Relative path from self.DataDir()/extensions to .crx extension |
| to be installed. |
| |
| Returns: |
| The extension ID. |
| """ |
| crx_file_path = os.path.abspath( |
| os.path.join(self.DataDir(), 'extensions', crx_file)) |
| ext_id = self.InstallExtension(crx_file_path) |
| extension = self._GetExtensionInfoById(self.GetExtensionsInfo(), ext_id) |
| self.assertTrue(extension['is_enabled'], |
| msg='Extension was not enabled on installation') |
| self.assertFalse(extension['allowed_in_incognito'], |
| msg='Extension was allowed in incognito on installation.') |
| |
| return ext_id |
| |
| def _ExtensionValue(self, ext_id, key): |
| """Returns the value of |key| for |ext_id|. |
| |
| Args: |
| ext_id: The extension ID. |
| key: The key for which the extensions info value is required. |
| |
| Returns: |
| The value of extensions info |key| for |ext_id|. |
| """ |
| return self._GetExtensionInfoById(self.GetExtensionsInfo(), ext_id)[key] |
| |
| def _FileAccess(self, ext_id): |
| """Returns the value of newAllowFileAccess for |ext_id|. |
| |
| Args: |
| ext_id: The extension ID. |
| |
| Returns: |
| The value of extensions settings newAllowFileAccess for |ext_id|. |
| """ |
| extension_settings = self.GetPrefsInfo().Prefs()['extensions']['settings'] |
| return extension_settings[ext_id]['newAllowFileAccess'] |
| |
| def testGetExtensionPermissions(self): |
| """Ensures we can retrieve the host/api permissions for an extension. |
| |
| This test assumes that the 'Bookmark Manager' extension exists in a fresh |
| profile. |
| """ |
| extensions_info = self.GetExtensionsInfo() |
| bm_exts = [x for x in extensions_info if x['name'] == 'Bookmark Manager'] |
| self.assertTrue(bm_exts, |
| msg='Could not find info for the Bookmark Manager ' |
| 'extension.') |
| ext = bm_exts[0] |
| |
| permissions_host = ext['host_permissions'] |
| self.assertTrue(len(permissions_host) == 2 and |
| 'chrome://favicon/*' in permissions_host and |
| 'chrome://resources/*' in permissions_host, |
| msg='Unexpected host permissions information.') |
| |
| permissions_api = ext['api_permissions'] |
| print permissions_api |
| self.assertTrue(len(permissions_api) == 5 and |
| 'bookmarks' in permissions_api and |
| 'bookmarkManagerPrivate' in permissions_api and |
| 'metricsPrivate' in permissions_api and |
| 'systemPrivate' in permissions_api and |
| 'tabs' in permissions_api, |
| msg='Unexpected API permissions information.') |
| |
| def testDisableEnableExtension(self): |
| """Tests that an extension can be disabled and enabled with the UI.""" |
| ext_id = self._InstallExtensionCheckDefaults('good.crx') |
| |
| # Disable extension. |
| driver = self.NewWebDriver() |
| ext_page = ExtensionsPage(driver) |
| self.WaitUntil(ext_page.CheckExtensionVisible, args=[ext_id]) |
| ext_page.SetEnabled(ext_id, False) |
| self.WaitUntil(self._ExtensionValue, args=[ext_id, 'is_enabled'], |
| expect_retval=False) |
| self.assertFalse(self._ExtensionValue(ext_id, 'is_enabled'), |
| msg='Extension did not get disabled.') |
| |
| # Enable extension. |
| self.WaitUntil(ext_page.CheckExtensionVisible, args=[ext_id]) |
| ext_page.SetEnabled(ext_id, True) |
| self.WaitUntil(self._ExtensionValue, args=[ext_id, 'is_enabled'], |
| expect_retval=True) |
| self.assertTrue(self._ExtensionValue(ext_id, 'is_enabled'), |
| msg='Extension did not get enabled.') |
| |
| def testAllowIncognitoExtension(self): |
| """Tests allowing and disallowing an extension in incognito mode.""" |
| ext_id = self._InstallExtensionCheckDefaults('good.crx') |
| |
| # Allow in incognito. |
| driver = self.NewWebDriver() |
| ext_page = ExtensionsPage(driver) |
| self.WaitUntil(ext_page.CheckExtensionVisible, args=[ext_id]) |
| ext_page.SetAllowInIncognito(ext_id, True) |
| |
| # Check extension now allowed in incognito. |
| self.WaitUntil(self._ExtensionValue, args=[ext_id, 'allowed_in_incognito'], |
| expect_retval=True) |
| self.assertTrue(self._ExtensionValue(ext_id, 'allowed_in_incognito'), |
| msg='Extension did not get allowed in incognito.') |
| |
| # Disallow in incognito. |
| self.WaitUntil(ext_page.CheckExtensionVisible, args=[ext_id]) |
| ext_page.SetAllowInIncognito(ext_id, False) |
| |
| # Check extension now disallowed in incognito. |
| self.WaitUntil(self._ExtensionValue, args=[ext_id, 'allowed_in_incognito'], |
| expect_retval=False) |
| self.assertFalse(self._ExtensionValue(ext_id, 'allowed_in_incognito'), |
| msg='Extension did not get disallowed in incognito.') |
| |
| def testAllowAccessFileURLs(self): |
| """Tests disallowing and allowing and extension access to file URLs.""" |
| ext_id = self._InstallExtensionCheckDefaults(os.path.join('permissions', |
| 'files')) |
| |
| # Check extension allowed access to file URLs by default. |
| extension_settings = self.GetPrefsInfo().Prefs()['extensions']['settings'] |
| self.assertTrue(extension_settings[ext_id]['newAllowFileAccess'], |
| msg='Extension was not allowed access to file URLs on ' |
| 'installation') |
| |
| # Disallow access to file URLs. |
| driver = self.NewWebDriver() |
| ext_page = ExtensionsPage(driver) |
| self.WaitUntil(ext_page.CheckExtensionVisible, args=[ext_id]) |
| ext_page.SetAllowAccessFileURLs(ext_id, False) |
| |
| # Check that extension does not have access to file URLs. |
| self.WaitUntil(self._FileAccess, args=[ext_id], expect_retval=False) |
| self.assertFalse(self._FileAccess(ext_id), |
| msg='Extension did not have access to file URLs denied.') |
| |
| # Allow access to file URLs. |
| self.WaitUntil(ext_page.CheckExtensionVisible, args=[ext_id]) |
| ext_page.SetAllowAccessFileURLs(ext_id, True) |
| |
| # Check that extension now has access to file URLs. |
| self.WaitUntil(self._FileAccess, args=[ext_id], expect_retval=True) |
| self.assertTrue(self._FileAccess(ext_id), |
| msg='Extension did not have access to file URLs granted.') |
| |
| |
| if __name__ == '__main__': |
| pyauto_functional.Main() |