| OLD | NEW |
| (Empty) |
| 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 | |
| 3 # found in the LICENSE file. | |
| 4 | |
| 5 """Host driven test server controller. | |
| 6 | |
| 7 This class controls the startup and shutdown of a python driven test server that | |
| 8 runs in a separate process. | |
| 9 | |
| 10 The server starts up automatically when the object is created. | |
| 11 | |
| 12 After it starts up, it is possible to retreive the hostname it started on | |
| 13 through accessing the member field |host| and the port name through |port|. | |
| 14 | |
| 15 For shutting down the server, call TearDown(). | |
| 16 """ | |
| 17 | |
| 18 import logging | |
| 19 import subprocess | |
| 20 import os | |
| 21 import os.path | |
| 22 import time | |
| 23 import urllib2 | |
| 24 | |
| 25 from pylib import constants | |
| 26 from pylib.constants import host_paths | |
| 27 | |
| 28 # NOTE: when adding or modifying these lines, omit any leading slashes! | |
| 29 # Otherwise os.path.join() will (correctly) treat them as absolute paths | |
| 30 # instead of relative paths, and will do nothing. | |
| 31 _PYTHONPATH_DIRS = [ | |
| 32 'net/tools/testserver/', | |
| 33 'third_party/', | |
| 34 'third_party/pyftpdlib/src/', | |
| 35 'third_party/pywebsocket/src', | |
| 36 'third_party/tlslite/', | |
| 37 ] | |
| 38 | |
| 39 # Python files in these directories are generated as part of the build. | |
| 40 # These dirs are located in out/(Debug|Release) directory. | |
| 41 # The correct path is determined based on the build type. E.g. out/Debug for | |
| 42 # debug builds and out/Release for release builds. | |
| 43 _GENERATED_PYTHONPATH_DIRS = [ | |
| 44 'pyproto/policy/proto/', | |
| 45 'pyproto/sync/protocol/', | |
| 46 'pyproto/' | |
| 47 ] | |
| 48 | |
| 49 _TEST_SERVER_HOST = '127.0.0.1' | |
| 50 # Paths for supported test server executables. | |
| 51 TEST_NET_SERVER_PATH = 'net/tools/testserver/testserver.py' | |
| 52 TEST_SYNC_SERVER_PATH = 'sync/tools/testserver/sync_testserver.py' | |
| 53 TEST_POLICY_SERVER_PATH = 'chrome/browser/policy/test/policy_testserver.py' | |
| 54 # Parameters to check that the server is up and running. | |
| 55 TEST_SERVER_CHECK_PARAMS = { | |
| 56 TEST_NET_SERVER_PATH: { | |
| 57 'url_path': '/', | |
| 58 'response': 'Default response given for path' | |
| 59 }, | |
| 60 TEST_SYNC_SERVER_PATH: { | |
| 61 'url_path': 'chromiumsync/time', | |
| 62 'response': '0123456789' | |
| 63 }, | |
| 64 TEST_POLICY_SERVER_PATH: { | |
| 65 'url_path': 'test/ping', | |
| 66 'response': 'Policy server is up.' | |
| 67 }, | |
| 68 } | |
| 69 | |
| 70 class TestServer(object): | |
| 71 """Sets up a host driven test server on the host machine. | |
| 72 | |
| 73 For shutting down the server, call TearDown(). | |
| 74 """ | |
| 75 | |
| 76 def __init__(self, shard_index, test_server_port, test_server_path, | |
| 77 test_server_flags=None): | |
| 78 """Sets up a Python driven test server on the host machine. | |
| 79 | |
| 80 Args: | |
| 81 shard_index: Index of the current shard. | |
| 82 test_server_port: Port to run the test server on. This is multiplexed with | |
| 83 the shard index. To retrieve the real port access the | |
| 84 member variable |port|. | |
| 85 test_server_path: The path (relative to the root src dir) of the server | |
| 86 test_server_flags: Optional list of additional flags to the test server | |
| 87 """ | |
| 88 self.host = _TEST_SERVER_HOST | |
| 89 self.port = test_server_port + shard_index | |
| 90 | |
| 91 src_dir = host_paths.DIR_SOURCE_ROOT | |
| 92 # Make dirs into a list of absolute paths. | |
| 93 abs_dirs = [os.path.join(src_dir, d) for d in _PYTHONPATH_DIRS] | |
| 94 # Add the generated python files to the path | |
| 95 abs_dirs.extend([os.path.join(src_dir, constants.GetOutDirectory(), d) | |
| 96 for d in _GENERATED_PYTHONPATH_DIRS]) | |
| 97 current_python_path = os.environ.get('PYTHONPATH') | |
| 98 extra_python_path = ':'.join(abs_dirs) | |
| 99 if current_python_path: | |
| 100 python_path = current_python_path + ':' + extra_python_path | |
| 101 else: | |
| 102 python_path = extra_python_path | |
| 103 | |
| 104 # NOTE: A separate python process is used to simplify getting the right | |
| 105 # system path for finding includes. | |
| 106 test_server_flags = test_server_flags or [] | |
| 107 cmd = ['python', os.path.join(src_dir, test_server_path), | |
| 108 '--log-to-console', | |
| 109 ('--host=%s' % self.host), | |
| 110 ('--port=%d' % self.port), | |
| 111 '--on-remote-server'] + test_server_flags | |
| 112 self._test_server_process = subprocess.Popen( | |
| 113 cmd, env={'PYTHONPATH': python_path}) | |
| 114 test_url = 'http://%s:%d/%s' % (self.host, self.port, | |
| 115 TEST_SERVER_CHECK_PARAMS[test_server_path]['url_path']) | |
| 116 expected_response = TEST_SERVER_CHECK_PARAMS[test_server_path]['response'] | |
| 117 retries = 0 | |
| 118 while retries < 5: | |
| 119 try: | |
| 120 d = urllib2.urlopen(test_url).read() | |
| 121 logging.info('URL %s GOT: %s', test_url, d) | |
| 122 if d.startswith(expected_response): | |
| 123 break | |
| 124 except Exception as e: # pylint: disable=broad-except | |
| 125 logging.info('URL %s GOT: %s', test_url, e) | |
| 126 time.sleep(retries * 0.1) | |
| 127 retries += 1 | |
| 128 | |
| 129 def TearDown(self): | |
| 130 self._test_server_process.kill() | |
| 131 self._test_server_process.wait() | |
| OLD | NEW |