Unreviewed. Update W3C WebDriver imported tests.
[WebKit-https.git] / WebDriverTests / imported / w3c / tools / wptrunner / wptrunner / browsers / sauce.py
1 # This Source Code Form is subject to the terms of the Mozilla Public
2 # License, v. 2.0. If a copy of the MPL was not distributed with this file,
3 # You can obtain one at http://mozilla.org/MPL/2.0/.
4
5 import glob
6 import os
7 import shutil
8 import subprocess
9 import tarfile
10 import tempfile
11 import time
12 from cStringIO import StringIO as CStringIO
13
14 import requests
15
16 from .base import Browser, ExecutorBrowser, require_arg
17 from ..executors import executor_kwargs as base_executor_kwargs
18 from ..executors.executorselenium import (SeleniumTestharnessExecutor,
19                                           SeleniumRefTestExecutor)
20
21 here = os.path.split(__file__)[0]
22
23
24 __wptrunner__ = {"product": "sauce",
25                  "check_args": "check_args",
26                  "browser": "SauceBrowser",
27                  "executor": {"testharness": "SeleniumTestharnessExecutor",
28                               "reftest": "SeleniumRefTestExecutor"},
29                  "browser_kwargs": "browser_kwargs",
30                  "executor_kwargs": "executor_kwargs",
31                  "env_extras": "env_extras",
32                  "env_options": "env_options"}
33
34
35 def get_capabilities(**kwargs):
36     browser_name = kwargs["sauce_browser"]
37     platform = kwargs["sauce_platform"]
38     version = kwargs["sauce_version"]
39     build = kwargs["sauce_build"]
40     tags = kwargs["sauce_tags"]
41     tunnel_id = kwargs["sauce_tunnel_id"]
42     prerun_script = {
43         "MicrosoftEdge": {
44             "executable": "sauce-storage:edge-prerun.bat",
45             "background": False,
46         },
47         "safari": {
48             "executable": "sauce-storage:safari-prerun.sh",
49             "background": False,
50         }
51     }
52     capabilities = {
53         "browserName": browser_name,
54         "build": build,
55         "disablePopupHandler": True,
56         "name": "%s %s on %s" % (browser_name, version, platform),
57         "platform": platform,
58         "public": "public",
59         "selenium-version": "3.3.1",
60         "tags": tags,
61         "tunnel-identifier": tunnel_id,
62         "version": version,
63         "prerun": prerun_script.get(browser_name)
64     }
65
66     if browser_name == 'MicrosoftEdge':
67         capabilities['selenium-version'] = '2.4.8'
68
69     return capabilities
70
71
72 def get_sauce_config(**kwargs):
73     browser_name = kwargs["sauce_browser"]
74     sauce_user = kwargs["sauce_user"]
75     sauce_key = kwargs["sauce_key"]
76
77     hub_url = "%s:%s@localhost:4445" % (sauce_user, sauce_key)
78     data = {
79         "url": "http://%s/wd/hub" % hub_url,
80         "browserName": browser_name,
81         "capabilities": get_capabilities(**kwargs)
82     }
83
84     return data
85
86
87 def check_args(**kwargs):
88     require_arg(kwargs, "sauce_browser")
89     require_arg(kwargs, "sauce_platform")
90     require_arg(kwargs, "sauce_version")
91     require_arg(kwargs, "sauce_user")
92     require_arg(kwargs, "sauce_key")
93
94
95 def browser_kwargs(test_type, run_info_data, **kwargs):
96     sauce_config = get_sauce_config(**kwargs)
97
98     return {"sauce_config": sauce_config}
99
100
101 def executor_kwargs(test_type, server_config, cache_manager, run_info_data,
102                     **kwargs):
103     executor_kwargs = base_executor_kwargs(test_type, server_config,
104                                            cache_manager, **kwargs)
105
106     executor_kwargs["capabilities"] = get_capabilities(**kwargs)
107
108     return executor_kwargs
109
110
111 def env_extras(**kwargs):
112     return [SauceConnect(**kwargs)]
113
114
115 def env_options():
116     return {"host": "web-platform.test",
117             "bind_hostname": "true",
118             "supports_debugger": False}
119
120
121 def get_tar(url, dest):
122     resp = requests.get(url, stream=True)
123     resp.raise_for_status()
124     with tarfile.open(fileobj=CStringIO(resp.raw.read())) as f:
125         f.extractall(path=dest)
126
127
128 class SauceConnect():
129
130     def __init__(self, **kwargs):
131         self.sauce_user = kwargs["sauce_user"]
132         self.sauce_key = kwargs["sauce_key"]
133         self.sauce_tunnel_id = kwargs["sauce_tunnel_id"]
134         self.sauce_connect_binary = kwargs.get("sauce_connect_binary")
135         self.sc_process = None
136         self.temp_dir = None
137
138     def __enter__(self, options):
139         if not self.sauce_connect_binary:
140             self.temp_dir = tempfile.mkdtemp()
141             get_tar("https://saucelabs.com/downloads/sc-4.4.9-linux.tar.gz", self.temp_dir)
142             self.sauce_connect_binary = glob.glob(os.path.join(self.temp_dir, "sc-*-linux/bin/sc"))[0]
143
144         self.upload_prerun_exec('edge-prerun.bat')
145         self.upload_prerun_exec('safari-prerun.sh')
146
147         self.sc_process = subprocess.Popen([
148             self.sauce_connect_binary,
149             "--user=%s" % self.sauce_user,
150             "--api-key=%s" % self.sauce_key,
151             "--no-remove-colliding-tunnels",
152             "--tunnel-identifier=%s" % self.sauce_tunnel_id,
153             "--metrics-address=0.0.0.0:9876",
154             "--readyfile=./sauce_is_ready",
155             "--tunnel-domains",
156             "web-platform.test",
157             "*.web-platform.test"
158         ])
159
160         # Timeout config vars
161         each_sleep_secs = 1
162         max_wait = 30
163         kill_wait = 5
164
165         tot_wait = 0
166         while not os.path.exists('./sauce_is_ready') and self.sc_process.poll() is None:
167             if tot_wait >= max_wait:
168                 self.sc_process.terminate()
169                 while self.sc_process.poll() is None:
170                     time.sleep(each_sleep_secs)
171                     tot_wait += each_sleep_secs
172                     if tot_wait >= (max_wait + kill_wait):
173                         self.sc_process.kill()
174                         break
175                 raise SauceException("Sauce Connect Proxy was not ready after %d seconds" % tot_wait)
176
177             time.sleep(each_sleep_secs)
178             tot_wait += each_sleep_secs
179
180         if self.sc_process.returncode is not None:
181             raise SauceException("Unable to start Sauce Connect Proxy. Process exited with code %s", self.sc_process.returncode)
182
183     def __exit__(self, exc_type, exc_val, exc_tb):
184         self.sc_process.terminate()
185         if self.temp_dir and os.path.exists(self.temp_dir):
186             try:
187                 shutil.rmtree(self.temp_dir)
188             except OSError:
189                 pass
190
191     def upload_prerun_exec(self, file_name):
192         auth = (self.sauce_user, self.sauce_key)
193         url = "https://saucelabs.com/rest/v1/storage/%s/%s?overwrite=true" % (self.sauce_user, file_name)
194
195         with open(os.path.join(here, 'sauce_setup', file_name), 'rb') as f:
196             requests.post(url, data=f, auth=auth)
197
198
199 class SauceException(Exception):
200     pass
201
202
203 class SauceBrowser(Browser):
204     init_timeout = 300
205
206     def __init__(self, logger, sauce_config):
207         Browser.__init__(self, logger)
208         self.sauce_config = sauce_config
209
210     def start(self):
211         pass
212
213     def stop(self, force=False):
214         pass
215
216     def pid(self):
217         return None
218
219     def is_alive(self):
220         # TODO: Should this check something about the connection?
221         return True
222
223     def cleanup(self):
224         pass
225
226     def executor_browser(self):
227         return ExecutorBrowser, {"webdriver_url": self.sauce_config["url"]}