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 sys |
19 import threading | 19 import threading |
20 import time | 20 import time |
21 import urlparse | 21 import urlparse |
22 | 22 |
23 import constants | 23 import constants |
24 from forwarder import Forwarder | |
25 import ports | 24 import ports |
26 | 25 |
27 | 26 |
28 # Path that are needed to import necessary modules when launching a testserver. | 27 # Path that are needed to import necessary modules when launching a testserver. |
29 os.environ['PYTHONPATH'] = os.environ.get('PYTHONPATH', '') + (':%s:%s:%s:%s:%s' | 28 os.environ['PYTHONPATH'] = os.environ.get('PYTHONPATH', '') + (':%s:%s:%s:%s:%s' |
30 % (os.path.join(constants.CHROME_DIR, 'third_party'), | 29 % (os.path.join(constants.CHROME_DIR, 'third_party'), |
31 os.path.join(constants.CHROME_DIR, 'third_party', 'tlslite'), | 30 os.path.join(constants.CHROME_DIR, 'third_party', 'tlslite'), |
32 os.path.join(constants.CHROME_DIR, 'third_party', 'pyftpdlib', 'src'), | 31 os.path.join(constants.CHROME_DIR, 'third_party', 'pyftpdlib', 'src'), |
33 os.path.join(constants.CHROME_DIR, 'net', 'tools', 'testserver'), | 32 os.path.join(constants.CHROME_DIR, 'net', 'tools', 'testserver'), |
34 os.path.join(constants.CHROME_DIR, 'sync', 'tools', 'testserver'))) | 33 os.path.join(constants.CHROME_DIR, 'sync', 'tools', 'testserver'))) |
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
92 raise NotImplementedError('Unknown server type: %s' % server_type) | 91 raise NotImplementedError('Unknown server type: %s' % server_type) |
93 if server_type == 'udpecho': | 92 if server_type == 'udpecho': |
94 raise Exception('Please do not run UDP echo tests because we do not have ' | 93 raise Exception('Please do not run UDP echo tests because we do not have ' |
95 'a UDP forwarder tool.') | 94 'a UDP forwarder tool.') |
96 return SERVER_TYPES[server_type] | 95 return SERVER_TYPES[server_type] |
97 | 96 |
98 | 97 |
99 class TestServerThread(threading.Thread): | 98 class TestServerThread(threading.Thread): |
100 """A thread to run the test server in a separate process.""" | 99 """A thread to run the test server in a separate process.""" |
101 | 100 |
102 def __init__(self, ready_event, arguments, adb, tool, build_type): | 101 def __init__(self, ready_event, arguments, adb, tool, forwarder, build_type): |
103 """Initialize TestServerThread with the following argument. | 102 """Initialize TestServerThread with the following argument. |
104 | 103 |
105 Args: | 104 Args: |
106 ready_event: event which will be set when the test server is ready. | 105 ready_event: event which will be set when the test server is ready. |
107 arguments: dictionary of arguments to run the test server. | 106 arguments: dictionary of arguments to run the test server. |
108 adb: instance of AndroidCommands. | 107 adb: instance of AndroidCommands. |
109 tool: instance of runtime error detection tool. | 108 tool: instance of runtime error detection tool. |
110 build_type: 'Release' or 'Debug'. | 109 build_type: 'Release' or 'Debug'. |
frankf
2013/05/15 17:24:30
Update the docstring
Philippe
2013/05/15 17:57:41
Done.
| |
111 """ | 110 """ |
112 threading.Thread.__init__(self) | 111 threading.Thread.__init__(self) |
113 self.wait_event = threading.Event() | 112 self.wait_event = threading.Event() |
114 self.stop_flag = False | 113 self.stop_flag = False |
115 self.ready_event = ready_event | 114 self.ready_event = ready_event |
116 self.ready_event.clear() | 115 self.ready_event.clear() |
117 self.arguments = arguments | 116 self.arguments = arguments |
118 self.adb = adb | 117 self.adb = adb |
119 self.tool = tool | 118 self.tool = tool |
120 self.test_server_process = None | 119 self.test_server_process = None |
121 self.is_ready = False | 120 self.is_ready = False |
122 self.host_port = self.arguments['port'] | 121 self.host_port = self.arguments['port'] |
123 assert isinstance(self.host_port, int) | 122 assert isinstance(self.host_port, int) |
124 self._test_server_forwarder = None | 123 self._test_server_forwarder = forwarder |
125 # The forwarder device port now is dynamically allocated. | 124 # The forwarder device port now is dynamically allocated. |
126 self.forwarder_device_port = 0 | 125 self.forwarder_device_port = 0 |
127 # Anonymous pipe in order to get port info from test server. | 126 # Anonymous pipe in order to get port info from test server. |
128 self.pipe_in = None | 127 self.pipe_in = None |
129 self.pipe_out = None | 128 self.pipe_out = None |
130 self.command_line = [] | 129 self.command_line = [] |
131 self.build_type = build_type | 130 self.build_type = build_type |
132 | 131 |
133 def _WaitToStartAndGetPortFromTestServer(self): | 132 def _WaitToStartAndGetPortFromTestServer(self): |
134 """Waits for the Python test server to start and gets the port it is using. | 133 """Waits for the Python test server to start and gets the port it is using. |
(...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
232 command = [os.path.join(command, 'net', 'tools', 'testserver', | 231 command = [os.path.join(command, 'net', 'tools', 'testserver', |
233 'testserver.py')] + self.command_line | 232 'testserver.py')] + self.command_line |
234 logging.info('Running: %s', command) | 233 logging.info('Running: %s', command) |
235 self.process = subprocess.Popen(command) | 234 self.process = subprocess.Popen(command) |
236 if self.process: | 235 if self.process: |
237 if self.pipe_out: | 236 if self.pipe_out: |
238 self.is_ready = self._WaitToStartAndGetPortFromTestServer() | 237 self.is_ready = self._WaitToStartAndGetPortFromTestServer() |
239 else: | 238 else: |
240 self.is_ready = _CheckPortStatus(self.host_port, True) | 239 self.is_ready = _CheckPortStatus(self.host_port, True) |
241 if self.is_ready: | 240 if self.is_ready: |
242 self._test_server_forwarder = Forwarder(self.adb, self.build_type) | |
243 self._test_server_forwarder.Run( | 241 self._test_server_forwarder.Run( |
244 [(0, self.host_port)], self.tool, '127.0.0.1') | 242 [(0, self.host_port)], self.tool, '127.0.0.1') |
245 # Check whether the forwarder is ready on the device. | 243 # Check whether the forwarder is ready on the device. |
246 self.is_ready = False | 244 self.is_ready = False |
247 device_port = self._test_server_forwarder.DevicePortForHostPort( | 245 device_port = self._test_server_forwarder.DevicePortForHostPort( |
248 self.host_port) | 246 self.host_port) |
249 if device_port and _CheckDevicePortStatus(self.adb, device_port): | 247 if device_port and _CheckDevicePortStatus(self.adb, device_port): |
250 self.is_ready = True | 248 self.is_ready = True |
251 self.forwarder_device_port = device_port | 249 self.forwarder_device_port = device_port |
252 # Wake up the request handler thread. | 250 # Wake up the request handler thread. |
253 self.ready_event.set() | 251 self.ready_event.set() |
254 # Keep thread running until Stop() gets called. | 252 # Keep thread running until Stop() gets called. |
255 _WaitUntil(lambda: self.stop_flag, max_attempts=sys.maxint) | 253 _WaitUntil(lambda: self.stop_flag, max_attempts=sys.maxint) |
256 if self.process.poll() is None: | 254 if self.process.poll() is None: |
257 self.process.kill() | 255 self.process.kill() |
258 if self._test_server_forwarder: | 256 self._test_server_forwarder.UnmapDevicePort(self.forwarder_device_port) |
259 self._test_server_forwarder.UnmapDevicePort(self.forwarder_device_port) | |
260 self.process = None | 257 self.process = None |
261 self.is_ready = False | 258 self.is_ready = False |
262 if self.pipe_out: | 259 if self.pipe_out: |
263 os.close(self.pipe_in) | 260 os.close(self.pipe_in) |
264 os.close(self.pipe_out) | 261 os.close(self.pipe_out) |
265 self.pipe_in = None | 262 self.pipe_in = None |
266 self.pipe_out = None | 263 self.pipe_out = None |
267 logging.info('Test-server has died.') | 264 logging.info('Test-server has died.') |
268 self.wait_event.set() | 265 self.wait_event.set() |
269 | 266 |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
319 logging.info(content_length) | 316 logging.info(content_length) |
320 test_server_argument_json = self.rfile.read(content_length) | 317 test_server_argument_json = self.rfile.read(content_length) |
321 logging.info(test_server_argument_json) | 318 logging.info(test_server_argument_json) |
322 assert not self.server.test_server_instance | 319 assert not self.server.test_server_instance |
323 ready_event = threading.Event() | 320 ready_event = threading.Event() |
324 self.server.test_server_instance = TestServerThread( | 321 self.server.test_server_instance = TestServerThread( |
325 ready_event, | 322 ready_event, |
326 json.loads(test_server_argument_json), | 323 json.loads(test_server_argument_json), |
327 self.server.adb, | 324 self.server.adb, |
328 self.server.tool, | 325 self.server.tool, |
326 self.server.forwarder, | |
329 self.server.build_type) | 327 self.server.build_type) |
330 self.server.test_server_instance.setDaemon(True) | 328 self.server.test_server_instance.setDaemon(True) |
331 self.server.test_server_instance.start() | 329 self.server.test_server_instance.start() |
332 ready_event.wait() | 330 ready_event.wait() |
333 if self.server.test_server_instance.is_ready: | 331 if self.server.test_server_instance.is_ready: |
334 self._SendResponse(200, 'OK', {}, json.dumps( | 332 self._SendResponse(200, 'OK', {}, json.dumps( |
335 {'port': self.server.test_server_instance.forwarder_device_port, | 333 {'port': self.server.test_server_instance.forwarder_device_port, |
336 'message': 'started'})) | 334 'message': 'started'})) |
337 logging.info('Test server is running on port: %d.', | 335 logging.info('Test server is running on port: %d.', |
338 self.server.test_server_instance.host_port) | 336 self.server.test_server_instance.host_port) |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
386 self._SendResponse(200, 'OK', {}, 'ready') | 384 self._SendResponse(200, 'OK', {}, 'ready') |
387 logging.info('Handled ping request and sent response.') | 385 logging.info('Handled ping request and sent response.') |
388 else: | 386 else: |
389 self._SendResponse(400, 'Unknown request', {}, '') | 387 self._SendResponse(400, 'Unknown request', {}, '') |
390 logging.info('Encounter unknown request: %s.', action) | 388 logging.info('Encounter unknown request: %s.', action) |
391 | 389 |
392 | 390 |
393 class SpawningServer(object): | 391 class SpawningServer(object): |
394 """The class used to start/stop a http server.""" | 392 """The class used to start/stop a http server.""" |
395 | 393 |
396 def __init__(self, test_server_spawner_port, adb, tool, build_type): | 394 def __init__(self, test_server_spawner_port, adb, tool, forwarder, |
395 build_type): | |
397 logging.info('Creating new spawner on port: %d.', test_server_spawner_port) | 396 logging.info('Creating new spawner on port: %d.', test_server_spawner_port) |
398 self.server = BaseHTTPServer.HTTPServer(('', test_server_spawner_port), | 397 self.server = BaseHTTPServer.HTTPServer(('', test_server_spawner_port), |
399 SpawningServerRequestHandler) | 398 SpawningServerRequestHandler) |
400 self.port = test_server_spawner_port | |
401 self.server.adb = adb | 399 self.server.adb = adb |
402 self.server.tool = tool | 400 self.server.tool = tool |
401 self.server.forwarder = forwarder | |
403 self.server.test_server_instance = None | 402 self.server.test_server_instance = None |
404 self.server.build_type = build_type | 403 self.server.build_type = build_type |
405 | 404 |
406 def _Listen(self): | 405 def _Listen(self): |
407 logging.info('Starting test server spawner') | 406 logging.info('Starting test server spawner') |
408 self.server.serve_forever() | 407 self.server.serve_forever() |
409 | 408 |
410 def Start(self): | 409 def Start(self): |
411 """Starts the test server spawner.""" | 410 """Starts the test server spawner.""" |
412 listener_thread = threading.Thread(target=self._Listen) | 411 listener_thread = threading.Thread(target=self._Listen) |
(...skipping 10 matching lines...) Expand all Loading... | |
423 | 422 |
424 def CleanupState(self): | 423 def CleanupState(self): |
425 """Cleans up the spawning server state. | 424 """Cleans up the spawning server state. |
426 | 425 |
427 This should be called if the test server spawner is reused, | 426 This should be called if the test server spawner is reused, |
428 to avoid sharing the test server instance. | 427 to avoid sharing the test server instance. |
429 """ | 428 """ |
430 if self.server.test_server_instance: | 429 if self.server.test_server_instance: |
431 self.server.test_server_instance.Stop() | 430 self.server.test_server_instance.Stop() |
432 self.server.test_server_instance = None | 431 self.server.test_server_instance = None |
OLD | NEW |