1c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi# Copyright (c) 2013 The Chromium OS Authors. All rights reserved. 2c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi# Use of this source code is governed by a BSD-style license that can be 3c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi# found in the LICENSE file. 4c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi 5c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shiimport atexit 6c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shiimport logging 7c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shiimport os 8c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shiimport urllib2 90b2becccaa6a25b59fa686f6ab72817e9bb4fac3Payton Turnageimport urlparse 10c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi 1196c77cba384edd7befc85530ab6f809cef429625Dan Shitry: 1296c77cba384edd7befc85530ab6f809cef429625Dan Shi from selenium import webdriver 1396c77cba384edd7befc85530ab6f809cef429625Dan Shiexcept ImportError: 1496c77cba384edd7befc85530ab6f809cef429625Dan Shi # Ignore import error, as this can happen when builder tries to call the 1596c77cba384edd7befc85530ab6f809cef429625Dan Shi # setup method of test that imports chromedriver. 1696c77cba384edd7befc85530ab6f809cef429625Dan Shi logging.error('selenium module failed to be imported.') 1796c77cba384edd7befc85530ab6f809cef429625Dan Shi pass 18c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi 19c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shifrom autotest_lib.client.bin import utils 20c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shifrom autotest_lib.client.common_lib.cros import chrome 21c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi 22c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan ShiCHROMEDRIVER_EXE_PATH = '/usr/local/chromedriver/chromedriver' 2347a5129545db2ad42740968b8c20e93cb04bf5aebeepsX_SERVER_DISPLAY = ':0' 2447a5129545db2ad42740968b8c20e93cb04bf5aebeepsX_AUTHORITY = '/home/chronos/.Xauthority' 2547a5129545db2ad42740968b8c20e93cb04bf5aebeeps 26c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi 27c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shiclass chromedriver(object): 28c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi """Wrapper class, a context manager type, for tests to use Chrome Driver.""" 29c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi 30c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi def __init__(self, extra_chrome_flags=[], subtract_extra_chrome_flags=[], 318949a8db9cd4d16f897d88241dfc0d658cf93d44Achuith Bhandarkar extension_paths=[], username=None, password=None, 328949a8db9cd4d16f897d88241dfc0d658cf93d44Achuith Bhandarkar server_port=None, skip_cleanup=False, url_base=None, 33e7756f78cfeffb677a68a5441d4b4e3854e5f7f5Bogineni Kasaiah extra_chromedriver_args=None, gaia_login=False, 3440520921e6ee98ba5c4ab76facb55d2c05d88967Jon Mann disable_default_apps=True, dont_override_profile=False, *args, 3540520921e6ee98ba5c4ab76facb55d2c05d88967Jon Mann **kwargs): 36c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi """Initialize. 37c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi 38c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi @param extra_chrome_flags: Extra chrome flags to pass to chrome, if any. 39c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi @param subtract_extra_chrome_flags: Remove default flags passed to 40c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi chrome by chromedriver, if any. 4147a5129545db2ad42740968b8c20e93cb04bf5aebeeps @param extension_paths: A list of paths to unzipped extensions. Note 4247a5129545db2ad42740968b8c20e93cb04bf5aebeeps that paths to crx files won't work. 43ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard @param username: Log in using this username instead of the default. 4498a0f7d6e5599a2b76310443a0b069189dfebda4Resetswitch @param password: Log in using this password instead of the default. 4598a0f7d6e5599a2b76310443a0b069189dfebda4Resetswitch @param server_port: Port number for the chromedriver server. If None, 4698a0f7d6e5599a2b76310443a0b069189dfebda4Resetswitch an available port is chosen at random. 47d1df3d5d21a9abff7028677ec2626c572f7a627eResetswitch @param skip_cleanup: If True, leave the server and browser running 48d1df3d5d21a9abff7028677ec2626c572f7a627eResetswitch so that remote tests can run after this script 49d1df3d5d21a9abff7028677ec2626c572f7a627eResetswitch ends. Default is False. 500b2becccaa6a25b59fa686f6ab72817e9bb4fac3Payton Turnage @param url_base: Optional base url for chromedriver. 510b2becccaa6a25b59fa686f6ab72817e9bb4fac3Payton Turnage @param extra_chromedriver_args: List of extra arguments to forward to 520b2becccaa6a25b59fa686f6ab72817e9bb4fac3Payton Turnage the chromedriver binary, if any. 539fbc9647e8c45833dac629889e5c72876f66e83cJon Mann @param gaia_login: Logs in to real gaia. 54e7756f78cfeffb677a68a5441d4b4e3854e5f7f5Bogineni Kasaiah @param disable_default_apps: For tests that exercise default apps. 5540520921e6ee98ba5c4ab76facb55d2c05d88967Jon Mann @param dont_override_profile: Don't delete cryptohome before login. 5640520921e6ee98ba5c4ab76facb55d2c05d88967Jon Mann Telemetry will output a warning with this 5740520921e6ee98ba5c4ab76facb55d2c05d88967Jon Mann option. 58c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi """ 59d1df3d5d21a9abff7028677ec2626c572f7a627eResetswitch self._cleanup = not skip_cleanup 60c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi assert os.geteuid() == 0, 'Need superuser privileges' 61c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi 62c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi # Log in with telemetry 63bff9f9d0c4bf77f09a41262061d1a9310d94db2fbeeps self._chrome = chrome.Chrome(extension_paths=extension_paths, 64ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard username=username, 65ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard password=password, 669fbc9647e8c45833dac629889e5c72876f66e83cJon Mann extra_browser_args=extra_chrome_flags, 67e7756f78cfeffb677a68a5441d4b4e3854e5f7f5Bogineni Kasaiah gaia_login=gaia_login, 6840520921e6ee98ba5c4ab76facb55d2c05d88967Jon Mann disable_default_apps=disable_default_apps, 6940520921e6ee98ba5c4ab76facb55d2c05d88967Jon Mann dont_override_profile=dont_override_profile 7040520921e6ee98ba5c4ab76facb55d2c05d88967Jon Mann ) 71bff9f9d0c4bf77f09a41262061d1a9310d94db2fbeeps self._browser = self._chrome.browser 722bff39b1c4212ab37787e529bd934614606dd948Dan Shi # Close all tabs owned and opened by Telemetry, as these cannot be 732bff39b1c4212ab37787e529bd934614606dd948Dan Shi # transferred to ChromeDriver. 742bff39b1c4212ab37787e529bd934614606dd948Dan Shi self._browser.tabs[0].Close() 75c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi 76c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi # Start ChromeDriver server 7798a0f7d6e5599a2b76310443a0b069189dfebda4Resetswitch self._server = chromedriver_server(CHROMEDRIVER_EXE_PATH, 78d1df3d5d21a9abff7028677ec2626c572f7a627eResetswitch port=server_port, 790b2becccaa6a25b59fa686f6ab72817e9bb4fac3Payton Turnage skip_cleanup=skip_cleanup, 800b2becccaa6a25b59fa686f6ab72817e9bb4fac3Payton Turnage url_base=url_base, 810b2becccaa6a25b59fa686f6ab72817e9bb4fac3Payton Turnage extra_args=extra_chromedriver_args) 82c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi 832bff39b1c4212ab37787e529bd934614606dd948Dan Shi # Open a new tab using Chrome remote debugging. ChromeDriver expects 842bff39b1c4212ab37787e529bd934614606dd948Dan Shi # a tab opened for remote to work. Tabs opened using Telemetry will be 852bff39b1c4212ab37787e529bd934614606dd948Dan Shi # owned by Telemetry, and will be inaccessible to ChromeDriver. 862bff39b1c4212ab37787e529bd934614606dd948Dan Shi urllib2.urlopen('http://localhost:%i/json/new' % 872bff39b1c4212ab37787e529bd934614606dd948Dan Shi utils.get_chrome_remote_debugging_port()) 882bff39b1c4212ab37787e529bd934614606dd948Dan Shi 89c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi chromeOptions = {'debuggerAddress': 90c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi ('localhost:%d' % 91c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi utils.get_chrome_remote_debugging_port())} 92c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi capabilities = {'chromeOptions':chromeOptions} 93c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi # Handle to chromedriver, for chrome automation. 9496c77cba384edd7befc85530ab6f809cef429625Dan Shi try: 9596c77cba384edd7befc85530ab6f809cef429625Dan Shi self.driver = webdriver.Remote(command_executor=self._server.url, 9696c77cba384edd7befc85530ab6f809cef429625Dan Shi desired_capabilities=capabilities) 9796c77cba384edd7befc85530ab6f809cef429625Dan Shi except NameError: 9896c77cba384edd7befc85530ab6f809cef429625Dan Shi logging.error('selenium module failed to be imported.') 9996c77cba384edd7befc85530ab6f809cef429625Dan Shi raise 100c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi 101c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi 102c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi def __enter__(self): 103c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi return self 104c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi 105c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi 106c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi def __exit__(self, *args): 107c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi """Clean up after running the test. 108c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi 109c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi """ 110c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi if hasattr(self, 'driver') and self.driver: 111c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi self.driver.close() 112c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi del self.driver 113c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi 114d1df3d5d21a9abff7028677ec2626c572f7a627eResetswitch if not hasattr(self, '_cleanup') or self._cleanup: 115d1df3d5d21a9abff7028677ec2626c572f7a627eResetswitch if hasattr(self, '_server') and self._server: 116d1df3d5d21a9abff7028677ec2626c572f7a627eResetswitch self._server.close() 117d1df3d5d21a9abff7028677ec2626c572f7a627eResetswitch del self._server 118c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi 119d1df3d5d21a9abff7028677ec2626c572f7a627eResetswitch if hasattr(self, '_browser') and self._browser: 120d1df3d5d21a9abff7028677ec2626c572f7a627eResetswitch self._browser.Close() 121d1df3d5d21a9abff7028677ec2626c572f7a627eResetswitch del self._browser 122c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi 123bff9f9d0c4bf77f09a41262061d1a9310d94db2fbeeps def get_extension(self, extension_path): 124bff9f9d0c4bf77f09a41262061d1a9310d94db2fbeeps """Gets an extension by proxying to the browser. 125bff9f9d0c4bf77f09a41262061d1a9310d94db2fbeeps 126bff9f9d0c4bf77f09a41262061d1a9310d94db2fbeeps @param extension_path: Path to the extension loaded in the browser. 127bff9f9d0c4bf77f09a41262061d1a9310d94db2fbeeps 128bff9f9d0c4bf77f09a41262061d1a9310d94db2fbeeps @return: A telemetry extension object representing the extension. 129bff9f9d0c4bf77f09a41262061d1a9310d94db2fbeeps """ 130bff9f9d0c4bf77f09a41262061d1a9310d94db2fbeeps return self._chrome.get_extension(extension_path) 131bff9f9d0c4bf77f09a41262061d1a9310d94db2fbeeps 132bff9f9d0c4bf77f09a41262061d1a9310d94db2fbeeps 133a074a5192ab9722d6c3b5de47ba814ed3f85d931Mussa @property 134a074a5192ab9722d6c3b5de47ba814ed3f85d931Mussa def chrome_instance(self): 135a074a5192ab9722d6c3b5de47ba814ed3f85d931Mussa """ The chrome instance used by this chrome driver instance. """ 136a074a5192ab9722d6c3b5de47ba814ed3f85d931Mussa return self._chrome 137a074a5192ab9722d6c3b5de47ba814ed3f85d931Mussa 138a074a5192ab9722d6c3b5de47ba814ed3f85d931Mussa 139c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shiclass chromedriver_server(object): 140c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi """A running ChromeDriver server. 141c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi 142c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi This code is migrated from chrome: 143c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi src/chrome/test/chromedriver/server/server.py 144c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi """ 145c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi 1460b2becccaa6a25b59fa686f6ab72817e9bb4fac3Payton Turnage def __init__(self, exe_path, port=None, skip_cleanup=False, 1470b2becccaa6a25b59fa686f6ab72817e9bb4fac3Payton Turnage url_base=None, extra_args=None): 148c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi """Starts the ChromeDriver server and waits for it to be ready. 149c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi 150c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi Args: 151c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi exe_path: path to the ChromeDriver executable 15298a0f7d6e5599a2b76310443a0b069189dfebda4Resetswitch port: server port. If None, an available port is chosen at random. 153d1df3d5d21a9abff7028677ec2626c572f7a627eResetswitch skip_cleanup: If True, leave the server running so that remote 154d1df3d5d21a9abff7028677ec2626c572f7a627eResetswitch tests can run after this script ends. Default is 155d1df3d5d21a9abff7028677ec2626c572f7a627eResetswitch False. 1560b2becccaa6a25b59fa686f6ab72817e9bb4fac3Payton Turnage url_base: Optional base url for chromedriver. 1570b2becccaa6a25b59fa686f6ab72817e9bb4fac3Payton Turnage extra_args: List of extra arguments to forward to the chromedriver 1580b2becccaa6a25b59fa686f6ab72817e9bb4fac3Payton Turnage binary, if any. 159c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi Raises: 160c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi RuntimeError if ChromeDriver fails to start 161c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi """ 162c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi if not os.path.exists(exe_path): 163c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi raise RuntimeError('ChromeDriver exe not found at: ' + exe_path) 164c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi 165d1df3d5d21a9abff7028677ec2626c572f7a627eResetswitch chromedriver_args = [exe_path] 166d1df3d5d21a9abff7028677ec2626c572f7a627eResetswitch if port: 167d1df3d5d21a9abff7028677ec2626c572f7a627eResetswitch # Allow remote connections if a port was specified 168d1df3d5d21a9abff7028677ec2626c572f7a627eResetswitch chromedriver_args.append('--whitelisted-ips') 169d1df3d5d21a9abff7028677ec2626c572f7a627eResetswitch else: 17098a0f7d6e5599a2b76310443a0b069189dfebda4Resetswitch port = utils.get_unused_port() 171d1df3d5d21a9abff7028677ec2626c572f7a627eResetswitch chromedriver_args.append('--port=%d' % port) 17247a5129545db2ad42740968b8c20e93cb04bf5aebeeps 1730b2becccaa6a25b59fa686f6ab72817e9bb4fac3Payton Turnage self.url = 'http://localhost:%d' % port 1740b2becccaa6a25b59fa686f6ab72817e9bb4fac3Payton Turnage if url_base: 1750b2becccaa6a25b59fa686f6ab72817e9bb4fac3Payton Turnage chromedriver_args.append('--url-base=%s' % url_base) 1760b2becccaa6a25b59fa686f6ab72817e9bb4fac3Payton Turnage self.url = urlparse.urljoin(self.url, url_base) 1770b2becccaa6a25b59fa686f6ab72817e9bb4fac3Payton Turnage 1780b2becccaa6a25b59fa686f6ab72817e9bb4fac3Payton Turnage if extra_args: 1790b2becccaa6a25b59fa686f6ab72817e9bb4fac3Payton Turnage chromedriver_args.extend(extra_args) 1800b2becccaa6a25b59fa686f6ab72817e9bb4fac3Payton Turnage 181a624576542ae9b524100eac4584d36f20aba4d7aIlja H. Friedel # TODO(ihf): Remove references to X after M45. 18247a5129545db2ad42740968b8c20e93cb04bf5aebeeps # Chromedriver will look for an X server running on the display 18347a5129545db2ad42740968b8c20e93cb04bf5aebeeps # specified through the DISPLAY environment variable. 18447a5129545db2ad42740968b8c20e93cb04bf5aebeeps os.environ['DISPLAY'] = X_SERVER_DISPLAY 18547a5129545db2ad42740968b8c20e93cb04bf5aebeeps os.environ['XAUTHORITY'] = X_AUTHORITY 18647a5129545db2ad42740968b8c20e93cb04bf5aebeeps 187c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi self.bg_job = utils.BgJob(chromedriver_args, stderr_level=logging.DEBUG) 188c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi if self.bg_job is None: 189c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi raise RuntimeError('ChromeDriver server cannot be started') 190c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi 191c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi try: 192c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi timeout_msg = 'Timeout on waiting for ChromeDriver to start.' 193c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi utils.poll_for_condition(self.is_running, 194c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi exception=utils.TimeoutError(timeout_msg), 195c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi timeout=10, 196c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi sleep_interval=.1) 197c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi except utils.TimeoutError: 198c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi self.close_bgjob() 199c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi raise RuntimeError('ChromeDriver server did not start') 200c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi 201c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi logging.debug('Chrome Driver server is up and listening at port %d.', 202c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi port) 203d1df3d5d21a9abff7028677ec2626c572f7a627eResetswitch if not skip_cleanup: 204d1df3d5d21a9abff7028677ec2626c572f7a627eResetswitch atexit.register(self.close) 205c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi 206c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi 207c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi def is_running(self): 208c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi """Returns whether the server is up and running.""" 209c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi try: 210c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi urllib2.urlopen(self.url + '/status') 211c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi return True 212c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi except urllib2.URLError as e: 213c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi return False 214c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi 215c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi 216c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi def close_bgjob(self): 217c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi """Close background job and log stdout and stderr.""" 218c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi utils.nuke_subprocess(self.bg_job.sp) 219c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi utils.join_bg_jobs([self.bg_job], timeout=1) 220c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi result = self.bg_job.result 221c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi if result.stdout or result.stderr: 222c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi logging.info('stdout of Chrome Driver:\n%s', result.stdout) 223c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi logging.error('stderr of Chrome Driver:\n%s', result.stderr) 224c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi 225c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi 226c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi def close(self): 227c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi """Kills the ChromeDriver server, if it is running.""" 228c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi if self.bg_job is None: 229c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi return 230c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi 231c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi try: 232c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi urllib2.urlopen(self.url + '/shutdown', timeout=10).close() 233c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi except: 234c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi pass 235c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi 236c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi self.close_bgjob() 237