728d3c682225f6d6a3227211304964da0da4b12f
[WebKit-https.git] / WebDriverTests / imported / w3c / tools / wptrunner / wptrunner / executors / executorselenium.py
1 import os
2 import socket
3 import sys
4 import threading
5 import time
6 import traceback
7 import urlparse
8 import uuid
9
10 from .base import (Protocol,
11                    RefTestExecutor,
12                    RefTestImplementation,
13                    TestharnessExecutor,
14                    extra_timeout,
15                    strip_server)
16 from ..testrunner import Stop
17
18 here = os.path.join(os.path.split(__file__)[0])
19
20 webdriver = None
21 exceptions = None
22 RemoteConnection = None
23
24
25 def do_delayed_imports():
26     global webdriver
27     global exceptions
28     global RemoteConnection
29     from selenium import webdriver
30     from selenium.common import exceptions
31     from selenium.webdriver.remote.remote_connection import RemoteConnection
32
33
34 class SeleniumProtocol(Protocol):
35     def __init__(self, executor, browser, capabilities, **kwargs):
36         do_delayed_imports()
37
38         Protocol.__init__(self, executor, browser)
39         self.capabilities = capabilities
40         self.url = browser.webdriver_url
41         self.webdriver = None
42
43     def setup(self, runner):
44         """Connect to browser via Selenium's WebDriver implementation."""
45         self.runner = runner
46         self.logger.debug("Connecting to Selenium on URL: %s" % self.url)
47
48         session_started = False
49         try:
50             self.webdriver = webdriver.Remote(command_executor=RemoteConnection(self.url.strip("/"),
51                                                                                 resolve_ip=False),
52                                               desired_capabilities=self.capabilities)
53         except:
54             self.logger.warning(
55                 "Connecting to Selenium failed:\n%s" % traceback.format_exc())
56         else:
57             self.logger.debug("Selenium session started")
58             session_started = True
59
60         if not session_started:
61             self.logger.warning("Failed to connect to Selenium")
62             self.executor.runner.send_message("init_failed")
63         else:
64             try:
65                 self.after_connect()
66             except:
67                 print >> sys.stderr, traceback.format_exc()
68                 self.logger.warning(
69                     "Failed to connect to navigate initial page")
70                 self.executor.runner.send_message("init_failed")
71             else:
72                 self.executor.runner.send_message("init_succeeded")
73
74     def teardown(self):
75         self.logger.debug("Hanging up on Selenium session")
76         try:
77             self.webdriver.quit()
78         except:
79             pass
80         del self.webdriver
81
82     def is_alive(self):
83         try:
84             # Get a simple property over the connection
85             self.webdriver.current_window_handle
86         # TODO what exception?
87         except (socket.timeout, exceptions.ErrorInResponseException):
88             return False
89         return True
90
91     def after_connect(self):
92         self.load_runner("http")
93
94     def load_runner(self, protocol):
95         url = urlparse.urljoin(self.executor.server_url(protocol),
96                                "/testharness_runner.html")
97         self.logger.debug("Loading %s" % url)
98         self.webdriver.get(url)
99         self.webdriver.execute_script("document.title = '%s'" %
100                                       threading.current_thread().name.replace("'", '"'))
101
102     def wait(self):
103         while True:
104             try:
105                 self.webdriver.execute_async_script("");
106             except exceptions.TimeoutException:
107                 pass
108             except (socket.timeout, exceptions.NoSuchWindowException,
109                     exceptions.ErrorInResponseException, IOError):
110                 break
111             except Exception as e:
112                 self.logger.error(traceback.format_exc(e))
113                 break
114
115
116 class SeleniumRun(object):
117     def __init__(self, func, webdriver, url, timeout):
118         self.func = func
119         self.result = None
120         self.webdriver = webdriver
121         self.url = url
122         self.timeout = timeout
123         self.result_flag = threading.Event()
124
125     def run(self):
126         timeout = self.timeout
127
128         try:
129             self.webdriver.set_script_timeout((timeout + extra_timeout) * 1000)
130         except exceptions.ErrorInResponseException:
131             self.logger.error("Lost WebDriver connection")
132             return Stop
133
134         executor = threading.Thread(target=self._run)
135         executor.start()
136
137         flag = self.result_flag.wait(timeout + 2 * extra_timeout)
138         if self.result is None:
139             assert not flag
140             self.result = False, ("EXTERNAL-TIMEOUT", None)
141
142         return self.result
143
144     def _run(self):
145         try:
146             self.result = True, self.func(self.webdriver, self.url, self.timeout)
147         except exceptions.TimeoutException:
148             self.result = False, ("EXTERNAL-TIMEOUT", None)
149         except (socket.timeout, exceptions.ErrorInResponseException):
150             self.result = False, ("CRASH", None)
151         except Exception as e:
152             message = getattr(e, "message", "")
153             if message:
154                 message += "\n"
155             message += traceback.format_exc(e)
156             self.result = False, ("ERROR", e)
157         finally:
158             self.result_flag.set()
159
160
161 class SeleniumTestharnessExecutor(TestharnessExecutor):
162     def __init__(self, browser, server_config, timeout_multiplier=1,
163                  close_after_done=True, capabilities=None, debug_info=None,
164                  **kwargs):
165         """Selenium-based executor for testharness.js tests"""
166         TestharnessExecutor.__init__(self, browser, server_config,
167                                      timeout_multiplier=timeout_multiplier,
168                                      debug_info=debug_info)
169         self.protocol = SeleniumProtocol(self, browser, capabilities)
170         with open(os.path.join(here, "testharness_webdriver.js")) as f:
171             self.script = f.read()
172         self.close_after_done = close_after_done
173         self.window_id = str(uuid.uuid4())
174
175     def is_alive(self):
176         return self.protocol.is_alive()
177
178     def on_environment_change(self, new_environment):
179         if new_environment["protocol"] != self.last_environment["protocol"]:
180             self.protocol.load_runner(new_environment["protocol"])
181
182     def do_test(self, test):
183         url = self.test_url(test)
184
185         success, data = SeleniumRun(self.do_testharness,
186                                     self.protocol.webdriver,
187                                     url,
188                                     test.timeout * self.timeout_multiplier).run()
189
190         if success:
191             return self.convert_result(test, data)
192
193         return (test.result_cls(*data), [])
194
195     def do_testharness(self, webdriver, url, timeout):
196         return webdriver.execute_async_script(
197             self.script % {"abs_url": url,
198                            "url": strip_server(url),
199                            "window_id": self.window_id,
200                            "timeout_multiplier": self.timeout_multiplier,
201                            "timeout": timeout * 1000})
202
203
204 class SeleniumRefTestExecutor(RefTestExecutor):
205     def __init__(self, browser, server_config, timeout_multiplier=1,
206                  screenshot_cache=None, close_after_done=True,
207                  debug_info=None, capabilities=None, **kwargs):
208         """Selenium WebDriver-based executor for reftests"""
209         RefTestExecutor.__init__(self,
210                                  browser,
211                                  server_config,
212                                  screenshot_cache=screenshot_cache,
213                                  timeout_multiplier=timeout_multiplier,
214                                  debug_info=debug_info)
215         self.protocol = SeleniumProtocol(self, browser,
216                                          capabilities=capabilities)
217         self.implementation = RefTestImplementation(self)
218         self.close_after_done = close_after_done
219         self.has_window = False
220
221         with open(os.path.join(here, "reftest.js")) as f:
222             self.script = f.read()
223         with open(os.path.join(here, "reftest-wait_webdriver.js")) as f:
224             self.wait_script = f.read()
225
226     def is_alive(self):
227         return self.protocol.is_alive()
228
229     def do_test(self, test):
230         self.logger.info("Test requires OS-level window focus")
231
232         self.protocol.webdriver.set_window_size(600, 600)
233
234         result = self.implementation.run_test(test)
235
236         return self.convert_result(test, result)
237
238     def screenshot(self, test, viewport_size, dpi):
239         # https://github.com/w3c/wptrunner/issues/166
240         assert viewport_size is None
241         assert dpi is None
242
243         return SeleniumRun(self._screenshot,
244                            self.protocol.webdriver,
245                            self.test_url(test),
246                            test.timeout).run()
247
248     def _screenshot(self, webdriver, url, timeout):
249         webdriver.get(url)
250
251         webdriver.execute_async_script(self.wait_script)
252
253         screenshot = webdriver.get_screenshot_as_base64()
254
255         # strip off the data:img/png, part of the url
256         if screenshot.startswith("data:image/png;base64,"):
257             screenshot = screenshot.split(",", 1)[1]
258
259         return screenshot