blob: f731b618fe31ec414f557a6899c52fa824fa419e [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'
23
24class chromedriver(object):
25 """Wrapper class, a context manager type, for tests to use Chrome Driver."""
26
27 def __init__(self, extra_chrome_flags=[], subtract_extra_chrome_flags=[],
28 *args, **kwargs):
29 """Initialize.
30
31 @param extra_chrome_flags: Extra chrome flags to pass to chrome, if any.
32 @param subtract_extra_chrome_flags: Remove default flags passed to
33 chrome by chromedriver, if any.
34 """
35 assert os.geteuid() == 0, 'Need superuser privileges'
36
37 # Log in with telemetry
38 self._browser = chrome.Chrome().browser
39
40 # Start ChromeDriver server
41 self._server = chromedriver_server(CHROMEDRIVER_EXE_PATH)
42
43 chromeOptions = {'debuggerAddress':
44 ('localhost:%d' %
45 utils.get_chrome_remote_debugging_port())}
46 capabilities = {'chromeOptions':chromeOptions}
47 # Handle to chromedriver, for chrome automation.
Dan Shi96c77cb2013-11-19 10:30:37 -080048 try:
49 self.driver = webdriver.Remote(command_executor=self._server.url,
50 desired_capabilities=capabilities)
51 except NameError:
52 logging.error('selenium module failed to be imported.')
53 raise
Dan Shic1d263b2013-10-04 17:31:38 -070054
55
56 def __enter__(self):
57 return self
58
59
60 def __exit__(self, *args):
61 """Clean up after running the test.
62
63 """
64 if hasattr(self, 'driver') and self.driver:
65 self.driver.close()
66 del self.driver
67
68 if hasattr(self, '_server') and self._server:
69 self._server.close()
70 del self._server
71
72 if hasattr(self, '_browser') and self._browser:
73 self._browser.Close()
74 del self._browser
75
76
77class chromedriver_server(object):
78 """A running ChromeDriver server.
79
80 This code is migrated from chrome:
81 src/chrome/test/chromedriver/server/server.py
82 """
83
84 def __init__(self, exe_path):
85 """Starts the ChromeDriver server and waits for it to be ready.
86
87 Args:
88 exe_path: path to the ChromeDriver executable
89 Raises:
90 RuntimeError if ChromeDriver fails to start
91 """
92 if not os.path.exists(exe_path):
93 raise RuntimeError('ChromeDriver exe not found at: ' + exe_path)
94
95 port = utils.get_unused_port()
96 chromedriver_args = [exe_path, '--port=%d' % port]
97 self.bg_job = utils.BgJob(chromedriver_args, stderr_level=logging.DEBUG)
98 self.url = 'http://localhost:%d' % port
99 if self.bg_job is None:
100 raise RuntimeError('ChromeDriver server cannot be started')
101
102 try:
103 timeout_msg = 'Timeout on waiting for ChromeDriver to start.'
104 utils.poll_for_condition(self.is_running,
105 exception=utils.TimeoutError(timeout_msg),
106 timeout=10,
107 sleep_interval=.1)
108 except utils.TimeoutError:
109 self.close_bgjob()
110 raise RuntimeError('ChromeDriver server did not start')
111
112 logging.debug('Chrome Driver server is up and listening at port %d.',
113 port)
114 atexit.register(self.close)
115
116
117 def is_running(self):
118 """Returns whether the server is up and running."""
119 try:
120 urllib2.urlopen(self.url + '/status')
121 return True
122 except urllib2.URLError as e:
123 return False
124
125
126 def close_bgjob(self):
127 """Close background job and log stdout and stderr."""
128 utils.nuke_subprocess(self.bg_job.sp)
129 utils.join_bg_jobs([self.bg_job], timeout=1)
130 result = self.bg_job.result
131 if result.stdout or result.stderr:
132 logging.info('stdout of Chrome Driver:\n%s', result.stdout)
133 logging.error('stderr of Chrome Driver:\n%s', result.stderr)
134
135
136 def close(self):
137 """Kills the ChromeDriver server, if it is running."""
138 if self.bg_job is None:
139 return
140
141 try:
142 urllib2.urlopen(self.url + '/shutdown', timeout=10).close()
143 except:
144 pass
145
146 self.close_bgjob()