blob: b9f26bf5fd62f96c1b6b1708976e6d8ac6291458 [file] [log] [blame]
Hayden Nix3f2eebf2020-01-14 10:39:21 -08001#!/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
Hayden Nixc8a02bf2020-04-14 19:12:34 +000017import json
18import os
19import time
Hayden Nix3f2eebf2020-01-14 10:39:21 -080020
Hayden Nixc8a02bf2020-04-14 19:12:34 +000021from statistics import pstdev
22
23from bokeh.models import FixedTicker
24from bokeh.plotting import ColumnDataSource
25from bokeh.plotting import figure
26from bokeh.plotting import output_file
27from bokeh.plotting import save
28
29from acts import asserts
30from acts import context
Hayden Nix3f2eebf2020-01-14 10:39:21 -080031from acts import utils
Hayden Nixfe643052020-12-09 00:50:05 +000032from acts.controllers.access_point import setup_ap
Hayden Nix3f2eebf2020-01-14 10:39:21 -080033from acts.controllers.ap_lib import hostapd_config
Hayden Nixc8a02bf2020-04-14 19:12:34 +000034from acts.controllers.ap_lib import hostapd_constants
35from acts.controllers.ap_lib.hostapd_security import Security
36from acts.controllers.iperf_server import IPerfResult
Xianyuan Jia24299b72020-10-21 13:52:47 -070037from acts_contrib.test_utils.abstract_devices.wlan_device import create_wlan_device
38from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
Hayden Nix3f2eebf2020-01-14 10:39:21 -080039
40N_CAPABILITIES_DEFAULT = [
41 hostapd_constants.N_CAPABILITY_LDPC, hostapd_constants.N_CAPABILITY_SGI20,
42 hostapd_constants.N_CAPABILITY_SGI40,
43 hostapd_constants.N_CAPABILITY_TX_STBC,
44 hostapd_constants.N_CAPABILITY_RX_STBC1
45]
46
47AC_CAPABILITIES_DEFAULT = [
48 hostapd_constants.AC_CAPABILITY_MAX_MPDU_11454,
49 hostapd_constants.AC_CAPABILITY_RXLDPC,
50 hostapd_constants.AC_CAPABILITY_SHORT_GI_80,
51 hostapd_constants.AC_CAPABILITY_TX_STBC_2BY1,
52 hostapd_constants.AC_CAPABILITY_RX_STBC_1,
53 hostapd_constants.AC_CAPABILITY_MAX_A_MPDU_LEN_EXP7,
54 hostapd_constants.AC_CAPABILITY_RX_ANTENNA_PATTERN,
55 hostapd_constants.AC_CAPABILITY_TX_ANTENNA_PATTERN
56]
57
Hayden Nixc8a02bf2020-04-14 19:12:34 +000058DEFAULT_MIN_THROUGHPUT = 0
59DEFAULT_MAX_STD_DEV = 1
Hayden Nix608ccda2021-04-21 19:51:46 +000060DEFAULT_IPERF_TIMEOUT = 30
Hayden Nix3f2eebf2020-01-14 10:39:21 -080061
Hayden Nixc8a02bf2020-04-14 19:12:34 +000062DEFAULT_TIME_TO_WAIT_FOR_IP_ADDR = 30
63GRAPH_CIRCLE_SIZE = 10
64IPERF_NO_THROUGHPUT_VALUE = 0
65MAX_2_4_CHANNEL = 14
66TIME_TO_SLEEP_BETWEEN_RETRIES = 1
Hayden Nix484f48c2020-04-14 20:54:17 +000067TIME_TO_WAIT_FOR_COUNTRY_CODE = 10
Hayden Nixc8a02bf2020-04-14 19:12:34 +000068WEP_HEX_STRING_LENGTH = 10
Hayden Nix3f2eebf2020-01-14 10:39:21 -080069
Hayden Nix7a69a4d2020-08-05 18:42:27 +000070MEGABITS_PER_SECOND = 'Mbps'
71
Hayden Nix3f2eebf2020-01-14 10:39:21 -080072
Hayden Nixc8a02bf2020-04-14 19:12:34 +000073def get_test_name(settings):
74 """Retrieves the test_name value from test_settings"""
75 return settings.get('test_name')
Hayden Nix3f2eebf2020-01-14 10:39:21 -080076
77
78class ChannelSweepTest(WifiBaseTest):
Hayden Nix484f48c2020-04-14 20:54:17 +000079 """Tests channel performance and regulatory compliance..
Hayden Nix3f2eebf2020-01-14 10:39:21 -080080
81 Testbed Requirement:
82 * One ACTS compatible device (dut)
83 * One Access Point
Hayden Nixc8a02bf2020-04-14 19:12:34 +000084 * One Linux Machine used as IPerfServer if running performance tests
85 Note: Performance tests should be done in isolated testbed.
Hayden Nix3f2eebf2020-01-14 10:39:21 -080086 """
87 def __init__(self, controllers):
88 WifiBaseTest.__init__(self, controllers)
Hayden Nixc8a02bf2020-04-14 19:12:34 +000089 if 'channel_sweep_test_params' in self.user_params:
90 self.time_to_wait_for_ip_addr = self.user_params[
91 'channel_sweep_test_params'].get(
92 'time_to_wait_for_ip_addr',
93 DEFAULT_TIME_TO_WAIT_FOR_IP_ADDR)
94 else:
95 self.time_to_wait_for_ip_addr = DEFAULT_TIME_TO_WAIT_FOR_IP_ADDR
Hayden Nix3f2eebf2020-01-14 10:39:21 -080096
97 def setup_class(self):
98 super().setup_class()
99 if 'dut' in self.user_params:
100 if self.user_params['dut'] == 'fuchsia_devices':
101 self.dut = create_wlan_device(self.fuchsia_devices[0])
102 elif self.user_params['dut'] == 'android_devices':
103 self.dut = create_wlan_device(self.android_devices[0])
104 else:
105 raise ValueError('Invalid DUT specified in config. (%s)' %
106 self.user_params['dut'])
107 else:
108 # Default is an android device, just like the other tests
109 self.dut = create_wlan_device(self.android_devices[0])
110
111 self.android_devices = getattr(self, 'android_devices', [])
Hayden Nixc8a02bf2020-04-14 19:12:34 +0000112
Hayden Nix3f2eebf2020-01-14 10:39:21 -0800113 self.access_point = self.access_points[0]
114 self.access_point.stop_all_aps()
115
Hayden Nixf6071682021-02-16 20:12:41 +0000116 self.iperf_server = None
117 self.iperf_client = None
118
119 self.channel_sweep_test_params = self.user_params.get(
120 'channel_sweep_test_params', {})
121 # Allows users to skip the iperf throughput measurements, just verifying
122 # association.
123 if not self.channel_sweep_test_params.get('skip_performance'):
124 try:
125 self.iperf_server = self.iperf_servers[0]
126 self.iperf_server.start()
127 self.iperf_client = self.iperf_clients[0]
128 except AttributeError:
129 self.log.warn(
130 'Missing iperf config. Throughput cannot be measured, so only '
131 'association will be tested.')
Tom Turneyc0201172021-04-28 16:08:33 -0700132 self.regulatory_results = "====CountryCode,Channel,Frequency,ChannelBandwith,Connected/Not-Connected====\n"
133
134 def teardown_class(self):
135 super().teardown_class()
136 output_path = context.get_current_context().get_base_output_path()
137 regulatory_save_path = '%s/ChannelSweepTest/%s' % (
138 output_path, "regulatory_results.txt")
139 f = open(regulatory_save_path, "w")
140 f.write(self.regulatory_results)
141 f.close()
Hayden Nixf6071682021-02-16 20:12:41 +0000142
Hayden Nix3f2eebf2020-01-14 10:39:21 -0800143 def setup_test(self):
Hayden Nix484f48c2020-04-14 20:54:17 +0000144 # TODO(fxb/46417): Uncomment when wlanClearCountry is implemented up any
145 # country code changes.
146 # for fd in self.fuchsia_devices:
147 # phy_ids_response = fd.wlan_lib.wlanPhyIdList()
148 # if phy_ids_response.get('error'):
149 # raise ConnectionError(
150 # 'Failed to retrieve phy ids from FuchsiaDevice (%s). '
151 # 'Error: %s' % (fd.ip, phy_ids_response['error']))
152 # for id in phy_ids_response['result']:
153 # clear_country_response = fd.wlan_lib.wlanClearCountry(id)
154 # if clear_country_response.get('error'):
155 # raise EnvironmentError(
156 # 'Failed to reset country code on FuchsiaDevice (%s). '
157 # 'Error: %s' % (fd.ip, clear_country_response['error'])
158 # )
Hayden Nixc8a02bf2020-04-14 19:12:34 +0000159 self.access_point.stop_all_aps()
Hayden Nix3f2eebf2020-01-14 10:39:21 -0800160 for ad in self.android_devices:
161 ad.droid.wakeLockAcquireBright()
162 ad.droid.wakeUpNow()
163 self.dut.wifi_toggle_state(True)
Hayden Nixc8a02bf2020-04-14 19:12:34 +0000164 self.dut.disconnect()
Hayden Nix3f2eebf2020-01-14 10:39:21 -0800165
166 def teardown_test(self):
167 for ad in self.android_devices:
168 ad.droid.wakeLockRelease()
169 ad.droid.goToSleepNow()
170 self.dut.turn_location_off_and_scan_toggle_off()
171 self.dut.disconnect()
Hayden Nix3f2eebf2020-01-14 10:39:21 -0800172 self.access_point.stop_all_aps()
173
174 def on_fail(self, test_name, begin_time):
175 self.dut.take_bug_report(test_name, begin_time)
176 self.dut.get_log(test_name, begin_time)
177
Hayden Nix484f48c2020-04-14 20:54:17 +0000178 def set_dut_country_code(self, country_code):
179 """Set the country code on the DUT. Then verify that the country
180 code was set successfully
181
182 Args:
183 country_code: string, the 2 character country code to set
184 """
185 self.log.info('Setting DUT country code to %s' % country_code)
186 country_code_response = self.dut.device.regulatory_region_lib.setRegion(
187 country_code)
188 if country_code_response.get('error'):
189 raise EnvironmentError(
190 'Failed to set country code (%s) on DUT. Error: %s' %
191 (country_code, country_code_response['error']))
192
193 self.log.info('Verifying DUT country code was correctly set to %s.' %
194 country_code)
195 phy_ids_response = self.dut.device.wlan_lib.wlanPhyIdList()
196 if phy_ids_response.get('error'):
197 raise ConnectionError('Failed to get phy ids from DUT. Error: %s' %
198 (country_code, phy_ids_response['error']))
199
200 end_time = time.time() + TIME_TO_WAIT_FOR_COUNTRY_CODE
201 while time.time() < end_time:
202 for id in phy_ids_response['result']:
203 get_country_response = self.dut.device.wlan_lib.wlanGetCountry(
204 id)
205 if get_country_response.get('error'):
206 raise ConnectionError(
207 'Failed to query PHY ID (%s) for country. Error: %s' %
208 (id, get_country_response['error']))
209
210 set_code = ''.join([
211 chr(ascii_char)
212 for ascii_char in get_country_response['result']
213 ])
214 if set_code != country_code:
215 self.log.debug(
216 'PHY (id: %s) has incorrect country code set. '
217 'Expected: %s, Got: %s' % (id, country_code, set_code))
218 break
219 else:
220 self.log.info('All PHYs have expected country code (%s)' %
221 country_code)
222 break
223 time.sleep(TIME_TO_SLEEP_BETWEEN_RETRIES)
224 else:
225 raise EnvironmentError('Failed to set DUT country code to %s.' %
226 country_code)
227
Hayden Nixc8a02bf2020-04-14 19:12:34 +0000228 def setup_ap(self, channel, channel_bandwidth, security_profile=None):
229 """Start network on AP with basic configuration.
Hayden Nix3f2eebf2020-01-14 10:39:21 -0800230
231 Args:
Hayden Nixc8a02bf2020-04-14 19:12:34 +0000232 channel: int, channel to use for network
233 channel_bandwidth: int, channel bandwidth in mhz to use for network,
234 security_profile: Security object, or None if open
235
236 Returns:
237 string, ssid of network running
238
239 Raises:
240 ConnectionError if network is not started successfully.
Hayden Nix3f2eebf2020-01-14 10:39:21 -0800241 """
Hayden Nixc8a02bf2020-04-14 19:12:34 +0000242 if channel > MAX_2_4_CHANNEL:
243 vht_bandwidth = channel_bandwidth
Hayden Nix3f2eebf2020-01-14 10:39:21 -0800244 else:
245 vht_bandwidth = None
246
Hayden Nixc8a02bf2020-04-14 19:12:34 +0000247 if channel_bandwidth == hostapd_constants.CHANNEL_BANDWIDTH_20MHZ:
Hayden Nix3f2eebf2020-01-14 10:39:21 -0800248 n_capabilities = N_CAPABILITIES_DEFAULT + [
249 hostapd_constants.N_CAPABILITY_HT20
250 ]
Hayden Nixc8a02bf2020-04-14 19:12:34 +0000251 elif (channel_bandwidth == hostapd_constants.CHANNEL_BANDWIDTH_40MHZ or
252 channel_bandwidth == hostapd_constants.CHANNEL_BANDWIDTH_80MHZ):
Hayden Nix3f2eebf2020-01-14 10:39:21 -0800253 if hostapd_config.ht40_plus_allowed(channel):
254 extended_channel = [hostapd_constants.N_CAPABILITY_HT40_PLUS]
255 elif hostapd_config.ht40_minus_allowed(channel):
256 extended_channel = [hostapd_constants.N_CAPABILITY_HT40_MINUS]
257 else:
258 raise ValueError('Invalid Channel: %s' % channel)
259 n_capabilities = N_CAPABILITIES_DEFAULT + extended_channel
260 else:
Hayden Nixc8a02bf2020-04-14 19:12:34 +0000261 raise ValueError('Invalid Bandwidth: %s' % channel_bandwidth)
262 ssid = utils.rand_ascii_str(hostapd_constants.AP_SSID_LENGTH_2G)
263 try:
Hayden Nixfe643052020-12-09 00:50:05 +0000264 setup_ap(access_point=self.access_point,
265 profile_name='whirlwind',
266 channel=channel,
267 security=security_profile,
268 n_capabilities=n_capabilities,
269 ac_capabilities=None,
270 force_wmm=True,
271 ssid=ssid,
272 vht_bandwidth=vht_bandwidth,
273 setup_bridge=True)
Hayden Nixc8a02bf2020-04-14 19:12:34 +0000274 except Exception as err:
275 raise ConnectionError(
276 'Failed to setup ap on channel: %s, channel bandwidth: %smhz. '
277 'Error: %s' % (channel, channel_bandwidth, err))
278 else:
279 self.log.info(
280 'Network (ssid: %s) up on channel %s w/ channel bandwidth %smhz'
281 % (ssid, channel, channel_bandwidth))
Hayden Nix3f2eebf2020-01-14 10:39:21 -0800282
Hayden Nixc8a02bf2020-04-14 19:12:34 +0000283 return ssid
Hayden Nix3f2eebf2020-01-14 10:39:21 -0800284
Hayden Nixc8a02bf2020-04-14 19:12:34 +0000285 def get_and_verify_iperf_address(self, channel, device, interface=None):
286 """Get ip address from a devices interface and verify it belongs to
287 expected subnet based on APs DHCP config.
Hayden Nix3f2eebf2020-01-14 10:39:21 -0800288
289 Args:
Hayden Nixc8a02bf2020-04-14 19:12:34 +0000290 channel: int, channel network is running on, to determine subnet
291 device: device to get ip address for
292 interface (default: None): interface on device to get ip address.
293 If None, uses device.test_interface.
Hayden Nix3f2eebf2020-01-14 10:39:21 -0800294
295 Returns:
Hayden Nixc8a02bf2020-04-14 19:12:34 +0000296 String, ip address of device on given interface (or test_interface)
297
298 Raises:
299 ConnectionError, if device does not have a valid ip address after
300 all retries.
Hayden Nix3f2eebf2020-01-14 10:39:21 -0800301 """
Hayden Nixc8a02bf2020-04-14 19:12:34 +0000302 if channel <= MAX_2_4_CHANNEL:
303 subnet = self.access_point._AP_2G_SUBNET_STR
304 else:
305 subnet = self.access_point._AP_5G_SUBNET_STR
306 end_time = time.time() + self.time_to_wait_for_ip_addr
307 while time.time() < end_time:
308 if interface:
309 device_addresses = device.get_interface_ip_addresses(interface)
310 else:
311 device_addresses = device.get_interface_ip_addresses(
312 device.test_interface)
313
314 if device_addresses['ipv4_private']:
315 for ip_addr in device_addresses['ipv4_private']:
316 if utils.ip_in_subnet(ip_addr, subnet):
317 return ip_addr
318 else:
319 self.log.debug(
320 'Device has an ip address (%s), but it is not in '
321 'subnet %s' % (ip_addr, subnet))
322 else:
323 self.log.debug(
324 'Device does not have a valid ip address. Retrying.')
325 time.sleep(TIME_TO_SLEEP_BETWEEN_RETRIES)
326 raise ConnectionError('Device failed to get an ip address.')
327
328 def get_iperf_throughput(self,
329 iperf_server_address,
330 iperf_client_address,
331 reverse=False):
332 """Run iperf between client and server and get the throughput.
333
334 Args:
335 iperf_server_address: string, ip address of running iperf server
336 iperf_client_address: string, ip address of iperf client (dut)
337 reverse (default: False): If True, run traffic in reverse direction,
338 from server to client.
339
340 Returns:
341 int, iperf throughput OR IPERF_NO_THROUGHPUT_VALUE, if iperf fails
342 """
343 if reverse:
344 self.log.info(
345 'Running IPerf traffic from server (%s) to dut (%s).' %
346 (iperf_server_address, iperf_client_address))
347 iperf_results_file = self.iperf_client.start(
Hayden Nix608ccda2021-04-21 19:51:46 +0000348 iperf_server_address,
349 '-i 1 -t 10 -R -J',
350 'channel_sweep_rx',
351 timeout=DEFAULT_IPERF_TIMEOUT)
Hayden Nixc8a02bf2020-04-14 19:12:34 +0000352 else:
353 self.log.info(
354 'Running IPerf traffic from dut (%s) to server (%s).' %
355 (iperf_client_address, iperf_server_address))
356 iperf_results_file = self.iperf_client.start(
Hayden Nix608ccda2021-04-21 19:51:46 +0000357 iperf_server_address,
358 '-i 1 -t 10 -J',
359 'channel_sweep_tx',
360 timeout=DEFAULT_IPERF_TIMEOUT)
Hayden Nixc8a02bf2020-04-14 19:12:34 +0000361 if iperf_results_file:
Hayden Nix7a69a4d2020-08-05 18:42:27 +0000362 iperf_results = IPerfResult(
363 iperf_results_file, reporting_speed_units=MEGABITS_PER_SECOND)
Hayden Nixc8a02bf2020-04-14 19:12:34 +0000364 return iperf_results.avg_send_rate
365 else:
366 return IPERF_NO_THROUGHPUT_VALUE
367
368 def log_to_file_and_throughput_data(self, channel, channel_bandwidth,
369 tx_throughput, rx_throughput):
370 """Write performance info to csv file and to throughput data.
371
372 Args:
373 channel: int, channel that test was run on
374 channel_bandwidth: int, channel bandwidth the test used
375 tx_throughput: float, throughput value from dut to iperf server
376 rx_throughput: float, throughput value from iperf server to dut
377 """
378 test_name = self.throughput_data['test']
379 output_path = context.get_current_context().get_base_output_path()
380 log_path = '%s/ChannelSweepTest/%s' % (output_path, test_name)
381 if not os.path.exists(log_path):
382 os.makedirs(log_path)
383 log_file = '%s/%s_%smhz.csv' % (log_path, test_name, channel_bandwidth)
384 self.log.info('Writing IPerf results for %s to %s' %
385 (test_name, log_file))
386 with open(log_file, 'a') as csv_file:
387 csv_file.write('%s,%s,%s\n' %
388 (channel, tx_throughput, rx_throughput))
389 self.throughput_data['results'][str(channel)] = {
390 'tx_throughput': tx_throughput,
391 'rx_throughput': rx_throughput
392 }
393
394 def write_graph(self):
395 """Create graph html files from throughput data, plotting channel vs
396 tx_throughput and channel vs rx_throughput.
397 """
Hayden Nixf6071682021-02-16 20:12:41 +0000398 # If performance measurement is skipped
399 if not self.iperf_server:
400 return
Hayden Nixc8a02bf2020-04-14 19:12:34 +0000401 output_path = context.get_current_context().get_base_output_path()
402 test_name = self.throughput_data['test']
403 channel_bandwidth = self.throughput_data['channel_bandwidth']
404 output_file_name = '%s/ChannelSweepTest/%s/%s_%smhz.html' % (
405 output_path, test_name, test_name, channel_bandwidth)
406 output_file(output_file_name)
407 channels = []
408 tx_throughputs = []
409 rx_throughputs = []
410 for channel in self.throughput_data['results']:
411 channels.append(str(channel))
412 tx_throughputs.append(
413 self.throughput_data['results'][channel]['tx_throughput'])
414 rx_throughputs.append(
415 self.throughput_data['results'][channel]['rx_throughput'])
416 channel_vs_throughput_data = ColumnDataSource(
417 data=dict(channels=channels,
418 tx_throughput=tx_throughputs,
419 rx_throughput=rx_throughputs))
420 TOOLTIPS = [('Channel', '@channels'),
421 ('TX_Throughput', '@tx_throughput'),
422 ('RX_Throughput', '@rx_throughput')]
423 channel_vs_throughput_graph = figure(title='Channels vs. Throughput',
424 x_axis_label='Channels',
425 x_range=channels,
426 y_axis_label='Throughput',
427 tooltips=TOOLTIPS)
428 channel_vs_throughput_graph.sizing_mode = 'stretch_both'
429 channel_vs_throughput_graph.title.align = 'center'
430 channel_vs_throughput_graph.line('channels',
431 'tx_throughput',
432 source=channel_vs_throughput_data,
433 line_width=2,
434 line_color='blue',
435 legend_label='TX_Throughput')
436 channel_vs_throughput_graph.circle('channels',
437 'tx_throughput',
438 source=channel_vs_throughput_data,
439 size=GRAPH_CIRCLE_SIZE,
440 color='blue')
441 channel_vs_throughput_graph.line('channels',
442 'rx_throughput',
443 source=channel_vs_throughput_data,
444 line_width=2,
445 line_color='red',
446 legend_label='RX_Throughput')
447 channel_vs_throughput_graph.circle('channels',
448 'rx_throughput',
449 source=channel_vs_throughput_data,
450 size=GRAPH_CIRCLE_SIZE,
451 color='red')
452
453 channel_vs_throughput_graph.legend.location = "top_left"
454 graph_file = save([channel_vs_throughput_graph])
455 self.log.info('Saved graph to %s' % graph_file)
456
457 def verify_standard_deviation(self, max_std_dev):
458 """Verifies the standard deviation of the throughput across the channels
459 does not exceed the max_std_dev value.
460
461 Args:
462 max_std_dev: float, max standard deviation of throughput for a test
Hayden Nix7a69a4d2020-08-05 18:42:27 +0000463 to pass (in Mb/s)
Hayden Nixc8a02bf2020-04-14 19:12:34 +0000464
465 Raises:
466 TestFailure, if standard deviation of throughput exceeds max_std_dev
467 """
Hayden Nixf6071682021-02-16 20:12:41 +0000468 # If performance measurement is skipped
469 if not self.iperf_server:
470 return
Hayden Nixc8a02bf2020-04-14 19:12:34 +0000471 self.log.info('Verifying standard deviation across channels does not '
Hayden Nix7a69a4d2020-08-05 18:42:27 +0000472 'exceed max standard deviation of %s Mb/s' % max_std_dev)
Hayden Nixc8a02bf2020-04-14 19:12:34 +0000473 tx_values = []
474 rx_values = []
475 for channel in self.throughput_data['results']:
476 if self.throughput_data['results'][channel][
477 'tx_throughput'] is not None:
478 tx_values.append(
479 self.throughput_data['results'][channel]['tx_throughput'])
480 if self.throughput_data['results'][channel][
481 'rx_throughput'] is not None:
482 rx_values.append(
483 self.throughput_data['results'][channel]['rx_throughput'])
484 tx_std_dev = pstdev(tx_values)
485 rx_std_dev = pstdev(rx_values)
486 if tx_std_dev > max_std_dev or rx_std_dev > max_std_dev:
487 asserts.fail(
488 'With %smhz channel bandwidth, throughput standard '
Hayden Nix7a69a4d2020-08-05 18:42:27 +0000489 'deviation (tx: %s Mb/s, rx: %s Mb/s) exceeds max standard '
490 'deviation (%s Mb/s).' %
Hayden Nix484f48c2020-04-14 20:54:17 +0000491 (self.throughput_data['channel_bandwidth'], tx_std_dev,
492 rx_std_dev, max_std_dev))
Hayden Nixc8a02bf2020-04-14 19:12:34 +0000493 else:
494 asserts.explicit_pass(
Hayden Nix7a69a4d2020-08-05 18:42:27 +0000495 'Throughput standard deviation (tx: %s Mb/s, rx: %s Mb/s) '
496 'with %smhz channel bandwidth does not exceed maximum (%s Mb/s).'
Hayden Nixc8a02bf2020-04-14 19:12:34 +0000497 % (tx_std_dev, rx_std_dev,
498 self.throughput_data['channel_bandwidth'], max_std_dev))
499
500 def run_channel_performance_tests(self, settings):
501 """Test function for running channel performance tests. Used by both
502 explicit test cases and debug test cases from config. Runs a performance
503 test for each channel in test_channels with test_channel_bandwidth, then
504 writes a graph and csv file of the channel vs throughput.
505
506 Args:
507 settings: dict, containing the following test settings
508 test_channels: list of channels to test.
509 test_channel_bandwidth: int, channel bandwidth to use for test.
510 test_security (optional): string, security type to use for test.
511 min_tx_throughput (optional, default: 0): float, minimum tx
512 throughput threshold to pass individual channel tests
Hayden Nix7a69a4d2020-08-05 18:42:27 +0000513 (in Mb/s).
Hayden Nixc8a02bf2020-04-14 19:12:34 +0000514 min_rx_throughput (optional, default: 0): float, minimum rx
515 throughput threshold to pass individual channel tests
Hayden Nix7a69a4d2020-08-05 18:42:27 +0000516 (in Mb/s).
Hayden Nixc8a02bf2020-04-14 19:12:34 +0000517 max_std_dev (optional, default: 1): float, maximum standard
518 deviation of throughput across all test channels to pass
Hayden Nix7a69a4d2020-08-05 18:42:27 +0000519 test (in Mb/s).
Hayden Nixc8a02bf2020-04-14 19:12:34 +0000520 base_test_name (optional): string, test name prefix to use with
521 generated subtests.
Hayden Nix484f48c2020-04-14 20:54:17 +0000522 country_name (optional): string, country name from
523 hostapd_constants to set on device.
524 country_code (optional): string, two-char country code to set on
525 the DUT. Takes priority over country_name.
Hayden Nixc8a02bf2020-04-14 19:12:34 +0000526 test_name (debug tests only): string, the test name for this
527 parent test case from the config file. In explicit tests,
528 this is not necessary.
529
530 Writes:
531 CSV file: channel, tx_throughput, rx_throughput
532 for every test channel.
533 Graph: channel vs tx_throughput & channel vs rx_throughput
534
535 Raises:
536 TestFailure, if throughput standard deviation across channels
537 exceeds max_std_dev
538
539 Example Explicit Test (see EOF for debug JSON example):
540 def test_us_2g_20mhz_wpa2(self):
541 self.run_channel_performance_tests(
542 dict(
543 test_channels=hostapd_constants.US_CHANNELS_2G,
544 test_channel_bandwidth=20,
545 test_security=hostapd_constants.WPA2_STRING,
546 min_tx_throughput=2,
547 min_rx_throughput=4,
548 max_std_dev=0.75,
Hayden Nix484f48c2020-04-14 20:54:17 +0000549 country_code='US',
Hayden Nixc8a02bf2020-04-14 19:12:34 +0000550 base_test_name='test_us'))
551 """
552 test_channels = settings['test_channels']
553 test_channel_bandwidth = settings['test_channel_bandwidth']
Hayden Nix7a69a4d2020-08-05 18:42:27 +0000554 test_security = settings.get('test_security', None)
Hayden Nixc8a02bf2020-04-14 19:12:34 +0000555 test_name = settings.get('test_name', self.test_name)
556 base_test_name = settings.get('base_test_name', 'test')
557 min_tx_throughput = settings.get('min_tx_throughput',
558 DEFAULT_MIN_THROUGHPUT)
559 min_rx_throughput = settings.get('min_rx_throughput',
560 DEFAULT_MIN_THROUGHPUT)
561 max_std_dev = settings.get('max_std_dev', DEFAULT_MAX_STD_DEV)
Hayden Nix484f48c2020-04-14 20:54:17 +0000562 country_code = settings.get('country_code')
563 country_name = settings.get('country_name')
564 country_label = None
565
566 if country_code:
567 country_label = country_code
568 self.set_dut_country_code(country_code)
569 elif country_name:
570 country_label = country_name
571 code = hostapd_constants.COUNTRY_CODE[country_name]['country_code']
572 self.set_dut_country_code(code)
Hayden Nixc8a02bf2020-04-14 19:12:34 +0000573
574 self.throughput_data = {
575 'test': test_name,
576 'channel_bandwidth': test_channel_bandwidth,
577 'results': {}
578 }
Hayden Nix3f2eebf2020-01-14 10:39:21 -0800579 test_list = []
Hayden Nixc8a02bf2020-04-14 19:12:34 +0000580 for channel in test_channels:
Hayden Nix7a69a4d2020-08-05 18:42:27 +0000581 sub_test_name = 'test_%schannel_%s_%smhz_%s_performance' % (
Hayden Nix484f48c2020-04-14 20:54:17 +0000582 '%s_' % country_label if country_label else '', channel,
Hayden Nix7a69a4d2020-08-05 18:42:27 +0000583 test_channel_bandwidth,
584 test_security if test_security else 'open')
Hayden Nixc8a02bf2020-04-14 19:12:34 +0000585 test_list.append({
586 'test_name': sub_test_name,
587 'channel': int(channel),
588 'channel_bandwidth': int(test_channel_bandwidth),
589 'security': test_security,
590 'min_tx_throughput': min_tx_throughput,
591 'min_rx_throughput': min_rx_throughput
592 })
593 self.run_generated_testcases(self.get_channel_performance,
Hayden Nix3f2eebf2020-01-14 10:39:21 -0800594 settings=test_list,
Hayden Nixc8a02bf2020-04-14 19:12:34 +0000595 name_func=get_test_name)
596 self.log.info('Channel tests completed.')
597 self.write_graph()
598 self.verify_standard_deviation(max_std_dev)
Hayden Nix3f2eebf2020-01-14 10:39:21 -0800599
Hayden Nixc8a02bf2020-04-14 19:12:34 +0000600 def get_channel_performance(self, settings):
601 """Run a single channel performance test and logs results to csv file
602 and throughput data. Run with generated sub test cases in
603 run_channel_performance_tests.
604
605 1. Sets up network with test settings
606 2. Associates DUT
607 3. Runs traffic between DUT and iperf server (both directions)
Hayden Nix7a69a4d2020-08-05 18:42:27 +0000608 4. Logs channel, tx_throughput (Mb/s), and rx_throughput (Mb/s) to
Hayden Nixc8a02bf2020-04-14 19:12:34 +0000609 log file and throughput data.
610 5. Checks throughput values against minimum throughput thresholds.
611
612 Args:
613 settings: see run_channel_performance_tests
614
615 Raises:
616 TestFailure, if throughput (either direction) is less than
617 the directions given minimum throughput threshold.
Hayden Nix3f2eebf2020-01-14 10:39:21 -0800618 """
Hayden Nixc8a02bf2020-04-14 19:12:34 +0000619 channel = settings['channel']
620 channel_bandwidth = settings['channel_bandwidth']
621 security = settings['security']
622 test_name = settings['test_name']
623 min_tx_throughput = settings['min_tx_throughput']
624 min_rx_throughput = settings['min_rx_throughput']
625 if security:
626 if security == hostapd_constants.WEP_STRING:
627 password = utils.rand_hex_str(WEP_HEX_STRING_LENGTH)
628 else:
629 password = utils.rand_ascii_str(
630 hostapd_constants.MIN_WPA_PSK_LENGTH)
631 security_profile = Security(security_mode=security,
632 password=password)
Hayden Nix6977b1f2021-02-04 19:00:46 +0000633 target_security = hostapd_constants.SECURITY_STRING_TO_DEFAULT_TARGET_SECURITY.get(
634 security)
Hayden Nixc8a02bf2020-04-14 19:12:34 +0000635 else:
636 password = None
637 security_profile = None
Hayden Nix6977b1f2021-02-04 19:00:46 +0000638 target_security = None
Hayden Nixc8a02bf2020-04-14 19:12:34 +0000639 ssid = self.setup_ap(channel, channel_bandwidth, security_profile)
Hayden Nix6977b1f2021-02-04 19:00:46 +0000640 associated = self.dut.associate(ssid,
641 target_pwd=password,
642 target_security=target_security)
Hayden Nixc8a02bf2020-04-14 19:12:34 +0000643 if not associated:
Hayden Nixf6071682021-02-16 20:12:41 +0000644 if self.iperf_server:
645 self.log_to_file_and_throughput_data(channel,
646 channel_bandwidth, None,
647 None)
Hayden Nixc8a02bf2020-04-14 19:12:34 +0000648 asserts.fail('Device failed to associate with network %s' % ssid)
649 self.log.info('DUT (%s) connected to network %s.' %
650 (self.dut.device.ip, ssid))
Hayden Nixf6071682021-02-16 20:12:41 +0000651 if self.iperf_server:
652 self.iperf_server.renew_test_interface_ip_address()
653 self.log.info(
654 'Getting ip address for iperf server. Will retry for %s seconds.'
655 % self.time_to_wait_for_ip_addr)
656 iperf_server_address = self.get_and_verify_iperf_address(
657 channel, self.iperf_server)
658 self.log.info(
659 'Getting ip address for DUT. Will retry for %s seconds.' %
660 self.time_to_wait_for_ip_addr)
661 iperf_client_address = self.get_and_verify_iperf_address(
662 channel, self.dut, self.iperf_client.test_interface)
663 tx_throughput = self.get_iperf_throughput(iperf_server_address,
664 iperf_client_address)
665 rx_throughput = self.get_iperf_throughput(iperf_server_address,
666 iperf_client_address,
667 reverse=True)
668 self.log_to_file_and_throughput_data(channel, channel_bandwidth,
669 tx_throughput, rx_throughput)
670 self.log.info('Throughput (tx, rx): (%s Mb/s, %s Mb/s), '
671 'Minimum threshold (tx, rx): (%s Mb/s, %s Mb/s)' %
672 (tx_throughput, rx_throughput, min_tx_throughput,
673 min_rx_throughput))
674 base_message = (
675 'Actual throughput (on channel: %s, channel bandwidth: '
676 '%s, security: %s)' % (channel, channel_bandwidth, security))
Hayden Nix608ccda2021-04-21 19:51:46 +0000677 if (not tx_throughput or not rx_throughput
678 or tx_throughput < min_tx_throughput
Hayden Nixf6071682021-02-16 20:12:41 +0000679 or rx_throughput < min_rx_throughput):
680 asserts.fail('%s below the minimum threshold.' % base_message)
681 asserts.explicit_pass('%s above the minimum threshold.' %
682 base_message)
683 else:
684 asserts.explicit_pass(
685 'Association test pass. No throughput measurement taken.')
Hayden Nix3f2eebf2020-01-14 10:39:21 -0800686
Hayden Nix484f48c2020-04-14 20:54:17 +0000687 def verify_regulatory_compliance(self, settings):
688 """Test function for regulatory compliance tests. Verify device complies
689 with provided regulatory requirements.
690
691 Args:
692 settings: dict, containing the following test settings
693 test_channels: dict, mapping channels to a set of the channel
694 bandwidths to test (see example for using JSON). Defaults
695 to hostapd_constants.ALL_CHANNELS.
696 country_code: string, two-char country code to set on device
697 (prioritized over country_name)
698 country_name: string, country name from hostapd_constants to set
699 on device.
700 base_test_name (optional): string, test name prefix to use with
701 generatedsubtests.
702 test_name: string, the test name for this
703 parent test case from the config file. In explicit tests,
704 this is not necessary.
705 """
706 country_name = settings.get('country_name')
707 country_code = settings.get('country_code')
708 if not (country_code or country_name):
709 raise ValueError('No country code or name provided.')
710
711 test_channels = settings.get('test_channels',
712 hostapd_constants.ALL_CHANNELS)
713 allowed_channels = settings['allowed_channels']
714
715 base_test_name = settings.get('base_test_name', 'test_compliance')
716
717 if country_code:
718 code = country_code
719 else:
720 code = hostapd_constants.COUNTRY_CODE[country_name]['country_code']
721
722 self.set_dut_country_code(code)
723
724 test_list = []
725 for channel in test_channels:
726 for channel_bandwidth in test_channels[channel]:
727 sub_test_name = '%s_channel_%s_%smhz' % (
728 base_test_name, channel, channel_bandwidth)
Hayden Nix7a69a4d2020-08-05 18:42:27 +0000729 should_associate = (channel in allowed_channels
730 and channel_bandwidth
731 in allowed_channels[channel])
Hayden Nix484f48c2020-04-14 20:54:17 +0000732 # Note: these int conversions because when these tests are
733 # imported via JSON, they may be strings since the channels
734 # will be keys. This makes the json/list test_channels param
735 # behave exactly like the in code dict/set test_channels.
736 test_list.append({
737 'country_code': code,
738 'channel': int(channel),
739 'channel_bandwidth': int(channel_bandwidth),
740 'should_associate': should_associate,
741 'test_name': sub_test_name
742 })
743 self.run_generated_testcases(test_func=self.verify_channel_compliance,
744 settings=test_list,
745 name_func=get_test_name)
746
747 def verify_channel_compliance(self, settings):
748 """Verify device complies with provided regulatory requirements for a
749 specific channel and channel bandwidth. Run with generated test cases
750 in the verify_regulatory_compliance parent test.
751_
752 Args:
753 settings: see verify_regulatory_compliance`
754 """
755 channel = settings['channel']
756 channel_bandwidth = settings['channel_bandwidth']
757 code = settings['country_code']
758 should_associate = settings['should_associate']
759
760 ssid = self.setup_ap(channel, channel_bandwidth)
761
762 self.log.info(
763 'Attempting to associate with network (%s) on channel %s @ %smhz. '
764 'Expected behavior: %s' %
765 (ssid, channel, channel_bandwidth, 'Device should associate'
766 if should_associate else 'Device should NOT associate.'))
767
Hayden Nixfe643052020-12-09 00:50:05 +0000768 associated = self.dut.associate(ssid)
Tom Turneyc0201172021-04-28 16:08:33 -0700769
770 regulatory_result_marker = "REGTRACKER: %s,%s,%s,%s,%s" % (
771 code, channel, '2.4' if channel < 36 else '5', channel_bandwidth,
772 'c' if associated else 'nc')
773 self.regulatory_results += regulatory_result_marker + "\n"
774 self.log.info(regulatory_result_marker)
775
Hayden Nix484f48c2020-04-14 20:54:17 +0000776 if associated == should_associate:
777 asserts.explicit_pass(
778 'Device complied with %s regulatory requirement for channel %s '
779 ' with channel bandwidth %smhz. %s' %
780 (code, channel, channel_bandwidth,
781 'Associated.' if associated else 'Refused to associate.'))
782 else:
783 asserts.fail(
784 'Device failed compliance with regulatory domain %s for '
785 'channel %s with channel bandwidth %smhz. Expected: %s, Got: %s'
786 % (code, channel, channel_bandwidth, 'Should associate'
787 if should_associate else 'Should not associate',
788 'Associated' if associated else 'Did not associate'))
789
Hayden Nixc8a02bf2020-04-14 19:12:34 +0000790 # Helper functions to allow explicit tests throughput and standard deviation
791 # thresholds to be passed in via config.
792 def _get_min_tx_throughput(self, test_name):
793 return self.user_params.get('channel_sweep_test_params',
794 {}).get(test_name,
795 {}).get('min_tx_throughput',
796 DEFAULT_MIN_THROUGHPUT)
Hayden Nix3f2eebf2020-01-14 10:39:21 -0800797
Hayden Nixc8a02bf2020-04-14 19:12:34 +0000798 def _get_min_rx_throughput(self, test_name):
799 return self.user_params.get('channel_sweep_test_params',
800 {}).get(test_name,
801 {}).get('min_rx_throughput',
802 DEFAULT_MIN_THROUGHPUT)
803
804 def _get_max_std_dev(self, test_name):
805 return self.user_params.get('channel_sweep_test_params',
806 {}).get(test_name,
807 {}).get('min_std_dev',
808 DEFAULT_MAX_STD_DEV)
809
Hayden Nix6f3d71c2020-05-20 00:10:39 +0000810 # Channel Performance of US Channels: 570 Test Cases
811 # 36 Test Cases
Hayden Nixc8a02bf2020-04-14 19:12:34 +0000812 def test_us_20mhz_open_channel_performance(self):
813 self.run_channel_performance_tests(
814 dict(test_channels=hostapd_constants.US_CHANNELS_2G +
815 hostapd_constants.US_CHANNELS_5G,
816 test_channel_bandwidth=hostapd_constants.
817 CHANNEL_BANDWIDTH_20MHZ,
818 base_test_name=self.test_name,
819 min_tx_throughput=self._get_min_tx_throughput(self.test_name),
820 min_rx_throughput=self._get_min_rx_throughput(self.test_name),
821 max_std_dev=self._get_max_std_dev(self.test_name)))
822
Hayden Nix6f3d71c2020-05-20 00:10:39 +0000823 # 35 Test Cases
Hayden Nixc8a02bf2020-04-14 19:12:34 +0000824 def test_us_40mhz_open_channel_performance(self):
825 self.run_channel_performance_tests(
826 dict(test_channels=hostapd_constants.US_CHANNELS_2G +
827 hostapd_constants.US_CHANNELS_5G[:-1],
828 test_channel_bandwidth=hostapd_constants.
829 CHANNEL_BANDWIDTH_40MHZ,
830 base_test_name=self.test_name,
831 min_tx_throughput=self._get_min_tx_throughput(self.test_name),
832 min_rx_throughput=self._get_min_rx_throughput(self.test_name),
833 max_std_dev=self._get_max_std_dev(self.test_name)))
834
Hayden Nix6f3d71c2020-05-20 00:10:39 +0000835 # 24 Test Cases
Hayden Nixc8a02bf2020-04-14 19:12:34 +0000836 def test_us_80mhz_open_channel_performance(self):
837 self.run_channel_performance_tests(
838 dict(test_channels=hostapd_constants.US_CHANNELS_5G[:-1],
839 test_channel_bandwidth=hostapd_constants.
840 CHANNEL_BANDWIDTH_80MHZ,
841 base_test_name=self.test_name,
842 min_tx_throughput=self._get_min_tx_throughput(self.test_name),
843 min_rx_throughput=self._get_min_rx_throughput(self.test_name),
844 max_std_dev=self._get_max_std_dev(self.test_name)))
845
Hayden Nix6f3d71c2020-05-20 00:10:39 +0000846 # 36 Test Cases
Hayden Nixc8a02bf2020-04-14 19:12:34 +0000847 def test_us_20mhz_wep_channel_performance(self):
848 self.run_channel_performance_tests(
849 dict(test_channels=hostapd_constants.US_CHANNELS_2G +
850 hostapd_constants.US_CHANNELS_5G,
851 test_channel_bandwidth=hostapd_constants.
852 CHANNEL_BANDWIDTH_20MHZ,
853 test_security=hostapd_constants.WEP_STRING,
854 base_test_name=self.test_name,
855 min_tx_throughput=self._get_min_tx_throughput(self.test_name),
856 min_rx_throughput=self._get_min_rx_throughput(self.test_name),
857 max_std_dev=self._get_max_std_dev(self.test_name)))
858
Hayden Nix6f3d71c2020-05-20 00:10:39 +0000859 # 35 Test Cases
Hayden Nixc8a02bf2020-04-14 19:12:34 +0000860 def test_us_40mhz_wep_channel_performance(self):
861 self.run_channel_performance_tests(
862 dict(test_channels=hostapd_constants.US_CHANNELS_2G +
863 hostapd_constants.US_CHANNELS_5G[:-1],
864 test_channel_bandwidth=hostapd_constants.
865 CHANNEL_BANDWIDTH_40MHZ,
866 test_security=hostapd_constants.WEP_STRING,
867 base_test_name=self.test_name,
868 min_tx_throughput=self._get_min_tx_throughput(self.test_name),
869 min_rx_throughput=self._get_min_rx_throughput(self.test_name),
870 max_std_dev=self._get_max_std_dev(self.test_name)))
871
Hayden Nix6f3d71c2020-05-20 00:10:39 +0000872 # 24 Test Cases
Hayden Nixc8a02bf2020-04-14 19:12:34 +0000873 def test_us_80mhz_wep_channel_performance(self):
874 self.run_channel_performance_tests(
875 dict(test_channels=hostapd_constants.US_CHANNELS_5G[:-1],
876 test_channel_bandwidth=hostapd_constants.
877 CHANNEL_BANDWIDTH_80MHZ,
878 test_security=hostapd_constants.WEP_STRING,
879 base_test_name=self.test_name,
880 min_tx_throughput=self._get_min_tx_throughput(self.test_name),
881 min_rx_throughput=self._get_min_rx_throughput(self.test_name),
882 max_std_dev=self._get_max_std_dev(self.test_name)))
883
Hayden Nix6f3d71c2020-05-20 00:10:39 +0000884 # 36 Test Cases
Hayden Nixc8a02bf2020-04-14 19:12:34 +0000885 def test_us_20mhz_wpa_channel_performance(self):
886 self.run_channel_performance_tests(
887 dict(test_channels=hostapd_constants.US_CHANNELS_2G +
888 hostapd_constants.US_CHANNELS_5G,
889 test_channel_bandwidth=hostapd_constants.
890 CHANNEL_BANDWIDTH_20MHZ,
891 test_security=hostapd_constants.WPA_STRING,
892 base_test_name=self.test_name,
893 min_tx_throughput=self._get_min_tx_throughput(self.test_name),
894 min_rx_throughput=self._get_min_rx_throughput(self.test_name),
895 max_std_dev=self._get_max_std_dev(self.test_name)))
896
Hayden Nix6f3d71c2020-05-20 00:10:39 +0000897 # 35 Test Cases
Hayden Nixc8a02bf2020-04-14 19:12:34 +0000898 def test_us_40mhz_wpa_channel_performance(self):
899 self.run_channel_performance_tests(
900 dict(test_channels=hostapd_constants.US_CHANNELS_2G +
901 hostapd_constants.US_CHANNELS_5G[:-1],
902 test_channel_bandwidth=hostapd_constants.
903 CHANNEL_BANDWIDTH_40MHZ,
904 test_security=hostapd_constants.WPA_STRING,
905 base_test_name=self.test_name,
906 min_tx_throughput=self._get_min_tx_throughput(self.test_name),
907 min_rx_throughput=self._get_min_rx_throughput(self.test_name),
908 max_std_dev=self._get_max_std_dev(self.test_name)))
909
Hayden Nix6f3d71c2020-05-20 00:10:39 +0000910 # 24 Test Cases
Hayden Nixc8a02bf2020-04-14 19:12:34 +0000911 def test_us_80mhz_wpa_channel_performance(self):
912 self.run_channel_performance_tests(
913 dict(test_channels=hostapd_constants.US_CHANNELS_5G[:-1],
914 test_channel_bandwidth=hostapd_constants.
915 CHANNEL_BANDWIDTH_80MHZ,
916 test_security=hostapd_constants.WPA_STRING,
917 base_test_name=self.test_name,
918 min_tx_throughput=self._get_min_tx_throughput(self.test_name),
919 min_rx_throughput=self._get_min_rx_throughput(self.test_name),
920 max_std_dev=self._get_max_std_dev(self.test_name)))
921
Hayden Nix6f3d71c2020-05-20 00:10:39 +0000922 # 36 Test Cases
Hayden Nixc8a02bf2020-04-14 19:12:34 +0000923 def test_us_20mhz_wpa2_channel_performance(self):
924 self.run_channel_performance_tests(
925 dict(test_channels=hostapd_constants.US_CHANNELS_2G +
926 hostapd_constants.US_CHANNELS_5G,
927 test_channel_bandwidth=hostapd_constants.
928 CHANNEL_BANDWIDTH_20MHZ,
929 test_security=hostapd_constants.WPA2_STRING,
930 base_test_name=self.test_name,
931 min_tx_throughput=self._get_min_tx_throughput(self.test_name),
932 min_rx_throughput=self._get_min_rx_throughput(self.test_name),
933 max_std_dev=self._get_max_std_dev(self.test_name)))
934
Hayden Nix6f3d71c2020-05-20 00:10:39 +0000935 # 35 Test Cases
Hayden Nixc8a02bf2020-04-14 19:12:34 +0000936 def test_us_40mhz_wpa2_channel_performance(self):
937 self.run_channel_performance_tests(
938 dict(test_channels=hostapd_constants.US_CHANNELS_2G +
939 hostapd_constants.US_CHANNELS_5G[:-1],
940 test_channel_bandwidth=hostapd_constants.
941 CHANNEL_BANDWIDTH_40MHZ,
942 test_security=hostapd_constants.WPA2_STRING,
943 base_test_name=self.test_name,
944 min_tx_throughput=self._get_min_tx_throughput(self.test_name),
945 min_rx_throughput=self._get_min_rx_throughput(self.test_name),
946 max_std_dev=self._get_max_std_dev(self.test_name)))
947
Hayden Nix6f3d71c2020-05-20 00:10:39 +0000948 # 24 Test Cases
Hayden Nixc8a02bf2020-04-14 19:12:34 +0000949 def test_us_80mhz_wpa2_channel_performance(self):
950 self.run_channel_performance_tests(
951 dict(test_channels=hostapd_constants.US_CHANNELS_5G[:-1],
952 test_channel_bandwidth=hostapd_constants.
953 CHANNEL_BANDWIDTH_80MHZ,
954 test_security=hostapd_constants.WPA2_STRING,
955 base_test_name=self.test_name,
956 min_tx_throughput=self._get_min_tx_throughput(self.test_name),
957 min_rx_throughput=self._get_min_rx_throughput(self.test_name),
958 max_std_dev=self._get_max_std_dev(self.test_name)))
959
Hayden Nix6f3d71c2020-05-20 00:10:39 +0000960 # 36 Test Cases
Hayden Nixc8a02bf2020-04-14 19:12:34 +0000961 def test_us_20mhz_wpa_wpa2_channel_performance(self):
962 self.run_channel_performance_tests(
963 dict(test_channels=hostapd_constants.US_CHANNELS_2G +
964 hostapd_constants.US_CHANNELS_5G,
965 test_channel_bandwidth=hostapd_constants.
966 CHANNEL_BANDWIDTH_20MHZ,
967 test_security=hostapd_constants.WPA_MIXED_STRING,
968 base_test_name=self.test_name,
969 min_tx_throughput=self._get_min_tx_throughput(self.test_name),
970 min_rx_throughput=self._get_min_rx_throughput(self.test_name),
971 max_std_dev=self._get_max_std_dev(self.test_name)))
972
Hayden Nix6f3d71c2020-05-20 00:10:39 +0000973 # 35 Test Cases
Hayden Nixc8a02bf2020-04-14 19:12:34 +0000974 def test_us_40mhz_wpa_wpa2_channel_performance(self):
975 self.run_channel_performance_tests(
976 dict(test_channels=hostapd_constants.US_CHANNELS_2G +
977 hostapd_constants.US_CHANNELS_5G[:-1],
978 test_channel_bandwidth=hostapd_constants.
979 CHANNEL_BANDWIDTH_40MHZ,
980 test_security=hostapd_constants.WPA_MIXED_STRING,
981 base_test_name=self.test_name,
982 min_tx_throughput=self._get_min_tx_throughput(self.test_name),
983 min_rx_throughput=self._get_min_rx_throughput(self.test_name),
984 max_std_dev=self._get_max_std_dev(self.test_name)))
985
Hayden Nix6f3d71c2020-05-20 00:10:39 +0000986 # 24 Test Cases
Hayden Nixc8a02bf2020-04-14 19:12:34 +0000987 def test_us_80mhz_wpa_wpa2_channel_performance(self):
988 self.run_channel_performance_tests(
989 dict(test_channels=hostapd_constants.US_CHANNELS_5G[:-1],
990 test_channel_bandwidth=hostapd_constants.
991 CHANNEL_BANDWIDTH_80MHZ,
992 test_security=hostapd_constants.WPA_MIXED_STRING,
993 base_test_name=self.test_name,
994 min_tx_throughput=self._get_min_tx_throughput(self.test_name),
995 min_rx_throughput=self._get_min_rx_throughput(self.test_name),
996 max_std_dev=self._get_max_std_dev(self.test_name)))
997
Hayden Nix6f3d71c2020-05-20 00:10:39 +0000998 # 36 Test Cases
Hayden Nixc8a02bf2020-04-14 19:12:34 +0000999 def test_us_20mhz_wpa3_channel_performance(self):
1000 self.run_channel_performance_tests(
1001 dict(test_channels=hostapd_constants.US_CHANNELS_2G +
1002 hostapd_constants.US_CHANNELS_5G,
1003 test_channel_bandwidth=hostapd_constants.
1004 CHANNEL_BANDWIDTH_20MHZ,
1005 test_security=hostapd_constants.WPA3_STRING,
1006 base_test_name=self.test_name,
1007 min_tx_throughput=self._get_min_tx_throughput(self.test_name),
1008 min_rx_throughput=self._get_min_rx_throughput(self.test_name),
1009 max_std_dev=self._get_max_std_dev(self.test_name)))
1010
Hayden Nix6f3d71c2020-05-20 00:10:39 +00001011 # 35 Test Cases
Hayden Nixc8a02bf2020-04-14 19:12:34 +00001012 def test_us_40mhz_wpa3_channel_performance(self):
1013 self.run_channel_performance_tests(
1014 dict(test_channels=hostapd_constants.US_CHANNELS_2G +
1015 hostapd_constants.US_CHANNELS_5G[:-1],
1016 test_channel_bandwidth=hostapd_constants.
1017 CHANNEL_BANDWIDTH_40MHZ,
1018 test_security=hostapd_constants.WPA3_STRING,
1019 base_test_name=self.test_name,
1020 min_tx_throughput=self._get_min_tx_throughput(self.test_name),
1021 min_rx_throughput=self._get_min_rx_throughput(self.test_name),
1022 max_std_dev=self._get_max_std_dev(self.test_name)))
1023
Hayden Nix6f3d71c2020-05-20 00:10:39 +00001024 # 24 Test Cases
Hayden Nixc8a02bf2020-04-14 19:12:34 +00001025 def test_us_80mhz_wpa3_channel_performance(self):
1026 self.run_channel_performance_tests(
1027 dict(test_channels=hostapd_constants.US_CHANNELS_5G[:-1],
1028 test_channel_bandwidth=hostapd_constants.
1029 CHANNEL_BANDWIDTH_80MHZ,
1030 test_security=hostapd_constants.WPA3_STRING,
1031 base_test_name=self.test_name,
1032 min_tx_throughput=self._get_min_tx_throughput(self.test_name),
1033 min_rx_throughput=self._get_min_rx_throughput(self.test_name),
1034 max_std_dev=self._get_max_std_dev(self.test_name)))
1035
1036 def test_channel_performance_debug(self):
1037 """Run channel performance test cases from the ACTS config file.
1038
1039 Example:
1040 "channel_sweep_test_params": {
1041 "debug_channel_performance_tests": [
1042 {
1043 "test_name": "test_123_20mhz_wpa2_performance"
1044 "test_channels": [1, 2, 3],
1045 "test_channel_bandwidth": 20,
1046 "test_security": "wpa2",
1047 "base_test_name": "test_123_perf",
1048 "min_tx_throughput": 1.1,
1049 "min_rx_throughput": 3,
1050 "max_std_dev": 0.5
1051 },
1052 ...
1053 ]
1054 }
1055
Hayden Nix3f2eebf2020-01-14 10:39:21 -08001056 """
Hayden Nixc8a02bf2020-04-14 19:12:34 +00001057 asserts.skip_if(
Hayden Nix7a69a4d2020-08-05 18:42:27 +00001058 'debug_channel_performance_tests'
1059 not in self.user_params.get('channel_sweep_test_params', {}),
Hayden Nixc8a02bf2020-04-14 19:12:34 +00001060 'No custom channel performance tests provided in config.')
1061 base_tests = self.user_params['channel_sweep_test_params'][
1062 'debug_channel_performance_tests']
1063 self.run_generated_testcases(self.run_channel_performance_tests,
1064 settings=base_tests,
1065 name_func=get_test_name)
Hayden Nix484f48c2020-04-14 20:54:17 +00001066
1067 def test_regulatory_compliance(self):
1068 """Run regulatory compliance test case from the ACTS config file.
1069 Note: only one country_name OR country_code is required.
1070
1071 Example:
1072 "channel_sweep_test_params": {
1073 "regulatory_compliance_tests": [
1074 {
1075 "test_name": "test_japan_compliance_1_13_36"
1076 "country_name": "JAPAN",
1077 "country_code": "JP",
1078 "test_channels": {
1079 "1": [20, 40], "13": [40], "36": [20, 40, 80]
1080 },
1081 "allowed_channels": {
1082 "1": [20, 40], "36": [20, 40, 80]
1083 },
1084 "base_test_name": "test_japan"
1085 },
1086 ...
1087 ]
1088 }
1089 """
1090 asserts.skip_if(
Hayden Nix7a69a4d2020-08-05 18:42:27 +00001091 'regulatory_compliance_tests'
1092 not in self.user_params.get('channel_sweep_test_params', {}),
Hayden Nix484f48c2020-04-14 20:54:17 +00001093 'No custom regulatory compliance tests provided in config.')
1094 base_tests = self.user_params['channel_sweep_test_params'][
1095 'regulatory_compliance_tests']
1096 self.run_generated_testcases(self.verify_regulatory_compliance,
1097 settings=base_tests,
1098 name_func=get_test_name)