blob: d2e203fd39a7fa517279b2399a7fc32b2ee0282d [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 logging
import time
from autotest_lib.client.common_lib import error
from import xmlrpc_datatypes
from autotest_lib.server import site_linux_system
from import hostap_config
from import wifi_cell_test_base
class network_WiFi_VerifyRouter(wifi_cell_test_base.WiFiCellTestBase):
"""Test that a dual radio router can use both radios."""
version = 1
MAX_ASSOCIATION_RETRIES = 8 # Super lucky number. Not science.
def _connect(self, wifi_params):
assoc_result = xmlrpc_datatypes.deserialize(
self.context.client.shill.connect_wifi(wifi_params))'Finished connection attempt to %s with times: '
'discovery=%.2f, association=%.2f, configuration=%.2f.',
return assoc_result.success
def _antenna_test(self, bitmap, channel):
"""Test that we can connect on |channel|, with given antenna |bitmap|.
Sets up two radios on |channel|, configures both radios with the
given antenna |bitmap|, and then verifies that a client can connect
to the AP on each radio.
Why do we run the two radios concurrently, instead of iterating over
them? That's simply because our lower-layer code doesn't provide an
interface for specifiying which PHY to run an AP on.
To work around the API limitaiton, we bring up multiple APs, and let
the lower-layer code spread them across radios. For stumpy/panther,
this works in an obvious way. That is, each call to this method
exercises phy0 and phy1.
For whirlwind, we still cover all radios, but in a less obvious way.
Calls with a 2.4 GHz channel exercise phy0 and phy2, while calls
with a 5 GHz channel exercise phy1 and phy2.
@param bitmap: int bitmask controlling which antennas to enable.
@param channel: int Wifi channel to conduct test on
# Antenna can only be configured when the wireless interface is down.
# Set the bitmasks to both antennas on before turning one off.
# This seems to increase the probability that our association
# attempts pass. It is the very definition of a dark incantation.
if bitmap != 3:
# Setup two APs on |channel|. configure() will spread these across
# radios.
n_mode = hostap_config.HostapConfig.MODE_11N_MIXED
ap_config = hostap_config.HostapConfig(channel=channel, mode=n_mode)
self.context.configure(ap_config, multi_interface=True)
failures = []
# Verify connectivity to both APs. As the APs are spread
# across radios, this exercises multiple radios.
for instance in range(2):
context_message = ('bitmap=%d, ap_instance=%d, channel=%d' %
(bitmap, instance, channel))'Connecting to AP with settings %s.',
client_conf = xmlrpc_datatypes.AssociationParameters(
if self._connect(client_conf):
signal_level = self.context.client.wifi_signal_level'Signal level for AP %d with bitmap %d is %d',
instance, bitmap, signal_level)
{'signal_for_ap_%d_bm_%d_ch_%d' %
(instance, bitmap, channel):
# Don't automatically reconnect to this AP.
return failures
def cleanup(self):
"""Clean up after the test is completed
Perform additional cleanups after the test, the important thing is
to re-enable all antennas.
super(network_WiFi_VerifyRouter, self).cleanup()
def run_once(self):
"""Verify that all radios on this router are functional."""
all_failures = []
# Run antenna test for 2GHz band and 5GHz band
for channel in (6, 149):
# First connect with both antennas enabled. Then connect with just
# one antenna enabled at a time.
for bitmap in (3, 1, 2):
failures = set()
for attempt in range(self.MAX_ASSOCIATION_RETRIES):
new_failures = self._antenna_test(bitmap, channel)
if not new_failures:
all_failures += failures
if all_failures:
failure_message = ', '.join(
['(' + message + ')' for message in all_failures])
raise error.TestFail('Failed to connect when %s.' % failure_message)