chromedriver.py revision 98a0f7d6e5599a2b76310443a0b069189dfebda4
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 9c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi 1096c77cba384edd7befc85530ab6f809cef429625Dan Shitry: 1196c77cba384edd7befc85530ab6f809cef429625Dan Shi from selenium import webdriver 1296c77cba384edd7befc85530ab6f809cef429625Dan Shiexcept ImportError: 1396c77cba384edd7befc85530ab6f809cef429625Dan Shi # Ignore import error, as this can happen when builder tries to call the 1496c77cba384edd7befc85530ab6f809cef429625Dan Shi # setup method of test that imports chromedriver. 1596c77cba384edd7befc85530ab6f809cef429625Dan Shi logging.error('selenium module failed to be imported.') 1696c77cba384edd7befc85530ab6f809cef429625Dan Shi pass 17c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi 18c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shifrom autotest_lib.client.bin import utils 19c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shifrom autotest_lib.client.common_lib.cros import chrome 20c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi 21c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan ShiCHROMEDRIVER_EXE_PATH = '/usr/local/chromedriver/chromedriver' 2247a5129545db2ad42740968b8c20e93cb04bf5aebeepsX_SERVER_DISPLAY = ':0' 2347a5129545db2ad42740968b8c20e93cb04bf5aebeepsX_AUTHORITY = '/home/chronos/.Xauthority' 2447a5129545db2ad42740968b8c20e93cb04bf5aebeeps 25c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi 26c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shiclass chromedriver(object): 27c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi """Wrapper class, a context manager type, for tests to use Chrome Driver.""" 28c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi 29c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi def __init__(self, extra_chrome_flags=[], subtract_extra_chrome_flags=[], 30ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard extension_paths=[], is_component=True, username=None, 3198a0f7d6e5599a2b76310443a0b069189dfebda4Resetswitch password=None, server_port=None, *args, **kwargs): 32c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi """Initialize. 33c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi 34c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi @param extra_chrome_flags: Extra chrome flags to pass to chrome, if any. 35c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi @param subtract_extra_chrome_flags: Remove default flags passed to 36c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi chrome by chromedriver, if any. 3747a5129545db2ad42740968b8c20e93cb04bf5aebeeps @param extension_paths: A list of paths to unzipped extensions. Note 3847a5129545db2ad42740968b8c20e93cb04bf5aebeeps that paths to crx files won't work. 3947a5129545db2ad42740968b8c20e93cb04bf5aebeeps @param is_component: True if the manifest.json has a key. 40ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard @param username: Log in using this username instead of the default. 4198a0f7d6e5599a2b76310443a0b069189dfebda4Resetswitch @param password: Log in using this password instead of the default. 4298a0f7d6e5599a2b76310443a0b069189dfebda4Resetswitch @param server_port: Port number for the chromedriver server. If None, 4398a0f7d6e5599a2b76310443a0b069189dfebda4Resetswitch an available port is chosen at random. 44c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi """ 45c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi assert os.geteuid() == 0, 'Need superuser privileges' 46c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi 47c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi # Log in with telemetry 48bff9f9d0c4bf77f09a41262061d1a9310d94db2fbeeps self._chrome = chrome.Chrome(extension_paths=extension_paths, 49bff9f9d0c4bf77f09a41262061d1a9310d94db2fbeeps is_component=is_component, 50ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard username=username, 51ab583045d1e3e21da15eeb5152c2f808f4aea8ffNathan Stoddard password=password, 52bff9f9d0c4bf77f09a41262061d1a9310d94db2fbeeps extra_browser_args=extra_chrome_flags) 53bff9f9d0c4bf77f09a41262061d1a9310d94db2fbeeps self._browser = self._chrome.browser 542bff39b1c4212ab37787e529bd934614606dd948Dan Shi # Close all tabs owned and opened by Telemetry, as these cannot be 552bff39b1c4212ab37787e529bd934614606dd948Dan Shi # transferred to ChromeDriver. 562bff39b1c4212ab37787e529bd934614606dd948Dan Shi self._browser.tabs[0].Close() 57c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi 58c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi # Start ChromeDriver server 5998a0f7d6e5599a2b76310443a0b069189dfebda4Resetswitch self._server = chromedriver_server(CHROMEDRIVER_EXE_PATH, 6098a0f7d6e5599a2b76310443a0b069189dfebda4Resetswitch port=server_port) 61c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi 622bff39b1c4212ab37787e529bd934614606dd948Dan Shi # Open a new tab using Chrome remote debugging. ChromeDriver expects 632bff39b1c4212ab37787e529bd934614606dd948Dan Shi # a tab opened for remote to work. Tabs opened using Telemetry will be 642bff39b1c4212ab37787e529bd934614606dd948Dan Shi # owned by Telemetry, and will be inaccessible to ChromeDriver. 652bff39b1c4212ab37787e529bd934614606dd948Dan Shi urllib2.urlopen('http://localhost:%i/json/new' % 662bff39b1c4212ab37787e529bd934614606dd948Dan Shi utils.get_chrome_remote_debugging_port()) 672bff39b1c4212ab37787e529bd934614606dd948Dan Shi 68c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi chromeOptions = {'debuggerAddress': 69c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi ('localhost:%d' % 70c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi utils.get_chrome_remote_debugging_port())} 71c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi capabilities = {'chromeOptions':chromeOptions} 72c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi # Handle to chromedriver, for chrome automation. 7396c77cba384edd7befc85530ab6f809cef429625Dan Shi try: 7496c77cba384edd7befc85530ab6f809cef429625Dan Shi self.driver = webdriver.Remote(command_executor=self._server.url, 7596c77cba384edd7befc85530ab6f809cef429625Dan Shi desired_capabilities=capabilities) 7696c77cba384edd7befc85530ab6f809cef429625Dan Shi except NameError: 7796c77cba384edd7befc85530ab6f809cef429625Dan Shi logging.error('selenium module failed to be imported.') 7896c77cba384edd7befc85530ab6f809cef429625Dan Shi raise 79c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi 80c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi 81c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi def __enter__(self): 82c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi return self 83c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi 84c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi 85c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi def __exit__(self, *args): 86c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi """Clean up after running the test. 87c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi 88c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi """ 89c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi if hasattr(self, 'driver') and self.driver: 90c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi self.driver.close() 91c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi del self.driver 92c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi 93c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi if hasattr(self, '_server') and self._server: 94c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi self._server.close() 95c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi del self._server 96c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi 97c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi if hasattr(self, '_browser') and self._browser: 98c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi self._browser.Close() 99c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi del self._browser 100c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi 101c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi 102bff9f9d0c4bf77f09a41262061d1a9310d94db2fbeeps def get_extension(self, extension_path): 103bff9f9d0c4bf77f09a41262061d1a9310d94db2fbeeps """Gets an extension by proxying to the browser. 104bff9f9d0c4bf77f09a41262061d1a9310d94db2fbeeps 105bff9f9d0c4bf77f09a41262061d1a9310d94db2fbeeps @param extension_path: Path to the extension loaded in the browser. 106bff9f9d0c4bf77f09a41262061d1a9310d94db2fbeeps 107bff9f9d0c4bf77f09a41262061d1a9310d94db2fbeeps @return: A telemetry extension object representing the extension. 108bff9f9d0c4bf77f09a41262061d1a9310d94db2fbeeps """ 109bff9f9d0c4bf77f09a41262061d1a9310d94db2fbeeps return self._chrome.get_extension(extension_path) 110bff9f9d0c4bf77f09a41262061d1a9310d94db2fbeeps 111bff9f9d0c4bf77f09a41262061d1a9310d94db2fbeeps 112c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shiclass chromedriver_server(object): 113c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi """A running ChromeDriver server. 114c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi 115c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi This code is migrated from chrome: 116c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi src/chrome/test/chromedriver/server/server.py 117c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi """ 118c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi 11998a0f7d6e5599a2b76310443a0b069189dfebda4Resetswitch def __init__(self, exe_path, port=None): 120c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi """Starts the ChromeDriver server and waits for it to be ready. 121c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi 122c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi Args: 123c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi exe_path: path to the ChromeDriver executable 12498a0f7d6e5599a2b76310443a0b069189dfebda4Resetswitch port: server port. If None, an available port is chosen at random. 125c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi Raises: 126c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi RuntimeError if ChromeDriver fails to start 127c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi """ 128c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi if not os.path.exists(exe_path): 129c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi raise RuntimeError('ChromeDriver exe not found at: ' + exe_path) 130c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi 13198a0f7d6e5599a2b76310443a0b069189dfebda4Resetswitch if not port: 13298a0f7d6e5599a2b76310443a0b069189dfebda4Resetswitch port = utils.get_unused_port() 133c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi chromedriver_args = [exe_path, '--port=%d' % port] 13447a5129545db2ad42740968b8c20e93cb04bf5aebeeps 13547a5129545db2ad42740968b8c20e93cb04bf5aebeeps # Chromedriver will look for an X server running on the display 13647a5129545db2ad42740968b8c20e93cb04bf5aebeeps # specified through the DISPLAY environment variable. 137a5ab166f36554278718e4b0c1412c49276ce1e25Ilja H. Friedel utils.assert_has_X_server() 13847a5129545db2ad42740968b8c20e93cb04bf5aebeeps os.environ['DISPLAY'] = X_SERVER_DISPLAY 13947a5129545db2ad42740968b8c20e93cb04bf5aebeeps os.environ['XAUTHORITY'] = X_AUTHORITY 14047a5129545db2ad42740968b8c20e93cb04bf5aebeeps 141c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi self.bg_job = utils.BgJob(chromedriver_args, stderr_level=logging.DEBUG) 142c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi self.url = 'http://localhost:%d' % port 143c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi if self.bg_job is None: 144c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi raise RuntimeError('ChromeDriver server cannot be started') 145c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi 146c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi try: 147c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi timeout_msg = 'Timeout on waiting for ChromeDriver to start.' 148c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi utils.poll_for_condition(self.is_running, 149c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi exception=utils.TimeoutError(timeout_msg), 150c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi timeout=10, 151c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi sleep_interval=.1) 152c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi except utils.TimeoutError: 153c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi self.close_bgjob() 154c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi raise RuntimeError('ChromeDriver server did not start') 155c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi 156c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi logging.debug('Chrome Driver server is up and listening at port %d.', 157c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi port) 158c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi atexit.register(self.close) 159c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi 160c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi 161c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi def is_running(self): 162c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi """Returns whether the server is up and running.""" 163c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi try: 164c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi urllib2.urlopen(self.url + '/status') 165c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi return True 166c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi except urllib2.URLError as e: 167c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi return False 168c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi 169c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi 170c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi def close_bgjob(self): 171c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi """Close background job and log stdout and stderr.""" 172c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi utils.nuke_subprocess(self.bg_job.sp) 173c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi utils.join_bg_jobs([self.bg_job], timeout=1) 174c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi result = self.bg_job.result 175c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi if result.stdout or result.stderr: 176c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi logging.info('stdout of Chrome Driver:\n%s', result.stdout) 177c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi logging.error('stderr of Chrome Driver:\n%s', result.stderr) 178c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi 179c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi 180c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi def close(self): 181c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi """Kills the ChromeDriver server, if it is running.""" 182c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi if self.bg_job is None: 183c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi return 184c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi 185c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi try: 186c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi urllib2.urlopen(self.url + '/shutdown', timeout=10).close() 187c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi except: 188c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi pass 189c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi 190c1d263b436976b7516f77e84386ec0fbe2b3cfbeDan Shi self.close_bgjob() 191