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

Unified Diff: build/android/pylib/chrome_test_server_spawner.py

Issue 2392643003: Removes files from //build that we don't need (Closed)
Patch Set: Created 4 years, 2 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « build/android/pylib/base/test_server.py ('k') | build/android/pylib/cmd_helper.py » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: build/android/pylib/chrome_test_server_spawner.py
diff --git a/build/android/pylib/chrome_test_server_spawner.py b/build/android/pylib/chrome_test_server_spawner.py
deleted file mode 100644
index 052c2fde78ea6516ab15d759a492bf957a28e0d1..0000000000000000000000000000000000000000
--- a/build/android/pylib/chrome_test_server_spawner.py
+++ /dev/null
@@ -1,422 +0,0 @@
-# Copyright 2013 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-"""A "Test Server Spawner" that handles killing/stopping per-test test servers.
-
-It's used to accept requests from the device to spawn and kill instances of the
-chrome test server on the host.
-"""
-# pylint: disable=W0702
-
-import BaseHTTPServer
-import json
-import logging
-import os
-import select
-import struct
-import subprocess
-import sys
-import threading
-import time
-import urlparse
-
-from pylib import constants
-from pylib import ports
-
-from pylib.forwarder import Forwarder
-
-
-# Path that are needed to import necessary modules when launching a testserver.
-os.environ['PYTHONPATH'] = os.environ.get('PYTHONPATH', '') + (':%s:%s:%s:%s:%s'
- % (os.path.join(constants.DIR_SOURCE_ROOT, 'third_party'),
- os.path.join(constants.DIR_SOURCE_ROOT, 'third_party', 'tlslite'),
- os.path.join(constants.DIR_SOURCE_ROOT, 'third_party', 'pyftpdlib',
- 'src'),
- os.path.join(constants.DIR_SOURCE_ROOT, 'net', 'tools', 'testserver'),
- os.path.join(constants.DIR_SOURCE_ROOT, 'sync', 'tools', 'testserver')))
-
-
-SERVER_TYPES = {
- 'http': '',
- 'ftp': '-f',
- 'sync': '', # Sync uses its own script, and doesn't take a server type arg.
- 'tcpecho': '--tcp-echo',
- 'udpecho': '--udp-echo',
-}
-
-
-# The timeout (in seconds) of starting up the Python test server.
-TEST_SERVER_STARTUP_TIMEOUT = 10
-
-def _WaitUntil(predicate, max_attempts=5):
- """Blocks until the provided predicate (function) is true.
-
- Returns:
- Whether the provided predicate was satisfied once (before the timeout).
- """
- sleep_time_sec = 0.025
- for _ in xrange(1, max_attempts):
- if predicate():
- return True
- time.sleep(sleep_time_sec)
- sleep_time_sec = min(1, sleep_time_sec * 2) # Don't wait more than 1 sec.
- return False
-
-
-def _CheckPortAvailable(port):
- """Returns True if |port| is available."""
- return _WaitUntil(lambda: ports.IsHostPortAvailable(port))
-
-
-def _CheckPortNotAvailable(port):
- """Returns True if |port| is not available."""
- return _WaitUntil(lambda: not ports.IsHostPortAvailable(port))
-
-
-def _CheckDevicePortStatus(device, port):
- """Returns whether the provided port is used."""
- return _WaitUntil(lambda: ports.IsDevicePortUsed(device, port))
-
-
-def _GetServerTypeCommandLine(server_type):
- """Returns the command-line by the given server type.
-
- Args:
- server_type: the server type to be used (e.g. 'http').
-
- Returns:
- A string containing the command-line argument.
- """
- if server_type not in SERVER_TYPES:
- raise NotImplementedError('Unknown server type: %s' % server_type)
- if server_type == 'udpecho':
- raise Exception('Please do not run UDP echo tests because we do not have '
- 'a UDP forwarder tool.')
- return SERVER_TYPES[server_type]
-
-
-class TestServerThread(threading.Thread):
- """A thread to run the test server in a separate process."""
-
- def __init__(self, ready_event, arguments, device, tool):
- """Initialize TestServerThread with the following argument.
-
- Args:
- ready_event: event which will be set when the test server is ready.
- arguments: dictionary of arguments to run the test server.
- device: An instance of DeviceUtils.
- tool: instance of runtime error detection tool.
- """
- threading.Thread.__init__(self)
- self.wait_event = threading.Event()
- self.stop_flag = False
- self.ready_event = ready_event
- self.ready_event.clear()
- self.arguments = arguments
- self.device = device
- self.tool = tool
- self.test_server_process = None
- self.is_ready = False
- self.host_port = self.arguments['port']
- assert isinstance(self.host_port, int)
- # The forwarder device port now is dynamically allocated.
- self.forwarder_device_port = 0
- # Anonymous pipe in order to get port info from test server.
- self.pipe_in = None
- self.pipe_out = None
- self.process = None
- self.command_line = []
-
- def _WaitToStartAndGetPortFromTestServer(self):
- """Waits for the Python test server to start and gets the port it is using.
-
- The port information is passed by the Python test server with a pipe given
- by self.pipe_out. It is written as a result to |self.host_port|.
-
- Returns:
- Whether the port used by the test server was successfully fetched.
- """
- assert self.host_port == 0 and self.pipe_out and self.pipe_in
- (in_fds, _, _) = select.select([self.pipe_in, ], [], [],
- TEST_SERVER_STARTUP_TIMEOUT)
- if len(in_fds) == 0:
- logging.error('Failed to wait to the Python test server to be started.')
- return False
- # First read the data length as an unsigned 4-byte value. This
- # is _not_ using network byte ordering since the Python test server packs
- # size as native byte order and all Chromium platforms so far are
- # configured to use little-endian.
- # TODO(jnd): Change the Python test server and local_test_server_*.cc to
- # use a unified byte order (either big-endian or little-endian).
- data_length = os.read(self.pipe_in, struct.calcsize('=L'))
- if data_length:
- (data_length,) = struct.unpack('=L', data_length)
- assert data_length
- if not data_length:
- logging.error('Failed to get length of server data.')
- return False
- port_json = os.read(self.pipe_in, data_length)
- if not port_json:
- logging.error('Failed to get server data.')
- return False
- logging.info('Got port json data: %s', port_json)
- port_json = json.loads(port_json)
- if port_json.has_key('port') and isinstance(port_json['port'], int):
- self.host_port = port_json['port']
- return _CheckPortNotAvailable(self.host_port)
- logging.error('Failed to get port information from the server data.')
- return False
-
- def _GenerateCommandLineArguments(self):
- """Generates the command line to run the test server.
-
- Note that all options are processed by following the definitions in
- testserver.py.
- """
- if self.command_line:
- return
-
- args_copy = dict(self.arguments)
-
- # Translate the server type.
- type_cmd = _GetServerTypeCommandLine(args_copy.pop('server-type'))
- if type_cmd:
- self.command_line.append(type_cmd)
-
- # Use a pipe to get the port given by the instance of Python test server
- # if the test does not specify the port.
- assert self.host_port == args_copy['port']
- if self.host_port == 0:
- (self.pipe_in, self.pipe_out) = os.pipe()
- self.command_line.append('--startup-pipe=%d' % self.pipe_out)
-
- # Pass the remaining arguments as-is.
- for key, values in args_copy.iteritems():
- if not isinstance(values, list):
- values = [values]
- for value in values:
- if value is None:
- self.command_line.append('--%s' % key)
- else:
- self.command_line.append('--%s=%s' % (key, value))
-
- def _CloseUnnecessaryFDsForTestServerProcess(self):
- # This is required to avoid subtle deadlocks that could be caused by the
- # test server child process inheriting undesirable file descriptors such as
- # file lock file descriptors.
- for fd in xrange(0, 1024):
- if fd != self.pipe_out:
- try:
- os.close(fd)
- except:
- pass
-
- def run(self):
- logging.info('Start running the thread!')
- self.wait_event.clear()
- self._GenerateCommandLineArguments()
- command = constants.DIR_SOURCE_ROOT
- if self.arguments['server-type'] == 'sync':
- command = [os.path.join(command, 'sync', 'tools', 'testserver',
- 'sync_testserver.py')] + self.command_line
- else:
- command = [os.path.join(command, 'net', 'tools', 'testserver',
- 'testserver.py')] + self.command_line
- logging.info('Running: %s', command)
- # Pass DIR_SOURCE_ROOT as the child's working directory so that relative
- # paths in the arguments are resolved correctly.
- self.process = subprocess.Popen(
- command, preexec_fn=self._CloseUnnecessaryFDsForTestServerProcess,
- cwd=constants.DIR_SOURCE_ROOT)
- if self.process:
- if self.pipe_out:
- self.is_ready = self._WaitToStartAndGetPortFromTestServer()
- else:
- self.is_ready = _CheckPortNotAvailable(self.host_port)
- if self.is_ready:
- Forwarder.Map([(0, self.host_port)], self.device, self.tool)
- # Check whether the forwarder is ready on the device.
- self.is_ready = False
- device_port = Forwarder.DevicePortForHostPort(self.host_port)
- if device_port and _CheckDevicePortStatus(self.device, device_port):
- self.is_ready = True
- self.forwarder_device_port = device_port
- # Wake up the request handler thread.
- self.ready_event.set()
- # Keep thread running until Stop() gets called.
- _WaitUntil(lambda: self.stop_flag, max_attempts=sys.maxint)
- if self.process.poll() is None:
- self.process.kill()
- Forwarder.UnmapDevicePort(self.forwarder_device_port, self.device)
- self.process = None
- self.is_ready = False
- if self.pipe_out:
- os.close(self.pipe_in)
- os.close(self.pipe_out)
- self.pipe_in = None
- self.pipe_out = None
- logging.info('Test-server has died.')
- self.wait_event.set()
-
- def Stop(self):
- """Blocks until the loop has finished.
-
- Note that this must be called in another thread.
- """
- if not self.process:
- return
- self.stop_flag = True
- self.wait_event.wait()
-
-
-class SpawningServerRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
- """A handler used to process http GET/POST request."""
-
- def _SendResponse(self, response_code, response_reason, additional_headers,
- contents):
- """Generates a response sent to the client from the provided parameters.
-
- Args:
- response_code: number of the response status.
- response_reason: string of reason description of the response.
- additional_headers: dict of additional headers. Each key is the name of
- the header, each value is the content of the header.
- contents: string of the contents we want to send to client.
- """
- self.send_response(response_code, response_reason)
- self.send_header('Content-Type', 'text/html')
- # Specify the content-length as without it the http(s) response will not
- # be completed properly (and the browser keeps expecting data).
- self.send_header('Content-Length', len(contents))
- for header_name in additional_headers:
- self.send_header(header_name, additional_headers[header_name])
- self.end_headers()
- self.wfile.write(contents)
- self.wfile.flush()
-
- def _StartTestServer(self):
- """Starts the test server thread."""
- logging.info('Handling request to spawn a test server.')
- content_type = self.headers.getheader('content-type')
- if content_type != 'application/json':
- raise Exception('Bad content-type for start request.')
- content_length = self.headers.getheader('content-length')
- if not content_length:
- content_length = 0
- try:
- content_length = int(content_length)
- except:
- raise Exception('Bad content-length for start request.')
- logging.info(content_length)
- test_server_argument_json = self.rfile.read(content_length)
- logging.info(test_server_argument_json)
- assert not self.server.test_server_instance
- ready_event = threading.Event()
- self.server.test_server_instance = TestServerThread(
- ready_event,
- json.loads(test_server_argument_json),
- self.server.device,
- self.server.tool)
- self.server.test_server_instance.setDaemon(True)
- self.server.test_server_instance.start()
- ready_event.wait()
- if self.server.test_server_instance.is_ready:
- self._SendResponse(200, 'OK', {}, json.dumps(
- {'port': self.server.test_server_instance.forwarder_device_port,
- 'message': 'started'}))
- logging.info('Test server is running on port: %d.',
- self.server.test_server_instance.host_port)
- else:
- self.server.test_server_instance.Stop()
- self.server.test_server_instance = None
- self._SendResponse(500, 'Test Server Error.', {}, '')
- logging.info('Encounter problem during starting a test server.')
-
- def _KillTestServer(self):
- """Stops the test server instance."""
- # There should only ever be one test server at a time. This may do the
- # wrong thing if we try and start multiple test servers.
- if not self.server.test_server_instance:
- return
- port = self.server.test_server_instance.host_port
- logging.info('Handling request to kill a test server on port: %d.', port)
- self.server.test_server_instance.Stop()
- # Make sure the status of test server is correct before sending response.
- if _CheckPortAvailable(port):
- self._SendResponse(200, 'OK', {}, 'killed')
- logging.info('Test server on port %d is killed', port)
- else:
- self._SendResponse(500, 'Test Server Error.', {}, '')
- logging.info('Encounter problem during killing a test server.')
- self.server.test_server_instance = None
-
- def do_POST(self):
- parsed_path = urlparse.urlparse(self.path)
- action = parsed_path.path
- logging.info('Action for POST method is: %s.', action)
- if action == '/start':
- self._StartTestServer()
- else:
- self._SendResponse(400, 'Unknown request.', {}, '')
- logging.info('Encounter unknown request: %s.', action)
-
- def do_GET(self):
- parsed_path = urlparse.urlparse(self.path)
- action = parsed_path.path
- params = urlparse.parse_qs(parsed_path.query, keep_blank_values=1)
- logging.info('Action for GET method is: %s.', action)
- for param in params:
- logging.info('%s=%s', param, params[param][0])
- if action == '/kill':
- self._KillTestServer()
- elif action == '/ping':
- # The ping handler is used to check whether the spawner server is ready
- # to serve the requests. We don't need to test the status of the test
- # server when handling ping request.
- self._SendResponse(200, 'OK', {}, 'ready')
- logging.info('Handled ping request and sent response.')
- else:
- self._SendResponse(400, 'Unknown request', {}, '')
- logging.info('Encounter unknown request: %s.', action)
-
-
-class SpawningServer(object):
- """The class used to start/stop a http server."""
-
- def __init__(self, test_server_spawner_port, device, tool):
- logging.info('Creating new spawner on port: %d.', test_server_spawner_port)
- self.server = BaseHTTPServer.HTTPServer(('', test_server_spawner_port),
- SpawningServerRequestHandler)
- self.server.device = device
- self.server.tool = tool
- self.server.test_server_instance = None
- self.server.build_type = constants.GetBuildType()
-
- def _Listen(self):
- logging.info('Starting test server spawner')
- self.server.serve_forever()
-
- def Start(self):
- """Starts the test server spawner."""
- listener_thread = threading.Thread(target=self._Listen)
- listener_thread.setDaemon(True)
- listener_thread.start()
-
- def Stop(self):
- """Stops the test server spawner.
-
- Also cleans the server state.
- """
- self.CleanupState()
- self.server.shutdown()
-
- def CleanupState(self):
- """Cleans up the spawning server state.
-
- This should be called if the test server spawner is reused,
- to avoid sharing the test server instance.
- """
- if self.server.test_server_instance:
- self.server.test_server_instance.Stop()
- self.server.test_server_instance = None
« no previous file with comments | « build/android/pylib/base/test_server.py ('k') | build/android/pylib/cmd_helper.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698