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