1#!/usr/bin/env python3 2# 3# Copyright 2020 - The Android Open Source Project 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16 17import itertools 18 19from acts import utils 20from acts.controllers.ap_lib import hostapd_constants 21from acts.controllers.ap_lib import hostapd_config 22from acts.test_utils.abstract_devices.wlan_device import create_wlan_device 23from acts.test_utils.abstract_devices.utils_lib.wlan_utils import validate_setup_ap_and_associate 24from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest 25from acts.utils import rand_ascii_str 26 27# 12, 13 outside the US 28# 14 in Japan, DSSS and CCK only 29CHANNELS_24 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] 30 31# 32, 34 for 20 and 40 mhz in Europe 32# 34, 42, 46 have mixed international support 33# 144 is supported by some chips 34CHANNELS_5 = [ 35 36, 40, 44, 48, 52, 56, 60, 64, 100, 104, 108, 112, 116, 120, 124, 128, 36 132, 136, 140, 144, 149, 153, 157, 161, 165 37] 38 39BANDWIDTH_24 = [20, 40] 40BANDWIDTH_5 = [20, 40, 80] 41 42N_CAPABILITIES_DEFAULT = [ 43 hostapd_constants.N_CAPABILITY_LDPC, hostapd_constants.N_CAPABILITY_SGI20, 44 hostapd_constants.N_CAPABILITY_SGI40, 45 hostapd_constants.N_CAPABILITY_TX_STBC, 46 hostapd_constants.N_CAPABILITY_RX_STBC1 47] 48 49AC_CAPABILITIES_DEFAULT = [ 50 hostapd_constants.AC_CAPABILITY_MAX_MPDU_11454, 51 hostapd_constants.AC_CAPABILITY_RXLDPC, 52 hostapd_constants.AC_CAPABILITY_SHORT_GI_80, 53 hostapd_constants.AC_CAPABILITY_TX_STBC_2BY1, 54 hostapd_constants.AC_CAPABILITY_RX_STBC_1, 55 hostapd_constants.AC_CAPABILITY_MAX_A_MPDU_LEN_EXP7, 56 hostapd_constants.AC_CAPABILITY_RX_ANTENNA_PATTERN, 57 hostapd_constants.AC_CAPABILITY_TX_ANTENNA_PATTERN 58] 59 60 61def generate_test_name(settings): 62 """Generates a string test name based on the channel and band 63 64 Args: 65 settings: A dict with 'channel' and 'bandwidth' keys. 66 67 Returns: 68 A string that represents a test case name. 69 """ 70 return 'test_channel_%s_bandwidth_%smhz' % (settings['channel'], 71 settings['bandwidth']) 72 73 74class ChannelSweepTest(WifiBaseTest): 75 """Tests for associating with all 2.5 and 5 channels and bands. 76 77 Testbed Requirement: 78 * One ACTS compatible device (dut) 79 * One Access Point 80 """ 81 def __init__(self, controllers): 82 WifiBaseTest.__init__(self, controllers) 83 self.tests = ['test_24ghz_channels', 'test_5ghz_channels'] 84 if 'debug_channel_sweep_tests' in self.user_params: 85 self.tests.append('test_channel_sweep_debug') 86 87 def setup_class(self): 88 super().setup_class() 89 if 'dut' in self.user_params: 90 if self.user_params['dut'] == 'fuchsia_devices': 91 self.dut = create_wlan_device(self.fuchsia_devices[0]) 92 elif self.user_params['dut'] == 'android_devices': 93 self.dut = create_wlan_device(self.android_devices[0]) 94 else: 95 raise ValueError('Invalid DUT specified in config. (%s)' % 96 self.user_params['dut']) 97 else: 98 # Default is an android device, just like the other tests 99 self.dut = create_wlan_device(self.android_devices[0]) 100 101 self.android_devices = getattr(self, 'android_devices', []) 102 self.access_point = self.access_points[0] 103 self.access_point.stop_all_aps() 104 105 def setup_test(self): 106 for ad in self.android_devices: 107 ad.droid.wakeLockAcquireBright() 108 ad.droid.wakeUpNow() 109 self.dut.wifi_toggle_state(True) 110 111 def teardown_test(self): 112 for ad in self.android_devices: 113 ad.droid.wakeLockRelease() 114 ad.droid.goToSleepNow() 115 self.dut.turn_location_off_and_scan_toggle_off() 116 self.dut.disconnect() 117 self.dut.reset_wifi() 118 self.access_point.stop_all_aps() 119 120 def on_fail(self, test_name, begin_time): 121 self.dut.take_bug_report(test_name, begin_time) 122 self.dut.get_log(test_name, begin_time) 123 124 def setup_and_connect(self, settings): 125 """Generates a hostapd config based on the provided channel and 126 bandwidth, starts AP with that config, and attempts to associate the 127 dut. 128 129 Args: 130 settings: A dict with 'channel' and 'bandwidth' keys. 131 """ 132 channel = settings['channel'] 133 bandwidth = settings['bandwidth'] 134 if channel > 14: 135 vht_bandwidth = bandwidth 136 else: 137 vht_bandwidth = None 138 139 if bandwidth == 20: 140 n_capabilities = N_CAPABILITIES_DEFAULT + [ 141 hostapd_constants.N_CAPABILITY_HT20 142 ] 143 elif bandwidth == 40 or bandwidth == 80: 144 if hostapd_config.ht40_plus_allowed(channel): 145 extended_channel = [hostapd_constants.N_CAPABILITY_HT40_PLUS] 146 elif hostapd_config.ht40_minus_allowed(channel): 147 extended_channel = [hostapd_constants.N_CAPABILITY_HT40_MINUS] 148 else: 149 raise ValueError('Invalid Channel: %s' % channel) 150 n_capabilities = N_CAPABILITIES_DEFAULT + extended_channel 151 else: 152 raise ValueError('Invalid Bandwidth: %s' % bandwidth) 153 154 validate_setup_ap_and_associate(access_point=self.access_point, 155 client=self.dut, 156 profile_name='whirlwind', 157 channel=channel, 158 n_capabilities=n_capabilities, 159 ac_capabilities=None, 160 force_wmm=True, 161 ssid=utils.rand_ascii_str(20), 162 vht_bandwidth=vht_bandwidth) 163 164 def create_test_settings(self, channels, bandwidths): 165 """Creates a list of test configurations to run from the product of the 166 given channels list and bandwidths list. 167 168 Args: 169 channels: A list of ints representing the channels to test. 170 bandwidths: A list of ints representing the bandwidths to test on 171 those channels. 172 173 Returns: 174 A list of dictionaries containing 'channel' and 'bandwidth' keys, 175 one for each test combination to be run. 176 """ 177 test_list = [] 178 for combination in itertools.product(channels, bandwidths): 179 test_settings = { 180 'channel': combination[0], 181 'bandwidth': combination[1] 182 } 183 test_list.append(test_settings) 184 return test_list 185 186 def test_24ghz_channels(self): 187 """Runs setup_and_connect for 2.4GHz channels on 20 and 40 MHz bands.""" 188 test_list = self.create_test_settings(CHANNELS_24, BANDWIDTH_24) 189 self.run_generated_testcases(self.setup_and_connect, 190 settings=test_list, 191 name_func=generate_test_name) 192 193 def test_5ghz_channels(self): 194 """Runs setup_and_connect for 5GHz channels on 20, 40, and 80 MHz bands. 195 """ 196 test_list = self.create_test_settings(CHANNELS_5, BANDWIDTH_5) 197 # Channel 165 is 20mhz only 198 test_list.remove({'channel': 165, 'bandwidth': 40}) 199 test_list.remove({'channel': 165, 'bandwidth': 80}) 200 self.run_generated_testcases(self.setup_and_connect, 201 settings=test_list, 202 name_func=generate_test_name) 203 204 def test_channel_sweep_debug(self): 205 """Runs test cases defined in the ACTS config file for debugging 206 purposes. 207 208 Usage: 209 1. Add 'debug_channel_sweep_tests' to ACTS config with list of 210 tests to run matching pattern: 211 test_channel_<CHANNEL>_bandwidth_<BANDWIDTH> 212 2. Run test_channel_sweep_debug test. 213 """ 214 allowed_channels = CHANNELS_24 + CHANNELS_5 215 chan_band_pattern = re.compile( 216 r'.*channel_([0-9]*)_.*bandwidth_([0-9]*)') 217 test_list = [] 218 for test_title in self.user_params['debug_channel_sweep_tests']: 219 test = re.match(chan_band_pattern, test_title) 220 if test: 221 channel = int(test.group(1)) 222 bandwidth = int(test.group(2)) 223 if channel not in allowed_channels: 224 raise ValueError("Invalid channel: %s" % channel) 225 if channel <= 14 and bandwidth not in BANDWIDTH_24: 226 raise ValueError( 227 "Channel %s does not support bandwidth %s" % 228 (channel, bandwidth)) 229 test_settings = {'channel': channel, 'bandwidth': bandwidth} 230 test_list.append(test_settings) 231 232 self.run_generated_testcases(self.setup_and_connect, 233 settings=test_list, 234 name_func=generate_test_name) 235