1 # Copyright (c) 2013 The Chromium OS 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 import collections 6 import logging 7 import os.path 8 import time 9 10 from autotest_lib.client.common_lib import error 11 from autotest_lib.client.common_lib.cros.network import xmlrpc_datatypes 12 from autotest_lib.server.cros.network import netperf_runner 13 from autotest_lib.server.cros.network import netperf_session 14 from autotest_lib.server.cros.network import wifi_cell_test_base 15 16 17 class network_WiFi_AttenuatedPerf(wifi_cell_test_base.WiFiCellTestBase): 18 """Test maximal achievable bandwidth while varying attenuation. 19 20 Performs a performance test for a specified router configuration as 21 signal attentuation increases. 22 23 """ 24 25 version = 1 26 27 CMDLINE_SERIES_NOTE = 'series_note' 28 29 NETPERF_CONFIGS = [ 30 netperf_runner.NetperfConfig( 31 netperf_runner.NetperfConfig.TEST_TYPE_TCP_STREAM), 32 netperf_runner.NetperfConfig( 33 netperf_runner.NetperfConfig.TEST_TYPE_TCP_MAERTS), 34 netperf_runner.NetperfConfig( 35 netperf_runner.NetperfConfig.TEST_TYPE_UDP_STREAM), 36 netperf_runner.NetperfConfig( 37 netperf_runner.NetperfConfig.TEST_TYPE_UDP_MAERTS), 38 ] 39 40 ATTENUATION_STEP = 4 41 FINAL_ATTENUATION = 100 42 43 TSV_OUTPUT_DIR = 'tsvs' 44 45 DataPoint = collections.namedtuple('DataPoint', 46 ['attenuation', 'throughput', 47 'variance', 'signal', 'test_type']) 48 49 50 def parse_additional_arguments(self, commandline_args, additional_params): 51 """Hook into super class to take control files parameters. 52 53 @param commandline_args dict of parsed parameters from the autotest. 54 @param additional_params list of dicts describing router configs. 55 56 """ 57 self._ap_config = additional_params 58 self.series_note = None 59 if self.CMDLINE_SERIES_NOTE in commandline_args: 60 self.series_note = commandline_args[self.CMDLINE_SERIES_NOTE] 61 62 63 def run_once(self): 64 start_time = time.time() 65 throughput_data = [] 66 max_atten = None 67 self.context.client.host.get_file('/etc/lsb-release', self.resultsdir) 68 # Set up the router and associate the client with it. 69 self.context.configure(self._ap_config) 70 assoc_params = xmlrpc_datatypes.AssociationParameters( 71 ssid=self.context.router.get_ssid(), 72 security_config=self._ap_config.security_config) 73 self.context.assert_connect_wifi(assoc_params) 74 75 # Conduct the performance tests. Ignore failures, since 76 # at high attenuations, sometimes the control connection 77 # is unable to terminate the test properly. 78 session = netperf_session.NetperfSession(self.context.client, 79 self.context.router, 80 ignore_failures=True) 81 session.warmup_stations() 82 start_atten = self.context.attenuator.get_minimal_total_attenuation() 83 for atten in range(start_atten, self.FINAL_ATTENUATION, 84 self.ATTENUATION_STEP): 85 atten_tag = 'atten%03d' % atten 86 self.context.attenuator.set_total_attenuation( 87 atten, self._ap_config.frequency) 88 logging.info('RvR test: current attenuation = %d dB', atten) 89 90 # Give this attenuation level a quick sanity test. If we can't stay 91 # associated and handle a few pings, we probably won't get 92 # meaningful results out of netperf. 93 try: 94 self.context.wait_for_connection(self.context.router.get_ssid()) 95 except error.TestFail as e: 96 logging.warning('Could not establish connection at %d dB (%s)', 97 atten, str(e)) 98 break 99 100 for config in self.NETPERF_CONFIGS: 101 results = session.run(config) 102 if not results: 103 logging.warning('Unable to take measurement for %s; aborting', 104 config.human_readable_tag) 105 break 106 graph_name = '.'.join( 107 [self._ap_config.perf_loggable_description, config.tag]) 108 values = [result.throughput for result in results] 109 self.output_perf_value(atten_tag, values, units='Mbps', 110 higher_is_better=True, graph=graph_name) 111 self.output_perf_value('_'.join([atten_tag, 'signal']), 112 self.context.client.wifi_signal_level, 113 units='dBm', higher_is_better=True, 114 graph=graph_name) 115 result = netperf_runner.NetperfResult.from_samples(results) 116 signal_level = self.context.client.wifi_signal_level 117 throughput_data.append(self.DataPoint( 118 atten, 119 result.throughput, 120 result.throughput_dev, 121 signal_level, 122 config.tag)) 123 keyval_prefix = '_'.join( 124 [self._ap_config.perf_loggable_description, config.tag, 125 atten_tag]) 126 self.write_perf_keyval(result.get_keyval(prefix=keyval_prefix)) 127 # Reported at least one successful result at this attenuation. 128 max_atten = (atten, signal_level) 129 130 signal_level = self.context.client.wifi_signal_level 131 self.write_perf_keyval( 132 {'_'.join([atten_tag, 'signal']): signal_level}) 133 134 if not results: 135 logging.warning('No results for atten %d dB; terminating', 136 atten) 137 138 # Clean up router and client state. 139 self.context.client.shill.disconnect(assoc_params.ssid) 140 self.context.router.deconfig() 141 end_time = time.time() 142 logging.info('Running time %0.1f seconds.', end_time - start_time) 143 144 if max_atten is None: 145 raise error.TestFail('Did not succeed at any atten level') 146 logging.info('Reached attenuation of: %d dB (signal %d)' % max_atten) 147 self.write_perf_keyval({'ch%03d_max_atten' % self._ap_config.channel: 148 max_atten[0]}) 149 self.write_perf_keyval({'ch%03d_min_signal' % self._ap_config.channel: 150 max_atten[1]}) 151 self.write_throughput_tsv_files(throughput_data) 152 153 154 def write_throughput_tsv_files(self, throughput_data): 155 """Write out .tsv files with plotable data from |throughput_data|. 156 157 Each .tsv file starts with a label for the series that can be 158 customized with a short note passed in from the command line. 159 It then has column headers and fields separated by tabs. This format 160 is easy to parse and also works well with spreadsheet programs for 161 custom report generation. 162 163 @param throughput_data a list of Datapoint namedtuples gathered from 164 tests. 165 166 """ 167 logging.info('Writing .tsv files.') 168 os.mkdir(os.path.join(self.resultsdir, self.TSV_OUTPUT_DIR)) 169 series_label_parts = [self.context.client.board, 170 'ch%03d' % self._ap_config.channel] 171 if self.series_note: 172 series_label_parts.insert(1, '(%s)' % self.series_note) 173 header_parts = ['Attenuation', 'Throughput(Mbps)', 'StdDev(Mbps)', 174 'Client Reported Signal'] 175 mode = self._ap_config.printable_mode 176 mode = mode.replace('+', 'p').replace('-', 'm').lower() 177 result_file_prefix = '%s_ch%03d' % (mode, self._ap_config.channel) 178 for test_type in set([data.test_type for data in throughput_data]): 179 result_file = os.path.join( 180 self.resultsdir, self.TSV_OUTPUT_DIR, 181 '%s_%s.tsv' % (result_file_prefix, test_type)) 182 lines = [' '.join(series_label_parts), 183 '\t'.join(header_parts)] 184 for result in sorted([datum for datum in throughput_data 185 if datum.test_type == test_type]): 186 lines.append('\t'.join(map(str, result[0:4]))) 187 with open(result_file, 'w') as f: 188 f.writelines(['%s\n' % line for line in lines]) 189