161 lines
6.6 KiB
Python
161 lines
6.6 KiB
Python
# Copyright (C) 2017 Igalia S.L.
|
|
#
|
|
# Redistribution and use in source and binary forms, with or without
|
|
# modification, are permitted provided that the following conditions
|
|
# are met:
|
|
# 1. Redistributions of source code must retain the above copyright
|
|
# notice, this list of conditions and the following disclaimer.
|
|
# 2. Redistributions in binary form must reproduce the above copyright
|
|
# notice, this list of conditions and the following disclaimer in the
|
|
# documentation and/or other materials provided with the distribution.
|
|
#
|
|
# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
|
|
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
# DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
|
|
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
|
# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
import errno
|
|
import json
|
|
import logging
|
|
import os
|
|
import socket
|
|
import tempfile
|
|
import time
|
|
|
|
from webkitpy.common.webkit_finder import WebKitFinder
|
|
|
|
_log = logging.getLogger(__name__)
|
|
|
|
|
|
class WebDriverW3CWebServer(object):
|
|
|
|
def __init__(self, port):
|
|
self._port = port
|
|
self._name = "wptwd"
|
|
|
|
layout_root = self._port.layout_tests_dir()
|
|
self._layout_doc_root = os.path.join(layout_root, 'imported', 'w3c', 'web-platform-tests')
|
|
self._process = None
|
|
self._pid = None
|
|
self._wsout = None
|
|
|
|
tmpdir = tempfile.gettempdir()
|
|
if self._port.host.platform.is_mac():
|
|
tmpdir = '/tmp'
|
|
self._runtime_path = os.path.join(tmpdir, "WebKitWebDriverTests")
|
|
self._port.host.filesystem.maybe_make_directory(self._runtime_path)
|
|
|
|
self._pid_file = os.path.join(self._runtime_path, '%s.pid' % self._name)
|
|
|
|
# FIXME: We use the runtime path for now for log output, since we don't have a results directory yet.
|
|
self._output_log_path = os.path.join(self._runtime_path, '%s_process_log.out.txt' % (self._name))
|
|
|
|
def _wait_for_server(self, wait_secs=20, sleep_secs=1):
|
|
def check_port(host, port):
|
|
s = socket.socket()
|
|
try:
|
|
s.connect((host, port))
|
|
except IOError as e:
|
|
if e.errno not in (errno.ECONNREFUSED, errno.ECONNRESET):
|
|
raise
|
|
return False
|
|
finally:
|
|
s.close()
|
|
return True
|
|
|
|
start_time = time.time()
|
|
while time.time() - start_time < wait_secs:
|
|
if self._port._executive.check_running_pid(self._pid) \
|
|
and check_port(self._server_host, self._server_http_port) \
|
|
and check_port(self._server_host, self._server_https_port):
|
|
return True
|
|
time.sleep(sleep_secs)
|
|
return False
|
|
|
|
def start(self):
|
|
assert not self._pid, '%s server is already running' % self._name
|
|
|
|
# Stop any stale servers left over from previous instances.
|
|
if self._port.host.filesystem.exists(self._pid_file):
|
|
try:
|
|
self._pid = int(self._port.host.filesystem.read_text_file(self._pid_file))
|
|
self.stop()
|
|
except (ValueError, UnicodeDecodeError):
|
|
# These could be raised if the pid file is corrupt.
|
|
self._port.host.filesystem.remove(self._pid_file)
|
|
self._pid = None
|
|
|
|
_log.debug('Copying WebDriver WPT server config.json')
|
|
doc_root = os.path.join(WebKitFinder(self._port.host.filesystem).path_from_webkit_base('WebDriverTests'), 'imported', 'w3c')
|
|
config_filename = os.path.join(doc_root, 'config.json')
|
|
config = json.loads(self._port.host.filesystem.read_text_file(config_filename))
|
|
config['doc_root'] = doc_root
|
|
config['ssl']['openssl']['base_path'] = os.path.join(self._runtime_path, '_wpt_certs')
|
|
self._port.host.filesystem.write_text_file(os.path.join(self._layout_doc_root, 'config.json'), json.dumps(config))
|
|
self._server_host = config['browser_host']
|
|
self._server_http_port = config['ports']['http'][0]
|
|
self._server_https_port = config['ports']['https'][0]
|
|
|
|
self._wsout = self._port.host.filesystem.open_text_file_for_writing(self._output_log_path)
|
|
wpt_file = os.path.join(self._layout_doc_root, "wpt.py")
|
|
cmd = ["python3", wpt_file, "serve", "--config", os.path.join(self._layout_doc_root, 'config.json')]
|
|
self._process = self._port._executive.popen(cmd, cwd=self._layout_doc_root, shell=False, stdin=self._port._executive.PIPE, stdout=self._wsout, stderr=self._wsout)
|
|
self._pid = self._process.pid
|
|
self._port.host.filesystem.write_text_file(self._pid_file, str(self._pid))
|
|
|
|
if not self._wait_for_server():
|
|
_log.error('WPT Server process exited prematurely with status code %s' % self._process.returncode)
|
|
self.stop()
|
|
raise RuntimeError
|
|
|
|
_log.info('WebDriver WPT server listening at http://%s:%s/ and https://%s:%s/' % (self._server_host, self._server_http_port, self._server_host, self._server_https_port))
|
|
|
|
def stop(self):
|
|
_log.debug('Cleaning WebDriver WPT server config.json')
|
|
temporary_config_file = os.path.join(self._layout_doc_root, 'config.json')
|
|
if self._port.host.filesystem.exists(temporary_config_file):
|
|
self._port.host.filesystem.remove(temporary_config_file)
|
|
if self._wsout:
|
|
self._wsout.close()
|
|
self._wsout = None
|
|
if self._pid:
|
|
# kill_process will not kill the subprocesses, interrupt does the job.
|
|
self._port._executive.interrupt(self._pid)
|
|
self._port.host.filesystem.remove(self._pid_file)
|
|
self._pid = None
|
|
|
|
def host(self):
|
|
return self._server_host
|
|
|
|
def http_port(self):
|
|
return self._server_http_port
|
|
|
|
def https_port(self):
|
|
return self._server_https_port
|
|
|
|
def document_root(self):
|
|
return self._layout_doc_root
|
|
|
|
# Waits indefinitely until the webserver process is terminated.
|
|
def wait(self):
|
|
if not self._pid:
|
|
return
|
|
|
|
self._process.wait()
|
|
|
|
def __enter__(self):
|
|
if not self._pid:
|
|
self.start()
|
|
|
|
return self
|
|
|
|
def __exit__(self, exc_type, exc_value, exc_traceback):
|
|
if self._pid:
|
|
self.stop()
|