blob: 0f3ad39e2b22a2d8687a01b5625ff5703db39d5f [file] [log] [blame]
# 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()