blob: a118433e26cda312947d4fc1bd1d3578dd1df8c0 [file] [log] [blame]
Dan Shic1d263b2013-10-04 17:31:38 -07001# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5import atexit
6import logging
7import os
8import urllib2
9
Dan Shi96c77cb2013-11-19 10:30:37 -080010try:
11 from selenium import webdriver
12except ImportError:
13 # Ignore import error, as this can happen when builder tries to call the
14 # setup method of test that imports chromedriver.
15 logging.error('selenium module failed to be imported.')
16 pass
Dan Shic1d263b2013-10-04 17:31:38 -070017
18import common
19from autotest_lib.client.bin import utils
20from autotest_lib.client.common_lib.cros import chrome
21
22CHROMEDRIVER_EXE_PATH = '/usr/local/chromedriver/chromedriver'
beeps47a51292013-11-20 08:55:23 -080023X_SERVER_DISPLAY = ':0'
24X_AUTHORITY = '/home/chronos/.Xauthority'
25
Dan Shic1d263b2013-10-04 17:31:38 -070026
27class chromedriver(object):
28 """Wrapper class, a context manager type, for tests to use Chrome Driver."""
29
30 def __init__(self, extra_chrome_flags=[], subtract_extra_chrome_flags=[],
beeps47a51292013-11-20 08:55:23 -080031 extension_paths=[], is_component=True, *args, **kwargs):
Dan Shic1d263b2013-10-04 17:31:38 -070032 """Initialize.
33
34 @param extra_chrome_flags: Extra chrome flags to pass to chrome, if any.
35 @param subtract_extra_chrome_flags: Remove default flags passed to
36 chrome by chromedriver, if any.
beeps47a51292013-11-20 08:55:23 -080037 @param extension_paths: A list of paths to unzipped extensions. Note
38 that paths to crx files won't work.
39 @param is_component: True if the manifest.json has a key.
Dan Shic1d263b2013-10-04 17:31:38 -070040 """
41 assert os.geteuid() == 0, 'Need superuser privileges'
42
43 # Log in with telemetry
beeps47a51292013-11-20 08:55:23 -080044 self._browser = chrome.Chrome(extension_paths=extension_paths,
45 is_component=is_component).browser
Dan Shic1d263b2013-10-04 17:31:38 -070046
47 # Start ChromeDriver server
48 self._server = chromedriver_server(CHROMEDRIVER_EXE_PATH)
49
50 chromeOptions = {'debuggerAddress':
51 ('localhost:%d' %
52 utils.get_chrome_remote_debugging_port())}
53 capabilities = {'chromeOptions':chromeOptions}
54 # Handle to chromedriver, for chrome automation.
Dan Shi96c77cb2013-11-19 10:30:37 -080055 try:
56 self.driver = webdriver.Remote(command_executor=self._server.url,
57 desired_capabilities=capabilities)
58 except NameError:
59 logging.error('selenium module failed to be imported.')
60 raise
Dan Shic1d263b2013-10-04 17:31:38 -070061
62
63 def __enter__(self):
64 return self
65
66
67 def __exit__(self, *args):
68 """Clean up after running the test.
69
70 """
71 if hasattr(self, 'driver') and self.driver:
72 self.driver.close()
73 del self.driver
74
75 if hasattr(self, '_server') and self._server:
76 self._server.close()
77 del self._server
78
79 if hasattr(self, '_browser') and self._browser:
80 self._browser.Close()
81 del self._browser
82
83
84class chromedriver_server(object):
85 """A running ChromeDriver server.
86
87 This code is migrated from chrome:
88 src/chrome/test/chromedriver/server/server.py
89 """
90
91 def __init__(self, exe_path):
92 """Starts the ChromeDriver server and waits for it to be ready.
93
94 Args:
95 exe_path: path to the ChromeDriver executable
96 Raises:
97 RuntimeError if ChromeDriver fails to start
98 """
99 if not os.path.exists(exe_path):
100 raise RuntimeError('ChromeDriver exe not found at: ' + exe_path)
101
102 port = utils.get_unused_port()
103 chromedriver_args = [exe_path, '--port=%d' % port]
beeps47a51292013-11-20 08:55:23 -0800104
105 # Chromedriver will look for an X server running on the display
106 # specified through the DISPLAY environment variable.
107 os.environ['DISPLAY'] = X_SERVER_DISPLAY
108 os.environ['XAUTHORITY'] = X_AUTHORITY
109
Dan Shic1d263b2013-10-04 17:31:38 -0700110 self.bg_job = utils.BgJob(chromedriver_args, stderr_level=logging.DEBUG)
111 self.url = 'http://localhost:%d' % port
112 if self.bg_job is None:
113 raise RuntimeError('ChromeDriver server cannot be started')
114
115 try:
116 timeout_msg = 'Timeout on waiting for ChromeDriver to start.'
117 utils.poll_for_condition(self.is_running,
118 exception=utils.TimeoutError(timeout_msg),
119 timeout=10,
120 sleep_interval=.1)
121 except utils.TimeoutError:
122 self.close_bgjob()
123 raise RuntimeError('ChromeDriver server did not start')
124
125 logging.debug('Chrome Driver server is up and listening at port %d.',
126 port)
127 atexit.register(self.close)
128
129
130 def is_running(self):
131 """Returns whether the server is up and running."""
132 try:
133 urllib2.urlopen(self.url + '/status')
134 return True
135 except urllib2.URLError as e:
136 return False
137
138
139 def close_bgjob(self):
140 """Close background job and log stdout and stderr."""
141 utils.nuke_subprocess(self.bg_job.sp)
142 utils.join_bg_jobs([self.bg_job], timeout=1)
143 result = self.bg_job.result
144 if result.stdout or result.stderr:
145 logging.info('stdout of Chrome Driver:\n%s', result.stdout)
146 logging.error('stderr of Chrome Driver:\n%s', result.stderr)
147
148
149 def close(self):
150 """Kills the ChromeDriver server, if it is running."""
151 if self.bg_job is None:
152 return
153
154 try:
155 urllib2.urlopen(self.url + '/shutdown', timeout=10).close()
156 except:
157 pass
158
159 self.close_bgjob()