| OLD | NEW |
| 1 # Copyright 2013 The Chromium Authors. All rights reserved. | 1 # Copyright 2013 The Chromium Authors. All rights reserved. |
| 2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
| 4 | 4 |
| 5 """A "Test Server Spawner" that handles killing/stopping per-test test servers. | 5 """A "Test Server Spawner" that handles killing/stopping per-test test servers. |
| 6 | 6 |
| 7 It's used to accept requests from the device to spawn and kill instances of the | 7 It's used to accept requests from the device to spawn and kill instances of the |
| 8 chrome test server on the host. | 8 chrome test server on the host. |
| 9 """ | 9 """ |
| 10 | 10 |
| 11 import BaseHTTPServer | 11 import BaseHTTPServer |
| 12 import json | 12 import json |
| 13 import logging | 13 import logging |
| 14 import os | 14 import os |
| 15 import select | 15 import select |
| 16 import struct | 16 import struct |
| 17 import subprocess | 17 import subprocess |
| 18 import sys |
| 18 import threading | 19 import threading |
| 19 import time | 20 import time |
| 20 import urlparse | 21 import urlparse |
| 21 | 22 |
| 22 import constants | 23 import constants |
| 23 from forwarder import Forwarder | 24 from forwarder import Forwarder |
| 24 import ports | 25 import ports |
| 25 | 26 |
| 26 | 27 |
| 27 # Path that are needed to import necessary modules when launching a testserver. | 28 # Path that are needed to import necessary modules when launching a testserver. |
| (...skipping 10 matching lines...) Expand all Loading... |
| 38 'ftp': '-f', | 39 'ftp': '-f', |
| 39 'sync': '', # Sync uses its own script, and doesn't take a server type arg. | 40 'sync': '', # Sync uses its own script, and doesn't take a server type arg. |
| 40 'tcpecho': '--tcp-echo', | 41 'tcpecho': '--tcp-echo', |
| 41 'udpecho': '--udp-echo', | 42 'udpecho': '--udp-echo', |
| 42 } | 43 } |
| 43 | 44 |
| 44 | 45 |
| 45 # The timeout (in seconds) of starting up the Python test server. | 46 # The timeout (in seconds) of starting up the Python test server. |
| 46 TEST_SERVER_STARTUP_TIMEOUT = 10 | 47 TEST_SERVER_STARTUP_TIMEOUT = 10 |
| 47 | 48 |
| 49 def _WaitUntil(predicate, max_attempts=5): |
| 50 """Blocks until the provided predicate (function) is true. |
| 51 |
| 52 Returns: |
| 53 Whether the provided predicate was satisfied once (before the timeout). |
| 54 """ |
| 55 sleep_time_sec = 0.025 |
| 56 for attempt in xrange(1, max_attempts): |
| 57 if predicate(): |
| 58 return True |
| 59 time.sleep(sleep_time_sec) |
| 60 sleep_time_sec = min(1, sleep_time_sec * 2) # Don't wait more than 1 sec. |
| 61 return False |
| 62 |
| 48 | 63 |
| 49 def _CheckPortStatus(port, expected_status): | 64 def _CheckPortStatus(port, expected_status): |
| 50 """Returns True if port has expected_status. | 65 """Returns True if port has expected_status. |
| 51 | 66 |
| 52 Args: | 67 Args: |
| 53 port: the port number. | 68 port: the port number. |
| 54 expected_status: boolean of expected status. | 69 expected_status: boolean of expected status. |
| 55 | 70 |
| 56 Returns: | 71 Returns: |
| 57 Returns True if the status is expected. Otherwise returns False. | 72 Returns True if the status is expected. Otherwise returns False. |
| 58 """ | 73 """ |
| 59 for timeout in range(1, 5): | 74 return _WaitUntil(lambda: ports.IsHostPortUsed(port) == expected_status) |
| 60 if ports.IsHostPortUsed(port) == expected_status: | 75 |
| 61 return True | 76 |
| 62 time.sleep(timeout) | 77 def _CheckDevicePortStatus(adb, port): |
| 63 return False | 78 """Returns whether the provided port is used.""" |
| 79 return _WaitUntil(lambda: ports.IsDevicePortUsed(adb, port)) |
| 64 | 80 |
| 65 | 81 |
| 66 def _GetServerTypeCommandLine(server_type): | 82 def _GetServerTypeCommandLine(server_type): |
| 67 """Returns the command-line by the given server type. | 83 """Returns the command-line by the given server type. |
| 68 | 84 |
| 69 Args: | 85 Args: |
| 70 server_type: the server type to be used (e.g. 'http'). | 86 server_type: the server type to be used (e.g. 'http'). |
| 71 | 87 |
| 72 Returns: | 88 Returns: |
| 73 A string containing the command-line argument. | 89 A string containing the command-line argument. |
| (...skipping 149 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 223 else: | 239 else: |
| 224 self.is_ready = _CheckPortStatus(self.host_port, True) | 240 self.is_ready = _CheckPortStatus(self.host_port, True) |
| 225 if self.is_ready: | 241 if self.is_ready: |
| 226 self._test_server_forwarder = Forwarder(self.adb, self.build_type) | 242 self._test_server_forwarder = Forwarder(self.adb, self.build_type) |
| 227 self._test_server_forwarder.Run( | 243 self._test_server_forwarder.Run( |
| 228 [(0, self.host_port)], self.tool, '127.0.0.1') | 244 [(0, self.host_port)], self.tool, '127.0.0.1') |
| 229 # Check whether the forwarder is ready on the device. | 245 # Check whether the forwarder is ready on the device. |
| 230 self.is_ready = False | 246 self.is_ready = False |
| 231 device_port = self._test_server_forwarder.DevicePortForHostPort( | 247 device_port = self._test_server_forwarder.DevicePortForHostPort( |
| 232 self.host_port) | 248 self.host_port) |
| 233 if device_port: | 249 if device_port and _CheckDevicePortStatus(self.adb, device_port): |
| 234 for timeout in range(1, 5): | 250 self.is_ready = True |
| 235 if ports.IsDevicePortUsed(self.adb, device_port, 'LISTEN'): | 251 self.forwarder_device_port = device_port |
| 236 self.is_ready = True | |
| 237 self.forwarder_device_port = device_port | |
| 238 break | |
| 239 time.sleep(timeout) | |
| 240 # Wake up the request handler thread. | 252 # Wake up the request handler thread. |
| 241 self.ready_event.set() | 253 self.ready_event.set() |
| 242 # Keep thread running until Stop() gets called. | 254 # Keep thread running until Stop() gets called. |
| 243 while not self.stop_flag: | 255 _WaitUntil(lambda: self.stop_flag, max_attempts=sys.maxint) |
| 244 time.sleep(1) | |
| 245 if self.process.poll() is None: | 256 if self.process.poll() is None: |
| 246 self.process.kill() | 257 self.process.kill() |
| 247 if self._test_server_forwarder: | 258 if self._test_server_forwarder: |
| 248 self._test_server_forwarder.Close() | 259 self._test_server_forwarder.Close() |
| 249 self.process = None | 260 self.process = None |
| 250 self.is_ready = False | 261 self.is_ready = False |
| 251 if self.pipe_out: | 262 if self.pipe_out: |
| 252 os.close(self.pipe_in) | 263 os.close(self.pipe_in) |
| 253 os.close(self.pipe_out) | 264 os.close(self.pipe_out) |
| 254 self.pipe_in = None | 265 self.pipe_in = None |
| (...skipping 139 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 394 | 405 |
| 395 def _Listen(self): | 406 def _Listen(self): |
| 396 logging.info('Starting test server spawner') | 407 logging.info('Starting test server spawner') |
| 397 self.server.serve_forever() | 408 self.server.serve_forever() |
| 398 | 409 |
| 399 def Start(self): | 410 def Start(self): |
| 400 """Starts the test server spawner.""" | 411 """Starts the test server spawner.""" |
| 401 listener_thread = threading.Thread(target=self._Listen) | 412 listener_thread = threading.Thread(target=self._Listen) |
| 402 listener_thread.setDaemon(True) | 413 listener_thread.setDaemon(True) |
| 403 listener_thread.start() | 414 listener_thread.start() |
| 404 time.sleep(1) | |
| 405 | 415 |
| 406 def Stop(self): | 416 def Stop(self): |
| 407 """Stops the test server spawner. | 417 """Stops the test server spawner. |
| 408 | 418 |
| 409 Also cleans the server state. | 419 Also cleans the server state. |
| 410 """ | 420 """ |
| 411 self.CleanupState() | 421 self.CleanupState() |
| 412 self.server.shutdown() | 422 self.server.shutdown() |
| 413 | 423 |
| 414 def CleanupState(self): | 424 def CleanupState(self): |
| 415 """Cleans up the spawning server state. | 425 """Cleans up the spawning server state. |
| 416 | 426 |
| 417 This should be called if the test server spawner is reused, | 427 This should be called if the test server spawner is reused, |
| 418 to avoid sharing the test server instance. | 428 to avoid sharing the test server instance. |
| 419 """ | 429 """ |
| 420 if self.server.test_server_instance: | 430 if self.server.test_server_instance: |
| 421 self.server.test_server_instance.Stop() | 431 self.server.test_server_instance.Stop() |
| 422 self.server.test_server_instance = None | 432 self.server.test_server_instance = None |
| OLD | NEW |