| OLD | NEW |
| 1 # Copyright (C) 2011 Google Inc. All rights reserved. | 1 # Copyright (C) 2011 Google Inc. All rights reserved. |
| 2 # | 2 # |
| 3 # Redistribution and use in source and binary forms, with or without | 3 # Redistribution and use in source and binary forms, with or without |
| 4 # modification, are permitted provided that the following conditions are | 4 # modification, are permitted provided that the following conditions are |
| 5 # met: | 5 # met: |
| 6 # | 6 # |
| 7 # * Redistributions of source code must retain the above copyright | 7 # * Redistributions of source code must retain the above copyright |
| 8 # notice, this list of conditions and the following disclaimer. | 8 # notice, this list of conditions and the following disclaimer. |
| 9 # * Redistributions in binary form must reproduce the above | 9 # * Redistributions in binary form must reproduce the above |
| 10 # copyright notice, this list of conditions and the following disclaimer | 10 # copyright notice, this list of conditions and the following disclaimer |
| (...skipping 25 matching lines...) Expand all Loading... |
| 36 | 36 |
| 37 | 37 |
| 38 _log = logging.getLogger(__name__) | 38 _log = logging.getLogger(__name__) |
| 39 | 39 |
| 40 | 40 |
| 41 class ServerError(Exception): | 41 class ServerError(Exception): |
| 42 pass | 42 pass |
| 43 | 43 |
| 44 | 44 |
| 45 class ServerBase(object): | 45 class ServerBase(object): |
| 46 |
| 46 """A skeleton class for starting and stopping servers used by the layout tes
ts.""" | 47 """A skeleton class for starting and stopping servers used by the layout tes
ts.""" |
| 47 | 48 |
| 48 def __init__(self, port_obj, output_dir): | 49 def __init__(self, port_obj, output_dir): |
| 49 self._port_obj = port_obj | 50 self._port_obj = port_obj |
| 50 self._executive = port_obj._executive | 51 self._executive = port_obj._executive |
| 51 self._filesystem = port_obj._filesystem | 52 self._filesystem = port_obj._filesystem |
| 52 self._platform = port_obj.host.platform | 53 self._platform = port_obj.host.platform |
| 53 self._output_dir = output_dir | 54 self._output_dir = output_dir |
| 54 | 55 |
| 55 # We need a non-checkout-dependent place to put lock files, etc. We | 56 # We need a non-checkout-dependent place to put lock files, etc. We |
| 56 # don't use the Python default on the Mac because it defaults to a | 57 # don't use the Python default on the Mac because it defaults to a |
| 57 # randomly-generated directory under /var/folders and no one would ever | 58 # randomly-generated directory under /var/folders and no one would ever |
| 58 # look there. | 59 # look there. |
| 59 tmpdir = tempfile.gettempdir() | 60 tmpdir = tempfile.gettempdir() |
| 60 if self._platform.is_mac(): | 61 if self._platform.is_mac(): |
| 61 tmpdir = '/tmp' | 62 tmpdir = '/tmp' |
| 62 | 63 |
| 63 self._runtime_path = self._filesystem.join(tmpdir, "WebKit") | 64 self._runtime_path = self._filesystem.join(tmpdir, 'WebKit') |
| 64 self._filesystem.maybe_make_directory(self._runtime_path) | 65 self._filesystem.maybe_make_directory(self._runtime_path) |
| 65 | 66 |
| 66 # Subclasses must override these fields. | 67 # Subclasses must override these fields. |
| 67 self._name = '<virtual>' | 68 self._name = '<virtual>' |
| 68 self._log_prefixes = tuple() | 69 self._log_prefixes = tuple() |
| 69 self._mappings = {} | 70 self._mappings = {} |
| 70 self._pid_file = None | 71 self._pid_file = None |
| 71 self._start_cmd = None | 72 self._start_cmd = None |
| 72 | 73 |
| 73 # Subclasses may override these fields. | 74 # Subclasses may override these fields. |
| (...skipping 21 matching lines...) Expand all Loading... |
| 95 self._remove_pid_file() | 96 self._remove_pid_file() |
| 96 self._pid = None | 97 self._pid = None |
| 97 | 98 |
| 98 self._remove_stale_logs() | 99 self._remove_stale_logs() |
| 99 self._prepare_config() | 100 self._prepare_config() |
| 100 self._check_that_all_ports_are_available() | 101 self._check_that_all_ports_are_available() |
| 101 | 102 |
| 102 self._pid = self._spawn_process() | 103 self._pid = self._spawn_process() |
| 103 | 104 |
| 104 if self._wait_for_action(self._is_server_running_on_all_ports): | 105 if self._wait_for_action(self._is_server_running_on_all_ports): |
| 105 _log.debug("%s successfully started (pid = %d)" % (self._name, self.
_pid)) | 106 _log.debug('%s successfully started (pid = %d)' % (self._name, self.
_pid)) |
| 106 else: | 107 else: |
| 107 self._log_errors_from_subprocess() | 108 self._log_errors_from_subprocess() |
| 108 self._stop_running_server() | 109 self._stop_running_server() |
| 109 raise ServerError('Failed to start %s server' % self._name) | 110 raise ServerError('Failed to start %s server' % self._name) |
| 110 | 111 |
| 111 def stop(self): | 112 def stop(self): |
| 112 """Stops the server. Stopping a server that isn't started is harmless.""
" | 113 """Stops the server. Stopping a server that isn't started is harmless.""
" |
| 113 actual_pid = None | 114 actual_pid = None |
| 114 try: | 115 try: |
| 115 if self._filesystem.exists(self._pid_file): | 116 if self._filesystem.exists(self._pid_file): |
| 116 try: | 117 try: |
| 117 actual_pid = int(self._filesystem.read_text_file(self._pid_f
ile)) | 118 actual_pid = int(self._filesystem.read_text_file(self._pid_f
ile)) |
| 118 except (ValueError, UnicodeDecodeError): | 119 except (ValueError, UnicodeDecodeError): |
| 119 # These could be raised if the pid file is corrupt. | 120 # These could be raised if the pid file is corrupt. |
| 120 pass | 121 pass |
| 121 if not self._pid: | 122 if not self._pid: |
| 122 self._pid = actual_pid | 123 self._pid = actual_pid |
| 123 | 124 |
| 124 if not self._pid: | 125 if not self._pid: |
| 125 return | 126 return |
| 126 | 127 |
| 127 if not actual_pid: | 128 if not actual_pid: |
| 128 _log.warning('Failed to stop %s: pid file is missing' % self._na
me) | 129 _log.warning('Failed to stop %s: pid file is missing' % self._na
me) |
| 129 return | 130 return |
| 130 if self._pid != actual_pid: | 131 if self._pid != actual_pid: |
| 131 _log.warning('Failed to stop %s: pid file contains %d, not %d' % | 132 _log.warning('Failed to stop %s: pid file contains %d, not %d' % |
| 132 (self._name, actual_pid, self._pid)) | 133 (self._name, actual_pid, self._pid)) |
| 133 # Try to kill the existing pid, anyway, in case it got orphaned. | 134 # Try to kill the existing pid, anyway, in case it got orphaned. |
| 134 self._executive.kill_process(self._pid) | 135 self._executive.kill_process(self._pid) |
| 135 self._pid = None | 136 self._pid = None |
| 136 return | 137 return |
| 137 | 138 |
| 138 _log.debug("Attempting to shut down %s server at pid %d" % (self._na
me, self._pid)) | 139 _log.debug('Attempting to shut down %s server at pid %d' % (self._na
me, self._pid)) |
| 139 self._stop_running_server() | 140 self._stop_running_server() |
| 140 _log.debug("%s server at pid %d stopped" % (self._name, self._pid)) | 141 _log.debug('%s server at pid %d stopped' % (self._name, self._pid)) |
| 141 self._pid = None | 142 self._pid = None |
| 142 finally: | 143 finally: |
| 143 # Make sure we delete the pid file no matter what happens. | 144 # Make sure we delete the pid file no matter what happens. |
| 144 self._remove_pid_file() | 145 self._remove_pid_file() |
| 145 | 146 |
| 146 def _prepare_config(self): | 147 def _prepare_config(self): |
| 147 """This routine can be overridden by subclasses to do any sort | 148 """This routine can be overridden by subclasses to do any sort |
| 148 of initialization required prior to starting the server that may fail.""
" | 149 of initialization required prior to starting the server that may fail.""
" |
| 149 pass | 150 pass |
| 150 | 151 |
| 151 def _remove_stale_logs(self): | 152 def _remove_stale_logs(self): |
| 152 """This routine can be overridden by subclasses to try and remove logs | 153 """This routine can be overridden by subclasses to try and remove logs |
| 153 left over from a prior run. This routine should log warnings if the | 154 left over from a prior run. This routine should log warnings if the |
| 154 files cannot be deleted, but should not fail unless failure to | 155 files cannot be deleted, but should not fail unless failure to |
| 155 delete the logs will actually cause start() to fail.""" | 156 delete the logs will actually cause start() to fail.""" |
| 156 # Sometimes logs are open in other processes but they should clear event
ually. | 157 # Sometimes logs are open in other processes but they should clear event
ually. |
| 157 for log_prefix in self._log_prefixes: | 158 for log_prefix in self._log_prefixes: |
| 158 try: | 159 try: |
| 159 self._remove_log_files(self._output_dir, log_prefix) | 160 self._remove_log_files(self._output_dir, log_prefix) |
| 160 except OSError, e: | 161 except OSError as e: |
| 161 _log.warning('Failed to remove old %s %s files' % (self._name, l
og_prefix)) | 162 _log.warning('Failed to remove old %s %s files' % (self._name, l
og_prefix)) |
| 162 | 163 |
| 163 def _spawn_process(self): | 164 def _spawn_process(self): |
| 164 _log.debug('Starting %s server, cmd="%s"' % (self._name, self._start_cmd
)) | 165 _log.debug('Starting %s server, cmd="%s"' % (self._name, self._start_cmd
)) |
| 165 process = self._executive.popen(self._start_cmd, env=self._env, stdout=s
elf._stdout, stderr=self._stderr) | 166 process = self._executive.popen(self._start_cmd, env=self._env, stdout=s
elf._stdout, stderr=self._stderr) |
| 166 pid = process.pid | 167 pid = process.pid |
| 167 self._filesystem.write_text_file(self._pid_file, str(pid)) | 168 self._filesystem.write_text_file(self._pid_file, str(pid)) |
| 168 return pid | 169 return pid |
| 169 | 170 |
| 170 def _stop_running_server(self): | 171 def _stop_running_server(self): |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 222 else: | 223 else: |
| 223 _log.error('%s no error log' % self._name) | 224 _log.error('%s no error log' % self._name) |
| 224 | 225 |
| 225 def _wait_for_action(self, action, wait_secs=20.0, sleep_secs=1.0): | 226 def _wait_for_action(self, action, wait_secs=20.0, sleep_secs=1.0): |
| 226 """Repeat the action for wait_sec or until it succeeds, sleeping for sle
ep_secs | 227 """Repeat the action for wait_sec or until it succeeds, sleeping for sle
ep_secs |
| 227 in between each attempt. Returns whether it succeeded.""" | 228 in between each attempt. Returns whether it succeeded.""" |
| 228 start_time = time.time() | 229 start_time = time.time() |
| 229 while time.time() - start_time < wait_secs: | 230 while time.time() - start_time < wait_secs: |
| 230 if action(): | 231 if action(): |
| 231 return True | 232 return True |
| 232 _log.debug("Waiting for action: %s" % action) | 233 _log.debug('Waiting for action: %s' % action) |
| 233 time.sleep(sleep_secs) | 234 time.sleep(sleep_secs) |
| 234 | 235 |
| 235 return False | 236 return False |
| 236 | 237 |
| 237 def _is_server_running_on_all_ports(self): | 238 def _is_server_running_on_all_ports(self): |
| 238 """Returns whether the server is running on all the desired ports.""" | 239 """Returns whether the server is running on all the desired ports.""" |
| 239 | 240 |
| 240 # TODO(dpranke): crbug/378444 maybe pid is unreliable on win? | 241 # TODO(dpranke): crbug/378444 maybe pid is unreliable on win? |
| 241 if not self._platform.is_win() and not self._executive.check_running_pid
(self._pid): | 242 if not self._platform.is_win() and not self._executive.check_running_pid
(self._pid): |
| 242 _log.debug("Server isn't running at all") | 243 _log.debug("Server isn't running at all") |
| 243 self._log_errors_from_subprocess() | 244 self._log_errors_from_subprocess() |
| 244 raise ServerError("Server exited") | 245 raise ServerError('Server exited') |
| 245 | 246 |
| 246 for mapping in self._mappings: | 247 for mapping in self._mappings: |
| 247 s = socket.socket() | 248 s = socket.socket() |
| 248 port = mapping['port'] | 249 port = mapping['port'] |
| 249 try: | 250 try: |
| 250 s.connect(('localhost', port)) | 251 s.connect(('localhost', port)) |
| 251 _log.debug("Server running on %d" % port) | 252 _log.debug('Server running on %d' % port) |
| 252 except IOError, e: | 253 except IOError as e: |
| 253 if e.errno not in (errno.ECONNREFUSED, errno.ECONNRESET): | 254 if e.errno not in (errno.ECONNREFUSED, errno.ECONNRESET): |
| 254 raise | 255 raise |
| 255 _log.debug("Server NOT running on %d: %s" % (port, e)) | 256 _log.debug('Server NOT running on %d: %s' % (port, e)) |
| 256 return False | 257 return False |
| 257 finally: | 258 finally: |
| 258 s.close() | 259 s.close() |
| 259 return True | 260 return True |
| 260 | 261 |
| 261 def _check_that_all_ports_are_available(self): | 262 def _check_that_all_ports_are_available(self): |
| 262 for mapping in self._mappings: | 263 for mapping in self._mappings: |
| 263 s = socket.socket() | 264 s = socket.socket() |
| 264 if not self._platform.is_win(): | 265 if not self._platform.is_win(): |
| 265 s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) | 266 s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) |
| 266 port = mapping['port'] | 267 port = mapping['port'] |
| 267 try: | 268 try: |
| 268 s.bind(('localhost', port)) | 269 s.bind(('localhost', port)) |
| 269 except IOError, e: | 270 except IOError as e: |
| 270 if e.errno in (errno.EALREADY, errno.EADDRINUSE): | 271 if e.errno in (errno.EALREADY, errno.EADDRINUSE): |
| 271 raise ServerError('Port %d is already in use.' % port) | 272 raise ServerError('Port %d is already in use.' % port) |
| 272 elif self._platform.is_win() and e.errno in (errno.WSAEACCES,):
# pylint: disable=E1101 | 273 elif self._platform.is_win() and e.errno in (errno.WSAEACCES,):
# pylint: disable=E1101 |
| 273 raise ServerError('Port %d is already in use.' % port) | 274 raise ServerError('Port %d is already in use.' % port) |
| 274 else: | 275 else: |
| 275 raise | 276 raise |
| 276 finally: | 277 finally: |
| 277 s.close() | 278 s.close() |
| 278 _log.debug('all ports are available') | 279 _log.debug('all ports are available') |
| OLD | NEW |