Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(11)

Side by Side Diff: build/android/pylib/chrome_test_server_spawner.py

Issue 20824008: Reland r212020: Move Python setup/tear down logic into Forwarder ... (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Close unused FDs Created 7 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « build/android/pylib/base/test_dispatcher.py ('k') | build/android/pylib/forwarder.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 import ports 24 import ports
25 25
26 from pylib.forwarder import Forwarder
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.
28 os.environ['PYTHONPATH'] = os.environ.get('PYTHONPATH', '') + (':%s:%s:%s:%s:%s' 29 os.environ['PYTHONPATH'] = os.environ.get('PYTHONPATH', '') + (':%s:%s:%s:%s:%s'
29 % (os.path.join(constants.DIR_SOURCE_ROOT, 'third_party'), 30 % (os.path.join(constants.DIR_SOURCE_ROOT, 'third_party'),
30 os.path.join(constants.DIR_SOURCE_ROOT, 'third_party', 'tlslite'), 31 os.path.join(constants.DIR_SOURCE_ROOT, 'third_party', 'tlslite'),
31 os.path.join(constants.DIR_SOURCE_ROOT, 'third_party', 'pyftpdlib', 32 os.path.join(constants.DIR_SOURCE_ROOT, 'third_party', 'pyftpdlib',
32 'src'), 33 'src'),
33 os.path.join(constants.DIR_SOURCE_ROOT, 'net', 'tools', 'testserver'), 34 os.path.join(constants.DIR_SOURCE_ROOT, 'net', 'tools', 'testserver'),
34 os.path.join(constants.DIR_SOURCE_ROOT, 'sync', 'tools', 'testserver'))) 35 os.path.join(constants.DIR_SOURCE_ROOT, 'sync', 'tools', 'testserver')))
35 36
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after
92 raise NotImplementedError('Unknown server type: %s' % server_type) 93 raise NotImplementedError('Unknown server type: %s' % server_type)
93 if server_type == 'udpecho': 94 if server_type == 'udpecho':
94 raise Exception('Please do not run UDP echo tests because we do not have ' 95 raise Exception('Please do not run UDP echo tests because we do not have '
95 'a UDP forwarder tool.') 96 'a UDP forwarder tool.')
96 return SERVER_TYPES[server_type] 97 return SERVER_TYPES[server_type]
97 98
98 99
99 class TestServerThread(threading.Thread): 100 class TestServerThread(threading.Thread):
100 """A thread to run the test server in a separate process.""" 101 """A thread to run the test server in a separate process."""
101 102
102 def __init__(self, ready_event, arguments, adb, tool, forwarder, build_type): 103 def __init__(self, ready_event, arguments, adb, tool, build_type):
103 """Initialize TestServerThread with the following argument. 104 """Initialize TestServerThread with the following argument.
104 105
105 Args: 106 Args:
106 ready_event: event which will be set when the test server is ready. 107 ready_event: event which will be set when the test server is ready.
107 arguments: dictionary of arguments to run the test server. 108 arguments: dictionary of arguments to run the test server.
108 adb: instance of AndroidCommands. 109 adb: instance of AndroidCommands.
109 tool: instance of runtime error detection tool. 110 tool: instance of runtime error detection tool.
110 forwarder: instance of Forwarder.
111 build_type: 'Release' or 'Debug'. 111 build_type: 'Release' or 'Debug'.
112 """ 112 """
113 threading.Thread.__init__(self) 113 threading.Thread.__init__(self)
114 self.wait_event = threading.Event() 114 self.wait_event = threading.Event()
115 self.stop_flag = False 115 self.stop_flag = False
116 self.ready_event = ready_event 116 self.ready_event = ready_event
117 self.ready_event.clear() 117 self.ready_event.clear()
118 self.arguments = arguments 118 self.arguments = arguments
119 self.adb = adb 119 self.adb = adb
120 self.tool = tool 120 self.tool = tool
121 self.test_server_process = None 121 self.test_server_process = None
122 self.is_ready = False 122 self.is_ready = False
123 self.host_port = self.arguments['port'] 123 self.host_port = self.arguments['port']
124 assert isinstance(self.host_port, int) 124 assert isinstance(self.host_port, int)
125 self._test_server_forwarder = forwarder
126 # The forwarder device port now is dynamically allocated. 125 # The forwarder device port now is dynamically allocated.
127 self.forwarder_device_port = 0 126 self.forwarder_device_port = 0
128 # Anonymous pipe in order to get port info from test server. 127 # Anonymous pipe in order to get port info from test server.
129 self.pipe_in = None 128 self.pipe_in = None
130 self.pipe_out = None 129 self.pipe_out = None
131 self.command_line = [] 130 self.command_line = []
132 self.build_type = build_type 131 self.build_type = build_type
133 132
134 def _WaitToStartAndGetPortFromTestServer(self): 133 def _WaitToStartAndGetPortFromTestServer(self):
135 """Waits for the Python test server to start and gets the port it is using. 134 """Waits for the Python test server to start and gets the port it is using.
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after
214 self.command_line.append('--tls-intolerant=%s' % 213 self.command_line.append('--tls-intolerant=%s' %
215 self.arguments['tls-intolerant']) 214 self.arguments['tls-intolerant'])
216 if self.arguments.has_key('ssl-client-ca'): 215 if self.arguments.has_key('ssl-client-ca'):
217 for ca in self.arguments['ssl-client-ca']: 216 for ca in self.arguments['ssl-client-ca']:
218 self.command_line.append('--ssl-client-ca=%s' % 217 self.command_line.append('--ssl-client-ca=%s' %
219 os.path.join(constants.DIR_SOURCE_ROOT, ca)) 218 os.path.join(constants.DIR_SOURCE_ROOT, ca))
220 if self.arguments.has_key('ssl-bulk-cipher'): 219 if self.arguments.has_key('ssl-bulk-cipher'):
221 for bulk_cipher in self.arguments['ssl-bulk-cipher']: 220 for bulk_cipher in self.arguments['ssl-bulk-cipher']:
222 self.command_line.append('--ssl-bulk-cipher=%s' % bulk_cipher) 221 self.command_line.append('--ssl-bulk-cipher=%s' % bulk_cipher)
223 222
223 def _CloseUnnecessaryFDsForTestServerProcess(self):
224 # This is required to avoid subtle deadlocks that could be caused by the
225 # test server child process inheriting undesirable file descriptors such as
226 # file lock file descriptors.
227 for fd in xrange(0, 1024):
228 if fd != self.pipe_out:
229 try:
230 os.close(fd)
231 except:
232 pass
233
224 def run(self): 234 def run(self):
225 logging.info('Start running the thread!') 235 logging.info('Start running the thread!')
226 self.wait_event.clear() 236 self.wait_event.clear()
227 self._GenerateCommandLineArguments() 237 self._GenerateCommandLineArguments()
228 command = constants.DIR_SOURCE_ROOT 238 command = constants.DIR_SOURCE_ROOT
229 if self.arguments['server-type'] == 'sync': 239 if self.arguments['server-type'] == 'sync':
230 command = [os.path.join(command, 'sync', 'tools', 'testserver', 240 command = [os.path.join(command, 'sync', 'tools', 'testserver',
231 'sync_testserver.py')] + self.command_line 241 'sync_testserver.py')] + self.command_line
232 else: 242 else:
233 command = [os.path.join(command, 'net', 'tools', 'testserver', 243 command = [os.path.join(command, 'net', 'tools', 'testserver',
234 'testserver.py')] + self.command_line 244 'testserver.py')] + self.command_line
235 logging.info('Running: %s', command) 245 logging.info('Running: %s', command)
236 self.process = subprocess.Popen(command) 246 self.process = subprocess.Popen(
247 command, preexec_fn=self._CloseUnnecessaryFDsForTestServerProcess)
237 if self.process: 248 if self.process:
238 if self.pipe_out: 249 if self.pipe_out:
239 self.is_ready = self._WaitToStartAndGetPortFromTestServer() 250 self.is_ready = self._WaitToStartAndGetPortFromTestServer()
240 else: 251 else:
241 self.is_ready = _CheckPortStatus(self.host_port, True) 252 self.is_ready = _CheckPortStatus(self.host_port, True)
242 if self.is_ready: 253 if self.is_ready:
243 self._test_server_forwarder.Run([(0, self.host_port)], self.tool) 254 Forwarder.Map([(0, self.host_port)], self.adb, self.build_type, self.tool)
244 # Check whether the forwarder is ready on the device. 255 # Check whether the forwarder is ready on the device.
245 self.is_ready = False 256 self.is_ready = False
246 device_port = self._test_server_forwarder.DevicePortForHostPort( 257 device_port = Forwarder.DevicePortForHostPort(self.host_port)
247 self.host_port)
248 if device_port and _CheckDevicePortStatus(self.adb, device_port): 258 if device_port and _CheckDevicePortStatus(self.adb, device_port):
249 self.is_ready = True 259 self.is_ready = True
250 self.forwarder_device_port = device_port 260 self.forwarder_device_port = device_port
251 # Wake up the request handler thread. 261 # Wake up the request handler thread.
252 self.ready_event.set() 262 self.ready_event.set()
253 # Keep thread running until Stop() gets called. 263 # Keep thread running until Stop() gets called.
254 _WaitUntil(lambda: self.stop_flag, max_attempts=sys.maxint) 264 _WaitUntil(lambda: self.stop_flag, max_attempts=sys.maxint)
255 if self.process.poll() is None: 265 if self.process.poll() is None:
256 self.process.kill() 266 self.process.kill()
257 self._test_server_forwarder.UnmapDevicePort(self.forwarder_device_port) 267 Forwarder.UnmapDevicePort(self.forwarder_device_port, self.adb)
258 self.process = None 268 self.process = None
259 self.is_ready = False 269 self.is_ready = False
260 if self.pipe_out: 270 if self.pipe_out:
261 os.close(self.pipe_in) 271 os.close(self.pipe_in)
262 os.close(self.pipe_out) 272 os.close(self.pipe_out)
263 self.pipe_in = None 273 self.pipe_in = None
264 self.pipe_out = None 274 self.pipe_out = None
265 logging.info('Test-server has died.') 275 logging.info('Test-server has died.')
266 self.wait_event.set() 276 self.wait_event.set()
267 277
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after
317 logging.info(content_length) 327 logging.info(content_length)
318 test_server_argument_json = self.rfile.read(content_length) 328 test_server_argument_json = self.rfile.read(content_length)
319 logging.info(test_server_argument_json) 329 logging.info(test_server_argument_json)
320 assert not self.server.test_server_instance 330 assert not self.server.test_server_instance
321 ready_event = threading.Event() 331 ready_event = threading.Event()
322 self.server.test_server_instance = TestServerThread( 332 self.server.test_server_instance = TestServerThread(
323 ready_event, 333 ready_event,
324 json.loads(test_server_argument_json), 334 json.loads(test_server_argument_json),
325 self.server.adb, 335 self.server.adb,
326 self.server.tool, 336 self.server.tool,
327 self.server.forwarder,
328 self.server.build_type) 337 self.server.build_type)
329 self.server.test_server_instance.setDaemon(True) 338 self.server.test_server_instance.setDaemon(True)
330 self.server.test_server_instance.start() 339 self.server.test_server_instance.start()
331 ready_event.wait() 340 ready_event.wait()
332 if self.server.test_server_instance.is_ready: 341 if self.server.test_server_instance.is_ready:
333 self._SendResponse(200, 'OK', {}, json.dumps( 342 self._SendResponse(200, 'OK', {}, json.dumps(
334 {'port': self.server.test_server_instance.forwarder_device_port, 343 {'port': self.server.test_server_instance.forwarder_device_port,
335 'message': 'started'})) 344 'message': 'started'}))
336 logging.info('Test server is running on port: %d.', 345 logging.info('Test server is running on port: %d.',
337 self.server.test_server_instance.host_port) 346 self.server.test_server_instance.host_port)
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
385 self._SendResponse(200, 'OK', {}, 'ready') 394 self._SendResponse(200, 'OK', {}, 'ready')
386 logging.info('Handled ping request and sent response.') 395 logging.info('Handled ping request and sent response.')
387 else: 396 else:
388 self._SendResponse(400, 'Unknown request', {}, '') 397 self._SendResponse(400, 'Unknown request', {}, '')
389 logging.info('Encounter unknown request: %s.', action) 398 logging.info('Encounter unknown request: %s.', action)
390 399
391 400
392 class SpawningServer(object): 401 class SpawningServer(object):
393 """The class used to start/stop a http server.""" 402 """The class used to start/stop a http server."""
394 403
395 def __init__(self, test_server_spawner_port, adb, tool, forwarder, 404 def __init__(self, test_server_spawner_port, adb, tool, build_type):
396 build_type):
397 logging.info('Creating new spawner on port: %d.', test_server_spawner_port) 405 logging.info('Creating new spawner on port: %d.', test_server_spawner_port)
398 self.server = BaseHTTPServer.HTTPServer(('', test_server_spawner_port), 406 self.server = BaseHTTPServer.HTTPServer(('', test_server_spawner_port),
399 SpawningServerRequestHandler) 407 SpawningServerRequestHandler)
400 self.server.adb = adb 408 self.server.adb = adb
401 self.server.tool = tool 409 self.server.tool = tool
402 self.server.forwarder = forwarder
403 self.server.test_server_instance = None 410 self.server.test_server_instance = None
404 self.server.build_type = build_type 411 self.server.build_type = build_type
405 412
406 def _Listen(self): 413 def _Listen(self):
407 logging.info('Starting test server spawner') 414 logging.info('Starting test server spawner')
408 self.server.serve_forever() 415 self.server.serve_forever()
409 416
410 def Start(self): 417 def Start(self):
411 """Starts the test server spawner.""" 418 """Starts the test server spawner."""
412 listener_thread = threading.Thread(target=self._Listen) 419 listener_thread = threading.Thread(target=self._Listen)
(...skipping 10 matching lines...) Expand all
423 430
424 def CleanupState(self): 431 def CleanupState(self):
425 """Cleans up the spawning server state. 432 """Cleans up the spawning server state.
426 433
427 This should be called if the test server spawner is reused, 434 This should be called if the test server spawner is reused,
428 to avoid sharing the test server instance. 435 to avoid sharing the test server instance.
429 """ 436 """
430 if self.server.test_server_instance: 437 if self.server.test_server_instance:
431 self.server.test_server_instance.Stop() 438 self.server.test_server_instance.Stop()
432 self.server.test_server_instance = None 439 self.server.test_server_instance = None
OLDNEW
« no previous file with comments | « build/android/pylib/base/test_dispatcher.py ('k') | build/android/pylib/forwarder.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698