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 7This class controls the startup and shutdown of a python driven test server that 8runs in a separate process. 9 10The server starts up automatically when the object is created. 11 12After it starts up, it is possible to retreive the hostname it started on 13through accessing the member field |host| and the port name through |port|. 14 15For shutting down the server, call TearDown(). 16""" 17 18import logging 19import subprocess 20import os 21import os.path 22import time 23import urllib2 24 25from 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. 49TEST_NET_SERVER_PATH = 'net/tools/testserver/testserver.py' 50TEST_SYNC_SERVER_PATH = 'sync/tools/testserver/sync_testserver.py' 51# Parameters to check that the server is up and running. 52TEST_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 63class 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