| OLD | NEW |
| (Empty) |
| 1 # Copyright (c) 2012 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 """Constrained network server (CNS) test base.""" | |
| 6 | |
| 7 import logging | |
| 8 import os | |
| 9 import Queue | |
| 10 import subprocess | |
| 11 import sys | |
| 12 import threading | |
| 13 import urllib2 | |
| 14 | |
| 15 import pyauto | |
| 16 import pyauto_paths | |
| 17 | |
| 18 | |
| 19 # List of commonly used network constraints settings. | |
| 20 # Each setting is a tuppe of the form: | |
| 21 # ('TEST_NAME', [BANDWIDTH_Kbps, LATENCY_ms, PACKET_LOSS_%]) | |
| 22 # | |
| 23 # Note: The test name should satisfy the regex [\w\.-]+ (check | |
| 24 # tools/perf_expectations/tests/perf_expectations_unittest.py for details). It | |
| 25 # is used to name the result graphs on the dashboards. | |
| 26 # | |
| 27 # The WiFi, DSL, and Cable settings were taken from webpagetest.org as | |
| 28 # approximations of their respective real world networks. The settings were | |
| 29 # based on 2011 FCC Broadband Data report (http://www.fcc.gov/document/ | |
| 30 # measuring-broadband-america-report-consumer-broadband-performance-us). | |
| 31 DialUp = ('DialUp', [56, 120, 5]) | |
| 32 Slow = ('Slow', [256, 105, 1]) | |
| 33 Wifi = ('Wifi', [1024, 60, 0]) | |
| 34 DSL = ('DSL', [1541, 50, 0]) | |
| 35 Cable = ('Cable', [5120, 28, 0]) | |
| 36 NoConstraints = ('NoConstraints', [0, 0, 0]) | |
| 37 | |
| 38 # Path to CNS executable relative to source root. | |
| 39 _CNS_PATH = os.path.join( | |
| 40 'media', 'tools', 'constrained_network_server', 'cns.py') | |
| 41 | |
| 42 # Port to start the CNS on. | |
| 43 _CNS_PORT = 9000 | |
| 44 | |
| 45 # A flag to determine whether to launch a local CNS instance or to connect | |
| 46 # to the external CNS server. Default to False since all current bots use an | |
| 47 # external instance. | |
| 48 # If not on Windows, set USE_LOCAL_CNS=1 env variable to switch the flag. | |
| 49 USE_LOCAL_CNS = ('win' not in sys.platform and 'USE_LOCAL_CNS' in os.environ and | |
| 50 os.environ['USE_LOCAL_CNS'] == '1') | |
| 51 | |
| 52 # Base CNS URL, only requires & separated parameter names appended. | |
| 53 if USE_LOCAL_CNS: | |
| 54 CNS_BASE_URL = 'http://127.0.0.1:%d/ServeConstrained?' % _CNS_PORT | |
| 55 else: | |
| 56 CNS_BASE_URL = 'http://chromeperf34:%d/ServeConstrained?' % _CNS_PORT | |
| 57 CNS_CLEANUP_URL = 'http://chromeperf34:%d/Cleanup' % _CNS_PORT | |
| 58 | |
| 59 # Used for server sanity check. | |
| 60 _TEST_VIDEO = 'roller.webm' | |
| 61 | |
| 62 # Directory root to serve files from. | |
| 63 _ROOT_PATH = os.path.join(pyauto.PyUITest.DataDir(), 'pyauto_private', 'media') | |
| 64 | |
| 65 | |
| 66 class CNSTestBase(pyauto.PyUITest): | |
| 67 """CNS test base hadles startup and teardown of CNS server.""" | |
| 68 | |
| 69 def __init__(self, *args, **kwargs): | |
| 70 """Initialize CNSTestBase by setting the arguments for CNS server. | |
| 71 | |
| 72 Args: | |
| 73 Check cns.py command line argument list for details. | |
| 74 """ | |
| 75 self._port = kwargs.get('port', _CNS_PORT) | |
| 76 self._interface = kwargs.get('interface', 'lo') | |
| 77 self._www_root = kwargs.get('www_root', _ROOT_PATH) | |
| 78 self._verbose = kwargs.get('verbose', True) | |
| 79 self._expiry_time = kwargs.get('expiry_time', 0) | |
| 80 self._socket_timeout = kwargs.get('socket_timeout') | |
| 81 pyauto.PyUITest.__init__(self, *args, **kwargs) | |
| 82 | |
| 83 def setUp(self): | |
| 84 """Ensures the Constrained Network Server (CNS) server is up and running.""" | |
| 85 if USE_LOCAL_CNS: | |
| 86 self._SetUpLocal() | |
| 87 else: | |
| 88 self._SetUpExternal() | |
| 89 | |
| 90 def _SetUpExternal(self): | |
| 91 """Ensures the test can connect to the external CNS server.""" | |
| 92 if self.WaitUntil(self._CanAccessServer, retry_sleep=3, timeout=30, | |
| 93 debug=False): | |
| 94 pyauto.PyUITest.setUp(self) | |
| 95 else: | |
| 96 self.fail('Failed to connect to CNS.') | |
| 97 | |
| 98 def _SetUpLocal(self): | |
| 99 """Starts the CNS server locally.""" | |
| 100 cmd = [sys.executable, os.path.join(pyauto_paths.GetSourceDir(), _CNS_PATH), | |
| 101 '--port', str(self._port), | |
| 102 '--interface', self._interface, | |
| 103 '--www-root', self._www_root, | |
| 104 '--expiry-time', str(self._expiry_time)] | |
| 105 | |
| 106 if self._socket_timeout: | |
| 107 cmd.extend(['--socket-timeout', str(self._socket_timeout)]) | |
| 108 if self._verbose: | |
| 109 cmd.append('-v') | |
| 110 logging.debug('Starting CNS server: %s ', ' '.join(cmd)) | |
| 111 | |
| 112 self._cns_process = subprocess.Popen(cmd, stderr=subprocess.PIPE) | |
| 113 ProcessLogger(self._cns_process) | |
| 114 | |
| 115 if self.WaitUntil(self._CanAccessServer, retry_sleep=3, timeout=30, | |
| 116 debug=False): | |
| 117 pyauto.PyUITest.setUp(self) | |
| 118 else: | |
| 119 self.tearDown() | |
| 120 self.fail('Failed to start CNS.') | |
| 121 | |
| 122 def _CanAccessServer(self): | |
| 123 """Checks if the CNS server can serve a file with no network constraints.""" | |
| 124 test_url = ''.join([CNS_BASE_URL, 'f=', _TEST_VIDEO]) | |
| 125 try: | |
| 126 return urllib2.urlopen(test_url) is not None | |
| 127 except Exception: | |
| 128 return False | |
| 129 | |
| 130 def tearDown(self): | |
| 131 """Stops the Constrained Network Server (CNS).""" | |
| 132 if USE_LOCAL_CNS: | |
| 133 logging.debug('Stopping CNS server.') | |
| 134 # Do not use process.kill(), it will not clean up cns. | |
| 135 self.Kill(self._cns_process.pid) | |
| 136 # Need to wait since the process logger has a lock on the process stderr. | |
| 137 self._cns_process.wait() | |
| 138 self.assertFalse(self._cns_process.returncode is None) | |
| 139 logging.debug('CNS server stopped.') | |
| 140 else: | |
| 141 # Call CNS Cleanup to remove all ports created by this client. | |
| 142 self.NavigateToURL(CNS_CLEANUP_URL) | |
| 143 pyauto.PyUITest.tearDown(self) | |
| 144 | |
| 145 | |
| 146 class ProcessLogger(threading.Thread): | |
| 147 """A thread to log a process's stderr output.""" | |
| 148 | |
| 149 def __init__(self, process): | |
| 150 """Starts the process logger thread. | |
| 151 | |
| 152 Args: | |
| 153 process: The process to log. | |
| 154 """ | |
| 155 threading.Thread.__init__(self) | |
| 156 self._process = process | |
| 157 self.start() | |
| 158 | |
| 159 def run(self): | |
| 160 """Adds debug statements for the process's stderr output.""" | |
| 161 line = True | |
| 162 while line: | |
| 163 line = self._process.stderr.readline() | |
| 164 logging.debug(line.strip()) | |
| 165 | |
| 166 | |
| 167 def GetFileURL(file_name, bandwidth=0, latency=0, loss=0, new_port=False): | |
| 168 """Returns CNS URL for the file with specified constraints. | |
| 169 | |
| 170 Args: | |
| 171 Check cns.ServeConstrained() args for more details. | |
| 172 """ | |
| 173 video_url = [CNS_BASE_URL, 'f=' + file_name] | |
| 174 if bandwidth > 0: | |
| 175 video_url.append('bandwidth=%d' % bandwidth) | |
| 176 if latency > 0: | |
| 177 video_url.append('latency=%d' % latency) | |
| 178 if loss > 0: | |
| 179 video_url.append('loss=%d' % loss) | |
| 180 if new_port: | |
| 181 video_url.append('new_port=%s' % new_port) | |
| 182 return '&'.join(video_url) | |
| 183 | |
| 184 | |
| 185 def CreateCNSPerfTasks(network_constraints_settings, test_media_files): | |
| 186 """Returns a queue of tasks combinining network constrains with media files. | |
| 187 | |
| 188 Args: | |
| 189 network_constraints_settings: List of (setting_name, setting_values) | |
| 190 tupples. | |
| 191 test_media_files: List of media files to run the tests on. | |
| 192 """ | |
| 193 # Convert relative test path into an absolute path. | |
| 194 tasks = Queue.Queue() | |
| 195 for file_name in test_media_files: | |
| 196 for series_name, settings in network_constraints_settings: | |
| 197 logging.debug('Add test: %s\tSettings: %s\tMedia: %s', series_name, | |
| 198 settings, file_name) | |
| 199 tasks.put((series_name, settings, file_name)) | |
| 200 | |
| 201 return tasks | |
| OLD | NEW |