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