Home | History | Annotate | Download | only in network_WiFi_AttenuatedPerf
      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