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

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

Issue 15151003: Use a single Python Forwarder instance for each base test runner instance. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 7 years, 7 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
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 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
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
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
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698