9e2547b0b8fdae836d5e7533bd2a1233ee79353c
[WebKit-https.git] / Tools / Scripts / webkitpy / layout_tests / servers / web_platform_test_server.py
1 #  Copyright (c) 2014, Canon Inc. All rights reserved.
2 #  Redistribution and use in source and binary forms, with or without
3 #  modification, are permitted provided that the following conditions
4 #  are met:
5 #  1.  Redistributions of source code must retain the above copyright
6 #      notice, this list of conditions and the following disclaimer.
7 #  2.  Redistributions in binary form must reproduce the above copyright
8 #      notice, this list of conditions and the following disclaimer in the
9 #      documentation and/or other materials provided with the distribution.
10 #  3.  Neither the name of Canon Inc. nor the names of
11 #      its contributors may be used to endorse or promote products derived
12 #      from this software without specific prior written permission.
13 #  THIS SOFTWARE IS PROVIDED BY CANON INC. AND ITS CONTRIBUTORS "AS IS" AND ANY
14 #  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15 #  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16 #  DISCLAIMED. IN NO EVENT SHALL CANON INC. AND ITS CONTRIBUTORS BE LIABLE FOR
17 #  ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18 #  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
19 #  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
20 #  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
21 #  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
22 #  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23
24 import json
25 import logging
26 import sys
27 import time
28
29 from webkitpy.common.system.autoinstall import AutoInstaller
30 from webkitpy.layout_tests.servers import http_server_base
31
32 _log = logging.getLogger(__name__)
33
34
35 def doc_root(port_obj):
36     doc_root = port_obj.get_option("wptserver_doc_root")
37     if doc_root is None:
38         return port_obj.host.filesystem.join("imported", "w3c", "web-platform-tests")
39     return doc_root
40
41
42 def base_url(port_obj):
43     config_wk_filepath = port_obj._filesystem.join(port_obj.layout_tests_dir(), "imported", "w3c", "resources", "config.json")
44     if not port_obj.host.filesystem.isfile(config_wk_filepath):
45         # This should only be hit by webkitpy unit tests
46         _log.debug("No WPT config file found")
47         return "http://localhost:8800/"
48     json_data = port_obj._filesystem.read_text_file(config_wk_filepath)
49     config = json.loads(json_data)
50     ports = config["ports"]
51     return "http://" + config["host"] + ":" + str(ports["http"][0]) + "/"
52
53
54 class WebPlatformTestServer(http_server_base.HttpServerBase):
55     def __init__(self, port_obj, name, pidfile=None):
56         http_server_base.HttpServerBase.__init__(self, port_obj)
57         self._output_dir = port_obj.results_directory()
58
59         self._name = name
60         self._log_file_name = '%s_process_log.out.txt' % (self._name)
61
62         self._wsout = None
63         self._process = None
64         self._pid_file = pidfile
65         if not self._pid_file:
66             self._pid_file = self._filesystem.join(self._runtime_path, '%s.pid' % self._name)
67         self._servers_file = self._filesystem.join(self._runtime_path, '%s_servers.json' % (self._name))
68
69         self._stdout_data = None
70         self._stderr_data = None
71         self._filesystem = port_obj.host.filesystem
72         self._layout_root = port_obj.layout_tests_dir()
73         self._doc_root = self._filesystem.join(self._layout_root, doc_root(port_obj))
74
75         self._resources_files_to_copy = ['testharness.css', 'testharnessreport.js']
76
77         current_dir_path = self._filesystem.abspath(self._filesystem.split(__file__)[0])
78         self._start_cmd = ["python", self._filesystem.join(current_dir_path, "web_platform_test_launcher.py"), self._servers_file]
79         self._doc_root_path = self._filesystem.join(self._layout_root, self._doc_root)
80
81     def _install_modules(self):
82         modules_file_path = self._filesystem.join(self._doc_root_path, "..", "resources", "web-platform-tests-modules.json")
83         if not self._filesystem.isfile(modules_file_path):
84             _log.warning("Cannot read " + modules_file_path)
85             return
86         modules = json.loads(self._filesystem.read_text_file(modules_file_path))
87         for module in modules:
88             AutoInstaller(target_dir=self._filesystem.join(self._doc_root, self._filesystem.sep.join(module["path"]))).install(url=module["url"], url_subpath=module["url_subpath"], target_name=module["name"])
89
90     def _copy_webkit_test_files(self):
91         _log.debug('Copying WebKit resources files')
92         for f in self._resources_files_to_copy:
93             webkit_filename = self._filesystem.join(self._layout_root, "resources", f)
94             if self._filesystem.isfile(webkit_filename):
95                 self._filesystem.copyfile(webkit_filename, self._filesystem.join(self._doc_root, "resources", f))
96         _log.debug('Copying WebKit web platform server config.json')
97         config_wk_filename = self._filesystem.join(self._layout_root, "imported", "w3c", "resources", "config.json")
98         if self._filesystem.isfile(config_wk_filename):
99             config_json = self._filesystem.read_text_file(config_wk_filename).replace("%CERTS_DIR%", self._filesystem.join(self._output_dir, "_wpt_certs"))
100             self._filesystem.write_text_file(self._filesystem.join(self._doc_root, "config.json"), config_json)
101
102         wpt_testharnessjs_file = self._filesystem.join(self._doc_root, "resources", "testharness.js")
103         layout_tests_testharnessjs_file = self._filesystem.join(self._layout_root, "resources", "testharness.js")
104         # FIXME: Next two lines are a temp hack for this patch to land smoothly on bots. They should be removed once patch landed and each bot runs these lines once.
105         if (not self._filesystem.isfile(wpt_testharnessjs_file)):
106             self._filesystem.copyfile(layout_tests_testharnessjs_file, wpt_testharnessjs_file)
107         if (not self._filesystem.compare(wpt_testharnessjs_file, layout_tests_testharnessjs_file)):
108             _log.warning("\n//////////\nWPT tests are not using the same testharness.js file as other WebKit Layout tests.\nWebKit testharness.js might need to be updated according WPT testharness.js.\n//////////\n")
109
110     def _clean_webkit_test_files(self):
111         _log.debug('Cleaning WPT resources files')
112         for f in self._resources_files_to_copy:
113             wpt_filename = self._filesystem.join(self._doc_root, "resources", f)
114             if self._filesystem.isfile(wpt_filename):
115                 self._filesystem.remove(wpt_filename)
116         _log.debug('Cleaning WPT web platform server config.json')
117         config_wpt_filename = self._filesystem.join(self._doc_root, "config.json")
118         if self._filesystem.isfile(config_wpt_filename):
119             self._filesystem.remove(config_wpt_filename)
120
121     def _prepare_config(self):
122         if self._filesystem.exists(self._output_dir):
123             output_log = self._filesystem.join(self._output_dir, self._log_file_name)
124             self._wsout = self._filesystem.open_text_file_for_writing(output_log)
125         self._install_modules()
126         self._copy_webkit_test_files()
127
128     def _spawn_process(self):
129         self._stdout_data = None
130         self._stderr_data = None
131         if self._wsout:
132             self._process = self._executive.popen(self._start_cmd, cwd=self._doc_root_path, shell=False, stdin=self._executive.PIPE, stdout=self._wsout, stderr=self._wsout)
133         else:
134             self._process = self._executive.popen(self._start_cmd, cwd=self._doc_root_path, shell=False, stdin=self._executive.PIPE, stdout=self._executive.PIPE, stderr=self._executive.STDOUT)
135         self._filesystem.write_text_file(self._pid_file, str(self._process.pid))
136
137         # Wait a second for the server to actually start so that tests do not start until server is running.
138         time.sleep(1)
139
140         return self._process.pid
141
142     def _stop_running_subservers(self):
143         if self._filesystem.exists(self._servers_file):
144             try:
145                 json_data = self._filesystem.read_text_file(self._servers_file)
146                 started_servers = json.loads(json_data)
147                 for server in started_servers:
148                     if self._executive.check_running_pid(server['pid']):
149                         _log.warning('Killing server process (protocol: %s , port: %d, pid: %d).' % (server['protocol'], server['port'], server['pid']))
150                         self._executive.kill_process(server['pid'])
151             finally:
152                 self._filesystem.remove(self._servers_file)
153
154     def stop(self):
155         super(WebPlatformTestServer, self).stop()
156         # In case of orphaned pid, kill the running subservers if any still alive.
157         self._stop_running_subservers()
158
159     def _stop_running_server(self):
160         _log.debug('Stopping %s server' % (self._name))
161         self._clean_webkit_test_files()
162
163         if self._process:
164             (self._stdout_data, self._stderr_data) = self._process.communicate(input='\n')
165         if self._wsout:
166             self._wsout.close()
167             self._wsout = None
168
169         if self._pid and self._executive.check_running_pid(self._pid):
170             _log.warning('Cannot stop %s server normally.' % (self._name))
171             _log.warning('Killing server launcher process (pid: %d).' % (self._pid))
172             self._executive.kill_process(self._pid)
173
174         self._remove_pid_file()
175         self._stop_running_subservers()