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 |