blob: 1bfbd6fdbcab3adc91dade8e090739a48c89b20d [file] [log] [blame]
# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import collections
import logging
import os.path
import time
from autotest_lib.client.common_lib import error
from autotest_lib.client.common_lib.cros.network import xmlrpc_datatypes
from autotest_lib.server.cros.network import netperf_runner
from autotest_lib.server.cros.network import netperf_session
from autotest_lib.server.cros.network import wifi_cell_test_base
class network_WiFi_AttenuatedPerf(wifi_cell_test_base.WiFiCellTestBase):
"""Test maximal achievable bandwidth while varying attenuation.
Performs a performance test for a specified router configuration as
signal attentuation increases.
"""
version = 1
CMDLINE_SERIES_NOTE = 'series_note'
NETPERF_CONFIGS = [
netperf_runner.NetperfConfig(
netperf_runner.NetperfConfig.TEST_TYPE_TCP_STREAM),
netperf_runner.NetperfConfig(
netperf_runner.NetperfConfig.TEST_TYPE_TCP_MAERTS),
netperf_runner.NetperfConfig(
netperf_runner.NetperfConfig.TEST_TYPE_UDP_STREAM),
netperf_runner.NetperfConfig(
netperf_runner.NetperfConfig.TEST_TYPE_UDP_MAERTS),
]
TSV_OUTPUT_DIR = 'tsvs'
DataPoint = collections.namedtuple('DataPoint',
['attenuation', 'throughput',
'variance', 'signal', 'test_type'])
def parse_additional_arguments(self, commandline_args, additional_params):
"""Hook into super class to take control files parameters.
@param commandline_args dict of parsed parameters from the autotest.
@param additional_params list of dicts describing router configs.
"""
self._ap_config = additional_params[0]
self.series_note = None
self._attenuation_increment = additional_params[1]
self._final_attenuation = additional_params[2]
if self.CMDLINE_SERIES_NOTE in commandline_args:
self.series_note = commandline_args[self.CMDLINE_SERIES_NOTE]
def run_once(self):
"""Run test."""
start_time = time.time()
throughput_data = []
max_atten = None
self.context.client.host.get_file('/etc/lsb-release', self.resultsdir)
# Set up the router and associate the client with it.
self.context.configure(self._ap_config)
assoc_params = xmlrpc_datatypes.AssociationParameters(
ssid=self.context.router.get_ssid(),
security_config=self._ap_config.security_config)
self.context.assert_connect_wifi(assoc_params)
# Conduct the performance tests. Ignore failures, since
# at high attenuations, sometimes the control connection
# is unable to terminate the test properly.
session = netperf_session.NetperfSession(self.context.client,
self.context.router,
ignore_failures=True)
session.warmup_stations()
start_atten = self.context.attenuator.get_minimal_total_attenuation()
for atten in range(start_atten, self._final_attenuation,
self._attenuation_increment):
atten_tag = 'atten%03d' % atten
self.context.attenuator.set_total_attenuation(
atten, self._ap_config.frequency)
logging.info('RvR test: current attenuation = %d dB', atten)
# Give this attenuation level a quick sanity test. If we can't stay
# associated and handle a few pings, we probably won't get
# meaningful results out of netperf.
try:
self.context.wait_for_connection(self.context.router.get_ssid())
except error.TestFail as e:
logging.warning('Could not establish connection at %d dB (%s)',
atten, str(e))
break
for config in self.NETPERF_CONFIGS:
results = session.run(config)
if not results:
logging.warning('Unable to take measurement for %s; '
'aborting', config.human_readable_tag)
break
graph_name = '.'.join(
[self._ap_config.perf_loggable_description, config.tag])
values = [result.throughput for result in results]
# If no signal is detected with client.wifi_signal_level, set
# signal_level to -100 to indicate weak signal.
signal_level = (self.context.client.wifi_signal_level if
self.context.client.wifi_signal_level else -100)
self.output_perf_value(atten_tag, values, units='Mbps',
higher_is_better=True, graph=graph_name)
self.output_perf_value('_'.join([atten_tag, 'signal']),
signal_level,
units='dBm', higher_is_better=True,
graph=graph_name)
result = netperf_runner.NetperfResult.from_samples(results)
throughput_data.append(self.DataPoint(
atten,
result.throughput,
result.throughput_dev,
signal_level,
config.tag))
keyval_prefix = '_'.join(
[self._ap_config.perf_loggable_description, config.tag,
atten_tag])
self.write_perf_keyval(result.get_keyval(prefix=keyval_prefix))
# Reported at least one successful result at this attenuation.
max_atten = (atten, signal_level)
signal_level = self.context.client.wifi_signal_level
self.write_perf_keyval(
{'_'.join([atten_tag, 'signal']): signal_level})
if not results:
logging.warning('No results for atten %d dB; terminating',
atten)
# Clean up router and client state.
self.context.client.shill.disconnect(assoc_params.ssid)
self.context.router.deconfig()
end_time = time.time()
logging.info('Running time %0.1f seconds.', end_time - start_time)
if max_atten is None:
raise error.TestFail('Did not succeed at any atten level')
logging.info('Reached attenuation of: %d dB (signal %d)',
max_atten[0], max_atten[1])
self.write_perf_keyval({'ch%03d_max_atten' % self._ap_config.channel:
max_atten[0]})
self.write_perf_keyval({'ch%03d_min_signal' % self._ap_config.channel:
max_atten[1]})
self.write_throughput_tsv_files(throughput_data)
def write_throughput_tsv_files(self, throughput_data):
"""Write out .tsv files with plotable data from |throughput_data|.
Each .tsv file starts with a label for the series that can be
customized with a short note passed in from the command line.
It then has column headers and fields separated by tabs. This format
is easy to parse and also works well with spreadsheet programs for
custom report generation.
@param throughput_data a list of Datapoint namedtuples gathered from
tests.
"""
logging.info('Writing .tsv files.')
os.mkdir(os.path.join(self.resultsdir, self.TSV_OUTPUT_DIR))
series_label_parts = [self.context.client.board,
'ch%03d' % self._ap_config.channel]
if self.series_note:
series_label_parts.insert(1, '(%s)' % self.series_note)
header_parts = ['Attenuation', 'Throughput(Mbps)', 'StdDev(Mbps)',
'Client Reported Signal']
mode = self._ap_config.printable_mode
mode = mode.replace('+', 'p').replace('-', 'm').lower()
result_file_prefix = '%s_ch%03d' % (mode, self._ap_config.channel)
for test_type in set([data.test_type for data in throughput_data]):
result_file = os.path.join(
self.resultsdir, self.TSV_OUTPUT_DIR,
'%s_%s.tsv' % (result_file_prefix, test_type))
lines = [' '.join(series_label_parts),
'\t'.join(header_parts)]
for result in sorted([datum for datum in throughput_data
if datum.test_type == test_type]):
lines.append('\t'.join(map(str, result[0:4])))
with open(result_file, 'w') as f:
f.writelines(['%s\n' % line for line in lines])